fix(tsbuild): Improve diagnostic error handling and summary reporting for TypeScript compilation by refactoring diagnostic processing and adding pre-emit error checks.
This commit is contained in:
parent
88c0601c03
commit
0909fa306a
@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-05-21 - 2.5.2 - fix(tsbuild)
|
||||
Improve diagnostic error handling and summary reporting for TypeScript compilation by refactoring diagnostic processing and adding pre-emit error checks.
|
||||
|
||||
- Introduce a dedicated processDiagnostics function that categorizes errors by file and computes error totals.
|
||||
- Refactor displayErrorSummary to provide clearer, color-coded output of error details.
|
||||
- Add pre-emit error checking in compileWithErrorTracking to prevent emission when errors exist.
|
||||
- Consolidate error summary merging in compileFileArrayWithErrorTracking for improved reporting.
|
||||
|
||||
## 2025-05-15 - 2.5.1 - fix(commitinfo)
|
||||
Update commit information and metadata to synchronize release data
|
||||
|
||||
|
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@git.zone/tsbuild',
|
||||
version: '2.5.1',
|
||||
version: '2.5.2',
|
||||
description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.'
|
||||
}
|
||||
|
@ -3,6 +3,16 @@ import * as plugins from './plugins.js';
|
||||
import * as paths from './paths.js';
|
||||
import type { CompilerOptions, ScriptTarget, ModuleKind } from './tsbuild.exports.js';
|
||||
|
||||
/**
|
||||
* Interface for error summary data
|
||||
*/
|
||||
export interface IErrorSummary {
|
||||
errorsByFile: Record<string, plugins.typescript.Diagnostic[]>;
|
||||
generalErrors: plugins.typescript.Diagnostic[];
|
||||
totalErrors: number;
|
||||
totalFiles: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default compiler options for TypeScript compilation
|
||||
*/
|
||||
@ -166,14 +176,9 @@ export class TsBuild {
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to handle and log TypeScript diagnostics
|
||||
* Helper function to process TypeScript diagnostics and return error summary
|
||||
*/
|
||||
private handleDiagnostics(diagnostics: readonly plugins.typescript.Diagnostic[]): boolean {
|
||||
if (diagnostics.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Group errors by file for better readability
|
||||
private processDiagnostics(diagnostics: readonly plugins.typescript.Diagnostic[]): IErrorSummary {
|
||||
const errorsByFile: Record<string, plugins.typescript.Diagnostic[]> = {};
|
||||
const generalErrors: plugins.typescript.Diagnostic[] = [];
|
||||
|
||||
@ -190,12 +195,27 @@ export class TsBuild {
|
||||
}
|
||||
});
|
||||
|
||||
// Print error summary header
|
||||
const totalErrorCount = diagnostics.length;
|
||||
const fileCount = Object.keys(errorsByFile).length;
|
||||
return {
|
||||
errorsByFile,
|
||||
generalErrors,
|
||||
totalErrors: diagnostics.length,
|
||||
totalFiles: Object.keys(errorsByFile).length
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to display error summary
|
||||
*/
|
||||
private displayErrorSummary(errorSummary: IErrorSummary): void {
|
||||
if (errorSummary.totalErrors === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { errorsByFile, generalErrors, totalErrors, totalFiles } = errorSummary;
|
||||
|
||||
// Print error summary header
|
||||
console.log('\n' + '='.repeat(80));
|
||||
console.log(`❌ Found ${totalErrorCount} error${totalErrorCount !== 1 ? 's' : ''} in ${fileCount} file${fileCount !== 1 ? 's' : ''}:`);
|
||||
console.log(`❌ Found ${totalErrors} error${totalErrors !== 1 ? 's' : ''} in ${totalFiles} file${totalFiles !== 1 ? 's' : ''}:`);
|
||||
console.log('='.repeat(80));
|
||||
|
||||
// Color codes for error formatting
|
||||
@ -255,8 +275,15 @@ export class TsBuild {
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(80) + '\n');
|
||||
|
||||
return diagnostics.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to handle and log TypeScript diagnostics (legacy method)
|
||||
*/
|
||||
private handleDiagnostics(diagnostics: readonly plugins.typescript.Diagnostic[]): boolean {
|
||||
const errorSummary = this.processDiagnostics(diagnostics);
|
||||
this.displayErrorSummary(errorSummary);
|
||||
return errorSummary.totalErrors > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -282,6 +309,72 @@ export class TsBuild {
|
||||
this.options = { ...this.options, ...options };
|
||||
}
|
||||
|
||||
/**
|
||||
* The main compiler function that compiles the files and returns error summary
|
||||
*/
|
||||
public async compileWithErrorTracking(): Promise<{ emittedFiles: any[], errorSummary: IErrorSummary }> {
|
||||
if (this.options.skipLibCheck) {
|
||||
console.log('\n⚠️ WARNING ⚠️');
|
||||
console.log('You are skipping libcheck... Is that really wanted?');
|
||||
console.log('Continuing in 5 seconds...\n');
|
||||
await plugins.smartdelay.delayFor(5000);
|
||||
}
|
||||
|
||||
console.log(`🔨 Compiling ${this.fileNames.length} files...`);
|
||||
const done = plugins.smartpromise.defer<{ emittedFiles: any[], errorSummary: IErrorSummary }>();
|
||||
const program = this.createProgram();
|
||||
|
||||
// Check for pre-emit diagnostics first
|
||||
const preEmitDiagnostics = plugins.typescript.getPreEmitDiagnostics(program);
|
||||
const preEmitErrorSummary = this.processDiagnostics(preEmitDiagnostics);
|
||||
|
||||
// Only continue to emit phase if no pre-emit errors
|
||||
if (preEmitErrorSummary.totalErrors > 0) {
|
||||
this.displayErrorSummary(preEmitErrorSummary);
|
||||
console.error('\n❌ TypeScript pre-emit checks failed. Please fix the issues listed above before proceeding.');
|
||||
console.error(' Type errors must be resolved before the compiler can emit output files.\n');
|
||||
// Return error summary instead of exiting to allow final summary display
|
||||
done.resolve({ emittedFiles: [], errorSummary: preEmitErrorSummary });
|
||||
return done.promise;
|
||||
}
|
||||
|
||||
// If no pre-emit errors, proceed with emit
|
||||
const emitResult = program.emit();
|
||||
const emitErrorSummary = this.processDiagnostics(emitResult.diagnostics);
|
||||
|
||||
// Combine error summaries
|
||||
const combinedErrorSummary: IErrorSummary = {
|
||||
errorsByFile: { ...preEmitErrorSummary.errorsByFile, ...emitErrorSummary.errorsByFile },
|
||||
generalErrors: [...preEmitErrorSummary.generalErrors, ...emitErrorSummary.generalErrors],
|
||||
totalErrors: preEmitErrorSummary.totalErrors + emitErrorSummary.totalErrors,
|
||||
totalFiles: Object.keys({ ...preEmitErrorSummary.errorsByFile, ...emitErrorSummary.errorsByFile }).length
|
||||
};
|
||||
|
||||
const exitCode = emitResult.emitSkipped ? 1 : 0;
|
||||
if (exitCode === 0) {
|
||||
console.log('\n✅ TypeScript emit succeeded!');
|
||||
|
||||
// Get count of emitted files by type
|
||||
const jsFiles = emitResult.emittedFiles?.filter(f => f.endsWith('.js')).length || 0;
|
||||
const dtsFiles = emitResult.emittedFiles?.filter(f => f.endsWith('.d.ts')).length || 0;
|
||||
const mapFiles = emitResult.emittedFiles?.filter(f => f.endsWith('.map')).length || 0;
|
||||
|
||||
// If we have emitted files, show a summary
|
||||
if (emitResult.emittedFiles && emitResult.emittedFiles.length > 0) {
|
||||
console.log(` Generated ${emitResult.emittedFiles.length} files: ${jsFiles} .js, ${dtsFiles} .d.ts, ${mapFiles} source maps`);
|
||||
}
|
||||
|
||||
done.resolve({ emittedFiles: emitResult.emittedFiles || [], errorSummary: combinedErrorSummary });
|
||||
} else {
|
||||
this.displayErrorSummary(combinedErrorSummary);
|
||||
console.error('\n❌ TypeScript emit failed. Please investigate the errors listed above!');
|
||||
console.error(' No output files have been generated.\n');
|
||||
process.exit(exitCode);
|
||||
}
|
||||
|
||||
return done.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main compiler function that compiles the files
|
||||
*/
|
||||
|
@ -6,6 +6,19 @@ export type { CompilerOptions, ScriptTarget, ModuleKind };
|
||||
|
||||
export * from './tsbuild.classes.tsbuild.js';
|
||||
|
||||
/**
|
||||
* compile an array of absolute file paths with error tracking
|
||||
*/
|
||||
export let compileFileArrayWithErrorTracking = async (
|
||||
fileStringArrayArg: string[],
|
||||
compilerOptionsArg: CompilerOptions = {},
|
||||
argvArg?: any
|
||||
): Promise<{ emittedFiles: any[], errorSummary: import('./tsbuild.classes.tsbuild.js').IErrorSummary }> => {
|
||||
const { TsBuild } = await import('./tsbuild.classes.tsbuild.js');
|
||||
const tsBuild = new TsBuild(fileStringArrayArg, compilerOptionsArg, argvArg);
|
||||
return tsBuild.compileWithErrorTracking();
|
||||
};
|
||||
|
||||
/**
|
||||
* compile am array of absolute file paths
|
||||
*/
|
||||
@ -17,6 +30,75 @@ export let compileFileArray = (
|
||||
return compiler(fileStringArrayArg, mergeCompilerOptions(compilerOptionsArg, argvArg), argvArg);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to merge error summaries
|
||||
*/
|
||||
function mergeErrorSummaries(summaries: import('./tsbuild.classes.tsbuild.js').IErrorSummary[]): import('./tsbuild.classes.tsbuild.js').IErrorSummary {
|
||||
const mergedErrorsByFile: Record<string, plugins.typescript.Diagnostic[]> = {};
|
||||
const mergedGeneralErrors: plugins.typescript.Diagnostic[] = [];
|
||||
let totalErrors = 0;
|
||||
|
||||
summaries.forEach(summary => {
|
||||
// Merge errors by file
|
||||
Object.entries(summary.errorsByFile).forEach(([fileName, errors]) => {
|
||||
if (!mergedErrorsByFile[fileName]) {
|
||||
mergedErrorsByFile[fileName] = [];
|
||||
}
|
||||
mergedErrorsByFile[fileName] = mergedErrorsByFile[fileName].concat(errors);
|
||||
});
|
||||
|
||||
// Merge general errors
|
||||
mergedGeneralErrors.push(...summary.generalErrors);
|
||||
totalErrors += summary.totalErrors;
|
||||
});
|
||||
|
||||
return {
|
||||
errorsByFile: mergedErrorsByFile,
|
||||
generalErrors: mergedGeneralErrors,
|
||||
totalErrors,
|
||||
totalFiles: Object.keys(mergedErrorsByFile).length
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to display final compilation summary
|
||||
*/
|
||||
function displayFinalErrorSummary(errorSummary: import('./tsbuild.classes.tsbuild.js').IErrorSummary): void {
|
||||
if (errorSummary.totalErrors === 0) {
|
||||
console.log('\n📊 \x1b[32mCompilation Summary: All tasks completed successfully! ✅\x1b[0m\n');
|
||||
return;
|
||||
}
|
||||
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
red: '\x1b[31m',
|
||||
yellow: '\x1b[33m',
|
||||
cyan: '\x1b[36m',
|
||||
brightRed: '\x1b[91m',
|
||||
brightYellow: '\x1b[93m'
|
||||
};
|
||||
|
||||
console.log('\n' + '='.repeat(80));
|
||||
console.log(`📊 ${colors.brightYellow}Final Compilation Summary${colors.reset}`);
|
||||
console.log('='.repeat(80));
|
||||
|
||||
if (errorSummary.totalFiles > 0) {
|
||||
console.log(`${colors.brightRed}❌ Files with errors (${errorSummary.totalFiles}):${colors.reset}`);
|
||||
|
||||
Object.entries(errorSummary.errorsByFile).forEach(([fileName, errors]) => {
|
||||
const displayPath = fileName.replace(process.cwd(), '').replace(/^\//, '');
|
||||
console.log(` ${colors.red}•${colors.reset} ${colors.cyan}${displayPath}${colors.reset} ${colors.yellow}(${errors.length} error${errors.length !== 1 ? 's' : ''})${colors.reset}`);
|
||||
});
|
||||
}
|
||||
|
||||
if (errorSummary.generalErrors.length > 0) {
|
||||
console.log(`${colors.brightRed}❌ General errors: ${errorSummary.generalErrors.length}${colors.reset}`);
|
||||
}
|
||||
|
||||
console.log(`\n${colors.brightRed}Total: ${errorSummary.totalErrors} error${errorSummary.totalErrors !== 1 ? 's' : ''} across ${errorSummary.totalFiles} file${errorSummary.totalFiles !== 1 ? 's' : ''}${colors.reset}`);
|
||||
console.log('='.repeat(80) + '\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* compile advanced glob configurations
|
||||
* @param globStringArrayArg a array of glob strings
|
||||
@ -31,6 +113,7 @@ export let compileGlobStringObject = async (
|
||||
argvArg?: any
|
||||
) => {
|
||||
let compiledFiles: any[] = [];
|
||||
const errorSummaries: import('./tsbuild.classes.tsbuild.js').IErrorSummary[] = [];
|
||||
|
||||
// Log the compilation tasks in a nice format
|
||||
console.log('\n👷 TypeScript Compilation Tasks:');
|
||||
@ -69,12 +152,16 @@ export let compileGlobStringObject = async (
|
||||
outDir: destDir,
|
||||
};
|
||||
|
||||
// Compile the files and correctly concat the results
|
||||
// Fixed: removed duplicating compiledFiles in the concat operation
|
||||
const newlyCompiledFiles = await compileFileArray(absoluteFilePathArray, updatedTsOptions, argvArg);
|
||||
compiledFiles = compiledFiles.concat(newlyCompiledFiles);
|
||||
// Compile with error tracking
|
||||
const result = await compileFileArrayWithErrorTracking(absoluteFilePathArray, updatedTsOptions, argvArg);
|
||||
compiledFiles = compiledFiles.concat(result.emittedFiles);
|
||||
errorSummaries.push(result.errorSummary);
|
||||
}
|
||||
}
|
||||
|
||||
// Display final error summary after all compilation tasks
|
||||
const finalErrorSummary = mergeErrorSummaries(errorSummaries);
|
||||
displayFinalErrorSummary(finalErrorSummary);
|
||||
|
||||
return compiledFiles;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user