Skip to content

Commit c7c5660

Browse files
joyeecheungBridgeAR
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 66e1adf commit c7c5660

25 files changed

+152
-101
lines changed

lib/internal/bootstrap/pre_execution.js

Lines changed: 9 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const { Object, SafeWeakMap } = primordials;
55
const { getOptionValue } = require('internal/options');
66
const { Buffer } = require('buffer');
77
const { ERR_MANIFEST_ASSERT_INTEGRITY } = require('internal/errors').codes;
8-
const path = require('path');
8+
const assert = require('internal/assert');
99

1010
function prepareMainThreadExecution(expandArgv1 = false) {
1111
// Patch the process object with legacy properties and normalizations
@@ -60,6 +60,9 @@ function prepareMainThreadExecution(expandArgv1 = false) {
6060
initializeDeprecations();
6161
initializeCJSLoader();
6262
initializeESMLoader();
63+
64+
const CJSLoader = require('internal/modules/cjs/loader');
65+
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
6366
loadPreloadModules();
6467
initializeFrozenIntrinsics();
6568
}
@@ -394,7 +397,11 @@ function initializePolicy() {
394397
}
395398

396399
function initializeCJSLoader() {
397-
require('internal/modules/cjs/loader').Module._initPaths();
400+
const CJSLoader = require('internal/modules/cjs/loader');
401+
CJSLoader.Module._initPaths();
402+
// TODO(joyeecheung): deprecate this in favor of a proper hook?
403+
CJSLoader.Module.runMain =
404+
require('internal/modules/run_main').executeUserEntryPoint;
398405
}
399406

400407
function initializeESMLoader() {
@@ -433,67 +440,11 @@ function loadPreloadModules() {
433440
}
434441
}
435442

436-
function resolveMainPath(main) {
437-
const { toRealPath, Module: CJSModule } =
438-
require('internal/modules/cjs/loader');
439-
440-
// Note extension resolution for the main entry point can be deprecated in a
441-
// future major.
442-
let mainPath = CJSModule._findPath(path.resolve(main), null, true);
443-
if (!mainPath)
444-
return;
445-
446-
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
447-
if (!preserveSymlinksMain)
448-
mainPath = toRealPath(mainPath);
449-
450-
return mainPath;
451-
}
452-
453-
function shouldUseESMLoader(mainPath) {
454-
const userLoader = getOptionValue('--experimental-loader');
455-
if (userLoader)
456-
return true;
457-
// Determine the module format of the main
458-
if (mainPath && mainPath.endsWith('.mjs'))
459-
return true;
460-
if (!mainPath || mainPath.endsWith('.cjs'))
461-
return false;
462-
const { readPackageScope } = require('internal/modules/cjs/loader');
463-
const pkg = readPackageScope(mainPath);
464-
return pkg && pkg.data.type === 'module';
465-
}
466-
467-
function runMainESM(mainPath) {
468-
const esmLoader = require('internal/process/esm_loader');
469-
const { pathToFileURL } = require('internal/url');
470-
const { hasUncaughtExceptionCaptureCallback } =
471-
require('internal/process/execution');
472-
return esmLoader.initializeLoader().then(() => {
473-
const main = path.isAbsolute(mainPath) ?
474-
pathToFileURL(mainPath).href : mainPath;
475-
return esmLoader.ESMLoader.import(main);
476-
}).catch((e) => {
477-
if (hasUncaughtExceptionCaptureCallback()) {
478-
process._fatalException(e);
479-
return;
480-
}
481-
internalBinding('errors').triggerUncaughtException(
482-
e,
483-
true /* fromPromise */
484-
);
485-
});
486-
}
487-
488-
489443
module.exports = {
490444
patchProcessObject,
491-
resolveMainPath,
492-
runMainESM,
493445
setupCoverageHooks,
494446
setupWarningHandler,
495447
setupDebugEnv,
496-
shouldUseESMLoader,
497448
prepareMainThreadExecution,
498449
initializeDeprecations,
499450
initializeESMLoader,

lib/internal/main/run_main_module.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ 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 the module is
14-
// determined to be an ES module
15-
CJSModule.runMain(process.argv[1]);
12+
// determined to be an ES module. This hangs from the CJS module loader
13+
// because we currently allow monkey-patching of the module loaders
14+
// in the preloaded scripts through require('module').
15+
// runMain here might be monkey-patched by users in --require.
16+
// XXX: the monkey-patchability here should probably be deprecated.
17+
require('internal/modules/cjs/loader').Module.runMain(process.argv[1]);

lib/internal/main/worker_thread.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ port.on('message', (message) => {
108108
initializeDeprecations();
109109
initializeCJSLoader();
110110
initializeESMLoader();
111+
112+
const CJSLoader = require('internal/modules/cjs/loader');
113+
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
111114
loadPreloadModules();
112115
initializeFrozenIntrinsics();
113116
publicWorker.parentPort = publicPort;
@@ -141,8 +144,9 @@ port.on('message', (message) => {
141144
evalScript('[worker eval]', filename);
142145
} else {
143146
// script filename
144-
const CJSModule = require('internal/modules/cjs/loader').Module;
145-
CJSModule.runMain(process.argv[1] = filename);
147+
// runMain here might be monkey-patched by users in --require.
148+
// XXX: the monkey-patchability here should probably be deprecated.
149+
CJSLoader.Module.runMain(process.argv[1] = filename);
146150
}
147151
} else if (message.type === STDIO_PAYLOAD) {
148152
const { stream, chunk, encoding } = message;

lib/internal/modules/cjs/loader.js

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,24 @@ const manifest = getOptionValue('--experimental-policy') ?
6464
require('internal/process/policy').manifest :
6565
null;
6666
const { compileFunction } = internalBinding('contextify');
67+
68+
// Whether any user-provided CJS modules had been loaded (executed).
69+
// Used for internal assertions.
70+
let hasLoadedAnyUserCJSModule = false;
71+
6772
const {
6873
ERR_INVALID_ARG_VALUE,
6974
ERR_INVALID_OPT_VALUE,
7075
ERR_INVALID_PACKAGE_CONFIG,
7176
ERR_REQUIRE_ESM
7277
} = require('internal/errors').codes;
7378
const { validateString } = require('internal/validators');
74-
const {
75-
resolveMainPath,
76-
shouldUseESMLoader,
77-
runMainESM
78-
} = require('internal/bootstrap/pre_execution');
7979
const pendingDeprecation = getOptionValue('--pending-deprecation');
8080

81-
module.exports = { wrapSafe, Module, toRealPath, readPackageScope };
81+
module.exports = {
82+
wrapSafe, Module, toRealPath, readPackageScope,
83+
get hasLoadedAnyUserCJSModule() { return hasLoadedAnyUserCJSModule; }
84+
};
8285

8386
let asyncESM, ModuleJob, ModuleWrap, kInstantiated;
8487

@@ -1118,6 +1121,7 @@ Module.prototype._compile = function(content, filename) {
11181121
result = compiledWrapper.call(thisValue, exports, require, module,
11191122
filename, dirname);
11201123
}
1124+
hasLoadedAnyUserCJSModule = true;
11211125
if (requireDepth === 0) statCache = null;
11221126
return result;
11231127
};
@@ -1186,17 +1190,6 @@ Module._extensions['.node'] = function(module, filename) {
11861190
return process.dlopen(module, path.toNamespacedPath(filename));
11871191
};
11881192

1189-
// Bootstrap main module.
1190-
Module.runMain = function(main = process.argv[1]) {
1191-
const resolvedMain = resolveMainPath(main);
1192-
const useESMLoader = shouldUseESMLoader(resolvedMain);
1193-
if (useESMLoader) {
1194-
runMainESM(resolvedMain || main);
1195-
} else {
1196-
Module._load(main, null, true);
1197-
}
1198-
};
1199-
12001193
function createRequireFromPath(filename) {
12011194
// Allow a directory to be passed as the filename
12021195
const trailingSlash =

lib/internal/modules/run_main.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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 userLoader = getOptionValue('--experimental-loader');
25+
if (userLoader)
26+
return true;
27+
// Determine the module format of the main
28+
if (mainPath && mainPath.endsWith('.mjs'))
29+
return true;
30+
if (!mainPath || mainPath.endsWith('.cjs'))
31+
return false;
32+
const pkg = readPackageScope(mainPath);
33+
return pkg && pkg.data.type === 'module';
34+
}
35+
36+
function runMainESM(mainPath) {
37+
const esmLoader = require('internal/process/esm_loader');
38+
const { pathToFileURL } = require('internal/url');
39+
const { hasUncaughtExceptionCaptureCallback } =
40+
require('internal/process/execution');
41+
return esmLoader.initializeLoader().then(() => {
42+
const main = path.isAbsolute(mainPath) ?
43+
pathToFileURL(mainPath).href : mainPath;
44+
return esmLoader.ESMLoader.import(main);
45+
}).catch((e) => {
46+
if (hasUncaughtExceptionCaptureCallback()) {
47+
process._fatalException(e);
48+
return;
49+
}
50+
internalBinding('errors').triggerUncaughtException(
51+
e,
52+
true /* fromPromise */
53+
);
54+
});
55+
}
56+
57+
// For backwards compatibility, we have to run a bunch of
58+
// monkey-patchable code that belongs to the CJS loader (exposed by
59+
// `require('module')`) even when the entry point is ESM.
60+
function executeUserEntryPoint(main = process.argv[1]) {
61+
const resolvedMain = resolveMainPath(main);
62+
const useESMLoader = shouldUseESMLoader(resolvedMain);
63+
if (useESMLoader) {
64+
runMainESM(resolvedMain || main);
65+
} else {
66+
// Module._load is the monkey-patchable CJS module loader.
67+
Module._load(main, null, true);
68+
}
69+
}
70+
71+
module.exports = {
72+
executeUserEntryPoint
73+
};

node.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
'lib/internal/main/run_main_module.js',
147147
'lib/internal/main/run_third_party_main.js',
148148
'lib/internal/main/worker_thread.js',
149+
'lib/internal/modules/run_main.js',
149150
'lib/internal/modules/cjs/helpers.js',
150151
'lib/internal/modules/cjs/loader.js',
151152
'lib/internal/modules/esm/loader.js',
Lines changed: 8 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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_undefined_traced.out

Lines changed: 1 addition & 1 deletion
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/undefined_reference_in_new_context.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ ReferenceError: foo is not defined
1313
at *..js (internal/modules/cjs/loader.js:*)
1414
at Module.load (internal/modules/cjs/loader.js:*)
1515
at Function.Module._load (internal/modules/cjs/loader.js:*:*)
16-
at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
16+
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*)

0 commit comments

Comments
 (0)