11cb0ef41Sopenharmony_ciimport { register } from 'node:module'; 21cb0ef41Sopenharmony_ciimport { MessageChannel } from 'node:worker_threads'; 31cb0ef41Sopenharmony_ci 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ciconst { port1, port2 } = new MessageChannel(); 61cb0ef41Sopenharmony_ci 71cb0ef41Sopenharmony_ciregister('./mock-loader.mjs', import.meta.url, { 81cb0ef41Sopenharmony_ci data: { 91cb0ef41Sopenharmony_ci port: port2, 101cb0ef41Sopenharmony_ci mainImportURL: import.meta.url, 111cb0ef41Sopenharmony_ci }, 121cb0ef41Sopenharmony_ci transferList: [port2], 131cb0ef41Sopenharmony_ci}); 141cb0ef41Sopenharmony_ci 151cb0ef41Sopenharmony_ci/** 161cb0ef41Sopenharmony_ci * This is the Map that saves *all* the mocked URL -> replacement Module 171cb0ef41Sopenharmony_ci * mappings 181cb0ef41Sopenharmony_ci * @type {Map<string, {namespace, listeners}>} 191cb0ef41Sopenharmony_ci */ 201cb0ef41Sopenharmony_ciexport const mockedModules = new Map(); 211cb0ef41Sopenharmony_cilet mockVersion = 0; 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ci/** 241cb0ef41Sopenharmony_ci * @param {string} resolved an absolute URL HREF string 251cb0ef41Sopenharmony_ci * @param {object} replacementProperties an object to pick properties from 261cb0ef41Sopenharmony_ci * to act as a module namespace 271cb0ef41Sopenharmony_ci * @returns {object} a mutator object that can update the module namespace 281cb0ef41Sopenharmony_ci * since we can't do something like old Object.observe 291cb0ef41Sopenharmony_ci */ 301cb0ef41Sopenharmony_ciexport function mock(resolved, replacementProperties) { 311cb0ef41Sopenharmony_ci const exportNames = Object.keys(replacementProperties); 321cb0ef41Sopenharmony_ci const namespace = { __proto__: null }; 331cb0ef41Sopenharmony_ci /** 341cb0ef41Sopenharmony_ci * @type {Array<(name: string)=>void>} functions to call whenever an 351cb0ef41Sopenharmony_ci * export name is updated 361cb0ef41Sopenharmony_ci */ 371cb0ef41Sopenharmony_ci const listeners = []; 381cb0ef41Sopenharmony_ci for (const name of exportNames) { 391cb0ef41Sopenharmony_ci let currentValueForPropertyName = replacementProperties[name]; 401cb0ef41Sopenharmony_ci Object.defineProperty(namespace, name, { 411cb0ef41Sopenharmony_ci __proto__: null, 421cb0ef41Sopenharmony_ci enumerable: true, 431cb0ef41Sopenharmony_ci get() { 441cb0ef41Sopenharmony_ci return currentValueForPropertyName; 451cb0ef41Sopenharmony_ci }, 461cb0ef41Sopenharmony_ci set(v) { 471cb0ef41Sopenharmony_ci currentValueForPropertyName = v; 481cb0ef41Sopenharmony_ci for (const fn of listeners) { 491cb0ef41Sopenharmony_ci try { 501cb0ef41Sopenharmony_ci fn(name); 511cb0ef41Sopenharmony_ci } catch { 521cb0ef41Sopenharmony_ci /* noop */ 531cb0ef41Sopenharmony_ci } 541cb0ef41Sopenharmony_ci } 551cb0ef41Sopenharmony_ci }, 561cb0ef41Sopenharmony_ci }); 571cb0ef41Sopenharmony_ci } 581cb0ef41Sopenharmony_ci mockedModules.set(encodeURIComponent(resolved), { 591cb0ef41Sopenharmony_ci namespace, 601cb0ef41Sopenharmony_ci listeners, 611cb0ef41Sopenharmony_ci }); 621cb0ef41Sopenharmony_ci mockVersion++; 631cb0ef41Sopenharmony_ci // Inform the loader that the `resolved` URL should now use the specific 641cb0ef41Sopenharmony_ci // `mockVersion` and has export names of `exportNames` 651cb0ef41Sopenharmony_ci // 661cb0ef41Sopenharmony_ci // This allows the loader to generate a fake module for that version 671cb0ef41Sopenharmony_ci // and names the next time it resolves a specifier to equal `resolved` 681cb0ef41Sopenharmony_ci port1.postMessage({ mockVersion, resolved, exports: exportNames }); 691cb0ef41Sopenharmony_ci return namespace; 701cb0ef41Sopenharmony_ci} 71