fix(typecheck): improve error message when tsc outputs help text (#9214) · vitest-dev/vitest@7b10ab4
@@ -53,7 +53,7 @@ export class Typechecker {
53535454protected files: string[] = []
555556-constructor(protected project: TestProject) {}
56+constructor(protected project: TestProject) { }
57575858public setFiles(files: string[]): void {
5959this.files = files
@@ -123,6 +123,22 @@ export class Typechecker {
123123sourceErrors: TestError[]
124124time: number
125125}> {
126+// Detect if tsc output is help text instead of error output
127+// This happens when tsconfig.json is missing and tsc can't find any config
128+if (output.includes('The TypeScript Compiler - Version') || output.includes('COMMON COMMANDS')) {
129+const { typecheck } = this.project.config
130+const tsconfigPath = typecheck.tsconfig || 'tsconfig.json'
131+const msg = `TypeScript compiler returned help text instead of type checking results.\n`
132++ `This usually means the tsconfig file was not found.\n\n`
133++ `Possible solutions:\n`
134++ ` 1. Ensure '${tsconfigPath}' exists in your project root\n`
135++ ` 2. If using a custom tsconfig, verify the path in your Vitest config:\n`
136++ ` test: { typecheck: { tsconfig: 'path/to/tsconfig.json' } }\n`
137++ ` 3. Check that the tsconfig file is valid JSON`
138+139+throw new Error(msg)
140+}
141+126142const typeErrors = await this.parseTscLikeOutput(output)
127143const testFiles = new Set(this.getFiles())
128144@@ -319,6 +335,8 @@ export class Typechecker {
319335return
320336}
321337338+let resolved = false
339+322340child.process.stdout.on('data', (chunk) => {
323341dataReceived = true
324342this._output += chunk
@@ -343,13 +361,25 @@ export class Typechecker {
343361}
344362})
345363364+// Also capture stderr for configuration errors like missing tsconfig
365+child.process.stderr?.on('data', (chunk) => {
366+this._output += chunk
367+})
368+346369const timeout = setTimeout(
347370() => reject(new Error(`${typecheck.checker} spawn timed out`)),
348371this.project.config.typecheck.spawnTimeout,
349372)
350373374+let winTimeout: NodeJS.Timeout | undefined
375+351376function onError(cause: Error) {
377+if (resolved) {
378+return
379+}
352380clearTimeout(timeout)
381+clearTimeout(winTimeout)
382+resolved = true
353383reject(new Error('Spawning typechecker failed - is typescript installed?', { cause }))
354384}
355385@@ -361,11 +391,13 @@ export class Typechecker {
361391// on Windows, the process might be spawned but fail to start
362392// we wait for a potential error here. if "close" event didn't trigger,
363393// we resolve the promise
364-setTimeout(() => {
394+winTimeout = setTimeout(() => {
395+resolved = true
365396resolve({ result: child })
366397}, 200)
367398}
368399else {
400+resolved = true
369401resolve({ result: child })
370402}
371403})