Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0e960aa

Browse files
authored
fix: throw an error if typechecker failed to spawn (#7990)
1 parente996b41 commit0e960aa

File tree

6 files changed

+111
-24
lines changed

6 files changed

+111
-24
lines changed

‎docs/config/index.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2258,6 +2258,13 @@ By default, if Vitest finds source error, it will fail test suite.
22582258

22592259
Path to custom tsconfig, relative to the project root.
22602260

2261+
####typecheck.spawnTimeout
2262+
2263+
-**Type**:`number`
2264+
-**Default**:`10_000`
2265+
2266+
Minimum time in milliseconds it takes to spawn the typechecker.
2267+
22612268
###slowTestThreshold<NonProjectOption />
22622269

22632270
-**Type**:`number`

‎packages/vitest/src/node/cli/cli-config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,10 @@ export const cliOptionsConfig: VitestCLIOptions = {
706706
argument:'<path>',
707707
normalize:true,
708708
},
709+
spawnTimeout:{
710+
description:'Minimum time in milliseconds it takes to spawn the typechecker',
711+
argument:'<time>',
712+
},
709713
include:null,
710714
exclude:null,
711715
},

‎packages/vitest/src/node/pools/typecheck.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export function createTypecheckPool(vitest: Vitest): ProcessPool {
101101

102102
asyncfunctionstartTypechecker(project:TestProject,files:string[]){
103103
if(project.typechecker){
104-
returnproject.typechecker
104+
return
105105
}
106106
constchecker=awaitcreateWorkspaceTypechecker(project,files)
107107
awaitchecker.collectTests()
@@ -154,7 +154,7 @@ export function createTypecheckPool(vitest: Vitest): ProcessPool {
154154
}
155155
promises.push(promise)
156156
promisesMap.set(project,promise)
157-
startTypechecker(project,files)
157+
promises.push(startTypechecker(project,files))
158158
}
159159

160160
awaitPromise.all(promises)

‎packages/vitest/src/node/types/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,11 @@ export interface TypecheckConfig {
907907
* Path to tsconfig, relative to the project root.
908908
*/
909909
tsconfig?:string
910+
/**
911+
* Minimum time in milliseconds it takes to spawn the typechecker.
912+
*@default 10_000
913+
*/
914+
spawnTimeout?:number
910915
}
911916

912917
exportinterfaceUserConfigextendsInlineConfig{

‎packages/vitest/src/typecheck/typechecker.ts

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { File, Task, TaskEventPack, TaskResultPack, TaskState } from '@vite
33
importtype{ParsedStack}from'@vitest/utils'
44
importtype{EachMapping}from'@vitest/utils/source-map'
55
importtype{ChildProcess}from'node:child_process'
6+
importtype{Result}from'tinyexec'
67
importtype{Vitest}from'../node/core'
78
importtype{TestProject}from'../node/project'
89
importtype{Awaitable}from'../types/general'
@@ -277,11 +278,7 @@ export class Typechecker {
277278
returnthis._output
278279
}
279280

280-
publicasyncstart():Promise<void>{
281-
if(this.process){
282-
return
283-
}
284-
281+
privateasyncspawn(){
285282
const{ root, watch, typecheck}=this.project.config
286283

287284
constargs=[
@@ -314,31 +311,88 @@ export class Typechecker {
314311
},
315312
throwOnError:false,
316313
})
314+
317315
this.process=child.process
318-
awaitthis._onParseStart?.()
316+
319317
letrerunTriggered=false
320-
child.process?.stdout?.on('data',(chunk)=>{
321-
this._output+=chunk
322-
if(!watch){
318+
letdataReceived=false
319+
320+
returnnewPromise<{result:Result}>((resolve,reject)=>{
321+
if(!child.process||!child.process.stdout){
322+
reject(newError(`Failed to initialize${typecheck.checker}. This is a bug in Vitest - please, open an issue with reproduction.`))
323323
return
324324
}
325-
if(this._output.includes('File change detected')&&!rerunTriggered){
326-
this._onWatcherRerun?.()
327-
this._startTime=performance.now()
328-
this._result.sourceErrors=[]
329-
this._result.files=[]
330-
this._tests=null// test structure might've changed
331-
rerunTriggered=true
325+
326+
child.process.stdout.on('data',(chunk)=>{
327+
dataReceived=true
328+
this._output+=chunk
329+
if(!watch){
330+
return
331+
}
332+
if(this._output.includes('File change detected')&&!rerunTriggered){
333+
this._onWatcherRerun?.()
334+
this._startTime=performance.now()
335+
this._result.sourceErrors=[]
336+
this._result.files=[]
337+
this._tests=null// test structure might've changed
338+
rerunTriggered=true
339+
}
340+
if(/Found\w+errors*.Watchingfor/.test(this._output)){
341+
rerunTriggered=false
342+
this.prepareResults(this._output).then((result)=>{
343+
this._result=result
344+
this._onParseEnd?.(result)
345+
})
346+
this._output=''
347+
}
348+
})
349+
350+
consttimeout=setTimeout(
351+
()=>reject(newError(`${typecheck.checker} spawn timed out`)),
352+
this.project.config.typecheck.spawnTimeout,
353+
)
354+
355+
functiononError(cause:Error){
356+
clearTimeout(timeout)
357+
reject(newError('Spawning typechecker failed - is typescript installed?',{ cause}))
332358
}
333-
if(/Found\w+errors*.Watchingfor/.test(this._output)){
334-
rerunTriggered=false
335-
this.prepareResults(this._output).then((result)=>{
336-
this._result=result
337-
this._onParseEnd?.(result)
359+
360+
child.process.once('spawn',()=>{
361+
this._onParseStart?.()
362+
child.process?.off('error',onError)
363+
clearTimeout(timeout)
364+
if(process.platform==='win32'){
365+
// on Windows, the process might be spawned but fail to start
366+
// we wait for a potential error here. if "close" event didn't trigger,
367+
// we resolve the promise
368+
setTimeout(()=>{
369+
resolve({result:child})
370+
},200)
371+
}
372+
else{
373+
resolve({result:child})
374+
}
375+
})
376+
377+
if(process.platform==='win32'){
378+
child.process.once('close',(code)=>{
379+
if(code!=null&&code!==0&&!dataReceived){
380+
onError(newError(`The${typecheck.checker} command exited with code${code}.`))
381+
}
338382
})
339-
this._output=''
340383
}
384+
child.process.once('error',onError)
341385
})
386+
}
387+
388+
publicasyncstart():Promise<void>{
389+
if(this.process){
390+
return
391+
}
392+
393+
const{ watch}=this.project.config
394+
const{result:child}=awaitthis.spawn()
395+
342396
if(!watch){
343397
awaitchild
344398
this._result=awaitthis.prepareResults(this._output)

‎test/typescript/test/runner.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,20 @@ describe('when the title is dynamic', () => {
134134
expect(vitest.stdout).toContain('✓ (() => "some name")()')
135135
})
136136
})
137+
138+
it('throws an error if typechecker process exists',async()=>{
139+
const{ stderr}=awaitrunVitest({
140+
root:resolve(__dirname,'../fixtures/source-error'),
141+
typecheck:{
142+
enabled:true,
143+
checker:'non-existing-command',
144+
},
145+
})
146+
expect(stderr).toContain('Error: Spawning typechecker failed - is typescript installed?')
147+
if(process.platform==='win32'){
148+
expect(stderr).toContain('Error: The non-existing-command command exited with code 1.')
149+
}
150+
else{
151+
expect(stderr).toContain('Error: spawn non-existing-command ENOENT')
152+
}
153+
})

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp