diff --git a/README.md b/README.md index e857cf7f..b7fe7daa 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ You can pass the following options via CLI arguments. You can also use `--config | Set the inspector port (default: 9320) | `-I` | `--debug-port` | `FASTIFY_DEBUG_PORT` | | Set the inspector host to listen on (default: loopback address or `0.0.0.0` inside Docker or Kubernetes) | | `--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` | +| Watch for changes and auto reload. Optional dir(s) (e.g. `--watch api`) limit watch to those; default process.cwd(). | `-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` ) | `-V` | `--verbose-watch` | `FASTIFY_VERBOSE_WATCH` | | Use custom options | `-o` | `--options` | `FASTIFY_OPTIONS` | diff --git a/args.js b/args.js index 854ca506..125c16e1 100644 --- a/args.js +++ b/args.js @@ -28,8 +28,8 @@ module.exports = function parseArgs (args) { 'populate--': true }, number: ['port', 'inspect-port', 'body-limit', 'plugin-timeout', 'close-grace-delay', 'trust-proxy-hop'], - string: ['log-level', 'address', 'socket', 'prefix', 'ignore-watch', 'logging-module', 'debug-host', 'lang', 'require', 'import', 'config', 'method', 'trust-proxy-ips'], - boolean: ['pretty-logs', 'options', 'watch', 'verbose-watch', 'debug', 'standardlint', 'common-prefix', 'include-hooks', 'trust-proxy-enabled'], + string: ['log-level', 'address', 'socket', 'prefix', 'ignore-watch', 'logging-module', 'debug-host', 'lang', 'require', 'import', 'config', 'method', 'trust-proxy-ips', 'watch'], + boolean: ['pretty-logs', 'options', 'verbose-watch', 'debug', 'standardlint', 'common-prefix', 'include-hooks', 'trust-proxy-enabled'], envPrefix: 'FASTIFY_', alias: { port: ['p'], @@ -73,6 +73,21 @@ module.exports = function parseArgs (args) { : parsedArgs.trustProxyEnabled === true || parsedArgs.trustProxyEnabled === 'true' const trustProxy = trustProxyEnabled || parsedArgs.trustProxyIps || parsedArgs.trustProxyHop + const watchRequested = (parsedArgs.watch !== undefined && parsedArgs.watch !== false && + (parsedArgs.watch === true || parsedArgs.watch === 'true' || parsedArgs.watch === '')) || + args.includes('-w') || args.includes('--watch') + let watch = false + let watchPaths + if (watchRequested) { + watch = true + const watchVal = parsedArgs.watch + if (typeof watchVal === 'string' && watchVal !== '' && watchVal !== 'true') { + watchPaths = watchVal.split(/\s+/).map((s) => s.trim()).filter(Boolean) + } else { + watchPaths = [] + } + } + return { _: parsedArgs._, '--': additionalArgs, @@ -83,7 +98,8 @@ module.exports = function parseArgs (args) { pluginOptions, prettyLogs: parsedArgs.prettyLogs, options: parsedArgs.options, - watch: parsedArgs.watch, + watch, + watchPaths, debug: parsedArgs.debug, debugPort: parsedArgs.debugPort, debugHost: parsedArgs.debugHost, diff --git a/help/start.txt b/help/start.txt index 6758c9c3..95ebb4af 100644 --- a/help/start.txt +++ b/help/start.txt @@ -38,9 +38,9 @@ OPTS [env: FASTIFY_OPTIONS] Use custom options - -w, --watch + -w, --watch [dirs...] [env: FASTIFY_WATCH] - Watch process.cwd() directory for changes, recursively; when that happens, the process will auto reload. + Watch for file changes and auto reload. With no value, watches process.cwd(); pass dir(s) (e.g. --watch api) to watch only those. -x, --prefix [env: FASTIFY_PREFIX] @@ -74,3 +74,7 @@ Examples: start plugin.js passing custom options to it fastify start plugin.js -- --custom-plugin-option-1 --custom-plugin-option-2 + + watch only the api directory + + fastify start --watch api api/server.js diff --git a/lib/watch/index.js b/lib/watch/index.js index d766c26f..ee86eeed 100644 --- a/lib/watch/index.js +++ b/lib/watch/index.js @@ -11,7 +11,7 @@ const chokidar = require('chokidar') const { loadEnvQuitely } = require('../../env-loader.js') const forkPath = path.join(__dirname, './fork.js') -const watch = function (args, ignoreWatch, verboseWatch) { +const watch = function (args, ignoreWatch, verboseWatch, watchPaths = null) { const emitter = new EventEmitter() let allStop = false let childs = [] @@ -80,7 +80,10 @@ const watch = function (args, ignoreWatch, verboseWatch) { const ignoredArr = ignoreWatch.split(' ').map((item) => item.trim()).filter((item) => item.length) const ignoredPattern = arrayToRegExp(ignoredArr) - const watcher = chokidar.watch(process.cwd(), { ignored: ignoredPattern }) + const watchTargets = (watchPaths && watchPaths.length > 0) + ? watchPaths.map((dir) => path.resolve(process.cwd(), dir)) + : [process.cwd()] + const watcher = chokidar.watch(watchTargets, { ignored: ignoredPattern }) watcher.on('ready', function () { watcher.on('all', function (event, filepath) { if (verboseWatch) { diff --git a/start.js b/start.js index fc1fef0d..f94e8eca 100755 --- a/start.js +++ b/start.js @@ -52,7 +52,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.watchPaths) } return runFastify(args) diff --git a/test/args.test.js b/test/args.test.js index 616291b5..4feb9741 100644 --- a/test/args.test.js +++ b/test/args.test.js @@ -37,6 +37,7 @@ test('should parse args correctly', t => { prettyLogs: true, options: true, watch: true, + watchPaths: [], ignoreWatch: 'node_modules build dist .git bower_components logs .swp .nyc_output ignoreme.js', verboseWatch: true, port: 7777, @@ -96,6 +97,7 @@ test('should parse args with = assignment correctly', t => { prettyLogs: true, options: true, watch: true, + watchPaths: [], ignoreWatch: 'node_modules build dist .git bower_components logs .swp .nyc_output ignoreme.js', verboseWatch: true, port: 7777, @@ -175,6 +177,7 @@ test('should parse env vars correctly', t => { prettyLogs: true, options: true, watch: true, + watchPaths: [], ignoreWatch: 'node_modules build dist .git bower_components logs .swp .nyc_output ignoreme.js', verboseWatch: true, address: 'fastify.dev:9999', @@ -201,7 +204,7 @@ test('should parse env vars correctly', t => { }) test('should respect default values', t => { - t.plan(14) + t.plan(15) const argv = [ 'app.js' @@ -213,6 +216,7 @@ test('should respect default values', t => { t.assert.equal(parsedArgs.options, false) t.assert.equal(parsedArgs.prettyLogs, false) t.assert.equal(parsedArgs.watch, false) + t.assert.equal(parsedArgs.watchPaths, undefined) t.assert.equal(parsedArgs.ignoreWatch, 'node_modules build dist .git bower_components logs .swp .nyc_output') t.assert.equal(parsedArgs.verboseWatch, false) t.assert.equal(parsedArgs.logLevel, 'fatal') @@ -265,6 +269,7 @@ test('should parse custom plugin options', t => { prettyLogs: true, options: true, watch: true, + watchPaths: [], ignoreWatch: 'node_modules build dist .git bower_components logs .swp .nyc_output ignoreme.js', verboseWatch: true, port: 7777, @@ -315,6 +320,7 @@ test('should parse config file correctly and prefer config values over default o prettyLogs: true, options: false, watch: true, + watchPaths: [], debug: false, debugPort: 4000, debugHost: '1.1.1.1', @@ -359,6 +365,7 @@ test('should prefer command line args over config file options', t => { prettyLogs: true, options: false, watch: true, + watchPaths: [], debug: false, debugPort: 9320, debugHost: '1.1.1.1', @@ -405,6 +412,7 @@ test('should favor trust proxy enabled over trust proxy ips and trust proxy hop' prettyLogs: false, options: false, watch: false, + watchPaths: undefined, debug: false, debugPort: 1111, debugHost: '1.1.1.1', @@ -425,6 +433,29 @@ test('should favor trust proxy enabled over trust proxy ips and trust proxy hop' }) }) +test('should parse --watch with single directory', t => { + t.plan(4) + + const argv = ['--watch', 'api', 'api/server.js'] + const parsedArgs = parseArgs(argv) + + t.assert.deepStrictEqual(parsedArgs._, ['api/server.js']) + t.assert.equal(parsedArgs.watch, true) + t.assert.deepStrictEqual(parsedArgs.watchPaths, ['api']) + t.assert.equal(parsedArgs.ignoreWatch, 'node_modules build dist .git bower_components logs .swp .nyc_output') +}) + +test('should parse --watch with multiple space-separated directories', t => { + t.plan(3) + + const argv = ['--watch', 'api lib', 'server.js'] + const parsedArgs = parseArgs(argv) + + t.assert.equal(parsedArgs.watch, true) + t.assert.deepStrictEqual(parsedArgs.watchPaths, ['api', 'lib']) + t.assert.deepStrictEqual(parsedArgs._[0], 'server.js') +}) + test('should favor trust proxy ips over trust proxy hop', t => { t.plan(1) @@ -450,6 +481,7 @@ test('should favor trust proxy ips over trust proxy hop', t => { prettyLogs: false, options: false, watch: false, + watchPaths: undefined, debug: false, debugPort: 1111, debugHost: '1.1.1.1', diff --git a/test/watch.test.js b/test/watch.test.js index c4814b7e..09eff968 100644 --- a/test/watch.test.js +++ b/test/watch.test.js @@ -1,6 +1,8 @@ 'use strict' +const path = require('node:path') const { arrayToRegExp } = require('../lib/watch/utils') +const watch = require('../lib/watch') const { test } = require('node:test') @@ -12,3 +14,15 @@ test('should equal expect RegExp', t => { t.assert.deepStrictEqual(regExp, expectRegExp) }) + +test('should return emitter when called with watchPaths', t => { + t.plan(2) + + const pluginPath = path.join(__dirname, 'plugindir', 'plugin.js') + const emitter = watch([pluginPath], 'node_modules', false, ['lib']) + + t.assert.equal(typeof emitter.stop, 'function') + t.assert.equal(typeof emitter.on, 'function') + + emitter.stop() +})