Skip to content

Commit fd28679

Browse files
fix: support file protocol in configuration options (#4702)
1 parent 37e4270 commit fd28679

File tree

25 files changed

+202
-38
lines changed

25 files changed

+202
-38
lines changed

.changeset/slow-oranges-repair.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"webpack-cli": patch
3+
---
4+
5+
The `file` protocol for configuration options (`--config`/`--extends`) is supported.

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

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from "node:fs";
22
import path from "node:path";
33
import { type Readable as ReadableType } from "node:stream";
4-
import { pathToFileURL } from "node:url";
4+
import { fileURLToPath, pathToFileURL } from "node:url";
55
import util from "node:util";
66
import { type stringifyChunked as stringifyChunkedType } from "@discoveryjs/json-ext";
77
import {
@@ -2163,12 +2163,14 @@ class WebpackCLI {
21632163
): Promise<{ options: Configuration | MultiConfiguration; path: string }> => {
21642164
let options: LoadableWebpackConfiguration | undefined;
21652165

2166+
const isFileURL = configPath.startsWith("file://");
2167+
21662168
try {
21672169
let loadingError;
21682170

21692171
try {
2170-
// eslint-disable-next-line no-eval
2171-
options = (await eval(`import("${pathToFileURL(configPath)}")`)).default;
2172+
options = // eslint-disable-next-line no-eval
2173+
(await eval(`import("${isFileURL ? configPath : pathToFileURL(configPath)}")`)).default;
21722174
} catch (err) {
21732175
if (this.isValidationError(err) || process.env?.WEBPACK_CLI_FORCE_LOAD_ESM_CONFIG) {
21742176
throw err;
@@ -2209,7 +2211,7 @@ class WebpackCLI {
22092211
}
22102212

22112213
try {
2212-
options = require(configPath);
2214+
options = require(isFileURL ? fileURLToPath(configPath) : path.resolve(configPath));
22132215
} catch (err) {
22142216
if (this.isValidationError(err)) {
22152217
throw err;
@@ -2301,9 +2303,7 @@ class WebpackCLI {
23012303

23022304
if (options.config && options.config.length > 0) {
23032305
const loadedConfigs = await Promise.all(
2304-
options.config.map((configPath: string) =>
2305-
loadConfigByPath(path.resolve(configPath), options.argv),
2306-
),
2306+
options.config.map((configPath: string) => loadConfigByPath(configPath, options.argv)),
23072307
);
23082308

23092309
if (loadedConfigs.length === 1) {
@@ -2406,9 +2406,7 @@ class WebpackCLI {
24062406
delete config.extends;
24072407

24082408
const loadedConfigs = await Promise.all(
2409-
extendsPaths.map((extendsPath) =>
2410-
loadConfigByPath(path.resolve(extendsPath), options.argv),
2411-
),
2409+
extendsPaths.map((extendsPath) => loadConfigByPath(extendsPath, options.argv)),
24122410
);
24132411

24142412
const { merge } = await import("webpack-merge");

test/build/basic/basic.test.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"use strict";
22

3-
const { resolve } = require("node:path");
43
const { run } = require("../../utils/test-utils");
54

65
describe("bundle command", () => {
@@ -181,11 +180,10 @@ describe("bundle command", () => {
181180

182181
it("should log supplied config when logging level is log", async () => {
183182
const { exitCode, stderr, stdout } = await run(__dirname, ["--config", "./log.config.js"]);
184-
const configPath = resolve(__dirname, "./log.config.js");
185183

186184
expect(exitCode).toBe(0);
187185
expect(stderr).toContain("Compiler starting...");
188-
expect(stderr).toContain(`Compiler is using config: '${configPath}'`);
186+
expect(stderr).toContain("Compiler is using config: './log.config.js'");
189187
expect(stderr).toContain("Compiler finished");
190188
expect(stdout).toBeTruthy();
191189
});
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
const path = require("node:path");
2-
31
const { run } = require("../../../utils/test-utils");
42

53
describe("failure", () => {
64
it("should log error on not installed registers", async () => {
75
const { exitCode, stderr, stdout } = await run(__dirname, ["-c", "webpack.config.iced"]);
86

97
expect(exitCode).toBe(2);
10-
expect(stderr).toContain(
11-
`Failed to load '${path.resolve(__dirname, "./webpack.config.iced")}'`,
12-
);
8+
expect(stderr).toContain("Failed to load 'webpack.config.iced'");
139
expect(stdout).toBeFalsy();
1410
});
1511
});

test/build/config-format/failure/webpack.config.json5

Lines changed: 0 additions & 4 deletions
This file was deleted.
Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,53 @@
11
"use strict";
22

33
const { resolve } = require("node:path");
4+
const { pathToFileURL } = require("node:url");
45
const { run } = require("../../../utils/test-utils");
56

67
describe("basic config file", () => {
78
it("should build and not throw error with a basic configuration file", async () => {
9+
const { exitCode, stderr, stdout } = await run(__dirname, ["-c", "webpack.config.js"]);
10+
console.log(stdout);
11+
expect(exitCode).toBe(0);
12+
expect(stderr).toBeFalsy();
13+
expect(stdout).toBeTruthy();
14+
});
15+
16+
it("should build and not throw error with a basic configuration file using relative path", async () => {
17+
const { exitCode, stderr, stdout } = await run(__dirname, ["-c", "./webpack.config.js"]);
18+
expect(exitCode).toBe(0);
19+
expect(stderr).toBeFalsy();
20+
expect(stdout).toBeTruthy();
21+
});
22+
23+
it("should build and not throw error with a basic configuration file using absolute path", async () => {
824
const { exitCode, stderr, stdout } = await run(__dirname, [
925
"-c",
1026
resolve(__dirname, "webpack.config.js"),
11-
"--output-path",
12-
"./binary",
1327
]);
1428
expect(exitCode).toBe(0);
1529
expect(stderr).toBeFalsy();
1630
expect(stdout).toBeTruthy();
1731
});
32+
33+
it("should build and not throw error with a basic configuration file using file protocol", async () => {
34+
const { exitCode, stderr, stdout } = await run(__dirname, [
35+
"-c",
36+
pathToFileURL(resolve(__dirname, "webpack.config.js")).toString(),
37+
]);
38+
expect(exitCode).toBe(0);
39+
expect(stderr).toBeFalsy();
40+
expect(stdout).toBeTruthy();
41+
});
42+
43+
it("should build and not throw error with a basic using weird path", async () => {
44+
const { exitCode, stderr, stdout } = await run(__dirname, [
45+
"-c",
46+
"../basic/./webpack.config.js",
47+
]);
48+
console.log(stdout);
49+
expect(exitCode).toBe(0);
50+
expect(stderr).toBeFalsy();
51+
expect(stdout).toBeTruthy();
52+
});
1853
});

test/build/config/error-array/__snapshots__/config-array-error.test.js.snap.webpack5

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
22

33
exports[`config with invalid array syntax should throw syntax error and exit with non-zero exit code when even 1 object has syntax error 1`] = `
4-
"[webpack-cli] Failed to load '<cwd>/test/build/config/error-array/webpack.config.js' config
4+
"[webpack-cli] Failed to load './webpack.config.js' config
55
▶ ESM (\`import\`) failed:
66
Unexpected token ';'
77

test/build/extends/extends.test.js

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use strict";
22

33
const path = require("node:path");
4+
const { pathToFileURL } = require("node:url");
45
const { run } = require("../../utils/test-utils");
56

67
describe("extends property", () => {
@@ -15,6 +16,38 @@ describe("extends property", () => {
1516
expect(stdout).toContain("mode: 'development'");
1617
});
1718

19+
it("extends a provided webpack config correctly using file protocol", async () => {
20+
const { exitCode, stderr, stdout } = await run(path.resolve(__dirname, "./file-protocol"));
21+
22+
expect(exitCode).toBe(0);
23+
expect(stderr).toBeFalsy();
24+
expect(stdout).toContain("base.webpack.config.js");
25+
expect(stdout).toContain("derived.webpack.config.js");
26+
expect(stdout).toContain("name: 'base_config'");
27+
expect(stdout).toContain("mode: 'development'");
28+
});
29+
30+
it("extends a provided webpack config correctly using `require.resolve`", async () => {
31+
const { exitCode, stderr, stdout } = await run(path.resolve(__dirname, "./require-resolve"));
32+
33+
expect(exitCode).toBe(0);
34+
expect(stderr).toBeFalsy();
35+
expect(stdout).toContain("base.webpack.config.js");
36+
expect(stdout).toContain("derived.webpack.config.js");
37+
expect(stdout).toContain("name: 'base_config'");
38+
expect(stdout).toContain("mode: 'development'");
39+
});
40+
41+
it("extends a provided webpack config correctly using `JSON` format", async () => {
42+
const { exitCode, stderr, stdout } = await run(path.resolve(__dirname, "./json"));
43+
44+
expect(exitCode).toBe(0);
45+
expect(stderr).toBeFalsy();
46+
expect(stdout).toContain("derived.webpack.config.js");
47+
expect(stdout).toContain("name: 'base_config'");
48+
expect(stdout).toContain("mode: 'development'");
49+
});
50+
1851
it("extends a provided array of webpack configs correctly", async () => {
1952
const { exitCode, stderr, stdout } = await run(path.resolve(__dirname, "./multiple-extends"));
2053

@@ -89,7 +122,7 @@ describe("extends property", () => {
89122
"--extends",
90123
"./base.webpack.config.js",
91124
"--extends",
92-
"./other.config.js",
125+
path.resolve(__dirname, "./multiple-configs1/other.config.js"),
93126
]);
94127

95128
expect(exitCode).toBe(0);
@@ -102,7 +135,7 @@ describe("extends property", () => {
102135
expect(stdout).toContain("topLevelAwait: true");
103136
});
104137

105-
it("cLI `extends` should override `extends` in a configuration", async () => {
138+
it("`extends` should override `extends` in a configuration", async () => {
106139
const { exitCode, stderr, stdout } = await run(path.resolve(__dirname, "./simple-case"), [
107140
"--extends",
108141
"./override.config.js",
@@ -116,6 +149,20 @@ describe("extends property", () => {
116149
expect(stdout).toContain("mode: 'development'");
117150
});
118151

152+
it("`extends` should override `extends` in a configuration using file protocol", async () => {
153+
const { exitCode, stderr, stdout } = await run(path.resolve(__dirname, "./simple-case"), [
154+
"--extends",
155+
pathToFileURL(path.resolve(__dirname, "./simple-case/override.config.js")).toString(),
156+
]);
157+
158+
expect(exitCode).toBe(0);
159+
expect(stderr).toBeFalsy();
160+
expect(stdout).toContain("override.config.js");
161+
expect(stdout).toContain("derived.webpack.config.js");
162+
expect(stdout).toContain("name: 'override_config'");
163+
expect(stdout).toContain("mode: 'development'");
164+
});
165+
119166
it("should throw an error on recursive", async () => {
120167
const { exitCode, stderr, stdout } = await run(path.resolve(__dirname, "./recursive-extends"));
121168

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default () => {
2+
console.log("base.webpack.config.js");
3+
4+
return {
5+
name: "base_config",
6+
mode: "development",
7+
entry: "./src/index.js",
8+
};
9+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default () => {
2+
console.log("override.config.js");
3+
4+
return {
5+
name: "override_config",
6+
mode: "development",
7+
entry: "./src/index.js",
8+
};
9+
};

0 commit comments

Comments
 (0)