UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

1 lines • 9.2 kB
{"version":3,"file":"catalog-overrides.mjs","names":[],"sources":["../../../src/services/workspace/catalog-overrides.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parse, stringify } from \"yaml\";\nimport { compareSemver } from \"../../utils/semver\";\n\n/** The pnpm pin stamped into every generated and migrated child workspace.\n * Drift-guarded against the parent's own `packageManager` pin by\n * tests/unit/pnpm-pin.test.ts. */\nexport const PNPM_PACKAGE_MANAGER = \"pnpm@12.16.1\";\n\n/**\n * Workspace keys whose `overrides:` row must equal the catalog row. An\n * override that re-pins a version literal drifts the moment the catalog\n * moves (#659/#682): the next build resolves the old version while the\n * upgrade reports success, and the #546 skew gate fails with a message\n * pointing at the wrong fix. Reconciliation (init, sync, upgrade) rewrites\n * these rows from the catalog on every run; keys absent from the catalog are\n * left unpinned.\n */\nexport const CATALOG_PINNED_OVERRIDE_KEYS = [\n \"@better-fetch/fetch\",\n \"@rspack/core\",\n \"@tanstack/react-router\",\n \"@tanstack/router-core\",\n \"better-auth\",\n \"react\",\n \"react-dom\",\n] as const;\n\n/**\n * Framework packages children install from the registry. A freshly released\n * version is younger than pnpm's `minimumReleaseAge` holdback (1440 default),\n * so each current version is excluded by exact `name@version` — derived from\n * the catalog on every write, never frozen. `minimumReleaseAgeExcludePrune`\n * makes pnpm drop entries the lockfile no longer resolves, so the list\n * replaces instead of accumulating (#659's append trap).\n */\nexport const FRAMEWORK_EXCLUDE_PACKAGES = [\n \"better-near-auth\",\n \"every-plugin\",\n \"everything-dev\",\n] as const;\n\nexport interface WorkspaceYamlDoc {\n catalog?: Record<string, string>;\n overrides?: Record<string, string>;\n minimumReleaseAgeExclude?: string[];\n minimumReleaseAgeExcludePrune?: boolean;\n [key: string]: unknown;\n}\n\nconst EXACT_VERSION_RE = /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$/;\n\n/**\n * pnpm's minimumReleaseAgeExclude accepts exact versions only — a range like\n * `everything-dev@^2.0.0-rc.27` is a hard config error (\"Use exact versions\n * only\") that fails every install. The catalog's framework rows are ranges;\n * strip the operator. A specifier that doesn't reduce to an exact version is\n * skipped rather than emitted invalid.\n */\nexport function exactReleaseAgeExclude(name: string, specifier: string): string | undefined {\n const exact = specifier\n .trim()\n .replace(/^[~^=]+/, \"\")\n .trim();\n return EXACT_VERSION_RE.test(exact) ? `${name}@${exact}` : undefined;\n}\n\nexport function frameworkReleaseAgeExcludes(catalog: Record<string, string>): string[] {\n return FRAMEWORK_EXCLUDE_PACKAGES.flatMap((name) => {\n const specifier = catalog[name];\n if (!specifier) return [];\n const entry = exactReleaseAgeExclude(name, specifier);\n return entry ? [entry] : [];\n });\n}\n\n/**\n * Mutates the parsed workspace doc: overrides for the catalog-pinned keys\n * follow the catalog rows, framework release-age excludes are regenerated\n * from the catalog's current framework versions (non-framework entries the\n * child authored are preserved), and the prune flag is set. Returns whether\n * anything changed.\n */\nexport function reconcileWorkspacePosture(doc: WorkspaceYamlDoc): boolean {\n let changed = false;\n const catalog = doc.catalog ?? {};\n\n const overrides: Record<string, string> = { ...doc.overrides };\n let overridesChanged = false;\n for (const key of CATALOG_PINNED_OVERRIDE_KEYS) {\n if (catalog[key] && overrides[key] !== catalog[key]) {\n overrides[key] = catalog[key];\n overridesChanged = true;\n }\n }\n if (overridesChanged) {\n doc.overrides = Object.fromEntries(\n Object.entries(overrides).sort(([a], [b]) => a.localeCompare(b)),\n );\n changed = true;\n }\n\n const excludes = frameworkReleaseAgeExcludes(catalog);\n if (excludes.length > 0) {\n const retained = (doc.minimumReleaseAgeExclude ?? []).filter(\n (entry) => !FRAMEWORK_EXCLUDE_PACKAGES.some((name) => entry.startsWith(`${name}@`)),\n );\n const merged = [...new Set([...retained, ...excludes])].sort();\n if (JSON.stringify(merged) !== JSON.stringify(doc.minimumReleaseAgeExclude ?? [])) {\n doc.minimumReleaseAgeExclude = merged;\n changed = true;\n }\n if (doc.minimumReleaseAgeExcludePrune !== true) {\n doc.minimumReleaseAgeExcludePrune = true;\n changed = true;\n }\n }\n\n return changed;\n}\n\n/**\n * File-level reconciliation for an existing child workspace: reads\n * pnpm-workspace.yaml, applies `reconcileWorkspacePosture`, writes back only\n * on change. Catalog-sync callers run this after writing new catalog rows so\n * the child's overrides and release-age excludes land with the versions they\n * must agree with.\n */\nexport function reconcileWorkspaceYamlFile(root: string): boolean {\n const yamlPath = join(root, \"pnpm-workspace.yaml\");\n if (!existsSync(yamlPath)) return false;\n\n let doc: WorkspaceYamlDoc;\n try {\n const parsed = parse(readFileSync(yamlPath, \"utf8\")) as WorkspaceYamlDoc | null;\n if (!parsed || typeof parsed !== \"object\") return false;\n doc = parsed;\n } catch {\n return false;\n }\n if (!doc.catalog) return false;\n\n if (!reconcileWorkspacePosture(doc)) return false;\n writeFileSync(yamlPath, `${stringify(doc, { lineWidth: 0 }).trimEnd()}\\n`);\n return true;\n}\n\n/**\n * Forward-only pnpm pin propagation for existing children: `migrate-pnpm` is\n * a one-time v1→v2 step and `bos init` only writes new projects, so without\n * this a parent toolchain bump never reaches already-v2 workspaces. Never\n * downgrades (a child pinning newer than the parent keeps its pin) and never\n * touches non-pnpm managers (the bun cutover is migrate-pnpm's job).\n */\nexport function reconcilePackageManagerPin(pkgPath: string): boolean {\n if (!existsSync(pkgPath)) return false;\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as Record<string, unknown>;\n } catch {\n return false;\n }\n\n const current = typeof pkg.packageManager === \"string\" ? pkg.packageManager : undefined;\n if (current !== undefined && !current.startsWith(\"pnpm@\")) return false;\n\n const currentVersion = current?.slice(\"pnpm@\".length).split(\"+\")[0];\n const targetVersion = PNPM_PACKAGE_MANAGER.slice(\"pnpm@\".length).split(\"+\")[0];\n if (currentVersion !== undefined && compareSemver(currentVersion, targetVersion) >= 0) {\n return false;\n }\n\n pkg.packageManager = PNPM_PACKAGE_MANAGER;\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n return true;\n}\n"],"mappings":"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"}