Skip to content

Commit b4eec9b

Browse files
committed
feat: add kill timeout
1 parent 8682363 commit b4eec9b

File tree

4 files changed

+16
-2
lines changed

4 files changed

+16
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ There are a couple of command-line options that can be used to control which fil
5151
- `--poll` - Force polling for file changes (Caution! CPU-heavy!)
5252
- `--respawn` - Keep watching for changes after the script has exited
5353
- `--timestamp` - The timestamp format to use for logging restarts
54+
- `--kill_timeout` - Time in milliseconds to wait before force killing a stuck child process on reload
5455
- `--vm` - Load files using Node's VM
5556

5657
### Passing arguments to node

src/cfg.cts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const defaultConfig: Options = {
2121
poll: false,
2222
respawn: false,
2323
timestamp: 'HH:MM:ss',
24+
kill_timeout: undefined,
2425
vm: true
2526
}
2627

src/cli.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const nodeCustom = ['inspect', 'inspect-brk', 'no-warnings']
1414
const nodeString = ['require']
1515

1616
const nodeDevBoolean = ['clear', 'dedupe', 'fork', 'notify', 'poll', 'respawn', 'vm']
17-
const nodeDevNumber = ['debounce', 'deps', 'interval']
17+
const nodeDevNumber = ['debounce', 'deps', 'interval', 'kill_timeout']
1818
const nodeDevString = ['graceful_ipc', 'ignore', 'timestamp']
1919

2020
const alias = { ...nodeAlias }
@@ -70,6 +70,7 @@ export interface Options {
7070
ignore: string[]
7171
interval: number
7272
timestamp: string
73+
kill_timeout?: number
7374
}
7475

7576
export const cli = (argv: string[]) => {

src/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ChildProcess, fork } from 'node:child_process'
22
import { join } from 'node:path'
33
import { pathToFileURL } from 'node:url'
4+
import { clearTimeout } from 'node:timers'
45
import semver from 'semver'
56
import { clearFactory } from './clear.cjs'
67
import { Options } from './cli.js'
@@ -23,7 +24,8 @@ export const dev = (script: string, scriptArgs: string[], nodeArgs: string[], {
2324
notify: notifyEnabled,
2425
poll: forcePolling,
2526
respawn,
26-
timestamp
27+
timestamp,
28+
kill_timeout: killTimeout
2729
}: Options) => {
2830
if (!script) {
2931
console.log('Usage: node-dev [options] script [arguments]\n')
@@ -55,6 +57,7 @@ export const dev = (script: string, scriptArgs: string[], nodeArgs: string[], {
5557

5658
const watcher = new FileWatcher({ debounce, forcePolling, interval })
5759
let isPaused = false
60+
let killTimer: NodeJS.Timeout | undefined
5861

5962
// The child_process
6063
let child: (ChildProcess & { respawn?: boolean }) | undefined
@@ -87,6 +90,8 @@ export const dev = (script: string, scriptArgs: string[], nodeArgs: string[], {
8790
*/
8891
function start() {
8992
isPaused = false
93+
if (killTimer)
94+
clearTimeout(killTimer)
9095

9196
const args = nodeArgs.slice()
9297

@@ -128,6 +133,12 @@ export const dev = (script: string, scriptArgs: string[], nodeArgs: string[], {
128133
function stop(willTerminate?: boolean) {
129134
child!.respawn = true
130135
if (!willTerminate) {
136+
if (typeof killTimeout === 'number') {
137+
killTimer = setTimeout(() => {
138+
log.warn('Sending SIGKILL after timeout (%s ms)', killTimeout)
139+
child?.kill('SIGKILL')
140+
}, killTimeout)
141+
}
131142
if (gracefulIPC) {
132143
log.info('Sending IPC: ' + JSON.stringify(gracefulIPC))
133144
child!.send(gracefulIPC)

0 commit comments

Comments
 (0)