Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Node-native recursive fs.watch on macOS and Windows, replace FSEventsWatcher and fsevents dependency #1420

Closed
wants to merge 8 commits into from
74 changes: 0 additions & 74 deletions flow-typed/fsevents.js

This file was deleted.

3 changes: 0 additions & 3 deletions packages/metro-file-map/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@
"devDependencies": {
"slash": "^3.0.0"
},
"optionalDependencies": {
"fsevents": "^2.3.2"
},
"engines": {
"node": ">=18.18"
}
Expand Down
67 changes: 31 additions & 36 deletions packages/metro-file-map/src/Watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
FileData,
Path,
PerfLogger,
WatcherBackend,
WatcherBackendChangeEvent,
WatchmanClocks,
} from './flow-types';
Expand All @@ -22,9 +23,9 @@ import type {AbortSignal} from 'node-abort-controller';

import nodeCrawl from './crawlers/node';
import watchmanCrawl from './crawlers/watchman';
import {ADD_EVENT, CHANGE_EVENT} from './watchers/common';
import FSEventsWatcher from './watchers/FSEventsWatcher';
import NodeWatcher from './watchers/NodeWatcher';
import {TOUCH_EVENT} from './watchers/common';
import FallbackWatcher from './watchers/FallbackWatcher';
import NativeWatcher from './watchers/NativeWatcher';
import WatchmanWatcher from './watchers/WatchmanWatcher';
import EventEmitter from 'events';
import * as fs from 'fs';
Expand Down Expand Up @@ -61,11 +62,6 @@ type WatcherOptions = {
watchmanDeferStates: $ReadOnlyArray<string>,
};

interface WatcherBackend {
getPauseReason(): ?string;
close(): Promise<void>;
}

let nextInstanceId = 0;

export type HealthCheckResult =
Expand Down Expand Up @@ -166,18 +162,18 @@ export class Watcher extends EventEmitter {
async watch(onChange: (change: WatcherBackendChangeEvent) => void) {
const {extensions, ignorePattern, useWatchman} = this._options;

// WatchmanWatcher > FSEventsWatcher > sane.NodeWatcher
// WatchmanWatcher > NativeWatcher > FallbackWatcher
const WatcherImpl = useWatchman
? WatchmanWatcher
: FSEventsWatcher.isSupported()
? FSEventsWatcher
: NodeWatcher;
: NativeWatcher.isSupported()
? NativeWatcher
: FallbackWatcher;

let watcher = 'node';
let watcher = 'fallback';
if (WatcherImpl === WatchmanWatcher) {
watcher = 'watchman';
} else if (WatcherImpl === FSEventsWatcher) {
watcher = 'fsevents';
} else if (WatcherImpl === NativeWatcher) {
watcher = 'native';
}
debug(`Using watcher: ${watcher}`);
this._options.perfLogger?.annotate({string: {watcher}});
Expand All @@ -186,7 +182,7 @@ export class Watcher extends EventEmitter {
const createWatcherBackend = (root: Path): Promise<WatcherBackend> => {
const watcherOptions: WatcherBackendOptions = {
dot: true,
glob: [
globs: [
// Ensure we always include package.json files, which are crucial for
/// module resolution.
'**/package.json',
Expand All @@ -197,33 +193,32 @@ export class Watcher extends EventEmitter {
ignored: ignorePattern,
watchmanDeferStates: this._options.watchmanDeferStates,
};
const watcher = new WatcherImpl(root, watcherOptions);
const watcher: WatcherBackend = new WatcherImpl(root, watcherOptions);

return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {
const rejectTimeout = setTimeout(
() => reject(new Error('Failed to start watch mode.')),
MAX_WAIT_TIME,
);

watcher.once('ready', () => {
clearTimeout(rejectTimeout);
watcher.on('all', (change: WatcherBackendChangeEvent) => {
const basename = path.basename(change.relativePath);
if (basename.startsWith(this._options.healthCheckFilePrefix)) {
if (change.event === ADD_EVENT || change.event === CHANGE_EVENT) {
debug(
'Observed possible health check cookie: %s in %s',
change.relativePath,
root,
);
this._handleHealthCheckObservation(basename);
}
return;
watcher.onFileEvent(change => {
const basename = path.basename(change.relativePath);
if (basename.startsWith(this._options.healthCheckFilePrefix)) {
if (change.event === TOUCH_EVENT) {
debug(
'Observed possible health check cookie: %s in %s',
change.relativePath,
root,
);
this._handleHealthCheckObservation(basename);
}
onChange(change);
});
resolve(watcher);
return;
}
onChange(change);
});
await watcher.startWatching();
clearTimeout(rejectTimeout);
resolve(watcher);
});
};

Expand All @@ -241,7 +236,7 @@ export class Watcher extends EventEmitter {
}

async close() {
await Promise.all(this._backends.map(watcher => watcher.close()));
await Promise.all(this._backends.map(watcher => watcher.stopWatching()));
this._activeWatcher = null;
}

Expand Down
Loading
Loading