diff --git a/README.md b/README.md index c746c63..5595677 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,9 @@ node --import=./my-loader.mjs ./my-code.mjs ``` When registering the loader hook programmatically, it's possible to pass a list -of modules or file URLs to either exclude or specifically include which modules -are intercepted. This is useful if a module is not compatible with the loader -hook. +of modules, file URLs or regular expressions to either exclude or specifically +include which modules are intercepted. This is useful if a module is not +compatible with the loader hook. ```js import * as module from 'module' diff --git a/hook.js b/hook.js index 296e90d..80a3032 100644 --- a/hook.js +++ b/hook.js @@ -116,7 +116,11 @@ function isBareSpecifier (specifier) { } } -function isBareSpecifierOrFileUrl (input) { +function isBareSpecifierFileUrlOrRegex (input) { + if (input instanceof RegExp) { + return true + } + // Relative and absolute paths if ( input.startsWith('.') || @@ -134,15 +138,15 @@ function isBareSpecifierOrFileUrl (input) { } } -function ensureArrayWithBareSpecifiersAndFileUrls (array, type) { +function ensureArrayWithBareSpecifiersFileUrlsAndRegex (array, type) { if (!Array.isArray(array)) { return undefined } - const invalid = array.filter(s => !isBareSpecifierOrFileUrl(s)) + const invalid = array.filter(s => !isBareSpecifierFileUrlOrRegex(s)) if (invalid.length) { - throw new Error(`'${type}' option only supports bare specifiers and file URLs. Invalid entries: ${inspect(invalid)}`) + throw new Error(`'${type}' option only supports bare specifiers, file URLs or regular expressions. Invalid entries: ${inspect(invalid)}`) } return array @@ -253,8 +257,8 @@ function createHook (meta) { async function initialize (data) { if (data) { - includeModules = ensureArrayWithBareSpecifiersAndFileUrls(data.include, 'include') - excludeModules = ensureArrayWithBareSpecifiersAndFileUrls(data.exclude, 'exclude') + includeModules = ensureArrayWithBareSpecifiersFileUrlsAndRegex(data.include, 'include') + excludeModules = ensureArrayWithBareSpecifiersFileUrlsAndRegex(data.exclude, 'exclude') } } @@ -283,13 +287,21 @@ function createHook (meta) { // For included/excluded modules, we check the specifier to match libraries // that are loaded with bare specifiers from node_modules. // - // For non-bare specifier imports, we only support matching file URL strings - // because using relative paths would be very error prone! - if (includeModules && !includeModules.some(lib => lib === specifier || lib === result.url.url)) { + // For non-bare specifier imports, we match to the full file URL because + // using relative paths would be very error prone! + function match (each) { + if (each instanceof RegExp) { + return each.test(result.url) + } + + return each === specifier || each === result.url + } + + if (includeModules && !includeModules.some(match)) { return result } - if (excludeModules && excludeModules.some(lib => lib === specifier || lib === result.url.url)) { + if (excludeModules && excludeModules.some(match)) { return result } diff --git a/test/register/v18.19-exclude-regex.mjs b/test/register/v18.19-exclude-regex.mjs new file mode 100644 index 0000000..4429c7e --- /dev/null +++ b/test/register/v18.19-exclude-regex.mjs @@ -0,0 +1,16 @@ +import { register } from 'module' +import Hook from '../../index.js' +import { strictEqual } from 'assert' + +register('../../hook.mjs', import.meta.url, { data: { exclude: [/openai/] } }) + +const hooked = new Set() + +Hook((_, name) => { + hooked.add(name) +}) + +await import('openai') + +strictEqual(hooked.has('openai'), false) +strictEqual(hooked.has('fs'), true)