Files
CRM_ENVR/scripts/lib/ts-error-parser.ts

92 lines
3.2 KiB
TypeScript

// ── TypeScript Error Parser ──────────────────────────────────────────
// Parses `tsc --noEmit` or `npm run build` output to extract
// "Module not found: Can't resolve '@/path/to/module'" errors.
// Returns actionable missing module info for the generator.
export interface MissingModule {
/** The import path that failed, e.g. "@/data/stickers" */
importPath: string;
/** The file that contains the failing import */
importerFile: string;
/** Line number in importer file */
line: number;
/** Column number */
column: number;
/** Full error message for context */
message: string;
/** Whether this is an internal (@/...) module we can generate */
isInternal: boolean;
/** The normalized internal path, e.g. "data/stickers" */
internalPath?: string;
}
/** Parse TypeScript compiler output for missing module errors */
export function parseMissingModules(tscOutput: string): MissingModule[] {
const results: MissingModule[] = [];
// Pattern: error TS2307: Cannot find module '@/data/stickers' or its corresponding type declarations.
// File: src/components/chats/media-picker.tsx:10:1
const errorRegex = /error\s+TS\d+:\s+Cannot find module\s+'([^']+)'\s+or its corresponding type declarations\.\s*(?:\n\s*(.*?):(\d+):(\d+))?/g;
let match;
while ((match = errorRegex.exec(tscOutput)) !== null) {
const importPath = match[1];
const importerFile = match[2] || "";
const line = parseInt(match[3] || "0", 10);
const column = parseInt(match[4] || "0", 10);
// Also handle: Module not found: Error: Can't resolve '@/data/stickers' in 'C:\...\src\components\chats'
const altRegex = /Module not found: Error: Can't resolve\s+'([^']+)'\s+in\s+'([^']+)'/g;
let altMatch;
let altImporter = importerFile;
while ((altMatch = altRegex.exec(tscOutput)) !== null) {
if (altMatch[1] === importPath) {
altImporter = altMatch[2];
break;
}
}
const isInternal = importPath.startsWith("@/") || importPath.startsWith("~/");
let internalPath: string | undefined;
if (isInternal) {
internalPath = importPath.replace(/^[@~]\//, "");
}
results.push({
importPath,
importerFile: altImporter,
line,
column,
message: `Cannot find module '${importPath}'`,
isInternal,
internalPath,
});
}
// Deduplicate by importPath + importerFile
const seen = new Set<string>();
return results.filter((m) => {
const key = `${m.importPath}|${m.importerFile}`;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
}
/** Filter to only internal (@/...) modules we can auto-generate */
export function filterGeneratable(modules: MissingModule[]): MissingModule[] {
return modules.filter((m) => m.isInternal && m.internalPath);
}
/** Group missing modules by internal path */
export function groupByInternalPath(modules: MissingModule[]): Map<string, MissingModule[]> {
const groups = new Map<string, MissingModule[]>();
for (const m of modules) {
if (!m.internalPath) continue;
const existing = groups.get(m.internalPath) || [];
existing.push(m);
groups.set(m.internalPath, existing);
}
return groups;
}