From 6b7fa9dcf580ee81734b18f155011242d07e3a51 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 3 Mar 2021 16:27:37 +0100 Subject: [PATCH 01/25] fix: only stub `process.env` if not targeting node --- src/index.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index 51d5236..71b706a 100644 --- a/src/index.js +++ b/src/index.js @@ -32,8 +32,14 @@ class Dotenv { }, config) this.checkDeprecation() + } + + apply (compiler) { + const target = compiler.options.target ?? 'web' + const variables = this.gatherVariables() + const data = this.formatData(variables, target) - return new DefinePlugin(this.formatData(this.gatherVariables())) + new DefinePlugin(data).apply(compiler) } checkDeprecation () { @@ -121,7 +127,7 @@ class Dotenv { return '' } - formatData (vars = {}) { + formatData (vars = {}, target) { const { expand } = this.config const formatted = Object.keys(vars).reduce((obj, key) => { const v = vars[key] @@ -144,8 +150,14 @@ class Dotenv { return obj }, {}) - // fix in case of missing - formatted['process.env'] = '{}' + // We have to stub any remaining `process.env`s due to Webpack 5 not polyfilling it anymore + // https://github.com/mrsteele/dotenv-webpack/issues/240#issuecomment-710231534 + // However, if someone targets Node or Electron `process.env` still exists, and should therefore be kept + // https://webpack.js.org/configuration/target + if (!target.startsWith('node') && !target.startsWith('electron')) { + // fix in case of missing + formatted['process.env'] = '{}' + } return formatted } From f57f1e290ec555b04f8312e984ba3d2c7dfc9502 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 3 Mar 2021 16:30:02 +0100 Subject: [PATCH 02/25] fix: use better replacement --- src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 71b706a..2d2efbd 100644 --- a/src/index.js +++ b/src/index.js @@ -155,8 +155,8 @@ class Dotenv { // However, if someone targets Node or Electron `process.env` still exists, and should therefore be kept // https://webpack.js.org/configuration/target if (!target.startsWith('node') && !target.startsWith('electron')) { - // fix in case of missing - formatted['process.env'] = '{}' + // Results in `"MISSING_ENV_VAR".NAME` which is valid JS + formatted['process.env'] = '"MISSING_ENV_VAR"' } return formatted From d94ded6325fb348948da66d9ddf123d9e59bf5cc Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 3 Mar 2021 21:37:11 +0100 Subject: [PATCH 03/25] only dont stub on main thread electron --- src/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 2d2efbd..e940f7a 100644 --- a/src/index.js +++ b/src/index.js @@ -16,6 +16,9 @@ const interpolate = (env, vars) => { return env } +const isMainThreadElectron = (target) => + target.startsWith('electron') && target.endsWith('main') + class Dotenv { /** * The dotenv-webpack plugin. @@ -154,7 +157,7 @@ class Dotenv { // https://github.com/mrsteele/dotenv-webpack/issues/240#issuecomment-710231534 // However, if someone targets Node or Electron `process.env` still exists, and should therefore be kept // https://webpack.js.org/configuration/target - if (!target.startsWith('node') && !target.startsWith('electron')) { + if (!target.startsWith('node') && !isMainThreadElectron(target)) { // Results in `"MISSING_ENV_VAR".NAME` which is valid JS formatted['process.env'] = '"MISSING_ENV_VAR"' } From 4bce4816836df7b899187bbf463fa6bece567f96 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 3 Mar 2021 23:55:57 +0100 Subject: [PATCH 04/25] add tests that use the compiler --- .gitignore | 3 ++ test/compiler.test.js | 93 ++++++++++++++++++++++++++++++++++++++++++ test/fixtures/index.js | 12 ++++++ 3 files changed, 108 insertions(+) create mode 100644 test/compiler.test.js create mode 100644 test/fixtures/index.js diff --git a/.gitignore b/.gitignore index ee565f9..ce8ebe6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Tests +test/output + # Logs logs *.log diff --git a/test/compiler.test.js b/test/compiler.test.js new file mode 100644 index 0000000..cebf7f5 --- /dev/null +++ b/test/compiler.test.js @@ -0,0 +1,93 @@ +/* global jest, describe, test, expect, beforeEach */ + +const { resolve } = require('path') +const webpack = require('webpack') +const { readFileSync, rmdirSync } = require('fs') + +const Src = require('../src') +const Dist = require('../dist') + +const envEmpty = resolve(__dirname, './envs/.empty') +const envEmptyExample = resolve(__dirname, './envs/.empty.example') +const envSimple = resolve(__dirname, './envs/.simple') +const envSimpleExample = resolve(__dirname, './envs/.simple.example') +const envOneEmpty = resolve(__dirname, './envs/.oneempty') +const envOneEmptyExample = resolve(__dirname, './envs/.oneempty.example') +const envMissingOne = resolve(__dirname, './envs/.missingone') +const envMissingOneExample = resolve(__dirname, './envs/.missingone.example') +const envSystemvars = resolve(__dirname, './envs/.systemvars') +const envSystemvarsExample = resolve(__dirname, './envs/.systemvars.example') +const envExpanded = resolve(__dirname, './envs/.expanded') +const envDefaults = resolve(__dirname, './envs/.defaults') + +const getConfig = (target, plugin) => ({ + mode: 'development', + devtool: false, + target, + entry: resolve(__dirname, './fixtures/index'), + output: { + path: resolve(__dirname, `./output/${target}`) + }, + plugins: [plugin] +}) + +const versions = [ + ['Source', Src.default], + ['Dist', Dist.default] +] + +describe.each(versions)('%s', (_, DotenvPlugin) => { + describe('process.env stubbing', () => { + const expectToBeStubbed = (result) => { + expect(result).toMatch('const test = "testing"') + expect(result).toMatch('const test2 = "MISSING_ENV_VAR".TEST2') + expect(result).toMatch('const nodeEnv = "development"') + expect(result).toMatch('const mongolabUser = "MISSING_ENV_VAR".MONGOLAB_USER') + } + + const expectNotToBeStubbed = (result) => { + expect(result).toMatch('const test = "testing"') + expect(result).toMatch('const test2 = process.env.TEST2') + expect(result).toMatch('const nodeEnv = "development"') + expect(result).toMatch('const mongolabUser = process.env.MONGOLAB_USER') + } + + const plugin = new DotenvPlugin({ path: envSimple }) + const cases = [ + ['web', getConfig('web', plugin), true], + ['es5', getConfig('es5', plugin), true], + ['es2020', getConfig('es2020', plugin), true], + ['electron-renderer', getConfig('electron-renderer', plugin), true], + ['electron9-renderer', getConfig('electron9-renderer', plugin), true], + ['electron-preload', getConfig('electron-preload', plugin), true], + ['node', getConfig('node', plugin), false], + ['node14', getConfig('node14', plugin), false], + ['electron-main', getConfig('electron-main', plugin), false], + ['electron9-main', getConfig('electron9-main', plugin), false] + ] + + beforeEach(() => { + rmdirSync(resolve(__dirname, 'output'), { recursive: true }) + }) + + test.each(cases)('%s', (target, config, shouldStub, done) => { + webpack(config, (err, stats) => { + expect(err).toBeNull() + expect(stats.compilation.errors).toHaveLength(0) + + const result = readFileSync( + resolve(__dirname, `./output/${target}/main.js`), + { encoding: 'utf-8' } + ) + + if (shouldStub) { + expectToBeStubbed(result) + } else { + expectNotToBeStubbed(result) + } + + done() + }) + }) + }) +}) diff --git a/test/fixtures/index.js b/test/fixtures/index.js new file mode 100644 index 0000000..a5c8c0f --- /dev/null +++ b/test/fixtures/index.js @@ -0,0 +1,12 @@ +/* eslint-disable no-unused-vars */ +const test = process.env.TEST +const test2 = process.env.TEST2 +const nodeEnv = process.env.NODE_ENV +const mongolabUser = process.env.MONGOLAB_USER + +export default { + test, + test2, + nodeEnv, + mongolabUser +} From b058cd392237428202a28c57e5534d7bdc41506b Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 10:23:26 +0100 Subject: [PATCH 05/25] start converting old tests --- test/compiler.test.js | 130 +++++++++++++++++++++++++++++++++++++---- test/fixtures/index.js | 33 +++++++---- 2 files changed, 141 insertions(+), 22 deletions(-) diff --git a/test/compiler.test.js b/test/compiler.test.js index cebf7f5..df1a89e 100644 --- a/test/compiler.test.js +++ b/test/compiler.test.js @@ -31,25 +31,135 @@ const getConfig = (target, plugin) => ({ plugins: [plugin] }) +const compile = (config, callback) => { + webpack(config, (err, stats) => { + expect(err).toBeNull() + expect(stats.compilation.errors).toHaveLength(0) + + const result = readFileSync( + resolve(__dirname, `./output/${config.target}/main.js`), + { encoding: 'utf-8' } + ) + + callback(result) + }) +} + +const expectResultsToContainReplacements = (result, env) => { + Object.entries(env).forEach(([key, value]) => { + expect(result).toMatch(`const ${key} = "${value}"`) + }) +} + const versions = [ ['Source', Src.default], ['Dist', Dist.default] ] +beforeEach(() => { + rmdirSync(resolve(__dirname, 'output'), { recursive: true }) +}) + describe.each(versions)('%s', (_, DotenvPlugin) => { + test('Should be an function.', () => { + expect(typeof DotenvPlugin).toEqual('function') + }) + + test('Should return a instance of Dotenv.', () => { + expect((new DotenvPlugin()).constructor.name).toEqual('Dotenv') + }) + + describe('Defaults', () => { + test('Should include environment variables that exist in .env file.', (done) => { + const config = getConfig('web', new DotenvPlugin()) + + compile(config, (result) => { + expectResultsToContainReplacements(result, { TEST: 'hi' }) + + done() + }) + }) + + test('Should not expand variables by default', (done) => { + const config = getConfig('web', new DotenvPlugin({ path: envExpanded })) + + compile(config, (result) => { + expectResultsToContainReplacements(result, { + NODE_ENV: 'test', + BASIC: 'basic', + BASIC_EXPAND: '$BASIC', + MACHINE: 'machine_env', + MACHINE_EXPAND: '$MACHINE', + UNDEFINED_EXPAND: '$UNDEFINED_ENV_KEY', + // eslint-disable-next-line + ESCAPED_EXPAND: '\\\\$ESCAPED', + MONGOLAB_DATABASE: 'heroku_db', + MONGOLAB_USER: 'username', + MONGOLAB_PASSWORD: 'password', + MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', + MONGOLAB_PORT: '12345', + // eslint-disable-next-line + MONGOLAB_URI: 'mongodb://${MONGOLAB_USER}:${MONGOLAB_PASSWORD}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', + // eslint-disable-next-line + MONGOLAB_USER_RECURSIVELY: '${MONGOLAB_USER}:${MONGOLAB_PASSWORD}', + // eslint-disable-next-line + MONGOLAB_URI_RECURSIVELY: 'mongodb://${MONGOLAB_USER_RECURSIVELY}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', + WITHOUT_CURLY_BRACES_URI: 'mongodb://$MONGOLAB_USER:$MONGOLAB_PASSWORD@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE', + WITHOUT_CURLY_BRACES_USER_RECURSIVELY: '$MONGOLAB_USER:$MONGOLAB_PASSWORD', + WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://$MONGOLAB_USER_RECURSIVELY@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE' + }) + + done() + }) + }) + + test('Should expand variables when configured', (done) => { + const config = getConfig( + 'web', + new DotenvPlugin({ path: envExpanded, expand: true }) + ) + + compile(config, (result) => { + expectResultsToContainReplacements(result, { + NODE_ENV: 'test', + BASIC: 'basic', + BASIC_EXPAND: 'basic', + MACHINE: 'machine_env', + MACHINE_EXPAND: 'machine_env', + UNDEFINED_EXPAND: '', + // eslint-disable-next-line + ESCAPED_EXPAND: '\$ESCAPED', + MONGOLAB_DATABASE: 'heroku_db', + MONGOLAB_USER: 'username', + MONGOLAB_PASSWORD: 'password', + MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', + MONGOLAB_PORT: '12345', + MONGOLAB_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', + MONGOLAB_USER_RECURSIVELY: 'username:password', + MONGOLAB_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', + WITHOUT_CURLY_BRACES_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', + WITHOUT_CURLY_BRACES_USER_RECURSIVELY: 'username:password', + WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db' + }) + + done() + }) + }) + }) + describe('process.env stubbing', () => { const expectToBeStubbed = (result) => { - expect(result).toMatch('const test = "testing"') - expect(result).toMatch('const test2 = "MISSING_ENV_VAR".TEST2') - expect(result).toMatch('const nodeEnv = "development"') - expect(result).toMatch('const mongolabUser = "MISSING_ENV_VAR".MONGOLAB_USER') + expect(result).toMatch('const TEST = "testing"') + expect(result).toMatch('const TEST2 = "MISSING_ENV_VAR".TEST2') + expect(result).toMatch('const NODE_ENV = "development"') + expect(result).toMatch('const MONGOLAB_USER = "MISSING_ENV_VAR".MONGOLAB_USER') } const expectNotToBeStubbed = (result) => { - expect(result).toMatch('const test = "testing"') - expect(result).toMatch('const test2 = process.env.TEST2') - expect(result).toMatch('const nodeEnv = "development"') - expect(result).toMatch('const mongolabUser = process.env.MONGOLAB_USER') + expect(result).toMatch('const TEST = "testing"') + expect(result).toMatch('const TEST2 = process.env.TEST2') + expect(result).toMatch('const NODE_ENV = "development"') + expect(result).toMatch('const MONGOLAB_USER = process.env.MONGOLAB_USER') } const plugin = new DotenvPlugin({ path: envSimple }) @@ -66,10 +176,6 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { ['electron9-main', getConfig('electron9-main', plugin), false] ] - beforeEach(() => { - rmdirSync(resolve(__dirname, 'output'), { recursive: true }) - }) - test.each(cases)('%s', (target, config, shouldStub, done) => { webpack(config, (err, stats) => { expect(err).toBeNull() diff --git a/test/fixtures/index.js b/test/fixtures/index.js index a5c8c0f..d4db25a 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -1,12 +1,25 @@ /* eslint-disable no-unused-vars */ -const test = process.env.TEST -const test2 = process.env.TEST2 -const nodeEnv = process.env.NODE_ENV -const mongolabUser = process.env.MONGOLAB_USER -export default { - test, - test2, - nodeEnv, - mongolabUser -} +// Basic +const TEST = process.env.TEST +const TEST2 = process.env.TEST2 + +// Expanded +const NODE_ENV = process.env.NODE_ENV +const BASIC = process.env.BASIC +const BASIC_EXPAND = process.env.BASIC_EXPAND +const MACHINE = process.env.MACHINE +const MACHINE_EXPAND = process.env.MACHINE_EXPAND +const UNDEFINED_EXPAND = process.env.UNDEFINED_EXPAND +const ESCAPED_EXPAND = process.env.ESCAPED_EXPAND +const MONGOLAB_DATABASE = process.env.MONGOLAB_DATABASE +const MONGOLAB_USER = process.env.MONGOLAB_USER +const MONGOLAB_PASSWORD = process.env.MONGOLAB_PASSWORD +const MONGOLAB_DOMAIN = process.env.MONGOLAB_DOMAIN +const MONGOLAB_PORT = process.env.MONGOLAB_PORT +const MONGOLAB_URI = process.env.MONGOLAB_URI +const MONGOLAB_USER_RECURSIVELY = process.env.MONGOLAB_USER_RECURSIVELY +const MONGOLAB_URI_RECURSIVELY = process.env.MONGOLAB_URI_RECURSIVELY +const WITHOUT_CURLY_BRACES_URI = process.env.WITHOUT_CURLY_BRACES_URI +const WITHOUT_CURLY_BRACES_USER_RECURSIVELY = process.env.WITHOUT_CURLY_BRACES_USER_RECURSIVELY +const WITHOUT_CURLY_BRACES_URI_RECURSIVELY = process.env.WITHOUT_CURLY_BRACES_URI_RECURSIVELY From cc55ac53dc0e7981a3b0a44a7635d76f7ff7d2e2 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 10:42:18 +0100 Subject: [PATCH 06/25] move more tests --- test/compiler.test.js | 185 +++++++++++++++++++++++++++++++++--------- 1 file changed, 145 insertions(+), 40 deletions(-) diff --git a/test/compiler.test.js b/test/compiler.test.js index df1a89e..f334411 100644 --- a/test/compiler.test.js +++ b/test/compiler.test.js @@ -1,4 +1,4 @@ -/* global jest, describe, test, expect, beforeEach */ +/* global jest, describe, test, expect, beforeAll, beforeEach */ const { resolve } = require('path') const webpack = require('webpack') @@ -20,6 +20,12 @@ const envSystemvarsExample = resolve(__dirname, './envs/.systemvars.example') const envExpanded = resolve(__dirname, './envs/.expanded') const envDefaults = resolve(__dirname, './envs/.defaults') +const emptyResult = {} +const defaultEnvResult = { TEST: 'hi' } +const simpleResult = { TEST: 'testing' } +const defaultsResult = { TEST: 'hi', TEST2: 'hidefault' } +const defaultsResult2 = { TEST: 'hi', TEST2: 'youcanseethis' } + const getConfig = (target, plugin) => ({ mode: 'development', devtool: false, @@ -45,9 +51,15 @@ const compile = (config, callback) => { }) } -const expectResultsToContainReplacements = (result, env) => { - Object.entries(env).forEach(([key, value]) => { - expect(result).toMatch(`const ${key} = "${value}"`) +const expectResultsToContainReplacements = (plugin, env, done) => { + const config = getConfig('web', plugin) + + compile(config, (result) => { + Object.entries(env).forEach(([key, value]) => { + expect(result).toMatch(`const ${key} = "${value}"`) + }) + + done?.() }) } @@ -56,7 +68,13 @@ const versions = [ ['Dist', Dist.default] ] +beforeAll(() => { + global.console.warn = jest.fn() +}) + beforeEach(() => { + jest.resetAllMocks() + rmdirSync(resolve(__dirname, 'output'), { recursive: true }) }) @@ -71,46 +89,40 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { describe('Defaults', () => { test('Should include environment variables that exist in .env file.', (done) => { - const config = getConfig('web', new DotenvPlugin()) - - compile(config, (result) => { - expectResultsToContainReplacements(result, { TEST: 'hi' }) - - done() - }) + expectResultsToContainReplacements(new DotenvPlugin(), defaultEnvResult, done) }) test('Should not expand variables by default', (done) => { - const config = getConfig('web', new DotenvPlugin({ path: envExpanded })) + const expected = { + NODE_ENV: 'test', + BASIC: 'basic', + BASIC_EXPAND: '$BASIC', + MACHINE: 'machine_env', + MACHINE_EXPAND: '$MACHINE', + UNDEFINED_EXPAND: '$UNDEFINED_ENV_KEY', + // eslint-disable-next-line + ESCAPED_EXPAND: '\\\\$ESCAPED', + MONGOLAB_DATABASE: 'heroku_db', + MONGOLAB_USER: 'username', + MONGOLAB_PASSWORD: 'password', + MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', + MONGOLAB_PORT: '12345', + // eslint-disable-next-line + MONGOLAB_URI: 'mongodb://${MONGOLAB_USER}:${MONGOLAB_PASSWORD}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', + // eslint-disable-next-line + MONGOLAB_USER_RECURSIVELY: '${MONGOLAB_USER}:${MONGOLAB_PASSWORD}', + // eslint-disable-next-line + MONGOLAB_URI_RECURSIVELY: 'mongodb://${MONGOLAB_USER_RECURSIVELY}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', + WITHOUT_CURLY_BRACES_URI: 'mongodb://$MONGOLAB_USER:$MONGOLAB_PASSWORD@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE', + WITHOUT_CURLY_BRACES_USER_RECURSIVELY: '$MONGOLAB_USER:$MONGOLAB_PASSWORD', + WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://$MONGOLAB_USER_RECURSIVELY@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE' + } - compile(config, (result) => { - expectResultsToContainReplacements(result, { - NODE_ENV: 'test', - BASIC: 'basic', - BASIC_EXPAND: '$BASIC', - MACHINE: 'machine_env', - MACHINE_EXPAND: '$MACHINE', - UNDEFINED_EXPAND: '$UNDEFINED_ENV_KEY', - // eslint-disable-next-line - ESCAPED_EXPAND: '\\\\$ESCAPED', - MONGOLAB_DATABASE: 'heroku_db', - MONGOLAB_USER: 'username', - MONGOLAB_PASSWORD: 'password', - MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', - MONGOLAB_PORT: '12345', - // eslint-disable-next-line - MONGOLAB_URI: 'mongodb://${MONGOLAB_USER}:${MONGOLAB_PASSWORD}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', - // eslint-disable-next-line - MONGOLAB_USER_RECURSIVELY: '${MONGOLAB_USER}:${MONGOLAB_PASSWORD}', - // eslint-disable-next-line - MONGOLAB_URI_RECURSIVELY: 'mongodb://${MONGOLAB_USER_RECURSIVELY}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', - WITHOUT_CURLY_BRACES_URI: 'mongodb://$MONGOLAB_USER:$MONGOLAB_PASSWORD@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE', - WITHOUT_CURLY_BRACES_USER_RECURSIVELY: '$MONGOLAB_USER:$MONGOLAB_PASSWORD', - WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://$MONGOLAB_USER_RECURSIVELY@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE' - }) - - done() - }) + expectResultsToContainReplacements( + new DotenvPlugin({ path: envExpanded }), + expected, + done + ) }) test('Should expand variables when configured', (done) => { @@ -147,6 +159,99 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { }) }) + describe('Simple configuration', () => { + test('Should load enviornment variables when they exist in the .env file.', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envExpanded }), + simpleResult, + done + ) + }) + + test('Should be an empty object when no environment variables exist in .env file.', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: false }), + emptyResult, + done + ) + }) + + test('Should recognize safe-mode', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ safe: true }), + defaultEnvResult, + done + ) + }) + + test('Should fail when not passing safe-mode', (done) => { + const config = getConfig('web', new DotenvPlugin({ path: envEmpty, safe: true })) + + webpack(config, (err) => { + expect(err.message).toBe('Missing environment variable: TEST') + + done() + }) + }) + }) + + describe('Safe configuration', () => { + test('Should load successfully if variables defined', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envEmpty, safe: envEmptyExample }), + emptyResult + ) + + expectResultsToContainReplacements( + new DotenvPlugin({ path: envSimple, safe: envSimpleExample }), + simpleResult, + done + ) + }) + + test('Should fail if env does not match sample.', (done) => { + const config = getConfig( + 'web', + new DotenvPlugin({ path: envEmpty, safe: envSimpleExample }) + ) + + webpack(config, (err) => { + expect(err.message).toBe('Missing environment variable: TEST') + + done() + }) + }) + }) + + describe('Defaults configuration', () => { + test('should support default configurations', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ defaults: true }), + defaultsResult, + done + ) + }) + + test('should support string configurations', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ defaults: envDefaults }), + defaultsResult2, + done + ) + }) + + test('Should display warning when default cannot be loaded', (done) => { + const envDefaultName = '.does.not.exist' + expectResultsToContainReplacements( + new DotenvPlugin({ defaults: envDefaultName }), + defaultEnvResult, + done + ) + + expect(global.console.warn).toHaveBeenCalledWith(`Failed to load ${envDefaultName}.`) + }) + }) + describe('process.env stubbing', () => { const expectToBeStubbed = (result) => { expect(result).toMatch('const TEST = "testing"') From 81869a01e5c02a2b97241bba2bd50ba7637d7b3d Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 11:43:23 +0100 Subject: [PATCH 07/25] replace old tests --- test/compiler.test.js | 304 ------------------- test/fixtures/index.js | 3 + test/index.test.js | 674 ++++++++++++++++++++++++++--------------- 3 files changed, 434 insertions(+), 547 deletions(-) delete mode 100644 test/compiler.test.js diff --git a/test/compiler.test.js b/test/compiler.test.js deleted file mode 100644 index f334411..0000000 --- a/test/compiler.test.js +++ /dev/null @@ -1,304 +0,0 @@ -/* global jest, describe, test, expect, beforeAll, beforeEach */ - -const { resolve } = require('path') -const webpack = require('webpack') -const { readFileSync, rmdirSync } = require('fs') - -const Src = require('../src') -const Dist = require('../dist') - -const envEmpty = resolve(__dirname, './envs/.empty') -const envEmptyExample = resolve(__dirname, './envs/.empty.example') -const envSimple = resolve(__dirname, './envs/.simple') -const envSimpleExample = resolve(__dirname, './envs/.simple.example') -const envOneEmpty = resolve(__dirname, './envs/.oneempty') -const envOneEmptyExample = resolve(__dirname, './envs/.oneempty.example') -const envMissingOne = resolve(__dirname, './envs/.missingone') -const envMissingOneExample = resolve(__dirname, './envs/.missingone.example') -const envSystemvars = resolve(__dirname, './envs/.systemvars') -const envSystemvarsExample = resolve(__dirname, './envs/.systemvars.example') -const envExpanded = resolve(__dirname, './envs/.expanded') -const envDefaults = resolve(__dirname, './envs/.defaults') - -const emptyResult = {} -const defaultEnvResult = { TEST: 'hi' } -const simpleResult = { TEST: 'testing' } -const defaultsResult = { TEST: 'hi', TEST2: 'hidefault' } -const defaultsResult2 = { TEST: 'hi', TEST2: 'youcanseethis' } - -const getConfig = (target, plugin) => ({ - mode: 'development', - devtool: false, - target, - entry: resolve(__dirname, './fixtures/index'), - output: { - path: resolve(__dirname, `./output/${target}`) - }, - plugins: [plugin] -}) - -const compile = (config, callback) => { - webpack(config, (err, stats) => { - expect(err).toBeNull() - expect(stats.compilation.errors).toHaveLength(0) - - const result = readFileSync( - resolve(__dirname, `./output/${config.target}/main.js`), - { encoding: 'utf-8' } - ) - - callback(result) - }) -} - -const expectResultsToContainReplacements = (plugin, env, done) => { - const config = getConfig('web', plugin) - - compile(config, (result) => { - Object.entries(env).forEach(([key, value]) => { - expect(result).toMatch(`const ${key} = "${value}"`) - }) - - done?.() - }) -} - -const versions = [ - ['Source', Src.default], - ['Dist', Dist.default] -] - -beforeAll(() => { - global.console.warn = jest.fn() -}) - -beforeEach(() => { - jest.resetAllMocks() - - rmdirSync(resolve(__dirname, 'output'), { recursive: true }) -}) - -describe.each(versions)('%s', (_, DotenvPlugin) => { - test('Should be an function.', () => { - expect(typeof DotenvPlugin).toEqual('function') - }) - - test('Should return a instance of Dotenv.', () => { - expect((new DotenvPlugin()).constructor.name).toEqual('Dotenv') - }) - - describe('Defaults', () => { - test('Should include environment variables that exist in .env file.', (done) => { - expectResultsToContainReplacements(new DotenvPlugin(), defaultEnvResult, done) - }) - - test('Should not expand variables by default', (done) => { - const expected = { - NODE_ENV: 'test', - BASIC: 'basic', - BASIC_EXPAND: '$BASIC', - MACHINE: 'machine_env', - MACHINE_EXPAND: '$MACHINE', - UNDEFINED_EXPAND: '$UNDEFINED_ENV_KEY', - // eslint-disable-next-line - ESCAPED_EXPAND: '\\\\$ESCAPED', - MONGOLAB_DATABASE: 'heroku_db', - MONGOLAB_USER: 'username', - MONGOLAB_PASSWORD: 'password', - MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', - MONGOLAB_PORT: '12345', - // eslint-disable-next-line - MONGOLAB_URI: 'mongodb://${MONGOLAB_USER}:${MONGOLAB_PASSWORD}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', - // eslint-disable-next-line - MONGOLAB_USER_RECURSIVELY: '${MONGOLAB_USER}:${MONGOLAB_PASSWORD}', - // eslint-disable-next-line - MONGOLAB_URI_RECURSIVELY: 'mongodb://${MONGOLAB_USER_RECURSIVELY}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', - WITHOUT_CURLY_BRACES_URI: 'mongodb://$MONGOLAB_USER:$MONGOLAB_PASSWORD@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE', - WITHOUT_CURLY_BRACES_USER_RECURSIVELY: '$MONGOLAB_USER:$MONGOLAB_PASSWORD', - WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://$MONGOLAB_USER_RECURSIVELY@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE' - } - - expectResultsToContainReplacements( - new DotenvPlugin({ path: envExpanded }), - expected, - done - ) - }) - - test('Should expand variables when configured', (done) => { - const config = getConfig( - 'web', - new DotenvPlugin({ path: envExpanded, expand: true }) - ) - - compile(config, (result) => { - expectResultsToContainReplacements(result, { - NODE_ENV: 'test', - BASIC: 'basic', - BASIC_EXPAND: 'basic', - MACHINE: 'machine_env', - MACHINE_EXPAND: 'machine_env', - UNDEFINED_EXPAND: '', - // eslint-disable-next-line - ESCAPED_EXPAND: '\$ESCAPED', - MONGOLAB_DATABASE: 'heroku_db', - MONGOLAB_USER: 'username', - MONGOLAB_PASSWORD: 'password', - MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', - MONGOLAB_PORT: '12345', - MONGOLAB_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', - MONGOLAB_USER_RECURSIVELY: 'username:password', - MONGOLAB_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', - WITHOUT_CURLY_BRACES_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', - WITHOUT_CURLY_BRACES_USER_RECURSIVELY: 'username:password', - WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db' - }) - - done() - }) - }) - }) - - describe('Simple configuration', () => { - test('Should load enviornment variables when they exist in the .env file.', (done) => { - expectResultsToContainReplacements( - new DotenvPlugin({ path: envExpanded }), - simpleResult, - done - ) - }) - - test('Should be an empty object when no environment variables exist in .env file.', (done) => { - expectResultsToContainReplacements( - new DotenvPlugin({ path: false }), - emptyResult, - done - ) - }) - - test('Should recognize safe-mode', (done) => { - expectResultsToContainReplacements( - new DotenvPlugin({ safe: true }), - defaultEnvResult, - done - ) - }) - - test('Should fail when not passing safe-mode', (done) => { - const config = getConfig('web', new DotenvPlugin({ path: envEmpty, safe: true })) - - webpack(config, (err) => { - expect(err.message).toBe('Missing environment variable: TEST') - - done() - }) - }) - }) - - describe('Safe configuration', () => { - test('Should load successfully if variables defined', (done) => { - expectResultsToContainReplacements( - new DotenvPlugin({ path: envEmpty, safe: envEmptyExample }), - emptyResult - ) - - expectResultsToContainReplacements( - new DotenvPlugin({ path: envSimple, safe: envSimpleExample }), - simpleResult, - done - ) - }) - - test('Should fail if env does not match sample.', (done) => { - const config = getConfig( - 'web', - new DotenvPlugin({ path: envEmpty, safe: envSimpleExample }) - ) - - webpack(config, (err) => { - expect(err.message).toBe('Missing environment variable: TEST') - - done() - }) - }) - }) - - describe('Defaults configuration', () => { - test('should support default configurations', (done) => { - expectResultsToContainReplacements( - new DotenvPlugin({ defaults: true }), - defaultsResult, - done - ) - }) - - test('should support string configurations', (done) => { - expectResultsToContainReplacements( - new DotenvPlugin({ defaults: envDefaults }), - defaultsResult2, - done - ) - }) - - test('Should display warning when default cannot be loaded', (done) => { - const envDefaultName = '.does.not.exist' - expectResultsToContainReplacements( - new DotenvPlugin({ defaults: envDefaultName }), - defaultEnvResult, - done - ) - - expect(global.console.warn).toHaveBeenCalledWith(`Failed to load ${envDefaultName}.`) - }) - }) - - describe('process.env stubbing', () => { - const expectToBeStubbed = (result) => { - expect(result).toMatch('const TEST = "testing"') - expect(result).toMatch('const TEST2 = "MISSING_ENV_VAR".TEST2') - expect(result).toMatch('const NODE_ENV = "development"') - expect(result).toMatch('const MONGOLAB_USER = "MISSING_ENV_VAR".MONGOLAB_USER') - } - - const expectNotToBeStubbed = (result) => { - expect(result).toMatch('const TEST = "testing"') - expect(result).toMatch('const TEST2 = process.env.TEST2') - expect(result).toMatch('const NODE_ENV = "development"') - expect(result).toMatch('const MONGOLAB_USER = process.env.MONGOLAB_USER') - } - - const plugin = new DotenvPlugin({ path: envSimple }) - const cases = [ - ['web', getConfig('web', plugin), true], - ['es5', getConfig('es5', plugin), true], - ['es2020', getConfig('es2020', plugin), true], - ['electron-renderer', getConfig('electron-renderer', plugin), true], - ['electron9-renderer', getConfig('electron9-renderer', plugin), true], - ['electron-preload', getConfig('electron-preload', plugin), true], - ['node', getConfig('node', plugin), false], - ['node14', getConfig('node14', plugin), false], - ['electron-main', getConfig('electron-main', plugin), false], - ['electron9-main', getConfig('electron9-main', plugin), false] - ] - - test.each(cases)('%s', (target, config, shouldStub, done) => { - webpack(config, (err, stats) => { - expect(err).toBeNull() - expect(stats.compilation.errors).toHaveLength(0) - - const result = readFileSync( - resolve(__dirname, `./output/${target}/main.js`), - { encoding: 'utf-8' } - ) - - if (shouldStub) { - expectToBeStubbed(result) - } else { - expectNotToBeStubbed(result) - } - - done() - }) - }) - }) -}) diff --git a/test/fixtures/index.js b/test/fixtures/index.js index d4db25a..60730f2 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -4,6 +4,9 @@ const TEST = process.env.TEST const TEST2 = process.env.TEST2 +// System +const PATH = process.env.PATH + // Expanded const NODE_ENV = process.env.NODE_ENV const BASIC = process.env.BASIC diff --git a/test/index.test.js b/test/index.test.js index eb4f7fb..d6522a4 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -1,310 +1,498 @@ -/* global jest, describe, test, expect, beforeEach */ +/* global jest, describe, test, expect, afterEach, beforeAll, beforeEach */ -// Tests suite -const path = require('path') +const { resolve } = require('path') +const { createHash } = require('crypto') +const webpack = require('webpack') +const { readFileSync, rmdirSync } = require('fs') -// The star of the show const Src = require('../src') const Dist = require('../dist') -const envEmpty = path.resolve(__dirname, './envs/.empty') -const envEmptyExample = path.resolve(__dirname, './envs/.empty.example') -const envSimple = path.resolve(__dirname, './envs/.simple') -const envSimpleExample = path.resolve(__dirname, './envs/.simple.example') -const envOneEmpty = path.resolve(__dirname, './envs/.oneempty') -const envOneEmptyExample = path.resolve(__dirname, './envs/.oneempty.example') -const envMissingOne = path.resolve(__dirname, './envs/.missingone') -const envMissingOneExample = path.resolve(__dirname, './envs/.missingone.example') -const envSystemvars = path.resolve(__dirname, './envs/.systemvars') -const envSystemvarsExample = path.resolve(__dirname, './envs/.systemvars.example') -const envExpanded = path.resolve(__dirname, './envs/.expanded') -const envDefaults = path.resolve(__dirname, './envs/.defaults') - -const buildExpectation = (obj) => { - const raw = Object.keys(obj).reduce((all, key) => { - all[`process.env.${key}`] = JSON.stringify(obj[key]) - return all - }, {}) - - raw['process.env'] = '{}' - - return raw +const envEmpty = resolve(__dirname, './envs/.empty') +const envEmptyExample = resolve(__dirname, './envs/.empty.example') +const envSimple = resolve(__dirname, './envs/.simple') +const envSimpleExample = resolve(__dirname, './envs/.simple.example') +const envOneEmpty = resolve(__dirname, './envs/.oneempty') +const envOneEmptyExample = resolve(__dirname, './envs/.oneempty.example') +const envMissingOne = resolve(__dirname, './envs/.missingone') +const envMissingOneExample = resolve(__dirname, './envs/.missingone.example') +const envSystemvars = resolve(__dirname, './envs/.systemvars') +const envSystemvarsExample = resolve(__dirname, './envs/.systemvars.example') +const envExpanded = resolve(__dirname, './envs/.expanded') +const envDefaults = resolve(__dirname, './envs/.defaults') + +const emptyResult = {} +const defaultEnvResult = { TEST: 'hi' } +const simpleResult = { TEST: 'testing' } +const defaultsResult = { TEST: 'hi', TEST2: 'hidefault' } +const defaultsResult2 = { TEST: 'hi', TEST2: 'youcanseethis' } +const oneEmptyResult = { TEST: '', TEST2: 'Hello' } +const missingOneResult = { TEST2: 'Hello' } + +const hash = (str) => createHash('md5').update(str).digest('hex').slice(0, 8) + +const getConfig = (target, plugin) => ({ + mode: 'development', + devtool: false, + target, + entry: resolve(__dirname, './fixtures/index'), + output: { + path: resolve(__dirname, `./output/${hash(expect.getState().currentTestName)}`) + }, + plugins: [plugin] +}) + +const compile = (config, callback) => { + webpack(config, (err, stats) => { + expect(err).toBeNull() + expect(stats.compilation.errors).toHaveLength(0) + + const result = readFileSync( + resolve(__dirname, config.output.path, 'main.js'), + { encoding: 'utf-8' } + ) + + callback(result) + }) } -const envDefJson = buildExpectation({ TEST: 'hi' }) -const envEmptyJson = buildExpectation({}) -const envSimpleJson = buildExpectation({ TEST: 'testing' }) -const envOneEmptyJson = buildExpectation({ TEST: '', TEST2: 'Hello' }) -const envMissingOneJson = buildExpectation({ TEST2: 'Hello' }) -const envDefaultsJson = buildExpectation({ TEST: 'hi', TEST2: 'hidefault' }) -const envDefaultsJson2 = buildExpectation({ TEST: 'hi', TEST2: 'youcanseethis' }) - -/* -NODE_ENV=test -BASIC=basic -BASIC_EXPAND=$BASIC -MACHINE=machine_env -MACHINE_EXPAND=$MACHINE -UNDEFINED_EXPAND=$UNDEFINED_ENV_KEY -ESCAPED_EXPAND=\$ESCAPED -MONGOLAB_DATABASE=heroku_db -MONGOLAB_USER=username -MONGOLAB_PASSWORD=password -MONGOLAB_DOMAIN=abcd1234.mongolab.com -MONGOLAB_PORT=12345 -MONGOLAB_URI=mongodb://${MONGOLAB_USER}:${MONGOLAB_PASSWORD}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE} - -MONGOLAB_USER_RECURSIVELY=${MONGOLAB_USER}:${MONGOLAB_PASSWORD} -MONGOLAB_URI_RECURSIVELY=mongodb://${MONGOLAB_USER_RECURSIVELY}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE} - -WITHOUT_CURLY_BRACES_URI=mongodb://$MONGOLAB_USER:$MONGOLAB_PASSWORD@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE -WITHOUT_CURLY_BRACES_USER_RECURSIVELY=$MONGOLAB_USER:$MONGOLAB_PASSWORD -WITHOUT_CURLY_BRACES_URI_RECURSIVELY=mongodb://$MONGOLAB_USER_RECURSIVELY@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE -*/ -const envExpandedNotJson = buildExpectation({ - NODE_ENV: 'test', - BASIC: 'basic', - BASIC_EXPAND: '$BASIC', - MACHINE: 'machine_env', - MACHINE_EXPAND: '$MACHINE', - UNDEFINED_EXPAND: '$UNDEFINED_ENV_KEY', - // eslint-disable-next-line - ESCAPED_EXPAND: '\\$ESCAPED', - MONGOLAB_DATABASE: 'heroku_db', - MONGOLAB_USER: 'username', - MONGOLAB_PASSWORD: 'password', - MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', - MONGOLAB_PORT: '12345', - // eslint-disable-next-line - MONGOLAB_URI: 'mongodb://${MONGOLAB_USER}:${MONGOLAB_PASSWORD}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', - // eslint-disable-next-line - MONGOLAB_USER_RECURSIVELY: '${MONGOLAB_USER}:${MONGOLAB_PASSWORD}', - // eslint-disable-next-line - MONGOLAB_URI_RECURSIVELY: 'mongodb://${MONGOLAB_USER_RECURSIVELY}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', - WITHOUT_CURLY_BRACES_URI: 'mongodb://$MONGOLAB_USER:$MONGOLAB_PASSWORD@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE', - WITHOUT_CURLY_BRACES_USER_RECURSIVELY: '$MONGOLAB_USER:$MONGOLAB_PASSWORD', - WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://$MONGOLAB_USER_RECURSIVELY@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE' +const expectResultsToContainReplacements = (plugin, env, done) => { + const config = getConfig('web', plugin) + + compile(config, (result) => { + Object.entries(env).forEach(([key, value]) => { + expect(result).toMatch(`const ${key} = "${value}"`) + }) + + done?.() + }) +} + +const versions = [ + ['Source', Src.default], + ['Dist', Dist.default] +] + +beforeAll(() => { + global.console.warn = jest.fn() }) -const envExpandedJson = buildExpectation({ - NODE_ENV: 'test', - BASIC: 'basic', - BASIC_EXPAND: 'basic', - MACHINE: 'machine_env', - MACHINE_EXPAND: 'machine_env', - UNDEFINED_EXPAND: '', - // eslint-disable-next-line - ESCAPED_EXPAND: '\$ESCAPED', - MONGOLAB_DATABASE: 'heroku_db', - MONGOLAB_USER: 'username', - MONGOLAB_PASSWORD: 'password', - MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', - MONGOLAB_PORT: '12345', - MONGOLAB_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', - MONGOLAB_USER_RECURSIVELY: 'username:password', - MONGOLAB_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', - WITHOUT_CURLY_BRACES_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', - WITHOUT_CURLY_BRACES_USER_RECURSIVELY: 'username:password', - WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db' + +beforeEach(() => { + jest.resetAllMocks() + + rmdirSync(resolve(__dirname, `output/${hash(expect.getState().currentTestName)}`), { recursive: true }) }) -// const consoleSpy = jest.spyOn(console, 'warn') -global.console.warn = jest.fn() +describe.each(versions)('%s', (_, DotenvPlugin) => { + test('Should be an function.', () => { + expect(typeof DotenvPlugin).toEqual('function') + }) -function runTests (Obj, name) { - function envTest (config) { - return new Obj(config).definitions - } + test('Should return a instance of Dotenv.', () => { + expect((new DotenvPlugin()).constructor.name).toEqual('Dotenv') + }) - /** @test {Dotenv} **/ - describe(name, () => { - beforeEach(() => { - global.console.warn.mockClear() + describe('Defaults', () => { + test('Should include environment variables that exist in .env file.', (done) => { + expectResultsToContainReplacements(new DotenvPlugin(), defaultEnvResult, done) }) - describe('Defaults', () => { - test('Should be an function.', () => { - expect(typeof Obj).toEqual('function') - }) + test('Should not expand variables by default', (done) => { + const expected = { + NODE_ENV: 'test', + BASIC: 'basic', + BASIC_EXPAND: '$BASIC', + MACHINE: 'machine_env', + MACHINE_EXPAND: '$MACHINE', + UNDEFINED_EXPAND: '$UNDEFINED_ENV_KEY', + // eslint-disable-next-line + ESCAPED_EXPAND: '\\\\$ESCAPED', + MONGOLAB_DATABASE: 'heroku_db', + MONGOLAB_USER: 'username', + MONGOLAB_PASSWORD: 'password', + MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', + MONGOLAB_PORT: '12345', + // eslint-disable-next-line + MONGOLAB_URI: 'mongodb://${MONGOLAB_USER}:${MONGOLAB_PASSWORD}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', + // eslint-disable-next-line + MONGOLAB_USER_RECURSIVELY: '${MONGOLAB_USER}:${MONGOLAB_PASSWORD}', + // eslint-disable-next-line + MONGOLAB_URI_RECURSIVELY: 'mongodb://${MONGOLAB_USER_RECURSIVELY}@${MONGOLAB_DOMAIN}:${MONGOLAB_PORT}/${MONGOLAB_DATABASE}', + WITHOUT_CURLY_BRACES_URI: 'mongodb://$MONGOLAB_USER:$MONGOLAB_PASSWORD@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE', + WITHOUT_CURLY_BRACES_USER_RECURSIVELY: '$MONGOLAB_USER:$MONGOLAB_PASSWORD', + WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://$MONGOLAB_USER_RECURSIVELY@$MONGOLAB_DOMAIN:$MONGOLAB_PORT/$MONGOLAB_DATABASE' + } + + expectResultsToContainReplacements( + new DotenvPlugin({ path: envExpanded }), + expected, + done + ) + }) - // @todo - This one isn't a great test, but it wasn't really working for me. - test('Should return a instance of DefinePlugin.', () => { - expect(typeof envTest()).toEqual('object') + test('Should expand variables when configured', (done) => { + const config = getConfig( + 'web', + new DotenvPlugin({ path: envExpanded, expand: true }) + ) + + compile(config, (result) => { + expectResultsToContainReplacements(result, { + NODE_ENV: 'test', + BASIC: 'basic', + BASIC_EXPAND: 'basic', + MACHINE: 'machine_env', + MACHINE_EXPAND: 'machine_env', + UNDEFINED_EXPAND: '', + // eslint-disable-next-line + ESCAPED_EXPAND: '\$ESCAPED', + MONGOLAB_DATABASE: 'heroku_db', + MONGOLAB_USER: 'username', + MONGOLAB_PASSWORD: 'password', + MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', + MONGOLAB_PORT: '12345', + MONGOLAB_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', + MONGOLAB_USER_RECURSIVELY: 'username:password', + MONGOLAB_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', + WITHOUT_CURLY_BRACES_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', + WITHOUT_CURLY_BRACES_USER_RECURSIVELY: 'username:password', + WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db' + }) + + done() }) + }) + }) - test('Should include environment variables that exist in .env file.', () => { - expect(envTest()).toEqual(envDefJson) - }) + describe('Simple configuration', () => { + test('Should load enviornment variables when they exist in the .env file.', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envExpanded }), + simpleResult, + done + ) + }) - test('Should not expand variables by default', () => { - expect(envTest({ path: envExpanded })).toEqual(envExpandedNotJson) - }) + test('Should be an empty object when no environment variables exist in .env file.', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: false }), + emptyResult, + done + ) + }) - test('Should expand variables when configured', () => { - expect(envTest({ path: envExpanded, expand: true })).toEqual(envExpandedJson) - }) + test('Should recognize safe-mode', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ safe: true }), + defaultEnvResult, + done + ) }) - describe('Simple configuration', () => { - test('Should load enviornment variables when they exist in the .env file.', () => { - expect(envTest({ path: envSimple })).toEqual(envSimpleJson) - }) + test('Should fail when not passing safe-mode', (done) => { + const config = getConfig('web', new DotenvPlugin({ path: envEmpty, safe: true })) - test('Should be an empty object when no environment variables exist in .env file.', () => { - expect(envTest({ path: false })).toEqual(envEmptyJson) - }) + webpack(config, (err) => { + expect(err.message).toBe('Missing environment variable: TEST') - test('Should recognize safe-mode', () => { - expect(envTest({ safe: true })).toEqual(envDefJson) + done() }) + }) + }) - test('Should fail when not passing safe-mode', () => { - try { - envTest({ path: envEmpty, safe: true }) - throw new Error('Should not get here') - } catch (err) { - expect(err.message).toEqual('Missing environment variable: TEST') - } - }) + describe('Safe configuration', () => { + test('Should load successfully if variables defined', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envEmpty, safe: envEmptyExample }), + emptyResult + ) + + expectResultsToContainReplacements( + new DotenvPlugin({ path: envSimple, safe: envSimpleExample }), + simpleResult, + done + ) }) - describe('Safe configuration', () => { - test('Should load successfully if variables defined', () => { - expect(envTest({ path: envEmpty, safe: envEmptyExample })).toEqual(envEmptyJson) - expect(envTest({ path: envSimple, safe: envSimpleExample })).toEqual(envSimpleJson) - }) + test('Should fail if env does not match sample.', (done) => { + const config = getConfig( + 'web', + new DotenvPlugin({ path: envEmpty, safe: envSimpleExample }) + ) - test('Should fail if env does not match sample.', () => { - try { - envTest({ path: envEmpty, safe: envSimpleExample }) - throw new Error('Should not get here') - } catch (err) { - expect(err.message).toEqual('Missing environment variable: TEST') - } + webpack(config, (err) => { + expect(err.message).toBe('Missing environment variable: TEST') + + done() }) }) + }) - describe('Defaults configuration', () => { - test('should support default configurations', () => { - expect(envTest({ defaults: true })).toEqual(envDefaultsJson) - }) + describe('Defaults configuration', () => { + test('should support default configurations', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ defaults: true }), + defaultsResult, + done + ) + }) - test('should support string configurations', () => { - expect(envTest({ defaults: envDefaults })).toEqual(envDefaultsJson2) - }) + test('should support string configurations', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ defaults: envDefaults }), + defaultsResult2, + done + ) + }) - test('Should display warning when default cannot be loaded', () => { - const envDefaultName = '.does.not.exist' - expect(envTest({ defaults: envDefaultName })).toEqual(envDefJson) - expect(global.console.warn).toHaveBeenCalledWith(`Failed to load ${envDefaultName}.`) - }) + test('Should display warning when default cannot be loaded', (done) => { + const envDefaultName = '.does.not.exist' + expectResultsToContainReplacements( + new DotenvPlugin({ defaults: envDefaultName }), + defaultEnvResult, + done + ) + + expect(global.console.warn).toHaveBeenCalledWith(`Failed to load ${envDefaultName}.`) }) + }) - describe('System variables', () => { - test('Should allow system env variables', () => { - const test = envTest({ path: envSimple, systemvars: true }) - const key = Object.keys(envSimpleJson)[0] - const value = envSimpleJson[key] - expect(test[key]).toEqual(value) - expect(Object.keys(test).length > Object.keys(envSimpleJson).length).toEqual(true) - }) + describe('System variables', () => { + const originalPath = process.env.PATH + beforeEach(() => { + process.env.PATH = '/usr/local/bin:/usr/local/sbin:' + }) + afterEach(() => { + process.env.PATH = originalPath + }) - test('should pass if the systemvar satisfies the requirement', () => { - const PATH = envTest({ safe: envSystemvarsExample, systemvars: true })['process.env.PATH'] - expect(typeof PATH).toEqual('string') - expect(PATH.indexOf('/') !== -1 || PATH.indexOf('\\') !== -1).toEqual(true) - }) + test('Should allow system env variables', (done) => { + const config = getConfig( + 'web', + new DotenvPlugin({ path: envSimple, systemvars: true }) + ) - test('should not allow local variables to override systemvars', () => { - expect(envTest({ path: envSystemvars, systemvars: true })['process.env.PATH2'] !== '""').toEqual(true) - }) + compile(config, (result) => { + expect(result).toMatch('const TEST = "testing"') + expect(result).toMatch('const PATH = "/usr/local/bin:/usr/local/sbin:') - test('Should give the highest priority for the system variables', () => { - process.env.TEST = 'production' - const test = envTest({ safe: true, systemvars: true, defaults: true }) - expect(test['process.env.TEST']).toEqual('"production"') - expect(test['process.env.TEST2']).toEqual('"hidefault"') - delete process.env.TEST + done() }) }) - describe('Empty variables', () => { - test('Should load fine (not-safe)', () => { - expect(envTest({ path: envOneEmpty })).toEqual(envOneEmptyJson) - }) + test('should pass if the systemvar satisfies the requirement', (done) => { + const config = getConfig( + 'web', + new DotenvPlugin({ safe: envSystemvarsExample, systemvars: true }) + ) - test('Should fail on safe mode', () => { - try { - envTest({ path: envOneEmpty, safe: envOneEmptyExample }) - throw new Error('Should not get here') - } catch (err) { - expect(err.message).toEqual('Missing environment variable: TEST') - } - }) + compile(config, (result) => { + expect(result).toMatch('const TEST = "hi"') + expect(result).toMatch(/const PATH = ".*[\\/].*"/) - test('Should succeed in safe mode if allowEmptyValues is true', () => { - expect(envTest({ path: envOneEmpty, safe: envOneEmptyExample, allowEmptyValues: true })).toEqual(envOneEmptyJson) + done() }) }) - describe('Missing a variable', () => { - test('Should load fine (not-safe)', () => { - expect(envTest({ path: envMissingOne })).toEqual(envMissingOneJson) - }) + test('should not allow local variables to override systemvars', (done) => { + const config = getConfig( + 'web', + new DotenvPlugin({ path: envSystemvars, systemvars: true }) + ) - test('Should fail on safe mode (if allowEmptyValues is false)', () => { - try { - envTest({ path: envMissingOne, safe: envMissingOneExample }) - throw new Error('Should not get here') - } catch (err) { - expect(err.message).toEqual('Missing environment variable: TEST') - } + compile(config, (result) => { + expect(result).toMatch('const TEST = "MISSING_ENV_VAR".TEST') + expect(result).not.toMatch('const PATH = ""') + + done() }) }) - describe('Deprecated configuration', () => { - test('Should use safe when safe and sample set', () => { - expect(envTest({ path: envSimple, safe: true, sample: envSimpleExample })).toEqual(envSimpleJson) - }) + test('Should give the highest priority for the system variables', (done) => { + process.env.TEST = 'production' - test('Should display deprecation warning by default', () => { - expect(envTest({ path: envSimple, safe: true, sample: envSimpleExample })).toEqual(envSimpleJson) - expect(global.console.warn).toHaveBeenCalled() + const config = getConfig( + 'web', + new DotenvPlugin({ safe: true, systemvars: true, defaults: true }) + ) + + compile(config, (result) => { + expect(result).toMatch('const TEST = "production"') + expect(result).toMatch('const TEST2 = "hidefault"') + + done() }) - test('Should not display deprecation warning when silent mode enabled', () => { - expect(envTest({ path: envSimple, safe: true, sample: envSimpleExample, silent: true })).toEqual(envSimpleJson) - expect(global.console.warn).toHaveBeenCalledTimes(0) + delete process.env.TEST + }) + }) + + describe('Empty variables', () => { + test('Should load fine (not-safe)', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envOneEmpty }), + oneEmptyResult, + done + ) + }) + + test('Should fail on safe mode', (done) => { + const config = getConfig( + 'web', + new DotenvPlugin({ path: envOneEmpty, safe: envOneEmptyExample }) + ) + + webpack(config, (err) => { + expect(err.message).toBe('Missing environment variable: TEST') + + done() }) + }) - test('Should fail naturally when using deprecated values', () => { - try { - envTest({ path: envMissingOne, safe: true, sample: envMissingOneExample }) - throw new Error('Should not get here') - } catch (err) { - expect(err.message).toEqual('Missing environment variable: TEST') - } + test('Should succeed in safe mode if allowEmptyValues is true', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envOneEmpty, safe: envOneEmptyExample, allowEmptyValues: true }), + oneEmptyResult, + done + ) + }) + }) + + describe('Missing a variable', () => { + test('Should load fine (not-safe)', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envMissingOne }), + missingOneResult, + done + ) + }) + + test('Should fail on safe mode (if allowEmptyValues is false)', (done) => { + const config = getConfig( + 'web', + new DotenvPlugin({ path: envMissingOne, safe: envMissingOneExample }) + ) + + webpack(config, (err) => { + expect(err.message).toBe('Missing environment variable: TEST') + + done() }) + }) + }) + + describe('Deprecated configuration', () => { + test('Should use safe when safe and sample set', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envSimple, safe: true, sample: envSimpleExample }), + simpleResult, + done + ) + }) + + test('Should display deprecation warning by default', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envSimple, safe: true, sample: envSimpleExample }), + simpleResult, + done + ) + expect(global.console.warn).toHaveBeenCalled() + }) + + test('Should not display deprecation warning when silent mode enabled', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envSimple, safe: true, sample: envSimpleExample, silent: true }), + simpleResult, + done + ) + expect(global.console.warn).toHaveBeenCalledTimes(0) + }) + + test('Should fail naturally when using deprecated values', (done) => { + const config = getConfig( + 'web', + new DotenvPlugin({ path: envMissingOne, safe: true, sample: envMissingOneExample }) + ) + + webpack(config, (err) => { + expect(err.message).toBe('Missing environment variable: TEST') - test('Should not fail naturally when using deprecated values improperly', () => { - expect(envTest({ path: envMissingOne, sample: envMissingOneExample })).toEqual(envMissingOneJson) + done() }) }) - describe('Silent mode', () => { - test('Should display warning by default', () => { - envTest({ path: false }) + test('Should not fail naturally when using deprecated values improperly', (done) => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envMissingOne, sample: envMissingOneExample }), + missingOneResult, + done + ) + }) + }) + + describe('Silent mode', () => { + test('Should display warning by default', (done) => { + compile(getConfig('web', new DotenvPlugin({ path: false })), () => { expect(global.console.warn).toHaveBeenCalled() - }) - test('Should not display warning when silent mode enabled', () => { - envTest({ path: false, silent: true }) - expect(global.console.warn).toHaveBeenCalledTimes(0) + done() }) }) + + test('Should not display warning when silent mode enabled', (done) => { + compile( + getConfig('web', new DotenvPlugin({ path: false, silent: true })), + () => { + expect(global.console.warn).toHaveBeenCalledTimes(0) + + done() + } + ) + }) }) -} -describe('Tests', () => { - runTests(Src.default, 'Source') - runTests(Dist.default, 'Dist') + describe('process.env stubbing', () => { + const expectToBeStubbed = (result) => { + expect(result).toMatch('const TEST = "testing"') + expect(result).toMatch('const TEST2 = "MISSING_ENV_VAR".TEST2') + expect(result).toMatch('const NODE_ENV = "development"') + expect(result).toMatch('const MONGOLAB_USER = "MISSING_ENV_VAR".MONGOLAB_USER') + } + + const expectNotToBeStubbed = (result) => { + expect(result).toMatch('const TEST = "testing"') + expect(result).toMatch('const TEST2 = process.env.TEST2') + expect(result).toMatch('const NODE_ENV = "development"') + expect(result).toMatch('const MONGOLAB_USER = process.env.MONGOLAB_USER') + } + + const plugin = new DotenvPlugin({ path: envSimple }) + const cases = [ + ['web', true], + ['es5', true], + ['es2020', true], + ['electron-renderer', true], + ['electron9-renderer', true], + ['electron-preload', true], + ['node', false], + ['node14', false], + ['electron-main', false], + ['electron9-main', false] + ] + + test.each(cases)('%s', (target, shouldStub, done) => { + compile( + getConfig(target, plugin), + (result) => { + if (shouldStub) { + expectToBeStubbed(result) + } else { + expectNotToBeStubbed(result) + } + + done() + } + ) + }) + }) }) From 1d11c9e6413cbdbc72868536a5b33238ed3cc799 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 11:53:06 +0100 Subject: [PATCH 08/25] fix errors --- test/index.test.js | 57 ++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/test/index.test.js b/test/index.test.js index d6522a4..aa9c182 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -131,43 +131,40 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { }) test('Should expand variables when configured', (done) => { - const config = getConfig( - 'web', - new DotenvPlugin({ path: envExpanded, expand: true }) - ) - - compile(config, (result) => { - expectResultsToContainReplacements(result, { - NODE_ENV: 'test', - BASIC: 'basic', - BASIC_EXPAND: 'basic', - MACHINE: 'machine_env', - MACHINE_EXPAND: 'machine_env', - UNDEFINED_EXPAND: '', - // eslint-disable-next-line - ESCAPED_EXPAND: '\$ESCAPED', - MONGOLAB_DATABASE: 'heroku_db', - MONGOLAB_USER: 'username', - MONGOLAB_PASSWORD: 'password', - MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', - MONGOLAB_PORT: '12345', - MONGOLAB_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', - MONGOLAB_USER_RECURSIVELY: 'username:password', - MONGOLAB_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', - WITHOUT_CURLY_BRACES_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', - WITHOUT_CURLY_BRACES_USER_RECURSIVELY: 'username:password', - WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db' - }) + const expected = { + NODE_ENV: 'test', + BASIC: 'basic', + BASIC_EXPAND: 'basic', + MACHINE: 'machine_env', + MACHINE_EXPAND: 'machine_env', + UNDEFINED_EXPAND: '', + // eslint-disable-next-line + ESCAPED_EXPAND: '\$ESCAPED', + MONGOLAB_DATABASE: 'heroku_db', + MONGOLAB_USER: 'username', + MONGOLAB_PASSWORD: 'password', + MONGOLAB_DOMAIN: 'abcd1234.mongolab.com', + MONGOLAB_PORT: '12345', + MONGOLAB_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', + MONGOLAB_USER_RECURSIVELY: 'username:password', + MONGOLAB_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', + WITHOUT_CURLY_BRACES_URI: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db', + WITHOUT_CURLY_BRACES_USER_RECURSIVELY: 'username:password', + WITHOUT_CURLY_BRACES_URI_RECURSIVELY: 'mongodb://username:password@abcd1234.mongolab.com:12345/heroku_db' + } - done() - }) + expectResultsToContainReplacements( + new DotenvPlugin({ path: envExpanded, expand: true }), + expected, + done + ) }) }) describe('Simple configuration', () => { test('Should load enviornment variables when they exist in the .env file.', (done) => { expectResultsToContainReplacements( - new DotenvPlugin({ path: envExpanded }), + new DotenvPlugin({ path: envSimple }), simpleResult, done ) From 7535539edaabd25da35d457141d3690e7aab8e33 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 11:56:11 +0100 Subject: [PATCH 09/25] fix race condition --- test/index.test.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/index.test.js b/test/index.test.js index aa9c182..cdf2370 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -201,13 +201,14 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { test('Should load successfully if variables defined', (done) => { expectResultsToContainReplacements( new DotenvPlugin({ path: envEmpty, safe: envEmptyExample }), - emptyResult - ) - - expectResultsToContainReplacements( - new DotenvPlugin({ path: envSimple, safe: envSimpleExample }), - simpleResult, - done + emptyResult, + () => { + expectResultsToContainReplacements( + new DotenvPlugin({ path: envSimple, safe: envSimpleExample }), + simpleResult, + done + ) + } ) }) From 3de630841be61b4514f16bf7ed8e383012cf57c0 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 12:05:30 +0100 Subject: [PATCH 10/25] handle no dir error --- test/index.test.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/index.test.js b/test/index.test.js index cdf2370..3c3005a 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -80,7 +80,15 @@ beforeAll(() => { beforeEach(() => { jest.resetAllMocks() - rmdirSync(resolve(__dirname, `output/${hash(expect.getState().currentTestName)}`), { recursive: true }) + const outputDir = resolve(__dirname, `output/${hash(expect.getState().currentTestName)}`) + try { + rmdirSync(outputDir, { recursive: true }) + } catch (err) { + // rmdir might error if the target doesn't exist, but we don't care about that. + if (!err.message.includes('ENOENT')) { + throw err + } + } }) describe.each(versions)('%s', (_, DotenvPlugin) => { From 5378c4e31cf14f554214980d037b4575225f0324 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 12:16:09 +0100 Subject: [PATCH 11/25] remove behavior changes --- src/index.js | 23 ++++------------------- test/index.test.js | 45 --------------------------------------------- 2 files changed, 4 insertions(+), 64 deletions(-) diff --git a/src/index.js b/src/index.js index e940f7a..51d5236 100644 --- a/src/index.js +++ b/src/index.js @@ -16,9 +16,6 @@ const interpolate = (env, vars) => { return env } -const isMainThreadElectron = (target) => - target.startsWith('electron') && target.endsWith('main') - class Dotenv { /** * The dotenv-webpack plugin. @@ -35,14 +32,8 @@ class Dotenv { }, config) this.checkDeprecation() - } - apply (compiler) { - const target = compiler.options.target ?? 'web' - const variables = this.gatherVariables() - const data = this.formatData(variables, target) - - new DefinePlugin(data).apply(compiler) + return new DefinePlugin(this.formatData(this.gatherVariables())) } checkDeprecation () { @@ -130,7 +121,7 @@ class Dotenv { return '' } - formatData (vars = {}, target) { + formatData (vars = {}) { const { expand } = this.config const formatted = Object.keys(vars).reduce((obj, key) => { const v = vars[key] @@ -153,14 +144,8 @@ class Dotenv { return obj }, {}) - // We have to stub any remaining `process.env`s due to Webpack 5 not polyfilling it anymore - // https://github.com/mrsteele/dotenv-webpack/issues/240#issuecomment-710231534 - // However, if someone targets Node or Electron `process.env` still exists, and should therefore be kept - // https://webpack.js.org/configuration/target - if (!target.startsWith('node') && !isMainThreadElectron(target)) { - // Results in `"MISSING_ENV_VAR".NAME` which is valid JS - formatted['process.env'] = '"MISSING_ENV_VAR"' - } + // fix in case of missing + formatted['process.env'] = '{}' return formatted } diff --git a/test/index.test.js b/test/index.test.js index 3c3005a..f4de57b 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -456,49 +456,4 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { ) }) }) - - describe('process.env stubbing', () => { - const expectToBeStubbed = (result) => { - expect(result).toMatch('const TEST = "testing"') - expect(result).toMatch('const TEST2 = "MISSING_ENV_VAR".TEST2') - expect(result).toMatch('const NODE_ENV = "development"') - expect(result).toMatch('const MONGOLAB_USER = "MISSING_ENV_VAR".MONGOLAB_USER') - } - - const expectNotToBeStubbed = (result) => { - expect(result).toMatch('const TEST = "testing"') - expect(result).toMatch('const TEST2 = process.env.TEST2') - expect(result).toMatch('const NODE_ENV = "development"') - expect(result).toMatch('const MONGOLAB_USER = process.env.MONGOLAB_USER') - } - - const plugin = new DotenvPlugin({ path: envSimple }) - const cases = [ - ['web', true], - ['es5', true], - ['es2020', true], - ['electron-renderer', true], - ['electron9-renderer', true], - ['electron-preload', true], - ['node', false], - ['node14', false], - ['electron-main', false], - ['electron9-main', false] - ] - - test.each(cases)('%s', (target, shouldStub, done) => { - compile( - getConfig(target, plugin), - (result) => { - if (shouldStub) { - expectToBeStubbed(result) - } else { - expectNotToBeStubbed(result) - } - - done() - } - ) - }) - }) }) From 9d9e6b5eeb8191370adca0fbd0fb870912571974 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 12:16:13 +0100 Subject: [PATCH 12/25] drop node 8 --- .github/workflows/main.yml | 2 +- package.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3eabb5a..c84d553 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -48,7 +48,7 @@ jobs: runs-on: ubuntu-18.04 strategy: matrix: - node: [ '8', '10', '12', '14' ] + node: [ '10', '12', '14' ] steps: - name: 🛑 Cancel Previous Runs uses: styfle/cancel-workflow-action@0.6.0 diff --git a/package.json b/package.json index ed866fd..5c2bbbe 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,9 @@ "url": "https://github.com/mrsteele/dotenv-webpack/issues" }, "homepage": "https://github.com/mrsteele/dotenv-webpack#readme", + "engines": { + "node": ">=10" + }, "peerDependencies": { "webpack": "^1 || ^2 || ^3 || ^4 || ^5" }, From 196171ae6639ca3201a62d19ef01f0b5640a2857 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 12:22:07 +0100 Subject: [PATCH 13/25] undo more fixes --- src/index.js | 8 +++++++- test/index.test.js | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 51d5236..267b9e3 100644 --- a/src/index.js +++ b/src/index.js @@ -32,8 +32,14 @@ class Dotenv { }, config) this.checkDeprecation() + } + + apply (compiler) { + const target = compiler.options.target ?? 'web' + const variables = this.gatherVariables() + const data = this.formatData(variables, target) - return new DefinePlugin(this.formatData(this.gatherVariables())) + new DefinePlugin(data).apply(compiler) } checkDeprecation () { diff --git a/test/index.test.js b/test/index.test.js index f4de57b..a5fad1d 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -307,7 +307,7 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { ) compile(config, (result) => { - expect(result).toMatch('const TEST = "MISSING_ENV_VAR".TEST') + expect(result).toMatch('const TEST = {}.TEST') expect(result).not.toMatch('const PATH = ""') done() From 33ca22e0f6b8fec4ef49a317634a27a2deb1ae2e Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 12:25:36 +0100 Subject: [PATCH 14/25] fix stubbing replacement, only stub on targets where we should --- src/index.js | 15 ++++++++++++--- test/index.test.js | 47 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index 267b9e3..e940f7a 100644 --- a/src/index.js +++ b/src/index.js @@ -16,6 +16,9 @@ const interpolate = (env, vars) => { return env } +const isMainThreadElectron = (target) => + target.startsWith('electron') && target.endsWith('main') + class Dotenv { /** * The dotenv-webpack plugin. @@ -127,7 +130,7 @@ class Dotenv { return '' } - formatData (vars = {}) { + formatData (vars = {}, target) { const { expand } = this.config const formatted = Object.keys(vars).reduce((obj, key) => { const v = vars[key] @@ -150,8 +153,14 @@ class Dotenv { return obj }, {}) - // fix in case of missing - formatted['process.env'] = '{}' + // We have to stub any remaining `process.env`s due to Webpack 5 not polyfilling it anymore + // https://github.com/mrsteele/dotenv-webpack/issues/240#issuecomment-710231534 + // However, if someone targets Node or Electron `process.env` still exists, and should therefore be kept + // https://webpack.js.org/configuration/target + if (!target.startsWith('node') && !isMainThreadElectron(target)) { + // Results in `"MISSING_ENV_VAR".NAME` which is valid JS + formatted['process.env'] = '"MISSING_ENV_VAR"' + } return formatted } diff --git a/test/index.test.js b/test/index.test.js index a5fad1d..3c3005a 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -307,7 +307,7 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { ) compile(config, (result) => { - expect(result).toMatch('const TEST = {}.TEST') + expect(result).toMatch('const TEST = "MISSING_ENV_VAR".TEST') expect(result).not.toMatch('const PATH = ""') done() @@ -456,4 +456,49 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { ) }) }) + + describe('process.env stubbing', () => { + const expectToBeStubbed = (result) => { + expect(result).toMatch('const TEST = "testing"') + expect(result).toMatch('const TEST2 = "MISSING_ENV_VAR".TEST2') + expect(result).toMatch('const NODE_ENV = "development"') + expect(result).toMatch('const MONGOLAB_USER = "MISSING_ENV_VAR".MONGOLAB_USER') + } + + const expectNotToBeStubbed = (result) => { + expect(result).toMatch('const TEST = "testing"') + expect(result).toMatch('const TEST2 = process.env.TEST2') + expect(result).toMatch('const NODE_ENV = "development"') + expect(result).toMatch('const MONGOLAB_USER = process.env.MONGOLAB_USER') + } + + const plugin = new DotenvPlugin({ path: envSimple }) + const cases = [ + ['web', true], + ['es5', true], + ['es2020', true], + ['electron-renderer', true], + ['electron9-renderer', true], + ['electron-preload', true], + ['node', false], + ['node14', false], + ['electron-main', false], + ['electron9-main', false] + ] + + test.each(cases)('%s', (target, shouldStub, done) => { + compile( + getConfig(target, plugin), + (result) => { + if (shouldStub) { + expectToBeStubbed(result) + } else { + expectNotToBeStubbed(result) + } + + done() + } + ) + }) + }) }) From 0b5ee68f662f2af4aed30f8798530d2dd9977ae6 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 17:40:22 +0100 Subject: [PATCH 15/25] remove deprecated code --- src/index.js | 13 ------------ test/index.test.js | 49 ---------------------------------------------- 2 files changed, 62 deletions(-) diff --git a/src/index.js b/src/index.js index e940f7a..1594a8a 100644 --- a/src/index.js +++ b/src/index.js @@ -33,8 +33,6 @@ class Dotenv { this.config = Object.assign({}, { path: './.env' }, config) - - this.checkDeprecation() } apply (compiler) { @@ -45,17 +43,6 @@ class Dotenv { new DefinePlugin(data).apply(compiler) } - checkDeprecation () { - const { sample, safe, silent } = this.config - // Catch older packages, but hold their hand (just for a bit) - if (sample) { - if (safe) { - this.config.safe = sample - } - this.warn('dotenv-webpack: "options.sample" is a deprecated property. Please update your configuration to use "options.safe" instead.', silent) - } - } - gatherVariables () { const { safe, allowEmptyValues } = this.config const vars = this.initializeVars() diff --git a/test/index.test.js b/test/index.test.js index 3c3005a..e24ae4b 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -387,55 +387,6 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { }) }) - describe('Deprecated configuration', () => { - test('Should use safe when safe and sample set', (done) => { - expectResultsToContainReplacements( - new DotenvPlugin({ path: envSimple, safe: true, sample: envSimpleExample }), - simpleResult, - done - ) - }) - - test('Should display deprecation warning by default', (done) => { - expectResultsToContainReplacements( - new DotenvPlugin({ path: envSimple, safe: true, sample: envSimpleExample }), - simpleResult, - done - ) - expect(global.console.warn).toHaveBeenCalled() - }) - - test('Should not display deprecation warning when silent mode enabled', (done) => { - expectResultsToContainReplacements( - new DotenvPlugin({ path: envSimple, safe: true, sample: envSimpleExample, silent: true }), - simpleResult, - done - ) - expect(global.console.warn).toHaveBeenCalledTimes(0) - }) - - test('Should fail naturally when using deprecated values', (done) => { - const config = getConfig( - 'web', - new DotenvPlugin({ path: envMissingOne, safe: true, sample: envMissingOneExample }) - ) - - webpack(config, (err) => { - expect(err.message).toBe('Missing environment variable: TEST') - - done() - }) - }) - - test('Should not fail naturally when using deprecated values improperly', (done) => { - expectResultsToContainReplacements( - new DotenvPlugin({ path: envMissingOne, sample: envMissingOneExample }), - missingOneResult, - done - ) - }) - }) - describe('Silent mode', () => { test('Should display warning by default', (done) => { compile(getConfig('web', new DotenvPlugin({ path: false })), () => { From 297142ad7734c7ac84f1963c236b3e436cb7ac36 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 17:43:45 +0100 Subject: [PATCH 16/25] drop support for webpack <4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c2bbbe..ad6dc42 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "node": ">=10" }, "peerDependencies": { - "webpack": "^1 || ^2 || ^3 || ^4 || ^5" + "webpack": "^4 || ^5" }, "dependencies": { "dotenv-defaults": "^2.0.1" From 2f2b7dec0379e0062489447266d5d8e7893d9784 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 22:29:52 +0100 Subject: [PATCH 17/25] merge stubIgnore PR --- README.md | 1 + package.json | 3 ++- src/index.js | 5 ++++- test/index.test.js | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0a7f582..9cbb92f 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ Use the following properties to configure your instance. * **silent** (`false`) - If true, all warnings will be suppressed. * **expand** (`false`) - Allows your variables to be "expanded" for reusability within your `.env` file. * **defaults** (`false`) - Adds support for `dotenv-defaults`. If set to `true`, uses `./.env.defaults`. If a string, uses that location for a defaults file. Read more at [npm](https://www.npmjs.com/package/dotenv-defaults). +* **ignoreStub** (`false`) - By default, this package will stub `process.env` in environments it doesn't exist so that if a variable is not set, the application will still run and not run into an "Uncaught ReferenceError". If you are running into issues where you or another package you use interfaces with `process.env`, it might be best to set this to `true` and make sure you always reference variables that exist within your code (See [this issue](https://github.com/mrsteele/dotenv-webpack/issues/271) for more information). The following example shows how to set any/all arguments. diff --git a/package.json b/package.json index ad6dc42..a1e5a4f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ }, "jest": { "coverageDirectory": "./coverage/", - "collectCoverage": true + "collectCoverage": true, + "watchPathIgnorePatterns": ["output/.*?"] }, "keywords": [ "dotenv", diff --git a/src/index.js b/src/index.js index 1594a8a..113e134 100644 --- a/src/index.js +++ b/src/index.js @@ -144,7 +144,10 @@ class Dotenv { // https://github.com/mrsteele/dotenv-webpack/issues/240#issuecomment-710231534 // However, if someone targets Node or Electron `process.env` still exists, and should therefore be kept // https://webpack.js.org/configuration/target - if (!target.startsWith('node') && !isMainThreadElectron(target)) { + if ( + !this.config.ignoreStub && + !target.startsWith('node') && !isMainThreadElectron(target) + ) { // Results in `"MISSING_ENV_VAR".NAME` which is valid JS formatted['process.env'] = '"MISSING_ENV_VAR"' } diff --git a/test/index.test.js b/test/index.test.js index e24ae4b..666b03f 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -451,5 +451,37 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { } ) }) + + describe('ignoreStub override', () => { + // `ignoreStub == null` case is covered by above test + + test('should never stub if set to true', (done) => { + const plugin = new DotenvPlugin({ ignoreStub: true, path: envSimple }) + + compile(getConfig('web', plugin), (result) => { + expectNotToBeStubbed(result) + + compile(getConfig('node', plugin), (result) => { + expectNotToBeStubbed(result) + + done() + }) + }) + }) + + test('should always stub if set to false', (done) => { + const plugin = new DotenvPlugin({ ignoreStub: false, path: envSimple }) + + compile(getConfig('web', plugin), (result) => { + expectToBeStubbed(result) + + compile(getConfig('node', plugin), (result) => { + expectToBeStubbed(result) + + done() + }) + }) + }) + }) }) }) From 05b119c373e0c1197256e6bf2a76d19fa93d0f64 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 22:38:09 +0100 Subject: [PATCH 18/25] update readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9cbb92f..e92c24f 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,9 @@ Use the following properties to configure your instance. * **silent** (`false`) - If true, all warnings will be suppressed. * **expand** (`false`) - Allows your variables to be "expanded" for reusability within your `.env` file. * **defaults** (`false`) - Adds support for `dotenv-defaults`. If set to `true`, uses `./.env.defaults`. If a string, uses that location for a defaults file. Read more at [npm](https://www.npmjs.com/package/dotenv-defaults). -* **ignoreStub** (`false`) - By default, this package will stub `process.env` in environments it doesn't exist so that if a variable is not set, the application will still run and not run into an "Uncaught ReferenceError". If you are running into issues where you or another package you use interfaces with `process.env`, it might be best to set this to `true` and make sure you always reference variables that exist within your code (See [this issue](https://github.com/mrsteele/dotenv-webpack/issues/271) for more information). +* **ignoreStub** (`false`) - Override the automatic check whether to stub `process.env`. + _`process.env` is not polyfilled in Webpack 5+, leading to errors in environments where `process` is `null` (browsers). We automatically stub any remaining `process.env`s in these environments to avoid these errors._ + _If you are running into issues where you or another package you use interfaces with `process.env`, it might be best to set this to `true` and make sure you always reference variables that exist within your code (See [this issue](https://github.com/mrsteele/dotenv-webpack/issues/271) for more information)._ The following example shows how to set any/all arguments. From 53f1478f03273c224a3771e049f5e69c9ca0759b Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 22:38:53 +0100 Subject: [PATCH 19/25] fix logic --- src/index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 113e134..aea51ca 100644 --- a/src/index.js +++ b/src/index.js @@ -145,8 +145,11 @@ class Dotenv { // However, if someone targets Node or Electron `process.env` still exists, and should therefore be kept // https://webpack.js.org/configuration/target if ( - !this.config.ignoreStub && - !target.startsWith('node') && !isMainThreadElectron(target) + this.config.ignoreStub !== true && + ( + this.config.ignoreStub === false || + (!target.startsWith('node') && !isMainThreadElectron(target)) + ) ) { // Results in `"MISSING_ENV_VAR".NAME` which is valid JS formatted['process.env'] = '"MISSING_ENV_VAR"' From 5c1d6143f54eabfb2e2566e9191ee00c040bdf14 Mon Sep 17 00:00:00 2001 From: Adam Haglund Date: Thu, 4 Mar 2021 13:45:01 -0800 Subject: [PATCH 20/25] Update README.md --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e92c24f..4725f55 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,14 @@ Add `.env` to your `.gitignore` file Due to the fact that we use `webpack.DefinePlugin` under the hood, we cannot support destructing as that breaks how this plugin is meant to be used. Because of this, please reference your variables without destructing. For more information about this, please review the issue [here](https://github.com/mrsteele/dotenv-webpack/issues/70). +## `process.env` stubbing / replacing + +`process.env` is not polyfilled in Webpack 5+, leading to errors in environments where `process` is `null` (browsers). + +We automatically replace any remaining `process.env`s in these environments with `"MISSING_ENV_VAR"` to avoid these errors. + +If you are running into issues where you or another package you use interfaces with `process.env`, it might be best to set `ignoreStubs: true` and make sure you always reference variables that exist within your code (See [this issue](https://github.com/mrsteele/dotenv-webpack/issues/271) for more information). + ## Properties Use the following properties to configure your instance. @@ -106,9 +114,7 @@ Use the following properties to configure your instance. * **silent** (`false`) - If true, all warnings will be suppressed. * **expand** (`false`) - Allows your variables to be "expanded" for reusability within your `.env` file. * **defaults** (`false`) - Adds support for `dotenv-defaults`. If set to `true`, uses `./.env.defaults`. If a string, uses that location for a defaults file. Read more at [npm](https://www.npmjs.com/package/dotenv-defaults). -* **ignoreStub** (`false`) - Override the automatic check whether to stub `process.env`. - _`process.env` is not polyfilled in Webpack 5+, leading to errors in environments where `process` is `null` (browsers). We automatically stub any remaining `process.env`s in these environments to avoid these errors._ - _If you are running into issues where you or another package you use interfaces with `process.env`, it might be best to set this to `true` and make sure you always reference variables that exist within your code (See [this issue](https://github.com/mrsteele/dotenv-webpack/issues/271) for more information)._ +* **ignoreStub** (`false`) - Override the automatic check whether to stub `process.env`. [Read more here](#user-content-processenv-stubbing--replacing). The following example shows how to set any/all arguments. From 1c71fb7a13c8b9b89a465097c6b355c70636b52b Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Mar 2021 23:19:00 +0100 Subject: [PATCH 21/25] skip stubbing on webpack 4, do not stub if target includes `node` --- src/index.js | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/index.js b/src/index.js index aea51ca..39bb2d7 100644 --- a/src/index.js +++ b/src/index.js @@ -36,9 +36,14 @@ class Dotenv { } apply (compiler) { - const target = compiler.options.target ?? 'web' const variables = this.gatherVariables() - const data = this.formatData(variables, target) + const target = compiler.options.target ?? 'web' + const version = compiler.webpack.version + const data = this.formatData({ + variables, + target, + version + }) new DefinePlugin(data).apply(compiler) } @@ -117,10 +122,10 @@ class Dotenv { return '' } - formatData (vars = {}, target) { + formatData ({ variables = {}, target, version }) { const { expand } = this.config - const formatted = Object.keys(vars).reduce((obj, key) => { - const v = vars[key] + const formatted = Object.keys(variables).reduce((obj, key) => { + const v = variables[key] const vKey = `process.env.${key}` let vValue if (expand) { @@ -129,7 +134,7 @@ class Dotenv { } else if (v.indexOf('\\$') > 0) { vValue = v.replace(/\\\$/g, '$') } else { - vValue = interpolate(v, vars) + vValue = interpolate(v, variables) } } else { vValue = v @@ -144,13 +149,7 @@ class Dotenv { // https://github.com/mrsteele/dotenv-webpack/issues/240#issuecomment-710231534 // However, if someone targets Node or Electron `process.env` still exists, and should therefore be kept // https://webpack.js.org/configuration/target - if ( - this.config.ignoreStub !== true && - ( - this.config.ignoreStub === false || - (!target.startsWith('node') && !isMainThreadElectron(target)) - ) - ) { + if (this.shouldStub({ target, version })) { // Results in `"MISSING_ENV_VAR".NAME` which is valid JS formatted['process.env'] = '"MISSING_ENV_VAR"' } @@ -158,6 +157,22 @@ class Dotenv { return formatted } + shouldStub ({ target, version }) { + return ( + // If we're on Webpack 5 + version.startsWith('5') && + // And we're not configured to not stub + this.config.ignoreStub !== true && + // And + ( + // We are configured to always stub + this.config.ignoreStub === false || + // Or if we should according to the target + (!target.includes('node') && !isMainThreadElectron(target)) + ) + ) + } + /** * Load a file. * @param {String} config.file - The file to load. From 7e96c08c6cded7fbb7f83d3d769f1b23730a2294 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 5 Mar 2021 14:26:45 +0100 Subject: [PATCH 22/25] handle target array --- src/index.js | 29 ++++++++++++++++------------- test/index.test.js | 6 +++++- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/index.js b/src/index.js index 39bb2d7..be8719d 100644 --- a/src/index.js +++ b/src/index.js @@ -157,19 +157,22 @@ class Dotenv { return formatted } - shouldStub ({ target, version }) { - return ( - // If we're on Webpack 5 - version.startsWith('5') && - // And we're not configured to not stub - this.config.ignoreStub !== true && - // And - ( - // We are configured to always stub - this.config.ignoreStub === false || - // Or if we should according to the target - (!target.includes('node') && !isMainThreadElectron(target)) - ) + shouldStub ({ target: targetInput, version }) { + const targets = Array.isArray(targetInput) ? targetInput : [targetInput] + + return targets.every( + target => + // If we're on Webpack 5 + version.startsWith('5') && + // And we're not configured to not stub + this.config.ignoreStub !== true && + // And + ( + // We are configured to always stub + this.config.ignoreStub === false || + // Or if we should according to the target + (!target.includes('node') && !isMainThreadElectron(target)) + ) ) } diff --git a/test/index.test.js b/test/index.test.js index 666b03f..9417b3a 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -426,15 +426,19 @@ describe.each(versions)('%s', (_, DotenvPlugin) => { const plugin = new DotenvPlugin({ path: envSimple }) const cases = [ ['web', true], + [['web'], true], ['es5', true], ['es2020', true], + [['es2020', 'web'], true], ['electron-renderer', true], ['electron9-renderer', true], ['electron-preload', true], ['node', false], + [['node'], false], ['node14', false], ['electron-main', false], - ['electron9-main', false] + ['electron9-main', false], + [['es2020', 'node'], false] ] test.each(cases)('%s', (target, shouldStub, done) => { From 87c2e20028594ae970448a57e0efc8b02f629a2c Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 5 Mar 2021 14:45:43 +0100 Subject: [PATCH 23/25] move version check --- src/index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index be8719d..5a92897 100644 --- a/src/index.js +++ b/src/index.js @@ -158,13 +158,15 @@ class Dotenv { } shouldStub ({ target: targetInput, version }) { + if (version.startsWith('5')) { + return false + } + const targets = Array.isArray(targetInput) ? targetInput : [targetInput] return targets.every( target => - // If we're on Webpack 5 - version.startsWith('5') && - // And we're not configured to not stub + // If we're not configured to never stub this.config.ignoreStub !== true && // And ( From 1d0861e4921724578b4f615d3d7fd7ae346e0dcb Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 5 Mar 2021 15:30:33 +0100 Subject: [PATCH 24/25] fix logic --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 5a92897..f0cbac5 100644 --- a/src/index.js +++ b/src/index.js @@ -158,7 +158,7 @@ class Dotenv { } shouldStub ({ target: targetInput, version }) { - if (version.startsWith('5')) { + if (!version.startsWith('5')) { return false } From acdd8afa7d5b124a3fe2b267df91f2cd28173d83 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 5 Mar 2021 15:36:03 +0100 Subject: [PATCH 25/25] replace usage of `rmdirSync(..., { recursive: true })` --- package-lock.json | 34 ++++++++++++++++++++++++++++++++++ package.json | 5 ++++- test/index.test.js | 5 +++-- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4d3e5e..dc983d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3865,6 +3865,12 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -5726,6 +5732,18 @@ "map-cache": "^0.2.2" } }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, "fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", @@ -7184,6 +7202,16 @@ "minimist": "^1.2.5" } }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -9746,6 +9774,12 @@ "set-value": "^2.0.1" } }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", diff --git a/package.json b/package.json index a1e5a4f..07c0efe 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,9 @@ "jest": { "coverageDirectory": "./coverage/", "collectCoverage": true, - "watchPathIgnorePatterns": ["output/.*?"] + "watchPathIgnorePatterns": [ + "output/.*?" + ] }, "keywords": [ "dotenv", @@ -56,6 +58,7 @@ "@babel/core": "^7.13.8", "@babel/preset-env": "^7.13.9", "@babel/register": "^7.13.8", + "fs-extra": "^9.1.0", "husky": "^5.1.3", "jest": "^25.5.4", "jsdoc": "^3.6.6", diff --git a/test/index.test.js b/test/index.test.js index 9417b3a..8873da4 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -3,7 +3,8 @@ const { resolve } = require('path') const { createHash } = require('crypto') const webpack = require('webpack') -const { readFileSync, rmdirSync } = require('fs') +const { readFileSync } = require('fs') +const { removeSync } = require('fs-extra') const Src = require('../src') const Dist = require('../dist') @@ -82,7 +83,7 @@ beforeEach(() => { const outputDir = resolve(__dirname, `output/${hash(expect.getState().currentTestName)}`) try { - rmdirSync(outputDir, { recursive: true }) + removeSync(outputDir) } catch (err) { // rmdir might error if the target doesn't exist, but we don't care about that. if (!err.message.includes('ENOENT')) {