Move local plugin discovery to dispatcher/redux store

Summary: In order to have update notifications, this must live outside the UI component, but it also gives some additional benefits like better testability of previously effectful UI.

Reviewed By: jknoxville

Differential Revision: D18173166

fbshipit-source-id: 1cacb6c7893423a7920a6620dfb76e631caba101
This commit is contained in:
Pascal Hartig
2019-11-05 05:27:38 -08:00
committed by Facebook Github Bot
parent 42a77094f4
commit 432bb1b00a
6 changed files with 170 additions and 42 deletions

View File

@@ -30,12 +30,14 @@ import {List} from 'immutable';
import algoliasearch from 'algoliasearch';
import path from 'path';
import fs from 'fs-extra';
import {homedir} from 'os';
import {PluginManager as PM} from 'live-plugin-manager';
import {reportPlatformFailures, reportUsage} from '../utils/metrics';
import restartFlipper from '../utils/restartFlipper';
import {PluginMap, PluginDefinition} from '../reducers/pluginManager';
import {PLUGIN_DIR} from '../dispatcher/pluginManager';
import {State as AppState} from '../reducers';
import {connect} from 'react-redux';
const PLUGIN_DIR = path.join(homedir(), '.flipper', 'thirdparty');
const ALGOLIA_APPLICATION_ID = 'OFCNCOG2CU';
const ALGOLIA_API_KEY = 'f54e21fa3a2a0160595bb058179bfb1e';
const TAG = 'PluginInstaller';
@@ -43,12 +45,6 @@ const PluginManager = new PM({
ignoredDependencies: ['flipper', 'react', 'react-dom', '@types/*'],
});
export type PluginDefinition = {
name: string;
version: string;
description: string;
};
const EllipsisText = styled(Text)({
overflow: 'hidden',
textOverflow: 'ellipsis',
@@ -94,18 +90,23 @@ const RestartBar = styled(FlexColumn)({
textAlign: 'center',
});
type Props = {
type PropsFromState = {
installedPlugins: PluginMap;
};
type OwnProps = {
searchIndexFactory: () => algoliasearch.Index;
getInstalledPlugins: () => Promise<Map<string, PluginDefinition>>;
autoHeight: boolean;
};
type Props = OwnProps & PropsFromState;
const defaultProps: Props = {
searchIndexFactory: () => {
const client = algoliasearch(ALGOLIA_APPLICATION_ID, ALGOLIA_API_KEY);
return client.initIndex('npm-search');
},
getInstalledPlugins: _getInstalledPlugins,
installedPlugins: new Map(),
autoHeight: false,
};
@@ -117,7 +118,8 @@ const PluginInstaller = function props(props: Props) {
query,
setQuery,
props.searchIndexFactory,
props.getInstalledPlugins,
// TODO(T56693735): Refactor this to directly take props.
async () => props.installedPlugins,
);
const restartApp = useCallback(() => {
restartFlipper();
@@ -155,7 +157,6 @@ const PluginInstaller = function props(props: Props) {
);
};
PluginInstaller.defaultProps = defaultProps;
export default PluginInstaller;
const TableButton = styled(Button)({
marginTop: 2,
@@ -365,32 +366,9 @@ function useNPMSearch(
return List(results.map(createRow));
}
async function _getInstalledPlugins(): Promise<Map<string, PluginDefinition>> {
const pluginDirExists = await fs.pathExists(PLUGIN_DIR);
if (!pluginDirExists) {
return new Map();
}
const dirs = await fs.readdir(PLUGIN_DIR);
const plugins = await Promise.all<[string, PluginDefinition]>(
dirs.map(
name =>
new Promise(async (resolve, reject) => {
if (!(await fs.lstat(path.join(PLUGIN_DIR, name))).isDirectory()) {
return resolve(undefined);
}
const packageJSON = await fs.readFile(
path.join(PLUGIN_DIR, name, 'package.json'),
);
try {
resolve([name, JSON.parse(packageJSON.toString())]);
} catch (e) {
reject(e);
}
}),
),
);
return new Map(plugins.filter(Boolean));
}
export default connect<PropsFromState, {}, OwnProps, AppState>(
({pluginManager: {installedPlugins}}) => ({
installedPlugins,
}),
{},
)(PluginInstaller);