module: eliminate performance cost of detection for cjs entry · nodejs/node@132a5c1

@@ -4,7 +4,6 @@ const {

44

StringPrototypeEndsWith,

55

} = primordials;

667-

const { containsModuleSyntax } = internalBinding('contextify');

87

const { getOptionValue } = require('internal/options');

98

const path = require('path');

109

const { pathToFileURL } = require('internal/url');

@@ -85,10 +84,6 @@ function shouldUseESMLoader(mainPath) {

8584

case 'commonjs':

8685

return false;

8786

default: { // No package.json or no `type` field.

88-

if (getOptionValue('--experimental-detect-module')) {

89-

// If the first argument of `containsModuleSyntax` is undefined, it will read `mainPath` from the file system.

90-

return containsModuleSyntax(undefined, mainPath);

91-

}

9287

return false;

9388

}

9489

}

@@ -153,12 +148,43 @@ function runEntryPointWithESMLoader(callback) {

153148

* by `require('module')`) even when the entry point is ESM.

154149

* This monkey-patchable code is bypassed under `--experimental-default-type=module`.

155150

* Because of backwards compatibility, this function is exposed publicly via `import { runMain } from 'node:module'`.

151+

* When `--experimental-detect-module` is passed, this function will attempt to run ambiguous (no explicit extension, no

152+

* `package.json` type field) entry points as CommonJS first; under certain conditions, it will retry running as ESM.

156153

* @param {string} main - First positional CLI argument, such as `'entry.js'` from `node entry.js`

157154

*/

158155

function executeUserEntryPoint(main = process.argv[1]) {

159156

const resolvedMain = resolveMainPath(main);

160157

const useESMLoader = shouldUseESMLoader(resolvedMain);

161-

if (useESMLoader) {

158+159+

// Unless we know we should use the ESM loader to handle the entry point per the checks in `shouldUseESMLoader`, first

160+

// try to run the entry point via the CommonJS loader; and if that fails under certain conditions, retry as ESM.

161+

let retryAsESM = false;

162+

if (!useESMLoader) {

163+

const cjsLoader = require('internal/modules/cjs/loader');

164+

const { Module } = cjsLoader;

165+

if (getOptionValue('--experimental-detect-module')) {

166+

try {

167+

// Module._load is the monkey-patchable CJS module loader.

168+

Module._load(main, null, true);

169+

} catch (error) {

170+

const source = cjsLoader.entryPointSource;

171+

const { shouldRetryAsESM } = require('internal/modules/helpers');

172+

retryAsESM = shouldRetryAsESM(error.message, source);

173+

// In case the entry point is a large file, such as a bundle,

174+

// ensure no further references can prevent it being garbage-collected.

175+

cjsLoader.entryPointSource = undefined;

176+

if (!retryAsESM) {

177+

const { enrichCJSError } = require('internal/modules/esm/translators');

178+

enrichCJSError(error, source, resolvedMain);

179+

throw error;

180+

}

181+

}

182+

} else { // `--experimental-detect-module` is not passed

183+

Module._load(main, null, true);

184+

}

185+

}

186+187+

if (useESMLoader || retryAsESM) {

162188

const mainPath = resolvedMain || main;

163189

const mainURL = pathToFileURL(mainPath).href;

164190

@@ -167,10 +193,6 @@ function executeUserEntryPoint(main = process.argv[1]) {

167193

// even after the event loop stops running.

168194

return cascadedLoader.import(mainURL, undefined, { __proto__: null }, true);

169195

});

170-

} else {

171-

// Module._load is the monkey-patchable CJS module loader.

172-

const { Module } = require('internal/modules/cjs/loader');

173-

Module._load(main, null, true);

174196

}

175197

}

176198