src: cache missing package.json files in the C++ package config cache · nodejs/node@58abe99 · GitHub
Skip to content

Commit 58abe99

Browse files
michaelsmithxyzaduh95
authored andcommitted
src: cache missing package.json files in the C++ package config cache
PR-URL: #60425 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 2a54209 commit 58abe99

4 files changed

Lines changed: 125 additions & 86 deletions

File tree

lib/internal/modules/package_json_reader.js

Lines changed: 59 additions & 74 deletions

src/node_modules.cc

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,27 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON(
9797

9898
auto cache_entry = binding_data->package_configs_.find(path.data());
9999
if (cache_entry != binding_data->package_configs_.end()) {
100-
return &cache_entry->second;
100+
auto& cache_value = cache_entry->second;
101+
if (cache_value) {
102+
return &*cache_value;
103+
}
104+
105+
// If we have a cache entry without a value, we've already
106+
// attempted to open and read this path and couldn't (it most
107+
// likely doesn't exist)
108+
return nullptr;
101109
}
102110

103111
PackageConfig package_config{};
104112
package_config.file_path = path;
105113
// No need to exclude BOM since simdjson will skip it.
106114
if (ReadFileSync(&package_config.raw_json, path.data()) < 0) {
115+
// Add `nullopt` to the package config cache so that we don't
116+
// need to open and attempt to read this path again
117+
binding_data->package_configs_.insert({std::string(path), std::nullopt});
107118
return nullptr;
108119
}
120+
109121
simdjson::ondemand::document document;
110122
simdjson::ondemand::object main_object;
111123
simdjson::error_code error =
@@ -238,7 +250,7 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON(
238250
auto cached = binding_data->package_configs_.insert(
239251
{std::string(path), std::move(package_config)});
240252

241-
return &cached.first->second;
253+
return &*cached.first->second;
242254
}
243255

244256
void BindingData::ReadPackageJSON(const FunctionCallbackInfo<Value>& args) {
@@ -321,24 +333,49 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
321333
return nullptr;
322334
}
323335

324-
void BindingData::GetNearestParentPackageJSONType(
325-
const FunctionCallbackInfo<Value>& args) {
336+
const std::filesystem::path BindingData::NormalizePath(
337+
Realm* realm, BufferValue* path_value) {
338+
// Check if the path has a trailing slash. If so, add it after
339+
// ToNamespacedPath() as it will be deleted by ToNamespacedPath()
340+
bool slashCheck = path_value->ToStringView().ends_with(kPathSeparator);
341+
342+
ToNamespacedPath(realm->env(), path_value);
343+
344+
auto path = path_value->ToPath();
345+
346+
if (slashCheck) {
347+
path /= "";
348+
}
349+
350+
return path;
351+
}
352+
353+
void BindingData::GetNearestParentPackageJSON(
354+
const v8::FunctionCallbackInfo<v8::Value>& args) {
326355
CHECK_GE(args.Length(), 1);
327356
CHECK(args[0]->IsString());
328357

329358
Realm* realm = Realm::GetCurrent(args);
330359
BufferValue path_value(realm->isolate(), args[0]);
331-
// Check if the path has a trailing slash. If so, add it after
332-
// ToNamespacedPath() as it will be deleted by ToNamespacedPath()
333-
bool slashCheck = path_value.ToStringView().ends_with(kPathSeparator);
334360

335-
ToNamespacedPath(realm->env(), &path_value);
361+
auto path = NormalizePath(realm, &path_value);
336362

337-
auto path = path_value.ToPath();
363+
auto package_json = TraverseParent(realm, path);
338364

339-
if (slashCheck) {
340-
path /= "";
365+
if (package_json != nullptr) {
366+
args.GetReturnValue().Set(package_json->Serialize(realm));
341367
}
368+
}
369+
370+
void BindingData::GetNearestParentPackageJSONType(
371+
const FunctionCallbackInfo<Value>& args) {
372+
CHECK_GE(args.Length(), 1);
373+
CHECK(args[0]->IsString());
374+
375+
Realm* realm = Realm::GetCurrent(args);
376+
BufferValue path_value(realm->isolate(), args[0]);
377+
378+
auto path = NormalizePath(realm, &path_value);
342379

343380
auto package_json = TraverseParent(realm, path);
344381

@@ -569,6 +606,10 @@ void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
569606
target,
570607
"getNearestParentPackageJSONType",
571608
GetNearestParentPackageJSONType);
609+
SetMethod(isolate,
610+
target,
611+
"getNearestParentPackageJSON",
612+
GetNearestParentPackageJSON);
572613
SetMethod(
573614
isolate, target, "getPackageScopeConfig", GetPackageScopeConfig<false>);
574615
SetMethod(isolate, target, "getPackageType", GetPackageScopeConfig<true>);
@@ -624,6 +665,7 @@ void BindingData::RegisterExternalReferences(
624665
ExternalReferenceRegistry* registry) {
625666
registry->Register(ReadPackageJSON);
626667
registry->Register(GetNearestParentPackageJSONType);
668+
registry->Register(GetNearestParentPackageJSON);
627669
registry->Register(GetPackageScopeConfig<false>);
628670
registry->Register(GetPackageScopeConfig<true>);
629671
registry->Register(EnableCompileCache);

src/node_modules.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ 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);
5860
static void GetNearestParentPackageJSONType(
5961
const v8::FunctionCallbackInfo<v8::Value>& args);
6062
template <bool return_only_type>
@@ -72,8 +74,17 @@ class BindingData : public SnapshotableObject {
7274
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
7375

7476
private:
75-
std::unordered_map<std::string, PackageConfig> package_configs_;
77+
/*
78+
* This map caches `PackageConfig` values by `package.json` path.
79+
* An empty optional value indicates that no `package.json` file
80+
* at the given path exists, which we cache to avoid repeated
81+
* attempts to open the same non-existent paths.
82+
*/
83+
std::unordered_map<std::string, std::optional<PackageConfig> >
84+
package_configs_;
7685
simdjson::ondemand::parser json_parser;
86+
static const std::filesystem::path NormalizePath(Realm* realm,
87+
BufferValue* path_value);
7788
// returns null on error
7889
static const PackageConfig* GetPackageJSON(
7990
Realm* realm,

typings/internalBinding/modules.d.ts

Lines changed: 1 addition & 0 deletions

0 commit comments

Comments
 (0)