Buckets:
| import path from "node:path" | |
| const args = process.argv.slice(2) | |
| const usage = "Usage: bun run script/upgrade-opentui.ts [--snapshot] <version>" | |
| if (args.includes("--help") || args.includes("-h")) { | |
| console.log(usage) | |
| process.exit(0) | |
| } | |
| const snapshotArg = args.find((arg) => arg.startsWith("--snapshot=")) | |
| const snapshot = args.includes("--snapshot") || snapshotArg !== undefined | |
| const unknown = args.find((arg) => arg.startsWith("-") && arg !== "--snapshot" && !arg.startsWith("--snapshot=")) | |
| if (unknown) { | |
| console.error(`Unknown option: ${unknown}`) | |
| console.error(usage) | |
| process.exit(1) | |
| } | |
| const positional = args.filter((arg) => arg !== "--snapshot" && !arg.startsWith("--snapshot=")) | |
| const raw = snapshotArg?.slice("--snapshot=".length) || positional[0] | |
| if (!raw || positional.length > (snapshotArg ? 0 : 1)) { | |
| console.error(usage) | |
| process.exit(1) | |
| } | |
| if (snapshotArg === "--snapshot=") { | |
| console.error("Missing snapshot version") | |
| console.error(usage) | |
| process.exit(1) | |
| } | |
| const ver = raw.replace(/^v/, "") | |
| const root = path.resolve(import.meta.dir, "..") | |
| const lockfile = path.join(root, "bun.lock") | |
| const skip = new Set([".git", ".opencode", ".turbo", "dist", "node_modules"]) | |
| const keys = ["@opentui/core", "@opentui/keymap", "@opentui/solid"] as const | |
| const files = (await Array.fromAsync(new Bun.Glob("**/package.json").scan({ cwd: root }))).filter( | |
| (file) => !file.split("/").some((part) => skip.has(part)), | |
| ) | |
| const setVersion = (cur: string, kind: "dep" | "peer") => { | |
| if (cur === "catalog:" || cur.startsWith("workspace:")) return cur | |
| if (snapshot) return ver | |
| if (kind === "peer") return `>=${ver}` | |
| if (cur.startsWith(">=")) return `>=${ver}` | |
| if (cur.startsWith("^")) return `^${ver}` | |
| if (cur.startsWith("~")) return `~${ver}` | |
| return ver | |
| } | |
| const editDeps = (obj: unknown, kind: "dep" | "peer") => { | |
| if (!obj || typeof obj !== "object") return false | |
| const map = obj as Record<string, unknown> | |
| return keys | |
| .map((key) => { | |
| const cur = map[key] | |
| if (typeof cur !== "string") return false | |
| const next = setVersion(cur, kind) | |
| if (next === cur) return false | |
| map[key] = next | |
| return true | |
| }) | |
| .some(Boolean) | |
| } | |
| const editCatalog = (obj: unknown) => { | |
| if (!obj || typeof obj !== "object") return false | |
| const map = obj as Record<string, unknown> | |
| return keys | |
| .map((key) => { | |
| const cur = map[key] | |
| if (typeof cur !== "string" || cur === ver) return false | |
| map[key] = ver | |
| return true | |
| }) | |
| .some(Boolean) | |
| } | |
| const editOverrides = (obj: unknown) => { | |
| if (!obj || typeof obj !== "object") return false | |
| const map = obj as Record<string, unknown> | |
| return keys | |
| .map((key) => { | |
| const cur = map[key] | |
| if (typeof cur !== "string") return false | |
| const next = snapshot ? ver : "catalog:" | |
| if (next === cur) return false | |
| map[key] = next | |
| return true | |
| }) | |
| .some(Boolean) | |
| } | |
| const out = ( | |
| await Promise.all( | |
| files.map(async (rel) => { | |
| const file = path.join(root, rel) | |
| const txt = await Bun.file(file).text() | |
| const json = JSON.parse(txt) | |
| const hit = [ | |
| editCatalog(json.workspaces?.catalog), | |
| editOverrides(json.overrides), | |
| editDeps(json.dependencies, "dep"), | |
| editDeps(json.devDependencies, "dep"), | |
| editDeps(json.peerDependencies, "peer"), | |
| ].some(Boolean) | |
| if (!hit) return null | |
| await Bun.write(file, `${JSON.stringify(json, null, 2)}\n`) | |
| return rel | |
| }), | |
| ) | |
| ).filter((item): item is string => item !== null) | |
| if (out.length === 0) { | |
| console.log(`No opentui manifest updates needed for ${ver}`) | |
| } | |
| if (out.length > 0) { | |
| console.log(`Updated opentui${snapshot ? " snapshot" : ""} to ${ver} in:`) | |
| for (const file of out) { | |
| console.log(`- ${file}`) | |
| } | |
| } | |
| console.log("Running bun install to update bun.lock...") | |
| const install = Bun.spawn([process.execPath, "install"], { | |
| cwd: root, | |
| stdout: "inherit", | |
| stderr: "inherit", | |
| }) | |
| const installCode = await install.exited | |
| if (installCode !== 0) process.exit(installCode) | |
| const fixed = await fixKnownLockfileIssues() | |
| if (fixed.length > 0) { | |
| console.log("Removed stale opentui-spinner peer lockfile entries:") | |
| for (const item of fixed) { | |
| console.log(`- ${item}`) | |
| } | |
| } | |
| const stale = await findStaleLockfileEntries() | |
| if (stale.length > 0) { | |
| console.error(`bun.lock still contains stale opentui versions after upgrading to ${ver}:`) | |
| for (const item of stale) { | |
| console.error(`- ${item.entry}: ${item.pkg}@${item.version}`) | |
| } | |
| process.exit(1) | |
| } | |
| console.log("bun.lock opentui versions are consistent") | |
| async function fixKnownLockfileIssues() { | |
| const txt = await Bun.file(lockfile).text() | |
| const stale = findStaleLockfileEntriesInText(txt) | |
| if (stale.length === 0) return [] | |
| if (stale.some((item) => !item.entry.startsWith("opentui-spinner/@opentui/"))) return [] | |
| const removed = txt | |
| .split("\n") | |
| .map((line) => line.match(/^ "(opentui-spinner\/@opentui\/[^\"]+)": /)?.[1]) | |
| .filter((item): item is string => item !== undefined) | |
| if (removed.length === 0) return [] | |
| await Bun.write( | |
| lockfile, | |
| txt | |
| .split("\n") | |
| .filter((line) => !line.match(/^ "opentui-spinner\/@opentui\//)) | |
| .join("\n"), | |
| ) | |
| return removed | |
| } | |
| async function findStaleLockfileEntries() { | |
| return findStaleLockfileEntriesInText(await Bun.file(lockfile).text()) | |
| } | |
| function findStaleLockfileEntriesInText(txt: string) { | |
| return Array.from(txt.matchAll(/^ "([^"]+)": \["(@opentui\/(?:core(?:-[^@"]+)?|keymap|solid))@([^"]+)"/gm)) | |
| .map((match) => ({ | |
| entry: match[1]!, | |
| pkg: match[2]!, | |
| version: match[3]!, | |
| })) | |
| .filter((item) => item.version !== ver) | |
| } | |
Xet Storage Details
- Size:
- 5.75 kB
- Xet hash:
- 5a969e2c0232d77e23e4880ea53aff3b9c7c80e9047708e3c7c859f91113c663
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.