From 3e846c43ec415b32a8118002a4e0505b7398c8b1 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sat, 2 Sep 2023 17:41:17 -0700 Subject: [PATCH 1/6] esm: refactor mocking test --- test/es-module/test-esm-loader-mock.mjs | 74 +++++++- .../es-module-loaders/mock-loader.mjs | 177 ++---------------- 2 files changed, 87 insertions(+), 164 deletions(-) diff --git a/test/es-module/test-esm-loader-mock.mjs b/test/es-module/test-esm-loader-mock.mjs index 2783bf694d239a..0a66df46aeefbf 100644 --- a/test/es-module/test-esm-loader-mock.mjs +++ b/test/es-module/test-esm-loader-mock.mjs @@ -1,9 +1,73 @@ -// Flags: --loader ./test/fixtures/es-module-loaders/mock-loader.mjs +// Flags: --no-warnings import '../common/index.mjs'; -import assert from 'assert/strict'; +import * as fixtures from '../common/fixtures.mjs'; +import assert from 'node:assert/strict'; +import { register } from 'node:module'; +import { MessageChannel } from 'node:worker_threads'; -// This is provided by test/fixtures/es-module-loaders/mock-loader.mjs -import mock from 'node:mock'; +const { port1, port2 } = new MessageChannel(); + +register(fixtures.fileURL('es-module-loaders/mock-loader.mjs'), { + parentURL: import.meta.url, + data: { port: port2 }, + transferList: [port2], +}); + +/** + * This is the Map that saves *all* the mocked URL -> replacement Module + * mappings + * @type {Map} + */ +globalThis.mockedModules = new Map(); +let mockVersion = 0; + +/** + * @param {string} resolved an absolute URL HREF string + * @param {object} replacementProperties an object to pick properties from + * to act as a module namespace + * @returns {object} a mutator object that can update the module namespace + * since we can't do something like old Object.observe + */ +function mock(resolved, replacementProperties) { + const exportNames = Object.keys(replacementProperties); + const namespace = { __proto__: null }; + /** + * @type {Array<(name: string)=>void>} functions to call whenever an + * export name is updated + */ + const listeners = []; + for (const name of exportNames) { + let currentValueForPropertyName = replacementProperties[name]; + Object.defineProperty(namespace, name, { + enumerable: true, + get() { + return currentValueForPropertyName; + }, + set(v) { + currentValueForPropertyName = v; + for (const fn of listeners) { + try { + fn(name); + } catch { + /* noop */ + } + } + } + }); + } + globalThis.mockedModules.set(encodeURIComponent(resolved), { + namespace, + listeners + }); + mockVersion++; + // Inform the loader that the `resolved` URL should now use the specific + // `mockVersion` and has export names of `exportNames` + // + // This allows the loader to generate a fake module for that version + // and names the next time it resolves a specifier to equal `resolved` + port1.postMessage({ mockVersion, resolved, exports: exportNames }); + return namespace; +} mock('node:events', { EventEmitter: 'This is mocked!' @@ -43,3 +107,5 @@ assert.deepStrictEqual(mockedV2, Object.defineProperty({ enumerable: false, value: 'Module' })); + +delete globalThis.mockedModules; diff --git a/test/fixtures/es-module-loaders/mock-loader.mjs b/test/fixtures/es-module-loaders/mock-loader.mjs index 062be39603e851..9e5f57658ad6e6 100644 --- a/test/fixtures/es-module-loaders/mock-loader.mjs +++ b/test/fixtures/es-module-loaders/mock-loader.mjs @@ -1,8 +1,7 @@ -import { receiveMessageOnPort } from 'node:worker_threads'; const mockedModuleExports = new Map(); let currentMockVersion = 0; -// This loader causes a new module `node:mock` to become available as a way to +// This loader enables code running on the application thread to // swap module resolution results for mocking purposes. It uses this instead // of import.meta so that CommonJS can still use the functionality. // @@ -22,7 +21,7 @@ let currentMockVersion = 0; // it cannot be changed. So things like the following DO NOT WORK: // // ```mjs -// import mock from 'node:mock'; +// import mock from 'test-esm-loader-mock'; // See test-esm-loader-mock.mjs // mock('file:///app.js', {x:1}); // const namespace1 = await import('file:///app.js'); // namespace1.x; // 1 @@ -34,148 +33,16 @@ let currentMockVersion = 0; // assert(namespace1 === namespace2); // ``` -/** - * FIXME: this is a hack to workaround loaders being - * single threaded for now, just ensures that the MessagePort drains - */ -function doDrainPort() { - let msg; - while (msg = receiveMessageOnPort(preloadPort)) { - onPreloadPortMessage(msg.message); - } -} -/** - * @param param0 message from the application context - */ -function onPreloadPortMessage({ - mockVersion, resolved, exports -}) { - currentMockVersion = mockVersion; - mockedModuleExports.set(resolved, exports); +export async function initialize({ port }) { + port.on('message', ({ mockVersion, resolved, exports }) => { + currentMockVersion = mockVersion; + mockedModuleExports.set(resolved, exports); + }); } -let preloadPort; -export function globalPreload({port}) { - // Save the communication port to the application context to send messages - // to it later - preloadPort = port; - // Every time the application context sends a message over the port - port.on('message', onPreloadPortMessage); - // This prevents the port that the Loader/application talk over - // from keeping the process alive, without this, an application would be kept - // alive just because a loader is waiting for messages - port.unref(); - - const insideAppContext = (getBuiltin, port, setImportMetaCallback) => { - /** - * This is the Map that saves *all* the mocked URL -> replacement Module - * mappings - * @type {Map} - */ - let mockedModules = new Map(); - let mockVersion = 0; - /** - * This is the value that is placed into the `node:mock` default export - * - * @example - * ```mjs - * import mock from 'node:mock'; - * const mutator = mock('file:///app.js', {x:1}); - * const namespace = await import('file:///app.js'); - * namespace.x; // 1; - * mutator.x = 2; - * namespace.x; // 2; - * ``` - * - * @param {string} resolved an absolute URL HREF string - * @param {object} replacementProperties an object to pick properties from - * to act as a module namespace - * @returns {object} a mutator object that can update the module namespace - * since we can't do something like old Object.observe - */ - const doMock = (resolved, replacementProperties) => { - let exportNames = Object.keys(replacementProperties); - let namespace = Object.create(null); - /** - * @type {Array<(name: string)=>void>} functions to call whenever an - * export name is updated - */ - let listeners = []; - for (const name of exportNames) { - let currentValueForPropertyName = replacementProperties[name]; - Object.defineProperty(namespace, name, { - enumerable: true, - get() { - return currentValueForPropertyName; - }, - set(v) { - currentValueForPropertyName = v; - for (let fn of listeners) { - try { - fn(name); - } catch { - } - } - } - }); - } - mockedModules.set(resolved, { - namespace, - listeners - }); - mockVersion++; - // Inform the loader that the `resolved` URL should now use the specific - // `mockVersion` and has export names of `exportNames` - // - // This allows the loader to generate a fake module for that version - // and names the next time it resolves a specifier to equal `resolved` - port.postMessage({ mockVersion, resolved, exports: exportNames }); - return namespace; - } - // Sets the import.meta properties up - // has the normal chaining workflow with `defaultImportMetaInitializer` - setImportMetaCallback((meta, context, defaultImportMetaInitializer) => { - /** - * 'node:mock' creates its default export by plucking off of import.meta - * and must do so in order to get the communications channel from inside - * preloadCode - */ - if (context.url === 'node:mock') { - meta.doMock = doMock; - return; - } - /** - * Fake modules created by `node:mock` get their meta.mock utility set - * to the corresponding value keyed off `mockedModules` and use this - * to setup their exports/listeners properly - */ - if (context.url.startsWith('mock-facade:')) { - let [proto, version, encodedTargetURL] = context.url.split(':'); - let decodedTargetURL = decodeURIComponent(encodedTargetURL); - if (mockedModules.has(decodedTargetURL)) { - meta.mock = mockedModules.get(decodedTargetURL); - return; - } - } - /** - * Ensure we still get things like `import.meta.url` - */ - defaultImportMetaInitializer(meta, context); - }); - }; - return `(${insideAppContext})(getBuiltin, port, setImportMetaCallback)` -} - // Rewrites node: loading to mock-facade: so that it can be intercepted export async function resolve(specifier, context, defaultResolve) { - if (specifier === 'node:mock') { - return { - shortCircuit: true, - url: specifier - }; - } - doDrainPort(); const def = await defaultResolve(specifier, context); if (context.parentURL?.startsWith('mock-facade:')) { // Do nothing, let it get the "real" module @@ -192,30 +59,16 @@ export async function resolve(specifier, context, defaultResolve) { } export async function load(url, context, defaultLoad) { - doDrainPort(); - if (url === 'node:mock') { - /** - * Simply grab the import.meta.doMock to establish the communication - * channel with preloadCode - */ - return { - shortCircuit: true, - source: 'export default import.meta.doMock', - format: 'module' - }; - } /** * Mocked fake module, not going to be handled in default way so it * generates the source text, then short circuits */ if (url.startsWith('mock-facade:')) { - let [proto, version, encodedTargetURL] = url.split(':'); - let ret = generateModule(mockedModuleExports.get( - decodeURIComponent(encodedTargetURL) - )); + let [_proto, _version, encodedTargetURL] = url.split(':'); + let source = generateModule(encodedTargetURL); return { shortCircuit: true, - source: ret, + source, format: 'module' }; } @@ -223,17 +76,21 @@ export async function load(url, context, defaultLoad) { } /** - * - * @param {Array} exports name of the exports of the module + * Generate the source code for a mocked module. + * @param {string} encodedTargetURL the module being mocked * @returns {string} */ -function generateModule(exports) { +function generateModule(encodedTargetURL) { + const exports = mockedModuleExports.get( + decodeURIComponent(encodedTargetURL) + ); let body = [ 'export {};', 'let mapping = {__proto__: null};' ]; for (const [i, name] of Object.entries(exports)) { let key = JSON.stringify(name); + body.push(`import.meta.mock = globalThis.mockedModules.get('${encodedTargetURL}');`); body.push(`var _${i} = import.meta.mock.namespace[${key}];`); body.push(`Object.defineProperty(mapping, ${key}, { enumerable: true, set(v) {_${i} = v;}, get() {return _${i};} });`); body.push(`export {_${i} as ${name}};`); From a12d520550868931cc68b0cb15fd8d865732efca Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sun, 3 Sep 2023 09:19:58 -0700 Subject: [PATCH 2/6] Move setup code into fixture; avoid global; add JSON.stringify, __proto__ --- test/es-module/test-esm-loader-mock.mjs | 73 +------------------ .../es-module-loaders/mock-loader.mjs | 4 +- test/fixtures/es-module-loaders/mock.mjs | 68 +++++++++++++++++ 3 files changed, 73 insertions(+), 72 deletions(-) create mode 100644 test/fixtures/es-module-loaders/mock.mjs diff --git a/test/es-module/test-esm-loader-mock.mjs b/test/es-module/test-esm-loader-mock.mjs index 0a66df46aeefbf..09458567657c5b 100644 --- a/test/es-module/test-esm-loader-mock.mjs +++ b/test/es-module/test-esm-loader-mock.mjs @@ -1,73 +1,6 @@ -// Flags: --no-warnings -import '../common/index.mjs'; -import * as fixtures from '../common/fixtures.mjs'; +// Flags: --no-warnings --import ./test/fixtures/es-module-loaders/mock.mjs import assert from 'node:assert/strict'; -import { register } from 'node:module'; -import { MessageChannel } from 'node:worker_threads'; - -const { port1, port2 } = new MessageChannel(); - -register(fixtures.fileURL('es-module-loaders/mock-loader.mjs'), { - parentURL: import.meta.url, - data: { port: port2 }, - transferList: [port2], -}); - -/** - * This is the Map that saves *all* the mocked URL -> replacement Module - * mappings - * @type {Map} - */ -globalThis.mockedModules = new Map(); -let mockVersion = 0; - -/** - * @param {string} resolved an absolute URL HREF string - * @param {object} replacementProperties an object to pick properties from - * to act as a module namespace - * @returns {object} a mutator object that can update the module namespace - * since we can't do something like old Object.observe - */ -function mock(resolved, replacementProperties) { - const exportNames = Object.keys(replacementProperties); - const namespace = { __proto__: null }; - /** - * @type {Array<(name: string)=>void>} functions to call whenever an - * export name is updated - */ - const listeners = []; - for (const name of exportNames) { - let currentValueForPropertyName = replacementProperties[name]; - Object.defineProperty(namespace, name, { - enumerable: true, - get() { - return currentValueForPropertyName; - }, - set(v) { - currentValueForPropertyName = v; - for (const fn of listeners) { - try { - fn(name); - } catch { - /* noop */ - } - } - } - }); - } - globalThis.mockedModules.set(encodeURIComponent(resolved), { - namespace, - listeners - }); - mockVersion++; - // Inform the loader that the `resolved` URL should now use the specific - // `mockVersion` and has export names of `exportNames` - // - // This allows the loader to generate a fake module for that version - // and names the next time it resolves a specifier to equal `resolved` - port1.postMessage({ mockVersion, resolved, exports: exportNames }); - return namespace; -} +import { mock } from '../fixtures/es-module-loaders/mock.mjs'; mock('node:events', { EventEmitter: 'This is mocked!' @@ -107,5 +40,3 @@ assert.deepStrictEqual(mockedV2, Object.defineProperty({ enumerable: false, value: 'Module' })); - -delete globalThis.mockedModules; diff --git a/test/fixtures/es-module-loaders/mock-loader.mjs b/test/fixtures/es-module-loaders/mock-loader.mjs index 9e5f57658ad6e6..de99a3f2a58cc1 100644 --- a/test/fixtures/es-module-loaders/mock-loader.mjs +++ b/test/fixtures/es-module-loaders/mock-loader.mjs @@ -75,6 +75,7 @@ export async function load(url, context, defaultLoad) { return defaultLoad(url, context); } +const mainImportURL = new URL('./mock.mjs', import.meta.url); /** * Generate the source code for a mocked module. * @param {string} encodedTargetURL the module being mocked @@ -85,12 +86,13 @@ function generateModule(encodedTargetURL) { decodeURIComponent(encodedTargetURL) ); let body = [ + `import { mockedModules } from '${mainImportURL}';`, 'export {};', 'let mapping = {__proto__: null};' ]; for (const [i, name] of Object.entries(exports)) { let key = JSON.stringify(name); - body.push(`import.meta.mock = globalThis.mockedModules.get('${encodedTargetURL}');`); + body.push(`import.meta.mock = mockedModules.get(${JSON.stringify(encodedTargetURL)});`); body.push(`var _${i} = import.meta.mock.namespace[${key}];`); body.push(`Object.defineProperty(mapping, ${key}, { enumerable: true, set(v) {_${i} = v;}, get() {return _${i};} });`); body.push(`export {_${i} as ${name}};`); diff --git a/test/fixtures/es-module-loaders/mock.mjs b/test/fixtures/es-module-loaders/mock.mjs new file mode 100644 index 00000000000000..3ab8e21f09695a --- /dev/null +++ b/test/fixtures/es-module-loaders/mock.mjs @@ -0,0 +1,68 @@ +import * as fixtures from '../../common/fixtures.mjs'; +import { register } from 'node:module'; +import { MessageChannel } from 'node:worker_threads'; + + +const { port1, port2 } = new MessageChannel(); + +register(fixtures.fileURL('es-module-loaders/mock-loader.mjs'), { + data: { port: port2 }, + transferList: [port2], +}); + +/** + * This is the Map that saves *all* the mocked URL -> replacement Module + * mappings + * @type {Map} + */ +export const mockedModules = new Map(); +let mockVersion = 0; + +/** + * @param {string} resolved an absolute URL HREF string + * @param {object} replacementProperties an object to pick properties from + * to act as a module namespace + * @returns {object} a mutator object that can update the module namespace + * since we can't do something like old Object.observe + */ +export function mock(resolved, replacementProperties) { + const exportNames = Object.keys(replacementProperties); + const namespace = { __proto__: null }; + /** + * @type {Array<(name: string)=>void>} functions to call whenever an + * export name is updated + */ + const listeners = []; + for (const name of exportNames) { + let currentValueForPropertyName = replacementProperties[name]; + Object.defineProperty(namespace, name, { + __proto__: null, + enumerable: true, + get() { + return currentValueForPropertyName; + }, + set(v) { + currentValueForPropertyName = v; + for (const fn of listeners) { + try { + fn(name); + } catch { + /* noop */ + } + } + } + }); + } + mockedModules.set(encodeURIComponent(resolved), { + namespace, + listeners + }); + mockVersion++; + // Inform the loader that the `resolved` URL should now use the specific + // `mockVersion` and has export names of `exportNames` + // + // This allows the loader to generate a fake module for that version + // and names the next time it resolves a specifier to equal `resolved` + port1.postMessage({ mockVersion, resolved, exports: exportNames }); + return namespace; +} From 844fe455701928518ffb3212b42fec18a99221f8 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sun, 3 Sep 2023 09:41:47 -0700 Subject: [PATCH 3/6] Review notes --- test/es-module/test-esm-loader-mock.mjs | 2 +- test/fixtures/es-module-loaders/mock-loader.mjs | 11 ++++++----- test/fixtures/es-module-loaders/mock.mjs | 5 ++++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/test/es-module/test-esm-loader-mock.mjs b/test/es-module/test-esm-loader-mock.mjs index 09458567657c5b..0e34ce96fa3688 100644 --- a/test/es-module/test-esm-loader-mock.mjs +++ b/test/es-module/test-esm-loader-mock.mjs @@ -1,4 +1,4 @@ -// Flags: --no-warnings --import ./test/fixtures/es-module-loaders/mock.mjs +// Flags: --import ./test/fixtures/es-module-loaders/mock.mjs import assert from 'node:assert/strict'; import { mock } from '../fixtures/es-module-loaders/mock.mjs'; diff --git a/test/fixtures/es-module-loaders/mock-loader.mjs b/test/fixtures/es-module-loaders/mock-loader.mjs index de99a3f2a58cc1..faaeac81b1ae75 100644 --- a/test/fixtures/es-module-loaders/mock-loader.mjs +++ b/test/fixtures/es-module-loaders/mock-loader.mjs @@ -33,9 +33,11 @@ let currentMockVersion = 0; // assert(namespace1 === namespace2); // ``` - -export async function initialize({ port }) { - port.on('message', ({ mockVersion, resolved, exports }) => { +/** @type {string} */ +let mainImportURL; +export async function initialize(data) { + mainImportURL = data.mainImportURL; + data.port.on('message', ({ mockVersion, resolved, exports }) => { currentMockVersion = mockVersion; mockedModuleExports.set(resolved, exports); }); @@ -75,7 +77,6 @@ export async function load(url, context, defaultLoad) { return defaultLoad(url, context); } -const mainImportURL = new URL('./mock.mjs', import.meta.url); /** * Generate the source code for a mocked module. * @param {string} encodedTargetURL the module being mocked @@ -86,7 +87,7 @@ function generateModule(encodedTargetURL) { decodeURIComponent(encodedTargetURL) ); let body = [ - `import { mockedModules } from '${mainImportURL}';`, + `import { mockedModules } from ${JSON.stringify(mainImportURL)};`, 'export {};', 'let mapping = {__proto__: null};' ]; diff --git a/test/fixtures/es-module-loaders/mock.mjs b/test/fixtures/es-module-loaders/mock.mjs index 3ab8e21f09695a..5d857bbff4a72a 100644 --- a/test/fixtures/es-module-loaders/mock.mjs +++ b/test/fixtures/es-module-loaders/mock.mjs @@ -6,7 +6,10 @@ import { MessageChannel } from 'node:worker_threads'; const { port1, port2 } = new MessageChannel(); register(fixtures.fileURL('es-module-loaders/mock-loader.mjs'), { - data: { port: port2 }, + data: { + port: port2, + mainImportURL: import.meta.url, + }, transferList: [port2], }); From d1bedb75fac617c6d9d396289667cd796e73fdb3 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 3 Sep 2023 20:44:57 +0200 Subject: [PATCH 4/6] do not mutate `import.meta`, trailing commas, add `doDrainPort` back --- test/es-module/test-esm-loader-mock.mjs | 2 +- .../es-module-loaders/mock-loader.mjs | 57 +++++++++++++------ test/fixtures/es-module-loaders/mock.mjs | 7 +-- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/test/es-module/test-esm-loader-mock.mjs b/test/es-module/test-esm-loader-mock.mjs index 0e34ce96fa3688..164d0ac3775039 100644 --- a/test/es-module/test-esm-loader-mock.mjs +++ b/test/es-module/test-esm-loader-mock.mjs @@ -1,4 +1,4 @@ -// Flags: --import ./test/fixtures/es-module-loaders/mock.mjs +import '../common/index.mjs'; import assert from 'node:assert/strict'; import { mock } from '../fixtures/es-module-loaders/mock.mjs'; diff --git a/test/fixtures/es-module-loaders/mock-loader.mjs b/test/fixtures/es-module-loaders/mock-loader.mjs index faaeac81b1ae75..1736f6105e729c 100644 --- a/test/fixtures/es-module-loaders/mock-loader.mjs +++ b/test/fixtures/es-module-loaders/mock-loader.mjs @@ -1,7 +1,8 @@ +import { receiveMessageOnPort } from 'node:worker_threads'; const mockedModuleExports = new Map(); let currentMockVersion = 0; -// This loader enables code running on the application thread to +// These hooks enable code running on the application thread to // swap module resolution results for mocking purposes. It uses this instead // of import.meta so that CommonJS can still use the functionality. // @@ -33,18 +34,40 @@ let currentMockVersion = 0; // assert(namespace1 === namespace2); // ``` -/** @type {string} */ -let mainImportURL; +/** + * @param param0 message from the application context + */ +function onPreloadPortMessage({ + mockVersion, resolved, exports +}) { + currentMockVersion = mockVersion; + mockedModuleExports.set(resolved, exports); +} + +/** @type {URL['href']} */ +let mainImportURL +/** @type {MessagePort} */ +let preloadPort; export async function initialize(data) { - mainImportURL = data.mainImportURL; - data.port.on('message', ({ mockVersion, resolved, exports }) => { - currentMockVersion = mockVersion; - mockedModuleExports.set(resolved, exports); - }); + ({ mainImportURL, port: preloadPort } = data); + + data.port.on('message', onPreloadPortMessage); +} + +/** + * FIXME: this is a hack to workaround loaders being + * single threaded for now, just ensures that the MessagePort drains + */ +function doDrainPort() { + let msg; + while (msg = receiveMessageOnPort(preloadPort)) { + onPreloadPortMessage(msg.message); + } } // Rewrites node: loading to mock-facade: so that it can be intercepted export async function resolve(specifier, context, defaultResolve) { + doDrainPort(); const def = await defaultResolve(specifier, context); if (context.parentURL?.startsWith('mock-facade:')) { // Do nothing, let it get the "real" module @@ -61,17 +84,17 @@ export async function resolve(specifier, context, defaultResolve) { } export async function load(url, context, defaultLoad) { + doDrainPort(); /** * Mocked fake module, not going to be handled in default way so it * generates the source text, then short circuits */ if (url.startsWith('mock-facade:')) { - let [_proto, _version, encodedTargetURL] = url.split(':'); - let source = generateModule(encodedTargetURL); + const encodedTargetURL = url.slice(url.lastIndexOf(':') + 1); return { shortCircuit: true, - source, - format: 'module' + source: generateModule(encodedTargetURL), + format: 'module', }; } return defaultLoad(url, context); @@ -89,19 +112,19 @@ function generateModule(encodedTargetURL) { let body = [ `import { mockedModules } from ${JSON.stringify(mainImportURL)};`, 'export {};', - 'let mapping = {__proto__: null};' + 'let mapping = {__proto__: null};', + `const mock = mockedModules.get(${JSON.stringify(encodedTargetURL)});`, ]; for (const [i, name] of Object.entries(exports)) { let key = JSON.stringify(name); - body.push(`import.meta.mock = mockedModules.get(${JSON.stringify(encodedTargetURL)});`); - body.push(`var _${i} = import.meta.mock.namespace[${key}];`); + body.push(`var _${i} = mock.namespace[${key}];`); body.push(`Object.defineProperty(mapping, ${key}, { enumerable: true, set(v) {_${i} = v;}, get() {return _${i};} });`); body.push(`export {_${i} as ${name}};`); } - body.push(`import.meta.mock.listeners.push(${ + body.push(`mock.listeners.push(${ () => { for (var k in mapping) { - mapping[k] = import.meta.mock.namespace[k]; + mapping[k] = mock.namespace[k]; } } });`); diff --git a/test/fixtures/es-module-loaders/mock.mjs b/test/fixtures/es-module-loaders/mock.mjs index 5d857bbff4a72a..cb167f1d5204c7 100644 --- a/test/fixtures/es-module-loaders/mock.mjs +++ b/test/fixtures/es-module-loaders/mock.mjs @@ -1,11 +1,10 @@ -import * as fixtures from '../../common/fixtures.mjs'; import { register } from 'node:module'; import { MessageChannel } from 'node:worker_threads'; const { port1, port2 } = new MessageChannel(); -register(fixtures.fileURL('es-module-loaders/mock-loader.mjs'), { +register('./mock-loader.mjs', import.meta.url, { data: { port: port2, mainImportURL: import.meta.url, @@ -53,12 +52,12 @@ export function mock(resolved, replacementProperties) { /* noop */ } } - } + }, }); } mockedModules.set(encodeURIComponent(resolved), { namespace, - listeners + listeners, }); mockVersion++; // Inform the loader that the `resolved` URL should now use the specific From 34cc279239ae9c4a993828fe94a651b6555b92db Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 4 Sep 2023 00:57:28 +0200 Subject: [PATCH 5/6] Update test/fixtures/es-module-loaders/mock-loader.mjs --- test/fixtures/es-module-loaders/mock-loader.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/fixtures/es-module-loaders/mock-loader.mjs b/test/fixtures/es-module-loaders/mock-loader.mjs index 1736f6105e729c..e509f725ef7f6a 100644 --- a/test/fixtures/es-module-loaders/mock-loader.mjs +++ b/test/fixtures/es-module-loaders/mock-loader.mjs @@ -55,8 +55,9 @@ export async function initialize(data) { } /** - * FIXME: this is a hack to workaround loaders being - * single threaded for now, just ensures that the MessagePort drains + * Because Node.js internals use a separate MessagePort for cross-thread + * communication, there could be some messages pending that we should handle + * before continuing. */ function doDrainPort() { let msg; From 7a58719f5a582f1278f9a5e1991ade209fed8159 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 4 Sep 2023 01:02:32 +0200 Subject: [PATCH 6/6] Update test/fixtures/es-module-loaders/mock-loader.mjs --- test/fixtures/es-module-loaders/mock-loader.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/es-module-loaders/mock-loader.mjs b/test/fixtures/es-module-loaders/mock-loader.mjs index e509f725ef7f6a..3bb349b5385362 100644 --- a/test/fixtures/es-module-loaders/mock-loader.mjs +++ b/test/fixtures/es-module-loaders/mock-loader.mjs @@ -45,7 +45,7 @@ function onPreloadPortMessage({ } /** @type {URL['href']} */ -let mainImportURL +let mainImportURL; /** @type {MessagePort} */ let preloadPort; export async function initialize(data) {