Plugin change detection is unreliable on Windows VM

Summary:
On Windows VM when "yarn start" is executed and compilation is in progress for some plugin, fs.watch randomly fires "changed" events for different files of other plugins. This leads to infinite attempts to rebuild the same plugin again and again, and this process never ends, so "yarn start" is almost unusable:
{F225467225}

I've tried to fix this by using watchman instead of fs.watch and on my tests with Windows build it works well:
{F225467508}

Also as watchman is more careful about opening file handles, hopefully this change will fix "too many files opened" problem as Michel suggested here https://fb.workplace.com/groups/flippersupport/permalink/764157990731528/ and here https://github.com/facebook/flipper/issues/699.

Reviewed By: mweststrate

Differential Revision: D19216026

fbshipit-source-id: acc53ae0d003a7936730e6423ac4dbca84f089c8
This commit is contained in:
Anton Nikolaev
2019-12-31 04:36:37 -08:00
committed by Facebook Github Bot
parent 477de3e9ba
commit fa7f970266
4 changed files with 146 additions and 29 deletions

View File

@@ -16,6 +16,7 @@ const recursiveReaddir = require('recursive-readdir');
const expandTilde = require('expand-tilde');
const pMap = require('p-map');
const HOME_DIR = require('os').homedir();
const Watchman = require('./watchman');
const DEFAULT_COMPILE_OPTIONS = {
force: false,
@@ -47,7 +48,7 @@ module.exports = async (
return dynamicPlugins;
};
function watchChanges(
async function watchChanges(
plugins,
reloadCallback,
pluginCache,
@@ -58,6 +59,9 @@ function watchChanges(
const delayedCompilation = {};
const kCompilationDelayMillis = 1000;
const rootDir = path.resolve(__dirname, '..');
const watchman = new Watchman(rootDir);
await watchman.initialize();
Object.values(plugins)
// no hot reloading for plugins in .flipper folder. This is to prevent
// Flipper from reloading, while we are doing changes on thirdparty plugins.
@@ -65,26 +69,27 @@ function watchChanges(
plugin => !plugin.rootDir.startsWith(path.join(HOME_DIR, '.flipper')),
)
.map(plugin =>
fs.watch(plugin.rootDir, {recursive: true}, (eventType, filename) => {
// only recompile for changes in not hidden files. Watchman might create
// a file called .watchman-cookie
if (
filename &&
!filename.startsWith('.') &&
!filename.includes('__tests__') &&
!delayedCompilation[plugin.name]
) {
delayedCompilation[plugin.name] = setTimeout(() => {
delayedCompilation[plugin.name] = null;
// eslint-disable-next-line no-console
console.log(`🕵️‍ Detected changes in ${plugin.name}`);
const watchOptions = Object.assign(options, {force: true});
compilePlugin(plugin, pluginCache, watchOptions).then(
reloadCallback,
);
}, kCompilationDelayMillis);
}
}),
watchman.startWatchFiles(
path.relative(rootDir, plugin.rootDir),
resp => {
// only recompile for changes in not hidden files. Watchman might create
// a file called .watchman-cookie
if (!delayedCompilation[plugin.name]) {
delayedCompilation[plugin.name] = setTimeout(() => {
delayedCompilation[plugin.name] = null;
// eslint-disable-next-line no-console
console.log(`🕵️‍ Detected changes in ${plugin.name}`);
const watchOptions = Object.assign(options, {force: true});
compilePlugin(plugin, pluginCache, watchOptions).then(
reloadCallback,
);
}, kCompilationDelayMillis);
}
},
{
excludes: ['**/__tests__/**/*', '**/node_modules/**/*', '**/.*'],
},
),
);
}
function hash(string) {
@@ -215,7 +220,7 @@ async function compilePlugin(
},
resolver: {
sourceExts: ['tsx', 'ts', 'js'],
blacklistRE: /\/(sonar|flipper|flipper-public)\/(dist|doctor)\/|(\.native\.js$)/,
blacklistRE: /(\/|\\)(sonar|flipper|flipper-public)(\/|\\)(dist|doctor)(\/|\\)|(\.native\.js$)/,
},
},
{