Skip to content

Commit

Permalink
Add deferStates option for Watchman
Browse files Browse the repository at this point in the history
Summary:
Adds a new config option which allows Metro, when using Watchman, to be configured to ignore file events (and avoid nondeterministic chunking that can trigger bugs) during specific external file operations such as large source control updates. This is achieved using Watchman's [`defer` behaviour](https://facebook.github.io/watchman/docs/cmd/subscribe.html#defer), which accepts a set of state names exposed by this option.

A root config key, `watcher`, has been introduced in addition to the `watcher.watchman.deferStates` option which configures this feature. This config structure change anticipates exposing further options against file watchers and Watchman.

A default value of `['hg.update']` is applied (at the `metro-config` level) in order to apply this behaviour for use cases within Meta. This should help mitigate consistency issues for developers caused by long-running `hg` operations.

Reviewed By: motiz88

Differential Revision: D36959202

fbshipit-source-id: 61acdc70ad621b24ee5125cc10756892ec41a454
  • Loading branch information
huntie authored and facebook-github-bot committed Jun 15, 2022
1 parent f121824 commit 570c380
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 0 deletions.
25 changes: 25 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ module.exports = {
},
server: {
/* server options */
},
watcher: {
/* watcher options */
watchman: {
/* Watchman-specific options */
}
}
};
```
Expand Down Expand Up @@ -373,6 +379,25 @@ Type: `boolean` (default: `true`)

Run Inspector Proxy server inside Metro to be able to inspect React Native code.

---

### Watcher Options

Options for the filesystem watcher.

:::note

Dot notation in this section indicates a nested structure, e.g. `watchman: { deferStates: ... }`.

:::

#### `watchman.deferStates`

Type: `Array<string>`

Applies when using Watchman. Metro will [defer processing filesystem updates](https://facebook.github.io/watchman/docs/cmd/subscribe.html#defer) while these [states](https://facebook.github.io/watchman/docs/cmd/state-enter.html) are asserted in the watch. This is useful for debouncing builds while the filesystem hasn't settled, e.g. during large source control operations.

The default value is `['hg.update']`.

## Merging Configurations

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,13 @@ Object {
"watchFolders": Array [
"/",
],
"watcher": Object {
"watchman": Object {
"deferStates": Array [
"hg.update",
],
},
},
}
`;

Expand Down Expand Up @@ -287,6 +294,13 @@ Object {
"watchFolders": Array [
"/",
],
"watcher": Object {
"watchman": Object {
"deferStates": Array [
"hg.update",
],
},
},
}
`;

Expand Down Expand Up @@ -432,6 +446,13 @@ Object {
"watchFolders": Array [
"/",
],
"watcher": Object {
"watchman": Object {
"deferStates": Array [
"hg.update",
],
},
},
}
`;

