Summary: Added some assertions and string casts to make plugins a bit more robust Reviewed By: passy Differential Revision: D19427909 fbshipit-source-id: 46a3138805db865b538f745fae25ce1897e35736
148 lines
3.4 KiB
JavaScript
148 lines
3.4 KiB
JavaScript
/**
|
|
* 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
|
|
*/
|
|
|
|
// $FlowFixMe
|
|
import {NativeModules, NativeEventEmitter} from 'react-native';
|
|
|
|
const {Flipper} = NativeModules;
|
|
|
|
export default Flipper;
|
|
|
|
const listeners = {}; // plugin#method -> callback
|
|
const plugins = {}; // plugin -> Plugin
|
|
|
|
function assertSerializable(data) {
|
|
if (
|
|
data === undefined ||
|
|
Array.isArray(data) ||
|
|
(data && typeof data === 'object')
|
|
) {
|
|
return true;
|
|
}
|
|
throw new Error(
|
|
'Flipper: Expected serializable (undefined, an array or an object). Got: ' +
|
|
data,
|
|
);
|
|
}
|
|
|
|
class Connection {
|
|
connected;
|
|
pluginId;
|
|
|
|
constructor(pluginId) {
|
|
this.connected = false;
|
|
this.pluginId = pluginId;
|
|
}
|
|
|
|
send(method, data) {
|
|
if (!this.connected) {
|
|
throw new Error('Cannot send data, not connected');
|
|
}
|
|
assertSerializable(data);
|
|
Flipper.send(this.pluginId, method, JSON.stringify(data));
|
|
}
|
|
|
|
reportErrorWithMetadata(reason, stackTrace) {
|
|
Flipper.reportErrorWithMetadata(
|
|
this.pluginId,
|
|
'' + reason,
|
|
'' + stackTrace,
|
|
);
|
|
}
|
|
|
|
reportError(error) {
|
|
Flipper.reportError(this.pluginId, '' + error);
|
|
}
|
|
|
|
receive(method, listener) {
|
|
if (!this.connected) {
|
|
throw new Error('Cannot receive data, not connected');
|
|
}
|
|
|
|
listeners[this.pluginId + '#' + method] = listener;
|
|
Flipper.subscribe(this.pluginId, method);
|
|
}
|
|
}
|
|
|
|
class Responder {
|
|
responderId;
|
|
|
|
constructor(responderId) {
|
|
this.responderId = responderId;
|
|
}
|
|
|
|
success(response) {
|
|
assertSerializable(response);
|
|
Flipper.respondSuccess(
|
|
this.responderId,
|
|
response == null ? null : JSON.stringify(response),
|
|
);
|
|
}
|
|
|
|
error(response) {
|
|
assertSerializable(response);
|
|
Flipper.respondError(this.responderId, JSON.stringify(response));
|
|
}
|
|
}
|
|
|
|
function startEventListeners() {
|
|
const emitter = new NativeEventEmitter(Flipper);
|
|
|
|
emitter.addListener('react-native-flipper-plugin-connect', event => {
|
|
const {plugin} = event;
|
|
if (plugins[plugin]) {
|
|
const p = plugins[plugin];
|
|
p._connection.connected = true;
|
|
p.onConnect(p._connection);
|
|
}
|
|
});
|
|
|
|
emitter.addListener('react-native-flipper-plugin-disconnect', event => {
|
|
const {plugin} = event;
|
|
if (plugins[plugin]) {
|
|
const p = plugins[plugin];
|
|
p._connection.connected = false;
|
|
p.onDisconnect();
|
|
}
|
|
});
|
|
|
|
emitter.addListener('react-native-flipper-receive-event', event => {
|
|
const {plugin, method, params, responderId} = event;
|
|
const key = plugin + '#' + method;
|
|
if (listeners[key]) {
|
|
const responder =
|
|
responderId != null ? new Responder(responderId) : undefined;
|
|
listeners[key](JSON.parse(params), responder);
|
|
}
|
|
});
|
|
}
|
|
|
|
// $FlowFixMe
|
|
export function addPlugin(plugin) {
|
|
if (!plugin || typeof plugin !== 'object') {
|
|
throw new Error('Expected plugin, got ' + plugin);
|
|
}
|
|
['getId', 'onConnect', 'onDisconnect'].forEach(method => {
|
|
if (typeof plugin[method] !== 'function') {
|
|
throw new Error(`Plugin misses an implementation for '${method}'`);
|
|
}
|
|
});
|
|
const runInBackground =
|
|
typeof plugin.runInBackground === 'function'
|
|
? !!plugin.runInBackground()
|
|
: false;
|
|
const id = plugin.getId();
|
|
plugin._connection = new Connection(id);
|
|
plugins[id] = plugin;
|
|
|
|
Flipper.registerPlugin(id, runInBackground);
|
|
}
|
|
|
|
startEventListeners();
|