Skip to content

Commit

Permalink
module: fix bad require.resolve with option paths for . and ..
Browse files Browse the repository at this point in the history
this change fixes `require.resolve` used with the `paths` option
not considering `.` and `..` as relative

Fixes: #47000
PR-URL: #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>
  • Loading branch information
dario-piotrowicz authored and aduh95 committed Jan 30, 2025
1 parent e1b0f44 commit 59510ab
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 17 deletions.
34 changes: 17 additions & 17 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.value = 'relative subdir';
43 changes: 43 additions & 0 deletions test/parallel/test-require-resolve-opts-paths-relative.js
Original file line number Diff line number Diff line change
@@ -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'));
}

0 comments on commit 59510ab

Please sign in to comment.