Skip to content

Commit 2e5f780

Browse files
committed
worker_threads: add support for data URLs
1 parent 4ed31e6 commit 2e5f780

File tree

4 files changed

+61
-22
lines changed

4 files changed

+61
-22
lines changed

doc/api/worker_threads.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,8 @@ changes:
588588
* `filename` {string|URL} The path to the Worker’s main script or module. Must
589589
be either an absolute path or a relative path (i.e. relative to the
590590
current working directory) starting with `./` or `../`, or a WHATWG `URL`
591-
object using `file:` protocol.
591+
object using `file:` or `data:` protocol.
592+
When using a [`data:` URL][], you must set the `type` option to `"module"`.
592593
If `options.eval` is `true`, this is a string containing JavaScript code
593594
rather than a path.
594595
* `options` {Object}
@@ -601,12 +602,13 @@ changes:
601602
to specify that the parent thread and the child thread should share their
602603
environment variables; in that case, changes to one thread’s `process.env`
603604
object will affect the other thread as well. **Default:** `process.env`.
604-
* `eval` {boolean} If `true`, interpret the first argument to the constructor
605+
* `eval` {boolean} If `true`, interprets the first argument to the constructor
605606
as a script (or a module if `type` is set to `module`) that is executed once
606607
the worker is online.
607-
* `type` {string} If `"module"` and `eval` is set to `true`, interpret the
608-
first argument as an ECMAScript 2015 module instead of a script. The default
609-
value is `"classic"`. Doesn't have any effect when `eval` is set to `false`.
608+
* `type` {string} If `"module"`, interprets the first argument as an
609+
ECMAScript 2015 module instead of a script. The default value is
610+
`"classic"`. Doesn't have any effect when `filename` refers to a file, in
611+
which case the type is inferred from the file extension.
610612
* `execArgv` {string[]} List of node CLI options passed to the worker.
611613
V8 options (such as `--max-old-space-size`) and options that affect the
612614
process (such as `--title`) are not supported. If set, this will be provided
@@ -869,3 +871,4 @@ active handle in the event system. If the worker is already `unref()`ed calling
869871
[child processes]: child_process.html
870872
[contextified]: vm.html#vm_what_does_it_mean_to_contextify_an_object
871873
[v8.serdes]: v8.html#v8_serialization_api
874+
[`data:` URL]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs

lib/internal/worker.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
const {
66
ArrayIsArray,
7+
JSONStringify,
78
MathMax,
89
ObjectCreate,
910
ObjectEntries,
@@ -99,7 +100,7 @@ class Worker extends EventEmitter {
99100
argv = options.argv.map(String);
100101
}
101102

102-
let url;
103+
let url, doEval;
103104
if (options.eval) {
104105
if (typeof filename !== 'string') {
105106
throw new ERR_INVALID_ARG_VALUE(
@@ -109,7 +110,20 @@ class Worker extends EventEmitter {
109110
);
110111
}
111112
url = null;
113+
doEval = true;
114+
} else if (filename?.protocol === 'data:') {
115+
if (options.type !== 'module') {
116+
throw new ERR_INVALID_ARG_VALUE(
117+
'options.type',
118+
options.type,
119+
'must be set to "module" when filename is a data: URL'
120+
);
121+
}
122+
url = null;
123+
doEval = true;
124+
filename = `import ${JSONStringify(filename)}`;
112125
} else {
126+
doEval = false;
113127
if (isURLInstance(filename)) {
114128
url = filename;
115129
filename = fileURLToPath(filename);
@@ -207,7 +221,7 @@ class Worker extends EventEmitter {
207221
argv,
208222
type: messageTypes.LOAD_SCRIPT,
209223
filename,
210-
doEval: !!options.eval,
224+
doEval,
211225
workerType: options.type,
212226
cwdCounter: cwdCounter || workerIo.sharedCwdCounter,
213227
workerData: options.workerData,

test/parallel/test-worker-eval.js

+37-13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const cjsLoadingESMError = /Unexpected token 'export'/;
1010
const esmLoadingCJSError = /module is not defined/;
1111
const invalidValueError =
1212
/The argument 'options\.type' must be either "module" or "classic"/;
13+
const invalidValueError2 =
14+
/The argument 'options\.type' must be set to "module" when filename is a data: URL/;
1315

1416
function runInWorker(evalString, options) {
1517
return new Promise((resolve, reject) => {
@@ -19,20 +21,42 @@ function runInWorker(evalString, options) {
1921
});
2022
}
2123

22-
let options;
24+
{
25+
const options = { eval: true };
26+
assert.rejects(runInWorker(moduleString, options), cjsLoadingESMError);
27+
runInWorker(cjsString, options); // does not reject
28+
}
2329

24-
options = { eval: true };
25-
assert.rejects(runInWorker(moduleString, options), cjsLoadingESMError);
26-
runInWorker(cjsString, options); // does not reject
30+
{
31+
const options = { eval: true, type: 'classic' };
32+
assert.rejects(runInWorker(moduleString, options), cjsLoadingESMError);
33+
runInWorker(cjsString, options); // does not reject
34+
}
2735

28-
options = { eval: true, type: 'classic' };
29-
assert.rejects(runInWorker(moduleString, options), cjsLoadingESMError);
30-
runInWorker(cjsString, options); // does not reject
36+
{
37+
const options = { eval: true, type: 'module' };
38+
runInWorker(moduleString, options); // does not reject
39+
assert.rejects(runInWorker(cjsString, options), esmLoadingCJSError);
40+
}
3141

32-
options = { eval: true, type: 'module' };
33-
runInWorker(moduleString, options); // does not reject
34-
assert.rejects(runInWorker(cjsString, options), esmLoadingCJSError);
42+
{
43+
const options = { eval: false, type: 'module' };
44+
runInWorker(new URL(`data:text/javascript,${moduleString}`),
45+
options); // does not reject
46+
assert.rejects(runInWorker(new URL(`data:text/javascript,${cjsString}`),
47+
options), esmLoadingCJSError);
48+
}
3549

36-
options = { eval: true, type: 'wasm' };
37-
assert.rejects(runInWorker(moduleString, options), invalidValueError);
38-
assert.rejects(runInWorker(cjsString, options), invalidValueError);
50+
{
51+
const options = { eval: true, type: 'wasm' };
52+
assert.rejects(runInWorker(moduleString, options), invalidValueError);
53+
assert.rejects(runInWorker(cjsString, options), invalidValueError);
54+
}
55+
56+
{
57+
const options = { type: 'classic' };
58+
assert.rejects(runInWorker(new URL(`data:text/javascript,${moduleString}`),
59+
options), invalidValueError2);
60+
assert.rejects(runInWorker(new URL(`data:text/javascript,${cjsString}`),
61+
options), invalidValueError2);
62+
}

test/parallel/test-worker-unsupported-path.js

-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,4 @@ const { Worker } = require('worker_threads');
4747
};
4848
assert.throws(() => { new Worker(new URL('https://www.url.com')); },
4949
expectedErr);
50-
assert.throws(() => { new Worker(new URL('data:application/javascript,')); },
51-
expectedErr);
5250
}

0 commit comments

Comments
 (0)