mirror of https://github.com/webpack/webpack.git
feat: add DotenvPlugin
This commit is contained in:
parent
e1afcd4cc2
commit
29feac18a4
|
@ -315,7 +315,8 @@
|
||||||
"spacek",
|
"spacek",
|
||||||
"thelarkinn",
|
"thelarkinn",
|
||||||
"behaviour",
|
"behaviour",
|
||||||
"WHATWG"
|
"WHATWG",
|
||||||
|
"systemvars"
|
||||||
],
|
],
|
||||||
"ignoreRegExpList": [
|
"ignoreRegExpList": [
|
||||||
"/Author.+/",
|
"/Author.+/",
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* This file was automatically generated.
|
||||||
|
* DO NOT MODIFY BY HAND.
|
||||||
|
* Run `yarn fix:special` to update
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface DotenvPluginOptions {
|
||||||
|
/**
|
||||||
|
* Whether to allow empty strings in safe mode. If false, will throw an error if any env variables are empty (but only if safe mode is enabled).
|
||||||
|
*/
|
||||||
|
allowEmptyValues?: boolean;
|
||||||
|
/**
|
||||||
|
* Adds support for dotenv-defaults. If set to true, uses ./.env.defaults. If a string, uses that location for a defaults file.
|
||||||
|
*/
|
||||||
|
defaults?: boolean | string;
|
||||||
|
/**
|
||||||
|
* Allows your variables to be "expanded" for reusability within your .env file.
|
||||||
|
*/
|
||||||
|
expand?: boolean;
|
||||||
|
/**
|
||||||
|
* The path to your environment variables. This same path applies to the .env.example and .env.defaults files.
|
||||||
|
*/
|
||||||
|
path?: string;
|
||||||
|
/**
|
||||||
|
* The prefix to use before the name of your env variables.
|
||||||
|
*/
|
||||||
|
prefix?: string;
|
||||||
|
/**
|
||||||
|
* If true, load '.env.example' to verify the '.env' variables are all set. Can also be a string to a different file.
|
||||||
|
*/
|
||||||
|
safe?: boolean | string;
|
||||||
|
/**
|
||||||
|
* Set to true if you would rather load all system variables as well (useful for CI purposes).
|
||||||
|
*/
|
||||||
|
systemvars?: boolean;
|
||||||
|
}
|
|
@ -0,0 +1,345 @@
|
||||||
|
/*
|
||||||
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||||
|
Author Natsu @xiaoxiaojx
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const createSchemaValidation = require("./util/create-schema-validation");
|
||||||
|
const { isAbsolute, join } = require("./util/fs");
|
||||||
|
|
||||||
|
/** @typedef {import("./Compiler")} Compiler */
|
||||||
|
/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
|
||||||
|
/** @typedef {import("../declarations/plugins/DotenvPlugin").DotenvPluginOptions} DotenvPluginOptions */
|
||||||
|
|
||||||
|
const DEFAULT_PATH = "./.env";
|
||||||
|
|
||||||
|
const DEFAULT_OPTIONS = {
|
||||||
|
path: DEFAULT_PATH,
|
||||||
|
prefix: "process.env."
|
||||||
|
};
|
||||||
|
|
||||||
|
const LINE =
|
||||||
|
/(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
|
||||||
|
|
||||||
|
const PLUGIN_NAME = "DotenvPlugin";
|
||||||
|
|
||||||
|
const validate = createSchemaValidation(
|
||||||
|
require("../schemas/plugins/DotenvPlugin.check"),
|
||||||
|
() => require("../schemas/plugins/DotenvPlugin.json"),
|
||||||
|
{
|
||||||
|
name: "Dotenv Plugin",
|
||||||
|
baseDataPath: "options"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} env environment variable value
|
||||||
|
* @param {Record<string, string>} vars variables object
|
||||||
|
* @returns {string} interpolated value
|
||||||
|
*/
|
||||||
|
const interpolate = (env, vars) => {
|
||||||
|
const matches = env.match(/\$([a-zA-Z0-9_]+)|\${([a-zA-Z0-9_]+)}/g) || [];
|
||||||
|
|
||||||
|
for (const match of matches) {
|
||||||
|
const key = match.replace(/\$|{|}/g, "");
|
||||||
|
let variable = vars[key] || "";
|
||||||
|
variable = interpolate(variable, vars);
|
||||||
|
env = env.replace(match, variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ported from https://github.com/motdotla/dotenv/blob/master/lib/main.js#L49
|
||||||
|
* @param {string|Buffer} src the source content to parse
|
||||||
|
* @returns {Record<string, string>} parsed environment variables object
|
||||||
|
*/
|
||||||
|
function parse(src) {
|
||||||
|
const obj = /** @type {Record<string, string>} */ ({});
|
||||||
|
|
||||||
|
// Convert buffer to string
|
||||||
|
let lines = src.toString();
|
||||||
|
|
||||||
|
// Convert line breaks to same format
|
||||||
|
lines = lines.replace(/\r\n?/gm, "\n");
|
||||||
|
|
||||||
|
let match;
|
||||||
|
while ((match = LINE.exec(lines)) !== null) {
|
||||||
|
const key = match[1];
|
||||||
|
|
||||||
|
// Default undefined or null to empty string
|
||||||
|
let value = match[2] || "";
|
||||||
|
|
||||||
|
// Remove whitespace
|
||||||
|
value = value.trim();
|
||||||
|
|
||||||
|
// Check if double quoted
|
||||||
|
const maybeQuote = value[0];
|
||||||
|
|
||||||
|
// Remove surrounding quotes
|
||||||
|
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
|
||||||
|
|
||||||
|
// Expand newlines if double quoted
|
||||||
|
if (maybeQuote === '"') {
|
||||||
|
value = value.replace(/\\n/g, "\n");
|
||||||
|
value = value.replace(/\\r/g, "\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to object
|
||||||
|
obj[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses objects like before, but with defaults!
|
||||||
|
* @param {string} src the original src
|
||||||
|
* @param {string=} defaultSrc the new-and-improved default source
|
||||||
|
* @returns {Record<string, string>} the parsed results
|
||||||
|
*/
|
||||||
|
const mergeParse = (src, defaultSrc = "") => {
|
||||||
|
const parsedSrc = parse(src);
|
||||||
|
const parsedDefault = parse(defaultSrc);
|
||||||
|
|
||||||
|
return { ...parsedDefault, ...parsedSrc };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ported from https://github.com/mrsteele/dotenv-webpack
|
||||||
|
class DotenvPlugin {
|
||||||
|
/**
|
||||||
|
* @param {DotenvPluginOptions=} options options object
|
||||||
|
*/
|
||||||
|
constructor(options = {}) {
|
||||||
|
validate(options);
|
||||||
|
this.config = { ...DEFAULT_OPTIONS, ...options };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the plugin
|
||||||
|
* @param {Compiler} compiler the compiler instance
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
apply(compiler) {
|
||||||
|
compiler.hooks.beforeCompile.tapAsync(PLUGIN_NAME, (_params, callback) => {
|
||||||
|
const inputFileSystem = /** @type {InputFileSystem} */ (
|
||||||
|
compiler.inputFileSystem
|
||||||
|
);
|
||||||
|
const context = compiler.context;
|
||||||
|
|
||||||
|
this.gatherVariables(inputFileSystem, context, (err, variables) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
|
||||||
|
const definitions = this.formatDefinitions(variables || {});
|
||||||
|
const DefinePlugin = compiler.webpack.DefinePlugin;
|
||||||
|
|
||||||
|
new DefinePlugin(definitions).apply(compiler);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {InputFileSystem} inputFileSystem the input file system
|
||||||
|
* @param {string} context the compiler context
|
||||||
|
* @param {(err: Error | null, variables?: Record<string, string>) => void} callback callback function
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
gatherVariables(inputFileSystem, context, callback) {
|
||||||
|
const { safe, allowEmptyValues } = /** @type {DotenvPluginOptions} */ (
|
||||||
|
this.config
|
||||||
|
);
|
||||||
|
const vars = /** @type {Record<string, string>} */ (this.initializeVars());
|
||||||
|
|
||||||
|
this.getEnvs(inputFileSystem, context, (err, result) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
if (!result) {
|
||||||
|
return callback(new Error("Failed to get environment variables"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const { env, blueprint } = result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const key of Object.keys(blueprint)) {
|
||||||
|
const value = Object.prototype.hasOwnProperty.call(vars, key)
|
||||||
|
? vars[key]
|
||||||
|
: env[key];
|
||||||
|
|
||||||
|
const isMissing =
|
||||||
|
typeof value === "undefined" ||
|
||||||
|
value === null ||
|
||||||
|
(!allowEmptyValues && value === "");
|
||||||
|
|
||||||
|
if (safe && isMissing) {
|
||||||
|
throw new Error(`Missing environment variable: ${key}`);
|
||||||
|
} else {
|
||||||
|
vars[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the leftovers
|
||||||
|
if (safe) {
|
||||||
|
for (const key of Object.keys(env)) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(vars, key)) {
|
||||||
|
vars[key] = env[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, vars);
|
||||||
|
} catch (error) {
|
||||||
|
callback(error instanceof Error ? error : new Error(String(error)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeVars() {
|
||||||
|
const config = /** @type {DotenvPluginOptions} */ (this.config);
|
||||||
|
if (config.systemvars) {
|
||||||
|
const vars = /** @type {Record<string, string>} */ ({});
|
||||||
|
for (const key in process.env) {
|
||||||
|
if (process.env[key] !== undefined) {
|
||||||
|
vars[key] = /** @type {string} */ (process.env[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vars;
|
||||||
|
}
|
||||||
|
return /** @type {Record<string, string>} */ ({});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {InputFileSystem} inputFileSystem the input file system
|
||||||
|
* @param {string} context the compiler context
|
||||||
|
* @param {(err: Error | null, result?: {env: Record<string, string>, blueprint: Record<string, string>}) => void} callback callback function
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
getEnvs(inputFileSystem, context, callback) {
|
||||||
|
const { path, safe } = /** @type {DotenvPluginOptions} */ (this.config);
|
||||||
|
|
||||||
|
// First load the main env file and defaults
|
||||||
|
this.loadFile(
|
||||||
|
{
|
||||||
|
file: path || DEFAULT_PATH,
|
||||||
|
inputFileSystem,
|
||||||
|
context
|
||||||
|
},
|
||||||
|
(err, envContent) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
|
||||||
|
this.getDefaults(inputFileSystem, context, (err, defaultsContent) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
|
||||||
|
const env = mergeParse(envContent || "", defaultsContent || "");
|
||||||
|
let blueprint = env;
|
||||||
|
|
||||||
|
if (safe) {
|
||||||
|
let file = `${path || DEFAULT_PATH}.example`;
|
||||||
|
if (safe !== true) {
|
||||||
|
file = safe;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadFile(
|
||||||
|
{
|
||||||
|
file,
|
||||||
|
inputFileSystem,
|
||||||
|
context
|
||||||
|
},
|
||||||
|
(err, blueprintContent) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
|
||||||
|
blueprint = mergeParse(blueprintContent || "");
|
||||||
|
callback(null, { env, blueprint });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
callback(null, { env, blueprint });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {InputFileSystem} inputFileSystem the input file system
|
||||||
|
* @param {string} context the compiler context
|
||||||
|
* @param {(err: Error | null, content?: string) => void} callback callback function
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
getDefaults(inputFileSystem, context, callback) {
|
||||||
|
const { path, defaults } = /** @type {DotenvPluginOptions} */ (this.config);
|
||||||
|
|
||||||
|
if (defaults) {
|
||||||
|
const defaultsFile =
|
||||||
|
defaults === true ? `${path || DEFAULT_PATH}.defaults` : defaults;
|
||||||
|
this.loadFile(
|
||||||
|
{
|
||||||
|
file: defaultsFile,
|
||||||
|
inputFileSystem,
|
||||||
|
context
|
||||||
|
},
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
callback(null, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a file with proper path resolution
|
||||||
|
* @param {object} options options object
|
||||||
|
* @param {string} options.file the file to load
|
||||||
|
* @param {InputFileSystem} options.inputFileSystem the input file system
|
||||||
|
* @param {string} options.context the compiler context for resolving relative paths
|
||||||
|
* @param {(err: Error | null, content?: string) => void} callback callback function
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
loadFile({ file, inputFileSystem, context }, callback) {
|
||||||
|
// Resolve relative paths based on compiler context
|
||||||
|
const resolvedPath = isAbsolute(file)
|
||||||
|
? file
|
||||||
|
: join(inputFileSystem, context, file);
|
||||||
|
|
||||||
|
inputFileSystem.readFile(resolvedPath, "utf8", (err, content) => {
|
||||||
|
if (err) {
|
||||||
|
// File doesn't exist, return empty string
|
||||||
|
callback(null, "");
|
||||||
|
} else {
|
||||||
|
callback(null, /** @type {string} */ (content));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, string>} variables variables object
|
||||||
|
* @returns {Record<string, string>} formatted data
|
||||||
|
*/
|
||||||
|
formatDefinitions(variables) {
|
||||||
|
const { expand, prefix } = /** @type {DotenvPluginOptions} */ (this.config);
|
||||||
|
const formatted = Object.keys(variables).reduce((obj, key) => {
|
||||||
|
const v = variables[key];
|
||||||
|
const vKey = `${prefix}${key}`;
|
||||||
|
let vValue;
|
||||||
|
if (expand) {
|
||||||
|
if (v.slice(0, 2) === "\\$") {
|
||||||
|
vValue = v.slice(1);
|
||||||
|
} else if (v.indexOf("\\$") > 0) {
|
||||||
|
vValue = v.replace(/\\\$/g, "$");
|
||||||
|
} else {
|
||||||
|
vValue = interpolate(v, variables);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vValue = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[vKey] = JSON.stringify(vValue);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}, /** @type {Record<string, string>} */ ({}));
|
||||||
|
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DotenvPlugin;
|
|
@ -232,6 +232,9 @@ module.exports = mergeExports(fn, {
|
||||||
get DynamicEntryPlugin() {
|
get DynamicEntryPlugin() {
|
||||||
return require("./DynamicEntryPlugin");
|
return require("./DynamicEntryPlugin");
|
||||||
},
|
},
|
||||||
|
get DotenvPlugin() {
|
||||||
|
return require("./DotenvPlugin");
|
||||||
|
},
|
||||||
get EntryOptionPlugin() {
|
get EntryOptionPlugin() {
|
||||||
return require("./EntryOptionPlugin");
|
return require("./EntryOptionPlugin");
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
* This file was automatically generated.
|
||||||
|
* DO NOT MODIFY BY HAND.
|
||||||
|
* Run `yarn fix:special` to update
|
||||||
|
*/
|
||||||
|
declare const check: (options: import("../../declarations/plugins/DotenvPlugin").DotenvPluginOptions) => boolean;
|
||||||
|
export = check;
|
|
@ -0,0 +1,6 @@
|
||||||
|
/*
|
||||||
|
* This file was automatically generated.
|
||||||
|
* DO NOT MODIFY BY HAND.
|
||||||
|
* Run `yarn fix:special` to update
|
||||||
|
*/
|
||||||
|
"use strict";function e(r,{instancePath:t="",parentData:s,parentDataProperty:o,rootData:a=r}={}){let n=null,l=0;if(0===l){if(!r||"object"!=typeof r||Array.isArray(r))return e.errors=[{params:{type:"object"}}],!1;{const t=l;for(const t in r)if("allowEmptyValues"!==t&&"defaults"!==t&&"expand"!==t&&"path"!==t&&"prefix"!==t&&"safe"!==t&&"systemvars"!==t)return e.errors=[{params:{additionalProperty:t}}],!1;if(t===l){if(void 0!==r.allowEmptyValues){const t=l;if("boolean"!=typeof r.allowEmptyValues)return e.errors=[{params:{type:"boolean"}}],!1;var i=t===l}else i=!0;if(i){if(void 0!==r.defaults){let t=r.defaults;const s=l,o=l;let a=!1;const f=l;if("boolean"!=typeof t){const e={params:{type:"boolean"}};null===n?n=[e]:n.push(e),l++}var p=f===l;if(a=a||p,!a){const e=l;if(l===e)if("string"==typeof t){if(t.length<1){const e={params:{}};null===n?n=[e]:n.push(e),l++}}else{const e={params:{type:"string"}};null===n?n=[e]:n.push(e),l++}p=e===l,a=a||p}if(!a){const r={params:{}};return null===n?n=[r]:n.push(r),l++,e.errors=n,!1}l=o,null!==n&&(o?n.length=o:n=null),i=s===l}else i=!0;if(i){if(void 0!==r.expand){const t=l;if("boolean"!=typeof r.expand)return e.errors=[{params:{type:"boolean"}}],!1;i=t===l}else i=!0;if(i){if(void 0!==r.path){let t=r.path;const s=l;if(l===s){if("string"!=typeof t)return e.errors=[{params:{type:"string"}}],!1;if(t.length<1)return e.errors=[{params:{}}],!1}i=s===l}else i=!0;if(i){if(void 0!==r.prefix){let t=r.prefix;const s=l;if(l===s){if("string"!=typeof t)return e.errors=[{params:{type:"string"}}],!1;if(t.length<1)return e.errors=[{params:{}}],!1}i=s===l}else i=!0;if(i){if(void 0!==r.safe){let t=r.safe;const s=l,o=l;let a=!1;const p=l;if("boolean"!=typeof t){const e={params:{type:"boolean"}};null===n?n=[e]:n.push(e),l++}var f=p===l;if(a=a||f,!a){const e=l;if(l===e)if("string"==typeof t){if(t.length<1){const e={params:{}};null===n?n=[e]:n.push(e),l++}}else{const e={params:{type:"string"}};null===n?n=[e]:n.push(e),l++}f=e===l,a=a||f}if(!a){const r={params:{}};return null===n?n=[r]:n.push(r),l++,e.errors=n,!1}l=o,null!==n&&(o?n.length=o:n=null),i=s===l}else i=!0;if(i)if(void 0!==r.systemvars){const t=l;if("boolean"!=typeof r.systemvars)return e.errors=[{params:{type:"boolean"}}],!1;i=t===l}else i=!0}}}}}}}}return e.errors=n,0===l}module.exports=e,module.exports.default=e;
|
|
@ -0,0 +1,53 @@
|
||||||
|
{
|
||||||
|
"title": "DotenvPluginOptions",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"allowEmptyValues": {
|
||||||
|
"description": "Whether to allow empty strings in safe mode. If false, will throw an error if any env variables are empty (but only if safe mode is enabled).",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"defaults": {
|
||||||
|
"description": "Adds support for dotenv-defaults. If set to true, uses ./.env.defaults. If a string, uses that location for a defaults file.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"expand": {
|
||||||
|
"description": "Allows your variables to be \"expanded\" for reusability within your .env file.",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"description": "The path to your environment variables. This same path applies to the .env.example and .env.defaults files.",
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"prefix": {
|
||||||
|
"description": "The prefix to use before the name of your env variables.",
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"safe": {
|
||||||
|
"description": "If true, load '.env.example' to verify the '.env' variables are all set. Can also be a string to a different file.",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"systemvars": {
|
||||||
|
"description": "Set to true if you would rather load all system variables as well (useful for CI purposes).",
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
MY_NODE_ENV=test
|
||||||
|
API_URL=https://api.example.com
|
||||||
|
DEBUG=true
|
||||||
|
PORT=3000
|
||||||
|
SECRET_KEY=my-secret-key
|
||||||
|
EMPTY_VALUE=
|
||||||
|
INTERPOLATED_VAR=$MY_NODE_ENV-mode
|
|
@ -0,0 +1,3 @@
|
||||||
|
MY_NODE_ENV=development
|
||||||
|
PORT=8080
|
||||||
|
DEFAULT_VALUE=default-from-defaults
|
|
@ -0,0 +1,6 @@
|
||||||
|
MY_NODE_ENV=
|
||||||
|
API_URL=
|
||||||
|
DEBUG=
|
||||||
|
PORT=
|
||||||
|
SECRET_KEY=
|
||||||
|
REQUIRED_VAR=
|
|
@ -0,0 +1,10 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
it("should load basic .env variables", () => {
|
||||||
|
expect(process.env.MY_NODE_ENV).toBe("test");
|
||||||
|
expect(process.env.API_URL).toBe("https://api.example.com");
|
||||||
|
expect(process.env.DEBUG).toBe("true");
|
||||||
|
expect(process.env.PORT).toBe("3000");
|
||||||
|
expect(process.env.SECRET_KEY).toBe("my-secret-key");
|
||||||
|
expect(process.env.EMPTY_VALUE).toBe("");
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
it("should load from custom path", () => {
|
||||||
|
// When using .env.example as path, values should be empty (as defined in .env.example)
|
||||||
|
expect(process.env.MY_NODE_ENV).toBe("");
|
||||||
|
expect(process.env.API_URL).toBe("");
|
||||||
|
expect(process.env.DEBUG).toBe("");
|
||||||
|
expect(process.env.PORT).toBe("");
|
||||||
|
expect(process.env.SECRET_KEY).toBe("");
|
||||||
|
expect(process.env.REQUIRED_VAR).toBe("");
|
||||||
|
});
|
|
@ -0,0 +1,12 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
it("should use custom prefix", () => {
|
||||||
|
expect(MY_ENV.MY_NODE_ENV).toBe("test");
|
||||||
|
expect(MY_ENV.API_URL).toBe("https://api.example.com");
|
||||||
|
expect(MY_ENV.DEBUG).toBe("true");
|
||||||
|
expect(MY_ENV.PORT).toBe("3000");
|
||||||
|
expect(MY_ENV.SECRET_KEY).toBe("my-secret-key");
|
||||||
|
|
||||||
|
// process.env should not be defined with custom prefix
|
||||||
|
expect(typeof process.env.MY_NODE_ENV).toBe("undefined");
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
it("should load variables with defaults", () => {
|
||||||
|
// Main .env values should override defaults
|
||||||
|
expect(process.env.MY_NODE_ENV).toBe("test");
|
||||||
|
expect(process.env.PORT).toBe("3000");
|
||||||
|
|
||||||
|
// Default values should be used when not in main .env
|
||||||
|
expect(process.env.DEFAULT_VALUE).toBe("default-from-defaults");
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
module.exports = [/Missing environment variable: /];
|
|
@ -0,0 +1,6 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
it("should expand variables when expand is true", () => {
|
||||||
|
expect(process.env.INTERPOLATED_VAR).toBe("test-mode");
|
||||||
|
expect(process.env.MY_NODE_ENV).toBe("test");
|
||||||
|
});
|
|
@ -0,0 +1,4 @@
|
||||||
|
MY_NODE_ENV=test
|
||||||
|
API_URL=https://api.example.com
|
||||||
|
DEBUG=true
|
||||||
|
PORT=3000
|
|
@ -0,0 +1,15 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
it("should include system variables when systemvars is true", () => {
|
||||||
|
// System variables should be available (we can't predict exact values, but PATH should exist)
|
||||||
|
expect(typeof process.env.PATH).toBe("string");
|
||||||
|
expect(process.env.PATH.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
// .env variables should also be loaded
|
||||||
|
expect(process.env.MY_NODE_ENV).toBe("test");
|
||||||
|
expect(process.env.API_URL).toBe("https://api.example.com");
|
||||||
|
|
||||||
|
// NODE_ENV might be set by the system/test environment
|
||||||
|
// We just check that it exists as a system variable
|
||||||
|
expect(typeof process.env.NODE_ENV).toBe("string");
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
module.exports = [[/Conflicting values for 'process.env.NODE_ENV'/]];
|
|
@ -0,0 +1,67 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const DotenvPlugin = require("../../../../lib/DotenvPlugin");
|
||||||
|
|
||||||
|
/** @type {import("../../../../").Configuration[]} */
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
name: "basic",
|
||||||
|
entry: "./basic",
|
||||||
|
plugins: [new DotenvPlugin()]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with-defaults",
|
||||||
|
entry: "./defaults",
|
||||||
|
plugins: [
|
||||||
|
new DotenvPlugin({
|
||||||
|
defaults: true
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with-expand",
|
||||||
|
entry: "./expand",
|
||||||
|
plugins: [
|
||||||
|
new DotenvPlugin({
|
||||||
|
expand: true
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with-systemvars",
|
||||||
|
entry: "./systemvars",
|
||||||
|
plugins: [
|
||||||
|
new DotenvPlugin({
|
||||||
|
systemvars: true
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-path",
|
||||||
|
entry: "./custom-path",
|
||||||
|
plugins: [
|
||||||
|
new DotenvPlugin({
|
||||||
|
path: "./.env.example"
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-prefix",
|
||||||
|
entry: "./custom-prefix",
|
||||||
|
plugins: [
|
||||||
|
new DotenvPlugin({
|
||||||
|
prefix: "MY_ENV."
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "safe-mode-error",
|
||||||
|
entry: "./basic", // Use existing entry file
|
||||||
|
plugins: [
|
||||||
|
new DotenvPlugin({
|
||||||
|
path: "./incomplete.env",
|
||||||
|
safe: "./.env.example"
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
|
@ -4419,6 +4419,124 @@ declare interface DllReferencePluginOptionsManifest {
|
||||||
| "jsonp"
|
| "jsonp"
|
||||||
| "system";
|
| "system";
|
||||||
}
|
}
|
||||||
|
declare class DotenvPlugin {
|
||||||
|
constructor(options?: DotenvPluginOptions);
|
||||||
|
config: {
|
||||||
|
/**
|
||||||
|
* Whether to allow empty strings in safe mode. If false, will throw an error if any env variables are empty (but only if safe mode is enabled).
|
||||||
|
*/
|
||||||
|
allowEmptyValues?: boolean;
|
||||||
|
/**
|
||||||
|
* Adds support for dotenv-defaults. If set to true, uses ./.env.defaults. If a string, uses that location for a defaults file.
|
||||||
|
*/
|
||||||
|
defaults?: string | boolean;
|
||||||
|
/**
|
||||||
|
* Allows your variables to be "expanded" for reusability within your .env file.
|
||||||
|
*/
|
||||||
|
expand?: boolean;
|
||||||
|
/**
|
||||||
|
* The path to your environment variables. This same path applies to the .env.example and .env.defaults files.
|
||||||
|
*/
|
||||||
|
path: string;
|
||||||
|
/**
|
||||||
|
* The prefix to use before the name of your env variables.
|
||||||
|
*/
|
||||||
|
prefix: string;
|
||||||
|
/**
|
||||||
|
* If true, load '.env.example' to verify the '.env' variables are all set. Can also be a string to a different file.
|
||||||
|
*/
|
||||||
|
safe?: string | boolean;
|
||||||
|
/**
|
||||||
|
* Set to true if you would rather load all system variables as well (useful for CI purposes).
|
||||||
|
*/
|
||||||
|
systemvars?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the plugin
|
||||||
|
*/
|
||||||
|
apply(compiler: Compiler): void;
|
||||||
|
gatherVariables(
|
||||||
|
inputFileSystem: InputFileSystem,
|
||||||
|
context: string,
|
||||||
|
callback: (err: null | Error, variables?: Record<string, string>) => void
|
||||||
|
): void;
|
||||||
|
initializeVars(): Record<string, string>;
|
||||||
|
getEnvs(
|
||||||
|
inputFileSystem: InputFileSystem,
|
||||||
|
context: string,
|
||||||
|
callback: (
|
||||||
|
err: null | Error,
|
||||||
|
result?: {
|
||||||
|
env: Record<string, string>;
|
||||||
|
blueprint: Record<string, string>;
|
||||||
|
}
|
||||||
|
) => void
|
||||||
|
): void;
|
||||||
|
getDefaults(
|
||||||
|
inputFileSystem: InputFileSystem,
|
||||||
|
context: string,
|
||||||
|
callback: (err: null | Error, content?: string) => void
|
||||||
|
): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a file with proper path resolution
|
||||||
|
*/
|
||||||
|
loadFile(
|
||||||
|
__0: {
|
||||||
|
/**
|
||||||
|
* the file to load
|
||||||
|
*/
|
||||||
|
file: string;
|
||||||
|
/**
|
||||||
|
* the input file system
|
||||||
|
*/
|
||||||
|
inputFileSystem: InputFileSystem;
|
||||||
|
/**
|
||||||
|
* the compiler context for resolving relative paths
|
||||||
|
*/
|
||||||
|
context: string;
|
||||||
|
},
|
||||||
|
callback: (err: null | Error, content?: string) => void
|
||||||
|
): void;
|
||||||
|
formatDefinitions(variables: Record<string, string>): Record<string, string>;
|
||||||
|
}
|
||||||
|
declare interface DotenvPluginOptions {
|
||||||
|
/**
|
||||||
|
* Whether to allow empty strings in safe mode. If false, will throw an error if any env variables are empty (but only if safe mode is enabled).
|
||||||
|
*/
|
||||||
|
allowEmptyValues?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds support for dotenv-defaults. If set to true, uses ./.env.defaults. If a string, uses that location for a defaults file.
|
||||||
|
*/
|
||||||
|
defaults?: string | boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows your variables to be "expanded" for reusability within your .env file.
|
||||||
|
*/
|
||||||
|
expand?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path to your environment variables. This same path applies to the .env.example and .env.defaults files.
|
||||||
|
*/
|
||||||
|
path?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prefix to use before the name of your env variables.
|
||||||
|
*/
|
||||||
|
prefix?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, load '.env.example' to verify the '.env' variables are all set. Can also be a string to a different file.
|
||||||
|
*/
|
||||||
|
safe?: string | boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true if you would rather load all system variables as well (useful for CI purposes).
|
||||||
|
*/
|
||||||
|
systemvars?: boolean;
|
||||||
|
}
|
||||||
declare class DynamicEntryPlugin {
|
declare class DynamicEntryPlugin {
|
||||||
constructor(context: string, entry: () => Promise<EntryStaticNormalized>);
|
constructor(context: string, entry: () => Promise<EntryStaticNormalized>);
|
||||||
context: string;
|
context: string;
|
||||||
|
@ -19237,6 +19355,7 @@ declare namespace exports {
|
||||||
DllPlugin,
|
DllPlugin,
|
||||||
DllReferencePlugin,
|
DllReferencePlugin,
|
||||||
DynamicEntryPlugin,
|
DynamicEntryPlugin,
|
||||||
|
DotenvPlugin,
|
||||||
EntryOptionPlugin,
|
EntryOptionPlugin,
|
||||||
EntryPlugin,
|
EntryPlugin,
|
||||||
EnvironmentPlugin,
|
EnvironmentPlugin,
|
||||||
|
|
Loading…
Reference in New Issue