feat: enable ESM worker chunk loading for Node.js targets (#19640)
Github Actions / lint (push) Has been cancelled Details
Github Actions / validate-legacy-node (push) Has been cancelled Details
Github Actions / benchmark (1/4) (push) Has been cancelled Details
Github Actions / benchmark (2/4) (push) Has been cancelled Details
Github Actions / benchmark (3/4) (push) Has been cancelled Details
Github Actions / benchmark (4/4) (push) Has been cancelled Details
Github Actions / basic (push) Has been cancelled Details
Github Actions / unit (push) Has been cancelled Details
Github Actions / integration (10.x, macos-latest, a) (push) Has been cancelled Details
Github Actions / integration (10.x, macos-latest, b) (push) Has been cancelled Details
Github Actions / integration (10.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (10.x, ubuntu-latest, b) (push) Has been cancelled Details
Github Actions / integration (10.x, windows-latest, a) (push) Has been cancelled Details
Github Actions / integration (10.x, windows-latest, b) (push) Has been cancelled Details
Github Actions / integration (12.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (14.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (16.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (18.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (20.x, macos-latest, a) (push) Has been cancelled Details
Github Actions / integration (20.x, macos-latest, b) (push) Has been cancelled Details
Github Actions / integration (20.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (20.x, ubuntu-latest, b) (push) Has been cancelled Details
Github Actions / integration (20.x, windows-latest, a) (push) Has been cancelled Details
Github Actions / integration (20.x, windows-latest, b) (push) Has been cancelled Details
Github Actions / integration (22.x, macos-latest, a) (push) Has been cancelled Details
Github Actions / integration (22.x, macos-latest, b) (push) Has been cancelled Details
Github Actions / integration (22.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (22.x, ubuntu-latest, b) (push) Has been cancelled Details
Github Actions / integration (22.x, windows-latest, a) (push) Has been cancelled Details
Github Actions / integration (22.x, windows-latest, b) (push) Has been cancelled Details
Github Actions / integration (24.x, macos-latest, a) (push) Has been cancelled Details
Github Actions / integration (24.x, macos-latest, b) (push) Has been cancelled Details
Github Actions / integration (24.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (24.x, ubuntu-latest, b) (push) Has been cancelled Details
Github Actions / integration (24.x, windows-latest, a) (push) Has been cancelled Details
Github Actions / integration (24.x, windows-latest, b) (push) Has been cancelled Details
Github Actions / integration (lts/*, ubuntu-latest, a, 1) (push) Has been cancelled Details
Github Actions / integration (lts/*, ubuntu-latest, b, 1) (push) Has been cancelled Details

This commit is contained in:
Ryuya 2025-07-03 08:09:39 -07:00 committed by GitHub
parent eaeea9c512
commit a11302288d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 329 additions and 4 deletions

View File

@ -1242,7 +1242,7 @@ const applyOutputDefaults = (
tp.nodeBuiltins === null ||
tp.importScriptsInWorker === null) &&
output.module &&
environment.dynamicImport
environment.dynamicImportInWorker
) {
return "universal";
}

View File

@ -194,7 +194,7 @@ You can also more options via the 'target' option: 'browserslist' / 'browserslis
destructuring: v(6),
bigIntLiteral: v(10, 4),
dynamicImport: v(12, 17),
dynamicImportInWorker: major ? false : undefined,
dynamicImportInWorker: v(12, 17),
module: v(12, 17)
};
}
@ -240,7 +240,7 @@ You can also more options via the 'target' option: 'browserslist' / 'browserslis
destructuring: v(1, 1),
bigIntLiteral: v(4),
dynamicImport: v(11),
dynamicImportInWorker: major ? false : undefined,
dynamicImportInWorker: v(11),
module: v(11)
};
}
@ -278,7 +278,7 @@ You can also more options via the 'target' option: 'browserslist' / 'browserslis
destructuring: v(0, 15),
bigIntLiteral: v(0, 32),
dynamicImport: v(0, 43),
dynamicImportInWorker: major ? false : undefined,
dynamicImportInWorker: v(0, 44),
module: v(0, 43)
};
}

View File

@ -2688,6 +2688,183 @@ describe("snapshots", () => {
+ /^(.+?[\\\\/]node_modules[\\\\/])/,
`)
);
test(
"target node with ESM output",
{ target: "node14", experiments: { outputModule: true } },
e =>
e.toMatchInlineSnapshot(`
- Expected
+ Received
@@ ... @@
- "outputModule": false,
+ "outputModule": true,
@@ ... @@
- "node": false,
+ "node": true,
@@ ... @@
- "web": true,
+ "web": false,
@@ ... @@
- "externalsType": "var",
+ "externalsType": "module-import",
@@ ... @@
- "document": true,
- "dynamicImport": undefined,
- "dynamicImportInWorker": undefined,
+ "document": false,
+ "dynamicImport": true,
+ "dynamicImportInWorker": true,
@@ ... @@
- "globalThis": undefined,
- "module": undefined,
- "nodePrefixForCoreModules": true,
+ "globalThis": true,
+ "module": true,
+ "nodePrefixForCoreModules": false,
@@ ... @@
- "target": "web",
+ "target": "node",
@@ ... @@
- "createRequire": false,
+ "createRequire": true,
@@ ... @@
- "__dirname": "mock",
- "__filename": "mock",
- "global": true,
+ "__dirname": "node-module",
+ "__filename": "node-module",
+ "global": false,
@@ ... @@
- "chunkFilename": "[name].js",
- "chunkFormat": "array-push",
+ "chunkFilename": "[name].mjs",
+ "chunkFormat": "module",
@@ ... @@
- "chunkLoading": "jsonp",
+ "chunkLoading": "import",
@@ ... @@
- "jsonp",
- "import-scripts",
+ "import",
@@ ... @@
- "fetch",
+ "async-node",
@@ ... @@
- "document": true,
- "dynamicImport": undefined,
- "dynamicImportInWorker": undefined,
+ "document": false,
+ "dynamicImport": true,
+ "dynamicImportInWorker": true,
@@ ... @@
- "globalThis": undefined,
- "module": undefined,
- "nodePrefixForCoreModules": true,
+ "globalThis": true,
+ "module": true,
+ "nodePrefixForCoreModules": false,
@@ ... @@
- "filename": "[name].js",
- "globalObject": "self",
+ "filename": "[name].mjs",
+ "globalObject": "global",
@@ ... @@
- "hotUpdateChunkFilename": "[id].[fullhash].hot-update.js",
+ "hotUpdateChunkFilename": "[id].[fullhash].hot-update.mjs",
@@ ... @@
- "hotUpdateMainFilename": "[runtime].[fullhash].hot-update.json",
+ "hotUpdateMainFilename": "[runtime].[fullhash].hot-update.json.mjs",
@@ ... @@
- "iife": true,
+ "iife": false,
@@ ... @@
- "module": false,
+ "module": true,
@@ ... @@
- "scriptType": false,
+ "scriptType": "module",
@@ ... @@
- "wasmLoading": "fetch",
+ "wasmLoading": "async-node",
@@ ... @@
- "workerChunkLoading": "import-scripts",
+ "workerChunkLoading": "import",
@@ ... @@
- "workerWasmLoading": "fetch",
+ "workerWasmLoading": "async-node",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],
+ "aliasFields": Array [],
@@ ... @@
- "browser",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],
+ "aliasFields": Array [],
@@ ... @@
- "browser",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],
+ "aliasFields": Array [],
@@ ... @@
- "browser",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],
+ "aliasFields": Array [],
@@ ... @@
- "browser",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],
+ "aliasFields": Array [],
@@ ... @@
- "browser",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],
+ "aliasFields": Array [],
@@ ... @@
- "browser",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],
+ "aliasFields": Array [],
@@ ... @@
- "browser",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],
+ "aliasFields": Array [],
@@ ... @@
- "browser",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],
+ "aliasFields": Array [],
@@ ... @@
- "browser",
@@ ... @@
- "browser",
+ "node",
@@ ... @@
- "target": "web",
+ "target": "node14",
`)
);
});
describe("Targets", () => {
@ -2709,6 +2886,12 @@ describe("Targets", () => {
- Expected
+ Received
@@ ... @@
- "dynamicImportInWorker": true,
+ "dynamicImportInWorker": false,
@@ ... @@
- "dynamicImportInWorker": true,
+ "dynamicImportInWorker": false,
@@ ... @@
- "target": "node12.17",
+ "target": "browserslist: node 12.17",

View File

@ -0,0 +1,3 @@
export function getMessage(msg) {
return msg + " from worker with ESM import";
}

View File

@ -0,0 +1,22 @@
import { Worker } from "worker_threads";
it("should support ESM worker chunks in Node.js", async () => {
const worker = new Worker(
new URL("./worker.js" + __resourceQuery, import.meta.url),
{
type: "module"
}
);
const promise = new Promise((resolve, reject) => {
worker.on("message", resolve);
worker.on("error", reject);
});
worker.postMessage("hello");
const result = await promise;
expect(result).toBe("hello from worker with ESM import");
await worker.terminate();
});

View File

@ -0,0 +1,20 @@
const fs = require("fs");
const path = require("path");
module.exports = {
findBundle() {
return "./bundle.js";
},
afterExecute(options) {
const workerCode = fs.readFileSync(
path.resolve(options.output.path, "./worker_js.bundle.js"),
"utf8"
);
if (!/ReadFile \+ VM\.run chunk loading for javascript/.test(workerCode)) {
throw new Error(
"require('fs').readFile(...) and require('vm').runInThisContext were not found in the worker code for loading async chunks"
);
}
}
};

View File

@ -0,0 +1,3 @@
const supportsWorker = require("../../../helpers/supportsWorker");
module.exports = () => supportsWorker();

View File

@ -0,0 +1,11 @@
/** @type {import("../../../../").Configuration} */
module.exports = {
target: "async-node14",
entry: "./index.js",
optimization: {
chunkIds: "named"
},
output: {
filename: "bundle.js"
}
};

View File

@ -0,0 +1,8 @@
import { parentPort } from "worker_threads";
const { getMessage } = await import("./chunk.js");
parentPort.on("message", (msg) => {
// Worker with ESM import
parentPort.postMessage(getMessage(msg));
});

View File

@ -0,0 +1,3 @@
export function getMessage(msg) {
return msg + " from worker with ESM import";
}

View File

@ -0,0 +1,22 @@
import { Worker } from "worker_threads";
it("should support ESM worker chunks in Node.js", async () => {
const worker = new Worker(
new URL("./worker.js" + __resourceQuery, import.meta.url),
{
type: "module"
}
);
const promise = new Promise((resolve, reject) => {
worker.on("message", resolve);
worker.on("error", reject);
});
worker.postMessage("hello");
const result = await promise;
expect(result).toBe("hello from worker with ESM import");
await worker.terminate();
});

View File

@ -0,0 +1,24 @@
const fs = require("fs");
const path = require("path");
const { URL } = require("url");
module.exports = {
findBundle() {
return "./bundle.mjs";
},
moduleScope(scope) {
scope.URL = URL;
},
afterExecute(options) {
const workerCode = fs.readFileSync(
path.resolve(options.output.path, "./worker_js.bundle.mjs"),
"utf8"
);
if (!/import\(\) chunk loading for javascript/.test(workerCode)) {
throw new Error(
"import() was not found in the worker code for loading async chunks"
);
}
}
};

View File

@ -0,0 +1,3 @@
const supportsWorker = require("../../../helpers/supportsWorker");
module.exports = () => supportsWorker();

View File

@ -0,0 +1,15 @@
/** @type {import("../../../../").Configuration} */
module.exports = {
target: "node14",
entry: "./index.js",
optimization: {
chunkIds: "named"
},
output: {
module: true,
filename: "bundle.mjs"
},
experiments: {
outputModule: true
}
};

View File

@ -0,0 +1,8 @@
import { parentPort } from "worker_threads";
const { getMessage } = await import("./chunk.js");
parentPort.on("message", (msg) => {
// Worker with ESM import
parentPort.postMessage(getMessage(msg));
});