Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow passing strings in env flag #1939

Merged
merged 4 commits into from Oct 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 1 addition & 11 deletions packages/webpack-cli/lib/groups/ConfigGroup.js
Expand Up @@ -167,17 +167,7 @@ const finalize = async (moduleObj, args) => {
// `Promise` may return `Function`
if (typeof rawConfig === 'function') {
// when config is a function, pass the env from args to the config function
let envs;

if (Array.isArray(env)) {
envs = env.reduce((envObject, envOption) => {
envObject[envOption] = true;

return envObject;
}, {});
}

rawConfig = await rawConfig(envs, args);
rawConfig = await rawConfig(env, args);
}

return rawConfig;
Expand Down
22 changes: 22 additions & 0 deletions packages/webpack-cli/lib/utils/arg-parser.js
Expand Up @@ -92,6 +92,28 @@ const argParser = (options, args, argsOnly = false, name = '') => {
// a multiple argument parsing function
const multiArg = (value, previous = []) => previous.concat([value]);
parserInstance.option(flagsWithType, option.description, multiArg, option.defaultValue).action(() => {});
} else if (option.multipleType) {
// for options which accept multiple types like env
// so you can do `--env platform=staging --env production`
// { platform: "staging", production: true }
const multiArg = (value, previous = {}) => {
// this ensures we're only splitting by the first `=`
const [allKeys, val] = value.split(/=(.+)/, 2);
const splitKeys = allKeys.split(/\.(?!$)/);
let prevRef = previous;
splitKeys.forEach((someKey, index) => {
if (!prevRef[someKey]) prevRef[someKey] = {};
if ('string' === typeof prevRef[someKey]) {
prevRef[someKey] = {};
}
if (index === splitKeys.length - 1) {
prevRef[someKey] = val || true;
}
prevRef = prevRef[someKey];
});
return previous;
};
parserInstance.option(flagsWithType, option.description, multiArg, option.defaultValue).action(() => {});
} else {
// Prevent default behavior for standalone options
parserInstance.option(flagsWithType, option.description, option.defaultValue).action(() => {});
Expand Down
2 changes: 1 addition & 1 deletion packages/webpack-cli/lib/utils/cli-flags.js
Expand Up @@ -205,7 +205,7 @@ const core = [
name: 'env',
usage: '--env',
type: String,
multiple: true,
multipleType: true,
description: 'Environment passed to the configuration when it is a function',
},
{
Expand Down
63 changes: 63 additions & 0 deletions test/config/type/function-with-env/function-with-env.test.js
Expand Up @@ -4,6 +4,13 @@ const { resolve } = require('path');
const { run } = require('../../../utils/test-utils');

describe('function configuration', () => {
it('should throw when env is not supplied', () => {
const { stderr, stdout, exitCode } = run(__dirname, ['--env'], false);
expect(stdout).toBeFalsy();
expect(stderr).toBeTruthy();
expect(stderr).toContain(`option '--env <value>' argument missing`);
expect(exitCode).toEqual(1);
});
it('is able to understand a configuration file as a function', () => {
const { stderr, stdout } = run(__dirname, ['--env', 'isProd']);
expect(stderr).toBeFalsy();
Expand All @@ -18,6 +25,62 @@ describe('function configuration', () => {
// Should generate the appropriate files
expect(existsSync(resolve(__dirname, './bin/dev.js'))).toBeTruthy();
});
it('Supports passing string in env', () => {
const { stderr, stdout } = run(__dirname, [
'--env',
'environment=production',
'--env',
'app.title=Luffy',
'-c',
'webpack.env.config.js',
]);
expect(stderr).toBeFalsy();
expect(stdout).toBeTruthy();
// Should generate the appropriate files
expect(existsSync(resolve(__dirname, './bin/Luffy.js'))).toBeTruthy();
});
it('Supports long nested values in env', () => {
const { stderr, stdout } = run(__dirname, [
'--env',
'file.name.is.this=Atsumu',
'--env',
'environment=production',
'-c',
'webpack.env.config.js',
]);
expect(stderr).toBeFalsy();
expect(stdout).toBeTruthy();
// Should generate the appropriate files
expect(existsSync(resolve(__dirname, './bin/Atsumu.js'))).toBeTruthy();
});
anshumanv marked this conversation as resolved.
Show resolved Hide resolved
it('Supports multiple equal in a string', () => {
const { stderr, stdout } = run(__dirname, [
'--env',
'file=name=is=Eren',
'--env',
'environment=multipleq',
'-c',
'webpack.env.config.js',
]);
expect(stderr).toBeFalsy();
expect(stdout).toBeTruthy();
// Should generate the appropriate files
expect(existsSync(resolve(__dirname, './bin/name=is=Eren.js'))).toBeTruthy();
});
it('Supports dot at the end', () => {
const { stderr, stdout } = run(__dirname, ['--env', 'name.=Hisoka', '--env', 'environment=dot', '-c', 'webpack.env.config.js']);
expect(stderr).toBeFalsy();
expect(stdout).toBeTruthy();
// Should generate the appropriate files
expect(existsSync(resolve(__dirname, './bin/Hisoka.js'))).toBeTruthy();
});
it('Supports dot at the end', () => {
const { stderr, stdout } = run(__dirname, ['--env', 'name.', '--env', 'environment=dot', '-c', 'webpack.env.config.js']);
expect(stderr).toBeFalsy();
expect(stdout).toBeTruthy();
// Should generate the appropriate files
expect(existsSync(resolve(__dirname, './bin/true.js'))).toBeTruthy();
});
it('is able to understand multiple env flags', (done) => {
const { stderr, stdout } = run(__dirname, ['--env', 'isDev', '--env', 'verboseStats', '--env', 'envMessage']);
expect(stderr).toBeFalsy();
Expand Down
32 changes: 32 additions & 0 deletions test/config/type/function-with-env/webpack.env.config.js
@@ -0,0 +1,32 @@
module.exports = (env) => {
const { environment, app, file } = env;
const customName = file && file.name && file.name.is && file.name.is.this;
const appTitle = app && app.title;
if (environment === 'production') {
return {
entry: './a.js',
output: {
filename: `${customName ? customName : appTitle}.js`,
},
};
}
if (environment === 'multipleq') {
const { file } = env;
return {
entry: './a.js',
output: {
filename: `${file}.js`,
},
};
}
if (environment === 'dot') {
const file = env['name.'];
return {
entry: './a.js',
output: {
filename: `${file}.js`,
},
};
}
return {};
};
2 changes: 0 additions & 2 deletions test/config/type/promise-function/promise-function.test.js
Expand Up @@ -6,8 +6,6 @@ const { run } = require('../../../utils/test-utils');
describe('promise function', () => {
it('is able to understand a configuration file as a promise', (done) => {
const { stdout, stderr } = run(__dirname, ['-c', './webpack.config.js'], false);
console.log(stdout);
console.log(stderr);
expect(stdout).toBeTruthy();
expect(stderr).toBeFalsy();
stat(resolve(__dirname, './binary/promise.js'), (err, stats) => {
Expand Down