From fd811db12b9f6a878d3fb12219e1ec4959fff5a8 Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Thu, 22 Sep 2022 04:17:24 -0700 Subject: [PATCH] Rebuild all plugins if a shared lib changed Summary: Some plugins import from shared directories. These directories are not plugins themselves, therefore the current plugin root searching mechanism does nto work for them. To support plugin reloading for this scenario, we start re-building all plugins if we fail to find a plugin root. Reviewed By: lblasa Differential Revision: D39693820 fbshipit-source-id: 33dd7de4121bd5665a39b0ea96adce4603dc7df0 --- .../flipper-server/src/attachDevServer.tsx | 23 +++--- desktop/pkg-lib/package.json | 3 +- desktop/pkg-lib/src/buildDefaultPlugins.tsx | 54 +++++++++++++ desktop/pkg-lib/src/getDefaultPlugins.tsx | 43 +++++++++++ desktop/pkg-lib/src/index.tsx | 2 + desktop/pkg-lib/src/startWatchPlugins.tsx | 71 +++++++++++------- desktop/scripts/build-utils.tsx | 75 ++----------------- desktop/scripts/start-dev-server.tsx | 7 +- desktop/yarn.lock | 2 +- 9 files changed, 165 insertions(+), 115 deletions(-) create mode 100644 desktop/pkg-lib/src/buildDefaultPlugins.tsx create mode 100644 desktop/pkg-lib/src/getDefaultPlugins.tsx diff --git a/desktop/flipper-server/src/attachDevServer.tsx b/desktop/flipper-server/src/attachDevServer.tsx index abb12b6b5..15bf5562f 100644 --- a/desktop/flipper-server/src/attachDevServer.tsx +++ b/desktop/flipper-server/src/attachDevServer.tsx @@ -151,16 +151,19 @@ export async function attachDevServer( next(); }); - await startWatchPlugins((changedPlugins: InstalledPluginDetails[]) => { - socket.clients.forEach((client) => { - client.send( - JSON.stringify({ - event: 'plugins-source-updated', - payload: changedPlugins, - }), - ); - }); - }); + await startWatchPlugins( + process.env.FLIPPER_RELEASE_CHANNEL === 'insiders', + (changedPlugins: InstalledPluginDetails[]) => { + socket.clients.forEach((client) => { + client.send( + JSON.stringify({ + event: 'plugins-source-updated', + payload: changedPlugins, + }), + ); + }); + }, + ); console.log('DEV webserver started.'); } diff --git a/desktop/pkg-lib/package.json b/desktop/pkg-lib/package.json index f9753bb15..2ddce172b 100644 --- a/desktop/pkg-lib/package.json +++ b/desktop/pkg-lib/package.json @@ -18,7 +18,8 @@ "metro": "^0.70.2", "metro-cache": "^0.70.2", "metro-minify-terser": "^0.70.2", - "npm-packlist": "^4.0.0" + "npm-packlist": "^4.0.0", + "p-map": "^4" }, "devDependencies": { "@types/fs-extra": "^9.0.13", diff --git a/desktop/pkg-lib/src/buildDefaultPlugins.tsx b/desktop/pkg-lib/src/buildDefaultPlugins.tsx new file mode 100644 index 000000000..9b7400381 --- /dev/null +++ b/desktop/pkg-lib/src/buildDefaultPlugins.tsx @@ -0,0 +1,54 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {InstalledPluginDetails} from 'flipper-common'; +import pMap from 'p-map'; +import path from 'path'; +import fs from 'fs-extra'; +import runBuild from './runBuild'; + +const defaultPluginsDir = path.join(__dirname, '../../static/defaultPlugins'); + +export async function buildDefaultPlugins( + defaultPlugins: InstalledPluginDetails[], + dev: boolean, +) { + if (process.env.FLIPPER_NO_REBUILD_PLUGINS) { + console.log( + `⚙️ Including ${ + defaultPlugins.length + } plugins into the default plugins list. Skipping rebuilding because "no-rebuild-plugins" option provided. List of default plugins: ${defaultPlugins + .map((p) => p.id) + .join(', ')}`, + ); + } + await pMap( + defaultPlugins, + async function (plugin) { + try { + if (!process.env.FLIPPER_NO_REBUILD_PLUGINS) { + console.log( + `⚙️ Building plugin ${plugin.id} to include it into the default plugins list...`, + ); + await runBuild(plugin.dir, dev); + } + await fs.ensureSymlink( + plugin.dir, + path.join(defaultPluginsDir, plugin.name), + 'junction', + ); + } catch (err) { + console.error(`✖ Failed to build plugin ${plugin.id}`, err); + } + }, + { + concurrency: 16, + }, + ); +} diff --git a/desktop/pkg-lib/src/getDefaultPlugins.tsx b/desktop/pkg-lib/src/getDefaultPlugins.tsx new file mode 100644 index 000000000..e84defa30 --- /dev/null +++ b/desktop/pkg-lib/src/getDefaultPlugins.tsx @@ -0,0 +1,43 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {getSourcePlugins} from 'flipper-plugin-lib'; + +// For insiders builds we bundle top 5 popular device plugins, +// plus top 10 popular "universal" plugins enabled by more than 100 users. +const hardcodedPlugins = new Set([ + // Popular device plugins + 'DeviceLogs', + 'CrashReporter', + 'MobileBuilds', + 'Hermesdebuggerrn', + 'React', + // Popular client plugins + 'Inspector', + 'Network', + 'AnalyticsLogging', + 'GraphQL', + 'UIPerf', + 'MobileConfig', + 'Databases', + 'FunnelLogger', + 'Navigation', + 'Fresco', + 'Preferences', +]); + +export const getDefaultPlugins = async (isInsidersBuild: boolean) => { + const sourcePlugins = await getSourcePlugins(); + const defaultPlugins = sourcePlugins + // we only include headless plugins and a predefined set of regular plugins into insiders release + .filter( + (p) => !isInsidersBuild || hardcodedPlugins.has(p.id) || p.headless, + ); + return defaultPlugins; +}; diff --git a/desktop/pkg-lib/src/index.tsx b/desktop/pkg-lib/src/index.tsx index 813ee02c6..8102e05ae 100644 --- a/desktop/pkg-lib/src/index.tsx +++ b/desktop/pkg-lib/src/index.tsx @@ -13,3 +13,5 @@ export {default as computePackageChecksum} from './computePackageChecksum'; export {default as stripSourceMapComment} from './stripSourceMap'; export {default as startWatchPlugins} from './startWatchPlugins'; export {default as Watchman} from './watchman'; +export {buildDefaultPlugins} from './buildDefaultPlugins'; +export {getDefaultPlugins} from './getDefaultPlugins'; diff --git a/desktop/pkg-lib/src/startWatchPlugins.tsx b/desktop/pkg-lib/src/startWatchPlugins.tsx index ca67e3183..5d3558f6c 100644 --- a/desktop/pkg-lib/src/startWatchPlugins.tsx +++ b/desktop/pkg-lib/src/startWatchPlugins.tsx @@ -17,6 +17,8 @@ import path from 'path'; import chalk from 'chalk'; import runBuild from './runBuild'; import {InstalledPluginDetails} from 'flipper-common'; +import {getDefaultPlugins} from './getDefaultPlugins'; +import {buildDefaultPlugins} from './buildDefaultPlugins'; async function rebuildPlugin(pluginPath: string) { try { @@ -33,6 +35,7 @@ async function rebuildPlugin(pluginPath: string) { } export default async function startWatchPlugins( + isInsidersBuild: boolean, onChanged?: ( changedPlugins: InstalledPluginDetails[], ) => void | Promise, @@ -48,36 +51,46 @@ export default async function startWatchPlugins( delayedCompilation = undefined; // eslint-disable-next-line no-console console.log(`🕵️‍ Detected plugin change`); - const changedDirs = 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; + try { + const changedDirs = 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.info( + chalk.yellow( + 'Failed to find a plugin root for path. Rebuilding all plugins', + ), + filePathAbs, + ); + throw new Error('REBUILD_ALL'); + } + dirPath = path.resolve(dirPath, '..'); } - dirPath = path.resolve(dirPath, '..'); - } - await rebuildPlugin(dirPath); - return dirPath; - }), - ); - const changedPlugins = await Promise.all( - changedDirs - .filter((dirPath): dirPath is string => !!dirPath) - .map((dirPath) => getInstalledPluginDetails(dirPath)), - ); - onChanged?.(changedPlugins); + await rebuildPlugin(dirPath); + return dirPath; + }), + ); + const changedPlugins = await Promise.all( + changedDirs.map((dirPath) => getInstalledPluginDetails(dirPath)), + ); + onChanged?.(changedPlugins); + } catch (e) { + if (e instanceof Error && e.message === 'REBUILD_ALL') { + const defaultPlugins = await getDefaultPlugins(isInsidersBuild); + await buildDefaultPlugins(defaultPlugins, true); + onChanged?.(defaultPlugins); + return; + } + throw e; + } }, kCompilationDelayMillis); } }; diff --git a/desktop/scripts/build-utils.tsx b/desktop/scripts/build-utils.tsx index 16db7ab6d..6c06094e0 100644 --- a/desktop/scripts/build-utils.tsx +++ b/desktop/scripts/build-utils.tsx @@ -18,13 +18,13 @@ import path from 'path'; import fs from 'fs-extra'; import {spawn, exec} from 'promisify-child-process'; import { + buildDefaultPlugins, + getDefaultPlugins, getWatchFolders, - runBuild, stripSourceMapComment, } from 'flipper-pkg-lib'; import getAppWatchFolders from './get-app-watch-folders'; -import {getSourcePlugins, getPluginSourceFolders} from 'flipper-plugin-lib'; -import type {InstalledPluginDetails} from 'flipper-common'; +import {getPluginSourceFolders} from 'flipper-plugin-lib'; import { appDir, staticDir, @@ -36,34 +36,9 @@ import { } from './paths'; 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'; -// For insiders builds we bundle top 5 popular device plugins, -// plus top 10 popular "universal" plugins enabled by more than 100 users. -const hardcodedPlugins = new Set([ - // Popular device plugins - 'DeviceLogs', - 'CrashReporter', - 'MobileBuilds', - 'Hermesdebuggerrn', - 'React', - // Popular client plugins - 'Inspector', - 'Network', - 'AnalyticsLogging', - 'GraphQL', - 'UIPerf', - 'MobileConfig', - 'Databases', - 'FunnelLogger', - 'Navigation', - 'Fresco', - 'Preferences', -]); - export function die(err: Error) { console.error('Script termnated.', err); process.exit(1); @@ -87,52 +62,12 @@ export async function prepareDefaultPlugins(isInsidersBuild: boolean = false) { }); console.log('✅ Copied the provided default plugins dir.'); } else { - const sourcePlugins = await getSourcePlugins(); - const defaultPlugins = sourcePlugins - // we only include headless plugins and a predefined set of regular plugins into insiders release - .filter( - (p) => !isInsidersBuild || hardcodedPlugins.has(p.id) || p.headless, - ); - await buildDefaultPlugins(defaultPlugins); + const defaultPlugins = await getDefaultPlugins(isInsidersBuild); + await buildDefaultPlugins(defaultPlugins, dev); } console.log('✅ Prepared default plugins.'); } -async function buildDefaultPlugins(defaultPlugins: InstalledPluginDetails[]) { - if (process.env.FLIPPER_NO_REBUILD_PLUGINS) { - console.log( - `⚙️ Including ${ - defaultPlugins.length - } plugins into the default plugins list. Skipping rebuilding because "no-rebuild-plugins" option provided. List of default plugins: ${defaultPlugins - .map((p) => p.id) - .join(', ')}`, - ); - } - await pMap( - defaultPlugins, - async function (plugin) { - try { - if (!process.env.FLIPPER_NO_REBUILD_PLUGINS) { - console.log( - `⚙️ Building plugin ${plugin.id} to include it into the default plugins list...`, - ); - await runBuild(plugin.dir, dev); - } - await fs.ensureSymlink( - plugin.dir, - path.join(defaultPluginsDir, plugin.name), - 'junction', - ); - } catch (err) { - console.error(`✖ Failed to build plugin ${plugin.id}`, err); - } - }, - { - concurrency: 16, - }, - ); -} - const minifierConfig = { minifierPath: require.resolve('metro-minify-terser'), minifierConfig: { diff --git a/desktop/scripts/start-dev-server.tsx b/desktop/scripts/start-dev-server.tsx index 68cf88c19..543f1767b 100644 --- a/desktop/scripts/start-dev-server.tsx +++ b/desktop/scripts/start-dev-server.tsx @@ -403,10 +403,9 @@ function checkDevServer() { } (async () => { + const isInsidersBuild = process.env.FLIPPER_RELEASE_CHANNEL === 'insiders'; checkDevServer(); - await prepareDefaultPlugins( - process.env.FLIPPER_RELEASE_CHANNEL === 'insiders', - ); + await prepareDefaultPlugins(isInsidersBuild); await ensurePluginFoldersWatchable(); const port = await detect(DEFAULT_PORT); const {app, server} = await startAssetServer(port); @@ -414,7 +413,7 @@ function checkDevServer() { await startMetroServer(app, server); outputScreen(socket); await compileMain(); - await startWatchPlugins((changedPlugins) => { + await startWatchPlugins(isInsidersBuild, (changedPlugins) => { socket.emit('plugins-source-updated', changedPlugins); }); if (dotenv && dotenv.parsed) { diff --git a/desktop/yarn.lock b/desktop/yarn.lock index 6612efa69..136a6fbca 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -11293,7 +11293,7 @@ p-map@^2.0.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== -p-map@^4.0.0: +p-map@^4, p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==