-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
index.js
180 lines (151 loc) · 4.76 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* istanbul ignore file */
import { options, Component, Fragment } from 'preact';
import { Renderer } from './renderer';
/**
* Wrap function with generic error logging
*
* @param {*} fn
* @returns
*/
function catchErrors(fn) {
return function(arg) {
try {
return fn(arg);
}
catch (e) {
/* istanbul ignore next */
console.error('The react devtools encountered an error');
/* istanbul ignore next */
console.error(e); // eslint-disable-line no-console
}
};
}
/* istanbul ignore next */
let noop = () => undefined;
export function initDevTools() {
// This global variable is injected by the devtools
/** @type {import('../internal').DevtoolsWindow} */
let hook = (window).__REACT_DEVTOOLS_GLOBAL_HOOK__;
if (hook==null) return;
/** @type {(vnode: import('../internal').VNode) => void} */
let onCommitRoot = noop;
/** @type {(vnode: import('../internal').VNode) => void} */
let onCommitUnmount = noop;
// Initialize our custom renderer
let rid = Math.random().toString(16).slice(2);
let preactRenderer = new Renderer(hook, rid);
catchErrors(() => {
let isDev = false;
try {
isDev = process.env.NODE_ENV!=='production';
}
catch (e) {}
// Tell devtools which bundle type we run in
window.parent.postMessage({
source: 'react-devtools-detector',
reactBuildType: /* istanbul ignore next */ isDev
? 'development'
: 'production'
}, '*');
let renderer = {
bundleType: /* istanbul ignore next */ isDev ? 1 : 0,
version: '16.5.2',
rendererPackageName: 'preact',
// We don't need this, but the devtools `attachRenderer` function relys
// it being there.
findHostInstanceByFiber(vnode) {
return vnode._dom;
},
// We don't need this, but the devtools `attachRenderer` function relys
// it being there.
findFiberByHostInstance(instance) {
return preactRenderer.inst2vnode.get(instance) || null;
}
};
hook._renderers[rid] = renderer;
// We can't bring our own `attachRenderer` function therefore we simply
// prevent the devtools from overwriting our custom renderer by creating
// a noop setter.
Object.defineProperty(hook.helpers, rid, {
get: () => preactRenderer,
set: () => {
if (!preactRenderer.connected) {
helpers.markConnected();
}
}
});
let helpers = hook.helpers[rid];
// Tell the devtools that we are ready to start
hook.emit('renderer-attached', {
id: rid,
renderer,
helpers
});
onCommitRoot = catchErrors(root => {
// Empty root
if (root.type===Fragment && root._children.length==0) return;
let roots = hook.getFiberRoots(rid);
root = helpers.handleCommitFiberRoot(root);
if (!roots.has(root)) roots.add(root);
});
onCommitUnmount = catchErrors(vnode => {
hook.onCommitFiberUnmount(rid, vnode);
});
})();
// Store (possible) previous hooks so that we don't overwrite them
let prevVNodeHook = options.vnode;
let prevCommitRoot = options._commit;
let prevBeforeUnmount = options.unmount;
let prevBeforeDiff = options._diff;
let prevAfterDiff = options.diffed;
options.vnode = (vnode) => {
// Tiny performance improvement by initializing fields as doubles
// from the start. `performance.now()` will always return a double.
// See https://github.com/facebook/react/issues/14365
// and https://slidr.io/bmeurer/javascript-engine-fundamentals-the-good-the-bad-and-the-ugly
vnode.startTime = NaN;
vnode.endTime = NaN;
vnode.startTime = 0;
vnode.endTime = -1;
if (prevVNodeHook) prevVNodeHook(vnode);
};
options._diff = (vnode) => {
vnode.startTime = now();
if (prevBeforeDiff!=null) prevBeforeDiff(vnode);
};
options.diffed = (vnode) => {
vnode.endTime = now();
if (prevAfterDiff!=null) prevAfterDiff(vnode);
};
options._commit = catchErrors((vnode) => {
// Call previously defined hook
if (prevCommitRoot!=null) prevCommitRoot(vnode);
// These cases are already handled by `unmount`
if (vnode==null) return;
onCommitRoot(vnode);
});
options.unmount = catchErrors((vnode) => {
// Call previously defined hook
if (prevBeforeUnmount!=null) prevBeforeUnmount(vnode);
onCommitUnmount(vnode);
});
// Inject tracking into setState
const setState = Component.prototype.setState;
Component.prototype.setState = function(update, callback) {
// Duplicated in setState() but doesn't matter due to the guard.
let s = (this._nextState!==this.state && this._nextState) || (this._nextState = Object.assign({}, this.state));
// Needed in order to check if state has changed after the tree has been committed:
this._prevState = Object.assign({}, s);
return setState.call(this, update, callback);
};
}
/**
* Get current timestamp in ms. Used for profiling.
* @returns {number}
*/
export let now = Date.now;
try {
/* istanbul ignore else */
now = performance.now.bind(performance);
}
catch (e) {}