Skip to content

[rush] Make "rush add" and "rush upgrade-interactive" aware of PNPM catalogs - #6141

Draft
Bharat Middha (bmiddha) wants to merge 1 commit into
bmiddha-catalog-aware-version-checksfrom
bmiddha-catalog-aware-rush-add
Draft

Bharat Middha (bmiddha) wants to merge 1 commit into
bmiddha-catalog-aware-version-checksfrom
bmiddha-catalog-aware-rush-add

Conversation

@bmiddha

Copy link
Copy Markdown
Member

Summary

This PR is part of the pnpm-catalog stack and sits on top of #6140 (#6140 → #6139 → #6138 → #6136 → #6133 → #6131). Review only the top commit.

rush add and rush upgrade-interactive ignored PNPM catalogs, which caused three problems:

  • rush add -p foo always wrote a literal version, even when a catalog had an entry for foo.
  • rush add -p foo@catalog: was rejected as an invalid SemVer specifier.
  • rush add and rush upgrade-interactive overwrote a project's own "foo": "catalog:" with a literal version.

rush upgrade-interactive also never offered dependencies referenced through a catalog, because npm-check skips non-SemVer specifiers.

Both commands now work with catalogs, much like pnpm's catalogMode: prefer and pnpm 12's update -i:

  • rush add writes catalog references when a catalog matches.
  • Explicit catalog references are accepted.
  • --make-consistent and rush upgrade-interactive update the catalog entry in pnpm-config.json instead of turning references into literal versions.

Details

"Catalogs" below means the effective globalCatalogs of the target project's subspace: its own pnpm-config.json, falling back to common/config/rush/pnpm-config.json (getEffectivePnpmCatalogs() from #6138).

rush add

Case Behavior
rush add -p foo, and a catalog has foo Writes catalog: if the default catalog is chosen, otherwise catalog:<name>. When several catalogs have foo, Rush prefers a catalog the target projects already reference foo through, then catalogs other projects reference it through (catalogNamesByPackageName), then default. If it is still ambiguous, Rush writes the literal version as before and prints a note suggesting foo@catalog:<name>.
rush add -p foo@<range>, and the range equals a catalog entry Writes the catalog reference. The version Rush finds, e.g. ~1.2.3 from the registry, is also matched against the catalog entries.
rush add -p foo@<range>, and the range differs from the catalog entry Writes the literal version (unchanged behavior) and prints a note. The ensureConsistentVersions gate still applies as usual.
rush add -p foo@<range> --make-consistent, and foo is cataloged Updates the catalog entry in the effective pnpm-config.json to <range> and writes the catalog reference. Every catalog consumer stays consistent, and consumers with literal versions (plus common-versions.json) are updated as before.
rush add -p foo@catalog: / foo@catalog:<name> Accepted by AddAction for PNPM repos only, with a clear error for npm/yarn. Rush validates that the catalog exists and has an entry for foo, with a clear error otherwise. catalog:default is normalized to catalog:. An existing literal version is replaced.
Target already has "foo": "catalog:..." The reference is never silently replaced with a literal. If the resolved version is unchanged, the existing spec string is kept, so even catalog:default stays as written. With --make-consistent, Rush updates the entry of the referenced catalog. Otherwise Rush fails with an error explaining how to proceed (--make-consistent, or foo@catalog:) before anything is written.

rush upgrade-interactive

  • InteractiveUpgrader gives npm-check a copy of the project's package.json in which each catalog reference is replaced by its catalog version, so cataloged dependencies are listed. This needed a small @rushstack/npm-check-fork change: honor a provided cwdPackageJson and stop mutating the default options object.
  • For a dependency that the chosen project references through a catalog, Rush updates the catalog entry and keeps catalog: in package.json. The range style (~/^) is inferred from the catalog version.
  • Other dependencies are upgraded as before.
  • With --make-consistent, catalog consumers in other projects get the catalog update, and literal consumers get the new version. This runs per subspace of the selected projects, using the pending catalog changes.
  • Without --make-consistent, the ensureConsistentVersions gate checks the versions every project will use after the catalog change. This matters because changing a catalog entry also changes what other consumers get.

