module: fix bad require.resolve with option paths for . and ..

this change fixes `require.resolve` used with the `paths` option
not considering `.` and `..` as relative

Fixes: https://github.com/nodejs/node/issues/47000
PR-URL: https://github.com/nodejs/node/pull/56735
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Jordan Harband <ljharb@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
Dario Piotrowicz 2025-01-25 23:32:13 +00:00 committed by GitHub
parent 59b3a8b21b
commit 7119303a81
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 17 deletions

View file

@ -722,18 +722,8 @@ Module._findPath = function(request, paths, isMain, conditions = getCjsCondition
)
));
const isRelative = StringPrototypeCharCodeAt(request, 0) === CHAR_DOT &&
(
request.length === 1 ||
StringPrototypeCharCodeAt(request, 1) === CHAR_FORWARD_SLASH ||
(isWindows && StringPrototypeCharCodeAt(request, 1) === CHAR_BACKWARD_SLASH) ||
(StringPrototypeCharCodeAt(request, 1) === CHAR_DOT && ((
request.length === 2 ||
StringPrototypeCharCodeAt(request, 2) === CHAR_FORWARD_SLASH) ||
(isWindows && StringPrototypeCharCodeAt(request, 2) === CHAR_BACKWARD_SLASH)))
);
let insidePath = true;
if (isRelative) {
if (isRelative(request)) {
const normalizedRequest = path.normalize(request);
if (StringPrototypeStartsWith(normalizedRequest, '..')) {
insidePath = false;
@ -1328,12 +1318,7 @@ Module._resolveFilename = function(request, parent, isMain, options) {
if (typeof options === 'object' && options !== null) {
if (ArrayIsArray(options.paths)) {
const isRelative = StringPrototypeStartsWith(request, './') ||
StringPrototypeStartsWith(request, '../') ||
((isWindows && StringPrototypeStartsWith(request, '.\\')) ||
StringPrototypeStartsWith(request, '..\\'));
if (isRelative) {
if (isRelative(request)) {
paths = options.paths;
} else {
const fakeParent = new Module('', null);
@ -1978,6 +1963,21 @@ function createRequire(filename) {
return createRequireFromPath(filepath);
}
/**
* Checks if a path is relative
* @param {string} path the target path
* @returns {boolean} true if the path is relative, false otherwise
*/
function isRelative(path) {
if (StringPrototypeCharCodeAt(path, 0) !== CHAR_DOT) { return false; }
return path.length === 1 || path === '..' ||
StringPrototypeStartsWith(path, './') ||
StringPrototypeStartsWith(path, '../') ||
((isWindows && StringPrototypeStartsWith(path, '.\\')) ||
StringPrototypeStartsWith(path, '..\\'));
}
Module.createRequire = createRequire;
/**

View file

@ -0,0 +1 @@
exports.value = 'relative subdir';

View file

@ -0,0 +1,43 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const fixtures = require('../common/fixtures');
if (!common.isMainThread)
common.skip('process.chdir is not available in Workers');
const subdir = fixtures.path('module-require', 'relative', 'subdir');
process.chdir(subdir);
// Parent directory paths (`..`) work as intended
{
assert(require.resolve('.', { paths: ['../'] }).endsWith('index.js'));
assert(require.resolve('./index.js', { paths: ['../'] }).endsWith('index.js'));
// paths: [".."] should resolve like paths: ["../"]
assert(require.resolve('.', { paths: ['..'] }).endsWith('index.js'));
assert(require.resolve('./index.js', { paths: ['..'] }).endsWith('index.js'));
}
process.chdir('..');
// Current directory paths (`.`) work as intended
{
assert(require.resolve('.', { paths: ['.'] }).endsWith('index.js'));
assert(require.resolve('./index.js', { paths: ['./'] }).endsWith('index.js'));
// paths: ["."] should resolve like paths: ["../"]
assert(require.resolve('.', { paths: ['.'] }).endsWith('index.js'));
assert(require.resolve('./index.js', { paths: ['.'] }).endsWith('index.js'));
}
// Sub directory paths work as intended
{
// assert.deepStrictEqual(fs.readdirSync('./subdir'), [5]);
assert(require.resolve('./relative-subdir.js', { paths: ['./subdir'] }).endsWith('relative-subdir.js'));
// paths: ["subdir"] should resolve like paths: ["./subdir"]
assert(require.resolve('./relative-subdir.js', { paths: ['subdir'] }).endsWith('relative-subdir.js'));
}