pullIntoScratch
uses node:fs
Pull definitions (names) into the scratch file file (text being what it holds), as tcm edit <names> --file
does: each written with what the file holds kept (a definition saved as it is there stays, written again with
them), one the file has already left as it is; JSX into a .ts file makes it a .tsx one (file, its name now). What the
file holds then, and what was done.
import { classify, edit } from "#codebase/tcmts/cli/main"; import { HttpError } from "#codebase/tcmts/server"; import { analyzedScratch, editableCodebase, scratchPath } from "#codebase/tcmts/studio"; import fs from "node:fs"; import path from "node:path"; import { format, stripVTControlCharacters } from "node:util"; export async function pullIntoScratch( file: string, text: string, names: string[], ): Promise<{ text: string; said: string; file?: string }> { const cb = editableCodebase(); let abs = scratchPath(cb, file, text); if (!/\.tsx?$/.test(file)) throw new HttpError(400, `${file} isn't code: definitions are pulled into a .ts or .tsx file.`); if (!names.length) return { text, said: "" }; for (const n of names) { const recs = (n.startsWith("#") ? [cb.lookupOne(n)] : cb.lookup(n)).map((h) => cb.store.getDef(h), ); if (!recs.length || recs.some((r) => !r)) throw new HttpError(404, `There's no ${n} in the codebase.`); if (recs.some((r) => r!.kind === "file")) throw new HttpError( 400, `${n} is a file kept as written: Edit, on its page, opens it apart.`, ); } // JSX goes in a .tsx file: a .ts one becomes one (what it holds reads the same there) let moved: string | undefined; if ( file.endsWith(".ts") && names.some((n) => (n.startsWith("#") ? [cb.lookupOne(n)] : cb.lookup(n)).some((h) => cb.store.getDef(h)?.jsx), ) ) { const to = abs.replace(/\.ts$/, ".tsx"); if (fs.existsSync(to)) throw new HttpError( 409, `It's JSX, which goes in a .tsx file, and ${path.basename(to)} is there already: pull it into that.`, ); fs.rmSync(abs, { force: true }); abs = to; moved = path.basename(to); } fs.writeFileSync(abs, text); // (what the file holds saved as it is: written again with them, so a file of saved definitions keeps them) const a = analyzedScratch(cb, abs, text); let kept: string[] = []; try { if (!a.diagnostics.length && !a.errors.length) kept = classify(cb, a.defs, a.namespace ?? "") .filter((r) => r.status === "identical") .map((r) => r.path); } catch { kept = []; } const said: string[] = []; const log = console.log; console.log = (...args: unknown[]) => void said.push(stripVTControlCharacters(format(...args))); try { edit(cb, [...new Set([...kept, ...names])], abs, false); } catch (e) { throw new HttpError(400, (e as Error).message); } finally { console.log = log; } // (said of the file by its name: where it is, the page says) const told = said.join("\n").replaceAll(abs, path.basename(abs)).trim(); return { text: fs.readFileSync(abs, "utf8"), said: told, ...(moved ? { file: moved } : {}) }; }
uses
- tcmts.cli.main.classify
- tcmts.cli.main.edit
- tcmts.server.HttpError
- tcmts.studio.analyzedScratch
- tcmts.studio.editableCodebase
- tcmts.studio.scratchPath