AUDIT / src /lib /astCheck.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 3)
95eb75a verified
Raw
History Blame Contribute Delete
3.47 kB
/**
* astCheck.ts — P45: AST syntax check via TypeScript compiler API.
* Zero shell, feedback istantaneo. Verifica sintassi PRIMA di eseguire/salvare.
* Supporta .ts .tsx .js .jsx .mjs .cjs .mts .cts
* Degradazione graceful se TypeScript non disponibile.
*
* @module astCheck
*/
import type * as TsType from "typescript";
export interface AstError {
line: number;
col: number;
message: string;
}
export interface AstCheckResult {
ok: boolean;
errors: AstError[];
}
const CODE_EXTS = new Set(["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"]);
/**
* Verifica sintattica AST di un frammento di codice tramite TypeScript compiler API.
* Usa `parseDiagnostics` interno — stesso meccanismo degli IDE — senza shell né network.
*/
export async function astCheck(code: string, filename: string): Promise<AstCheckResult> {
const ext = (filename.split(".").pop() ?? "").toLowerCase();
if (!CODE_EXTS.has(ext)) return { ok: true, errors: [] };
// Skipa file estremamente piccoli (stub, placeholder)
if (code.trim().length < 20) return { ok: true, errors: [] };
try {
const ts = (await import("typescript")) as typeof TsType;
const scriptKind =
ext === "tsx" ? ts.ScriptKind.TSX
: ext === "jsx" ? ts.ScriptKind.JSX
: ext === "ts" || ext === "mts" || ext === "cts" ? ts.ScriptKind.TS
: ts.ScriptKind.JS;
// createSourceFile con setParentNodes=true popola parseDiagnostics
const sf = ts.createSourceFile(filename, code, ts.ScriptTarget.Latest, true, scriptKind);
// parseDiagnostics è il campo interno che gli IDE usano per evidenziare errori in real-time.
// È stabile dalla TS 3.x ed è esattamente ciò di cui abbiamo bisogno (solo errori sintattici).
type SfInternal = { parseDiagnostics?: TsType.Diagnostic[] };
const rawDiags: TsType.Diagnostic[] = (sf as unknown as SfInternal).parseDiagnostics ?? [];
if (rawDiags.length === 0) return { ok: true, errors: [] };
const errors: AstError[] = rawDiags.map(d => {
const pos = sf.getLineAndCharacterOfPosition(d.start ?? 0);
const msg =
typeof d.messageText === "string"
? d.messageText
: (d.messageText as TsType.DiagnosticMessageChain).messageText;
return { line: pos.line + 1, col: pos.character + 1, message: msg };
});
return { ok: false, errors };
} catch {
// TypeScript non disponibile o errore interno → fail-open (non blocca il loop)
return { ok: true, errors: [] };
}
}
/**
* Costruisce il repair hint da iniettare come messaggio "user" nel loop.
*/
export function buildAstRepairHint(filename: string, errors: AstError[], code: string): string {
const lines = code.split("\n");
const snippets = errors.slice(0, 3).map(e => {
const lineContent = (lines[e.line - 1] ?? "").trim().slice(0, 120);
return ` • riga ${e.line}, col ${e.col}: ${e.message}\n → ${lineContent}`;
});
const extra = errors.length > 3 ? `\n (+ altri ${errors.length - 3} errori sintattici)` : "";
return [
`❌ ERRORE SINTATTICO AST in \`${filename}\` — ${errors.length} errore/i trovato/i:`,
...snippets,
extra,
``,
`AZIONE RICHIESTA: Riscrivi \`${filename}\` correggendo TUTTI gli errori sintattici sopra.`,
`⚠️ Il file ha sintassi non valida — non può essere eseguito finché non corretto.`,
`Usa write_file per riscrivere il file con la sintassi corretta, poi riprova.`,
].join("\n");
}