import * as plugins from './plugins.js'; import type { CompilerOptions, ScriptTarget, ModuleKind } from 'typescript'; import { compiler, mergeCompilerOptions, emitCheck, checkTypes } from './tsbuild.classes.tsbuild.js'; export type { CompilerOptions, ScriptTarget, ModuleKind }; export * from './tsbuild.classes.tsbuild.js'; /** * Interface for task information */ export interface ITaskInfo { taskNumber: number; totalTasks: number; sourcePattern: string; destDir: string; fileCount: number; } /** * compile an array of absolute file paths with error tracking */ export let compileFileArrayWithErrorTracking = async ( fileStringArrayArg: string[], compilerOptionsArg: CompilerOptions = {}, argvArg?: any, taskInfo?: ITaskInfo ): 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, taskInfo); return tsBuild.compileWithErrorTracking(); }; /** * compile am array of absolute file paths */ export let compileFileArray = ( fileStringArrayArg: string[], compilerOptionsArg: CompilerOptions = {}, argvArg?: any ): Promise => { 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 = {}; 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 * { * './some/origin/folder/**\/*.ts': './some/destination/folder' * } */ export let compileGlobStringObject = async ( globStringObjectArg: Record, tsOptionsArg: CompilerOptions = {}, cwdArg: string = process.cwd(), argvArg?: any ) => { let compiledFiles: any[] = []; const errorSummaries: import('./tsbuild.classes.tsbuild.js').IErrorSummary[] = []; const totalTasks = Object.keys(globStringObjectArg).length; let currentTask = 0; // Log the compilation tasks in a nice format (skip for --quiet or --json) const isQuiet = argvArg?.quiet === true; const isJson = argvArg?.json === true; if (!isQuiet && !isJson) { console.log(`\nšŸ‘· TypeScript Compilation Tasks (${totalTasks} task${totalTasks !== 1 ? 's' : ''}):`); Object.entries(globStringObjectArg).forEach(([source, dest]) => { console.log(` šŸ“‚ ${source} → ${dest}`); }); console.log(''); } for (const keyArg in globStringObjectArg) { // Type safety check for key if (keyArg && typeof keyArg === 'string' && globStringObjectArg[keyArg]) { // Get files matching the glob pattern const fileTreeArray = await plugins.smartfile.fs.listFileTree(cwdArg, keyArg); // Ensure fileTreeArray contains only strings before transforming const stringFileTreeArray = Array.isArray(fileTreeArray) ? fileTreeArray.filter((item): item is string => typeof item === 'string') : []; // Transform to absolute paths const absoluteFilePathArray = plugins.smartpath.transform.toAbsolute( stringFileTreeArray, cwdArg ) as string[]; // Get destination directory as absolute path const destDir: string = plugins.smartpath.transform.toAbsolute( globStringObjectArg[keyArg], cwdArg ) as string; // Update compiler options with the output directory const updatedTsOptions: CompilerOptions = { ...tsOptionsArg, outDir: destDir, }; // Compile with error tracking currentTask++; const taskInfo = { taskNumber: currentTask, totalTasks, sourcePattern: keyArg, destDir: globStringObjectArg[keyArg], fileCount: absoluteFilePathArray.length }; const result = await compileFileArrayWithErrorTracking(absoluteFilePathArray, updatedTsOptions, argvArg, taskInfo); compiledFiles = compiledFiles.concat(result.emittedFiles); errorSummaries.push(result.errorSummary); } } // Display final error summary after all compilation tasks const finalErrorSummary = mergeErrorSummaries(errorSummaries); // Output summary based on mode if (isJson) { const result = { success: finalErrorSummary.totalErrors === 0, totals: { errors: finalErrorSummary.totalErrors, filesWithErrors: finalErrorSummary.totalFiles, tasks: totalTasks, }, errorsByFile: Object.fromEntries( Object.entries(finalErrorSummary.errorsByFile).map(([file, diags]) => [ file, diags.map(d => ({ code: d.code, message: plugins.typescript.flattenDiagnosticMessageText(d.messageText as any, '\n'), })) ]) ), }; console.log(JSON.stringify(result)); } else if (!isQuiet) { displayFinalErrorSummary(finalErrorSummary); } // Attach summary to argvArg so CLI can decide exit behavior if (argvArg && typeof argvArg === 'object') { (argvArg as any).__tsbuildFinalErrorSummary = finalErrorSummary; } return compiledFiles; };