-
Notifications
You must be signed in to change notification settings - Fork 48.3k
/
Copy pathinspectedElementMutableSource.js
132 lines (112 loc) · 3.67 KB
/
inspectedElementMutableSource.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
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import {
convertInspectedElementBackendToFrontend,
hydrateHelper,
inspectElement as inspectElementAPI,
} from 'react-devtools-shared/src/backendAPI';
import {fillInPath} from 'react-devtools-shared/src/hydration';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type {
InspectElementFullData,
InspectElementHydratedPath,
} from 'react-devtools-shared/src/backend/types';
import type {
Element,
InspectedElement as InspectedElementFrontend,
InspectedElementResponseType,
} from 'react-devtools-shared/src/devtools/views/Components/types';
// Map an Element in the Store to the most recent copy of its inspected data.
// As updates comes from the backend, inspected data is updated.
// Both this map and the inspected objects in it are mutable.
// They should never be read from directly during render;
// Use a Suspense cache to ensure that transitions work correctly and there is no tearing.
const inspectedElementMap: WeakMap<
Element,
InspectedElementFrontend,
> = new WeakMap();
type Path = Array<string | number>;
type InspectElementReturnType = [
InspectedElementFrontend,
InspectedElementResponseType,
];
export function inspectElement({
bridge,
element,
path,
rendererID,
}: {|
bridge: FrontendBridge,
element: Element,
path: Path | null,
rendererID: number,
|}): Promise<InspectElementReturnType> {
const {id} = element;
return inspectElementAPI({
bridge,
id,
path,
rendererID,
}).then((data: any) => {
const {type} = data;
let inspectedElement;
switch (type) {
case 'no-change':
// This is a no-op for the purposes of our cache.
inspectedElement = inspectedElementMap.get(element);
if (inspectedElement != null) {
return [inspectedElement, type];
}
break;
case 'not-found':
// This is effectively a no-op.
// If the Element is still in the Store, we can eagerly remove it from the Map.
inspectedElementMap.delete(element);
throw Error(`Element ${id} not found`);
case 'full-data':
const fullData = ((data: any): InspectElementFullData);
// New data has come in.
// We should replace the data in our local mutable copy.
inspectedElement = convertInspectedElementBackendToFrontend(
fullData.value,
);
inspectedElementMap.set(element, inspectedElement);
return [inspectedElement, type];
case 'hydrated-path':
const hydratedPathData = ((data: any): InspectElementHydratedPath);
const {value} = hydratedPathData;
// A path has been hydrated.
// Merge it with the latest copy we have locally and resolve with the merged value.
inspectedElement = inspectedElementMap.get(element) || null;
if (inspectedElement !== null) {
// Clone element
inspectedElement = {...inspectedElement};
// Merge hydrated data
fillInPath(
inspectedElement,
value,
((path: any): Path),
hydrateHelper(value, ((path: any): Path)),
);
inspectedElementMap.set(element, inspectedElement);
return [inspectedElement, type];
}
break;
default:
// Should never happen.
if (__DEV__) {
console.error(
`Unexpected inspected element response data: "${type}"`,
);
}
break;
}
throw Error(`Unable to inspect element with id ${id}`);
});
}