Skip to content

Commit

Permalink
module: move helpers out of cjs loader
Browse files Browse the repository at this point in the history
PR-URL: nodejs#49912
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
  • Loading branch information
GeoffreyBooth authored and debadree25 committed Apr 15, 2024
1 parent 8762899 commit 2f830af
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 70 deletions.
72 changes: 6 additions & 66 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ const {
StringPrototypeCharAt,
StringPrototypeCharCodeAt,
StringPrototypeEndsWith,
StringPrototypeLastIndexOf,
StringPrototypeIndexOf,
StringPrototypeRepeat,
StringPrototypeSlice,
Expand All @@ -68,7 +67,7 @@ const cjsParseCache = new SafeWeakMap();

// Set first due to cycle with ESM loader functions.
module.exports = {
wrapSafe, Module, toRealPath, readPackageScope, cjsParseCache,
wrapSafe, Module, cjsParseCache,
get hasLoadedAnyUserCJSModule() { return hasLoadedAnyUserCJSModule; },
initializeCJS,
};
Expand All @@ -88,9 +87,7 @@ const {
const { internalCompileFunction } = require('internal/vm');
const assert = require('internal/assert');
const fs = require('fs');
const internalFS = require('internal/fs/utils');
const path = require('path');
const { sep } = path;
const { internalModuleStat } = internalBinding('fs');
const { safeGetenv } = internalBinding('credentials');
const {
Expand All @@ -106,6 +103,7 @@ const {
makeRequireFunction,
normalizeReferrerURL,
stripBOM,
toRealPath,
} = require('internal/modules/helpers');
const packageJsonReader = require('internal/modules/package_json_reader');
const { getOptionValue, getEmbedderOptions } = require('internal/options');
Expand Down Expand Up @@ -403,15 +401,7 @@ function initializeCJS() {
// -> a.<ext>
// -> a/index.<ext>

/**
* @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; },
Expand All @@ -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
Expand Down Expand Up @@ -498,14 +457,6 @@ function tryPackage(requestPath, exts, isMain, originalPath) {
return actual;
}

/**
* Cache for storing resolved real paths of modules.
* In order to minimize unnecessary lstat() calls, this cache is a list of known-real paths.
* Set to an empty Map to reset.
* @type {Map<string, string>}
*/
const realpathCache = new SafeMap();

/**
* Check if the file exists and is not a directory if using `--preserve-symlinks` and `isMain` is false, keep symlinks
* intact, otherwise resolve to the absolute realpath.
Expand All @@ -521,17 +472,6 @@ function tryFile(requestPath, isMain) {
return toRealPath(requestPath);
}


/**
* Resolves the path of a given `require` specifier, following symlinks.
* @param {string} requestPath The `require` specifier
*/
function toRealPath(requestPath) {
return fs.realpathSync(requestPath, {
[internalFS.realpathCacheKey]: realpathCache,
});
}

/**
* Given a path, check if the file exists with any of the set extensions.
* @param {string} basePath The path and filename without extension
Expand Down Expand Up @@ -593,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;
}
Expand Down Expand Up @@ -1153,7 +1093,7 @@ Module._resolveFilename = function(request, parent, isMain, options) {

if (request[0] === '#' && (parent?.filename || parent?.id === '<repl>')) {
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');
Expand Down Expand Up @@ -1450,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);
Expand Down
20 changes: 20 additions & 0 deletions lib/internal/modules/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const {
const { BuiltinModule } = require('internal/bootstrap/realm');

const { validateString } = require('internal/validators');
const fs = require('fs'); // Import all of `fs` so that it can be monkey-patched.
const internalFS = require('internal/fs/utils');
const path = require('path');
const { pathToFileURL, fileURLToPath, URL } = require('internal/url');

Expand All @@ -39,6 +41,23 @@ let debug = require('internal/util/debuglog').debuglog('module', (fn) => {

/** @typedef {import('internal/modules/cjs/loader.js').Module} Module */

/**
* Cache for storing resolved real paths of modules.
* In order to minimize unnecessary lstat() calls, this cache is a list of known-real paths.
* Set to an empty Map to reset.
* @type {Map<string, string>}
*/
const realpathCache = new SafeMap();
/**
* Resolves the path of a given `require` specifier, following symlinks.
* @param {string} requestPath The `require` specifier
*/
function toRealPath(requestPath) {
return fs.realpathSync(requestPath, {
[internalFS.realpathCacheKey]: realpathCache,
});
}

/** @type {Set<string>} */
let cjsConditions;
/**
Expand Down Expand Up @@ -310,4 +329,5 @@ module.exports = {
makeRequireFunction,
normalizeReferrerURL,
stripBOM,
toRealPath,
};
52 changes: 50 additions & 2 deletions lib/internal/modules/package_json_reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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,
};
5 changes: 3 additions & 2 deletions lib/internal/modules/run_main.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ function resolveMainPath(main) {
// Note extension resolution for the main entry point can be deprecated in a
// future major.
// Module._findPath is monkey-patchable here.
const { Module, toRealPath } = require('internal/modules/cjs/loader');
const { Module } = require('internal/modules/cjs/loader');
let mainPath = Module._findPath(path.resolve(main), null, true);
if (!mainPath) { return; }

const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
if (!preserveSymlinksMain) {
const { toRealPath } = require('internal/modules/helpers');
mainPath = toRealPath(mainPath);
}

Expand Down Expand Up @@ -48,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';
Expand Down

0 comments on commit 2f830af

Please sign in to comment.