Skip to content

Commit f64b4b4

Browse files
committed
module: only emit require(esm) warning under --trace-require-module
require(esm) is relatively stable now and the experimental warning has run its course - it's now more troublesome than useful. This patch changes it to no longer emit a warning unless `--trace-require-module` is explicitly used. The flag supports two modes: - `--trace-require-module=all`: emit warnings for all usages - `--trace-require-module=no-node-modules`: emit warnings for usages that do not come from a `node_modules` folder.
1 parent 7bedcfd commit f64b4b4

11 files changed

+81
-48
lines changed

doc/api/cli.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2616,6 +2616,18 @@ added:
26162616
Prints a stack trace whenever an environment is exited proactively,
26172617
i.e. invoking `process.exit()`.
26182618

2619+
### `--trace-require-module=mode`
2620+
2621+
<!-- YAML
2622+
added:
2623+
- REPLACEME
2624+
-->
2625+
2626+
Prints information about usage of [Loading ECMAScript modules using `require()`][].
2627+
2628+
When `mode` is `all`, all usage is printed. When `mode` is `no-node-modules`, usage
2629+
from the `node_modules` folder is excluded.
2630+
26192631
### `--trace-sigint`
26202632

26212633
<!-- YAML
@@ -3131,6 +3143,7 @@ one is included in the list below.
31313143
* `--trace-event-file-pattern`
31323144
* `--trace-events-enabled`
31333145
* `--trace-exit`
3146+
* `--trace-require-module`
31343147
* `--trace-sigint`
31353148
* `--trace-sync-io`
31363149
* `--trace-tls`

doc/api/modules.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ added:
175175
- v22.0.0
176176
- v20.17.0
177177
changes:
178+
- version:
179+
- REPLACEME
180+
pr-url: REPLACEME
181+
description: This feature no longer emits an experimental warning by default,
182+
though the warning can still be emitted by --trace-require-module.
178183
- version:
179184
- v23.0.0
180185
- v22.12.0
@@ -319,9 +324,8 @@ help users fix them.
319324

320325
Support for loading ES modules using `require()` is currently
321326
experimental and can be disabled using `--no-experimental-require-module`.
322-
When `require()` actually encounters an ES module for the
323-
first time in the process, it will emit an experimental warning. The
324-
warning is expected to be removed when this feature stablizes.
327+
To print where this feature is used, use [`--trace-require-module`][].
328+
325329
This feature can be detected by checking if
326330
[`process.features.require_module`][] is `true`.
327331

@@ -1271,6 +1275,7 @@ This section was moved to
12711275
[GLOBAL_FOLDERS]: #loading-from-the-global-folders
12721276
[`"main"`]: packages.md#main
12731277
[`"type"`]: packages.md#type
1278+
[`--trace-require-module`]: cli.md#--trace-require-modulemode
12741279
[`ERR_REQUIRE_ASYNC_MODULE`]: errors.md#err_require_async_module
12751280
[`ERR_UNSUPPORTED_DIR_IMPORT`]: errors.md#err_unsupported_dir_import
12761281
[`MODULE_NOT_FOUND`]: errors.md#module_not_found

lib/internal/modules/cjs/loader.js

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,7 +1330,7 @@ Module.prototype.require = function(id) {
13301330
}
13311331
};
13321332

