Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

module: move helpers out of CommonJS loader #49912

Merged
merged 2 commits into from
Sep 30, 2023
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
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