@@ -98,15 +98,27 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON(
9898
9999 auto cache_entry = binding_data->package_configs_ .find (path.data ());
100100 if (cache_entry != binding_data->package_configs_ .end ()) {
101- return &cache_entry->second ;
101+ auto & cache_value = cache_entry->second ;
102+ if (cache_value.has_value ()) {
103+ return &cache_value.value ();
104+ }
105+
106+ // If we have a cache entry without a value, we've already
107+ // attempted to open and read this path and couldn't (it most
108+ // likely doesn't exist)
109+ return nullptr ;
102110 }
103111
104112 PackageConfig package_config{};
105113 package_config.file_path = path;
106114 // No need to exclude BOM since simdjson will skip it.
107115 if (ReadFileSync (&package_config.raw_json , path.data ()) < 0 ) {
116+ // Add `nullopt` to the package config cache so that we don't
117+ // need to open and attempt to read this path again
118+ binding_data->package_configs_ .insert ({std::string (path), std::nullopt });
108119 return nullptr ;
109120 }
121+
110122 simdjson::ondemand::document document;
111123 simdjson::ondemand::object main_object;
112124 simdjson::error_code error =
@@ -239,7 +251,7 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON(
239251 auto cached = binding_data->package_configs_ .insert (
240252 {std::string (path), std::move (package_config)});
241253
242- return &cached.first ->second ;
254+ return &cached.first ->second . value () ;
243255}
244256
245257void BindingData::ReadPackageJSON (const FunctionCallbackInfo<Value>& args) {
@@ -320,6 +332,40 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
320332 return nullptr ;
321333}
322334
335+ void BindingData::GetNearestParentPackageJSON (
336+ const v8::FunctionCallbackInfo<v8::Value>& args) {
337+ CHECK_GE (args.Length (), 1 );
338+ CHECK (args[0 ]->IsString ());
339+
340+ Realm* realm = Realm::GetCurrent (args);
341+ BufferValue path_value (realm->isolate (), args[0 ]);
342+ // Check if the path has a trailing slash. If so, add it after
343+ // ToNamespacedPath() as it will be deleted by ToNamespacedPath()
344+ bool slashCheck = path_value.ToStringView ().ends_with (kPathSeparator );
345+
346+ ToNamespacedPath (realm->env (), &path_value);
347+
348+ std::string path_value_str = path_value.ToString ();
349+ if (slashCheck) {
350+ path_value_str.push_back (kPathSeparator );
351+ }
352+
353+ std::filesystem::path path;
354+
355+ #ifdef _WIN32
356+ std::wstring wide_path = ConvertToWideString (path_value_str, GetACP ());
357+ path = std::filesystem::path (wide_path);
358+ #else
359+ path = std::filesystem::path (path_value_str);
360+ #endif
361+
362+ auto package_json = TraverseParent (realm, path);
363+
364+ if (package_json != nullptr ) {
365+ args.GetReturnValue ().Set (package_json->Serialize (realm));
366+ }
367+ }
368+
323369void BindingData::GetNearestParentPackageJSONType (
324370 const FunctionCallbackInfo<Value>& args) {
325371 CHECK_GE (args.Length (), 1 );
@@ -646,6 +692,10 @@ void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
646692 target,
647693 " getNearestParentPackageJSONType" ,
648694 GetNearestParentPackageJSONType);
695+ SetMethod (isolate,
696+ target,
697+ " getNearestParentPackageJSON" ,
698+ GetNearestParentPackageJSON);
649699 SetMethod (
650700 isolate, target, " getPackageScopeConfig" , GetPackageScopeConfig<false >);
651701 SetMethod (isolate, target, " getPackageType" , GetPackageScopeConfig<true >);
@@ -702,6 +752,7 @@ void BindingData::RegisterExternalReferences(
702752 ExternalReferenceRegistry* registry) {
703753 registry->Register (ReadPackageJSON);
704754 registry->Register (GetNearestParentPackageJSONType);
755+ registry->Register (GetNearestParentPackageJSON);
705756 registry->Register (GetPackageScopeConfig<false >);
706757 registry->Register (GetPackageScopeConfig<true >);
707758 registry->Register (EnableCompileCache);
0 commit comments