kiru 0.45.2 → 0.46.0
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/appContext.d.ts +0 -13
- package/dist/appContext.d.ts.map +1 -1
- package/dist/appContext.js +15 -55
- package/dist/appContext.js.map +1 -1
- package/dist/constants.d.ts +5 -7
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +5 -7
- package/dist/constants.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +2 -4
- package/dist/context.js.map +1 -1
- package/dist/dom.d.ts +2 -2
- package/dist/dom.d.ts.map +1 -1
- package/dist/dom.js +36 -32
- package/dist/dom.js.map +1 -1
- package/dist/form/index.d.ts.map +1 -1
- package/dist/form/index.js +17 -3
- package/dist/form/index.js.map +1 -1
- package/dist/form/types.d.ts +1 -0
- package/dist/form/types.d.ts.map +1 -1
- package/dist/globals.d.ts +1 -6
- package/dist/globals.d.ts.map +1 -1
- package/dist/globals.js +1 -5
- package/dist/globals.js.map +1 -1
- package/dist/hmr.d.ts +1 -1
- package/dist/hmr.d.ts.map +1 -1
- package/dist/hmr.js +2 -4
- package/dist/hmr.js.map +1 -1
- package/dist/hooks/useViewTransition.d.ts.map +1 -1
- package/dist/hooks/useViewTransition.js +3 -3
- package/dist/hooks/useViewTransition.js.map +1 -1
- package/dist/hooks/utils.d.ts +1 -5
- package/dist/hooks/utils.d.ts.map +1 -1
- package/dist/hooks/utils.js +8 -20
- package/dist/hooks/utils.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/lazy.d.ts.map +1 -1
- package/dist/lazy.js +4 -4
- package/dist/lazy.js.map +1 -1
- package/dist/portal.d.ts.map +1 -1
- package/dist/portal.js +2 -3
- package/dist/portal.js.map +1 -1
- package/dist/props.js +1 -1
- package/dist/props.js.map +1 -1
- package/dist/reconciler.d.ts.map +1 -1
- package/dist/reconciler.js +105 -79
- package/dist/reconciler.js.map +1 -1
- package/dist/renderToString.d.ts.map +1 -1
- package/dist/renderToString.js +5 -8
- package/dist/renderToString.js.map +1 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +5 -6
- package/dist/router/router.js.map +1 -1
- package/dist/scheduler.d.ts +4 -11
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +365 -390
- package/dist/scheduler.js.map +1 -1
- package/dist/signals/base.d.ts +4 -4
- package/dist/signals/base.d.ts.map +1 -1
- package/dist/signals/base.js +38 -24
- package/dist/signals/base.js.map +1 -1
- package/dist/signals/computed.d.ts +2 -2
- package/dist/signals/computed.d.ts.map +1 -1
- package/dist/signals/computed.js +11 -3
- package/dist/signals/computed.js.map +1 -1
- package/dist/signals/effect.d.ts +1 -1
- package/dist/signals/effect.d.ts.map +1 -1
- package/dist/signals/globals.d.ts +2 -2
- package/dist/signals/globals.d.ts.map +1 -1
- package/dist/signals/globals.js.map +1 -1
- package/dist/signals/jsx.d.ts +4 -3
- package/dist/signals/jsx.d.ts.map +1 -1
- package/dist/signals/jsx.js +1 -1
- package/dist/signals/jsx.js.map +1 -1
- package/dist/signals/types.d.ts +2 -2
- package/dist/signals/types.d.ts.map +1 -1
- package/dist/signals/utils.d.ts +1 -1
- package/dist/signals/utils.d.ts.map +1 -1
- package/dist/signals/utils.js +1 -1
- package/dist/signals/utils.js.map +1 -1
- package/dist/ssr/server.d.ts.map +1 -1
- package/dist/ssr/server.js +1 -5
- package/dist/ssr/server.js.map +1 -1
- package/dist/types.d.ts +9 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +17 -17
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/appContext.ts +21 -67
- package/src/constants.ts +8 -7
- package/src/context.ts +2 -4
- package/src/dom.ts +50 -29
- package/src/env.ts +6 -6
- package/src/form/index.ts +17 -3
- package/src/form/types.ts +1 -0
- package/src/globals.ts +1 -9
- package/src/hmr.ts +3 -5
- package/src/hooks/useViewTransition.ts +3 -3
- package/src/hooks/utils.ts +7 -22
- package/src/index.ts +3 -3
- package/src/lazy.ts +4 -4
- package/src/portal.ts +2 -3
- package/src/props.ts +1 -1
- package/src/reconciler.ts +116 -90
- package/src/renderToString.ts +5 -8
- package/src/router/router.ts +4 -6
- package/src/scheduler.ts +369 -407
- package/src/signals/base.ts +40 -33
- package/src/signals/computed.ts +8 -3
- package/src/signals/effect.ts +1 -1
- package/src/signals/globals.ts +2 -2
- package/src/signals/jsx.ts +12 -11
- package/src/signals/types.ts +2 -2
- package/src/signals/utils.ts +1 -1
- package/src/ssr/server.ts +1 -5
- package/src/types.ts +9 -2
- package/src/utils.ts +26 -21
- package/dist/flags.d.ts +0 -6
- package/dist/flags.d.ts.map +0 -1
- package/dist/flags.js +0 -16
- package/dist/flags.js.map +0 -1
- package/src/flags.ts +0 -15
package/src/scheduler.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import type { AppContext } from "./appContext"
|
|
2
1
|
import type {
|
|
3
2
|
ContextProviderNode,
|
|
4
3
|
DomVNode,
|
|
5
4
|
FunctionVNode,
|
|
6
5
|
} from "./types.utils"
|
|
7
|
-
import { flags } from "./flags.js"
|
|
8
6
|
import {
|
|
9
7
|
$CONTEXT_PROVIDER,
|
|
10
8
|
CONSECUTIVE_DIRTY_LIMIT,
|
|
11
|
-
|
|
9
|
+
FLAG_DELETION,
|
|
12
10
|
} from "./constants.js"
|
|
13
|
-
import { commitWork, createDom, hydrateDom } from "./dom.js"
|
|
11
|
+
import { commitDeletion, commitWork, createDom, hydrateDom } from "./dom.js"
|
|
14
12
|
import { __DEV__ } from "./env.js"
|
|
15
13
|
import { KiruError } from "./error.js"
|
|
16
|
-
import {
|
|
14
|
+
import { hookIndex, node, renderMode } from "./globals.js"
|
|
17
15
|
import { hydrationStack } from "./hydration.js"
|
|
18
16
|
import { assertValidElementProps } from "./props.js"
|
|
19
17
|
import { reconcileChildren } from "./reconciler.js"
|
|
@@ -23,500 +21,464 @@ import {
|
|
|
23
21
|
traverseApply,
|
|
24
22
|
vNodeContains,
|
|
25
23
|
isExoticType,
|
|
24
|
+
getVNodeAppContext,
|
|
26
25
|
} from "./utils.js"
|
|
27
|
-
import {
|
|
26
|
+
import type { AppContext } from "./appContext"
|
|
28
27
|
|
|
29
28
|
type VNode = Kiru.VNode
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
let appCtx: AppContext | null
|
|
31
|
+
let nextUnitOfWork: VNode | null = null
|
|
32
|
+
let treesInProgress: VNode[] = []
|
|
33
|
+
let currentTreeIndex = 0
|
|
34
|
+
let isRunning = false
|
|
35
|
+
let nextIdleEffects: (() => void)[] = []
|
|
36
|
+
let deletions: VNode[] = []
|
|
37
|
+
let isImmediateEffectsMode = false
|
|
38
|
+
let immediateEffectDirtiedRender = false
|
|
39
|
+
let isRenderDirtied = false
|
|
40
|
+
let consecutiveDirtyCount = 0
|
|
41
|
+
let pendingContextChanges = new Set<ContextProviderNode<any>>()
|
|
42
|
+
let preEffects: Array<Function> = []
|
|
43
|
+
let postEffects: Array<Function> = []
|
|
44
|
+
|
|
45
|
+
export function nextIdle(fn: () => void, wakeUpIfIdle = true) {
|
|
46
|
+
nextIdleEffects.push(fn)
|
|
47
|
+
if (wakeUpIfIdle) wake()
|
|
39
48
|
}
|
|
40
49
|
|
|
41
|
-
export function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
): Scheduler {
|
|
45
|
-
let nextUnitOfWork: VNode | null = null
|
|
46
|
-
let treesInProgress: VNode[] = []
|
|
47
|
-
let currentTreeIndex = 0
|
|
48
|
-
let isRunning = false
|
|
49
|
-
let nextIdleEffects: ((scheduler: Scheduler) => void)[] = []
|
|
50
|
-
let deletions: VNode[] = []
|
|
51
|
-
let frameDeadline = 0
|
|
52
|
-
let pendingCallback: IdleRequestCallback | null = null
|
|
53
|
-
let frameHandle: number | null = null
|
|
54
|
-
let isImmediateEffectsMode = false
|
|
55
|
-
let immediateEffectDirtiedRender = false
|
|
56
|
-
let isRenderDirtied = false
|
|
57
|
-
let consecutiveDirtyCount = 0
|
|
58
|
-
let pendingContextChanges = new Set<ContextProviderNode<any>>()
|
|
59
|
-
let effectCallbacks = {
|
|
60
|
-
pre: [] as Function[],
|
|
61
|
-
post: [] as Function[],
|
|
62
|
-
}
|
|
63
|
-
let scheduler: Scheduler
|
|
50
|
+
export function flushSync() {
|
|
51
|
+
workLoop()
|
|
52
|
+
}
|
|
64
53
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
54
|
+
export function requestUpdate(vNode: VNode): void {
|
|
55
|
+
if (vNode.flags & FLAG_DELETION) return
|
|
56
|
+
if (__DEV__) {
|
|
57
|
+
// if (options?.debug?.onRequestUpdate) {
|
|
58
|
+
// options.debug.onRequestUpdate(vNode)
|
|
59
|
+
// }
|
|
69
60
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
61
|
+
if (renderMode.current === "hydrate") {
|
|
62
|
+
return nextIdle(() => {
|
|
63
|
+
vNode.flags & FLAG_DELETION || queueUpdate(vNode)
|
|
64
|
+
})
|
|
75
65
|
}
|
|
66
|
+
queueUpdate(vNode)
|
|
67
|
+
}
|
|
76
68
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
effectCallbacks = { pre: [], post: [] }
|
|
84
|
-
frameDeadline = 0
|
|
85
|
-
pendingCallback = null
|
|
86
|
-
sleep()
|
|
69
|
+
export function requestDelete(vNode: VNode): void {
|
|
70
|
+
if (vNode.flags & FLAG_DELETION) return
|
|
71
|
+
if (renderMode.current === "hydrate") {
|
|
72
|
+
return nextIdle(() => {
|
|
73
|
+
vNode.flags & FLAG_DELETION || queueDelete(vNode)
|
|
74
|
+
})
|
|
87
75
|
}
|
|
76
|
+
queueDelete(vNode)
|
|
77
|
+
}
|
|
88
78
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
79
|
+
function queueWorkLoop() {
|
|
80
|
+
queueMicrotask(workLoop)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function wake() {
|
|
84
|
+
if (isRunning) return
|
|
85
|
+
isRunning = true
|
|
86
|
+
queueWorkLoop()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function sleep() {
|
|
90
|
+
isRunning = false
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function queueUpdate(vNode: VNode) {
|
|
94
|
+
// In immediate effect mode (useLayoutEffect), immediately mark the render as dirty
|
|
95
|
+
if (isImmediateEffectsMode) {
|
|
96
|
+
immediateEffectDirtiedRender = true
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
frameHandle = null
|
|
99
|
+
// If this node is currently being rendered, just mark it dirty
|
|
100
|
+
if (node.current === vNode) {
|
|
101
|
+
if (__DEV__) {
|
|
102
|
+
window.__kiru?.profilingContext?.emit("updateDirtied", appCtx!)
|
|
100
103
|
}
|
|
104
|
+
isRenderDirtied = true
|
|
105
|
+
return
|
|
101
106
|
}
|
|
102
107
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
// If it's already the next unit of work, no need to queue again
|
|
109
|
+
if (nextUnitOfWork === vNode) {
|
|
110
|
+
return
|
|
106
111
|
}
|
|
107
112
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
workLoop()
|
|
113
|
+
if (nextUnitOfWork === null) {
|
|
114
|
+
treesInProgress.push(vNode)
|
|
115
|
+
nextUnitOfWork = vNode
|
|
116
|
+
return wake()
|
|
114
117
|
}
|
|
115
118
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (__DEV__) {
|
|
125
|
-
window.__kiru?.profilingContext?.emit("updateDirtied", appCtx)
|
|
126
|
-
}
|
|
127
|
-
isRenderDirtied = true
|
|
128
|
-
return
|
|
119
|
+
for (let i = 0; i < treesInProgress.length; i++) {
|
|
120
|
+
const tree = treesInProgress[i]
|
|
121
|
+
if (tree !== vNode) continue
|
|
122
|
+
if (i < currentTreeIndex) {
|
|
123
|
+
// It was already processed; requeue it to the end
|
|
124
|
+
currentTreeIndex--
|
|
125
|
+
treesInProgress.splice(i, 1)
|
|
126
|
+
treesInProgress.push(tree)
|
|
129
127
|
}
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
// Check if this node is a descendant of any trees already queued
|
|
132
|
+
for (let i = 0; i < treesInProgress.length; i++) {
|
|
133
|
+
const tree = treesInProgress[i]
|
|
134
|
+
if (!vNodeContains(tree, vNode)) continue
|
|
135
135
|
|
|
136
|
-
if (
|
|
137
|
-
|
|
136
|
+
if (i === currentTreeIndex) {
|
|
137
|
+
// It's a child of the currently worked-on tree
|
|
138
|
+
// If it's deeper within the same tree, we can skip
|
|
139
|
+
if (vNodeContains(nextUnitOfWork, vNode)) return
|
|
140
|
+
// If it's not in the current work subtree, move back up to it
|
|
138
141
|
nextUnitOfWork = vNode
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// Check if the node is already in the treesInProgress queue
|
|
143
|
-
const treeIdx = treesInProgress.indexOf(vNode)
|
|
144
|
-
if (treeIdx !== -1) {
|
|
145
|
-
if (treeIdx === currentTreeIndex) {
|
|
146
|
-
// Replace current node if it's being worked on now
|
|
147
|
-
treesInProgress[treeIdx] = vNode
|
|
148
|
-
nextUnitOfWork = vNode
|
|
149
|
-
} else if (treeIdx < currentTreeIndex) {
|
|
150
|
-
// It was already processed; requeue it to the end
|
|
151
|
-
currentTreeIndex--
|
|
152
|
-
treesInProgress.splice(treeIdx, 1)
|
|
153
|
-
treesInProgress.push(vNode)
|
|
154
|
-
}
|
|
155
|
-
return
|
|
142
|
+
} else if (i < currentTreeIndex) {
|
|
143
|
+
// It's a descendant of an already processed tree; treat as a new update
|
|
144
|
+
treesInProgress.push(vNode)
|
|
156
145
|
}
|
|
157
146
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// Check if this node is a descendant of any trees already queued
|
|
161
|
-
for (let i = 0; i < treesInProgress.length; i++) {
|
|
162
|
-
const tree = treesInProgress[i]
|
|
163
|
-
if (tree.depth > nodeDepth) continue // Can't be an ancestor
|
|
164
|
-
if (!vNodeContains(tree, vNode)) continue
|
|
165
|
-
|
|
166
|
-
if (i === currentTreeIndex) {
|
|
167
|
-
// It's a child of the currently worked-on tree
|
|
168
|
-
// If it's deeper within the same tree, we can skip
|
|
169
|
-
if (vNodeContains(nextUnitOfWork, vNode)) return
|
|
170
|
-
// If it's not in the current work subtree, move back up to it
|
|
171
|
-
nextUnitOfWork = vNode
|
|
172
|
-
} else if (i < currentTreeIndex) {
|
|
173
|
-
// It's a descendant of an already processed tree; treat as a new update
|
|
174
|
-
treesInProgress.push(vNode)
|
|
175
|
-
}
|
|
147
|
+
return
|
|
148
|
+
}
|
|
176
149
|
|
|
177
|
-
|
|
150
|
+
// Check if this node contains any of the currently queued trees
|
|
151
|
+
let didReplaceTree = false
|
|
152
|
+
let shouldQueueAtEnd = false
|
|
153
|
+
for (let i = 0; i < treesInProgress.length; ) {
|
|
154
|
+
const tree = treesInProgress[i]
|
|
155
|
+
if (!vNodeContains(vNode, tree)) {
|
|
156
|
+
i++
|
|
157
|
+
continue
|
|
178
158
|
}
|
|
159
|
+
// This node contains another update root, replace it
|
|
179
160
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
i++
|
|
187
|
-
continue
|
|
188
|
-
}
|
|
189
|
-
// This node contains another update root, replace it
|
|
190
|
-
|
|
191
|
-
if (i === currentTreeIndex) {
|
|
192
|
-
if (!didReplaceTree) {
|
|
193
|
-
treesInProgress.splice(i, 1, vNode)
|
|
194
|
-
nextUnitOfWork = vNode
|
|
195
|
-
didReplaceTree = true
|
|
196
|
-
i++ // advance past replaced node
|
|
197
|
-
} else {
|
|
198
|
-
treesInProgress.splice(i, 1)
|
|
199
|
-
// no increment
|
|
200
|
-
}
|
|
201
|
-
} else if (i < currentTreeIndex) {
|
|
202
|
-
currentTreeIndex--
|
|
203
|
-
treesInProgress.splice(i, 1)
|
|
204
|
-
if (!didReplaceTree) {
|
|
205
|
-
shouldQueueAtEnd = true
|
|
206
|
-
didReplaceTree = true
|
|
207
|
-
}
|
|
208
|
-
// no increment
|
|
161
|
+
if (i === currentTreeIndex) {
|
|
162
|
+
if (!didReplaceTree) {
|
|
163
|
+
treesInProgress.splice(i, 1, vNode)
|
|
164
|
+
nextUnitOfWork = vNode
|
|
165
|
+
didReplaceTree = true
|
|
166
|
+
i++ // advance past replaced node
|
|
209
167
|
} else {
|
|
210
|
-
// i > currentTreeIndex
|
|
211
168
|
treesInProgress.splice(i, 1)
|
|
212
|
-
if (!didReplaceTree) {
|
|
213
|
-
shouldQueueAtEnd = true
|
|
214
|
-
didReplaceTree = true
|
|
215
|
-
}
|
|
216
169
|
// no increment
|
|
217
170
|
}
|
|
171
|
+
} else if (i < currentTreeIndex) {
|
|
172
|
+
currentTreeIndex--
|
|
173
|
+
treesInProgress.splice(i, 1)
|
|
174
|
+
if (!didReplaceTree) {
|
|
175
|
+
shouldQueueAtEnd = true
|
|
176
|
+
didReplaceTree = true
|
|
177
|
+
}
|
|
178
|
+
// no increment
|
|
179
|
+
} else {
|
|
180
|
+
// i > currentTreeIndex
|
|
181
|
+
treesInProgress.splice(i, 1)
|
|
182
|
+
if (!didReplaceTree) {
|
|
183
|
+
shouldQueueAtEnd = true
|
|
184
|
+
didReplaceTree = true
|
|
185
|
+
}
|
|
186
|
+
// no increment
|
|
218
187
|
}
|
|
219
|
-
if (!shouldQueueAtEnd && didReplaceTree) {
|
|
220
|
-
return
|
|
221
|
-
}
|
|
222
|
-
// If it doesn't overlap with any queued tree, queue as new independent update root
|
|
223
|
-
treesInProgress.push(vNode)
|
|
224
188
|
}
|
|
189
|
+
if (!shouldQueueAtEnd && didReplaceTree) {
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
// If it doesn't overlap with any queued tree, queue as new independent update root
|
|
193
|
+
treesInProgress.push(vNode)
|
|
194
|
+
}
|
|
225
195
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
196
|
+
function queueDelete(vNode: VNode) {
|
|
197
|
+
traverseApply(vNode, (n) => (n.flags |= FLAG_DELETION))
|
|
198
|
+
deletions.push(vNode)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function workLoop(): void {
|
|
202
|
+
if (__DEV__) {
|
|
203
|
+
const n = nextUnitOfWork ?? deletions[0] ?? treesInProgress[0]
|
|
204
|
+
if (n) {
|
|
205
|
+
appCtx = getVNodeAppContext(n)!
|
|
206
|
+
window.__kiru?.profilingContext?.beginTick(appCtx)
|
|
207
|
+
} else {
|
|
208
|
+
appCtx = null
|
|
209
|
+
}
|
|
229
210
|
}
|
|
230
211
|
|
|
231
|
-
|
|
232
|
-
|
|
212
|
+
while (nextUnitOfWork) {
|
|
213
|
+
nextUnitOfWork =
|
|
214
|
+
performUnitOfWork(nextUnitOfWork) ??
|
|
215
|
+
treesInProgress[++currentTreeIndex] ??
|
|
216
|
+
queueBlockedContextDependencyRoots()
|
|
233
217
|
}
|
|
234
218
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
219
|
+
if (!nextUnitOfWork && (deletions.length || treesInProgress.length)) {
|
|
220
|
+
while (deletions.length) {
|
|
221
|
+
commitDeletion(deletions.shift()!)
|
|
238
222
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
queueBlockedContextDependencyRoots()
|
|
245
|
-
|
|
246
|
-
if ((deadline?.timeRemaining() ?? 1) < 1) break
|
|
223
|
+
const workRoots = [...treesInProgress]
|
|
224
|
+
treesInProgress.length = 0
|
|
225
|
+
currentTreeIndex = 0
|
|
226
|
+
for (const root of workRoots) {
|
|
227
|
+
commitWork(root)
|
|
247
228
|
}
|
|
248
229
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
const treesInProgressCopy = [...treesInProgress]
|
|
254
|
-
treesInProgress = []
|
|
255
|
-
currentTreeIndex = 0
|
|
256
|
-
for (const tree of treesInProgressCopy) {
|
|
257
|
-
commitWork(tree)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
isImmediateEffectsMode = true
|
|
261
|
-
flushEffects(effectCallbacks.pre)
|
|
262
|
-
isImmediateEffectsMode = false
|
|
263
|
-
|
|
264
|
-
if (immediateEffectDirtiedRender) {
|
|
265
|
-
checkForTooManyConsecutiveDirtyRenders()
|
|
266
|
-
flushEffects(effectCallbacks.post)
|
|
267
|
-
immediateEffectDirtiedRender = false
|
|
268
|
-
consecutiveDirtyCount++
|
|
269
|
-
if (__DEV__) {
|
|
270
|
-
window.__kiru?.profilingContext?.endTick(appCtx)
|
|
271
|
-
window.__kiru?.profilingContext?.emit("updateDirtied", appCtx)
|
|
272
|
-
}
|
|
273
|
-
return workLoop()
|
|
274
|
-
}
|
|
275
|
-
consecutiveDirtyCount = 0
|
|
230
|
+
isImmediateEffectsMode = true
|
|
231
|
+
flushEffects(preEffects)
|
|
232
|
+
isImmediateEffectsMode = false
|
|
276
233
|
|
|
277
|
-
|
|
278
|
-
|
|
234
|
+
if (immediateEffectDirtiedRender) {
|
|
235
|
+
checkForTooManyConsecutiveDirtyRenders()
|
|
236
|
+
flushEffects(postEffects)
|
|
237
|
+
immediateEffectDirtiedRender = false
|
|
238
|
+
consecutiveDirtyCount++
|
|
279
239
|
if (__DEV__) {
|
|
280
|
-
window.__kiru?.profilingContext?.
|
|
240
|
+
window.__kiru?.profilingContext?.endTick(appCtx!)
|
|
241
|
+
window.__kiru?.profilingContext?.emit("updateDirtied", appCtx!)
|
|
281
242
|
}
|
|
243
|
+
return flushSync()
|
|
282
244
|
}
|
|
245
|
+
consecutiveDirtyCount = 0
|
|
283
246
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
247
|
+
flushEffects(postEffects)
|
|
248
|
+
if (__DEV__) {
|
|
249
|
+
window.__kiru!.emit("update", appCtx!)
|
|
250
|
+
window.__kiru?.profilingContext?.emit("update", appCtx!)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!nextUnitOfWork) {
|
|
255
|
+
sleep()
|
|
256
|
+
while (nextIdleEffects.length) {
|
|
257
|
+
nextIdleEffects.shift()!()
|
|
258
|
+
}
|
|
259
|
+
if (__DEV__) {
|
|
260
|
+
if (appCtx) {
|
|
290
261
|
window.__kiru?.profilingContext?.endTick(appCtx)
|
|
291
262
|
}
|
|
292
|
-
return
|
|
293
263
|
}
|
|
294
|
-
|
|
295
|
-
requestIdleCallback(workLoop)
|
|
264
|
+
return
|
|
296
265
|
}
|
|
297
266
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
frameDeadline = time + maxFrameMs
|
|
301
|
-
pendingCallback = callback
|
|
302
|
-
channel.port1.postMessage(null)
|
|
303
|
-
})
|
|
304
|
-
}
|
|
267
|
+
queueWorkLoop()
|
|
268
|
+
}
|
|
305
269
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
// root is a parent of dep and there's a memo between them, prevent consolidation and queue as new root
|
|
323
|
-
break
|
|
324
|
-
}
|
|
325
|
-
return
|
|
326
|
-
}
|
|
327
|
-
if (depDepth < rootDepth && vNodeContains(dep, root)) {
|
|
328
|
-
jobRoots[i] = dep
|
|
329
|
-
return
|
|
270
|
+
function queueBlockedContextDependencyRoots(): VNode | null {
|
|
271
|
+
if (pendingContextChanges.size === 0) return null
|
|
272
|
+
|
|
273
|
+
// TODO: it's possible that a 'job' created by this process is
|
|
274
|
+
// blocked by a parent memo after a queueUpdate -> replaceTree action.
|
|
275
|
+
// To prevent this, we might need to add these to a distinct queue.
|
|
276
|
+
const jobRoots: VNode[] = []
|
|
277
|
+
pendingContextChanges.forEach((provider) => {
|
|
278
|
+
provider.props.dependents.forEach((dep) => {
|
|
279
|
+
if (!willMemoBlockUpdate(provider, dep)) return
|
|
280
|
+
for (let i = 0; i < jobRoots.length; i++) {
|
|
281
|
+
const root = jobRoots[i]
|
|
282
|
+
if (vNodeContains(root, dep)) {
|
|
283
|
+
if (willMemoBlockUpdate(root, dep)) {
|
|
284
|
+
// root is a parent of dep and there's a memo between them, prevent consolidation and queue as new root
|
|
285
|
+
break
|
|
330
286
|
}
|
|
287
|
+
return
|
|
331
288
|
}
|
|
332
|
-
|
|
333
|
-
|
|
289
|
+
if (vNodeContains(dep, root)) {
|
|
290
|
+
jobRoots[i] = dep
|
|
291
|
+
return
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
jobRoots.push(dep)
|
|
334
295
|
})
|
|
296
|
+
})
|
|
335
297
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
298
|
+
pendingContextChanges.clear()
|
|
299
|
+
treesInProgress.push(...jobRoots)
|
|
300
|
+
return jobRoots[0] ?? null
|
|
301
|
+
}
|
|
340
302
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
303
|
+
function performUnitOfWork(vNode: VNode): VNode | void {
|
|
304
|
+
let renderChild = true
|
|
305
|
+
try {
|
|
306
|
+
const { props } = vNode
|
|
307
|
+
if (typeof vNode.type === "string") {
|
|
308
|
+
updateHostComponent(vNode as DomVNode)
|
|
309
|
+
} else if (isExoticType(vNode.type)) {
|
|
310
|
+
if (vNode.type === $CONTEXT_PROVIDER) {
|
|
311
|
+
const asProvider = vNode as ContextProviderNode<any>
|
|
312
|
+
const { dependents, value } = asProvider.props
|
|
313
|
+
if (
|
|
314
|
+
dependents.size &&
|
|
315
|
+
asProvider.prev &&
|
|
316
|
+
asProvider.prev.props.value !== value
|
|
317
|
+
) {
|
|
318
|
+
pendingContextChanges.add(asProvider)
|
|
358
319
|
}
|
|
359
|
-
vNode.child = reconcileChildren(vNode, props.children)
|
|
360
|
-
vNode.deletions?.forEach((d) => queueDelete(d))
|
|
361
|
-
} else {
|
|
362
|
-
renderChild = updateFunctionComponent(vNode as FunctionVNode)
|
|
363
320
|
}
|
|
364
|
-
|
|
321
|
+
vNode.child = reconcileChildren(vNode, props.children)
|
|
322
|
+
queueNodeChildDeletions(vNode)
|
|
323
|
+
} else {
|
|
324
|
+
renderChild = updateFunctionComponent(vNode as FunctionVNode)
|
|
325
|
+
}
|
|
326
|
+
} catch (error) {
|
|
327
|
+
if (__DEV__) {
|
|
365
328
|
window.__kiru?.emit(
|
|
366
329
|
"error",
|
|
367
|
-
appCtx
|
|
330
|
+
appCtx!,
|
|
368
331
|
error instanceof Error ? error : new Error(String(error))
|
|
369
332
|
)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
throw error
|
|
378
|
-
}
|
|
379
|
-
console.error(error)
|
|
380
|
-
return
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (KiruError.isKiruError(error)) {
|
|
336
|
+
if (error.customNodeStack) {
|
|
337
|
+
setTimeout(() => {
|
|
338
|
+
throw new Error(error.customNodeStack)
|
|
339
|
+
})
|
|
381
340
|
}
|
|
382
|
-
|
|
341
|
+
if (error.fatal) {
|
|
383
342
|
throw error
|
|
384
|
-
}
|
|
343
|
+
}
|
|
344
|
+
console.error(error)
|
|
345
|
+
return
|
|
385
346
|
}
|
|
347
|
+
setTimeout(() => {
|
|
348
|
+
throw error
|
|
349
|
+
})
|
|
350
|
+
}
|
|
386
351
|
|
|
387
|
-
|
|
388
|
-
|
|
352
|
+
if (renderChild && vNode.child) {
|
|
353
|
+
return vNode.child
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
let nextNode: VNode | null = vNode
|
|
357
|
+
while (nextNode) {
|
|
358
|
+
// queue effects upon ascent
|
|
359
|
+
if (nextNode.immediateEffects) {
|
|
360
|
+
preEffects.push(...nextNode.immediateEffects)
|
|
361
|
+
nextNode.immediateEffects = undefined
|
|
362
|
+
}
|
|
363
|
+
if (nextNode.effects) {
|
|
364
|
+
postEffects.push(...nextNode.effects)
|
|
365
|
+
nextNode.effects = undefined
|
|
389
366
|
}
|
|
390
367
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
effectCallbacks.pre.push(...nextNode.immediateEffects)
|
|
396
|
-
nextNode.immediateEffects = undefined
|
|
397
|
-
}
|
|
398
|
-
if (nextNode.effects) {
|
|
399
|
-
effectCallbacks.post.push(...nextNode.effects)
|
|
400
|
-
nextNode.effects = undefined
|
|
401
|
-
}
|
|
402
|
-
if (nextNode === treesInProgress[currentTreeIndex]) return
|
|
403
|
-
if (nextNode.sibling) {
|
|
404
|
-
return nextNode.sibling
|
|
405
|
-
}
|
|
368
|
+
if (nextNode === treesInProgress[currentTreeIndex]) return
|
|
369
|
+
if (nextNode.sibling) {
|
|
370
|
+
return nextNode.sibling
|
|
371
|
+
}
|
|
406
372
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
373
|
+
nextNode = nextNode.parent
|
|
374
|
+
if (renderMode.current === "hydrate" && nextNode?.dom) {
|
|
375
|
+
hydrationStack.pop()
|
|
411
376
|
}
|
|
412
377
|
}
|
|
378
|
+
}
|
|
413
379
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}
|
|
380
|
+
function updateFunctionComponent(vNode: FunctionVNode) {
|
|
381
|
+
const { type, props, subs, prev, isMemoized } = vNode
|
|
382
|
+
if (isMemoized) {
|
|
383
|
+
vNode.memoizedProps = props
|
|
384
|
+
if (
|
|
385
|
+
prev?.memoizedProps &&
|
|
386
|
+
vNode.arePropsEqual!(prev.memoizedProps, props) &&
|
|
387
|
+
!vNode.hmrUpdated
|
|
388
|
+
) {
|
|
389
|
+
return false
|
|
425
390
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
subs.clear()
|
|
449
|
-
}
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
node.current = vNode
|
|
394
|
+
let newChild
|
|
395
|
+
let renderTryCount = 0
|
|
396
|
+
do {
|
|
397
|
+
isRenderDirtied = false
|
|
398
|
+
hookIndex.current = 0
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* remove previous signal subscriptions (if any) every render.
|
|
402
|
+
* this prevents no-longer-observed signals from triggering updates
|
|
403
|
+
* in components that are not currently using them.
|
|
404
|
+
*
|
|
405
|
+
* TODO: in future, we might be able to optimize this by
|
|
406
|
+
* only clearing the subscriptions that are no longer needed
|
|
407
|
+
* and not clearing the entire set.
|
|
408
|
+
*/
|
|
409
|
+
if (subs) {
|
|
410
|
+
subs.forEach((unsub) => unsub())
|
|
411
|
+
subs.clear()
|
|
412
|
+
}
|
|
450
413
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
462
|
-
continue
|
|
414
|
+
if (__DEV__) {
|
|
415
|
+
newChild = latest(type)(props)
|
|
416
|
+
delete vNode.hmrUpdated
|
|
417
|
+
if (++renderTryCount > CONSECUTIVE_DIRTY_LIMIT) {
|
|
418
|
+
throw new KiruError({
|
|
419
|
+
message:
|
|
420
|
+
"Too many re-renders. Kiru limits the number of renders to prevent an infinite loop.",
|
|
421
|
+
fatal: true,
|
|
422
|
+
vNode,
|
|
423
|
+
})
|
|
463
424
|
}
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
425
|
+
continue
|
|
426
|
+
}
|
|
427
|
+
newChild = type(props)
|
|
428
|
+
} while (isRenderDirtied)
|
|
429
|
+
vNode.child = reconcileChildren(vNode, newChild)
|
|
430
|
+
queueNodeChildDeletions(vNode)
|
|
431
|
+
return true
|
|
432
|
+
} finally {
|
|
433
|
+
node.current = null
|
|
472
434
|
}
|
|
435
|
+
}
|
|
473
436
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
437
|
+
function updateHostComponent(vNode: DomVNode) {
|
|
438
|
+
const { props, type } = vNode
|
|
439
|
+
if (__DEV__) {
|
|
440
|
+
assertValidElementProps(vNode)
|
|
441
|
+
}
|
|
442
|
+
if (!vNode.dom) {
|
|
443
|
+
if (renderMode.current === "hydrate") {
|
|
444
|
+
hydrateDom(vNode)
|
|
445
|
+
} else {
|
|
446
|
+
vNode.dom = createDom(vNode)
|
|
478
447
|
}
|
|
479
|
-
if (
|
|
480
|
-
if (
|
|
481
|
-
hydrateDom(vNode)
|
|
482
|
-
} else {
|
|
483
|
-
vNode.dom = createDom(vNode)
|
|
484
|
-
}
|
|
485
|
-
if (__DEV__) {
|
|
486
|
-
// @ts-expect-error we apply vNode to the dom node
|
|
448
|
+
if (__DEV__) {
|
|
449
|
+
if (vNode.dom instanceof Element) {
|
|
487
450
|
vNode.dom.__kiruNode = vNode
|
|
488
451
|
}
|
|
489
452
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
453
|
+
}
|
|
454
|
+
// text should _never_ have children
|
|
455
|
+
if (type !== "#text") {
|
|
456
|
+
vNode.child = reconcileChildren(vNode, props.children)
|
|
457
|
+
queueNodeChildDeletions(vNode)
|
|
496
458
|
if (vNode.child && renderMode.current === "hydrate") {
|
|
497
459
|
hydrationStack.push(vNode.dom!)
|
|
498
460
|
}
|
|
499
461
|
}
|
|
462
|
+
}
|
|
500
463
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
)
|
|
506
|
-
}
|
|
464
|
+
function queueNodeChildDeletions(vNode: VNode) {
|
|
465
|
+
if (vNode.deletions) {
|
|
466
|
+
vNode.deletions.forEach(queueDelete)
|
|
467
|
+
vNode.deletions = null
|
|
507
468
|
}
|
|
469
|
+
}
|
|
508
470
|
|
|
509
|
-
|
|
510
|
-
|
|
471
|
+
function checkForTooManyConsecutiveDirtyRenders() {
|
|
472
|
+
if (consecutiveDirtyCount > CONSECUTIVE_DIRTY_LIMIT) {
|
|
473
|
+
throw new KiruError(
|
|
474
|
+
"Maximum update depth exceeded. This can happen when a component repeatedly calls setState during render or in useLayoutEffect. Kiru limits the number of nested updates to prevent infinite loops."
|
|
475
|
+
)
|
|
511
476
|
}
|
|
477
|
+
}
|
|
512
478
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
flushSync,
|
|
519
|
-
queueUpdate,
|
|
520
|
-
queueDelete,
|
|
521
|
-
})
|
|
479
|
+
function flushEffects(effectArr: Function[]) {
|
|
480
|
+
for (let i = 0; i < effectArr.length; i++) {
|
|
481
|
+
effectArr[i]()
|
|
482
|
+
}
|
|
483
|
+
effectArr.length = 0
|
|
522
484
|
}
|