@@ -116,6 +116,38 @@ function isBareSpecifier (specifier) {
116
116
}
117
117
}
118
118
119
+ function isBareSpecifierOrFileUrl ( input ) {
120
+ // Relative and absolute paths
121
+ if (
122
+ input . startsWith ( '.' ) ||
123
+ input . startsWith ( '/' ) ) {
124
+ return false
125
+ }
126
+
127
+ try {
128
+ // eslint-disable-next-line no-new
129
+ const url = new URL ( input )
130
+ return url . protocol === 'file:'
131
+ } catch ( err ) {
132
+ // Anything that fails parsing is a bare specifier
133
+ return true
134
+ }
135
+ }
136
+
137
+ function ensureArrayWithBareSpecifiersAndFileUrls ( array , type ) {
138
+ if ( ! Array . isArray ( array ) ) {
139
+ return undefined
140
+ }
141
+
142
+ const invalid = array . filter ( s => ! isBareSpecifierOrFileUrl ( s ) )
143
+
144
+ if ( invalid . length ) {
145
+ throw new Error ( `'${ type } ' option only supports bare specifiers and file URLs. Invalid entries: ${ inspect ( invalid ) } ` )
146
+ }
147
+
148
+ return array
149
+ }
150
+
119
151
function emitWarning ( err ) {
120
152
// Unfortunately, process.emitWarning does not output the full error
121
153
// with error.cause like console.warn does so we need to inspect it when
@@ -217,6 +249,14 @@ function addIitm (url) {
217
249
function createHook ( meta ) {
218
250
let cachedResolve
219
251
const iitmURL = new URL ( 'lib/register.js' , meta . url ) . toString ( )
252
+ let includeModules , excludeModules
253
+
254
+ async function initialize ( data ) {
255
+ if ( data ) {
256
+ includeModules = ensureArrayWithBareSpecifiersAndFileUrls ( data . include , 'include' )
257
+ excludeModules = ensureArrayWithBareSpecifiersAndFileUrls ( data . exclude , 'exclude' )
258
+ }
259
+ }
220
260
221
261
async function resolve ( specifier , context , parentResolve ) {
222
262
cachedResolve = parentResolve
@@ -234,39 +274,52 @@ function createHook (meta) {
234
274
if ( isWin && parentURL . indexOf ( 'file:node' ) === 0 ) {
235
275
context . parentURL = ''
236
276
}
237
- const url = await parentResolve ( newSpecifier , context , parentResolve )
238
- if ( parentURL === '' && ! EXTENSION_RE . test ( url . url ) ) {
239
- entrypoint = url . url
240
- return { url : url . url , format : 'commonjs' }
277
+ const result = await parentResolve ( newSpecifier , context , parentResolve )
278
+ if ( parentURL === '' && ! EXTENSION_RE . test ( result . url ) ) {
279
+ entrypoint = result . url
280
+ return { url : result . url , format : 'commonjs' }
281
+ }
282
+
283
+ // For included/excluded modules, we check the specifier to match libraries
284
+ // that are loaded with bare specifiers from node_modules.
285
+ //
286
+ // For non-bare specifier imports, we only support matching file URL strings
287
+ // because using relative paths would be very error prone!
288
+ if ( includeModules && ! includeModules . some ( lib => lib === specifier || lib === result . url . url ) ) {
289
+ return result
290
+ }
291
+
292
+ if ( excludeModules && excludeModules . some ( lib => lib === specifier || lib === result . url . url ) ) {
293
+ return result
241
294
}
242
295
243
296
if ( isIitm ( parentURL , meta ) || hasIitm ( parentURL ) ) {
244
- return url
297
+ return result
245
298
}
246
299
247
300
// Node.js v21 renames importAssertions to importAttributes
248
301
if (
249
302
( context . importAssertions && context . importAssertions . type === 'json' ) ||
250
303
( context . importAttributes && context . importAttributes . type === 'json' )
251
304
) {
252
- return url
305
+ return result
253
306
}
254
307
255
308
// If the file is referencing itself, we need to skip adding the iitm search params
256
- if ( url . url === parentURL ) {
309
+ if ( result . url === parentURL ) {
257
310
return {
258
- url : url . url ,
311
+ url : result . url ,
259
312
shortCircuit : true ,
260
- format : url . format
313
+ format : result . format
261
314
}
262
315
}
263
316
264
- specifiers . set ( url . url , specifier )
317
+ specifiers . set ( result . url , specifier )
265
318
266
319
return {
267
- url : addIitm ( url . url ) ,
320
+ url : addIitm ( result . url ) ,
268
321
shortCircuit : true ,
269
- format : url . format
322
+ format : result . format
270
323
}
271
324
}
272
325
@@ -337,9 +390,10 @@ register(${JSON.stringify(realUrl)}, _, set, ${JSON.stringify(specifiers.get(rea
337
390
}
338
391
339
392
if ( NODE_MAJOR >= 17 || ( NODE_MAJOR === 16 && NODE_MINOR >= 12 ) ) {
340
- return { load, resolve }
393
+ return { initialize , load, resolve }
341
394
} else {
342
395
return {
396
+ initialize,
343
397
load,
344
398
resolve,
345
399
getSource,
0 commit comments