diff --git a/lib/internal/modules/package_json_reader.js b/lib/internal/modules/package_json_reader.js index 65f5ce3551bbd0..6daa6127e73240 100644 --- a/lib/internal/modules/package_json_reader.js +++ b/lib/internal/modules/package_json_reader.js @@ -1,40 +1,19 @@ 'use strict'; const { - JSONParse, - ObjectPrototypeHasOwnProperty, - SafeMap, StringPrototypeEndsWith, StringPrototypeIndexOf, StringPrototypeLastIndexOf, StringPrototypeSlice, } = primordials; -const { - ERR_INVALID_PACKAGE_CONFIG, -} = require('internal/errors').codes; -const { internalModuleReadJSON } = internalBinding('fs'); +const modulesBinding = internalBinding('modules'); const { resolve, sep, toNamespacedPath } = require('path'); const permission = require('internal/process/permission'); -const { kEmptyObject, setOwnProperty } = require('internal/util'); - -const { fileURLToPath, pathToFileURL } = require('internal/url'); +const { kEmptyObject } = require('internal/util'); -const cache = new SafeMap(); +const { pathToFileURL } = require('internal/url'); let manifest; - -/** - * @typedef {{ - * exists: boolean, - * pjsonPath: string, - * exports?: string | string[] | Record, - * imports?: string | string[] | Record, - * name?: string, - * main?: string, - * type: 'commonjs' | 'module' | 'none', - * }} PackageConfig - */ - /** * @param {string} jsonPath * @param {{ @@ -42,64 +21,15 @@ let manifest; * specifier: string, * isESM: boolean, * }} options - * @returns {PackageConfig} + * @returns {import('typings/internalBinding/modules').PackageConfig} */ function read(jsonPath, { base, specifier, isESM } = kEmptyObject) { - if (cache.has(jsonPath)) { - return cache.get(jsonPath); - } - - const string = internalModuleReadJSON( + const parsed = modulesBinding.readPackageJSON( toNamespacedPath(jsonPath), ); - const result = { - __proto__: null, - exists: false, - pjsonPath: jsonPath, - main: undefined, - name: undefined, - type: 'none', // Ignore unknown types for forwards compatibility - exports: undefined, - imports: undefined, - }; - - if (string !== undefined) { - let parsed; - try { - parsed = JSONParse(string); - } catch (cause) { - const error = new ERR_INVALID_PACKAGE_CONFIG( - jsonPath, - isESM && (base ? `"${specifier}" from ` : '') + fileURLToPath(base || specifier), - cause.message, - ); - setOwnProperty(error, 'cause', cause); - throw error; - } - - result.exists = true; - // ObjectPrototypeHasOwnProperty is used to avoid prototype pollution. - if (ObjectPrototypeHasOwnProperty(parsed, 'name') && typeof parsed.name === 'string') { - result.name = parsed.name; - } - - if (ObjectPrototypeHasOwnProperty(parsed, 'main') && typeof parsed.main === 'string') { - result.main = parsed.main; - } - - if (ObjectPrototypeHasOwnProperty(parsed, 'exports')) { - result.exports = parsed.exports; - } - - if (ObjectPrototypeHasOwnProperty(parsed, 'imports')) { - result.imports = parsed.imports; - } - - // Ignore unknown types for forwards compatibility - if (ObjectPrototypeHasOwnProperty(parsed, 'type') && (parsed.type === 'commonjs' || parsed.type === 'module')) { - result.type = parsed.type; - } + if (parsed !== undefined) { + parsed.pjsonPath = jsonPath; if (manifest === undefined) { const { getOptionValue } = require('internal/options'); @@ -108,12 +38,24 @@ function read(jsonPath, { base, specifier, isESM } = kEmptyObject) { null; } if (manifest !== null) { - const jsonURL = pathToFileURL(jsonPath); - manifest.assertIntegrity(jsonURL, string); + // TODO(@anonrig): Find a way to assert integrity without returning string. + // const jsonURL = pathToFileURL(jsonPath); + // manifest.assertIntegrity(jsonURL, string); } + + return parsed; } - cache.set(jsonPath, result); - return result; + + return { + __proto__: null, + exists: false, + pjsonPath: jsonPath, + main: undefined, + name: undefined, + type: 'none', // Ignore unknown types for forwards compatibility + exports: undefined, + imports: undefined, + }; } /** diff --git a/lib/internal/modules/run_main.js b/lib/internal/modules/run_main.js index 1f03c313121db0..d90da8a2fe74ea 100644 --- a/lib/internal/modules/run_main.js +++ b/lib/internal/modules/run_main.js @@ -5,6 +5,7 @@ const { } = primordials; const { containsModuleSyntax } = internalBinding('contextify'); +const { getPackageJSONType } = internalBinding('modules'); const { getOptionValue } = require('internal/options'); const path = require('path'); @@ -68,10 +69,10 @@ function shouldUseESMLoader(mainPath) { if (mainPath && StringPrototypeEndsWith(mainPath, '.mjs')) { return true; } if (!mainPath || StringPrototypeEndsWith(mainPath, '.cjs')) { return false; } - const { readPackageScope } = require('internal/modules/package_json_reader'); - const pkg = readPackageScope(mainPath); - // No need to guard `pkg` as it can only be an object or `false`. - switch (pkg.data?.type) { + // TODO(@anonrig): Move resolve functionality to C++. + const type = getPackageJSONType(path.resolve(mainPath, 'package.json')); + + switch (type) { case 'module': return true; case 'commonjs': diff --git a/node.gyp b/node.gyp index 93e4235a0f3efd..c6afd6690d4d21 100644 --- a/node.gyp +++ b/node.gyp @@ -112,6 +112,7 @@ 'src/node_main_instance.cc', 'src/node_messaging.cc', 'src/node_metadata.cc', + 'src/node_modules.cc', 'src/node_options.cc', 'src/node_os.cc', 'src/node_perf.cc', @@ -234,6 +235,7 @@ 'src/node_messaging.h', 'src/node_metadata.h', 'src/node_mutex.h', + 'src/node_modules.h', 'src/node_object_wrap.h', 'src/node_options.h', 'src/node_options-inl.h', diff --git a/src/base_object_types.h b/src/base_object_types.h index cb034f1d62b681..9cfe6a77f71708 100644 --- a/src/base_object_types.h +++ b/src/base_object_types.h @@ -17,7 +17,8 @@ namespace node { V(blob_binding_data, BlobBindingData) \ V(process_binding_data, process::BindingData) \ V(timers_binding_data, timers::BindingData) \ - V(url_binding_data, url::BindingData) + V(url_binding_data, url::BindingData) \ + V(modules_binding_data, modules::BindingData) #define UNSERIALIZABLE_BINDING_TYPES(V) \ V(http2_binding_data, http2::BindingData) \ diff --git a/src/node_binding.cc b/src/node_binding.cc index 97257d47c61738..2b69a828a744b6 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -49,6 +49,7 @@ V(js_stream) \ V(js_udp_wrap) \ V(messaging) \ + V(modules) \ V(module_wrap) \ V(mksnapshot) \ V(options) \ diff --git a/src/node_binding.h b/src/node_binding.h index 9f0692ca4e190b..29bb478b99e8eb 100644 --- a/src/node_binding.h +++ b/src/node_binding.h @@ -38,6 +38,7 @@ static_assert(static_cast(NM_F_LINKED) == V(encoding_binding) \ V(fs) \ V(mksnapshot) \ + V(modules) \ V(timers) \ V(process_methods) \ V(performance) \ diff --git a/src/node_errors.h b/src/node_errors.h index 0f4a2d0cc6eaaf..d86281f48f15f9 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -71,6 +71,7 @@ void AppendExceptionLine(Environment* env, V(ERR_INVALID_ARG_TYPE, TypeError) \ V(ERR_INVALID_FILE_URL_HOST, TypeError) \ V(ERR_INVALID_FILE_URL_PATH, TypeError) \ + V(ERR_INVALID_PACKAGE_CONFIG, Error) \ V(ERR_INVALID_OBJECT_DEFINE_PROPERTY, TypeError) \ V(ERR_INVALID_MODULE, Error) \ V(ERR_INVALID_STATE, Error) \ diff --git a/src/node_external_reference.h b/src/node_external_reference.h index ae37094c8e117e..a647967077967e 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -100,6 +100,7 @@ class ExternalReferenceRegistry { V(messaging) \ V(mksnapshot) \ V(module_wrap) \ + V(modules) \ V(options) \ V(os) \ V(performance) \ diff --git a/src/node_file.cc b/src/node_file.cc index a9b9231009c144..2579c465e4d43f 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -1050,68 +1050,6 @@ static void ExistsSync(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(err == 0); } -// Used to speed up module loading. Returns an array [string, boolean] -static void InternalModuleReadJSON(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Isolate* isolate = env->isolate(); - uv_loop_t* loop = env->event_loop(); - - CHECK(args[0]->IsString()); - node::Utf8Value path(isolate, args[0]); - THROW_IF_INSUFFICIENT_PERMISSIONS( - env, permission::PermissionScope::kFileSystemRead, path.ToStringView()); - - if (strlen(*path) != path.length()) { - return; // Contains a nul byte. - } - uv_fs_t open_req; - const int fd = uv_fs_open(loop, &open_req, *path, O_RDONLY, 0, nullptr); - uv_fs_req_cleanup(&open_req); - - if (fd < 0) { - return; - } - - auto defer_close = OnScopeLeave([fd, loop]() { - uv_fs_t close_req; - CHECK_EQ(0, uv_fs_close(loop, &close_req, fd, nullptr)); - uv_fs_req_cleanup(&close_req); - }); - - const size_t kBlockSize = 32 << 10; - std::vector chars; - int64_t offset = 0; - ssize_t numchars; - do { - const size_t start = chars.size(); - chars.resize(start + kBlockSize); - - uv_buf_t buf; - buf.base = &chars[start]; - buf.len = kBlockSize; - - uv_fs_t read_req; - numchars = uv_fs_read(loop, &read_req, fd, &buf, 1, offset, nullptr); - uv_fs_req_cleanup(&read_req); - - if (numchars < 0) { - return; - } - offset += numchars; - } while (static_cast(numchars) == kBlockSize); - - size_t start = 0; - if (offset >= 3 && 0 == memcmp(chars.data(), "\xEF\xBB\xBF", 3)) { - start = 3; // Skip UTF-8 BOM. - } - const size_t size = offset - start; - - args.GetReturnValue().Set( - String::NewFromUtf8( - isolate, &chars[start], v8::NewStringType::kNormal, size) - .ToLocalChecked()); -} - // Used to speed up module loading. Returns 0 if the path refers to // a file, 1 when it's a directory or < 0 on error (usually -ENOENT.) // The speedup comes from not creating thousands of Stat and Error objects. @@ -3255,7 +3193,6 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data, SetMethod(isolate, target, "rmdir", RMDir); SetMethod(isolate, target, "mkdir", MKDir); SetMethod(isolate, target, "readdir", ReadDir); - SetMethod(isolate, target, "internalModuleReadJSON", InternalModuleReadJSON); SetMethod(isolate, target, "internalModuleStat", InternalModuleStat); SetMethod(isolate, target, "stat", Stat); SetMethod(isolate, target, "lstat", LStat); @@ -3375,7 +3312,6 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(RMDir); registry->Register(MKDir); registry->Register(ReadDir); - registry->Register(InternalModuleReadJSON); registry->Register(InternalModuleStat); registry->Register(Stat); registry->Register(LStat); diff --git a/src/node_modules.cc b/src/node_modules.cc new file mode 100644 index 00000000000000..b8c6d19dedd569 --- /dev/null +++ b/src/node_modules.cc @@ -0,0 +1,280 @@ +#include "node_modules.h" +#include "base_object-inl.h" +#include "node_errors.h" +#include "node_external_reference.h" +#include "util-inl.h" +#include "v8-fast-api-calls.h" +#include "v8.h" + +namespace node { +namespace modules { + +using errors::TryCatchScope; + +using v8::CFunction; +using v8::Context; +using v8::FastOneByteString; +using v8::FunctionCallbackInfo; +using v8::HandleScope; +using v8::Isolate; +using v8::JSON; +using v8::Local; +using v8::MaybeLocal; +using v8::NewStringType; +using v8::Object; +using v8::ObjectTemplate; +using v8::String; +using v8::Value; + +void BindingData::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("package_json_files", package_json_files_); +} + +BindingData::BindingData(Realm* realm, v8::Local object) + : SnapshotableObject(realm, object, type_int) { + package_json_files_ = + v8::Global(realm->isolate(), v8::Map::New(realm->isolate())); + object + ->Set(realm->context(), + FIXED_ONE_BYTE_STRING(realm->isolate(), "packageJsonFiles"), + package_json_files_.Get(realm->isolate())) + .Check(); + + package_json_files_.SetWeak(); +} + +bool BindingData::PrepareForSerialization(v8::Local context, + v8::SnapshotCreator* creator) { + package_json_files_.Reset(); + // Return true because we need to maintain the reference to the binding from + // JS land. + return true; +} + +InternalFieldInfoBase* BindingData::Serialize(int index) { + DCHECK_IS_SNAPSHOT_SLOT(index); + InternalFieldInfo* info = + InternalFieldInfoBase::New(type()); + return info; +} + +void BindingData::Deserialize(v8::Local context, + v8::Local holder, + int index, + InternalFieldInfoBase* info) { + DCHECK_IS_SNAPSHOT_SLOT(index); + v8::HandleScope scope(context->GetIsolate()); + Realm* realm = Realm::GetCurrent(context); + BindingData* binding = realm->AddBindingData(holder); + CHECK_NOT_NULL(binding); +} + +constexpr std::array package_json_fields = { + "name", + "main", + "exports", + "imports", + "type", +}; + +std::optional> BindingData::GetPackageJSON( + Realm* realm, std::string_view path) { + auto context = realm->context(); + auto isolate = realm->isolate(); + auto binding_data = realm->GetBindingData(); + auto package_json_files_ = binding_data->package_json_files_.Get(isolate); + + Local cache_key = ToV8Value(context, path, isolate).ToLocalChecked(); + Local existing_value; + if (package_json_files_->Get(context, cache_key).ToLocal(&existing_value) && + !existing_value->IsUndefined()) { + CHECK(existing_value->IsObject()); + return existing_value.As(); + } + + uv_fs_t open_req; + const int fd = + uv_fs_open(nullptr, &open_req, path.data(), O_RDONLY, 0, nullptr); + uv_fs_req_cleanup(&open_req); + + if (fd < 0) { + return std::nullopt; + } + + auto defer_close = OnScopeLeave([fd]() { + uv_fs_t close_req; + CHECK_EQ(0, uv_fs_close(nullptr, &close_req, fd, nullptr)); + uv_fs_req_cleanup(&close_req); + }); + + const size_t kBlockSize = 32 << 10; + std::vector chars; + int64_t offset = 0; + ssize_t numchars; + do { + const size_t start = chars.size(); + chars.resize(start + kBlockSize); + + uv_buf_t buf; + buf.base = &chars[start]; + buf.len = kBlockSize; + + uv_fs_t read_req; + numchars = uv_fs_read(nullptr, &read_req, fd, &buf, 1, offset, nullptr); + uv_fs_req_cleanup(&read_req); + + if (numchars < 0) { + return std::nullopt; + } + offset += numchars; + } while (static_cast(numchars) == kBlockSize); + + size_t start = 0; + if (offset >= 3 && 0 == memcmp(chars.data(), "\xEF\xBB\xBF", 3)) { + start = 3; // Skip UTF-8 BOM. + } + + Local json_string = + String::NewFromUtf8( + isolate, &chars[start], v8::NewStringType::kNormal, offset - start) + .ToLocalChecked(); + + TryCatchScope try_catch(realm->env()); + MaybeLocal maybe_json = JSON::Parse(context, json_string); + Local json; + if (!maybe_json.ToLocal(&json)) { + THROW_ERR_INVALID_PACKAGE_CONFIG( + isolate, "Invalid package config %s. %s", path.data()); + return std::nullopt; + } else if (try_catch.HasCaught()) { + Utf8Value message(isolate, try_catch.Message()->Get()); + + THROW_ERR_INVALID_PACKAGE_CONFIG( + isolate, "Invalid package config %s. %s.", path.data(), message.out()); + return std::nullopt; + } + + CHECK(json->IsObject()); + Local json_object = json.As(); + + auto commonjs = FIXED_ONE_BYTE_STRING(isolate, "commonjs"); + auto module = FIXED_ONE_BYTE_STRING(isolate, "module"); + auto sanitized = Object::New(isolate); + + USE(sanitized->Set(context, + String::NewFromUtf8(isolate, "exists").ToLocalChecked(), + v8::Boolean::New(isolate, true))); + USE(sanitized->Set(context, + String::NewFromUtf8(isolate, "type").ToLocalChecked(), + String::NewFromUtf8(isolate, "none").ToLocalChecked())); + + using std::string_view_literals::operator""sv; + for (const auto& field : package_json_fields) { + auto key_value = ToV8Value(context, field, isolate).ToLocalChecked(); + Local value; + if (!json_object->Get(context, key_value).ToLocal(&value)) { + continue; + } + + if (field == "name"sv || field == "main"sv) { + if (value->IsString()) { + USE(sanitized->Set(context, key_value, value)); + } + } else if (field == "type"sv) { + Local string_value; + if (value->ToString(context).ToLocal(&string_value)) { + if (string_value->StringEquals(commonjs) || + string_value->StringEquals(module)) { + USE(sanitized->Set(context, key_value, value)); + } + } + } else { + USE(sanitized->Set(context, key_value, value.As())); + } + } + + USE(package_json_files_->Set(context, cache_key, sanitized)); + + return sanitized; +} + +void BindingData::ReadPackageJSON(const FunctionCallbackInfo& args) { + CHECK_GE(args.Length(), 1); + CHECK(args[0]->IsString()); + + Realm* realm = Realm::GetCurrent(args); + Utf8Value path(realm->isolate(), args[0]); + THROW_IF_INSUFFICIENT_PERMISSIONS( + realm->env(), + permission::PermissionScope::kFileSystemRead, + path.ToStringView()); + + if (strlen(*path) != path.length()) { + return; // Contains a nul byte. + } + + auto package_json = GetPackageJSON(realm, path.ToStringView()); + if (!package_json.has_value()) { + return; + } + + args.GetReturnValue().Set(package_json.value()); +} + +void BindingData::GetPackageJSONType(const FunctionCallbackInfo& args) { + CHECK_GE(args.Length(), 1); + CHECK(args[0]->IsString()); + + Realm* realm = Realm::GetCurrent(args); + Utf8Value path(realm->isolate(), args[0]); + THROW_IF_INSUFFICIENT_PERMISSIONS( + realm->env(), + permission::PermissionScope::kFileSystemRead, + path.ToStringView()); + + if (strlen(*path) != path.length()) { + return; // Contains a nul byte. + } + + auto type_key = FIXED_ONE_BYTE_STRING(realm->isolate(), "type"); + auto package_json = GetPackageJSON(realm, path.ToStringView()); + if (!package_json.has_value()) { + return; + } + + Local type_value = + package_json.value()->Get(realm->context(), type_key).ToLocalChecked(); + CHECK(type_value->IsString()); + args.GetReturnValue().Set(type_value); +} + +void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data, + Local target) { + Isolate* isolate = isolate_data->isolate(); + SetMethod(isolate, target, "readPackageJSON", ReadPackageJSON); + SetMethod(isolate, target, "getPackageJSONType", GetPackageJSONType); +} + +void BindingData::CreatePerContextProperties(Local target, + Local unused, + Local context, + void* priv) { + Realm* realm = Realm::GetCurrent(context); + realm->AddBindingData(target); +} + +void BindingData::RegisterExternalReferences( + ExternalReferenceRegistry* registry) { + registry->Register(ReadPackageJSON); + registry->Register(GetPackageJSONType); +} + +} // namespace modules +} // namespace node + +NODE_BINDING_CONTEXT_AWARE_INTERNAL( + modules, node::modules::BindingData::CreatePerContextProperties) +NODE_BINDING_PER_ISOLATE_INIT( + modules, node::modules::BindingData::CreatePerIsolateProperties) +NODE_BINDING_EXTERNAL_REFERENCE( + modules, node::modules::BindingData::RegisterExternalReferences) diff --git a/src/node_modules.h b/src/node_modules.h new file mode 100644 index 00000000000000..2591cd7aafd125 --- /dev/null +++ b/src/node_modules.h @@ -0,0 +1,58 @@ +#ifndef SRC_NODE_MODULES_H_ +#define SRC_NODE_MODULES_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node.h" +#include "node_snapshotable.h" +#include "util.h" +#include "v8-fast-api-calls.h" +#include "v8.h" + +#include +#include +#include + +namespace node { +class ExternalReferenceRegistry; + +namespace modules { + +class BindingData : public SnapshotableObject { + public: + BindingData(Realm* realm, v8::Local obj); + + using InternalFieldInfo = InternalFieldInfoBase; + + SERIALIZABLE_OBJECT_METHODS() + SET_BINDING_ID(modules_binding_data) + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_SELF_SIZE(BindingData) + SET_MEMORY_INFO_NAME(BindingData) + + static void ReadPackageJSON(const v8::FunctionCallbackInfo& args); + static void GetPackageJSONType( + const v8::FunctionCallbackInfo& args); + + static void CreatePerIsolateProperties(IsolateData* isolate_data, + v8::Local ctor); + static void CreatePerContextProperties(v8::Local target, + v8::Local unused, + v8::Local context, + void* priv); + static void RegisterExternalReferences(ExternalReferenceRegistry* registry); + + private: + v8::Global package_json_files_; + + static std::optional> GetPackageJSON( + Realm* realm, std::string_view path); +}; + +} // namespace modules +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_MODULES_H_ diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index 562a47ddcc9c8e..71d64325765048 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -19,6 +19,7 @@ #include "node_internals.h" #include "node_main_instance.h" #include "node_metadata.h" +#include "node_modules.h" #include "node_process.h" #include "node_snapshot_builder.h" #include "node_url.h" diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 78db466a95b38e..212ef834f7d863 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -11,6 +11,7 @@ const assert = require('assert'); const expectedModules = new Set([ 'Internal Binding builtins', 'Internal Binding encoding_binding', + 'Internal Binding modules', 'Internal Binding errors', 'Internal Binding util', 'NativeModule internal/errors', diff --git a/typings/globals.d.ts b/typings/globals.d.ts index d72bf937bb75c9..39df64f7ec5bf4 100644 --- a/typings/globals.d.ts +++ b/typings/globals.d.ts @@ -14,6 +14,7 @@ import {TypesBinding} from "./internalBinding/types"; import {URLBinding} from "./internalBinding/url"; import {UtilBinding} from "./internalBinding/util"; import {WorkerBinding} from "./internalBinding/worker"; +import {ModulesBinding} from "./internalBinding/modules"; declare type TypedArray = | Uint8Array @@ -36,6 +37,7 @@ interface InternalBindingMap { fs: FsBinding; http_parser: HttpParserBinding; messaging: MessagingBinding; + modules: ModulesBinding; options: OptionsBinding; os: OSBinding; serdes: SerdesBinding; diff --git a/typings/internalBinding/fs.d.ts b/typings/internalBinding/fs.d.ts index 6bed01519fa2b0..611ccbec63d060 100644 --- a/typings/internalBinding/fs.d.ts +++ b/typings/internalBinding/fs.d.ts @@ -111,7 +111,6 @@ declare namespace InternalFSBinding { function futimes(fd: number, atime: number, mtime: number): void; function futimes(fd: number, atime: number, mtime: number, usePromises: typeof kUsePromises): Promise; - function internalModuleReadJSON(path: string): [] | [string, boolean]; function internalModuleStat(path: string): number; function lchown(path: string, uid: number, gid: number, req: FSReqCallback): void; diff --git a/typings/internalBinding/modules.d.ts b/typings/internalBinding/modules.d.ts new file mode 100644 index 00000000000000..6fa13145e8c0e7 --- /dev/null +++ b/typings/internalBinding/modules.d.ts @@ -0,0 +1,14 @@ +export type PackageType = 'commonjs' | 'module' | 'none' +export type PackageConfig = { + exists: boolean + main?: any + name?: string + type: PackageType + exports?: string | string[] | Record + imports?: string | string[] | Record +} + +export interface ModulesBinding { + readPackageJSON(path: string): PackageConfig | undefined; + getPackageJSONType(path: string): PackageType | undefined +}