diff --git a/doc/api/errors.md b/doc/api/errors.md index 31138fab0b23f1..17de5b0b96a27f 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1754,10 +1754,11 @@ The fulfilled value of a linking promise is not a `vm.Module` object. The current module's status does not allow for this operation. The specific meaning of the error depends on the specific function. - -### ERR_WORKER_NEED_ABSOLUTE_PATH + +### ERR_WORKER_PATH -The path for the main script of a worker is not an absolute path. +The path for the main script of a worker is neither an absolute path +nor a relative path starting with `./` or `../`. ### ERR_WORKER_UNSERIALIZABLE_ERROR diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index b56fb32aec6525..9578403c5ca94d 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -306,7 +306,9 @@ if (isMainThread) { ### new Worker(filename[, options]) -* `filename` {string} The absolute path to the Worker’s main script. +* `filename` {string} The path to the Worker’s main script. Must be + either an absolute path or a relative path (i.e. relative to the + current working directory) starting with `./` or `../`. If `options.eval` is true, this is a string containing JavaScript code rather than a path. * `options` {Object} diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 469917c40fbca1..e37400c822aa5a 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -854,8 +854,9 @@ E('ERR_VM_MODULE_NOT_LINKED', E('ERR_VM_MODULE_NOT_MODULE', 'Provided module is not an instance of Module', Error); E('ERR_VM_MODULE_STATUS', 'Module status %s', Error); -E('ERR_WORKER_NEED_ABSOLUTE_PATH', - 'The worker script filename must be an absolute path. Received "%s"', +E('ERR_WORKER_PATH', + 'The worker script filename must be an absolute path or a relative ' + + 'path starting with \'./\' or \'../\'. Received "%s"', TypeError); E('ERR_WORKER_UNSERIALIZABLE_ERROR', 'Serializing an uncaught exception failed', Error); diff --git a/lib/internal/worker.js b/lib/internal/worker.js index de00f20d4f0e1d..df4f28cf749fe9 100644 --- a/lib/internal/worker.js +++ b/lib/internal/worker.js @@ -7,7 +7,7 @@ const util = require('util'); const { Readable, Writable } = require('stream'); const { ERR_INVALID_ARG_TYPE, - ERR_WORKER_NEED_ABSOLUTE_PATH, + ERR_WORKER_PATH, ERR_WORKER_UNSERIALIZABLE_ERROR, ERR_WORKER_UNSUPPORTED_EXTENSION, } = require('internal/errors').codes; @@ -212,9 +212,15 @@ class Worker extends EventEmitter { } if (!options.eval) { - if (!path.isAbsolute(filename)) { - throw new ERR_WORKER_NEED_ABSOLUTE_PATH(filename); + if (!path.isAbsolute(filename) && + !filename.startsWith('./') && + !filename.startsWith('../') && + !filename.startsWith('.' + path.sep) && + !filename.startsWith('..' + path.sep)) { + throw new ERR_WORKER_PATH(filename); } + filename = path.resolve(filename); + const ext = path.extname(filename); if (ext !== '.js' && ext !== '.mjs') { throw new ERR_WORKER_UNSUPPORTED_EXTENSION(ext); diff --git a/test/parallel/test-worker-relative-path-double-dot.js b/test/parallel/test-worker-relative-path-double-dot.js new file mode 100644 index 00000000000000..ecd294a4d3cba4 --- /dev/null +++ b/test/parallel/test-worker-relative-path-double-dot.js @@ -0,0 +1,17 @@ +// Flags: --experimental-worker +'use strict'; +const path = require('path'); +const assert = require('assert'); +const common = require('../common'); +const { Worker, isMainThread, parentPort } = require('worker_threads'); + +if (isMainThread) { + const cwdName = path.relative('../', '.'); + const relativePath = path.relative('.', __filename); + const w = new Worker(path.join('..', cwdName, relativePath)); + w.on('message', common.mustCall((message) => { + assert.strictEqual(message, 'Hello, world!'); + })); +} else { + parentPort.postMessage('Hello, world!'); +} diff --git a/test/parallel/test-worker-relative-path.js b/test/parallel/test-worker-relative-path.js new file mode 100644 index 00000000000000..30d2a5dde3e779 --- /dev/null +++ b/test/parallel/test-worker-relative-path.js @@ -0,0 +1,15 @@ +// Flags: --experimental-worker +'use strict'; +const path = require('path'); +const assert = require('assert'); +const common = require('../common'); +const { Worker, isMainThread, parentPort } = require('worker_threads'); + +if (isMainThread) { + const w = new Worker('./' + path.relative('.', __filename)); + w.on('message', common.mustCall((message) => { + assert.strictEqual(message, 'Hello, world!'); + })); +} else { + parentPort.postMessage('Hello, world!'); +} diff --git a/test/parallel/test-worker-unsupported-path.js b/test/parallel/test-worker-unsupported-path.js index b4de6fd1976013..71999eb5fe44ec 100644 --- a/test/parallel/test-worker-unsupported-path.js +++ b/test/parallel/test-worker-unsupported-path.js @@ -1,21 +1,11 @@ // Flags: --experimental-worker 'use strict'; +const path = require('path'); const common = require('../common'); const assert = require('assert'); const { Worker } = require('worker_threads'); -{ - const expectedErr = common.expectsError({ - code: 'ERR_WORKER_NEED_ABSOLUTE_PATH', - type: TypeError - }, 4); - assert.throws(() => { new Worker('a.js'); }, expectedErr); - assert.throws(() => { new Worker('b'); }, expectedErr); - assert.throws(() => { new Worker('c/d.js'); }, expectedErr); - assert.throws(() => { new Worker('a.mjs'); }, expectedErr); -} - { const expectedErr = common.expectsError({ code: 'ERR_WORKER_UNSUPPORTED_EXTENSION', @@ -25,3 +15,15 @@ const { Worker } = require('worker_threads'); assert.throws(() => { new Worker('/c.wasm'); }, expectedErr); assert.throws(() => { new Worker('/d.txt'); }, expectedErr); } + +{ + const expectedErr = common.expectsError({ + code: 'ERR_WORKER_PATH', + type: TypeError + }, 4); + const existingRelPathNoDot = path.relative('.', __filename); + assert.throws(() => { new Worker(existingRelPathNoDot); }, expectedErr); + assert.throws(() => { new Worker('relative_no_dot'); }, expectedErr); + assert.throws(() => { new Worker('file:///file_url'); }, expectedErr); + assert.throws(() => { new Worker('https://www.url.com'); }, expectedErr); +}