diff --git a/changelog.md b/changelog.md index 19588f8..11608b2 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,14 @@ # Changelog +## 2025-08-29 - 2.6.8 - fix(tsbuild) +Avoid process.exit in library, add confirmskiplibcheck flag, improve CLI exit handling and JSON/quiet modes, update test script + +- Changed package.json test script from "tsrun test/test.ts --verbose" to "tstest test/test.ts --verbose". +- Library no longer calls process.exit from compile and compileWithErrorTracking; errors are returned or thrown so callers can decide process termination. +- skipLibCheck behavior updated: delay/warning only happens when --confirmskiplibcheck is present; otherwise a short informational note is printed (suppressed in --quiet/--json). +- CLI now awaits compileGlobStringObject calls and inspects a final error summary attached to argv to decide process.exit(1) when errors occurred. +- compileGlobStringObject/exports now respect --quiet and --json modes, emit a JSON summary when --json is used, and attach the final error summary to argv so the CLI can determine exit behavior. + ## 2025-08-18 - 2.6.7 - fix(tspublish) Bump @git.zone/tspublish dependency to ^1.10.3 diff --git a/package.json b/package.json index 536b60b..ab392d9 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "tsbuild": "./cli.js" }, "scripts": { - "test": "tsrun test/test.ts --verbose", + "test": "tstest test/test.ts --verbose", "build": "node cli.ts.js --web", "buildDocs": "tsdoc" }, diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 71b69d1..bfcf91c 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@git.zone/tsbuild', - version: '2.6.7', + version: '2.6.8', description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.' } diff --git a/ts/tsbuild.classes.tsbuild.ts b/ts/tsbuild.classes.tsbuild.ts index f0e716d..984a53b 100644 --- a/ts/tsbuild.classes.tsbuild.ts +++ b/ts/tsbuild.classes.tsbuild.ts @@ -309,10 +309,17 @@ export class TsBuild { */ 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); + if (this.argvArg?.confirmskiplibcheck) { + 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); + } else { + // No delay by default; keep a short note unless in quiet/json modes + if (!this.argvArg?.quiet && !this.argvArg?.json) { + console.log('⚠️ skipLibCheck enabled; use --confirmskiplibcheck to pause with warning.'); + } + } } // Enhanced logging with task info @@ -382,7 +389,9 @@ export class TsBuild { 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); + // Do not exit here; return error summary so caller can decide + done.resolve({ emittedFiles: [], errorSummary: combinedErrorSummary }); + return done.promise; } return done.promise; @@ -393,10 +402,16 @@ export class TsBuild { */ public async compile(): Promise { 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); + if (this.argvArg?.confirmskiplibcheck) { + 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); + } else { + if (!this.argvArg?.quiet && !this.argvArg?.json) { + console.log('⚠️ skipLibCheck enabled; use --confirmskiplibcheck to pause with warning.'); + } + } } console.log(`🔨 Compiling ${this.fileNames.length} files...`); @@ -412,7 +427,8 @@ export class TsBuild { 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'); - process.exit(1); + // Throw instead of exiting to keep library pure + throw new Error('TypeScript pre-emit checks failed.'); } // If no pre-emit errors, proceed with emit @@ -438,7 +454,8 @@ export class TsBuild { this.displayErrorSummary(emitErrorSummary); console.error('\n❌ TypeScript emit failed. Please investigate the errors listed above!'); console.error(' No output files have been generated.\n'); - process.exit(exitCode); + // Throw instead of exiting to keep library pure + throw new Error('TypeScript emit failed.'); } return done.promise; @@ -565,4 +582,4 @@ export const checkTypes = async ( ): Promise => { const tsBuild = new TsBuild(fileNames, options, argvArg); return tsBuild.checkTypes(); -}; \ No newline at end of file +}; diff --git a/ts/tsbuild.cli.ts b/ts/tsbuild.cli.ts index 939bb00..0b40a25 100644 --- a/ts/tsbuild.cli.ts +++ b/ts/tsbuild.cli.ts @@ -9,7 +9,7 @@ export const runCli = async () => { * the standard task compiles anything in ts/ directory to dist directory */ tsbuildCli.standardCommand().subscribe(async (argvArg) => { - tsbuild.compileGlobStringObject( + await tsbuild.compileGlobStringObject( { './ts/**/*.ts': './dist_ts', }, @@ -17,6 +17,10 @@ export const runCli = async () => { process.cwd(), argvArg ); + const summary = (argvArg as any)?.__tsbuildFinalErrorSummary; + if (summary && summary.totalErrors > 0) { + process.exit(1); + } }); /** @@ -30,6 +34,10 @@ export const runCli = async () => { compilationCommandObject[`./${directory}/**/*.ts`] = `./dist_${directory}`; } await tsbuild.compileGlobStringObject(compilationCommandObject, {}, process.cwd(), argvArg); + const summary = (argvArg as any)?.__tsbuildFinalErrorSummary; + if (summary && summary.totalErrors > 0) { + process.exit(1); + } }); /** @@ -182,6 +190,10 @@ export const runCli = async () => { compilationCommandObject[`./${tsFolder}/**/*.ts`] = `./dist_${tsFolder}`; } await tsbuild.compileGlobStringObject(compilationCommandObject, {}, process.cwd(), argvArg); + const summary = (argvArg as any)?.__tsbuildFinalErrorSummary; + if (summary && summary.totalErrors > 0) { + process.exit(1); + } }); /** diff --git a/ts/tsbuild.exports.ts b/ts/tsbuild.exports.ts index e4ff349..abf967d 100644 --- a/ts/tsbuild.exports.ts +++ b/ts/tsbuild.exports.ts @@ -130,12 +130,16 @@ export let compileGlobStringObject = async ( const totalTasks = Object.keys(globStringObjectArg).length; let currentTask = 0; - // Log the compilation tasks in a nice format - console.log(`\n👷 TypeScript Compilation Tasks (${totalTasks} task${totalTasks !== 1 ? 's' : ''}):`); - Object.entries(globStringObjectArg).forEach(([source, dest]) => { - console.log(` 📂 ${source} → ${dest}`); - }); - console.log(''); + // 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 @@ -185,7 +189,35 @@ export let compileGlobStringObject = async ( // Display final error summary after all compilation tasks const finalErrorSummary = mergeErrorSummaries(errorSummaries); - displayFinalErrorSummary(finalErrorSummary); - + + // 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; -}; \ No newline at end of file +};