UNPKG

react-native-flipper

Version:

Flipper bindings for React Native

167 lines (142 loc) 4.04 kB
/** * Copyright (c) Meta Platforms, Inc. and 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 (!Flipper) { printNoFlipperWarning(); return; } 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, status => { if (status === 'noflipper') { printNoFlipperWarning(); } }); } function printNoFlipperWarning() { // $FlowFixMe if (typeof __DEV__ !== 'undefined' && __DEV__) { console.warn( 'The native module for Flipper seems unavailable. Please verify that `react-native-flipper` is installed as yarn dependency to your project and, for iOS, that `pod install` is run in the `ios` directory.', ); } } if (Flipper) { startEventListeners(); }