Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4669340

Browse files
committedOct 1, 2023
esm: bypass CommonJS loader under --default-type
1 parent 0fd188b commit 4669340

File tree

4 files changed

+69
-17
lines changed

4 files changed

+69
-17
lines changed
 

‎lib/internal/main/run_main_module.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,24 @@ const {
66
prepareMainThreadExecution,
77
markBootstrapComplete,
88
} = require('internal/process/pre_execution');
9+
const { getOptionValue } = require('internal/options');
910

10-
prepareMainThreadExecution(true);
11+
const mainEntry = prepareMainThreadExecution(true);
1112

1213
markBootstrapComplete();
1314

1415
// Necessary to reset RegExp statics before user code runs.
1516
RegExpPrototypeExec(/^/, '');
1617

17-
// Note: this loads the module through the ESM loader if the module is
18-
// determined to be an ES module. This hangs from the CJS module loader
19-
// because we currently allow monkey-patching of the module loaders
20-
// in the preloaded scripts through require('module').
21-
// runMain here might be monkey-patched by users in --require.
22-
// XXX: the monkey-patchability here should probably be deprecated.
23-
require('internal/modules/cjs/loader').Module.runMain(process.argv[1]);
18+
if (getOptionValue('--experimental-default-type') === 'module') {
19+
require('internal/modules/run_main').executeUserEntryPoint(mainEntry);
20+
} else {
21+
/**
22+
* To support legacy monkey-patching of `Module.runMain`, we call `runMain` here to have the CommonJS loader begin
23+
* the execution of the main entry point, even if the ESM loader immediately takes over because the main entry is an
24+
* ES module or one of the other opt-in conditions (such as the use of `--import`) are met. Users can monkey-patch
25+
* before the main entry point is loaded by doing so via scripts loaded through `--require`. This monkey-patchability
26+
* is undesirable and is removed in `--experimental-default-type=module` mode.
27+
*/
28+
require('internal/modules/cjs/loader').Module.runMain(mainEntry);
29+
}

