usageError
uses tcmts.cli.help.usageOf, which uses tcmts.cli.style.bold, which uses node:util
A parse failure, as tcm reports it: what's wrong (with the option meant, for one that's misspelt), and what the
command takes, as its help says: its usage, arguments and options. --branch given to a command that changes
things is refused, saying which commands read another branch.
import { routes } from "#codebase/tcmts/cli/commands"; import { childrenOf, distance, lookup, paramsOf, readsBranches, usageOf } from "#codebase/tcmts/cli/help"; import { dim } from "#codebase/tcmts/cli/style"; export function usageError( path: string[], result: { code: string; issues?: { message: string; path?: unknown[] }[] }, ): string { const where = `tcm ${path.join(" ")}`.trim(); const unexpected = (result.issues ?? []).flatMap( (i) => /^unexpected "(.*)"$/.exec(i.message)?.[1] ?? [], ); // `tcm update --branch <b>`: reading another branch is for the commands that only read if (path.length && !readsBranches(path) && unexpected.some((u) => /^--branch(=|$)/.test(u))) { const readers = Object.keys(routes()).filter((c) => readsBranches([c])); return `${where}: --branch reads another branch, changing nothing, so only the commands that just read take it (${readers.join(", ")}); ${where} would change the codebase. To work on that branch, \`tcm switch <branch>\` (a session moves with tcm_cli ["switch", "<branch>"])`; } const options = path.length ? paramsOf(lookup(path)) .filter((p) => p.cli.syntax!.type === "option") .map((p) => p.name) : []; const problems = result.code === "method-not-allowed" ? [ `${where} needs a command: ${childrenOf(lookup(path)) .map((c) => c.name) .join(", ")}`, ] : (result.issues ?? []).map((i) => { const param = Array.isArray(i.path) && i.path.length ? String(i.path.at(-1)) : undefined; if (param) return `${param}: ${i.message}`; const given = /^unexpected "(.*)"$/.exec(i.message)?.[1]; if (given === undefined) return i.message; // a misspelt option: the one meant (few letters off: a command has a few options, and `--prot` is one // swap from `--port`) const option = /^--(?:no-)?([^=]+)/.exec(given)?.[1]; const scored = option ? [...options, "help"].map((o) => [o, distance(option, o)] as const) : []; const best = Math.min(...scored.map(([, d]) => d)); const meant = option && best <= Math.max(1, Math.floor(option.length / 2)) ? scored.filter(([, d]) => d === best).map(([o]) => o) : []; return `unexpected '${given}'${meant.length ? ` (did you mean ${meant.map((m) => `--${m}`).join(" or ")}?)` : ""}`; }); // `tcm run lib.main --verbose`: the flag is for the definition const flag = path[0] === "run" && unexpected.some((u) => u.startsWith("-")); return `${where}: ${problems.join("; ")}${flag ? " (arguments for the definition that start with - go after `--`)" : ""}${path.length ? `\n\n${usageOf(path).join("\n")}` : `\n${dim(`see \`${where} --help\``)}`}`; }
uses
- tcmts.cli.commands.routes
- tcmts.cli.help.childrenOf
- tcmts.cli.help.distance
- tcmts.cli.help.lookup
- tcmts.cli.help.paramsOf
- tcmts.cli.help.readsBranches
- tcmts.cli.help.usageOf
- tcmts.cli.style.dim