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",
|
||||
"thelarkinn",
|
||||
"behaviour",
|
||||
"WHATWG"
|
||||
"WHATWG",
|
||||
"systemvars"
|
||||
],
|
||||
"ignoreRegExpList": [
|
||||
"/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() {
|
||||
return require("./DynamicEntryPlugin");
|
||||
},
|
||||
get DotenvPlugin() {
|
||||
return require("./DotenvPlugin");
|
||||
},
|
||||
get 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"
|
||||
| "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 {
|
||||
constructor(context: string, entry: () => Promise<EntryStaticNormalized>);
|
||||
context: string;
|
||||
|
@ -19237,6 +19355,7 @@ declare namespace exports {
|
|||
DllPlugin,
|
||||
DllReferencePlugin,
|
||||
DynamicEntryPlugin,
|
||||
DotenvPlugin,
|
||||
EntryOptionPlugin,
|
||||
EntryPlugin,
|
||||
EnvironmentPlugin,
|
||||
|
|
Loading…
Reference in New Issue