@samitouri / QOS-React-2 / commits / edc46d7be7

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"