diff --git a/app/worker/devMenu.js b/app/worker/devMenu.js index caa5f357..71955ccb 100644 --- a/app/worker/devMenu.js +++ b/app/worker/devMenu.js @@ -1,16 +1,13 @@ /* eslint-disable no-underscore-dangle */ -import { avoidWarnForRequire } from './utils'; import { toggleNetworkInspect } from './networkInspect'; let availableDevMenuMethods = {}; -export const checkAvailableDevMenuMethods = async (enableNetworkInspect = false) => { - const done = await avoidWarnForRequire('NativeModules', 'AsyncStorage'); - const NativeModules = window.__DEV__ ? window.require('NativeModules') : {}; - const AsyncStorage = window.__DEV__ ? window.require('AsyncStorage') : {}; - done(); - +export const checkAvailableDevMenuMethods = async ( + { NativeModules, AsyncStorage }, + enableNetworkInspect = false +) => { // RN 0.43 use DevSettings, DevMenu will be deprecated const DevSettings = NativeModules.DevSettings || NativeModules.DevMenu; // Currently `show dev menu` is only on DevMenu @@ -23,7 +20,7 @@ export const checkAvailableDevMenuMethods = async (enableNetworkInspect = false) ...DevSettings, show: showDevMenu, networkInspect: toggleNetworkInspect, - clearAsyncStorage: () => AsyncStorage.clear().catch(f => f), + clearAsyncStorage: AsyncStorage.clear ? () => AsyncStorage.clear().catch(f => f) : undefined, }; const result = Object.keys(methods).filter(key => !!methods[key]); availableDevMenuMethods = methods; diff --git a/app/worker/index.js b/app/worker/index.js index cd8b12e2..ae713ae1 100644 --- a/app/worker/index.js +++ b/app/worker/index.js @@ -15,6 +15,7 @@ import { checkAvailableDevMenuMethods, invokeDevMenuMethod } from './devMenu'; import { reportDefaultReactDevToolsPort } from './reactDevTools'; import devToolsEnhancer, { composeWithDevTools } from './reduxAPI'; import * as RemoteDev from './remotedev'; +import { getRequiredModules, ignoreRNDIntervalSpy } from './utils'; /* eslint-disable no-underscore-dangle */ self.__REMOTEDEV__ = RemoteDev; @@ -34,14 +35,15 @@ const setupRNDebuggerBeforeImportScript = message => { self.__REACT_DEVTOOLS_PORT__ = message.reactDevToolsPort; }; -const setupRNDebugger = message => { +const setupRNDebugger = async message => { // We need to regularly update JS runtime // because the changes of worker message (Redux DevTools, DevMenu) // doesn't notify to the remote JS runtime self.__RND_INTERVAL__ = setInterval(function() {}, 100); // eslint-disable-line - - checkAvailableDevMenuMethods(message.networkInspect); - reportDefaultReactDevToolsPort(); + const modules = await getRequiredModules(); + ignoreRNDIntervalSpy(modules); + checkAvailableDevMenuMethods(modules, message.networkInspect); + reportDefaultReactDevToolsPort(modules); }; const messageHandlers = { diff --git a/app/worker/reactDevTools.js b/app/worker/reactDevTools.js index d57b680e..43bb7762 100644 --- a/app/worker/reactDevTools.js +++ b/app/worker/reactDevTools.js @@ -1,7 +1,5 @@ /* eslint-disable no-underscore-dangle */ -import { avoidWarnForRequire } from './utils'; - const methodGlobalName = '__REPORT_REACT_DEVTOOLS_PORT__'; const reportReactDevToolsPort = (port, platform) => @@ -10,11 +8,8 @@ const reportReactDevToolsPort = (port, platform) => platform, }); -export const reportDefaultReactDevToolsPort = async () => { - const done = await avoidWarnForRequire('setupDevtools', 'Platform'); - const setupDevtools = window.__DEV__ ? window.require('setupDevtools') : undefined; - const Platform = window.__DEV__ ? window.require('Platform') : {}; - done(); +export const reportDefaultReactDevToolsPort = async ({ setupDevtools, Platform }) => { + if (Platform.__empty) return; /* * [Fallback] React Native version under 0.39 can't specified the port */ diff --git a/app/worker/utils.js b/app/worker/utils.js index b3c668d7..27a4a5fe 100644 --- a/app/worker/utils.js +++ b/app/worker/utils.js @@ -1,5 +1,7 @@ +/* eslint-disable no-underscore-dangle */ + // Avoid warning of use `window.require` on dev mode -export const avoidWarnForRequire = (...moduleNames) => { +const avoidWarnForRequire = moduleNames => { if (!moduleNames.length) moduleNames.push('NativeModules'); return new Promise(resolve => setTimeout(() => { @@ -20,3 +22,60 @@ export const avoidWarnForRequire = (...moduleNames) => { }) ); }; + +const requiredModules = [ + 'MessageQueue', + 'NativeModules', + 'AsyncStorage', + 'Platform', + 'setupDevtools', +]; +export const getRequiredModules = async () => { + const done = await avoidWarnForRequire(requiredModules); + const modules = {}; + requiredModules.forEach( + name => (modules[name] = window.__DEV__ ? window.require(name) : { __empty: true }) + ); + done(); + return modules; +}; + +const TO_JS = 0; +const isRNDInterval = info => + info.type === TO_JS && + info.module === 'JSTimersExecution' && + info.method === 'callTimers' && + info.args && + info.args[0] && + info.args[0][0] === self.__RND_INTERVAL__; + +export const ignoreRNDIntervalSpy = async ({ MessageQueue }) => { + if (MessageQueue.__empty) return; + // Wrap spy function if it already set + if (MessageQueue.prototype.__spy) { + const originalSpyFn = MessageQueue.prototype.__spy; + MessageQueue.prototype.__spy = info => { + if (isRNDInterval(info)) return; + return originalSpyFn(info); + }; + } + MessageQueue.spy = spyOrToggle => { + if (spyOrToggle === true) { + MessageQueue.prototype.__spy = info => { + if (isRNDInterval(info)) return; + console.log( + `${info.type === TO_JS ? 'N->JS' : 'JS->N'} : ` + + `${info.module ? `${info.module}.` : ''}${info.method}` + + `(${JSON.stringify(info.args)})` + ); + }; + } else if (spyOrToggle === false) { + MessageQueue.prototype.__spy = null; + } else { + MessageQueue.prototype.__spy = info => { + if (isRNDInterval(info)) return; + return spyOrToggle(info); + }; + } + }; +}; diff --git a/test/e2e/fixture/setup.js b/test/e2e/fixture/setup.js index 25e86463..162953b5 100644 --- a/test/e2e/fixture/setup.js +++ b/test/e2e/fixture/setup.js @@ -10,6 +10,10 @@ const requiredModules = { Platform: {}, setupDevtools: undefined, AsyncStorage: {}, + MessageQueue: { + spy: () => {}, + prototype: { __spy: null }, + }, }; // Simulate React Native's window.require polyfill window.require = moduleName => {