1333-
let emittedRequireModuleWarning = false;
1333+
let requireModuleWarningMode;
13341334
/**
13351335
* Resolve and evaluate it synchronously as ESM if it's ESM.
13361336
* @param {Module} mod CJS module instance
@@ -1351,17 +1351,22 @@ function loadESMFromCJS(mod, filename, format, source) {
13511351
} else {
13521352
const parent = mod[kModuleParent];
13531353

1354-
if (!emittedRequireModuleWarning) {
1354+
requireModuleWarningMode ??= getOptionValue('--trace-require-module');
1355+
if (requireModuleWarningMode) {
13551356
let shouldEmitWarning = false;
1356-
// Check if the require() comes from node_modules.
1357-
if (parent) {
1358-
shouldEmitWarning = !isUnderNodeModules(parent.filename);
1359-
} else if (mod[kIsCachedByESMLoader]) {
1360-
// It comes from the require() built for `import cjs` and doesn't have a parent recorded
1361-
// in the CJS module instance. Inspect the stack trace to see if the require()
1362-
// comes from node_modules and reduce the noise. If there are more than 100 frames,
1363-
// just give up and assume it is under node_modules.
1364-
shouldEmitWarning = !isInsideNodeModules(100, true);
1357+
if (requireModuleWarningMode === 'no-node-modules') {
1358+
// Check if the require() comes from node_modules.
1359+
if (parent) {
1360+
shouldEmitWarning = !isUnderNodeModules(parent.filename);
1361+
} else if (mod[kIsCachedByESMLoader]) {
1362+
// It comes from the require() built for `import cjs` and doesn't have a parent recorded
1363+
// in the CJS module instance. Inspect the stack trace to see if the require()
1364+
// comes from node_modules and reduce the noise. If there are more than 100 frames,
1365+
// just give up and assume it is under node_modules.
1366+
shouldEmitWarning = !isInsideNodeModules(100, true);
1367+
}
1368+
} else {
1369+
shouldEmitWarning = true;
13651370
}
13661371
if (shouldEmitWarning) {
13671372
let messagePrefix;
@@ -1387,7 +1392,7 @@ function loadESMFromCJS(mod, filename, format, source) {
13871392
messagePrefix,
13881393
undefined,
13891394
parent?.require);
1390-
emittedRequireModuleWarning = true;
1395+
requireModuleWarningMode = true;
13911396
}
13921397
}
13931398
const {

src/node_options.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors,
135135
errors->push_back("--heapsnapshot-near-heap-limit must not be negative");
136136
}
137137

138+
if (!trace_require_module.empty() &&
139+
trace_require_module != "all" &&
140+
trace_require_module != "no-node-modules") {
141+
errors->push_back("invalid value for --trace-require-module");
142+
}
143+
138144
if (test_runner) {
139145
if (test_isolation == "none") {
140146
debug_options_.allow_attaching_debugger = true;
@@ -771,6 +777,13 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
771777
&EnvironmentOptions::trace_env_native_stack,
772778
kAllowedInEnvvar);
773779

780+
AddOption(
781+
"--trace-require-module",
782+
"Print access to require(esm). Options are 'all' (print all usage) and "
783+
"'no-node-modules' (excluding usage from the node_modules folder)",
784+
&EnvironmentOptions::trace_require_module,
785+
kAllowedInEnvvar);
786+
774787
AddOption("--extra-info-on-fatal-exception",
775788
"hide extra information on fatal exception that causes exit",
776789
&EnvironmentOptions::extra_info_on_fatal_exception,

src/node_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ class EnvironmentOptions : public Options {
210210
bool trace_env = false;
211211
bool trace_env_js_stack = false;
212212
bool trace_env_native_stack = false;
213+
std::string trace_require_module;
213214
bool extra_info_on_fatal_exception = true;
214215
std::string unhandled_rejections;
215216
std::vector<std::string> userland_loaders;

test/es-module/test-require-module-preload.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ const { spawnSyncAndAssert } = require('../common/child_process');
55
const { fixturesDir } = require('../common/fixtures');
66

77
function testPreload(preloadFlag) {
8-
// The warning is only emitted when ESM is loaded by --require.
9-
const isRequire = preloadFlag === '--require';
108
// Test named exports.
119
{
1210
spawnSyncAndAssert(
@@ -22,8 +20,6 @@ function testPreload(preloadFlag) {
2220
},
2321
{
2422
stdout: 'A',
25-
stderr: isRequire ?
26-
/ExperimentalWarning: --require is loading ES Module .*module-named-exports\.mjs using require/ : undefined,
2723
trim: true,
2824
}
2925
);
@@ -43,8 +39,6 @@ function testPreload(preloadFlag) {
4339
cwd: fixturesDir
4440
},
4541
{
46-
stderr: isRequire ?
47-
/ExperimentalWarning: --require is loading ES Module .*import-esm\.mjs using require/ : undefined,
4842
stdout: /^world\s+A$/,
4943
trim: true,
5044
}
@@ -66,8 +60,6 @@ function testPreload(preloadFlag) {
6660
},
6761
{
6862
stdout: /^ok\s+A$/,
69-
stderr: isRequire ?
70-
/ExperimentalWarning: --require is loading ES Module .*cjs-exports\.mjs using require/ : undefined,
7163
trim: true,
7264
}
7365
);
@@ -90,8 +82,6 @@ function testPreload(preloadFlag) {
9082
},
9183
{
9284
stdout: /^world\s+A$/,
93-
stderr: isRequire ?
94-
/ExperimentalWarning: --require is loading ES Module .*require-cjs\.mjs using require/ : undefined,
9585
trim: true,
9686
}
9787
);
@@ -117,7 +107,6 @@ testPreload('--import');
117107
},
118108
{
119109
stdout: /^package-type-module\s+A$/,
120-
stderr: /ExperimentalWarning: --require is loading ES Module .*package-type-module[\\/]index\.js using require/,
121110
trim: true,
122111
}
123112
);

test/es-module/test-require-module-warning.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
'use strict';
22

3-
// This checks the warning and the stack trace emitted by the require(esm)
4-
// experimental warning. It can get removed when `require(esm)` becomes stable.
5-
3+
// This checks the warning and the stack trace emitted by --trace-require-module=all.
64
require('../common');
75
const { spawnSyncAndAssert } = require('../common/child_process');
86
const fixtures = require('../common/fixtures');
97
const assert = require('assert');
108

119
spawnSyncAndAssert(process.execPath, [
1210
'--trace-warnings',
11+
'--trace-require-module=all',
1312
fixtures.path('es-modules', 'require-module.js'),
1413
], {
1514
trim: true,
@@ -33,3 +32,12 @@ spawnSyncAndAssert(process.execPath, [
3332
);
3433
}
3534
});
35+
36+
spawnSyncAndAssert(process.execPath, [
37+
'--trace-require-module=1',
38+
fixtures.path('es-modules', 'require-module.js'),
39+
], {
40+
status: 9,
41+
trim: true,
42+
stderr: /invalid value for --trace-require-module/
43+
});

test/es-module/test-require-module.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,6 @@
33

44
const common = require('../common');
55
const assert = require('assert');
6-
const path = require('path');
7-
8-
// Only the first load will trigger the warning.
9-
common.expectWarning(
10-
'ExperimentalWarning',
11-
`CommonJS module ${__filename} is loading ES Module ` +
12-
`${path.resolve(__dirname, '../fixtures/es-module-loaders/module-named-exports.mjs')} using require().\n` +
13-
'Support for loading ES Module in require() is an experimental feature ' +
14-
'and might change at any time'
15-
);
166

177
// Test named exports.
188
{

test/es-module/test-require-node-modules-warning.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

3-
// This checks the experimental warning for require(esm) is disabled when the
4-
// require() comes from node_modules.
3+
// This checks the warning and the stack trace emitted by
4+
// --trace-require-module=no-node-modules.
55
require('../common');
66
const { spawnSyncAndAssert } = require('../common/child_process');
77
const fixtures = require('../common/fixtures');
@@ -14,7 +14,10 @@ const warningRE = /Support for loading ES Module in require\(\)/;
1414
// require() in non-node_modules -> esm in node_modules should warn.
1515
spawnSyncAndAssert(
1616
process.execPath,
17-
[fixtures.path('es-modules', 'test_node_modules', 'require-esm.js')],
17+
[
18+
'--trace-require-module=no-node-modules',
19+
fixtures.path('es-modules', 'test_node_modules', 'require-esm.js')
20+
],
1821
{
1922
trim: true,
2023
stderr: warningRE,
@@ -26,7 +29,10 @@ spawnSyncAndAssert(
2629
// should not warn.
2730
spawnSyncAndAssert(
2831
process.execPath,
29-
[fixtures.path('es-modules', 'test_node_modules', 'require-require-esm.js')],
32+
[
33+
'--trace-require-module=no-node-modules',
34+
fixtures.path('es-modules', 'test_node_modules', 'require-require-esm.js')
35+
],
3036
{
3137
trim: true,
3238
stderr: '',
@@ -38,7 +44,10 @@ spawnSyncAndAssert(
3844
// should not warn.
3945
spawnSyncAndAssert(
4046
process.execPath,
41-
[fixtures.path('es-modules', 'test_node_modules', 'import-require-esm.mjs')],
47+
[
48+
'--trace-require-module=no-node-modules',
49+
fixtures.path('es-modules', 'test_node_modules', 'import-require-esm.mjs')
50+
],
4251
{
4352
trim: true,
4453
stderr: '',
@@ -50,7 +59,10 @@ spawnSyncAndAssert(
5059
// require() in node_modules -> esm in node_modules should not warn.
5160
spawnSyncAndAssert(
5261
process.execPath,
53-
[fixtures.path('es-modules', 'test_node_modules', 'import-import-require-esm.mjs')],
62+
[
63+
'--trace-require-module=no-node-modules',
64+
fixtures.path('es-modules', 'test_node_modules', 'import-import-require-esm.mjs')
65+
],
5466
{
5567
trim: true,
5668
stderr: '',

test/es-module/test-typescript-commonjs.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ test('execute a .cts file importing a .mts file export', async () => {
116116
fixtures.path('typescript/cts/test-require-mts-module.cts'),
117117
]);
118118

119-
match(result.stderr, /Support for loading ES Module in require\(\) is an experimental feature and might change at any time/);
120119
match(result.stdout, /Hello, TypeScript!/);
121120
strictEqual(result.code, 0);
122121
});

test/es-module/test-typescript.mjs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ test('expect failure of a TypeScript file requiring ES module syntax', async ()
175175
fixtures.path('typescript/ts/test-require-module.ts'),
176176
]);
177177

178-
match(result.stderr, /Support for loading ES Module in require\(\) is an experimental feature and might change at any time/);
179178
match(result.stdout, /Hello, TypeScript!/);
180179
strictEqual(result.code, 0);
181180
});
@@ -231,7 +230,6 @@ test('execute a TypeScript file with CommonJS syntax requiring .mts using requir
231230
fixtures.path('typescript/ts/test-require-mts.ts'),
232231
]);
233232

234-
match(result.stderr, /Support for loading ES Module in require\(\) is an experimental feature and might change at any time/);
235233
match(result.stdout, /Hello, TypeScript!/);
236234
strictEqual(result.code, 0);
237235
});

0 commit comments

Comments
 (0)