From 3ea0b324e38fa1840d9611dd4f50ae66a7dca437 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Wed, 27 Sep 2023 15:46:51 -0700 Subject: [PATCH] readPackage and readPackageScope --- lib/internal/modules/cjs/loader.js | 51 ++------------------ lib/internal/modules/package_json_reader.js | 52 ++++++++++++++++++++- lib/internal/modules/run_main.js | 2 +- 3 files changed, 56 insertions(+), 49 deletions(-) diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 3fb223ba0a7e5e..44c3f4c31fb352 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -55,7 +55,6 @@ const { StringPrototypeCharAt, StringPrototypeCharCodeAt, StringPrototypeEndsWith, - StringPrototypeLastIndexOf, StringPrototypeIndexOf, StringPrototypeRepeat, StringPrototypeSlice, @@ -68,7 +67,7 @@ const cjsParseCache = new SafeWeakMap(); // Set first due to cycle with ESM loader functions. module.exports = { - wrapSafe, Module, readPackageScope, cjsParseCache, + wrapSafe, Module, cjsParseCache, get hasLoadedAnyUserCJSModule() { return hasLoadedAnyUserCJSModule; }, initializeCJS, }; @@ -89,7 +88,6 @@ const { internalCompileFunction } = require('internal/vm'); const assert = require('internal/assert'); const fs = require('fs'); const path = require('path'); -const { sep } = path; const { internalModuleStat } = internalBinding('fs'); const { safeGetenv } = internalBinding('credentials'); const { @@ -403,15 +401,7 @@ function initializeCJS() { // -> a. // -> a/index. -/** - * @param {string} requestPath - * @return {PackageConfig} - */ -function readPackage(requestPath) { - return packageJsonReader.read(path.resolve(requestPath, 'package.json')); -} - -let _readPackage = readPackage; +let _readPackage = packageJsonReader.readPackage; ObjectDefineProperty(Module, '_readPackage', { __proto__: null, get() { return _readPackage; }, @@ -423,37 +413,6 @@ ObjectDefineProperty(Module, '_readPackage', { configurable: true, }); -/** - * 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 false. - * @param {string} checkPath The path to start searching from. - */ -function readPackageScope(checkPath) { - const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, sep); - let separatorIndex; - const enabledPermission = permission.isEnabled(); - do { - separatorIndex = StringPrototypeLastIndexOf(checkPath, sep); - checkPath = StringPrototypeSlice(checkPath, 0, separatorIndex); - // Stop the search when the process doesn't have permissions - // to walk upwards - if (enabledPermission && !permission.has('fs.read', checkPath + sep)) { - return false; - } - if (StringPrototypeEndsWith(checkPath, sep + 'node_modules')) { - return false; - } - const pjson = _readPackage(checkPath + sep); - if (pjson.exists) { - return { - data: pjson, - path: checkPath, - }; - } - } while (separatorIndex > rootSeparatorIndex); - return false; -} - /** * Try to load a specifier as a package. * @param {string} requestPath The path to what we are trying to load @@ -574,7 +533,7 @@ function trySelfParentPath(parent) { function trySelf(parentPath, request) { if (!parentPath) { return false; } - const { data: pkg, path: pkgPath } = readPackageScope(parentPath); + const { data: pkg, path: pkgPath } = packageJsonReader.readPackageScope(parentPath); if (!pkg || pkg.exports == null || pkg.name === undefined) { return false; } @@ -1134,7 +1093,7 @@ Module._resolveFilename = function(request, parent, isMain, options) { if (request[0] === '#' && (parent?.filename || parent?.id === '')) { const parentPath = parent?.filename ?? process.cwd() + path.sep; - const pkg = readPackageScope(parentPath) || { __proto__: null }; + const pkg = packageJsonReader.readPackageScope(parentPath) || { __proto__: null }; if (pkg.data?.imports != null) { try { const { packageImportsResolve } = require('internal/modules/esm/resolve'); @@ -1431,7 +1390,7 @@ Module._extensions['.js'] = function(module, filename) { content = fs.readFileSync(filename, 'utf8'); } if (StringPrototypeEndsWith(filename, '.js')) { - const pkg = readPackageScope(filename) || { __proto__: null }; + const pkg = packageJsonReader.readPackageScope(filename) || { __proto__: null }; // Function require shouldn't be used in ES modules. if (pkg.data?.type === 'module') { const parent = moduleParentCache.get(module); diff --git a/lib/internal/modules/package_json_reader.js b/lib/internal/modules/package_json_reader.js index c4afd159ebdb63..65f5ce3551bbd0 100644 --- a/lib/internal/modules/package_json_reader.js +++ b/lib/internal/modules/package_json_reader.js @@ -4,12 +4,17 @@ const { JSONParse, ObjectPrototypeHasOwnProperty, SafeMap, + StringPrototypeEndsWith, + StringPrototypeIndexOf, + StringPrototypeLastIndexOf, + StringPrototypeSlice, } = primordials; const { ERR_INVALID_PACKAGE_CONFIG, } = require('internal/errors').codes; const { internalModuleReadJSON } = internalBinding('fs'); -const { toNamespacedPath } = require('path'); +const { resolve, sep, toNamespacedPath } = require('path'); +const permission = require('internal/process/permission'); const { kEmptyObject, setOwnProperty } = require('internal/util'); const { fileURLToPath, pathToFileURL } = require('internal/url'); @@ -111,4 +116,47 @@ function read(jsonPath, { base, specifier, isESM } = kEmptyObject) { return result; } -module.exports = { read }; +/** + * @param {string} requestPath + * @return {PackageConfig} + */ +function readPackage(requestPath) { + return read(resolve(requestPath, 'package.json')); +} + +/** + * 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 false. + * @param {string} checkPath The path to start searching from. + */ +function readPackageScope(checkPath) { + const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, sep); + let separatorIndex; + const enabledPermission = permission.isEnabled(); + do { + separatorIndex = StringPrototypeLastIndexOf(checkPath, sep); + checkPath = StringPrototypeSlice(checkPath, 0, separatorIndex); + // Stop the search when the process doesn't have permissions + // to walk upwards + if (enabledPermission && !permission.has('fs.read', checkPath + sep)) { + return false; + } + if (StringPrototypeEndsWith(checkPath, sep + 'node_modules')) { + return false; + } + const pjson = readPackage(checkPath + sep); + if (pjson.exists) { + return { + data: pjson, + path: checkPath, + }; + } + } while (separatorIndex > rootSeparatorIndex); + return false; +} + +module.exports = { + read, + readPackage, + readPackageScope, +}; diff --git a/lib/internal/modules/run_main.js b/lib/internal/modules/run_main.js index 01829f4e34092f..e5969bf7b75ea6 100644 --- a/lib/internal/modules/run_main.js +++ b/lib/internal/modules/run_main.js @@ -49,7 +49,7 @@ function shouldUseESMLoader(mainPath) { if (mainPath && StringPrototypeEndsWith(mainPath, '.mjs')) { return true; } if (!mainPath || StringPrototypeEndsWith(mainPath, '.cjs')) { return false; } - const { readPackageScope } = require('internal/modules/cjs/loader'); + 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`. return pkg.data?.type === 'module' || getOptionValue('--experimental-default-type') === 'module';