From fec74c9a86fefb5c1573d749517b42a8838cdce6 Mon Sep 17 00:00:00 2001 From: osztenkurden Date: Thu, 17 Sep 2026 14:09:40 +0200 Subject: [PATCH 1/3] worker: strip types in Web Worker module entries Strip TypeScript from file-backed module worker entries before evaluating the fetched source. Add regression coverage and document the supported entry types. Assisted-by: Codex Signed-off-by: Hubert Walczak --- doc/api/globals.md | 9 ++ doc/api/typescript.md | 5 ++ lib/internal/webworker.js | 25 +++++- .../web-worker/typescript/dependency.ts | 1 + test/fixtures/web-worker/typescript/entry.ts | 2 + test/fixtures/web-worker/typescript/module.ts | 10 +++ .../test-webworker-typescript-disabled.js | 28 ++++++ test/parallel/test-webworker-typescript.js | 85 +++++++++++++++++++ 8 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/web-worker/typescript/dependency.ts create mode 100644 test/fixtures/web-worker/typescript/entry.ts create mode 100644 test/fixtures/web-worker/typescript/module.ts create mode 100644 test/parallel/test-webworker-typescript-disabled.js create mode 100644 test/parallel/test-webworker-typescript.js diff --git a/doc/api/globals.md b/doc/api/globals.md index a5998d14885d..b1080902e8e3 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -1373,6 +1373,15 @@ accepted and how failures are reported: * For `blob:` URLs, the script must be held in memory, so blobs backed by a file, such as those returned by [`fs.openAsBlob()`][], cannot be used. +Module workers (`{ type: 'module' }`) loaded from `file:` URLs support +[TypeScript type stripping](typescript.md#type-stripping) for `.ts`, `.mts`, and +`.cts` entries, unless `--no-strip-types` is enabled. The worker's `type` option +determines how the entry is executed, regardless of its extension or the +surrounding `package.json`: even a `.cts` module-worker entry is evaluated as an +ES module. Imported modules follow the usual Node.js module-loading rules. +Classic worker entries, `importScripts()`, and `data:` or `blob:` entry sources +do not support type stripping. + ### Differences from the HTML Standard Besides script loading, mentioned above: diff --git a/doc/api/typescript.md b/doc/api/typescript.md index 3959fd58b56c..0018d06b1d1a 100644 --- a/doc/api/typescript.md +++ b/doc/api/typescript.md @@ -127,6 +127,11 @@ if you want your code to run as CommonJS you must use `require` and * `.cts` files will always be run as CommonJS modules, similar to `.cjs` files. * `.tsx` files are unsupported. +For [Web Worker module entries](globals.md#loading-worker-scripts), the worker's +`type` option takes precedence over these module-system rules. A TypeScript +file used as a module-worker entry is evaluated as an ES module, including when +it has a `.cts` extension. Its imports still follow the rules above. + As in JavaScript files, [file extensions are mandatory][] in `import` statements and `import()` expressions: `import './file.ts'`, not `import './file'`. Because of backward compatibility, file extensions are also mandatory in `require()` diff --git a/lib/internal/webworker.js b/lib/internal/webworker.js index e2a2086f9039..a82ea6623e74 100644 --- a/lib/internal/webworker.js +++ b/lib/internal/webworker.js @@ -29,6 +29,7 @@ const { ERR_ILLEGAL_CONSTRUCTOR, ERR_INVALID_STATE, ERR_NO_CRYPTO, + ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING, } = require('internal/errors').codes; const { @@ -43,6 +44,7 @@ const { exposeInterface, getCWDURL, getLazy, + isUnderNodeModules, kEmptyObject, kEnumerableProperty, lazyDOMException, @@ -89,6 +91,7 @@ const { const { URL, URLParse, + fileURLToPath, } = require('internal/url'); const { @@ -253,10 +256,26 @@ function runClassicScriptSource(source, url) { * @returns {Promise} */ function runModuleScriptSource(source, url) { - // Necessary to reset RegExp statics before user code runs. - RegExpPrototypeExec(/^/, ''); return require('internal/modules/run_main').runEntryPointWithESMLoader( - (loader) => loader.eval(source, url, true), + (loader) => { + if (require('internal/options').getOptionValue('--strip-types')) { + const parsedURL = new URL(url); + if (parsedURL.protocol === 'file:') { + const filename = fileURLToPath(parsedURL); + const extension = require('path').extname(filename); + if (extension === '.ts' || extension === '.mts' || extension === '.cts') { + if (isUnderNodeModules(filename)) { + throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename); + } + const { stripTypeScriptModuleTypes } = require('internal/modules/typescript'); + source = stripTypeScriptModuleTypes(source, url); + } + } + } + // Necessary to reset RegExp statics before user code runs. + RegExpPrototypeExec(/^/, ''); + return loader.eval(source, url, true); + }, ); } diff --git a/test/fixtures/web-worker/typescript/dependency.ts b/test/fixtures/web-worker/typescript/dependency.ts new file mode 100644 index 000000000000..1b55ac618d95 --- /dev/null +++ b/test/fixtures/web-worker/typescript/dependency.ts @@ -0,0 +1 @@ +export const value: number = 42; diff --git a/test/fixtures/web-worker/typescript/entry.ts b/test/fixtures/web-worker/typescript/entry.ts new file mode 100644 index 000000000000..99961c3cd8ea --- /dev/null +++ b/test/fixtures/web-worker/typescript/entry.ts @@ -0,0 +1,2 @@ +const value: number = 1; +postMessage(value); diff --git a/test/fixtures/web-worker/typescript/module.ts b/test/fixtures/web-worker/typescript/module.ts new file mode 100644 index 000000000000..42ee6113f4c4 --- /dev/null +++ b/test/fixtures/web-worker/typescript/module.ts @@ -0,0 +1,10 @@ +import { value } from './dependency.ts'; + +const result: number = await Promise.resolve(value); +postMessage({ + value: result, + url: import.meta.url, + main: import.meta.main, + requireType: typeof require, + thisIsUndefined: this === undefined, +}); diff --git a/test/parallel/test-webworker-typescript-disabled.js b/test/parallel/test-webworker-typescript-disabled.js new file mode 100644 index 000000000000..423394e9bd93 --- /dev/null +++ b/test/parallel/test-webworker-typescript-disabled.js @@ -0,0 +1,28 @@ +// Flags: --experimental-web-worker --no-strip-types +'use strict'; + +const common = require('../common'); +// Disabling stripping must reject annotations, not JavaScript-compatible .ts files. +const assert = require('node:assert'); +const { writeFileSync } = require('node:fs'); +const { join } = require('node:path'); +const { pathToFileURL } = require('node:url'); +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +const worker = new Worker(fixtures.fileURL('web-worker', 'typescript', 'entry.ts'), { type: 'module' }); +worker.onmessage = common.mustNotCall('types must not be stripped'); +worker.onerror = common.mustCall(({ error }) => { + assert.strictEqual(error.name, 'SyntaxError'); +}); + +const path = join(tmpdir.path, 'entry.ts'); +writeFileSync(path, 'postMessage(1);'); +const untyped = new Worker(pathToFileURL(path), { type: 'module' }); +untyped.onerror = common.mustNotCall('JavaScript-compatible entries must still work'); +untyped.onmessage = common.mustCall(({ data }) => { + assert.strictEqual(data, 1); + untyped.terminate(); +}); diff --git a/test/parallel/test-webworker-typescript.js b/test/parallel/test-webworker-typescript.js new file mode 100644 index 000000000000..6e91d07657a1 --- /dev/null +++ b/test/parallel/test-webworker-typescript.js @@ -0,0 +1,85 @@ +// Flags: --experimental-web-worker +'use strict'; + +const common = require('../common'); +if (!process.config.variables.node_use_amaro) { + common.skip('Requires Amaro'); +} + +// Strip file entry types without changing the worker's module semantics. +const assert = require('node:assert'); +const { mkdirSync, writeFileSync } = require('node:fs'); +const { join } = require('node:path'); +const { pathToFileURL } = require('node:url'); +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +function createEntry(name, source) { + const path = join(tmpdir.path, name); + writeFileSync(path, source); + return pathToFileURL(path); +} + +function expectMessage(url, expected) { + const worker = new Worker(url, { type: 'module' }); + worker.onerror = common.mustNotCall('worker failed'); + worker.onmessage = common.mustCall(({ data }) => { + assert.deepStrictEqual(data, expected); + worker.terminate(); + }); +} + +function expectError(url, code, type = 'module') { + const worker = new Worker(url, { type }); + worker.onmessage = common.mustNotCall('worker unexpectedly succeeded'); + worker.onerror = common.mustCall(({ error }) => { + assert.strictEqual(error.code ?? error.name, code); + }); +} + +const entry = fixtures.fileURL('web-worker', 'typescript', 'entry.ts'); +expectMessage(entry, 1); +expectError(entry, 'SyntaxError', 'classic'); + +// Worker type takes precedence over both the extension and package type. +writeFileSync(join(tmpdir.path, 'package.json'), '{ "type": "commonjs" }'); +for (const extension of ['ts', 'mts', 'cts']) { + const url = createEntry(`entry.${extension}`, ` + const value: number = 1; + postMessage([value, typeof require, this === undefined, import.meta.main]); + `); + expectMessage(url, [1, 'undefined', true, true]); +} + +// The stripped entry can still import TypeScript and keeps its original URL. +{ + const url = fixtures.fileURL('web-worker', 'typescript', 'module.ts'); + url.search = '?version=1'; + url.hash = '#entry'; + expectMessage(url, { + value: 42, + url: url.href, + main: true, + requireType: 'undefined', + thisIsUndefined: true, + }); +} + +// Extension detection and the node_modules restriction use the decoded path. +{ + const url = createEntry('space \u00e9.ts', 'const value: number = 1; postMessage(value);'); + expectMessage(url.href.replace('.ts', '.%74s') + '?version=2#entry', 1); +} +mkdirSync(join(tmpdir.path, 'node_modules')); +const dependency = createEntry('node_modules/entry.ts', 'postMessage(1);'); +for (const url of [dependency.href, dependency.href.replace('node_modules', '%6eode_modules')]) { + expectError(url, 'ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING'); +} + +// Type-stripping errors must reach the parent's error handler. JavaScript +// entries must not acquire TypeScript support. +expectError(createEntry('invalid.ts', 'const value: = 1;'), 'ERR_INVALID_TYPESCRIPT_SYNTAX'); +expectError(createEntry('enum.ts', 'enum Value { A }'), 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX'); +expectError(createEntry('entry.js', 'const value: number = 1;'), 'SyntaxError'); From 29c8d520186e74db4046f8c20bbb1e185b5043d6 Mon Sep 17 00:00:00 2001 From: osztenkurden Date: Wed, 23 Sep 2026 09:48:11 +0200 Subject: [PATCH 2/3] worker: simplify Web Worker type stripping Pass the decoded path to stripTypeScriptModuleTypes() so it does the node_modules check, instead of repeating it in webworker.js. Shorten the note in globals.md and drop the typescript.md paragraph. Remove test cases that repeat what other cases already cover. Signed-off-by: Hubert Walczak --- doc/api/globals.md | 12 ++---- doc/api/typescript.md | 5 --- lib/internal/webworker.js | 25 +++++------ test/fixtures/web-worker/typescript/module.ts | 10 +---- .../test-webworker-typescript-disabled.js | 17 +------- test/parallel/test-webworker-typescript.js | 41 +++++-------------- 6 files changed, 28 insertions(+), 82 deletions(-) diff --git a/doc/api/globals.md b/doc/api/globals.md index b1080902e8e3..87e9671dbca7 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -1373,14 +1373,9 @@ accepted and how failures are reported: * For `blob:` URLs, the script must be held in memory, so blobs backed by a file, such as those returned by [`fs.openAsBlob()`][], cannot be used. -Module workers (`{ type: 'module' }`) loaded from `file:` URLs support -[TypeScript type stripping](typescript.md#type-stripping) for `.ts`, `.mts`, and -`.cts` entries, unless `--no-strip-types` is enabled. The worker's `type` option -determines how the entry is executed, regardless of its extension or the -surrounding `package.json`: even a `.cts` module-worker entry is evaluated as an -ES module. Imported modules follow the usual Node.js module-loading rules. -Classic worker entries, `importScripts()`, and `data:` or `blob:` entry sources -do not support type stripping. +[Type stripping][] only applies to module workers loaded from `file:` URLs. The +`type` option, not the file extension, decides how an entry is run, so a `.cts` +entry is still evaluated as an ES module. ### Differences from the HTML Standard @@ -1476,6 +1471,7 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][]. [Navigator API]: https://html.spec.whatwg.org/multipage/system-state.html#the-navigator-object [RFC 5646]: https://www.rfc-editor.org/rfc/rfc5646.txt [Refable protocol]: process.md#processrefmayberefable +[Type stripping]: typescript.md#type-stripping [Web Crypto API]: webcrypto.md [`--experimental-eventsource`]: cli.md#--experimental-eventsource [`--experimental-web-worker`]: cli.md#--experimental-web-worker diff --git a/doc/api/typescript.md b/doc/api/typescript.md index 0018d06b1d1a..3959fd58b56c 100644 --- a/doc/api/typescript.md +++ b/doc/api/typescript.md @@ -127,11 +127,6 @@ if you want your code to run as CommonJS you must use `require` and * `.cts` files will always be run as CommonJS modules, similar to `.cjs` files. * `.tsx` files are unsupported. -For [Web Worker module entries](globals.md#loading-worker-scripts), the worker's -`type` option takes precedence over these module-system rules. A TypeScript -file used as a module-worker entry is evaluated as an ES module, including when -it has a `.cts` extension. Its imports still follow the rules above. - As in JavaScript files, [file extensions are mandatory][] in `import` statements and `import()` expressions: `import './file.ts'`, not `import './file'`. Because of backward compatibility, file extensions are also mandatory in `require()` diff --git a/lib/internal/webworker.js b/lib/internal/webworker.js index a82ea6623e74..8121d2921018 100644 --- a/lib/internal/webworker.js +++ b/lib/internal/webworker.js @@ -25,11 +25,12 @@ const { globalThis, } = primordials; +const { extname } = require('path'); + const { ERR_ILLEGAL_CONSTRUCTOR, ERR_INVALID_STATE, ERR_NO_CRYPTO, - ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING, } = require('internal/errors').codes; const { @@ -38,13 +39,14 @@ const { initEventTarget, } = require('internal/event_target'); +const { getOptionValue } = require('internal/options'); + const { assignFunctionName, defineOperation, exposeInterface, getCWDURL, getLazy, - isUnderNodeModules, kEmptyObject, kEnumerableProperty, lazyDOMException, @@ -258,18 +260,13 @@ function runClassicScriptSource(source, url) { function runModuleScriptSource(source, url) { return require('internal/modules/run_main').runEntryPointWithESMLoader( (loader) => { - if (require('internal/options').getOptionValue('--strip-types')) { - const parsedURL = new URL(url); - if (parsedURL.protocol === 'file:') { - const filename = fileURLToPath(parsedURL); - const extension = require('path').extname(filename); - if (extension === '.ts' || extension === '.mts' || extension === '.cts') { - if (isUnderNodeModules(filename)) { - throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename); - } - const { stripTypeScriptModuleTypes } = require('internal/modules/typescript'); - source = stripTypeScriptModuleTypes(source, url); - } + const parsedURL = new URL(url); + if (parsedURL.protocol === 'file:' && getOptionValue('--strip-types')) { + const filename = fileURLToPath(parsedURL); + const extension = extname(filename); + if (extension === '.ts' || extension === '.mts' || extension === '.cts') { + const { stripTypeScriptModuleTypes } = require('internal/modules/typescript'); + source = stripTypeScriptModuleTypes(source, filename, url); } } // Necessary to reset RegExp statics before user code runs. diff --git a/test/fixtures/web-worker/typescript/module.ts b/test/fixtures/web-worker/typescript/module.ts index 42ee6113f4c4..a2a7b8f4af39 100644 --- a/test/fixtures/web-worker/typescript/module.ts +++ b/test/fixtures/web-worker/typescript/module.ts @@ -1,10 +1,4 @@ import { value } from './dependency.ts'; -const result: number = await Promise.resolve(value); -postMessage({ - value: result, - url: import.meta.url, - main: import.meta.main, - requireType: typeof require, - thisIsUndefined: this === undefined, -}); +const result: number = value; +postMessage(result); diff --git a/test/parallel/test-webworker-typescript-disabled.js b/test/parallel/test-webworker-typescript-disabled.js index 423394e9bd93..a19e5b122abd 100644 --- a/test/parallel/test-webworker-typescript-disabled.js +++ b/test/parallel/test-webworker-typescript-disabled.js @@ -2,27 +2,12 @@ 'use strict'; const common = require('../common'); -// Disabling stripping must reject annotations, not JavaScript-compatible .ts files. const assert = require('node:assert'); -const { writeFileSync } = require('node:fs'); -const { join } = require('node:path'); -const { pathToFileURL } = require('node:url'); const fixtures = require('../common/fixtures'); -const tmpdir = require('../common/tmpdir'); - -tmpdir.refresh(); +// Without type stripping, annotated `.ts` entries fail to parse. const worker = new Worker(fixtures.fileURL('web-worker', 'typescript', 'entry.ts'), { type: 'module' }); worker.onmessage = common.mustNotCall('types must not be stripped'); worker.onerror = common.mustCall(({ error }) => { assert.strictEqual(error.name, 'SyntaxError'); }); - -const path = join(tmpdir.path, 'entry.ts'); -writeFileSync(path, 'postMessage(1);'); -const untyped = new Worker(pathToFileURL(path), { type: 'module' }); -untyped.onerror = common.mustNotCall('JavaScript-compatible entries must still work'); -untyped.onmessage = common.mustCall(({ data }) => { - assert.strictEqual(data, 1); - untyped.terminate(); -}); diff --git a/test/parallel/test-webworker-typescript.js b/test/parallel/test-webworker-typescript.js index 6e91d07657a1..058c1728f347 100644 --- a/test/parallel/test-webworker-typescript.js +++ b/test/parallel/test-webworker-typescript.js @@ -26,7 +26,7 @@ function expectMessage(url, expected) { const worker = new Worker(url, { type: 'module' }); worker.onerror = common.mustNotCall('worker failed'); worker.onmessage = common.mustCall(({ data }) => { - assert.deepStrictEqual(data, expected); + assert.strictEqual(data, expected); worker.terminate(); }); } @@ -39,47 +39,26 @@ function expectError(url, code, type = 'module') { }); } -const entry = fixtures.fileURL('web-worker', 'typescript', 'entry.ts'); -expectMessage(entry, 1); -expectError(entry, 'SyntaxError', 'classic'); - // Worker type takes precedence over both the extension and package type. writeFileSync(join(tmpdir.path, 'package.json'), '{ "type": "commonjs" }'); for (const extension of ['ts', 'mts', 'cts']) { - const url = createEntry(`entry.${extension}`, ` - const value: number = 1; - postMessage([value, typeof require, this === undefined, import.meta.main]); - `); - expectMessage(url, [1, 'undefined', true, true]); + const url = createEntry(`entry.${extension}`, 'const type: string = typeof require; postMessage(type);'); + expectMessage(url, 'undefined'); } -// The stripped entry can still import TypeScript and keeps its original URL. +// The entry can import TypeScript, and a query or hash does not affect detection. { const url = fixtures.fileURL('web-worker', 'typescript', 'module.ts'); url.search = '?version=1'; url.hash = '#entry'; - expectMessage(url, { - value: 42, - url: url.href, - main: true, - requireType: 'undefined', - thisIsUndefined: true, - }); + expectMessage(url, 42); } -// Extension detection and the node_modules restriction use the decoded path. -{ - const url = createEntry('space \u00e9.ts', 'const value: number = 1; postMessage(value);'); - expectMessage(url.href.replace('.ts', '.%74s') + '?version=2#entry', 1); -} +// Stripping errors reach the parent's error handler. mkdirSync(join(tmpdir.path, 'node_modules')); -const dependency = createEntry('node_modules/entry.ts', 'postMessage(1);'); -for (const url of [dependency.href, dependency.href.replace('node_modules', '%6eode_modules')]) { - expectError(url, 'ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING'); -} +expectError(createEntry('node_modules/entry.ts', 'postMessage(1);'), + 'ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING'); -// Type-stripping errors must reach the parent's error handler. JavaScript -// entries must not acquire TypeScript support. -expectError(createEntry('invalid.ts', 'const value: = 1;'), 'ERR_INVALID_TYPESCRIPT_SYNTAX'); -expectError(createEntry('enum.ts', 'enum Value { A }'), 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX'); +// Classic workers and `.js` entries are not stripped. +expectError(fixtures.fileURL('web-worker', 'typescript', 'entry.ts'), 'SyntaxError', 'classic'); expectError(createEntry('entry.js', 'const value: number = 1;'), 'SyntaxError'); From acbfc92b8cef930c048f92d0a156f8341317afa0 Mon Sep 17 00:00:00 2001 From: osztenkurden Date: Wed, 23 Sep 2026 18:26:13 +0200 Subject: [PATCH 3/3] worker: address review comments Lazy load the type stripping helper with getLazy(), and list type stripping under the differences from the HTML Standard. Signed-off-by: Hubert Walczak --- doc/api/globals.md | 9 +++++---- lib/internal/webworker.js | 6 ++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/api/globals.md b/doc/api/globals.md index 87e9671dbca7..3016f27c2672 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -1373,9 +1373,9 @@ accepted and how failures are reported: * For `blob:` URLs, the script must be held in memory, so blobs backed by a file, such as those returned by [`fs.openAsBlob()`][], cannot be used. -[Type stripping][] only applies to module workers loaded from `file:` URLs. The -`type` option, not the file extension, decides how an entry is run, so a `.cts` -entry is still evaluated as an ES module. +[Type stripping][type stripping] only applies to module workers loaded from +`file:` URLs. The `type` option, not the file extension, decides how an entry is +run, so a `.cts` entry is still evaluated as an ES module. ### Differences from the HTML Standard @@ -1401,6 +1401,7 @@ Besides script loading, mentioned above: `unhandledrejection`, since Node.js exposes the equivalent does not implement the `PromiseRejectionEvent` interface or the per-rejection `preventDefault()` behavior required by the HTML Standard. +* Module workers loaded from `file:` URLs support [type stripping][]. ### Web Workers and `node:worker_threads` @@ -1471,7 +1472,6 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][]. [Navigator API]: https://html.spec.whatwg.org/multipage/system-state.html#the-navigator-object [RFC 5646]: https://www.rfc-editor.org/rfc/rfc5646.txt [Refable protocol]: process.md#processrefmayberefable -[Type stripping]: typescript.md#type-stripping [Web Crypto API]: webcrypto.md [`--experimental-eventsource`]: cli.md#--experimental-eventsource [`--experimental-web-worker`]: cli.md#--experimental-web-worker @@ -1545,5 +1545,6 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][]. [buffer section]: buffer.md [built-in objects]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects [timers]: timers.md +[type stripping]: typescript.md#type-stripping [webassembly-mdn]: https://developer.mozilla.org/en-US/docs/WebAssembly [webassembly-org]: https://webassembly.org diff --git a/lib/internal/webworker.js b/lib/internal/webworker.js index 8121d2921018..d7e768372def 100644 --- a/lib/internal/webworker.js +++ b/lib/internal/webworker.js @@ -122,6 +122,9 @@ let scopeBaseURL = null; const lazyErrorEvent = getLazy(() => require('internal/deps/undici/undici').ErrorEvent); +const lazyStripTypeScriptModuleTypes = getLazy( + () => require('internal/modules/typescript').stripTypeScriptModuleTypes); + function createErrorEvent(init) { const ErrorEvent = lazyErrorEvent(); return new ErrorEvent('error', init); @@ -265,8 +268,7 @@ function runModuleScriptSource(source, url) { const filename = fileURLToPath(parsedURL); const extension = extname(filename); if (extension === '.ts' || extension === '.mts' || extension === '.cts') { - const { stripTypeScriptModuleTypes } = require('internal/modules/typescript'); - source = stripTypeScriptModuleTypes(source, filename, url); + source = lazyStripTypeScriptModuleTypes()(source, filename, url); } } // Necessary to reset RegExp statics before user code runs.