diff --git a/README.md b/README.md index 3976e32..e7826fc 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Options include: ```js { - // An additional "imports" map to apply to all specifiers. Follows the same + // A default "imports" map to apply to all specifiers. Follows the same // syntax and rules as the "imports" property defined in `package.json`. imports, // A list of builtin module specifiers. If matched, the protocol of the @@ -109,31 +109,34 @@ Options are the same as `resolve()` for all functions. 2. If `options.resolutions` is set: 1. If `preresolved(specifier, options.resolutions, parentURL, options)` returns `true`: 1. Return `true`. -3. If `options.imports` is set: - 1. If `packageImportsExports(specifier, options.imports, parentURL, true, options)` returns `true`: - 1. Return `true`. 4. If `url(specifier, parentURL, options)` returns `true`: 1. Return `true`. 5. If `packageImports(specifier, parentURL, options)` returns `true`: 1. Return `true`. 6. If `specifier` equals `.` or `..`, or if `specifier` starts with `/`, `\`, `./`, `.\`, `../`, or `..\`: - 1. Let `yielded` be `false`. - 2. If `file(specifier, parentURL, false, options)` returns `true`: + 1. If `options.imports` is set: + 1. If `packageImportsExports(specifier, options.imports, parentURL, true, options)` returns `true`: + 1. Return `true`. + 2. Let `yielded` be `false`. + 3. If `file(specifier, parentURL, false, options)` returns `true`: 1. Set `yielded` to `true`. - 3. If `directory(specifier, parentURL, options)` returns `true`: + 4. If `directory(specifier, parentURL, options)` returns `true`: 1. Set `yielded` to `true`. - 4. Return `yielded`. + 5. Return `yielded`. 7. Return `package(specifier, parentURL, options)`. #### `const generator = resolve.url(url, parentURL[, options])` 1. If `url` is not a valid URL: 1. Return `false`. -2. If `url.protocol` equals `node:`: +2. If `options.imports` is set: + 1. If `packageImportsExports(url.href, options.imports, parentURL, true, options)` returns `true`: + 1. Return `true`. +3. If `url.protocol` equals `node:`: 1. Let `specifier` be `url.pathname`. 2. If `specifier` equals `.` or `..`, or if `specifier` starts with `/`, `\`, `./`, `.\`, `../`, or `..\`, throw. 3. Return `package(specifier, parentURL, options)` -3. Yield `url` and return `true`. +4. Yield `url` and return `true`. #### `const generator = resolve.preresolved(specifier, resolutions, parentURL[, options])` @@ -220,7 +223,10 @@ Options are the same as `resolve()` for all functions. 1. Return `true`. 2. If specifier starts with `#`, throw. 3. Return `false`. -3. Return `false`. +3. If `options.imports` is set: + 1. If `packageImportsExports(url.href, options.imports, parentURL, true, options)` returns `true`: + 1. Return `true`. +4. Return `false`. #### `const generator = resolve.packageImportsExports(matchKey, matchObject, packageURL, isImports[, options])` diff --git a/index.js b/index.js index b977709..738179c 100644 --- a/index.js +++ b/index.js @@ -66,12 +66,6 @@ exports.module = function * (specifier, parentURL, opts = {}) { } } - if (imports) { - if (yield * exports.packageImportsExports(specifier, imports, parentURL, true, opts)) { - return true - } - } - if (yield * exports.url(specifier, parentURL, opts)) { return true } @@ -81,6 +75,12 @@ exports.module = function * (specifier, parentURL, opts = {}) { } if (specifier === '.' || specifier === '..' || specifier[0] === '/' || specifier[0] === '\\' || specifier.startsWith('./') || specifier.startsWith('.\\') || specifier.startsWith('../') || specifier.startsWith('..\\')) { + if (imports) { + if (yield * exports.packageImportsExports(specifier, imports, parentURL, true, opts)) { + return true + } + } + let yielded = false if (yield * exports.file(specifier, parentURL, false, opts)) { @@ -98,6 +98,8 @@ exports.module = function * (specifier, parentURL, opts = {}) { } exports.url = function * (url, parentURL, opts = {}) { + const { imports = null } = opts + let resolution try { resolution = new URL(url) @@ -105,6 +107,12 @@ exports.url = function * (url, parentURL, opts = {}) { return false } + if (imports) { + if (yield * exports.packageImportsExports(resolution.href, imports, parentURL, true, opts)) { + return true + } + } + if (resolution.protocol === 'node:') { const specifier = resolution.pathname @@ -257,6 +265,8 @@ exports.packageExports = function * (packageURL, subpath, packageExports, opts = } exports.packageImports = function * (specifier, parentURL, opts = {}) { + const { imports = null } = opts + if (specifier === '#' || specifier.startsWith('#/')) { throw errors.INVALID_MODULE_SPECIFIER(`Module specifier '${specifier}' is not a valid internal imports specifier`) } @@ -279,6 +289,12 @@ exports.packageImports = function * (specifier, parentURL, opts = {}) { } } + if (imports) { + if (yield * exports.packageImportsExports(specifier, imports, parentURL, true, opts)) { + return true + } + } + return false } diff --git a/test.js b/test.js index a1c12bd..878eb34 100644 --- a/test.js +++ b/test.js @@ -1069,6 +1069,32 @@ test('imports override with relative specifier', (t) => { t.alike(result, ['file:///a/b/e.js']) }) +test('imports override with package.json#imports', (t) => { + function readPackage (url) { + if (url.href === 'file:///a/b/c/package.json') { + return { + imports: { + './d': './f.js' + } + } + } + + return null + } + + const imports = { + './d': './e.js' + } + + const result = [] + + for (const resolution of resolve('./d', new URL('file:///a/b/c/'), { imports }, readPackage)) { + result.push(resolution.href) + } + + t.alike(result, ['file:///a/b/c/f.js']) +}) + test('scoped package.json inside package', (t) => { function readPackage (url) { if (url.href === 'file:///a/b/node_modules/d/e/package.json') {