diff --git a/README.md b/README.md index fcf84461..32041620 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ You can pass the following options via cli arguments, every options has the corr | 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` | | 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` ) | | `--verbose-watch` | `FASTIFY_VERBOSE_WATCH` | | Use custom options | `-o` | `--options` | `FASTIFY_OPTIONS` | | Set the prefix | `-x` | `--prefix` | `FASTIFY_PREFIX` | | Set the plugin timeout | `-T` | `--plugin-timeout` | `FASTIFY_PLUGIN_TIMEOUT` | diff --git a/args.js b/args.js index 4e7f7e2b..97d8f772 100644 --- a/args.js +++ b/args.js @@ -8,8 +8,8 @@ module.exports = function parseArgs (args) { 'populate--': true }, number: ['port', 'inspect-port', 'body-limit', 'plugin-timeout'], - boolean: ['pretty-logs', 'options', 'watch', 'debug'], string: ['log-level', 'address', 'socket', 'prefix', 'ignore-watch', 'logging-module', 'debug-host', 'lang', 'require'], + boolean: ['pretty-logs', 'options', 'watch', 'verbose-watch', 'debug'], envPrefix: 'FASTIFY_', alias: { port: ['p'], @@ -31,6 +31,7 @@ module.exports = function parseArgs (args) { 'log-level': 'fatal', 'pretty-logs': false, watch: false, + verboseWatch: false, debug: false, debugPort: 9320, 'ignore-watch': 'node_modules build dist .git bower_components logs .swp .nyc_output', @@ -57,6 +58,7 @@ module.exports = function parseArgs (args) { debugPort: parsedArgs.debugPort, debugHost: parsedArgs.debugHost, ignoreWatch: parsedArgs.ignoreWatch, + verboseWatch: parsedArgs.verboseWatch, logLevel: parsedArgs.logLevel, address: parsedArgs.address, socket: parsedArgs.socket, diff --git a/lib/watch/index.js b/lib/watch/index.js index f414c16e..dd80eac0 100644 --- a/lib/watch/index.js +++ b/lib/watch/index.js @@ -3,14 +3,14 @@ const path = require('path') const cp = require('child_process') const chalk = require('chalk') -const { arrayToRegExp } = require('./utils') +const { arrayToRegExp, logWatchVerbose } = require('./utils') const { GRACEFUL_SHUT } = require('./constants.js') const EventEmitter = require('events') const chokidar = require('chokidar') const forkPath = path.join(__dirname, './fork.js') -const watch = function (args, ignoreWatch) { +const watch = function (args, ignoreWatch, verboseWatch) { const emitter = new EventEmitter() let allStop = false let childs = [] @@ -84,7 +84,10 @@ const watch = function (args, ignoreWatch) { const watcher = chokidar.watch(process.cwd(), { ignored: ignoredPattern }) watcher.on('ready', function () { - watcher.on('all', function () { + watcher.on('all', function (event, filepath) { + if (verboseWatch) { + logWatchVerbose(event, filepath) + } try { const child = childs.shift() child.send(GRACEFUL_SHUT) diff --git a/lib/watch/utils.js b/lib/watch/utils.js index 5ee00c3c..894a2626 100644 --- a/lib/watch/utils.js +++ b/lib/watch/utils.js @@ -1,5 +1,8 @@ 'use strict' +const chalk = require('chalk') +const path = require('path') + const arrayToRegExp = (arr) => { const reg = arr.map((file) => { if (/^\./.test(file)) { return `\\${file}` } @@ -8,6 +11,16 @@ const arrayToRegExp = (arr) => { return new RegExp(`(${reg})`) } +const logWatchVerbose = (event, filepath) => { + const relativeFilepath = path.relative(process.cwd(), filepath) + console.log( + chalk.gray( + `[fastify-cli] watch - '${event}' occurred on '${relativeFilepath}'` + ) + ) +} + module.exports = { - arrayToRegExp + arrayToRegExp, + logWatchVerbose } diff --git a/start.js b/start.js index 9a5b4c35..b9b50766 100755 --- a/start.js +++ b/start.js @@ -48,7 +48,7 @@ async function start (args) { loadModules(opts) if (opts.watch) { - return watch(args, opts.ignoreWatch) + return watch(args, opts.ignoreWatch, opts.verboseWatch) } return runFastify(args) diff --git a/test/args.test.js b/test/args.test.js index 1a34181d..47705ecc 100644 --- a/test/args.test.js +++ b/test/args.test.js @@ -14,6 +14,7 @@ test('should parse args correctly', t => { '--pretty-logs', 'true', '--watch', 'true', '--ignore-watch', 'ignoreme.js', + '--verbose-watch', 'true', '--options', 'true', '--prefix', 'FASTIFY_', '--plugin-timeout', '500', @@ -33,6 +34,7 @@ test('should parse args correctly', t => { options: true, watch: true, ignoreWatch: 'ignoreme.js', + verboseWatch: true, port: 7777, address: 'fastify.io:9999', socket: 'fastify.io.socket:9999', @@ -62,6 +64,7 @@ test('should parse args with = assignment correctly', t => { '--pretty-logs=true', '--watch=true', '--ignore-watch=ignoreme.js', + '--verbose-watch=true', '--options=true', '--prefix=FASTIFY_', '--plugin-timeout=500', @@ -81,6 +84,7 @@ test('should parse args with = assignment correctly', t => { options: true, watch: true, ignoreWatch: 'ignoreme.js', + verboseWatch: true, port: 7777, address: 'fastify.io:9999', socket: 'fastify.io.socket:9999', @@ -109,6 +113,7 @@ test('should parse env vars correctly', t => { process.env.FASTIFY_PRETTY_LOGS = 'true' process.env.FASTIFY_WATCH = 'true' process.env.FASTIFY_IGNORE_WATCH = 'ignoreme.js' + process.env.FASTIFY_VERBOSE_WATCH = 'true' process.env.FASTIFY_OPTIONS = 'true' process.env.FASTIFY_PREFIX = 'FASTIFY_' process.env.FASTIFY_BODY_LIMIT = '5242880' @@ -127,6 +132,7 @@ test('should parse env vars correctly', t => { delete process.env.FASTIFY_PRETTY_LOGS delete process.env.FASTIFY_WATCH delete process.env.FASTIFY_IGNORE_WATCH + delete process.env.FASTIFY_VERBOSE_WATCH delete process.env.FASTIFY_OPTIONS delete process.env.FASTIFY_PREFIX delete process.env.FASTIFY_BODY_LIMIT @@ -145,6 +151,7 @@ test('should parse env vars correctly', t => { options: true, watch: true, ignoreWatch: 'ignoreme.js', + verboseWatch: true, address: 'fastify.io:9999', bodyLimit: 5242880, logLevel: 'info', @@ -163,7 +170,7 @@ test('should parse env vars correctly', t => { }) test('should respect default values', t => { - t.plan(11) + t.plan(12) const argv = [ 'app.js' @@ -176,6 +183,7 @@ test('should respect default values', t => { t.is(parsedArgs.prettyLogs, false) t.is(parsedArgs.watch, false) t.is(parsedArgs.ignoreWatch, 'node_modules build dist .git bower_components logs .swp .nyc_output') + t.is(parsedArgs.verboseWatch, false) t.is(parsedArgs.logLevel, 'fatal') t.is(parsedArgs.pluginTimeout, 10000) t.is(parsedArgs.debug, false) @@ -196,6 +204,7 @@ test('should parse custom plugin options', t => { '--pretty-logs', 'true', '--watch', 'true', '--ignore-watch', 'ignoreme.js', + '--verbose-watch', 'true', '--options', 'true', '--prefix', 'FASTIFY_', '--plugin-timeout', '500', @@ -222,6 +231,7 @@ test('should parse custom plugin options', t => { options: true, watch: true, ignoreWatch: 'ignoreme.js', + verboseWatch: true, port: 7777, address: 'fastify.io:9999', socket: 'fastify.io.socket:9999', diff --git a/test/start.test.js b/test/start.test.js index 937ff23f..9dbe650a 100644 --- a/test/start.test.js +++ b/test/start.test.js @@ -558,6 +558,49 @@ test('should start the server with watch options that the child process restart t.pass('should receive restart event') }) +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: onGithubAction }, async (t) => { + t.plan(5) + + const spy = sinon.spy() + const watch = proxyquire('../lib/watch', { + './utils': { + logWatchVerbose: spy + } + }) + + const start = proxyquire('../start', { + './lib/watch': watch + }) + + const tmpjs = path.resolve(baseFilename + '.js') + + await writeFile(tmpjs, 'hello world') + const argv = ['-p', '4042', '-w', '--verbose-watch', './examples/plugin.js'] + const fastifyEmitter = await start.start(argv) + + t.tearDown(() => { + if (fs.existsSync(tmpjs)) { + fs.unlinkSync(tmpjs) + } + fastifyEmitter.emit('close') + }) + + await once(fastifyEmitter, 'start') + t.pass('should receive start event') + + 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') + + // this might happen more than once but does not matter in this context + await once(fastifyEmitter, 'restart') + t.pass('should receive restart event') + + t.ok(spy.calledOnce, 'should print a console message on file update') +}) + test('should reload the env on restart when watching', async (t) => { const testdir = t.testdir({ '.env': 'GREETING=world',