Skip to content

Commit 4de6097

Browse files
joyeecheungtargos
authored andcommitted
module: reduce circular dependency of internal/modules/cjs/loader
Previously `internal/bootstrap/pre_execution.js` requires `internal/modules/cjs/loader.js` which in turn requires `internal/bootstrap/pre_execution.js`. This patch moves the entry point execution logic out of `pre_execution.js` and puts it into `internal/modules/run_main.js`. It also tests that `Module.runMain` can be monkey-patched before further deprecation/refactoring can be done. Also added an internal assertion `hasLoadedAnyUserCJSModule` for documentation purposes. PR-URL: #30349 Reviewed-By: Guy Bedford <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 0dd8605 commit 4de6097

25 files changed

+158
-109
lines changed

lib/internal/bootstrap/pre_execution.js

+9-65
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const {
99
const { getOptionValue } = require('internal/options');
1010
const { Buffer } = require('buffer');
1111
const { ERR_MANIFEST_ASSERT_INTEGRITY } = require('internal/errors').codes;
12-
const path = require('path');
12+
const assert = require('internal/assert');
1313

1414
function prepareMainThreadExecution(expandArgv1 = false) {
1515
// Patch the process object with legacy properties and normalizations
@@ -64,6 +64,9 @@ function prepareMainThreadExecution(expandArgv1 = false) {
6464
initializeDeprecations();
6565
initializeCJSLoader();
6666
initializeESMLoader();
67+
68+
const CJSLoader = require('internal/modules/cjs/loader');
69+
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
6770
loadPreloadModules();
6871
initializeFrozenIntrinsics();
6972
}
@@ -398,7 +401,11 @@ function initializePolicy() {
398401
}
399402

400403
function initializeCJSLoader() {
401-
require('internal/modules/cjs/loader').Module._initPaths();
404+
const CJSLoader = require('internal/modules/cjs/loader');
405+
CJSLoader.Module._initPaths();
406+
// TODO(joyeecheung): deprecate this in favor of a proper hook?
407+
CJSLoader.Module.runMain =
408+
require('internal/modules/run_main').executeUserEntryPoint;
402409
}
403410

404411
function initializeESMLoader() {
@@ -446,74 +453,11 @@ function loadPreloadModules() {
446453
}
447454
}
448455

449-
function resolveMainPath(main) {
450-
const { toRealPath, Module: CJSModule } =
451-
require('internal/modules/cjs/loader');
452-
453-
// Note extension resolution for the main entry point can be deprecated in a
454-
// future major.
455-
let mainPath = CJSModule._findPath(path.resolve(main), null, true);
456-
if (!mainPath)
457-
return;
458-
459-
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
460-
if (!preserveSymlinksMain)
461-
mainPath = toRealPath(mainPath);
462-
463-
return mainPath;
464-
}
465-
466-
function shouldUseESMLoader(mainPath) {
467-
const experimentalModules = getOptionValue('--experimental-modules');
468-
if (!experimentalModules)
469-
return false;
470-
const userLoader = getOptionValue('--experimental-loader');
471-
if (userLoader)
472-
return true;
473-
const experimentalSpecifierResolution =
474-
getOptionValue('--experimental-specifier-resolution');
475-
if (experimentalSpecifierResolution === 'node')
476-
return true;
477-
// Determine the module format of the main
478-
if (mainPath && mainPath.endsWith('.mjs'))
479-
return true;
480-
if (!mainPath || mainPath.endsWith('.cjs'))
481-
return false;
482-
const { readPackageScope } = require('internal/modules/cjs/loader');
483-
const pkg = readPackageScope(mainPath);
484-
return pkg && pkg.data.type === 'module';
485-
}
486-
487-
function runMainESM(mainPath) {
488-
const esmLoader = require('internal/process/esm_loader');
489-
const { pathToFileURL } = require('internal/url');
490-
const { hasUncaughtExceptionCaptureCallback } =
491-
require('internal/process/execution');
492-
return esmLoader.initializeLoader().then(() => {
493-
const main = path.isAbsolute(mainPath) ?
494-
pathToFileURL(mainPath).href : mainPath;
495-
return esmLoader.ESMLoader.import(main);
496-
}).catch((e) => {
497-
if (hasUncaughtExceptionCaptureCallback()) {
498-
process._fatalException(e);
499-
return;
500-
}
501-
internalBinding('errors').triggerUncaughtException(
502-
e,
503-
true /* fromPromise */
504-
);
505-
});
506-
}
507-
508-
509456
module.exports = {
510457
patchProcessObject,
511-
resolveMainPath,
512-
runMainESM,
513458
setupCoverageHooks,
514459
setupWarningHandler,
515460
setupDebugEnv,
516-
shouldUseESMLoader,
517461
prepareMainThreadExecution,
518462
initializeDeprecations,
519463
initializeESMLoader,

lib/internal/main/run_main_module.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ const {
66

77
prepareMainThreadExecution(true);
88

9-
const CJSModule = require('internal/modules/cjs/loader').Module;
10-
119
markBootstrapComplete();
1210

1311
// Note: this loads the module through the ESM loader if
1412
// --experimental-loader is provided or --experimental-modules is on
15-
// and the module is determined to be an ES module
16-
CJSModule.runMain(process.argv[1]);
13+
// and the module is determined to be an ES module. This hangs from the CJS
14+
// module loader because we currently allow monkey-patching of the module
15+
// loaders in the preloaded scripts through require('module').
16+
// runMain here might be monkey-patched by users in --require.
17+
// XXX: the monkey-patchability here should probably be deprecated.
18+
require('internal/modules/cjs/loader').Module.runMain(process.argv[1]);

lib/internal/main/worker_thread.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ port.on('message', (message) => {
110110
initializeDeprecations();
111111
initializeCJSLoader();
112112
initializeESMLoader();
113+
114+
const CJSLoader = require('internal/modules/cjs/loader');
115+
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
113116
loadPreloadModules();
114117
initializeFrozenIntrinsics();
115118
publicWorker.parentPort = publicPort;
@@ -138,8 +141,9 @@ port.on('message', (message) => {
138141
evalScript('[worker eval]', filename);
139142
} else {
140143
// script filename
141-
const CJSModule = require('internal/modules/cjs/loader').Module;
142-
CJSModule.runMain(process.argv[1] = filename);
144+
// runMain here might be monkey-patched by users in --require.
145+
// XXX: the monkey-patchability here should probably be deprecated.
146+
CJSLoader.Module.runMain(process.argv[1] = filename);
143147
}
144148
} else if (message.type === STDIO_PAYLOAD) {
145149
const { stream, chunk, encoding } = message;

lib/internal/modules/cjs/loader.js

+9-18
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,23 @@ const manifest = getOptionValue('--experimental-policy') ?
7474
null;
7575
const { compileFunction } = internalBinding('contextify');
7676

77+
// Whether any user-provided CJS modules had been loaded (executed).
78+
// Used for internal assertions.
79+
let hasLoadedAnyUserCJSModule = false;
80+
7781
const {
7882
ERR_INVALID_ARG_VALUE,
7983
ERR_INVALID_OPT_VALUE,
8084
ERR_INVALID_PACKAGE_CONFIG,
8185
ERR_REQUIRE_ESM
8286
} = require('internal/errors').codes;
8387
const { validateString } = require('internal/validators');
84-
const {
85-
resolveMainPath,
86-
shouldUseESMLoader,
87-
runMainESM
88-
} = require('internal/bootstrap/pre_execution');
8988
const pendingDeprecation = getOptionValue('--pending-deprecation');
9089

91-
module.exports = { wrapSafe, Module, toRealPath, readPackageScope };
90+
module.exports = {
91+
wrapSafe, Module, toRealPath, readPackageScope,
92+
get hasLoadedAnyUserCJSModule() { return hasLoadedAnyUserCJSModule; }
93+
};
9294

9395
let asyncESM, ModuleJob, ModuleWrap, kInstantiated;
9496

@@ -1148,6 +1150,7 @@ Module.prototype._compile = function(content, filename) {
11481150
result = compiledWrapper.call(thisValue, exports, require, module,
11491151
filename, dirname);
11501152
}
1153+
hasLoadedAnyUserCJSModule = true;
11511154
if (requireDepth === 0) statCache = null;
11521155
return result;
11531156
};
@@ -1197,18 +1200,6 @@ Module._extensions['.node'] = function(module, filename) {
11971200
return process.dlopen(module, path.toNamespacedPath(filename));
11981201
};
11991202

1200-
// Bootstrap main module.
1201-
Module.runMain = function(main = process.argv[1]) {
1202-
const resolvedMain = resolveMainPath(main);
1203-
const useESMLoader = shouldUseESMLoader(resolvedMain);
1204-
module.exports.asyncRunMain = useESMLoader;
1205-
if (useESMLoader) {
1206-
runMainESM(resolvedMain || main);
1207-
} else {
1208-
Module._load(main, null, true);
1209-
}
1210-
};
1211-
12121203
function createRequireFromPath(filename) {
12131204
// Allow a directory to be passed as the filename
12141205
const trailingSlash =

lib/internal/modules/run_main.js

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
'use strict';
2+
3+
const CJSLoader = require('internal/modules/cjs/loader');
4+
const { Module, toRealPath, readPackageScope } = CJSLoader;
5+
const { getOptionValue } = require('internal/options');
6+
const path = require('path');
7+
8+
function resolveMainPath(main) {
9+
// Note extension resolution for the main entry point can be deprecated in a
10+
// future major.
11+
// Module._findPath is monkey-patchable here.
12+
let mainPath = Module._findPath(path.resolve(main), null, true);
13+
if (!mainPath)
14+
return;
15+
16+
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
17+
if (!preserveSymlinksMain)
18+
mainPath = toRealPath(mainPath);
19+
20+
return mainPath;
21+
}
22+
23+
function shouldUseESMLoader(mainPath) {
24+
const experimentalModules = getOptionValue('--experimental-modules');
25+
if (!experimentalModules)
26+
return false;
27+
const userLoader = getOptionValue('--experimental-loader');
28+
if (userLoader)
29+
return true;
30+
const experimentalSpecifierResolution =
31+
getOptionValue('--experimental-specifier-resolution');
32+
if (experimentalSpecifierResolution === 'node')
33+
return true;
34+
// Determine the module format of the main
35+
if (mainPath && mainPath.endsWith('.mjs'))
36+
return true;
37+
if (!mainPath || mainPath.endsWith('.cjs'))
38+
return false;
39+
const pkg = readPackageScope(mainPath);
40+
return pkg && pkg.data.type === 'module';
41+
}
42+
43+
function runMainESM(mainPath) {
44+
const esmLoader = require('internal/process/esm_loader');
45+
const { pathToFileURL } = require('internal/url');
46+
const { hasUncaughtExceptionCaptureCallback } =
47+
require('internal/process/execution');
48+
return esmLoader.initializeLoader().then(() => {
49+
const main = path.isAbsolute(mainPath) ?
50+
pathToFileURL(mainPath).href : mainPath;
51+
return esmLoader.ESMLoader.import(main);
52+
}).catch((e) => {
53+
if (hasUncaughtExceptionCaptureCallback()) {
54+
process._fatalException(e);
55+
return;
56+
}
57+
internalBinding('errors').triggerUncaughtException(
58+
e,
59+
true /* fromPromise */
60+
);
61+
});
62+
}
63+
64+
// For backwards compatibility, we have to run a bunch of
65+
// monkey-patchable code that belongs to the CJS loader (exposed by
66+
// `require('module')`) even when the entry point is ESM.
67+
function executeUserEntryPoint(main = process.argv[1]) {
68+
const resolvedMain = resolveMainPath(main);
69+
const useESMLoader = shouldUseESMLoader(resolvedMain);
70+
if (useESMLoader) {
71+
runMainESM(resolvedMain || main);
72+
} else {
73+
// Module._load is the monkey-patchable CJS module loader.
74+
Module._load(main, null, true);
75+
}
76+
}
77+
78+
module.exports = {
79+
executeUserEntryPoint
80+
};

node.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
'lib/internal/main/run_main_module.js',
146146
'lib/internal/main/run_third_party_main.js',
147147
'lib/internal/main/worker_thread.js',
148+
'lib/internal/modules/run_main.js',
148149
'lib/internal/modules/cjs/helpers.js',
149150
'lib/internal/modules/cjs/loader.js',
150151
'lib/internal/modules/esm/loader.js',
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use strict';
2+
3+
const oldRunMain = require('module').runMain;
4+
5+
require('module').runMain = function(...args) {
6+
console.log('runMain is monkey patched!');
7+
oldRunMain(...args);
8+
};

test/message/core_line_numbers.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ RangeError: Invalid input
1010
at Object.Module._extensions..js (internal/modules/cjs/loader.js:*:*)
1111
at Module.load (internal/modules/cjs/loader.js:*:*)
1212
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
13-
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
13+
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*)
1414
at internal/main/run_main_module.js:*:*

test/message/error_exit.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
1212
at Object.Module._extensions..js (internal/modules/cjs/loader.js:*:*)
1313
at Module.load (internal/modules/cjs/loader.js:*:*)
1414
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
15-
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
15+
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*)
1616
at internal/main/run_main_module.js:*:* {
1717
generatedMessage: true,
1818
code: 'ERR_ASSERTION',

test/message/esm_loader_not_found.out

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ Error: Cannot find package 'i-dont-exist' imported from *
1111
at Loader.import (internal/modules/esm/loader.js:*:*)
1212
at internal/process/esm_loader.js:*:*
1313
at Object.initializeLoader (internal/process/esm_loader.js:*:*)
14-
at runMainESM (internal/bootstrap/pre_execution.js:*:*)
15-
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
14+
at runMainESM (internal/modules/run_main.js:*:*)
15+
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*)
1616
at internal/main/run_main_module.js:*:* {
1717
code: 'ERR_MODULE_NOT_FOUND'
1818
}

test/message/events_unhandled_error_common_trace.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Error: foo:bar
1010
at Object.Module._extensions..js (internal/modules/cjs/loader.js:*:*)
1111
at Module.load (internal/modules/cjs/loader.js:*:*)
1212
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
13-
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
13+
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*)
1414
at internal/main/run_main_module.js:*:*
1515
Emitted 'error' event at:
1616
at quux (*events_unhandled_error_common_trace.js:*:*)

test/message/events_unhandled_error_nexttick.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Error
88
at Object.Module._extensions..js (internal/modules/cjs/loader.js:*:*)
99
at Module.load (internal/modules/cjs/loader.js:*:*)
1010
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
11-
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
11+
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*)
1212
at internal/main/run_main_module.js:*:*
1313
Emitted 'error' event at:
1414
at *events_unhandled_error_nexttick.js:*:*

test/message/events_unhandled_error_sameline.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Error
88
at Object.Module._extensions..js (internal/modules/cjs/loader.js:*:*)
99
at Module.load (internal/modules/cjs/loader.js:*:*)
1010
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
11-
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
11+
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*)
1212
at internal/main/run_main_module.js:*:*
1313
Emitted 'error' event at:
1414
at Object.<anonymous> (*events_unhandled_error_sameline.js:*:*)

test/message/events_unhandled_error_subclass.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Error
88
at Object.Module._extensions..js (internal/modules/cjs/loader.js:*:*)
99
at Module.load (internal/modules/cjs/loader.js:*:*)
1010
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
11-
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
11+
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*)
1212
at internal/main/run_main_module.js:*:*
1313
Emitted 'error' event on Foo instance at:
1414
at Object.<anonymous> (*events_unhandled_error_subclass.js:*:*)

test/message/if-error-has-good-stack.out

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error
1515
at Object.Module._extensions..js (internal/modules/cjs/loader.js:*:*)
1616
at Module.load (internal/modules/cjs/loader.js:*:*)
1717
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
18-
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
18+
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*)
1919
at internal/main/run_main_module.js:*:* {
2020
generatedMessage: false,
2121
code: 'ERR_ASSERTION',
@@ -28,7 +28,7 @@ AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error
2828
at Object.Module._extensions..js (internal/modules/cjs/loader.js:*:*)
2929
at Module.load (internal/modules/cjs/loader.js:*:*)
3030
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
31-
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
31+
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*)
3232
at internal/main/run_main_module.js:*:*
3333
expected: null,
3434
operator: 'ifError'

test/message/throw_error_with_getter_throw_traced.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ Thrown at:
99
at Module._extensions..js (internal/modules/cjs/loader.js:*:*)
1010
at Module.load (internal/modules/cjs/loader.js:*:*)
1111
at Module._load (internal/modules/cjs/loader.js:*:*)
12-
at Module.runMain (internal/modules/cjs/loader.js:*:*)
12+
at executeUserEntryPoint (internal/modules/run_main.js:*:*)

test/message/throw_null_traced.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ Thrown at:
99
at Module._extensions..js (internal/modules/cjs/loader.js:*:*)
1010
at Module.load (internal/modules/cjs/loader.js:*:*)
1111
at Module._load (internal/modules/cjs/loader.js:*:*)
12-
at Module.runMain (internal/modules/cjs/loader.js:*:*)
12+
at executeUserEntryPoint (internal/modules/run_main.js:*:*)

0 commit comments

Comments
 (0)