Skip to content

refactor(parsers): drive lockfile handling from one precedence list - #1130

Open
sonukapoor wants to merge 2 commits into
mainfrom
refactor/lockfile-handler-table
Open

refactor(parsers): drive lockfile handling from one precedence list#1130
sonukapoor wants to merge 2 commits into
mainfrom
refactor/lockfile-handler-table

Conversation

@sonukapoor

Copy link
Copy Markdown
Collaborator

The problem

loadPackages handled the same five lockfiles twice, once for the repo root and once for nested discovery, as two if-chains about 100 lines apart containing 12 near-identical ScanInput literals. chooseBestLockfile in src/utils/file.ts then kept a third, separate ordering of its own.

Three copies of the same knowledge meant three chances to disagree, and they did.

The change

LOCKFILE_PRIORITY in constants.ts is now the single source of truth for precedence. src/parsers/index.ts holds a LOCKFILE_HANDLERS table keyed by filename saying how to read each one. Root detection, nested discovery and chooseBestLockfile all walk that one list.

Keying the table by filename rather than using an array means the compiler requires a handler for every entry in the priority list, so adding a sixth lockfile cannot half-land.

constants.ts was chosen as the home because utils/file.ts already imports from it, which avoids an import cycle.

Two precedence bugs this exposed

npm-shrinkwrap.json lost to package-lock.json when nested. The root chain checked shrinkwrap first, but chooseBestLockfile scored package-lock.json at 0 and never scored npm-shrinkwrap.json at all, so it fell through to the default and sorted last. The same repo resolved differently depending on whether the lockfiles sat at the root or one directory down. npm itself ignores package-lock.json when a shrinkwrap is present, so the root behaviour was the correct one. New tests cover both levels.

findNestedLockfiles picked whichever lockfile readdirSync listed first. That matched precedence only because these five names happen to sort in priority order alphabetically, and readdirSync guarantees no ordering. It now picks by precedence explicitly.

One deliberate behaviour change

A nested bun.lock now outranks a nested package-lock.json, which is what the root scan has always done. This follows from the two paths agreeing rather than being a separate decision, but it is a real change for anyone with both files in a subdirectory.

Also

buildNoPackagesMessage listed the supported lockfiles by hand in its error text. It now derives them, so the message cannot go stale.

Verification

  • Full suite green: 1830 tests, 140 suites
  • tsc --noEmit clean
  • Two new tests, written failing first, covering nested shrinkwrap precedence in both loadPackages and chooseBestLockfile
  • Real CLI runs, not just unit tests: this repo resolves to its root package-lock.json and parses 385 packages, and purpose-built fixtures confirm both nested precedence cases

src/parsers/index.ts goes from 221 lines to 176, and from 12 ScanInput literals to 3.

loadPackages handled the same five lockfiles twice, once for the repo
root and once for nested discovery, as two if-chains about 100 lines
apart holding 12 near-identical ScanInput literals. chooseBestLockfile
then kept a third, separate ordering of its own.

Three copies meant three chances to disagree, and they did.

Add LOCKFILE_PRIORITY to constants.ts as the single source of truth,
and a LOCKFILE_HANDLERS table keyed by filename saying how to read each
one. Root detection, nested discovery and chooseBestLockfile all walk
that one list, so precedence cannot drift again. Keying the table by
filename means the compiler requires a handler for every entry.

Fixes two precedence bugs this exposed:

- npm-shrinkwrap.json lost to package-lock.json for nested lockfiles
  while winning at the root, because chooseBestLockfile never scored
  it and fell through to its default. npm ignores package-lock.json
  when a shrinkwrap is present, so the root behaviour was the correct
  one. Covered by new tests at both levels
- findNestedLockfiles picked whichever lockfile readdirSync listed
  first. That matched precedence only because these five names happen
  to sort that way, and readdirSync guarantees no order. It now picks
  by precedence explicitly

Nested bun.lock now outranks a nested package-lock.json, matching what
the root scan has always done. That is a deliberate consequence of the
two paths agreeing rather than a separate change.

buildNoPackagesMessage listed the supported lockfiles by hand in its
error text. It now derives them, so the message cannot go stale.

No other behaviour change. src/parsers/index.ts drops from 221 lines to
176 and from 12 ScanInput literals to 3. Verified with the full suite
(1830 tests) plus real CLI runs against this repo and fixtures for both
precedence cases.
Matches the inline type-import convention already used in nine other
files, and marks the notes parameter readonly since the callee never
mutates it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant