@tanstack/redact 0.0.21 → 0.1.1
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/dist/core/attributes.js +2 -1
- package/dist/core/attributes.js.map +2 -2
- package/dist/core/browser.d.ts +9 -0
- package/dist/core/browser.js +35 -0
- package/dist/core/browser.js.map +7 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +3 -3
- package/dist/core/internal.d.ts +31 -8
- package/dist/core/internal.js +3 -1
- package/dist/core/internal.js.map +2 -2
- package/dist/core/resource-hints.d.ts +7 -0
- package/dist/core/resource-hints.js +19 -0
- package/dist/core/resource-hints.js.map +7 -0
- package/dist/core/types.js +1 -1
- package/dist/dom/_all.d.ts +11 -16
- package/dist/dom/_all.js +9 -20
- package/dist/dom/_all.js.map +2 -2
- package/dist/dom/client.d.ts +4 -4
- package/dist/dom/client.js +4 -4
- package/dist/dom/commit.d.ts +23 -0
- package/dist/dom/commit.js +191 -0
- package/dist/dom/commit.js.map +7 -0
- package/dist/dom/dispatcher.d.ts +4 -22
- package/dist/dom/dispatcher.js +184 -126
- package/dist/dom/dispatcher.js.map +3 -3
- package/dist/dom/dom.d.ts +2 -0
- package/dist/dom/dom.js +72 -52
- package/dist/dom/dom.js.map +2 -2
- package/dist/dom/error-info.d.ts +2 -0
- package/dist/dom/error-info.js +16 -0
- package/dist/dom/error-info.js.map +7 -0
- package/dist/dom/event-replay.js +1 -1
- package/dist/dom/features/activity/full.d.ts +1 -0
- package/dist/dom/features/activity/full.js +162 -0
- package/dist/dom/features/activity/full.js.map +7 -0
- package/dist/dom/features/activity/index.d.ts +1 -0
- package/dist/dom/features/activity/index.js +3 -0
- package/dist/dom/features/activity/index.js.map +7 -0
- package/dist/dom/features/activity/stub.d.ts +1 -0
- package/dist/dom/features/activity/stub.js +12 -0
- package/dist/dom/features/activity/stub.js.map +7 -0
- package/dist/dom/features/class/full.js +87 -27
- package/dist/dom/features/class/full.js.map +2 -2
- package/dist/dom/features/class/index.d.ts +1 -1
- package/dist/dom/features/class/index.js +2 -2
- package/dist/dom/features/class/stub.js +3 -3
- package/dist/dom/features/context/full.d.ts +4 -1
- package/dist/dom/features/context/full.js +72 -15
- package/dist/dom/features/context/full.js.map +2 -2
- package/dist/dom/features/context/index.d.ts +1 -1
- package/dist/dom/features/context/index.js +2 -2
- package/dist/dom/features/context/stub.d.ts +4 -1
- package/dist/dom/features/context/stub.js +17 -8
- package/dist/dom/features/context/stub.js.map +2 -2
- package/dist/dom/features/forward-ref/full.js +12 -21
- package/dist/dom/features/forward-ref/full.js.map +2 -2
- package/dist/dom/features/forward-ref/index.d.ts +1 -1
- package/dist/dom/features/forward-ref/index.js +2 -2
- package/dist/dom/features/forward-ref/stub.js +4 -4
- package/dist/dom/features/fragment-refs/index.d.ts +1 -0
- package/dist/dom/features/fragment-refs/index.js +8 -0
- package/dist/dom/features/fragment-refs/index.js.map +7 -0
- package/dist/dom/features/fragment-refs/stub.d.ts +3 -0
- package/dist/dom/features/fragment-refs/stub.js +14 -0
- package/dist/dom/features/fragment-refs/stub.js.map +7 -0
- package/dist/dom/features/hydration/full.d.ts +11 -6
- package/dist/dom/features/hydration/full.js +76 -77
- package/dist/dom/features/hydration/full.js.map +2 -2
- package/dist/dom/features/hydration/index.d.ts +1 -1
- package/dist/dom/features/hydration/index.js +2 -2
- package/dist/dom/features/hydration/stub.d.ts +7 -2
- package/dist/dom/features/hydration/stub.js +1 -1
- package/dist/dom/features/hydration/stub.js.map +1 -1
- package/dist/dom/features/index.d.ts +8 -7
- package/dist/dom/features/index.js +9 -8
- package/dist/dom/features/index.js.map +2 -2
- package/dist/dom/features/lazy/full.js +4 -4
- package/dist/dom/features/lazy/index.d.ts +1 -1
- package/dist/dom/features/lazy/index.js +2 -2
- package/dist/dom/features/lazy/stub.js +4 -4
- package/dist/dom/features/memo/full.js +8 -6
- package/dist/dom/features/memo/full.js.map +2 -2
- package/dist/dom/features/memo/index.d.ts +1 -1
- package/dist/dom/features/memo/index.js +2 -2
- package/dist/dom/features/memo/stub.js +4 -4
- package/dist/dom/features/portal/full.js +4 -4
- package/dist/dom/features/portal/index.d.ts +1 -1
- package/dist/dom/features/portal/index.js +2 -2
- package/dist/dom/features/portal/stub.js +4 -4
- package/dist/dom/features/suspense/full.js +197 -52
- package/dist/dom/features/suspense/full.js.map +2 -2
- package/dist/dom/features/suspense/index.d.ts +1 -1
- package/dist/dom/features/suspense/index.js +2 -2
- package/dist/dom/features/suspense/stub.js +4 -4
- package/dist/dom/features/view-transition/full.d.ts +6 -0
- package/dist/dom/features/view-transition/full.js +280 -0
- package/dist/dom/features/view-transition/full.js.map +7 -0
- package/dist/dom/features/view-transition/index.d.ts +1 -0
- package/dist/dom/features/view-transition/index.js +3 -0
- package/dist/dom/features/view-transition/index.js.map +7 -0
- package/dist/dom/features/view-transition/resources.d.ts +11 -0
- package/dist/dom/features/view-transition/resources.js +108 -0
- package/dist/dom/features/view-transition/resources.js.map +7 -0
- package/dist/dom/features/view-transition/stub.d.ts +6 -0
- package/dist/dom/features/view-transition/stub.js +22 -0
- package/dist/dom/features/view-transition/stub.js.map +7 -0
- package/dist/dom/features/view-transition/visual.d.ts +14 -0
- package/dist/dom/features/view-transition/visual.js +356 -0
- package/dist/dom/features/view-transition/visual.js.map +7 -0
- package/dist/dom/fragment-instance.d.ts +40 -0
- package/dist/dom/fragment-instance.js +324 -0
- package/dist/dom/fragment-instance.js.map +7 -0
- package/dist/dom/index.d.ts +11 -11
- package/dist/dom/index.js +11 -18
- package/dist/dom/index.js.map +3 -3
- package/dist/dom/portal.d.ts +2 -2
- package/dist/dom/portal.js +2 -2
- package/dist/dom/portal.js.map +1 -1
- package/dist/dom/reconcile.d.ts +32 -3
- package/dist/dom/reconcile.js +689 -221
- package/dist/dom/reconcile.js.map +2 -2
- package/dist/dom/resource-hints.d.ts +6 -0
- package/dist/dom/resource-hints.js +115 -0
- package/dist/dom/resource-hints.js.map +7 -0
- package/dist/dom/resource-store.d.ts +13 -0
- package/dist/dom/resource-store.js +43 -0
- package/dist/dom/resource-store.js.map +7 -0
- package/dist/dom/resources.d.ts +1 -0
- package/dist/dom/resources.js +44 -0
- package/dist/dom/resources.js.map +7 -0
- package/dist/dom/retained-effects.d.ts +6 -0
- package/dist/dom/retained-effects.js +49 -0
- package/dist/dom/retained-effects.js.map +7 -0
- package/dist/dom/root-internal.d.ts +4 -4
- package/dist/dom/root-internal.js +2 -2
- package/dist/dom/root-internal.js.map +2 -2
- package/dist/dom/root.d.ts +5 -5
- package/dist/dom/root.js +11 -11
- package/dist/dom/root.js.map +2 -2
- package/dist/dom/test-utils.js +1 -1
- package/dist/react/activity.d.ts +7 -0
- package/dist/react/activity.js +8 -0
- package/dist/react/activity.js.map +7 -0
- package/dist/react/children.d.ts +1 -1
- package/dist/react/children.js +3 -3
- package/dist/react/children.js.map +1 -1
- package/dist/react/class.d.ts +1 -1
- package/dist/react/class.js +3 -3
- package/dist/react/class.js.map +1 -1
- package/dist/react/compiler-runtime.js +2 -2
- package/dist/react/context.d.ts +7 -2
- package/dist/react/context.js +3 -11
- package/dist/react/context.js.map +3 -3
- package/dist/react/element.d.ts +6 -3
- package/dist/react/element.js +7 -11
- package/dist/react/element.js.map +2 -2
- package/dist/react/hooks.d.ts +26 -7
- package/dist/react/hooks.js +13 -3
- package/dist/react/hooks.js.map +2 -2
- package/dist/react/index.d.ts +39 -24
- package/dist/react/index.js +39 -19
- package/dist/react/index.js.map +3 -3
- package/dist/react/jsx-runtime.d.ts +2 -2
- package/dist/react/jsx-runtime.js +3 -3
- package/dist/react/memo.d.ts +1 -1
- package/dist/react/memo.js +1 -1
- package/dist/react/portal.js +1 -1
- package/dist/react/shared-internals.d.ts +15 -5
- package/dist/react/shared-internals.js +2 -3
- package/dist/react/shared-internals.js.map +2 -2
- package/dist/react/suspense.js +1 -1
- package/dist/react/view-transition.d.ts +32 -0
- package/dist/react/view-transition.js +13 -0
- package/dist/react/view-transition.js.map +7 -0
- package/dist/scheduler/index.js +1 -1
- package/dist/server/bootstrap-script.d.ts +1 -23
- package/dist/server/bootstrap-script.js +7 -6
- package/dist/server/bootstrap-script.js.map +2 -2
- package/dist/server/dispatcher.d.ts +28 -6
- package/dist/server/dispatcher.js +33 -15
- package/dist/server/dispatcher.js.map +2 -2
- package/dist/server/escape.js +16 -15
- package/dist/server/escape.js.map +2 -2
- package/dist/server/index.d.ts +6 -6
- package/dist/server/index.js +7 -7
- package/dist/server/renderToString.d.ts +1 -1
- package/dist/server/renderToString.js +12 -8
- package/dist/server/renderToString.js.map +2 -2
- package/dist/server/resource-hints.d.ts +22 -0
- package/dist/server/resource-hints.js +148 -0
- package/dist/server/resource-hints.js.map +7 -0
- package/dist/server/stream.d.ts +15 -2
- package/dist/server/stream.js +155 -63
- package/dist/server/stream.js.map +2 -2
- package/dist/server/walk.d.ts +15 -2
- package/dist/server/walk.js +150 -110
- package/dist/server/walk.js.map +2 -2
- package/dist/vite/index.d.ts +6 -0
- package/dist/vite/index.js +22 -6
- package/dist/vite/index.js.map +2 -2
- package/package.json +1 -1
- package/src/core/attributes.ts +1 -0
- package/src/core/browser.ts +33 -0
- package/src/core/internal.ts +24 -6
- package/src/core/resource-hints.ts +36 -0
- package/src/dom/_all.ts +1 -7
- package/src/dom/commit.ts +186 -0
- package/src/dom/dispatcher.ts +190 -134
- package/src/dom/dom.ts +70 -54
- package/src/dom/error-info.ts +15 -0
- package/src/dom/features/activity/full.ts +166 -0
- package/src/dom/features/activity/index.ts +1 -0
- package/src/dom/features/activity/stub.ts +14 -0
- package/src/dom/features/class/full.ts +72 -28
- package/src/dom/features/context/full.ts +70 -13
- package/src/dom/features/context/stub.ts +10 -5
- package/src/dom/features/forward-ref/full.ts +9 -19
- package/src/dom/features/fragment-refs/index.ts +1 -0
- package/src/dom/features/fragment-refs/stub.ts +3 -0
- package/src/dom/features/hydration/full.ts +84 -85
- package/src/dom/features/hydration/stub.ts +1 -1
- package/src/dom/features/index.ts +1 -0
- package/src/dom/features/memo/full.ts +4 -3
- package/src/dom/features/suspense/full.ts +192 -55
- package/src/dom/features/view-transition/full.ts +288 -0
- package/src/dom/features/view-transition/index.ts +1 -0
- package/src/dom/features/view-transition/resources.ts +107 -0
- package/src/dom/features/view-transition/stub.ts +7 -0
- package/src/dom/features/view-transition/visual.ts +406 -0
- package/src/dom/fragment-instance.ts +337 -0
- package/src/dom/index.ts +6 -7
- package/src/dom/portal.ts +1 -1
- package/src/dom/reconcile.ts +695 -242
- package/src/dom/resource-hints.ts +115 -0
- package/src/dom/resource-store.ts +52 -0
- package/src/dom/resources.ts +39 -0
- package/src/dom/retained-effects.ts +38 -0
- package/src/dom/root-internal.ts +4 -4
- package/src/dom/root.ts +12 -14
- package/src/react/activity.ts +10 -0
- package/src/react/context.ts +4 -11
- package/src/react/element.ts +11 -12
- package/src/react/hooks.ts +12 -3
- package/src/react/index.ts +18 -0
- package/src/react/shared-internals.ts +3 -3
- package/src/react/view-transition.ts +41 -0
- package/src/server/bootstrap-script.ts +36 -5
- package/src/server/dispatcher.ts +35 -13
- package/src/server/escape.ts +15 -12
- package/src/server/renderToString.ts +7 -5
- package/src/server/resource-hints.ts +165 -0
- package/src/server/stream.ts +154 -62
- package/src/server/walk.ts +79 -26
- package/src/vite/index.ts +30 -13
package/src/dom/reconcile.ts
CHANGED
|
@@ -8,17 +8,25 @@ import {
|
|
|
8
8
|
type FiberRoot,
|
|
9
9
|
type ReactElement,
|
|
10
10
|
type ReactNode,
|
|
11
|
-
type Hook,
|
|
12
11
|
type Effect,
|
|
12
|
+
type Hook,
|
|
13
13
|
} from '../core'
|
|
14
14
|
import {
|
|
15
15
|
ReactSharedInternals,
|
|
16
16
|
REACT_LAZY_TYPE,
|
|
17
17
|
REACT_STRICT_MODE_TYPE,
|
|
18
18
|
REACT_PROFILER_TYPE,
|
|
19
|
+
REACT_VIEW_TRANSITION_TYPE,
|
|
19
20
|
} from '../react'
|
|
20
|
-
import { createHostNode, setProp } from './dom'
|
|
21
|
-
import {
|
|
21
|
+
import { createHostNode, setProp, syncInputValue, syncTextareaValue } from './dom'
|
|
22
|
+
import { HOOK_BAILOUT, renderWithHooks } from './dispatcher'
|
|
23
|
+
import { rememberRetainedEffect, setLayoutDisconnected } from './retained-effects'
|
|
24
|
+
import { componentStack } from './error-info'
|
|
25
|
+
import { resourceKind } from '../core/resource-hints'
|
|
26
|
+
import { acquireResource } from './resources'
|
|
27
|
+
import { currentCommit, commitSynchronously, queueMutation, queueText, queueCommitEffects, setCommitCheckpointHook, onCommitRollback, onCommitFailure, checkpointCommit, rewindCommit, rememberChildList } from './commit'
|
|
28
|
+
import { markTransitionUpdate, deferTransition, cancelTransitions, deferTransitionLayout, deferTransitionPassive } from './features/view-transition'
|
|
29
|
+
import { createFragmentInstance, updateFragmentHost, refreshFragmentInstances } from './features/fragment-refs'
|
|
22
30
|
import {
|
|
23
31
|
adoptHostDom,
|
|
24
32
|
adoptTextDom,
|
|
@@ -28,6 +36,7 @@ import {
|
|
|
28
36
|
findHostParent as findHydrationHost,
|
|
29
37
|
abortHydration,
|
|
30
38
|
recoverHydration,
|
|
39
|
+
isHydrationBailout,
|
|
31
40
|
} from './features/hydration'
|
|
32
41
|
|
|
33
42
|
// ---------------------------------------------------------------------------
|
|
@@ -35,6 +44,7 @@ import {
|
|
|
35
44
|
// ---------------------------------------------------------------------------
|
|
36
45
|
|
|
37
46
|
let currentRoot: FiberRoot | null = null
|
|
47
|
+
let preparedParent: Node | null = null
|
|
38
48
|
let flushing = false
|
|
39
49
|
let isBatching = false
|
|
40
50
|
const pendingRoots = new Set<FiberRoot>()
|
|
@@ -54,11 +64,21 @@ export function scheduleUpdate(fiber: Fiber): void {
|
|
|
54
64
|
// query, any external store) can fire after unmount if their cleanup was
|
|
55
65
|
// missed, and letting those reach rerenderFiber mounts zombie DOM into the
|
|
56
66
|
// old .parent's DOM (which stays reachable via the stale pointer).
|
|
57
|
-
if (fiber.um) return
|
|
67
|
+
if (fiber.um || fiber.pd) return
|
|
68
|
+
fiber.dy = true
|
|
69
|
+
// A component finishes its render-phase updates before reconciling children.
|
|
70
|
+
if (ReactSharedInternals.F === fiber) return
|
|
58
71
|
const root = findRoot(fiber)
|
|
59
72
|
if (!root) return
|
|
73
|
+
// Retained Suspense primaries must retry their boundary, not commit a child
|
|
74
|
+
// against the older props preserved by the failed attempt.
|
|
75
|
+
while (fiber.su) {
|
|
76
|
+
fiber = fiber.su
|
|
77
|
+
fiber.ms.u = true
|
|
78
|
+
fiber.dy = true
|
|
79
|
+
}
|
|
80
|
+
markTransitionUpdate(root)
|
|
60
81
|
root.p.add(fiber)
|
|
61
|
-
fiber.dy = true
|
|
62
82
|
pendingRoots.add(root)
|
|
63
83
|
if (isBatching) return
|
|
64
84
|
if (!root.s) {
|
|
@@ -68,14 +88,19 @@ export function scheduleUpdate(fiber: Fiber): void {
|
|
|
68
88
|
}
|
|
69
89
|
|
|
70
90
|
export function flushSyncWork(fn: () => void): void {
|
|
91
|
+
cancelTransitions()
|
|
92
|
+
const transition = ReactSharedInternals.T
|
|
93
|
+
ReactSharedInternals.T = null
|
|
71
94
|
const wasBatching = isBatching
|
|
72
95
|
isBatching = true
|
|
73
96
|
try {
|
|
74
97
|
fn()
|
|
98
|
+
isBatching = wasBatching
|
|
99
|
+
flushPending()
|
|
75
100
|
} finally {
|
|
76
101
|
isBatching = wasBatching
|
|
102
|
+
ReactSharedInternals.T = transition
|
|
77
103
|
}
|
|
78
|
-
flushPending()
|
|
79
104
|
}
|
|
80
105
|
|
|
81
106
|
export function batchedUpdates<T>(fn: () => T): T {
|
|
@@ -105,27 +130,11 @@ function flushPending(): void {
|
|
|
105
130
|
pendingRoots.clear()
|
|
106
131
|
for (const root of roots) {
|
|
107
132
|
root.s = false
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// rerenderFiber's own `if (!dy) return` is our short-circuit. We
|
|
113
|
-
// previously filtered descendants of dy ancestors here, but that
|
|
114
|
-
// loses updates whenever an ancestor's render doesn't actually reach
|
|
115
|
-
// the descendant — e.g. React.memo bailing on equal props. Keep all
|
|
116
|
-
// dy fibers and let rerenderFiber de-dupe via its dy check.
|
|
117
|
-
const pending = [...root.p]
|
|
118
|
-
root.p.clear()
|
|
119
|
-
pending.sort((a, b) => fiberDepth(a) - fiberDepth(b))
|
|
120
|
-
for (const fiber of pending) {
|
|
121
|
-
try {
|
|
122
|
-
rerenderFiber(fiber, root)
|
|
123
|
-
} catch (error) {
|
|
124
|
-
if (!recoverHydration(root, error)) throw error
|
|
125
|
-
break
|
|
126
|
-
}
|
|
133
|
+
const render = () => flushRoot(root)
|
|
134
|
+
if (!deferTransition(root, render, flushPending)) {
|
|
135
|
+
try { commitSynchronously(render) }
|
|
136
|
+
catch (error) { if (!recoverRootError(error)) throw error }
|
|
127
137
|
}
|
|
128
|
-
runEffects(root)
|
|
129
138
|
}
|
|
130
139
|
}
|
|
131
140
|
} finally {
|
|
@@ -133,26 +142,80 @@ function flushPending(): void {
|
|
|
133
142
|
}
|
|
134
143
|
}
|
|
135
144
|
|
|
145
|
+
function flushRoot(root: FiberRoot): void {
|
|
146
|
+
if (root.er) {
|
|
147
|
+
flushRootErrors(root)
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
const render = root.u
|
|
151
|
+
root.u = undefined
|
|
152
|
+
if (render) render()
|
|
153
|
+
// Ancestors run first, but keep descendants in the queue: an ancestor can
|
|
154
|
+
// bail out without reaching a descendant's independently scheduled update.
|
|
155
|
+
const pending = [...root.p]
|
|
156
|
+
root.p.clear()
|
|
157
|
+
pending.sort((a, b) => fiberDepth(a) - fiberDepth(b))
|
|
158
|
+
for (const fiber of pending) {
|
|
159
|
+
if (!fiber.dy || fiber.um || fiber.pd) continue
|
|
160
|
+
const boundary = root.eb && findErrorBoundary(fiber)
|
|
161
|
+
const checkpoint = boundary || (root.sp && findSuspenseBoundary(fiber)) ? checkpointCommit(fiber) : -1
|
|
162
|
+
try {
|
|
163
|
+
rerenderFiber(fiber, root)
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if (error instanceof RenderSuspenseCapture) {
|
|
166
|
+
rewindCommit(checkpoint)
|
|
167
|
+
error.boundary.ms.u = true
|
|
168
|
+
scheduleUpdate(error.boundary)
|
|
169
|
+
continue
|
|
170
|
+
}
|
|
171
|
+
if (error instanceof RenderErrorCapture) {
|
|
172
|
+
if (error.boundary.tag === FiberTag.Root) throw error
|
|
173
|
+
rewindCommit(checkpoint)
|
|
174
|
+
captureError(error)
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
if (!recoverHydration(root, error)) throw error
|
|
178
|
+
break
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
runEffects(root)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function scheduleRootRender(root: FiberRoot, render: () => void): void {
|
|
185
|
+
markTransitionUpdate(root)
|
|
186
|
+
root.u = render
|
|
187
|
+
pendingRoots.add(root)
|
|
188
|
+
if (isBatching) return
|
|
189
|
+
flushPending()
|
|
190
|
+
}
|
|
191
|
+
|
|
136
192
|
export function discardPendingWork(root: FiberRoot): void {
|
|
193
|
+
root.u = undefined
|
|
137
194
|
root.p.clear()
|
|
138
195
|
root.s = false
|
|
139
196
|
pendingRoots.delete(root)
|
|
140
197
|
}
|
|
141
198
|
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
199
|
+
export function discardPendingEffects(root: FiberRoot): void {
|
|
200
|
+
for (const queue of [pendingEffects, pendingInsertionEffects, pendingCommits]) {
|
|
201
|
+
for (let index = queue.length - 1; index >= 0; index--) {
|
|
202
|
+
if (findRoot(queue[index]![0]) === root) removePendingEffect(queue, index)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
for (const fiber of stagedCommits.keys()) {
|
|
206
|
+
if (findRoot(fiber) === root) removeStagedCommits(fiber)
|
|
148
207
|
}
|
|
149
|
-
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function fiberDepth(fiber: Fiber): number {
|
|
211
|
+
// Reused fibers keep their parent and root, including keyed sibling moves.
|
|
212
|
+
return fiber.depth ??= fiber.parent ? fiberDepth(fiber.parent) + 1 : 0
|
|
150
213
|
}
|
|
151
214
|
|
|
152
215
|
export function findRoot(fiber: Fiber): FiberRoot | null {
|
|
153
216
|
let f: Fiber | null = fiber
|
|
154
217
|
while (f) {
|
|
155
|
-
if (f.root) return f.root
|
|
218
|
+
if (f.root) return fiber.root = f.root
|
|
156
219
|
f = f.parent
|
|
157
220
|
}
|
|
158
221
|
return null
|
|
@@ -163,6 +226,7 @@ export function findRoot(fiber: Fiber): FiberRoot | null {
|
|
|
163
226
|
// ---------------------------------------------------------------------------
|
|
164
227
|
|
|
165
228
|
export function renderRoot(root: FiberRoot, children: ReactNode): void {
|
|
229
|
+
if (!currentCommit) return commitSynchronously(() => renderRoot(root, children))
|
|
166
230
|
const rootFiber = root.r
|
|
167
231
|
rootFiber.pp = { children }
|
|
168
232
|
currentRoot = root
|
|
@@ -185,7 +249,7 @@ function rerenderFiber(fiber: Fiber, root: FiberRoot): void {
|
|
|
185
249
|
// content from a previous location staying on screen after nav, because a
|
|
186
250
|
// pending rerender on the old route's LibraryLandingPage (um during
|
|
187
251
|
// Outlet's shallow-first render) still fires from root.pending.
|
|
188
|
-
if (fiber.um) return
|
|
252
|
+
if (fiber.um || fiber.pd) return
|
|
189
253
|
// Clear BEFORE rendering so a scheduleUpdate() triggered mid-render (e.g.
|
|
190
254
|
// error boundary catching a descendant throw) marks us dy for the next
|
|
191
255
|
// flush iteration instead of being wiped out when render() completes.
|
|
@@ -204,7 +268,8 @@ function rerenderFiber(fiber: Fiber, root: FiberRoot): void {
|
|
|
204
268
|
const prevForcing = forceRerenderingFiber
|
|
205
269
|
forceRerenderingFiber = fiber
|
|
206
270
|
try {
|
|
207
|
-
|
|
271
|
+
const domParent = getHostParent(fiber)
|
|
272
|
+
renderFiber(fiber, domParent, getAnchor(fiber, domParent))
|
|
208
273
|
} finally {
|
|
209
274
|
forceRerenderingFiber = prevForcing
|
|
210
275
|
if (resumeHydration) {
|
|
@@ -255,10 +320,6 @@ function pushChildren(node: ReactNode, out: NormalizedChild[]): void {
|
|
|
255
320
|
for (let i = 0; i < node.length; i++) pushChildren(node[i], out)
|
|
256
321
|
return
|
|
257
322
|
}
|
|
258
|
-
if (isIterable(node)) {
|
|
259
|
-
for (const item of node as Iterable<ReactNode>) pushChildren(item, out)
|
|
260
|
-
return
|
|
261
|
-
}
|
|
262
323
|
if (typeof node === 'object') {
|
|
263
324
|
const t = (node as any).$$typeof
|
|
264
325
|
if (ACCEPTED_ELEMENT_MARKERS.has(t)) {
|
|
@@ -278,6 +339,9 @@ function pushChildren(node: ReactNode, out: NormalizedChild[]): void {
|
|
|
278
339
|
return
|
|
279
340
|
}
|
|
280
341
|
}
|
|
342
|
+
if (isIterable(node)) {
|
|
343
|
+
for (const item of node as Iterable<ReactNode>) pushChildren(item, out)
|
|
344
|
+
}
|
|
281
345
|
}
|
|
282
346
|
|
|
283
347
|
function isIterable(obj: any): boolean {
|
|
@@ -318,7 +382,7 @@ function fiberFromChild(child: NormalizedChild, parent: Fiber): Fiber {
|
|
|
318
382
|
const marker = type && (type as any).$$typeof
|
|
319
383
|
if (typeof type === 'string') tag = FiberTag.Host
|
|
320
384
|
else if (type === REACT_FRAGMENT_TYPE) tag = FiberTag.Fragment
|
|
321
|
-
else if (type === REACT_STRICT_MODE_TYPE || type === REACT_PROFILER_TYPE) tag = FiberTag.Fragment
|
|
385
|
+
else if (type === REACT_STRICT_MODE_TYPE || type === REACT_PROFILER_TYPE || type === REACT_VIEW_TRANSITION_TYPE) tag = FiberTag.Fragment
|
|
322
386
|
else {
|
|
323
387
|
// Feature-registered type matchers (Portal, future extractions). Features
|
|
324
388
|
// that carry the symbol as element.type directly (rather than wrapping in
|
|
@@ -355,8 +419,9 @@ export function reconcileChildren(
|
|
|
355
419
|
domParent: Node,
|
|
356
420
|
anchor: Node | null,
|
|
357
421
|
): void {
|
|
358
|
-
// Fast path:
|
|
359
|
-
// chain and newChildren in lockstep, validating
|
|
422
|
+
// Fast path: positional steady-state, including unchanged keyed lists.
|
|
423
|
+
// Walk the sibling chain and newChildren in lockstep, validating and
|
|
424
|
+
// committing in one pass.
|
|
360
425
|
// On any divergence we fall back to the slow path, which rebuilds the
|
|
361
426
|
// sibling chain anyway — partial pp writes are idempotent.
|
|
362
427
|
// Skips the Map / Set / existing-array allocation entirely.
|
|
@@ -365,12 +430,12 @@ export function reconcileChildren(
|
|
|
365
430
|
let ok = true
|
|
366
431
|
for (let i = 0; i < newChildren.length; i++) {
|
|
367
432
|
const child = newChildren[i]
|
|
368
|
-
if (child == null || !f
|
|
433
|
+
if (child == null || !f) { ok = false; break }
|
|
369
434
|
if (typeof child === 'string') {
|
|
370
|
-
if (f.tag !== FiberTag.Text) { ok = false; break }
|
|
435
|
+
if (f.tag !== FiberTag.Text || f.key != null) { ok = false; break }
|
|
371
436
|
f.pp = child
|
|
372
437
|
} else {
|
|
373
|
-
if ((child as ReactElement).key
|
|
438
|
+
if (!sameKey(f.key, (child as ReactElement).key)) { ok = false; break }
|
|
374
439
|
if (f.type !== (child as ReactElement).type) { ok = false; break }
|
|
375
440
|
f.pp = (child as ReactElement).props
|
|
376
441
|
f.ref = (child as any).ref ?? null
|
|
@@ -380,19 +445,32 @@ export function reconcileChildren(
|
|
|
380
445
|
if (ok && f === null) {
|
|
381
446
|
// Pass 2: render forward with per-child anchors. Identical to the slow
|
|
382
447
|
// path's pass 2.
|
|
383
|
-
|
|
384
|
-
let a = anchor
|
|
385
|
-
for (let s: Fiber | null = r.sibling; s; s = s.sibling) {
|
|
386
|
-
const d = firstDomNode(s)
|
|
387
|
-
if (d && d.parentNode === domParent) { a = d; break }
|
|
388
|
-
}
|
|
389
|
-
renderFiber(r, domParent, a)
|
|
390
|
-
}
|
|
448
|
+
renderChildChain(parent, domParent, anchor, false)
|
|
391
449
|
return
|
|
392
450
|
}
|
|
393
451
|
}
|
|
394
452
|
|
|
453
|
+
if (!parent.child) {
|
|
454
|
+
// Build every sibling before rendering, just like the matching path.
|
|
455
|
+
// A first mount has no old fibers to index, claim or remove.
|
|
456
|
+
rememberChildList(parent)
|
|
457
|
+
let previous: Fiber | null = null
|
|
458
|
+
for (const child of newChildren) {
|
|
459
|
+
if (child == null) continue
|
|
460
|
+
const fiber = fiberFromChild(child, parent)
|
|
461
|
+
if (previous) previous.sibling = fiber
|
|
462
|
+
else parent.child = fiber
|
|
463
|
+
previous = fiber
|
|
464
|
+
}
|
|
465
|
+
renderChildChain(parent, domParent, anchor, true)
|
|
466
|
+
if (previous && !currentRoot?.h && domParent.nodeName !== 'HEAD') {
|
|
467
|
+
placeChildrenInOrder(parent, domParent, anchor)
|
|
468
|
+
}
|
|
469
|
+
return
|
|
470
|
+
}
|
|
471
|
+
|
|
395
472
|
const existing = collectChildren(parent)
|
|
473
|
+
rememberChildList(parent, existing)
|
|
396
474
|
const keyed = new Map<string, Fiber>()
|
|
397
475
|
for (const f of existing) {
|
|
398
476
|
if (f.key != null) keyed.set('k' + f.key, f)
|
|
@@ -450,7 +528,7 @@ export function reconcileChildren(
|
|
|
450
528
|
if (!match) {
|
|
451
529
|
while (existingIdx < existing.length) {
|
|
452
530
|
const cand = existing[existingIdx]!
|
|
453
|
-
if (
|
|
531
|
+
if (cand.key != null) {
|
|
454
532
|
existingIdx++
|
|
455
533
|
continue
|
|
456
534
|
}
|
|
@@ -503,17 +581,10 @@ export function reconcileChildren(
|
|
|
503
581
|
// walks DOM forward and each renderFiber adopts the next existing node,
|
|
504
582
|
// so per-child anchors are moot — fall back to the parent's anchor.
|
|
505
583
|
const hydrating = !!currentRoot?.h
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
for (let s: Fiber | null = f.sibling; s; s = s.sibling) {
|
|
511
|
-
const d = firstDomNode(s)
|
|
512
|
-
if (d && d.parentNode === domParent) { a = d; break }
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
renderFiber(f, domParent, a)
|
|
516
|
-
}
|
|
584
|
+
// New siblings have no DOM yet, so searching them for an anchor cannot help.
|
|
585
|
+
// An empty replacement has no new chain. The old children remain linked
|
|
586
|
+
// until cleanup below, and must not render again on their way out.
|
|
587
|
+
if (prevNewFiber) renderChildChain(parent, domParent, anchor, hydrating || existing.length === 0)
|
|
517
588
|
|
|
518
589
|
if (!prevNewFiber) parent.child = null
|
|
519
590
|
else prevNewFiber.sibling = null
|
|
@@ -531,14 +602,7 @@ export function reconcileChildren(
|
|
|
531
602
|
// Unmount unclaimed
|
|
532
603
|
for (const f of existing) {
|
|
533
604
|
if (!claimed.has(f)) {
|
|
534
|
-
unmountFiber(f)
|
|
535
|
-
structurallyChanged = true
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
// Leftover keyed
|
|
539
|
-
for (const f of keyed.values()) {
|
|
540
|
-
if (!claimed.has(f)) {
|
|
541
|
-
unmountFiber(f)
|
|
605
|
+
unmountFiber(f, domParent)
|
|
542
606
|
structurallyChanged = true
|
|
543
607
|
}
|
|
544
608
|
}
|
|
@@ -560,14 +624,49 @@ export function reconcileChildren(
|
|
|
560
624
|
}
|
|
561
625
|
}
|
|
562
626
|
|
|
627
|
+
function renderChildChain(
|
|
628
|
+
parent: Fiber,
|
|
629
|
+
domParent: Node,
|
|
630
|
+
anchor: Node | null,
|
|
631
|
+
skipAnchors: boolean,
|
|
632
|
+
): void {
|
|
633
|
+
for (let f = parent.child; f; f = f.sibling) {
|
|
634
|
+
let nextAnchor = anchor
|
|
635
|
+
if (!skipAnchors) {
|
|
636
|
+
for (let sibling = f.sibling; sibling; sibling = sibling.sibling) {
|
|
637
|
+
const dom = firstDomNode(sibling, domParent)
|
|
638
|
+
if (dom) {
|
|
639
|
+
nextAnchor = dom
|
|
640
|
+
break
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
renderFiber(f, domParent, nextAnchor)
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
563
648
|
function placeChildrenInOrder(parent: Fiber, domParent: Node, anchor: Node | null): void {
|
|
564
|
-
const doms: Node[] = []
|
|
565
649
|
let c = parent.child
|
|
650
|
+
// One direct DOM child already at its exact anchor needs no collection.
|
|
651
|
+
if ((!currentCommit || domParent === preparedParent) && c && !c.sibling && (c.tag === FiberTag.Host || c.tag === FiberTag.Text) &&
|
|
652
|
+
c.dom?.parentNode === domParent && c.dom.nextSibling === anchor) return
|
|
653
|
+
const doms: Node[] = []
|
|
566
654
|
while (c) {
|
|
567
655
|
collectHostDoms(c, doms)
|
|
568
656
|
c = c.sibling
|
|
569
657
|
}
|
|
570
658
|
|
|
659
|
+
if (currentCommit && domParent !== preparedParent) {
|
|
660
|
+
if (doms.length) queuePlacement(doms, domParent, anchor)
|
|
661
|
+
} else placeDomsInOrder(doms, domParent, anchor)
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function queuePlacement(doms: Node[], parent: Node, anchor: Node | null): void {
|
|
665
|
+
queueMutation(() => placeDomsInOrder(doms, parent, anchor))
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function placeDomsInOrder(doms: Node[], domParent: Node, anchor: Node | null): void {
|
|
669
|
+
|
|
571
670
|
// Pre-check: if our fiber-owned DOM is already in document order within
|
|
572
671
|
// domParent AND the trailing anchor matches, no reorder is needed. This is
|
|
573
672
|
// the common case on stable re-renders, and avoids detaching/re-attaching
|
|
@@ -579,7 +678,7 @@ function placeChildrenInOrder(parent: Fiber, domParent: Node, anchor: Node | nul
|
|
|
579
678
|
current = current!.nextSibling
|
|
580
679
|
// Skip foreign nodes (SSR-injected scripts, dev-styles) between owned
|
|
581
680
|
// fiber DOMs — they should stay where they are.
|
|
582
|
-
while (current && !doms.includes(current as Node)) {
|
|
681
|
+
while (current && current !== doms[i] && !doms.includes(current as Node)) {
|
|
583
682
|
current = current.nextSibling
|
|
584
683
|
}
|
|
585
684
|
if (current !== doms[i]) inOrder = false
|
|
@@ -636,6 +735,7 @@ function collectHostDoms(fiber: Fiber, out: Node[]): void {
|
|
|
636
735
|
collectHostDoms(c, out)
|
|
637
736
|
c = c.sibling
|
|
638
737
|
}
|
|
738
|
+
if (fiber.tag === FiberTag.Suspense && fiber.ms?.f) collectHostDoms(fiber.ms.f, out)
|
|
639
739
|
}
|
|
640
740
|
|
|
641
741
|
function collectChildren(parent: Fiber): Fiber[] {
|
|
@@ -711,12 +811,27 @@ export function getForceRerenderingFiber(): Fiber | null {
|
|
|
711
811
|
|
|
712
812
|
registerRenderer(FiberTag.Text, renderText)
|
|
713
813
|
registerRenderer(FiberTag.Host, renderHost)
|
|
814
|
+
registerRenderer(FiberTag.Resource, renderHost)
|
|
714
815
|
registerRenderer(FiberTag.Function, renderFunction)
|
|
715
816
|
registerRenderer(FiberTag.Fragment, renderFragment)
|
|
716
817
|
|
|
717
818
|
export function renderFiber(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
819
|
+
// Memo consumes pending state when it delegates to its inner renderer.
|
|
820
|
+
if (fiber.tag !== FiberTag.Memo) fiber.dy = false
|
|
821
|
+
fiber.root ||= currentRoot || findRoot(fiber)
|
|
822
|
+
if (fiber.root?.sp && fiber.parent?.ld && fiber.mp === null) setLayoutDisconnected(fiber, true)
|
|
823
|
+
if (fiber.root?.a) CAPABILITIES.prepareActivityFiber(fiber)
|
|
718
824
|
const fn = RENDERERS[fiber.tag]
|
|
719
|
-
|
|
825
|
+
try {
|
|
826
|
+
if (fn) fn(fiber, domParent, anchor)
|
|
827
|
+
} catch (error) {
|
|
828
|
+
removeStagedCommits(fiber)
|
|
829
|
+
if (currentRoot?.h || error instanceof RenderErrorCapture || error instanceof RenderSuspenseCapture || isHydrationBailout(error)) throw error
|
|
830
|
+
if (isThenable(error)) handleSuspended(fiber, error)
|
|
831
|
+
else handleErrorInRender(fiber, error)
|
|
832
|
+
}
|
|
833
|
+
if (fiber.root?.a) CAPABILITIES.syncActivity(fiber)
|
|
834
|
+
if (lastStagedFiber === fiber) flushFiberCommits(fiber)
|
|
720
835
|
}
|
|
721
836
|
|
|
722
837
|
function renderText(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
@@ -729,32 +844,62 @@ function renderText(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
|
729
844
|
fiber.dom = document.createTextNode(text)
|
|
730
845
|
insertInto(domParent, fiber.dom, anchor)
|
|
731
846
|
}
|
|
847
|
+
updateFragmentHost(fiber, true)
|
|
732
848
|
} else {
|
|
733
849
|
// Past the fast path, and adoptTextDom already realigned `.data` on
|
|
734
850
|
// hydration — `.data !== text` here is guaranteed, so write directly.
|
|
735
|
-
|
|
851
|
+
queueText(fiber.dom as Text, text)
|
|
736
852
|
}
|
|
737
853
|
fiber.mp = text
|
|
738
854
|
// dy cleared at rerender start; leaving true lets mid-render schedule persist
|
|
739
855
|
}
|
|
740
856
|
|
|
741
857
|
function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
858
|
+
const hadDom = !!fiber.dom
|
|
742
859
|
const props = fiber.pp ?? {}
|
|
743
860
|
const prev = fiber.mp ?? {}
|
|
744
861
|
const type = fiber.type as string
|
|
745
862
|
const isSvg = type === 'svg' || (domParent as Element).namespaceURI === 'http://www.w3.org/2000/svg'
|
|
863
|
+
const isResource = fiber.tag === FiberTag.Resource
|
|
864
|
+
const canAcquire = !isSvg && (isResource || !fiber.mp) && resourceKind(type, props)
|
|
865
|
+
if (isResource || canAcquire) {
|
|
866
|
+
fiber.tag = FiberTag.Resource
|
|
867
|
+
if (canAcquire) {
|
|
868
|
+
let container: Node = domParent.nodeType === 9 ? domParent : domParent.ownerDocument!
|
|
869
|
+
if (type !== 'script') {
|
|
870
|
+
// Host assembly may still be detached. Style ownership follows the
|
|
871
|
+
// logical root or nearest portal, not that temporary DOM tree.
|
|
872
|
+
container = fiber.root?.c || domParent
|
|
873
|
+
for (let parent = fiber.parent; parent; parent = parent.parent) {
|
|
874
|
+
if (parent.tag === FiberTag.Portal) { container = parent.pp.container; break }
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
queueMutation(() => {
|
|
878
|
+
const resource = acquireResource(type, props, container)
|
|
879
|
+
fiber.sn ||= resource
|
|
880
|
+
})
|
|
881
|
+
}
|
|
882
|
+
if (fiber.mp) syncRefIfChanged(fiber, fiber.sn)
|
|
883
|
+
else attachRef(fiber, null)
|
|
884
|
+
fiber.mp = props
|
|
885
|
+
return
|
|
886
|
+
}
|
|
746
887
|
|
|
747
888
|
// <select value> must be applied AFTER children mount — setting `.value`
|
|
748
889
|
// on a `<select>` with no matching `<option>` yet resets it to empty. Same
|
|
749
890
|
// for `defaultValue` on first mount. Stash and replay.
|
|
750
891
|
const isSelect = type === 'select'
|
|
892
|
+
const isInput = !isSvg && type === 'input'
|
|
893
|
+
const isTextarea = !isSvg && type === 'textarea'
|
|
894
|
+
const deferValue = isSelect || isInput || isTextarea
|
|
895
|
+
let hydrated = false
|
|
751
896
|
const deferredSelectValue =
|
|
752
897
|
isSelect && (props.value !== undefined || props.defaultValue !== undefined)
|
|
753
898
|
? props.value !== undefined ? props.value : props.defaultValue
|
|
754
899
|
: undefined
|
|
755
900
|
|
|
756
901
|
if (!fiber.dom) {
|
|
757
|
-
|
|
902
|
+
hydrated = currentRoot?.h ? adoptHostDom(fiber, fiber.parent!) : false
|
|
758
903
|
if (!hydrated) {
|
|
759
904
|
fiber.dom = createHostNode(type, isSvg)
|
|
760
905
|
// Two passes so form-control attributes (notably <input type>) are in
|
|
@@ -763,7 +908,7 @@ function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
|
763
908
|
// → `input` vs `change`); binding before `type` is applied would
|
|
764
909
|
// attach to the wrong event for checkbox/radio/file inputs.
|
|
765
910
|
for (const k in props) {
|
|
766
|
-
if (
|
|
911
|
+
if ((deferValue && (k === 'value' || k === 'defaultValue')) || (isInput && (k === 'checked' || k === 'defaultChecked' || k === 'name'))) continue
|
|
767
912
|
if (isEventProp(k)) continue
|
|
768
913
|
setProp(fiber.dom as Element, k, props[k], undefined, isSvg)
|
|
769
914
|
}
|
|
@@ -771,45 +916,30 @@ function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
|
771
916
|
if (!isEventProp(k)) continue
|
|
772
917
|
setProp(fiber.dom as Element, k, props[k], undefined, isSvg)
|
|
773
918
|
}
|
|
919
|
+
if (isInput) {
|
|
920
|
+
syncInputValue(fiber.dom as HTMLInputElement, props)
|
|
921
|
+
setProp(fiber.dom as Element, 'name', props.name, undefined, false)
|
|
922
|
+
}
|
|
923
|
+
if (isTextarea) syncTextareaValue(fiber.dom as HTMLTextAreaElement, props, true)
|
|
774
924
|
insertInto(domParent, fiber.dom, anchor)
|
|
775
925
|
}
|
|
926
|
+
updateFragmentHost(fiber, true)
|
|
776
927
|
attachRef(fiber, fiber.dom)
|
|
777
|
-
} else if (prev !== props) {
|
|
778
|
-
|
|
779
|
-
// Single-pass diff. Defer changed event props into a small array so the
|
|
780
|
-
// `type-before-events` invariant the mount path needs (setEventHandler
|
|
781
|
-
// reads `el.type` to resolve onChange→input vs change) still holds when
|
|
782
|
-
// a render flips both `type` and an event handler in the same pass.
|
|
783
|
-
// The vast majority of host updates have no events at all (e.g. data-*
|
|
784
|
-
// attributes flipping on a stable list), so the deferred array stays
|
|
785
|
-
// null and we collapse to one for-in over `props`.
|
|
786
|
-
let deferredEvents: string[] | null = null
|
|
787
|
-
for (const k in props) {
|
|
788
|
-
if (isSelect && (k === 'value' || k === 'defaultValue')) continue
|
|
789
|
-
if (isEventProp(k)) {
|
|
790
|
-
if (prev[k] !== props[k]) {
|
|
791
|
-
deferredEvents ||= []
|
|
792
|
-
deferredEvents.push(k)
|
|
793
|
-
}
|
|
794
|
-
continue
|
|
795
|
-
}
|
|
796
|
-
if (prev[k] !== props[k]) setProp(el, k, props[k], prev[k], isSvg)
|
|
797
|
-
}
|
|
798
|
-
// Removals — keys present in prev but not in props.
|
|
799
|
-
for (const k in prev) {
|
|
800
|
-
if (!(k in props)) setProp(el, k, undefined, prev[k], isSvg)
|
|
801
|
-
}
|
|
802
|
-
if (deferredEvents) {
|
|
803
|
-
for (let i = 0; i < deferredEvents.length; i++) {
|
|
804
|
-
const k = deferredEvents[i]!
|
|
805
|
-
setProp(el, k, props[k], prev[k], isSvg)
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
syncRefIfChanged(fiber, fiber.dom)
|
|
928
|
+
} else if (prev !== props && (isInput || isTextarea || hostNeedsUpdate(props, prev))) {
|
|
929
|
+
queueHostProps(fiber.dom as Element, props, prev, isSvg, isInput, isTextarea, deferValue)
|
|
809
930
|
}
|
|
931
|
+
if (hadDom) syncRefIfChanged(fiber, fiber.dom)
|
|
810
932
|
|
|
811
933
|
// Children go into this DOM node
|
|
812
|
-
|
|
934
|
+
// Textarea's value/defaultValue owns its text node, not child fibers.
|
|
935
|
+
if (!hadDom && !hydrated) {
|
|
936
|
+
// Only a host allocated by this render is safe to assemble eagerly.
|
|
937
|
+
// User-owned detached roots and portal containers still commit normally.
|
|
938
|
+
const previous = preparedParent
|
|
939
|
+
preparedParent = fiber.dom
|
|
940
|
+
try { reconcileChildren(fiber, isTextarea ? [] : childrenToArray(props.children), fiber.dom!, null) }
|
|
941
|
+
finally { preparedParent = previous }
|
|
942
|
+
} else reconcileChildren(fiber, isTextarea ? [] : childrenToArray(props.children), fiber.dom!, null)
|
|
813
943
|
|
|
814
944
|
// During hydration, if after reconciling all client-expected children we
|
|
815
945
|
// still have server DOM left in the cursor for this host, that's a
|
|
@@ -820,7 +950,7 @@ function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
|
820
950
|
const parentTag = (fiber.type as string).toLowerCase()
|
|
821
951
|
const hasOpaqueHydrationChildren =
|
|
822
952
|
props.dangerouslySetInnerHTML != null ||
|
|
823
|
-
|
|
953
|
+
isTextarea
|
|
824
954
|
if (
|
|
825
955
|
parentTag !== 'head' &&
|
|
826
956
|
parentTag !== 'html' &&
|
|
@@ -835,7 +965,7 @@ function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
|
835
965
|
? `Hydration mismatch: server rendered extra nodes inside <${parentTag}>.`
|
|
836
966
|
: 'Hydration mismatch.',
|
|
837
967
|
)
|
|
838
|
-
if (currentRoot.re) currentRoot.re(error)
|
|
968
|
+
if (currentRoot.re) currentRoot.re(error, { componentStack: null })
|
|
839
969
|
abortHydration(error, fiber)
|
|
840
970
|
}
|
|
841
971
|
}
|
|
@@ -843,57 +973,86 @@ function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
|
843
973
|
}
|
|
844
974
|
|
|
845
975
|
// Apply <select> value after options are mounted.
|
|
846
|
-
if (isSelect && deferredSelectValue !== undefined) {
|
|
847
|
-
|
|
848
|
-
if (Array.isArray(deferredSelectValue)) {
|
|
849
|
-
const asStrings = deferredSelectValue.map((v) => '' + v)
|
|
850
|
-
for (const opt of Array.from(select.options)) {
|
|
851
|
-
opt.selected = asStrings.includes(opt.value)
|
|
852
|
-
}
|
|
853
|
-
} else {
|
|
854
|
-
select.value = '' + deferredSelectValue
|
|
855
|
-
}
|
|
976
|
+
if (isSelect && !hydrated && deferredSelectValue !== undefined) {
|
|
977
|
+
queueSelectValue(fiber.dom as HTMLSelectElement, deferredSelectValue)
|
|
856
978
|
}
|
|
857
979
|
|
|
858
980
|
fiber.mp = props
|
|
859
981
|
// dy cleared at rerender start; leaving true lets mid-render schedule persist
|
|
860
982
|
}
|
|
861
983
|
|
|
862
|
-
function
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
984
|
+
function queueSelectValue(select: HTMLSelectElement, value: any): void {
|
|
985
|
+
queueMutation(() => {
|
|
986
|
+
if (Array.isArray(value)) {
|
|
987
|
+
const selected = new Set(value.map((v) => '' + v))
|
|
988
|
+
const options = select.options
|
|
989
|
+
for (let i = 0; i < options.length; i++) {
|
|
990
|
+
const opt = options[i]!
|
|
991
|
+
opt.selected = selected.has(opt.value)
|
|
992
|
+
}
|
|
993
|
+
} else {
|
|
994
|
+
select.value = '' + value
|
|
995
|
+
}
|
|
996
|
+
})
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
function hostNeedsUpdate(props: any, previous: any): boolean {
|
|
1000
|
+
for (const key in props) {
|
|
1001
|
+
if (props[key] !== previous[key] && key !== 'children' && key !== 'ref' && key !== 'key') return true
|
|
1002
|
+
}
|
|
1003
|
+
for (const key in previous) {
|
|
1004
|
+
if (key !== 'children' && key !== 'ref' && key !== 'key' && !(key in props)) return true
|
|
1005
|
+
}
|
|
1006
|
+
return false
|
|
1007
|
+
}
|
|
867
1008
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1009
|
+
function queueHostProps(el: Element, props: any, prev: any, isSvg: boolean, isInput: boolean, isTextarea: boolean, deferValue: boolean): void {
|
|
1010
|
+
queueMutation(() => {
|
|
1011
|
+
// Radio groups stay disconnected until type, value and checked agree.
|
|
1012
|
+
if (isInput) setProp(el, 'name', '', undefined, false)
|
|
1013
|
+
let events: string[] | undefined
|
|
1014
|
+
for (const key in props) {
|
|
1015
|
+
const next = props[key], previous = prev[key]
|
|
1016
|
+
if (next === previous || key === 'children' || key === 'ref' || key === 'key' || (deferValue && (key === 'value' || key === 'defaultValue')) || (isInput && (key === 'checked' || key === 'defaultChecked' || key === 'name'))) continue
|
|
1017
|
+
if (isEventProp(key)) {
|
|
1018
|
+
(events ||= []).push(key)
|
|
1019
|
+
} else setProp(el, key, next, previous, isSvg)
|
|
1020
|
+
}
|
|
1021
|
+
for (const key in prev) {
|
|
1022
|
+
if (key === 'children' || key === 'ref' || key === 'key' || ((isInput || isTextarea) && (key === 'value' || key === 'defaultValue')) || (isInput && (key === 'checked' || key === 'defaultChecked' || key === 'name'))) continue
|
|
1023
|
+
if (!(key in props)) setProp(el, key, undefined, prev[key], isSvg)
|
|
1024
|
+
}
|
|
1025
|
+
// Event resolution sees the new input type, as it does on mount.
|
|
1026
|
+
if (events) for (const key of events) setProp(el, key, props[key], prev[key], isSvg)
|
|
1027
|
+
if (isInput) {
|
|
1028
|
+
syncInputValue(el as HTMLInputElement, props, prev)
|
|
1029
|
+
setProp(el, 'name', props.name, prev.name, false)
|
|
1030
|
+
} else if (isTextarea) syncTextareaValue(el as HTMLTextAreaElement, props)
|
|
1031
|
+
})
|
|
1032
|
+
}
|
|
872
1033
|
|
|
873
|
-
|
|
1034
|
+
function renderFunction(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
1035
|
+
const canBail = fiber === forceRerenderingFiber && fiber.pp === fiber.mp
|
|
1036
|
+
fiber.cx?.clear()
|
|
1037
|
+
let rendered: ReactNode | typeof HOOK_BAILOUT
|
|
874
1038
|
let deferredForHydration = false
|
|
875
1039
|
try {
|
|
876
|
-
rendered = (fiber.type
|
|
1040
|
+
rendered = renderWithHooks(fiber, fiber.type, fiber.pp ?? {}, undefined, canBail)
|
|
877
1041
|
} catch (e: any) {
|
|
878
1042
|
if (isThenable(e)) {
|
|
879
1043
|
if (deferHydration(fiber, e)) {
|
|
880
1044
|
deferredForHydration = true
|
|
881
1045
|
} else {
|
|
882
|
-
|
|
883
|
-
|
|
1046
|
+
handleSuspended(fiber, e)
|
|
1047
|
+
return
|
|
884
1048
|
}
|
|
885
1049
|
} else {
|
|
886
1050
|
handleErrorInRender(fiber, e)
|
|
887
1051
|
return
|
|
888
1052
|
}
|
|
889
|
-
} finally {
|
|
890
|
-
ReactSharedInternals.H = prevDispatcher
|
|
891
|
-
ReactSharedInternals.F = prevFiber
|
|
892
|
-
ReactSharedInternals.K = prevHook
|
|
893
|
-
ReactSharedInternals.I = prevIndex
|
|
894
1053
|
}
|
|
895
1054
|
|
|
896
|
-
if (deferredForHydration) return
|
|
1055
|
+
if (deferredForHydration || rendered === HOOK_BAILOUT) return
|
|
897
1056
|
|
|
898
1057
|
reconcileChildren(fiber, childrenToArray(rendered), domParent, anchor)
|
|
899
1058
|
fiber.mp = fiber.pp
|
|
@@ -930,6 +1089,24 @@ export function deferHydration(fiber: Fiber, thenable: Promise<any>): boolean {
|
|
|
930
1089
|
function renderFragment(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
931
1090
|
const props = fiber.pp ?? {}
|
|
932
1091
|
reconcileChildren(fiber, childrenToArray(props.children), domParent, anchor)
|
|
1092
|
+
if (fiber.type === REACT_FRAGMENT_TYPE) {
|
|
1093
|
+
if (fiber.ref && !fiber.sn) fiber.sn = createFragmentInstance(fiber)
|
|
1094
|
+
const previous = fiber.ms?.ref
|
|
1095
|
+
if (previous !== fiber.ref) {
|
|
1096
|
+
if (fiber.cu) {
|
|
1097
|
+
const cleanups = fiber.cu
|
|
1098
|
+
queueMutation(() => {
|
|
1099
|
+
for (const cleanup of cleanups) {
|
|
1100
|
+
try { cleanup() } catch (error) { handleCommitError(fiber, error) }
|
|
1101
|
+
}
|
|
1102
|
+
})
|
|
1103
|
+
fiber.cu = null
|
|
1104
|
+
}
|
|
1105
|
+
detachRef(previous, fiber)
|
|
1106
|
+
fiber.ms = { ...fiber.ms, ref: fiber.ref }
|
|
1107
|
+
if (fiber.sn) attachRef(fiber, fiber.sn)
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
933
1110
|
fiber.mp = props
|
|
934
1111
|
// dy cleared at rerender start; leaving true lets mid-render schedule persist
|
|
935
1112
|
}
|
|
@@ -960,11 +1137,35 @@ function defaultHandleSuspended(fiber: Fiber, thenable: Promise<any>): void {
|
|
|
960
1137
|
export interface Capabilities {
|
|
961
1138
|
handleSuspended: (fiber: Fiber, thenable: Promise<any>) => void
|
|
962
1139
|
readContext: (fiber: Fiber, ctx: any) => any
|
|
1140
|
+
isActivityHidden: (fiber: Fiber) => boolean
|
|
1141
|
+
activityIsDisconnected: (fiber: Fiber) => boolean
|
|
1142
|
+
prepareActivityFiber: (fiber: Fiber) => void
|
|
1143
|
+
syncActivity: (fiber: Fiber) => void
|
|
963
1144
|
}
|
|
964
1145
|
|
|
965
1146
|
const CAPABILITIES: Capabilities = {
|
|
966
1147
|
handleSuspended: defaultHandleSuspended,
|
|
967
1148
|
readContext: defaultReadContext,
|
|
1149
|
+
isActivityHidden: () => false,
|
|
1150
|
+
activityIsDisconnected: () => false,
|
|
1151
|
+
prepareActivityFiber: () => {},
|
|
1152
|
+
syncActivity: () => {},
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
export function isActivityHidden(fiber: Fiber): boolean {
|
|
1156
|
+
return !!(fiber.root ?? currentRoot ?? findRoot(fiber))?.a && CAPABILITIES.isActivityHidden(fiber)
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
export function activityIsDisconnected(fiber: Fiber): boolean {
|
|
1160
|
+
return !!fiber.root?.a && CAPABILITIES.activityIsDisconnected(fiber)
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
export function layoutIsDisconnected(fiber: Fiber): boolean {
|
|
1164
|
+
return !!fiber.ld || activityIsDisconnected(fiber)
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
export function rememberActivityEffect(fiber: Fiber, hook: Hook, effect: Effect): void {
|
|
1168
|
+
if (fiber.root?.a || (fiber.root?.sp && effect.t === 1 && !effect.s)) rememberRetainedEffect(fiber, hook, effect)
|
|
968
1169
|
}
|
|
969
1170
|
|
|
970
1171
|
export function installCapability<K extends keyof Capabilities>(
|
|
@@ -977,35 +1178,119 @@ export function installCapability<K extends keyof Capabilities>(
|
|
|
977
1178
|
// Wrapper for features that catch thrown thenables inside their render
|
|
978
1179
|
// functions. Delegates to the installed Suspense capability.
|
|
979
1180
|
export function handleSuspended(fiber: Fiber, thenable: Promise<any>): void {
|
|
1181
|
+
if (isActivityHidden(fiber) && !findSuspenseBoundary(fiber)) {
|
|
1182
|
+
defaultHandleSuspended(fiber, thenable)
|
|
1183
|
+
return
|
|
1184
|
+
}
|
|
980
1185
|
CAPABILITIES.handleSuspended(fiber, thenable)
|
|
981
1186
|
}
|
|
982
1187
|
|
|
1188
|
+
export class RenderSuspenseCapture {
|
|
1189
|
+
constructor(public boundary: Fiber) {}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
export function findSuspenseBoundary(fiber: Fiber): Fiber | null {
|
|
1193
|
+
let child = fiber
|
|
1194
|
+
for (let parent = fiber.parent; parent; parent = parent.parent) {
|
|
1195
|
+
if (parent.tag === FiberTag.Suspense && parent.ms?.f !== child) return parent
|
|
1196
|
+
// Hidden prerendering cannot activate a fallback outside its Activity.
|
|
1197
|
+
if (parent.tag === FiberTag.Activity && parent.pp?.mode === 'hidden') return null
|
|
1198
|
+
child = parent
|
|
1199
|
+
}
|
|
1200
|
+
return null
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
export class RenderErrorCapture {
|
|
1204
|
+
stack: string
|
|
1205
|
+
constructor(public boundary: Fiber, public error: unknown, source: Fiber = boundary) {
|
|
1206
|
+
this.stack = componentStack(source)
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
function captureError(capture: RenderErrorCapture): void {
|
|
1211
|
+
const fiber = capture.boundary
|
|
1212
|
+
fiber.ms = { ...fiber.ms, error: capture }
|
|
1213
|
+
scheduleUpdate(fiber)
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
function findErrorBoundary(fiber: Fiber): Fiber | null {
|
|
1217
|
+
for (let parent = fiber.parent; parent; parent = parent.parent) {
|
|
1218
|
+
if (parent.um || parent.pd) continue
|
|
1219
|
+
const instance = parent.sn
|
|
1220
|
+
if (instance?._fiber === parent && (parent.ms?.b?.getDerivedStateFromError || instance.componentDidCatch)) return parent
|
|
1221
|
+
}
|
|
1222
|
+
return null
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
function captureRootError(root: FiberRoot, stack: string, error: unknown): void {
|
|
1226
|
+
const errors = root.er ||= []
|
|
1227
|
+
if (!errors.length) root.u = undefined
|
|
1228
|
+
errors.push({ error, stack })
|
|
1229
|
+
scheduleUpdate(root.r)
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
function flushRootErrors(root: FiberRoot): void {
|
|
1233
|
+
const errors = root.er!
|
|
1234
|
+
root.er = undefined
|
|
1235
|
+
if (root.r.mp === null) queueMutation(() => { root.c.textContent = '' })
|
|
1236
|
+
renderRoot(root, null)
|
|
1237
|
+
queueCommitEffects(() => {
|
|
1238
|
+
for (const captured of errors) {
|
|
1239
|
+
try {
|
|
1240
|
+
if (root.ue) root.ue(captured.error, { componentStack: captured.stack })
|
|
1241
|
+
else if (typeof reportError === 'function') reportError(captured.error)
|
|
1242
|
+
else queueMicrotask(() => { throw captured.error })
|
|
1243
|
+
} catch (error) { setTimeout(() => { throw error }) }
|
|
1244
|
+
}
|
|
1245
|
+
})
|
|
1246
|
+
if (root.u) pendingRoots.add(root)
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
export function recoverRootError(error: unknown, abort?: () => void): boolean {
|
|
1250
|
+
if (!(error instanceof RenderErrorCapture) || error.boundary.tag !== FiberTag.Root) return false
|
|
1251
|
+
const root = error.boundary.root!
|
|
1252
|
+
abort?.()
|
|
1253
|
+
discardPendingWork(root)
|
|
1254
|
+
discardPendingEffects(root)
|
|
1255
|
+
captureRootError(root, error.stack, error.error)
|
|
1256
|
+
return true
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
export function handleCommitError(fiber: Fiber, error: unknown): void {
|
|
1260
|
+
const boundary = findErrorBoundary(fiber)
|
|
1261
|
+
if (boundary) {
|
|
1262
|
+
captureError(new RenderErrorCapture(boundary, error, fiber))
|
|
1263
|
+
return
|
|
1264
|
+
}
|
|
1265
|
+
const root = findRoot(fiber)
|
|
1266
|
+
if (root) captureRootError(root, componentStack(fiber), error)
|
|
1267
|
+
else queueMicrotask(() => { throw error })
|
|
1268
|
+
}
|
|
1269
|
+
|
|
983
1270
|
export function handleErrorInRender(fiber: Fiber, err: any): void {
|
|
1271
|
+
if (err instanceof RenderErrorCapture) throw err
|
|
1272
|
+
removeStagedCommits(fiber)
|
|
1273
|
+
for (const queue of [pendingEffects, pendingInsertionEffects, pendingCommits]) {
|
|
1274
|
+
for (let index = queue.length - 1; index >= 0; index--) {
|
|
1275
|
+
if (queue[index]![0] === fiber) removePendingEffect(queue, index)
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
984
1278
|
if (currentRoot?.h) {
|
|
985
1279
|
abortHydration(err, fiber)
|
|
986
1280
|
}
|
|
987
1281
|
// Bubble to nearest class boundary with getDerivedStateFromError / componentDidCatch
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
const update = Ctor.getDerivedStateFromError(err)
|
|
995
|
-
instance.state = { ...instance.state, ...update }
|
|
996
|
-
}
|
|
997
|
-
if (instance.componentDidCatch) {
|
|
998
|
-
try {
|
|
999
|
-
instance.componentDidCatch(err, { componentStack: '' })
|
|
1000
|
-
} catch {}
|
|
1001
|
-
}
|
|
1002
|
-
scheduleUpdate(f)
|
|
1003
|
-
return
|
|
1004
|
-
}
|
|
1005
|
-
f = f.parent
|
|
1282
|
+
const boundary = findErrorBoundary(fiber)
|
|
1283
|
+
if (boundary) {
|
|
1284
|
+
const capture = new RenderErrorCapture(boundary, err, fiber)
|
|
1285
|
+
if (currentCommit) throw capture
|
|
1286
|
+
captureError(capture)
|
|
1287
|
+
return
|
|
1006
1288
|
}
|
|
1007
|
-
//
|
|
1008
|
-
|
|
1289
|
+
// Abort the prepared render. Its mutation/effect queues must not commit
|
|
1290
|
+
// before the root is cleared and the error callback runs.
|
|
1291
|
+
const root = currentRoot ?? findRoot(fiber)
|
|
1292
|
+
if (root && currentCommit && (flushing || root.ue)) throw new RenderErrorCapture(root.r, err, fiber)
|
|
1293
|
+
if (root?.ue) root.ue(err, { componentStack: componentStack(fiber) })
|
|
1009
1294
|
else throw err
|
|
1010
1295
|
}
|
|
1011
1296
|
|
|
@@ -1017,49 +1302,95 @@ export function isThenable(x: any): x is Promise<any> {
|
|
|
1017
1302
|
// Unmount
|
|
1018
1303
|
// ---------------------------------------------------------------------------
|
|
1019
1304
|
|
|
1020
|
-
export function unmountFiber(fiber: Fiber,
|
|
1305
|
+
export function unmountFiber(fiber: Fiber, domParent: Node, remove = true): void {
|
|
1306
|
+
if (fiber.um) return
|
|
1307
|
+
if (currentCommit) return queueUnmount(fiber, domParent, remove)
|
|
1308
|
+
removeStagedCommits(fiber)
|
|
1309
|
+
if (fiber.type === REACT_FRAGMENT_TYPE && fiber.sn) {
|
|
1310
|
+
if (fiber.cu) {
|
|
1311
|
+
for (const cleanup of fiber.cu) {
|
|
1312
|
+
try { cleanup() } catch (error) { handleCommitError(fiber, error) }
|
|
1313
|
+
}
|
|
1314
|
+
fiber.cu = null
|
|
1315
|
+
}
|
|
1316
|
+
detachRef(fiber.cr ?? fiber.ref, fiber)
|
|
1317
|
+
}
|
|
1021
1318
|
fiber.um = true
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
c = next
|
|
1319
|
+
if (fiber.tag === FiberTag.Host || fiber.tag === FiberTag.Text) updateFragmentHost(fiber, false)
|
|
1320
|
+
if (fiber.type === REACT_FRAGMENT_TYPE && fiber.sn) fiber.sn.dispose()
|
|
1321
|
+
if (fiber.tag === FiberTag.Suspense && fiber.ms?.f) {
|
|
1322
|
+
unmountFiber(fiber.ms.f, domParent, remove)
|
|
1323
|
+
fiber.ms.f = null
|
|
1028
1324
|
}
|
|
1029
|
-
fiber.child = null
|
|
1030
|
-
|
|
1031
1325
|
// Run cu (fx + layout fx)
|
|
1032
1326
|
if (fiber.cu) {
|
|
1033
|
-
|
|
1327
|
+
const cleanups = fiber.cu
|
|
1328
|
+
fiber.cu = null
|
|
1329
|
+
for (const cleanup of cleanups) {
|
|
1034
1330
|
try {
|
|
1035
1331
|
cleanup()
|
|
1036
1332
|
} catch (e) {
|
|
1037
|
-
|
|
1333
|
+
handleCommitError(fiber, e)
|
|
1038
1334
|
}
|
|
1039
1335
|
}
|
|
1040
|
-
fiber.cu = null
|
|
1041
1336
|
}
|
|
1042
1337
|
|
|
1043
|
-
if (fiber.
|
|
1338
|
+
if (fiber.sn?._fiber === fiber) {
|
|
1339
|
+
const committed = fiber.ms?.c
|
|
1340
|
+
if (committed) { fiber.sn.props = committed.props; fiber.sn.state = committed.state }
|
|
1341
|
+
if (fiber.sn.componentWillUnmount && !layoutIsDisconnected(fiber)) {
|
|
1342
|
+
try {
|
|
1343
|
+
fiber.sn.componentWillUnmount()
|
|
1344
|
+
} catch (e) {
|
|
1345
|
+
handleCommitError(fiber, e)
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1044
1348
|
fiber.sn._fiber = null
|
|
1045
1349
|
fiber.sn._enqueueUpdate = null
|
|
1046
1350
|
fiber.sn._forceUpdate = null
|
|
1047
1351
|
}
|
|
1048
1352
|
|
|
1049
1353
|
// Detach ref
|
|
1050
|
-
if (fiber.ref) detachRef(fiber.ref)
|
|
1354
|
+
if (fiber.cr ?? fiber.ref) detachRef(fiber.cr ?? fiber.ref, fiber)
|
|
1051
1355
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1356
|
+
let c = fiber.child
|
|
1357
|
+
while (c) {
|
|
1358
|
+
const next = c.sibling
|
|
1359
|
+
unmountFiber(c, fiber.tag === FiberTag.Host ? fiber.dom! : domParent, fiber.tag === FiberTag.Portal || (remove && fiber.tag !== FiberTag.Host))
|
|
1360
|
+
c = next
|
|
1361
|
+
}
|
|
1362
|
+
fiber.child = null
|
|
1363
|
+
|
|
1364
|
+
// Remove DOM if host
|
|
1365
|
+
if (remove && (fiber.tag === FiberTag.Host || fiber.tag === FiberTag.Text) && fiber.dom?.parentNode) {
|
|
1366
|
+
fiber.dom.parentNode.removeChild(fiber.dom)
|
|
1055
1367
|
}
|
|
1056
1368
|
}
|
|
1057
1369
|
|
|
1058
|
-
|
|
1370
|
+
function queueUnmount(fiber: Fiber, domParent: Node, remove: boolean): void {
|
|
1371
|
+
if (fiber.pd) return
|
|
1372
|
+
// Keep committed DOM available for snapshots, but stop deleted work now.
|
|
1373
|
+
const deleted: Fiber[] = []
|
|
1374
|
+
markPendingDeletion(fiber, deleted)
|
|
1375
|
+
const restore = () => { for (const child of deleted) child.pd = false }
|
|
1376
|
+
onCommitRollback(restore)
|
|
1377
|
+
onCommitFailure(restore)
|
|
1378
|
+
queueMutation(() => unmountFiber(fiber, domParent, remove))
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
function markPendingDeletion(fiber: Fiber, deleted: Fiber[]): void {
|
|
1382
|
+
if (fiber.pd) return
|
|
1383
|
+
fiber.pd = true
|
|
1384
|
+
deleted.push(fiber)
|
|
1385
|
+
for (let child = fiber.child; child; child = child.sibling) markPendingDeletion(child, deleted)
|
|
1386
|
+
if (fiber.tag === FiberTag.Suspense && fiber.ms?.f) markPendingDeletion(fiber.ms.f, deleted)
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
export function unmountAllChildren(parent: Fiber, domParent: Node): void {
|
|
1059
1390
|
let c = parent.child
|
|
1060
1391
|
while (c) {
|
|
1061
1392
|
const next = c.sibling
|
|
1062
|
-
unmountFiber(c)
|
|
1393
|
+
unmountFiber(c, domParent)
|
|
1063
1394
|
c = next
|
|
1064
1395
|
}
|
|
1065
1396
|
parent.child = null
|
|
@@ -1070,6 +1401,7 @@ export function unmountAllChildren(parent: Fiber): void {
|
|
|
1070
1401
|
// ---------------------------------------------------------------------------
|
|
1071
1402
|
|
|
1072
1403
|
function insertInto(parent: Node, node: Node, anchor: Node | null): void {
|
|
1404
|
+
if (currentCommit && parent !== preparedParent) return queueInsertion(parent, node, anchor)
|
|
1073
1405
|
const projectedHeadParent = getDocumentHeadInsertionParent(parent, node)
|
|
1074
1406
|
if (projectedHeadParent) {
|
|
1075
1407
|
projectedHeadParent.appendChild(node)
|
|
@@ -1087,6 +1419,10 @@ function insertInto(parent: Node, node: Node, anchor: Node | null): void {
|
|
|
1087
1419
|
}
|
|
1088
1420
|
}
|
|
1089
1421
|
|
|
1422
|
+
function queueInsertion(parent: Node, node: Node, anchor: Node | null): void {
|
|
1423
|
+
queueMutation(() => insertInto(parent, node, anchor))
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1090
1426
|
const DOCUMENT_HEAD_TAGS = new Set(['base', 'link', 'meta', 'script', 'style', 'title'])
|
|
1091
1427
|
|
|
1092
1428
|
function getDocumentHeadInsertionParent(parent: Node, node: Node): HTMLHeadElement | null {
|
|
@@ -1096,7 +1432,7 @@ function getDocumentHeadInsertionParent(parent: Node, node: Node): HTMLHeadEleme
|
|
|
1096
1432
|
return (parent as Document).head
|
|
1097
1433
|
}
|
|
1098
1434
|
|
|
1099
|
-
function getHostParent(fiber: Fiber): Node {
|
|
1435
|
+
export function getHostParent(fiber: Fiber): Node {
|
|
1100
1436
|
let p = fiber.parent
|
|
1101
1437
|
while (p) {
|
|
1102
1438
|
if (p.tag === FiberTag.Host) return p.dom!
|
|
@@ -1120,35 +1456,32 @@ function getHostParent(fiber: Fiber): Node {
|
|
|
1120
1456
|
throw new Error()
|
|
1121
1457
|
}
|
|
1122
1458
|
|
|
1123
|
-
function getAnchor(fiber: Fiber): Node | null {
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
f = f.sibling
|
|
1130
|
-
}
|
|
1131
|
-
// Ascend
|
|
1132
|
-
let p = fiber.parent
|
|
1133
|
-
while (p && p.tag !== FiberTag.Host && p.tag !== FiberTag.Root && p.tag !== FiberTag.Portal) {
|
|
1134
|
-
if (p.sibling) {
|
|
1135
|
-
const d = firstDomNode(p.sibling)
|
|
1459
|
+
export function getAnchor(fiber: Fiber, domParent: Node): Node | null {
|
|
1460
|
+
let p: Fiber | null = fiber
|
|
1461
|
+
while (p) {
|
|
1462
|
+
let sibling = p.sibling
|
|
1463
|
+
while (sibling) {
|
|
1464
|
+
const d = firstDomNode(sibling, domParent)
|
|
1136
1465
|
if (d) return d
|
|
1466
|
+
sibling = sibling.sibling
|
|
1137
1467
|
}
|
|
1138
1468
|
p = p.parent
|
|
1469
|
+
if (p?.tag === FiberTag.Host || p?.tag === FiberTag.Root || p?.tag === FiberTag.Portal) break
|
|
1139
1470
|
}
|
|
1140
1471
|
return null
|
|
1141
1472
|
}
|
|
1142
1473
|
|
|
1143
|
-
function firstDomNode(fiber: Fiber): Node | null {
|
|
1144
|
-
if (fiber.tag === FiberTag.Host || fiber.tag === FiberTag.Text)
|
|
1474
|
+
function firstDomNode(fiber: Fiber, domParent: Node): Node | null {
|
|
1475
|
+
if (fiber.tag === FiberTag.Host || fiber.tag === FiberTag.Text) {
|
|
1476
|
+
return fiber.dom?.parentNode === domParent ? fiber.dom : null
|
|
1477
|
+
}
|
|
1145
1478
|
let c = fiber.child
|
|
1146
1479
|
while (c) {
|
|
1147
|
-
const d = firstDomNode(c)
|
|
1480
|
+
const d = firstDomNode(c, domParent)
|
|
1148
1481
|
if (d) return d
|
|
1149
1482
|
c = c.sibling
|
|
1150
1483
|
}
|
|
1151
|
-
return null
|
|
1484
|
+
return fiber.tag === FiberTag.Suspense && fiber.ms?.f ? firstDomNode(fiber.ms.f, domParent) : null
|
|
1152
1485
|
}
|
|
1153
1486
|
|
|
1154
1487
|
// ---------------------------------------------------------------------------
|
|
@@ -1172,36 +1505,57 @@ function defaultReadContext(_fiber: Fiber, ctx: any): any {
|
|
|
1172
1505
|
// Refs
|
|
1173
1506
|
// ---------------------------------------------------------------------------
|
|
1174
1507
|
|
|
1175
|
-
function attachRef(fiber: Fiber, value: any): void {
|
|
1508
|
+
export function attachRef(fiber: Fiber, value: any): void {
|
|
1176
1509
|
const ref = fiber.ref ?? (fiber.pp?.ref ?? null)
|
|
1177
1510
|
if (!ref) return
|
|
1178
|
-
if (
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1511
|
+
if (isActivityHidden(fiber) || layoutIsDisconnected(fiber)) return
|
|
1512
|
+
stageCommit(fiber, () => {
|
|
1513
|
+
if (fiber.tag === FiberTag.Resource) value = fiber.sn
|
|
1514
|
+
fiber.cr = ref
|
|
1515
|
+
if (typeof ref == 'function') {
|
|
1516
|
+
// Callback refs run during commit, after insertion effects install
|
|
1517
|
+
// event callback implementations and in subtree layout order.
|
|
1184
1518
|
const cleanup = ref(value)
|
|
1185
1519
|
fiber.cu ||= []
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1520
|
+
const detach = typeof cleanup == 'function' ? cleanup : () => ref(null)
|
|
1521
|
+
fiber.rc = detach
|
|
1522
|
+
fiber.cu.push(detach)
|
|
1523
|
+
} else {
|
|
1524
|
+
ref.current = value
|
|
1525
|
+
}
|
|
1526
|
+
}, true)
|
|
1191
1527
|
}
|
|
1192
1528
|
|
|
1193
|
-
function syncRefIfChanged(fiber: Fiber, value: any): void {
|
|
1529
|
+
export function syncRefIfChanged(fiber: Fiber, value: any): void {
|
|
1194
1530
|
const ref = fiber.ref ?? (fiber.pp?.ref ?? null)
|
|
1195
|
-
if (!ref) return
|
|
1196
|
-
if (
|
|
1531
|
+
if (!ref && !fiber.cr) return
|
|
1532
|
+
if (isActivityHidden(fiber) || layoutIsDisconnected(fiber)) return
|
|
1533
|
+
const previous = fiber.cr ?? null
|
|
1534
|
+
if (previous !== ref) {
|
|
1535
|
+
const cleanup = fiber.rc
|
|
1536
|
+
queueMutation(() => {
|
|
1537
|
+
if (cleanup) {
|
|
1538
|
+
const index = fiber.cu?.indexOf(cleanup) ?? -1
|
|
1539
|
+
if (index >= 0) fiber.cu!.splice(index, 1)
|
|
1540
|
+
fiber.rc = null
|
|
1541
|
+
try { cleanup() } catch (error) { handleCommitError(fiber, error) }
|
|
1542
|
+
}
|
|
1543
|
+
detachRef(previous, fiber)
|
|
1544
|
+
fiber.cr = null
|
|
1545
|
+
})
|
|
1546
|
+
attachRef(fiber, value)
|
|
1547
|
+
} else if (ref && typeof ref === 'object' && ref.current !== value) attachRef(fiber, value)
|
|
1197
1548
|
}
|
|
1198
1549
|
|
|
1199
|
-
function detachRef(ref: any): void {
|
|
1550
|
+
export function detachRef(ref: any, fiber?: Fiber): void {
|
|
1200
1551
|
// Function refs are handled via fiber.cu (queued in attachRef during
|
|
1201
1552
|
// the commit phase): the cleanup either invokes the user-returned cleanup
|
|
1202
1553
|
// fn or calls ref(null). Calling ref(null) here would double-fire it.
|
|
1203
1554
|
if (ref && typeof ref === 'object') {
|
|
1204
|
-
|
|
1555
|
+
queueMutation(() => {
|
|
1556
|
+
try { ref.current = null }
|
|
1557
|
+
catch (error) { if (fiber) handleCommitError(fiber, error); else throw error }
|
|
1558
|
+
})
|
|
1205
1559
|
}
|
|
1206
1560
|
}
|
|
1207
1561
|
|
|
@@ -1210,47 +1564,146 @@ function detachRef(ref: any): void {
|
|
|
1210
1564
|
// ---------------------------------------------------------------------------
|
|
1211
1565
|
|
|
1212
1566
|
const pendingEffects: Array<[Fiber, Effect]> = []
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1567
|
+
const pendingInsertionEffects: Array<[Fiber, Effect]> = []
|
|
1568
|
+
type LayoutCommit = [Fiber, Effect | (() => void), boolean]
|
|
1569
|
+
const pendingCommits: LayoutCommit[] = []
|
|
1570
|
+
const stagedCommits = new Map<Fiber, LayoutCommit[]>()
|
|
1571
|
+
// Track only fibers with queued work. A ref-less descendant can finish with
|
|
1572
|
+
// one identity check, even while an ancestor has a pending layout effect.
|
|
1573
|
+
const stagedFibers: Fiber[] = []
|
|
1574
|
+
let lastStagedFiber: Fiber | undefined
|
|
1575
|
+
|
|
1576
|
+
// Appends are covered by checkpoint lengths. Only removals need to retain
|
|
1577
|
+
// the affected entry, and only while a render has a rollback checkpoint.
|
|
1578
|
+
function removePendingEffect(queue: Array<[Fiber, Effect] | LayoutCommit>, index: number): void {
|
|
1579
|
+
const entry = queue[index]!
|
|
1580
|
+
queue.splice(index, 1)
|
|
1581
|
+
if (currentCommit?.c.length) onCommitRollback(() => { queue.splice(index, 0, entry) })
|
|
1582
|
+
}
|
|
1215
1583
|
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1584
|
+
function stageCommit(fiber: Fiber, work: Effect | (() => void), ref = false): void {
|
|
1585
|
+
let entries = stagedCommits.get(fiber)
|
|
1586
|
+
if (!entries) {
|
|
1587
|
+
stagedCommits.set(fiber, entries = [])
|
|
1588
|
+
stagedFibers.push(fiber)
|
|
1589
|
+
lastStagedFiber = fiber
|
|
1590
|
+
}
|
|
1591
|
+
entries.push([fiber, work, ref])
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
function removeStagedCommits(fiber: Fiber): void {
|
|
1595
|
+
if (!stagedCommits.delete(fiber)) return
|
|
1596
|
+
if (lastStagedFiber === fiber) stagedFibers.pop()
|
|
1597
|
+
else stagedFibers.splice(stagedFibers.lastIndexOf(fiber), 1)
|
|
1598
|
+
lastStagedFiber = stagedFibers[stagedFibers.length - 1]
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
// Rendering already visits the tree in order. Completing a fiber stages its
|
|
1602
|
+
// layout work after its children, without another tree walk at commit time.
|
|
1603
|
+
export function flushFiberCommits(fiber: Fiber): void {
|
|
1604
|
+
const entries = stagedCommits.get(fiber)
|
|
1605
|
+
if (!entries) return
|
|
1606
|
+
removeStagedCommits(fiber)
|
|
1607
|
+
if (fiber.type === REACT_FRAGMENT_TYPE) {
|
|
1608
|
+
// React attaches Fragment refs before their descendants. Only referenced
|
|
1609
|
+
// Fragments need to locate this insertion point among queued commits.
|
|
1610
|
+
let index = pendingCommits.length
|
|
1611
|
+
outer: for (let i = 0; i < pendingCommits.length; i++) {
|
|
1612
|
+
for (let parent = pendingCommits[i]![0].parent; parent; parent = parent.parent) {
|
|
1613
|
+
if (parent === fiber) { index = i; break outer }
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
if (index < pendingCommits.length && currentCommit?.c.length) {
|
|
1617
|
+
const count = entries.length
|
|
1618
|
+
onCommitRollback(() => { pendingCommits.splice(index, count) })
|
|
1619
|
+
}
|
|
1620
|
+
pendingCommits.splice(index, 0, ...entries)
|
|
1621
|
+
} else if (fiber.tag === FiberTag.Class || fiber.sn?._fiber === fiber) {
|
|
1622
|
+
for (const entry of entries) if (!entry[2]) pendingCommits.push(entry)
|
|
1623
|
+
for (const entry of entries) if (entry[2]) pendingCommits.push(entry)
|
|
1219
1624
|
} else {
|
|
1220
|
-
|
|
1625
|
+
for (const entry of entries) pendingCommits.push(entry)
|
|
1221
1626
|
}
|
|
1222
1627
|
}
|
|
1223
1628
|
|
|
1629
|
+
export function enqueueEffect(fiber: Fiber, effect: Effect): void {
|
|
1630
|
+
if (effect.t !== 2 && (isActivityHidden(fiber) || activityIsDisconnected(fiber) || (effect.t === 1 && !effect.s && fiber.ld))) return
|
|
1631
|
+
if (effect.t === 2) pendingInsertionEffects.push([fiber, effect])
|
|
1632
|
+
else if (effect.t === 1) stageCommit(fiber, effect)
|
|
1633
|
+
else pendingEffects.push([fiber, effect])
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1224
1636
|
export function scheduleLifecycle(fiber: Fiber, fn: () => void): void {
|
|
1225
|
-
|
|
1637
|
+
if (isActivityHidden(fiber) || layoutIsDisconnected(fiber)) return
|
|
1638
|
+
stageCommit(fiber, fn)
|
|
1226
1639
|
}
|
|
1227
1640
|
|
|
1228
1641
|
export function runEffects(root: FiberRoot): void {
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1642
|
+
if (root.fr) refreshFragmentInstances(root)
|
|
1643
|
+
if (!pendingInsertionEffects.length && !pendingCommits.length && !pendingEffects.length) return
|
|
1644
|
+
flushEffects(root)
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
function flushEffects(root: FiberRoot): void {
|
|
1648
|
+
const insertion = pendingInsertionEffects.splice(0)
|
|
1649
|
+
const layout = pendingCommits.splice(0)
|
|
1650
|
+
const batch = pendingEffects.splice(0)
|
|
1651
|
+
if (currentCommit?.c.length) onCommitRollback(() => {
|
|
1652
|
+
// A nested render can drain these queues before an enclosing attempt
|
|
1653
|
+
// fails. Reuse the captured batches, then older checkpoints trim tails.
|
|
1654
|
+
pendingInsertionEffects.unshift(...insertion)
|
|
1655
|
+
pendingCommits.unshift(...layout)
|
|
1656
|
+
pendingEffects.unshift(...batch)
|
|
1657
|
+
})
|
|
1658
|
+
queueCommitEffects(() => {
|
|
1659
|
+
for (const [fiber, effect] of insertion) runEffect(fiber, effect, root)
|
|
1660
|
+
if (layout.length) {
|
|
1661
|
+
for (const [fiber, work] of layout) {
|
|
1662
|
+
if (typeof work !== 'function' && !fiber.um && !isActivityHidden(fiber) && !layoutIsDisconnected(fiber)) work.d?.()
|
|
1663
|
+
}
|
|
1664
|
+
const runLayout = () => {
|
|
1665
|
+
for (const [fiber, work] of layout) {
|
|
1666
|
+
if (typeof work !== 'function') runEffect(fiber, work, root)
|
|
1667
|
+
else if (!fiber.um && !isActivityHidden(fiber) && !layoutIsDisconnected(fiber)) {
|
|
1668
|
+
try { work() } catch (error) { handleCommitError(fiber, error) }
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1241
1671
|
}
|
|
1672
|
+
if (!deferTransitionLayout(runLayout)) runLayout()
|
|
1242
1673
|
}
|
|
1243
1674
|
// Passive fx on microtask
|
|
1244
|
-
if (
|
|
1245
|
-
const
|
|
1246
|
-
queueMicrotask(() => {
|
|
1675
|
+
if (batch.length) {
|
|
1676
|
+
const passive = () => {
|
|
1247
1677
|
for (const [fiber, effect] of batch) runEffect(fiber, effect, root)
|
|
1248
|
-
}
|
|
1678
|
+
}
|
|
1679
|
+
if (!deferTransitionPassive(passive)) queueMicrotask(passive)
|
|
1249
1680
|
}
|
|
1681
|
+
})
|
|
1250
1682
|
}
|
|
1251
1683
|
|
|
1684
|
+
setCommitCheckpointHook(() => {
|
|
1685
|
+
// A parent allocated before this savepoint can contain eager insertions
|
|
1686
|
+
// from an abandoned child render. Restore only that new, detached host.
|
|
1687
|
+
const parent = preparedParent as Element | null
|
|
1688
|
+
const children = parent && Array.from(parent.childNodes)
|
|
1689
|
+
const effects = pendingEffects.length, insertion = pendingInsertionEffects.length, commits = pendingCommits.length
|
|
1690
|
+
const staged = new Map([...stagedCommits].map(([fiber, work]) => [fiber, work.slice()]))
|
|
1691
|
+
const stack = stagedFibers.slice(), last = lastStagedFiber
|
|
1692
|
+
onCommitRollback(() => {
|
|
1693
|
+
if (parent) parent.replaceChildren(...children!)
|
|
1694
|
+
pendingEffects.length = effects
|
|
1695
|
+
pendingInsertionEffects.length = insertion
|
|
1696
|
+
pendingCommits.length = commits
|
|
1697
|
+
stagedCommits.clear()
|
|
1698
|
+
for (const [fiber, work] of staged) stagedCommits.set(fiber, work)
|
|
1699
|
+
stagedFibers.splice(0, stagedFibers.length, ...stack)
|
|
1700
|
+
lastStagedFiber = last
|
|
1701
|
+
})
|
|
1702
|
+
})
|
|
1703
|
+
|
|
1252
1704
|
function runEffect(fiber: Fiber, effect: Effect, root: FiberRoot): void {
|
|
1253
1705
|
if (fiber.um) return
|
|
1706
|
+
if (effect.t !== 2 && (isActivityHidden(fiber) || activityIsDisconnected(fiber) || (effect.t === 1 && !effect.s && fiber.ld))) return
|
|
1254
1707
|
try {
|
|
1255
1708
|
const cleanup = effect.c()
|
|
1256
1709
|
if (typeof cleanup == 'function') {
|
|
@@ -1258,7 +1711,7 @@ function runEffect(fiber: Fiber, effect: Effect, root: FiberRoot): void {
|
|
|
1258
1711
|
fiber.cu.push(cleanup)
|
|
1259
1712
|
}
|
|
1260
1713
|
} catch (e) {
|
|
1261
|
-
|
|
1714
|
+
handleCommitError(fiber, e)
|
|
1262
1715
|
}
|
|
1263
1716
|
}
|
|
1264
1717
|
|