UNPKG

everything-dev

Version:

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

205 lines (203 loc) • 8.17 kB
const require_fs = require('../fs.cjs'); let effect = require("effect"); //#region src/services/scaffold/upgrade-audit/steps/retire-zephyr.ts /** * Strips ZephyrCloud deploy wiring from a v1 child tree. v2 deploys run * through the `bos deploy` train (build → bundle upload to the R2-backed * store → FastKV publish → image → Railway); the per-build Zephyr hooks * and their `reportDeployResult` writes into `bos.config.json` are v1-only * machinery. The production URLs currently in the authored descriptor come * from the last v1 Zephyr deploys and are replaced by the first v2 deploy. * * Handles three shapes: * - ui/rsbuild.config.ts: statement blocks * `if (shouldDeploy) { plugins.push(withZephyr({ ... })); }` * plus the now-unused integrity import and bos-config path const. * - api/plugins rspack configs: conditional default export * `export default shouldDeploy ? withZephyr({ ... }) : baseConfig` * unwrapped to `export default baseConfig`. * - package.json deps: `zephyr-rsbuild-plugin` / `zephyr-rspack-plugin` * removed from every workspace package.json and the root catalog. * * `shouldDeploy` itself is kept where it still gates non-Zephyr behavior * (e.g. `devtool: shouldDeploy ? false : "source-map"`). */ const ZEPHYR_DEPS = ["zephyr-rsbuild-plugin", "zephyr-rspack-plugin"]; /** Extracts a balanced-brace block starting at `openIdx` (on "{"). */ const extractBlock = (source, openIdx) => { let depth = 0; for (let i = openIdx; i < source.length; i++) { const ch = source[i]; if (ch === "{") depth++; else if (ch === "}") { depth--; if (depth === 0) return source.slice(openIdx, i + 1); } } return null; }; /** Removes whole `if (shouldDeploy) { ... withZephyr ... }` statement blocks. */ const removeZephyrIfBlocks = (source) => { let next = source; let removed = 0; for (;;) { const ifStart = next.indexOf("if (shouldDeploy)"); if (ifStart === -1) break; const blockOpen = next.indexOf("{", ifStart); if (blockOpen === -1) break; const block = extractBlock(next, blockOpen); if (block === null || !block.includes("withZephyr")) break; let end = blockOpen + block.length; if (next[end] === "\n") end += 1; next = (next.slice(0, ifStart) + next.slice(end)).replace(/\n{3,}/g, "\n\n"); removed += 1; } return { next, removed }; }; /** * Unwraps `export default shouldDeploy ? withZephyr({ ... }) : BASE;` to * `export default BASE;`. Locates the ternary colon after the withZephyr * call closes (never inside the args object, where `key:` pairs live). */ const unwrapZephyrTernary = (source) => { const marker = source.indexOf("export default shouldDeploy"); if (marker === -1) return { next: source, unwrapped: false }; const withIdx = source.indexOf("withZephyr(", marker); if (withIdx === -1) return { next: source, unwrapped: false }; const callOpen = source.indexOf("{", withIdx); if (callOpen === -1) return { next: source, unwrapped: false }; const args = extractBlock(source, callOpen); if (args === null) return { next: source, unwrapped: false }; const closeParen = source.indexOf(")", callOpen + args.length); if (closeParen === -1) return { next: source, unwrapped: false }; const colonIdx = source.indexOf(":", closeParen); if (colonIdx === -1) return { next: source, unwrapped: false }; const semiIdx = source.indexOf(";", colonIdx); if (semiIdx === -1) return { next: source, unwrapped: false }; const base = source.slice(colonIdx + 1, semiIdx).trim(); if (!base || base.includes("withZephyr")) return { next: source, unwrapped: false }; return { next: `${source.slice(0, marker)}export default ${base};${source.slice(semiIdx + 1)}`, unwrapped: true }; }; const stripUnusedImports = (source, path, notes) => { let next = source; const zephyrImport = next.match(/import\s*\{\s*withZephyr\s*\}\s*from\s*"zephyr-(?:rsbuild|rspack)-plugin";\n/); if (zephyrImport && !next.replace(zephyrImport[0], "").includes("withZephyr")) { next = next.replace(zephyrImport[0], ""); notes.push(`${path}: removed the withZephyr import`); } const integrityImport = next.match(/import\s*\{([^}]*)\}\s*from\s*"everything-dev\/integrity";\n/); if (integrityImport) { const names = integrityImport[1].split(",").map((n) => n.trim().replace(/\s+as\s+\S+/, "")).filter(Boolean); const withoutImport = next.replace(integrityImport[0], ""); if (names.every((name) => !withoutImport.includes(name))) { next = next.replace(integrityImport[0], ""); notes.push(`${path}: removed the unused everything-dev/integrity import`); } } const declMatch = next.match(/const shouldDeploy = process\.env\.DEPLOY === "true";\n/); if (declMatch && !next.replace(declMatch[0], "").includes("shouldDeploy")) { next = next.replace(declMatch[0], ""); notes.push(`${path}: removed the unused shouldDeploy declaration`); } const bosPathMatch = next.match(/const uiBosConfigPath = [^\n]*;\n/); if (bosPathMatch && !next.replace(bosPathMatch[0], "").includes("uiBosConfigPath")) { next = next.replace(bosPathMatch[0], ""); notes.push(`${path}: removed the unused uiBosConfigPath declaration`); } return next.replace(/\n{3,}/g, "\n\n"); }; const rewriteBuildConfig = (projectDir, path, dryRun, notes) => effect.Effect.gen(function* () { if (!(yield* require_fs.fileExists(projectDir, path))) { notes.push(`${path} absent — skipped`); return false; } const source = yield* require_fs.readText(projectDir, path); if (!source.includes("withZephyr")) { notes.push(`${path}: no Zephyr wiring — left untouched`); return false; } let next = source; const ifBlocks = removeZephyrIfBlocks(next); next = ifBlocks.next; if (ifBlocks.removed > 0) notes.push(`${path}: removed ${ifBlocks.removed} withZephyr deploy block(s)`); const ternary = unwrapZephyrTernary(next); next = ternary.next; if (ternary.unwrapped) notes.push(`${path}: unwrapped the shouldDeploy ? withZephyr(...) : base default export`); next = stripUnusedImports(next, path, notes); if (next !== source && !dryRun) yield* require_fs.writeText(projectDir, path, next); return next !== source; }); const stripZephyrDeps = (projectDir, dryRun, notes) => effect.Effect.gen(function* () { const changed = []; const pkgPaths = yield* require_fs.listFiles(projectDir, "{package.json,ui/package.json,api/package.json,plugins/*/package.json}"); for (const pkgPath of pkgPaths) { const source = yield* require_fs.readText(projectDir, pkgPath); const pkg = JSON.parse(source); let modified = false; for (const dep of ZEPHYR_DEPS) for (const field of ["dependencies", "devDependencies"]) if (pkg[field] && dep in pkg[field]) { delete pkg[field][dep]; modified = true; notes.push(`${pkgPath}: removed ${dep} from ${field}`); } if (pkg.workspaces?.catalog) { for (const dep of ZEPHYR_DEPS) if (dep in pkg.workspaces.catalog) { delete pkg.workspaces.catalog[dep]; modified = true; notes.push(`${pkgPath}: removed ${dep} from the catalog`); } } if (modified) { if (!dryRun) yield* require_fs.writeText(projectDir, pkgPath, `${JSON.stringify(pkg, null, 2)}\n`); changed.push(pkgPath); } } return changed; }); const retireZephyr = (projectDir, dryRun) => effect.Effect.gen(function* () { const writes = []; const notes = ["v2 deploys run through the bos deploy train (bundle upload + FastKV publish + image + Railway) — Zephyr wiring is retired", "production URLs in the authored descriptor are from the last v1 Zephyr deploy; the first v2 deploy replaces them"]; const configs = ["ui/rsbuild.config.ts", "api/rspack.config.js"]; const pluginConfigs = yield* require_fs.listFiles(projectDir, "plugins/*/rspack.config.js"); for (const path of [...configs, ...pluginConfigs]) if (yield* rewriteBuildConfig(projectDir, path, dryRun, notes)) writes.push(path); const depChanges = yield* stripZephyrDeps(projectDir, dryRun, notes); writes.push(...depChanges); if (writes.length === 0) notes.push("no Zephyr wiring found — nothing to retire"); return { writes, deletes: [], notes }; }); //#endregion exports.retireZephyr = retireZephyr; //# sourceMappingURL=retire-zephyr.cjs.map