@@ -4,6 +4,7 @@ import process from 'node:process';
4
4
import degit from 'degit' ;
5
5
import { x , exec } from 'tinyexec' ;
6
6
import { create } from '@sveltejs/create' ;
7
+ import pstree , { type PS } from 'ps-tree' ;
7
8
8
9
export { addPnpmBuildDependencies } from '../utils/package-manager.ts' ;
9
10
export type ProjectVariant = 'kit-js' | 'kit-ts' | 'vite-js' | 'vite-ts' ;
@@ -121,17 +122,36 @@ export async function startPreview({
121
122
} ) ;
122
123
}
123
124
125
+ async function getProcessTree ( pid : number ) {
126
+ return new Promise < readonly PS [ ] > ( ( res , rej ) => {
127
+ pstree ( pid , ( err , children ) => {
128
+ if ( err ) rej ( err ) ;
129
+ res ( children ) ;
130
+ } ) ;
131
+ } ) ;
132
+ }
133
+
124
134
async function terminate ( pid : number ) {
135
+ if ( process . platform === 'win32' ) {
136
+ // on windows, use taskkill to terminate the process tree
137
+ await x ( 'taskkill' , [ '/PID' , `${ pid } ` , '/T' , '/F' ] ) ;
138
+ return ;
139
+ }
140
+ const children = await getProcessTree ( pid ) ;
141
+ // the process tree is ordered from parents -> children,
142
+ // so we'll iterate in the reverse order to terminate the children first
143
+ for ( let i = children . length - 1 ; i >= 0 ; i -- ) {
144
+ const child = children [ i ] ;
145
+ const pid = Number ( child . PID ) ;
146
+ kill ( pid ) ;
147
+ }
148
+ kill ( pid ) ;
149
+ }
150
+
151
+ function kill ( pid : number ) {
125
152
try {
126
- if ( process . platform === 'win32' ) {
127
- // on windows, use taskkill to terminate the process tree
128
- await x ( 'taskkill' , [ '/PID' , `${ pid } ` , '/T' , '/F' ] ) ;
129
- } else {
130
- process . kill ( - pid , 'SIGTERM' ) ; // Kill the process group
131
- }
153
+ process . kill ( pid ) ;
132
154
} catch {
133
- try {
134
- process . kill ( pid , 'SIGTERM' ) ; // Kill just the process
135
- } catch { }
155
+ // this can happen if a process has been automatically terminated.
136
156
}
137
157
}
0 commit comments