summaryrefslogtreecommitdiff
path: root/lib/internal/modules/esm/create_dynamic_module.js
blob: 8e93a08502c3cfa5727c6e3150b5b0b0932ae37c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
'use strict';

const { ModuleWrap } = internalBinding('module_wrap');
const debug = require('util').debuglog('esm');
const ArrayJoin = Function.call.bind(Array.prototype.join);
const ArrayMap = Function.call.bind(Array.prototype.map);

const createDynamicModule = (exports, url = '', evaluate) => {
  debug(
    `creating ESM facade for ${url} with exports: ${ArrayJoin(exports, ', ')}`
  );
  const names = ArrayMap(exports, (name) => `${name}`);
  // Create two modules: One whose exports are get- and set-able ('reflective'),
  // and one which re-exports all of these but additionally may
  // run an executor function once everything is set up.
  const src = `
  export let executor;
  ${ArrayJoin(ArrayMap(names, (name) => `export let $${name};`), '\n')}
  /* This function is implicitly returned as the module's completion value */
  (() => ({
    setExecutor: fn => executor = fn,
    reflect: {
      exports: { ${
  ArrayJoin(ArrayMap(names, (name) => `
        ${name}: {
          get: () => $${name},
          set: v => $${name} = v
        }`), ', \n')}
      }
    }
  }));`;
  const reflectiveModule = new ModuleWrap(src, `cjs-facade:${url}`);
  reflectiveModule.instantiate();
  const { setExecutor, reflect } = reflectiveModule.evaluate(-1, false)();
  // public exposed ESM
  const reexports = `
  import {
    executor,
    ${ArrayMap(names, (name) => `$${name}`)}
  } from "";
  export {
    ${ArrayJoin(ArrayMap(names, (name) => `$${name} as ${name}`), ', ')}
  }
  if (typeof executor === "function") {
    // add await to this later if top level await comes along
    executor()
  }`;
  if (typeof evaluate === 'function') {
    setExecutor(() => evaluate(reflect));
  }
  const module = new ModuleWrap(reexports, `${url}`);
  module.link(async () => reflectiveModule);
  module.instantiate();
  reflect.namespace = module.namespace();
  return {
    module,
    reflect,
  };
};

module.exports = createDynamicModule;