Skip to content

packageImportsResolve: wildcard imports keys with a trailer (#*.js) never match #56

Description

@pi0x

packageImportsResolve misses wildcard keys with a trailer (#*.js, #lib/*.js) because the pattern base is computed with key.slice(0, -1) instead of key.slice(0, patternIndex).

https://github.com/unjs/exsolve/blob/main/src/internal/resolve.ts#L788

if (patternIndex !== -1 && name.startsWith(key.slice(0, -1))) {

For key #*.js this tests name.startsWith("#*."), which never matches, so the key is skipped and resolution throws ERR_PACKAGE_IMPORT_NOT_DEFINED. Keys that end in * (#internal/*) work by accident since slice(0, -1) and slice(0, patternIndex) coincide there.

The exports counterpart a few hundred lines up is already correct — the asymmetry is the whole bug:

https://github.com/unjs/exsolve/blob/main/src/internal/resolve.ts#L673

packageSubpath.startsWith(key.slice(0, patternIndex))

Node uses StringPrototypeSlice(key, 0, patternIndex) in both places.

Reproduction

// node_modules/pkg/package.json
{
  "name": "pkg",
  "type": "module",
  "imports": { "#*.js": "./runtime/*.js" }
}
import { resolveModulePath } from "exsolve";

// from node_modules/pkg/index.mjs
resolveModulePath("#internal/marker.js", { from: parent, conditions: ["node", "import"] });
// exsolve 1.1.0: Cannot resolve module "#internal/marker.js"
// node:            resolves to node_modules/pkg/runtime/internal/marker.js

Node resolves this fine (createRequire(parent).resolve("#internal/marker.js") returns the expected path), so exsolve diverges from the reference implementation here.

Fix

-if (patternIndex !== -1 && name.startsWith(key.slice(0, -1))) {
+if (patternIndex !== -1 && name.startsWith(key.slice(0, patternIndex))) {

I applied this one-character change to the installed exsolve@1.1.0 dist locally and verified all three cases now resolve identically to Node:

specifier imports map before after
#internal/marker.js "#*.js": "./runtime/*.js" throws runtime/internal/marker.js
#ok/x.js "#ok/*.js": "./local/*.js" throws local/x.js
#lib/y.js "#lib/*.js": "dep/*.js" (external target) throws node_modules/dep/y.js

Trailing-star keys and the exports path were unaffected.

Worth a regression test with a trailer-pattern imports key alongside the existing exports fixtures.

Context

Found while reviewing unjs/nf3#66@vercel/nft has the same class of bug in its own resolver, and exsolve was the natural patch-free fallback for nf3 until it turned out to share the defect.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions