Skip to content

Commit 7cbe3de

Browse files
joyeecheungruyadorno
authored andcommitted
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. PR-URL: #56194 Fixes: #55417 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
1 parent bfd11d7 commit 7cbe3de

11 files changed

+83
-49
lines changed

doc/api/cli.md

+13
Original file line numberDiff line numberDiff line change
@@ -2669,6 +2669,18 @@ added:
26692669
Prints a stack trace whenever an environment is exited proactively,
26702670
i.e. invoking `process.exit()`.
26712671

2672+
### `--trace-require-module=mode`
2673+
2674+
<!-- YAML
2675+
added:
2676+
- REPLACEME
2677+
-->
2678+
2679+
Prints information about usage of [Loading ECMAScript modules using `require()`][].
2680+
2681+
When `mode` is `all`, all usage is printed. When `mode` is `no-node-modules`, usage
2682+
from the `node_modules` folder is excluded.
2683+
26722684
### `--trace-sigint`
26732685

26742686
<!-- YAML
@@ -3180,6 +3192,7 @@ one is included in the list below.
31803192
* `--trace-event-file-pattern`
31813193
* `--trace-events-enabled`
31823194
* `--trace-exit`
3195+
* `--trace-require-module`
31833196
* `--trace-sigint`
31843197
* `--trace-sync-io`
31853198
* `--trace-tls`

doc/api/modules.md

+10-4
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,15 @@ relative, and based on the real path of the files making the calls to
174174
added:
175175
- v22.0.0
176176
changes:
177-
- version: v22.12.0
177+
- version:
178+
- v23.0.0
179+
- v22.12.0
178180
pr-url: https://github.com/nodejs/node/pull/55085
179181
description: This feature is no longer behind the `--experimental-require-module` CLI flag.
182+
- version: REPLACEME
183+
pr-url: https://github.com/nodejs/node/pull/56194
184+
description: This feature no longer emits an experimental warning by default,
185+
though the warning can still be emitted by --trace-require-module.
180186
- version: v22.12.0
181187
pr-url: https://github.com/nodejs/node/pull/54563
182188
description: Support `'module.exports'` interop export in `require(esm)`.
@@ -314,9 +320,8 @@ help users fix them.
314320

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

@@ -1266,6 +1271,7 @@ This section was moved to
12661271
[GLOBAL_FOLDERS]: #loading-from-the-global-folders
12671272
[`"main"`]: packages.md#main
12681273
[`"type"`]: packages.md#type
1274+
[`--trace-require-module`]: cli.md#--trace-require-modulemode
12691275
[`ERR_REQUIRE_ASYNC_MODULE`]: errors.md#err_require_async_module
12701276
[`ERR_UNSUPPORTED_DIR_IMPORT`]: errors.md#err_unsupported_dir_import
12711277
[`MODULE_NOT_FOUND`]: errors.md#module_not_found

lib/internal/modules/cjs/loader.js

+17-12
Original file line numberDiff line numberDiff line change
@@ -1338,7 +1338,7 @@ Module.prototype.require = function(id) {
13381338
}
13391339
};
13401340

1341-
let emittedRequireModuleWarning = false;
1341+
let requireModuleWarningMode;
13421342
/**
13431343
* Resolve and evaluate it synchronously as ESM if it's ESM.
13441344
* @param {Module} mod CJS module instance
@@ -1361,17 +1361,22 @@ function loadESMFromCJS(mod, filename) {
13611361
} else {
13621362
const parent = mod[kModuleParent];
13631363

1364-
if (!emittedRequireModuleWarning) {
1364+
requireModuleWarningMode ??= getOptionValue('--trace-require-module');
1365+
if (requireModuleWarningMode) {
13651366
let shouldEmitWarning = false;
1366-
// Check if the require() comes from node_modules.
1367-
if (parent) {
1368-
shouldEmitWarning = !isUnderNodeModules(parent.filename);
1369-
} else if (mod[kIsCachedByESMLoader]) {
1370-
// It comes from the require() built for `import cjs` and doesn't have a parent recorded
1371-
// in the CJS module instance. Inspect the stack trace to see if the require()
1372-
// comes from node_modules and reduce the noise. If there are more than 100 frames,
1373-
// just give up and assume it is under node_modules.
1374-
shouldEmitWarning = !isInsideNodeModules(100, true);
1367+
if (requireModuleWarningMode === 'no-node-modules') {
1368+
// Check if the require() comes from node_modules.
1369+
if (parent) {
1370+
shouldEmitWarning = !isUnderNodeModules(parent.filename);
1371+
} else if (mod[kIsCachedByESMLoader]) {
1372+
// It comes from the require() built for `import cjs` and doesn't have a parent recorded
1373+
// in the CJS module instance. Inspect the stack trace to see if the require()
1374+
// comes from node_modules and reduce the noise. If there are more than 100 frames,
1375+
// just give up and assume it is under node_modules.
1376+
shouldEmitWarning = !isInsideNodeModules(100, true);
1377+
}
1378+
} else {
1379+
shouldEmitWarning = true;
13751380
}
13761381
if (shouldEmitWarning) {
13771382
let messagePrefix;
@@ -1397,7 +1402,7 @@ function loadESMFromCJS(mod, filename) {
13971402
messagePrefix,
13981403
undefined,
13991404
parent?.require);
1400-
emittedRequireModuleWarning = true;
1405+
requireModuleWarningMode = true;
14011406
}
14021407
}
14031408
const {

src/node_options.cc

+13
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors,
142142
errors->push_back("--heapsnapshot-near-heap-limit must not be negative");
143143
}
144144

145+
if (!trace_require_module.empty() && trace_require_module != "all" &&
146+
trace_require_module != "no-node-modules") {
147+
errors->push_back("invalid value for --trace-require-module");
148+
}
149+
145150
if (test_runner) {
146151
if (test_isolation == "none") {
147152
debug_options_.allow_attaching_debugger = true;
@@ -797,6 +802,14 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
797802
"set module system to use by default",
798803
&EnvironmentOptions::type,
799804
kAllowedInEnvvar);
805+
806+
AddOption(
807+
"--trace-require-module",
808+
"Print access to require(esm). Options are 'all' (print all usage) and "
809+
"'no-node-modules' (excluding usage from the node_modules folder)",
810+
&EnvironmentOptions::trace_require_module,
811+
kAllowedInEnvvar);
812+
800813
AddOption("--extra-info-on-fatal-exception",
801814
"hide extra information on fatal exception that causes exit",
802815
&EnvironmentOptions::extra_info_on_fatal_exception,

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ class EnvironmentOptions : public Options {
213213
bool trace_env = false;
214214
bool trace_env_js_stack = false;
215215
bool trace_env_native_stack = false;
216+
std::string trace_require_module;
216217
bool extra_info_on_fatal_exception = true;
217218
std::string unhandled_rejections;
218219
std::vector<std::string> userland_loaders;

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

-11
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

+11-3
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

-10
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

+18-6
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

-1
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

-2
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,6 @@ test('expect failure of a TypeScript file requiring ES module syntax', async ()
239239
fixtures.path('typescript/ts/test-require-module.ts'),
240240
]);
241241

242-
match(result.stderr, /Support for loading ES Module in require\(\) is an experimental feature and might change at any time/);
243242
match(result.stdout, /Hello, TypeScript!/);
244243
strictEqual(result.code, 0);
245244
});
@@ -319,7 +318,6 @@ test('execute a TypeScript file with CommonJS syntax requiring .mts using requir
319318
fixtures.path('typescript/ts/test-require-mts.ts'),
320319
]);
321320

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

0 commit comments

Comments
 (0)