Skip to content

Commit

Permalink
Support fsevents watcher on Apple silicon (#875)
Browse files Browse the repository at this point in the history
Summary:
If Watchman is unavailable or disabled, we try to fall back to `FSEventsWatcher` and then to `NodeWatcher`. `FSEventsWatcher` is only available on `darwin`, so we `require` it in a try/catch and swallow any error.

This was hiding the fact that `fsevents` was also failing to load on Apple silicon:
```
 dlopen(/Users/robhogan/gh/metro/node_modules/fsevents/fsevents.node, 0x0001): tried: '/Users/robhogan/gh/metro/node_modules/fsevents/fsevents.node' (mach-o file, but is an incompatible architecture (have (x86_64), need (arm64e)))
 ```

`fsevents@2.3` introduces support for Apple silicon. This updates to that version, and also adds Flow types based on the TypeScript types bundled with `fsevents` so that Flow check passes on `darwin` (the Flow error was `untyped-import` so the existing `cannot-resolve-module` suppression doesn't work).

Pull Request resolved: #875

Test Plan:
### After:
```
$ node packages/metro/src/cli serve -c ../dummyproj/metro.config.js

                        ▒▒▓▓▓▓▒▒
                     ▒▓▓▓▒▒░░▒▒▓▓▓▒
                  ▒▓▓▓▓░░░▒▒▒▒░░░▓▓▓▓▒
                 ▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓
                 ▓▓░░░░░▒▓▓▓▓▓▓▒░░░░░▓▓
                 ▓▓░░▓▓▒░░░▒▒░░░▒▓▒░░▓▓
                 ▓▓░░▓▓▓▓▓▒▒▒▒▓▓▓▓▒░░▓▓
                 ▓▓░░▓▓▓▓▓▓▓▓▓▓▓▓▓▒░░▓▓
                 ▓▓▒░░▒▒▓▓▓▓▓▓▓▓▒░░░▒▓▓
                  ▒▓▓▓▒░░░▒▓▓▒░░░▒▓▓▓▒
                     ▒▓▓▓▒░░░░▒▓▓▓▒
                        ▒▒▓▓▓▓▒▒

                Welcome to Metro v0.72.3
              Fast - Scalable - Integrated

  Metro:FSEventsWatcher Watching /Users/robhogan/gh/dummyproj +0ms
```

Reviewed By: huntie

Differential Revision: D39883504

Pulled By: huntie

fbshipit-source-id: 223a390715f13f32cdc1788185c75a96433581de
  • Loading branch information
robhogan authored and facebook-github-bot committed Sep 28, 2022
1 parent 556a4c1 commit 9329b82
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 7 deletions.
73 changes: 73 additions & 0 deletions flow-typed/fsevents.js
@@ -0,0 +1,73 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
* @oncall react_native
*/

declare module 'fsevents' {
declare type Event =
| 'created'
| 'cloned'
| 'modified'
| 'deleted'
| 'moved'
| 'root-changed'
| 'unknown';

declare type Type = 'file' | 'directory' | 'symlink';

declare type FileChanges = {
inode: boolean,
finder: boolean,
access: boolean,
xattrs: boolean,
};

declare type Info = {
event: Event,
path: string,
type: Type,
changes: FileChanges,
flags: number,
};

declare type WatchHandler = (path: string, flags: number, id: string) => void;

declare type FSEvents = {
watch(path: string, handler: WatchHandler): () => Promise<void>,
getInfo(path: string, flags: number): Info,
constants: {
None: 0x00000000,
MustScanSubDirs: 0x00000001,
UserDropped: 0x00000002,
KernelDropped: 0x00000004,
EventIdsWrapped: 0x00000008,
HistoryDone: 0x00000010,
RootChanged: 0x00000020,
Mount: 0x00000040,
Unmount: 0x00000080,
ItemCreated: 0x00000100,
ItemRemoved: 0x00000200,
ItemInodeMetaMod: 0x00000400,
ItemRenamed: 0x00000800,
ItemModified: 0x00001000,
ItemFinderInfoMod: 0x00002000,
ItemChangeOwner: 0x00004000,
ItemXattrMod: 0x00008000,
ItemIsFile: 0x00010000,
ItemIsDir: 0x00020000,
ItemIsSymlink: 0x00040000,
ItemIsHardlink: 0x00100000,
ItemIsLastHardlink: 0x00200000,
OwnEvent: 0x00080000,
ItemCloned: 0x00400000,
},
};

declare module.exports: FSEvents;
}
2 changes: 1 addition & 1 deletion packages/metro-file-map/package.json
Expand Up @@ -30,6 +30,6 @@
"slash": "^3.0.0"
},
"optionalDependencies": {
"fsevents": "^2.1.2"
"fsevents": "^2.3.2"
}
}
17 changes: 11 additions & 6 deletions packages/metro-file-map/src/watchers/FSEventsWatcher.js
Expand Up @@ -8,6 +8,9 @@
* @flow strict-local
*/

// $FlowFixMe[cannot-resolve-module] - Optional, Darwin only
import type {FSEvents} from 'fsevents';

// $FlowFixMe[untyped-import] - anymatch
import anymatch from 'anymatch';
import EventEmitter from 'events';
Expand All @@ -19,10 +22,11 @@ import walker from 'walker';
// $FlowFixMe[untyped-import] - micromatch
const micromatch = require('micromatch');

const debug = require('debug')('Metro:FSEventsWatcher');

type Matcher = typeof anymatch.Matcher;

// $FlowFixMe[unclear-type] - fsevents
let fsevents: any = null;
let fsevents: ?FSEvents = null;
try {
// $FlowFixMe[cannot-resolve-module] - Optional, Darwin only
fsevents = require('fsevents');
Expand Down Expand Up @@ -116,12 +120,13 @@ export default class FSEventsWatcher extends EventEmitter {
this.doIgnore = opts.ignored ? anymatch(opts.ignored) : () => false;

this.root = path.resolve(dir);
this.fsEventsWatchStopper = fsevents.watch(
this.root,
// $FlowFixMe[method-unbinding] - Refactor
this._handleEvent.bind(this),

this.fsEventsWatchStopper = fsevents.watch(this.root, path =>
this._handleEvent(path),
);

debug(`Watching ${this.root}`);

this._tracked = new Set();
FSEventsWatcher._recReaddir(
this.root,
Expand Down

0 comments on commit 9329b82

Please sign in to comment.