Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 52 additions & 4 deletions lib/internal/modules/package_json_reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ const {
ObjectDefineProperty,
RegExpPrototypeExec,
SafeMap,
StringPrototypeEndsWith,
StringPrototypeIndexOf,
StringPrototypeLastIndexOf,
StringPrototypeSlice,
} = primordials;
const {
Expand All @@ -26,6 +28,7 @@ const {
const { kEmptyObject } = require('internal/util');
const modulesBinding = internalBinding('modules');
const path = require('path');
const permission = require('internal/process/permission');
const { validateString } = require('internal/validators');
const internalFsBinding = internalBinding('fs');

Expand Down Expand Up @@ -127,26 +130,71 @@ function read(jsonPath, { base, specifier, isESM } = kEmptyObject) {
};
}

/**
* Given a file path, walk the filesystem upwards until we find its closest parent
* `package.json` file, stopping when:
* 1. we find a `package.json` file;
* 2. we find a path that we do not have permission to read;
* 3. we find a containing `node_modules` directory;
* 4. or, we reach the filesystem root
* @returns {undefined | string}
*/
function findParentPackageJSON(checkPath) {
const enabledPermission = permission.isEnabled();

const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, path.sep);
let separatorIndex;

do {
separatorIndex = StringPrototypeLastIndexOf(checkPath, path.sep);
checkPath = StringPrototypeSlice(checkPath, 0, separatorIndex);

if (enabledPermission && !permission.has('fs.read', checkPath + path.sep)) {
return undefined;
}

if (StringPrototypeEndsWith(checkPath, path.sep + 'node_modules')) {
return undefined;
}

const maybePackageJSONPath = checkPath + path.sep + 'package.json';
const stat = internalFsBinding.internalModuleStat(checkPath + path.sep + 'package.json');

const packageJSONExists = stat === 0;
if (packageJSONExists) {
return maybePackageJSONPath;
}
} while (separatorIndex > rootSeparatorIndex);

return undefined;
}

/**
* Get the nearest parent package.json file from a given path.
* Return the package.json data and the path to the package.json file, or undefined.
* @param {string} checkPath The path to start searching from.
* @returns {undefined | DeserializedPackageConfig}
*/
function getNearestParentPackageJSON(checkPath) {
if (nearestParentPackageJSONCache.has(checkPath)) {
return nearestParentPackageJSONCache.get(checkPath);
const nearestParentPackageJSON = findParentPackageJSON(checkPath);

if (nearestParentPackageJSON === undefined) {
return undefined;
}

if (nearestParentPackageJSONCache.has(nearestParentPackageJSON)) {
return nearestParentPackageJSONCache.get(nearestParentPackageJSON);
}

const result = modulesBinding.getNearestParentPackageJSON(checkPath);
const result = modulesBinding.readPackageJSON(nearestParentPackageJSON);

if (result === undefined) {
nearestParentPackageJSONCache.set(checkPath, undefined);
return undefined;
}

const packageConfig = deserializePackageJSON(checkPath, result);
nearestParentPackageJSONCache.set(checkPath, packageConfig);
nearestParentPackageJSONCache.set(nearestParentPackageJSON, packageConfig);

return packageConfig;
}
Expand Down
39 changes: 0 additions & 39 deletions src/node_modules.cc
Original file line number Diff line number Diff line change
Expand Up @@ -320,40 +320,6 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
return nullptr;
}

void BindingData::GetNearestParentPackageJSON(
const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK_GE(args.Length(), 1);
CHECK(args[0]->IsString());

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

ToNamespacedPath(realm->env(), &path_value);

std::string path_value_str = path_value.ToString();
if (slashCheck) {
path_value_str.push_back(kPathSeparator);
}

std::filesystem::path path;

#ifdef _WIN32
std::wstring wide_path = ConvertToWideString(path_value_str, GetACP());
path = std::filesystem::path(wide_path);
#else
path = std::filesystem::path(path_value_str);
#endif

auto package_json = TraverseParent(realm, path);

if (package_json != nullptr) {
args.GetReturnValue().Set(package_json->Serialize(realm));
}
}

void BindingData::GetNearestParentPackageJSONType(
const FunctionCallbackInfo<Value>& args) {
CHECK_GE(args.Length(), 1);
Expand Down Expand Up @@ -680,10 +646,6 @@ void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
target,
"getNearestParentPackageJSONType",
GetNearestParentPackageJSONType);
SetMethod(isolate,
target,
"getNearestParentPackageJSON",
GetNearestParentPackageJSON);
SetMethod(
isolate, target, "getPackageScopeConfig", GetPackageScopeConfig<false>);
SetMethod(isolate, target, "getPackageType", GetPackageScopeConfig<true>);
Expand Down Expand Up @@ -740,7 +702,6 @@ void BindingData::RegisterExternalReferences(
ExternalReferenceRegistry* registry) {
registry->Register(ReadPackageJSON);
registry->Register(GetNearestParentPackageJSONType);
registry->Register(GetNearestParentPackageJSON);
registry->Register(GetPackageScopeConfig<false>);
registry->Register(GetPackageScopeConfig<true>);
registry->Register(EnableCompileCache);
Expand Down
2 changes: 0 additions & 2 deletions src/node_modules.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ class BindingData : public SnapshotableObject {
SET_MEMORY_INFO_NAME(BindingData)

static void ReadPackageJSON(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetNearestParentPackageJSON(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetNearestParentPackageJSONType(
const v8::FunctionCallbackInfo<v8::Value>& args);
template <bool return_only_type>
Expand Down
1 change: 0 additions & 1 deletion typings/internalBinding/modules.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export type SerializedPackageConfig = [
export interface ModulesBinding {
readPackageJSON(path: string): SerializedPackageConfig | undefined;
getNearestParentPackageJSONType(path: string): PackageConfig['type']
getNearestParentPackageJSON(path: string): SerializedPackageConfig | undefined
getPackageScopeConfig(path: string): SerializedPackageConfig | undefined
getPackageType(path: string): PackageConfig['type'] | undefined
enableCompileCache(path?: string): { status: number, message?: string, directory?: string }
Expand Down
Loading