Add headless option to dump on disconnect
Summary: This adds an optional exit strategy that reacts to the client disconnecting rather than a `SIGINT` which can be used for integration tests. `MiddlewareAPI` is a subset of `Store` and required to work here. Annoyingly, it's not quite clear to me why this does not work as part of an event loop cycle and requires a `setTimeout`. This doesn't have any negative effects and works in the same way that the SIGINT interruption works, but it's a bit of an eyesore. Reviewed By: danielbuechele Differential Revision: D14622111 fbshipit-source-id: e2caca056e478428d977565dc9bc09eefca4230c
This commit is contained in:
committed by
Facebook Github Bot
parent
ddd06971f1
commit
b20d0a4c8b
@@ -5,15 +5,17 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
import {createStore} from 'redux';
|
import {createStore} from 'redux';
|
||||||
import reducers from '../src/reducers/index.js';
|
import {applyMiddleware} from 'redux';
|
||||||
|
import yargs from 'yargs';
|
||||||
|
|
||||||
import dispatcher from '../src/dispatcher/index.js';
|
import dispatcher from '../src/dispatcher/index.js';
|
||||||
import {init as initLogger} from '../src/fb-stubs/Logger.js';
|
import {init as initLogger} from '../src/fb-stubs/Logger.js';
|
||||||
import path from 'path';
|
import reducers from '../src/reducers/index.js';
|
||||||
|
import {exportStore} from '../src/utils/exportData.js';
|
||||||
// $FlowFixMe this file exist, trust me, flow!
|
// $FlowFixMe this file exist, trust me, flow!
|
||||||
import setup from '../static/setup.js';
|
import setup from '../static/setup.js';
|
||||||
import yargs from 'yargs';
|
|
||||||
import {exportStore} from '../src/utils/exportData.js';
|
|
||||||
|
|
||||||
yargs
|
yargs
|
||||||
.usage('$0 [args]')
|
.usage('$0 [args]')
|
||||||
@@ -37,6 +39,11 @@ yargs
|
|||||||
'Enable redux-devtools. Tries to connect to devtools running on port 8181',
|
'Enable redux-devtools. Tries to connect to devtools running on port 8181',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
});
|
});
|
||||||
|
yargs.option('exit', {
|
||||||
|
describe: 'Controls when to exit and dump the store to stdout.',
|
||||||
|
choices: ['sigint', 'disconnect'],
|
||||||
|
default: 'sigint',
|
||||||
|
});
|
||||||
yargs.option('v', {
|
yargs.option('v', {
|
||||||
alias: 'verbose',
|
alias: 'verbose',
|
||||||
default: false,
|
default: false,
|
||||||
@@ -52,6 +59,7 @@ yargs
|
|||||||
function startFlipper({
|
function startFlipper({
|
||||||
dev,
|
dev,
|
||||||
verbose,
|
verbose,
|
||||||
|
exit,
|
||||||
'insecure-port': insecurePort,
|
'insecure-port': insecurePort,
|
||||||
'secure-port': securePort,
|
'secure-port': securePort,
|
||||||
}) {
|
}) {
|
||||||
@@ -85,24 +93,40 @@ function startFlipper({
|
|||||||
process.env.FLIPPER_PORTS = `${insecurePort},${securePort}`;
|
process.env.FLIPPER_PORTS = `${insecurePort},${securePort}`;
|
||||||
|
|
||||||
// needs to be required after WebSocket polyfill is loaded
|
// needs to be required after WebSocket polyfill is loaded
|
||||||
const devToolsEnhancer = require('remote-redux-devtools').default;
|
const devToolsEnhancer = require('remote-redux-devtools');
|
||||||
|
|
||||||
|
const headlessMiddleware = store => next => action => {
|
||||||
|
if (exit == 'disconnect' && action.type == 'CLIENT_REMOVED') {
|
||||||
|
// TODO(T42325892): Investigate why the export stalls without exiting the
|
||||||
|
// current eventloop task here.
|
||||||
|
setTimeout(() => {
|
||||||
|
exportStore(store)
|
||||||
|
.then(output => {
|
||||||
|
originalConsole.log(output);
|
||||||
|
process.exit();
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
return next(action);
|
||||||
|
};
|
||||||
|
|
||||||
setup({});
|
setup({});
|
||||||
const store = dev
|
const store = createStore(
|
||||||
? createStore(
|
reducers,
|
||||||
reducers,
|
devToolsEnhancer.composeWithDevTools(applyMiddleware(headlessMiddleware)),
|
||||||
devToolsEnhancer({realtime: true, hostname: 'localhost', port: 8181}),
|
);
|
||||||
)
|
|
||||||
: createStore(reducers);
|
|
||||||
const logger = initLogger(store, {isHeadless: true});
|
const logger = initLogger(store, {isHeadless: true});
|
||||||
dispatcher(store, logger);
|
dispatcher(store, logger);
|
||||||
|
|
||||||
process.on('SIGINT', async () => {
|
if (exit == 'sigint') {
|
||||||
try {
|
process.on('SIGINT', async () => {
|
||||||
originalConsole.log(await exportStore(store));
|
try {
|
||||||
} catch (e) {
|
originalConsole.log(await exportStore(store));
|
||||||
console.error(e);
|
} catch (e) {
|
||||||
}
|
console.error(e);
|
||||||
process.exit();
|
}
|
||||||
});
|
process.exit();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export {connect} from 'react-redux';
|
|||||||
export {selectPlugin} from './reducers/connections';
|
export {selectPlugin} from './reducers/connections';
|
||||||
export {getPluginKey, getPersistedState} from './utils/pluginUtils.js';
|
export {getPluginKey, getPersistedState} from './utils/pluginUtils.js';
|
||||||
export {default as BaseDevice} from './devices/BaseDevice.js';
|
export {default as BaseDevice} from './devices/BaseDevice.js';
|
||||||
export type {Store} from './reducers/index.js';
|
export type {Store, MiddlewareAPI} from './reducers/index.js';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
default as SidebarExtensions,
|
default as SidebarExtensions,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type {KeyboardActions} from './MenuBar.js';
|
|||||||
import type {App} from './App.js';
|
import type {App} from './App.js';
|
||||||
import type {Logger} from './fb-interfaces/Logger.js';
|
import type {Logger} from './fb-interfaces/Logger.js';
|
||||||
import type Client from './Client.js';
|
import type Client from './Client.js';
|
||||||
import type {Store} from './reducers/index.js';
|
import type {Store, MiddlewareAPI} from './reducers/index.js';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type {Node} from 'react';
|
import type {Node} from 'react';
|
||||||
@@ -86,7 +86,7 @@ export class FlipperBasePlugin<
|
|||||||
static exportPersistedState: ?(
|
static exportPersistedState: ?(
|
||||||
callClient: (string, ?Object) => Promise<Object>,
|
callClient: (string, ?Object) => Promise<Object>,
|
||||||
persistedState: ?PersistedState,
|
persistedState: ?PersistedState,
|
||||||
store: ?Store,
|
store: ?MiddlewareAPI,
|
||||||
) => Promise<?PersistedState>;
|
) => Promise<?PersistedState>;
|
||||||
static getActiveNotifications: ?(
|
static getActiveNotifications: ?(
|
||||||
persistedState: PersistedState,
|
persistedState: PersistedState,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type {
|
|||||||
ElementID,
|
ElementID,
|
||||||
Element,
|
Element,
|
||||||
ElementSearchResultSet,
|
ElementSearchResultSet,
|
||||||
Store,
|
MiddlewareAPI,
|
||||||
PluginClient,
|
PluginClient,
|
||||||
} from 'flipper';
|
} from 'flipper';
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ export default class Layout extends FlipperPlugin<State, void, PersistedState> {
|
|||||||
static exportPersistedState = (
|
static exportPersistedState = (
|
||||||
callClient: (string, ?Object) => Promise<Object>,
|
callClient: (string, ?Object) => Promise<Object>,
|
||||||
persistedState: ?PersistedState,
|
persistedState: ?PersistedState,
|
||||||
store: ?Store,
|
store: ?MiddlewareAPI,
|
||||||
): Promise<?PersistedState> => {
|
): Promise<?PersistedState> => {
|
||||||
const defaultPromise = Promise.resolve(persistedState);
|
const defaultPromise = Promise.resolve(persistedState);
|
||||||
if (!store) {
|
if (!store) {
|
||||||
|
|||||||
@@ -37,7 +37,10 @@ import type {
|
|||||||
Action as PluginsAction,
|
Action as PluginsAction,
|
||||||
} from './plugins.js';
|
} from './plugins.js';
|
||||||
import type {State as UserState, Action as UserAction} from './user.js';
|
import type {State as UserState, Action as UserAction} from './user.js';
|
||||||
import type {Store as ReduxStore} from 'redux';
|
import type {
|
||||||
|
Store as ReduxStore,
|
||||||
|
MiddlewareAPI as ReduxMiddlewareAPI,
|
||||||
|
} from 'redux';
|
||||||
|
|
||||||
type Actions =
|
type Actions =
|
||||||
| ApplicationAction
|
| ApplicationAction
|
||||||
@@ -58,6 +61,7 @@ export type State = {|
|
|||||||
|};
|
|};
|
||||||
|
|
||||||
export type Store = ReduxStore<State, Actions>;
|
export type Store = ReduxStore<State, Actions>;
|
||||||
|
export type MiddlewareAPI = ReduxMiddlewareAPI<State, Actions>;
|
||||||
|
|
||||||
export default combineReducers<_, Actions>({
|
export default combineReducers<_, Actions>({
|
||||||
application,
|
application,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
import {getInstance as getLogger} from '../fb-stubs/Logger';
|
import {getInstance as getLogger} from '../fb-stubs/Logger';
|
||||||
import type {Store} from '../reducers';
|
import type {Store, MiddlewareAPI} from '../reducers';
|
||||||
import type {DeviceExport} from '../devices/BaseDevice';
|
import type {DeviceExport} from '../devices/BaseDevice';
|
||||||
import type {State as PluginStates} from '../reducers/pluginStates';
|
import type {State as PluginStates} from '../reducers/pluginStates';
|
||||||
import type {PluginNotification} from '../reducers/notifications.js';
|
import type {PluginNotification} from '../reducers/notifications.js';
|
||||||
@@ -183,7 +183,9 @@ export const processStore = async (
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getStoreExport(store: Store): Promise<?ExportType> {
|
export async function getStoreExport(
|
||||||
|
store: MiddlewareAPI,
|
||||||
|
): Promise<?ExportType> {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const {clients} = state.connections;
|
const {clients} = state.connections;
|
||||||
const {pluginStates} = state;
|
const {pluginStates} = state;
|
||||||
@@ -234,7 +236,7 @@ export async function getStoreExport(store: Store): Promise<?ExportType> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exportStore(store: Store): Promise<string> {
|
export function exportStore(store: MiddlewareAPI): Promise<string> {
|
||||||
getLogger().track('usage', EXPORT_FLIPPER_TRACE_EVENT);
|
getLogger().track('usage', EXPORT_FLIPPER_TRACE_EVENT);
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const storeExport = await getStoreExport(store);
|
const storeExport = await getStoreExport(store);
|
||||||
|
|||||||
Reference in New Issue
Block a user