Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit 28c6f58

Browse files
committed
Merge pull request #241 from kassens/globalhook
Instrument Relay on page load time instead of when the devtools are opened
2 parents 2f584e2 + 756bfea commit 28c6f58

File tree

4 files changed

+133
-52
lines changed

4 files changed

+133
-52
lines changed

plugins/Relay/backend.js

Lines changed: 8 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,6 @@
1313
import type Bridge from '../../agent/Bridge';
1414
import type Agent from '../../agent/Agent';
1515

16-
import guid from '../../utils/guid';
17-
18-
function decorate(obj, attr, fn) {
19-
var old = obj[attr];
20-
obj[attr] = function() {
21-
var res = old.apply(this, arguments);
22-
fn.apply(this, arguments);
23-
return res;
24-
};
25-
return () => (obj[attr] = old);
26-
}
27-
2816
module.exports = (bridge: Bridge, agent: Agent, hook: Object) => {
2917
var shouldEnable = !!(
3018
hook._relayInternals &&
@@ -35,45 +23,14 @@ module.exports = (bridge: Bridge, agent: Agent, hook: Object) => {
3523
if (!shouldEnable) {
3624
return;
3725
}
38-
var NetworkLayer = hook._relayInternals.NetworkLayer;
39-
40-
bridge.send('relay:store', {id: 'relay:store', nodes: hook._relayInternals.DefaultStoreData.getNodeData()});
41-
var restore = [
42-
decorate(NetworkLayer, 'sendMutation', mut => {
43-
var id = guid();
44-
bridge.send('relay:pending', [{
45-
id,
46-
type: 'mutation',
47-
start: Date.now(),
48-
text: mut.getQueryString(),
49-
variables: mut.getVariables(),
50-
name: mut.getDebugName(),
51-
}]);
52-
mut.then(
53-
response => bridge.send('relay:success', {id, response: response.response, end: Date.now()}),
54-
error => bridge.send('relay:failure', {id, error, end: Date.now()})
55-
);
56-
}),
26+
var {
27+
DefaultStoreData,
28+
setRequestListener,
29+
} = hook._relayInternals;
5730

58-
decorate(NetworkLayer, 'sendQueries', queries => {
59-
bridge.send('relay:pending', queries.map(q => {
60-
var id = guid();
61-
q.then(
62-
response => bridge.send('relay:success', {id, response: response.response, end: Date.now()}),
63-
error => bridge.send('relay:failure', {id, error, end: Date.now()})
64-
);
65-
return {
66-
id,
67-
type: 'query',
68-
start: Date.now(),
69-
text: q.getQueryString(),
70-
variables: q.getVariables(),
71-
name: q.getDebugName(),
72-
};
73-
}));
74-
}),
75-
];
76-
hook.on('shutdown', () => {
77-
restore.forEach(fn => fn());
31+
bridge.send('relay:store', {id: 'relay:store', nodes: DefaultStoreData.getNodeData()});
32+
var removeListener = setRequestListener((event, data) => {
33+
bridge.send(event, data);
7834
});
35+
hook.on('shutdown', removeListener);
7936
};

plugins/Relay/installRelayHook.js

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
* @flow
10+
*/
11+
'use strict';
12+
13+
/**
14+
* NOTE: This file cannot `require` any other modules. We `.toString()` the
15+
* function in some places and inject the source into the page.
16+
*/
17+
function installRelayHook(window: Object) {
18+
var hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
19+
if (!hook) {
20+
return;
21+
}
22+
23+
function decorate(obj, attr, fn) {
24+
var old = obj[attr];
25+
obj[attr] = function() {
26+
var res = old.apply(this, arguments);
27+
fn.apply(this, arguments);
28+
return res;
29+
};
30+
}
31+
32+
var _eventQueue = [];
33+
var _listener = null;
34+
function emit(name: string, data: mixed) {
35+
_eventQueue.push({name, data});
36+
if (_listener) {
37+
_listener(name, data);
38+
}
39+
}
40+
41+
function setRequestListener(
42+
listener: (name: string, data: mixed) => void
43+
): () => void {
44+
if (_listener) {
45+
throw new Error(
46+
'Relay Devtools: Called only call setRequestListener once.'
47+
);
48+
}
49+
_listener = listener;
50+
_eventQueue.forEach(({name, data}) => {
51+
listener(name, data);
52+
});
53+
54+
return () => {
55+
_listener = null;
56+
};
57+
}
58+
59+
function recordRequest(type: 'mutation' | 'query', request) {
60+
var id = Math.random().toString(16).substr(2);
61+
request.then(
62+
response => {
63+
emit('relay:success', {
64+
id: id,
65+
end: Date.now(),
66+
response: response.response,
67+
});
68+
},
69+
error => {
70+
emit('relay:failure', {
71+
id: id,
72+
end: Date.now(),
73+
error: error,
74+
});
75+
},
76+
);
77+
return {
78+
id: id,
79+
type: type,
80+
start: Date.now(),
81+
text: request.getQueryString(),
82+
variables: request.getVariables(),
83+
name: request.getDebugName(),
84+
};
85+
}
86+
87+
function instrumentRelayRequests(relayInternals: Object) {
88+
var NetworkLayer = relayInternals.NetworkLayer;
89+
90+
decorate(NetworkLayer, 'sendMutation', mutation => {
91+
emit('relay:pending', [recordRequest('mutation', mutation)]);
92+
});
93+
94+
decorate(NetworkLayer, 'sendQueries', queries => {
95+
emit('relay:pending', queries.map(query => recordRequest('query', query)));
96+
});
97+
98+
var instrumented = {};
99+
for (var key in relayInternals) {
100+
if (relayInternals.hasOwnProperty(key)) {
101+
instrumented[key] = relayInternals[key];
102+
}
103+
}
104+
instrumented.setRequestListener = setRequestListener;
105+
return instrumented;
106+
}
107+
108+
var _relayInternals = null;
109+
Object.defineProperty(hook, '_relayInternals', ({
110+
set: function(relayInternals) {
111+
_relayInternals = instrumentRelayRequests(relayInternals);
112+
},
113+
get: function() {
114+
return _relayInternals;
115+
},
116+
}: any));
117+
}
118+
119+
module.exports = installRelayHook;

shells/chrome/src/GlobalHook.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// devtools are installed (and skip its suggestion to install the devtools).
1515

1616
var installGlobalHook = require('../../../backend/installGlobalHook.js');
17+
var installRelayHook = require('../../../plugins/Relay/installRelayHook.js');
1718

1819
var saveNativeValues = `
1920
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeObjectCreate = Object.create;
@@ -23,7 +24,9 @@ window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeSet = Set;
2324
`;
2425

2526
var js = (
26-
';(' + installGlobalHook.toString() + '(window))'
27+
';(' + installGlobalHook.toString() + '(window))' +
28+
';(' + installRelayHook.toString() + '(window))' +
29+
saveNativeValues
2730
);
2831

2932
// This script runs before the <head> element is created, so we add the script

shells/plain/container.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
var React = require('react');
1414
var installGlobalHook = require('../../backend/installGlobalHook');
15+
var installRelayHook = require('../../plugins/Relay/installRelayHook');
1516

1617
window.React = React;
1718

@@ -24,6 +25,7 @@ var devtoolsSrc = target.getAttribute('data-devtools-src') || './build/backend.j
2425

2526
var win = target.contentWindow;
2627
installGlobalHook(win);
28+
installRelayHook(win);
2729

2830
var iframeSrc = document.getElementById('iframe-src');
2931
if (iframeSrc) {

0 commit comments

Comments
 (0)