Skip to content

Commit

Permalink
module: do not warn when require(esm) comes from node_modules
Browse files Browse the repository at this point in the history
Some packages have been using try-catch to load require(esm)
in environments that are available. In 23, where require(esm)
is unflagged, we emit an experimental warning when require()
is used to load ESM. To backport require(esm) to older LTS
releases, however, this could break existing CLI output.
To reduce the disruption for LTS, on older release lines,
this commit is applied to skip the warning if require(esm)
comes from node_modules. This warning will be eventually
removed when require(esm) becomes stable.

PR-URL: #55217
Refs: #52697
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
  • Loading branch information
joyeecheung committed Nov 12, 2024
1 parent b6f753e commit 14170f8
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 0 deletions.
19 changes: 19 additions & 0 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const {
module_export_private_symbol,
module_parent_private_symbol,
},
isInsideNodeModules,
} = internalBinding('util');

const { kEvaluated, createRequiredModuleFacade } = internalBinding('module_wrap');
Expand Down Expand Up @@ -1351,6 +1352,7 @@ let resolvedArgv;
let hasPausedEntry = false;
/** @type {import('vm').Script} */

let emittedRequireModuleWarning = false;
/**
* Resolve and evaluate it synchronously as ESM if it's ESM.
* @param {Module} mod CJS module instance
Expand All @@ -1375,6 +1377,20 @@ function loadESMFromCJS(mod, filename) {
setOwnProperty(process, 'mainModule', undefined);
} else {
const parent = mod[kModuleParent];

if (!emittedRequireModuleWarning) {
let shouldEmitWarning = false;
// Check if the require() comes from node_modules.
if (parent) {
shouldEmitWarning = !isUnderNodeModules(parent.filename);
} else if (mod[kIsCachedByESMLoader]) {
// It comes from the require() built for `import cjs` and doesn't have a parent recorded
// in the CJS module instance. Inspect the stack trace to see if the require()
// comes from node_modules and reduce the noise. If there are more than 100 frames,
// just give up and assume it is under node_modules.
shouldEmitWarning = !isInsideNodeModules(100, true);
}
if (shouldEmitWarning) {
let messagePrefix;
if (parent) {
// In the case of the module calling `require()`, it's more useful to know its absolute path.
Expand All @@ -1395,6 +1411,9 @@ function loadESMFromCJS(mod, filename) {
}
}
emitExperimentalWarning('Support for loading ES Module in require()', messagePrefix);
emittedRequireModuleWarning = true;
}
}
const {
wrap,
namespace,
Expand Down
59 changes: 59 additions & 0 deletions test/es-module/test-require-node-modules-warning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use strict';

// This checks the experimental warning for require(esm) is disabled when the
// require() comes from node_modules.
require('../common');
const { spawnSyncAndAssert } = require('../common/child_process');
const fixtures = require('../common/fixtures');

const warningRE = /Support for loading ES Module in require\(\)/;

// The fixtures are placed in a directory that includes "node_modules" in its name
// to check false negatives.

// require() in non-node_modules -> esm in node_modules should warn.
spawnSyncAndAssert(
process.execPath,
[fixtures.path('es-modules', 'test_node_modules', 'require-esm.js')],
{
trim: true,
stderr: warningRE,
stdout: 'world',
}
);

// require() in non-node_modules -> require() in node_modules -> esm in node_modules
// should not warn.
spawnSyncAndAssert(
process.execPath,
[fixtures.path('es-modules', 'test_node_modules', 'require-require-esm.js')],
{
trim: true,
stderr: '',
stdout: 'world',
}
);

// Import in non-node_modules -> require() in node_modules -> esm in node_modules
// should not warn.
spawnSyncAndAssert(
process.execPath,
[fixtures.path('es-modules', 'test_node_modules', 'import-require-esm.mjs')],
{
trim: true,
stderr: '',
stdout: 'world',
}
);

// Import in non-node_modules -> import in node_modules ->
// require() in node_modules -> esm in node_modules should not warn.
spawnSyncAndAssert(
process.execPath,
[fixtures.path('es-modules', 'test_node_modules', 'import-import-require-esm.mjs')],
{
trim: true,
stderr: '',
stdout: 'world',
}
);

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions test/fixtures/es-modules/test_node_modules/require-esm.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 14170f8

Please sign in to comment.