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