UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

199 lines (182 loc) 7.71 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/modules/run_main.js import * as __hoisted_internal_modules_helpers__ from "nstdlib/lib/internal/modules/helpers"; import * as __hoisted_internal_modules_esm_resolve__ from "nstdlib/lib/internal/modules/esm/resolve"; import * as __hoisted_internal_util__ from "nstdlib/lib/internal/util"; import * as __hoisted_internal_modules_esm_loader__ from "nstdlib/lib/internal/modules/esm/loader"; import { getNearestParentPackageJSONType } from "nstdlib/stub/binding/modules"; import { getOptionValue } from "nstdlib/lib/internal/options"; import * as path from "nstdlib/lib/path"; import { pathToFileURL } from "nstdlib/lib/internal/url"; import { kEmptyObject, getCWDURL } from "nstdlib/lib/internal/util"; import { hasUncaughtExceptionCaptureCallback } from "nstdlib/lib/internal/process/execution"; import { triggerUncaughtException } from "nstdlib/stub/binding/errors"; import { privateSymbols as __privateSymbols__ } from "nstdlib/stub/binding/util"; import * as __hoisted_internal_modules_cjs_loader__ from "nstdlib/lib/internal/modules/cjs/loader"; const { entry_point_promise_private_symbol } = __privateSymbols__; /** * Get the absolute path to the main entry point. * @param {string} main - Entry point path */ function resolveMainPath(main) { const defaultType = getOptionValue("--experimental-default-type"); /** @type {string} */ let mainPath; if (defaultType === "module") { if (getOptionValue("--preserve-symlinks-main")) { return; } mainPath = path.resolve(main); } else { // Extension searching for the main entry point is supported only in legacy mode. // Module._findPath is monkey-patchable here. const { Module } = __hoisted_internal_modules_cjs_loader__; mainPath = Module._findPath(path.resolve(main), null, true); } if (!mainPath) { return; } const preserveSymlinksMain = getOptionValue("--preserve-symlinks-main"); if (!preserveSymlinksMain) { const { toRealPath } = __hoisted_internal_modules_helpers__; try { mainPath = toRealPath(mainPath); } catch (err) { if (defaultType === "module" && err?.code === "ENOENT") { const { decorateErrorWithCommonJSHints } = __hoisted_internal_modules_esm_resolve__; const { getCWDURL } = __hoisted_internal_util__; decorateErrorWithCommonJSHints(err, mainPath, getCWDURL()); } throw err; } } return mainPath; } /** * Determine whether the main entry point should be loaded through the ESM Loader. * @param {string} mainPath - Absolute path to the main entry point */ function shouldUseESMLoader(mainPath) { if (getOptionValue("--experimental-default-type") === "module") { return true; } /** * @type {string[]} userLoaders A list of custom loaders registered by the user * (or an empty list when none have been registered). */ const userLoaders = getOptionValue("--experimental-loader"); /** * @type {string[]} userImports A list of preloaded modules registered by the user * (or an empty list when none have been registered). */ const userImports = getOptionValue("--import"); if (userLoaders.length > 0 || userImports.length > 0) { return true; } // Determine the module format of the entry point. if (mainPath && String.prototype.endsWith.call(mainPath, ".mjs")) { return true; } if (!mainPath || String.prototype.endsWith.call(mainPath, ".cjs")) { return false; } if (getOptionValue("--experimental-strip-types")) { // This ensures that --experimental-default-type=commonjs and .mts files are treated as commonjs if (getOptionValue("--experimental-default-type") === "commonjs") { return false; } if (!mainPath || String.prototype.endsWith.call(mainPath, ".cts")) { return false; } // This will likely change in the future to start with commonjs loader by default if (mainPath && String.prototype.endsWith.call(mainPath, ".mts")) { return true; } } const type = getNearestParentPackageJSONType(mainPath); // No package.json or no `type` field. if (type === undefined || type === "none") { return false; } return type === "module"; } /** * @param {function(ModuleLoader):ModuleWrap|undefined} callback */ async function asyncRunEntryPointWithESMLoader(callback) { const cascadedLoader = __hoisted_internal_modules_esm_loader__.getOrInitializeCascadedLoader(); try { const userImports = getOptionValue("--import"); if (userImports.length > 0) { const parentURL = getCWDURL().href; for (let i = 0; i < userImports.length; i++) { await cascadedLoader.import(userImports[i], parentURL, kEmptyObject); } } else { cascadedLoader.forceLoadHooks(); } await callback(cascadedLoader); } catch (err) { if (hasUncaughtExceptionCaptureCallback()) { process._fatalException(err); return; } triggerUncaughtException(err, true /* fromPromise */); } } /** * This initializes the ESM loader and runs --import (if any) before executing the * callback to run the entry point. * If the callback intends to evaluate a ESM module as entry point, it should return * the corresponding ModuleWrap so that stalled TLA can be checked a process exit. * @param {function(ModuleLoader):ModuleWrap|undefined} callback * @returns {Promise} */ function runEntryPointWithESMLoader(callback) { const promise = asyncRunEntryPointWithESMLoader(callback); // Register the promise - if by the time the event loop finishes running, this is // still unsettled, we'll search the graph from the entry point module and print // the location of any unsettled top-level await found. globalThis[entry_point_promise_private_symbol] = promise; return promise; } /** * Parse the CLI main entry point string and run it. * For backwards compatibility, we have to run a bunch of monkey-patchable code that belongs to the CJS loader (exposed * by `require('module')`) even when the entry point is ESM. * This monkey-patchable code is bypassed under `--experimental-default-type=module`. * Because of backwards compatibility, this function is exposed publicly via `import { runMain } from 'node:module'`. * Because of module detection, this function will attempt to run ambiguous (no explicit extension, no * `package.json` type field) entry points as CommonJS first; under certain conditions, it will retry running as ESM. * @param {string} main - First positional CLI argument, such as `'entry.js'` from `node entry.js` */ function executeUserEntryPoint(main = process.argv[1]) { const resolvedMain = resolveMainPath(main); const useESMLoader = shouldUseESMLoader(resolvedMain); let mainURL; // Unless we know we should use the ESM loader to handle the entry point per the checks in `shouldUseESMLoader`, first // try to run the entry point via the CommonJS loader; and if that fails under certain conditions, retry as ESM. if (!useESMLoader) { const cjsLoader = __hoisted_internal_modules_cjs_loader__; const { wrapModuleLoad } = cjsLoader; wrapModuleLoad(main, null, true); } else { const mainPath = resolvedMain || main; if (mainURL === undefined) { mainURL = pathToFileURL(mainPath).href; } runEntryPointWithESMLoader((cascadedLoader) => { // Note that if the graph contains unsettled TLA, this may never resolve // even after the event loop stops running. return cascadedLoader.import( mainURL, undefined, { __proto__: null }, true, ); }); } } export { executeUserEntryPoint }; export { runEntryPointWithESMLoader };