1+ import { receiveMessageOnPort } from 'node:worker_threads' ;
12const mockedModuleExports = new Map ( ) ;
23let currentMockVersion = 0 ;
34
4- // This loader enables code running on the application thread to
5+ // These hooks enable code running on the application thread to
56// swap module resolution results for mocking purposes. It uses this instead
67// of import.meta so that CommonJS can still use the functionality.
78//
@@ -33,18 +34,40 @@ let currentMockVersion = 0;
3334// assert(namespace1 === namespace2);
3435// ```
3536
36- /** @type {string } */
37- let mainImportURL ;
37+ /**
38+ * @param param0 message from the application context
39+ */
40+ function onPreloadPortMessage ( {
41+ mockVersion, resolved, exports
42+ } ) {
43+ currentMockVersion = mockVersion ;
44+ mockedModuleExports . set ( resolved , exports ) ;
45+ }
46+
47+ /** @type {URL['href'] } */
48+ let mainImportURL
49+ /** @type {MessagePort } */
50+ let preloadPort ;
3851export async function initialize ( data ) {
39- mainImportURL = data . mainImportURL ;
40- data . port . on ( 'message' , ( { mockVersion, resolved, exports } ) => {
41- currentMockVersion = mockVersion ;
42- mockedModuleExports . set ( resolved , exports ) ;
43- } ) ;
52+ ( { mainImportURL, port : preloadPort } = data ) ;
53+
54+ data . port . on ( 'message' , onPreloadPortMessage ) ;
55+ }
56+
57+ /**
58+ * FIXME: this is a hack to workaround loaders being
59+ * single threaded for now, just ensures that the MessagePort drains
60+ */
61+ function doDrainPort ( ) {
62+ let msg ;
63+ while ( msg = receiveMessageOnPort ( preloadPort ) ) {
64+ onPreloadPortMessage ( msg . message ) ;
65+ }
4466}
4567
4668// Rewrites node: loading to mock-facade: so that it can be intercepted
4769export async function resolve ( specifier , context , defaultResolve ) {
70+ doDrainPort ( ) ;
4871 const def = await defaultResolve ( specifier , context ) ;
4972 if ( context . parentURL ?. startsWith ( 'mock-facade:' ) ) {
5073 // Do nothing, let it get the "real" module
@@ -61,17 +84,17 @@ export async function resolve(specifier, context, defaultResolve) {
6184}
6285
6386export async function load ( url , context , defaultLoad ) {
87+ doDrainPort ( ) ;
6488 /**
6589 * Mocked fake module, not going to be handled in default way so it
6690 * generates the source text, then short circuits
6791 */
6892 if ( url . startsWith ( 'mock-facade:' ) ) {
69- let [ _proto , _version , encodedTargetURL ] = url . split ( ':' ) ;
70- let source = generateModule ( encodedTargetURL ) ;
93+ const encodedTargetURL = url . slice ( url . lastIndexOf ( ':' ) + 1 ) ;
7194 return {
7295 shortCircuit : true ,
73- source,
74- format : 'module'
96+ source : generateModule ( encodedTargetURL ) ,
97+ format : 'module' ,
7598 } ;
7699 }
77100 return defaultLoad ( url , context ) ;
@@ -89,19 +112,19 @@ function generateModule(encodedTargetURL) {
89112 let body = [
90113 `import { mockedModules } from ${ JSON . stringify ( mainImportURL ) } ;` ,
91114 'export {};' ,
92- 'let mapping = {__proto__: null};'
115+ 'let mapping = {__proto__: null};' ,
116+ `const mock = mockedModules.get(${ JSON . stringify ( encodedTargetURL ) } );` ,
93117 ] ;
94118 for ( const [ i , name ] of Object . entries ( exports ) ) {
95119 let key = JSON . stringify ( name ) ;
96- body . push ( `import.meta.mock = mockedModules.get(${ JSON . stringify ( encodedTargetURL ) } );` ) ;
97- body . push ( `var _${ i } = import.meta.mock.namespace[${ key } ];` ) ;
120+ body . push ( `var _${ i } = mock.namespace[${ key } ];` ) ;
98121 body . push ( `Object.defineProperty(mapping, ${ key } , { enumerable: true, set(v) {_${ i } = v;}, get() {return _${ i } ;} });` ) ;
99122 body . push ( `export {_${ i } as ${ name } };` ) ;
100123 }
101- body . push ( `import.meta. mock.listeners.push(${
124+ body . push ( `mock.listeners.push(${
102125 ( ) => {
103126 for ( var k in mapping ) {
104- mapping [ k ] = import . meta . mock . namespace [ k ] ;
127+ mapping [ k ] = mock . namespace [ k ] ;
105128 }
106129 }
107130 } );`) ;
0 commit comments