diff --git a/src/resolve-dependency.ts b/src/resolve-dependency.ts index cf47212c..9cfc6062 100644 --- a/src/resolve-dependency.ts +++ b/src/resolve-dependency.ts @@ -128,6 +128,20 @@ function getExportsTarget(exports: PackageTarget, conditions: string[], cjsResol return undefined; } +function patternKeyCompare(a: string, b: string) { + const aPatternIndex = a.indexOf("*"); + const bPatternIndex = b.indexOf("*"); + const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1; + const baseLenB = bPatternIndex === -1 ? b.length : bPatternIndex + 1; + if (baseLenA > baseLenB) return -1; + if (baseLenB > baseLenA) return 1; + if (aPatternIndex === -1) return 1; + if (bPatternIndex === -1) return -1; + if (a.length > b.length) return -1; + if (b.length > a.length) return 1; + return 0; +} + function resolveExportsImports (pkgPath: string, obj: PackageTarget, subpath: string, job: Job, isImports: boolean, cjsResolve: boolean): string | undefined { let matchObj: { [key: string]: PackageTarget }; if (isImports) { @@ -141,17 +155,66 @@ function resolveExportsImports (pkgPath: string, obj: PackageTarget, subpath: st matchObj = obj; } - if (subpath in matchObj) { + if (subpath in matchObj && !subpath.includes('*')) { const target = getExportsTarget(matchObj[subpath], job.conditions, cjsResolve); if (typeof target === 'string' && target.startsWith('./')) return pkgPath + target.slice(1); } - for (const match of Object.keys(matchObj).sort((a, b) => b.length - a.length)) { - if (match.endsWith('*') && subpath.startsWith(match.slice(0, -1))) { - const target = getExportsTarget(matchObj[match], job.conditions, cjsResolve); - if (typeof target === 'string' && target.startsWith('./')) - return pkgPath + target.slice(1).replace(/\*/g, subpath.slice(match.length - 1)); + + + let bestMatch = ""; + let bestMatchSubpath; + + for (const match of Object.keys(matchObj)) { + const patternIndex = match.indexOf("*"); + if ( + patternIndex !== -1 && + subpath.startsWith(match.slice(0, patternIndex)) + ) { + const patternTrailer = match.slice(patternIndex + 1); + if ( + subpath.length >= match.length && + subpath.endsWith(patternTrailer) && + patternKeyCompare(bestMatch, match) === 1 && + match.lastIndexOf("*") === patternIndex + ) { + bestMatch = match; + bestMatchSubpath = subpath.slice( + patternIndex, + subpath.length - patternTrailer.length + ); + } + } else if ( + match[match.length - 1] === "/" && + subpath.startsWith(match) && + patternKeyCompare(bestMatch, match) === 1 + ) { + bestMatch = match; + bestMatchSubpath = subpath.slice(match.length); } + } + + if (bestMatch) { + const target = getExportsTarget( + matchObj[bestMatch], + job.conditions, + cjsResolve + ); + if (typeof target === "string" && target.startsWith("./")) { + const patternIndex = target.indexOf("*"); + if (patternIndex !== -1 && target.lastIndexOf("*") === patternIndex) { + return ( + pkgPath + + target.substring(1, patternIndex) + + bestMatchSubpath + + target.substring(patternIndex + 1) + ); + } + return target; + } + } + + for (const match of Object.keys(matchObj).sort((a, b) => b.length - a.length)) { if (!match.endsWith('/')) continue; if (subpath.startsWith(match)) { diff --git a/test/unit/exports-wildcard-trailer/f.js b/test/unit/exports-wildcard-trailer/f.js new file mode 100644 index 00000000..5039a381 --- /dev/null +++ b/test/unit/exports-wildcard-trailer/f.js @@ -0,0 +1 @@ +export var x = 'x'; diff --git a/test/unit/exports-wildcard-trailer/foo.js b/test/unit/exports-wildcard-trailer/foo.js new file mode 100644 index 00000000..5039a381 --- /dev/null +++ b/test/unit/exports-wildcard-trailer/foo.js @@ -0,0 +1 @@ +export var x = 'x'; diff --git a/test/unit/exports-wildcard-trailer/foobar.js b/test/unit/exports-wildcard-trailer/foobar.js new file mode 100644 index 00000000..9d1b9688 --- /dev/null +++ b/test/unit/exports-wildcard-trailer/foobar.js @@ -0,0 +1 @@ +export var y = 'y'; diff --git a/test/unit/exports-wildcard-trailer/input.js b/test/unit/exports-wildcard-trailer/input.js new file mode 100644 index 00000000..6b4e2021 --- /dev/null +++ b/test/unit/exports-wildcard-trailer/input.js @@ -0,0 +1,2 @@ +import { y } from "y/foob.js"; +console.log(y); diff --git a/test/unit/exports-wildcard-trailer/output.js b/test/unit/exports-wildcard-trailer/output.js new file mode 100644 index 00000000..a8a31ac1 --- /dev/null +++ b/test/unit/exports-wildcard-trailer/output.js @@ -0,0 +1,5 @@ +[ + "test/unit/exports-wildcard-trailer/foobar.js", + "test/unit/exports-wildcard-trailer/input.js", + "test/unit/exports-wildcard-trailer/package.json" +] diff --git a/test/unit/exports-wildcard-trailer/package.json b/test/unit/exports-wildcard-trailer/package.json new file mode 100644 index 00000000..874656e6 --- /dev/null +++ b/test/unit/exports-wildcard-trailer/package.json @@ -0,0 +1,24 @@ +{ + "name": "y", + "type": "module", + "exports": { + "./*.js": { + "module": "./*.js", + "default": { + "node": "./*b.js" + } + }, + "./foo*.js": { + "module": "./foo*.js", + "default": { + "node": "./foo*ar.js" + } + }, + "./fo*.js": { + "module": "./fo*.js", + "default": { + "node": "./fo*ba.js" + } + } + } +}