tcmts.cli.main

Types

PatchEdit type
A change `tcm patch` makes: text to find, and what replaces it.
PatchImport type
An import `tcm patch` is asked to add (`--imports`, or `imports` in a patch): `name` from `from`, as `as` if given; `default` is the module's default export, `*` the module itself (each named by `as`); `type` imports only its type.
Printed typeof Printed
Thrown to finish a command early with output already decided (help, the version).
Row type
Status type

Server

STATUS_LABEL Record<Status, string>
addToScratch (file: string, existing: string, text: string) => string
A scratch file's text with more definitions in it (`text`, rendered as `tcm edit` writes them): their imports after the file's own, without the names it imports already, and their code before the first of the file's that uses them (a constant's value, say: declared before it, as TypeScript wants), unless they use what's declared after that, else at the end. A name from a module the file imports from already joins that import, if it's the same kind (values, or types: `import type` stays apart); the file's imports from the codebase of what they declare go (it has them now: the names mean the same, as `tcm edit` writes them).
analyzeScratch (cb: Codebase, file: string, into?: string | undefined, text?: string | undefined, how?: { ignore?:…
Analyze a scratch file (`text` instead of what's in it, if given), saying what's wrong with it (a syntax error alone, as `syntaxError` says it: the rest follows from it). Undefined if something is; null if, given `how.own` (the file's own lines, the rest being definitions added to it), every problem is in what was added, and saying so is the caller's. `how.ignore`: diagnostics not to report, by code.
apply (cb: Codebase, rows: Row[], mode: "add" | "update", command: string, quiet: boolean) => boolean
Store the analyzed definitions and move names: `add` only binds free names, `update` also replaces.
applyPatch (text: string, edits: PatchEdit[], all?: boolean, from?: number, code?: string | undefined) => { te…
The edits made to `text`: each `old` must be in it exactly once (every one, with `all`), else why not. An `old` that's there only with other spacing or line breaks (the formatter rewraps lines) is found that way, if it's there once; one that isn't there at all is reported with the line most like its first (numbered from `from` + 1: the view's line of the text's first, less one). In code (`code`: the file name it's parsed as, `x.ts` or `x.tsx`), an `old` that's there only with its escapes taken the other way (`\n` or a line break, `` \` `` or `` ` ``, `\${` or `${`: what JSON makes easy to get wrong) is found that way, if it's there once, with `new` written the same way, and said in `notes`; and an edit that would end or split a template literal it's inside (a bare backtick or `${` in `new`) isn't made: why is the problem.
at (cb: Codebase, arg: string) => void
`tcm at <file>[:<line>]`: the definition at a line of a file, or every definition in it with its lines. For scratch files, definitions' documents, generated modules (what stack traces name) and mirrored files. A module named by its component's hash (`.tcm/cache/<hash>.ts`, a packed package's `modules/<hash>.js`, from a stack trace or a profile, here or elsewhere) is its component's: the member at the line, or every member. A view reads as its branch has it now (rendered: one only read, `--branch`, has no files written); another branch's view is that branch's to say (`--branch` it), not this one's.
blankImports (file: string, text: string, names: Set<string>) => string
A module's text without the imports of these names, blanked (spaces where they were) so nothing else moves.
brokenLiteral (before: string, after: string, places: { index: number; length: number; }[], inserted: string, fil…
Why an edit can't be made, if it would end or split a template literal it's inside: `before` and `after` it, the `places` in `before` it replaced with `inserted`. A literal an edit is inside (its backticks left as they were) is expected to end where it did, moved by what the edits changed, as parsing both (as `file`) finds; an edit that takes in a literal's backtick changes it on purpose. Lines are numbered from `from` + 1.
builtinImport (name: string) => string | undefined
Where a name Node's builtins export comes from (`stripVTControlCharacters` from `node:util`), when exactly one module exports it (`node:fs`, not `node:fs/promises` too: subpaths and deprecated modules aren't asked); undefined for a name none or several export.
causalArg (cb: Codebase, ref: string) => string
A history entry: a number, how many entries back on this branch (0 is the latest; `tcm history` shows them), or a hash from any branch's history (a prefix will do; the `#` is optional, since shells read `#…` as a comment).
check (cb: Codebase, file: string, into?: string | undefined, fresh?: boolean, imports?: "check" | "say")…
checkStoredRows (cb: Codebase, defs: DefRecord[]) => void
When a table's row type changes, look at what's already stored, now rather than in production: rows that still fit are fine as is, rows that don't need the table's `migrate`.
clearSaved (cb: Codebase, file: string, text: string) => { text: string; cleared: string[]; }
A scratch file's text without the definitions in it saved as they are (their docs and `// tcm: path` lines with them, and imports only they used), so it holds what's being worked on; one the rest of the file mentions (a watch expression, another definition) stays. The names taken out.
data (cb: Codebase, table?: string | undefined, limit?: number) => void
dependents (cb: Codebase, target: string) => void
What uses a definition now: the named definitions that refer to it (not the earlier versions of them, which the codebase keeps too), by name.
deps (cb: Codebase, target: string) => void
diff (cb: Codebase, args: string[]) => void
dispatch (argv: string[]) => Promise<number>
edit (cb: Codebase, targets: string[], target: string | ((jsx: boolean) => string), force: boolean) => v…
editFiles (cb: Codebase, targets: string[], file: string | undefined, force: boolean) => void
`tcm edit` of files kept as written: each into a file of its own, named as it is (or `file`). A Markdown file's first line says where it goes back; another's is saved with `--into`.
execute <R extends { ok: boolean; }>(cmd: string | undefined, r: R) => Extract<R, { method: "execute"; }>
A parsed command line, if it asks to run the command: help and the version are printed instead, and a line the command doesn't accept is a usage error saying why.
fileBindings (file: string, text: string, ns: string) => Map<string, string>
What each name a scratch file binds is: a definition of the codebase, by path (one it declares, at `<namespace>.<name>` or as its `// tcm: path` line says, or one it imports from `#codebase/…`), or an export of a module outside it (`node:path#join`). Two files binding a name alike mean the same by it.
fileRows (cb: Codebase, file: string, into: string | undefined, format?: string) => Row[] | undefined
A file kept as written (Markdown, CSS, an image), as `check` and `save` see it: its definition, where it goes (a Markdown file's first line `<!-- tcm: path a.b.c -->` or `<!-- tcm: namespace a.b -->`, or `--into` and its name), and whether that's new. Undefined, said why, if it can't be saved. `format` is what `keptAs` says.
find (cb: Codebase, query: string) => void
`tcm find <query>`: the names with every word of `query` in their name, signature or doc (any case), those it's in the name or signature of first; the docs are as this branch shows them (its text versions). A query starting with `:` is a type ([findByType]).
findByType (cb: Codebase, text: string) => void
`tcm find ':<type>'`: terms whose type has that shape (see search.ts).
followImports (cb: Codebase, file: string, into: string | undefined, how: "check" | "save" | "say") => void
Imports follow the code in a scratch file, as in `tcm patch`: names it uses and doesn't import are imported (importMissing, near its namespace: `into`, else its `// tcm: namespace` line), and imports nothing in it uses are taken out (pruneUnusedImports). How far depends on `how`: "save" does both, before it's saved; "check" imports, and says what saving would take out (an import not used yet may be for code still to be written); "say" changes nothing, saying what it would import (`tcm watch`: the file is open in an editor, being written). What it does, or would, is said; a file that isn't code is left alone.
followMirror (cb: Codebase) => void
Before a command: a mirror's files changed outside tcm (by hand, a git pull or checkout) are imported first, so the command sees them. Files that don't import yet (an edit half made) wait, and so does writing back to them. What it says goes to stderr: a command's output is its own.
formattedRows (cb: Codebase, file: string, into: string | undefined, a: Analysis, rows: Row[], analyzed?: string …
What's saved is formatted: the scratch file's definitions as oxfmt formats them (the file itself is left as written). Formatting can't change a hash, and the formatted text is only used if, analyzed again, every definition hashes the same. Where formatting one would change what it does (oxfmt breaks JSX text around a `{" "}`, giving React other children), that one is kept as written, and the rest formatted. `analyzed` is the text `a` is of, when that isn't the file's (the file with its cycles' other members): that's what's formatted and analyzed again.
header (cb: Codebase, hash: string) => string
history (cb: Codebase, limit: number, all?: boolean) => void
This branch's history, newest first: each entry, its message if it has one, the command that made it, and what it changed: by name, but for the definitions only re-hashed because a dependency changed, which are counted (a change to a definition much code uses re-hashes hundreds), unless `all`.
importAssets (cb: Codebase, root: string, dir: string, mode: "add" | "update", into: string, verbose: boolean, e…
Import the files under `root` that aren't code (Markdown, CSS, images), as files of the codebase: their hashes, by name, or undefined if they don't import. Following a mirror, one it hasn't changed stays as the codebase has it, and one gone from the files goes from the codebase.
importBindings (file: string, text: string) => Map<string, { module: string; type: boolean; }>
What a module's imports bind: local name → the module, and whether only as a type.
importFiles (cb: Codebase, root: string, dir: string, mode: "add" | "update", into: string, verbose: boolean, e…
Import the files under `root`: the hash of each definition's code in them, by path, or undefined if they don't import. Following a mirror's files, a definition they haven't changed since tcm last saw them stays as the codebase has it (it may have changed it while write-back waited), and one gone from the files goes from the codebase.
importMissing (cb: Codebase, file: string, near: string, write?: boolean) => string[]
Names a scratch file uses but doesn't have (TypeScript can't find them), imported: from the codebase, the one in `near`'s namespace; else from a package or module the file imports other names from already, if it exports it (`cli` from `@bomb.sh/router`); else the only definition by that name; from outside it (`spawnSync`, `createRequire`, `import os from "node:os"`), where the codebase's own code imports it from most; else a page or other file, if it's the only one by that name. The imports added (`write`: else only worked out, the file left as it is); a name with several candidates is left to say so.
importProject (cb: Codebase, dir: string, mode: "add" | "update", into: string, verbose: boolean, exclude: string…
importedPaths (file: string, text: string) => string[]
The codebase paths a scratch file imports (from `#codebase/a/b`: `a.b.<name>`).
jobs (cb: Codebase, r: Execute<"/", { "/": { n: number; }; }> | Execute<"/cancel", { "/": { n: number; }…
keptAs (cb: Codebase, file: string, into: string | undefined) => string | undefined
The format `file` is saved in as a file kept as written (as `assetFormat` says: "" for a `Dockerfile`), or undefined if it's a scratch file of code. A page, a stylesheet, an image, a file without an extension (`Makefile`, `.gitignore`) always is: anything but TypeScript. Code is when the name it goes to holds a file (an example's `ui.tsx`, as `tcm edit` writes it), so it's saved back as written rather than read as definitions, or when it's in a directory marked as files (`.tcm-files`, as a mirror's are): how a new one is added.
locationRewriter (file: string, places: { name: string; line: number; end: number; viewAt: number; }[], names: strin…
A rewriter of what's printed while a file that's gone afterwards (a patch's) is checked and saved: each place in it (`patch-123.ts:51:44`, as the checker and linter say, its path whole or relative, spaces and all) said as where it is in a definition (`tcmts.lib.f:12:44`, the line as `tcm view` numbers it), and the file itself as the names it holds. `places` are the definitions' lines in the file (from 0), with their first line's in the view (-1: not known).
ls (cb: Codebase, ns?: string) => void
main (argv: string[]) => Promise<number>
mv (cb: Codebase, from: string, to: string) => void
nameAt (cb: Codebase, file: string, text: string) => (leaf: string, line: number) => string | undefined
The codebase name of a definition declared as `leaf` at `line` of `file`: a definition's document (.tcm/src) is one name's, a generated module (.tcm/cache) a component's, a mirrored file its namespace's, and a scratch file's say where they go (`// tcm: path`, or its `// tcm: namespace`).
nameChanges (before: NameState, after: NameState, texts?: { before: ReadonlyMap<string, string>; after: Readonl…
What changed between two states, name by name.
patch (cb: Codebase, name: string, edits: PatchEdit[], all: boolean, imports?: PatchImport[]) => boolean
`tcm patch <name>`: change a definition by find-and-replace on its code as `tcm view` shows it (current names), then save it as `tcm update` does: checked, its dependents re-hashed. Nothing is saved if an edit doesn't apply or the result doesn't typecheck. The change is shown as lines removed and added. A file kept as written (a page, a script) is patched as its text, and saved back as the file it is. `imports` are written above the code, for what importing the names it uses can't find (an alias, say): they go if it doesn't use them. A place in the definition (a problem the checker finds, the line nearest an edit that isn't there) is said by its line as `tcm view` numbers it, the edits made (but not the imports added).
patchLocations (cb: Codebase, file: string, text: string, names: string[], before?: string) => (s: string) => stri…
Where a place in a patch's file (`patch-123.ts:51:44`, as the checker and linter say) is in the definitions patched: `tcmts.lib.f:12:44`, the line as `tcm view` numbers it. A rewriter for what's printed while the file is checked and saved (the file is gone afterwards, and its lines aren't the view's): see `locationRewriter`. Where each declaration starts in the view is found from the file `before` it was patched (an edit can change its first line).
patchMany (cb: Codebase, patches: { name: string; edits: PatchEdit[]; all?: boolean | undefined; imports?: Pa…
`tcm patch --patches`: change several definitions at once, each by find-and-replace on its code as `tcm view` shows it, and save them together, as one update (a signature and the code calling it, say, or code and the page documenting it): checked together, one history entry, dependents re-hashed once. Each edit applies within its own definition, as `tcm view` shows it, whatever writing them together takes (`patchedTogether`); a page, or another file kept as written, is patched as its text, as `tcm patch` does one. Nothing is saved if an edit doesn't apply, or the code doesn't typecheck (but definitions declared alike, which one file can't hold, are saved one file after another, and a later one is checked once the first is saved). A patch's `imports` are added to the code's (they go if nothing uses them).
patchedTogether (cb: Codebase, targets: { name: string; edits: PatchEdit[]; all?: boolean | undefined; imports?: Pa…
The file `tcm patch --patches` saves: the definitions patched, and the rest of the cycles they're in (saved with them, as they are), written together as `tcm edit` writes definitions, with each patch applied to its definition as `tcm view` shows it. Written together, a name can read otherwise than alone (`find` from two namespaces, an import two of them name alike, a definition named as another's local is): the lines an edit leaves are the file's, and the lines it writes are given the file's names. Also each definition's view before and after (the change, to show), its lines in the file (from 0) and where they start in its view (to say where a problem is), and the cycles' other members (`mates`). They're written in an order that typechecks (`declarationOrder`): one whose value uses another's after it. A target's `imports` are written with the file's (they go if nothing uses them). Places are runs of lines, numbered as the definition's view, its edits made, numbers them (a `// tcm: path` line written among them is in none).
pathsIn (file: string, text: string, into: string | undefined) => string[]
The codebase paths a scratch file's declarations go to: its namespace and their names, or a `// tcm: path`.
printCheck (plan: Plan) => void
`tcm release check`: the next version, and every finding behind it.
printDependents (paths: string[]) => void
Dependents re-hashed by an update: a line each when there are a few, else by namespace, with how many and the first of them (`tcm diff` lists them all). Brief (`TCM_BRIEF=1`), just how many.
printFindings (report: ApiReport) => void
An API report's findings, a line each with why it matters, breaking ones first; and what follows them.
printIssue (text: string, i: LintIssue, indent?: string) => void
printRows (rows: Row[], shown?: (def: DefRecord) => string) => void
The report of what saving does, a line per definition. Brief (`TCM_BRIEF=1`, as the MCP server's tools run commands), what's saved only because it's in a cycle with what changed is one line, and a type is shown only where it says something: what's new, and an update whose type changed.
printScratchLint (cb: Codebase, file: string, only?: { start: number; end: number; }[] | undefined) => boolean
oxlint's findings in a scratch file, printed under its report; whether any is an error. Brief (`TCM_BRIEF=1`), warnings are a count, and errors are shown.
pruneUnusedImports (file: string, text: string) => { text: string; dropped: string[]; }
A scratch file's text without the imports nothing in it uses, as saving it takes them out: a name an import binds that the rest of the file never mentions. Mentioned is any word outside the imports, in code or not: a comment's `{@link}`, a string, either side of a conflict's markers (an import kept that isn't needed is harmless; one taken out that is isn't). An import with nothing left goes, its line with it; one binding nothing (`import "./setup"`, for what running it does) stays, and so does `React` in a .tsx file (JSX may use it without naming it). The text, and the names taken out.
releaseAt (cb: Codebase, spec: string) => Release
A release: `<package>@<version>`, or `@<version>` when the codebase has one package.
renamed (code: string, names: ReadonlyMap<string, string>, jsx?: boolean) => string
A definition's code, as its view writes it (without its imports), with the names `names` maps given theirs where they're what the view's imports and declarations bind, as the checker resolves them: not a word in a comment, a string, a regular expression or JSX text, a property's name (`.x`, `{ x: … }`, `<a x={…} />`), or a local that only shadows one; a shorthand (`{ x }`) is written out (`{ x: x_1 }`). How a definition's edited lines are written in a file holding others too, where some of its names are others'.
reset (cb: Codebase, target: string) => void
Bring back the names as of a history entry: a new entry on top, so history (pushed or not) is never rewritten.
resolveConflict (cb: Codebase, name: string, keep: string) => void
Settle a conflict by keeping one of the definitions a name holds. The others are replaced by it, as an update replaces: what was derived from them (code that used them, re-derived against them by a merge) is re-derived against the one kept.
respelled (def: DefRecord, names: ReadonlyMap<string, string>) => DefRecord
A definition as analyzed, with the names its references are written with changed as `names` says (`find_1` → `find`): the same code, so the same hash (a reference is by hash, whatever it's written as), read with other names. A shorthand property the file had to write out (`{ find: find_1 }`) is one again (`{ find }`, which hashes the same). How a definition saved from a file holding others too is stored as it reads alone.
rewritingOutput <T>(rewrite: (s: string) => string, fn: () => T) => T
Run `fn` with what it prints rewritten by `rewrite`: through `console`, and written to stdout and stderr (as the checker's diagnostics are).
rm (cb: Codebase, name: string, force: boolean, removing?: ReadonlySet<string>) => Promise<void>
Remove a name. One that something still named uses is refused (unless `force`): the things using it keep working, but by hash. What's being removed with it (`removing`, the other names of one `tcm rm`) doesn't count as using it.
runCli (...argv: string[]) => Promise<void>
Process entry point: run a command, report errors, set the exit code.
runCommand (argv: string[]) => Promise<number>
Run a command as `tcm` does at a terminal, errors reported: its exit code (-1 for one that keeps running, like `tcm watch`). A bug's error (a TypeError, a RangeError) is thrown on, with its stack.
save (cb: Codebase, file: string, mode: "add" | "update", into?: string | undefined, how?: { command?: s…
Save a scratch file (`tcm add`, `tcm update`). Its history entry is named by what it changes (`update lib.f, lib.g (+3)`; a conflict it settles: `resolve lib.k (merged)`), or `how.command`; `how.mates` are paths it holds only as the rest of a cycle, saved as they are. A definition `how.respell` has names for (by path) is stored with its references written so (see `respelled`); every definition is stored as it reads alone (`spelledAlone`), whatever names the file had to give what it uses. A function or class declared `find_1` where the codebase has it as `find` isn't saved: its name is part of its hash.
search (cb: Codebase, text: string, opts?: { regex?: boolean | undefined; caseSensitive?: boolean | undefi…
`tcm search <text>`: every line of the codebase's code and pages with `text` in it, by definition, as an editor's find in files (`tcm find` searches names, docs and signatures). Lines are numbered as `tcm view` shows the definition. The text is as written, or a regular expression written `/…/` (as `tcm view --grep` takes it); `regex` takes all of it as one, the old way. `under` keeps to a namespace; at most `limit` lines are shown.
spelledAlone (cb: Codebase, file: string, text: string, row: Row, rows: Row[]) => Row
A definition saved from a file holding others too (`rows`, analyzed from `text`), spelled as it reads alone, as `tcm view` shows it: what writing them together made the file call otherwise (`find_1`, `Move_1`, `join_1`, a name another there has) is stored with the name it has alone. A reference to the codebase is written with its name, as alone; an import from outside it keeps the name it's imported as, unless that's another's name with a number (`join_1` where the file has a `join`), which alone it doesn't need. Only names change, so its hash doesn't (see `respelled`); a function's or class's own name, part of its hash, stays.
status (cb: Codebase, build?: string | undefined) => void
`tcm status`: where things stand. The branch; what's in the codebase, by namespace; what needs fixing; scratch files holding work that isn't saved, and what saving them would do; other branches holding work main doesn't have (an earlier session's, to land or go on with), and what this branch has that main doesn't; session directories whose branch is gone that hold unsaved work; and whether a mirror's files are in step. `build` is which tcm this is: the hash of its code, which a new build changes.
storeFile (cb: Codebase) => string
syntaxError (d: { code: number; message: string; formatted: string; }, text: string) => string
The first syntax error in some code, said alone (what the checker says besides follows from reading the code wrong): where it is, as the checker says it (`scratch.ts:12:5`), and the lines around it, the place marked. A long line (a template literal's, say) is shown around the place.
templateLiterals (text: string, file: string) => [number, number][]
The template literals in some code (parsed as `file`, `x.ts` or `x.tsx`), whole: from the backtick opening one to the one closing it, substitutions and all.
todo (cb: Codebase, all?: boolean) => void
What needs fixing: conflicts, then the dependents that stopped typechecking (the first 20 unless `all`: one change can break hundreds, and fixing the first often fixes the rest), then code using a version nothing names.
unknownCommand (word: string) => string
verify (cb: Codebase, fresh?: boolean) => boolean
view (cb: Codebase, targets: string[], part?: { lines?: string | undefined; grep?: string | undefined; }…
Show definitions, with current names. `part` shows only some of each one's code, numbered: `lines` ("120-180", or several: "1-20,90-"), or the lines with `grep` in them and two lines either side (a large definition's one method, say): its text, or a regular expression written `/…/` (flags after it: `/a|b/i`), as [textPattern] takes it. A name that isn't found is said first, with the names meant most likely (a namespace's, with what's in it), and the rest are shown: 1 if any wasn't.
watch (cb: Codebase, into?: string | undefined, fresh?: boolean) => void
withCycle (cb: Codebase, file: string, text: string, members: string[]) => { text: string; } | { problem: str…
A scratch file's text followed by definitions of a cycle it changes, as stored (written as `tcm edit` would). Its imports of them go, and so do their imports of what it declares or imports already: blanked, so positions in the file stay where they were. A name the two would bind differently is a problem, and so is a function or class that would have to be written with another name (its name is part of its hash).
withTheirCycles (cb: Codebase, file: string, into: string | undefined) => { a: Analysis; rows: Row[]; text?: string…
A scratch file analyzed with the rest of the cycles it holds members of: definitions that refer to each other are hashed as one, so the members it doesn't hold are saved with it, as they are, against its new code (none goes on using an old one). They're found from the paths it declares, before it's checked: alone, a cycle's member may not even check (a class whose others take the stored one). So are definitions it imports that use what it changes (an interface of a class it changes, a function taking that): alone, the file would be checked against their old versions, typed with the old code; with it, they're re-derived against the new, and saved with it. Undefined, said why, if it can't be. `text` is what was analyzed, when that's the file and the definitions added to it rather than the file as it is.

Shared

BIG_CYCLE 5
How many definitions calling each other are worth a word: two or three that recurse together are fine.
SIGNATURE_WIDTH 120
How much of a definition's type a report line shows.
USED_BEFORE_DECLARED number[]
TypeScript's "used before its declaration" (of a variable, a class, an enum) and "used before being assigned"
alias (cb: Codebase, from: string, to: string) => void
changedCode (cb: Codebase, rows: Row[]) => { start: number; end: number; }[]
Where a scratch file's changed code is: definitions new or changed, leaving out ones re-hashed only because they refer to one that changed and back (a cycle's members are hashed together; a member's IR is as it was, but for the positions it refers to the others by, which a change to one can reorder).
checkDistinct <P extends { def: DefRecord; path: string; }>(planned: P[], message: (p: P) => string) => void
No two planned definitions may claim the same path in the same space (a type and a value may share one).
checkInto (into: string | undefined) => string | undefined
chooseReset (cb: Codebase) => Promise<string>
`tcm reset` with no entry: which command to undo (it, and everything after it).
classify (cb: Codebase, defs: DefRecord[], ns: string) => Row[]
classifyPaths (cb: Codebase, planned: { def: DefRecord; path: string; }[]) => Row[]
cutSignature (sig: string, width?: number) => string
A signature on one line, cut at `width` (`tcm view` shows all of it): a long literal type, or an object type written over lines, doesn't flood a listing.
cycleWarnings (cb: Codebase, rows: Row[]) => string[]
What to say of the cycles a save makes or grows: definitions that refer to each other are hashed as one, so a change to any re-hashes and re-checks all of them, and editing one brings the rest along. One that's a cycle only because types name each other (a function taking the class that calls it) says which, since an interface of what's needed breaks it; a big one of code calling itself says so too. Cycles that were there already, as big, aren't mentioned again.
declarationOrder <T>(items: T[], recsOf: (item: T) => DefRecord[]) => T[]
Definitions, in an order one file can declare them in: each after what it uses as it's initialized (a constant's value, a class it extends), as TypeScript wants it ("used before its declaration"), and otherwise as given. Each item is one or more definitions written together (a type and a value of one name); a cycle of such uses, which no order typechecks, is broken where it's come to.
entryName (command: string, paths: string[], fallback?: string) => string
A history entry's name, by what it changed: `update lib.f`, or `update lib.f, lib.g (+3)` when more did (rather than the scratch file it came from). `fallback` if nothing's named.
escapedMatch (text: string, old: string, replacement: string) => { matches: { index: number; length: number; }[]…
Where `old`, not in `text` as written, is with its escapes taken the other way, as JSON makes easy to get wrong: a line break or `\n` (a backslash and an n), a bare backtick or `` \` ``, `${` or `\${`. Undefined if it isn't (or has none of them). Else where it is, `replacement` written as the source writes them (what old needed changed, changed in it too), and what was changed, said; or why `replacement` can't be written so (old needed one of them changed in some places and not in others, and the replacement has it).
firstSyntaxError (a: Analysis, file: string) => Diagnostic | undefined
The first syntax error in a file's analysis, if its code doesn't parse: one the parser reports, not one of the checker's that TypeScript numbers alike (1000–1999: an enum, where only erasable syntax is allowed, say).
holdsOnlySaved (cb: Codebase, file: string) => boolean
Whether every definition in a scratch file is saved as it is (and nothing else is in it: a watch expression, code that doesn't check).
importLines (st: ImportDeclaration, text: string) => { local: string; line: string; }[]
An import declaration as one import per name it binds (for grouping), each with the name.
importedFrom (cb: Codebase, name: string) => string | undefined
Where the codebase's own code imports `name` from outside it (a package's or Node's export), most often: the import (`import { spawnSync } from "node:child_process"`, or `import fs from "node:fs"` for a default export the code calls `fs`, or `import { Node as ts_Node } from "typescript/ast"` for an export the code calls `ts_Node`), or undefined if nothing does.
importedLocals (st: ImportDeclaration) => string[]
The names an import declaration binds.
isEntryRef (ref: string) => boolean
isHistoryRef (ref: string) => boolean
lineSpans (spec: string) => [number, number][] | undefined
The lines `--lines` asks for, as [first, last] spans counted from 1: "120", "120-180", "120-" (to the end), or several, comma-separated. Forgiving of how they're written (quotes, spaces, "120:180", "120..180", an en dash). Undefined if it isn't lines.
listed (names: string[], n: number) => string
Names, the first `n` of them and how many more.
nearestLine (text: string, old: string, from?: number) => string
Where in `text` the first line of `old` most likely is, said for a patch that didn't find it (or nothing, if no line is much like it): its line number (counting from `from` + 1) and text.
patchImportLines (imports: PatchImport[]) => string[]
The import lines that add `imports` (`PatchImport`), each checked.
patchImportsOf (value: unknown, where: string) => PatchImport[]
The imports a patch asks for (`--imports`, or a patch's `imports` in `--patches`), from its JSON: each a name and where it's from, and maybe `as` and `type`.
releaseSpec (arg: string | undefined) => { name?: string | undefined; version?: string | undefined; }
What `tcm release` was given: `<package>[@version]`, or (with one package) `@version`, a bare version, or nothing.
replacedUses (cb: Codebase, recs: DefRecord[]) => { notes: string[]; byHash: string[]; }
What definitions use that nothing names now (a todo item's, made against the versions before a change it couldn't follow), as a scratch file writes it: the version that replaced it, by its name (that's what it's to be fixed against, and the reason it's in todo), or, where nothing names that, the version used, by its hash (`byHash`: its module must be in the cache for the file to check). `notes` says which, a line each.
sameNames (a: string, b: string, names: Map<string, string>) => void
Two renderings of a line of code, alike but for some names (a definition written alone, and written with others, where a name it had alone was taken): each name in `a` that's another in `b`, added to `names`.
stateAt (cb: Codebase, causal: string) => NameState
textChanged (cb: Codebase, def: DefRecord) => boolean
Whether `def`, the same code as what's stored under its hash, reads differently: its comments or layout changed, or the name it imports something from outside the codebase as (which it's shown with). References to the codebase are left out of the comparison: the stored text has each name as first written, a scratch file the names as they are now (and either is shown with its name now).
when (ts: number) => string
withoutCyclePositions (ir: string) => string
A definition's IR without the positions of the cycle members it refers to.
words (literals: Iterable<{ text: string; }>) => string[]

Tests

testAddToScratch () => void
Definitions added to a scratch file go after what's in it, their imports with its imports, without repeating any it has.
testAddToScratchJoinsImports () => void
testApplyPatch () => void
A patch's edits apply where their text is, once each unless every one is asked for; else nothing changes.
testApplyPatchKeepsTemplateLiteralsWhole () => void
In code, an edit inside a template literal that would end or split it (a bare backtick or `${` in `new`) isn't made, and why is said, with the literal's line; one that keeps it whole (an escaped backtick, a whole substitution), or takes in its backtick on purpose, is.
testApplyPatchTakesEscapesEitherWay () => void
In code, a patch's `old` that's there only with its escapes taken the other way (a line break for `\n`, a bare backtick for `` \` ``, `${` for `\${`, as JSON makes easy) is found that way when that's once, with `new` written alike, and said; found more than once so, or taken both ways where `new` has one, it isn't; a page's text is as written.
testBlankImportsKeepsTheListWhole () => void
Blanking imports leaves a list that still parses, whichever of its names go: the last few (with a trailing comma), one in the middle, all of the braces' beside a default; and nothing else moves
testBuiltinImports () => void
testDeclarationOrder () => void
testEntryName () => void
testFileBindings () => void
testLineSpansAreForgiving () => void
testLocationRewriterTakesPathsWithSpaces () => void
testOutputIsShort () => void
Listings cut long signatures to one line; reports say a lot of sameness in a line.
testPatchADocWrittenOnOneLine () => void
A doc comment can be patched as it was written, on one line, though it's stored on several (each led by ` * `)
testPatchImportLines () => void
testPatchImportsOf () => void
testPatchLocationsNameTheDefinition () => void
testPatchSaysWhatsNearest () => void
testPatchSaysWhereAsTheViewNumbers () => void
testPruneUnusedImports () => void
testRespelled () => void
testSameNamesAndRenamed () => void
testSyntaxErrorSaysWhereAlone () => void
A syntax error is said alone: where it is, as the checker says it, and the lines around it, the place marked; a long line around the place.