main
js 533 lines 17.3 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
11 import type {WorkTagMap} from 'react-devtools-shared/src/backend/types';
12
13 import {
14 getDisplayName,
15 getWrappedDisplayName,
16 } from 'react-devtools-shared/src/utils';
17 import {gt, gte} from 'react-devtools-shared/src/backend/utils';
18 import {
19 CONCURRENT_MODE_NUMBER,
20 CONCURRENT_MODE_SYMBOL_STRING,
21 DEPRECATED_ASYNC_MODE_SYMBOL_STRING,
22 PROVIDER_NUMBER,
23 PROVIDER_SYMBOL_STRING,
24 CONTEXT_NUMBER,
25 CONTEXT_SYMBOL_STRING,
26 CONSUMER_SYMBOL_STRING,
27 STRICT_MODE_NUMBER,
28 STRICT_MODE_SYMBOL_STRING,
29 PROFILER_NUMBER,
30 PROFILER_SYMBOL_STRING,
31 REACT_MEMO_CACHE_SENTINEL,
32 SCOPE_NUMBER,
33 SCOPE_SYMBOL_STRING,
34 FORWARD_REF_NUMBER,
35 FORWARD_REF_SYMBOL_STRING,
36 MEMO_NUMBER,
37 MEMO_SYMBOL_STRING,
38 SERVER_CONTEXT_SYMBOL_STRING,
39 } from 'react-devtools-shared/src/backend/shared/ReactSymbols';
40
41 export type getDisplayNameForFiberType = (fiber: Fiber) => string | null;
42 export type getTypeSymbolType = (type: any) => symbol | string | number;
43
44 export type ReactPriorityLevelsType = {
45 ImmediatePriority: number,
46 UserBlockingPriority: number,
47 NormalPriority: number,
48 LowPriority: number,
49 IdlePriority: number,
50 NoPriority: number,
51 };
52
53 export function getInternalReactConstants(version: string): {
54 getDisplayNameForFiber: getDisplayNameForFiberType,
55 getTypeSymbol: getTypeSymbolType,
56 ReactPriorityLevels: ReactPriorityLevelsType,
57 ReactTypeOfWork: WorkTagMap,
58 StrictModeBits: number,
59 SuspenseyImagesMode: number,
60 } {
61 // **********************************************************
62 // The section below is copied from files in React repo.
63 // Keep it in sync, and add version guards if it changes.
64 //
65 // Technically these priority levels are invalid for versions before 16.9,
66 // but 16.9 is the first version to report priority level to DevTools,
67 // so we can avoid checking for earlier versions and support pre-16.9 canary releases in the process.
68 let ReactPriorityLevels: ReactPriorityLevelsType = {
69 ImmediatePriority: 99,
70 UserBlockingPriority: 98,
71 NormalPriority: 97,
72 LowPriority: 96,
73 IdlePriority: 95,
74 NoPriority: 90,
75 };
76
77 if (gt(version, '17.0.2')) {
78 ReactPriorityLevels = {
79 ImmediatePriority: 1,
80 UserBlockingPriority: 2,
81 NormalPriority: 3,
82 LowPriority: 4,
83 IdlePriority: 5,
84 NoPriority: 0,
85 };
86 }
87
88 let StrictModeBits = 0;
89 if (gte(version, '18.0.0-alpha')) {
90 // 18+
91 StrictModeBits = 0b011000;
92 } else if (gte(version, '16.9.0')) {
93 // 16.9 - 17
94 StrictModeBits = 0b1;
95 } else if (gte(version, '16.3.0')) {
96 // 16.3 - 16.8
97 StrictModeBits = 0b10;
98 }
99
100 const SuspenseyImagesMode = 0b0100000;
101
102 let ReactTypeOfWork: WorkTagMap = null as any as WorkTagMap;
103
104 // **********************************************************
105 // The section below is copied from files in React repo.
106 // Keep it in sync, and add version guards if it changes.
107 //
108 // TODO Update the gt() check below to be gte() whichever the next version number is.
109 // Currently the version in Git is 17.0.2 (but that version has not been/may not end up being released).
110 if (gt(version, '17.0.1')) {
111 ReactTypeOfWork = {
112 CacheComponent: 24, // Experimental
113 ClassComponent: 1,
114 ContextConsumer: 9,
115 ContextProvider: 10,
116 CoroutineComponent: -1, // Removed
117 CoroutineHandlerPhase: -1, // Removed
118 DehydratedSuspenseComponent: 18, // Behind a flag
119 ForwardRef: 11,
120 Fragment: 7,
121 FunctionComponent: 0,
122 HostComponent: 5,
123 HostPortal: 4,
124 HostRoot: 3,
125 HostHoistable: 26, // In reality, 18.2+. But doesn't hurt to include it here
126 HostSingleton: 27, // Same as above
127 HostText: 6,
128 IncompleteClassComponent: 17,
129 IncompleteFunctionComponent: 28,
130 IndeterminateComponent: 2, // removed in 19.0.0
131 LazyComponent: 16,
132 LegacyHiddenComponent: 23, // Does not exist in 18+ OSS but exists in fb builds
133 MemoComponent: 14,
134 Mode: 8,
135 OffscreenComponent: 22, // Experimental in 17. Stable in 18+
136 Profiler: 12,
137 ScopeComponent: 21, // Experimental
138 SimpleMemoComponent: 15,
139 SuspenseComponent: 13,
140 SuspenseListComponent: 19, // Experimental
141 TracingMarkerComponent: 25, // Experimental - This is technically in 18 but we don't
142 // want to fork again so we're adding it here instead
143 YieldComponent: -1, // Removed
144 Throw: 29,
145 ViewTransitionComponent: 30, // Experimental
146 ActivityComponent: 31,
147 };
148 } else if (gte(version, '17.0.0-alpha')) {
149 ReactTypeOfWork = {
150 CacheComponent: -1, // Doesn't exist yet
151 ClassComponent: 1,
152 ContextConsumer: 9,
153 ContextProvider: 10,
154 CoroutineComponent: -1, // Removed
155 CoroutineHandlerPhase: -1, // Removed
156 DehydratedSuspenseComponent: 18, // Behind a flag
157 ForwardRef: 11,
158 Fragment: 7,
159 FunctionComponent: 0,
160 HostComponent: 5,
161 HostPortal: 4,
162 HostRoot: 3,
163 HostHoistable: -1, // Doesn't exist yet
164 HostSingleton: -1, // Doesn't exist yet
165 HostText: 6,
166 IncompleteClassComponent: 17,
167 IncompleteFunctionComponent: -1, // Doesn't exist yet
168 IndeterminateComponent: 2,
169 LazyComponent: 16,
170 LegacyHiddenComponent: 24,
171 MemoComponent: 14,
172 Mode: 8,
173 OffscreenComponent: 23, // Experimental
174 Profiler: 12,
175 ScopeComponent: 21, // Experimental
176 SimpleMemoComponent: 15,
177 SuspenseComponent: 13,
178 SuspenseListComponent: 19, // Experimental
179 TracingMarkerComponent: -1, // Doesn't exist yet
180 YieldComponent: -1, // Removed
181 Throw: -1, // Doesn't exist yet
182 ViewTransitionComponent: -1, // Doesn't exist yet
183 ActivityComponent: -1, // Doesn't exist yet
184 };
185 } else if (gte(version, '16.6.0-beta.0')) {
186 ReactTypeOfWork = {
187 CacheComponent: -1, // Doesn't exist yet
188 ClassComponent: 1,
189 ContextConsumer: 9,
190 ContextProvider: 10,
191 CoroutineComponent: -1, // Removed
192 CoroutineHandlerPhase: -1, // Removed
193 DehydratedSuspenseComponent: 18, // Behind a flag
194 ForwardRef: 11,
195 Fragment: 7,
196 FunctionComponent: 0,
197 HostComponent: 5,
198 HostPortal: 4,
199 HostRoot: 3,
200 HostHoistable: -1, // Doesn't exist yet
201 HostSingleton: -1, // Doesn't exist yet
202 HostText: 6,
203 IncompleteClassComponent: 17,
204 IncompleteFunctionComponent: -1, // Doesn't exist yet
205 IndeterminateComponent: 2,
206 LazyComponent: 16,
207 LegacyHiddenComponent: -1,
208 MemoComponent: 14,
209 Mode: 8,
210 OffscreenComponent: -1, // Experimental
211 Profiler: 12,
212 ScopeComponent: -1, // Experimental
213 SimpleMemoComponent: 15,
214 SuspenseComponent: 13,
215 SuspenseListComponent: 19, // Experimental
216 TracingMarkerComponent: -1, // Doesn't exist yet
217 YieldComponent: -1, // Removed
218 Throw: -1, // Doesn't exist yet
219 ViewTransitionComponent: -1, // Doesn't exist yet
220 ActivityComponent: -1, // Doesn't exist yet
221 };
222 } else if (gte(version, '16.4.3-alpha')) {
223 ReactTypeOfWork = {
224 CacheComponent: -1, // Doesn't exist yet
225 ClassComponent: 2,
226 ContextConsumer: 11,
227 ContextProvider: 12,
228 CoroutineComponent: -1, // Removed
229 CoroutineHandlerPhase: -1, // Removed
230 DehydratedSuspenseComponent: -1, // Doesn't exist yet
231 ForwardRef: 13,
232 Fragment: 9,
233 FunctionComponent: 0,
234 HostComponent: 7,
235 HostPortal: 6,
236 HostRoot: 5,
237 HostHoistable: -1, // Doesn't exist yet
238 HostSingleton: -1, // Doesn't exist yet
239 HostText: 8,
240 IncompleteClassComponent: -1, // Doesn't exist yet
241 IncompleteFunctionComponent: -1, // Doesn't exist yet
242 IndeterminateComponent: 4,
243 LazyComponent: -1, // Doesn't exist yet
244 LegacyHiddenComponent: -1,
245 MemoComponent: -1, // Doesn't exist yet
246 Mode: 10,
247 OffscreenComponent: -1, // Experimental
248 Profiler: 15,
249 ScopeComponent: -1, // Experimental
250 SimpleMemoComponent: -1, // Doesn't exist yet
251 SuspenseComponent: 16,
252 SuspenseListComponent: -1, // Doesn't exist yet
253 TracingMarkerComponent: -1, // Doesn't exist yet
254 YieldComponent: -1, // Removed
255 Throw: -1, // Doesn't exist yet
256 ViewTransitionComponent: -1, // Doesn't exist yet
257 ActivityComponent: -1, // Doesn't exist yet
258 };
259 } else {
260 ReactTypeOfWork = {
261 CacheComponent: -1, // Doesn't exist yet
262 ClassComponent: 2,
263 ContextConsumer: 12,
264 ContextProvider: 13,
265 CoroutineComponent: 7,
266 CoroutineHandlerPhase: 8,
267 DehydratedSuspenseComponent: -1, // Doesn't exist yet
268 ForwardRef: 14,
269 Fragment: 10,
270 FunctionComponent: 1,
271 HostComponent: 5,
272 HostPortal: 4,
273 HostRoot: 3,
274 HostHoistable: -1, // Doesn't exist yet
275 HostSingleton: -1, // Doesn't exist yet
276 HostText: 6,
277 IncompleteClassComponent: -1, // Doesn't exist yet
278 IncompleteFunctionComponent: -1, // Doesn't exist yet
279 IndeterminateComponent: 0,
280 LazyComponent: -1, // Doesn't exist yet
281 LegacyHiddenComponent: -1,
282 MemoComponent: -1, // Doesn't exist yet
283 Mode: 11,
284 OffscreenComponent: -1, // Experimental
285 Profiler: 15,
286 ScopeComponent: -1, // Experimental
287 SimpleMemoComponent: -1, // Doesn't exist yet
288 SuspenseComponent: 16,
289 SuspenseListComponent: -1, // Doesn't exist yet
290 TracingMarkerComponent: -1, // Doesn't exist yet
291 YieldComponent: 9,
292 Throw: -1, // Doesn't exist yet
293 ViewTransitionComponent: -1, // Doesn't exist yet
294 ActivityComponent: -1, // Doesn't exist yet
295 };
296 }
297 // **********************************************************
298 // End of copied code.
299 // **********************************************************
300
301 function getTypeSymbol(type: any): symbol | string | number {
302 const symbolOrNumber =
303 typeof type === 'object' && type !== null ? type.$$typeof : type;
304
305 return typeof symbolOrNumber === 'symbol'
306 ? symbolOrNumber.toString()
307 : symbolOrNumber;
308 }
309
310 const {
311 CacheComponent,
312 ClassComponent,
313 IncompleteClassComponent,
314 IncompleteFunctionComponent,
315 FunctionComponent,
316 IndeterminateComponent,
317 ForwardRef,
318 HostRoot,
319 HostHoistable,
320 HostSingleton,
321 HostComponent,
322 HostPortal,
323 HostText,
324 Fragment,
325 LazyComponent,
326 LegacyHiddenComponent,
327 MemoComponent,
328 OffscreenComponent,
329 Profiler,
330 ScopeComponent,
331 SimpleMemoComponent,
332 SuspenseComponent,
333 SuspenseListComponent,
334 TracingMarkerComponent,
335 Throw,
336 ViewTransitionComponent,
337 ActivityComponent,
338 } = ReactTypeOfWork;
339
340 function resolveFiberType(type: any): $FlowFixMe {
341 const typeSymbol = getTypeSymbol(type);
342 switch (typeSymbol) {
343 case MEMO_NUMBER:
344 case MEMO_SYMBOL_STRING:
345 // recursively resolving memo type in case of memo(forwardRef(Component))
346 return resolveFiberType(type.type);
347 case FORWARD_REF_NUMBER:
348 case FORWARD_REF_SYMBOL_STRING:
349 return type.render;
350 default:
351 return type;
352 }
353 }
354
355 // NOTICE Keep in sync with shouldFilterFiber() and other get*ForFiber methods
356 function getDisplayNameForFiber(
357 fiber: Fiber,
358 shouldSkipForgetCheck: boolean = false,
359 ): string | null {
360 const {elementType, type, tag} = fiber;
361
362 let resolvedType = type;
363 if (typeof type === 'object' && type !== null) {
364 resolvedType = resolveFiberType(type);
365 }
366
367 let resolvedContext: any = null;
368 if (
369 !shouldSkipForgetCheck &&
370 // $FlowFixMe[incompatible-type] fiber.updateQueue is mixed
371 (fiber.updateQueue?.memoCache != null ||
372 (Array.isArray(fiber.memoizedState?.memoizedState) &&
373 fiber.memoizedState.memoizedState[0]?.[REACT_MEMO_CACHE_SENTINEL]) ||
374 fiber.memoizedState?.memoizedState?.[REACT_MEMO_CACHE_SENTINEL])
375 ) {
376 const displayNameWithoutForgetWrapper = getDisplayNameForFiber(
377 fiber,
378 true,
379 );
380 if (displayNameWithoutForgetWrapper == null) {
381 return null;
382 }
383
384 return `Forget(${displayNameWithoutForgetWrapper})`;
385 }
386
387 switch (tag) {
388 // $FlowFixMe[invalid-compare]
389 case ActivityComponent:
390 return 'Activity';
391 // $FlowFixMe[invalid-compare]
392 case CacheComponent:
393 return 'Cache';
394 case ClassComponent:
395 // $FlowFixMe[invalid-compare] -- falls through
396 case IncompleteClassComponent:
397 // $FlowFixMe[invalid-compare] -- falls through
398 case IncompleteFunctionComponent:
399 case FunctionComponent:
400 case IndeterminateComponent:
401 return getDisplayName(resolvedType);
402 case ForwardRef:
403 return getWrappedDisplayName(
404 elementType,
405 resolvedType,
406 'ForwardRef',
407 'Anonymous',
408 );
409 case HostRoot:
410 const fiberRoot = fiber.stateNode;
411 if (fiberRoot != null && fiberRoot._debugRootType !== null) {
412 return fiberRoot._debugRootType;
413 }
414 return null;
415 case HostComponent:
416 // $FlowFixMe[invalid-compare] -- falls through
417 case HostSingleton:
418 // $FlowFixMe[invalid-compare] -- falls through
419 case HostHoistable:
420 return type;
421 case HostPortal:
422 case HostText:
423 return null;
424 case Fragment:
425 return 'Fragment';
426 // $FlowFixMe[invalid-compare]
427 case LazyComponent:
428 // This display name will not be user visible.
429 // Once a Lazy component loads its inner component, React replaces the tag and type.
430 // This display name will only show up in console logs when DevTools DEBUG mode is on.
431 return 'Lazy';
432 // $FlowFixMe[invalid-compare]
433 case MemoComponent:
434 // $FlowFixMe[invalid-compare] -- falls through
435 case SimpleMemoComponent:
436 // Display name in React does not use `Memo` as a wrapper but fallback name.
437 return getWrappedDisplayName(
438 elementType,
439 resolvedType,
440 'Memo',
441 'Anonymous',
442 );
443 case SuspenseComponent:
444 return 'Suspense';
445 // $FlowFixMe[invalid-compare]
446 case LegacyHiddenComponent:
447 return 'LegacyHidden';
448 // $FlowFixMe[invalid-compare]
449 case OffscreenComponent:
450 return 'Offscreen';
451 // $FlowFixMe[invalid-compare]
452 case ScopeComponent:
453 return 'Scope';
454 // $FlowFixMe[invalid-compare]
455 case SuspenseListComponent:
456 return 'SuspenseList';
457 case Profiler:
458 return 'Profiler';
459 // $FlowFixMe[invalid-compare]
460 case TracingMarkerComponent:
461 return 'TracingMarker';
462 // $FlowFixMe[invalid-compare]
463 case ViewTransitionComponent:
464 return 'ViewTransition';
465 // $FlowFixMe[invalid-compare]
466 case Throw:
467 // This should really never be visible.
468 return 'Error';
469 default:
470 const typeSymbol = getTypeSymbol(type);
471
472 switch (typeSymbol) {
473 case CONCURRENT_MODE_NUMBER:
474 case CONCURRENT_MODE_SYMBOL_STRING:
475 case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
476 return null;
477 case PROVIDER_NUMBER:
478 case PROVIDER_SYMBOL_STRING:
479 // 16.3.0 exposed the context object as "context"
480 // PR #12501 changed it to "_context" for 16.3.1+
481 // NOTE Keep in sync with inspectElementRaw()
482 resolvedContext = fiber.type._context || fiber.type.context;
483 return `${resolvedContext.displayName || 'Context'}.Provider`;
484 case CONTEXT_NUMBER:
485 case CONTEXT_SYMBOL_STRING:
486 case SERVER_CONTEXT_SYMBOL_STRING:
487 if (
488 fiber.type._context === undefined &&
489 fiber.type.Provider === fiber.type
490 ) {
491 // In 19+, Context.Provider === Context, so this is a provider.
492 resolvedContext = fiber.type;
493 return `${resolvedContext.displayName || 'Context'}.Provider`;
494 }
495
496 // 16.3-16.5 read from "type" because the Consumer is the actual context object.
497 // 16.6+ should read from "type._context" because Consumer can be different (in DEV).
498 // NOTE Keep in sync with inspectElementRaw()
499 resolvedContext = fiber.type._context || fiber.type;
500
501 // NOTE: TraceUpdatesBackendManager depends on the name ending in '.Consumer'
502 // If you change the name, figure out a more resilient way to detect it.
503 return `${resolvedContext.displayName || 'Context'}.Consumer`;
504 case CONSUMER_SYMBOL_STRING:
505 // 19+
506 resolvedContext = fiber.type._context;
507 return `${resolvedContext.displayName || 'Context'}.Consumer`;
508 case STRICT_MODE_NUMBER:
509 case STRICT_MODE_SYMBOL_STRING:
510 return null;
511 case PROFILER_NUMBER:
512 case PROFILER_SYMBOL_STRING:
513 return `Profiler(${fiber.memoizedProps.id})`;
514 case SCOPE_NUMBER:
515 case SCOPE_SYMBOL_STRING:
516 return 'Scope';
517 default:
518 // Unknown element type.
519 // This may mean a new element type that has not yet been added to DevTools.
520 return null;
521 }
522 }
523 }
524
525 return {
526 getDisplayNameForFiber,
527 getTypeSymbol,
528 ReactPriorityLevels,
529 ReactTypeOfWork,
530 StrictModeBits,
531 SuspenseyImagesMode,
532 };
533 }