From be2f4f11d8213bde21eef459e8aed80946e8960c Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:29:06 +0000 Subject: [PATCH 1/2] chore: load configuration files without eval TypeScript with module nodenext keeps import() in the CommonJS output, so eval is no longer needed to stop it turning into require(). Claude-Session: https://claude.ai/code/session_013FhutRFjuQemBzekJMYcqL --- .changeset/load-config-without-eval.md | 5 +++++ packages/webpack-cli/src/webpack-cli.ts | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 .changeset/load-config-without-eval.md diff --git a/.changeset/load-config-without-eval.md b/.changeset/load-config-without-eval.md new file mode 100644 index 00000000000..e0e5e58f5db --- /dev/null +++ b/.changeset/load-config-without-eval.md @@ -0,0 +1,5 @@ +--- +"webpack-cli": patch +--- + +chore: load configuration files with `import()` instead of `eval` diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 12eae0a3794..250cd0558bf 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -3338,9 +3338,8 @@ class WebpackCLI { let loadingError; try { - options = // eslint-disable-next-line no-eval - (await eval(`import("${isFileURL ? configPath : pathToFileURL(configPath)}")`)) - .default; + options = (await import(isFileURL ? configPath : pathToFileURL(configPath).href)) + .default; } catch (err) { if (this.isValidationError(err) || process.env?.WEBPACK_CLI_FORCE_LOAD_ESM_CONFIG) { throw err; From 42cf33bfadddfb2cc964f1ce21401c29811758de Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:59:48 +0000 Subject: [PATCH 2/2] test: load a configuration path only as a module specifier Claude-Session: https://claude.ai/code/session_013FhutRFjuQemBzekJMYcqL --- test/build/config/basic/basic-config.test.js | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/build/config/basic/basic-config.test.js b/test/build/config/basic/basic-config.test.js index 4f861f55f71..b7f44998d05 100644 --- a/test/build/config/basic/basic-config.test.js +++ b/test/build/config/basic/basic-config.test.js @@ -1,5 +1,8 @@ "use strict"; +const fs = require("node:fs"); +const os = require("node:os"); +const path = require("node:path"); const { resolve } = require("node:path"); const { pathToFileURL } = require("node:url"); const { cleanStderr, run } = require("../../../utils/test-utils"); @@ -40,6 +43,24 @@ describe("basic config file", () => { expect(stdout).toBeTruthy(); }); + it("should load a configuration path as a module specifier and not run it as code", async () => { + const marker = path.join( + fs.mkdtempSync(path.join(os.tmpdir(), "webpack-cli-config-path-")), + "executed", + ); + const configPath = `file:///webpack.config.js"),require("node:fs").writeFileSync(${JSON.stringify(marker)},""),import("file:///webpack.config.js`; + + try { + const { exitCode, stderr } = await run(__dirname, ["-c", configPath]); + + expect(exitCode).toBe(2); + expect(stderr).toContain("Failed to load"); + expect(fs.existsSync(marker)).toBe(false); + } finally { + fs.rmSync(path.dirname(marker), { recursive: true, force: true }); + } + }); + it("should build and not throw error with a basic using weird path", async () => { const { exitCode, stderr, stdout } = await run(__dirname, [ "-c",