diff --git a/packages/node-resolve/src/resolveImportSpecifiers.js b/packages/node-resolve/src/resolveImportSpecifiers.js index d29af56c1..cbb2f1e1b 100644 --- a/packages/node-resolve/src/resolveImportSpecifiers.js +++ b/packages/node-resolve/src/resolveImportSpecifiers.js @@ -125,9 +125,18 @@ async function resolveWithExportMap({ importer, moduleDirs: moduleDirectories, conditions: exportConditions, + // Resolve targets of "imports" mappings using the same algorithm + // we use for normal specifiers: try export maps first and then + // fall back to classic resolution. This is important for cases + // like "#foo/*": "@scope/pkg/*" where the target package relies + // on "exports" to expose subpaths. Using the classic resolver + // alone would fail to find those subpaths. resolveId(id /* , parent*/) { - return resolveIdClassic({ - importSpecifier: id, + return resolveImportSpecifiers({ + importer, + importSpecifierList: [id], + exportConditions, + // pass-through of the rest of the context packageInfoCache, extensions, mainFields, @@ -135,11 +144,22 @@ async function resolveWithExportMap({ useBrowserOverrides, baseDir, moduleDirectories, - modulePaths + modulePaths, + rootDir, + ignoreSideEffectsForRoot, + allowExportsFolderMapping }); } }); + if (resolveResult == null) { + // When the target of an "imports" mapping cannot be resolved, + // surface a proper resolve error instead of throwing from + // fileURLToPath(null). + throw new ResolveError( + `Could not resolve import "${importSpecifier}" in ${importer} using imports.` + ); + } const location = fileURLToPath(resolveResult); return { location: preserveSymlinks ? location : await resolveSymlink(location), diff --git a/packages/node-resolve/test/fixtures/imports-bare-pattern-exports.js b/packages/node-resolve/test/fixtures/imports-bare-pattern-exports.js new file mode 100644 index 000000000..7b9062da2 --- /dev/null +++ b/packages/node-resolve/test/fixtures/imports-bare-pattern-exports.js @@ -0,0 +1,3 @@ +import { a, b, c } from 'imports-bare-pattern-exports'; + +export default { a, b, c }; diff --git a/packages/node-resolve/test/fixtures/node_modules/imports-bare-pattern-exports/index.js b/packages/node-resolve/test/fixtures/node_modules/imports-bare-pattern-exports/index.js new file mode 100644 index 000000000..aedbab60c --- /dev/null +++ b/packages/node-resolve/test/fixtures/node_modules/imports-bare-pattern-exports/index.js @@ -0,0 +1,5 @@ +import a from '#foo/a.js'; +import b from '#foo/b.js'; +import c from '#foo/nested/c.js'; + +export { a, b, c }; diff --git a/packages/node-resolve/test/fixtures/node_modules/imports-bare-pattern-exports/package.json b/packages/node-resolve/test/fixtures/node_modules/imports-bare-pattern-exports/package.json new file mode 100644 index 000000000..da14fad95 --- /dev/null +++ b/packages/node-resolve/test/fixtures/node_modules/imports-bare-pattern-exports/package.json @@ -0,0 +1,7 @@ +{ + "name": "imports-bare-pattern-exports", + "main": "index.js", + "imports": { + "#foo/*": "exports-directory/foo/*" + } +} diff --git a/packages/node-resolve/test/package-entry-points.js b/packages/node-resolve/test/package-entry-points.js index 42932d90a..a5b6c3cf9 100644 --- a/packages/node-resolve/test/package-entry-points.js +++ b/packages/node-resolve/test/package-entry-points.js @@ -349,6 +349,23 @@ test('can resolve a package import with a pattern', async (t) => { }); }); +test('can resolve a package import pattern to a bare package that uses exports', async (t) => { + const bundle = await rollup({ + input: 'imports-bare-pattern-exports.js', + onwarn: () => { + t.fail('No warnings were expected'); + }, + plugins: [nodeResolve()] + }); + const { module } = await testBundle(t, bundle); + + t.deepEqual(module.exports, { + a: 'exported-foo a', + b: 'exported-foo b', + c: 'exported-foo c' + }); +}); + test('can override a star pattern using null', async (t) => { const errors = []; const bundle = await rollup({