Watch and rebuild plugins

Summary: Now, once we build all plugins before we start Flipper, we need to rebuild some of them when they change. Previously, it was handled by Metro when we included plugins int he bundle, but we no longer include them in the bundle.

Reviewed By: lblasa

Differential Revision: D39510213

fbshipit-source-id: a352d78946f844a25d9127ac09c26e43e6739ca9
This commit is contained in:
Andrey Goncharov
2022-09-15 10:02:19 -07:00
committed by Facebook GitHub Bot
parent 9dda947371
commit 3639feef61
4 changed files with 71 additions and 14 deletions

View File

@@ -37,6 +37,7 @@ import {
import pFilter from 'p-filter'; import pFilter from 'p-filter';
import child from 'child_process'; import child from 'child_process';
import pMap from 'p-map'; import pMap from 'p-map';
import chalk from 'chalk';
const dev = process.env.NODE_ENV !== 'production'; const dev = process.env.NODE_ENV !== 'production';
@@ -510,3 +511,17 @@ function defaultResolve(...rest: any[]) {
...rest, ...rest,
); );
} }
export async function rebuildPlugin(pluginPath: string) {
try {
await runBuild(pluginPath, true);
console.info(chalk.green('Rebuilt plugin'), pluginPath);
} catch (e) {
console.error(
chalk.red(
'Failed to compile a plugin, waiting for additional changes...',
),
e,
);
}
}

View File

@@ -362,9 +362,6 @@ async function startWatchChanges(io: socketIo.Server) {
), ),
), ),
); );
await startWatchPlugins(() => {
io.emit('refresh');
});
} catch (err) { } catch (err) {
console.error( console.error(
'Failed to start watching for changes using Watchman, continue without hot reloading', 'Failed to start watching for changes using Watchman, continue without hot reloading',
@@ -448,6 +445,9 @@ function checkDevServer() {
await startMetroServer(app, server); await startMetroServer(app, server);
outputScreen(socket); outputScreen(socket);
await compileMain(); await compileMain();
await startWatchPlugins(() => {
socket.emit('refresh');
});
if (dotenv && dotenv.parsed) { if (dotenv && dotenv.parsed) {
console.log('✅ Loaded env vars from .env file: ', dotenv.parsed); console.log('✅ Loaded env vars from .env file: ', dotenv.parsed);
} }

View File

@@ -19,6 +19,7 @@ import Watchman from './watchman';
import isFB from './isFB'; import isFB from './isFB';
import yargs from 'yargs'; import yargs from 'yargs';
import ensurePluginFoldersWatchable from './ensurePluginFoldersWatchable'; import ensurePluginFoldersWatchable from './ensurePluginFoldersWatchable';
import startWatchPlugins from './startWatchPlugins';
const argv = yargs const argv = yargs
.usage('yarn flipper-server [args]') .usage('yarn flipper-server [args]')
@@ -149,12 +150,15 @@ async function startWatchChanges() {
// For UI changes, Metro / hot module reloading / fast refresh take care of the changes // For UI changes, Metro / hot module reloading / fast refresh take care of the changes
await Promise.all( await Promise.all(
[ [
'pkg',
'doctor', 'doctor',
'pkg-lib',
'plugin-lib', 'plugin-lib',
'flipper-server',
'flipper-common', 'flipper-common',
'flipper-frontend-core',
'flipper-plugin-core',
'flipper-server-companion',
'flipper-server-core', 'flipper-server-core',
'flipper-server',
].map((dir) => ].map((dir) =>
watchman.startWatchFiles( watchman.startWatchFiles(
dir, dir,
@@ -191,4 +195,5 @@ async function startWatchChanges() {
await restartServer(); await restartServer();
// watch // watch
await startWatchChanges(); await startWatchChanges();
await startWatchPlugins();
})(); })();

View File

@@ -8,23 +8,49 @@
*/ */
import Watchman from './watchman'; import Watchman from './watchman';
import {getPluginSourceFolders} from 'flipper-plugin-lib'; import {getPluginSourceFolders, isPluginDir} from 'flipper-plugin-lib';
import path from 'path';
import chalk from 'chalk';
import {rebuildPlugin} from './build-utils';
export default async function startWatchPlugins( export default async function startWatchPlugins(
onChanged: () => void | Promise<void>, onChanged?: () => void | Promise<void>,
) { ) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('🕵️‍ Watching for plugin changes'); console.log('🕵️‍ Watching for plugin changes');
let delayedCompilation: NodeJS.Timeout | undefined; let delayedCompilation: NodeJS.Timeout | undefined;
const kCompilationDelayMillis = 1000; const kCompilationDelayMillis = 1000;
const onPluginChangeDetected = () => { const onPluginChangeDetected = (root: string, files: string[]) => {
if (!delayedCompilation) { if (!delayedCompilation) {
delayedCompilation = setTimeout(() => { delayedCompilation = setTimeout(async () => {
delayedCompilation = undefined; delayedCompilation = undefined;
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(`🕵️‍ Detected plugin change`); console.log(`🕵️‍ Detected plugin change`);
onChanged(); await Promise.all(
// https://facebook.github.io/watchman/docs/nodejs.html#subscribing-to-changes
files.map(async (file: string) => {
const filePathAbs = path.resolve(root, file);
let dirPath = path.dirname(filePathAbs);
while (
// Stop when we reach plugin root
!(await isPluginDir(dirPath))
) {
const relative = path.relative(root, dirPath);
// Stop when we reach desktop/plugins folder
if (!relative || relative.startsWith('..')) {
console.warn(
chalk.yellow('Failed to find a plugin root for path'),
filePathAbs,
);
return;
}
dirPath = path.resolve(dirPath, '..');
}
await rebuildPlugin(dirPath);
}),
);
onChanged?.();
}, kCompilationDelayMillis); }, kCompilationDelayMillis);
} }
}; };
@@ -38,15 +64,26 @@ export default async function startWatchPlugins(
} }
} }
async function startWatchingPluginsUsingWatchman(onChange: () => void) { async function startWatchingPluginsUsingWatchman(
onChange: (root: string, files: string[]) => void,
) {
const pluginFolders = await getPluginSourceFolders(); const pluginFolders = await getPluginSourceFolders();
await Promise.all( await Promise.all(
pluginFolders.map(async (pluginFolder) => { pluginFolders.map(async (pluginFolder) => {
const watchman = new Watchman(pluginFolder); const watchman = new Watchman(pluginFolder);
await watchman.initialize(); await watchman.initialize();
await watchman.startWatchFiles('.', () => onChange(), { await watchman.startWatchFiles(
excludes: ['**/__tests__/**/*', '**/node_modules/**/*', '**/.*'], '.',
}); ({files}) => onChange(pluginFolder, files),
{
excludes: [
'**/__tests__/**/*',
'**/node_modules/**/*',
'**/dist/*',
'**/.*',
],
},
);
}), }),
); );
} }