1
- import { type ChildProcess , fork } from 'node:child_process'
1
+ import { spawn } from 'node:child_process'
2
2
import { pathToFileURL } from 'node:url'
3
- import * as vscode from 'vscode'
4
- import { gte } from 'semver'
5
- import { findNode , formatPkg , getNodeJsVersion , showVitestError } from '../utils'
3
+ import { createServer } from 'node:http'
4
+ import getPort from 'get-port'
5
+ import { WebSocketServer } from 'ws'
6
+ import { formatPkg } from '../utils'
6
7
import { log } from '../log'
7
8
import type { ResolvedMeta } from '../api'
8
- import type { WorkerEvent , WorkerRunnerOptions } from '../worker/types'
9
9
import { getConfig } from '../config'
10
- import { minimumNodeVersion , workerPath } from '../constants'
10
+ import { workerPath } from '../constants'
11
11
import type { VitestPackage } from './pkg'
12
- import { createVitestRpc } from './rpc'
13
- import type { VitestProcess } from './types'
12
+ import { waitForWsResolvedMeta } from './ws'
14
13
15
14
async function createChildVitestProcess ( pkg : VitestPackage ) {
16
15
const pnpLoader = pkg . loader
17
16
const pnp = pkg . pnp
18
17
if ( pnpLoader && ! pnp )
19
18
throw new Error ( 'pnp file is required if loader option is used' )
20
19
const env = getConfig ( ) . env || { }
21
- const execPath = await findNode ( vscode . workspace . workspaceFile ?. fsPath || pkg . cwd )
22
- const execVersion = await getNodeJsVersion ( execPath )
23
- if ( execVersion && ! gte ( execVersion , minimumNodeVersion ) ) {
24
- const errorMsg = `Node.js version ${ execVersion } is not supported. Minimum required version is ${ minimumNodeVersion } `
25
- log . error ( '[API]' , errorMsg )
26
- throw new Error ( errorMsg )
27
- }
20
+ // const execPath = await findNode(vscode.workspace.workspaceFile?.fsPath || pkg.cwd)
21
+ // const execVersion = await getNodeJsVersion(execPath)
22
+ // if (execVersion && !gte(execVersion, minimumNodeVersion)) {
23
+ // const errorMsg = `Node.js version ${execVersion} is not supported. Minimum required version is ${minimumNodeVersion}`
24
+ // log.error('[API]', errorMsg)
25
+ // throw new Error(errorMsg)
26
+ // }
28
27
const runtimeArgs = getConfig ( pkg . folder ) . nodeExecArgs || [ ]
29
28
const execArgv = pnpLoader && pnp // && !gte(execVersion, '18.19.0')
30
29
? [
@@ -35,145 +34,168 @@ async function createChildVitestProcess(pkg: VitestPackage) {
35
34
...runtimeArgs ,
36
35
]
37
36
: runtimeArgs
38
- log . info ( '[API]' , `Running ${ formatPkg ( pkg ) } with Node.js@${ execVersion } : ${ execPath } ${ execArgv ? execArgv . join ( ' ' ) : '' } ` )
37
+ const script = `node ${ workerPath } ${ execArgv . join ( ' ' ) } ` . trim ( )
38
+ log . info ( '[API]' , `Running ${ formatPkg ( pkg ) } with "${ script } "` )
39
39
const logLevel = getConfig ( pkg . folder ) . logLevel
40
- const vitest = fork (
41
- workerPath ,
42
- {
43
- execPath,
44
- execArgv,
45
- env : {
46
- ...process . env ,
47
- ...env ,
48
- VITEST_VSCODE_LOG : env . VITEST_VSCODE_LOG ?? process . env . VITEST_VSCODE_LOG ?? logLevel ,
49
- VITEST_VSCODE : 'true' ,
50
- // same env var as `startVitest`
51
- // https://github.com/vitest-dev/vitest/blob/5c7e9ca05491aeda225ce4616f06eefcd068c0b4/packages/vitest/src/node/cli/cli-api.ts
52
- TEST : 'true' ,
53
- VITEST : 'true' ,
54
- NODE_ENV : env . NODE_ENV ?? process . env . NODE_ENV ?? 'test' ,
55
- } ,
56
- stdio : 'overlapped' ,
57
- cwd : pkg . cwd ,
40
+ const port = await getPort ( )
41
+ const server = createServer ( ) . listen ( port )
42
+ const wss = new WebSocketServer ( { server } )
43
+ const wsAddress = `ws://localhost:${ port } `
44
+ spawn ( 'node' , [ workerPath , ...execArgv ] , {
45
+ env : {
46
+ ...process . env ,
47
+ ...env ,
48
+ VITEST_VSCODE_LOG : env . VITEST_VSCODE_LOG ?? process . env . VITEST_VSCODE_LOG ?? logLevel ,
49
+ VITEST_VSCODE : 'true' ,
50
+ // same env var as `startVitest`
51
+ // https://github.com/vitest-dev/vitest/blob/5c7e9ca05491aeda225ce4616f06eefcd068c0b4/packages/vitest/src/node/cli/cli-api.ts
52
+ TEST : 'true' ,
53
+ VITEST_WS_ADDRESS : wsAddress ,
54
+ VITEST : 'true' ,
55
+ NODE_ENV : env . NODE_ENV ?? process . env . NODE_ENV ?? 'test' ,
58
56
} ,
59
- )
60
-
61
- vitest . stdout ?. on ( 'data' , d => log . worker ( 'info' , d . toString ( ) ) )
62
- vitest . stderr ?. on ( 'data' , ( chunk ) => {
63
- const string = chunk . toString ( )
64
- log . worker ( 'error' , string )
65
- if ( string . startsWith ( ' MISSING DEPENDENCY' ) ) {
66
- const error = string . split ( / \r ? \n / , 1 ) [ 0 ] . slice ( ' MISSING DEPENDENCY' . length )
67
- showVitestError ( error )
68
- }
57
+ // stdio: 'overlapped',
58
+ cwd : pkg . cwd ,
69
59
} )
70
60
71
- return new Promise < { process : ChildProcess ; configs : string [ ] } > ( ( resolve , reject ) => {
72
- function onMessage ( message : WorkerEvent ) {
73
- if ( message . type === 'debug' )
74
- log . worker ( 'info' , ...message . args )
75
-
76
- if ( message . type === 'ready' ) {
77
- resolve ( { process : vitest , configs : message . configs } )
78
- }
79
- if ( message . type === 'error' ) {
80
- const error = new Error ( `Vitest failed to start: \n${ message . error } ` )
81
- reject ( error )
82
- }
83
- vitest . off ( 'error' , onError )
84
- vitest . off ( 'message' , onMessage )
85
- vitest . off ( 'exit' , onExit )
86
- }
87
-
88
- function onError ( err : Error ) {
89
- log . error ( '[API]' , err )
90
- reject ( err )
91
- vitest . off ( 'error' , onError )
92
- vitest . off ( 'message' , onMessage )
93
- vitest . off ( 'exit' , onExit )
94
- }
95
-
96
- function onExit ( code : number ) {
97
- reject ( new Error ( `Vitest process exited with code ${ code } ` ) )
98
- }
99
-
100
- vitest . on ( 'error' , onError )
101
- vitest . on ( 'message' , onMessage )
102
- vitest . on ( 'exit' , onExit )
103
- vitest . once ( 'spawn' , ( ) => {
104
- const runnerOptions : WorkerRunnerOptions = {
105
- type : 'init' ,
106
- meta : {
107
- shellType : 'child_process' ,
108
- vitestNodePath : pkg . vitestNodePath ,
109
- env : getConfig ( pkg . folder ) . env || undefined ,
110
- configFile : pkg . configFile ,
111
- cwd : pkg . cwd ,
112
- arguments : pkg . arguments ,
113
- workspaceFile : pkg . workspaceFile ,
114
- id : pkg . id ,
115
- pnpApi : pnp ,
116
- pnpLoader : pnpLoader // && gte(execVersion, '18.19.0')
117
- ? pathToFileURL ( pnpLoader ) . toString ( )
118
- : undefined ,
119
- } ,
120
- debug : false ,
121
- astCollect : getConfig ( pkg . folder ) . experimentalStaticAstCollect ,
122
- }
123
-
124
- vitest . send ( runnerOptions )
125
- } )
126
- } )
61
+ return await waitForWsResolvedMeta ( wss , pkg , true , 'child_process' )
62
+ // const vitest = fork(
63
+ // workerPath,
64
+ // {
65
+ // execPath,
66
+ // execArgv,
67
+ // env: {
68
+ // ...process.env,
69
+ // ...env,
70
+ // VITEST_VSCODE_LOG: env.VITEST_VSCODE_LOG ?? process.env.VITEST_VSCODE_LOG ?? logLevel,
71
+ // VITEST_VSCODE: 'true',
72
+ // // same env var as `startVitest`
73
+ // // https://github.com/vitest-dev/vitest/blob/5c7e9ca05491aeda225ce4616f06eefcd068c0b4/packages/vitest/src/node/cli/cli-api.ts
74
+ // TEST: 'true',
75
+ // VITEST: 'true',
76
+ // NODE_ENV: env.NODE_ENV ?? process.env.NODE_ENV ?? 'test',
77
+ // },
78
+ // stdio: 'overlapped',
79
+ // cwd: pkg.cwd,
80
+ // },
81
+ // )
82
+
83
+ // vitest.stdout?.on('data', d => log.worker('info', d.toString()))
84
+ // vitest.stderr?.on('data', (chunk) => {
85
+ // const string = chunk.toString()
86
+ // log.worker('error', string)
87
+ // if (string.startsWith(' MISSING DEPENDENCY')) {
88
+ // const error = string.split(/\r?\n/, 1)[0].slice(' MISSING DEPENDENCY'.length)
89
+ // showVitestError(error)
90
+ // }
91
+ // })
92
+
93
+ // return new Promise<{ process: ChildProcess; configs: string[] }>((resolve, reject) => {
94
+ // function onMessage(message: WorkerEvent) {
95
+ // if (message.type === 'debug')
96
+ // log.worker('info', ...message.args)
97
+
98
+ // if (message.type === 'ready') {
99
+ // resolve({ process: vitest, configs: message.configs })
100
+ // }
101
+ // if (message.type === 'error') {
102
+ // const error = new Error(`Vitest failed to start: \n${message.error}`)
103
+ // reject(error)
104
+ // }
105
+ // vitest.off('error', onError)
106
+ // vitest.off('message', onMessage)
107
+ // vitest.off('exit', onExit)
108
+ // }
109
+
110
+ // function onError(err: Error) {
111
+ // log.error('[API]', err)
112
+ // reject(err)
113
+ // vitest.off('error', onError)
114
+ // vitest.off('message', onMessage)
115
+ // vitest.off('exit', onExit)
116
+ // }
117
+
118
+ // function onExit(code: number) {
119
+ // reject(new Error(`Vitest process exited with code ${code}`))
120
+ // }
121
+
122
+ // vitest.on('error', onError)
123
+ // vitest.on('message', onMessage)
124
+ // vitest.on('exit', onExit)
125
+ // vitest.once('spawn', () => {
126
+ // const runnerOptions: WorkerRunnerOptions = {
127
+ // type: 'init',
128
+ // meta: {
129
+ // shellType: 'child_process',
130
+ // vitestNodePath: pkg.vitestNodePath,
131
+ // env: getConfig(pkg.folder).env || undefined,
132
+ // configFile: pkg.configFile,
133
+ // cwd: pkg.cwd,
134
+ // arguments: pkg.arguments,
135
+ // workspaceFile: pkg.workspaceFile,
136
+ // id: pkg.id,
137
+ // pnpApi: pnp,
138
+ // pnpLoader: pnpLoader // && gte(execVersion, '18.19.0')
139
+ // ? pathToFileURL(pnpLoader).toString()
140
+ // : undefined,
141
+ // },
142
+ // debug: false,
143
+ // astCollect: getConfig(pkg.folder).experimentalStaticAstCollect,
144
+ // }
145
+
146
+ // vitest.send(runnerOptions)
147
+ // })
148
+ // })
127
149
}
128
150
129
151
export async function createVitestProcess ( pkg : VitestPackage ) : Promise < ResolvedMeta > {
130
- const { process : vitest , configs } = await createChildVitestProcess ( pkg )
131
-
132
- log . info ( '[API]' , `${ formatPkg ( pkg ) } child process ${ vitest . pid } created` )
133
-
134
- const { handlers, api } = createVitestRpc ( {
135
- on : listener => vitest . on ( 'message' , listener ) ,
136
- send : message => vitest . send ( message ) ,
137
- } )
138
-
139
- vitest . once ( 'exit' , ( ) => {
140
- log . verbose ?.( '[API]' , 'Vitest child_process connection closed, cannot call RPC anymore.' )
141
- api . $close ( )
142
- } )
143
-
144
- return {
145
- rpc : api ,
146
- configs,
147
- process : new VitestChildProcess ( vitest ) ,
148
- handlers,
149
- pkg,
150
- }
152
+ return await createChildVitestProcess ( pkg )
153
+
154
+ // log.info('[API]', `${formatPkg(pkg)} child process ${vitest.pid} created`)
155
+
156
+ // const { handlers, api } = createVitestRpc({
157
+ // on: listener => vitest.on('message', listener),
158
+ // send: message => vitest.send(message),
159
+ // })
160
+
161
+ // vitest.once('exit', () => {
162
+ // log.verbose?.('[API]', 'Vitest child_process connection closed, cannot call RPC anymore.')
163
+ // api.$close()
164
+ // })
165
+
166
+ // return {
167
+ // rpc: api,
168
+ // configs,
169
+ // process: new VitestChildProcess(vitest),
170
+ // handlers,
171
+ // pkg,
172
+ // }
151
173
}
152
174
153
- class VitestChildProcess implements VitestProcess {
154
- constructor ( private child : ChildProcess ) { }
175
+ // class VitestChildProcess implements VitestProcess {
176
+ // constructor(private child: ChildProcess) {}
155
177
156
- get id ( ) {
157
- return this . child . pid ?? 0
158
- }
178
+ // get id() {
179
+ // return this.child.pid ?? 0
180
+ // }
159
181
160
- get closed ( ) {
161
- return this . child . killed
162
- }
182
+ // get closed() {
183
+ // return this.child.killed
184
+ // }
163
185
164
- on ( event : string , listener : ( ...args : any [ ] ) => void ) {
165
- this . child . on ( event , listener )
166
- }
186
+ // on(event: string, listener: (...args: any[]) => void) {
187
+ // this.child.on(event, listener)
188
+ // }
167
189
168
- once ( event : string , listener : ( ...args : any [ ] ) => void ) {
169
- this . child . once ( event , listener )
170
- }
190
+ // once(event: string, listener: (...args: any[]) => void) {
191
+ // this.child.once(event, listener)
192
+ // }
171
193
172
- off ( event : string , listener : ( ...args : any [ ] ) => void ) {
173
- this . child . off ( event , listener )
174
- }
194
+ // off(event: string, listener: (...args: any[]) => void) {
195
+ // this.child.off(event, listener)
196
+ // }
175
197
176
- close ( ) {
177
- this . child . kill ( )
178
- }
179
- }
198
+ // close() {
199
+ // this.child.kill()
200
+ // }
201
+ // }
0 commit comments