Misc Flow and import fixes
1. Fixed all reported Flow errors 2. Added a few missing package declarations 3. Deleted ReactDebugHooks fork in favor of react-debug-tools
Brian Vaughn committed
Aug 13, 2019 at 17:53 UTC
edc46d7be7ce8fff2b5c21a00eb6741efbb9ef42
23 files changed
+146
-767
packages/react-debug-tools/src/ReactDebugHooks.js
+2
-2
@@ -253,14 +253,14 @@ const Dispatcher: DispatcherType = {
253
254
// Inspect
255
256
-type HooksNode = {
256
+export type HooksNode = {
257
id: number | null,
258
isStateEditable: boolean,
259
name: string,
260
value: mixed,
261
subHooks: Array<HooksNode>,
262
};
263
-type HooksTree = Array<HooksNode>;
263
+export type HooksTree = Array<HooksNode>;
264
265
// Don't assume
266
//
packages/react-devtools-core/src/standalone.js
+3
-1
@@ -300,7 +300,9 @@ function startServer(port?: number = 8097) {
300
close: function() {
301
connected = null;
302
onDisconnected();
303
- clearTimeout(startServerTimeoutID);
303
+ if (startServerTimeoutID !== null) {
304
+ clearTimeout(startServerTimeoutID);
305
+ }
306
server.close();
307
httpServer.close();
308
},
packages/react-devtools-extensions/flow.js
+1
-1
@@ -1,6 +1,6 @@
1
// @flow
2
3
-declare module 'events' {
3
+declare module 'node-events' {
4
declare class EventEmitter<Events: Object> {
5
addListener<Event: $Keys<Events>>(
6
event: Event,
packages/react-devtools-shared/package.json
+1
@@ -8,6 +8,7 @@
8
"clipboard-js": "^0.3.6",
9
"lodash.throttle": "^4.1.1",
10
"memoize-one": "^3.1.1",
11
+ "node-events": "npm:events@^3.0.0",
12
"react-virtualized-auto-sizer": "^1.0.2"
13
}
14
}
packages/react-devtools-shared/src/__tests__/storeOwners-test.js
+1
-1
@@ -1,6 +1,6 @@
1
// @flow
2
3
-const { printOwnersList } = require('./storeSerializer');
3
+const { printOwnersList } = require('../devtools/utils');
4
5
describe('Store owners list', () => {
6
let React;
packages/react-devtools-shared/src/__tests__/storeSerializer.js
+3
-69
@@ -1,3 +1,5 @@
1
+import { printStore } from 'react-devtools-shared/src/devtools/utils'
2
+
3
// test() is part of Jest's serializer API
4
export function test(maybeStore) {
5
// It's important to lazy-require the Store rather than imported at the head of the module.
@@ -11,74 +13,6 @@ export function print(store, serialize, indent) {
13
return printStore(store);
14
}
15
14
-export function printElement(element, includeWeight = false) {
15
- let prefix = ' ';
16
- if (element.children.length > 0) {
17
- prefix = element.isCollapsed ? '▸' : '▾';
18
- }
19
-
20
- let key = '';
21
- if (element.key !== null) {
22
- key = ` key="${element.key}"`;
23
- }
24
-
25
- let hocs = '';
26
- if (element.hocDisplayNames !== null) {
27
- hocs = ` [${element.hocDisplayNames.join('][')}]`;
28
- }
29
-
30
- let suffix = '';
31
- if (includeWeight) {
32
- suffix = ` (${element.isCollapsed ? 1 : element.weight})`;
33
- }
34
-
35
- return `${' '.repeat(element.depth + 1)}${prefix} <${element.displayName ||
36
- 'null'}${key}>${hocs}${suffix}`;
37
-}
38
-
39
-export function printOwnersList(elements, includeWeight = false) {
40
- return elements
41
- .map(element => printElement(element, includeWeight))
42
- .join('\n');
43
-}
44
-
16
// Used for Jest snapshot testing.
17
// May also be useful for visually debugging the tree, so it lives on the Store.
47
-export function printStore(store, includeWeight = false) {
48
- const snapshotLines = [];
49
-
50
- let rootWeight = 0;
51
-
52
- store.roots.forEach(rootID => {
53
- const { weight } = store.getElementByID(rootID);
54
-
55
- snapshotLines.push('[root]' + (includeWeight ? ` (${weight})` : ''));
56
-
57
- for (let i = rootWeight; i < rootWeight + weight; i++) {
58
- const element = store.getElementAtIndex(i);
59
-
60
- if (element == null) {
61
- throw Error(`Could not find element at index ${i}`);
62
- }
63
-
64
- snapshotLines.push(printElement(element, includeWeight));
65
- }
66
-
67
- rootWeight += weight;
68
- });
69
-
70
- // Make sure the pretty-printed test align with the Store's reported number of total rows.
71
- if (rootWeight !== store.numElements) {
72
- throw Error(
73
- `Inconsistent Store state. Individual root weights (${rootWeight}) do not match total weight (${
74
- store.numElements
75
- })`
76
- );
77
- }
78
-
79
- // If roots have been unmounted, verify that they've been removed from maps.
80
- // This helps ensure the Store doesn't leak memory.
81
- store.assertExpectedRootMapSizes();
82
-
83
- return snapshotLines.join('\n');
84
-}
18
+export { printStore };
packages/react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor.js
+3
-4
@@ -7,7 +7,7 @@ import type { BackendBridge } from 'react-devtools-shared/src/bridge';
7
import type { RendererID } from '../types';
8
import type { StyleAndLayout } from './types';
9
10
-export type ResolveNativeStyle = (stylesheetID: number) => ?Object;
10
+export type ResolveNativeStyle = (stylesheetID: any) => ?Object;
11
12
export default function setupNativeStyleEditor(
13
bridge: BackendBridge,
@@ -136,9 +136,8 @@ function measureStyle(
136
);
137
return;
138
}
139
- const margin = resolveBoxStyle('margin', resolvedStyle) || EMPTY_BOX_STYLE;
140
- const padding =
141
- resolveBoxStyle('padding', resolvedStyle) || EMPTY_BOX_STYLE;
139
+ const margin = resolvedStyle != null && resolveBoxStyle('margin', resolvedStyle) || EMPTY_BOX_STYLE;
140
+ const padding = resolvedStyle != null && resolveBoxStyle('padding', resolvedStyle) || EMPTY_BOX_STYLE;
141
bridge.send(
142
'NativeStyleEditor_styleAndLayout',
143
({
packages/react-devtools-shared/src/backend/ReactDebugHooks.js
deleted
-636
@@ -1,636 +0,0 @@
1
-// @flow
2
-
3
-// This file was forked from the React GitHub repo:
4
-// https://github.com/facebook/react/blob/master/packages/react-debug-tools/src/ReactDebugHooks.js
5
-// It has been modified slightly though to account for "shared" imports and different lint configs.
6
-// I've also removed some of the Flow types that don't exist in DevTools.
7
-// TODO Remove this fork and use the NPM version of this package once it's released.
8
-
9
-import ErrorStackParser from 'error-stack-parser';
10
-
11
-import type {
12
- ReactContext,
13
- ReactEventResponder,
14
- ReactEventResponderListener,
15
-} from './types';
16
-
17
-type Fiber = any;
18
-type Hook = any;
19
-
20
-// HACK: These values are copied from attachRendererFiber
21
-// In the future, the react-debug-hooks package will be published to NPM,
22
-// and be locked to a specific range of react versions
23
-// For now we are just hard-coding the current/latest versions.
24
-const ContextProvider = 10;
25
-const ForwardRef = 11;
26
-const FunctionComponent = 0;
27
-const SimpleMemoComponent = 15;
28
-
29
-// Used to track hooks called during a render
30
-
31
-type HookLogEntry = {
32
- primitive: string,
33
- stackError: Error,
34
- value: mixed,
35
-};
36
-
37
-let hookLog: Array<HookLogEntry> = [];
38
-
39
-// Primitives
40
-
41
-type BasicStateAction<S> = (S => S) | S;
42
-
43
-type Dispatch<A> = A => void;
44
-
45
-let primitiveStackCache: null | Map<string, Array<any>> = null;
46
-
47
-function getPrimitiveStackCache(): Map<string, Array<any>> {
48
- // This initializes a cache of all primitive hooks so that the top
49
- // most stack frames added by calling the primitive hook can be removed.
50
- if (primitiveStackCache === null) {
51
- const cache = new Map();
52
- let readHookLog;
53
- try {
54
- // Use all hooks here to add them to the hook log.
55
- Dispatcher.useContext(({ _currentValue: null }: any));
56
- Dispatcher.useState(null);
57
- Dispatcher.useReducer((s, a) => s, null);
58
- Dispatcher.useRef(null);
59
- Dispatcher.useLayoutEffect(() => {});
60
- Dispatcher.useEffect(() => {});
61
- Dispatcher.useImperativeHandle(undefined, () => null);
62
- Dispatcher.useDebugValue(null);
63
- Dispatcher.useCallback(() => {});
64
- Dispatcher.useMemo(() => null);
65
- } finally {
66
- readHookLog = hookLog;
67
- hookLog = [];
68
- }
69
- for (let i = 0; i < readHookLog.length; i++) {
70
- const hook = readHookLog[i];
71
- cache.set(hook.primitive, ErrorStackParser.parse(hook.stackError));
72
- }
73
- primitiveStackCache = cache;
74
- }
75
- return primitiveStackCache;
76
-}
77
-
78
-let currentHook: null | Hook = null;
79
-function nextHook(): null | Hook {
80
- const hook = currentHook;
81
- if (hook !== null) {
82
- currentHook = hook.next;
83
- }
84
- return hook;
85
-}
86
-
87
-function readContext<T>(
88
- context: ReactContext<T>,
89
- observedBits: void | number | boolean
90
-): T {
91
- // For now we don't expose readContext usage in the hooks debugging info.
92
- return context._currentValue;
93
-}
94
-
95
-function useContext<T>(
96
- context: ReactContext<T>,
97
- observedBits: void | number | boolean
98
-): T {
99
- hookLog.push({
100
- primitive: 'Context',
101
- stackError: new Error(),
102
- value: context._currentValue,
103
- });
104
- return context._currentValue;
105
-}
106
-
107
-function useState<S>(
108
- initialState: (() => S) | S
109
-): [S, Dispatch<BasicStateAction<S>>] {
110
- const hook = nextHook();
111
- const state: S =
112
- hook !== null
113
- ? hook.memoizedState
114
- : typeof initialState === 'function'
115
- ? (initialState: any)()
116
- : initialState;
117
- hookLog.push({ primitive: 'State', stackError: new Error(), value: state });
118
- return [state, (action: BasicStateAction<S>) => {}];
119
-}
120
-
121
-function useReducer<S, I, A>(
122
- reducer: (S, A) => S,
123
- initialArg: I,
124
- init?: I => S
125
-): [S, Dispatch<A>] {
126
- const hook = nextHook();
127
- let state;
128
- if (hook !== null) {
129
- state = hook.memoizedState;
130
- } else {
131
- state = init !== undefined ? init(initialArg) : ((initialArg: any): S);
132
- }
133
- hookLog.push({
134
- primitive: 'Reducer',
135
- stackError: new Error(),
136
- value: state,
137
- });
138
- return [state, (action: A) => {}];
139
-}
140
-
141
-function useRef<T>(initialValue: T): { current: T } {
142
- const hook = nextHook();
143
- const ref = hook !== null ? hook.memoizedState : { current: initialValue };
144
- hookLog.push({
145
- primitive: 'Ref',
146
- stackError: new Error(),
147
- value: ref.current,
148
- });
149
- return ref;
150
-}
151
-
152
-function useLayoutEffect(
153
- create: () => (() => void) | void,
154
- inputs: Array<mixed> | void | null
155
-): void {
156
- nextHook();
157
- hookLog.push({
158
- primitive: 'LayoutEffect',
159
- stackError: new Error(),
160
- value: create,
161
- });
162
-}
163
-
164
-function useEffect(
165
- create: () => (() => void) | void,
166
- inputs: Array<mixed> | void | null
167
-): void {
168
- nextHook();
169
- hookLog.push({ primitive: 'Effect', stackError: new Error(), value: create });
170
-}
171
-
172
-function useImperativeHandle<T>(
173
- ref: { current: T | null } | ((inst: T | null) => mixed) | null | void,
174
- create: () => T,
175
- inputs: Array<mixed> | void | null
176
-): void {
177
- nextHook();
178
- // We don't actually store the instance anywhere if there is no ref callback
179
- // and if there is a ref callback it might not store it but if it does we
180
- // have no way of knowing where. So let's only enable introspection of the
181
- // ref itself if it is using the object form.
182
- let instance = undefined;
183
- if (ref !== null && typeof ref === 'object') {
184
- instance = ref.current;
185
- }
186
- hookLog.push({
187
- primitive: 'ImperativeHandle',
188
- stackError: new Error(),
189
- value: instance,
190
- });
191
-}
192
-
193
-function useDebugValue(value: any, formatterFn: ?(value: any) => any) {
194
- hookLog.push({
195
- primitive: 'DebugValue',
196
- stackError: new Error(),
197
- value: typeof formatterFn === 'function' ? formatterFn(value) : value,
198
- });
199
-}
200
-
201
-function useCallback<T>(callback: T, inputs: Array<mixed> | void | null): T {
202
- const hook = nextHook();
203
- hookLog.push({
204
- primitive: 'Callback',
205
- stackError: new Error(),
206
- value: hook !== null ? hook.memoizedState[0] : callback,
207
- });
208
- return callback;
209
-}
210
-
211
-function useMemo<T>(
212
- nextCreate: () => T,
213
- inputs: Array<mixed> | void | null
214
-): T {
215
- const hook = nextHook();
216
- const value = hook !== null ? hook.memoizedState[0] : nextCreate();
217
- hookLog.push({ primitive: 'Memo', stackError: new Error(), value });
218
- return value;
219
-}
220
-
221
-function useResponder(
222
- responder: ReactEventResponder<any, any>,
223
- listenerProps: Object
224
-): ReactEventResponderListener<any, any> {
225
- // Don't put the actual event responder object in, just its displayName
226
- const value = {
227
- responder: responder.displayName || 'EventResponder',
228
- props: listenerProps,
229
- };
230
- hookLog.push({ primitive: 'Responder', stackError: new Error(), value });
231
- return {
232
- responder,
233
- props: listenerProps,
234
- };
235
-}
236
-
237
-const Dispatcher = {
238
- readContext,
239
- useCallback,
240
- useContext,
241
- useEffect,
242
- useImperativeHandle,
243
- useDebugValue,
244
- useLayoutEffect,
245
- useMemo,
246
- useReducer,
247
- useRef,
248
- useState,
249
- useResponder,
250
-};
251
-
252
-// Inspect
253
-
254
-type ReactCurrentDispatcher = {
255
- current: null | typeof Dispatcher,
256
-};
257
-
258
-type HooksNode = {
259
- id: number | null,
260
- isStateEditable: boolean,
261
- name: string,
262
- value: mixed,
263
- subHooks: Array<HooksNode>,
264
-};
265
-type HooksTree = Array<HooksNode>;
266
-
267
-// Don't assume
268
-//
269
-// We can't assume that stack frames are nth steps away from anything.
270
-// E.g. we can't assume that the root call shares all frames with the stack
271
-// of a hook call. A simple way to demonstrate this is wrapping `new Error()`
272
-// in a wrapper constructor like a polyfill. That'll add an extra frame.
273
-// Similar things can happen with the call to the dispatcher. The top frame
274
-// may not be the primitive. Likewise the primitive can have fewer stack frames
275
-// such as when a call to useState got inlined to use dispatcher.useState.
276
-//
277
-// We also can't assume that the last frame of the root call is the same
278
-// frame as the last frame of the hook call because long stack traces can be
279
-// truncated to a stack trace limit.
280
-
281
-let mostLikelyAncestorIndex = 0;
282
-
283
-function findSharedIndex(hookStack, rootStack, rootIndex) {
284
- const source = rootStack[rootIndex].source;
285
- hookSearch: for (let i = 0; i < hookStack.length; i++) {
286
- if (hookStack[i].source === source) {
287
- // This looks like a match. Validate that the rest of both stack match up.
288
- for (
289
- let a = rootIndex + 1, b = i + 1;
290
- a < rootStack.length && b < hookStack.length;
291
- a++, b++
292
- ) {
293
- if (hookStack[b].source !== rootStack[a].source) {
294
- // If not, give up and try a different match.
295
- continue hookSearch;
296
- }
297
- }
298
- return i;
299
- }
300
- }
301
- return -1;
302
-}
303
-
304
-function findCommonAncestorIndex(rootStack, hookStack) {
305
- let rootIndex = findSharedIndex(
306
- hookStack,
307
- rootStack,
308
- mostLikelyAncestorIndex
309
- );
310
- if (rootIndex !== -1) {
311
- return rootIndex;
312
- }
313
- // If the most likely one wasn't a hit, try any other frame to see if it is shared.
314
- // If that takes more than 5 frames, something probably went wrong.
315
- for (let i = 0; i < rootStack.length && i < 5; i++) {
316
- rootIndex = findSharedIndex(hookStack, rootStack, i);
317
- if (rootIndex !== -1) {
318
- mostLikelyAncestorIndex = i;
319
- return rootIndex;
320
- }
321
- }
322
- return -1;
323
-}
324
-
325
-function isReactWrapper(functionName, primitiveName) {
326
- if (!functionName) {
327
- return false;
328
- }
329
- const expectedPrimitiveName = 'use' + primitiveName;
330
- if (functionName.length < expectedPrimitiveName.length) {
331
- return false;
332
- }
333
- return (
334
- functionName.lastIndexOf(expectedPrimitiveName) ===
335
- functionName.length - expectedPrimitiveName.length
336
- );
337
-}
338
-
339
-function findPrimitiveIndex(hookStack, hook) {
340
- const stackCache = getPrimitiveStackCache();
341
- const primitiveStack = stackCache.get(hook.primitive);
342
- if (primitiveStack === undefined) {
343
- return -1;
344
- }
345
- for (let i = 0; i < primitiveStack.length && i < hookStack.length; i++) {
346
- if (primitiveStack[i].source !== hookStack[i].source) {
347
- // If the next two frames are functions called `useX` then we assume that they're part of the
348
- // wrappers that the React packager or other packages adds around the dispatcher.
349
- if (
350
- i < hookStack.length - 1 &&
351
- isReactWrapper(hookStack[i].functionName, hook.primitive)
352
- ) {
353
- i++;
354
- }
355
- if (
356
- i < hookStack.length - 1 &&
357
- isReactWrapper(hookStack[i].functionName, hook.primitive)
358
- ) {
359
- i++;
360
- }
361
- return i;
362
- }
363
- }
364
- return -1;
365
-}
366
-
367
-function parseTrimmedStack(rootStack, hook) {
368
- // Get the stack trace between the primitive hook function and
369
- // the root function call. I.e. the stack frames of custom hooks.
370
- const hookStack = ErrorStackParser.parse(hook.stackError);
371
- const rootIndex = findCommonAncestorIndex(rootStack, hookStack);
372
- const primitiveIndex = findPrimitiveIndex(hookStack, hook);
373
- if (
374
- rootIndex === -1 ||
375
- primitiveIndex === -1 ||
376
- rootIndex - primitiveIndex < 2
377
- ) {
378
- // Something went wrong. Give up.
379
- return null;
380
- }
381
- return hookStack.slice(primitiveIndex, rootIndex - 1);
382
-}
383
-
384
-function parseCustomHookName(functionName: void | string): string {
385
- if (!functionName) {
386
- return '';
387
- }
388
- let startIndex = functionName.lastIndexOf('.');
389
- if (startIndex === -1) {
390
- startIndex = 0;
391
- }
392
- if (functionName.substr(startIndex, 3) === 'use') {
393
- startIndex += 3;
394
- }
395
- return functionName.substr(startIndex);
396
-}
397
-
398
-function buildTree(rootStack, readHookLog): HooksTree {
399
- const rootChildren = [];
400
- let prevStack = null;
401
- let levelChildren = rootChildren;
402
- let nativeHookID = 0;
403
- const stackOfChildren = [];
404
- for (let i = 0; i < readHookLog.length; i++) {
405
- const hook = readHookLog[i];
406
- const stack = parseTrimmedStack(rootStack, hook);
407
- if (stack !== null) {
408
- // Note: The indices 0 <= n < length-1 will contain the names.
409
- // The indices 1 <= n < length will contain the source locations.
410
- // That's why we get the name from n - 1 and don't check the source
411
- // of index 0.
412
- let commonSteps = 0;
413
- if (prevStack !== null) {
414
- // Compare the current level's stack to the new stack.
415
- while (commonSteps < stack.length && commonSteps < prevStack.length) {
416
- const stackSource = stack[stack.length - commonSteps - 1].source;
417
- const prevSource =
418
- prevStack[prevStack.length - commonSteps - 1].source;
419
- if (stackSource !== prevSource) {
420
- break;
421
- }
422
- commonSteps++;
423
- }
424
- // Pop back the stack as many steps as were not common.
425
- for (let j = prevStack.length - 1; j > commonSteps; j--) {
426
- levelChildren = stackOfChildren.pop();
427
- }
428
- }
429
- // The remaining part of the new stack are custom hooks. Push them
430
- // to the tree.
431
- for (let j = stack.length - commonSteps - 1; j >= 1; j--) {
432
- const children = [];
433
- levelChildren.push({
434
- id: null,
435
- isStateEditable: false,
436
- name: parseCustomHookName(stack[j - 1].functionName),
437
- value: undefined,
438
- subHooks: children,
439
- });
440
- stackOfChildren.push(levelChildren);
441
- levelChildren = children;
442
- }
443
- prevStack = stack;
444
- }
445
- const { primitive } = hook;
446
-
447
- // For now, the "id" of stateful hooks is just the stateful hook index.
448
- // Custom hooks have no ids, nor do non-stateful native hooks (e.g. Context, DebugValue).
449
- const id =
450
- primitive === 'Context' || primitive === 'DebugValue'
451
- ? null
452
- : nativeHookID++;
453
-
454
- // For the time being, only State and Reducer hooks support runtime overrides.
455
- const isStateEditable = primitive === 'Reducer' || primitive === 'State';
456
-
457
- levelChildren.push({
458
- id,
459
- isStateEditable,
460
- name: primitive,
461
- value: hook.value,
462
- subHooks: [],
463
- });
464
- }
465
-
466
- // Associate custom hook values (useDebugValue() hook entries) with the correct hooks.
467
- processDebugValues(rootChildren, null);
468
-
469
- return rootChildren;
470
-}
471
-
472
-// Custom hooks support user-configurable labels (via the special useDebugValue() hook).
473
-// That hook adds user-provided values to the hooks tree,
474
-// but these values aren't intended to appear alongside of the other hooks.
475
-// Instead they should be attributed to their parent custom hook.
476
-// This method walks the tree and assigns debug values to their custom hook owners.
477
-function processDebugValues(
478
- hooksTree: HooksTree,
479
- parentHooksNode: HooksNode | null
480
-): void {
481
- const debugValueHooksNodes: Array<HooksNode> = [];
482
-
483
- for (let i = 0; i < hooksTree.length; i++) {
484
- const hooksNode = hooksTree[i];
485
- if (hooksNode.name === 'DebugValue' && hooksNode.subHooks.length === 0) {
486
- hooksTree.splice(i, 1);
487
- i--;
488
- debugValueHooksNodes.push(hooksNode);
489
- } else {
490
- processDebugValues(hooksNode.subHooks, hooksNode);
491
- }
492
- }
493
-
494
- // Bubble debug value labels to their custom hook owner.
495
- // If there is no parent hook, just ignore them for now.
496
- // (We may warn about this in the future.)
497
- if (parentHooksNode !== null) {
498
- if (debugValueHooksNodes.length === 1) {
499
- parentHooksNode.value = debugValueHooksNodes[0].value;
500
- } else if (debugValueHooksNodes.length > 1) {
501
- parentHooksNode.value = debugValueHooksNodes.map(({ value }) => value);
502
- }
503
- }
504
-}
505
-
506
-export function inspectHooks<Props>(
507
- renderFunction: Props => React$Node,
508
- props: Props,
509
- currentDispatcher: ReactCurrentDispatcher
510
-): HooksTree {
511
- // DevTools will pass the current renderer's injected dispatcher.
512
- // Other apps might compile debug hooks as part of their app though.
513
- //if (currentDispatcher == null) {
514
- //currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
515
- //}
516
-
517
- const previousDispatcher = currentDispatcher.current;
518
- let readHookLog;
519
- currentDispatcher.current = Dispatcher;
520
- let ancestorStackError;
521
- try {
522
- ancestorStackError = new Error();
523
- renderFunction(props);
524
- } finally {
525
- readHookLog = hookLog;
526
- hookLog = [];
527
- currentDispatcher.current = previousDispatcher;
528
- }
529
- const rootStack = ErrorStackParser.parse(ancestorStackError);
530
- return buildTree(rootStack, readHookLog);
531
-}
532
-
533
-function setupContexts(contextMap: Map<any, any>, fiber: Fiber) {
534
- let current = fiber;
535
- while (current) {
536
- if (current.tag === ContextProvider) {
537
- const providerType: any = current.type;
538
- const context: any = providerType._context;
539
- if (!contextMap.has(context)) {
540
- // Store the current value that we're going to restore later.
541
- contextMap.set(context, context._currentValue);
542
- // Set the inner most provider value on the context.
543
- context._currentValue = current.memoizedProps.value;
544
- }
545
- }
546
- current = current.return;
547
- }
548
-}
549
-
550
-function restoreContexts(contextMap: Map<any, any>) {
551
- contextMap.forEach((value, context) => (context._currentValue = value));
552
-}
553
-
554
-function inspectHooksOfForwardRef<Props, Ref>(
555
- renderFunction: (Props, Ref) => React$Node,
556
- props: Props,
557
- ref: Ref,
558
- currentDispatcher: ReactCurrentDispatcher
559
-): HooksTree {
560
- const previousDispatcher = currentDispatcher.current;
561
- let readHookLog;
562
- currentDispatcher.current = Dispatcher;
563
- let ancestorStackError;
564
- try {
565
- ancestorStackError = new Error();
566
- renderFunction(props, ref);
567
- } finally {
568
- readHookLog = hookLog;
569
- hookLog = [];
570
- currentDispatcher.current = previousDispatcher;
571
- }
572
- const rootStack = ErrorStackParser.parse(ancestorStackError);
573
- return buildTree(rootStack, readHookLog);
574
-}
575
-
576
-function resolveDefaultProps(Component, baseProps) {
577
- if (Component && Component.defaultProps) {
578
- // Resolve default props. Taken from ReactElement
579
- const props = Object.assign({}, baseProps);
580
- const defaultProps = Component.defaultProps;
581
- for (const propName in defaultProps) {
582
- if (props[propName] === undefined) {
583
- props[propName] = defaultProps[propName];
584
- }
585
- }
586
- return props;
587
- }
588
- return baseProps;
589
-}
590
-
591
-export function inspectHooksOfFiber(
592
- fiber: Fiber,
593
- currentDispatcher: ReactCurrentDispatcher
594
-) {
595
- // DevTools will pass the current renderer's injected dispatcher.
596
- // Other apps might compile debug hooks as part of their app though.
597
- //if (currentDispatcher == null) {
598
- //currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
599
- //}
600
-
601
- if (
602
- fiber.tag !== FunctionComponent &&
603
- fiber.tag !== SimpleMemoComponent &&
604
- fiber.tag !== ForwardRef
605
- ) {
606
- throw new Error(
607
- 'Unknown Fiber. Needs to be a function component to inspect hooks.'
608
- );
609
- }
610
- // Warm up the cache so that it doesn't consume the currentHook.
611
- getPrimitiveStackCache();
612
- const type = fiber.type;
613
- let props = fiber.memoizedProps;
614
- if (type !== fiber.elementType) {
615
- props = resolveDefaultProps(type, props);
616
- }
617
- // Set up the current hook so that we can step through and read the
618
- // current state from them.
619
- currentHook = (fiber.memoizedState: Hook);
620
- const contextMap = new Map();
621
- try {
622
- setupContexts(contextMap, fiber);
623
- if (fiber.tag === ForwardRef) {
624
- return inspectHooksOfForwardRef(
625
- type.render,
626
- props,
627
- fiber.ref,
628
- currentDispatcher
629
- );
630
- }
631
- return inspectHooks(type, props, currentDispatcher);
632
- } finally {
633
- currentHook = null;
634
- restoreContexts(contextMap);
635
- }
636
-}
packages/react-devtools-shared/src/backend/agent.js
+1
-1
@@ -1,6 +1,6 @@
1
// @flow
2
3
-import EventEmitter from 'events';
3
+import EventEmitter from 'node-events';
4
import throttle from 'lodash.throttle';
5
import {
6
SESSION_STORAGE_LAST_SELECTION_KEY,
packages/react-devtools-shared/src/backend/legacy/renderer.js
+35
-31
@@ -407,17 +407,18 @@ export function attach(
407
parentID: number,
408
rootID: number
409
) {
410
- const internalInstance = idToInternalInstanceMap.get(id);
411
-
410
if (__DEBUG__) {
411
console.group('crawlAndRecordInitialMounts() id:', id);
412
}
413
416
- internalInstanceToRootIDMap.set(internalInstance, rootID);
417
- recordMount(internalInstance, id, parentID);
418
- getChildren(internalInstance).forEach(child =>
419
- crawlAndRecordInitialMounts(getID(child), id, rootID)
420
- );
414
+ const internalInstance = idToInternalInstanceMap.get(id);
415
+ if (internalInstance != null) {
416
+ internalInstanceToRootIDMap.set(internalInstance, rootID);
417
+ recordMount(internalInstance, id, parentID);
418
+ getChildren(internalInstance).forEach(child =>
419
+ crawlAndRecordInitialMounts(getID(child), id, rootID)
420
+ );
421
+ }
422
423
if (__DEBUG__) {
424
console.groupEnd();
@@ -686,7 +687,12 @@ export function attach(
687
688
function inspectElementRaw(id: number): InspectedElement | null {
689
const internalInstance = idToInternalInstanceMap.get(id);
689
- const displayName = getData(internalInstance).displayName;
690
+
691
+ if (internalInstance == null) {
692
+ return null;
693
+ }
694
+
695
+ const { displayName } = getData(internalInstance);
696
const type = getElementType(internalInstance);
697
698
let context = null;
@@ -695,33 +701,31 @@ export function attach(
701
let state = null;
702
let source = null;
703
698
- if (internalInstance != null) {
699
- const element = internalInstance._currentElement;
700
- if (element !== null) {
701
- props = element.props;
702
- source = element._source != null ? element._source : null;
703
-
704
- let owner = element._owner;
705
- if (owner) {
706
- owners = [];
707
- while (owner != null) {
708
- owners.push({
709
- displayName: getData(owner).displayName || 'Unknown',
710
- id: getID(owner),
711
- type: getElementType(owner),
712
- });
713
- if (owner._currentElement) {
714
- owner = owner._currentElement._owner;
715
- }
704
+ const element = internalInstance._currentElement;
705
+ if (element !== null) {
706
+ props = element.props;
707
+ source = element._source != null ? element._source : null;
708
+
709
+ let owner = element._owner;
710
+ if (owner) {
711
+ owners = [];
712
+ while (owner != null) {
713
+ owners.push({
714
+ displayName: getData(owner).displayName || 'Unknown',
715
+ id: getID(owner),
716
+ type: getElementType(owner),
717
+ });
718
+ if (owner._currentElement) {
719
+ owner = owner._currentElement._owner;
720
}
721
}
722
}
723
+ }
724
720
- const publicInstance = internalInstance._instance;
721
- if (publicInstance != null) {
722
- context = publicInstance.context || null;
723
- state = publicInstance.state || null;
724
- }
725
+ const publicInstance = internalInstance._instance;
726
+ if (publicInstance != null) {
727
+ context = publicInstance.context || null;
728
+ state = publicInstance.state || null;
729
}
730
731
return {
packages/react-devtools-shared/src/backend/renderer.js
+5
-2
@@ -36,7 +36,7 @@ import {
36
TREE_OPERATION_REORDER_CHILDREN,
37
TREE_OPERATION_UPDATE_TREE_BASE_DURATION,
38
} from '../constants';
39
-import { inspectHooksOfFiber } from './ReactDebugHooks';
39
+import { inspectHooksOfFiber } from 'react-debug-tools';
40
import {
41
patch as patchConsole,
42
registerRenderer as registerRendererWithConsole,
@@ -285,6 +285,7 @@ export function getInternalReactConstants(
285
const symbolOrNumber =
286
typeof type === 'object' && type !== null ? type.$$typeof : type;
287
288
+ // $FlowFixMe Flow doesn't know about typeof "symbol"
289
return typeof symbolOrNumber === 'symbol'
290
? symbolOrNumber.toString()
291
: symbolOrNumber;
@@ -2746,7 +2747,9 @@ export function attach(
2747
}
2748
}
2749
const fiber = idToFiberMap.get(id);
2749
- scheduleUpdate(fiber);
2750
+ if (fiber != null) {
2751
+ scheduleUpdate(fiber);
2752
+ }
2753
}
2754
2755
// Remember if we're trying to restore the selection after reload.
packages/react-devtools-shared/src/backend/types.js
-9
@@ -343,15 +343,6 @@ export type DevToolsHook = {
343
) => void,
344
};
345
346
-export type HooksNode = {
347
- id: number,
348
- isStateEditable: boolean,
349
- name: string,
350
- value: mixed,
351
- subHooks: Array<HooksNode>,
352
-};
353
-export type HooksTree = Array<HooksNode>;
354
-
346
export type ReactEventResponder<E, C> = {
347
$$typeof: Symbol | number,
348
displayName: string,
packages/react-devtools-shared/src/backend/views/Highlighter/index.js
+1
@@ -106,6 +106,7 @@ export default function setupHighlighter(
106
if (scrollIntoView && typeof node.scrollIntoView === 'function') {
107
// If the node isn't visible show it before highlighting it.
108
// We may want to reconsider this; it might be a little disruptive.
109
+ // $FlowFixMe Flow only knows about 'start' | 'end'
110
node.scrollIntoView({ block: 'nearest', inline: 'nearest' });
111
}
112
packages/react-devtools-shared/src/bridge.js
+1
-1
@@ -1,6 +1,6 @@
1
// @flow
2
3
-import EventEmitter from 'events';
3
+import EventEmitter from 'node-events';
4
5
import type { ComponentFilter, Wall } from './types';
6
import type {
packages/react-devtools-shared/src/devtools/ProfilerStore.js
+1
-1
@@ -1,6 +1,6 @@
1
// @flow
2
3
-import EventEmitter from 'events';
3
+import EventEmitter from 'node-events';
4
import { prepareProfilingDataFrontendFromBackendAndStore } from './views/Profiler/utils';
5
import ProfilingCache from './ProfilingCache';
6
import Store from './store';
packages/react-devtools-shared/src/devtools/store.js
+2
-2
@@ -1,6 +1,6 @@
1
// @flow
2
3
-import EventEmitter from 'events';
3
+import EventEmitter from 'node-events';
4
import { inspect } from 'util';
5
import {
6
TREE_OPERATION_ADD,
@@ -18,7 +18,7 @@ import {
18
} from '../utils';
19
import { localStorageGetItem, localStorageSetItem } from '../storage';
20
import { __DEBUG__ } from '../constants';
21
-import { printStore } from '../__tests__/storeSerializer';
21
+import { printStore } from './utils';
22
import ProfilerStore from './ProfilerStore';
23
24
import type { Element } from './views/Components/types';
packages/react-devtools-shared/src/devtools/utils.js
new
+74
@@ -0,0 +1,74 @@
1
+// @flow
2
+
3
+import type { Element } from './views/Components/types';
4
+import type Store from './store';
5
+
6
+export function printElement(element: Element, includeWeight: boolean = false) {
7
+ let prefix = ' ';
8
+ if (element.children.length > 0) {
9
+ prefix = element.isCollapsed ? '▸' : '▾';
10
+ }
11
+
12
+ let key = '';
13
+ if (element.key !== null) {
14
+ key = ` key="${element.key}"`;
15
+ }
16
+
17
+ let hocs = '';
18
+ if (element.hocDisplayNames !== null) {
19
+ hocs = ` [${element.hocDisplayNames.join('][')}]`;
20
+ }
21
+
22
+ let suffix = '';
23
+ if (includeWeight) {
24
+ suffix = ` (${element.isCollapsed ? 1 : element.weight})`;
25
+ }
26
+
27
+ return `${' '.repeat(element.depth + 1)}${prefix} <${element.displayName ||
28
+ 'null'}${key}>${hocs}${suffix}`;
29
+}
30
+
31
+export function printOwnersList(elements: Array<Element>, includeWeight: boolean = false) {
32
+ return elements
33
+ .map(element => printElement(element, includeWeight))
34
+ .join('\n');
35
+}
36
+
37
+export function printStore(store: Store, includeWeight: boolean = false) {
38
+ const snapshotLines = [];
39
+
40
+ let rootWeight = 0;
41
+
42
+ store.roots.forEach(rootID => {
43
+ const { weight } = ((store.getElementByID(rootID): any): Element);
44
+
45
+ snapshotLines.push('[root]' + (includeWeight ? ` (${weight})` : ''));
46
+
47
+ for (let i = rootWeight; i < rootWeight + weight; i++) {
48
+ const element = store.getElementAtIndex(i);
49
+
50
+ if (element == null) {
51
+ throw Error(`Could not find element at index ${i}`);
52
+ }
53
+
54
+ snapshotLines.push(printElement(element, includeWeight));
55
+ }
56
+
57
+ rootWeight += weight;
58
+ });
59
+
60
+ // Make sure the pretty-printed test align with the Store's reported number of total rows.
61
+ if (rootWeight !== store.numElements) {
62
+ throw Error(
63
+ `Inconsistent Store state. Individual root weights (${rootWeight}) do not match total weight (${
64
+ store.numElements
65
+ })`
66
+ );
67
+ }
68
+
69
+ // If roots have been unmounted, verify that they've been removed from maps.
70
+ // This helps ensure the Store doesn't leak memory.
71
+ store.assertExpectedRootMapSizes();
72
+
73
+ return snapshotLines.join('\n');
74
+}
\ No newline at end of file
packages/react-devtools-shared/src/devtools/views/Components/HooksTree.js
+1
-1
@@ -14,7 +14,7 @@ import styles from './HooksTree.css';
14
import { meta } from '../../../hydration';
15
16
import type { InspectPath } from './SelectedElement';
17
-import type { HooksNode, HooksTree } from 'react-devtools-shared/src/backend/types';
17
+import type { HooksNode, HooksTree } from 'react-debug-tools/src/ReactDebugHooks';
18
19
type HooksTreeViewProps = {|
20
canEditHooks: boolean,
packages/react-devtools-shared/src/devtools/views/Components/InspectedElementContext.js
+1
-1
@@ -285,7 +285,7 @@ function InspectedElementContextController({ children }: Props) {
285
}
286
287
function hydrateHelper(
288
- dehydratedData: DehydratedData | null,
288
+ dehydratedData: any | null,
289
path?: Array<string | number>
290
): Object | null {
291
if (dehydratedData !== null) {
packages/react-devtools-shared/src/devtools/views/Components/types.js
+2
-1
@@ -1,5 +1,6 @@
1
// @flow
2
3
+import type { Dehydrated } from 'react-devtools-shared/src/hydration';
4
import type { ElementType } from 'react-devtools-shared/src/types';
5
6
// Each element on the frontend corresponds to a Fiber on the backend.
@@ -83,5 +84,5 @@ export type InspectedElement = {|
84
85
export type DehydratedData = {|
86
cleaned: Array<Array<string | number>>,
86
- data: Object,
87
+ data: string | Dehydrated | Array<Dehydrated> | { [key: string]: string | Dehydrated },
88
|};
packages/react-devtools-shared/src/devtools/views/utils.js
+1
-1
@@ -3,7 +3,7 @@
3
import escapeStringRegExp from 'escape-string-regexp';
4
import { meta } from '../../hydration';
5
6
-import type { HooksTree } from 'react-devtools-shared/src/backend/types';
6
+import type { HooksTree } from 'react-debug-tools/src/ReactDebugHooks';
7
8
export function createRegExp(string: string): RegExp {
9
// Allow /regex/ syntax with optional last /
packages/react-devtools-shared/src/hydration.js
+2
-2
@@ -27,7 +27,7 @@ export const meta = {
27
type: Symbol('type'),
28
};
29
30
-type Dehydrated = {|
30
+export type Dehydrated = {|
31
inspectable: boolean,
32
name: string | null,
33
readonly?: boolean,
@@ -176,7 +176,7 @@ export function dehydrate(
176
path: Array<string | number>,
177
isPathWhitelisted: (path: Array<string | number>) => boolean,
178
level?: number = 0
179
-): string | Dehydrated | { [key: string]: string | Dehydrated } {
179
+): string | Dehydrated | Array<Dehydrated> | { [key: string]: string | Dehydrated } {
180
const type = getDataType(data);
181
182
switch (type) {
yarn.lock
+5
@@ -4643,6 +4643,11 @@ node-cleanup@^2.1.2:
4643
version "2.1.2"
4644
resolved "https://registry.yarnpkg.com/node-cleanup/-/node-cleanup-2.1.2.tgz#7ac19abd297e09a7f72a71545d951b517e4dde2c"
4645
4646
+"node-events@npm:events@^3.0.0":
4647
+ version "3.0.0"
4648
+ resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
4649
+ integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==
4650
+
4651
node-fetch@^1.0.1, node-fetch@^1.7.3:
4652
version "1.7.3"
4653
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"