Skip to content

Commit 36b68db

Browse files
michaelsmithxyzaduh95
authored andcommitted
src: reduce the nearest parent package JSON cache size
PR-URL: #59888 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Rafael Gonzaga <[email protected]> Reviewed-By: Edy Silva <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
1 parent 26b40ba commit 36b68db

File tree

4 files changed

+52
-46
lines changed

4 files changed

+52
-46
lines changed

lib/internal/modules/package_json_reader.js

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ const {
66
ObjectDefineProperty,
77
RegExpPrototypeExec,
88
SafeMap,
9+
StringPrototypeEndsWith,
910
StringPrototypeIndexOf,
11+
StringPrototypeLastIndexOf,
1012
StringPrototypeSlice,
1113
} = primordials;
1214
const {
@@ -26,6 +28,7 @@ const {
2628
const { kEmptyObject } = require('internal/util');
2729
const modulesBinding = internalBinding('modules');
2830
const path = require('path');
31+
const permission = require('internal/process/permission');
2932
const { validateString } = require('internal/validators');
3033
const internalFsBinding = internalBinding('fs');
3134

@@ -127,26 +130,71 @@ function read(jsonPath, { base, specifier, isESM } = kEmptyObject) {
127130
};
128131
}
129132

133+
/**
134+
* Given a file path, walk the filesystem upwards until we find its closest parent
135+
* `package.json` file, stopping when:
136+
* 1. we find a `package.json` file;
137+
* 2. we find a path that we do not have permission to read;
138+
* 3. we find a containing `node_modules` directory;
139+
* 4. or, we reach the filesystem root
140+
* @returns {undefined | string}
141+
*/
142+
function findParentPackageJSON(checkPath) {
143+
const enabledPermission = permission.isEnabled();
144+
145+
const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, path.sep);
146+
let separatorIndex;
147+
148+
do {
149+
separatorIndex = StringPrototypeLastIndexOf(checkPath, path.sep);
150+
checkPath = StringPrototypeSlice(checkPath, 0, separatorIndex);
151+
152+
if (enabledPermission && !permission.has('fs.read', checkPath + path.sep)) {
153+
return undefined;
154+
}
155+
156+
if (StringPrototypeEndsWith(checkPath, path.sep + 'node_modules')) {
157+
return undefined;
158+
}
159+
160+
const maybePackageJSONPath = checkPath + path.sep + 'package.json';
161+
const stat = internalFsBinding.internalModuleStat(checkPath + path.sep + 'package.json');
162+
163+
const packageJSONExists = stat === 0;
164+
if (packageJSONExists) {
165+
return maybePackageJSONPath;
166+
}
167+
} while (separatorIndex > rootSeparatorIndex);
168+
169+
return undefined;
170+
}
171+
130172
/**
131173
* Get the nearest parent package.json file from a given path.
132174
* Return the package.json data and the path to the package.json file, or undefined.
133175
* @param {string} checkPath The path to start searching from.
134176
* @returns {undefined | DeserializedPackageConfig}
135177
*/
136178
function getNearestParentPackageJSON(checkPath) {
137-
if (nearestParentPackageJSONCache.has(checkPath)) {
138-
return nearestParentPackageJSONCache.get(checkPath);
179+
const nearestParentPackageJSON = findParentPackageJSON(checkPath);
180+
181+
if (nearestParentPackageJSON === undefined) {
182+
return undefined;
183+
}
184+
185+
if (nearestParentPackageJSONCache.has(nearestParentPackageJSON)) {
186+
return nearestParentPackageJSONCache.get(nearestParentPackageJSON);
139187
}
140188

141-
const result = modulesBinding.getNearestParentPackageJSON(checkPath);
189+
const result = modulesBinding.readPackageJSON(nearestParentPackageJSON);
142190

143191
if (result === undefined) {
144192
nearestParentPackageJSONCache.set(checkPath, undefined);
145193
return undefined;
146194
}
147195

148196
const packageConfig = deserializePackageJSON(checkPath, result);
149-
nearestParentPackageJSONCache.set(checkPath, packageConfig);
197+
nearestParentPackageJSONCache.set(nearestParentPackageJSON, packageConfig);
150198

151199
return packageConfig;
152200
}

src/node_modules.cc

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -315,40 +315,6 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
315315
return nullptr;
316316
}
317317

