Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding apollo dev tools. #298

Merged
merged 19 commits into from
Jan 30, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"__PLATFORM__",
"__REPORT_REACT_DEVTOOLS_PORT__",
"__FETCH_SUPPORT__",
"__NETWORK_INSPECT__"
"__NETWORK_INSPECT__",
"__APOLLO_DEVTOOLS_SHOULD_DISPLAY_PANEL__"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apollo dev tools are not displayed in the chrome dev tools by default.
It tries to check if window.apolloClient is set. Because we don't have apollo client in window (it is in web worker), I've created a separate bool for it.

]
}
],
Expand Down
23 changes: 22 additions & 1 deletion app/middlewares/debuggerAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ let socket;

const workerOnMessage = message => {
const { data } = message;

if (data && data.source === 'apollo-devtools-backend') {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we get at least one message from apollo backend (the code in the worker) it means that apollo client is being used in the app. So we tell apollo dev tools to display its panel in chrome dev tools.

Also we forward the message from webworker to react native debugger web part.

if (!window.__APOLLO_DEVTOOLS_SHOULD_DISPLAY_PANEL__) {
window.__APOLLO_DEVTOOLS_SHOULD_DISPLAY_PANEL__ = true;
}

postMessage({
source: 'apollo-devtools-backend',
payload: data,
}, '*');
}

if (data && (data.__IS_REDUX_NATIVE_MESSAGE__ || data.__REPORT_REACT_DEVTOOLS_PORT__)) {
return true;
}
Expand All @@ -43,14 +55,22 @@ const workerOnMessage = message => {
socket.send(JSON.stringify(data));
};

const onWindowMessage = e => {
const {data} = e;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we receive data from panel in web part, we forward the message to the worker. There are some checks for payload, but they are there since apollo dev tools don't strictly follow api for event emitter.

if (data && data.source === 'apollo-devtools-proxy') {
const message = typeof data.payload === 'string' ? { event: data.payload } : data.payload;
worker.postMessage({ source: 'apollo-devtools-proxy', ...message });
}
};

const createJSRuntime = () => {
// This worker will run the application javascript code,
// making sure that it's run in an environment without a global
// document, to make it consistent with the JSC executor environment.
// eslint-disable-next-line
worker = new Worker(`${__webpack_public_path__}RNDebuggerWorker.js`);
worker.addEventListener('message', workerOnMessage);

window.addEventListener('message', onWindowMessage);
actions.setDebuggerWorker(worker, 'connected');
};

Expand All @@ -59,6 +79,7 @@ const shutdownJSRuntime = () => {
scriptExecuted = false;
if (worker) {
worker.terminate();
window.removeEventListener('messsage', onWindowMessage);
setDevMenuMethods([]);
}
worker = null;
Expand Down
18 changes: 18 additions & 0 deletions app/worker/asyncStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@ export const getClearAsyncStorageFn = AsyncStorage => {
return () => AsyncStorage.clear().catch(f => f);
};

export const getSafeAsyncStorage = AsyncStorage => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes Async storage throws errors, we want to silence them

return {
async getItem(key) {
try {
return AsyncStorage.getItem(key);
} catch (e) {
return null;
}
},
async setItem(key, value) {
try {
return AsyncStorage.setItem(key, value);
} catch (e) {
}
}
}
};

export const getShowAsyncStorageFn = AsyncStorage => {
if (!AsyncStorage.getAllKeys || !AsyncStorage.getItem) return;
return async () => {
Expand Down
48 changes: 47 additions & 1 deletion app/worker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import devToolsEnhancer, { composeWithDevTools } from './reduxAPI';
import * as RemoteDev from './remotedev';
import { getRequiredModules, ignoreRNDIntervalSpy } from './utils';
import { toggleNetworkInspect } from './networkInspect';

import { getSafeAsyncStorage } from './asyncStorage';
import Bridge from 'apollo-client-devtools/bridge';
import { initBackend, sendBridgeReady } from 'apollo-client-devtools/backend';
import { version as devToolsVersion } from 'apollo-client-devtools/package.json';
/* eslint-disable no-underscore-dangle */
self.__REMOTEDEV__ = RemoteDev;

Expand Down Expand Up @@ -51,6 +54,48 @@ const setupRNDebugger = async message => {
checkAvailableDevMenuMethods(modules, message.networkInspect);
reportDefaultReactDevToolsPort(modules);
}

const interval = setInterval(() => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apollo client doesn't have any event emitted when it is set up. To overcome that I just poll the client till it appears.
It is not an optimal approach, but simple polling shouldn't be too much overhead

if (!self.__APOLLO_CLIENT__) {
return;
}

clearInterval(interval);

const hook = {
ApolloClient: self.__APOLLO_CLIENT__,
devToolsVersion
};

let listener;

const bridge = new Bridge({
listen(fn) {
listener = self.addEventListener('message', evt => {
if (evt.data.source === 'apollo-devtools-proxy') {
return fn(evt.data);
}
});
},
send(data) {
postMessage({
...data,
source: 'apollo-devtools-backend',
});
},
});

bridge.on('init', () => {
sendBridgeReady();
});

bridge.on("shutdown", () => {
self.removeEventListener('message', listener);
});

initBackend(bridge, hook, getSafeAsyncStorage(modules.AsyncStorage));

}, 1000);
};

const messageHandlers = {
Expand All @@ -66,6 +111,7 @@ const messageHandlers = {
} catch (err) {
error = err.message;
}

sendReply(null /* result */, error);

if (!error) {
Expand Down
2 changes: 2 additions & 0 deletions electron/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export default async () => {
} catch (e) {} // eslint-disable-line
}
BrowserWindow.addDevToolsExtension(path.resolve('dist/devtools-helper/'));

} else {
BrowserWindow.addDevToolsExtension(path.join(__dirname, 'devtools-helper/'));
}
BrowserWindow.addDevToolsExtension(path.resolve('../apollo-client-devtools/shells/webextension/'));
Gongreg marked this conversation as resolved.
Show resolved Hide resolved
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
},
"dependencies": {
"adbkit": "^2.11.0",
"apollo-dev-tools": "*",
"electron-context-menu": "jhen0409/electron-context-menu#async-popup",
"electron-fetch": "^1.2.1",
"electron-gh-releases": "^2.0.4",
Expand Down