Skip to content

Commit 032055b

Browse files
authored
feat(node-resolve): allow preferBuiltins to be a function (#1694)
* feat: allow `options.preferBuiltins` to be function * docs: update readme * feat: add types in dts * tests: add testcase about passing function to preferBuiltins
1 parent 0af45c2 commit 032055b

File tree

5 files changed

+57
-14
lines changed

5 files changed

+57
-14
lines changed

packages/node-resolve/README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,19 @@ Specifies the properties to scan within a `package.json`, used to determine the
133133

134134
### `preferBuiltins`
135135

136-
Type: `Boolean`<br>
136+
Type: `Boolean | (module: string) => boolean`<br>
137137
Default: `true` (with warnings if a builtin module is used over a local version. Set to `true` to disable warning.)
138138

139139
If `true`, the plugin will prefer built-in modules (e.g. `fs`, `path`). If `false`, the plugin will look for locally installed modules of the same name.
140140

141+
Alternatively, you may pass in a function that returns a boolean to confirm whether the plugin should prefer built-in modules. e.g.
142+
143+
```js
144+
preferBuiltins: (module) => module !== 'punycode';
145+
```
146+
147+
will not treat `punycode` as a built-in module
148+
141149
### `modulesOnly`
142150

143151
Type: `Boolean`<br>

packages/node-resolve/src/index.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export function nodeResolve(opts = {}) {
5858
const idToPackageInfo = new Map();
5959
const mainFields = getMainFields(options);
6060
const useBrowserOverrides = mainFields.indexOf('browser') !== -1;
61-
const isPreferBuiltinsSet = options.preferBuiltins === true || options.preferBuiltins === false;
61+
const isPreferBuiltinsSet = Object.prototype.hasOwnProperty.call(options, 'preferBuiltins');
6262
const preferBuiltins = isPreferBuiltinsSet ? options.preferBuiltins : true;
6363
const rootDir = resolve(options.rootDir || process.cwd());
6464
let { dedupe } = options;
@@ -194,8 +194,10 @@ export function nodeResolve(opts = {}) {
194194
});
195195

196196
const importeeIsBuiltin = builtinModules.includes(importee.replace(nodeImportPrefix, ''));
197+
const preferImporteeIsBuiltin =
198+
typeof preferBuiltins === 'function' ? preferBuiltins(importee) : preferBuiltins;
197199
const resolved =
198-
importeeIsBuiltin && preferBuiltins
200+
importeeIsBuiltin && preferImporteeIsBuiltin
199201
? {
200202
packageInfo: undefined,
201203
hasModuleSideEffects: () => null,
@@ -230,11 +232,14 @@ export function nodeResolve(opts = {}) {
230232
idToPackageInfo.set(location, packageInfo);
231233

232234
if (hasPackageEntry) {
233-
if (importeeIsBuiltin && preferBuiltins) {
235+
if (importeeIsBuiltin && preferImporteeIsBuiltin) {
234236
if (!isPreferBuiltinsSet && resolvedWithoutBuiltins && resolved !== importee) {
235-
context.warn(
236-
`preferring built-in module '${importee}' over local alternative at '${resolvedWithoutBuiltins.location}', pass 'preferBuiltins: false' to disable this behavior or 'preferBuiltins: true' to disable this warning`
237-
);
237+
context.warn({
238+
message:
239+
`preferring built-in module '${importee}' over local alternative at '${resolvedWithoutBuiltins.location}', pass 'preferBuiltins: false' to disable this behavior or 'preferBuiltins: true' to disable this warning.` +
240+
`or passing a function to 'preferBuiltins' to provide more fine-grained control over which built-in modules to prefer.`,
241+
pluginCode: 'PREFER_BUILTINS'
242+
});
238243
}
239244
return false;
240245
} else if (jail && location.indexOf(normalize(jail.trim(sep))) !== 0) {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { sep } from 'path';
2+
import events from 'events';
3+
4+
export default { sep, events };

packages/node-resolve/test/prefer-builtins.js

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,12 @@ test('handles importing builtins', async (t) => {
3030
});
3131

3232
test('warning when preferring a builtin module, no explicit configuration', async (t) => {
33-
let warning = null;
33+
let warning = '';
3434
await rollup({
3535
input: 'prefer-builtin.js',
36-
onwarn({ message }) {
37-
// eslint-disable-next-line no-bitwise
38-
if (~message.indexOf('preferring')) {
39-
warning = message;
36+
onwarn({ message, pluginCode }) {
37+
if (pluginCode === 'PREFER_BUILTINS') {
38+
warning += message;
4039
}
4140
},
4241
plugins: [nodeResolve()]
@@ -47,7 +46,8 @@ test('warning when preferring a builtin module, no explicit configuration', asyn
4746
warning,
4847
`preferring built-in module 'events' over local alternative ` +
4948
`at '${localPath}', pass 'preferBuiltins: false' to disable this behavior ` +
50-
`or 'preferBuiltins: true' to disable this warning`
49+
`or 'preferBuiltins: true' to disable this warning.` +
50+
`or passing a function to 'preferBuiltins' to provide more fine-grained control over which built-in modules to prefer.`
5151
);
5252
});
5353

@@ -134,3 +134,27 @@ test('detects builtins imported with node: protocol', async (t) => {
134134

135135
t.is(warnings.length, 0);
136136
});
137+
138+
test('accpet passing a function to determine which builtins to prefer', async (t) => {
139+
const warnings = [];
140+
const bundle = await rollup({
141+
input: 'prefer-builtin-local-and-builtin.js',
142+
onwarn({ message }) {
143+
warnings.push(message);
144+
},
145+
plugins: [
146+
nodeResolve({
147+
preferBuiltins: (id) => id !== 'events'
148+
})
149+
]
150+
});
151+
152+
const {
153+
module: { exports }
154+
} = await testBundle(t, bundle);
155+
156+
t.is(warnings.length, 0);
157+
t.is(exports.sep, require('node:path').sep);
158+
t.not(exports.events, require('node:events'));
159+
t.is(exports.events, 'not the built-in events module');
160+
});

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,11 @@ export interface RollupNodeResolveOptions {
7979
/**
8080
* If `true`, the plugin will prefer built-in modules (e.g. `fs`, `path`). If `false`,
8181
* the plugin will look for locally installed modules of the same name.
82+
*
83+
* If a function is provided, it will be called to determine whether to prefer built-ins.
8284
* @default true
8385
*/
84-
preferBuiltins?: boolean;
86+
preferBuiltins?: boolean | ((module: string) => boolean);
8587

8688
/**
8789
* An `Array` which instructs the plugin to limit module resolution to those whose

0 commit comments

Comments
 (0)