1'use strict'; 2 3const { 4 StringPrototypeEndsWith, 5} = primordials; 6 7const { getOptionValue } = require('internal/options'); 8const path = require('path'); 9 10/** 11 * Get the absolute path to the main entry point. 12 * @param {string} main - Entry point path 13 */ 14function resolveMainPath(main) { 15 const defaultType = getOptionValue('--experimental-default-type'); 16 /** @type {string} */ 17 let mainPath; 18 if (defaultType === 'module') { 19 if (getOptionValue('--preserve-symlinks-main')) { return; } 20 mainPath = path.resolve(main); 21 } else { 22 // Extension searching for the main entry point is supported only in legacy mode. 23 // Module._findPath is monkey-patchable here. 24 const { Module } = require('internal/modules/cjs/loader'); 25 mainPath = Module._findPath(path.resolve(main), null, true); 26 } 27 if (!mainPath) { return; } 28 29 const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); 30 if (!preserveSymlinksMain) { 31 const { toRealPath } = require('internal/modules/helpers'); 32 try { 33 mainPath = toRealPath(mainPath); 34 } catch (err) { 35 if (defaultType === 'module' && err?.code === 'ENOENT') { 36 const { decorateErrorWithCommonJSHints } = require('internal/modules/esm/resolve'); 37 const { getCWDURL } = require('internal/util'); 38 decorateErrorWithCommonJSHints(err, mainPath, getCWDURL()); 39 } 40 throw err; 41 } 42 } 43 44 return mainPath; 45} 46 47/** 48 * Determine whether the main entry point should be loaded through the ESM Loader. 49 * @param {string} mainPath - Absolute path to the main entry point 50 */ 51function shouldUseESMLoader(mainPath) { 52 if (getOptionValue('--experimental-default-type') === 'module') { return true; } 53 54 /** 55 * @type {string[]} userLoaders A list of custom loaders registered by the user 56 * (or an empty list when none have been registered). 57 */ 58 const userLoaders = getOptionValue('--experimental-loader'); 59 /** 60 * @type {string[]} userImports A list of preloaded modules registered by the user 61 * (or an empty list when none have been registered). 62 */ 63 const userImports = getOptionValue('--import'); 64 if (userLoaders.length > 0 || userImports.length > 0) { 65 return true; 66 } 67 const esModuleSpecifierResolution = 68 getOptionValue('--experimental-specifier-resolution'); 69 if (esModuleSpecifierResolution === 'node') { 70 return true; 71 } 72 // Determine the module format of the entry point. 73 if (mainPath && StringPrototypeEndsWith(mainPath, '.mjs')) { return true; } 74 if (!mainPath || StringPrototypeEndsWith(mainPath, '.cjs')) { return false; } 75 76 const { readPackageScope } = require('internal/modules/package_json_reader'); 77 const pkg = readPackageScope(mainPath); 78 // No need to guard `pkg` as it can only be an object or `false`. 79 return pkg.data?.type === 'module' || getOptionValue('--experimental-default-type') === 'module'; 80} 81 82/** 83 * Run the main entry point through the ESM Loader. 84 * @param {string} mainPath - Absolute path for the main entry point 85 */ 86function runMainESM(mainPath) { 87 const { loadESM } = require('internal/process/esm_loader'); 88 const { pathToFileURL } = require('internal/url'); 89 const main = pathToFileURL(mainPath).href; 90 91 handleMainPromise(loadESM((esmLoader) => { 92 return esmLoader.import(main, undefined, { __proto__: null }); 93 })); 94} 95 96/** 97 * Handle process exit events around the main entry point promise. 98 * @param {Promise} promise - Main entry point promise 99 */ 100async function handleMainPromise(promise) { 101 const { 102 handleProcessExit, 103 } = require('internal/modules/esm/handle_process_exit'); 104 process.on('exit', handleProcessExit); 105 try { 106 return await promise; 107 } finally { 108 process.off('exit', handleProcessExit); 109 } 110} 111 112/** 113 * Parse the CLI main entry point string and run it. 114 * For backwards compatibility, we have to run a bunch of monkey-patchable code that belongs to the CJS loader (exposed 115 * by `require('module')`) even when the entry point is ESM. 116 * This monkey-patchable code is bypassed under `--experimental-default-type=module`. 117 * Because of backwards compatibility, this function is exposed publicly via `import { runMain } from 'node:module'`. 118 * @param {string} main - First positional CLI argument, such as `'entry.js'` from `node entry.js` 119 */ 120function executeUserEntryPoint(main = process.argv[1]) { 121 const resolvedMain = resolveMainPath(main); 122 const useESMLoader = shouldUseESMLoader(resolvedMain); 123 if (useESMLoader) { 124 runMainESM(resolvedMain || main); 125 } else { 126 // Module._load is the monkey-patchable CJS module loader. 127 const { Module } = require('internal/modules/cjs/loader'); 128 Module._load(main, null, true); 129 } 130} 131 132module.exports = { 133 executeUserEntryPoint, 134 handleMainPromise, 135}; 136