Skip to content

Commit 402c0fe

Browse files
authored
fix: improve parsing of --env flag (#3286)
1 parent ce7352d commit 402c0fe

File tree

4 files changed

+80
-21
lines changed

4 files changed

+80
-21
lines changed

packages/webpack-cli/src/webpack-cli.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -756,18 +756,22 @@ class WebpackCLI implements IWebpackCLI {
756756
value: string,
757757
previous: Record<string, BasicPrimitive | object> = {},
758758
): Record<string, BasicPrimitive | object> => {
759-
// for https://github.com/webpack/webpack-cli/issues/2642
760-
if (value.endsWith("=")) {
761-
value.concat('""');
762-
}
763-
764759
// This ensures we're only splitting by the first `=`
765760
const [allKeys, val] = value.split(/=(.+)/, 2);
766761
const splitKeys = allKeys.split(/\.(?!$)/);
767762

768763
let prevRef = previous;
769764

770765
splitKeys.forEach((someKey, index) => {
766+
// https://github.com/webpack/webpack-cli/issues/3284
767+
if (someKey.endsWith("=")) {
768+
// remove '=' from key
769+
someKey = someKey.slice(0, -1);
770+
// @ts-expect-error we explicitly want to set it to undefined
771+
prevRef[someKey] = undefined;
772+
return;
773+
}
774+
771775
if (!prevRef[someKey]) {
772776
prevRef[someKey] = {};
773777
}

test/build/config/type/function-with-env/function-with-env.test.js

+67-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use strict";
22
const { existsSync } = require("fs");
33
const { resolve } = require("path");
4-
const { run, readFile } = require("../../../../utils/test-utils");
4+
const { run, readFile, isWindows } = require("../../../../utils/test-utils");
55

66
describe("function configuration", () => {
77
it("should throw when env is not supplied", async () => {
@@ -17,7 +17,7 @@ describe("function configuration", () => {
1717

1818
expect(exitCode).toBe(0);
1919
expect(stderr).toBeFalsy();
20-
expect(stdout).toBeTruthy();
20+
expect(stdout).toContain("isProd: true");
2121
// Should generate the appropriate files
2222
expect(existsSync(resolve(__dirname, "./dist/prod.js"))).toBeTruthy();
2323
});
@@ -27,7 +27,7 @@ describe("function configuration", () => {
2727

2828
expect(exitCode).toBe(0);
2929
expect(stderr).toBeFalsy();
30-
expect(stdout).toBeTruthy();
30+
expect(stdout).toContain("isDev: true");
3131
// Should generate the appropriate files
3232
expect(existsSync(resolve(__dirname, "./dist/dev.js"))).toBeTruthy();
3333
});
@@ -44,7 +44,8 @@ describe("function configuration", () => {
4444

4545
expect(exitCode).toBe(0);
4646
expect(stderr).toBeFalsy();
47-
expect(stdout).toBeTruthy();
47+
expect(stdout).toContain("environment: 'production'");
48+
expect(stdout).toContain("app: { title: 'Luffy' }");
4849
// Should generate the appropriate files
4950
expect(existsSync(resolve(__dirname, "./dist/Luffy.js"))).toBeTruthy();
5051
});
@@ -61,7 +62,7 @@ describe("function configuration", () => {
6162

6263
expect(exitCode).toBe(0);
6364
expect(stderr).toBeFalsy();
64-
expect(stdout).toBeTruthy();
65+
expect(stdout).toContain("environment: 'production'");
6566
// Should generate the appropriate files
6667
expect(existsSync(resolve(__dirname, "./dist/Atsumu.js"))).toBeTruthy();
6768
});
@@ -78,7 +79,8 @@ describe("function configuration", () => {
7879

7980
expect(exitCode).toBe(0);
8081
expect(stderr).toBeFalsy();
81-
expect(stdout).toBeTruthy();
82+
expect(stdout).toContain("environment: 'multipleq'");
83+
expect(stdout).toContain("file: 'name=is=Eren'");
8284
// Should generate the appropriate files
8385
expect(existsSync(resolve(__dirname, "./dist/name=is=Eren.js"))).toBeTruthy();
8486
});
@@ -95,7 +97,8 @@ describe("function configuration", () => {
9597

9698
expect(exitCode).toBe(0);
9799
expect(stderr).toBeFalsy();
98-
expect(stdout).toBeTruthy();
100+
expect(stdout).toContain("environment: 'dot'");
101+
expect(stdout).toContain("'name.': 'Hisoka'");
99102
// Should generate the appropriate files
100103
expect(existsSync(resolve(__dirname, "./dist/Hisoka.js"))).toBeTruthy();
101104
});
@@ -112,7 +115,8 @@ describe("function configuration", () => {
112115

113116
expect(exitCode).toBe(0);
114117
expect(stderr).toBeFalsy();
115-
expect(stdout).toBeTruthy();
118+
expect(stdout).toContain("environment: 'dot'");
119+
expect(stdout).toContain("'name.': true");
116120
// Should generate the appropriate files
117121
expect(existsSync(resolve(__dirname, "./dist/true.js"))).toBeTruthy();
118122
});
@@ -122,7 +126,7 @@ describe("function configuration", () => {
122126

123127
expect(exitCode).toBe(0);
124128
expect(stderr).toBeFalsy();
125-
expect(stdout).toBeTruthy();
129+
expect(stdout).toContain(`foo: "''"`);
126130
// Should generate the appropriate files
127131
expect(existsSync(resolve(__dirname, "./dist/empty-string.js"))).toBeTruthy();
128132
});
@@ -132,7 +136,7 @@ describe("function configuration", () => {
132136

133137
expect(exitCode).toBe(0);
134138
expect(stderr).toBeFalsy();
135-
expect(stdout).toBeTruthy();
139+
expect(stdout).toContain(`foo: "bar=''"`);
136140
// Should generate the appropriate files
137141
expect(existsSync(resolve(__dirname, "./dist/new-empty-string.js"))).toBeTruthy();
138142
});
@@ -142,11 +146,59 @@ describe("function configuration", () => {
142146

143147
expect(exitCode).toBe(0);
144148
expect(stderr).toBeFalsy();
145-
expect(stdout).toBeTruthy();
149+
// should log foo: undefined
150+
expect(stdout).toContain("foo: undefined");
151+
});
152+
153+
it('Supports env variable with "foo=undefined" at the end', async () => {
154+
const { exitCode, stderr, stdout } = await run(__dirname, ["--env", `foo=undefined`]);
155+
156+
expect(exitCode).toBe(0);
157+
expect(stderr).toBeFalsy();
158+
// should log foo: 'undefined'
159+
expect(stdout).toContain("foo: 'undefined'");
146160
// Should generate the appropriate files
147-
expect(existsSync(resolve(__dirname, "./dist/equal-at-the-end.js"))).toBeTruthy();
161+
expect(existsSync(resolve(__dirname, "./dist/undefined-foo.js"))).toBeTruthy();
148162
});
149163

164+
// macOS/Linux specific syntax
165+
if (!isWindows) {
166+
it("Supports empty string in shell environment", async () => {
167+
const { exitCode, stderr, stdout } = await run(__dirname, ["--env", "foo=\\'\\'"], {
168+
shell: true,
169+
});
170+
171+
expect(exitCode).toBe(0);
172+
expect(stderr).toBeFalsy();
173+
expect(stdout).toContain(`foo: "''"`);
174+
// Should generate the appropriate files
175+
expect(existsSync(resolve(__dirname, "./dist/empty-string.js"))).toBeTruthy();
176+
});
177+
it("should set the variable to undefined if empty string is not escaped in shell environment", async () => {
178+
const { exitCode, stderr, stdout } = await run(__dirname, ["--env", "foo=''"], {
179+
shell: true,
180+
});
181+
182+
expect(exitCode).toBe(0);
183+
expect(stderr).toBeFalsy();
184+
expect(stdout).toContain(`foo: undefined`);
185+
});
186+
it('Supports env variable with "=$NON_EXISTENT_VAR" at the end', async () => {
187+
const { exitCode, stderr, stdout } = await run(
188+
__dirname,
189+
["--env", `foo=$NON_EXISTENT_VAR`],
190+
{
191+
shell: true,
192+
},
193+
);
194+
195+
expect(exitCode).toBe(0);
196+
expect(stderr).toBeFalsy();
197+
// should log foo: undefined
198+
expect(stdout).toContain("foo: undefined");
199+
});
200+
}
201+
150202
it("is able to understand multiple env flags", async () => {
151203
const { exitCode, stderr, stdout } = await run(__dirname, [
152204
"--env",
@@ -159,7 +211,8 @@ describe("function configuration", () => {
159211

160212
expect(exitCode).toBe(0);
161213
expect(stderr).toBeFalsy();
162-
expect(stdout).toBeTruthy();
214+
expect(stdout).toContain("verboseStats: true");
215+
expect(stdout).toContain("envMessage: true");
163216
// check that the verbose env is respected
164217
expect(stdout).toContain("LOG from webpack");
165218

@@ -189,7 +242,7 @@ describe("function configuration", () => {
189242

190243
expect(exitCode).toBe(0);
191244
expect(stderr).toBeFalsy();
192-
expect(stdout).toBeTruthy();
245+
expect(stdout).toContain("'name.': 'baz'");
193246
// Should generate the appropriate files
194247
expect(existsSync(resolve(__dirname, "./dist/baz.js"))).toBeTruthy();
195248
});

test/build/config/type/function-with-env/webpack.config.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const { DefinePlugin } = require("webpack");
22

33
module.exports = (env) => {
4+
console.log(env);
45
if (env.isProd) {
56
return {
67
entry: "./a.js",
@@ -25,11 +26,11 @@ module.exports = (env) => {
2526
},
2627
};
2728
}
28-
if (env["foo="]) {
29+
if (env.foo === "undefined") {
2930
return {
3031
entry: "./a.js",
3132
output: {
32-
filename: "equal-at-the-end.js",
33+
filename: "undefined-foo.js",
3334
},
3435
};
3536
}

test/build/config/type/function-with-env/webpack.env.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
module.exports = (env) => {
2+
console.log(env);
23
const { environment, app, file } = env;
34
const customName = file && file.name && file.name.is && file.name.is.this;
45
const appTitle = app && app.title;

0 commit comments

Comments
 (0)