| 'use strict'; |
| |
| const { |
| StringPrototypeEndsWith, |
| } = primordials; |
| |
| const { containsModuleSyntax } = internalBinding('contextify'); |
| const { getNearestParentPackageJSONType } = internalBinding('modules'); |
| const { getOptionValue } = require('internal/options'); |
| const { checkPackageJSONIntegrity } = require('internal/modules/package_json_reader'); |
| const path = require('path'); |
| |
| /** |
| * 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 } = require('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 } = require('internal/modules/helpers'); |
| try { |
| mainPath = toRealPath(mainPath); |
| } catch (err) { |
| if (defaultType === 'module' && err?.code === 'ENOENT') { |
| const { decorateErrorWithCommonJSHints } = require('internal/modules/esm/resolve'); |
| const { getCWDURL } = require('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 && StringPrototypeEndsWith(mainPath, '.mjs')) { return true; } |
| if (!mainPath || StringPrototypeEndsWith(mainPath, '.cjs')) { return false; } |
| |
| const response = getNearestParentPackageJSONType(mainPath); |
| |
| // No package.json or no `type` field. |
| if (response === undefined || response[0] === 'none') { |
| if (getOptionValue('--experimental-detect-module')) { |
| // If the first argument of `containsModuleSyntax` is undefined, it will read `mainPath` from the file system. |
| return containsModuleSyntax(undefined, mainPath); |
| } |
| return false; |
| } |
| |
| // TODO(@anonrig): Do not return filePath and rawContent if experimental-policy is not used. |
| const { |
| 0: type, |
| 1: filePath, |
| 2: rawContent, |
| } = response; |
| |
| checkPackageJSONIntegrity(filePath, rawContent); |
| |
| return type === 'module'; |
| } |
| |
| /** |
| * Run the main entry point through the ESM Loader. |
| * @param {string} mainPath - Absolute path for the main entry point |
| */ |
| function runMainESM(mainPath) { |
| const { loadESM } = require('internal/process/esm_loader'); |
| const { pathToFileURL } = require('internal/url'); |
| const main = pathToFileURL(mainPath).href; |
| |
| handleMainPromise(loadESM((esmLoader) => { |
| return esmLoader.import(main, undefined, { __proto__: null }); |
| })); |
| } |
| |
| /** |
| * Handle process exit events around the main entry point promise. |
| * @param {Promise} promise - Main entry point promise |
| */ |
| async function handleMainPromise(promise) { |
| const { |
| handleProcessExit, |
| } = require('internal/modules/esm/handle_process_exit'); |
| process.on('exit', handleProcessExit); |
| try { |
| return await promise; |
| } finally { |
| process.off('exit', handleProcessExit); |
| } |
| } |
| |
| /** |
| * 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'`. |
| * @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); |
| if (useESMLoader) { |
| runMainESM(resolvedMain || main); |
| } else { |
| // Module._load is the monkey-patchable CJS module loader. |
| const { Module } = require('internal/modules/cjs/loader'); |
| Module._load(main, null, true); |
| } |
| } |
| |
| module.exports = { |
| executeUserEntryPoint, |
| handleMainPromise, |
| }; |