Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4de6097

Browse filesBrowse 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
Copy full SHA for 4de6097
Expand file treeCollapse file tree

25 files changed

+158
-109
lines changed
Open diff view settings
Collapse file

‎lib/internal/bootstrap/pre_execution.js‎

Copy file name to clipboardExpand all lines: lib/internal/bootstrap/pre_execution.js
+9-65Lines 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-
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,
Collapse file

‎lib/internal/main/run_main_module.js‎

Copy file name to clipboardExpand all lines: lib/internal/main/run_main_module.js
+6-4Lines changed: 6 additions & 4 deletions
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]);
Collapse file

‎lib/internal/main/worker_thread.js‎

Copy file name to clipboardExpand all lines: lib/internal/main/worker_thread.js
+6-2Lines 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+
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;
Collapse file

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

Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/loader.js
+9-18Lines 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+
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 =
Collapse file

‎lib/internal/modules/run_main.js‎

Copy file name to clipboard
+80Lines changed: 80 additions & 0 deletions
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+
};
Collapse file

‎node.gyp‎

Copy file name to clipboardExpand all lines: node.gyp
+1Lines 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',
Collapse file
+8Lines 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+
};
Collapse file

‎test/message/core_line_numbers.out‎

Copy file name to clipboardExpand all lines: test/message/core_line_numbers.out
+1-1Lines 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:*:*
Collapse file

‎test/message/error_exit.out‎

Copy file name to clipboardExpand all lines: test/message/error_exit.out
+1-1Lines 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',
Collapse file

‎test/message/esm_loader_not_found.out‎

Copy file name to clipboardExpand all lines: test/message/esm_loader_not_found.out
+2-2Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.