Launch without plugin hot reload in case plugin change detection failed to start
Summary: Launch without plugin hot reload in case plugin change detection failed to start Reviewed By: mweststrate Differential Revision: D19264418 fbshipit-source-id: 089f818b9101d924c504c7d96f71ebca11c9422f
This commit is contained in:
committed by
Facebook Github Bot
parent
205e04fe6c
commit
857b9816a0
@@ -32,14 +32,6 @@ matrix:
|
|||||||
node_js:
|
node_js:
|
||||||
- "11"
|
- "11"
|
||||||
|
|
||||||
before_install:
|
|
||||||
- cd ..
|
|
||||||
- git clone https://github.com/facebook/watchman.git
|
|
||||||
- cd watchman
|
|
||||||
- git checkout v4.9.0
|
|
||||||
- ./autogen.sh && ./configure && make && sudo make install
|
|
||||||
- cd ../flipper
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- yarn
|
- yarn
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ function compileDefaultPlugins(defaultPluginDir, skipAll = false) {
|
|||||||
path.join(__dirname, '..', 'src', 'fb', 'plugins'),
|
path.join(__dirname, '..', 'src', 'fb', 'plugins'),
|
||||||
],
|
],
|
||||||
defaultPluginDir,
|
defaultPluginDir,
|
||||||
{force: true, failSilently: false},
|
{force: true, failSilently: false, recompileOnChanges: false},
|
||||||
)
|
)
|
||||||
.then(defaultPlugins =>
|
.then(defaultPlugins =>
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
|
|||||||
@@ -127,17 +127,29 @@ async function addWebsocket(server) {
|
|||||||
|
|
||||||
// refresh the app on changes to the src folder
|
// refresh the app on changes to the src folder
|
||||||
// this can be removed once metroServer notifies us about file changes
|
// this can be removed once metroServer notifies us about file changes
|
||||||
|
try {
|
||||||
const watchman = new Watchman(path.resolve(__dirname, '..', 'src'));
|
const watchman = new Watchman(path.resolve(__dirname, '..', 'src'));
|
||||||
await watchman.initialize();
|
await watchman.initialize();
|
||||||
await watchman.startWatchFiles(
|
await watchman.startWatchFiles(
|
||||||
'/',
|
'',
|
||||||
resp => {
|
() => {
|
||||||
io.emit('refresh');
|
io.emit('refresh');
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
excludes: ['**/__tests__/**/*', '**/node_modules/**/*', '**/.*'],
|
excludes: [
|
||||||
|
'**/__tests__/**/*',
|
||||||
|
'**/node_modules/**/*',
|
||||||
|
'**/.*',
|
||||||
|
'plugins/**/*', // plugin changes are tracked separately, so exlcuding them here to avoid double reloading.
|
||||||
|
],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
'Failed to start watching for changes using Watchman, continue without hot reloading',
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return io;
|
return io;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ const Watchman = require('./watchman');
|
|||||||
const DEFAULT_COMPILE_OPTIONS = {
|
const DEFAULT_COMPILE_OPTIONS = {
|
||||||
force: false,
|
force: false,
|
||||||
failSilently: true,
|
failSilently: true,
|
||||||
|
recompileOnChanges: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = async (
|
module.exports = async (
|
||||||
@@ -29,11 +30,14 @@ module.exports = async (
|
|||||||
pluginCache,
|
pluginCache,
|
||||||
options = DEFAULT_COMPILE_OPTIONS,
|
options = DEFAULT_COMPILE_OPTIONS,
|
||||||
) => {
|
) => {
|
||||||
|
options = Object.assign({}, DEFAULT_COMPILE_OPTIONS, options);
|
||||||
const plugins = pluginEntryPoints(pluginPaths);
|
const plugins = pluginEntryPoints(pluginPaths);
|
||||||
if (!fs.existsSync(pluginCache)) {
|
if (!fs.existsSync(pluginCache)) {
|
||||||
fs.mkdirSync(pluginCache);
|
fs.mkdirSync(pluginCache);
|
||||||
}
|
}
|
||||||
watchChanges(plugins, reloadCallback, pluginCache, options);
|
if (options.recompileOnChanges) {
|
||||||
|
await startWatchChanges(plugins, reloadCallback, pluginCache, options);
|
||||||
|
}
|
||||||
const compilations = pMap(
|
const compilations = pMap(
|
||||||
Object.values(plugins),
|
Object.values(plugins),
|
||||||
plugin => {
|
plugin => {
|
||||||
@@ -48,7 +52,24 @@ module.exports = async (
|
|||||||
return dynamicPlugins;
|
return dynamicPlugins;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function watchChanges(
|
async function startWatchingPluginsUsingWatchman(plugins, onPluginChanged) {
|
||||||
|
const rootDir = path.resolve(__dirname, '..');
|
||||||
|
const watchman = new Watchman(rootDir);
|
||||||
|
await watchman.initialize();
|
||||||
|
await Promise.all(
|
||||||
|
plugins.map(plugin =>
|
||||||
|
watchman.startWatchFiles(
|
||||||
|
path.relative(rootDir, plugin.rootDir),
|
||||||
|
() => onPluginChanged(plugin),
|
||||||
|
{
|
||||||
|
excludes: ['**/__tests__/**/*', '**/node_modules/**/*', '**/.*'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startWatchChanges(
|
||||||
plugins,
|
plugins,
|
||||||
reloadCallback,
|
reloadCallback,
|
||||||
pluginCache,
|
pluginCache,
|
||||||
@@ -59,38 +80,31 @@ async function watchChanges(
|
|||||||
|
|
||||||
const delayedCompilation = {};
|
const delayedCompilation = {};
|
||||||
const kCompilationDelayMillis = 1000;
|
const kCompilationDelayMillis = 1000;
|
||||||
const rootDir = path.resolve(__dirname, '..');
|
const onPluginChanged = plugin => {
|
||||||
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.
|
|
||||||
.filter(
|
|
||||||
plugin => !plugin.rootDir.startsWith(path.join(HOME_DIR, '.flipper')),
|
|
||||||
)
|
|
||||||
.map(plugin =>
|
|
||||||
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]) {
|
if (!delayedCompilation[plugin.name]) {
|
||||||
delayedCompilation[plugin.name] = setTimeout(() => {
|
delayedCompilation[plugin.name] = setTimeout(() => {
|
||||||
delayedCompilation[plugin.name] = null;
|
delayedCompilation[plugin.name] = null;
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(`🕵️ Detected changes in ${plugin.name}`);
|
console.log(`🕵️ Detected changes in ${plugin.name}`);
|
||||||
const watchOptions = Object.assign(options, {force: true});
|
const watchOptions = Object.assign(options, {force: true});
|
||||||
compilePlugin(plugin, pluginCache, watchOptions).then(
|
compilePlugin(plugin, pluginCache, watchOptions).then(reloadCallback);
|
||||||
reloadCallback,
|
|
||||||
);
|
|
||||||
}, kCompilationDelayMillis);
|
}, kCompilationDelayMillis);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
{
|
const filteredPlugins = Object.values(plugins)
|
||||||
excludes: ['**/__tests__/**/*', '**/node_modules/**/*', '**/.*'],
|
// no hot reloading for plugins in .flipper folder. This is to prevent
|
||||||
},
|
// Flipper from reloading, while we are doing changes on thirdparty plugins.
|
||||||
),
|
.filter(
|
||||||
|
plugin => !plugin.rootDir.startsWith(path.join(HOME_DIR, '.flipper')),
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
|
await startWatchingPluginsUsingWatchman(filteredPlugins, onPluginChanged);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
'Failed to start watching plugin files using Watchman, continue without hot reloading',
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function hash(string) {
|
function hash(string) {
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
|
|||||||
@@ -16,28 +16,33 @@ module.exports = class Watchman {
|
|||||||
this.rootDir = rootDir;
|
this.rootDir = rootDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
initialize() {
|
||||||
if (this.client) {
|
if (this.client) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.client = new watchman.Client();
|
this.client = new watchman.Client();
|
||||||
this.client.setMaxListeners(250);
|
this.client.setMaxListeners(250);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const onError = err => {
|
||||||
|
this.client.removeAllListeners('error');
|
||||||
|
reject(err);
|
||||||
|
this.client.end();
|
||||||
|
delete this.client;
|
||||||
|
};
|
||||||
|
this.client.once('error', onError);
|
||||||
this.client.capabilityCheck(
|
this.client.capabilityCheck(
|
||||||
{optional: [], required: ['relative_root']},
|
{optional: [], required: ['relative_root']},
|
||||||
error => {
|
error => {
|
||||||
if (error) {
|
if (error) {
|
||||||
this.client.end();
|
onError(error);
|
||||||
delete this.client;
|
return;
|
||||||
return reject(error);
|
|
||||||
}
|
}
|
||||||
this.client.command(
|
this.client.command(
|
||||||
['watch-project', this.rootDir],
|
['watch-project', this.rootDir],
|
||||||
(error, resp) => {
|
(error, resp) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
this.client.end();
|
onError(error);
|
||||||
delete this.client;
|
return;
|
||||||
return reject(error);
|
|
||||||
}
|
}
|
||||||
if ('warning' in resp) {
|
if ('warning' in resp) {
|
||||||
console.warn(resp.warning);
|
console.warn(resp.warning);
|
||||||
|
|||||||
Reference in New Issue
Block a user