kiru 0.54.0-preview.1 → 0.54.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/components/derive.d.ts +1 -1
- package/dist/components/derive.d.ts.map +1 -1
- package/dist/components/derive.js +2 -3
- package/dist/components/derive.js.map +1 -1
- package/dist/components/memo.d.ts +1 -3
- package/dist/components/memo.d.ts.map +1 -1
- package/dist/components/memo.js +2 -2
- package/dist/components/memo.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -23
- package/dist/context.js.map +1 -1
- package/dist/dom.d.ts.map +1 -1
- package/dist/dom.js +111 -78
- package/dist/dom.js.map +1 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +2 -4
- package/dist/error.js.map +1 -1
- package/dist/form/index.d.ts.map +1 -1
- package/dist/form/index.js +6 -10
- package/dist/form/index.js.map +1 -1
- package/dist/globals.d.ts +1 -1
- package/dist/globals.d.ts.map +1 -1
- package/dist/globals.js.map +1 -1
- package/dist/hmr.d.ts +1 -0
- package/dist/hmr.d.ts.map +1 -1
- package/dist/hmr.js +11 -3
- package/dist/hmr.js.map +1 -1
- package/dist/hooks/useEffectEvent.d.ts.map +1 -1
- package/dist/hooks/useEffectEvent.js.map +1 -1
- package/dist/hooks/usePromise.d.ts +1 -2
- package/dist/hooks/usePromise.d.ts.map +1 -1
- package/dist/hooks/usePromise.js +62 -31
- package/dist/hooks/usePromise.js.map +1 -1
- package/dist/hooks/utils.d.ts.map +1 -1
- package/dist/hooks/utils.js +10 -10
- package/dist/hooks/utils.js.map +1 -1
- package/dist/hydration.d.ts +6 -13
- package/dist/hydration.d.ts.map +1 -1
- package/dist/hydration.js +20 -50
- package/dist/hydration.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/reconciler.d.ts.map +1 -1
- package/dist/reconciler.js +3 -6
- package/dist/reconciler.js.map +1 -1
- package/dist/recursiveRender.d.ts.map +1 -1
- package/dist/recursiveRender.js +9 -8
- package/dist/recursiveRender.js.map +1 -1
- package/dist/renderToString.d.ts.map +1 -1
- package/dist/renderToString.js.map +1 -1
- package/dist/router/client/index.d.ts +2 -4
- package/dist/router/client/index.d.ts.map +1 -1
- package/dist/router/client/index.js +13 -59
- package/dist/router/client/index.js.map +1 -1
- package/dist/router/context.d.ts +5 -2
- package/dist/router/context.d.ts.map +1 -1
- package/dist/router/context.js +1 -5
- package/dist/router/context.js.map +1 -1
- package/dist/router/fileRouter.d.ts.map +1 -1
- package/dist/router/fileRouter.js +2 -4
- package/dist/router/fileRouter.js.map +1 -1
- package/dist/router/fileRouterController.d.ts +2 -2
- package/dist/router/fileRouterController.d.ts.map +1 -1
- package/dist/router/fileRouterController.js +135 -214
- package/dist/router/fileRouterController.js.map +1 -1
- package/dist/router/globals.d.ts +0 -3
- package/dist/router/globals.d.ts.map +1 -1
- package/dist/router/globals.js +0 -3
- package/dist/router/globals.js.map +1 -1
- package/dist/router/head.d.ts.map +1 -1
- package/dist/router/head.js +7 -5
- package/dist/router/head.js.map +1 -1
- package/dist/router/index.d.ts +1 -2
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +1 -2
- package/dist/router/index.js.map +1 -1
- package/dist/router/link.js +3 -3
- package/dist/router/link.js.map +1 -1
- package/dist/router/{ssg → server}/index.d.ts +4 -4
- package/dist/router/server/index.d.ts.map +1 -0
- package/dist/router/{ssg → server}/index.js +7 -9
- package/dist/router/server/index.js.map +1 -0
- package/dist/router/types.d.ts +16 -42
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/types.internal.d.ts +0 -4
- package/dist/router/types.internal.d.ts.map +1 -1
- package/dist/router/utils/index.d.ts +3 -8
- package/dist/router/utils/index.d.ts.map +1 -1
- package/dist/router/utils/index.js +8 -40
- package/dist/router/utils/index.js.map +1 -1
- package/dist/scheduler.d.ts +3 -14
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +64 -56
- package/dist/scheduler.js.map +1 -1
- package/dist/signals/base.d.ts +0 -2
- package/dist/signals/base.d.ts.map +1 -1
- package/dist/signals/base.js +0 -6
- package/dist/signals/base.js.map +1 -1
- package/dist/signals/computed.d.ts +3 -0
- package/dist/signals/computed.d.ts.map +1 -1
- package/dist/signals/computed.js +29 -20
- package/dist/signals/computed.js.map +1 -1
- package/dist/signals/for.d.ts +3 -3
- package/dist/signals/for.d.ts.map +1 -1
- package/dist/signals/for.js +2 -1
- package/dist/signals/for.js.map +1 -1
- package/dist/signals/utils.d.ts.map +1 -1
- package/dist/signals/utils.js +2 -1
- package/dist/signals/utils.js.map +1 -1
- package/dist/signals/watch.d.ts.map +1 -1
- package/dist/signals/watch.js +18 -22
- package/dist/signals/watch.js.map +1 -1
- package/dist/ssr/client.d.ts +1 -1
- package/dist/ssr/client.d.ts.map +1 -1
- package/dist/ssr/client.js +0 -2
- package/dist/ssr/client.js.map +1 -1
- package/dist/ssr/server.d.ts +3 -9
- package/dist/ssr/server.d.ts.map +1 -1
- package/dist/ssr/server.js +30 -37
- package/dist/ssr/server.js.map +1 -1
- package/dist/types.d.ts +0 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.dom.d.ts +3 -3
- package/dist/types.dom.d.ts.map +1 -1
- package/dist/utils/format.d.ts +1 -2
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +1 -4
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/promise.d.ts +0 -2
- package/dist/utils/promise.d.ts.map +1 -1
- package/dist/utils/promise.js +1 -45
- package/dist/utils/promise.js.map +1 -1
- package/dist/utils/runtime.d.ts +3 -2
- package/dist/utils/runtime.d.ts.map +1 -1
- package/dist/utils/runtime.js +5 -2
- package/dist/utils/runtime.js.map +1 -1
- package/dist/utils/vdom.d.ts.map +1 -1
- package/dist/utils/vdom.js +2 -2
- package/dist/utils/vdom.js.map +1 -1
- package/package.json +4 -8
- package/src/components/derive.ts +3 -5
- package/src/components/memo.ts +3 -11
- package/src/context.ts +1 -24
- package/src/dom.ts +146 -101
- package/src/error.ts +2 -4
- package/src/form/index.ts +6 -9
- package/src/globals.ts +1 -1
- package/src/hmr.ts +14 -5
- package/src/hooks/useEffectEvent.ts +0 -1
- package/src/hooks/usePromise.ts +77 -58
- package/src/hooks/utils.ts +12 -12
- package/src/hydration.ts +21 -57
- package/src/index.ts +1 -1
- package/src/reconciler.ts +2 -6
- package/src/recursiveRender.ts +10 -9
- package/src/renderToString.ts +0 -1
- package/src/router/client/index.ts +16 -114
- package/src/router/context.ts +6 -7
- package/src/router/fileRouter.ts +2 -6
- package/src/router/fileRouterController.ts +161 -324
- package/src/router/globals.ts +0 -4
- package/src/router/head.ts +7 -5
- package/src/router/index.ts +1 -12
- package/src/router/link.ts +3 -3
- package/src/router/{ssg → server}/index.ts +13 -18
- package/src/router/types.internal.ts +0 -5
- package/src/router/types.ts +16 -53
- package/src/router/utils/index.ts +16 -79
- package/src/scheduler.ts +85 -89
- package/src/signals/base.ts +0 -8
- package/src/signals/computed.ts +30 -18
- package/src/signals/for.ts +15 -10
- package/src/signals/utils.ts +2 -1
- package/src/signals/watch.ts +27 -22
- package/src/ssr/client.ts +1 -4
- package/src/ssr/server.ts +34 -59
- package/src/types.dom.ts +4 -5
- package/src/types.ts +0 -10
- package/src/utils/format.ts +0 -5
- package/src/utils/index.ts +1 -1
- package/src/utils/promise.ts +1 -70
- package/src/utils/runtime.ts +6 -2
- package/src/utils/vdom.ts +2 -7
- package/dist/router/constants.d.ts +0 -2
- package/dist/router/constants.d.ts.map +0 -1
- package/dist/router/constants.js +0 -2
- package/dist/router/constants.js.map +0 -1
- package/dist/router/guard.d.ts +0 -17
- package/dist/router/guard.d.ts.map +0 -1
- package/dist/router/guard.js +0 -45
- package/dist/router/guard.js.map +0 -1
- package/dist/router/ssg/index.d.ts.map +0 -1
- package/dist/router/ssg/index.js.map +0 -1
- package/dist/router/ssr/index.d.ts +0 -20
- package/dist/router/ssr/index.d.ts.map +0 -1
- package/dist/router/ssr/index.js +0 -163
- package/dist/router/ssr/index.js.map +0 -1
- package/src/router/constants.ts +0 -1
- package/src/router/guard.ts +0 -72
- package/src/router/ssr/index.ts +0 -252
package/src/scheduler.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
import {
|
|
8
8
|
$CONTEXT_PROVIDER,
|
|
9
9
|
$ERROR_BOUNDARY,
|
|
10
|
+
$MEMO,
|
|
10
11
|
CONSECUTIVE_DIRTY_LIMIT,
|
|
11
12
|
FLAG_DELETION,
|
|
12
13
|
FLAG_DIRTY,
|
|
@@ -33,8 +34,11 @@ import {
|
|
|
33
34
|
isExoticType,
|
|
34
35
|
getVNodeAppContext,
|
|
35
36
|
findParentErrorBoundary,
|
|
37
|
+
call,
|
|
36
38
|
} from "./utils/index.js"
|
|
37
39
|
import type { AppContext } from "./appContext"
|
|
40
|
+
import type { MemoFn } from "./components/memo"
|
|
41
|
+
import { isHmrUpdate } from "./hmr.js"
|
|
38
42
|
|
|
39
43
|
type VNode = Kiru.VNode
|
|
40
44
|
|
|
@@ -52,27 +56,10 @@ let postEffects: Array<Function> = []
|
|
|
52
56
|
let animationFrameHandle = -1
|
|
53
57
|
|
|
54
58
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* This works in two modes:
|
|
58
|
-
* - `await nextIdle()` resolves once idle, allowing async/await usage.
|
|
59
|
-
* - `nextIdle(fn)` schedules a callback to run when idle.
|
|
60
|
-
*
|
|
61
|
-
* Callbacks are executed before promises resolve,
|
|
62
|
-
* and multiple calls queue until the scheduler becomes idle.
|
|
59
|
+
* Runs a function after any existing work has been completed,
|
|
60
|
+
* or immediately if the scheduler is already idle.
|
|
63
61
|
*/
|
|
64
|
-
export function nextIdle():
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Schedules `fn` to run once the scheduler becomes idle.
|
|
68
|
-
* If already idle, `fn` executes immediately.
|
|
69
|
-
*/
|
|
70
|
-
export function nextIdle<T extends () => void>(fn: T): void
|
|
71
|
-
|
|
72
|
-
export function nextIdle(fn?: () => void): void | Promise<void> {
|
|
73
|
-
if (!fn) {
|
|
74
|
-
return new Promise<void>(nextIdle)
|
|
75
|
-
}
|
|
62
|
+
export function nextIdle(fn: () => void): void {
|
|
76
63
|
if (isRunningOrQueued) {
|
|
77
64
|
nextIdleEffects.push(fn)
|
|
78
65
|
return
|
|
@@ -83,13 +70,13 @@ export function nextIdle(fn?: () => void): void | Promise<void> {
|
|
|
83
70
|
/**
|
|
84
71
|
* Syncronously flushes any pending work.
|
|
85
72
|
*/
|
|
86
|
-
export function flushSync() {
|
|
73
|
+
export function flushSync(): void {
|
|
87
74
|
if (!isRunningOrQueued) return
|
|
88
75
|
window.cancelAnimationFrame(animationFrameHandle)
|
|
89
76
|
doWork()
|
|
90
77
|
}
|
|
91
78
|
|
|
92
|
-
export function renderRootSync(rootNode: VNode) {
|
|
79
|
+
export function renderRootSync(rootNode: VNode): void {
|
|
93
80
|
rootNode.flags |= FLAG_DIRTY
|
|
94
81
|
treesInProgress.push(rootNode)
|
|
95
82
|
|
|
@@ -107,20 +94,20 @@ export function requestUpdate(vNode: VNode): void {
|
|
|
107
94
|
queueUpdate(vNode)
|
|
108
95
|
}
|
|
109
96
|
|
|
110
|
-
function queueBeginWork() {
|
|
97
|
+
function queueBeginWork(): void {
|
|
111
98
|
if (isRunningOrQueued) return
|
|
112
99
|
isRunningOrQueued = true
|
|
113
100
|
animationFrameHandle = window.requestAnimationFrame(doWork)
|
|
114
101
|
}
|
|
115
102
|
|
|
116
|
-
function onWorkFinished() {
|
|
103
|
+
function onWorkFinished(): void {
|
|
117
104
|
isRunningOrQueued = false
|
|
118
105
|
while (nextIdleEffects.length) {
|
|
119
106
|
nextIdleEffects.shift()!()
|
|
120
107
|
}
|
|
121
108
|
}
|
|
122
109
|
|
|
123
|
-
function queueUpdate(vNode: VNode) {
|
|
110
|
+
function queueUpdate(vNode: VNode): void {
|
|
124
111
|
// In immediate effect mode (useLayoutEffect), immediately mark the render as dirty
|
|
125
112
|
if (isImmediateEffectsMode) {
|
|
126
113
|
immediateEffectDirtiedRender = true
|
|
@@ -146,12 +133,12 @@ function queueUpdate(vNode: VNode) {
|
|
|
146
133
|
treesInProgress.push(vNode)
|
|
147
134
|
}
|
|
148
135
|
|
|
149
|
-
function queueDelete(vNode: VNode) {
|
|
136
|
+
function queueDelete(vNode: VNode): void {
|
|
150
137
|
traverseApply(vNode, (n) => (n.flags |= FLAG_DELETION))
|
|
151
138
|
deletions.push(vNode)
|
|
152
139
|
}
|
|
153
140
|
|
|
154
|
-
const depthSort = (a: VNode, b: VNode) => b.depth - a.depth
|
|
141
|
+
const depthSort = (a: VNode, b: VNode): number => b.depth - a.depth
|
|
155
142
|
|
|
156
143
|
let currentWorkRoot: VNode | null = null
|
|
157
144
|
|
|
@@ -180,7 +167,7 @@ function doWork(): void {
|
|
|
180
167
|
const flags = currentWorkRoot.flags
|
|
181
168
|
if (flags & FLAG_DELETION) continue
|
|
182
169
|
if (flags & FLAG_DIRTY) {
|
|
183
|
-
let n: VNode |
|
|
170
|
+
let n: VNode | null = currentWorkRoot
|
|
184
171
|
while ((n = performUnitOfWork(n))) {}
|
|
185
172
|
|
|
186
173
|
while (deletions.length) {
|
|
@@ -211,7 +198,7 @@ function doWork(): void {
|
|
|
211
198
|
consecutiveDirtyCount = 0
|
|
212
199
|
|
|
213
200
|
onWorkFinished()
|
|
214
|
-
flushEffects(postEffects)
|
|
201
|
+
queueMicrotask(() => flushEffects(postEffects))
|
|
215
202
|
if (__DEV__) {
|
|
216
203
|
window.__kiru.emit("update", appCtx!)
|
|
217
204
|
window.__kiru.profilingContext?.emit("update", appCtx!)
|
|
@@ -219,15 +206,57 @@ function doWork(): void {
|
|
|
219
206
|
}
|
|
220
207
|
}
|
|
221
208
|
|
|
222
|
-
function performUnitOfWork(vNode: VNode): VNode |
|
|
223
|
-
|
|
209
|
+
function performUnitOfWork(vNode: VNode): VNode | null {
|
|
210
|
+
const next = updateVNode(vNode)
|
|
211
|
+
|
|
212
|
+
if (vNode.deletions !== null) {
|
|
213
|
+
vNode.deletions.forEach(queueDelete)
|
|
214
|
+
vNode.deletions = null
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (next) {
|
|
218
|
+
return next
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let nextNode: VNode | null = vNode
|
|
222
|
+
while (nextNode) {
|
|
223
|
+
// queue effects upon ascent
|
|
224
|
+
if (nextNode.immediateEffects) {
|
|
225
|
+
preEffects.push(...nextNode.immediateEffects)
|
|
226
|
+
nextNode.immediateEffects = undefined
|
|
227
|
+
}
|
|
228
|
+
if (nextNode.effects) {
|
|
229
|
+
postEffects.push(...nextNode.effects)
|
|
230
|
+
nextNode.effects = undefined
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (nextNode === currentWorkRoot) return null
|
|
234
|
+
if (nextNode.sibling) {
|
|
235
|
+
return nextNode.sibling
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
nextNode = nextNode.parent
|
|
239
|
+
if (renderMode.current === "hydrate" && nextNode?.dom) {
|
|
240
|
+
hydrationStack.pop()
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return null
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function updateVNode(vNode: VNode): VNode | null {
|
|
248
|
+
const { type, props, prev, flags } = vNode
|
|
249
|
+
if (__DEV__ && isHmrUpdate()) {
|
|
250
|
+
} else if ((flags & FLAG_DIRTY) === 0 && props === prev?.props) {
|
|
251
|
+
return null
|
|
252
|
+
}
|
|
224
253
|
try {
|
|
225
|
-
if (typeof
|
|
226
|
-
updateHostComponent(vNode as DomVNode)
|
|
227
|
-
} else if (isExoticType(
|
|
228
|
-
updateExoticComponent(vNode)
|
|
254
|
+
if (typeof type === "string") {
|
|
255
|
+
return updateHostComponent(vNode as DomVNode)
|
|
256
|
+
} else if (isExoticType(type)) {
|
|
257
|
+
return updateExoticComponent(vNode)
|
|
229
258
|
} else {
|
|
230
|
-
|
|
259
|
+
return updateFunctionComponent(vNode as FunctionVNode)
|
|
231
260
|
}
|
|
232
261
|
} catch (error) {
|
|
233
262
|
if (__DEV__) {
|
|
@@ -260,47 +289,16 @@ function performUnitOfWork(vNode: VNode): VNode | void {
|
|
|
260
289
|
throw error
|
|
261
290
|
}
|
|
262
291
|
console.error(error)
|
|
263
|
-
return
|
|
292
|
+
return vNode.child
|
|
264
293
|
}
|
|
265
294
|
setTimeout(() => {
|
|
266
295
|
throw error
|
|
267
296
|
})
|
|
268
297
|
}
|
|
269
|
-
|
|
270
|
-
if (vNode.deletions !== null) {
|
|
271
|
-
vNode.deletions.forEach(queueDelete)
|
|
272
|
-
vNode.deletions = null
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (renderChild && vNode.child) {
|
|
276
|
-
return vNode.child
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
let nextNode: VNode | null = vNode
|
|
280
|
-
while (nextNode) {
|
|
281
|
-
// queue effects upon ascent
|
|
282
|
-
if (nextNode.immediateEffects) {
|
|
283
|
-
preEffects.push(...nextNode.immediateEffects)
|
|
284
|
-
nextNode.immediateEffects = undefined
|
|
285
|
-
}
|
|
286
|
-
if (nextNode.effects) {
|
|
287
|
-
postEffects.push(...nextNode.effects)
|
|
288
|
-
nextNode.effects = undefined
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (nextNode === currentWorkRoot) return
|
|
292
|
-
if (nextNode.sibling) {
|
|
293
|
-
return nextNode.sibling
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
nextNode = nextNode.parent
|
|
297
|
-
if (renderMode.current === "hydrate" && nextNode?.dom) {
|
|
298
|
-
hydrationStack.pop()
|
|
299
|
-
}
|
|
300
|
-
}
|
|
298
|
+
return null
|
|
301
299
|
}
|
|
302
300
|
|
|
303
|
-
function updateExoticComponent(vNode: VNode) {
|
|
301
|
+
function updateExoticComponent(vNode: VNode): VNode | null {
|
|
304
302
|
const { props, type } = vNode
|
|
305
303
|
let children = props.children
|
|
306
304
|
|
|
@@ -326,20 +324,19 @@ function updateExoticComponent(vNode: VNode) {
|
|
|
326
324
|
}
|
|
327
325
|
}
|
|
328
326
|
|
|
329
|
-
vNode.child = reconcileChildren(vNode, children)
|
|
327
|
+
return (vNode.child = reconcileChildren(vNode, children))
|
|
330
328
|
}
|
|
331
329
|
|
|
332
|
-
function updateFunctionComponent(vNode: FunctionVNode) {
|
|
330
|
+
function updateFunctionComponent(vNode: FunctionVNode): VNode | null {
|
|
333
331
|
const { type, props, subs, prev, flags } = vNode
|
|
334
332
|
if (flags & FLAG_MEMO) {
|
|
335
|
-
vNode.memoizedProps = props
|
|
336
333
|
if (
|
|
337
|
-
prev
|
|
338
|
-
|
|
339
|
-
!
|
|
334
|
+
prev &&
|
|
335
|
+
(type as MemoFn)[$MEMO](prev.props, props) &&
|
|
336
|
+
!(__DEV__ && isHmrUpdate())
|
|
340
337
|
) {
|
|
341
338
|
vNode.flags |= FLAG_NOOP
|
|
342
|
-
return
|
|
339
|
+
return null
|
|
343
340
|
}
|
|
344
341
|
vNode.flags &= ~FLAG_NOOP
|
|
345
342
|
}
|
|
@@ -362,14 +359,14 @@ function updateFunctionComponent(vNode: FunctionVNode) {
|
|
|
362
359
|
* and not clearing the entire set.
|
|
363
360
|
*/
|
|
364
361
|
if (subs) {
|
|
365
|
-
subs.forEach(
|
|
362
|
+
subs.forEach(call)
|
|
366
363
|
subs.clear()
|
|
367
364
|
}
|
|
368
365
|
|
|
369
366
|
if (__DEV__) {
|
|
370
367
|
newChild = latest(type)(props)
|
|
371
368
|
|
|
372
|
-
if (
|
|
369
|
+
if (isHmrUpdate() && vNode.hooks && vNode.hookSig) {
|
|
373
370
|
const len = vNode.hooks.length
|
|
374
371
|
if (hookIndex.current < len) {
|
|
375
372
|
// clean up any hooks that were removed
|
|
@@ -382,7 +379,6 @@ function updateFunctionComponent(vNode: FunctionVNode) {
|
|
|
382
379
|
}
|
|
383
380
|
}
|
|
384
381
|
|
|
385
|
-
delete vNode.hmrUpdated
|
|
386
382
|
if (++renderTryCount > CONSECUTIVE_DIRTY_LIMIT) {
|
|
387
383
|
throw new KiruError({
|
|
388
384
|
message:
|
|
@@ -395,14 +391,14 @@ function updateFunctionComponent(vNode: FunctionVNode) {
|
|
|
395
391
|
}
|
|
396
392
|
newChild = type(props)
|
|
397
393
|
} while (isRenderDirtied)
|
|
398
|
-
|
|
399
|
-
return
|
|
394
|
+
|
|
395
|
+
return (vNode.child = reconcileChildren(vNode, newChild))
|
|
400
396
|
} finally {
|
|
401
397
|
node.current = null
|
|
402
398
|
}
|
|
403
399
|
}
|
|
404
400
|
|
|
405
|
-
function updateHostComponent(vNode: DomVNode) {
|
|
401
|
+
function updateHostComponent(vNode: DomVNode): VNode | null {
|
|
406
402
|
const { props, type } = vNode
|
|
407
403
|
if (__DEV__) {
|
|
408
404
|
assertValidElementProps(vNode)
|
|
@@ -413,10 +409,8 @@ function updateHostComponent(vNode: DomVNode) {
|
|
|
413
409
|
} else {
|
|
414
410
|
vNode.dom = createDom(vNode)
|
|
415
411
|
}
|
|
416
|
-
if (__DEV__) {
|
|
417
|
-
|
|
418
|
-
vNode.dom.__kiruNode = vNode
|
|
419
|
-
}
|
|
412
|
+
if (__DEV__ && vNode.dom instanceof Element) {
|
|
413
|
+
vNode.dom.__kiruNode = vNode
|
|
420
414
|
}
|
|
421
415
|
}
|
|
422
416
|
// text should _never_ have children
|
|
@@ -426,9 +420,11 @@ function updateHostComponent(vNode: DomVNode) {
|
|
|
426
420
|
hydrationStack.push(vNode.dom!)
|
|
427
421
|
}
|
|
428
422
|
}
|
|
423
|
+
|
|
424
|
+
return vNode.child
|
|
429
425
|
}
|
|
430
426
|
|
|
431
|
-
function checkForTooManyConsecutiveDirtyRenders() {
|
|
427
|
+
function checkForTooManyConsecutiveDirtyRenders(): void {
|
|
432
428
|
if (consecutiveDirtyCount > CONSECUTIVE_DIRTY_LIMIT) {
|
|
433
429
|
throw new KiruError(
|
|
434
430
|
"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."
|
|
@@ -436,7 +432,7 @@ function checkForTooManyConsecutiveDirtyRenders() {
|
|
|
436
432
|
}
|
|
437
433
|
}
|
|
438
434
|
|
|
439
|
-
function flushEffects(effectArr: Function[]) {
|
|
435
|
+
function flushEffects(effectArr: Function[]): void {
|
|
440
436
|
for (let i = 0; i < effectArr.length; i++) {
|
|
441
437
|
effectArr[i]()
|
|
442
438
|
}
|
package/src/signals/base.ts
CHANGED
|
@@ -17,7 +17,6 @@ export class Signal<T> {
|
|
|
17
17
|
[$SIGNAL] = true;
|
|
18
18
|
[$HMR_ACCEPT]?: HMRAccept<Signal<any>>
|
|
19
19
|
displayName?: string
|
|
20
|
-
private onBeforeRead?: () => void
|
|
21
20
|
protected $subs?: Set<SignalSubscriber<any>>
|
|
22
21
|
protected $id: string
|
|
23
22
|
protected $value: T
|
|
@@ -60,7 +59,6 @@ export class Signal<T> {
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
get value() {
|
|
63
|
-
this.onBeforeRead?.()
|
|
64
62
|
if (__DEV__) {
|
|
65
63
|
const tgt = latest(this)
|
|
66
64
|
Signal.entangle(tgt)
|
|
@@ -86,7 +84,6 @@ export class Signal<T> {
|
|
|
86
84
|
}
|
|
87
85
|
|
|
88
86
|
peek() {
|
|
89
|
-
this.onBeforeRead?.()
|
|
90
87
|
if (__DEV__) {
|
|
91
88
|
return latest(this).$value
|
|
92
89
|
}
|
|
@@ -105,7 +102,6 @@ export class Signal<T> {
|
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
toString() {
|
|
108
|
-
this.onBeforeRead?.()
|
|
109
105
|
if (__DEV__) {
|
|
110
106
|
const tgt = latest(this)
|
|
111
107
|
Signal.entangle(tgt)
|
|
@@ -192,10 +188,6 @@ export class Signal<T> {
|
|
|
192
188
|
;(vNode.subs ??= new Set()).add(unsub)
|
|
193
189
|
}
|
|
194
190
|
|
|
195
|
-
static configure(signal: Signal<any>, onBeforeRead?: () => void) {
|
|
196
|
-
signal.onBeforeRead = onBeforeRead
|
|
197
|
-
}
|
|
198
|
-
|
|
199
191
|
static dispose(signal: Signal<any>) {
|
|
200
192
|
signal.$isDisposed = true
|
|
201
193
|
if (__DEV__) {
|
package/src/signals/computed.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { __DEV__ } from "../env.js"
|
|
2
2
|
import { $HMR_ACCEPT } from "../constants.js"
|
|
3
3
|
import { depsRequireChange, useHook } from "../hooks/utils.js"
|
|
4
|
-
import { latest } from "../utils/index.js"
|
|
4
|
+
import { call, latest } from "../utils/index.js"
|
|
5
5
|
import { effectQueue, signalSubsMap } from "./globals.js"
|
|
6
6
|
import { executeWithTracking } from "./effect.js"
|
|
7
7
|
import { Signal } from "./base.js"
|
|
@@ -32,28 +32,23 @@ export class ComputedSignal<T> extends Signal<T> {
|
|
|
32
32
|
destroy: () => {},
|
|
33
33
|
} satisfies HMRAccept<ComputedSignal<T>>
|
|
34
34
|
}
|
|
35
|
-
Signal.configure(this, () => {
|
|
36
|
-
if (!this.$isDirty) return
|
|
37
|
-
if (__DEV__) {
|
|
38
|
-
/**
|
|
39
|
-
* This is a safeguard for dev-mode only, where a 'read' on an
|
|
40
|
-
* already-disposed signal during HMR update => `dom.setSignalProp`
|
|
41
|
-
* would throw due to invalid subs-map access.
|
|
42
|
-
*
|
|
43
|
-
* Perhaps in future we could handle this better by carrying over
|
|
44
|
-
* the previous signal's ID and not disposing it / deleting the
|
|
45
|
-
* map entry.
|
|
46
|
-
*/
|
|
47
|
-
if (this.$isDisposed) return
|
|
48
|
-
}
|
|
49
|
-
ComputedSignal.run(this)
|
|
50
|
-
})
|
|
51
35
|
}
|
|
52
36
|
|
|
53
37
|
get value() {
|
|
38
|
+
this.ensureNotDirty()
|
|
54
39
|
return super.value
|
|
55
40
|
}
|
|
56
41
|
|
|
42
|
+
toString() {
|
|
43
|
+
this.ensureNotDirty()
|
|
44
|
+
return super.toString()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
peek() {
|
|
48
|
+
this.ensureNotDirty()
|
|
49
|
+
return super.peek()
|
|
50
|
+
}
|
|
51
|
+
|
|
57
52
|
// @ts-expect-error
|
|
58
53
|
set value(next: T) {}
|
|
59
54
|
|
|
@@ -83,7 +78,7 @@ export class ComputedSignal<T> extends Signal<T> {
|
|
|
83
78
|
const { $id, $unsubs } = latest(computed)
|
|
84
79
|
|
|
85
80
|
effectQueue.delete($id)
|
|
86
|
-
$unsubs.forEach(
|
|
81
|
+
$unsubs.forEach(call)
|
|
87
82
|
$unsubs.clear()
|
|
88
83
|
computed.$isDirty = true
|
|
89
84
|
}
|
|
@@ -111,6 +106,23 @@ export class ComputedSignal<T> extends Signal<T> {
|
|
|
111
106
|
$computed.sneak(value)
|
|
112
107
|
$computed.$isDirty = false
|
|
113
108
|
}
|
|
109
|
+
|
|
110
|
+
private ensureNotDirty() {
|
|
111
|
+
if (!this.$isDirty) return
|
|
112
|
+
if (__DEV__) {
|
|
113
|
+
/**
|
|
114
|
+
* This is a safeguard for dev-mode only, where a 'read' on an
|
|
115
|
+
* already-disposed signal during HMR update => `dom.setSignalProp`
|
|
116
|
+
* would throw due to invalid subs-map access.
|
|
117
|
+
*
|
|
118
|
+
* Perhaps in future we could handle this better by carrying over
|
|
119
|
+
* the previous signal's ID and not disposing it / deleting the
|
|
120
|
+
* map entry.
|
|
121
|
+
*/
|
|
122
|
+
if (this.$isDisposed) return
|
|
123
|
+
}
|
|
124
|
+
ComputedSignal.run(this)
|
|
125
|
+
}
|
|
114
126
|
}
|
|
115
127
|
|
|
116
128
|
export function computed<T>(
|
package/src/signals/for.ts
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
import type { Signal } from "./base.js"
|
|
2
|
+
import { unwrap } from "./utils.js"
|
|
2
3
|
|
|
3
|
-
type InferArraySignalItemType<T extends Signal<any[]
|
|
4
|
-
infer V
|
|
5
|
-
>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
:
|
|
9
|
-
|
|
4
|
+
type InferArraySignalItemType<T extends Signal<any[]> | readonly unknown[]> =
|
|
5
|
+
T extends Signal<infer V>
|
|
6
|
+
? V extends Array<infer W>
|
|
7
|
+
? W
|
|
8
|
+
: never
|
|
9
|
+
: T extends unknown[]
|
|
10
|
+
? T[number]
|
|
11
|
+
: never
|
|
10
12
|
|
|
11
|
-
type ForProps<
|
|
13
|
+
type ForProps<
|
|
14
|
+
T extends Signal<any[]> | readonly unknown[],
|
|
15
|
+
U = InferArraySignalItemType<T>,
|
|
16
|
+
> = {
|
|
12
17
|
each: T
|
|
13
18
|
fallback?: JSX.Element
|
|
14
19
|
children: (value: U, index: number, array: U[]) => JSX.Element
|
|
15
20
|
}
|
|
16
21
|
|
|
17
|
-
export function For<T extends Signal<any[]
|
|
22
|
+
export function For<T extends Signal<any[]> | unknown[]>({
|
|
18
23
|
each,
|
|
19
24
|
fallback,
|
|
20
25
|
children,
|
|
21
26
|
}: ForProps<T>) {
|
|
22
|
-
const items = each
|
|
27
|
+
const items = unwrap(each, true)
|
|
23
28
|
if (items.length === 0) return fallback
|
|
24
29
|
return items.map(children)
|
|
25
30
|
}
|
package/src/signals/utils.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { call } from "../utils/index.js"
|
|
1
2
|
import { Signal } from "./base.js"
|
|
2
3
|
import { effectQueue } from "./globals.js"
|
|
3
4
|
|
|
@@ -7,6 +8,6 @@ export function unwrap<T>(value: T | Signal<T>, reactive = false): T {
|
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export const tick = () => {
|
|
10
|
-
effectQueue.forEach(
|
|
11
|
+
effectQueue.forEach(call)
|
|
11
12
|
effectQueue.clear()
|
|
12
13
|
}
|
package/src/signals/watch.ts
CHANGED
|
@@ -2,7 +2,12 @@ import { __DEV__ } from "../env.js"
|
|
|
2
2
|
import { useHook } from "../hooks/utils.js"
|
|
3
3
|
import { effectQueue } from "./globals.js"
|
|
4
4
|
import { executeWithTracking } from "./effect.js"
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
latest,
|
|
7
|
+
sideEffectsEnabled,
|
|
8
|
+
generateRandomID,
|
|
9
|
+
call,
|
|
10
|
+
} from "../utils/index.js"
|
|
6
11
|
import type { Signal } from "./base.js"
|
|
7
12
|
import type { SignalValues } from "./types.js"
|
|
8
13
|
|
|
@@ -26,12 +31,12 @@ export class WatchEffect<const Deps extends readonly Signal<unknown>[] = []> {
|
|
|
26
31
|
this.unsubs = new Map()
|
|
27
32
|
this.isRunning = false
|
|
28
33
|
this.cleanup = null
|
|
29
|
-
if (__DEV__) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
if (__DEV__ && "window" in globalThis) {
|
|
35
|
+
const { isWaitingForNextWatchCall, pushWatch } =
|
|
36
|
+
window.__kiru.HMRContext!.signals
|
|
37
|
+
|
|
38
|
+
if (isWaitingForNextWatchCall()) {
|
|
39
|
+
pushWatch(this as WatchEffect)
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
this.start()
|
|
@@ -44,22 +49,24 @@ export class WatchEffect<const Deps extends readonly Signal<unknown>[] = []> {
|
|
|
44
49
|
|
|
45
50
|
this.isRunning = true
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
// postpone execution during HMR
|
|
53
|
+
if (
|
|
54
|
+
__DEV__ &&
|
|
55
|
+
"window" in globalThis &&
|
|
56
|
+
window.__kiru.HMRContext?.isReplacement()
|
|
57
|
+
) {
|
|
58
|
+
return queueMicrotask(() => {
|
|
59
|
+
if (this.isRunning) {
|
|
60
|
+
WatchEffect.run(this as WatchEffect)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
56
63
|
}
|
|
57
64
|
WatchEffect.run(this as WatchEffect)
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
stop() {
|
|
61
68
|
effectQueue.delete(this.id)
|
|
62
|
-
this.unsubs.forEach(
|
|
69
|
+
this.unsubs.forEach(call)
|
|
63
70
|
this.unsubs.clear()
|
|
64
71
|
this.cleanup?.()
|
|
65
72
|
this.cleanup = null
|
|
@@ -117,11 +124,9 @@ export function useWatch<const Deps extends readonly Signal<unknown>[]>(
|
|
|
117
124
|
"useWatch",
|
|
118
125
|
{ watcher: null as any as WatchEffect<Deps> },
|
|
119
126
|
({ hook, isInit, isHMR }) => {
|
|
120
|
-
if (__DEV__) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
isInit = true
|
|
124
|
-
}
|
|
127
|
+
if (__DEV__ && isHMR) {
|
|
128
|
+
hook.cleanup?.()
|
|
129
|
+
isInit = true
|
|
125
130
|
}
|
|
126
131
|
if (isInit) {
|
|
127
132
|
const watcher = (hook.watcher = watch(depsOrGetter as Deps, getter!))
|
package/src/ssr/client.ts
CHANGED
|
@@ -10,7 +10,7 @@ interface HydrationAppContextOptions extends AppContextOptions {
|
|
|
10
10
|
* - "dynamic": SSR with lazy promise hydration
|
|
11
11
|
* @default "dynamic"
|
|
12
12
|
*/
|
|
13
|
-
hydrationMode?:
|
|
13
|
+
hydrationMode?: "static" | "dynamic"
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function hydrate(
|
|
@@ -22,7 +22,6 @@ export function hydrate(
|
|
|
22
22
|
|
|
23
23
|
const prevRenderMode = renderMode.current
|
|
24
24
|
renderMode.current = "hydrate"
|
|
25
|
-
hydrationStack.captureEvents(container)
|
|
26
25
|
|
|
27
26
|
const prevHydrationMode = hydrationMode.current
|
|
28
27
|
hydrationMode.current = options?.hydrationMode ?? "dynamic"
|
|
@@ -30,8 +29,6 @@ export function hydrate(
|
|
|
30
29
|
const app = mount(children, container, options)
|
|
31
30
|
|
|
32
31
|
renderMode.current = prevRenderMode
|
|
33
|
-
hydrationStack.releaseEvents(container)
|
|
34
|
-
|
|
35
32
|
hydrationMode.current = prevHydrationMode
|
|
36
33
|
|
|
37
34
|
return app
|