‎lib/internal/modules/run_main.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ const path = require('path');
1212
* @param {string} main - Entry point path
1313
*/
1414
function resolveMainPath(main) {
15-
// Note extension resolution for the main entry point can be deprecated in a
16-
// future major.
17-
// Module._findPath is monkey-patchable here.
18-
const { Module } = require('internal/modules/cjs/loader');
19-
let mainPath = Module._findPath(path.resolve(main), null, true);
15+
/** @type {string} */
16+
let mainPath;
17+
if (getOptionValue('--experimental-default-type') === 'module') {
18+
mainPath = path.resolve(main);
19+
} else {
20+
// Extension searching for the main entry point is supported only in legacy mode.
21+
// Module._findPath is monkey-patchable here.
22+
const { Module } = require('internal/modules/cjs/loader');
23+
mainPath = Module._findPath(path.resolve(main), null, true);
24+
}
2025
if (!mainPath) { return; }
2126

2227
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
@@ -33,6 +38,8 @@ function resolveMainPath(main) {
3338
* @param {string} mainPath - Absolute path to the main entry point
3439
*/
3540
function shouldUseESMLoader(mainPath) {
41+
if (getOptionValue('--experimental-default-type') === 'module') { return true; }
42+
3643
/**
3744
* @type {string[]} userLoaders A list of custom loaders registered by the user
3845
* (or an empty list when none have been registered).
@@ -90,6 +97,7 @@ async function handleMainPromise(promise) {
9097
* Parse the CLI main entry point string and run it.
9198
* For backwards compatibility, we have to run a bunch of monkey-patchable code that belongs to the CJS loader (exposed
9299
* by `require('module')`) even when the entry point is ESM.
100+
* This monkey-patchable code is bypassed under `--experimental-default-type=module`.
93101
* Because of backwards compatibility, this function is exposed publicly via `import { runMain } from 'node:module'`.
94102
* @param {string} main - Resolved absolute path for the main entry point, if found
95103
*/

‎lib/internal/process/pre_execution.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ const {
5050
isBuildingSnapshot,
5151
},
5252
} = require('internal/v8/startup_snapshot');
53+
const { resolve } = require('path');
5354

5455
function prepareMainThreadExecution(expandArgv1 = false, initializeModules = true) {
55-
prepareExecution({
56+
return prepareExecution({
5657
expandArgv1,
5758
initializeModules,
5859
isMainThread: true,
@@ -73,8 +74,8 @@ function prepareExecution(options) {
7374
refreshRuntimeOptions();
7475
reconnectZeroFillToggle();
7576

76-
// Patch the process object with legacy properties and normalizations
77-
patchProcessObject(expandArgv1);
77+
// Patch the process object and get the resolved main entry point.
78+
const mainEntry = patchProcessObject(expandArgv1);
7879
setupTraceCategoryState();
7980
setupInspectorHooks();
8081
setupWarningHandler();
@@ -131,6 +132,8 @@ function prepareExecution(options) {
131132
if (initializeModules) {
132133
setupUserModules();
133134
}
135+
136+
return mainEntry;
134137
}
135138

136139
function setupSymbolDisposePolyfill() {
@@ -202,14 +205,17 @@ function patchProcessObject(expandArgv1) {
202205
process._exiting = false;
203206
process.argv[0] = process.execPath;
204207

208+
/** @type {string} */
209+
let mainEntry;
205210
// If requested, update process.argv[1] to replace whatever the user provided with the resolved absolute file path of
206211
// the entry point.
207212
if (expandArgv1 && process.argv[1] &&
208213
!StringPrototypeStartsWith(process.argv[1], '-')) {
209214
// Expand process.argv[1] into a full path.
210215
const path = require('path');
211216
try {
212-
process.argv[1] = path.resolve(process.argv[1]);
217+
mainEntry = path.resolve(process.argv[1]);
218+
process.argv[1] = mainEntry;
213219
} catch {
214220
// Continue regardless of error.
215221
}
@@ -236,6 +242,8 @@ function patchProcessObject(expandArgv1) {
236242
addReadOnlyProcessAlias('traceDeprecation', '--trace-deprecation');
237243
addReadOnlyProcessAlias('_breakFirstLine', '--inspect-brk', false);
238244
addReadOnlyProcessAlias('_breakNodeFirstLine', '--inspect-brk-node', false);
245+
246+
return mainEntry;
239247
}
240248

241249
function addReadOnlyProcessAlias(name, option, enumerable = true) {

‎test/es-module/test-esm-type-flag-errors.mjs

+30
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,36 @@ import * as fixtures from '../common/fixtures.mjs';
33
import { describe, it } from 'node:test';
44
import { match, strictEqual } from 'node:assert';
55

6+
describe('--experimental-default-type=module should not support extension searching', { concurrency: true }, () => {
7+
it('should support extension searching under --experimental-default-type=commonjs', async () => {
8+
const { code, signal, stdout, stderr } = await spawnPromisified(process.execPath, [
9+
'--experimental-default-type=commonjs',
10+
'./index',
11+
], {
12+
cwd: fixtures.path('es-modules/package-without-type'),
13+
});
14+
15+
strictEqual(stdout, 'package-without-type\n');
16+
strictEqual(stderr, '');
17+
strictEqual(code, 0);
18+
strictEqual(signal, null);
19+
});
20+
21+
it('should error with implicit extension under --experimental-default-type=module', async () => {
22+
const { code, signal, stdout, stderr } = await spawnPromisified(process.execPath, [
23+
'--experimental-default-type=module',
24+
'./index',
25+
], {
26+
cwd: fixtures.path('es-modules/package-without-type'),
27+
});
28+
29+
match(stderr, /ENOENT/);
30+
strictEqual(stdout, '');
31+
strictEqual(code, 1);
32+
strictEqual(signal, null);
33+
});
34+
});
35+
636
describe('--experimental-default-type=module should not affect the interpretation of files with unknown extensions',
737
{ concurrency: true }, () => {
838
it('should error on an entry point with an unknown extension', async () => {

0 commit comments

Comments
 (0)
Please sign in to comment.