analyzeScratch
uses node:fs
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.
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { firstSyntaxError, syntaxError } from "#codebase/tcmts/cli/main"; import { red, yellow } from "#codebase/tcmts/cli/style"; import { TCM_REACT_GONE, TCM_SERVER_GONE } from "#codebase/tcmts/core/analyze"; import type { Analysis } from "#codebase/tcmts/core/analyze"; import { shortHash } from "#codebase/tcmts/core/hash"; import fs from "node:fs"; import path from "node:path"; import { stripVTControlCharacters } from "node:util"; export function analyzeScratch( cb: Codebase, file: string, into?: string, text?: string, how: { ignore?: number[]; own?: number } = {}, ): Analysis | undefined | null { cb.refreshShim(); const a = cb.analyze( file, cb.scratchSalts(into), how.ignore ?? [], text === undefined ? undefined : new Map([[file, text]]), ); for (const w of a.warnings) console.log(yellow(`warning: ${w}`)); // a syntax error: what else the checker says follows from reading the code wrong (type errors by the screenful), // so it alone is said const syntax = firstSyntaxError(a, file); if (syntax) { console.log(syntaxError(syntax, text ?? fs.readFileSync(file, "utf8"))); console.log(red(`\n${path.basename(file)} doesn't parse; nothing else was checked.`)); return undefined; } if (a.diagnostics.length || a.errors.length) { // a version imported by its hash whose module isn't in the cache: that's the problem, and what else the checker // says follows from it (what it imports has no type), so it's said alone, with what to do const missing = a.diagnostics.flatMap((d) => { const m = d.code === 2307 && /Cannot find module '[^']*\/([a-z2-7]{52})\.tsx?'/.exec(d.message); return m ? [{ d, hash: m[1]! }] : []; }); if (missing.length) { for (const { d, hash } of missing) { const at = stripVTControlCharacters(d.formatted).split("\n")[0]!.split(" - ")[0]; const now = cb.store.patched(hash); const name = cb.store.pathsOf(now)[0]; const ns = name?.slice(0, Math.max(0, name.lastIndexOf("."))) ?? ""; const from = ns ? `#codebase/${ns.replaceAll(".", "/")}` : "#codebase"; console.log( red( `${at}: ${ !cb.store.getDef(hash) ? `imports ${shortHash(hash)} by its hash, which the codebase doesn't have` : name && now === hash ? `imports ${name} by its hash, from a module the cache doesn't have: import it by its name, from "${from}"` : name ? `uses ${shortHash(hash)}, a version of ${name} nothing names now: import the current one by its name, from "${from}" (\`tcm edit\` writes it so)` : `uses ${shortHash(hash)} (made as ${cb.store.getDef(hash)!.nameHint}), which nothing names, by its hash, from a module the cache doesn't have: \`tcm edit\` what uses it again, which writes the module` }`, ), ); } const rest = a.diagnostics.length - missing.length + a.errors.length; if (rest) console.log( `(and ${rest} more problem${rest > 1 ? "s" : ""} the checker found, which may follow from ${missing.length > 1 ? "those" : "that"}: \`tcm check\` again once it's fixed)`, ); console.log(red(`\n${path.basename(file)} has problems; nothing was checked.`)); return undefined; } for (const d of a.diagnostics) process.stdout.write(d.formatted); // (the modules tcm generated for React's routes and data, and the server API: libraries now) if (a.diagnostics.some((d) => d.code === 2307 && d.message.includes("'#tcm/react'"))) console.log(red(TCM_REACT_GONE)); if (a.diagnostics.some((d) => d.code === 2307 && d.message.includes("'#tcm/server'"))) console.log(red(TCM_SERVER_GONE)); for (const e of a.errors) console.log(red(e)); // (the line a diagnostic is at, as `file:12:5 - error` says) const line = (formatted: string) => { const m = / ?:(\d+):\d+ - /.exec(stripVTControlCharacters(formatted).split("\n")[0]!); return m ? Number(m[1]) : undefined; }; const theirs = how.own !== undefined && !a.errors.length && a.diagnostics.every((d) => (line(d.formatted) ?? 0) > how.own!); if (theirs) return null; console.log( red( `\n${path.basename(file)} has problems; nothing was ${a.diagnostics.length ? "checked" : "hashed"}.`, ), ); return undefined; } return a; }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.main.firstSyntaxError
- tcmts.cli.main.syntaxError
- tcmts.cli.style.red
- tcmts.cli.style.yellow
- tcmts.core.analyze.Analysis
- tcmts.core.analyze.TCM_REACT_GONE
- tcmts.core.analyze.TCM_SERVER_GONE
- tcmts.core.hash.shortHash