From df6b0cd1c8d8eb5dd9c74219e1b95948a9ef85df Mon Sep 17 00:00:00 2001 From: Ruy Adorno Date: Mon, 31 Oct 2022 13:23:55 -0400 Subject: [PATCH] cli: fix --watch reload for built files When watching folders that contain files that result from a build step, e.g: TypeScript output, stored template result, etc. The watcher will not reload the application in case one of these resulting files gets regenerated, given that when these files are removed they're no longer tracked by the watcher. This changeset enables reloading the app when changing files that result from a build step by removing the reset of the tracked filtered files on every reload. Fixing the ability to watch and reload the app when a previously-known output file is changed. Signed-off-by: Ruy Adorno --- lib/internal/main/watch_mode.js | 1 - test/sequential/test-watch-mode.mjs | 38 ++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/internal/main/watch_mode.js b/lib/internal/main/watch_mode.js index ea02190fa13d00..fc635e269a2e29 100644 --- a/lib/internal/main/watch_mode.js +++ b/lib/internal/main/watch_mode.js @@ -93,7 +93,6 @@ function reportGracefulTermination() { } async function stop() { - watcher.clearFileFilters(); const clearGraceReport = reportGracefulTermination(); await killAndWait(); clearGraceReport(); diff --git a/test/sequential/test-watch-mode.mjs b/test/sequential/test-watch-mode.mjs index e24fb97ad234a8..2f7b2c88251a78 100644 --- a/test/sequential/test-watch-mode.mjs +++ b/test/sequential/test-watch-mode.mjs @@ -6,9 +6,10 @@ import path from 'node:path'; import { execPath } from 'node:process'; import { describe, it } from 'node:test'; import { spawn } from 'node:child_process'; -import { writeFileSync, readFileSync } from 'node:fs'; +import { writeFileSync, rmSync, readFileSync } from 'node:fs'; import { inspect } from 'node:util'; import { once } from 'node:events'; +import { setTimeout } from 'node:timers/promises'; if (common.isIBMi) common.skip('IBMi does not support `fs.watch()`'); @@ -174,6 +175,41 @@ describe('watch mode', { concurrency: true, timeout: 60_0000 }, () => { }); }); + it('should watch changes to previously loaded dependencies', async () => { + const dependencyContent = 'module.exports = {}'; + const dependency = createTmpFile(dependencyContent); + const relativeDependencyPath = `./${path.basename(dependency)}`; + const dependant = createTmpFile(`console.log(require('${relativeDependencyPath}'))`); + + let stderr = ''; + let stdout = ''; + const child = spawn(execPath, ['--watch', '--no-warnings', dependant], { encoding: 'utf8' }); + child.stdout.on('data', (data) => { stdout += data; }); + child.stderr.on('data', (data) => { stderr += data; }); + child.on('error', (err) => { throw err; }); + + await once(child.stdout, 'data'); + rmSync(dependency, { force: true }); + + await setTimeout(600); // throttle + 100 + writeFileSync(dependency, dependencyContent); + + await setTimeout(600); // throttle + 100 + child.kill(); + await once(child, 'exit'); + + // TODO(ruyadorno): fs.watch is flaky when removing files from a watched + // folder, in this test we want to assert the expected reload happened + // after a missing file is recreated so we'll skip the assertions in case + // the expected reload+failure never happened. + // This should be an assertion for the failure instead of an if block once + // the source of this flakyness gets resolved. + if (stdout.match(/Failed/g)?.length) { + assert.strictEqual(stdout.split('Failed')[1].match(/Restarting/g)?.length, 1, new Error('should have restarted after fail')); + assert.ok(stderr.match(/MODULE_NOT_FOUND/g)?.length, new Error('should have failed for not finding the removed file')); + } + }); + it('should restart multiple times', async () => { const file = createTmpFile(); const { stderr, stdout } = await spawnWithRestarts({ file, restarts: 3 });