kiru 0.52.4 → 0.54.0-preview.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.map +1 -1
- package/dist/appContext.js +14 -11
- package/dist/appContext.js.map +1 -1
- package/dist/components/derive.d.ts +20 -0
- package/dist/components/derive.d.ts.map +1 -0
- package/dist/components/derive.js +57 -0
- package/dist/components/derive.js.map +1 -0
- package/dist/components/errorBoundary.d.ts +1 -1
- package/dist/components/errorBoundary.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/memo.d.ts +6 -3
- package/dist/components/memo.d.ts.map +1 -1
- package/dist/components/memo.js.map +1 -1
- package/dist/constants.d.ts +3 -3
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -3
- package/dist/constants.js.map +1 -1
- package/dist/dom.d.ts.map +1 -1
- package/dist/dom.js +69 -66
- package/dist/dom.js.map +1 -1
- package/dist/element.d.ts +2 -2
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +20 -32
- package/dist/element.js.map +1 -1
- package/dist/globalContext.d.ts +6 -1
- package/dist/globalContext.d.ts.map +1 -1
- package/dist/globalContext.js +14 -2
- package/dist/globalContext.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/hooks/usePromise.d.ts +4 -9
- package/dist/hooks/usePromise.d.ts.map +1 -1
- package/dist/hooks/usePromise.js +25 -28
- package/dist/hooks/usePromise.js.map +1 -1
- package/dist/hooks/useRef.d.ts +2 -2
- package/dist/hooks/useRef.d.ts.map +1 -1
- package/dist/jsx.d.ts +1 -1
- package/dist/jsx.d.ts.map +1 -1
- package/dist/reconciler.d.ts +1 -1
- package/dist/reconciler.d.ts.map +1 -1
- package/dist/reconciler.js +57 -117
- package/dist/reconciler.js.map +1 -1
- package/dist/recursiveRender.d.ts +4 -3
- package/dist/recursiveRender.d.ts.map +1 -1
- package/dist/recursiveRender.js +20 -19
- package/dist/recursiveRender.js.map +1 -1
- package/dist/renderToString.js +2 -2
- package/dist/renderToString.js.map +1 -1
- package/dist/router/client/index.d.ts +4 -2
- package/dist/router/client/index.d.ts.map +1 -1
- package/dist/router/client/index.js +49 -11
- package/dist/router/client/index.js.map +1 -1
- package/dist/router/context.d.ts +2 -0
- package/dist/router/context.d.ts.map +1 -1
- package/dist/router/context.js +5 -1
- package/dist/router/context.js.map +1 -1
- package/dist/router/fileRouterController.d.ts +3 -1
- package/dist/router/fileRouterController.d.ts.map +1 -1
- package/dist/router/fileRouterController.js +80 -9
- package/dist/router/fileRouterController.js.map +1 -1
- package/dist/router/globals.d.ts +3 -0
- package/dist/router/globals.d.ts.map +1 -1
- package/dist/router/globals.js +3 -0
- package/dist/router/globals.js.map +1 -1
- package/dist/router/guard.d.ts +17 -0
- package/dist/router/guard.d.ts.map +1 -0
- package/dist/router/guard.js +45 -0
- package/dist/router/guard.js.map +1 -0
- package/dist/router/head.d.ts.map +1 -1
- package/dist/router/head.js +5 -7
- package/dist/router/head.js.map +1 -1
- package/dist/router/index.d.ts +3 -2
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +2 -1
- package/dist/router/index.js.map +1 -1
- package/dist/router/{server → ssg}/index.d.ts +4 -3
- package/dist/router/ssg/index.d.ts.map +1 -0
- package/dist/router/{server → ssg}/index.js +7 -4
- package/dist/router/ssg/index.js.map +1 -0
- package/dist/router/ssr/index.d.ts +20 -0
- package/dist/router/ssr/index.d.ts.map +1 -0
- package/dist/router/ssr/index.js +160 -0
- package/dist/router/ssr/index.js.map +1 -0
- package/dist/router/types.d.ts +37 -4
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/types.internal.d.ts +4 -0
- package/dist/router/types.internal.d.ts.map +1 -1
- package/dist/router/utils/index.d.ts +9 -4
- package/dist/router/utils/index.d.ts.map +1 -1
- package/dist/router/utils/index.js +38 -6
- package/dist/router/utils/index.js.map +1 -1
- package/dist/signals/base.d.ts +1 -3
- package/dist/signals/base.d.ts.map +1 -1
- package/dist/signals/base.js +3 -3
- package/dist/signals/base.js.map +1 -1
- package/dist/signals/computed.d.ts +4 -2
- package/dist/signals/computed.d.ts.map +1 -1
- package/dist/signals/computed.js +29 -6
- package/dist/signals/computed.js.map +1 -1
- package/dist/signals/for.d.ts +10 -0
- package/dist/signals/for.d.ts.map +1 -0
- package/dist/signals/for.js +7 -0
- package/dist/signals/for.js.map +1 -0
- package/dist/signals/index.d.ts +1 -1
- package/dist/signals/index.js +1 -1
- package/dist/signals/types.d.ts +1 -1
- package/dist/signals/types.d.ts.map +1 -1
- package/dist/ssr/client.d.ts +1 -1
- package/dist/ssr/client.d.ts.map +1 -1
- package/dist/ssr/server.d.ts +1 -2
- package/dist/ssr/server.d.ts.map +1 -1
- package/dist/ssr/server.js +26 -29
- package/dist/ssr/server.js.map +1 -1
- package/dist/types.d.ts +13 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.dom.d.ts +33 -32
- package/dist/types.dom.d.ts.map +1 -1
- package/dist/types.utils.d.ts +5 -1
- package/dist/types.utils.d.ts.map +1 -1
- package/dist/utils/format.d.ts +4 -3
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +8 -4
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +0 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/promise.d.ts +16 -0
- package/dist/utils/promise.d.ts.map +1 -0
- package/dist/utils/promise.js +14 -0
- package/dist/utils/promise.js.map +1 -0
- package/dist/utils/runtime.d.ts +11 -2
- package/dist/utils/runtime.d.ts.map +1 -1
- package/dist/utils/runtime.js +26 -2
- package/dist/utils/runtime.js.map +1 -1
- package/dist/utils/vdom.d.ts +4 -4
- package/dist/utils/vdom.d.ts.map +1 -1
- package/dist/utils/vdom.js +18 -17
- package/dist/utils/vdom.js.map +1 -1
- package/dist/vNode.d.ts +4 -0
- package/dist/vNode.d.ts.map +1 -0
- package/dist/vNode.js +22 -0
- package/dist/vNode.js.map +1 -0
- package/package.json +8 -4
- package/src/appContext.ts +15 -13
- package/src/components/derive.ts +121 -0
- package/src/components/index.ts +1 -1
- package/src/components/memo.ts +3 -2
- package/src/constants.ts +4 -4
- package/src/dom.ts +71 -66
- package/src/element.ts +22 -35
- package/src/globalContext.ts +24 -3
- package/src/globals.ts +1 -1
- package/src/hooks/usePromise.ts +32 -41
- package/src/hooks/useRef.ts +2 -2
- package/src/reconciler.ts +87 -125
- package/src/recursiveRender.ts +25 -23
- package/src/renderToString.ts +3 -3
- package/src/router/client/index.ts +100 -14
- package/src/router/context.ts +7 -1
- package/src/router/fileRouterController.ts +137 -8
- package/src/router/globals.ts +4 -0
- package/src/router/guard.ts +72 -0
- package/src/router/head.ts +5 -7
- package/src/router/index.ts +12 -1
- package/src/router/{server → ssg}/index.ts +16 -9
- package/src/router/ssr/index.ts +247 -0
- package/src/router/types.internal.ts +5 -0
- package/src/router/types.ts +48 -4
- package/src/router/utils/index.ts +74 -8
- package/src/signals/base.ts +3 -3
- package/src/signals/computed.ts +43 -6
- package/src/signals/for.ts +25 -0
- package/src/signals/index.ts +1 -1
- package/src/signals/types.ts +5 -1
- package/src/ssr/client.ts +1 -1
- package/src/ssr/server.ts +31 -33
- package/src/types.dom.ts +40 -40
- package/src/types.ts +14 -11
- package/src/types.utils.ts +11 -1
- package/src/utils/format.ts +12 -4
- package/src/utils/index.ts +0 -2
- package/src/utils/promise.ts +26 -0
- package/src/utils/runtime.ts +30 -2
- package/src/utils/vdom.ts +20 -23
- package/src/vNode.ts +30 -0
- package/dist/components/suspense.d.ts +0 -24
- package/dist/components/suspense.d.ts.map +0 -1
- package/dist/components/suspense.js +0 -36
- package/dist/components/suspense.js.map +0 -1
- package/dist/router/server/index.d.ts.map +0 -1
- package/dist/router/server/index.js.map +0 -1
- package/dist/signals/jsx.d.ts +0 -17
- package/dist/signals/jsx.d.ts.map +0 -1
- package/dist/signals/jsx.js +0 -11
- package/dist/signals/jsx.js.map +0 -1
- package/src/components/suspense.ts +0 -72
- package/src/signals/jsx.ts +0 -46
package/src/hooks/usePromise.ts
CHANGED
|
@@ -1,43 +1,36 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { STREAMED_DATA_EVENT } from "../constants.js"
|
|
2
2
|
import { __DEV__ } from "../env.js"
|
|
3
3
|
import { hydrationMode, renderMode } from "../globals.js"
|
|
4
|
-
import { requestUpdate } from "../scheduler.js"
|
|
5
4
|
import { Signal, useSignal } from "../signals/base.js"
|
|
6
5
|
import { cleanupHook, depsRequireChange, useHook } from "./utils.js"
|
|
7
6
|
import { useId } from "./useId.js"
|
|
8
7
|
|
|
9
8
|
export { usePromise }
|
|
10
|
-
export type { UsePromiseCallbackContext, UsePromiseState }
|
|
11
9
|
|
|
12
10
|
const nodeToPromiseIndex = new WeakMap<Kiru.VNode, number>()
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
signal: AbortSignal
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface UsePromiseState<T> {
|
|
19
|
-
data: Kiru.StatefulPromise<T>
|
|
12
|
+
type UsePromiseResult<T> = Kiru.StatefulPromise<T> & {
|
|
20
13
|
refresh: () => void
|
|
21
|
-
|
|
14
|
+
isPending: Signal<boolean>
|
|
22
15
|
}
|
|
23
16
|
|
|
24
17
|
function usePromise<T>(
|
|
25
|
-
callback: (
|
|
18
|
+
callback: (signal: AbortSignal) => Promise<T>,
|
|
26
19
|
deps: unknown[]
|
|
27
|
-
):
|
|
20
|
+
): UsePromiseResult<T> {
|
|
28
21
|
const id = useId()
|
|
29
|
-
const
|
|
22
|
+
const isPending = useSignal(true)
|
|
30
23
|
|
|
31
24
|
return useHook(
|
|
32
25
|
"usePromise",
|
|
33
26
|
{} as {
|
|
34
|
-
promise:
|
|
27
|
+
promise: UsePromiseResult<T>
|
|
35
28
|
abortController?: AbortController
|
|
36
29
|
deps?: unknown[]
|
|
37
30
|
},
|
|
38
|
-
({ hook, isInit, vNode }) => {
|
|
31
|
+
({ hook, isInit, vNode, update }) => {
|
|
39
32
|
if (isInit || depsRequireChange(deps, hook.deps)) {
|
|
40
|
-
|
|
33
|
+
isPending.value = true
|
|
41
34
|
hook.deps = deps
|
|
42
35
|
cleanupHook(hook)
|
|
43
36
|
|
|
@@ -59,77 +52,75 @@ function usePromise<T>(
|
|
|
59
52
|
) {
|
|
60
53
|
// if we're hydrating and the hydration mode is not static,
|
|
61
54
|
// we need to resolve the promise from cache/event
|
|
62
|
-
promise =
|
|
55
|
+
promise = resolveDeferredPromise<T>(promiseId, controller.signal)
|
|
63
56
|
} else {
|
|
64
57
|
// dom / stream / (hydrate + static)
|
|
65
|
-
promise = callback(
|
|
58
|
+
promise = callback(controller.signal)
|
|
66
59
|
}
|
|
67
60
|
|
|
68
61
|
const state: Kiru.PromiseState<T> = { id: promiseId, state: "pending" }
|
|
69
|
-
const statefulPromise = (hook.promise =
|
|
62
|
+
const statefulPromise: Kiru.StatefulPromise<T> = (hook.promise =
|
|
63
|
+
Object.assign(promise, state, {
|
|
64
|
+
isPending,
|
|
65
|
+
refresh: () => {
|
|
66
|
+
hook.deps = undefined
|
|
67
|
+
update()
|
|
68
|
+
},
|
|
69
|
+
}))
|
|
70
70
|
|
|
71
71
|
statefulPromise
|
|
72
72
|
.then((value) => {
|
|
73
73
|
statefulPromise.state = "fulfilled"
|
|
74
74
|
statefulPromise.value = value
|
|
75
|
+
isPending.value = false
|
|
75
76
|
})
|
|
76
77
|
.catch((error) => {
|
|
77
78
|
statefulPromise.state = "rejected"
|
|
78
79
|
statefulPromise.error =
|
|
79
80
|
error instanceof Error ? error : new Error(error)
|
|
80
81
|
})
|
|
81
|
-
.finally(() => {
|
|
82
|
-
pending.value = false
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
data: hook.promise,
|
|
87
|
-
refresh: () => {
|
|
88
|
-
hook.deps = undefined
|
|
89
|
-
requestUpdate(vNode)
|
|
90
|
-
},
|
|
91
|
-
pending,
|
|
92
82
|
}
|
|
83
|
+
return hook.promise
|
|
93
84
|
}
|
|
94
85
|
)
|
|
95
86
|
}
|
|
96
87
|
|
|
97
|
-
interface
|
|
88
|
+
interface DeferredPromiseEventDetail<T> {
|
|
98
89
|
id: string
|
|
99
90
|
data?: T
|
|
100
91
|
error?: string
|
|
101
92
|
}
|
|
102
93
|
|
|
103
|
-
function
|
|
94
|
+
function resolveDeferredPromise<T>(
|
|
104
95
|
id: string,
|
|
105
96
|
signal: AbortSignal
|
|
106
97
|
): Promise<T> {
|
|
107
98
|
return new Promise<T>((resolve, reject) => {
|
|
108
|
-
const
|
|
109
|
-
(window[
|
|
99
|
+
const deferralCache: Map<string, { data?: T; error?: string }> = // @ts-ignore
|
|
100
|
+
(window[STREAMED_DATA_EVENT] ??= new Map())
|
|
110
101
|
|
|
111
|
-
const existing =
|
|
102
|
+
const existing = deferralCache.get(id)
|
|
112
103
|
if (existing) {
|
|
113
104
|
const { data, error } = existing
|
|
114
|
-
|
|
105
|
+
deferralCache.delete(id)
|
|
115
106
|
if (error) return reject(error)
|
|
116
107
|
return resolve(data!)
|
|
117
108
|
}
|
|
118
109
|
|
|
119
110
|
const onDataEvent = (event: Event) => {
|
|
120
|
-
const { detail } = event as CustomEvent<
|
|
111
|
+
const { detail } = event as CustomEvent<DeferredPromiseEventDetail<T>>
|
|
121
112
|
if (detail.id === id) {
|
|
122
|
-
|
|
123
|
-
window.removeEventListener(
|
|
113
|
+
deferralCache.delete(id)
|
|
114
|
+
window.removeEventListener(STREAMED_DATA_EVENT, onDataEvent)
|
|
124
115
|
const { data, error } = detail
|
|
125
116
|
if (error) return reject(error)
|
|
126
117
|
resolve(data!)
|
|
127
118
|
}
|
|
128
119
|
}
|
|
129
120
|
|
|
130
|
-
window.addEventListener(
|
|
121
|
+
window.addEventListener(STREAMED_DATA_EVENT, onDataEvent)
|
|
131
122
|
signal.addEventListener("abort", () => {
|
|
132
|
-
window.removeEventListener(
|
|
123
|
+
window.removeEventListener(STREAMED_DATA_EVENT, onDataEvent)
|
|
133
124
|
reject()
|
|
134
125
|
})
|
|
135
126
|
})
|
package/src/hooks/useRef.ts
CHANGED
|
@@ -8,9 +8,9 @@ import { useHook } from "./utils.js"
|
|
|
8
8
|
*
|
|
9
9
|
* @see https://kirujs.dev/docs/hooks/useRef
|
|
10
10
|
*/
|
|
11
|
-
export function useRef<T>(initialValue: T): Kiru.
|
|
11
|
+
export function useRef<T>(initialValue: T): Kiru.RefObject<T>
|
|
12
12
|
export function useRef<T>(initialValue: T | null): Kiru.RefObject<T>
|
|
13
|
-
export function useRef<T = undefined>(): Kiru.
|
|
13
|
+
export function useRef<T = undefined>(): Kiru.RefObject<T | undefined>
|
|
14
14
|
export function useRef<T>(initialValue?: T | null) {
|
|
15
15
|
if (!sideEffectsEnabled()) return { current: initialValue }
|
|
16
16
|
return useHook(
|
package/src/reconciler.ts
CHANGED
|
@@ -1,19 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
$FRAGMENT,
|
|
3
|
+
$MEMO,
|
|
4
|
+
FLAG_MEMO,
|
|
5
|
+
FLAG_PLACEMENT,
|
|
6
|
+
FLAG_UPDATE,
|
|
7
|
+
} from "./constants.js"
|
|
2
8
|
import {
|
|
3
9
|
getVNodeAppContext,
|
|
10
|
+
isElement,
|
|
4
11
|
isValidTextChild,
|
|
5
|
-
isVNode,
|
|
6
12
|
latest,
|
|
7
13
|
} from "./utils/index.js"
|
|
8
14
|
import { Signal } from "./signals/base.js"
|
|
9
15
|
import { __DEV__ } from "./env.js"
|
|
10
|
-
import { createElement, Fragment } from "./element.js"
|
|
11
16
|
import type { AppContext } from "./appContext.js"
|
|
17
|
+
import { isMemoFn } from "./components/memo.js"
|
|
18
|
+
import { createVNode as createBaseVNode } from "./vNode.js"
|
|
12
19
|
|
|
13
20
|
type VNode = Kiru.VNode
|
|
21
|
+
type KElement = Kiru.Element
|
|
14
22
|
let appCtx: AppContext
|
|
15
23
|
|
|
16
|
-
export function reconcileChildren(
|
|
24
|
+
export function reconcileChildren(
|
|
25
|
+
parent: VNode,
|
|
26
|
+
children: unknown
|
|
27
|
+
): VNode | null {
|
|
17
28
|
if (__DEV__) {
|
|
18
29
|
appCtx = getVNodeAppContext(parent)!
|
|
19
30
|
}
|
|
@@ -30,7 +41,7 @@ export function reconcileChildren(parent: VNode, children: unknown) {
|
|
|
30
41
|
return reconcileSingleChild(parent, children)
|
|
31
42
|
}
|
|
32
43
|
|
|
33
|
-
function reconcileSingleChild(parent: VNode, child: unknown) {
|
|
44
|
+
function reconcileSingleChild(parent: VNode, child: unknown): VNode | null {
|
|
34
45
|
const oldChild = parent.child
|
|
35
46
|
if (oldChild === null) {
|
|
36
47
|
return createChild(parent, child)
|
|
@@ -52,9 +63,9 @@ function reconcileSingleChild(parent: VNode, child: unknown) {
|
|
|
52
63
|
if (newNode !== null) {
|
|
53
64
|
const prev = newNode.prev
|
|
54
65
|
if (prev !== null) {
|
|
55
|
-
const key = prev.
|
|
66
|
+
const key = prev.key
|
|
56
67
|
// node persisted, remove it from the list so it doesn't get deleted
|
|
57
|
-
existingChildren.delete(key ===
|
|
68
|
+
existingChildren.delete(key === null ? prev.index : key)
|
|
58
69
|
}
|
|
59
70
|
placeChild(newNode, 0, 0)
|
|
60
71
|
}
|
|
@@ -134,9 +145,9 @@ function reconcileChildrenArray(parent: VNode, children: unknown[]) {
|
|
|
134
145
|
if (newNode !== null) {
|
|
135
146
|
const prev = newNode.prev
|
|
136
147
|
if (prev !== null) {
|
|
137
|
-
const key = prev.
|
|
148
|
+
const key = prev.key
|
|
138
149
|
// node persisted, remove it from the list so it doesn't get deleted
|
|
139
|
-
existingChildren.delete(key ===
|
|
150
|
+
existingChildren.delete(key === null ? prev.index : key)
|
|
140
151
|
}
|
|
141
152
|
lastPlacedIndex = placeChild(newNode, lastPlacedIndex, newIdx)
|
|
142
153
|
if (prevNewChild === null) {
|
|
@@ -152,11 +163,15 @@ function reconcileChildrenArray(parent: VNode, children: unknown[]) {
|
|
|
152
163
|
return resultingChild
|
|
153
164
|
}
|
|
154
165
|
|
|
155
|
-
function updateSlot(
|
|
166
|
+
function updateSlot(
|
|
167
|
+
parent: VNode,
|
|
168
|
+
oldChild: VNode | null,
|
|
169
|
+
child: unknown
|
|
170
|
+
): VNode | null {
|
|
156
171
|
// Update the node if the keys match, otherwise return null.
|
|
157
|
-
const key = oldChild
|
|
172
|
+
const key = oldChild === null ? null : oldChild.key
|
|
158
173
|
if (isValidTextChild(child)) {
|
|
159
|
-
if (key !==
|
|
174
|
+
if (key !== null) return null
|
|
160
175
|
if (
|
|
161
176
|
oldChild?.type === "#text" &&
|
|
162
177
|
Signal.isSignal(oldChild.props.nodeValue)
|
|
@@ -169,12 +184,12 @@ function updateSlot(parent: VNode, oldChild: VNode | null, child: unknown) {
|
|
|
169
184
|
if (!!oldChild && oldChild.props.nodeValue !== child) return null
|
|
170
185
|
return updateTextNode(parent, oldChild, child)
|
|
171
186
|
}
|
|
172
|
-
if (
|
|
173
|
-
if (child.
|
|
187
|
+
if (isElement(child)) {
|
|
188
|
+
if (child.key !== key) return null
|
|
174
189
|
return updateNode(parent, oldChild, child)
|
|
175
190
|
}
|
|
176
191
|
if (Array.isArray(child)) {
|
|
177
|
-
if (key !==
|
|
192
|
+
if (key !== null) return null
|
|
178
193
|
if (__DEV__) {
|
|
179
194
|
markListChild(child)
|
|
180
195
|
}
|
|
@@ -187,14 +202,9 @@ function updateTextNode(
|
|
|
187
202
|
parent: VNode,
|
|
188
203
|
oldChild: VNode | null,
|
|
189
204
|
content: string | Signal<JSX.PrimitiveChild>
|
|
190
|
-
) {
|
|
205
|
+
): VNode {
|
|
191
206
|
if (oldChild === null || oldChild.type !== "#text") {
|
|
192
|
-
|
|
193
|
-
dev_emitCreateNode()
|
|
194
|
-
}
|
|
195
|
-
const newChild = createElement("#text", { nodeValue: content })
|
|
196
|
-
setParent(newChild, parent)
|
|
197
|
-
return newChild
|
|
207
|
+
return createVNode(parent, "#text", { nodeValue: content })
|
|
198
208
|
}
|
|
199
209
|
|
|
200
210
|
if (__DEV__) {
|
|
@@ -209,44 +219,38 @@ function updateTextNode(
|
|
|
209
219
|
return oldChild
|
|
210
220
|
}
|
|
211
221
|
|
|
212
|
-
function updateNode(parent: VNode, oldChild: VNode | null, newChild:
|
|
213
|
-
let { type
|
|
222
|
+
function updateNode(parent: VNode, oldChild: VNode | null, newChild: KElement) {
|
|
223
|
+
let { type, props, key } = newChild
|
|
214
224
|
if (__DEV__) {
|
|
215
|
-
if (typeof
|
|
216
|
-
|
|
225
|
+
if (typeof type === "function") {
|
|
226
|
+
type = latest(type)
|
|
217
227
|
}
|
|
218
228
|
}
|
|
219
|
-
if (
|
|
229
|
+
if (type === $FRAGMENT) {
|
|
220
230
|
return updateFragment(
|
|
221
231
|
parent,
|
|
222
232
|
oldChild,
|
|
223
|
-
(
|
|
224
|
-
|
|
233
|
+
(props.children as VNode[]) || [],
|
|
234
|
+
props
|
|
225
235
|
)
|
|
226
236
|
}
|
|
227
|
-
if (oldChild?.type ===
|
|
237
|
+
if (oldChild?.type === type) {
|
|
228
238
|
if (__DEV__) {
|
|
229
239
|
dev_emitUpdateNode()
|
|
230
240
|
}
|
|
231
241
|
oldChild.index = 0
|
|
232
242
|
oldChild.sibling = null
|
|
233
|
-
if (typeof
|
|
234
|
-
if (propsChanged(oldChild.props,
|
|
243
|
+
if (typeof type === "string") {
|
|
244
|
+
if (propsChanged(oldChild.props, props)) {
|
|
235
245
|
oldChild.flags |= FLAG_UPDATE
|
|
236
246
|
}
|
|
237
247
|
} else {
|
|
238
248
|
oldChild.flags |= FLAG_UPDATE
|
|
239
249
|
}
|
|
240
|
-
oldChild.props =
|
|
241
|
-
oldChild.memoizedProps = newChild.memoizedProps
|
|
250
|
+
oldChild.props = props
|
|
242
251
|
return oldChild
|
|
243
252
|
}
|
|
244
|
-
|
|
245
|
-
dev_emitCreateNode()
|
|
246
|
-
}
|
|
247
|
-
const created = createElement(nodeType, newProps)
|
|
248
|
-
setParent(created, parent)
|
|
249
|
-
return created
|
|
253
|
+
return createVNode(parent, type, props, key)
|
|
250
254
|
}
|
|
251
255
|
|
|
252
256
|
function updateFragment(
|
|
@@ -256,12 +260,7 @@ function updateFragment(
|
|
|
256
260
|
newProps = {}
|
|
257
261
|
) {
|
|
258
262
|
if (oldChild === null || oldChild.type !== $FRAGMENT) {
|
|
259
|
-
|
|
260
|
-
dev_emitCreateNode()
|
|
261
|
-
}
|
|
262
|
-
const el = createElement($FRAGMENT, { children, ...newProps })
|
|
263
|
-
setParent(el, parent)
|
|
264
|
-
return el
|
|
263
|
+
return createVNode(parent, $FRAGMENT, { children, ...newProps })
|
|
265
264
|
}
|
|
266
265
|
if (__DEV__) {
|
|
267
266
|
dev_emitUpdateNode()
|
|
@@ -274,45 +273,22 @@ function updateFragment(
|
|
|
274
273
|
|
|
275
274
|
function createChild(parent: VNode, child: unknown): VNode | null {
|
|
276
275
|
if (isValidTextChild(child)) {
|
|
277
|
-
|
|
278
|
-
dev_emitCreateNode()
|
|
279
|
-
}
|
|
280
|
-
const el = createElement("#text", {
|
|
281
|
-
nodeValue: "" + child,
|
|
282
|
-
})
|
|
283
|
-
setParent(el, parent)
|
|
284
|
-
return el
|
|
276
|
+
return createVNode(parent, "#text", { nodeValue: "" + child })
|
|
285
277
|
}
|
|
286
278
|
|
|
287
279
|
if (Signal.isSignal(child)) {
|
|
288
|
-
|
|
289
|
-
dev_emitCreateNode()
|
|
290
|
-
}
|
|
291
|
-
const el = createElement("#text", {
|
|
292
|
-
nodeValue: child,
|
|
293
|
-
})
|
|
294
|
-
setParent(el, parent)
|
|
295
|
-
return el
|
|
280
|
+
return createVNode(parent, "#text", { nodeValue: child })
|
|
296
281
|
}
|
|
297
282
|
|
|
298
|
-
if (
|
|
299
|
-
|
|
300
|
-
dev_emitCreateNode()
|
|
301
|
-
}
|
|
302
|
-
const newNode = createElement(child.type, child.props)
|
|
303
|
-
setParent(newNode, parent)
|
|
304
|
-
newNode.flags |= FLAG_PLACEMENT
|
|
305
|
-
return newNode
|
|
283
|
+
if (isElement(child)) {
|
|
284
|
+
return createVNode(parent, child.type, child.props, child.key)
|
|
306
285
|
}
|
|
307
286
|
|
|
308
287
|
if (Array.isArray(child)) {
|
|
309
288
|
if (__DEV__) {
|
|
310
|
-
dev_emitCreateNode()
|
|
311
289
|
markListChild(child)
|
|
312
290
|
}
|
|
313
|
-
|
|
314
|
-
setParent(el, parent)
|
|
315
|
-
return el
|
|
291
|
+
return createVNode(parent, $FRAGMENT, { children: child })
|
|
316
292
|
}
|
|
317
293
|
|
|
318
294
|
return null
|
|
@@ -360,47 +336,30 @@ function updateFromMap(
|
|
|
360
336
|
}
|
|
361
337
|
}
|
|
362
338
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
setParent(newChild, parent)
|
|
370
|
-
newChild.flags |= FLAG_PLACEMENT
|
|
371
|
-
newChild.index = index
|
|
372
|
-
return newChild
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (isVNode(child)) {
|
|
376
|
-
const { type, props: newProps } = child
|
|
377
|
-
const key = newProps.key
|
|
378
|
-
const oldChild = existingChildren.get(key === undefined ? index : key)
|
|
339
|
+
return createVNode(parent, "#text", { nodeValue: child }, null, index)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (isElement(child)) {
|
|
343
|
+
const { type, props, key } = child
|
|
344
|
+
const oldChild = existingChildren.get(key === null ? index : key)
|
|
379
345
|
if (oldChild?.type === type) {
|
|
380
346
|
if (__DEV__) {
|
|
381
347
|
dev_emitUpdateNode()
|
|
382
348
|
}
|
|
383
349
|
if (typeof type === "string") {
|
|
384
|
-
if (propsChanged(oldChild.props,
|
|
350
|
+
if (propsChanged(oldChild.props, props)) {
|
|
385
351
|
oldChild.flags |= FLAG_UPDATE
|
|
386
352
|
}
|
|
387
353
|
} else {
|
|
388
354
|
oldChild.flags |= FLAG_UPDATE
|
|
389
355
|
}
|
|
390
|
-
oldChild.props =
|
|
356
|
+
oldChild.props = props
|
|
391
357
|
oldChild.sibling = null
|
|
392
358
|
oldChild.index = index
|
|
393
359
|
return oldChild
|
|
394
360
|
}
|
|
395
361
|
|
|
396
|
-
|
|
397
|
-
dev_emitCreateNode()
|
|
398
|
-
}
|
|
399
|
-
const newChild = createElement(child.type, child.props)
|
|
400
|
-
setParent(newChild, parent)
|
|
401
|
-
newChild.flags |= FLAG_PLACEMENT
|
|
402
|
-
newChild.index = index
|
|
403
|
-
return newChild
|
|
362
|
+
return createVNode(parent, type, props, key, index)
|
|
404
363
|
}
|
|
405
364
|
|
|
406
365
|
if (Array.isArray(child)) {
|
|
@@ -417,14 +376,7 @@ function updateFromMap(
|
|
|
417
376
|
return oldChild
|
|
418
377
|
}
|
|
419
378
|
|
|
420
|
-
|
|
421
|
-
dev_emitCreateNode()
|
|
422
|
-
}
|
|
423
|
-
const newChild = Fragment({ children: child })
|
|
424
|
-
setParent(newChild, parent)
|
|
425
|
-
newChild.flags |= FLAG_PLACEMENT
|
|
426
|
-
newChild.index = index
|
|
427
|
-
return newChild
|
|
379
|
+
return createVNode(parent, $FRAGMENT, { children: child }, null, index)
|
|
428
380
|
}
|
|
429
381
|
|
|
430
382
|
return null
|
|
@@ -441,21 +393,11 @@ function propsChanged(oldProps: VNode["props"], newProps: VNode["props"]) {
|
|
|
441
393
|
return false
|
|
442
394
|
}
|
|
443
395
|
|
|
444
|
-
function setParent(child: VNode, parent: VNode) {
|
|
445
|
-
child.parent = parent
|
|
446
|
-
child.depth = parent.depth + 1
|
|
447
|
-
}
|
|
448
|
-
|
|
449
396
|
function dev_emitUpdateNode() {
|
|
450
397
|
if (!("window" in globalThis)) return
|
|
451
398
|
window.__kiru.profilingContext?.emit("updateNode", appCtx)
|
|
452
399
|
}
|
|
453
400
|
|
|
454
|
-
function dev_emitCreateNode() {
|
|
455
|
-
if (!("window" in globalThis)) return
|
|
456
|
-
window.__kiru.profilingContext?.emit("createNode", appCtx)
|
|
457
|
-
}
|
|
458
|
-
|
|
459
401
|
const $LIST_CHILD = Symbol("kiru:marked-list-child")
|
|
460
402
|
function markListChild(children: unknown[]) {
|
|
461
403
|
Object.assign(children, { [$LIST_CHILD]: true })
|
|
@@ -464,8 +406,8 @@ function markListChild(children: unknown[]) {
|
|
|
464
406
|
function mapRemainingChildren(child: VNode | null) {
|
|
465
407
|
const map: Map<JSX.ElementKey, VNode> = new Map()
|
|
466
408
|
while (child) {
|
|
467
|
-
const key = child.
|
|
468
|
-
map.set(key ===
|
|
409
|
+
const key = child.key
|
|
410
|
+
map.set(key === null ? child.index : key, child)
|
|
469
411
|
child = child.sibling
|
|
470
412
|
}
|
|
471
413
|
return map
|
|
@@ -490,8 +432,8 @@ function checkForDuplicateKeys(parent: VNode, children: unknown[]) {
|
|
|
490
432
|
const keys = new Set<string>()
|
|
491
433
|
let warned = false
|
|
492
434
|
for (const child of children) {
|
|
493
|
-
if (!
|
|
494
|
-
const key = child.
|
|
435
|
+
if (!isElement(child)) continue
|
|
436
|
+
const key = child.key
|
|
495
437
|
if (typeof key === "string") {
|
|
496
438
|
if (!warned && keys.has(key)) {
|
|
497
439
|
const fn = getNearestParentFcTag(parent)
|
|
@@ -509,9 +451,8 @@ function checkForMissingKeys(parent: VNode, children: unknown[]) {
|
|
|
509
451
|
let hasKey = false
|
|
510
452
|
let hasMissingKey = false
|
|
511
453
|
for (const child of children) {
|
|
512
|
-
if (!
|
|
513
|
-
|
|
514
|
-
if (typeof key === "string") {
|
|
454
|
+
if (!isElement(child)) continue
|
|
455
|
+
if (typeof child.key === "string") {
|
|
515
456
|
hasKey = true
|
|
516
457
|
} else {
|
|
517
458
|
hasMissingKey = true
|
|
@@ -545,3 +486,24 @@ function getNearestParentFcTag(vNode: VNode) {
|
|
|
545
486
|
parentFcTagLookups.set(vNode, tag)
|
|
546
487
|
return tag
|
|
547
488
|
}
|
|
489
|
+
|
|
490
|
+
function createVNode(
|
|
491
|
+
parent: VNode,
|
|
492
|
+
type: VNode["type"],
|
|
493
|
+
props: VNode["props"],
|
|
494
|
+
key: VNode["key"] = null,
|
|
495
|
+
index = 0
|
|
496
|
+
): VNode {
|
|
497
|
+
const node = createBaseVNode(type, parent, props, key, index)
|
|
498
|
+
node.flags |= FLAG_PLACEMENT
|
|
499
|
+
|
|
500
|
+
if (typeof type === "function" && isMemoFn(type)) {
|
|
501
|
+
node.flags |= FLAG_MEMO
|
|
502
|
+
node.arePropsEqual = type[$MEMO].arePropsEqual
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (__DEV__ && "window" in globalThis) {
|
|
506
|
+
window.__kiru.profilingContext?.emit("createNode", appCtx)
|
|
507
|
+
}
|
|
508
|
+
return node
|
|
509
|
+
}
|
package/src/recursiveRender.ts
CHANGED
|
@@ -7,19 +7,21 @@ import {
|
|
|
7
7
|
assertValidElementProps,
|
|
8
8
|
isPrimitiveChild,
|
|
9
9
|
} from "./utils/index.js"
|
|
10
|
+
import { isStreamDataThrowValue } from "./utils/promise.js"
|
|
10
11
|
import { Signal } from "./signals/base.js"
|
|
11
|
-
import { $ERROR_BOUNDARY, voidElements, $
|
|
12
|
+
import { $ERROR_BOUNDARY, voidElements, $STREAM_DATA } from "./constants.js"
|
|
12
13
|
import { __DEV__ } from "./env.js"
|
|
13
|
-
import { isSuspenseThrowValue } from "./components/suspense.js"
|
|
14
14
|
import type { ErrorBoundaryNode } from "./types.utils"
|
|
15
15
|
|
|
16
|
-
export interface
|
|
16
|
+
export interface HeadlessRenderContext {
|
|
17
17
|
write(chunk: string): void
|
|
18
|
-
|
|
18
|
+
onStreamData?: (data: Kiru.StatefulPromise<unknown>[]) => void
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export
|
|
22
|
-
|
|
21
|
+
export { render as headlessRender }
|
|
22
|
+
|
|
23
|
+
function render(
|
|
24
|
+
ctx: HeadlessRenderContext,
|
|
23
25
|
el: unknown,
|
|
24
26
|
parent: Kiru.VNode | null,
|
|
25
27
|
idx: number
|
|
@@ -34,7 +36,7 @@ export function recursiveRender(
|
|
|
34
36
|
return ctx.write(el.toString())
|
|
35
37
|
}
|
|
36
38
|
if (el instanceof Array) {
|
|
37
|
-
return el.forEach((c, i) =>
|
|
39
|
+
return el.forEach((c, i) => render(ctx, c, parent, i))
|
|
38
40
|
}
|
|
39
41
|
if (Signal.isSignal(el)) {
|
|
40
42
|
const value = el.peek()
|
|
@@ -65,22 +67,22 @@ export function recursiveRender(
|
|
|
65
67
|
if (isExoticType(type)) {
|
|
66
68
|
if (type === $ERROR_BOUNDARY) {
|
|
67
69
|
let boundaryBuffer = ""
|
|
68
|
-
const
|
|
69
|
-
const boundaryCtx:
|
|
70
|
+
const streamPromises = new Set<Kiru.StatefulPromise<unknown>>()
|
|
71
|
+
const boundaryCtx: HeadlessRenderContext = {
|
|
70
72
|
write(chunk) {
|
|
71
73
|
boundaryBuffer += chunk
|
|
72
74
|
},
|
|
73
|
-
|
|
74
|
-
data.forEach((p) =>
|
|
75
|
+
onStreamData(data) {
|
|
76
|
+
data.forEach((p) => streamPromises.add(p))
|
|
75
77
|
},
|
|
76
78
|
}
|
|
77
79
|
try {
|
|
78
|
-
|
|
80
|
+
render(boundaryCtx, children, el, idx)
|
|
79
81
|
// flush successful render
|
|
80
82
|
ctx.write(boundaryBuffer)
|
|
81
|
-
ctx.
|
|
83
|
+
ctx.onStreamData?.([...streamPromises])
|
|
82
84
|
} catch (error) {
|
|
83
|
-
if (
|
|
85
|
+
if (isStreamDataThrowValue(error)) {
|
|
84
86
|
throw error
|
|
85
87
|
}
|
|
86
88
|
const e = error instanceof Error ? error : new Error(String(error))
|
|
@@ -88,12 +90,12 @@ export function recursiveRender(
|
|
|
88
90
|
onError?.(e)
|
|
89
91
|
const fallbackContent =
|
|
90
92
|
typeof fallback === "function" ? fallback(e) : fallback
|
|
91
|
-
|
|
93
|
+
render(ctx, fallbackContent, el, 0)
|
|
92
94
|
}
|
|
93
95
|
return
|
|
94
96
|
}
|
|
95
97
|
|
|
96
|
-
|
|
98
|
+
render(ctx, children, el, idx)
|
|
97
99
|
return
|
|
98
100
|
}
|
|
99
101
|
|
|
@@ -102,13 +104,13 @@ export function recursiveRender(
|
|
|
102
104
|
hookIndex.current = 0
|
|
103
105
|
node.current = el
|
|
104
106
|
const res = type(props)
|
|
105
|
-
|
|
107
|
+
render(ctx, res, el, idx)
|
|
106
108
|
return
|
|
107
109
|
} catch (error) {
|
|
108
|
-
if (
|
|
109
|
-
const { fallback,
|
|
110
|
-
ctx.
|
|
111
|
-
return
|
|
110
|
+
if (isStreamDataThrowValue(error)) {
|
|
111
|
+
const { fallback, data } = error[$STREAM_DATA]
|
|
112
|
+
ctx.onStreamData?.(data)
|
|
113
|
+
return render(ctx, fallback, el, 0)
|
|
112
114
|
}
|
|
113
115
|
throw error
|
|
114
116
|
} finally {
|
|
@@ -131,9 +133,9 @@ export function recursiveRender(
|
|
|
131
133
|
)
|
|
132
134
|
)
|
|
133
135
|
} else if (Array.isArray(children)) {
|
|
134
|
-
children.forEach((c, i) =>
|
|
136
|
+
children.forEach((c, i) => render(ctx, c, el, i))
|
|
135
137
|
} else {
|
|
136
|
-
|
|
138
|
+
render(ctx, children, el, 0)
|
|
137
139
|
}
|
|
138
140
|
ctx.write(`</${type}>`)
|
|
139
141
|
}
|
package/src/renderToString.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { renderMode } from "./globals.js"
|
|
2
2
|
import { Fragment } from "./element.js"
|
|
3
3
|
import { __DEV__ } from "./env.js"
|
|
4
|
-
import {
|
|
4
|
+
import { headlessRender, HeadlessRenderContext } from "./recursiveRender.js"
|
|
5
5
|
|
|
6
6
|
export function renderToString(element: JSX.Element) {
|
|
7
7
|
const prev = renderMode.current
|
|
8
8
|
renderMode.current = "string"
|
|
9
9
|
let result = ""
|
|
10
|
-
const ctx:
|
|
10
|
+
const ctx: HeadlessRenderContext = {
|
|
11
11
|
write(chunk) {
|
|
12
12
|
result += chunk
|
|
13
13
|
},
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
headlessRender(ctx, Fragment({ children: element }), null, 0)
|
|
16
16
|
renderMode.current = prev
|
|
17
17
|
return result
|
|
18
18
|
}
|