feat(npm): add `catalog:` protocol for centralized dependency versions in workspaces (#32947)
## Summary
Adds `catalog:` protocol support for `package.json`
dependencies in workspace members, enabling centralized
version management in monorepos. This matches the catalog
feature available in pnpm, Bun, and Yarn.
- Workspace root defines catalog mappings (package name
to version requirement)
- Members reference entries via `"pkg": "catalog:"` or
`"pkg": "catalog:<name>"` in their `package.json`
- Supports both default and **named catalogs** via the
`"catalog"` (singular) and `"catalogs"` (plural) fields
- Catalog can be defined in either root `deno.json` or
root `package.json`; if `package.json` has any catalog
config it takes exclusive precedence (no merging)
- Resolves catalog entries lazily during install,
resolution, lockfile, publish, and all other code paths
### Example: default catalog in `deno.json`
Root `deno.json`:
```jsonc
{
"workspace": ["./packages/a", "./packages/b"],
"catalog": {
"react": "^18.3.0",
"react-dom": "^18.3.0",
"chalk": "^5.3.0"
}
}
```
Member `packages/a/package.json`:
```json
{
"dependencies": {
"react": "catalog:",
"react-dom": "catalog:"
}
}
```
### Example: named catalogs in `deno.json`
Root `deno.json`:
```jsonc
{
"workspace": ["./packages/a", "./packages/b"],
"catalog": {
"chalk": "^5.3.0"
},
"catalogs": {
"react18": {
"react": "^18.3.0",
"react-dom": "^18.3.0"
},
"react19": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
}
}
}
```
Member `packages/a/package.json`:
```json
{
"dependencies": {
"chalk": "catalog:",
"react": "catalog:react19",
"react-dom": "catalog:react19"
}
}
```
Member `packages/b/package.json`:
```json
{
"dependencies": {
"chalk": "catalog:",
"react": "catalog:react18",
"react-dom": "catalog:react18"
}
}
```
### Example: catalog in root `package.json`
Root `package.json`:
```json
{
"catalog": {
"react": "^19.0.0"
},
"catalogs": {
"testing": {
"jest": "^30.0.0"
}
}
}
```
Root `deno.json`:
```jsonc
{
"workspace": ["./packages/a"]
// no catalog here; package.json's catalog is used
}
```
When both `deno.json` and `package.json` define catalogs,
`package.json` wins entirely (no merging between them).
Closes #30403
---------
Co-authored-by: lunadogbot <lunadogbot@users.noreply.github.com>