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