Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/update-ejs-v7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-webpack-app": patch
---

chore: update `ejs` to v7
5 changes: 5 additions & 0 deletions .changeset/update-rechoir-v1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"webpack-cli": patch
---

chore: update `rechoir` to v1, which resolves loaders with Node.js's own resolver and drops the `resolve` dependency
2 changes: 1 addition & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [20.x, 22.x, 24.x, 25.x, 26.x]
node-version: [20.x, 22.x, 24.x, 26.x]
webpack-version: [latest]
dev-server-version: [5, latest]

Expand Down
11,386 changes: 4,945 additions & 6,441 deletions package-lock.json

Large diffs are not rendered by default.

48 changes: 24 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,60 +47,60 @@
"release": "npm run build && changeset publish",
"prepare": "husky"
},
"allowScripts": {
"@parcel/watcher": true,
"esbuild": true,
"@swc/core": true,
"unrs-resolver": true
},
"devDependencies": {
"@bomb.sh/tab": "^0.0.22",
"@babel/core": "^8.0.1",
"@babel/preset-env": "^8.0.2",
"@babel/register": "^8.0.1",
"@babel/core": "^8.0.7",
"@babel/preset-env": "^8.0.7",
"@babel/register": "^8.0.6",
"@changesets/cli": "^3.0.1",
"@changesets/get-github-info": "^1.0.0",
"@swc/core": "^1.16.1",
"@swc/core": "^1.16.18",
"@swc/jest": "^0.2.39",
"@types/jest": "^30.0.0",
"@types/js-yaml": "^4.0.9",
"@types/node": "^26.4.0",
"@types/node": "^26.6.5",
"@types/rechoir": "^0.6.4",
"c8": "^12.0.0",
"coffeescript": "^2.7.0",
"colorette": "^2.0.20",
"concat-stream": "^2.0.0",
"cspell": "^10.1.1",
"cspell": "^10.4.0",
"css-loader": "^7.1.4",
"del-cli": "^7.0.0",
"eslint": "^10.9.1",
"eslint-config-webpack": "^4.9.6",
"eslint": "^10.12.0",
"eslint-config-webpack": "^4.15.0",
"execa": "^9.6.1",
"get-port": "^7.2.0",
"husky": "^9.1.7",
"jest": "^30.4.2",
"js-yaml": "^5.4.1",
"jest": "^30.5.2",
"js-yaml": "^5.5.0",
"json5": "^2.2.3",
"lint-staged": "^17.4.1",
"lint-staged": "^17.6.0",
"mini-css-extract-plugin": "^2.10.2",
"pkg-pr-new": "^0.0.88",
"prettier": "^3.9.6",
"readable-stream": "^4.7.0",
"sass": "^1.103.1",
"pkg-pr-new": "^0.0.91",
"prettier": "^3.9.10",
"readable-stream": "^4.8.0",
"sass": "^1.105.2",
"sass-loader": "^17.0.0",
"style-loader": "^4.0.0",
"ts-loader": "^9.6.2",
"ts-node": "^10.9.2",
"tsx": "^4.23.12",
"tsx": "^4.23.15",
"typescript": "^6.0.3",
"webpack": "^5.110.1",
"webpack-bundle-analyzer": "^5.3.2",
"webpack": "^5.112.0",
"webpack-bundle-analyzer": "^5.4.0",
"webpack-dev-server": "^6.0.0"
},
"peerDependencies": {
"webpack": "5.x.x"
},
"engines": {
"node": ">=20.9.0"
},
"allowScripts": {
"@parcel/watcher": true,
"esbuild": true,
"@swc/core": true,
"unrs-resolver": true
}
}
6 changes: 3 additions & 3 deletions packages/create-webpack-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@
"watch": "tsc --watch"
},
"dependencies": {
"@inquirer/expand": "^5.1.5",
"@inquirer/select": "^5.2.5",
"@inquirer/expand": "^5.1.6",
"@inquirer/select": "^5.2.6",
"colorette": "^2.0.20",
"commander": "^14.0.3",
"cross-spawn": "^7.0.6",
"ejs": "^6.0.1",
"ejs": "^7.0.1",
"node-plop": "^0.32.3"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/webpack-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"envinfo": "^7.21.0",
"import-local": "^3.2.0",
"interpret": "^3.1.1",
"rechoir": "^0.8.0",
"rechoir": "^1.0.0",
Comment thread
alexander-akait marked this conversation as resolved.
"webpack-merge": "^6.0.1"
},
"devDependencies": {
Expand Down
37 changes: 24 additions & 13 deletions packages/webpack-cli/src/webpack-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3102,8 +3102,8 @@ class WebpackCLI {

// `tsx` is missing from `interpret`'s loader tables, and it cannot
// be added there: its `tsx/cjs` entry point exists only in the
// package's `exports` map, which `rechoir`'s resolver does not
// support. Register it manually instead, resolved from the
// package's `exports` map, which `rechoir` < 1 could not resolve.
// Register it manually instead, resolved from the
// configuration file's location so the user's copy is picked up.
// Requiring `tsx/cjs` installs its require hook as a side effect.
const registerTsxLoader = (): Error | undefined => {
Expand Down Expand Up @@ -3260,7 +3260,7 @@ class WebpackCLI {
} else {
config.options = [];
for (const loadedConfig of loadedConfigs) {
if (Array.isArray(loadedConfig.options)) {
if (this.isMultipleConfiguration(loadedConfig.options)) {
for (const item of loadedConfig.options) {
(config.options as Configuration[]).push(item);
config.path.set(options, [loadedConfig.path]);
Expand Down Expand Up @@ -3652,17 +3652,28 @@ class WebpackCLI {
const config = await this.loadConfig(options);
let compiler: Compiler | MultiCompiler;

try {
compiler = callback
? webpack(config.options, (error, stats) => {
if (error && this.isValidationError(error)) {
this.logger.error(error.message);
process.exit(2);
}
const webpackCallback = callback
? (error: Error | null, stats: Stats | MultiStats | undefined) => {
if (error && this.isValidationError(error)) {
this.logger.error(error.message);
process.exit(2);
}

callback(error as Error | null, stats);
})!
: webpack(config.options);
(callback as (error: Error | null, stats?: Stats | MultiStats) => void)(error, stats);
}
: undefined;

try {
// Branch on the configuration shape so each call matches one `webpack()` overload
if (this.isMultipleConfiguration(config.options)) {
compiler = webpackCallback
? webpack(config.options, webpackCallback)!
: webpack(config.options);
} else {
compiler = webpackCallback
? webpack(config.options, webpackCallback)!
: webpack(config.options);
}
} catch (error) {
if (this.isValidationError(error)) {
this.logger.error(error.message);
Expand Down
60 changes: 60 additions & 0 deletions test/api/clean-stderr.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"use strict";

const { cleanStderr } = require("../utils/test-utils");

const TRACE_WARNINGS_HINT =
"(Use `node --trace-warnings ...` to show where the warning was created)";
const TRACE_DEPRECATION_HINT =
"(Use `node --trace-deprecation ...` to show where the warning was created)";

describe("cleanStderr", () => {
it("should return non-string values as is", () => {
expect(cleanStderr(undefined)).toBeUndefined();
});

it("should drop a Node.js experimental warning and its hint", () => {
expect(
cleanStderr(
`(node:1) ExperimentalWarning: Type Stripping is experimental\n${TRACE_WARNINGS_HINT}\n`,
),
).toBe("");
});

it("should drop a multi-line Node.js warning up to its hint", () => {
expect(
cleanStderr(
`(node:1) ExperimentalWarning: first line\nsecond line\n${TRACE_WARNINGS_HINT}\n(node:1) ExperimentalWarning: again\n`,
),
).toBe("");
});

it("should drop Node.js core deprecations raised inside dependencies", () => {
expect(
cleanStderr(`(node:1) [DEP0040] DeprecationWarning: punycode\n${TRACE_DEPRECATION_HINT}`),
).toBe("");
});

it("should drop webpack infrastructure info logs", () => {
expect(cleanStderr("<i> [webpack-bundle-analyzer] Started\n<i> Use Ctrl+C to close it\n")).toBe(
"",
);
});

it("should keep webpack deprecations, warnings and errors", () => {
const stderr = [
"(node:1) [DEP_WEBPACK_COMPILATION_ASSETS] DeprecationWarning: deprecated",
"<w> [plugin] warning",
"<e> [plugin] error",
].join("\n");

expect(cleanStderr(stderr)).toBe(stderr);
});

it("should keep CLI output found between a warning without hint and a later hint", () => {
expect(
cleanStderr(
`(node:1) ExperimentalWarning: no hint here\n[webpack-cli] Error: boom\n${TRACE_WARNINGS_HINT}`,
),
).toContain("[webpack-cli] Error: boom");
});
});
14 changes: 8 additions & 6 deletions test/build/analyze/analyze-flag.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";

const { normalizeStdout, run } = require("../../utils/test-utils");
const { cleanStderr, normalizeStdout, run } = require("../../utils/test-utils");

describe('"analyze" option', () => {
it("should not load webpack-bundle-analyzer plugin twice with --analyze flag and plugin", async () => {
Expand All @@ -11,10 +11,12 @@ describe('"analyze" option', () => {
]);

expect(exitCode).toBe(0);
expect(stderr).toBeFalsy();
expect(normalizeStdout(stdout)).toContain("Webpack Bundle Analyzer saved report to");
expect(normalizeStdout(stdout).match(/Webpack Bundle Analyzer saved report to/g)).toHaveLength(
1,
);
expect(cleanStderr(stderr)).toBeFalsy();

// webpack-bundle-analyzer moved its messages between stdout and stderr across releases,
// so look for them in both
const output = normalizeStdout(`${stdout}\n${stderr}`);

expect(output.match(/Webpack Bundle Analyzer saved report to/g)).toHaveLength(1);
});
});
6 changes: 3 additions & 3 deletions test/build/bail/bail.test.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
"use strict";

const { run } = require("../../utils/test-utils");
const { cleanStderr, run } = require("../../utils/test-utils");

describe("bail and watch warning", () => {
it("should not log warning in not watch mode", async () => {
const { exitCode, stderr, stdout } = await run(__dirname, ["-c", "bail-webpack.config.js"]);

expect(exitCode).toBe(0);
expect(stderr).toBeFalsy();
expect(cleanStderr(stderr)).toBeFalsy();
expect(stdout).toBeTruthy();
});

it('should not log warning in not watch mode without the "bail" option', async () => {
const { exitCode, stderr, stdout } = await run(__dirname, ["-c", "no-bail-webpack.config.js"]);

expect(exitCode).toBe(0);
expect(stderr).toBeFalsy();
expect(cleanStderr(stderr)).toBeFalsy();
expect(stdout).toBeTruthy();
});
});
Loading
Loading