resolveBuild
uses node:path
The tcm.js a build's name means: known-good, previous, a kept build's id (the history entry it was made from:
a prefix will do, # or not), or a path (a tcm.js, or a build's directory).
import { buildBin, keptBuilds } from "#codebase/tcmts/cli/builds"; import { UsageError } from "#codebase/tcmts/cli/ui"; import fs from "node:fs"; import path from "node:path"; export function resolveBuild(spec: string, root: string | undefined, cwd = process.cwd()): string { const builds = root ? keptBuilds(root) : []; const id = spec.replace(/^#/, ""); const named = builds.filter( (b) => b.name === spec || (spec === "known-good-previous" && b.name === "previous") || (/^[a-z2-7]{4,}$/.test(id) && b.head?.startsWith(id)), ); const distinct = [...new Map(named.map((b) => [b.bin, b])).values()]; if (distinct.length === 1) return distinct[0]!.bin; if (distinct.length > 1) throw new UsageError( `${spec} is ${distinct.map((b) => b.name).join(" and ")}: give more of the hash`, ); const given = path.resolve(cwd, spec); if (fs.existsSync(given)) { if (fs.statSync(given).isFile()) return given; const bin = buildBin(given); if (bin) return bin; throw new UsageError(`${spec} isn't a tcm build: it has no node_modules/tcm-ts/bin/tcm.js`); } throw new UsageError( root ? `no build ${spec}: ${builds.length ? `kept are ${builds.map((b) => b.name).join(", ")}` : "none are kept"} (\`tcm builds\` lists them; \`tcm builds make <entry>\` makes one from a history entry)` : `no build ${spec}, and no builds are kept here: TCM_BUILDS names the directory they're in (tcm's repository's dist/), or give a tcm.js`, ); }