Introduce types for Sandy plugins through code base
Summary:
So far there were 2 types of plugins: `FlipperPlugin` and `FlipperDevicePlugin`. This introduces a third kind: `SandyPluginDefinition`.
Unlike with the old plugins, the export of the module is not directly exposed as the plugin definition. Rather, we use class `SandyPluginDefinition` (instance) that holds a loaded definition and its meta data separately (`PluginDetails`). This means that we don't have to mix in and mutate loaded definitions, and that for unit tests we can avoid needing to provide a bunch of meta data. This also prevents a bunch of meta data existing on two places: on the loaded classes as static fields, and in the meta data field of the loaded class as well. Finally, we can now freely extends the `PluginDetails` interface in flipper, without needing to store it on the loaded classes and we are sure that no naming conflicts are caused by this in the future.
For compatibility with the existing code base, common fields are delegated from the `SandyPluginDefinition` class to the meta data.
Also cleaned up types around plugins a little bit and removed some unnecessary casts.
For all features that reason about plugins in general (such as exports), sandy plugins are ignored for now.
`SandyPluginInstance` is worked out in further diffs
The `instanceof` calls are replaced by a utility function in later diffs.
{F241363645}
Reviewed By: jknoxville
Differential Revision: D22091432
fbshipit-source-id: 3aa6b12fda5925268913779f3c3c9e84494438f8
This commit is contained in:
committed by
Facebook GitHub Bot
parent
845c9b67f5
commit
1029a6c97c
133
desktop/flipper-plugin/src/plugin/Plugin.tsx
Normal file
133
desktop/flipper-plugin/src/plugin/Plugin.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* 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 {PluginDetails} from 'flipper-plugin-lib';
|
||||
|
||||
type EventsContract = Record<string, any>;
|
||||
type MethodsContract = Record<string, (params: any) => Promise<any>>;
|
||||
|
||||
/**
|
||||
* API available to a plugin factory
|
||||
*/
|
||||
export interface FlipperClient<
|
||||
Events extends EventsContract,
|
||||
Methods extends MethodsContract
|
||||
> {}
|
||||
|
||||
/**
|
||||
* Internal API exposed by Flipper, and wrapped by FlipperPluginInstance to be passed to the
|
||||
* Plugin Factory
|
||||
*/
|
||||
interface RealFlipperClient {}
|
||||
|
||||
export type FlipperPluginFactory<
|
||||
Events extends EventsContract,
|
||||
Methods extends MethodsContract
|
||||
> = (client: FlipperClient<Events, Methods>) => object;
|
||||
|
||||
export type FlipperPluginComponent = React.FC<{}>;
|
||||
|
||||
export type FlipperPluginModule = {
|
||||
/** the factory function that initializes a plugin instance */
|
||||
plugin: FlipperPluginFactory<any, any>;
|
||||
/** the component type that can render this plugin */
|
||||
Component: FlipperPluginComponent;
|
||||
// TODO: support device plugins T68738317
|
||||
// devicePlugin: FlipperPluginFactory
|
||||
};
|
||||
|
||||
export class FlipperPluginInstance {
|
||||
/** base client provided by Flipper */
|
||||
realClient: RealFlipperClient;
|
||||
/** client that is bound to this instance */
|
||||
client: FlipperClient<any, any>;
|
||||
/** the original plugin definition */
|
||||
definition: FlipperPluginModule;
|
||||
/** the plugin instance api as used inside components and such */
|
||||
instanceApi: object;
|
||||
|
||||
constructor(realClient: RealFlipperClient, definition: FlipperPluginModule) {
|
||||
this.realClient = realClient;
|
||||
this.definition = definition;
|
||||
this.client = {};
|
||||
this.instanceApi = definition.plugin(this.client);
|
||||
}
|
||||
|
||||
deactivate() {
|
||||
// TODO:
|
||||
}
|
||||
}
|
||||
|
||||
export class SandyPluginDefinition {
|
||||
id: string;
|
||||
module: FlipperPluginModule;
|
||||
details: PluginDetails;
|
||||
|
||||
// TODO: Implement T68683449
|
||||
exportPersistedState:
|
||||
| ((
|
||||
callClient: (method: string, params?: any) => Promise<any>,
|
||||
persistedState: any, // TODO: type StaticPersistedState | undefined,
|
||||
store: any, // TODO: ReduxState | undefined,
|
||||
idler?: any, // TODO: Idler,
|
||||
statusUpdate?: (msg: string) => void,
|
||||
supportsMethod?: (method: string) => Promise<boolean>,
|
||||
) => Promise<any /* TODO: StaticPersistedState | undefined */>)
|
||||
| undefined = undefined;
|
||||
|
||||
constructor(details: PluginDetails, module: FlipperPluginModule) {
|
||||
this.id = details.id;
|
||||
this.details = details;
|
||||
if (!module.plugin || typeof module.plugin !== 'function') {
|
||||
throw new Error(
|
||||
`Sandy plugin ${this.id} doesn't export a named function called 'plugin'`,
|
||||
);
|
||||
}
|
||||
if (!module.Component || typeof module.Component !== 'function') {
|
||||
throw new Error(
|
||||
`Sandy plugin ${this.id} doesn't export a named function called 'Component'`,
|
||||
);
|
||||
}
|
||||
this.module = module;
|
||||
this.module.Component.displayName = `FlipperPlugin(${this.id})`;
|
||||
}
|
||||
|
||||
get packageName() {
|
||||
return this.details.name;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this.details.title;
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return this.details.icon;
|
||||
}
|
||||
|
||||
get category() {
|
||||
return this.details.category;
|
||||
}
|
||||
|
||||
get gatekeeper() {
|
||||
return this.details.gatekeeper;
|
||||
}
|
||||
|
||||
get version() {
|
||||
return this.details.version;
|
||||
}
|
||||
|
||||
get isDefault() {
|
||||
return this.details.isDefault;
|
||||
}
|
||||
|
||||
get keyboardActions() {
|
||||
// TODO: T68882551 support keyboard actions
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user