openclaw/scripts/lib/native-typescript-diagnostics.mts
Peter Steinberger 614aa3acff perf(build): avoid duplicate native declaration transforms
Native SDK preparation transformed declarations separately for diagnostics,
then repeated preflight and transformation during in-memory emission. Keep
configuration and requested semantic validation, collect declaration errors
from EmitResult, and reject errors or skipped emission before publication.
Remove the unused includeDeclaration diagnostic option.

Measured cold Node 24 preparation fell from 156.294s to 89.817s; sampled
process-group memory fell from 8.845 to 6.961 GiB. The existing 300-second
limit and Go memory settings are unchanged. This removes proven duplicate
work; it does not conclusively reproduce the historical CI timeout.

Proof: real native tests reject TS4094 and lazy-global TS2318 in both
modes, reject TS2322 in all mode, and accept valid declarations. Focused
suite: 14 passed/3 platform skips, 6.176s wall. Sibling preparation and
tsdown integration: 52 passed/7 platform skips. Changed-file checks,
types/lint, and final independent review through P2 pass.

The native API global-diagnostics handler already forces semantic checking
before collecting fileless diagnostics; source inspection and real API
probes resolve the apparent late-global diagnostic gap. Declaration member
order and synthesized readonly modifiers vary across unchanged original
compiler runs, so generated byte equivalence is not claimed.
2026-09-26 10:07:01 -07:00

61 lines
2.3 KiB
TypeScript

import type { Project as AsyncProject } from "typescript/unstable/async";
import { DiagnosticCategory, type Diagnostic, type Project } from "typescript/unstable/sync";
type DiagnosticOptions = { includeSemantic?: boolean };
function diagnosticQueries(options: DiagnosticOptions) {
return [
"getConfigFileParsingDiagnostics",
"getProgramDiagnostics",
"getGlobalDiagnostics",
"getSyntacticDiagnostics",
"getBindDiagnostics",
...(options.includeSemantic === false ? [] : ["getSemanticDiagnostics" as const]),
] as const;
}
function uniqueDiagnostics(diagnostics: readonly Diagnostic[]): Diagnostic[] {
const seen = new Set<string>();
return diagnostics.filter((diagnostic) => {
const identity = JSON.stringify(diagnostic);
if (seen.has(identity)) {
return false;
}
seen.add(identity);
return true;
});
}
/** Preserve compiler diagnostics across the native program's separate query surfaces. */
export function collectNativeTypeScriptDiagnostics(
project: Project,
options: DiagnosticOptions = {},
): Diagnostic[] {
return uniqueDiagnostics(diagnosticQueries(options).flatMap((query) => project.program[query]()));
}
export async function collectNativeTypeScriptDiagnosticsAsync(
project: AsyncProject,
options: DiagnosticOptions = {},
): Promise<Diagnostic[]> {
const diagnostics: Diagnostic[] = [];
for (const query of diagnosticQueries(options)) {
diagnostics.push(...(await project.program[query]()));
}
return uniqueDiagnostics(diagnostics);
}
export function formatNativeTypeScriptDiagnostics(diagnostics: readonly Diagnostic[]): string {
const format = (diagnostic: Diagnostic, indent = ""): string => {
const location = diagnostic.fileName
? `${diagnostic.fileName}${diagnostic.pos >= 0 ? `@${diagnostic.pos}` : ""}: `
: "";
const category = (DiagnosticCategory[diagnostic.category] ?? "Error").toLowerCase();
return [
`${indent}${location}${category} TS${diagnostic.code}: ${diagnostic.text}`,
...(diagnostic.messageChain ?? []).map((message) => format(message, `${indent} `)),
...(diagnostic.relatedInformation ?? []).map((message) => format(message, `${indent} `)),
].join("\n");
};
return diagnostics.map((diagnostic) => format(diagnostic)).join("\n");
}