From da34146e0d1af7ae7c7afb5175ba1f1b33c496e8 Mon Sep 17 00:00:00 2001 From: adminy Date: Sun, 4 Dec 2022 14:12:46 +0000 Subject: [PATCH 1/2] feat: support on-event command for watch changes --- README.md | 1 + args.js | 5 ++++- help/start.txt | 4 ++++ lib/watch/fork.js | 7 +++++++ lib/watch/index.js | 8 +++++--- start.js | 2 +- test/args.test.js | 14 +++++++++++++- test/start.test.js | 15 +++++++++++++-- 8 files changed, 48 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0f51490f..c98c6faa 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ You can pass the following options via CLI arguments. You can also use `--config | Set the inspector host to listen on (default: loopback address or `0.0.0.0` inside Docker) | | `--debug-host` | `FASTIFY_DEBUG_HOST` | | Prints pretty logs | `-P` | `--pretty-logs` | `FASTIFY_PRETTY_LOGS` | | Watch process.cwd() directory for changes, recursively; when that happens, the process will auto reload | `-w` | `--watch` | `FASTIFY_WATCH` | +| When watch triggers a restart, run a command. | `-e` | `--on-watch-event` | `FASTIFY_ON_WATCH_EVENT ` | | Ignore changes to the specified files or directories when watch is enabled. (e.g. `--ignore-watch='node_modules .git logs/error.log'` ) | | `--ignore-watch` | `FASTIFY_IGNORE_WATCH` | | Prints events triggered by watch listener (useful to debug unexpected reload when using `--watch` ) | `-V` | `--verbose-watch` | `FASTIFY_VERBOSE_WATCH` | | Use custom options | `-o` | `--options` | `FASTIFY_OPTIONS` | diff --git a/args.js b/args.js index 0e689b68..175d2811 100644 --- a/args.js +++ b/args.js @@ -11,6 +11,7 @@ const DEFAULT_ARGUMENTS = { prettyLogs: false, watch: false, verboseWatch: false, + onWatchEvent: '', debug: false, debugPort: 9320, options: false, @@ -27,7 +28,7 @@ module.exports = function parseArgs (args) { 'populate--': true }, number: ['port', 'inspect-port', 'body-limit', 'plugin-timeout', 'close-grace-delay'], - string: ['log-level', 'address', 'socket', 'prefix', 'ignore-watch', 'logging-module', 'debug-host', 'lang', 'require', 'config'], + string: ['log-level', 'address', 'socket', 'prefix', 'ignore-watch', 'on-watch-event', 'logging-module', 'debug-host', 'lang', 'require', 'config'], boolean: ['pretty-logs', 'options', 'watch', 'verbose-watch', 'debug', 'standardlint'], envPrefix: 'FASTIFY_', alias: { @@ -41,6 +42,7 @@ module.exports = function parseArgs (args) { prefix: ['x'], require: ['r'], debug: ['d'], + 'on-watch-event': ['e'], 'debug-port': ['I'], 'log-level': ['l'], 'pretty-logs': ['P'], @@ -81,6 +83,7 @@ module.exports = function parseArgs (args) { debugHost: parsedArgs.debugHost, ignoreWatch, verboseWatch: parsedArgs.verboseWatch, + onWatchEvent: parsedArgs.onWatchEvent, logLevel: parsedArgs.logLevel, address: parsedArgs.address, socket: parsedArgs.socket, diff --git a/help/start.txt b/help/start.txt index 84b642da..dd7ef2a7 100644 --- a/help/start.txt +++ b/help/start.txt @@ -38,6 +38,10 @@ OPTS [env: FASTIFY_WATCH] Watch process.cwd() directory for changes, recursively; when that happens, the process will auto reload. + -e, --on-watch-event + [env: FASTIFY_ON_WATCH_EVENT] + When watch triggers a restart, run a command. + -x, --prefix [env: FASTIFY_PREFIX] Set the prefix diff --git a/lib/watch/fork.js b/lib/watch/fork.js index d4ee7922..bcceba32 100644 --- a/lib/watch/fork.js +++ b/lib/watch/fork.js @@ -1,5 +1,6 @@ 'use strict' +const { execSync } = require('child_process') const chalk = require('chalk') const { stop, runFastify } = require('../../start') @@ -37,6 +38,12 @@ process.on('uncaughtException', (err) => { }) const main = async () => { + + if (process.env.onWatchEvent) { + const output = execSync(process.env.onWatchEvent).toString() + process.send(output) + } + fastify = await runFastify(process.argv.splice(2)) const type = process.env.childEvent diff --git a/lib/watch/index.js b/lib/watch/index.js index 12adee00..4ac56f51 100644 --- a/lib/watch/index.js +++ b/lib/watch/index.js @@ -10,7 +10,7 @@ const EventEmitter = require('events') const chokidar = require('chokidar') const forkPath = path.join(__dirname, './fork.js') -const watch = function (args, ignoreWatch, verboseWatch) { +const watch = function (args, ignoreWatch, verboseWatch, onWatchEvent) { const emitter = new EventEmitter() let allStop = false let childs = [] @@ -38,12 +38,13 @@ const watch = function (args, ignoreWatch, verboseWatch) { let readyEmitted = false const run = (event) => { - const childEvent = { childEvent: event } + const childEvent = { childEvent: event, onWatchEvent } const env = Object.assign({}, require('dotenv').config().parsed, process.env, childEvent) const _child = cp.fork(forkPath, args, { env, cwd: process.cwd(), - encoding: 'utf8' + encoding: 'utf8', + stdio: 'inherit' }) _child.on('exit', function (code, signal) { @@ -106,6 +107,7 @@ const watch = function (args, ignoreWatch, verboseWatch) { }) emitter.stop = stop.bind(null, watcher) + emitter.childs = childs return emitter } diff --git a/start.js b/start.js index 9d0116bd..e5b5fafe 100755 --- a/start.js +++ b/start.js @@ -49,7 +49,7 @@ async function start (args) { loadModules(opts) if (opts.watch) { - return watch(args, opts.ignoreWatch, opts.verboseWatch) + return watch(args, opts.ignoreWatch, opts.verboseWatch, opts.onWatchEvent) } return runFastify(args) diff --git a/test/args.test.js b/test/args.test.js index 0162c341..2ee56c49 100644 --- a/test/args.test.js +++ b/test/args.test.js @@ -13,6 +13,7 @@ test('should parse args correctly', t => { '--log-level', 'info', '--pretty-logs', 'true', '--watch', 'true', + '--on-watch-event', 'echo changed', '--ignore-watch', 'ignoreme.js', '--verbose-watch', 'true', '--options', 'true', @@ -34,6 +35,7 @@ test('should parse args correctly', t => { prettyLogs: true, options: true, watch: true, + onWatchEvent: 'echo changed', ignoreWatch: 'node_modules build dist .git bower_components logs .swp .nyc_output ignoreme.js', verboseWatch: true, port: 7777, @@ -65,6 +67,7 @@ test('should parse args with = assignment correctly', t => { '--log-level=info', '--pretty-logs=true', '--watch=true', + '--on-watch-event="echo changed"', '--ignore-watch=ignoreme.js', '--verbose-watch=true', '--options=true', @@ -86,6 +89,7 @@ test('should parse args with = assignment correctly', t => { prettyLogs: true, options: true, watch: true, + onWatchEvent: 'echo changed', ignoreWatch: 'node_modules build dist .git bower_components logs .swp .nyc_output ignoreme.js', verboseWatch: true, port: 7777, @@ -116,6 +120,7 @@ test('should parse env vars correctly', t => { process.env.FASTIFY_LOG_LEVEL = 'info' process.env.FASTIFY_PRETTY_LOGS = 'true' process.env.FASTIFY_WATCH = 'true' + process.env.FASTIFY_ON_WATCH_EVENT = 'echo changed' process.env.FASTIFY_IGNORE_WATCH = 'ignoreme.js' process.env.FASTIFY_VERBOSE_WATCH = 'true' process.env.FASTIFY_OPTIONS = 'true' @@ -136,6 +141,7 @@ test('should parse env vars correctly', t => { delete process.env.FASTIFY_LOG_LEVEL delete process.env.FASTIFY_PRETTY_LOGS delete process.env.FASTIFY_WATCH + delete process.env.FASTIFY_ON_WATCH_EVENT delete process.env.FASTIFY_IGNORE_WATCH delete process.env.FASTIFY_VERBOSE_WATCH delete process.env.FASTIFY_OPTIONS @@ -156,6 +162,7 @@ test('should parse env vars correctly', t => { prettyLogs: true, options: true, watch: true, + onWatchEvent: 'echo changed', ignoreWatch: 'node_modules build dist .git bower_components logs .swp .nyc_output ignoreme.js', verboseWatch: true, address: 'fastify.io:9999', @@ -177,7 +184,7 @@ test('should parse env vars correctly', t => { }) test('should respect default values', t => { - t.plan(13) + t.plan(14) const argv = [ 'app.js' @@ -189,6 +196,7 @@ test('should respect default values', t => { t.equal(parsedArgs.options, false) t.equal(parsedArgs.prettyLogs, false) t.equal(parsedArgs.watch, false) + t.equal(parsedArgs.onWatchEvent, '') t.equal(parsedArgs.ignoreWatch, 'node_modules build dist .git bower_components logs .swp .nyc_output') t.equal(parsedArgs.verboseWatch, false) t.equal(parsedArgs.logLevel, 'fatal') @@ -211,6 +219,7 @@ test('should parse custom plugin options', t => { '--log-level', 'info', '--pretty-logs', 'true', '--watch', 'true', + '--on-watch-event', 'echo changed', '--ignore-watch', 'ignoreme.js', '--verbose-watch', 'true', '--options', 'true', @@ -239,6 +248,7 @@ test('should parse custom plugin options', t => { prettyLogs: true, options: true, watch: true, + onWatchEvent: 'echo changed', ignoreWatch: 'node_modules build dist .git bower_components logs .swp .nyc_output ignoreme.js', verboseWatch: true, port: 7777, @@ -284,6 +294,7 @@ test('should parse config file correctly and prefer config values over default o prettyLogs: true, options: false, watch: true, + onWatchEvent: '', debug: false, debugPort: 4000, debugHost: '1.1.1.1', @@ -323,6 +334,7 @@ test('should prefer command line args over config file options', t => { prettyLogs: true, options: false, watch: true, + onWatchEvent: '', debug: false, debugPort: 9320, debugHost: '1.1.1.1', diff --git a/test/start.test.js b/test/start.test.js index 0994c167..904b718d 100644 --- a/test/start.test.js +++ b/test/start.test.js @@ -564,7 +564,7 @@ test('should start the server with watch options that the child process restart }) test('should start the server with watch and verbose-watch options that the child process restart when directory changed with console message about changes ', { skip: process.platform === 'win32' }, async (t) => { - t.plan(4) + t.plan(5) const spy = sinon.spy() const watch = proxyquire('../lib/watch', { @@ -582,7 +582,7 @@ test('should start the server with watch and verbose-watch options that the chil const port = getPort() await writeFile(tmpjs, 'hello world') - const argv = ['-p', port, '-w', '--verbose-watch', './examples/plugin.js'] + const argv = ['-p', port, '-w', '--verbose-watch', '--on-watch-event="echo changed"', './examples/plugin.js'] const fastifyEmitter = await start.start(argv) t.teardown(async () => { @@ -592,12 +592,23 @@ test('should start the server with watch and verbose-watch options that the chil await fastifyEmitter.stop() }) + // listen for any of the forked processes messages + const onChanges = new Promise((resolve, reject) => { + for (const child of fastifyEmitter.childs) { + child.on('message', resolve) + setTimeout(reject, 300) + } + }) + await once(fastifyEmitter, 'ready') t.pass('should receive ready event') await writeFile(tmpjs, 'hello fastify', { flag: 'a+' }) // chokidar watch can't catch change event in CI, but local test is all ok. you can remove annotation in local environment. t.pass('change tmpjs') + const message = await onChanges + t.ok(message.includes('changed'), 'forked process sends "changed" message') + // this might happen more than once but does not matter in this context await once(fastifyEmitter, 'restart') t.pass('should receive restart event') From 1dfd6fc59545fcb23ec888fb1bf08f829bbcbb99 Mon Sep 17 00:00:00 2001 From: adminy Date: Sun, 4 Dec 2022 22:33:20 +0000 Subject: [PATCH 2/2] make the linter happy --- lib/watch/fork.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/watch/fork.js b/lib/watch/fork.js index bcceba32..73f09fa9 100644 --- a/lib/watch/fork.js +++ b/lib/watch/fork.js @@ -38,7 +38,6 @@ process.on('uncaughtException', (err) => { }) const main = async () => { - if (process.env.onWatchEvent) { const output = execSync(process.env.onWatchEvent).toString() process.send(output)