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