• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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