diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index 13f1326f58f96f..87f28d5287612a 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -22,7 +22,6 @@ const { StringPrototypeSlice, StringPrototypeSplit, StringPrototypeStartsWith, - StringPrototypeSubstr, } = primordials; const internalFS = require('internal/fs/utils'); const { NativeModule } = require('internal/bootstrap/loaders'); @@ -582,7 +581,9 @@ function packageImportsResolve(name, base, conditions) { packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); const imports = packageConfig.imports; if (imports) { - if (ObjectPrototypeHasOwnProperty(imports, name)) { + if (ObjectPrototypeHasOwnProperty(imports, name) && + !StringPrototypeIncludes(name, '*') && + !StringPrototypeEndsWith(name, '/')) { const resolved = resolvePackageTarget( packageJSONUrl, imports[name], '', name, base, false, true, conditions ); @@ -590,30 +591,39 @@ function packageImportsResolve(name, base, conditions) { return { resolved, exact: true }; } else { let bestMatch = ''; + let bestMatchSubpath; const keys = ObjectGetOwnPropertyNames(imports); for (let i = 0; i < keys.length; i++) { const key = keys[i]; - if (key[key.length - 1] === '*' && + const patternIndex = StringPrototypeIndexOf(key, '*'); + if (patternIndex !== -1 && StringPrototypeStartsWith(name, - StringPrototypeSlice(key, 0, -1)) && - name.length >= key.length && - key.length > bestMatch.length) { - bestMatch = key; + StringPrototypeSlice(key, 0, + patternIndex))) { + const patternTrailer = StringPrototypeSlice(key, patternIndex + 1); + if (name.length >= key.length && + StringPrototypeEndsWith(name, patternTrailer) && + patternKeyCompare(bestMatch, key) === 1 && + StringPrototypeLastIndexOf(key, '*') === patternIndex) { + bestMatch = key; + bestMatchSubpath = StringPrototypeSlice( + name, patternIndex, name.length - patternTrailer.length); + } } else if (key[key.length - 1] === '/' && StringPrototypeStartsWith(name, key) && - key.length > bestMatch.length) { + patternKeyCompare(bestMatch, key) === 1) { bestMatch = key; + bestMatchSubpath = StringPrototypeSlice(name, key.length); } } if (bestMatch) { const target = imports[bestMatch]; - const pattern = bestMatch[bestMatch.length - 1] === '*'; - const subpath = StringPrototypeSubstr(name, bestMatch.length - - (pattern ? 1 : 0)); - const resolved = resolvePackageTarget( - packageJSONUrl, target, subpath, bestMatch, base, pattern, true, - conditions); + const pattern = StringPrototypeIncludes(bestMatch, '*'); + const resolved = resolvePackageTarget(packageJSONUrl, target, + bestMatchSubpath, bestMatch, + base, pattern, true, + conditions); if (resolved !== null) return { resolved, exact: pattern }; } diff --git a/test/es-module/test-esm-imports.mjs b/test/es-module/test-esm-imports.mjs index 97bdc618c70d23..5155e482a2d4fc 100644 --- a/test/es-module/test-esm-imports.mjs +++ b/test/es-module/test-esm-imports.mjs @@ -20,6 +20,8 @@ const { requireImport, importImport } = importer; ['#external', { default: 'asdf' }], // External subpath imports ['#external/subpath/asdf.js', { default: 'asdf' }], + // Trailing pattern imports + ['#subpath/asdf.asdf', { default: 'test' }], ]); for (const [validSpecifier, expected] of internalImports) { diff --git a/test/fixtures/es-modules/pkgimports/package.json b/test/fixtures/es-modules/pkgimports/package.json index ca2c6c65e00a8f..5be55cecbd606c 100644 --- a/test/fixtures/es-modules/pkgimports/package.json +++ b/test/fixtures/es-modules/pkgimports/package.json @@ -6,6 +6,7 @@ "require": "./requirebranch.js" }, "#subpath/*": "./sub/*", + "#subpath/*.asdf": "./test.js", "#external": "pkgexports/valid-cjs", "#external/subpath/*": "pkgexports/sub/*", "#external/invalidsubpath/": "pkgexports/sub",