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 <guybedford@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent 0dd8605 commit 4de6097

25 files changed

Lines changed: 158 additions & 109 deletions

‎lib/internal/bootstrap/pre_execution.js‎

Lines changed: 9 additions & 65 deletions
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-
constpath=require('path');
12+
constassert=require('internal/assert');
1313

1414
functionprepareMainThreadExecution(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+
constCJSLoader=require('internal/modules/cjs/loader');
69+
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
6770
loadPreloadModules();
6871
initializeFrozenIntrinsics();
6972
}
@@ -398,7 +401,11 @@ function initializePolicy() {
398401
}
399402

400403
functioninitializeCJSLoader(){
401-
require('internal/modules/cjs/loader').Module._initPaths();
404+
constCJSLoader=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
functioninitializeESMLoader(){
@@ -446,74 +453,11 @@ function loadPreloadModules() {
446453
}
447454
}
448455

449-
functionresolveMainPath(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-
letmainPath=CJSModule._findPath(path.resolve(main),null,true);
456-
if(!mainPath)
457-
return;
458-
459-
constpreserveSymlinksMain=getOptionValue('--preserve-symlinks-main');
460-
if(!preserveSymlinksMain)
461-
mainPath=toRealPath(mainPath);
462-
463-
returnmainPath;
464-
}
465-
466-
functionshouldUseESMLoader(mainPath){
467-
constexperimentalModules=getOptionValue('--experimental-modules');
468-
if(!experimentalModules)
469-
returnfalse;
470-
constuserLoader=getOptionValue('--experimental-loader');
471-
if(userLoader)
472-
returntrue;
473-
constexperimentalSpecifierResolution=
474-
getOptionValue('--experimental-specifier-resolution');
475-
if(experimentalSpecifierResolution==='node')
476-
returntrue;
477-
// Determine the module format of the main
478-
if(mainPath&&mainPath.endsWith('.mjs'))
479-
returntrue;
480-
if(!mainPath||mainPath.endsWith('.cjs'))
481-
returnfalse;
482-
const{ readPackageScope }=require('internal/modules/cjs/loader');
483-
constpkg=readPackageScope(mainPath);
484-
returnpkg&&pkg.data.type==='module';
485-
}
486-
487-
functionrunMainESM(mainPath){
488-
constesmLoader=require('internal/process/esm_loader');
489-
const{ pathToFileURL }=require('internal/url');
490-
const{ hasUncaughtExceptionCaptureCallback }=
491-
require('internal/process/execution');
492-
returnesmLoader.initializeLoader().then(()=>{
493-
constmain=path.isAbsolute(mainPath) ?
494-
pathToFileURL(mainPath).href : mainPath;
495-
returnesmLoader.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‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ const {
66

77
prepareMainThreadExecution(true);
88

9-
constCJSModule=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‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ port.on('message', (message) => {
110110
initializeDeprecations();
111111
initializeCJSLoader();
112112
initializeESMLoader();
113+
114+
constCJSLoader=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-
constCJSModule=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
}elseif(message.type===STDIO_PAYLOAD){
145149
const{ stream, chunk, encoding }=message;

‎lib/internal/modules/cjs/loader.js‎

Lines changed: 9 additions & 18 deletions
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+
lethasLoadedAnyUserCJSModule=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
constpendingDeprecation=getOptionValue('--pending-deprecation');
9089

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

9395
letasyncESM,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
returnresult;
11531156
};
@@ -1197,18 +1200,6 @@ Module._extensions['.node'] = function(module, filename) {
11971200
returnprocess.dlopen(module,path.toNamespacedPath(filename));
11981201
};
11991202

1200-
// Bootstrap main module.
1201-
Module.runMain=function(main=process.argv[1]){
1202-
constresolvedMain=resolveMainPath(main);
1203-
constuseESMLoader=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
functioncreateRequireFromPath(filename){
12131204
// Allow a directory to be passed as the filename
12141205
consttrailingSlash=

‎lib/internal/modules/run_main.js‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
'use strict';
2+
3+
constCJSLoader=require('internal/modules/cjs/loader');
4+
const{ Module, toRealPath, readPackageScope }=CJSLoader;
5+
const{ getOptionValue }=require('internal/options');
6+
constpath=require('path');
7+
8+
functionresolveMainPath(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+
letmainPath=Module._findPath(path.resolve(main),null,true);
13+
if(!mainPath)
14+
return;
15+
16+
constpreserveSymlinksMain=getOptionValue('--preserve-symlinks-main');
17+
if(!preserveSymlinksMain)
18+
mainPath=toRealPath(mainPath);
19+
20+
returnmainPath;
21+
}
22+
23+
functionshouldUseESMLoader(mainPath){
24+
constexperimentalModules=getOptionValue('--experimental-modules');
25+
if(!experimentalModules)
26+
returnfalse;
27+
constuserLoader=getOptionValue('--experimental-loader');
28+
if(userLoader)
29+
returntrue;
30+
constexperimentalSpecifierResolution=
31+
getOptionValue('--experimental-specifier-resolution');
32+
if(experimentalSpecifierResolution==='node')
33+
returntrue;
34+
// Determine the module format of the main
35+
if(mainPath&&mainPath.endsWith('.mjs'))
36+
returntrue;
37+
if(!mainPath||mainPath.endsWith('.cjs'))
38+
returnfalse;
39+
constpkg=readPackageScope(mainPath);
40+
returnpkg&&pkg.data.type==='module';
41+
}
42+
43+
functionrunMainESM(mainPath){
44+
constesmLoader=require('internal/process/esm_loader');
45+
const{ pathToFileURL }=require('internal/url');
46+
const{ hasUncaughtExceptionCaptureCallback }=
47+
require('internal/process/execution');
48+
returnesmLoader.initializeLoader().then(()=>{
49+
constmain=path.isAbsolute(mainPath) ?
50+
pathToFileURL(mainPath).href : mainPath;
51+
returnesmLoader.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+
functionexecuteUserEntryPoint(main=process.argv[1]){
68+
constresolvedMain=resolveMainPath(main);
69+
constuseESMLoader=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‎

Lines changed: 1 addition & 0 deletions
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',
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use strict';
2+
3+
constoldRunMain=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
}

0 commit comments

Comments
 (0)