From d6868546cec4cab2ad156e7961d86f8f89036c9c Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Sun, 6 Nov 2022 23:22:52 +0200 Subject: [PATCH] watch: watch for unexsiting dependencies --- lib/internal/modules/cjs/loader.js | 10 +++++++++- lib/internal/modules/esm/loader.js | 2 +- lib/internal/modules/esm/resolve.js | 3 +++ lib/internal/watch_mode/files_watcher.js | 11 +++++++---- test/fixtures/watch-mode/ipc.js | 8 ++++---- test/sequential/test-watch-mode.mjs | 8 -------- 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 02a07222746dfd..7a94f7fb9c53e8 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -28,6 +28,7 @@ const { ArrayPrototypeIncludes, ArrayPrototypeIndexOf, ArrayPrototypeJoin, + ArrayPrototypeMap, ArrayPrototypePush, ArrayPrototypeSlice, ArrayPrototypeSplice, @@ -191,7 +192,13 @@ function updateChildren(parent, child, scan) { function reportModuleToWatchMode(filename) { if (shouldReportRequiredModules && process.send) { - process.send({ 'watch:require': filename }); + process.send({ 'watch:require': [filename] }); + } +} + +function reportModuleNotFoundToWatchMode(basePath, extensions) { + if (shouldReportRequiredModules && process.send) { + process.send({ 'watch:require': ArrayPrototypeMap(extensions, (ext) => path.resolve(`${basePath}${ext}`)) }); } } @@ -648,6 +655,7 @@ Module._findPath = function(request, paths, isMain) { Module._pathCache[cacheKey] = filename; return filename; } + reportModuleNotFoundToWatchMode(basePath, exts); } return false; diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 0f23d48084abe8..a356272c98b614 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -463,7 +463,7 @@ class ESMLoader { ); if (process.env.WATCH_REPORT_DEPENDENCIES && process.send) { - process.send({ 'watch:import': url }); + process.send({ 'watch:import': [url] }); } const job = new ModuleJob( diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index 0b3414535ba3ee..917fee6430060a 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -254,6 +254,9 @@ function finalizeResolution(resolved, base, preserveSymlinks) { err.url = String(resolved); throw err; } else if (!stats.isFile()) { + if (process.env.WATCH_REPORT_DEPENDENCIES && process.send) { + process.send({ 'watch:require': [path || resolved.pathname] }); + } throw new ERR_MODULE_NOT_FOUND( path || resolved.pathname, base && fileURLToPath(base), 'module'); } diff --git a/lib/internal/watch_mode/files_watcher.js b/lib/internal/watch_mode/files_watcher.js index 6c6c0f27fd8f8d..f2141051fceaf4 100644 --- a/lib/internal/watch_mode/files_watcher.js +++ b/lib/internal/watch_mode/files_watcher.js @@ -1,6 +1,8 @@ 'use strict'; const { + ArrayIsArray, + ArrayPrototypeForEach, SafeMap, SafeSet, StringPrototypeStartsWith, @@ -94,6 +96,7 @@ class FilesWatcher extends EventEmitter { } filterFile(file) { + if (!file) return; if (supportsRecursiveWatching) { this.watchPath(dirname(file)); } else { @@ -109,11 +112,11 @@ class FilesWatcher extends EventEmitter { } child.on('message', (message) => { try { - if (message['watch:require']) { - this.filterFile(message['watch:require']); + if (ArrayIsArray(message['watch:require'])) { + ArrayPrototypeForEach(message['watch:require'], (file) => this.filterFile(file)); } - if (message['watch:import']) { - this.filterFile(fileURLToPath(message['watch:import'])); + if (ArrayIsArray(message['watch:import'])) { + ArrayPrototypeForEach(message['watch:import'], (file) => this.filterFile(fileURLToPath(file))); } } catch { // Failed watching file. ignore diff --git a/test/fixtures/watch-mode/ipc.js b/test/fixtures/watch-mode/ipc.js index 021df9973511d0..5881299387e5b4 100644 --- a/test/fixtures/watch-mode/ipc.js +++ b/test/fixtures/watch-mode/ipc.js @@ -6,7 +6,7 @@ const tmpdir = require('../../common/tmpdir'); const tmpfile = path.join(tmpdir.path, 'file'); fs.writeFileSync(tmpfile, ''); -process.send({ 'watch:require': path.resolve(__filename) }); -process.send({ 'watch:import': url.pathToFileURL(path.resolve(__filename)).toString() }); -process.send({ 'watch:import': url.pathToFileURL(tmpfile).toString() }); -process.send({ 'watch:import': new URL('http://invalid.com').toString() }); +process.send({ 'watch:require': [path.resolve(__filename)] }); +process.send({ 'watch:import': [url.pathToFileURL(path.resolve(__filename)).toString()] }); +process.send({ 'watch:import': [url.pathToFileURL(tmpfile).toString()] }); +process.send({ 'watch:import': [new URL('http://invalid.com').toString()] }); diff --git a/test/sequential/test-watch-mode.mjs b/test/sequential/test-watch-mode.mjs index e24fb97ad234a8..cbda2318a264d0 100644 --- a/test/sequential/test-watch-mode.mjs +++ b/test/sequential/test-watch-mode.mjs @@ -104,14 +104,6 @@ describe('watch mode', { concurrency: true, timeout: 60_0000 }, () => { }); }); - it('should not watch when running an non-existing file', async () => { - const file = fixtures.path('watch-mode/non-existing.js'); - const { stderr, stdout } = await spawnWithRestarts({ file, restarts: 0 }); - - assert.match(stderr, /code: 'MODULE_NOT_FOUND'/); - assert.strictEqual(stdout, `Failed running ${inspect(file)}\n`); - }); - it('should watch when running an non-existing file - when specified under --watch-path', { skip: !common.isOSX && !common.isWindows }, async () => {