318-
void BindingData::GetNearestParentPackageJSON(
319-
const v8::FunctionCallbackInfo<v8::Value>& args) {
320-
CHECK_GE(args.Length(), 1);
321-
CHECK(args[0]->IsString());
322-
323-
Realm* realm = Realm::GetCurrent(args);
324-
BufferValue path_value(realm->isolate(), args[0]);
325-
// Check if the path has a trailing slash. If so, add it after
326-
// ToNamespacedPath() as it will be deleted by ToNamespacedPath()
327-
bool slashCheck = path_value.ToStringView().ends_with(kPathSeparator);
328-
329-
ToNamespacedPath(realm->env(), &path_value);
330-
331-
std::string path_value_str = path_value.ToString();
332-
if (slashCheck) {
333-
path_value_str.push_back(kPathSeparator);
334-
}
335-
336-
std::filesystem::path path;
337-
338-
#ifdef _WIN32
339-
std::wstring wide_path = ConvertToWideString(path_value_str, GetACP());
340-
path = std::filesystem::path(wide_path);
341-
#else
342-
path = std::filesystem::path(path_value_str);
343-
#endif
344-
345-
auto package_json = TraverseParent(realm, path);
346-
347-
if (package_json != nullptr) {
348-
args.GetReturnValue().Set(package_json->Serialize(realm));
349-
}
350-
}
351-
352318
void BindingData::GetNearestParentPackageJSONType(
353319
const FunctionCallbackInfo<Value>& args) {
354320
CHECK_GE(args.Length(), 1);
@@ -597,10 +563,6 @@ void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
597563
target,
598564
"getNearestParentPackageJSONType",
599565
GetNearestParentPackageJSONType);
600-
SetMethod(isolate,
601-
target,
602-
"getNearestParentPackageJSON",
603-
GetNearestParentPackageJSON);
604566
SetMethod(
605567
isolate, target, "getPackageScopeConfig", GetPackageScopeConfig<false>);
606568
SetMethod(isolate, target, "getPackageType", GetPackageScopeConfig<true>);
@@ -636,7 +598,6 @@ void BindingData::RegisterExternalReferences(
636598
ExternalReferenceRegistry* registry) {
637599
registry->Register(ReadPackageJSON);
638600
registry->Register(GetNearestParentPackageJSONType);
639-
registry->Register(GetNearestParentPackageJSON);
640601
registry->Register(GetPackageScopeConfig<false>);
641602
registry->Register(GetPackageScopeConfig<true>);
642603
registry->Register(EnableCompileCache);

src/node_modules.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ class BindingData : public SnapshotableObject {
5555
SET_MEMORY_INFO_NAME(BindingData)
5656

5757
static void ReadPackageJSON(const v8::FunctionCallbackInfo<v8::Value>& args);
58-
static void GetNearestParentPackageJSON(
59-
const v8::FunctionCallbackInfo<v8::Value>& args);
6058
static void GetNearestParentPackageJSONType(
6159
const v8::FunctionCallbackInfo<v8::Value>& args);
6260
template <bool return_only_type>

typings/internalBinding/modules.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export type SerializedPackageConfig = [
2323
export interface ModulesBinding {
2424
readPackageJSON(path: string): SerializedPackageConfig | undefined;
2525
getNearestParentPackageJSONType(path: string): PackageConfig['type']
26-
getNearestParentPackageJSON(path: string): SerializedPackageConfig | undefined
2726
getPackageScopeConfig(path: string): SerializedPackageConfig | undefined
2827
getPackageType(path: string): PackageConfig['type'] | undefined
2928
enableCompileCache(path?: string): { status: number, message?: string, directory?: string }

0 commit comments

Comments
 (0)