reset-framework-cli 1.2.2 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -20
- package/README.md +25 -25
- package/package.json +7 -7
- package/src/commands/build.js +144 -144
- package/src/commands/dev.js +195 -195
- package/src/commands/doctor.js +140 -140
- package/src/commands/init.js +946 -946
- package/src/commands/package.js +68 -68
- package/src/index.js +195 -195
- package/src/lib/backend.js +123 -123
- package/src/lib/context.js +66 -66
- package/src/lib/framework.js +57 -57
- package/src/lib/logger.js +11 -11
- package/src/lib/output.js +283 -283
- package/src/lib/process.js +303 -303
- package/src/lib/project.js +893 -866
- package/src/lib/toolchain.js +62 -62
- package/src/lib/ui.js +244 -244
- package/templates/basic/README.md +15 -15
- package/templates/basic/frontend/README.md +73 -73
- package/templates/basic/frontend/eslint.config.js +23 -23
- package/templates/basic/frontend/index.html +13 -13
- package/templates/basic/frontend/package.json +31 -31
- package/templates/basic/frontend/public/icons.svg +24 -24
- package/templates/basic/frontend/src/App.css +216 -216
- package/templates/basic/frontend/src/App.tsx +77 -77
- package/templates/basic/frontend/src/assets/vite.svg +1 -1
- package/templates/basic/frontend/src/index.css +111 -111
- package/templates/basic/frontend/src/lib/reset.ts +1 -1
- package/templates/basic/frontend/src/main.tsx +10 -10
- package/templates/basic/frontend/tsconfig.app.json +28 -28
- package/templates/basic/frontend/tsconfig.json +7 -7
- package/templates/basic/frontend/tsconfig.node.json +26 -26
- package/templates/basic/frontend/vite.config.ts +16 -16
- package/templates/basic/reset.config.json +58 -58
package/src/lib/process.js
CHANGED
|
@@ -1,303 +1,303 @@
|
|
|
1
|
-
import { spawn, spawnSync } from "node:child_process"
|
|
2
|
-
|
|
3
|
-
import { logger } from "./logger.js"
|
|
4
|
-
|
|
5
|
-
function sleep(ms) {
|
|
6
|
-
return new Promise((resolve) => {
|
|
7
|
-
setTimeout(resolve, ms)
|
|
8
|
-
})
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function resolveNpmCommand() {
|
|
12
|
-
return process.platform === "win32" ? "npm.cmd" : "npm"
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function resolvePackageManagerCommand(packageManager = "npm") {
|
|
16
|
-
if (packageManager === "npm") {
|
|
17
|
-
return resolveNpmCommand()
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (packageManager === "yarn") {
|
|
21
|
-
return process.platform === "win32" ? "yarn.cmd" : "yarn"
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (packageManager === "pnpm") {
|
|
25
|
-
return process.platform === "win32" ? "pnpm.cmd" : "pnpm"
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (packageManager === "bun") {
|
|
29
|
-
return process.platform === "win32" ? "bun.exe" : "bun"
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
throw new Error(`Unsupported package manager: ${packageManager}`)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function getInstallCommandArgs(packageManager = "npm") {
|
|
36
|
-
if (packageManager === "npm") {
|
|
37
|
-
return ["install", "--include=optional"]
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (packageManager === "yarn") {
|
|
41
|
-
return ["install"]
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return ["install"]
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function formatCommand(command, args = []) {
|
|
48
|
-
return [command, ...args].join(" ")
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function shouldUseShell(command) {
|
|
52
|
-
return process.platform === "win32" && typeof command === "string" && /\.(cmd|bat)$/i.test(command)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export async function runCommand(command, args = [], options = {}) {
|
|
56
|
-
const { cwd, dryRun = false, env } = options
|
|
57
|
-
|
|
58
|
-
logger.info(`$ ${formatCommand(command, args)}`)
|
|
59
|
-
|
|
60
|
-
if (dryRun) {
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
await new Promise((resolve, reject) => {
|
|
65
|
-
const child = spawn(command, args, {
|
|
66
|
-
cwd,
|
|
67
|
-
env: env ? { ...process.env, ...env } : process.env,
|
|
68
|
-
stdio: "inherit",
|
|
69
|
-
shell: shouldUseShell(command)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
child.once("error", reject)
|
|
73
|
-
child.once("exit", (code, signal) => {
|
|
74
|
-
if (code === 0) {
|
|
75
|
-
resolve(undefined)
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
reject(
|
|
80
|
-
new Error(
|
|
81
|
-
signal
|
|
82
|
-
? `Command terminated by signal: ${signal}`
|
|
83
|
-
: `Command exited with code ${code}`
|
|
84
|
-
)
|
|
85
|
-
)
|
|
86
|
-
})
|
|
87
|
-
})
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export async function captureCommandOutput(command, args = [], options = {}) {
|
|
91
|
-
const { cwd, env } = options
|
|
92
|
-
|
|
93
|
-
await Promise.resolve()
|
|
94
|
-
|
|
95
|
-
return await new Promise((resolve, reject) => {
|
|
96
|
-
const child = spawn(command, args, {
|
|
97
|
-
cwd,
|
|
98
|
-
env: env ? { ...process.env, ...env } : process.env,
|
|
99
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
100
|
-
shell: shouldUseShell(command)
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
let stdout = ""
|
|
104
|
-
let stderr = ""
|
|
105
|
-
|
|
106
|
-
child.stdout?.on("data", (chunk) => {
|
|
107
|
-
stdout += String(chunk)
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
child.stderr?.on("data", (chunk) => {
|
|
111
|
-
stderr += String(chunk)
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
child.once("error", reject)
|
|
115
|
-
child.once("exit", (code, signal) => {
|
|
116
|
-
if (code === 0) {
|
|
117
|
-
resolve(stdout.trim())
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
reject(
|
|
122
|
-
new Error(
|
|
123
|
-
signal
|
|
124
|
-
? `Command terminated by signal: ${signal}`
|
|
125
|
-
: (stderr.trim() || stdout.trim() || `Command exited with code ${code}`)
|
|
126
|
-
)
|
|
127
|
-
)
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function spawnCommand(command, args = [], options = {}) {
|
|
133
|
-
const { cwd, dryRun = false, env } = options
|
|
134
|
-
|
|
135
|
-
logger.info(`$ ${formatCommand(command, args)}`)
|
|
136
|
-
|
|
137
|
-
if (dryRun) {
|
|
138
|
-
return null
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return spawn(command, args, {
|
|
142
|
-
cwd,
|
|
143
|
-
env: env ? { ...process.env, ...env } : process.env,
|
|
144
|
-
detached: process.platform !== "win32",
|
|
145
|
-
stdio: "inherit",
|
|
146
|
-
shell: shouldUseShell(command)
|
|
147
|
-
})
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function waitForProcessExit(child, label) {
|
|
151
|
-
return new Promise((resolve, reject) => {
|
|
152
|
-
child.once("error", reject)
|
|
153
|
-
child.once("exit", (code, signal) => {
|
|
154
|
-
if (code === 0 || signal === "SIGINT" || signal === "SIGTERM") {
|
|
155
|
-
resolve(undefined)
|
|
156
|
-
return
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
reject(
|
|
160
|
-
new Error(
|
|
161
|
-
signal
|
|
162
|
-
? `${label} terminated by signal: ${signal}`
|
|
163
|
-
: `${label} exited with code ${code}`
|
|
164
|
-
)
|
|
165
|
-
)
|
|
166
|
-
})
|
|
167
|
-
})
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export function registerChildCleanup(children) {
|
|
171
|
-
const activeChildren = children.filter(Boolean)
|
|
172
|
-
let cleanupPromise = null
|
|
173
|
-
|
|
174
|
-
const waitForChildExit = (child, timeoutMs = 3000) =>
|
|
175
|
-
new Promise((resolve) => {
|
|
176
|
-
if (!child || child.exitCode !== null || child.signalCode !== null) {
|
|
177
|
-
resolve(true)
|
|
178
|
-
return
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const handleExit = () => {
|
|
182
|
-
clearTimeout(timeout)
|
|
183
|
-
child.off("error", handleError)
|
|
184
|
-
resolve(true)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const handleError = () => {
|
|
188
|
-
clearTimeout(timeout)
|
|
189
|
-
child.off("exit", handleExit)
|
|
190
|
-
resolve(true)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const timeout = setTimeout(() => {
|
|
194
|
-
child.off("exit", handleExit)
|
|
195
|
-
child.off("error", handleError)
|
|
196
|
-
resolve(false)
|
|
197
|
-
}, timeoutMs)
|
|
198
|
-
|
|
199
|
-
child.once("exit", handleExit)
|
|
200
|
-
child.once("error", handleError)
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
const terminateChild = async (child) => {
|
|
204
|
-
if (!child || child.pid == null || child.exitCode !== null || child.signalCode !== null) {
|
|
205
|
-
return
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (process.platform === "win32") {
|
|
209
|
-
const result = spawnSync("taskkill.exe", ["/pid", String(child.pid), "/t", "/f"], {
|
|
210
|
-
stdio: "ignore",
|
|
211
|
-
windowsHide: true
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
if (result.status !== 0 && child.exitCode === null && child.signalCode === null) {
|
|
215
|
-
try {
|
|
216
|
-
child.kill("SIGTERM")
|
|
217
|
-
} catch {
|
|
218
|
-
// Ignore late shutdown races.
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
await waitForChildExit(child, 2000)
|
|
223
|
-
return
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
try {
|
|
227
|
-
process.kill(-child.pid, "SIGTERM")
|
|
228
|
-
} catch (error) {
|
|
229
|
-
if (error?.code !== "ESRCH") {
|
|
230
|
-
try {
|
|
231
|
-
child.kill("SIGTERM")
|
|
232
|
-
} catch {
|
|
233
|
-
// Ignore late shutdown races.
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (await waitForChildExit(child, 3000)) {
|
|
239
|
-
return
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
try {
|
|
243
|
-
process.kill(-child.pid, "SIGKILL")
|
|
244
|
-
} catch (error) {
|
|
245
|
-
if (error?.code !== "ESRCH") {
|
|
246
|
-
try {
|
|
247
|
-
child.kill("SIGKILL")
|
|
248
|
-
} catch {
|
|
249
|
-
// Ignore late shutdown races.
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
await waitForChildExit(child, 1000)
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const cleanup = async () => {
|
|
258
|
-
if (cleanupPromise) {
|
|
259
|
-
return cleanupPromise
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
cleanupPromise = (async () => {
|
|
263
|
-
await Promise.all(activeChildren.map((child) => terminateChild(child)))
|
|
264
|
-
})()
|
|
265
|
-
|
|
266
|
-
return cleanupPromise
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const handleSignal = () => {
|
|
270
|
-
void cleanup().finally(() => {
|
|
271
|
-
process.exit(0)
|
|
272
|
-
})
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
process.on("SIGINT", handleSignal)
|
|
276
|
-
process.on("SIGTERM", handleSignal)
|
|
277
|
-
|
|
278
|
-
return async () => {
|
|
279
|
-
process.off("SIGINT", handleSignal)
|
|
280
|
-
process.off("SIGTERM", handleSignal)
|
|
281
|
-
await cleanup()
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export async function waitForUrl(url, options = {}) {
|
|
286
|
-
const { intervalMs = 250, timeoutMs = 15000 } = options
|
|
287
|
-
const deadline = Date.now() + timeoutMs
|
|
288
|
-
|
|
289
|
-
while (Date.now() < deadline) {
|
|
290
|
-
try {
|
|
291
|
-
const response = await fetch(url)
|
|
292
|
-
if (response.status < 500) {
|
|
293
|
-
return
|
|
294
|
-
}
|
|
295
|
-
} catch {
|
|
296
|
-
// Retry until timeout.
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
await sleep(intervalMs)
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
throw new Error(`Timed out waiting for ${url}`)
|
|
303
|
-
}
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process"
|
|
2
|
+
|
|
3
|
+
import { logger } from "./logger.js"
|
|
4
|
+
|
|
5
|
+
function sleep(ms) {
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
setTimeout(resolve, ms)
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function resolveNpmCommand() {
|
|
12
|
+
return process.platform === "win32" ? "npm.cmd" : "npm"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function resolvePackageManagerCommand(packageManager = "npm") {
|
|
16
|
+
if (packageManager === "npm") {
|
|
17
|
+
return resolveNpmCommand()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (packageManager === "yarn") {
|
|
21
|
+
return process.platform === "win32" ? "yarn.cmd" : "yarn"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (packageManager === "pnpm") {
|
|
25
|
+
return process.platform === "win32" ? "pnpm.cmd" : "pnpm"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (packageManager === "bun") {
|
|
29
|
+
return process.platform === "win32" ? "bun.exe" : "bun"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
throw new Error(`Unsupported package manager: ${packageManager}`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getInstallCommandArgs(packageManager = "npm") {
|
|
36
|
+
if (packageManager === "npm") {
|
|
37
|
+
return ["install", "--include=optional"]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (packageManager === "yarn") {
|
|
41
|
+
return ["install"]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return ["install"]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function formatCommand(command, args = []) {
|
|
48
|
+
return [command, ...args].join(" ")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function shouldUseShell(command) {
|
|
52
|
+
return process.platform === "win32" && typeof command === "string" && /\.(cmd|bat)$/i.test(command)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function runCommand(command, args = [], options = {}) {
|
|
56
|
+
const { cwd, dryRun = false, env } = options
|
|
57
|
+
|
|
58
|
+
logger.info(`$ ${formatCommand(command, args)}`)
|
|
59
|
+
|
|
60
|
+
if (dryRun) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await new Promise((resolve, reject) => {
|
|
65
|
+
const child = spawn(command, args, {
|
|
66
|
+
cwd,
|
|
67
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
68
|
+
stdio: "inherit",
|
|
69
|
+
shell: shouldUseShell(command)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
child.once("error", reject)
|
|
73
|
+
child.once("exit", (code, signal) => {
|
|
74
|
+
if (code === 0) {
|
|
75
|
+
resolve(undefined)
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
reject(
|
|
80
|
+
new Error(
|
|
81
|
+
signal
|
|
82
|
+
? `Command terminated by signal: ${signal}`
|
|
83
|
+
: `Command exited with code ${code}`
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function captureCommandOutput(command, args = [], options = {}) {
|
|
91
|
+
const { cwd, env } = options
|
|
92
|
+
|
|
93
|
+
await Promise.resolve()
|
|
94
|
+
|
|
95
|
+
return await new Promise((resolve, reject) => {
|
|
96
|
+
const child = spawn(command, args, {
|
|
97
|
+
cwd,
|
|
98
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
99
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
100
|
+
shell: shouldUseShell(command)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
let stdout = ""
|
|
104
|
+
let stderr = ""
|
|
105
|
+
|
|
106
|
+
child.stdout?.on("data", (chunk) => {
|
|
107
|
+
stdout += String(chunk)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
child.stderr?.on("data", (chunk) => {
|
|
111
|
+
stderr += String(chunk)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
child.once("error", reject)
|
|
115
|
+
child.once("exit", (code, signal) => {
|
|
116
|
+
if (code === 0) {
|
|
117
|
+
resolve(stdout.trim())
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
reject(
|
|
122
|
+
new Error(
|
|
123
|
+
signal
|
|
124
|
+
? `Command terminated by signal: ${signal}`
|
|
125
|
+
: (stderr.trim() || stdout.trim() || `Command exited with code ${code}`)
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function spawnCommand(command, args = [], options = {}) {
|
|
133
|
+
const { cwd, dryRun = false, env } = options
|
|
134
|
+
|
|
135
|
+
logger.info(`$ ${formatCommand(command, args)}`)
|
|
136
|
+
|
|
137
|
+
if (dryRun) {
|
|
138
|
+
return null
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return spawn(command, args, {
|
|
142
|
+
cwd,
|
|
143
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
144
|
+
detached: process.platform !== "win32",
|
|
145
|
+
stdio: "inherit",
|
|
146
|
+
shell: shouldUseShell(command)
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function waitForProcessExit(child, label) {
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
child.once("error", reject)
|
|
153
|
+
child.once("exit", (code, signal) => {
|
|
154
|
+
if (code === 0 || signal === "SIGINT" || signal === "SIGTERM") {
|
|
155
|
+
resolve(undefined)
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
reject(
|
|
160
|
+
new Error(
|
|
161
|
+
signal
|
|
162
|
+
? `${label} terminated by signal: ${signal}`
|
|
163
|
+
: `${label} exited with code ${code}`
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function registerChildCleanup(children) {
|
|
171
|
+
const activeChildren = children.filter(Boolean)
|
|
172
|
+
let cleanupPromise = null
|
|
173
|
+
|
|
174
|
+
const waitForChildExit = (child, timeoutMs = 3000) =>
|
|
175
|
+
new Promise((resolve) => {
|
|
176
|
+
if (!child || child.exitCode !== null || child.signalCode !== null) {
|
|
177
|
+
resolve(true)
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const handleExit = () => {
|
|
182
|
+
clearTimeout(timeout)
|
|
183
|
+
child.off("error", handleError)
|
|
184
|
+
resolve(true)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const handleError = () => {
|
|
188
|
+
clearTimeout(timeout)
|
|
189
|
+
child.off("exit", handleExit)
|
|
190
|
+
resolve(true)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const timeout = setTimeout(() => {
|
|
194
|
+
child.off("exit", handleExit)
|
|
195
|
+
child.off("error", handleError)
|
|
196
|
+
resolve(false)
|
|
197
|
+
}, timeoutMs)
|
|
198
|
+
|
|
199
|
+
child.once("exit", handleExit)
|
|
200
|
+
child.once("error", handleError)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
const terminateChild = async (child) => {
|
|
204
|
+
if (!child || child.pid == null || child.exitCode !== null || child.signalCode !== null) {
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (process.platform === "win32") {
|
|
209
|
+
const result = spawnSync("taskkill.exe", ["/pid", String(child.pid), "/t", "/f"], {
|
|
210
|
+
stdio: "ignore",
|
|
211
|
+
windowsHide: true
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
if (result.status !== 0 && child.exitCode === null && child.signalCode === null) {
|
|
215
|
+
try {
|
|
216
|
+
child.kill("SIGTERM")
|
|
217
|
+
} catch {
|
|
218
|
+
// Ignore late shutdown races.
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
await waitForChildExit(child, 2000)
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
process.kill(-child.pid, "SIGTERM")
|
|
228
|
+
} catch (error) {
|
|
229
|
+
if (error?.code !== "ESRCH") {
|
|
230
|
+
try {
|
|
231
|
+
child.kill("SIGTERM")
|
|
232
|
+
} catch {
|
|
233
|
+
// Ignore late shutdown races.
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (await waitForChildExit(child, 3000)) {
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
process.kill(-child.pid, "SIGKILL")
|
|
244
|
+
} catch (error) {
|
|
245
|
+
if (error?.code !== "ESRCH") {
|
|
246
|
+
try {
|
|
247
|
+
child.kill("SIGKILL")
|
|
248
|
+
} catch {
|
|
249
|
+
// Ignore late shutdown races.
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
await waitForChildExit(child, 1000)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const cleanup = async () => {
|
|
258
|
+
if (cleanupPromise) {
|
|
259
|
+
return cleanupPromise
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
cleanupPromise = (async () => {
|
|
263
|
+
await Promise.all(activeChildren.map((child) => terminateChild(child)))
|
|
264
|
+
})()
|
|
265
|
+
|
|
266
|
+
return cleanupPromise
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const handleSignal = () => {
|
|
270
|
+
void cleanup().finally(() => {
|
|
271
|
+
process.exit(0)
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
process.on("SIGINT", handleSignal)
|
|
276
|
+
process.on("SIGTERM", handleSignal)
|
|
277
|
+
|
|
278
|
+
return async () => {
|
|
279
|
+
process.off("SIGINT", handleSignal)
|
|
280
|
+
process.off("SIGTERM", handleSignal)
|
|
281
|
+
await cleanup()
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export async function waitForUrl(url, options = {}) {
|
|
286
|
+
const { intervalMs = 250, timeoutMs = 15000 } = options
|
|
287
|
+
const deadline = Date.now() + timeoutMs
|
|
288
|
+
|
|
289
|
+
while (Date.now() < deadline) {
|
|
290
|
+
try {
|
|
291
|
+
const response = await fetch(url)
|
|
292
|
+
if (response.status < 500) {
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
} catch {
|
|
296
|
+
// Retry until timeout.
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
await sleep(intervalMs)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
throw new Error(`Timed out waiting for ${url}`)
|
|
303
|
+
}
|