Files
flipper/desktop/app/src/utils/loadDynamicPlugins.tsx
Anton Nikolaev f3e1a48ff3 Plugin Marketplace state refresh and cache
Summary:
Separate dispatcher for periodic refreshing available plugins data from the Marketplace backend and caching it locally.

The plugin auto update downloader subscribes to these state refreshes and automatically schedules plugin update downloads when required.

Reviewed By: passy

Differential Revision: D25360897

fbshipit-source-id: 5b6d95b63ff47b8ae9ad8b12e2480d1fed524ca5
2020-12-15 09:31:54 -08:00

93 lines
2.8 KiB
TypeScript

/**
* Copyright (c) Facebook, Inc. and its 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 path from 'path';
import fs from 'fs-extra';
import pMap from 'p-map';
import {
PluginDetails,
getSourcePlugins,
getInstalledPlugins,
finishPendingPluginInstallations,
} from 'flipper-plugin-lib';
import os from 'os';
import {getStaticPath} from '../utils/pathUtils';
const pluginCache = path.join(os.homedir(), '.flipper', 'plugins');
// Load "dynamic" plugins, e.g. those which are either installed or loaded from sources for development purposes.
// This opposed to "static" plugins which are already included into Flipper bundle.
export default async function loadDynamicPlugins(): Promise<PluginDetails[]> {
if (process.env.FLIPPER_FAST_REFRESH) {
console.log(
'❌ Skipping loading of dynamic plugins because Fast Refresh is enabled. Fast Refresh only works with bundled plugins.',
);
return [];
}
try {
await finishPendingPluginInstallations();
} catch (err) {
console.error('❌ Failed to finish pending installations', err);
}
const staticPath = getStaticPath();
const defaultPlugins = new Set<string>(
(
await fs.readJson(path.join(staticPath, 'defaultPlugins', 'index.json'))
).map((p: any) => p.name) as string[],
);
const dynamicPlugins = [
...(await getInstalledPlugins()),
...(await getSourcePlugins()).filter((p) => !defaultPlugins.has(p.name)),
];
await fs.ensureDir(pluginCache);
const compilations = pMap(
dynamicPlugins,
(plugin) => {
return loadPlugin(plugin);
},
{concurrency: 4},
);
const compiledDynamicPlugins = (await compilations).filter(
(c) => c !== null,
) as PluginDetails[];
console.log(
`✅ Loaded ${dynamicPlugins.length} dynamic plugins: ${dynamicPlugins
.map((x) => x.title)
.join(', ')}.`,
);
return compiledDynamicPlugins;
}
async function loadPlugin(
pluginDetails: PluginDetails,
): Promise<PluginDetails | null> {
const {specVersion, version, entry, name} = pluginDetails;
if (specVersion > 1) {
if (await fs.pathExists(entry)) {
return pluginDetails;
} else {
console.error(
`❌ Plugin ${name} is ignored, because its entry point not found: ${entry}.`,
);
return null;
}
} else {
// Try to load cached version of legacy plugin
const entry = path.join(pluginCache, `${name}@${version || '0.0.0'}.js`);
if (await fs.pathExists(entry)) {
console.log(`🥫 Using cached version of legacy plugin ${name}...`);
return pluginDetails;
} else {
console.error(
`❌ Plugin ${name} is ignored, because it is defined by the unsupported spec v1 and could not be compiled.`,
);
return null;
}
}
}