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 child from 'child_process';
import pMap from 'p-map';
import chalk from 'chalk';
const dev = process.env.NODE_ENV !== 'production';
@@ -510,3 +511,17 @@ function defaultResolve(...rest: any[]) {
...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) {
console.error(
'Failed to start watching for changes using Watchman, continue without hot reloading',
@@ -448,6 +445,9 @@ function checkDevServer() {
await startMetroServer(app, server);
outputScreen(socket);
await compileMain();
await startWatchPlugins(() => {
socket.emit('refresh');
});
if (dotenv && 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 yargs from 'yargs';
import ensurePluginFoldersWatchable from './ensurePluginFoldersWatchable';
import startWatchPlugins from './startWatchPlugins';
const argv = yargs
.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
await Promise.all(
[
'pkg',
'doctor',
'pkg-lib',
'plugin-lib',
'flipper-server',
'flipper-common',
'flipper-frontend-core',
'flipper-plugin-core',
'flipper-server-companion',
'flipper-server-core',
'flipper-server',
].map((dir) =>
watchman.startWatchFiles(
dir,
@@ -191,4 +195,5 @@ async function startWatchChanges() {
await restartServer();
// watch
await startWatchChanges();
await startWatchPlugins();
})();

View File

@@ -8,23 +8,49 @@
*/
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(
onChanged: () => void | Promise<void>,
onChanged?: () => void | Promise<void>,
) {
// eslint-disable-next-line no-console
console.log('🕵️‍ Watching for plugin changes');
let delayedCompilation: NodeJS.Timeout | undefined;
const kCompilationDelayMillis = 1000;
const onPluginChangeDetected = () => {
const onPluginChangeDetected = (root: string, files: string[]) => {
if (!delayedCompilation) {
delayedCompilation = setTimeout(() => {
delayedCompilation = setTimeout(async () => {
delayedCompilation = undefined;
// eslint-disable-next-line no-console
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);
}
};
@@ -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();
await Promise.all(
pluginFolders.map(async (pluginFolder) => {
const watchman = new Watchman(pluginFolder);
await watchman.initialize();
await watchman.startWatchFiles('.', () => onChange(), {
excludes: ['**/__tests__/**/*', '**/node_modules/**/*', '**/.*'],
});
await watchman.startWatchFiles(
'.',
({files}) => onChange(pluginFolder, files),
{
excludes: [
'**/__tests__/**/*',
'**/node_modules/**/*',
'**/dist/*',
'**/.*',
],
},
);
}),
);
}