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 <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Jordan Harband <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
  • Loading branch information
dario-piotrowicz authored Jan 25, 2025
1 parent 59b3a8b commit 7119303
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)

Check failure on line 7 in test/parallel/test-require-resolve-opts-paths-relative.js

View workflow job for this annotation

GitHub Actions / test-macOS (macos-14)

--- stderr --- /Users/runner/work/node/node/node/test/common/index.js:991 throw new Error(`Using invalid common property: '${prop}'`); ^ Error: Using invalid common property: 'isMainThread' at Object.get (/Users/runner/work/node/node/node/test/common/index.js:991:13) at Object.<anonymous> (/Users/runner/work/node/node/node/test/parallel/test-require-resolve-opts-paths-relative.js:7:13) at Module._compile (node:internal/modules/cjs/loader:1723:14) at Object..js (node:internal/modules/cjs/loader:1888:10) at Module.load (node:internal/modules/cjs/loader:1458:32) at Function._load (node:internal/modules/cjs/loader:1275:12) at TracingChannel.traceSync (node:diagnostics_channel:322:14) at wrapModuleLoad (node:internal/modules/cjs/loader:234:24) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:151:5) at node:internal/main/run_main_module:33:47 Node.js v24.0.0-pre Command: out/Release/node --test-reporter=./test/common/test-error-reporter.js --test-reporter-destination=stdout --test-reporter=./test/common/test-error-reporter.js --test-reporter-destination=stdout /Users/runner/work/node/node/node/test/parallel/test-require-resolve-opts-paths-relative.js

Check failure on line 7 in test/parallel/test-require-resolve-opts-paths-relative.js

View workflow job for this annotation

GitHub Actions / test-linux

--- stderr --- /home/runner/work/node/node/node/test/common/index.js:991 throw new Error(`Using invalid common property: '${prop}'`); ^ Error: Using invalid common property: 'isMainThread' at Object.get (/home/runner/work/node/node/node/test/common/index.js:991:13) at Object.<anonymous> (/home/runner/work/node/node/node/test/parallel/test-require-resolve-opts-paths-relative.js:7:13) at Module._compile (node:internal/modules/cjs/loader:1723:14) at Object..js (node:internal/modules/cjs/loader:1888:10) at Module.load (node:internal/modules/cjs/loader:1458:32) at Function._load (node:internal/modules/cjs/loader:1275:12) at TracingChannel.traceSync (node:diagnostics_channel:322:14) at wrapModuleLoad (node:internal/modules/cjs/loader:234:24) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:151:5) at node:internal/main/run_main_module:33:47 Node.js v24.0.0-pre Command: out/Release/node --test-reporter=./test/common/test-error-reporter.js --test-reporter-destination=stdout --test-reporter=./test/common/test-error-reporter.js --test-reporter-destination=stdout /home/runner/work/node/node/node/test/parallel/test-require-resolve-opts-paths-relative.js

Check failure on line 7 in test/parallel/test-require-resolve-opts-paths-relative.js

View workflow job for this annotation

GitHub Actions / test-macOS (macos-13)

--- stderr --- /Users/runner/work/node/node/node/test/common/index.js:991 throw new Error(`Using invalid common property: '${prop}'`); ^ Error: Using invalid common property: 'isMainThread' at Object.get (/Users/runner/work/node/node/node/test/common/index.js:991:13) at Object.<anonymous> (/Users/runner/work/node/node/node/test/parallel/test-require-resolve-opts-paths-relative.js:7:13) at Module._compile (node:internal/modules/cjs/loader:1723:14) at Object..js (node:internal/modules/cjs/loader:1888:10) at Module.load (node:internal/modules/cjs/loader:1458:32) at Function._load (node:internal/modules/cjs/loader:1275:12) at TracingChannel.traceSync (node:diagnostics_channel:322:14) at wrapModuleLoad (node:internal/modules/cjs/loader:234:24) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:151:5) at node:internal/main/run_main_module:33:47 Node.js v24.0.0-pre Command: out/Release/node --test-reporter=./test/common/test-error-reporter.js --test-reporter-destination=stdout --test-reporter=./test/common/test-error-reporter.js --test-reporter-destination=stdout /Users/runner/work/node/node/node/test/parallel/test-require-resolve-opts-paths-relative.js
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 7119303

Please sign in to comment.