diff --git a/.gitignore b/.gitignore index d1c154e5dd0..66ad094ccf6 100644 --- a/.gitignore +++ b/.gitignore @@ -64,5 +64,7 @@ test/**/**/**/bin/ test/**/**/binary/** test/**/dist test/**/**/dist +test/**/**/dist1 +test/**/**/dist2 test/**/**/**/dist test/**/stats.json diff --git a/OPTIONS.md b/OPTIONS.md index a638429def9..d179f550f10 100644 --- a/OPTIONS.md +++ b/OPTIONS.md @@ -706,6 +706,7 @@ Options: --performance-max-entrypoint-size Total size of an entry point (in bytes). --profile Capture timing information for each module. --no-profile Negative 'profile' option. + --read-dot-env Read environment variables from dotenv files --records-input-path Store compiler state to a json file. --no-records-input-path Negative 'records-input-path' option. --records-output-path Load compiler state from a json file. diff --git a/packages/README.md b/packages/README.md index 185874c38f4..a3ac51f4406 100644 --- a/packages/README.md +++ b/packages/README.md @@ -18,6 +18,7 @@ This folder is the collection of those packages. 3. [info](https://github.com/webpack/webpack-cli/tree/master/packages/info) 4. [serve](https://github.com/webpack/webpack-cli/tree/master/packages/serve) 5. [webpack-cli](https://github.com/webpack/webpack-cli/tree/master/packages/webpack-cli) +6. [dotenv-webpack-plugin](https://github.com/webpack/webpack-cli/tree/master/packages/dotenv-webpack-plugin) ## Generic Installation diff --git a/packages/dotenv-webpack-plugin/README.md b/packages/dotenv-webpack-plugin/README.md new file mode 100644 index 00000000000..34c201d2754 --- /dev/null +++ b/packages/dotenv-webpack-plugin/README.md @@ -0,0 +1,91 @@ +# Dotenv Webpack Plugin + +`dotenv-webpack-plugin` is a webpack plugin that enables consumers to load environment variables from dotenv files. This plugin simplifies the process of managing environment-specific configurations in your webpack projects. + +## Features + +- Loads environment variables from dotenv files +- Provides a convenient way to manage environment-specific configurations +- Fully configurable via an options API + +## Installation + +Install `dotenv-webpack-plugin` using npm: + +```bash +npm install dotenv-webpack-plugin --save-dev +``` + +or using yarn: + +```bash +yarn add dotenv-webpack-plugin --dev +``` + +or using pnpm: + +```bash +pnpm add dotenv-webpack-plugin --save-dev +``` + +## Usage + +To use `dotenv-webpack-plugin`, follow these steps: + +1. Create a `.env` file in the root directory of your project. Add each environment variable on a new lines in the form of `PUBLIC_NAME=VALUE`. By default only variables that are prefixed with `PUBLIC_` will be exposed to webpack. The prefix can be changed by passing the `envVarPrefix` option to the plugin. + +1. Import `dotenv-webpack-plugin` in your webpack configuration file: + + ```javascript + const DotenvWebpackPlugin = require("dotenv-webpack-plugin"); + ``` + +1. Add an instance of DotenvWebpackPlugin to your webpack plugins: + + ```javascript + module.exports = { + // Your webpack configuration options... + plugins: [new DotenvWebpackPlugin()], + }; + ``` + +## Configuration Options + +DotenvWebpackPlugin accepts the following configuration options: + +1. `envFiles`: An array of dotenv files to load. By default, DotenvWebpackPlugin will look for the following files in the root directory of your project: + + - `.env.[mode].local` + - `.env.local` + - `.env.[mode]` + - `.env` + - `.env.example` + + The `[mode]` placeholder will be replaced with the current webpack mode. For example, if the current webpack mode is `development`, DotenvWebpackPlugin will look for the following files: + + - `.env.development.local` + - `.env.local` + - `.env.development` + - `.env` + - `.env.example` + + If the same variable is defined in multiple files, the value from the file with the highest precedence will be used. The precedence order is same as the order of files listed above. + + While passing an array of dotenv files, the path towards the right of the array will have the highest precedence. For example, if you pass `["./.env", "./.env.local"]`, the value from `.env.local` will be used if the same variable is defined in both files. + +1. `envVarPrefix`: The prefix to use when loading environment variables. By default, DotenvWebpackPlugin will look for variables prefixed with `PUBLIC_`. + +1. `prefixes`: An array of prefixes to prepend to the names of environment variables. By default, DotenvWebpackPlugin will prepend `process.env.` and `import.meta.env.` to the names of environment variables. + +1. `allowEmptyValues`: A boolean value indicating whether to allow empty values. By default this value is set to `false`, DotenvWebpackPlugin will throw an error if an environment variable is defined without a value. + +You can pass these options when creating an instance of DotenvWebpackPlugin: + +```javascript +new DotenvWebpackPlugin({ + envFiles: ["./.env", "./.env.local"], + prefixes: ["process.env.", "import.meta.env."], + envVarPrefix: "PUBLIC_", + allowEmptyValues: false, +}); +``` diff --git a/packages/dotenv-webpack-plugin/package.json b/packages/dotenv-webpack-plugin/package.json new file mode 100644 index 00000000000..38223cd1973 --- /dev/null +++ b/packages/dotenv-webpack-plugin/package.json @@ -0,0 +1,21 @@ +{ + "name": "dotenv-webpack-plugin", + "version": "1.0.0", + "description": "A webpack plugin to support env files", + "main": "src/index.js", + "types": "src/types.d.ts", + "repository": "https://github.com/webpack/webpack-cli", + "license": "MIT", + "private": true, + "dependencies": { + "dotenv": "^16.0.3", + "dotenv-expand": "^10.0.0", + "schema-utils": "^4.0.1" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "devDependencies": { + "webpack": "^5.81.0" + } +} diff --git a/packages/dotenv-webpack-plugin/src/index.js b/packages/dotenv-webpack-plugin/src/index.js new file mode 100644 index 00000000000..88b4258ecee --- /dev/null +++ b/packages/dotenv-webpack-plugin/src/index.js @@ -0,0 +1,212 @@ +const dotenv = require("dotenv"); +const dotenvExpand = require("dotenv-expand"); +const { DefinePlugin, WebpackError } = require("webpack"); +const { validate } = require("schema-utils"); +const schema = require("./options.json"); + +/** @typedef {import("./types").Config} Config */ +/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */ +/** @typedef {import("webpack").Compiler} Compiler */ +/** @typedef {import("webpack").Compilation} Compilation */ + +class DotenvWebpackPlugin { + /** + * Dotenv Webpack Plugin + * @param {Config} options - Configuration options + */ + constructor(options = {}) { + validate(/** @type {Schema} */ (schema), options || {}, { + name: "DotenvWebpackPlugin", + baseDataPath: "options", + }); + + const currentDirectory = process.cwd(); + + this.defaultFileList = [ + `${currentDirectory}/.env.example`, // loaded in all cases + `${currentDirectory}/.env`, // loaded in all cases + `${currentDirectory}/.env.[mode]`, // only loaded in specified mode + `${currentDirectory}/.env.local`, // loaded in all cases, ignored by git + `${currentDirectory}/.env.[mode].local`, // only loaded in specified mode, ignored by git + ]; + + const { + // priority is in ascending order + // files at the end of the array have higher priority + envFiles = this.defaultFileList, + prefixes = ["process.env.", "import.meta.env."], + envVarPrefix = "PUBLIC_", + allowEmptyValues = false, + } = options; + + this.options = { + envFiles, + prefixes, + envVarPrefix, + allowEmptyValues, + }; + + this.errors = []; + } + + /** + * Default file list and the options file list are updated with the + * value of the mode if [mode] placeholder is used + * @param {String} mode - Webpack mode + */ + updateFileListWithMode(mode) { + this.options.envFiles = this.options.envFiles.map((environmentFile) => + environmentFile.replace(/\[mode\]/g, mode), + ); + this.defaultFileList = this.defaultFileList.map((environmentFile) => + environmentFile.replace(/\[mode\]/g, mode), + ); + } + + /** + * Read file from path and parse it + * @param {Compiler} compiler - Webpack compiler + * @param {string} environmentFile - Path to environment file + */ + readFile(compiler, environmentFile) { + return new Promise((resolve, reject) => { + const fs = compiler.inputFileSystem; + + fs.readFile(environmentFile, (err, environmentFileContents) => { + if (err) { + if (!this.defaultFileList.includes(environmentFile)) { + this.collectError(`Could not read ${environmentFile}`); + return reject(err); + } else { + return resolve(); + } + } + + const parsedEnvVariables = dotenv.parse(environmentFileContents); + + resolve(parsedEnvVariables); + }); + }); + } + + filterVariables(envVariables) { + const filteredEnvVariables = {}; + + for (const [key, value] of Object.entries(envVariables)) { + // only add variables starting with the provided prefix + if (key.startsWith(this.options.envVarPrefix)) { + filteredEnvVariables[key] = value; + } + } + + return filteredEnvVariables; + } + + assignPrefixes(envVariables) { + const prefixedEnvVariables = {}; + + for (const [key, value] of Object.entries(envVariables)) { + for (let index = 0; index < this.options.prefixes.length; index++) { + const prefix = this.options.prefixes[index]; + prefixedEnvVariables[`${prefix}${key}`] = value; + } + } + + return prefixedEnvVariables; + } + + /** + * Throw collected errors to fail the build + * @param {Compilation} compilation - Webpack compilation + */ + throwErrors(compilation) { + const errors = this.errors.map((error) => { + const webpackError = new WebpackError(error); + webpackError.name = "DotenvWebpackPluginError"; + return webpackError; + }); + compilation.errors.push(...errors); + } + + collectError(errorMessage) { + this.errors.push(errorMessage); + } + + /** + * Get list of empty values + * @param {Object} envVariables - Environment variables + * @returns {Array} - List of empty values + */ + getEmptyValues(envVariables) { + const emptyValues = []; + + for (const [key, value] of Object.entries(envVariables)) { + if (value === "") { + emptyValues.push(key); + } + } + + return emptyValues; + } + + /** + * Webpack apply hook + * @param {Compiler} compiler - Webpack compiler + * @returns {void} + */ + apply(compiler) { + const mode = compiler.options.mode || "production"; + this.updateFileListWithMode(mode); + + compiler.hooks.beforeRun.tapPromise("DotenvWebpackPlugin", (compiler) => { + compiler.hooks.compilation.tap("DotenvWebpackPlugin", (compilation) => { + compilation.buildDependencies.addAll(this.options.envFiles); + if (this.errors.length > 0) { + this.throwErrors(compilation); + } + }); + + return Promise.all( + this.options.envFiles.map((environmentFile) => this.readFile(compiler, environmentFile)), + ) + .then((valuesList) => { + const envVariables = {}; + + valuesList.forEach((values) => { + if (values) { + Object.entries(values).forEach(([key, value]) => { + envVariables[key] = value; + }); + } + }); + + const filteredEnvVariables = this.filterVariables(envVariables); + + const emptyValues = this.getEmptyValues(filteredEnvVariables); + + if (!this.options.allowEmptyValues && emptyValues.length > 0) { + this.collectError( + `Environment variables cannot have an empty value. The following variables are empty: ${emptyValues.join( + ", ", + )}`, + ); + return; + } + + const prefixedEnvVariables = this.assignPrefixes(filteredEnvVariables); + + // expand environment vars + const expandedEnvVariables = dotenvExpand.expand({ + parsed: prefixedEnvVariables, + // don't write to process.env + ignoreProcessEnv: true, + }).parsed; + + new DefinePlugin(expandedEnvVariables).apply(compiler); + }) + .catch(() => {}); + }); + } +} + +module.exports = DotenvWebpackPlugin; diff --git a/packages/dotenv-webpack-plugin/src/options.json b/packages/dotenv-webpack-plugin/src/options.json new file mode 100644 index 00000000000..da69467fe14 --- /dev/null +++ b/packages/dotenv-webpack-plugin/src/options.json @@ -0,0 +1,30 @@ +{ + "definitions": {}, + "title": "DotenvWebpackPlugin", + "type": "object", + "additionalProperties": false, + "properties": { + "envFiles": { + "description": "The paths to the .env files to load", + "type": "array", + "items": { + "type": "string" + } + }, + "prefixes": { + "description": "The prefixes to prepend to the environment variables", + "type": "array", + "items": { + "type": "string" + } + }, + "envVarPrefix": { + "description": "The prefix to filter environment variables by", + "type": "string" + }, + "allowEmptyValues": { + "description": "Whether to allow empty values for environment variables", + "type": "boolean" + } + } +} diff --git a/packages/dotenv-webpack-plugin/src/types.d.ts b/packages/dotenv-webpack-plugin/src/types.d.ts new file mode 100644 index 00000000000..d863dae736b --- /dev/null +++ b/packages/dotenv-webpack-plugin/src/types.d.ts @@ -0,0 +1,6 @@ +export type Config = { + envFiles?: string[]; + prefixes?: string[]; + envVarPrefix?: string; + allowEmptyValues?: boolean; +}; diff --git a/packages/webpack-cli/README.md b/packages/webpack-cli/README.md index 430d7f08b46..d443dfa029d 100644 --- a/packages/webpack-cli/README.md +++ b/packages/webpack-cli/README.md @@ -84,6 +84,7 @@ Options: -m, --merge Merge two or more configurations using 'webpack-merge'. --disable-interpret Disable interpret for loading the config file. --env Environment passed to the configuration when it is a function. + --read-dot-env Read environment variables from dotenv files --node-env Sets process.env.NODE_ENV to the specified value. --define-process-env-node-env Sets process.env.NODE_ENV to the specified value. (Currently an alias for `--node-env`) --analyze It invokes webpack-bundle-analyzer plugin to get bundle information. diff --git a/packages/webpack-cli/package.json b/packages/webpack-cli/package.json index 117d4be9f3d..e03ef45c5cc 100644 --- a/packages/webpack-cli/package.json +++ b/packages/webpack-cli/package.json @@ -58,6 +58,9 @@ "@webpack-cli/generators": { "optional": true }, + "dotenv-webpack-plugin": { + "optional": true + }, "webpack-bundle-analyzer": { "optional": true }, diff --git a/packages/webpack-cli/src/types.ts b/packages/webpack-cli/src/types.ts index af34ae8ce1c..c62910f9387 100644 --- a/packages/webpack-cli/src/types.ts +++ b/packages/webpack-cli/src/types.ts @@ -179,6 +179,7 @@ type WebpackDevServerOptions = DevServerConfig & configName?: string[]; disableInterpret?: boolean; extends?: string[]; + readDotEnv?: boolean; argv: Argv; }; diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 6d190e65ad8..e4143750de6 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -998,6 +998,17 @@ class WebpackCLI implements IWebpackCLI { "Path to the configuration to be extended (only works when using webpack-cli).", helpLevel: "minimum", }, + { + name: "read-dot-env", + configs: [ + { + type: "enum", + values: [true], + }, + ], + description: "Read environment variables from .env files", + helpLevel: "minimum", + }, ]; const minimumHelpFlags = [ @@ -2279,6 +2290,47 @@ class WebpackCLI implements IWebpackCLI { ); }; + if (options.readDotEnv) { + const isWebpackInstalled = this.checkPackageExists("webpack"); + + if (!isWebpackInstalled) { + this.logger.error("The 'webpack' package is required to use the '--read-dot-env' option."); + } + + const isDotEnvPluginInstalled = this.checkPackageExists("dotenv-webpack-plugin"); + + if (!isDotEnvPluginInstalled) { + this.logger.error( + "The 'dotenv-webpack-plugin' package is required to use the '--read-dot-env' option.", + ); + } + + const shouldAddDotEnvPlugin = + !!options.readDotEnv && isWebpackInstalled && isDotEnvPluginInstalled; + + if (shouldAddDotEnvPlugin) { + const DotenvWebpackPlugin = await this.tryRequireThenImport< + Instantiable void, []> + >("dotenv-webpack-plugin"); + + const addDotEnvPlugin = (item: WebpackConfiguration) => { + if (!item.plugins) { + item.plugins = []; + } + + item.plugins.push(new DotenvWebpackPlugin()); + }; + + if (Array.isArray(config.options)) { + for (const item of config.options) { + addDotEnvPlugin(item); + } + } else { + addDotEnvPlugin(config.options); + } + } + } + if (Array.isArray(config.options)) { for (const item of config.options) { internalBuildConfig(item); diff --git a/test/build/dotenv-webpack-plugin/allow-empty-values/.env b/test/build/dotenv-webpack-plugin/allow-empty-values/.env new file mode 100644 index 00000000000..048ec8a8dd4 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/allow-empty-values/.env @@ -0,0 +1,2 @@ +PUBLIC_VARIABLE= +PUBLIC_VARIABLE2= \ No newline at end of file diff --git a/test/build/dotenv-webpack-plugin/allow-empty-values/src/index.js b/test/build/dotenv-webpack-plugin/allow-empty-values/src/index.js new file mode 100644 index 00000000000..2b4c95d39d1 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/allow-empty-values/src/index.js @@ -0,0 +1 @@ +console.log("Hello from index.js"); diff --git a/test/build/dotenv-webpack-plugin/allow-empty-values/webpack.config.js b/test/build/dotenv-webpack-plugin/allow-empty-values/webpack.config.js new file mode 100644 index 00000000000..782eee0c94f --- /dev/null +++ b/test/build/dotenv-webpack-plugin/allow-empty-values/webpack.config.js @@ -0,0 +1,15 @@ +const { join } = require("path"); +const DotenvWebpackPlugin = require("../../../../packages/dotenv-webpack-plugin/src"); + +module.exports = { + entry: "./src/index.js", + mode: "production", + output: { + path: join(__dirname, "dist"), + }, + plugins: [ + new DotenvWebpackPlugin({ + allowEmptyValues: true, + }), + ], +}; diff --git a/test/build/dotenv-webpack-plugin/builtin-config/.env b/test/build/dotenv-webpack-plugin/builtin-config/.env new file mode 100644 index 00000000000..c5dda70fc93 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/builtin-config/.env @@ -0,0 +1,2 @@ +PUBLIC_VARIABLE1=value1 +PUBLIC_VARIABLE2=value2 diff --git a/test/build/dotenv-webpack-plugin/builtin-config/src/index.js b/test/build/dotenv-webpack-plugin/builtin-config/src/index.js new file mode 100644 index 00000000000..600e863e06b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/builtin-config/src/index.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js") +console.log(process.env.PUBLIC_VARIABLE1) +console.log(import.meta.env.PUBLIC_VARIABLE2) diff --git a/test/build/dotenv-webpack-plugin/custom-config-path/.env.vars b/test/build/dotenv-webpack-plugin/custom-config-path/.env.vars new file mode 100644 index 00000000000..2eaacc7cf87 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/custom-config-path/.env.vars @@ -0,0 +1 @@ +PUBLIC_VARIABLE=default_value diff --git a/test/build/dotenv-webpack-plugin/custom-config-path/src/index.js b/test/build/dotenv-webpack-plugin/custom-config-path/src/index.js new file mode 100644 index 00000000000..c8ae6a18f7b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/custom-config-path/src/index.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js"); +console.log("process.env.PUBLIC_VARIABLE:", process.env.PUBLIC_VARIABLE); +console.log("import.meta.env.PUBLIC_VARIABLE:", import.meta.env.PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/custom-config-path/webpack.config.js b/test/build/dotenv-webpack-plugin/custom-config-path/webpack.config.js new file mode 100644 index 00000000000..a918ac8f2c1 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/custom-config-path/webpack.config.js @@ -0,0 +1,15 @@ +const { join } = require("path"); +const DotenvWebpackPlugin = require("../../../../packages/dotenv-webpack-plugin/src"); + +module.exports = { + entry: "./src/index.js", + mode: "production", + output: { + path: join(__dirname, "dist"), + }, + plugins: [ + new DotenvWebpackPlugin({ + envFiles: ["./.env.vars"], + }), + ], +}; diff --git a/test/build/dotenv-webpack-plugin/custom-prefix/.env b/test/build/dotenv-webpack-plugin/custom-prefix/.env new file mode 100644 index 00000000000..2eaacc7cf87 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/custom-prefix/.env @@ -0,0 +1 @@ +PUBLIC_VARIABLE=default_value diff --git a/test/build/dotenv-webpack-plugin/custom-prefix/src/index.js b/test/build/dotenv-webpack-plugin/custom-prefix/src/index.js new file mode 100644 index 00000000000..4a13d4605af --- /dev/null +++ b/test/build/dotenv-webpack-plugin/custom-prefix/src/index.js @@ -0,0 +1,2 @@ +console.log("Hello from index.js"); +console.log("custom_prefix_PUBLIC_VARIABLE:", custom_prefix_PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/custom-prefix/webpack.config.js b/test/build/dotenv-webpack-plugin/custom-prefix/webpack.config.js new file mode 100644 index 00000000000..6bcd31d6e49 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/custom-prefix/webpack.config.js @@ -0,0 +1,15 @@ +const { join } = require("path"); +const DotenvWebpackPlugin = require("../../../../packages/dotenv-webpack-plugin/src"); + +module.exports = { + entry: "./src/index.js", + mode: "production", + output: { + path: join(__dirname, "dist"), + }, + plugins: [ + new DotenvWebpackPlugin({ + prefixes: ["custom_prefix_"], + }), + ], +}; diff --git a/test/build/dotenv-webpack-plugin/dotenv-webpack-plugin.test.js b/test/build/dotenv-webpack-plugin/dotenv-webpack-plugin.test.js new file mode 100644 index 00000000000..23fa05ee7b5 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/dotenv-webpack-plugin.test.js @@ -0,0 +1,305 @@ +"use strict"; + +const { run, readFile } = require("../../utils/test-utils"); +const { resolve, join } = require("path"); +const { existsSync } = require("fs"); + +const assertNoErrors = (exitCode, stderr, stdout, testDir, buildPath = "dist") => { + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toBeTruthy(); + expect(stdout).not.toContain("ERROR"); + expect(existsSync(resolve(testDir, join(buildPath, "main.js")))).toBeTruthy(); +}; + +const getBuildOutput = async (testDir, buildPath = "dist") => { + try { + return readFile(resolve(testDir, join(buildPath, "main.js")), "utf-8"); + } catch (error) { + expect(error).toBe(null); + } +}; + +describe("dotenv-webpack-plugin", () => { + it("reads .env file and defines variables correctly", async () => { + const testDir = join(__dirname, "builtin-config"); + const { exitCode, stderr, stdout } = await run(testDir, [ + "--entry", + "./src/index.js", + "--read-dot-env", + ]); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain("value1"); + expect(data).toContain("value2"); + }); + + it("reads .env.production file and overrides values from .env variables correctly", async () => { + const testDir = join(__dirname, "overrides-config"); + const { exitCode, stderr, stdout } = await run(testDir, [ + "--entry", + "./src/index.js", + "--read-dot-env", + ]); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain('"process.env.PUBLIC_VARIABLE:",production_value'); + expect(data).toContain('"import.meta.env.PUBLIC_VARIABLE:",production_value'); + }); + + it("reads .env file and does not define a variable when it does not start with PUBLIC_", async () => { + const testDir = join(__dirname, "non-webpack-variable"); + const { exitCode, stderr, stdout } = await run(testDir, [ + "--entry", + "./src/index.js", + "--read-dot-env", + ]); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain('"process.env.NON_PUBLIC_VARIABLE:",process.env.NON_PUBLIC_VARIABLE'); + expect(data).toContain('"import.meta.env.NON_PUBLIC_VARIABLE:",(void 0).NON_PUBLIC_VARIABLE'); + expect(data).not.toContain("variable_value"); + }); + + it("reads .env.production when mode is set to production", async () => { + const testDir = join(__dirname, "mode-production"); + const { exitCode, stderr, stdout } = await run(testDir, [ + "--entry", + "./src/index.js", + "--read-dot-env", + "--mode", + "production", + ]); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain('"process.env.PUBLIC_VARIABLE:",production_value'); + expect(data).toContain('"import.meta.env.PUBLIC_VARIABLE:",production_value'); + expect(data).not.toContain("default_value"); + expect(data).not.toContain("development_value"); + }); + + it("reads .env.development when mode is set to development", async () => { + const testDir = join(__dirname, "mode-development"); + const { exitCode, stderr, stdout } = await run(testDir, [ + "--entry", + "./src/index.js", + "--read-dot-env", + "--mode", + "development", + ]); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + // TODO check why webpack adds "\\" to the value only in development mode + expect(data).toContain('"process.env.PUBLIC_VARIABLE:\\", development_value'); + expect(data).toContain('"import.meta.env.PUBLIC_VARIABLE:\\", development_value'); + expect(data).not.toContain("default_value"); + expect(data).not.toContain("production_value"); + }); + + it("reads .env.none when mode is set to none", async () => { + const testDir = join(__dirname, "mode-none"); + const { exitCode, stderr, stdout } = await run(testDir, [ + "--entry", + "./src/index.js", + "--read-dot-env", + "--mode", + "none", + ]); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain('"process.env.PUBLIC_VARIABLE:", none_value'); + expect(data).toContain('"import.meta.env.PUBLIC_VARIABLE:", none_value'); + expect(data).not.toContain("default_value"); + expect(data).not.toContain("production_value"); + expect(data).not.toContain("development_value"); + }); + + it("reads .env.example when file is present", async () => { + const testDir = join(__dirname, "env-example"); + const { exitCode, stderr, stdout } = await run(testDir, [ + "--entry", + "./src/index.js", + "--read-dot-env", + ]); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain('"process.env.PUBLIC_VARIABLE:",example_value'); + expect(data).toContain('"import.meta.env.PUBLIC_VARIABLE:",example_value'); + }); + + it("overrides value from .env when same key in .env.local is present", async () => { + const testDir = join(__dirname, "overrides-local"); + const { exitCode, stderr, stdout } = await run(testDir, [ + "--entry", + "./src/index.js", + "--read-dot-env", + ]); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain('"process.env.PUBLIC_VARIABLE:",local_value'); + expect(data).toContain('"import.meta.env.PUBLIC_VARIABLE:",local_value'); + expect(data).not.toContain("default_value"); + }); + + it("overrides value from .env.[mode] when same key in .env.[mode].local is present", async () => { + const testDir = join(__dirname, "overrides-local"); + const { exitCode, stderr, stdout } = await run(testDir, [ + "--entry", + "./src/index.js", + "--read-dot-env", + ]); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain('"process.env.PUBLIC_VARIABLE:",local_value'); + expect(data).toContain('"import.meta.env.PUBLIC_VARIABLE:",local_value'); + expect(data).not.toContain("production_value"); + }); + + it("reads .env file and applies for all configs", async () => { + const testDir = join(__dirname, "multiple-configs"); + const { exitCode, stderr, stdout } = await run(testDir, ["--read-dot-env"]); + + assertNoErrors(exitCode, stderr, stdout, testDir, "dist1"); + + let data = await getBuildOutput(testDir, "dist1"); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain('"process.env.PUBLIC_VARIABLE:",default_value'); + expect(data).toContain('"import.meta.env.PUBLIC_VARIABLE:",default_value'); + + data = await getBuildOutput(testDir, "dist2"); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain('"process.env.PUBLIC_VARIABLE:",default_value'); + expect(data).toContain('"import.meta.env.PUBLIC_VARIABLE:",default_value'); + }); + + it("reads env vars from a custom file path correctly", async () => { + const testDir = join(__dirname, "custom-config-path"); + const { exitCode, stderr, stdout } = await run(testDir); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain('"process.env.PUBLIC_VARIABLE:",default_value'); + expect(data).toContain('"import.meta.env.PUBLIC_VARIABLE:",default_value'); + }); + + it("creates variables with custom prefixes when passed", async () => { + const testDir = join(__dirname, "custom-prefix"); + const { exitCode, stderr, stdout } = await run(testDir); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain('"custom_prefix_PUBLIC_VARIABLE:",default_value'); + }); + + it("validates env files paths", async () => { + const testDir = join(__dirname, "validate-config-paths"); + const { stderr } = await run(testDir); + + expect(stderr).toContain("options.envFiles should be an array"); + }); + + it("validates custom prefixes", async () => { + const testDir = join(__dirname, "validates-prefixes"); + const { stderr } = await run(testDir); + + expect(stderr).toContain("options.prefixes should be an array"); + }); + + it("uses the passed env var prefix correctly", async () => { + const testDir = join(__dirname, "env-var-prefix"); + const { exitCode, stderr, stdout } = await run(testDir, ["--read-dot-env"]); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain("default_value"); + }); + + it("overrides the variables according to priority order correctly", async () => { + const testDir = join(__dirname, "priority-order"); + const { exitCode, stderr, stdout } = await run(testDir, ["--read-dot-env"]); + + assertNoErrors(exitCode, stderr, stdout, testDir); + + const data = await getBuildOutput(testDir); + + expect(data).toContain("Hello from index.js"); + expect(data).toContain(`"process.env.PUBLIC_EXAMPLE_VARIABLE:",public_example_value_override`); + expect(data).toContain(`"process.env.PUBLIC_ENV_VARIABLE:",public_env_value_override`); + expect(data).toContain( + `"process.env.PUBLIC_ENV_MODE_VARIABLE:",public_env_mode_value_override`, + ); + expect(data).toContain( + `"process.env.PUBLIC_ENV_LOCAL_VARIABLE:",public_env_local_value_override`, + ); + }); + + it("throws an error if custom env file path is passed and file could not be read", async () => { + const testDir = join(__dirname, "validates-file-exists"); + const { stdout } = await run(testDir); + + expect(stdout).toContain("Could not read ./env.custom"); + }); + + it("throws an error if empty value is passed for an environment variable", async () => { + const testDir = join(__dirname, "validates-empty-value"); + const { stdout } = await run(testDir); + + expect(stdout).toContain( + "Environment variables cannot have an empty value. The following variables are empty: PUBLIC_VARIABLE, PUBLIC_VARIABLE2", + ); + }); + + it("does not throw an error if empty value is passed for an environment variable and allowEmptyValues is set to true", async () => { + const testDir = join(__dirname, "allow-empty-values"); + const { exitCode, stderr, stdout } = await run(testDir); + + assertNoErrors(exitCode, stderr, stdout, testDir); + }); +}); diff --git a/test/build/dotenv-webpack-plugin/env-example/.env.example b/test/build/dotenv-webpack-plugin/env-example/.env.example new file mode 100644 index 00000000000..5bc1af18652 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/env-example/.env.example @@ -0,0 +1 @@ +PUBLIC_VARIABLE=example_value diff --git a/test/build/dotenv-webpack-plugin/env-example/src/index.js b/test/build/dotenv-webpack-plugin/env-example/src/index.js new file mode 100644 index 00000000000..c8ae6a18f7b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/env-example/src/index.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js"); +console.log("process.env.PUBLIC_VARIABLE:", process.env.PUBLIC_VARIABLE); +console.log("import.meta.env.PUBLIC_VARIABLE:", import.meta.env.PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/env-var-prefix/.env b/test/build/dotenv-webpack-plugin/env-var-prefix/.env new file mode 100644 index 00000000000..994d1cf0c6e --- /dev/null +++ b/test/build/dotenv-webpack-plugin/env-var-prefix/.env @@ -0,0 +1 @@ +WEBPACK_VARIABLE=default_value diff --git a/test/build/dotenv-webpack-plugin/env-var-prefix/src/index.js b/test/build/dotenv-webpack-plugin/env-var-prefix/src/index.js new file mode 100644 index 00000000000..23aac4380ff --- /dev/null +++ b/test/build/dotenv-webpack-plugin/env-var-prefix/src/index.js @@ -0,0 +1,2 @@ +console.log("Hello from index.js"); +console.log("process.env.WEBPACK_VARIABLE:", process.env.WEBPACK_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/env-var-prefix/webpack.config.js b/test/build/dotenv-webpack-plugin/env-var-prefix/webpack.config.js new file mode 100644 index 00000000000..673849dcf0a --- /dev/null +++ b/test/build/dotenv-webpack-plugin/env-var-prefix/webpack.config.js @@ -0,0 +1,15 @@ +const { join } = require("path"); +const DotenvWebpackPlugin = require("../../../../packages/dotenv-webpack-plugin/src"); + +module.exports = { + entry: "./src/index.js", + mode: "production", + output: { + path: join(__dirname, "dist"), + }, + plugins: [ + new DotenvWebpackPlugin({ + envVarPrefix: "WEBPACK_", + }), + ], +}; diff --git a/test/build/dotenv-webpack-plugin/mode-development/.env b/test/build/dotenv-webpack-plugin/mode-development/.env new file mode 100644 index 00000000000..2eaacc7cf87 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-development/.env @@ -0,0 +1 @@ +PUBLIC_VARIABLE=default_value diff --git a/test/build/dotenv-webpack-plugin/mode-development/.env.development b/test/build/dotenv-webpack-plugin/mode-development/.env.development new file mode 100644 index 00000000000..a58008f1597 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-development/.env.development @@ -0,0 +1 @@ +PUBLIC_VARIABLE=development_value diff --git a/test/build/dotenv-webpack-plugin/mode-development/.env.production b/test/build/dotenv-webpack-plugin/mode-development/.env.production new file mode 100644 index 00000000000..abd00c1fc28 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-development/.env.production @@ -0,0 +1 @@ +PUBLIC_VARIABLE=production_value diff --git a/test/build/dotenv-webpack-plugin/mode-development/src/index.js b/test/build/dotenv-webpack-plugin/mode-development/src/index.js new file mode 100644 index 00000000000..c8ae6a18f7b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-development/src/index.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js"); +console.log("process.env.PUBLIC_VARIABLE:", process.env.PUBLIC_VARIABLE); +console.log("import.meta.env.PUBLIC_VARIABLE:", import.meta.env.PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/mode-none/.env b/test/build/dotenv-webpack-plugin/mode-none/.env new file mode 100644 index 00000000000..2eaacc7cf87 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-none/.env @@ -0,0 +1 @@ +PUBLIC_VARIABLE=default_value diff --git a/test/build/dotenv-webpack-plugin/mode-none/.env.development b/test/build/dotenv-webpack-plugin/mode-none/.env.development new file mode 100644 index 00000000000..a58008f1597 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-none/.env.development @@ -0,0 +1 @@ +PUBLIC_VARIABLE=development_value diff --git a/test/build/dotenv-webpack-plugin/mode-none/.env.none b/test/build/dotenv-webpack-plugin/mode-none/.env.none new file mode 100644 index 00000000000..cf185ab413b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-none/.env.none @@ -0,0 +1 @@ +PUBLIC_VARIABLE=none_value diff --git a/test/build/dotenv-webpack-plugin/mode-none/.env.production b/test/build/dotenv-webpack-plugin/mode-none/.env.production new file mode 100644 index 00000000000..abd00c1fc28 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-none/.env.production @@ -0,0 +1 @@ +PUBLIC_VARIABLE=production_value diff --git a/test/build/dotenv-webpack-plugin/mode-none/src/index.js b/test/build/dotenv-webpack-plugin/mode-none/src/index.js new file mode 100644 index 00000000000..c8ae6a18f7b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-none/src/index.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js"); +console.log("process.env.PUBLIC_VARIABLE:", process.env.PUBLIC_VARIABLE); +console.log("import.meta.env.PUBLIC_VARIABLE:", import.meta.env.PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/mode-production/.env b/test/build/dotenv-webpack-plugin/mode-production/.env new file mode 100644 index 00000000000..2eaacc7cf87 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-production/.env @@ -0,0 +1 @@ +PUBLIC_VARIABLE=default_value diff --git a/test/build/dotenv-webpack-plugin/mode-production/.env.development b/test/build/dotenv-webpack-plugin/mode-production/.env.development new file mode 100644 index 00000000000..a58008f1597 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-production/.env.development @@ -0,0 +1 @@ +PUBLIC_VARIABLE=development_value diff --git a/test/build/dotenv-webpack-plugin/mode-production/.env.production b/test/build/dotenv-webpack-plugin/mode-production/.env.production new file mode 100644 index 00000000000..abd00c1fc28 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-production/.env.production @@ -0,0 +1 @@ +PUBLIC_VARIABLE=production_value diff --git a/test/build/dotenv-webpack-plugin/mode-production/src/index.js b/test/build/dotenv-webpack-plugin/mode-production/src/index.js new file mode 100644 index 00000000000..c8ae6a18f7b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/mode-production/src/index.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js"); +console.log("process.env.PUBLIC_VARIABLE:", process.env.PUBLIC_VARIABLE); +console.log("import.meta.env.PUBLIC_VARIABLE:", import.meta.env.PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/multiple-configs/.env b/test/build/dotenv-webpack-plugin/multiple-configs/.env new file mode 100644 index 00000000000..2eaacc7cf87 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/multiple-configs/.env @@ -0,0 +1 @@ +PUBLIC_VARIABLE=default_value diff --git a/test/build/dotenv-webpack-plugin/multiple-configs/src/index.js b/test/build/dotenv-webpack-plugin/multiple-configs/src/index.js new file mode 100644 index 00000000000..c8ae6a18f7b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/multiple-configs/src/index.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js"); +console.log("process.env.PUBLIC_VARIABLE:", process.env.PUBLIC_VARIABLE); +console.log("import.meta.env.PUBLIC_VARIABLE:", import.meta.env.PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/multiple-configs/src/index2.js b/test/build/dotenv-webpack-plugin/multiple-configs/src/index2.js new file mode 100644 index 00000000000..c8ae6a18f7b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/multiple-configs/src/index2.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js"); +console.log("process.env.PUBLIC_VARIABLE:", process.env.PUBLIC_VARIABLE); +console.log("import.meta.env.PUBLIC_VARIABLE:", import.meta.env.PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/multiple-configs/webpack.config.js b/test/build/dotenv-webpack-plugin/multiple-configs/webpack.config.js new file mode 100644 index 00000000000..940a6abe120 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/multiple-configs/webpack.config.js @@ -0,0 +1,18 @@ +const { join } = require("path"); + +module.exports = [ + { + entry: "./src/index.js", + mode: "production", + output: { + path: join(__dirname, "dist1"), + }, + }, + { + entry: "./src/index2.js", + mode: "production", + output: { + path: join(__dirname, "dist2"), + }, + }, +]; diff --git a/test/build/dotenv-webpack-plugin/non-webpack-variable/.env b/test/build/dotenv-webpack-plugin/non-webpack-variable/.env new file mode 100644 index 00000000000..a8532c76489 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/non-webpack-variable/.env @@ -0,0 +1 @@ +NON_PUBLIC_VARIABLE=variable_value diff --git a/test/build/dotenv-webpack-plugin/non-webpack-variable/src/index.js b/test/build/dotenv-webpack-plugin/non-webpack-variable/src/index.js new file mode 100644 index 00000000000..830999536e6 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/non-webpack-variable/src/index.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js"); +console.log("process.env.NON_PUBLIC_VARIABLE:", process.env.NON_PUBLIC_VARIABLE); +console.log("import.meta.env.NON_PUBLIC_VARIABLE:", import.meta.env.NON_PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/overrides-config/.env b/test/build/dotenv-webpack-plugin/overrides-config/.env new file mode 100644 index 00000000000..a58008f1597 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/overrides-config/.env @@ -0,0 +1 @@ +PUBLIC_VARIABLE=development_value diff --git a/test/build/dotenv-webpack-plugin/overrides-config/.env.production b/test/build/dotenv-webpack-plugin/overrides-config/.env.production new file mode 100644 index 00000000000..abd00c1fc28 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/overrides-config/.env.production @@ -0,0 +1 @@ +PUBLIC_VARIABLE=production_value diff --git a/test/build/dotenv-webpack-plugin/overrides-config/src/index.js b/test/build/dotenv-webpack-plugin/overrides-config/src/index.js new file mode 100644 index 00000000000..c8ae6a18f7b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/overrides-config/src/index.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js"); +console.log("process.env.PUBLIC_VARIABLE:", process.env.PUBLIC_VARIABLE); +console.log("import.meta.env.PUBLIC_VARIABLE:", import.meta.env.PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/overrides-local/.env b/test/build/dotenv-webpack-plugin/overrides-local/.env new file mode 100644 index 00000000000..2eaacc7cf87 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/overrides-local/.env @@ -0,0 +1 @@ +PUBLIC_VARIABLE=default_value diff --git a/test/build/dotenv-webpack-plugin/overrides-local/.env.local b/test/build/dotenv-webpack-plugin/overrides-local/.env.local new file mode 100644 index 00000000000..de73c9136ca --- /dev/null +++ b/test/build/dotenv-webpack-plugin/overrides-local/.env.local @@ -0,0 +1 @@ +PUBLIC_VARIABLE=local_value diff --git a/test/build/dotenv-webpack-plugin/overrides-local/src/index.js b/test/build/dotenv-webpack-plugin/overrides-local/src/index.js new file mode 100644 index 00000000000..c8ae6a18f7b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/overrides-local/src/index.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js"); +console.log("process.env.PUBLIC_VARIABLE:", process.env.PUBLIC_VARIABLE); +console.log("import.meta.env.PUBLIC_VARIABLE:", import.meta.env.PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/overrides-mode-local/.env.production b/test/build/dotenv-webpack-plugin/overrides-mode-local/.env.production new file mode 100644 index 00000000000..abd00c1fc28 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/overrides-mode-local/.env.production @@ -0,0 +1 @@ +PUBLIC_VARIABLE=production_value diff --git a/test/build/dotenv-webpack-plugin/overrides-mode-local/.env.production.local b/test/build/dotenv-webpack-plugin/overrides-mode-local/.env.production.local new file mode 100644 index 00000000000..de73c9136ca --- /dev/null +++ b/test/build/dotenv-webpack-plugin/overrides-mode-local/.env.production.local @@ -0,0 +1 @@ +PUBLIC_VARIABLE=local_value diff --git a/test/build/dotenv-webpack-plugin/overrides-mode-local/src/index.js b/test/build/dotenv-webpack-plugin/overrides-mode-local/src/index.js new file mode 100644 index 00000000000..c8ae6a18f7b --- /dev/null +++ b/test/build/dotenv-webpack-plugin/overrides-mode-local/src/index.js @@ -0,0 +1,3 @@ +console.log("Hello from index.js"); +console.log("process.env.PUBLIC_VARIABLE:", process.env.PUBLIC_VARIABLE); +console.log("import.meta.env.PUBLIC_VARIABLE:", import.meta.env.PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/priority-order/.env b/test/build/dotenv-webpack-plugin/priority-order/.env new file mode 100644 index 00000000000..16ea9537be2 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/priority-order/.env @@ -0,0 +1,2 @@ +PUBLIC_EXAMPLE_VARIABLE=public_example_value_override +PUBLIC_ENV_VARIABLE=public_env_value \ No newline at end of file diff --git a/test/build/dotenv-webpack-plugin/priority-order/.env.example b/test/build/dotenv-webpack-plugin/priority-order/.env.example new file mode 100644 index 00000000000..f69be280e6d --- /dev/null +++ b/test/build/dotenv-webpack-plugin/priority-order/.env.example @@ -0,0 +1 @@ +PUBLIC_EXAMPLE_VARIABLE=public_example_value diff --git a/test/build/dotenv-webpack-plugin/priority-order/.env.local b/test/build/dotenv-webpack-plugin/priority-order/.env.local new file mode 100644 index 00000000000..33db2187942 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/priority-order/.env.local @@ -0,0 +1,2 @@ +PUBLIC_ENV_MODE_VARIABLE=public_env_mode_value_override +PUBLIC_ENV_LOCAL_VARIABLE=public_env_local_value \ No newline at end of file diff --git a/test/build/dotenv-webpack-plugin/priority-order/.env.production b/test/build/dotenv-webpack-plugin/priority-order/.env.production new file mode 100644 index 00000000000..6d8491573dc --- /dev/null +++ b/test/build/dotenv-webpack-plugin/priority-order/.env.production @@ -0,0 +1,2 @@ +PUBLIC_ENV_VARIABLE=public_env_value_override +PUBLIC_ENV_MODE_VARIABLE=public_env_mode_value \ No newline at end of file diff --git a/test/build/dotenv-webpack-plugin/priority-order/.env.production.local b/test/build/dotenv-webpack-plugin/priority-order/.env.production.local new file mode 100644 index 00000000000..6dda4786329 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/priority-order/.env.production.local @@ -0,0 +1 @@ +PUBLIC_ENV_LOCAL_VARIABLE=public_env_local_value_override diff --git a/test/build/dotenv-webpack-plugin/priority-order/src/index.js b/test/build/dotenv-webpack-plugin/priority-order/src/index.js new file mode 100644 index 00000000000..e93cdf516a1 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/priority-order/src/index.js @@ -0,0 +1,13 @@ +console.log("Hello from index.js"); + +// value from .env.example to be overridden by .env +console.log("process.env.PUBLIC_EXAMPLE_VARIABLE:", process.env.PUBLIC_EXAMPLE_VARIABLE); + +// value from .env to be overridden by .env.[mode] +console.log("process.env.PUBLIC_ENV_VARIABLE:", process.env.PUBLIC_ENV_VARIABLE); + +// value from .env.[mode] to be overridden by .env.local +console.log("process.env.PUBLIC_ENV_MODE_VARIABLE:", process.env.PUBLIC_ENV_MODE_VARIABLE); + +// value from .env.local to be overridden by .env.[mode].local +console.log("process.env.PUBLIC_ENV_LOCAL_VARIABLE:", process.env.PUBLIC_ENV_LOCAL_VARIABLE); \ No newline at end of file diff --git a/test/build/dotenv-webpack-plugin/validate-config-paths/.env b/test/build/dotenv-webpack-plugin/validate-config-paths/.env new file mode 100644 index 00000000000..2eaacc7cf87 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/validate-config-paths/.env @@ -0,0 +1 @@ +PUBLIC_VARIABLE=default_value diff --git a/test/build/dotenv-webpack-plugin/validate-config-paths/src/index.js b/test/build/dotenv-webpack-plugin/validate-config-paths/src/index.js new file mode 100644 index 00000000000..4a13d4605af --- /dev/null +++ b/test/build/dotenv-webpack-plugin/validate-config-paths/src/index.js @@ -0,0 +1,2 @@ +console.log("Hello from index.js"); +console.log("custom_prefix_PUBLIC_VARIABLE:", custom_prefix_PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/validate-config-paths/webpack.config.js b/test/build/dotenv-webpack-plugin/validate-config-paths/webpack.config.js new file mode 100644 index 00000000000..9fbb84427cf --- /dev/null +++ b/test/build/dotenv-webpack-plugin/validate-config-paths/webpack.config.js @@ -0,0 +1,15 @@ +const { join } = require("path"); +const DotenvWebpackPlugin = require("../../../../packages/dotenv-webpack-plugin/src"); + +module.exports = { + entry: "./src/index.js", + mode: "production", + output: { + path: join(__dirname, "dist"), + }, + plugins: [ + new DotenvWebpackPlugin({ + envFiles: "./.env.vars", + }), + ], +}; diff --git a/test/build/dotenv-webpack-plugin/validates-empty-value/.env b/test/build/dotenv-webpack-plugin/validates-empty-value/.env new file mode 100644 index 00000000000..048ec8a8dd4 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/validates-empty-value/.env @@ -0,0 +1,2 @@ +PUBLIC_VARIABLE= +PUBLIC_VARIABLE2= \ No newline at end of file diff --git a/test/build/dotenv-webpack-plugin/validates-empty-value/src/index.js b/test/build/dotenv-webpack-plugin/validates-empty-value/src/index.js new file mode 100644 index 00000000000..2b4c95d39d1 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/validates-empty-value/src/index.js @@ -0,0 +1 @@ +console.log("Hello from index.js"); diff --git a/test/build/dotenv-webpack-plugin/validates-empty-value/webpack.config.js b/test/build/dotenv-webpack-plugin/validates-empty-value/webpack.config.js new file mode 100644 index 00000000000..d602814b4f3 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/validates-empty-value/webpack.config.js @@ -0,0 +1,11 @@ +const { join } = require("path"); +const DotenvWebpackPlugin = require("../../../../packages/dotenv-webpack-plugin/src"); + +module.exports = { + entry: "./src/index.js", + mode: "production", + output: { + path: join(__dirname, "dist"), + }, + plugins: [new DotenvWebpackPlugin()], +}; diff --git a/test/build/dotenv-webpack-plugin/validates-file-exists/src/index.js b/test/build/dotenv-webpack-plugin/validates-file-exists/src/index.js new file mode 100644 index 00000000000..2b4c95d39d1 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/validates-file-exists/src/index.js @@ -0,0 +1 @@ +console.log("Hello from index.js"); diff --git a/test/build/dotenv-webpack-plugin/validates-file-exists/webpack.config.js b/test/build/dotenv-webpack-plugin/validates-file-exists/webpack.config.js new file mode 100644 index 00000000000..5770b61c059 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/validates-file-exists/webpack.config.js @@ -0,0 +1,15 @@ +const { join } = require("path"); +const DotenvWebpackPlugin = require("../../../../packages/dotenv-webpack-plugin/src"); + +module.exports = { + entry: "./src/index.js", + mode: "production", + output: { + path: join(__dirname, "dist"), + }, + plugins: [ + new DotenvWebpackPlugin({ + envFiles: ["./env.custom"], + }), + ], +}; diff --git a/test/build/dotenv-webpack-plugin/validates-prefixes/.env b/test/build/dotenv-webpack-plugin/validates-prefixes/.env new file mode 100644 index 00000000000..2eaacc7cf87 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/validates-prefixes/.env @@ -0,0 +1 @@ +PUBLIC_VARIABLE=default_value diff --git a/test/build/dotenv-webpack-plugin/validates-prefixes/src/index.js b/test/build/dotenv-webpack-plugin/validates-prefixes/src/index.js new file mode 100644 index 00000000000..4a13d4605af --- /dev/null +++ b/test/build/dotenv-webpack-plugin/validates-prefixes/src/index.js @@ -0,0 +1,2 @@ +console.log("Hello from index.js"); +console.log("custom_prefix_PUBLIC_VARIABLE:", custom_prefix_PUBLIC_VARIABLE); diff --git a/test/build/dotenv-webpack-plugin/validates-prefixes/webpack.config.js b/test/build/dotenv-webpack-plugin/validates-prefixes/webpack.config.js new file mode 100644 index 00000000000..592ef5a9c31 --- /dev/null +++ b/test/build/dotenv-webpack-plugin/validates-prefixes/webpack.config.js @@ -0,0 +1,15 @@ +const { join } = require("path"); +const DotenvWebpackPlugin = require("../../../../packages/dotenv-webpack-plugin/src"); + +module.exports = { + entry: "./src/index.js", + mode: "production", + output: { + path: join(__dirname, "dist"), + }, + plugins: [ + new DotenvWebpackPlugin({ + prefixes: "custom_prefix_", + }), + ], +}; diff --git a/test/help/__snapshots__/help.test.js.snap.devServer4.webpack5 b/test/help/__snapshots__/help.test.js.snap.devServer4.webpack5 index af6dbd3a74c..8e400e66efa 100644 --- a/test/help/__snapshots__/help.test.js.snap.devServer4.webpack5 +++ b/test/help/__snapshots__/help.test.js.snap.devServer4.webpack5 @@ -105,6 +105,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -166,6 +167,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -227,6 +229,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -287,6 +290,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -335,6 +339,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -396,6 +401,8 @@ Options: exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env + files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). @@ -470,6 +477,8 @@ Options: exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env + files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). @@ -531,6 +540,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -579,6 +589,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1561,6 +1572,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1607,6 +1619,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1666,6 +1679,8 @@ Options: exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env + files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). @@ -1737,6 +1752,8 @@ Options: exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env + files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). @@ -1795,6 +1812,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1841,6 +1859,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1888,6 +1907,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1949,6 +1969,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -2200,6 +2221,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -2259,6 +2281,7 @@ Options: -j, --json [pathToJsonFile] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack. -e, --extends Path to the configuration to be extended (only works when using webpack-cli). + --read-dot-env Read environment variables from .env files -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. diff --git a/yarn.lock b/yarn.lock index f78e0ab6e89..d834a734076 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2608,7 +2608,7 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-import-assertions@^1.9.0: +acorn-import-assertions@^1.7.6, acorn-import-assertions@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== @@ -4235,6 +4235,16 @@ dot-prop@^5.1.0, dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" +dotenv-expand@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" + integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== + +dotenv@^16.0.3: + version "16.0.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" + integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== + dotenv@~10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" @@ -4309,6 +4319,14 @@ enhanced-resolve@^5.0.0, enhanced-resolve@^5.14.1: graceful-fs "^4.2.4" tapable "^2.2.0" +enhanced-resolve@^5.13.0: + version "5.14.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.14.0.tgz#0b6c676c8a3266c99fa281e4433a706f5c0c61c4" + integrity sha512-+DCows0XNwLDcUhbFJPdlQEVnT2zXlCv7hPxemTz86/O+B/hCQ+mb7ydkPKiflpVraqLPCAfu7lDy+hBXueojw== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + enquirer@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -8909,7 +8927,7 @@ schema-utils@^3.1.1, schema-utils@^3.1.2: ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^4.0.0: +schema-utils@^4.0.0, schema-utils@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.1.tgz#eb2d042df8b01f4b5c276a2dfd41ba0faab72e8d" integrity sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ== @@ -10214,6 +10232,36 @@ webpack@^5.72.0: watchpack "^2.4.0" webpack-sources "^3.2.3" +webpack@^5.81.0: + version "5.82.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.82.0.tgz#3c0d074dec79401db026b4ba0fb23d6333f88e7d" + integrity sha512-iGNA2fHhnDcV1bONdUu554eZx+XeldsaeQ8T67H6KKHl2nUSwX8Zm7cmzOA46ox/X1ARxf7Bjv8wQ/HsB5fxBg== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.13.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.2" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760"