Skip to content

Commit

Permalink
module: support custom paths to require.resolve()
Browse files Browse the repository at this point in the history
This commit allows custom lookup paths to be passed to
require.resolve(). It also adds require.resolve.paths()
which retrieves the default resolution paths.

Fixes: #5963
Fixes: #16389
PR-URL: #16397
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Wyatt Preul <wpreul@gmail.com>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
  • Loading branch information
cjihrig authored and gibfahn committed Oct 31, 2017
1 parent 9bca620 commit ac02a0b
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 7 deletions.
24 changes: 23 additions & 1 deletion doc/api/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -598,14 +598,36 @@ filename scales linearly with the number of registered extensions.
In other words, adding extensions slows down the module loader and
should be discouraged.

#### require.resolve()
#### require.resolve(request[, options])
<!-- YAML
added: v0.3.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/16397
description: The `paths` option is now supported.
-->

* `request` {string} The module path to resolve.
* `options` {Object}
* `paths` {Array} Paths to resolve module location from. If present, these
paths are used instead of the default resolution paths. Note that each of
these paths is used as a starting point for the module resolution algorithm,
meaning that the `node_modules` hierarchy is checked from this location.
* Returns: {string}

Use the internal `require()` machinery to look up the location of a module,
but rather than loading the module, just return the resolved filename.

#### require.resolve.paths(request)
<!-- YAML
added: REPLACEME
-->

* `request` {string} The module path whose lookup paths are being retrieved.
* Returns: {Array}

Returns an array containing the paths searched during resolution of `request`.

## The `module` Object
<!-- YAML
added: v0.1.16
Expand Down
1 change: 0 additions & 1 deletion doc/api/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -1850,7 +1850,6 @@ cases:
[`process.kill()`]: #process_process_kill_pid_signal
[`promise.catch()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
[`require.main`]: modules.html#modules_accessing_the_main_module
[`require.resolve()`]: modules.html#modules_require_resolve
[`setTimeout(fn, 0)`]: timers.html#timers_settimeout_callback_delay_args
[Child Process]: child_process.html
[Cluster]: cluster.html
Expand Down
10 changes: 8 additions & 2 deletions lib/internal/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ function makeRequireFunction(mod) {
}
}

function resolve(request) {
return Module._resolveFilename(request, mod);
function resolve(request, options) {
return Module._resolveFilename(request, mod, false, options);
}

require.resolve = resolve;

function paths(request) {
return Module._resolveLookupPaths(request, mod, true);
}

resolve.paths = paths;

require.main = process.mainModule;

// Enable support to add extra extension types.
Expand Down
24 changes: 22 additions & 2 deletions lib/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -500,12 +500,32 @@ function tryModuleLoad(module, filename) {
}
}

Module._resolveFilename = function(request, parent, isMain) {
Module._resolveFilename = function(request, parent, isMain, options) {
if (NativeModule.nonInternalExists(request)) {
return request;
}

var paths = Module._resolveLookupPaths(request, parent, true);
var paths;

if (typeof options === 'object' && options !== null &&
Array.isArray(options.paths)) {
paths = [];

for (var i = 0; i < options.paths.length; i++) {
const path = options.paths[i];
const lookupPaths = Module._resolveLookupPaths(path, parent, true);

if (!paths.includes(path))
paths.push(path);

for (var j = 0; j < lookupPaths.length; j++) {
if (!paths.includes(lookupPaths[j]))
paths.push(lookupPaths[j]);
}
}
} else {
paths = Module._resolveLookupPaths(request, parent, true);
}

// look up the filename first, since that's the cache key.
var filename = Module._findPath(request, paths, isMain);
Expand Down
55 changes: 55 additions & 0 deletions test/fixtures/require-resolve.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';
require('../common');
const assert = require('assert');
const path = require('path');
const nodeModules = path.join(__dirname, 'node_modules');
const nestedNodeModules = path.join(__dirname, 'node_modules', 'node_modules');
const nestedIndex = path.join(__dirname, 'nested-index');

// Test the default behavior.
assert.strictEqual(
require.resolve('bar'),
path.join(nodeModules, 'bar.js')
);

// Verify that existing paths are removed.
assert.throws(() => {
require.resolve('bar', { paths: [] })
}, /^Error: Cannot find module 'bar'$/);

// Verify that resolution path can be overwritten.
{
// three.js cannot be loaded from this file by default.
assert.throws(() => {
require.resolve('three')
}, /^Error: Cannot find module 'three'$/);

// However, it can be found if resolution contains the nested index directory.
assert.strictEqual(
require.resolve('three', { paths: [nestedIndex] }),
path.join(nestedIndex, 'three.js')
);

// Resolution from nested index directory also checks node_modules.
assert.strictEqual(
require.resolve('bar', { paths: [nestedIndex] }),
path.join(nodeModules, 'bar.js')
);
}

// Verify that the default paths can be used and modified.
{
const paths = require.resolve.paths('bar');

assert.strictEqual(paths[0], nodeModules);
assert.strictEqual(
require.resolve('bar', { paths }),
path.join(nodeModules, 'bar.js')
);

paths.unshift(nestedNodeModules);
assert.strictEqual(
require.resolve('bar', { paths }),
path.join(nestedNodeModules, 'bar.js')
);
}
3 changes: 2 additions & 1 deletion test/parallel/test-require-resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ assert.strictEqual(
require.resolve(fixtures.path('nested-index', 'one')).toLowerCase());
assert.strictEqual('path', require.resolve('path'));

console.log('ok');
// Test configurable resolve() paths.
require(fixtures.path('require-resolve.js'));

0 comments on commit ac02a0b

Please sign in to comment.