Expand Down Expand Up @@ -577,6 +598,13 @@ Object {
"watchFolders": Array [
"/",
],
"watcher": Object {
"watchman": Object {
"deferStates": Array [
"hg.update",
],
},
},
}
`;

Expand Down
9 changes: 9 additions & 0 deletions packages/metro-config/src/configTypes.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ type SymbolicatorConfigT = {
}) => ?{+collapse?: boolean} | Promise<?{+collapse?: boolean}>,
};

type WatcherConfigT = {
watchman: {
deferStates?: $ReadOnlyArray<string>,
},
};

export type InputConfigT = $Shape<{
...MetalConfigT,
...$ReadOnly<{
Expand All @@ -177,6 +183,7 @@ export type InputConfigT = $Shape<{
serializer: $Shape<SerializerConfigT>,
symbolicator: $Shape<SymbolicatorConfigT>,
transformer: $Shape<TransformerConfigT>,
watcher: $Shape<WatcherConfigT>,
}>,
}>;

Expand All @@ -188,6 +195,7 @@ export type IntermediateConfigT = {
serializer: SerializerConfigT,
symbolicator: SymbolicatorConfigT,
transformer: TransformerConfigT,
watcher: WatcherConfigT,
},
};

Expand All @@ -199,6 +207,7 @@ export type ConfigT = $ReadOnly<{
serializer: $ReadOnly<SerializerConfigT>,
symbolicator: $ReadOnly<SymbolicatorConfigT>,
transformer: $ReadOnly<TransformerConfigT>,
watcher: $ReadOnly<WatcherConfigT>,
}>,
}>;

Expand Down
5 changes: 5 additions & 0 deletions packages/metro-config/src/defaults/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ const getDefaultValues = (projectRoot: ?string): ConfigT => ({
unstable_disableNormalizePseudoGlobals: false,
unstable_compactOutput: false,
},
watcher: {
watchman: {
deferStates: ['hg.update'],
},
},
cacheStores: [
new FileStore({
root: path.join(os.tmpdir(), 'metro-cache'),
Expand Down
8 changes: 8 additions & 0 deletions packages/metro-config/src/loadConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ function mergeConfig<T: InputConfigT>(
...totalConfig.symbolicator,
...(nextConfig.symbolicator || {}),
},
watcher: {
...totalConfig.watcher,
...nextConfig.watcher,
watchman: {
...totalConfig.watcher?.watchman,
...nextConfig.watcher?.watchman,
},
},
}),
defaultConfig,
);
Expand Down
4 changes: 4 additions & 0 deletions packages/metro-file-map/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export type InputOptions = $ReadOnly<{
maxWorkers: number,
throwOnModuleCollision?: ?boolean,
useWatchman?: ?boolean,
watchmanDeferStates?: $ReadOnlyArray<string>,
watch?: ?boolean,
console?: Console,
cacheManagerFactory?: ?CacheManagerFactory,
Expand All @@ -106,6 +107,7 @@ type InternalOptions = {
throwOnModuleCollision: boolean,
useWatchman: boolean,
watch: boolean,
watchmanDeferStates: $ReadOnlyArray<string>,
};

interface Watcher {
Expand Down Expand Up @@ -296,6 +298,7 @@ export default class HasteMap extends EventEmitter {
throwOnModuleCollision: !!options.throwOnModuleCollision,
useWatchman: options.useWatchman == null ? true : options.useWatchman,
watch: !!options.watch,
watchmanDeferStates: options.watchmanDeferStates ?? [],
};

this._console = options.console || global.console;
Expand Down Expand Up @@ -861,6 +864,7 @@ export default class HasteMap extends EventEmitter {
dot: true,
glob: extensions.map(extension => '**/*.' + extension),
ignored: ignorePattern,
watchmanDeferStates: this._options.watchmanDeferStates,
});

return new Promise((resolve, reject) => {
Expand Down
1 change: 1 addition & 0 deletions packages/metro-file-map/src/watchers/FSEventsWatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export default class FSEventsWatcher extends EventEmitter {
ignored?: Matcher,
glob: string | $ReadOnlyArray<string>,
dot: boolean,
...
}>,
) {
if (!fsevents) {
Expand Down
1 change: 1 addition & 0 deletions packages/metro-file-map/src/watchers/WatchmanWatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ WatchmanWatcher.prototype.init = function () {
const options = {
fields: ['name', 'exists', 'new'],
since: resp.clock,
defer: self.watchmanDeferStates,
};

// If the server has the wildmatch capability available it supports
Expand Down
1 change: 1 addition & 0 deletions packages/metro-file-map/src/watchers/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ exports.assignOptions = function (watcher, opts) {
watcher.globs = opts.glob || [];
watcher.dot = opts.dot || false;
watcher.ignored = opts.ignored || false;
watcher.watchmanDeferStates = opts.watchmanDeferStates;

if (!Array.isArray(watcher.globs)) {
watcher.globs = [watcher.globs];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ function createHasteMap(
throwOnModuleCollision: options?.throwOnModuleCollision ?? true,
useWatchman: config.resolver.useWatchman,
watch: options?.watch == null ? !ci.isCI : options.watch,
watchmanDeferStates: config.watcher.watchman.deferStates,
});
}

Expand Down

0 comments on commit 570c380

Please sign in to comment.