Skip to content

Commit

Permalink
fix: improve parsing of --env flag (#3286)
Browse files Browse the repository at this point in the history
  • Loading branch information
snitin315 authored Jun 10, 2022
1 parent ce7352d commit 402c0fe
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 21 deletions.
14 changes: 9 additions & 5 deletions packages/webpack-cli/src/webpack-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -756,18 +756,22 @@ class WebpackCLI implements IWebpackCLI {
value: string,
previous: Record<string, BasicPrimitive | object> = {},
): Record<string, BasicPrimitive | object> => {
// for https://github.com/webpack/webpack-cli/issues/2642
if (value.endsWith("=")) {
value.concat('""');
}

// 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) => {
// https://github.com/webpack/webpack-cli/issues/3284
if (someKey.endsWith("=")) {
// remove '=' from key
someKey = someKey.slice(0, -1);
// @ts-expect-error we explicitly want to set it to undefined
prevRef[someKey] = undefined;
return;
}

if (!prevRef[someKey]) {
prevRef[someKey] = {};
}
Expand Down
81 changes: 67 additions & 14 deletions test/build/config/type/function-with-env/function-with-env.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use strict";
const { existsSync } = require("fs");
const { resolve } = require("path");
const { run, readFile } = require("../../../../utils/test-utils");
const { run, readFile, isWindows } = require("../../../../utils/test-utils");

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

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

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

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

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

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

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

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

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

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

expect(exitCode).toBe(0);
expect(stderr).toBeFalsy();
expect(stdout).toBeTruthy();
// should log foo: undefined
expect(stdout).toContain("foo: undefined");
});

it('Supports env variable with "foo=undefined" at the end', async () => {
const { exitCode, stderr, stdout } = await run(__dirname, ["--env", `foo=undefined`]);

expect(exitCode).toBe(0);
expect(stderr).toBeFalsy();
// should log foo: 'undefined'
expect(stdout).toContain("foo: 'undefined'");
// Should generate the appropriate files
expect(existsSync(resolve(__dirname, "./dist/equal-at-the-end.js"))).toBeTruthy();
expect(existsSync(resolve(__dirname, "./dist/undefined-foo.js"))).toBeTruthy();
});

// macOS/Linux specific syntax
if (!isWindows) {
it("Supports empty string in shell environment", async () => {
const { exitCode, stderr, stdout } = await run(__dirname, ["--env", "foo=\\'\\'"], {
shell: true,
});

expect(exitCode).toBe(0);
expect(stderr).toBeFalsy();
expect(stdout).toContain(`foo: "''"`);
// Should generate the appropriate files
expect(existsSync(resolve(__dirname, "./dist/empty-string.js"))).toBeTruthy();
});
it("should set the variable to undefined if empty string is not escaped in shell environment", async () => {
const { exitCode, stderr, stdout } = await run(__dirname, ["--env", "foo=''"], {
shell: true,
});

expect(exitCode).toBe(0);
expect(stderr).toBeFalsy();
expect(stdout).toContain(`foo: undefined`);
});
it('Supports env variable with "=$NON_EXISTENT_VAR" at the end', async () => {
const { exitCode, stderr, stdout } = await run(
__dirname,
["--env", `foo=$NON_EXISTENT_VAR`],
{
shell: true,
},
);

expect(exitCode).toBe(0);
expect(stderr).toBeFalsy();
// should log foo: undefined
expect(stdout).toContain("foo: undefined");
});
}

it("is able to understand multiple env flags", async () => {
const { exitCode, stderr, stdout } = await run(__dirname, [
"--env",
Expand All @@ -159,7 +211,8 @@ describe("function configuration", () => {

expect(exitCode).toBe(0);
expect(stderr).toBeFalsy();
expect(stdout).toBeTruthy();
expect(stdout).toContain("verboseStats: true");
expect(stdout).toContain("envMessage: true");
// check that the verbose env is respected
expect(stdout).toContain("LOG from webpack");

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

expect(exitCode).toBe(0);
expect(stderr).toBeFalsy();
expect(stdout).toBeTruthy();
expect(stdout).toContain("'name.': 'baz'");
// Should generate the appropriate files
expect(existsSync(resolve(__dirname, "./dist/baz.js"))).toBeTruthy();
});
Expand Down
5 changes: 3 additions & 2 deletions test/build/config/type/function-with-env/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { DefinePlugin } = require("webpack");

module.exports = (env) => {
console.log(env);
if (env.isProd) {
return {
entry: "./a.js",
Expand All @@ -25,11 +26,11 @@ module.exports = (env) => {
},
};
}
if (env["foo="]) {
if (env.foo === "undefined") {
return {
entry: "./a.js",
output: {
filename: "equal-at-the-end.js",
filename: "undefined-foo.js",
},
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module.exports = (env) => {
console.log(env);
const { environment, app, file } = env;
const customName = file && file.name && file.name.is && file.name.is.this;
const appTitle = app && app.title;
Expand Down

0 comments on commit 402c0fe

Please sign in to comment.