vite-plugin-env-parse
Version:
Parse env string values to correct values, not all values are strings. And automatically generate ImportMetaEnv
240 lines (232 loc) • 9.38 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
envParse: () => envParse,
parseLoadedEnv: () => parseEnv
});
module.exports = __toCommonJS(src_exports);
var import_node_fs2 = __toESM(require("fs"));
var import_node_path = __toESM(require("path"));
// src/parseEnvComment.ts
function parseEnvComment(content) {
let res = {};
let lines = content.toString().replace(/\r\n?/gm, "\n").split("\n");
const commentReg = /^\s*#\s*(.*)\s*/;
const keyReg = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)/;
const startMultiSharpReg = /^\#+/;
let commentString = [];
lines.forEach((line) => {
if (commentReg.test(line)) {
const comment = line.replace(commentReg, "$1").replace(startMultiSharpReg, "").trim();
commentString.push(comment);
} else if (keyReg.test(line)) {
const matched = line.match(keyReg);
const key = matched ? matched[1] : "";
key && commentString.length && (res[key] = commentString.join("\n * "));
commentString.length = 0;
} else if (commentString.length) {
commentString.length = 0;
}
});
return res;
}
// src/utils.ts
var htmlLangRE = /\.(?:html|htm)$/;
var CSS_LANGS_RE = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
var nonJsRe = /\.json(?:$|\?)/;
var isHTMLRequest = (requestId) => htmlLangRE.test(requestId);
var isCSSRequest = (requestId) => CSS_LANGS_RE.test(requestId);
var isNonJsRequest = (requestId) => nonJsRe.test(requestId);
function errorLog(content) {
console.log("\n");
console.log("\x1B[31m%s%s\x1B[0m", "\u2718 [env-parse] - ", content);
console.log();
}
// src/parseEnv.ts
function parseEnv(env, options = {}) {
const { parseJson = true, exclude = [], customParser } = options;
const parsedRes = {};
const booleanValueMap = {
true: true,
false: false
};
for (const envKey of Object.keys(env)) {
let value = env[envKey];
if (typeof value === "string" || !exclude.includes(envKey)) {
if (value === "true" || value === "false") {
value = booleanValueMap[value];
} else if (typeof value !== "boolean" && value !== "" && !isNaN(value)) {
value = parseFloat(value) || value;
} else if (parseJson) {
try {
value = JSON.parse(value);
} catch (e) {
try {
const temp = (0, eval)(`(${value})`);
if (!(temp instanceof RegExp)) {
value = temp;
}
} catch (e2) {
}
}
}
if (customParser) {
value = customParser && customParser(envKey, value) || value;
}
}
parsedRes[envKey] = value;
}
return parsedRes;
}
// src/writeEnvInterface.ts
var import_node_fs = __toESM(require("fs"));
function writeEnvInterface(path2, envInterface) {
const importMetaEnvRegexp = /interface ImportMetaEnv\s*\{[\s\S]*?\}/g;
if (import_node_fs.default.existsSync(path2)) {
const fileContent = import_node_fs.default.readFileSync(path2, { encoding: "utf-8" });
if (importMetaEnvRegexp.test(fileContent)) {
envInterface = fileContent.replace(importMetaEnvRegexp, envInterface);
} else {
envInterface = `${fileContent}
${envInterface}`;
}
} else {
envInterface = `/// <reference types="vite/client" />
${envInterface}`;
}
import_node_fs.default.writeFileSync(path2, envInterface);
}
// src/generateEnvInterface.ts
function generateEnvInterface(env, commentRecord) {
let interfaceItem = [];
const excludeKey = ["MODE", "BASE_URL", "PROD", "DEV", "SSR"];
const typeMap = {
boolean: "boolean",
string: "string",
number: "number",
array: "any[]",
object: "Record<string, any>"
};
for (const envKey of Object.keys(env)) {
if (excludeKey.includes(envKey)) continue;
const value = env[envKey];
const comment = commentRecord[envKey];
let valueType = typeof value;
valueType = valueType === "object" ? Array.isArray(value) ? "array" : valueType : valueType;
const jsDocComment = comment ? `/**
* ${comment}
*/
` : "";
const keyValue = `readonly ${envKey}: ${typeMap[valueType] || "any"}`;
interfaceItem.push(jsDocComment + keyValue);
}
if (!interfaceItem.length) return;
return `interface ImportMetaEnv {
// Auto generate by env-parse
${interfaceItem.join("\n ")}
}`;
}
// src/index.ts
function envParse(options = {}) {
const { parseJson = true, exclude = [], dtsPath = "env.d.ts", customParser, dev = true, build = false } = options;
let parsedEnv;
let isBuild = false;
let userConfig;
const importMetaEnvReg = /(?<![\'\"])import\.meta\.env\.([\w-]+)/gi;
const importObjReg = /(import\.meta\.env)(?:[^.])/gi;
return {
name: "vite-plugin-env-parse",
enforce: "pre",
transform(code, id) {
if (!isBuild || // exclude html, css and static assets for performance
isHTMLRequest(id) || isCSSRequest(id) || isNonJsRequest(id) || userConfig.assetsInclude(id)) {
return;
}
if (code.includes("import.meta.env")) {
code = code.replace(importMetaEnvReg, (matched, envKey) => {
let val = parsedEnv[envKey];
if (typeof val !== "undefined") {
return typeof val === "string" ? `'${val}'` : JSON.stringify(val);
}
return matched;
}).replace(importObjReg, (matched, envKey) => matched.replace(envKey, JSON.stringify(parsedEnv)));
return {
code,
map: null
};
}
},
configResolved(config) {
const { command } = config;
try {
isBuild = command === "build";
userConfig = config;
parsedEnv = parseEnv(config.env, { parseJson, customParser, exclude });
if (!isBuild && dev || isBuild && build) {
const { mode, envDir, root } = config;
const baseEnvFilePath = import_node_path.default.resolve(envDir || root, `.env`);
const modeFilePath = import_node_path.default.resolve(envDir || root, `.env.${mode}`);
const localEnvFilePath = import_node_path.default.resolve(envDir || root, `.env.local`);
const modeLocalFilePath = import_node_path.default.resolve(envDir || root, `.env.${mode}.local`);
const modeEnvFileContent = import_node_fs2.default.existsSync(modeFilePath) && import_node_fs2.default.readFileSync(modeFilePath, "utf-8");
const modeLocalEnvFileContent = import_node_fs2.default.existsSync(modeLocalFilePath) && import_node_fs2.default.readFileSync(modeLocalFilePath, "utf-8");
const localEnvFileContent = import_node_fs2.default.existsSync(localEnvFilePath) && import_node_fs2.default.readFileSync(localEnvFilePath, "utf-8");
const baseEnvFileContent = import_node_fs2.default.existsSync(baseEnvFilePath) && import_node_fs2.default.readFileSync(baseEnvFilePath, "utf-8");
const modeEnvCommentRecord = modeEnvFileContent ? parseEnvComment(modeEnvFileContent) : {};
const modeLocalCommentRecord = modeLocalEnvFileContent ? parseEnvComment(modeLocalEnvFileContent) : {};
const localEnvCommentRecord = localEnvFileContent ? parseEnvComment(localEnvFileContent) : {};
const baseEnvCommentRecord = baseEnvFileContent ? parseEnvComment(baseEnvFileContent) : {};
const envCommentRecord = {
...baseEnvCommentRecord,
...localEnvCommentRecord,
...modeEnvCommentRecord,
...modeLocalCommentRecord
};
const envInterface = generateEnvInterface(parsedEnv, envCommentRecord);
envInterface && writeEnvInterface(import_node_path.default.resolve(root, dtsPath), envInterface);
}
if (!isBuild && dev) {
Object.defineProperty(config, "env", {
get() {
return parsedEnv;
}
});
}
} catch (error) {
errorLog(error.message);
}
}
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
envParse,
parseLoadedEnv
});