lib,src: isInsideNodeModules should test on the first non-internal frame

PR-URL: https://github.com/nodejs/node/pull/60991
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Daniel Lemire <daniel@lemire.me>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Chengzhong Wu
2025-12-08 12:36:38 +00:00
committed by Node.js GitHub Bot
parent d5f2f14fad
commit 32ea48d749
8 changed files with 58 additions and 29 deletions

View File

@@ -4,14 +4,13 @@ const assert = require('assert');
const bench = common.createBenchmark(main, { const bench = common.createBenchmark(main, {
n: [1e6], n: [1e6],
stackLimit: [100],
stackCount: [99, 101], stackCount: [99, 101],
method: ['isInsideNodeModules', 'noop'], method: ['isInsideNodeModules', 'noop'],
}, { }, {
flags: ['--expose-internals', '--disable-warning=internal/test/binding'], flags: ['--expose-internals', '--disable-warning=internal/test/binding'],
}); });
function main({ n, stackLimit, stackCount, method }) { function main({ n, stackCount, method }) {
const { internalBinding } = require('internal/test/binding'); const { internalBinding } = require('internal/test/binding');
const { isInsideNodeModules } = internalBinding('util'); const { isInsideNodeModules } = internalBinding('util');
@@ -30,7 +29,7 @@ function main({ n, stackLimit, stackCount, method }) {
bench.start(); bench.start();
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
isInsideNodeModules(stackLimit, true); isInsideNodeModules();
} }
bench.end(n); bench.end(n);
}; };

View File

