'use strict'; const { ArrayIsArray, ArrayPrototypePushApply, } = primordials; const { ESMLoader } = require('internal/modules/esm/loader'); const { hasUncaughtExceptionCaptureCallback, } = require('internal/process/execution'); const { pathToFileURL } = require('internal/url'); const { kEmptyObject } = require('internal/util'); const esmLoader = new ESMLoader(); exports.esmLoader = esmLoader; // Module.runMain() causes loadESM() to re-run (which it should do); however, this should NOT cause // ESM to be re-initialised; doing so causes duplicate custom loaders to be added to the public // esmLoader. let isESMInitialized = false; /** * Causes side-effects: user-defined loader hooks are added to esmLoader. * @returns {void} */ async function initializeLoader() { if (isESMInitialized) { return; } const { getOptionValue } = require('internal/options'); const customLoaders = getOptionValue('--experimental-loader'); const preloadModules = getOptionValue('--import'); let cwd; try { cwd = process.cwd() + '/'; } catch { cwd = '/'; } const internalEsmLoader = new ESMLoader(); const allLoaders = []; const parentURL = pathToFileURL(cwd).href; for (let i = 0; i < customLoaders.length; i++) { const customLoader = customLoaders[i]; // Importation must be handled by internal loader to avoid polluting user-land const keyedExportsSublist = await internalEsmLoader.import( [customLoader], parentURL, kEmptyObject, ); internalEsmLoader.addCustomLoaders(keyedExportsSublist); ArrayPrototypePushApply(allLoaders, keyedExportsSublist); } // Hooks must then be added to external/public loader // (so they're triggered in userland) esmLoader.addCustomLoaders(allLoaders); esmLoader.preload(); // Preload after loaders are added so they can be used if (preloadModules?.length) { await loadModulesInIsolation(parentURL, preloadModules, allLoaders); } isESMInitialized = true; } function loadModulesInIsolation(parentURL, specifiers, loaders = []) { if (!ArrayIsArray(specifiers) || specifiers.length === 0) { return; } // A separate loader instance is necessary to avoid cross-contamination // between internal Node.js and userland. For example, a module with internal // state (such as a counter) should be independent. const internalEsmLoader = new ESMLoader(); internalEsmLoader.addCustomLoaders(loaders); internalEsmLoader.preload(); // Importation must be handled by internal loader to avoid poluting userland return internalEsmLoader.import( specifiers, parentURL, kEmptyObject, ); } exports.loadESM = async function loadESM(callback) { try { await initializeLoader(); await callback(esmLoader); } catch (err) { if (hasUncaughtExceptionCaptureCallback()) { process._fatalException(err); return; } internalBinding('errors').triggerUncaughtException( err, true /* fromPromise */ ); } };