@reckona/mreact-compat 0.0.65 → 0.0.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -3
- package/src/class-component.ts +386 -0
- package/src/context.ts +140 -0
- package/src/devtools.ts +1275 -0
- package/src/dom-children.ts +34 -0
- package/src/dom-props.ts +408 -0
- package/src/element.ts +317 -0
- package/src/event-listeners.ts +27 -0
- package/src/event-priority.ts +1 -0
- package/src/event-replay.ts +154 -0
- package/src/event-types.ts +18 -0
- package/src/events.ts +384 -0
- package/src/fiber-child.ts +364 -0
- package/src/fiber-commit.ts +83 -0
- package/src/fiber-flags.ts +21 -0
- package/src/fiber-host.ts +1564 -0
- package/src/fiber-lanes.ts +99 -0
- package/src/fiber-reconciler.ts +639 -0
- package/src/fiber-scheduler.ts +435 -0
- package/src/fiber-work-loop.ts +224 -0
- package/src/fiber.ts +148 -0
- package/src/flight-decoder.ts +205 -0
- package/src/flight-element-builder.ts +110 -0
- package/src/flight-parser.ts +698 -0
- package/src/flight-protocol.ts +71 -0
- package/src/flight-types.ts +148 -0
- package/src/flight.ts +162 -0
- package/src/hooks.ts +1940 -0
- package/src/hydration.ts +314 -0
- package/src/index.ts +95 -0
- package/src/internal.ts +7 -0
- package/src/jsx-dev-runtime.ts +40 -0
- package/src/jsx-runtime.ts +119 -0
- package/src/prop-comparison.ts +50 -0
- package/src/reconcile-types.ts +26 -0
- package/src/reconciler.ts +692 -0
- package/src/render.ts +29 -0
- package/src/root.ts +493 -0
- package/src/scheduler.ts +157 -0
- package/src/suspense.ts +317 -0
- package/src/thenable.ts +7 -0
- package/src/url-safety.ts +7 -0
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ERROR_BOUNDARY_TYPE,
|
|
3
|
+
FORWARD_REF_TYPE,
|
|
4
|
+
Fragment,
|
|
5
|
+
LAZY_TYPE,
|
|
6
|
+
MEMO_TYPE,
|
|
7
|
+
STRICT_MODE_TYPE,
|
|
8
|
+
Suspense,
|
|
9
|
+
SuspenseList,
|
|
10
|
+
createElement,
|
|
11
|
+
isReactCompatElement,
|
|
12
|
+
type LazyType,
|
|
13
|
+
type MemoType,
|
|
14
|
+
type ReactCompatNode,
|
|
15
|
+
} from "./element.js";
|
|
16
|
+
import {
|
|
17
|
+
isReactCompatConsumer,
|
|
18
|
+
isReactCompatProvider,
|
|
19
|
+
popContextProvider,
|
|
20
|
+
pushContextProvider,
|
|
21
|
+
useContext,
|
|
22
|
+
type ReactCompatProvider,
|
|
23
|
+
} from "./context.js";
|
|
24
|
+
import { reconcileChildFibers } from "./fiber-child.js";
|
|
25
|
+
import { type Fiber, type FiberRoot } from "./fiber.js";
|
|
26
|
+
import { DidCapture } from "./fiber-flags.js";
|
|
27
|
+
import { mergeLanes, NoLanes } from "./fiber-lanes.js";
|
|
28
|
+
import { isThenable } from "./thenable.js";
|
|
29
|
+
import {
|
|
30
|
+
isClassComponentType,
|
|
31
|
+
type ClassComponentInstance,
|
|
32
|
+
type ClassComponentType,
|
|
33
|
+
} from "./class-component.js";
|
|
34
|
+
import { areMemoPropsEqual } from "./prop-comparison.js";
|
|
35
|
+
|
|
36
|
+
interface ContextProviderFiberState {
|
|
37
|
+
provider: ReactCompatProvider<unknown>;
|
|
38
|
+
pushed: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface SuspenseFiberState {
|
|
42
|
+
didSuspend: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function performUnitOfWork(
|
|
46
|
+
root: FiberRoot,
|
|
47
|
+
unit: Fiber,
|
|
48
|
+
): Fiber | undefined {
|
|
49
|
+
let next: Fiber | undefined;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
next = beginWork(unit);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return captureThrownValue(root, unit, error);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (next !== undefined) {
|
|
58
|
+
return next;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return completeUnitOfWork(root, unit);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function beginWork(unit: Fiber): Fiber | undefined {
|
|
65
|
+
if (unit.tag === "host-root") {
|
|
66
|
+
const children = (unit.pendingProps as { children?: ReactCompatNode })
|
|
67
|
+
.children;
|
|
68
|
+
return reconcileChildFibers(
|
|
69
|
+
unit,
|
|
70
|
+
unit.alternate?.child,
|
|
71
|
+
children as ReactCompatNode,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (unit.tag === "host-component") {
|
|
76
|
+
const children = (unit.pendingProps as { children?: ReactCompatNode })
|
|
77
|
+
.children;
|
|
78
|
+
return reconcileChildFibers(
|
|
79
|
+
unit,
|
|
80
|
+
unit.alternate?.child,
|
|
81
|
+
children as ReactCompatNode,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (unit.tag === "fragment") {
|
|
86
|
+
return reconcileChildFibers(
|
|
87
|
+
unit,
|
|
88
|
+
unit.alternate?.child,
|
|
89
|
+
unit.pendingProps as ReactCompatNode,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (unit.tag === "strict-mode") {
|
|
94
|
+
previewStrictModeNode(
|
|
95
|
+
(unit.pendingProps as { children?: ReactCompatNode }).children,
|
|
96
|
+
);
|
|
97
|
+
return reconcileChildFibers(
|
|
98
|
+
unit,
|
|
99
|
+
unit.alternate?.child,
|
|
100
|
+
(unit.pendingProps as { children?: ReactCompatNode }).children,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (unit.tag === "function-component" && isFunctionComponentType(unit.type)) {
|
|
105
|
+
const children = unit.type(unit.pendingProps as Record<string, unknown>);
|
|
106
|
+
return reconcileChildFibers(unit, unit.alternate?.child, children);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (unit.tag === "forward-ref" && isForwardRefType(unit.type)) {
|
|
110
|
+
const props = unit.pendingProps as Record<string, unknown>;
|
|
111
|
+
const children = unit.type.render(props, props.ref ?? null);
|
|
112
|
+
return reconcileChildFibers(unit, unit.alternate?.child, children);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (unit.tag === "memo" && isMemoType(unit.type)) {
|
|
116
|
+
const previousProps = unit.alternate?.memoizedProps as
|
|
117
|
+
| Record<string, unknown>
|
|
118
|
+
| undefined;
|
|
119
|
+
const nextProps = unit.pendingProps as Record<string, unknown>;
|
|
120
|
+
|
|
121
|
+
if (
|
|
122
|
+
unit.alternate !== undefined &&
|
|
123
|
+
previousProps !== undefined &&
|
|
124
|
+
areMemoPropsEqual(unit.type, previousProps, nextProps)
|
|
125
|
+
) {
|
|
126
|
+
unit.child = unit.alternate.child;
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return reconcileChildFibers(
|
|
131
|
+
unit,
|
|
132
|
+
unit.alternate?.child,
|
|
133
|
+
createElement(unit.type.type, nextProps),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (unit.tag === "context-provider" && isReactCompatProvider(unit.type)) {
|
|
138
|
+
const props = unit.pendingProps as {
|
|
139
|
+
value: unknown;
|
|
140
|
+
children?: ReactCompatNode;
|
|
141
|
+
};
|
|
142
|
+
pushContextProvider(unit.type, props.value);
|
|
143
|
+
unit.memoizedState = {
|
|
144
|
+
provider: unit.type,
|
|
145
|
+
pushed: true,
|
|
146
|
+
} satisfies ContextProviderFiberState;
|
|
147
|
+
return reconcileChildFibers(unit, unit.alternate?.child, props.children);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (unit.tag === "context-consumer" && isReactCompatConsumer(unit.type)) {
|
|
151
|
+
const props = unit.pendingProps as {
|
|
152
|
+
children?: unknown;
|
|
153
|
+
};
|
|
154
|
+
const render =
|
|
155
|
+
typeof props.children === "function"
|
|
156
|
+
? (props.children as (value: unknown) => ReactCompatNode)
|
|
157
|
+
: () => null;
|
|
158
|
+
return reconcileChildFibers(
|
|
159
|
+
unit,
|
|
160
|
+
unit.alternate?.child,
|
|
161
|
+
render(useContext(unit.type.context)),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (unit.tag === "class-component" && isClassComponentType(unit.type)) {
|
|
166
|
+
return beginClassComponent(unit, unit.type);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (unit.tag === "lazy" && isLazyType(unit.type)) {
|
|
170
|
+
const lazyType = unit.type;
|
|
171
|
+
|
|
172
|
+
if (lazyType.status === "resolved" && lazyType.resolved !== undefined) {
|
|
173
|
+
return reconcileChildFibers(
|
|
174
|
+
unit,
|
|
175
|
+
unit.alternate?.child,
|
|
176
|
+
createElement(lazyType.resolved, unit.pendingProps as Record<string, unknown>),
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (lazyType.status === "rejected") {
|
|
181
|
+
throw lazyType.error;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (lazyType.status === "uninitialized") {
|
|
185
|
+
lazyType.status = "pending";
|
|
186
|
+
lazyType.promise = lazyType
|
|
187
|
+
.load()
|
|
188
|
+
.then((module) => {
|
|
189
|
+
lazyType.status = "resolved";
|
|
190
|
+
lazyType.resolved = module.default;
|
|
191
|
+
})
|
|
192
|
+
.catch((error: unknown) => {
|
|
193
|
+
lazyType.status = "rejected";
|
|
194
|
+
lazyType.error = error;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
throw lazyType.promise;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (unit.tag === "suspense" && unit.type === Suspense) {
|
|
202
|
+
unit.memoizedState = { didSuspend: false } satisfies SuspenseFiberState;
|
|
203
|
+
return reconcileChildFibers(
|
|
204
|
+
unit,
|
|
205
|
+
unit.alternate?.child,
|
|
206
|
+
(unit.pendingProps as { children?: ReactCompatNode }).children,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (unit.tag === "suspense-list" && unit.type === SuspenseList) {
|
|
211
|
+
return reconcileChildFibers(
|
|
212
|
+
unit,
|
|
213
|
+
unit.alternate?.child,
|
|
214
|
+
(unit.pendingProps as { children?: ReactCompatNode }).children,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (unit.tag === "error-boundary" && unit.type === ERROR_BOUNDARY_TYPE) {
|
|
219
|
+
return reconcileChildFibers(
|
|
220
|
+
unit,
|
|
221
|
+
unit.alternate?.child,
|
|
222
|
+
(unit.pendingProps as { children?: ReactCompatNode }).children,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function previewStrictModeNode(node: ReactCompatNode): void {
|
|
230
|
+
if (
|
|
231
|
+
node === null ||
|
|
232
|
+
node === undefined ||
|
|
233
|
+
typeof node === "boolean" ||
|
|
234
|
+
typeof node === "string" ||
|
|
235
|
+
typeof node === "number"
|
|
236
|
+
) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (Array.isArray(node)) {
|
|
241
|
+
for (const child of node) {
|
|
242
|
+
previewStrictModeNode(child);
|
|
243
|
+
}
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (!isReactCompatElement(node)) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (isClassComponentType(node.type)) {
|
|
252
|
+
const instance = new node.type(node.props as Record<string, unknown>);
|
|
253
|
+
previewStrictModeNode(instance.render());
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (isForwardRefType(node.type)) {
|
|
258
|
+
previewStrictModeNode(
|
|
259
|
+
node.type.render(
|
|
260
|
+
node.props as Record<string, unknown>,
|
|
261
|
+
(node.props as { ref?: unknown }).ref ?? null,
|
|
262
|
+
),
|
|
263
|
+
);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (isMemoType(node.type)) {
|
|
268
|
+
previewStrictModeNode(createElement(node.type.type, node.props));
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (typeof node.type === "function") {
|
|
273
|
+
const component = node.type as (props: Record<string, unknown>) => ReactCompatNode;
|
|
274
|
+
previewStrictModeNode(
|
|
275
|
+
component(node.props as Record<string, unknown>),
|
|
276
|
+
);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
previewStrictModeNode(node.props.children as ReactCompatNode);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function completeWork(unit: Fiber): void {
|
|
284
|
+
if (unit.tag === "context-provider") {
|
|
285
|
+
popPushedContextProvider(unit);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (unit.tag === "host-component") {
|
|
289
|
+
const current = unit.alternate;
|
|
290
|
+
|
|
291
|
+
unit.stateNode =
|
|
292
|
+
current?.tag === "host-component" &&
|
|
293
|
+
current.type === unit.type &&
|
|
294
|
+
current.stateNode instanceof HTMLElement
|
|
295
|
+
? current.stateNode
|
|
296
|
+
: document.createElement(String(unit.type));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (unit.tag === "host-text") {
|
|
301
|
+
const current = unit.alternate;
|
|
302
|
+
|
|
303
|
+
unit.stateNode =
|
|
304
|
+
current?.tag === "host-text" && current.stateNode instanceof Text
|
|
305
|
+
? current.stateNode
|
|
306
|
+
: document.createTextNode("");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export function cleanupUnfinishedWork(unit: Fiber | undefined): void {
|
|
311
|
+
if (unit === undefined) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
let cursor: Fiber | undefined = unit;
|
|
316
|
+
|
|
317
|
+
while (cursor !== undefined) {
|
|
318
|
+
cleanupUnfinishedWork(cursor.child);
|
|
319
|
+
popPushedContextProvider(cursor);
|
|
320
|
+
cursor = cursor.sibling;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function completeUnitOfWork(
|
|
325
|
+
root: FiberRoot,
|
|
326
|
+
completedWork: Fiber,
|
|
327
|
+
): Fiber | undefined {
|
|
328
|
+
let unit: Fiber | undefined = completedWork;
|
|
329
|
+
|
|
330
|
+
while (unit !== undefined) {
|
|
331
|
+
completeWork(unit);
|
|
332
|
+
bubbleCompletedProperties(unit);
|
|
333
|
+
|
|
334
|
+
if (unit.sibling !== undefined) {
|
|
335
|
+
return unit.sibling;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (unit.return === undefined) {
|
|
339
|
+
unit.memoizedProps = unit.pendingProps;
|
|
340
|
+
root.finishedWork = unit;
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
unit.memoizedProps = unit.pendingProps;
|
|
345
|
+
unit = unit.return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function bubbleCompletedProperties(unit: Fiber): void {
|
|
352
|
+
let subtreeFlags = unit.subtreeFlags;
|
|
353
|
+
let childLanes = NoLanes;
|
|
354
|
+
let child = unit.child;
|
|
355
|
+
|
|
356
|
+
while (child !== undefined) {
|
|
357
|
+
subtreeFlags |= child.subtreeFlags | child.flags;
|
|
358
|
+
childLanes = mergeLanes(childLanes, mergeLanes(child.lanes, child.childLanes));
|
|
359
|
+
child.return = unit;
|
|
360
|
+
child = child.sibling;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
unit.subtreeFlags = subtreeFlags;
|
|
364
|
+
unit.childLanes = childLanes;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function canReconcileConcurrently(node: ReactCompatNode): boolean {
|
|
368
|
+
if (
|
|
369
|
+
node === null ||
|
|
370
|
+
node === undefined ||
|
|
371
|
+
typeof node === "boolean" ||
|
|
372
|
+
typeof node === "string" ||
|
|
373
|
+
typeof node === "number"
|
|
374
|
+
) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (Array.isArray(node)) {
|
|
379
|
+
return node.every(canReconcileConcurrently);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (!isReactCompatElement(node)) {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (
|
|
387
|
+
typeof node.type === "string" ||
|
|
388
|
+
node.type === Fragment ||
|
|
389
|
+
node.type === STRICT_MODE_TYPE
|
|
390
|
+
) {
|
|
391
|
+
return canReconcileConcurrently(node.props.children as ReactCompatNode);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (node.type === Suspense || node.type === SuspenseList) {
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (node.type === ERROR_BOUNDARY_TYPE) {
|
|
399
|
+
return canReconcileConcurrently(node.props.children as ReactCompatNode);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (isReactCompatProvider(node.type)) {
|
|
403
|
+
return canReconcileConcurrently(node.props.children as ReactCompatNode);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (isReactCompatConsumer(node.type)) {
|
|
407
|
+
return typeof node.props.children === "function";
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (isMemoType(node.type)) {
|
|
411
|
+
return canReconcileElementTypeConcurrently(node.type.type);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return (
|
|
415
|
+
isFunctionComponentType(node.type) ||
|
|
416
|
+
isForwardRefType(node.type) ||
|
|
417
|
+
isLazyType(node.type) ||
|
|
418
|
+
isClassComponentType(node.type)
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function popPushedContextProvider(unit: Fiber): void {
|
|
423
|
+
const state = unit.memoizedState as ContextProviderFiberState | undefined;
|
|
424
|
+
|
|
425
|
+
if (state?.pushed === true) {
|
|
426
|
+
popContextProvider(state.provider);
|
|
427
|
+
state.pushed = false;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function isFunctionComponentType(
|
|
432
|
+
type: unknown,
|
|
433
|
+
): type is (props: Record<string, unknown>) => ReactCompatNode {
|
|
434
|
+
return typeof type === "function";
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function isForwardRefType(
|
|
438
|
+
type: unknown,
|
|
439
|
+
): type is {
|
|
440
|
+
$$typeof: typeof FORWARD_REF_TYPE;
|
|
441
|
+
render: (props: Record<string, unknown>, ref: unknown) => ReactCompatNode;
|
|
442
|
+
} {
|
|
443
|
+
return (
|
|
444
|
+
typeof type === "object" &&
|
|
445
|
+
type !== null &&
|
|
446
|
+
(type as { $$typeof?: unknown }).$$typeof === FORWARD_REF_TYPE
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function isMemoType(
|
|
451
|
+
type: unknown,
|
|
452
|
+
): type is MemoType<Record<string, unknown>> {
|
|
453
|
+
return (
|
|
454
|
+
typeof type === "object" &&
|
|
455
|
+
type !== null &&
|
|
456
|
+
(type as { $$typeof?: unknown }).$$typeof === MEMO_TYPE
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function isLazyType(
|
|
461
|
+
type: unknown,
|
|
462
|
+
): type is LazyType<Record<string, unknown>> {
|
|
463
|
+
return (
|
|
464
|
+
typeof type === "object" &&
|
|
465
|
+
type !== null &&
|
|
466
|
+
(type as { $$typeof?: unknown }).$$typeof === LAZY_TYPE
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function canReconcileElementTypeConcurrently(type: unknown): boolean {
|
|
471
|
+
return (
|
|
472
|
+
typeof type === "string" ||
|
|
473
|
+
type === Fragment ||
|
|
474
|
+
isFunctionComponentType(type) ||
|
|
475
|
+
isForwardRefType(type) ||
|
|
476
|
+
isLazyType(type) ||
|
|
477
|
+
(isMemoType(type) && canReconcileElementTypeConcurrently(type.type))
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function captureThrownValue(
|
|
482
|
+
root: FiberRoot,
|
|
483
|
+
source: Fiber,
|
|
484
|
+
thrownValue: unknown,
|
|
485
|
+
): Fiber | undefined {
|
|
486
|
+
let boundary = source.return;
|
|
487
|
+
|
|
488
|
+
while (boundary !== undefined) {
|
|
489
|
+
if (isThenable(thrownValue) && boundary.tag === "suspense") {
|
|
490
|
+
return captureSuspenseBoundary(root, boundary);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (!isThenable(thrownValue) && boundary.tag === "error-boundary") {
|
|
494
|
+
return captureErrorBoundary(root, boundary, thrownValue);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (!isThenable(thrownValue) && boundary.tag === "class-component") {
|
|
498
|
+
const captured = captureClassErrorBoundary(root, boundary, thrownValue);
|
|
499
|
+
|
|
500
|
+
if (captured !== undefined) {
|
|
501
|
+
return captured;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
boundary = boundary.return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
throw thrownValue;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function beginClassComponent(
|
|
512
|
+
unit: Fiber,
|
|
513
|
+
type: ClassComponentType,
|
|
514
|
+
): Fiber | undefined {
|
|
515
|
+
const nextProps = unit.pendingProps as Record<string, unknown>;
|
|
516
|
+
const currentInstance =
|
|
517
|
+
unit.alternate?.stateNode instanceof type
|
|
518
|
+
? unit.alternate.stateNode
|
|
519
|
+
: undefined;
|
|
520
|
+
const instance = currentInstance ?? new type(nextProps);
|
|
521
|
+
const nextState = instance.state ?? {};
|
|
522
|
+
|
|
523
|
+
unit.stateNode = instance;
|
|
524
|
+
|
|
525
|
+
if (
|
|
526
|
+
unit.alternate !== undefined &&
|
|
527
|
+
instance.shouldComponentUpdate?.(nextProps, nextState) === false
|
|
528
|
+
) {
|
|
529
|
+
instance.props = nextProps;
|
|
530
|
+
unit.child = unit.alternate.child;
|
|
531
|
+
return undefined;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
instance.props = nextProps;
|
|
535
|
+
return reconcileChildFibers(unit, unit.alternate?.child, instance.render());
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function captureClassErrorBoundary(
|
|
539
|
+
root: FiberRoot,
|
|
540
|
+
boundary: Fiber,
|
|
541
|
+
thrownValue: unknown,
|
|
542
|
+
): Fiber | undefined {
|
|
543
|
+
if (!isClassComponentType(boundary.type)) {
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const instance = boundary.stateNode as ClassComponentInstance | undefined;
|
|
548
|
+
|
|
549
|
+
if (instance === undefined || !isClassErrorBoundary(boundary.type, instance)) {
|
|
550
|
+
return undefined;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
cleanupUnfinishedWork(boundary.child);
|
|
554
|
+
boundary.flags |= DidCapture;
|
|
555
|
+
const error = thrownValue instanceof Error ? thrownValue : new Error(String(thrownValue));
|
|
556
|
+
const derivedState = boundary.type.getDerivedStateFromError?.(error);
|
|
557
|
+
|
|
558
|
+
if (derivedState !== undefined && derivedState !== null) {
|
|
559
|
+
instance.state = {
|
|
560
|
+
...instance.state,
|
|
561
|
+
...derivedState,
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
instance.componentDidCatch?.(error, { componentStack: "" });
|
|
566
|
+
boundary.child = reconcileChildFibers(
|
|
567
|
+
boundary,
|
|
568
|
+
boundary.alternate?.child,
|
|
569
|
+
instance.render(),
|
|
570
|
+
);
|
|
571
|
+
return boundary.child ?? completeUnitOfWork(root, boundary);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function isClassErrorBoundary(
|
|
575
|
+
type: ClassComponentType,
|
|
576
|
+
instance: ClassComponentInstance,
|
|
577
|
+
): boolean {
|
|
578
|
+
return (
|
|
579
|
+
typeof type.getDerivedStateFromError === "function" ||
|
|
580
|
+
typeof instance.componentDidCatch === "function"
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function captureSuspenseBoundary(
|
|
585
|
+
root: FiberRoot,
|
|
586
|
+
boundary: Fiber,
|
|
587
|
+
): Fiber | undefined {
|
|
588
|
+
cleanupUnfinishedWork(boundary.child);
|
|
589
|
+
boundary.flags |= DidCapture;
|
|
590
|
+
boundary.memoizedState = { didSuspend: true } satisfies SuspenseFiberState;
|
|
591
|
+
boundary.child = reconcileChildFibers(
|
|
592
|
+
boundary,
|
|
593
|
+
boundary.alternate?.child,
|
|
594
|
+
(boundary.pendingProps as { fallback?: ReactCompatNode }).fallback,
|
|
595
|
+
);
|
|
596
|
+
applySuspenseListRevealOrder(boundary);
|
|
597
|
+
return boundary.child ?? completeUnitOfWork(root, boundary);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function captureErrorBoundary(
|
|
601
|
+
root: FiberRoot,
|
|
602
|
+
boundary: Fiber,
|
|
603
|
+
thrownValue: unknown,
|
|
604
|
+
): Fiber | undefined {
|
|
605
|
+
cleanupUnfinishedWork(boundary.child);
|
|
606
|
+
boundary.flags |= DidCapture;
|
|
607
|
+
const error = thrownValue instanceof Error ? thrownValue : new Error(String(thrownValue));
|
|
608
|
+
const props = boundary.pendingProps as {
|
|
609
|
+
fallback?: unknown;
|
|
610
|
+
onError?: unknown;
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
if (typeof props.onError === "function") {
|
|
614
|
+
(props.onError as (error: Error) => void)(error);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const fallback =
|
|
618
|
+
typeof props.fallback === "function"
|
|
619
|
+
? (props.fallback as (error: Error) => ReactCompatNode)(error)
|
|
620
|
+
: null;
|
|
621
|
+
boundary.child = reconcileChildFibers(boundary, boundary.alternate?.child, fallback);
|
|
622
|
+
return boundary.child ?? completeUnitOfWork(root, boundary);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function applySuspenseListRevealOrder(boundary: Fiber): void {
|
|
626
|
+
const parent = boundary.return;
|
|
627
|
+
|
|
628
|
+
if (parent?.tag !== "suspense-list") {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const revealOrder = (parent.pendingProps as { revealOrder?: unknown }).revealOrder;
|
|
633
|
+
|
|
634
|
+
if (revealOrder === "forwards") {
|
|
635
|
+
boundary.sibling = undefined;
|
|
636
|
+
} else if (revealOrder === "backwards") {
|
|
637
|
+
parent.child = boundary;
|
|
638
|
+
}
|
|
639
|
+
}
|