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 39803ae466fc81a6b2ff6e12093cdf2082790a9f..29ce687a03ccc8e45881f70d34e009e028aa4074 100644 --- a/src/env_properties.h +++ b/src/env_properties.h @@ -490,6 +490,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 9eec93f52f0d0b2e45ae04ff357b4ced0770782f..eea4ba313d8dbcf7b88b79f5a3e9ba2eb39d7c3e 100644 --- a/src/node_modules.cc +++ b/src/node_modules.cc @@ -23,6 +23,7 @@ namespace modules { using v8::Array; using v8::Context; using v8::External; +using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Integer; @@ -94,6 +95,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()); @@ -103,8 +105,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; @@ -242,6 +272,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 @@ -635,6 +671,8 @@ void SaveCompileCacheEntry(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, @@ -694,6 +732,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);