Skip to content

Commit 9e9c70b

Browse files
authored
fix: mode behaviour (#1824)
* fix: mode behaviour * fix: mode behaviour
1 parent e2f43c2 commit 9e9c70b

File tree

7 files changed

+164
-30
lines changed

7 files changed

+164
-30
lines changed

packages/webpack-cli/__tests__/resolveMode.test.js

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,81 @@ const resolveMode = require('../lib/groups/resolveMode');
22

33
describe('resolveMode', function () {
44
it('should handle the mode option [production]', () => {
5-
const result = resolveMode({
6-
mode: 'production',
7-
});
5+
const result = resolveMode(
6+
{
7+
mode: 'production',
8+
},
9+
{},
10+
);
811
// ensure no other properties are added
912
expect(result.options).toMatchObject({ mode: 'production' });
1013
expect(result.options.mode).toEqual('production');
1114
});
1215

1316
it('should handle the mode option [development]', () => {
14-
const result = resolveMode({
15-
mode: 'development',
16-
});
17+
const result = resolveMode(
18+
{
19+
mode: 'development',
20+
},
21+
{},
22+
);
1723

1824
// ensure no other properties are added
1925
expect(result.options).toMatchObject({ mode: 'development' });
2026
expect(result.options.mode).toEqual('development');
2127
});
2228

2329
it('should handle the mode option [none]', () => {
24-
const result = resolveMode({
25-
mode: 'none',
26-
});
30+
const result = resolveMode(
31+
{
32+
mode: 'none',
33+
},
34+
{},
35+
);
2736

2837
// ensure no other properties are added
2938
expect(result.options).toMatchObject({ mode: 'none' });
3039
expect(result.options.mode).toEqual('none');
3140
});
3241

33-
it('should prefer supplied move over NODE_ENV', () => {
42+
it('should prefer supplied move flag over NODE_ENV', () => {
3443
process.env.NODE_ENV = 'production';
35-
const result = resolveMode({
36-
mode: 'development',
37-
});
44+
const result = resolveMode(
45+
{
46+
mode: 'development',
47+
},
48+
{},
49+
);
3850

3951
// ensure no other properties are added
4052
expect(result.options).toMatchObject({ mode: 'development' });
4153
});
54+
55+
it('should prefer supplied move flag over mode from config', () => {
56+
const result = resolveMode(
57+
{
58+
mode: 'development',
59+
},
60+
{ mode: 'production' },
61+
);
62+
63+
// ensure no other properties are added
64+
expect(result.options).toMatchObject({ mode: 'development' });
65+
});
66+
67+
it('should prefer mode form config over NODE_ENV', () => {
68+
process.env.NODE_ENV = 'development';
69+
const result = resolveMode({}, { mode: 'production' });
70+
71+
// ensure no other properties are added
72+
expect(result.options).toMatchObject({ mode: 'production' });
73+
});
74+
75+
it('should prefer mode form flag over NODE_ENV and config', () => {
76+
process.env.NODE_ENV = 'development';
77+
const result = resolveMode({ mode: 'none' }, { mode: 'production' });
78+
79+
// ensure no other properties are added
80+
expect(result.options).toMatchObject({ mode: 'none' });
81+
});
4282
});
Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,49 @@
11
const PRODUCTION = 'production';
22
const DEVELOPMENT = 'development';
33

4-
const resolveMode = (args) => {
4+
/*
5+
Mode priority:
6+
- Mode flag
7+
- Mode from config
8+
- Mode form NODE_ENV
9+
*/
10+
11+
/**
12+
*
13+
* @param {string} mode - mode flag value
14+
* @param {Object} configObject - contains relevant loaded config
15+
*/
16+
const assignMode = (mode, configObject) => {
517
const {
618
env: { NODE_ENV },
719
} = process;
8-
const { mode } = args;
9-
20+
const { mode: configMode } = configObject;
1021
let finalMode;
11-
/**
12-
* It determines the mode to pass to webpack compiler
13-
* @returns {string} The mode
14-
*/
15-
if (!mode && NODE_ENV && (NODE_ENV === PRODUCTION || NODE_ENV === DEVELOPMENT)) {
22+
if (mode) {
23+
finalMode = mode;
24+
} else if (configMode) {
25+
finalMode = configMode;
26+
} else if (NODE_ENV && (NODE_ENV === PRODUCTION || NODE_ENV === DEVELOPMENT)) {
1627
finalMode = NODE_ENV;
1728
} else {
18-
finalMode = mode || PRODUCTION;
29+
finalMode = PRODUCTION;
1930
}
31+
return { mode: finalMode };
32+
};
33+
34+
/**
35+
*
36+
* @param {Object} args - parsedArgs from the CLI
37+
* @param {Object | Array} configOptions - Contains loaded config or array of configs
38+
*/
39+
const resolveMode = (args, configOptions) => {
40+
const { mode } = args;
41+
let resolvedMode;
42+
if (Array.isArray(configOptions)) {
43+
resolvedMode = configOptions.map((configObject) => assignMode(mode, configObject));
44+
} else resolvedMode = assignMode(mode, configOptions);
2045

21-
return {
22-
options: { mode: finalMode },
23-
};
46+
return { options: resolvedMode };
2447
};
2548

2649
module.exports = resolveMode;

packages/webpack-cli/lib/webpack-cli.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ class WebpackCLI extends GroupHelper {
7777
this._mergeOptionsToOutputConfiguration(resolvedConfig.outputOptions);
7878
}
7979

80-
async _baseResolver(cb, parsedArgs) {
81-
const resolvedConfig = cb(parsedArgs);
80+
async _baseResolver(cb, parsedArgs, configOptions) {
81+
const resolvedConfig = cb(parsedArgs, configOptions);
8282
this._mergeOptionsToConfiguration(resolvedConfig.options);
8383
this._mergeOptionsToOutputConfiguration(resolvedConfig.outputOptions);
8484
}
@@ -143,6 +143,21 @@ class WebpackCLI extends GroupHelper {
143143
* @returns {void}
144144
*/
145145
_mergeOptionsToConfiguration(options, strategy) {
146+
/**
147+
* options where they differ per config use this method to apply relevant option to relevant config
148+
* eg mode flag applies per config
149+
*/
150+
if (Array.isArray(options) && Array.isArray(this.compilerConfiguration)) {
151+
this.compilerConfiguration = options.map((option, index) => {
152+
const compilerConfig = this.compilerConfiguration[index];
153+
if (strategy) {
154+
return webpackMerge.strategy(strategy)(compilerConfig, option);
155+
}
156+
return webpackMerge(compilerConfig, option);
157+
});
158+
return;
159+
}
160+
146161
/**
147162
* options is an array (multiple configuration) so we create a new
148163
* configuration where each element is individually merged
@@ -229,9 +244,9 @@ class WebpackCLI extends GroupHelper {
229244
*/
230245
async runOptionGroups(parsedArgs) {
231246
await Promise.resolve()
232-
.then(() => this._baseResolver(resolveMode, parsedArgs))
233247
.then(() => this._handleDefaultEntry())
234248
.then(() => this._handleConfig(parsedArgs))
249+
.then(() => this._baseResolver(resolveMode, parsedArgs, this.compilerConfiguration))
235250
.then(() => this._handleGroupHelper(this.outputGroup))
236251
.then(() => this._handleCoreFlags())
237252
.then(() => this._handleGroupHelper(this.basicGroup))

test/mode/mode-single-arg/mode-single-arg.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ describe('mode flags', () => {
2626
expect(stdout).toContain(`mode: 'none'`);
2727
});
2828

29+
it('should pick mode form NODE_ENV', () => {
30+
const { stderr, stdout } = run(__dirname, [], false, [], { NODE_ENV: 'development' });
31+
expect(stderr).toBeFalsy();
32+
expect(stdout).toContain(`mode: 'development'`);
33+
});
34+
2935
it('should throw error when --mode=abcd is passed', () => {
3036
const { stderr } = run(__dirname, ['--mode', 'abcd']);
3137
expect(stderr).toContain('configuration.mode should be one of these');

test/mode/mode-with-config/mode-with-config.test.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,53 @@ describe('mode flags with config', () => {
9292
});
9393
});
9494

95-
it('should use mode from config over flags', () => {
95+
it('should use mode flag over config', () => {
9696
const { stdout, stderr, exitCode } = run(__dirname, ['--mode', 'production', '-c', 'webpack.config2.js']);
9797
expect(stderr).toBeFalsy();
9898
expect(exitCode).toEqual(0);
99+
expect(stdout).toContain(`mode: 'production'`);
100+
});
101+
102+
it('should use mode from flag over NODE_ENV', () => {
103+
const { stdout, stderr, exitCode } = run(__dirname, ['--mode', 'none', '-c', 'webpack.config2.js'], false, [], {
104+
NODE_ENV: 'production',
105+
});
106+
expect(stderr).toBeFalsy();
107+
expect(exitCode).toEqual(0);
108+
expect(stdout).toContain(`mode: 'none'`);
109+
});
110+
111+
it('should use mode from config over NODE_ENV', () => {
112+
const { stdout, stderr, exitCode } = run(__dirname, ['-c', 'webpack.config2.js']);
113+
expect(stderr).toBeFalsy();
114+
expect(exitCode).toEqual(0);
115+
expect(stdout).toContain(`mode: 'development'`);
116+
});
117+
118+
it('should use mode from config when multiple config are supplied', () => {
119+
const { stdout, stderr } = run(__dirname, ['-c', 'webpack.config3.js', '-c', 'webpack.config2.js']);
120+
expect(stderr).toBeFalsy();
121+
expect(stdout).toContain(`mode: 'development'`);
122+
expect(stdout.match(new RegExp("mode: 'development'", 'g')).length).toEqual(1);
123+
});
124+
125+
it('mode flag should apply to all configs', () => {
126+
const { stdout, stderr, exitCode } = run(__dirname, ['--mode', 'none', '-c', './webpack.config3.js', '-c', './webpack.config2.js']);
127+
expect(stderr).toBeFalsy();
128+
expect(exitCode).toEqual(0);
129+
expect(stdout).toContain(`mode: 'none'`);
130+
expect(stdout.match(new RegExp("mode: 'none'", 'g')).length).toEqual(2);
131+
});
132+
133+
it('only config where mode is absent pick up from NODE_ENV', () => {
134+
const { stdout, stderr, exitCode } = run(__dirname, ['-c', './webpack.config3.js', '-c', './webpack.config2.js'], false, [], {
135+
NODE_ENV: 'production',
136+
});
137+
expect(stderr).toBeFalsy();
138+
expect(exitCode).toEqual(0);
139+
expect(stdout).toContain(`mode: 'production'`);
99140
expect(stdout).toContain(`mode: 'development'`);
141+
expect(stdout.match(new RegExp("mode: 'production'", 'g')).length).toEqual(1);
142+
expect(stdout.match(new RegExp("mode: 'development'", 'g')).length).toEqual(1);
100143
});
101144
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// eslint-disable-next-line node/no-unpublished-require
2+
const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin');
3+
4+
module.exports = {
5+
plugins: [new WebpackCLITestPlugin()],
6+
};

test/utils/test-utils.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const isWebpack5 = version.startsWith('5');
1919
* @param {Boolean} setOutput Boolean that decides if a default output path will be set or not
2020
* @returns {Object} The webpack output or Promise when nodeOptions are present
2121
*/
22-
function run(testCase, args = [], setOutput = true, nodeArgs = []) {
22+
function run(testCase, args = [], setOutput = true, nodeArgs = [], env) {
2323
const cwd = path.resolve(testCase);
2424

2525
const outputPath = path.resolve(testCase, 'bin');
@@ -29,6 +29,7 @@ function run(testCase, args = [], setOutput = true, nodeArgs = []) {
2929
cwd,
3030
reject: false,
3131
nodeOptions: nodeArgs,
32+
env,
3233
stdio: ENABLE_LOG_COMPILATION ? 'inherit' : 'pipe',
3334
});
3435

0 commit comments

Comments
 (0)