From 81619e6b2d26333a04ee3053dc3dfab586074051 Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Mon, 16 Dec 2024 11:27:16 -0800 Subject: [PATCH 1/2] Flow-only: tighten types around watcher backend "ignore" param Summary: Minor Flow type refinement - I originally typed this as `?(boolean | RegExp)` when conservatively adding types to untyped JS. In fact we only ever pass `?RegExp`, so we can tidy up a bit. Changelog: Internal Differential Revision: D67285745 --- .../metro-file-map/src/watchers/FSEventsWatcher.js | 8 +++----- packages/metro-file-map/src/watchers/NodeWatcher.js | 2 +- .../src/watchers/__tests__/WatchmanWatcher-test.js | 4 ++-- packages/metro-file-map/src/watchers/common.js | 12 +++++------- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/metro-file-map/src/watchers/FSEventsWatcher.js b/packages/metro-file-map/src/watchers/FSEventsWatcher.js index c646f77fb8..5386621ebe 100644 --- a/packages/metro-file-map/src/watchers/FSEventsWatcher.js +++ b/packages/metro-file-map/src/watchers/FSEventsWatcher.js @@ -25,8 +25,6 @@ import walker from 'walker'; const debug = require('debug')('Metro:FSEventsWatcher'); -type Matcher = typeof anymatch.Matcher; - // $FlowFixMe[value-as-type] let fsevents: ?FSEvents = null; try { @@ -54,7 +52,7 @@ type FsEventsWatcherEvent = */ export default class FSEventsWatcher extends EventEmitter { +root: string; - +ignored: ?Matcher; + +ignored: ?RegExp; +glob: $ReadOnlyArray; +dot: boolean; +doIgnore: (path: string) => boolean; @@ -81,7 +79,7 @@ export default class FSEventsWatcher extends EventEmitter { endCallback: () => void, // $FlowFixMe[unclear-type] Add types for callback errorCallback: Function, - ignored?: Matcher, + ignored: ?RegExp, ) { walker(dir) .filterDir( @@ -97,7 +95,7 @@ export default class FSEventsWatcher extends EventEmitter { constructor( dir: string, opts: $ReadOnly<{ - ignored?: Matcher, + ignored: ?RegExp, glob: string | $ReadOnlyArray, dot: boolean, ... diff --git a/packages/metro-file-map/src/watchers/NodeWatcher.js b/packages/metro-file-map/src/watchers/NodeWatcher.js index 4304f2cc80..183d99b1dd 100644 --- a/packages/metro-file-map/src/watchers/NodeWatcher.js +++ b/packages/metro-file-map/src/watchers/NodeWatcher.js @@ -48,7 +48,7 @@ module.exports = class NodeWatcher extends EventEmitter { doIgnore: string => boolean; dot: boolean; globs: $ReadOnlyArray; - ignored: ?(boolean | RegExp); + ignored: ?RegExp; root: string; watched: {[key: string]: FSWatcher, __proto__: null}; watchmanDeferStates: $ReadOnlyArray; diff --git a/packages/metro-file-map/src/watchers/__tests__/WatchmanWatcher-test.js b/packages/metro-file-map/src/watchers/__tests__/WatchmanWatcher-test.js index f4cccde538..876340d901 100644 --- a/packages/metro-file-map/src/watchers/__tests__/WatchmanWatcher-test.js +++ b/packages/metro-file-map/src/watchers/__tests__/WatchmanWatcher-test.js @@ -36,7 +36,7 @@ describe('WatchmanWatcher', () => { test('initializes with watch-project, clock, subscribe', () => { const watchmanWatcher = new WatchmanWatcher('/project/subdir/js', { dot: true, - ignored: false, + ignored: null, glob: ['**/*.js'], watchmanDeferStates: ['busy'], }); @@ -86,7 +86,7 @@ describe('WatchmanWatcher', () => { beforeEach(() => { watchmanWatcher = new WatchmanWatcher('/project/subdir/js', { dot: true, - ignored: false, + ignored: null, glob: ['**/*.js'], watchmanDeferStates: ['busy'], }); diff --git a/packages/metro-file-map/src/watchers/common.js b/packages/metro-file-map/src/watchers/common.js index 2dbdb6d99c..099ba16260 100644 --- a/packages/metro-file-map/src/watchers/common.js +++ b/packages/metro-file-map/src/watchers/common.js @@ -39,7 +39,7 @@ export const ALL_EVENT = 'all'; export type WatcherOptions = $ReadOnly<{ glob: $ReadOnlyArray, dot: boolean, - ignored: boolean | RegExp, + ignored: ?RegExp, watchmanDeferStates: $ReadOnlyArray, watchman?: mixed, watchmanPath?: string, @@ -49,7 +49,7 @@ interface Watcher { doIgnore: string => boolean; dot: boolean; globs: $ReadOnlyArray; - ignored?: ?(boolean | RegExp); + ignored?: ?RegExp; watchmanDeferStates: $ReadOnlyArray; watchmanPath?: ?string; } @@ -68,16 +68,14 @@ export const assignOptions = function ( ): WatcherOptions { watcher.globs = opts.glob ?? []; watcher.dot = opts.dot ?? false; - watcher.ignored = opts.ignored ?? false; + watcher.ignored = opts.ignored ?? null; watcher.watchmanDeferStates = opts.watchmanDeferStates; if (!Array.isArray(watcher.globs)) { watcher.globs = [watcher.globs]; } watcher.doIgnore = - opts.ignored != null && opts.ignored !== false - ? anymatch(opts.ignored) - : () => false; + opts.ignored != null ? anymatch(opts.ignored) : () => false; if (opts.watchman == true && opts.watchmanPath != null) { watcher.watchmanPath = opts.watchmanPath; @@ -117,7 +115,7 @@ export function recReaddir( symlinkCallback: (string, Stats) => void, endCallback: () => void, errorCallback: Error => void, - ignored: ?(boolean | RegExp), + ignored: ?RegExp, ) { walker(dir) .filterDir(currentDir => !anymatch(ignored, currentDir)) From 22fc1051905dc3b5539cd2cd8b6450e6ab37bca1 Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Mon, 16 Dec 2024 11:27:16 -0800 Subject: [PATCH 2/2] FSEventsWatcher: use common recReadDir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: `FSEventsWatcher` has its own implementation of `recReaddir` which is almost identical to the one in `./common.js`, except that it wraps handlers in `path.normalize`. There’s only one handler, so we can trivially remove this proxy abstraction and reuse the common `recReaddir`. (The end goal here is to slim down and tidy up FSEventsWatcher ahead of using it as a basis for native recursive watching) Changelog: Internal Differential Revision: D67286092 --- .../src/watchers/FSEventsWatcher.js | 37 ++----------------- 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/packages/metro-file-map/src/watchers/FSEventsWatcher.js b/packages/metro-file-map/src/watchers/FSEventsWatcher.js index 5386621ebe..fe3e16bbe0 100644 --- a/packages/metro-file-map/src/watchers/FSEventsWatcher.js +++ b/packages/metro-file-map/src/watchers/FSEventsWatcher.js @@ -9,19 +9,16 @@ */ import type {ChangeEventMetadata} from '../flow-types'; -import type {Stats} from 'fs'; // $FlowFixMe[cannot-resolve-module] - Optional, Darwin only // $FlowFixMe[untyped-type-import] import type {FSEvents} from 'fsevents'; -import {isIncluded, typeFromStat} from './common'; +import {isIncluded, recReaddir, typeFromStat} from './common'; // $FlowFixMe[untyped-import] - anymatch import anymatch from 'anymatch'; import EventEmitter from 'events'; import {promises as fsPromises} from 'fs'; import * as path from 'path'; -// $FlowFixMe[untyped-import] - walker -import walker from 'walker'; const debug = require('debug')('Metro:FSEventsWatcher'); @@ -64,34 +61,6 @@ export default class FSEventsWatcher extends EventEmitter { return fsevents != null; } - static _normalizeProxy( - callback: (normalizedPath: string, stats: Stats) => void, - ): (filepath: string, stats: Stats) => void { - return (filepath: string, stats: Stats): void => - callback(path.normalize(filepath), stats); - } - - static _recReaddir( - dir: string, - dirCallback: (normalizedPath: string, stats: Stats) => void, - fileCallback: (normalizedPath: string, stats: Stats) => void, - symlinkCallback: (normalizedPath: string, stats: Stats) => void, - endCallback: () => void, - // $FlowFixMe[unclear-type] Add types for callback - errorCallback: Function, - ignored: ?RegExp, - ) { - walker(dir) - .filterDir( - (currentDir: string) => !ignored || !anymatch(ignored, currentDir), - ) - .on('dir', FSEventsWatcher._normalizeProxy(dirCallback)) - .on('file', FSEventsWatcher._normalizeProxy(fileCallback)) - .on('symlink', FSEventsWatcher._normalizeProxy(symlinkCallback)) - .on('error', errorCallback) - .on('end', endCallback); - } - constructor( dir: string, opts: $ReadOnly<{ @@ -126,10 +95,10 @@ export default class FSEventsWatcher extends EventEmitter { this._tracked = new Set(); const trackPath = (filePath: string) => { - this._tracked.add(filePath); + this._tracked.add(path.normalize(filePath)); }; this.watcherInitialReaddirPromise = new Promise(resolve => { - FSEventsWatcher._recReaddir( + recReaddir( this.root, trackPath, trackPath,