Shared pnpm-config.json

  • Catalog edits are batched in a new internal PendingPnpmCatalogUpdates class.
  • They are saved once, after the package.json files, through PnpmOptionsConfiguration.updateGlobalCatalogsAsync(). That also updates the in-memory catalogs, so the following rush update sees them.
  • Rush prints each change (catalog:tools typescript: ~5.3.0 -> ~5.4.5).
  • Entries are only added or updated, never removed.
  • When the edited file is shared with other subspaces that this command does not update (with subspaces enabled), Rush prints a warning with one rush update --subspace <name> per affected subspace. Otherwise pnpm 12's frozen-install catalog check and the repo-state hashes would drift. With --skip-update, the target's own subspace is listed too.

--variant

pnpm-config.json does not vary by variant: Subspace.getPnpmConfigFilePath() has no variant parameter, and only common-versions, the shrinkwrap, and the pnpmfile do. So --variant keeps selecting common-versions.json as before, while catalogs always come from the subspace's (or the common) pnpm-config.json.

Relation to #6140

In #6140, --make-consistent skipped other projects' catalog references with a warning. Now that catalog entries can be updated safely, those references are handled by updating the catalog entry, and the warning is gone.

Compatibility and performance

  • Repos without catalogs, and npm/yarn repos, behave as before; the existing tests are unchanged apart from the two catalog tests from [rush] Make version consistency checks aware of PNPM catalogs #6140.
  • Help text changed for rush add (--package and the command description).
  • There is no meaningful performance impact. Catalog lookups are in-memory, and getMismatches runs once per affected subspace only with --make-consistent.

How it was tested

New unit tests

  • PackageJsonUpdater.test.ts (31 tests) uses the repoWithCatalogs and repoWithSubspacesCommonCatalogs fixtures, with mocked registry, install, and file writes. It covers:
    • every rush add case in the table above, including the catalog-choice preferences and the ambiguous note
    • explicit references, plus the missing-catalog, missing-entry, and non-PNPM errors
    • a target's existing reference: kept, bumped with -m, and the error without -m
    • shared-config hints, including a subspace with its own pnpm-config.json (no hint) and --skip-update
    • the ECV gate
    • upgrade-interactive for regular and dev deps, mixed catalog and literal projects, -m, and its ECV gate
  • AddAction.test.ts: parses semver@catalog: and typescript@catalog:tools (passed through with rangeStyle: passthrough), and rejects assert@catalog: in an npm repo.
  • NpmCheckState.test.ts: covers a provided cwdPackageJson.

Commands run on the final commit

The pinned Rush can't install pnpm 12.13.0, so a locally built dev Rush from #6136 and above was used. For install, common/scripts was refreshed temporarily and is not part of this PR.

Command Result
node <dev-rush>/apps/rush/lib-commonjs/start-dev.js install ✅ passed
node <dev-rush>/apps/rush/lib-commonjs/start-dev.js build --to @microsoft/rush-lib ✅ passed, no warnings
cd libraries/rush-lib && node_modules/.bin/heft test ✅ 2007 passed, 1 failed (pre-existing, unrelated: TarExecutable › kills tar when its signal aborts while it runs)
cd libraries/rush-lib && node_modules/.bin/heft test --test-path-pattern "PackageJsonUpdater|AddAction|CommandLineHelp|RushCommandLine|VersionMismatch|InteractiveUpgrader|DependencyAnalyzer" ✅ 141 passed, 0 failed, build and lint warning-free
cd libraries/npm-check-fork && node_modules/.bin/heft test ✅ 46 passed, 0 failed
rush prettier (pre-commit hook) ✅ passed

The CommandLineHelp and RushCommandLine snapshots were updated for the new rush add help text.

Impacted documentation

Change files: @microsoft/rush (minor), @rushstack/npm-check-fork (patch).

"rush add" writes PNPM catalog references (catalog: / catalog:<name>)
when a catalog of the project's subspace has a matching entry, accepts
explicit catalog references, and with --make-consistent updates the
catalog entry instead of replacing references with versions. A project's
existing catalog reference is never silently replaced with a version.

"rush upgrade-interactive" lists dependencies referenced through a
catalog and upgrades them by bumping the catalog entry. When the updated
pnpm-config.json is shared with other subspaces, the needed
"rush update --subspace" commands are printed.

npm-check-fork now honors a provided cwdPackageJson.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

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

Labels

None yet

Projects

Status: Needs triage

Development

Successfully merging this pull request may close these issues.

1 participant