Skip to content

Commit c73d16a

Browse files
slorberclaude
andauthored
chore(deps): move all dependencies to pnpm catalogs (AI-assisted) (#12696)
* chore(deps): use a pnpm catalog for dependencies shared by several packages (AI-assisted) - Declare the 49 dependencies shared by several monorepo packages once, in the `catalog` of `pnpm-workspace.yaml`, and reference them with the `catalog:` protocol. The resolved versions in the lockfile don't change - Dependency group members, template dependencies, pnpm overrides and peer dependencies keep explicit ranges, so syncpack keeps checking them - Publish preview packages with `pkg-pr-new --pnpm`: `pnpm pack` replaces `catalog:` with versions, `npm pack` doesn't - Document the catalog rules in the `dependencies` agent skill Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(deps): move all dependencies to pnpm catalogs, with a dev catalog for dev-only dependencies (AI-assisted) - Every dependency of the monorepo packages now uses the pnpm catalogs: - The default catalog (`catalog:`) holds the dependencies used at runtime by at least one package - The `dev` catalog (`catalog:dev`) holds the dependencies only used as devDependencies - Only the site templates, the pnpm overrides and the peer dependencies with intentionally broader ranges keep explicit versions - `pnpm lint:syncpack` checks that dependencies use a catalog - New `pnpm lint:catalogs` check, for what Syncpack can't check: catalog entries are in the right catalog, templates and overrides match the catalogs, and the members of each Dependabot group use the same version - Dependency groups are now only declared in `.github/dependabot.yml` The resolved packages of the lockfile don't change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(scripts): make lintCatalogs.js easier to review (AI-assisted) Organize the script as a tree: main() gives the high-level picture, then the data loading, one pure function per check, and the low-level helpers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(deps): enable pnpm catalogMode strict and catalogPrune, check unused catalog entries (AI-assisted) - `catalogMode: strict`: `pnpm add` only accepts catalog versions, and adds new dependencies to the default catalog - `catalogPrune: true`: `pnpm remove` removes the unused catalog entries - `pnpm lint:catalogs` reports unused catalog entries: pnpm doesn't prune them when a package.json is edited manually Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: type-check the website with min/max TypeScript versions through pnpm dlx (AI-assisted) `pnpm add typescript@<version>` modified the workspace dependencies, and fails with the new `catalogMode: strict` setting: the min version step adds TypeScript to the catalog, then the max version step is outside its range. Run the TypeScript compiler of the tested version with `pnpm dlx` instead, without modifying package.json, the lockfile or the catalogs. Type resolution still uses the workspace node_modules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 9123931 commit c73d16a

53 files changed

Lines changed: 2094 additions & 892 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/dependencies/SKILL.md‎

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,39 @@ description: Add, remove or upgrade npm dependencies in the Docusaurus monorepo.
88
## Lockfile
99

1010
- Run `pnpm dedupe` after any lockfile change. CI fails if `pnpm-lock.yaml` is not deduplicated (`pnpm lint:deps`).
11-
- Run `pnpm lint:syncpack`: the same dependency must use the same version range in all packages.
1211
- Site template dependencies live in `packages/create-docusaurus/templates/`. Never edit `examples/`, it is CLI-generated.
1312

13+
## Catalogs
14+
15+
All the monorepo dependencies are declared in the [pnpm catalogs](https://pnpm.io/catalogs) of `pnpm-workspace.yaml`. Packages reference them with the catalog protocol instead of a version range:
16+
17+
- `"catalog:"`: the default catalog, for dependencies used at runtime by at least one package (`dependencies`, `optionalDependencies` or `peerDependencies`).
18+
- `"catalog:dev"`: the dev catalog, for dependencies only used as `devDependencies`.
19+
20+
Use pnpm commands to add and remove dependencies:
21+
22+
- `pnpm add` only accepts catalog versions (`catalogMode: strict`), and adds new dependencies to the default catalog. Move dev-only dependencies to the dev catalog.
23+
- `pnpm remove` removes the catalog entries that are no longer used (`catalogPrune: true`). Manual `package.json` edits don't.
24+
25+
Move a dev catalog entry to the default catalog as soon as a package uses it at runtime.
26+
27+
Exceptions, keeping explicit versions:
28+
29+
- The site templates, copied to user sites. Their versions must match the catalogs.
30+
- pnpm `overrides`. Their versions must match the catalogs.
31+
- Peer dependencies whose range intentionally differs from the catalog (see the ignored version groups of `.syncpackrc.ts`).
32+
33+
Two checks enforce it:
34+
35+
- `pnpm lint:syncpack`: dependencies must use a catalog.
36+
- `pnpm lint:catalogs` (`admin/scripts/lintCatalogs.js`): catalog entries are used and in the right catalog, templates and overrides match the catalogs, and dependency group members use the same version.
37+
1438
## Dependency groups
1539

16-
Some packages must always use the same version. They are declared as groups in two places, which must stay in sync:
40+
Some packages must always use the same version. They are declared as `groups` in `.github/dependabot.yml`:
1741

18-
- `.github/dependabot.yml` (`groups`): Dependabot upgrades them atomically in a single PR, instead of opening broken intermediate PRs.
19-
- `.syncpackrc.ts` (`dependencyGroups`): `pnpm lint:syncpack` fails if their versions differ.
42+
- Dependabot upgrades them atomically in a single PR, instead of opening broken intermediate PRs.
43+
- `pnpm lint:catalogs` fails if their catalog versions differ.
2044

2145
Packages belong to a group when, for example:
2246

@@ -29,6 +53,6 @@ Packages belong to a group when, for example:
2953
When you add a dependency, check whether it belongs to a group:
3054

3155
- If an existing group already matches it (`@svgr/*` matches `@svgr/plugin-jsx`), there is nothing to do.
32-
- Otherwise, add it to the matching group, or create a new group, in both files. Follow the existing groups: one group per package family, with a comment explaining why the packages must stay in sync.
56+
- Otherwise, add it to the matching group, or create a new group. Follow the existing groups: one group per package family, with a comment explaining why the packages must stay in sync.
3357

3458
Do the same when a Dependabot PR can't pass CI on its own because a related package wasn't upgraded with it.

‎.github/dependabot.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ updates:
3434
semver-minor-days: 8
3535
semver-patch-days: 8
3636
# Packages that must be upgraded atomically in a single PR
37-
# Keep in sync with the dependencyGroups of .syncpackrc.ts
37+
# pnpm lint:catalogs checks that the catalog versions of each group match
3838
groups:
3939
# Released in lockstep, mixing versions breaks typechecking
4040
# See https://github.com/facebook/docusaurus/pull/12634

‎.github/workflows/continuous-releases.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,5 @@ jobs:
5959
node ./packages/create-docusaurus/bin/index.js template/docusaurus-classic-ts classic --typescript -p npm
6060
6161
- name: Release
62-
run: pnpm pkg-pr-new publish './packages/*' --template './template/*' --compact --comment=off
62+
# --pnpm: use pnpm pack, which replaces the catalog: protocol with versions
63+
run: pnpm pkg-pr-new publish './packages/*' --template './template/*' --compact --comment=off --pnpm

‎.github/workflows/lint.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ jobs:
5454
run: pnpm lint:spelling
5555
- name: Syncpack
5656
run: pnpm lint:syncpack
57+
- name: Catalogs
58+
run: pnpm lint:catalogs
5759
- name: Check deduped pnpm-lock.yaml
5860
run: pnpm lint:deps
5961
- name: Knip

‎.github/workflows/tests-windows.yml‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,16 @@ jobs:
7070
# see https://github.com/facebook/docusaurus/pull/10486
7171
run: pnpm --filter website typecheck
7272

73+
# The TypeScript compiler of the tested version runs with pnpm dlx,
74+
# without modifying the workspace dependencies (package.json, catalogs)
7375
- name: TypeCheck website - min version - v6.0
74-
# We call the "typescript" package compiler explicitly, because the
75-
# root "tsc" bin comes from "@typescript/native" (pinned TS 7)
7676
run: |
77-
pnpm add typescript@6.0 --save-exact -D -w --ignore-scripts
78-
node node_modules/typescript/bin/tsc --version
79-
node node_modules/typescript/bin/tsc --project website/tsconfig.json
77+
pnpm dlx --package=typescript@6.0 tsc --version
78+
pnpm dlx --package=typescript@6.0 tsc --project website/tsconfig.json
8079
8180
- name: TypeCheck website - max version - Latest
8281
# For latest TS there are often lib check errors, so we disable it
8382
# Details: https://github.com/facebook/docusaurus/pull/10486
8483
run: |
85-
pnpm add typescript@latest --save-exact -D -w --ignore-scripts
86-
node node_modules/typescript/bin/tsc --version
87-
node node_modules/typescript/bin/tsc --project website/tsconfig.skipLibCheck.json
84+
pnpm dlx --package=typescript@latest tsc --version
85+
pnpm dlx --package=typescript@latest tsc --project website/tsconfig.skipLibCheck.json

‎.github/workflows/tests.yml‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,16 @@ jobs:
6767
# see https://github.com/facebook/docusaurus/pull/10486
6868
run: pnpm --filter website typecheck
6969

70+
# The TypeScript compiler of the tested version runs with pnpm dlx,
71+
# without modifying the workspace dependencies (package.json, catalogs)
7072
- name: TypeCheck website - min version - v6.0
71-
# We call the "typescript" package compiler explicitly, because the
72-
# root "tsc" bin comes from "@typescript/native" (pinned TS 7)
7373
run: |
74-
pnpm add typescript@6.0 --save-exact -D -w --ignore-scripts
75-
node node_modules/typescript/bin/tsc --version
76-
node node_modules/typescript/bin/tsc --project website/tsconfig.json
74+
pnpm dlx --package=typescript@6.0 tsc --version
75+
pnpm dlx --package=typescript@6.0 tsc --project website/tsconfig.json
7776
7877
- name: TypeCheck website - max version - Latest
7978
# For latest TS there are often lib check errors, so we disable it
8079
# Details: https://github.com/facebook/docusaurus/pull/10486
8180
run: |
82-
pnpm add typescript@latest --save-exact -D -w --ignore-scripts
83-
node node_modules/typescript/bin/tsc --version
84-
node node_modules/typescript/bin/tsc --project website/tsconfig.skipLibCheck.json
81+
pnpm dlx --package=typescript@latest tsc --version
82+
pnpm dlx --package=typescript@latest tsc --project website/tsconfig.skipLibCheck.json

‎.syncpackrc.ts‎

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,6 @@ export default {
2020
'packages/create-docusaurus/templates/*/package.json',
2121
],
2222

23-
// Packages that must use the same version, treated as a single dependency
24-
// Keep in sync with the groups of .github/dependabot.yml
25-
dependencyGroups: [
26-
{aliasName: 'svgr', dependencies: ['@svgr/**']},
27-
{aliasName: 'workbox', dependencies: ['workbox-*']},
28-
{aliasName: 'rsdoctor', dependencies: ['@rsdoctor/**']},
29-
{aliasName: 'react', dependencies: ['react', 'react-dom']},
30-
{
31-
aliasName: 'react-types',
32-
dependencies: ['@types/react', '@types/react-dom'],
33-
},
34-
{
35-
aliasName: 'react-router',
36-
dependencies: ['react-router', 'react-router-dom'],
37-
},
38-
{
39-
aliasName: 'typescript-eslint',
40-
dependencies: ['typescript-eslint', '@typescript-eslint/**'],
41-
},
42-
],
43-
4423
semverGroups: [
4524
{
4625
label: 'Use ~ for TypeScript monorepo root and init templates',
@@ -72,9 +51,9 @@ export default {
7251
'@docusaurus/types',
7352
],
7453
dependencies: [
75-
// Aliases from dependencyGroups
7654
'react',
77-
'react-types',
55+
'react-dom',
56+
'@types/react',
7857
'@types/react-router-config',
7958
'@types/react-router-dom',
8059
],
@@ -126,6 +105,23 @@ export default {
126105
pinVersion: 'workspace:*',
127106
},
128107

129-
// Default: all remaining dependencies — highest version wins (syncpack default)
108+
{
109+
label:
110+
"Templates can't use catalogs, lintCatalogs.js checks their versions against the catalogs",
111+
packages: [
112+
'docusaurus-2-classic-template',
113+
'docusaurus-2-classic-typescript-template',
114+
],
115+
isIgnored: true,
116+
},
117+
118+
{
119+
// Runtime dependencies use the default catalog ("catalog:")
120+
// Dev-only dependencies use the dev catalog ("catalog:dev")
121+
// lintCatalogs.js checks the catalogs content
122+
label: 'All the other dependencies must use a pnpm catalog',
123+
dependencyTypes: ['prod', 'dev', 'peer'],
124+
policy: 'catalog',
125+
},
130126
],
131127
} satisfies RcFile;

‎AGENTS.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ The main CLI commands available
5555
- `pnpm build:packages` - Build all monorepo packages
5656
- `pnpm watch` - Incremental build of monorepo packages with file watchers
5757
- `pnpm format` - Format code with oxfmt
58-
- `pnpm lint` - Run all the lint checks that CI runs (ESLint + Stylelint + spell check + Syncpack + dedupe check + Knip + formatting check)
58+
- `pnpm lint` - Run all the lint checks that CI runs (ESLint + Stylelint + spell check + Syncpack + catalogs check + dedupe check + Knip + formatting check)
5959
- `pnpm dedupe` - Deduplicate `pnpm-lock.yaml` dependencies, run it after any lockfile change
6060
- `pnpm test` - Run all tests using Vitest
6161
- `pnpm clear` - Clean all build artifacts and caches
@@ -87,7 +87,8 @@ Update Vitest failing snapshots with the `-u` option. Do not blindly update snap
8787
- `pnpm lint:js` - ESLint for JS/TS files
8888
- `pnpm lint:style` - Stylelint for CSS files
8989
- `pnpm lint:spelling` - CSpell for spell checking
90-
- `pnpm lint:syncpack` - Syncpack to check consistent dependency versions across packages
90+
- `pnpm lint:syncpack` - Syncpack to check that dependencies use the pnpm catalogs
91+
- `pnpm lint:catalogs` - Check the content of the pnpm catalogs
9192
- `pnpm lint:knip` - Knip to find unused files, exports and dependencies (config in `knip.config.ts`)
9293
- `pnpm lint:deps` - Check that `pnpm-lock.yaml` is deduplicated (`pnpm dedupe --check`)
9394
- Add `:fix` suffix to auto-fix issues (e.g., `pnpm lint:js:fix`)

0 commit comments

Comments
 (0)