Commit f53f438a3b5 for nodejs
commit f53f438a3b5a5392656150060951d72d27030e70
Author: Filip Skokan <panva.ip@gmail.com>
Date: Sun Sep 27 19:02:12 2026 +0200
worker: preserve blob module URLs
Evaluate blob module sources under their original URL instead of
rewrapping them in a data URL. This preserves import.meta.url and module
identity while retaining the captured source after URL revocation.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66354
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
diff --git a/lib/internal/webworker.js b/lib/internal/webworker.js
index e798c814f28..cf5f1df6372 100644
--- a/lib/internal/webworker.js
+++ b/lib/internal/webworker.js
@@ -20,8 +20,6 @@ const {
SymbolFor,
SymbolIterator,
SymbolToStringTag,
- TypedArrayPrototypeGetLength,
- Uint8Array,
globalThis,
} = primordials;
@@ -77,10 +75,6 @@ const {
vm_dynamic_import_default_internal,
} = internalBinding('symbols');
-const {
- base64Slice,
-} = internalBinding('buffer');
-
const {
hasOpenSSL,
} = internalBinding('config');
@@ -757,10 +751,9 @@ const convertWorkerOptions = createDictionaryConverter(
/**
* @param {URL} workerURL The parsed URL of the worker script.
- * @param {'classic'|'module'} type The worker's type.
* @returns {{ value?: URL, source?: string }|null}
*/
-function resolveWorkerEntry(workerURL, type) {
+function resolveWorkerEntry(workerURL) {
switch (workerURL.protocol) {
case 'file:':
return { value: workerURL };
@@ -779,17 +772,6 @@ function resolveWorkerEntry(workerURL, type) {
if (data === undefined) {
return null;
}
- if (type === 'module') {
- // The blob's contents are re-wrapped as a data: URL so that the
- // module loader evaluates them as a module script.
- // TODO(@avivkeller): What if we update the ESM loader to accept blob:
- // urls?
- const bytes = new Uint8Array(data);
- return {
- value: new URL('data:text/javascript;base64,' + base64Slice(
- bytes, 0, TypedArrayPrototypeGetLength(bytes))),
- };
- }
return { source: utf8Decode(data) };
}
default:
@@ -824,7 +806,7 @@ class Worker extends EventTarget {
}
this[kWorker] = null;
- const entry = resolveWorkerEntry(workerURL, options.type);
+ const entry = resolveWorkerEntry(workerURL);
if (entry === null) {
// "If the algorithm asynchronously completes with null or with a
// script whose error to rethrow is non-null, then: Queue a global
diff --git a/test/parallel/test-webworker-blob-module-url.js b/test/parallel/test-webworker-blob-module-url.js
new file mode 100644
index 00000000000..7e51b727446
--- /dev/null
+++ b/test/parallel/test-webworker-blob-module-url.js
@@ -0,0 +1,19 @@
+// Flags: --experimental-web-worker
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto) common.skip('missing crypto');
+
+const assert = require('node:assert');
+const source = `
+ import value from 'data:text/javascript,export default 42';
+ postMessage([import.meta.url, location.href, value]);
+`;
+const url = URL.createObjectURL(new Blob([source], { type: 'text/javascript' }));
+const worker = new Worker(url, { type: 'module' });
+URL.revokeObjectURL(url);
+worker.onerror = common.mustNotCall('worker failed');
+worker.onmessage = common.mustCall(({ data }) => {
+ worker.terminate();
+ assert.deepStrictEqual(data, [url, url, 42]);
+});