JamieMason / JamieMason/syncpack
feat: support Yarn catalogs in .yarnrc.yml
- Dominant language
- Rust
- Stars
- 2.1k
- Forks
- 72
- PR merge metrics
- No merged PRs in 30d
Description
### Description
syncpack supports catalogs for pnpm (`pnpm-workspace.yaml`) and Bun (root `package.json`), but not for Yarn.
Yarn added catalogs in [4.10.0](https://yarnpkg.com/features/catalogs). They are declared in `.yarnrc.yml` and consumed with the same `catalog:` protocol syncpack already understands:
```yaml
# .yarnrc.yml
catalog:
react: ^18.0.0
catalogs:
legacy:
react: ^17.0.0
```
```json
// packages/a/package.json
{ "dependencies": { "react": "catalog:" } }
```
In a Yarn workspace today, `syncpack lint` reports `catalog:` consumers as `DependsOnMissingCatalogDefinition` because no definitions are discovered, and a Yarn project with no catalogs yet reports `CannotInferCatalogFile` rather than offering to create one. The definitions in `.yarnrc.yml` are invisible to `list`, `lint`, `fix` and `update`, so the version that actually governs the monorepo is the one version syncpack cannot see or update.
This is the situation described in #258, where the answer was:
> It is not supported currently. [...] I'm not likely to support this any time soon, at least not until more package managers support it.
That condition now holds — pnpm, Bun and Yarn all ship catalogs, and syncpack already supports two of the three.
Why syncpack rather than a workaround: a config-side workaround can pin consumers to `catalog:` (as suggested in #258), but it cannot lint the catalog definitions themselves, cannot add a missing dependency to a catalog, and cannot update catalog entries from the registry. `update` in particular has no substitute — the catalog entry is the version, and nothing else in the repo carries it.
### Suggested Solution
No new configuration. Yarn catalogs would behave exactly as pnpm's do today.
```bash
syncpack list
```
```
= Catalog Definitions ==========================================================
1x react ^18.0.0 in /catalog of .yarnrc.yml
```
Two dependency types would be generated from the file, matching the existing naming:
| Name | Source |
| :------------------- | :------------------------------ |
| `yarnCatalog` | `.yarnrc.yml` `catalog` |
| `yarnCatalog:` | `.yarnrc.yml` `catalogs.` |
```bash
syncpack update --dependency-types yarnCatalog
syncpack lint --dependency-types 'yarnCatalog:legacy'
```
The existing `catalog` version group policy would work unchanged:
```json
{
"versionGroups": [
{
"label": "All production deps come from the catalog",
"dependencyTypes": ["prod"],
"policy": "catalog"
}
]
}
```
`fix` would write definitions back to `.yarnrc.yml`, and create the file when a Yarn project has no catalog yet — the same behaviour pnpm gets with `pnpm-workspace.yaml`.
### Optional comments
**One behavioural difference worth knowing about.** pnpm treats `catalog:default` as another way of writing `catalog:`. Yarn does not: `catalog:` reads the `catalog:` block, and `catalog:default` addresses a catalog literally named `default` under `catalogs:`. The two can coexist in one file and hold different versions, so the rule has to be applied per package manager rather than globally.
**There is a working attempt, but it needs your eye.** I don't have much Rust experience, so please read the branches below as a reference implementation rather than a finished contribution — the behaviour and tests are there, but the design choices are the part I'm least sure of. Branches from `main` at v15.3.3:
- https://github.com/unrevised6419/syncpack/pull/1 — refactor only, no behaviour change. Replaces the "exactly one yaml file" assumption with a `YamlKind`, then describes each package manager's catalogs as data (a `CatalogFormat` table holding the file location, the dep type prefix, and whether `catalog:default` is a named catalog) instead of `match` arms on `PackageManager` spread across discovery, naming, fix-time writes and reference resolution.
- https://github.com/unrevised6419/syncpack/pull/2 — Yarn itself, 38 production lines on top of that: one `FORMATS` entry plus reading `.yarnrc.yml`. Discovery, linting, `fix`, `update`, banned-definition removal and file auto-creation all follow from the table.
Covered by 20 new tests; writes go through the existing format-preserving yaml patch path, so comments and unrelated settings in `.yarnrc.yml` survive. That refactor is the part most likely to be wrong for this codebase — if you'd rather have a much smaller change that only adds Yarn, or would rather implement it yourself and just take whatever is useful from it, either is completely fine by me.
Contributor guide
Research direction
Start by tracing the existing pnpm catalog support and the format-preserving YAML write path; the issue does not name source files. Compare the referenced branches #1 and #2, then verify discovery, list, lint, fix, update, and file creation for .yarnrc.yml, with the 20 new tests passing and unrelated YAML content preserved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100