• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  ArrayIsArray,
5  ObjectCreate,
6} = primordials;
7
8const {
9  ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING,
10} = require('internal/errors').codes;
11const { ESMLoader } = require('internal/modules/esm/loader');
12const {
13  hasUncaughtExceptionCaptureCallback,
14} = require('internal/process/execution');
15const { pathToFileURL } = require('internal/url');
16const {
17  getModuleFromWrap,
18} = require('internal/vm/module');
19
20exports.initializeImportMetaObject = function(wrap, meta) {
21  const { callbackMap } = internalBinding('module_wrap');
22  if (callbackMap.has(wrap)) {
23    const { initializeImportMeta } = callbackMap.get(wrap);
24    if (initializeImportMeta !== undefined) {
25      initializeImportMeta(meta, getModuleFromWrap(wrap) || wrap);
26    }
27  }
28};
29
30exports.importModuleDynamicallyCallback =
31async function importModuleDynamicallyCallback(wrap, specifier, assertions) {
32  const { callbackMap } = internalBinding('module_wrap');
33  if (callbackMap.has(wrap)) {
34    const { importModuleDynamically } = callbackMap.get(wrap);
35    if (importModuleDynamically !== undefined) {
36      return importModuleDynamically(
37        specifier, getModuleFromWrap(wrap) || wrap, assertions);
38    }
39  }
40  throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING();
41};
42
43const esmLoader = new ESMLoader();
44exports.esmLoader = esmLoader;
45
46// Module.runMain() causes loadESM() to re-run (which it should do); however, this should NOT cause
47// ESM to be re-initialised; doing so causes duplicate custom loaders to be added to the public
48// esmLoader.
49let isESMInitialized = false;
50
51/**
52 * Causes side-effects: user-defined loader hooks are added to esmLoader.
53 * @returns {void}
54 */
55async function initializeLoader() {
56  if (isESMInitialized) { return; }
57
58  const { getOptionValue } = require('internal/options');
59  const customLoaders = getOptionValue('--experimental-loader');
60  const preloadModules = getOptionValue('--import');
61  const loaders = await loadModulesInIsolation(customLoaders);
62
63  // Hooks must then be added to external/public loader
64  // (so they're triggered in userland)
65  esmLoader.addCustomLoaders(loaders);
66
67  // Preload after loaders are added so they can be used
68  if (preloadModules?.length) {
69    await loadModulesInIsolation(preloadModules, loaders);
70  }
71
72  isESMInitialized = true;
73}
74
75function loadModulesInIsolation(specifiers, loaders = []) {
76  if (!ArrayIsArray(specifiers) || specifiers.length === 0) { return; }
77
78  let cwd;
79  try {
80    cwd = process.cwd() + '/';
81  } catch {
82    cwd = 'file:///';
83  }
84
85  // A separate loader instance is necessary to avoid cross-contamination
86  // between internal Node.js and userland. For example, a module with internal
87  // state (such as a counter) should be independent.
88  const internalEsmLoader = new ESMLoader();
89  internalEsmLoader.addCustomLoaders(loaders);
90
91  // Importation must be handled by internal loader to avoid poluting userland
92  return internalEsmLoader.import(
93    specifiers,
94    pathToFileURL(cwd).href,
95    ObjectCreate(null),
96  );
97}
98
99exports.loadESM = async function loadESM(callback) {
100  try {
101    await initializeLoader();
102    await callback(esmLoader);
103  } catch (err) {
104    if (hasUncaughtExceptionCaptureCallback()) {
105      process._fatalException(err);
106      return;
107    }
108    internalBinding('errors').triggerUncaughtException(
109      err,
110      true, /* fromPromise */
111    );
112  }
113};
114