Skip to content

packageRootOf matches the declaration KEY, so an aliased dual-published package silently loads its require build on the succeeding (#13330) path #15044

Description

@os-trump

Filed unassigned by the domain:cli dev seat implementing #14278 (session session_016yfqQh2dBgPAymYd7xipza, branch claude/issue-14278-aliased-install-host-importer), as an out-of-scope finding of that card. ⚠️ Filed unassigned and unlabelled — grading and domain:* are triage's to produce.

The residue

#14278 fixed the ALIAS blindness in createHostImporter's fallback finder (hostInstalledPackageDir, the leg that runs inside hostRequire.resolve's catch). The same blindness survives one leg over, on the path where the CJS resolve succeeds — the #13330 condition re-decision:

packages/types/src/node.ts
  packageRootOf(resolvedFile, packageName)   // walks up from the resolved entry
    if (manifest.name === packageName) return dir;

packageName there is the DECLARATION KEY. For an aliased install:

{ "dependencies": { "foo": "npm:bar@1" } }

hostRoot/node_modules/foo/package.json is named bar, so the walk never matches, packageRootOf returns undefined, esmEntryForDeclared returns undefined, and createHostImporter falls back to resolved — the entry the CJS resolver answered, i.e. the require condition.

⇒ For an aliased dual-published package the host importer silently keeps the pre-#13330 behaviour: it loads the CommonJS build while the caller's own ESM chain loads the import build, which is exactly the two-instances-of-one-package split #13330 exists to remove (a plugin registry, a singleton kernel, a module-level cache: one copy each).

Why this is NOT what #14278 fixed

#14278's ruled route was deliberately confined to the fallback finder, whose whole leg was a hard failure before #14041 — so a change there is strictly additive and cannot move a currently-succeeding load. This path is the opposite: the load SUCCEEDS today, and correcting the expectation here changes which file a working import() returns. That is a real blast radius and a decision of its own, which is why the implementing dev declined to fold it in and filed this instead.

The mechanism the fix would reuse already exists after #14278: the declaration is parsed for the manifest name it promises (npm:name@range, aliased workspace:name@range), and packageRootOf would take that expected name instead of the key. The judgment this card owes is whether changing a succeeding load's selected entry is right here, not how to compute the name.

Direction

Not an access-control consequence and nothing newly reachable: the wrong ENTRY of the RIGHT package is loaded, never another package. The damage is a duplicated module instance for aliased dual publishes, plus the import-condition contract silently not holding for them.

Re-check

git grep -n "manifest.name === packageName" -- packages/types/src/node.ts

Reverse-check any zero against a term known present in the same file (hostRequire.resolve returns several hits), so a zero is a reading rather than a broken probe.

Refs

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions