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:
committed by
Facebook Github Bot
parent
e23da69db9
commit
6ffc027051
@@ -472,7 +472,7 @@ class NotificationItem extends Component<ItemProps, ItemState> {
|
|||||||
const items = [];
|
const items = [];
|
||||||
if (props.onHidePlugin && props.plugin) {
|
if (props.onHidePlugin && props.plugin) {
|
||||||
items.push({
|
items.push({
|
||||||
label: `Hide ${props.plugin.title} plugin`,
|
label: `Hide ${props.plugin.title || props.plugin.id} plugin`,
|
||||||
click: this.props.onHidePlugin,
|
click: this.props.onHidePlugin,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,9 +120,8 @@ class PluginContainer extends PureComponent<Props> {
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Container key="plugin">
|
<Container key="plugin">
|
||||||
<ErrorBoundary
|
<ErrorBoundary
|
||||||
heading={`Plugin "${
|
heading={`Plugin "${activePlugin.title ||
|
||||||
activePlugin.title
|
'Unknown'}" encountered an error during render`}
|
||||||
}" encountered an error during render`}
|
|
||||||
logger={this.props.logger}>
|
logger={this.props.logger}>
|
||||||
{React.createElement(activePlugin, props)}
|
{React.createElement(activePlugin, props)}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ import {createTablePlugin} from '../createTablePlugin.js';
|
|||||||
import {FlipperPlugin} from '../plugin.js';
|
import {FlipperPlugin} from '../plugin.js';
|
||||||
|
|
||||||
const PROPS = {
|
const PROPS = {
|
||||||
title: 'Plugin Title',
|
|
||||||
id: 'pluginID',
|
|
||||||
icon: 'icon',
|
|
||||||
method: 'method',
|
method: 'method',
|
||||||
resetMethod: 'resetMethod',
|
resetMethod: 'resetMethod',
|
||||||
columns: {},
|
columns: {},
|
||||||
@@ -25,24 +22,6 @@ test('createTablePlugin returns FlipperPlugin', () => {
|
|||||||
expect(tablePlugin.prototype).toBeInstanceOf(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', () => {
|
test('persistedStateReducer is resetting data', () => {
|
||||||
const resetMethod = 'resetMethod';
|
const resetMethod = 'resetMethod';
|
||||||
const tablePlugin = createTablePlugin({...PROPS, resetMethod});
|
const tablePlugin = createTablePlugin({...PROPS, resetMethod});
|
||||||
|
|||||||
@@ -146,11 +146,11 @@ class PluginSidebarListItem extends Component<{
|
|||||||
<ListItem active={isActive} onClick={this.props.onClick}>
|
<ListItem active={isActive} onClick={this.props.onClick}>
|
||||||
<PluginIcon
|
<PluginIcon
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
name={plugin.icon}
|
name={plugin.icon || 'apps'}
|
||||||
backgroundColor={iconColor}
|
backgroundColor={iconColor}
|
||||||
color={colors.white}
|
color={colors.white}
|
||||||
/>
|
/>
|
||||||
<PluginName>{plugin.title}</PluginName>
|
<PluginName>{plugin.title || plugin.id}</PluginName>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,11 @@ class MainSidebar extends PureComponent<MainSidebarProps> {
|
|||||||
}>
|
}>
|
||||||
<PluginIcon
|
<PluginIcon
|
||||||
color={colors.light50}
|
color={colors.light50}
|
||||||
name={numNotifications > 0 ? NotificationsHub.icon : 'bell-null'}
|
name={
|
||||||
|
numNotifications > 0
|
||||||
|
? NotificationsHub.icon || 'bell'
|
||||||
|
: 'bell-null'
|
||||||
|
}
|
||||||
isActive={selectedPlugin === NotificationsHub.id}
|
isActive={selectedPlugin === NotificationsHub.id}
|
||||||
/>
|
/>
|
||||||
<PluginName
|
<PluginName
|
||||||
|
|||||||
@@ -22,17 +22,14 @@ type RowData = {
|
|||||||
id: ID,
|
id: ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props<T> = {|
|
type Props<T> = {
|
||||||
title: string,
|
|
||||||
id: string,
|
|
||||||
icon: string,
|
|
||||||
method: string,
|
method: string,
|
||||||
resetMethod?: string,
|
resetMethod?: string,
|
||||||
columns: TableColumns,
|
columns: TableColumns,
|
||||||
columnSizes: TableColumnSizes,
|
columnSizes: TableColumnSizes,
|
||||||
renderSidebar: (row: T) => any,
|
renderSidebar: (row: T) => any,
|
||||||
buildRow: (row: T) => any,
|
buildRow: (row: T) => any,
|
||||||
|};
|
};
|
||||||
|
|
||||||
type PersistedState<T> = {|
|
type PersistedState<T> = {|
|
||||||
rows: TableRows,
|
rows: TableRows,
|
||||||
@@ -59,9 +56,6 @@ type State = {|
|
|||||||
export function createTablePlugin<T: RowData>(props: Props<T>) {
|
export function createTablePlugin<T: RowData>(props: Props<T>) {
|
||||||
// $FlowFixMe persistedStateReducer is fine to accept payload of type T, because it is of type RowData
|
// $FlowFixMe persistedStateReducer is fine to accept payload of type T, because it is of type RowData
|
||||||
return class extends FlipperPlugin<State, *, PersistedState<T>> {
|
return class extends FlipperPlugin<State, *, PersistedState<T>> {
|
||||||
static title = props.title;
|
|
||||||
static id = props.id;
|
|
||||||
static icon = props.icon;
|
|
||||||
static keyboardActions = ['clear', 'createPaste'];
|
static keyboardActions = ['clear', 'createPaste'];
|
||||||
|
|
||||||
static defaultPersistedState: PersistedState<T> = {
|
static defaultPersistedState: PersistedState<T> = {
|
||||||
@@ -170,7 +164,7 @@ export function createTablePlugin<T: RowData>(props: Props<T>) {
|
|||||||
return (
|
return (
|
||||||
<FlexColumn grow={true}>
|
<FlexColumn grow={true}>
|
||||||
<SearchableTable
|
<SearchableTable
|
||||||
key={props.id}
|
key={this.constructor.id}
|
||||||
rowLineHeight={28}
|
rowLineHeight={28}
|
||||||
floating={false}
|
floating={false}
|
||||||
multiline={true}
|
multiline={true}
|
||||||
|
|||||||
@@ -7,4 +7,6 @@
|
|||||||
|
|
||||||
import {FlipperPlugin} from '../../plugin.js';
|
import {FlipperPlugin} from '../../plugin.js';
|
||||||
|
|
||||||
export default class extends FlipperPlugin {}
|
export default class extends FlipperPlugin {
|
||||||
|
static id = 'Static ID';
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import reducers from '../../reducers/index.js';
|
|||||||
import Logger from '../../fb-stubs/Logger.js';
|
import Logger from '../../fb-stubs/Logger.js';
|
||||||
import configureStore from 'redux-mock-store';
|
import configureStore from 'redux-mock-store';
|
||||||
import {TEST_PASSING_GK, TEST_FAILING_GK} from '../../fb-stubs/GK';
|
import {TEST_PASSING_GK, TEST_FAILING_GK} from '../../fb-stubs/GK';
|
||||||
|
import TestPlugin from './TestPlugin';
|
||||||
|
|
||||||
const mockStore = configureStore([])(reducers(undefined, {type: 'INIT'}));
|
const mockStore = configureStore([])(reducers(undefined, {type: 'INIT'}));
|
||||||
const logger = new Logger();
|
const logger = new Logger();
|
||||||
@@ -111,11 +112,17 @@ test('requirePlugin returns null for invalid requires', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('requirePlugin loads plugin', () => {
|
test('requirePlugin loads plugin', () => {
|
||||||
|
const name = 'pluginID';
|
||||||
|
const homepage = 'https://fb.workplace.com/groups/230455004101832/';
|
||||||
const plugin = requirePlugin(require)({
|
const plugin = requirePlugin(require)({
|
||||||
name: 'pluginID',
|
name,
|
||||||
|
homepage,
|
||||||
out: path.join(__dirname, 'TestPlugin.js'),
|
out: path.join(__dirname, 'TestPlugin.js'),
|
||||||
// $FlowFixMe Electron require returns default exports wrapped in an object
|
});
|
||||||
}).default;
|
// $FlowFixMe
|
||||||
|
expect(plugin.prototype).toBeInstanceOf(FlipperPlugin);
|
||||||
expect(plugin.prototype).toBeInstanceOf(FlipperPlugin);
|
// $FlowFixMe
|
||||||
|
expect(plugin.homepage).toBe(homepage);
|
||||||
|
// $FlowFixMe
|
||||||
|
expect(plugin.id).toBe(TestPlugin.id);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export default (store: Store, logger: Logger) => {
|
|||||||
window.Flipper = Flipper;
|
window.Flipper = Flipper;
|
||||||
|
|
||||||
const disabled = checkDisabled();
|
const disabled = checkDisabled();
|
||||||
|
|
||||||
const initialPlugins: Array<
|
const initialPlugins: Array<
|
||||||
Class<FlipperPlugin<> | FlipperDevicePlugin<>>,
|
Class<FlipperPlugin<> | FlipperDevicePlugin<>>,
|
||||||
> = [...getBundledPlugins(), ...getDynamicPlugins()]
|
> = [...getBundledPlugins(), ...getDynamicPlugins()]
|
||||||
@@ -113,10 +114,27 @@ export function requirePlugin(
|
|||||||
pluginDefinition: PluginDefinition,
|
pluginDefinition: PluginDefinition,
|
||||||
): ?Class<FlipperPlugin<> | FlipperDevicePlugin<>> => {
|
): ?Class<FlipperPlugin<> | FlipperDevicePlugin<>> => {
|
||||||
try {
|
try {
|
||||||
const plugin = requireFunction(pluginDefinition.out);
|
let plugin = requireFunction(pluginDefinition.out);
|
||||||
|
if (plugin.default) {
|
||||||
|
plugin = plugin.default;
|
||||||
|
}
|
||||||
if (!plugin.prototype instanceof FlipperBasePlugin) {
|
if (!plugin.prototype instanceof FlipperBasePlugin) {
|
||||||
throw new Error(`Plugin ${plugin.name} is not a 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;
|
return plugin;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(pluginDefinition, e);
|
console.error(pluginDefinition, e);
|
||||||
|
|||||||
@@ -49,9 +49,13 @@ export class FlipperBasePlugin<
|
|||||||
Actions = *,
|
Actions = *,
|
||||||
PersistedState = *,
|
PersistedState = *,
|
||||||
> extends React.Component<Props<PersistedState>, State> {
|
> extends React.Component<Props<PersistedState>, State> {
|
||||||
static title: string = 'Unknown';
|
static title: ?string = null;
|
||||||
static id: string = 'Unknown';
|
static id: string = '';
|
||||||
static icon: string = 'apps';
|
static icon: ?string = null;
|
||||||
|
static bugs: ?{
|
||||||
|
email?: string,
|
||||||
|
url?: string,
|
||||||
|
} = null;
|
||||||
static keyboardActions: ?KeyboardActions;
|
static keyboardActions: ?KeyboardActions;
|
||||||
static screenshot: ?string;
|
static screenshot: ?string;
|
||||||
static defaultPersistedState: PersistedState;
|
static defaultPersistedState: PersistedState;
|
||||||
@@ -78,7 +82,7 @@ export class FlipperBasePlugin<
|
|||||||
onKeyboardAction: ?(action: string) => void;
|
onKeyboardAction: ?(action: string) => void;
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
return `<${this.constructor.name}#${this.constructor.title}>`;
|
return `<${this.constructor.name}#${this.constructor.id}>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// methods to be overriden by plugins
|
// methods to be overriden by plugins
|
||||||
|
|||||||
Reference in New Issue
Block a user