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: support custom paths to require.resolve() #16397

Merged
merged 1 commit into from
Oct 25, 2017
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
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
2 changes: 1 addition & 1 deletion doc/api/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -1899,7 +1899,7 @@ cases:
[`promise.catch()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
[`require()`]: globals.html#globals_require
[`require.main`]: modules.html#modules_accessing_the_main_module
[`require.resolve()`]: modules.html#modules_require_resolve
[`require.resolve()`]: modules.html#modules_require_resolve_request_options
[`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 @@ -484,12 +484,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')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something wrong here? bar.js is not present under nestedIndex so can you double-check this check?
This is what I get for bar.js and three.js:

#find . -name bar.js
./test/fixtures/node_modules/bar.js
./test/fixtures/node_modules/node_modules/bar.js

#find . -name three.js
./test/fixtures/json-with-directory-name-module/module-stub/one/two/three.js
./test/fixtures/json-with-directory-name-module/module-stub/one-trailing-slash/two/three.js
./test/fixtures/nested-index/three.js
#

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The require() machinery checks the node_modules directory in the parent, which is how it finds bar.js.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, ok - got it, thanks!

);
}

// 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'));