UNPKG

everything-dev

Version:

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

117 lines (115 loc) • 5.37 kB
import { MISSING_CONFIG_MESSAGE, readAuthoredConfigInput } from "../resolution/load.mjs"; import { openResolution } from "../resolution/session.mjs"; import { timePhase } from "../../utils/timing.mjs"; import { syncResolvedSharedDeps } from "../workspace/shared-deps.mjs"; import { saveBosConfig } from "../../utils/save-config.mjs"; import { runTypesGen } from "./init.mjs"; import { syncTemplate } from "./sync.mjs"; import { formatUnknownError } from "../../utils/errors.mjs"; import { migrateChildRootPackageJson } from "./upgrade-packages.mjs"; import { buildChangelogUrl } from "./upgrade-source.mjs"; import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; //#region src/services/scaffold/upgrade-migration.ts /** * Surgical platform-pin edit on the authored `bos.app.ts` source: rewrites * only the `platform: "…"` line (or inserts one after `extends`), leaving * every other byte — formatting, comments, attachments — untouched. Throws * with the version to set by hand when no safe anchor exists; the authored * file is never re-serialized. */ function stampPlatformPin(source, version) { const platformLineRe = /^([ \t]*)(platform|"platform")[ \t]*:[ \t]*"([^"]*)"(,?)[ \t]*$/; const extendsLineRe = /^([ \t]*)(extends|"extends")[ \t]*:[ \t]*("[^"]*")(,?)[ \t]*$/; const lines = source.split("\n"); for (let i = 0; i < lines.length; i++) { const match = lines[i].match(platformLineRe); if (match) { lines[i] = `${match[1]}platform: "${version}"${match[4]}`; return lines.join("\n"); } } for (let i = 0; i < lines.length; i++) { const match = lines[i].match(extendsLineRe); if (match) { lines[i] = `${match[1]}${match[2]}: ${match[3]},`; lines.splice(i + 1, 0, `${match[1]}platform: "${version}",`); return lines.join("\n"); } } throw new Error(`could not stamp the platform pin surgically — no platform or extends property found in the authored config; set platform: "${version}" in bos.app.ts manually and re-run`); } /** * Stamp the resolved framework version into the authored config's `platform` * pin (ADR 0034). Runs at the top of the migration phase on both the inline * and re-exec'd paths, so everyday resolution (sync, init, runtime) always * freezes to the release this upgrade just landed from — whether or not the * package versions actually moved. The authored `bos.app.ts` is edited * surgically (the pin line only) — never re-serialized. */ async function applyPlatformPin(projectDir, parentSource) { const version = parentSource.platform.manifest.frameworkVersion; const config = await readAuthoredConfigInput(projectDir); if (!config) return void 0; if (config.platform === version) return void 0; const appConfigPath = join(projectDir, "bos.app.ts"); if (existsSync(appConfigPath)) { const source = readFileSync(appConfigPath, "utf-8"); writeFileSync(appConfigPath, stampPlatformPin(source, version)); return "bos.app.ts"; } config.platform = version; await saveBosConfig(projectDir, config); return "bos.config.json"; } /** * The re-exec'd migration child runs `--json`, so its stdout is the structured * result — a failed run carries the real failure in its `error` field, which * raw stderr/stdout concatenation hides behind `[CLI] [object Object]`. */ function describeMigrationFailure(stdout, stderr) { if (stdout) try { const parsed = JSON.parse(stdout); if (parsed && typeof parsed === "object" && parsed.error != null) return formatUnknownError(parsed.error); } catch {} const raw = (stderr || stdout || "").trim(); return raw.length > 0 ? raw : "Unknown error"; } async function runMigrationPhase(projectDir, options, parentSource, scope) { const timings = []; const platformPinned = await timePhase(timings, "stamp platform pin", () => applyPlatformPin(projectDir, parentSource)); const migratedRootPackageJson = await timePhase(timings, "migrate root package", () => migrateChildRootPackageJson(projectDir)); let syncResult; if (scope.versionDelta && !options.noSync) { syncResult = await timePhase(timings, "sync template", () => syncTemplate(projectDir, { dryRun: false, noInstall: true, force: options.force })); if (syncResult?.status === "conflicted") throw new Error(syncResult.error ?? "Template sync hit unresolvable local conflicts"); } await timePhase(timings, "sync shared deps", async () => { const configSession = await openResolution({ cwd: projectDir }); if (!configSession?.config) throw new Error(`${MISSING_CONFIG_MESSAGE} in current directory`); return syncResolvedSharedDeps({ configDir: projectDir, hostMode: "local", bosConfig: configSession.config }); }); if (!options.noInstall) await timePhase(timings, "generate types", () => runTypesGen(projectDir)); const migratedFiles = [...platformPinned ? [platformPinned] : [], ...migratedRootPackageJson ? ["package.json"] : []]; const mainPkg = parentSource.catalog["everything-dev"]; const changelogUrl = mainPkg ? buildChangelogUrl(void 0, mainPkg, parentSource.repository) : void 0; return { status: "upgraded", packages: [], sync: syncResult, migrated: migratedFiles.length > 0 ? migratedFiles : void 0, timings, changelogUrl }; } //#endregion export { applyPlatformPin, describeMigrationFailure, runMigrationPhase, stampPlatformPin }; //# sourceMappingURL=upgrade-migration.mjs.map