Skip to content

Commit 12d87a4

Browse files
ajhyndmanAndrew Hyndmantjenkinsonshellscape
authored
feat(node-resolve): add new option, modulePaths (#1104)
* [node-resolve] Implement new modulePaths option * Add unit tests distinguishing modulePaths from moduleDirectories * Document new option * Update packages/node-resolve/test/test.js Co-authored-by: Tom Jenkinson <tjenkinson@users.noreply.github.com> * Update packages/node-resolve/README.md Co-authored-by: Tom Jenkinson <tjenkinson@users.noreply.github.com> * fixup! Update packages/node-resolve/test/test.js * Reject moduleDirectories containing a slash * chore: arbitrary edit to kick github's actions ui Co-authored-by: Andrew Hyndman <ahyndman@dropbox.com> Co-authored-by: Tom Jenkinson <tjenkinson@users.noreply.github.com> Co-authored-by: Andrew Powell <shellscape@users.noreply.github.com>
1 parent a43a6d6 commit 12d87a4

File tree

10 files changed

+77
-6
lines changed

10 files changed

+77
-6
lines changed

packages/node-resolve/README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,14 @@ If `true`, instructs the plugin to use the browser module resolutions in `packag
7575
Type: `Array[...String]`<br>
7676
Default: `['node_modules']`
7777

78-
One or more directories in which to recursively look for modules.
78+
A list of directory names in which to recursively look for modules.
79+
80+
### `modulePaths`
81+
82+
Type: `Array[...String]`<br>
83+
Default: `[]`
84+
85+
A list of absolute paths to additional locations to search for modules. [This is analogous to setting the `NODE_PATH` environment variable for node](https://nodejs.org/api/modules.html#loading-from-the-global-folders).
7986

8087
### `dedupe`
8188

@@ -214,7 +221,7 @@ export default ({
214221
})
215222
```
216223

217-
## Resolving require statements
224+
## Resolving Require Statements
218225

219226
According to [NodeJS module resolution](https://nodejs.org/api/packages.html#packages_package_entry_points) `require` statements should resolve using the `require` condition in the package exports field, while es modules should use the `import` condition.
220227

packages/node-resolve/src/index.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export function nodeResolve(opts = {}) {
4444
const { warnings } = handleDeprecatedOptions(opts);
4545

4646
const options = { ...defaults, ...opts };
47-
const { extensions, jail, moduleDirectories, ignoreSideEffectsForRoot } = options;
47+
const { extensions, jail, moduleDirectories, modulePaths, ignoreSideEffectsForRoot } = options;
4848
const conditionsEsm = [...baseConditionsEsm, ...(options.exportConditions || [])];
4949
const conditionsCjs = [...baseConditionsCjs, ...(options.exportConditions || [])];
5050
const packageInfoCache = new Map();
@@ -57,6 +57,12 @@ export function nodeResolve(opts = {}) {
5757
let { dedupe } = options;
5858
let rollupOptions;
5959

60+
if (moduleDirectories.some((name) => name.includes('/'))) {
61+
throw new Error(
62+
'`moduleDirectories` option must only contain directory names. If you want to load modules from somewhere not supported by the default module resolution algorithm, see `modulePaths`.'
63+
);
64+
}
65+
6066
if (typeof dedupe !== 'function') {
6167
dedupe = (importee) =>
6268
options.dedupe.includes(importee) || options.dedupe.includes(getPackageName(importee));
@@ -167,6 +173,7 @@ export function nodeResolve(opts = {}) {
167173
useBrowserOverrides,
168174
baseDir,
169175
moduleDirectories,
176+
modulePaths,
170177
rootDir,
171178
ignoreSideEffectsForRoot
172179
});

packages/node-resolve/src/resolveImportSpecifiers.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ async function resolveIdClassic({
4343
useBrowserOverrides,
4444
baseDir,
4545
moduleDirectories,
46+
modulePaths,
4647
rootDir,
4748
ignoreSideEffectsForRoot
4849
}) {
@@ -77,6 +78,7 @@ async function resolveIdClassic({
7778
extensions,
7879
includeCoreModules: false,
7980
moduleDirectory: moduleDirectories,
81+
paths: modulePaths,
8082
preserveSymlinks,
8183
packageFilter: filter
8284
};
@@ -111,6 +113,7 @@ async function resolveWithExportMap({
111113
useBrowserOverrides,
112114
baseDir,
113115
moduleDirectories,
116+
modulePaths,
114117
rootDir,
115118
ignoreSideEffectsForRoot
116119
}) {
@@ -130,7 +133,8 @@ async function resolveWithExportMap({
130133
preserveSymlinks,
131134
useBrowserOverrides,
132135
baseDir,
133-
moduleDirectories
136+
moduleDirectories,
137+
modulePaths
134138
});
135139
}
136140
});
@@ -180,6 +184,7 @@ async function resolveWithExportMap({
180184
extensions,
181185
includeCoreModules: false,
182186
moduleDirectory: moduleDirectories,
187+
paths: modulePaths,
183188
preserveSymlinks,
184189
packageFilter: filter
185190
};
@@ -230,6 +235,7 @@ async function resolveWithClassic({
230235
useBrowserOverrides,
231236
baseDir,
232237
moduleDirectories,
238+
modulePaths,
233239
rootDir,
234240
ignoreSideEffectsForRoot
235241
}) {
@@ -247,6 +253,7 @@ async function resolveWithClassic({
247253
useBrowserOverrides,
248254
baseDir,
249255
moduleDirectories,
256+
modulePaths,
250257
rootDir,
251258
ignoreSideEffectsForRoot
252259
});
@@ -275,6 +282,7 @@ export default async function resolveImportSpecifiers({
275282
useBrowserOverrides,
276283
baseDir,
277284
moduleDirectories,
285+
modulePaths,
278286
rootDir,
279287
ignoreSideEffectsForRoot
280288
}) {
@@ -290,6 +298,7 @@ export default async function resolveImportSpecifiers({
290298
useBrowserOverrides,
291299
baseDir,
292300
moduleDirectories,
301+
modulePaths,
293302
rootDir,
294303
ignoreSideEffectsForRoot
295304
});
@@ -315,6 +324,7 @@ export default async function resolveImportSpecifiers({
315324
useBrowserOverrides,
316325
baseDir,
317326
moduleDirectories,
327+
modulePaths,
318328
rootDir,
319329
ignoreSideEffectsForRoot
320330
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import PACKAGE, {dependency} from 'package-with-dependency';
2+
3+
export { PACKAGE, dependency };

packages/node-resolve/test/fixtures/custom-module-path/node_modules/package-with-dependency/main.js

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/node-resolve/test/fixtures/custom-module-path/node_modules/package-with-dependency/node_modules/dependency/main.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/node-resolve/test/fixtures/custom-module-path/node_modules/package-with-dependency/node_modules/dependency/package.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/node-resolve/test/fixtures/custom-module-path/node_modules/package-with-dependency/package.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/node-resolve/test/test.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { rollup } from 'rollup';
88

99
import { nodeResolve } from '..';
1010

11-
import { getCode, getImports, testBundle } from '../../../util/test';
11+
import { evaluateBundle, getCode, getImports, testBundle } from '../../../util/test';
1212

1313
process.chdir(join(__dirname, 'fixtures'));
1414

@@ -273,6 +273,33 @@ test('allows custom moduleDirectories with legacy customResolveOptions.moduleDir
273273
t.snapshot(warnings);
274274
});
275275

276+
test('moduleDirectories option rejects paths that contain a slash', async (t) => {
277+
t.throws(
278+
() =>
279+
nodeResolve({
280+
moduleDirectories: ['some/path']
281+
}),
282+
{
283+
message: /must only contain directory names/
284+
}
285+
);
286+
});
287+
288+
test('allows custom modulePaths', async (t) => {
289+
const bundle = await rollup({
290+
input: 'custom-module-path/main.js',
291+
onwarn: failOnWarn(t),
292+
plugins: [
293+
nodeResolve({
294+
modulePaths: [join(process.cwd(), 'custom-module-path/node_modules')]
295+
})
296+
]
297+
});
298+
299+
const { dependency } = await evaluateBundle(bundle);
300+
t.is(dependency, 'DEPENDENCY');
301+
});
302+
276303
test('ignores deep-import non-modules', async (t) => {
277304
const warnings = [];
278305
const bundle = await rollup({

packages/node-resolve/types/index.d.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,18 @@ export interface RollupNodeResolveOptions {
3131
browser?: boolean;
3232

3333
/**
34-
* One or more directories in which to recursively look for modules.
34+
* A list of directory names in which to recursively look for modules.
3535
* @default ['node_modules']
3636
*/
3737
moduleDirectories?: string[];
3838

39+
/**
40+
* A list of absolute paths to additional locations to search for modules.
41+
* This is analogous to setting the `NODE_PATH` environment variable for node.
42+
* @default []
43+
*/
44+
modulePaths?: string[];
45+
3946
/**
4047
* An `Array` of modules names, which instructs the plugin to force resolving for the
4148
* specified modules to the root `node_modules`. Helps to prevent bundling the same

0 commit comments

Comments
 (0)