@@ -178,15 +178,14 @@ function showFlaggedDeprecation() {
if (bufferWarningAlreadyEmitted || if (bufferWarningAlreadyEmitted ||
++nodeModulesCheckCounter > 10000 || ++nodeModulesCheckCounter > 10000 ||
(!require('internal/options').getOptionValue('--pending-deprecation') && (!require('internal/options').getOptionValue('--pending-deprecation') &&
isInsideNodeModules(100, true))) { isInsideNodeModules(3))) {
// We don't emit a warning, because we either: // We don't emit a warning, because we either:
// - Already did so, or // - Already did so, or
// - Already checked too many times whether a call is coming // - Already checked too many times whether a call is coming
// from node_modules and want to stop slowing down things, or // from node_modules and want to stop slowing down things, or
// - We aren't running with `--pending-deprecation` enabled, // - We aren't running with `--pending-deprecation` enabled,
// and the code is inside `node_modules`. // and the code is inside `node_modules`.
// - We found node_modules in up to the topmost 100 frames, or // - If the topmost non-internal frame is not inside `node_modules`.
// there are more than 100 frames and we don't want to search anymore.
return; return;
} }

View File

@@ -1537,9 +1537,8 @@ function loadESMFromCJS(mod, filename, format, source) {
} else if (mod[kIsCachedByESMLoader]) { } else if (mod[kIsCachedByESMLoader]) {
// It comes from the require() built for `import cjs` and doesn't have a parent recorded // 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() // 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, // comes from node_modules as a direct call and reduce the noise.
// just give up and assume it is under node_modules. shouldEmitWarning = !isInsideNodeModules();
shouldEmitWarning = !isInsideNodeModules(100, true);
} }
} else { } else {
shouldEmitWarning = true; shouldEmitWarning = true;

View File

@@ -3,7 +3,7 @@ const {
isInsideNodeModules, isInsideNodeModules,
} = internalBinding('util'); } = internalBinding('util');
if (!isInsideNodeModules(100, true)) { if (!isInsideNodeModules()) {
process.emitWarning( process.emitWarning(
'The `punycode` module is deprecated. Please use a userland ' + 'The `punycode` module is deprecated. Please use a userland ' +
'alternative instead.', 'alternative instead.',

View File

@@ -132,7 +132,7 @@ const {
let urlParseWarned = false; let urlParseWarned = false;
function urlParse(url, parseQueryString, slashesDenoteHost) { function urlParse(url, parseQueryString, slashesDenoteHost) {
if (!urlParseWarned && !isInsideNodeModules(100, true)) { if (!urlParseWarned && !isInsideNodeModules(2)) {
urlParseWarned = true; urlParseWarned = true;
process.emitWarning( process.emitWarning(
'`url.parse()` behavior is not standardized and prone to ' + '`url.parse()` behavior is not standardized and prone to ' +

View File

@@ -320,23 +320,21 @@ static void GetCallSites(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(callsites); args.GetReturnValue().Set(callsites);
} }
/**
* Checks whether the current call directly initiated from a file inside
* node_modules. This checks up to `frame_limit` stack frames, until it finds
* a frame that is not part of node internal modules.
*/
static void IsInsideNodeModules(const FunctionCallbackInfo<Value>& args) { static void IsInsideNodeModules(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate(); Isolate* isolate = args.GetIsolate();
CHECK_EQ(args.Length(), 2);
CHECK(args[0]->IsInt32()); // frame_limit
// The second argument is the default value.
int frames_limit = args[0].As<v8::Int32>()->Value(); int frames_limit = (args.Length() > 0 && args[0]->IsInt32())
? args[0].As<v8::Int32>()->Value()
: 10;
Local<StackTrace> stack = Local<StackTrace> stack =
StackTrace::CurrentStackTrace(isolate, frames_limit); StackTrace::CurrentStackTrace(isolate, frames_limit);
int frame_count = stack->GetFrameCount(); int frame_count = stack->GetFrameCount();
// If the search requires looking into more than |frames_limit| frames, give
// up and return the specified default value.
if (frame_count == frames_limit) {
return args.GetReturnValue().Set(args[1]);
}
bool result = false; bool result = false;
for (int i = 0; i < frame_count; ++i) { for (int i = 0; i < frame_count; ++i) {
Local<StackFrame> stack_frame = stack->GetFrame(isolate, i); Local<StackFrame> stack_frame = stack->GetFrame(isolate, i);
@@ -350,13 +348,11 @@ static void IsInsideNodeModules(const FunctionCallbackInfo<Value>& args) {
if (script_name_str.starts_with("node:")) { if (script_name_str.starts_with("node:")) {
continue; continue;
} }
if (script_name_str.find("/node_modules/") != std::string::npos || result = script_name_str.find("/node_modules/") != std::string::npos ||
script_name_str.find("\\node_modules\\") != std::string::npos || script_name_str.find("\\node_modules\\") != std::string::npos ||
script_name_str.find("/node_modules\\") != std::string::npos || script_name_str.find("/node_modules\\") != std::string::npos ||
script_name_str.find("\\node_modules/") != std::string::npos) { script_name_str.find("\\node_modules/") != std::string::npos;
result = true; break;
break;
}
} }
args.GetReturnValue().Set(result); args.GetReturnValue().Set(result);

View File

@@ -0,0 +1,36 @@
'use strict';
// Flags: --expose-internals
const common = require('../common');
const assert = require('assert');
const vm = require('vm');
const { internalBinding } = require('internal/test/binding');
const { isInsideNodeModules } = internalBinding('util');
const script = new vm.Script(`
const runInsideNodeModules = (cb) => {
return cb();
};
runInsideNodeModules;
`, {
filename: '/workspace/node_modules/test.js',
});
const runInsideNodeModules = script.runInThisContext();
// Test when called directly inside node_modules
assert.strictEqual(runInsideNodeModules(isInsideNodeModules), true);
// Test when called inside a user callback from node_modules
runInsideNodeModules(common.mustCall(() => {
function nonNodeModulesFunction() {
assert.strictEqual(isInsideNodeModules(), false);
}
nonNodeModulesFunction();
}));
// Test when called outside node_modules
assert.strictEqual(isInsideNodeModules(), false);

View File

@@ -45,7 +45,7 @@ export interface UtilBinding {
guessHandleType(fd: number): 'TCP' | 'TTY' | 'UDP' | 'FILE' | 'PIPE' | 'UNKNOWN'; guessHandleType(fd: number): 'TCP' | 'TTY' | 'UDP' | 'FILE' | 'PIPE' | 'UNKNOWN';
parseEnv(content: string): Record<string, string>; parseEnv(content: string): Record<string, string>;
styleText(format: Array<string> | string, text: string): string; styleText(format: Array<string> | string, text: string): string;
isInsideNodeModules(frameLimit: number, defaultValue: unknown): boolean; isInsideNodeModules(frameLimit?: number): boolean;
constructSharedArrayBuffer(length?: number): SharedArrayBuffer; constructSharedArrayBuffer(length?: number): SharedArrayBuffer;
constants: { constants: {