From 70f289005eb133a443fb713a454d00d40f9d4403 Mon Sep 17 00:00:00 2001 From: userquin Date: Sun, 26 Jan 2025 20:01:45 +0100 Subject: [PATCH 1/4] fix(cli): handle pnpm catalogs --- packages/cli/catalog.test.ts | 53 +++++++++++++++++++++++++++ packages/cli/catalog.ts | 69 ++++++++++++++++++++++++++++++++++++ packages/cli/index.ts | 6 ++++ packages/cli/package.json | 1 + pnpm-lock.yaml | 18 ++++++++++ 5 files changed, 147 insertions(+) create mode 100644 packages/cli/catalog.test.ts create mode 100644 packages/cli/catalog.ts diff --git a/packages/cli/catalog.test.ts b/packages/cli/catalog.test.ts new file mode 100644 index 00000000..43708212 --- /dev/null +++ b/packages/cli/catalog.test.ts @@ -0,0 +1,53 @@ +import { it, expect } from "vitest"; +import { PackageJson } from "pkg-types"; +import { createResolver } from "./catalog"; + +it("pnpm catalogs", () => { + const json: PackageJson = { + dependencies: { + a: "catalog:", + b: "catalog:named", + }, + devDependencies: { + c: "catalog:", + d: "catalog:named", + }, + peerDependencies: { + e: "catalog:", + f: "catalog:named1", + }, + }; + + createResolver({ + catalog: { + a: "1.0.0", + c: "2.0.0", + e: "3.0.0", + }, + catalogs: { + named: { + b: "1.0.0", + d: "2.0.0", + }, + named1: { + f: "1.0.0", + }, + }, + })(json); + expect(json).toMatchInlineSnapshot(` + { + "dependencies": { + "a": "1.0.0", + "b": "1.0.0", + }, + "devDependencies": { + "c": "2.0.0", + "d": "2.0.0", + }, + "peerDependencies": { + "e": "3.0.0", + "f": "1.0.0", + }, + } + `); +}); diff --git a/packages/cli/catalog.ts b/packages/cli/catalog.ts new file mode 100644 index 00000000..61f0e381 --- /dev/null +++ b/packages/cli/catalog.ts @@ -0,0 +1,69 @@ +import fs from "node:fs"; +import path from "node:path"; +import readYamlFile from "read-yaml-file"; +import { PackageJson } from "pkg-types"; + +interface PnpmWorkspaceYaml { + catalog?: Record; + catalogs?: Record>; +} + +// https://github.com/pnpm/rfcs/blob/main/text/0001-catalogs.md +export function createResolver(rootYaml: PnpmWorkspaceYaml) { + const catalog = rootYaml.catalog ?? {}; + const catalogs = rootYaml.catalogs ?? {}; + + // see + function resolveVersion(name: string, version: string) { + if (!version.startsWith("catalog:")) { + return version; + } + + const useName = version.slice("catalog:".length).trim(); + if (useName.length === 0 || useName === "default") { + const catalogVersion = catalog[name]; + if (!catalogVersion) { + throw new Error(`Missing pnpm catalog version for ${name}`); + } + return catalogVersion; + } + + const catalogVersion = catalogs[useName]?.[name]; + if (!catalogVersion) { + throw new Error( + `Missing pnpm catalog version for ${name} in catalogs.${useName}`, + ); + } + return catalogVersion; + } + + function resolveCatalogVersions(pJson: PackageJson) { + // TODO: should we also include overrides, resolutions, and pnpm.overrides? + for (const depObjKey of [ + "dependencies", + "devDependencies", + "peerDependencies", + ]) { + const depObj = pJson[depObjKey]; + if (!depObj) { + continue; + } + for (const depName of Object.keys(depObj)) { + depObj[depName] = resolveVersion(depName, depObj[depName]); + } + } + } + + return resolveCatalogVersions; +} + +export async function loadCatalogs(root = process.cwd()) { + const pnpmWorkspace = path.resolve(root, "pnpm-workspace.yaml"); + if (!fs.existsSync(pnpmWorkspace)) { + return undefined; + } + + const rootYaml: PnpmWorkspaceYaml = await readYamlFile(pnpmWorkspace); + + return createResolver(rootYaml); +} diff --git a/packages/cli/index.ts b/packages/cli/index.ts index 5f964014..238b4eaa 100644 --- a/packages/cli/index.ts +++ b/packages/cli/index.ts @@ -20,6 +20,7 @@ import pkg from "./package.json" with { type: "json" }; import { isBinaryFile } from "isbinaryfile"; import { writePackageJSON, type PackageJson } from "pkg-types"; import { createDefaultTemplate } from "./template"; +import { loadCatalogs } from "./catalog"; declare global { var API_URL: string; @@ -314,6 +315,9 @@ const main = defineCommand({ } } + const resolveCatalogVersions = isPnpm + ? await loadCatalogs(process.cwd()) + : null; const restoreMap = new Map< string, Awaited> @@ -333,6 +337,8 @@ const main = defineCommand({ continue; } + resolveCatalogVersions?.(pJson); + restoreMap.set( p, await writeDeps(p, pJsonContents, pJson, deps, realDeps), diff --git a/packages/cli/package.json b/packages/cli/package.json index f7b177ee..c9b757b5 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -27,6 +27,7 @@ "isbinaryfile": "^5.0.2", "pkg-types": "^1.1.1", "query-registry": "^3.0.1", + "read-yaml-file": "^2.1.0", "tinyglobby": "^0.2.9" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f2a93223..1b57819a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,6 +155,9 @@ importers: query-registry: specifier: ^3.0.1 version: 3.0.1 + read-yaml-file: + specifier: ^2.1.0 + version: 2.1.0 tinyglobby: specifier: ^0.2.9 version: 0.2.9 @@ -3520,6 +3523,10 @@ packages: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} + read-yaml-file@2.1.0: + resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} + engines: {node: '>=10.13'} + readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -3854,6 +3861,10 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -7641,6 +7652,11 @@ snapshots: parse-json: 5.2.0 type-fest: 0.6.0 + read-yaml-file@2.1.0: + dependencies: + js-yaml: 4.1.0 + strip-bom: 4.0.0 + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -8045,6 +8061,8 @@ snapshots: strip-bom@3.0.0: {} + strip-bom@4.0.0: {} + strip-final-newline@2.0.0: {} strip-final-newline@3.0.0: {} From f54a6b1f303377e9982cbdce5407d1646e43a34d Mon Sep 17 00:00:00 2001 From: userquin Date: Sun, 26 Jan 2025 20:10:18 +0100 Subject: [PATCH 2/4] chore: cleanup --- packages/cli/catalog.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli/catalog.ts b/packages/cli/catalog.ts index 61f0e381..d217ed5c 100644 --- a/packages/cli/catalog.ts +++ b/packages/cli/catalog.ts @@ -13,7 +13,6 @@ export function createResolver(rootYaml: PnpmWorkspaceYaml) { const catalog = rootYaml.catalog ?? {}; const catalogs = rootYaml.catalogs ?? {}; - // see function resolveVersion(name: string, version: string) { if (!version.startsWith("catalog:")) { return version; From 8d57d475cbb31b0dca22818fa167ae8ef831ea4c Mon Sep 17 00:00:00 2001 From: userquin Date: Sun, 26 Jan 2025 20:37:30 +0100 Subject: [PATCH 3/4] chore: . --- packages/cli/catalog.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/cli/catalog.ts b/packages/cli/catalog.ts index d217ed5c..c2e64cc8 100644 --- a/packages/cli/catalog.ts +++ b/packages/cli/catalog.ts @@ -18,8 +18,8 @@ export function createResolver(rootYaml: PnpmWorkspaceYaml) { return version; } - const useName = version.slice("catalog:".length).trim(); - if (useName.length === 0 || useName === "default") { + const useCatalog = version.slice("catalog:".length).trim(); + if (useCatalog.length === 0 || useCatalog === "default") { const catalogVersion = catalog[name]; if (!catalogVersion) { throw new Error(`Missing pnpm catalog version for ${name}`); @@ -27,10 +27,10 @@ export function createResolver(rootYaml: PnpmWorkspaceYaml) { return catalogVersion; } - const catalogVersion = catalogs[useName]?.[name]; + const catalogVersion = catalogs[useCatalog]?.[name]; if (!catalogVersion) { throw new Error( - `Missing pnpm catalog version for ${name} in catalogs.${useName}`, + `Missing pnpm catalog version for ${name} in catalogs.${useCatalog}`, ); } return catalogVersion; @@ -62,7 +62,5 @@ export async function loadCatalogs(root = process.cwd()) { return undefined; } - const rootYaml: PnpmWorkspaceYaml = await readYamlFile(pnpmWorkspace); - - return createResolver(rootYaml); + return createResolver(await readYamlFile(pnpmWorkspace)); } From 2685ab129e1af61c9ecf3e12b56e28f18ee9d001 Mon Sep 17 00:00:00 2001 From: userquin Date: Sun, 26 Jan 2025 20:40:39 +0100 Subject: [PATCH 4/4] chore: add `catalog:default` test --- packages/cli/catalog.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/catalog.test.ts b/packages/cli/catalog.test.ts index 43708212..014a9394 100644 --- a/packages/cli/catalog.test.ts +++ b/packages/cli/catalog.test.ts @@ -13,7 +13,7 @@ it("pnpm catalogs", () => { d: "catalog:named", }, peerDependencies: { - e: "catalog:", + e: "catalog:default", f: "catalog:named1", }, };