read infos from package.json

Summary:
Adding the properties from a plugin's `package.json` as static properties to the class.
The name from `package.json` is used as it's `id`.

This allows us in the future to add meta information about a plugin to it's package.json and still use the data inside the app.

Reviewed By: priteshrnandgaonkar

Differential Revision: D13417288

fbshipit-source-id: 3d0a62d4cb0115153cce1aaee677b9680fefebf4
This commit is contained in:
Daniel Büchele
2018-12-18 08:27:25 -08:00
committed by Facebook Github Bot
parent e23da69db9
commit 6ffc027051
9 changed files with 54 additions and 47 deletions

View File

@@ -472,7 +472,7 @@ class NotificationItem extends Component<ItemProps, ItemState> {
const items = [];
if (props.onHidePlugin && props.plugin) {
items.push({
label: `Hide ${props.plugin.title} plugin`,
label: `Hide ${props.plugin.title || props.plugin.id} plugin`,
click: this.props.onHidePlugin,
});
}

View File

@@ -120,9 +120,8 @@ class PluginContainer extends PureComponent<Props> {
<React.Fragment>
<Container key="plugin">
<ErrorBoundary
heading={`Plugin "${
activePlugin.title
}" encountered an error during render`}
heading={`Plugin "${activePlugin.title ||
'Unknown'}" encountered an error during render`}
logger={this.props.logger}>
{React.createElement(activePlugin, props)}
</ErrorBoundary>

View File

@@ -9,9 +9,6 @@ import {createTablePlugin} from '../createTablePlugin.js';
import {FlipperPlugin} from '../plugin.js';
const PROPS = {
title: 'Plugin Title',
id: 'pluginID',
icon: 'icon',
method: 'method',
resetMethod: 'resetMethod',
columns: {},
@@ -25,24 +22,6 @@ test('createTablePlugin returns FlipperPlugin', () => {
expect(tablePlugin.prototype).toBeInstanceOf(FlipperPlugin);
});
test('Plugin ID is set', () => {
const id = 'pluginID';
const tablePlugin = createTablePlugin({...PROPS, id});
expect(tablePlugin.id).toBe(id);
});
test('Plugin title is set', () => {
const title = 'My Plugin Title';
const tablePlugin = createTablePlugin({...PROPS, title});
expect(tablePlugin.title).toBe(title);
});
test('Plugin icon is set', () => {
const icon = 'icon';
const tablePlugin = createTablePlugin({...PROPS, icon});
expect(tablePlugin.icon).toBe(icon);
});
test('persistedStateReducer is resetting data', () => {
const resetMethod = 'resetMethod';
const tablePlugin = createTablePlugin({...PROPS, resetMethod});

View File

@@ -146,11 +146,11 @@ class PluginSidebarListItem extends Component<{
<ListItem active={isActive} onClick={this.props.onClick}>
<PluginIcon
isActive={isActive}
name={plugin.icon}
name={plugin.icon || 'apps'}
backgroundColor={iconColor}
color={colors.white}
/>
<PluginName>{plugin.title}</PluginName>
<PluginName>{plugin.title || plugin.id}</PluginName>
</ListItem>
);
}
@@ -228,7 +228,11 @@ class MainSidebar extends PureComponent<MainSidebarProps> {
}>
<PluginIcon
color={colors.light50}
name={numNotifications > 0 ? NotificationsHub.icon : 'bell-null'}
name={
numNotifications > 0
? NotificationsHub.icon || 'bell'
: 'bell-null'
}
isActive={selectedPlugin === NotificationsHub.id}
/>
<PluginName

View File

@@ -22,17 +22,14 @@ type RowData = {
id: ID,
};
type Props<T> = {|
title: string,
id: string,
icon: string,
type Props<T> = {
method: string,
resetMethod?: string,
columns: TableColumns,
columnSizes: TableColumnSizes,
renderSidebar: (row: T) => any,
buildRow: (row: T) => any,
|};
};
type PersistedState<T> = {|
rows: TableRows,
@@ -59,9 +56,6 @@ type State = {|
export function createTablePlugin<T: RowData>(props: Props<T>) {
// $FlowFixMe persistedStateReducer is fine to accept payload of type T, because it is of type RowData
return class extends FlipperPlugin<State, *, PersistedState<T>> {
static title = props.title;
static id = props.id;
static icon = props.icon;
static keyboardActions = ['clear', 'createPaste'];
static defaultPersistedState: PersistedState<T> = {
@@ -170,7 +164,7 @@ export function createTablePlugin<T: RowData>(props: Props<T>) {
return (
<FlexColumn grow={true}>
<SearchableTable
key={props.id}
key={this.constructor.id}
rowLineHeight={28}
floating={false}
multiline={true}

View File

@@ -7,4 +7,6 @@
import {FlipperPlugin} from '../../plugin.js';
export default class extends FlipperPlugin {}
export default class extends FlipperPlugin {
static id = 'Static ID';
}

View File

@@ -18,6 +18,7 @@ import reducers from '../../reducers/index.js';
import Logger from '../../fb-stubs/Logger.js';
import configureStore from 'redux-mock-store';
import {TEST_PASSING_GK, TEST_FAILING_GK} from '../../fb-stubs/GK';
import TestPlugin from './TestPlugin';
const mockStore = configureStore([])(reducers(undefined, {type: 'INIT'}));
const logger = new Logger();
@@ -111,11 +112,17 @@ test('requirePlugin returns null for invalid requires', () => {
});
test('requirePlugin loads plugin', () => {
const name = 'pluginID';
const homepage = 'https://fb.workplace.com/groups/230455004101832/';
const plugin = requirePlugin(require)({
name: 'pluginID',
name,
homepage,
out: path.join(__dirname, 'TestPlugin.js'),
// $FlowFixMe Electron require returns default exports wrapped in an object
}).default;
});
// $FlowFixMe
expect(plugin.prototype).toBeInstanceOf(FlipperPlugin);
// $FlowFixMe
expect(plugin.homepage).toBe(homepage);
// $FlowFixMe
expect(plugin.id).toBe(TestPlugin.id);
});

View File

@@ -32,6 +32,7 @@ export default (store: Store, logger: Logger) => {
window.Flipper = Flipper;
const disabled = checkDisabled();
const initialPlugins: Array<
Class<FlipperPlugin<> | FlipperDevicePlugin<>>,
> = [...getBundledPlugins(), ...getDynamicPlugins()]
@@ -113,10 +114,27 @@ export function requirePlugin(
pluginDefinition: PluginDefinition,
): ?Class<FlipperPlugin<> | FlipperDevicePlugin<>> => {
try {
const plugin = requireFunction(pluginDefinition.out);
let plugin = requireFunction(pluginDefinition.out);
if (plugin.default) {
plugin = plugin.default;
}
if (!plugin.prototype instanceof FlipperBasePlugin) {
throw new Error(`Plugin ${plugin.name} is not a FlipperBasePlugin`);
}
// set values from package.json as static variables on class
Object.keys(pluginDefinition).forEach(key => {
if (key === 'name') {
plugin.id = plugin.id || pluginDefinition.name;
} else if (key === 'id') {
throw new Error(
'Field "id" not allowed in package.json. The plugin\'s name will be used as ID"',
);
} else {
plugin[key] = plugin[key] || pluginDefinition[key];
}
});
return plugin;
} catch (e) {
console.error(pluginDefinition, e);

View File

@@ -49,9 +49,13 @@ export class FlipperBasePlugin<
Actions = *,
PersistedState = *,
> extends React.Component<Props<PersistedState>, State> {
static title: string = 'Unknown';
static id: string = 'Unknown';
static icon: string = 'apps';
static title: ?string = null;
static id: string = '';
static icon: ?string = null;
static bugs: ?{
email?: string,
url?: string,
} = null;
static keyboardActions: ?KeyboardActions;
static screenshot: ?string;
static defaultPersistedState: PersistedState;
@@ -78,7 +82,7 @@ export class FlipperBasePlugin<
onKeyboardAction: ?(action: string) => void;
toJSON() {
return `<${this.constructor.name}#${this.constructor.title}>`;
return `<${this.constructor.name}#${this.constructor.id}>`;
}
// methods to be overriden by plugins