From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Mon, 31 Mar 2025 11:21:29 -0700 Subject: fix: expose ReadFileSync override for modules To avoid copying out `package.json` files out of the ASAR file we need an API override to replace the native `ReadFileSync` in the `modules` binding. diff --git a/src/env_properties.h b/src/env_properties.h index 82225b0a53dd828750991a4e15a060b736b6ea2b..4b0d31356a2496a7fc67876a22da2453efc54f53 100644 --- a/src/env_properties.h +++ b/src/env_properties.h @@ -508,6 +508,7 @@ V(maybe_cache_generated_source_map, v8::Function) \ V(messaging_deserialize_create_object, v8::Function) \ V(message_port, v8::Object) \ + V(modules_read_file_sync, v8::Function) \ V(builtin_module_require, v8::Function) \ V(performance_entry_callback, v8::Function) \ V(prepare_stack_trace_callback, v8::Function) \ diff --git a/src/node_modules.cc b/src/node_modules.cc index 60b03b1563b230f78d8a9bdd8480da4d2ae90a27..62050791174563f88b8629d576eed8959b3c2e20 100644 --- a/src/node_modules.cc +++ b/src/node_modules.cc @@ -21,6 +21,7 @@ namespace modules { using v8::Array; using v8::Context; +using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Isolate; @@ -89,6 +90,7 @@ Local BindingData::PackageConfig::Serialize(Realm* realm) const { const BindingData::PackageConfig* BindingData::GetPackageJSON( Realm* realm, std::string_view path, ErrorContext* error_context) { + auto isolate = realm->isolate(); auto binding_data = realm->GetBindingData(); auto cache_entry = binding_data->package_configs_.find(path.data()); @@ -98,8 +100,36 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON( PackageConfig package_config{}; package_config.file_path = path; + + Local modules_read_file_sync = realm->modules_read_file_sync(); + + int read_err; // No need to exclude BOM since simdjson will skip it. - if (ReadFileSync(&package_config.raw_json, path.data()) < 0) { + if (modules_read_file_sync.IsEmpty()) { + read_err = ReadFileSync(&package_config.raw_json, path.data()); + } else { + Local args[] = { + v8::String::NewFromUtf8(isolate, path.data()).ToLocalChecked(), + }; + Local result = modules_read_file_sync->Call( + realm->context(), + Undefined(isolate), + arraysize(args), + args).ToLocalChecked(); + + if (result->IsUndefined()) { + // Fallback + read_err = ReadFileSync(&package_config.raw_json, path.data()); + } else if (result->IsFalse()) { + // Not found + read_err = -1; + } else { + BufferValue data(isolate, result); + package_config.raw_json = data.ToString(); + read_err = 0; + } + } + if (read_err < 0) { return nullptr; } simdjson::ondemand::document document; @@ -237,6 +267,12 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON( return &cached.first->second; } +void BindingData::OverrideReadFileSync(const FunctionCallbackInfo& args) { + Realm* realm = Realm::GetCurrent(args); + CHECK(args[0]->IsFunction()); + realm->set_modules_read_file_sync(args[0].As()); +} + void BindingData::ReadPackageJSON(const FunctionCallbackInfo& args) { CHECK_GE(args.Length(), 1); // path, [is_esm, base, specifier] CHECK(args[0]->IsString()); // path @@ -558,6 +594,8 @@ void InitImportMetaPathHelpers(const FunctionCallbackInfo& args) { void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data, Local target) { Isolate* isolate = isolate_data->isolate(); + SetMethod(isolate, target, "overrideReadFileSync", OverrideReadFileSync); + SetMethod(isolate, target, "readPackageJSON", ReadPackageJSON); SetMethod(isolate, target, @@ -596,6 +634,8 @@ void BindingData::CreatePerContextProperties(Local target, void BindingData::RegisterExternalReferences( ExternalReferenceRegistry* registry) { + registry->Register(OverrideReadFileSync); + registry->Register(ReadPackageJSON); registry->Register(GetNearestParentPackageJSONType); registry->Register(GetPackageScopeConfig); diff --git a/src/node_modules.h b/src/node_modules.h index e4ba6b75bc86d14deada835903ba68a4cb0eccc5..ae77f9ec81b358bd356993617cd07671d382e8ca 100644 --- a/src/node_modules.h +++ b/src/node_modules.h @@ -54,6 +54,8 @@ class BindingData : public SnapshotableObject { SET_SELF_SIZE(BindingData) SET_MEMORY_INFO_NAME(BindingData) + static void OverrideReadFileSync( + const v8::FunctionCallbackInfo& args); static void ReadPackageJSON(const v8::FunctionCallbackInfo& args); static void GetNearestParentPackageJSONType( const v8::FunctionCallbackInfo& args);