1'use strict'; 2 3const { 4 ArrayPrototypeJoin, 5 ArrayPrototypeMap, 6 JSONStringify, 7 ObjectCreate, 8 SafeSet, 9} = primordials; 10 11let debug = require('internal/util/debuglog').debuglog('esm', (fn) => { 12 debug = fn; 13}); 14 15/** 16 * Creates an import statement for a given module path and index. 17 * @param {string} impt - The module path to import. 18 * @param {number} index - The index of the import statement. 19 */ 20function createImport(impt, index) { 21 const imptPath = JSONStringify(impt); 22 return `import * as $import_${index} from ${imptPath}; 23import.meta.imports[${imptPath}] = $import_${index};`; 24} 25 26/** 27 * Creates an export for a given module. 28 * @param {string} expt - The name of the export. 29 * @param {number} index - The index of the export statement. 30 */ 31function createExport(expt, index) { 32 const nameStringLit = JSONStringify(expt); 33 return `let $export_${index}; 34export { $export_${index} as ${nameStringLit} }; 35import.meta.exports[${nameStringLit}] = { 36 get: () => $export_${index}, 37 set: (v) => $export_${index} = v, 38};`; 39} 40 41/** 42 * Creates a dynamic module with the given imports, exports, URL, and evaluate function. 43 * @param {string[]} imports - An array of imports. 44 * @param {string[]} exports - An array of exports. 45 * @param {string} [url=''] - The URL of the module. 46 * @param {(reflect: DynamicModuleReflect) => void} evaluate - The function to evaluate the module. 47 * @typedef {object} DynamicModuleReflect 48 * @property {string[]} imports - The imports of the module. 49 * @property {string[]} exports - The exports of the module. 50 * @property {(cb: (reflect: DynamicModuleReflect) => void) => void} onReady - Callback to evaluate the module. 51 */ 52const createDynamicModule = (imports, exports, url = '', evaluate) => { 53 debug('creating ESM facade for %s with exports: %j', url, exports); 54 const source = ` 55${ArrayPrototypeJoin(ArrayPrototypeMap(imports, createImport), '\n')} 56${ArrayPrototypeJoin(ArrayPrototypeMap(exports, createExport), '\n')} 57import.meta.done(); 58`; 59 const { ModuleWrap } = internalBinding('module_wrap'); 60 const m = new ModuleWrap(`${url}`, undefined, source, 0, 0); 61 62 const readyfns = new SafeSet(); 63 /** @type {DynamicModuleReflect} */ 64 const reflect = { 65 exports: ObjectCreate(null), 66 onReady: (cb) => { readyfns.add(cb); }, 67 }; 68 69 if (imports.length) { 70 reflect.imports = { __proto__: null }; 71 } 72 const { registerModule } = require('internal/modules/esm/utils'); 73 registerModule(m, { 74 __proto__: null, 75 initializeImportMeta: (meta, wrap) => { 76 meta.exports = reflect.exports; 77 if (reflect.imports) { 78 meta.imports = reflect.imports; 79 } 80 meta.done = () => { 81 evaluate(reflect); 82 reflect.onReady = (cb) => cb(reflect); 83 for (const fn of readyfns) { 84 readyfns.delete(fn); 85 fn(reflect); 86 } 87 }; 88 }, 89 }); 90 91 return { 92 module: m, 93 reflect, 94 }; 95}; 96 97module.exports = createDynamicModule; 98