fix(tsbuild): Avoid process.exit in library, add confirmskiplibcheck flag, improve CLI exit handling and JSON/quiet modes, update test script
This commit is contained in:
		| @@ -1,5 +1,14 @@ | |||||||
| # Changelog | # 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) | ## 2025-08-18 - 2.6.7 - fix(tspublish) | ||||||
| Bump @git.zone/tspublish dependency to ^1.10.3 | Bump @git.zone/tspublish dependency to ^1.10.3 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ | |||||||
|     "tsbuild": "./cli.js" |     "tsbuild": "./cli.js" | ||||||
|   }, |   }, | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "test": "tsrun test/test.ts --verbose", |     "test": "tstest test/test.ts --verbose", | ||||||
|     "build": "node cli.ts.js --web", |     "build": "node cli.ts.js --web", | ||||||
|     "buildDocs": "tsdoc" |     "buildDocs": "tsdoc" | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -3,6 +3,6 @@ | |||||||
|  */ |  */ | ||||||
| export const commitinfo = { | export const commitinfo = { | ||||||
|   name: '@git.zone/tsbuild', |   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.' |   description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.' | ||||||
| } | } | ||||||
|   | |||||||
| @@ -309,10 +309,17 @@ export class TsBuild { | |||||||
|    */ |    */ | ||||||
|   public async compileWithErrorTracking(): Promise<{ emittedFiles: any[], errorSummary: IErrorSummary }> { |   public async compileWithErrorTracking(): Promise<{ emittedFiles: any[], errorSummary: IErrorSummary }> { | ||||||
|     if (this.options.skipLibCheck) { |     if (this.options.skipLibCheck) { | ||||||
|       console.log('\n⚠️  WARNING ⚠️'); |       if (this.argvArg?.confirmskiplibcheck) { | ||||||
|       console.log('You are skipping libcheck... Is that really wanted?'); |         console.log('\n⚠️  WARNING ⚠️'); | ||||||
|       console.log('Continuing in 5 seconds...\n'); |         console.log('You are skipping libcheck... Is that really wanted?'); | ||||||
|       await plugins.smartdelay.delayFor(5000); |         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 |     // Enhanced logging with task info | ||||||
| @@ -382,7 +389,9 @@ export class TsBuild { | |||||||
|       this.displayErrorSummary(combinedErrorSummary); |       this.displayErrorSummary(combinedErrorSummary); | ||||||
|       console.error('\n❌ TypeScript emit failed. Please investigate the errors listed above!'); |       console.error('\n❌ TypeScript emit failed. Please investigate the errors listed above!'); | ||||||
|       console.error('   No output files have been generated.\n'); |       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; |     return done.promise; | ||||||
| @@ -393,10 +402,16 @@ export class TsBuild { | |||||||
|    */ |    */ | ||||||
|   public async compile(): Promise<any[]> { |   public async compile(): Promise<any[]> { | ||||||
|     if (this.options.skipLibCheck) { |     if (this.options.skipLibCheck) { | ||||||
|       console.log('\n⚠️  WARNING ⚠️'); |       if (this.argvArg?.confirmskiplibcheck) { | ||||||
|       console.log('You are skipping libcheck... Is that really wanted?'); |         console.log('\n⚠️  WARNING ⚠️'); | ||||||
|       console.log('Continuing in 5 seconds...\n'); |         console.log('You are skipping libcheck... Is that really wanted?'); | ||||||
|       await plugins.smartdelay.delayFor(5000); |         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...`); |     console.log(`🔨 Compiling ${this.fileNames.length} files...`); | ||||||
| @@ -412,7 +427,8 @@ export class TsBuild { | |||||||
|       this.displayErrorSummary(preEmitErrorSummary); |       this.displayErrorSummary(preEmitErrorSummary); | ||||||
|       console.error('\n❌ TypeScript pre-emit checks failed. Please fix the issues listed above before proceeding.'); |       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'); |       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 |     // If no pre-emit errors, proceed with emit | ||||||
| @@ -438,7 +454,8 @@ export class TsBuild { | |||||||
|       this.displayErrorSummary(emitErrorSummary); |       this.displayErrorSummary(emitErrorSummary); | ||||||
|       console.error('\n❌ TypeScript emit failed. Please investigate the errors listed above!'); |       console.error('\n❌ TypeScript emit failed. Please investigate the errors listed above!'); | ||||||
|       console.error('   No output files have been generated.\n'); |       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; |     return done.promise; | ||||||
| @@ -565,4 +582,4 @@ export const checkTypes = async ( | |||||||
| ): Promise<boolean> => { | ): Promise<boolean> => { | ||||||
|   const tsBuild = new TsBuild(fileNames, options, argvArg); |   const tsBuild = new TsBuild(fileNames, options, argvArg); | ||||||
|   return tsBuild.checkTypes(); |   return tsBuild.checkTypes(); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ export const runCli = async () => { | |||||||
|    * the standard task compiles anything in ts/ directory to dist directory |    * the standard task compiles anything in ts/ directory to dist directory | ||||||
|    */ |    */ | ||||||
|   tsbuildCli.standardCommand().subscribe(async (argvArg) => { |   tsbuildCli.standardCommand().subscribe(async (argvArg) => { | ||||||
|     tsbuild.compileGlobStringObject( |     await tsbuild.compileGlobStringObject( | ||||||
|       { |       { | ||||||
|         './ts/**/*.ts': './dist_ts', |         './ts/**/*.ts': './dist_ts', | ||||||
|       }, |       }, | ||||||
| @@ -17,6 +17,10 @@ export const runCli = async () => { | |||||||
|       process.cwd(), |       process.cwd(), | ||||||
|       argvArg |       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}`; |       compilationCommandObject[`./${directory}/**/*.ts`] = `./dist_${directory}`; | ||||||
|     } |     } | ||||||
|     await tsbuild.compileGlobStringObject(compilationCommandObject, {}, process.cwd(), argvArg); |     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}`; |       compilationCommandObject[`./${tsFolder}/**/*.ts`] = `./dist_${tsFolder}`; | ||||||
|     } |     } | ||||||
|     await tsbuild.compileGlobStringObject(compilationCommandObject, {}, process.cwd(), argvArg); |     await tsbuild.compileGlobStringObject(compilationCommandObject, {}, process.cwd(), argvArg); | ||||||
|  |     const summary = (argvArg as any)?.__tsbuildFinalErrorSummary; | ||||||
|  |     if (summary && summary.totalErrors > 0) { | ||||||
|  |       process.exit(1); | ||||||
|  |     } | ||||||
|   }); |   }); | ||||||
|    |    | ||||||
|   /** |   /** | ||||||
|   | |||||||
| @@ -130,12 +130,16 @@ export let compileGlobStringObject = async ( | |||||||
|   const totalTasks = Object.keys(globStringObjectArg).length; |   const totalTasks = Object.keys(globStringObjectArg).length; | ||||||
|   let currentTask = 0; |   let currentTask = 0; | ||||||
|    |    | ||||||
|   // Log the compilation tasks in a nice format |   // Log the compilation tasks in a nice format (skip for --quiet or --json) | ||||||
|   console.log(`\n👷 TypeScript Compilation Tasks (${totalTasks} task${totalTasks !== 1 ? 's' : ''}):`); |   const isQuiet = argvArg?.quiet === true; | ||||||
|   Object.entries(globStringObjectArg).forEach(([source, dest]) => { |   const isJson = argvArg?.json === true; | ||||||
|     console.log(`  📂 ${source} → ${dest}`); |   if (!isQuiet && !isJson) { | ||||||
|   }); |     console.log(`\n👷 TypeScript Compilation Tasks (${totalTasks} task${totalTasks !== 1 ? 's' : ''}):`); | ||||||
|   console.log(''); |     Object.entries(globStringObjectArg).forEach(([source, dest]) => { | ||||||
|  |       console.log(`  📂 ${source} → ${dest}`); | ||||||
|  |     }); | ||||||
|  |     console.log(''); | ||||||
|  |   } | ||||||
|    |    | ||||||
|   for (const keyArg in globStringObjectArg) { |   for (const keyArg in globStringObjectArg) { | ||||||
|     // Type safety check for key |     // Type safety check for key | ||||||
| @@ -185,7 +189,35 @@ export let compileGlobStringObject = async ( | |||||||
|    |    | ||||||
|   // Display final error summary after all compilation tasks |   // Display final error summary after all compilation tasks | ||||||
|   const finalErrorSummary = mergeErrorSummaries(errorSummaries); |   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; |   return compiledFiles; | ||||||
| }; | }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user