-
Notifications
You must be signed in to change notification settings - Fork 28.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
655 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
'use strict'; | ||
const { | ||
ArrayPrototypeFilter, | ||
ArrayPrototypeJoin, | ||
ArrayPrototypeMap, | ||
ArrayPrototypePush, | ||
ArrayPrototypePushApply, | ||
ArrayPrototypeSlice, | ||
} = primordials; | ||
|
||
const { | ||
prepareMainThreadExecution, | ||
markBootstrapComplete | ||
} = require('internal/process/pre_execution'); | ||
const { getOptionValue } = require('internal/options'); | ||
const { emitExperimentalWarning } = require('internal/util'); | ||
const { FilesWatcher } = require('internal/watch_mode/files_watcher'); | ||
const { green, blue, red, white, clear } = require('internal/util/colors'); | ||
|
||
const { spawn } = require('child_process'); | ||
const { inspect } = require('util'); | ||
const { setTimeout, clearTimeout } = require('timers'); | ||
const { resolve } = require('path'); | ||
const { once, on } = require('events'); | ||
|
||
|
||
prepareMainThreadExecution(false, false); | ||
markBootstrapComplete(); | ||
|
||
// TODO(MoLow): Make kill signal configurable | ||
const kKillSignal = 'SIGTERM'; | ||
const kShouldFilterModules = getOptionValue('--watch-path').length === 0; | ||
const kWatchedPaths = getOptionValue('--watch-path').length ? | ||
ArrayPrototypeMap(getOptionValue('--watch-path'), (path) => resolve(path)) : | ||
[]; | ||
const kCommand = ArrayPrototypeSlice(process.argv, 1); | ||
const kCommandStr = inspect(ArrayPrototypeJoin(kCommand, ' ')); | ||
const args = ArrayPrototypeFilter(process.execArgv, (x, i, arr) => | ||
x !== '--watch-path' && arr[i - 1] !== '--watch-path' && x !== '--watch'); | ||
ArrayPrototypePush(args, '--watch-report-ipc'); | ||
ArrayPrototypePushApply(args, kCommand); | ||
|
||
const watcher = new FilesWatcher({ throttle: 500, mode: kShouldFilterModules ? 'filter' : 'all' }); | ||
kWatchedPaths.forEach((p) => watcher.watchPath(p)); | ||
let graceTimer; | ||
let child; | ||
let exited; | ||
|
||
function start() { | ||
// Spawning in detached mode so node can control when signals are forwarded | ||
exited = false; | ||
const stdio = kShouldFilterModules ? ['inherit', 'inherit', 'inherit', 'ipc'] : undefined; | ||
child = spawn(process.execPath, args, { stdio, detached: true }); | ||
watcher.watchChildProcessModules(child); | ||
child.once('exit', (code) => { | ||
exited = true; | ||
if (code === 0) { | ||
process.stdout.write(`${blue}Completed running ${kCommandStr}${white}\n`); | ||
} else { | ||
process.stdout.write(`${red}Failed running ${kCommandStr}${white}\n`); | ||
} | ||
}); | ||
} | ||
|
||
async function killAndWait(signal = kKillSignal) { | ||
child?.removeAllListeners(); | ||
if (!child || child.killed || exited) { | ||
return; | ||
} | ||
const onExit = once(child, 'exit'); | ||
child.kill(signal); | ||
const { 0: exitCode } = await onExit; | ||
return exitCode; | ||
} | ||
|
||
function reportGracefulTermination() { | ||
// Log if process takes more than 500ms to stop | ||
let reported = false; | ||
clearTimeout(graceTimer); | ||
graceTimer = setTimeout(() => { | ||
reported = true; | ||
process.stdout.write(`${blue}Waiting for graceful termination...${white}\n`); | ||
}, 500).unref(); | ||
return () => { | ||
clearTimeout(graceTimer); | ||
if (reported) { | ||
process.stdout.write(`${clear}${green}Gracefully restarted ${kCommandStr}${white}\n`); | ||
} | ||
}; | ||
} | ||
|
||
async function stop() { | ||
watcher.clearFileFilters(); | ||
const clearGraceReport = reportGracefulTermination(); | ||
await killAndWait(); | ||
clearGraceReport(); | ||
} | ||
|
||
async function restart() { | ||
process.stdout.write(`${clear}${green}Restarting ${kCommandStr}${white}\n`); | ||
await stop(); | ||
start(); | ||
} | ||
|
||
(async () => { | ||
emitExperimentalWarning('Watch mode'); | ||
start(); | ||
|
||
// eslint-disable-next-line no-unused-vars | ||
for await (const _ of on(watcher, 'changed')) { | ||
await restart(); | ||
} | ||
})(); | ||
|
||
// Exiting gracefully to avoid stdout/stderr getting written after | ||
// parent process is killed. | ||
// this is fairly safe since user code cannot run in this process | ||
function signalHandler(signal) { | ||
return async () => { | ||
watcher.clear(); | ||
process.exit(await killAndWait(signal)); | ||
}; | ||
} | ||
process.on('SIGTERM', signalHandler('SIGTERM')); | ||
process.on('SIGINT', signalHandler('SIGINT')); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
'use strict'; | ||
|
||
module.exports = { | ||
blue: '', | ||
green: '', | ||
white: '', | ||
red: '', | ||
clear: '', | ||
refresh() { | ||
if (process.stderr.isTTY && process.stderr.hasColors()) { | ||
module.exports.blue = '\u001b[34m'; | ||
module.exports.green = '\u001b[32m'; | ||
module.exports.white = '\u001b[39m'; | ||
module.exports.red = '\u001b[31m'; | ||
module.exports.clear = '\u001bc'; | ||
} | ||
} | ||
}; | ||
|
||
module.exports.refresh(); |
Oops, something went wrong.