kiru 0.48.3 → 0.50.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/action.d.ts +22 -0
- package/dist/action.d.ts.map +1 -0
- package/dist/action.js +20 -0
- package/dist/action.js.map +1 -0
- package/dist/components/errorBoundary.d.ts +7 -0
- package/dist/components/errorBoundary.d.ts.map +1 -0
- package/dist/components/errorBoundary.js +6 -0
- package/dist/components/errorBoundary.js.map +1 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +7 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/lazy.d.ts.map +1 -0
- package/dist/{lazy.js → components/lazy.js} +11 -11
- package/dist/components/lazy.js.map +1 -0
- package/dist/{memo.d.ts → components/memo.d.ts} +1 -1
- package/dist/components/memo.d.ts.map +1 -0
- package/dist/{memo.js → components/memo.js} +2 -2
- package/dist/components/memo.js.map +1 -0
- package/dist/components/portal.d.ts.map +1 -0
- package/dist/{portal.js → components/portal.js} +6 -6
- package/dist/components/portal.js.map +1 -0
- package/dist/components/router/index.d.ts.map +1 -0
- package/dist/components/router/index.js.map +1 -0
- package/dist/components/router/route.d.ts.map +1 -0
- package/dist/{router → components/router}/route.js +1 -1
- package/dist/components/router/route.js.map +1 -0
- package/dist/{router → components/router}/router.d.ts +1 -1
- package/dist/components/router/router.d.ts.map +1 -0
- package/dist/{router → components/router}/router.js +8 -8
- package/dist/components/router/router.js.map +1 -0
- package/dist/components/router/routerUtils.d.ts.map +1 -0
- package/dist/components/router/routerUtils.js.map +1 -0
- package/dist/components/suspense.d.ts +33 -0
- package/dist/components/suspense.d.ts.map +1 -0
- package/dist/components/suspense.js +113 -0
- package/dist/components/suspense.js.map +1 -0
- package/dist/components/transition.d.ts.map +1 -0
- package/dist/{transition.js → components/transition.js} +5 -5
- package/dist/components/transition.js.map +1 -0
- package/dist/constants.d.ts +6 -12
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +83 -12
- package/dist/constants.js.map +1 -1
- package/dist/context.js +1 -1
- package/dist/context.js.map +1 -1
- package/dist/dom.d.ts.map +1 -1
- package/dist/dom.js +9 -12
- package/dist/dom.js.map +1 -1
- package/dist/element.js +2 -2
- package/dist/element.js.map +1 -1
- package/dist/error.js +1 -1
- package/dist/error.js.map +1 -1
- package/dist/form/index.d.ts.map +1 -1
- package/dist/form/index.js +1 -2
- package/dist/form/index.js.map +1 -1
- package/dist/hmr.js +1 -1
- package/dist/hmr.js.map +1 -1
- package/dist/hooks/useAsync.d.ts.map +1 -1
- package/dist/hooks/useAsync.js +2 -2
- package/dist/hooks/useAsync.js.map +1 -1
- package/dist/hooks/useCallback.d.ts.map +1 -1
- package/dist/hooks/useCallback.js +2 -1
- package/dist/hooks/useCallback.js.map +1 -1
- package/dist/hooks/useEffect.d.ts.map +1 -1
- package/dist/hooks/useEffect.js +2 -1
- package/dist/hooks/useEffect.js.map +1 -1
- package/dist/hooks/useEffectEvent.d.ts.map +1 -1
- package/dist/hooks/useEffectEvent.js +2 -1
- package/dist/hooks/useEffectEvent.js.map +1 -1
- package/dist/hooks/useLayoutEffect.d.ts.map +1 -1
- package/dist/hooks/useLayoutEffect.js +2 -1
- package/dist/hooks/useLayoutEffect.js.map +1 -1
- package/dist/hooks/useMemo.d.ts.map +1 -1
- package/dist/hooks/useMemo.js +2 -1
- package/dist/hooks/useMemo.js.map +1 -1
- package/dist/hooks/useReducer.js +2 -2
- package/dist/hooks/useReducer.js.map +1 -1
- package/dist/hooks/useRef.d.ts.map +1 -1
- package/dist/hooks/useRef.js +2 -1
- package/dist/hooks/useRef.js.map +1 -1
- package/dist/hooks/useState.js +2 -2
- package/dist/hooks/useState.js.map +1 -1
- package/dist/hooks/useSyncExternalStore.js +2 -2
- package/dist/hooks/useSyncExternalStore.js.map +1 -1
- package/dist/hooks/useViewTransition.d.ts.map +1 -1
- package/dist/hooks/useViewTransition.js +1 -2
- package/dist/hooks/useViewTransition.js.map +1 -1
- package/dist/hooks/utils.d.ts +0 -1
- package/dist/hooks/utils.d.ts.map +1 -1
- package/dist/hooks/utils.js +2 -2
- package/dist/hooks/utils.js.map +1 -1
- package/dist/index.d.ts +3 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/reconciler.js +8 -7
- package/dist/reconciler.js.map +1 -1
- package/dist/renderToString.d.ts.map +1 -1
- package/dist/renderToString.js +93 -38
- package/dist/renderToString.js.map +1 -1
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +48 -11
- package/dist/scheduler.js.map +1 -1
- package/dist/signals/base.d.ts.map +1 -1
- package/dist/signals/base.js +1 -2
- package/dist/signals/base.js.map +1 -1
- package/dist/signals/computed.js +1 -1
- package/dist/signals/computed.js.map +1 -1
- package/dist/signals/effect.js +1 -1
- package/dist/signals/effect.js.map +1 -1
- package/dist/signals/watch.d.ts.map +1 -1
- package/dist/signals/watch.js +2 -3
- package/dist/signals/watch.js.map +1 -1
- package/dist/ssr/server.d.ts +4 -1
- package/dist/ssr/server.d.ts.map +1 -1
- package/dist/ssr/server.js +136 -66
- package/dist/ssr/server.js.map +1 -1
- package/dist/store.d.ts +1 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +2 -2
- package/dist/store.js.map +1 -1
- package/dist/swr.js +1 -1
- package/dist/swr.js.map +1 -1
- package/dist/types.d.ts +14 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.dom.d.ts +2 -2
- package/dist/types.dom.d.ts.map +1 -1
- package/dist/types.utils.d.ts +7 -1
- package/dist/types.utils.d.ts.map +1 -1
- package/dist/utils/compare.d.ts +3 -0
- package/dist/utils/compare.d.ts.map +1 -0
- package/dist/utils/compare.js +123 -0
- package/dist/utils/compare.js.map +1 -0
- package/dist/utils/format.d.ts +20 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +136 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/generateId.d.ts.map +1 -0
- package/dist/utils/generateId.js.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/runtime.d.ts +11 -0
- package/dist/utils/runtime.d.ts.map +1 -0
- package/dist/utils/runtime.js +23 -0
- package/dist/utils/runtime.js.map +1 -0
- package/dist/utils/vdom.d.ts +27 -0
- package/dist/utils/vdom.d.ts.map +1 -0
- package/dist/utils/vdom.js +128 -0
- package/dist/utils/vdom.js.map +1 -0
- package/package.json +6 -6
- package/src/action.ts +42 -0
- package/src/components/errorBoundary.ts +16 -0
- package/src/components/index.ts +6 -0
- package/src/{lazy.ts → components/lazy.ts} +12 -12
- package/src/{memo.ts → components/memo.ts} +3 -3
- package/src/{portal.ts → components/portal.ts} +6 -6
- package/src/{router → components/router}/route.ts +1 -1
- package/src/{router → components/router}/router.ts +9 -9
- package/src/components/suspense.ts +193 -0
- package/src/{transition.ts → components/transition.ts} +5 -5
- package/src/constants.ts +86 -13
- package/src/context.ts +1 -1
- package/src/customEvents.ts +22 -22
- package/src/dom.ts +8 -11
- package/src/element.ts +2 -2
- package/src/error.ts +1 -1
- package/src/form/index.ts +5 -2
- package/src/hmr.ts +1 -1
- package/src/hooks/useAsync.ts +5 -3
- package/src/hooks/useCallback.ts +2 -1
- package/src/hooks/useEffect.ts +2 -6
- package/src/hooks/useEffectEvent.ts +2 -1
- package/src/hooks/useLayoutEffect.ts +2 -6
- package/src/hooks/useMemo.ts +2 -1
- package/src/hooks/useReducer.ts +2 -2
- package/src/hooks/useRef.ts +2 -1
- package/src/hooks/useState.ts +2 -2
- package/src/hooks/useSyncExternalStore.ts +2 -2
- package/src/hooks/useViewTransition.ts +1 -2
- package/src/hooks/utils.ts +2 -2
- package/src/index.ts +3 -5
- package/src/reconciler.ts +13 -11
- package/src/renderToString.ts +108 -42
- package/src/scheduler.ts +61 -14
- package/src/signals/base.ts +6 -2
- package/src/signals/computed.ts +1 -1
- package/src/signals/effect.ts +1 -1
- package/src/signals/watch.ts +2 -3
- package/src/ssr/server.ts +163 -71
- package/src/store.ts +6 -2
- package/src/swr.ts +3 -3
- package/src/types.dom.ts +2 -1
- package/src/types.ts +20 -2
- package/src/types.utils.ts +8 -0
- package/src/utils/compare.ts +125 -0
- package/src/utils/format.ts +162 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/runtime.ts +25 -0
- package/src/utils/vdom.ts +195 -0
- package/dist/cloneVNode.d.ts +0 -2
- package/dist/cloneVNode.d.ts.map +0 -1
- package/dist/cloneVNode.js +0 -14
- package/dist/cloneVNode.js.map +0 -1
- package/dist/generateId.d.ts.map +0 -1
- package/dist/generateId.js.map +0 -1
- package/dist/lazy.d.ts.map +0 -1
- package/dist/lazy.js.map +0 -1
- package/dist/memo.d.ts.map +0 -1
- package/dist/memo.js.map +0 -1
- package/dist/portal.d.ts.map +0 -1
- package/dist/portal.js.map +0 -1
- package/dist/props.d.ts +0 -4
- package/dist/props.d.ts.map +0 -1
- package/dist/props.js +0 -27
- package/dist/props.js.map +0 -1
- package/dist/router/index.d.ts.map +0 -1
- package/dist/router/index.js.map +0 -1
- package/dist/router/route.d.ts.map +0 -1
- package/dist/router/route.js.map +0 -1
- package/dist/router/router.d.ts.map +0 -1
- package/dist/router/router.js.map +0 -1
- package/dist/router/routerUtils.d.ts.map +0 -1
- package/dist/router/routerUtils.js.map +0 -1
- package/dist/transition.d.ts.map +0 -1
- package/dist/transition.js.map +0 -1
- package/dist/utils.d.ts +0 -52
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -433
- package/dist/utils.js.map +0 -1
- package/dist/warning.d.ts +0 -2
- package/dist/warning.d.ts.map +0 -1
- package/dist/warning.js +0 -4
- package/dist/warning.js.map +0 -1
- package/src/cloneVNode.ts +0 -14
- package/src/props.ts +0 -34
- package/src/utils.ts +0 -518
- package/src/warning.ts +0 -9
- /package/dist/{lazy.d.ts → components/lazy.d.ts} +0 -0
- /package/dist/{portal.d.ts → components/portal.d.ts} +0 -0
- /package/dist/{router → components/router}/index.d.ts +0 -0
- /package/dist/{router → components/router}/index.js +0 -0
- /package/dist/{router → components/router}/route.d.ts +0 -0
- /package/dist/{router → components/router}/routerUtils.d.ts +0 -0
- /package/dist/{router → components/router}/routerUtils.js +0 -0
- /package/dist/{transition.d.ts → components/transition.d.ts} +0 -0
- /package/dist/{generateId.d.ts → utils/generateId.d.ts} +0 -0
- /package/dist/{generateId.js → utils/generateId.js} +0 -0
- /package/src/{router → components/router}/index.ts +0 -0
- /package/src/{router → components/router}/routerUtils.ts +0 -0
- /package/src/{generateId.ts → utils/generateId.ts} +0 -0
package/src/reconciler.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { $FRAGMENT, FLAG_PLACEMENT, FLAG_UPDATE } from "./constants.js"
|
|
2
|
-
import { getVNodeAppContext, isVNode, latest } from "./utils.js"
|
|
2
|
+
import { getVNodeAppContext, isVNode, latest } from "./utils/index.js"
|
|
3
3
|
import { Signal } from "./signals/base.js"
|
|
4
4
|
import { __DEV__ } from "./env.js"
|
|
5
5
|
import { createElement, Fragment } from "./element.js"
|
|
@@ -150,11 +150,7 @@ function reconcileChildrenArray(parent: VNode, children: unknown[]) {
|
|
|
150
150
|
function updateSlot(parent: VNode, oldChild: VNode | null, child: unknown) {
|
|
151
151
|
// Update the node if the keys match, otherwise return null.
|
|
152
152
|
const key = oldChild?.props.key
|
|
153
|
-
if (
|
|
154
|
-
(typeof child === "string" && child !== "") ||
|
|
155
|
-
typeof child === "number" ||
|
|
156
|
-
typeof child === "bigint"
|
|
157
|
-
) {
|
|
153
|
+
if (isValidTextNodeValue(child)) {
|
|
158
154
|
if (key !== undefined) return null
|
|
159
155
|
if (
|
|
160
156
|
oldChild?.type === "#text" &&
|
|
@@ -272,11 +268,7 @@ function updateFragment(
|
|
|
272
268
|
}
|
|
273
269
|
|
|
274
270
|
function createChild(parent: VNode, child: unknown): VNode | null {
|
|
275
|
-
if (
|
|
276
|
-
(typeof child === "string" && child !== "") ||
|
|
277
|
-
typeof child === "number" ||
|
|
278
|
-
typeof child === "bigint"
|
|
279
|
-
) {
|
|
271
|
+
if (isValidTextNodeValue(child)) {
|
|
280
272
|
if (__DEV__) {
|
|
281
273
|
dev_emitCreateNode()
|
|
282
274
|
}
|
|
@@ -438,6 +430,16 @@ function updateFromMap(
|
|
|
438
430
|
return null
|
|
439
431
|
}
|
|
440
432
|
|
|
433
|
+
function isValidTextNodeValue(
|
|
434
|
+
value: unknown
|
|
435
|
+
): value is string | number | bigint {
|
|
436
|
+
return (
|
|
437
|
+
(typeof value === "string" && value !== "") ||
|
|
438
|
+
typeof value === "number" ||
|
|
439
|
+
typeof value === "bigint"
|
|
440
|
+
)
|
|
441
|
+
}
|
|
442
|
+
|
|
441
443
|
function propsChanged(oldProps: VNode["props"], newProps: VNode["props"]) {
|
|
442
444
|
const aKeys = Object.keys(oldProps)
|
|
443
445
|
const bKeys = Object.keys(newProps)
|
package/src/renderToString.ts
CHANGED
|
@@ -5,78 +5,144 @@ import {
|
|
|
5
5
|
encodeHtmlEntities,
|
|
6
6
|
propsToElementAttributes,
|
|
7
7
|
isExoticType,
|
|
8
|
-
|
|
8
|
+
assertValidElementProps,
|
|
9
|
+
} from "./utils/index.js"
|
|
9
10
|
import { Signal } from "./signals/base.js"
|
|
10
|
-
import {
|
|
11
|
-
|
|
11
|
+
import {
|
|
12
|
+
$ERROR_BOUNDARY,
|
|
13
|
+
$HYDRATION_BOUNDARY,
|
|
14
|
+
voidElements,
|
|
15
|
+
} from "./constants.js"
|
|
12
16
|
import { HYDRATION_BOUNDARY_MARKER } from "./ssr/hydrationBoundary.js"
|
|
13
17
|
import { __DEV__ } from "./env.js"
|
|
18
|
+
import { ErrorBoundaryNode } from "./types.utils.js"
|
|
19
|
+
import { isSuspenseThrowValue } from "./components/suspense.js"
|
|
20
|
+
|
|
21
|
+
interface StringRenderContext {
|
|
22
|
+
write(chunk: string): void
|
|
23
|
+
beginNewBoundary(): number
|
|
24
|
+
resetBoundary(idx: number): void
|
|
25
|
+
}
|
|
14
26
|
|
|
15
27
|
export function renderToString(element: JSX.Element) {
|
|
16
28
|
const prev = renderMode.current
|
|
17
29
|
renderMode.current = "string"
|
|
18
|
-
const
|
|
19
|
-
const
|
|
30
|
+
const parts: string[] = [""]
|
|
31
|
+
const ctx: StringRenderContext = {
|
|
32
|
+
write(chunk) {
|
|
33
|
+
parts[parts.length - 1] += chunk
|
|
34
|
+
},
|
|
35
|
+
beginNewBoundary() {
|
|
36
|
+
parts.push("")
|
|
37
|
+
return parts.length - 1
|
|
38
|
+
},
|
|
39
|
+
resetBoundary(idx) {
|
|
40
|
+
parts[idx] = ""
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
renderToString_internal(ctx, Fragment({ children: element }), null, 0)
|
|
20
44
|
renderMode.current = prev
|
|
21
|
-
return
|
|
45
|
+
return parts.join("")
|
|
22
46
|
}
|
|
23
47
|
|
|
24
48
|
function renderToString_internal(
|
|
49
|
+
ctx: StringRenderContext,
|
|
25
50
|
el: unknown,
|
|
26
51
|
parent: Kiru.VNode | null,
|
|
27
52
|
idx: number
|
|
28
|
-
):
|
|
29
|
-
if (el === null) return
|
|
30
|
-
if (el === undefined) return
|
|
31
|
-
if (typeof el === "boolean") return
|
|
32
|
-
if (typeof el === "string")
|
|
33
|
-
|
|
53
|
+
): void {
|
|
54
|
+
if (el === null) return
|
|
55
|
+
if (el === undefined) return
|
|
56
|
+
if (typeof el === "boolean") return
|
|
57
|
+
if (typeof el === "string") {
|
|
58
|
+
return ctx.write(encodeHtmlEntities(el))
|
|
59
|
+
}
|
|
60
|
+
if (typeof el === "number" || typeof el === "bigint") {
|
|
61
|
+
return ctx.write(el.toString())
|
|
62
|
+
}
|
|
34
63
|
if (el instanceof Array) {
|
|
35
|
-
return el.
|
|
64
|
+
return el.forEach((c, i) => renderToString_internal(ctx, c, parent, i))
|
|
65
|
+
}
|
|
66
|
+
if (Signal.isSignal(el)) {
|
|
67
|
+
return ctx.write(String(el.peek()))
|
|
68
|
+
}
|
|
69
|
+
if (!isVNode(el)) {
|
|
70
|
+
return ctx.write(String(el))
|
|
36
71
|
}
|
|
37
|
-
if (Signal.isSignal(el)) return String(el.peek())
|
|
38
|
-
if (!isVNode(el)) return String(el)
|
|
39
72
|
el.parent = parent
|
|
40
73
|
el.depth = (parent?.depth ?? -1) + 1
|
|
41
74
|
el.index = idx
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
75
|
+
const { type, props = {} } = el
|
|
76
|
+
if (type === "#text") {
|
|
77
|
+
return ctx.write(encodeHtmlEntities(props.nodeValue ?? ""))
|
|
78
|
+
}
|
|
45
79
|
|
|
46
80
|
const children = props.children
|
|
47
81
|
if (isExoticType(type)) {
|
|
48
82
|
if (type === $HYDRATION_BOUNDARY) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
83
|
+
ctx.write(`<!--${HYDRATION_BOUNDARY_MARKER}-->`)
|
|
84
|
+
renderToString_internal(ctx, children, el, idx)
|
|
85
|
+
ctx.write(`<!--/${HYDRATION_BOUNDARY_MARKER}-->`)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (type === $ERROR_BOUNDARY) {
|
|
90
|
+
const boundaryIdx = ctx.beginNewBoundary()
|
|
91
|
+
try {
|
|
92
|
+
renderToString_internal(ctx, children, el, idx)
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (isSuspenseThrowValue(error)) {
|
|
95
|
+
throw error
|
|
96
|
+
}
|
|
97
|
+
ctx.resetBoundary(boundaryIdx)
|
|
98
|
+
const e = error instanceof Error ? error : new Error(String(error))
|
|
99
|
+
const { fallback, onError } = props as ErrorBoundaryNode["props"]
|
|
100
|
+
onError?.(e)
|
|
101
|
+
const fallbackContent =
|
|
102
|
+
typeof fallback === "function" ? fallback(e) : fallback
|
|
103
|
+
renderToString_internal(ctx, fallbackContent, el, 0)
|
|
104
|
+
}
|
|
105
|
+
return
|
|
54
106
|
}
|
|
55
107
|
|
|
56
|
-
|
|
108
|
+
renderToString_internal(ctx, children, el, idx)
|
|
109
|
+
return
|
|
57
110
|
}
|
|
58
111
|
|
|
59
112
|
if (typeof type !== "string") {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
113
|
+
try {
|
|
114
|
+
node.current = el
|
|
115
|
+
const res = type(props)
|
|
116
|
+
renderToString_internal(ctx, res, el, idx)
|
|
117
|
+
return
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (isSuspenseThrowValue(error)) {
|
|
120
|
+
return renderToString_internal(ctx, error.fallback, el, 0)
|
|
121
|
+
}
|
|
122
|
+
throw error
|
|
123
|
+
} finally {
|
|
124
|
+
node.current = null
|
|
125
|
+
}
|
|
64
126
|
}
|
|
65
127
|
|
|
66
|
-
if (__DEV__)
|
|
67
|
-
assertValidElementProps(el)
|
|
68
|
-
}
|
|
128
|
+
if (__DEV__) assertValidElementProps(el)
|
|
69
129
|
const attrs = propsToElementAttributes(props)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
? props.innerHTML.peek()
|
|
74
|
-
: props.innerHTML
|
|
75
|
-
: Array.isArray(children)
|
|
76
|
-
? children.map((c, i) => renderToString_internal(c, el, i)).join("")
|
|
77
|
-
: renderToString_internal(children, el, 0)
|
|
130
|
+
ctx.write(`<${type}${attrs.length ? ` ${attrs}` : ""}>`)
|
|
131
|
+
|
|
132
|
+
if (voidElements.has(type)) return
|
|
78
133
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
134
|
+
if ("innerHTML" in props) {
|
|
135
|
+
ctx.write(
|
|
136
|
+
String(
|
|
137
|
+
Signal.isSignal(props.innerHTML)
|
|
138
|
+
? props.innerHTML.peek()
|
|
139
|
+
: props.innerHTML
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
} else if (Array.isArray(children)) {
|
|
143
|
+
children.forEach((c, i) => renderToString_internal(ctx, c, el, i))
|
|
144
|
+
} else {
|
|
145
|
+
renderToString_internal(ctx, children, el, 0)
|
|
146
|
+
}
|
|
147
|
+
ctx.write(`</${type}>`)
|
|
82
148
|
}
|
package/src/scheduler.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ContextProviderNode,
|
|
3
3
|
DomVNode,
|
|
4
|
+
ErrorBoundaryNode,
|
|
4
5
|
FunctionVNode,
|
|
5
6
|
} from "./types.utils"
|
|
6
7
|
import {
|
|
7
8
|
$CONTEXT_PROVIDER,
|
|
9
|
+
$ERROR_BOUNDARY,
|
|
8
10
|
CONSECUTIVE_DIRTY_LIMIT,
|
|
9
11
|
FLAG_DELETION,
|
|
10
12
|
FLAG_DIRTY,
|
|
@@ -23,14 +25,15 @@ import { __DEV__ } from "./env.js"
|
|
|
23
25
|
import { KiruError } from "./error.js"
|
|
24
26
|
import { hookIndex, node, renderMode } from "./globals.js"
|
|
25
27
|
import { hydrationStack } from "./hydration.js"
|
|
26
|
-
import { assertValidElementProps } from "./
|
|
28
|
+
import { assertValidElementProps } from "./utils/index.js"
|
|
27
29
|
import { reconcileChildren } from "./reconciler.js"
|
|
28
30
|
import {
|
|
29
31
|
latest,
|
|
30
32
|
traverseApply,
|
|
31
33
|
isExoticType,
|
|
32
34
|
getVNodeAppContext,
|
|
33
|
-
|
|
35
|
+
findParentErrorBoundary,
|
|
36
|
+
} from "./utils/index.js"
|
|
34
37
|
import type { AppContext } from "./appContext"
|
|
35
38
|
|
|
36
39
|
type VNode = Kiru.VNode
|
|
@@ -202,21 +205,10 @@ function doWork(): void {
|
|
|
202
205
|
function performUnitOfWork(vNode: VNode): VNode | void {
|
|
203
206
|
let renderChild = true
|
|
204
207
|
try {
|
|
205
|
-
const { props } = vNode
|
|
206
208
|
if (typeof vNode.type === "string") {
|
|
207
209
|
updateHostComponent(vNode as DomVNode)
|
|
208
210
|
} else if (isExoticType(vNode.type)) {
|
|
209
|
-
|
|
210
|
-
const {
|
|
211
|
-
props: { dependents, value },
|
|
212
|
-
prev,
|
|
213
|
-
} = vNode as ContextProviderNode<unknown>
|
|
214
|
-
|
|
215
|
-
if (dependents.size && prev && prev.props.value !== value) {
|
|
216
|
-
dependents.forEach(queueUpdate)
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
vNode.child = reconcileChildren(vNode, props.children)
|
|
211
|
+
updateExoticComponent(vNode)
|
|
220
212
|
} else {
|
|
221
213
|
renderChild = updateFunctionComponent(vNode as FunctionVNode)
|
|
222
214
|
}
|
|
@@ -229,6 +221,18 @@ function performUnitOfWork(vNode: VNode): VNode | void {
|
|
|
229
221
|
)
|
|
230
222
|
}
|
|
231
223
|
|
|
224
|
+
const handler = findParentErrorBoundary(vNode)
|
|
225
|
+
if (handler) {
|
|
226
|
+
const e = (handler.error =
|
|
227
|
+
error instanceof Error ? error : new Error(String(error)))
|
|
228
|
+
|
|
229
|
+
handler.props.onError?.(e)
|
|
230
|
+
if (handler.depth < currentWorkRoot!.depth) {
|
|
231
|
+
currentWorkRoot = handler
|
|
232
|
+
}
|
|
233
|
+
return handler
|
|
234
|
+
}
|
|
235
|
+
|
|
232
236
|
if (KiruError.isKiruError(error)) {
|
|
233
237
|
if (error.customNodeStack) {
|
|
234
238
|
setTimeout(() => {
|
|
@@ -279,6 +283,35 @@ function performUnitOfWork(vNode: VNode): VNode | void {
|
|
|
279
283
|
}
|
|
280
284
|
}
|
|
281
285
|
|
|
286
|
+
function updateExoticComponent(vNode: VNode) {
|
|
287
|
+
const { props, type } = vNode
|
|
288
|
+
let children = props.children
|
|
289
|
+
|
|
290
|
+
if (type === $CONTEXT_PROVIDER) {
|
|
291
|
+
const {
|
|
292
|
+
props: { dependents, value },
|
|
293
|
+
prev,
|
|
294
|
+
} = vNode as ContextProviderNode<unknown>
|
|
295
|
+
|
|
296
|
+
if (dependents.size && prev && prev.props.value !== value) {
|
|
297
|
+
dependents.forEach(queueUpdate)
|
|
298
|
+
}
|
|
299
|
+
} else if (type === $ERROR_BOUNDARY) {
|
|
300
|
+
const n = vNode as ErrorBoundaryNode
|
|
301
|
+
const { error } = n
|
|
302
|
+
if (error) {
|
|
303
|
+
children =
|
|
304
|
+
typeof props.fallback === "function"
|
|
305
|
+
? props.fallback(error)
|
|
306
|
+
: props.fallback
|
|
307
|
+
|
|
308
|
+
delete n.error
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
vNode.child = reconcileChildren(vNode, children)
|
|
313
|
+
}
|
|
314
|
+
|
|
282
315
|
function updateFunctionComponent(vNode: FunctionVNode) {
|
|
283
316
|
const { type, props, subs, prev, flags } = vNode
|
|
284
317
|
if (flags & FLAG_MEMO) {
|
|
@@ -318,6 +351,20 @@ function updateFunctionComponent(vNode: FunctionVNode) {
|
|
|
318
351
|
|
|
319
352
|
if (__DEV__) {
|
|
320
353
|
newChild = latest(type)(props)
|
|
354
|
+
|
|
355
|
+
if (vNode.hmrUpdated && vNode.hooks && vNode.hookSig) {
|
|
356
|
+
const len = vNode.hooks.length
|
|
357
|
+
if (hookIndex.current < len) {
|
|
358
|
+
// clean up any hooks that were removed
|
|
359
|
+
for (let i = hookIndex.current; i < len; i++) {
|
|
360
|
+
const hook = vNode.hooks[i]
|
|
361
|
+
hook.cleanup?.()
|
|
362
|
+
}
|
|
363
|
+
vNode.hooks.length = hookIndex.current
|
|
364
|
+
vNode.hookSig.length = hookIndex.current
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
321
368
|
delete vNode.hmrUpdated
|
|
322
369
|
if (++renderTryCount > CONSECUTIVE_DIRTY_LIMIT) {
|
|
323
370
|
throw new KiruError({
|
package/src/signals/base.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
latest,
|
|
3
|
+
safeStringify,
|
|
4
|
+
sideEffectsEnabled,
|
|
5
|
+
generateRandomID,
|
|
6
|
+
} from "../utils/index.js"
|
|
2
7
|
import { $HMR_ACCEPT, $SIGNAL } from "../constants.js"
|
|
3
8
|
import { __DEV__ } from "../env.js"
|
|
4
9
|
import { node } from "../globals.js"
|
|
5
10
|
import { useHook } from "../hooks/utils.js"
|
|
6
|
-
import { generateRandomID } from "../generateId.js"
|
|
7
11
|
import { requestUpdate } from "../scheduler.js"
|
|
8
12
|
import { tracking, signalSubsMap } from "./globals.js"
|
|
9
13
|
import type { SignalSubscriber, ReadonlySignal } from "./types.js"
|
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 { useHook } from "../hooks/utils.js"
|
|
4
|
-
import { latest } from "../utils.js"
|
|
4
|
+
import { 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"
|
package/src/signals/effect.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { node } from "../globals.js"
|
|
2
|
-
import { sideEffectsEnabled } from "../utils.js"
|
|
2
|
+
import { sideEffectsEnabled } from "../utils/index.js"
|
|
3
3
|
import { tracking, effectQueue } from "./globals.js"
|
|
4
4
|
import { tick } from "./utils.js"
|
|
5
5
|
import type { Signal } from "./base.js"
|
package/src/signals/watch.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { __DEV__ } from "../env.js"
|
|
2
2
|
import type { HMRAccept } from "../hmr.js"
|
|
3
3
|
import { $HMR_ACCEPT } from "../constants.js"
|
|
4
|
-
import {
|
|
4
|
+
import { useHook } from "../hooks/utils.js"
|
|
5
5
|
import { effectQueue } from "./globals.js"
|
|
6
|
-
import { generateRandomID } from "../generateId.js"
|
|
7
6
|
import { executeWithTracking } from "./effect.js"
|
|
8
|
-
import { latest } from "../utils.js"
|
|
7
|
+
import { latest, sideEffectsEnabled, generateRandomID } from "../utils/index.js"
|
|
9
8
|
import type { Signal } from "./base.js"
|
|
10
9
|
import type { SignalValues } from "./types.js"
|
|
11
10
|
|
package/src/ssr/server.ts
CHANGED
|
@@ -1,110 +1,202 @@
|
|
|
1
1
|
import { Readable } from "node:stream"
|
|
2
2
|
import { Fragment } from "../element.js"
|
|
3
|
-
import { renderMode, node } from "../globals.js"
|
|
3
|
+
import { renderMode, node, hookIndex } from "../globals.js"
|
|
4
4
|
import {
|
|
5
5
|
isVNode,
|
|
6
6
|
encodeHtmlEntities,
|
|
7
7
|
propsToElementAttributes,
|
|
8
8
|
isExoticType,
|
|
9
|
-
|
|
9
|
+
assertValidElementProps,
|
|
10
|
+
} from "../utils/index.js"
|
|
10
11
|
import { Signal } from "../signals/base.js"
|
|
11
|
-
import {
|
|
12
|
-
|
|
12
|
+
import {
|
|
13
|
+
$HYDRATION_BOUNDARY,
|
|
14
|
+
$ERROR_BOUNDARY,
|
|
15
|
+
PREFETCHED_DATA_EVENT,
|
|
16
|
+
voidElements,
|
|
17
|
+
} from "../constants.js"
|
|
13
18
|
import { HYDRATION_BOUNDARY_MARKER } from "./hydrationBoundary.js"
|
|
14
19
|
import { __DEV__ } from "../env.js"
|
|
20
|
+
import type { ErrorBoundaryNode } from "../types.utils"
|
|
21
|
+
import { isSuspenseThrowValue } from "../components/suspense.js"
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
interface ServerRenderContext {
|
|
24
|
+
write: (chunk: string) => void
|
|
25
|
+
queuePendingData: (data: Kiru.StatefulPromise<unknown>[]) => void
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const PREFETCH_EVENTS_SETUP = `
|
|
29
|
+
<script type="text/javascript">
|
|
30
|
+
const d = document,
|
|
31
|
+
m = (window["${PREFETCHED_DATA_EVENT}"] ??= new Map());
|
|
32
|
+
d.querySelectorAll("[x-data]").forEach((p) => {
|
|
33
|
+
const id = p.getAttribute("id");
|
|
34
|
+
const { data, error } = JSON.parse(p.innerHTML);
|
|
35
|
+
m.set(id, { data, error });
|
|
36
|
+
const event = new CustomEvent("${PREFETCHED_DATA_EVENT}", { detail: { id, data, error } });
|
|
37
|
+
window.dispatchEvent(event);
|
|
38
|
+
p.remove();
|
|
39
|
+
});
|
|
40
|
+
d.currentScript.remove()
|
|
41
|
+
</script>
|
|
42
|
+
`
|
|
43
|
+
|
|
44
|
+
export function renderToReadableStream(element: JSX.Element): {
|
|
45
|
+
immediate: string
|
|
46
|
+
stream: Readable
|
|
47
|
+
} {
|
|
48
|
+
const stream = new Readable({ read() {} })
|
|
20
49
|
const rootNode = Fragment({ children: element })
|
|
50
|
+
const prefetchPromises = new Set<Kiru.StatefulPromise<unknown>>()
|
|
51
|
+
const pendingWritePromises: Promise<unknown>[] = []
|
|
21
52
|
|
|
22
|
-
|
|
23
|
-
|
|
53
|
+
let immediate = ""
|
|
54
|
+
|
|
55
|
+
const ctx: ServerRenderContext = {
|
|
56
|
+
write: (chunk) => (immediate += chunk),
|
|
57
|
+
queuePendingData(data) {
|
|
58
|
+
for (const promise of data) {
|
|
59
|
+
if (prefetchPromises.has(promise)) continue
|
|
60
|
+
prefetchPromises.add(promise)
|
|
61
|
+
|
|
62
|
+
const writePromise = promise
|
|
63
|
+
.then(() => ({ data: promise.value }))
|
|
64
|
+
.catch(() => ({ error: promise.error?.message }))
|
|
65
|
+
.then((value) => {
|
|
66
|
+
const content = JSON.stringify(value)
|
|
67
|
+
stream.push(
|
|
68
|
+
`<script id="${promise.id}" x-data type="application/json">${content}</script>`
|
|
69
|
+
)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
pendingWritePromises.push(writePromise)
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const prev = renderMode.current
|
|
78
|
+
renderMode.current = "stream"
|
|
79
|
+
renderToStream_internal(ctx, rootNode, null, 0)
|
|
24
80
|
renderMode.current = prev
|
|
25
81
|
|
|
26
|
-
|
|
82
|
+
if (pendingWritePromises.length > 0) {
|
|
83
|
+
Promise.all(pendingWritePromises).then(() => {
|
|
84
|
+
stream.push(PREFETCH_EVENTS_SETUP)
|
|
85
|
+
stream.push(null)
|
|
86
|
+
})
|
|
87
|
+
} else {
|
|
88
|
+
stream.push(null)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { immediate, stream }
|
|
27
92
|
}
|
|
28
93
|
|
|
29
94
|
function renderToStream_internal(
|
|
30
|
-
|
|
95
|
+
ctx: ServerRenderContext,
|
|
31
96
|
el: unknown,
|
|
32
97
|
parent: Kiru.VNode | null,
|
|
33
98
|
idx: number
|
|
34
99
|
): void {
|
|
35
|
-
if (el === null) return
|
|
36
|
-
if (el ===
|
|
37
|
-
if (typeof el === "
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
return
|
|
45
|
-
}
|
|
46
|
-
if (el instanceof Array) {
|
|
47
|
-
el.forEach((c, i) => renderToStream_internal(stream, c, parent, i))
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
if (Signal.isSignal(el)) {
|
|
51
|
-
stream.push(String(el.peek()))
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
if (!isVNode(el)) {
|
|
55
|
-
stream.push(String(el))
|
|
56
|
-
return
|
|
57
|
-
}
|
|
100
|
+
if (el === null || el === undefined || typeof el === "boolean") return
|
|
101
|
+
if (typeof el === "string") return ctx.write(encodeHtmlEntities(el))
|
|
102
|
+
if (typeof el === "number" || typeof el === "bigint")
|
|
103
|
+
return ctx.write(el.toString())
|
|
104
|
+
if (el instanceof Array)
|
|
105
|
+
return el.forEach((c, i) => renderToStream_internal(ctx, c, parent, i))
|
|
106
|
+
if (Signal.isSignal(el)) return ctx.write(String(el.peek()))
|
|
107
|
+
if (!isVNode(el)) return ctx.write(String(el))
|
|
108
|
+
|
|
58
109
|
el.parent = parent
|
|
59
110
|
el.depth = (parent?.depth ?? -1) + 1
|
|
60
111
|
el.index = idx
|
|
61
|
-
const
|
|
112
|
+
const { type, props = {} } = el
|
|
62
113
|
const children = props.children
|
|
63
|
-
|
|
64
|
-
if (type === "#text")
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
114
|
+
|
|
115
|
+
if (type === "#text")
|
|
116
|
+
return ctx.write(encodeHtmlEntities(props.nodeValue ?? ""))
|
|
117
|
+
|
|
68
118
|
if (isExoticType(type)) {
|
|
69
119
|
if (type === $HYDRATION_BOUNDARY) {
|
|
70
|
-
|
|
71
|
-
renderToStream_internal(
|
|
72
|
-
|
|
120
|
+
ctx.write(`<!--${HYDRATION_BOUNDARY_MARKER}-->`)
|
|
121
|
+
renderToStream_internal(ctx, children, el, idx)
|
|
122
|
+
ctx.write(`<!--/${HYDRATION_BOUNDARY_MARKER}-->`)
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (type === $ERROR_BOUNDARY) {
|
|
127
|
+
let boundaryBuffer = ""
|
|
128
|
+
const localPromises = new Set<Kiru.StatefulPromise<unknown>>()
|
|
129
|
+
|
|
130
|
+
const boundaryCtx: ServerRenderContext = {
|
|
131
|
+
write(chunk) {
|
|
132
|
+
boundaryBuffer += chunk
|
|
133
|
+
},
|
|
134
|
+
queuePendingData(data) {
|
|
135
|
+
data.forEach((p) => localPromises.add(p))
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
renderToStream_internal(boundaryCtx, children, el, idx)
|
|
141
|
+
// flush successful render
|
|
142
|
+
ctx.write(boundaryBuffer)
|
|
143
|
+
// merge local promises into global queue
|
|
144
|
+
ctx.queuePendingData([...localPromises])
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (isSuspenseThrowValue(error)) {
|
|
147
|
+
throw error
|
|
148
|
+
}
|
|
149
|
+
const e = error instanceof Error ? error : new Error(String(error))
|
|
150
|
+
const { fallback, onError } = props as ErrorBoundaryNode["props"]
|
|
151
|
+
onError?.(e)
|
|
152
|
+
const fallbackContent =
|
|
153
|
+
typeof fallback === "function" ? fallback(e) : fallback
|
|
154
|
+
renderToStream_internal(ctx, fallbackContent, el, 0)
|
|
155
|
+
}
|
|
73
156
|
return
|
|
74
157
|
}
|
|
75
|
-
|
|
158
|
+
|
|
159
|
+
// other exotic types
|
|
160
|
+
return renderToStream_internal(ctx, children, el, idx)
|
|
76
161
|
}
|
|
77
162
|
|
|
78
163
|
if (typeof type !== "string") {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
164
|
+
try {
|
|
165
|
+
hookIndex.current = 0
|
|
166
|
+
node.current = el
|
|
167
|
+
const res = type(props)
|
|
168
|
+
return renderToStream_internal(ctx, res, el, idx)
|
|
169
|
+
} catch (error) {
|
|
170
|
+
if (isSuspenseThrowValue(error)) {
|
|
171
|
+
const { fallback, pendingData } = error
|
|
172
|
+
if (pendingData) ctx.queuePendingData(pendingData)
|
|
173
|
+
renderToStream_internal(ctx, fallback, el, 0)
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
throw error
|
|
177
|
+
} finally {
|
|
178
|
+
node.current = null
|
|
179
|
+
}
|
|
83
180
|
}
|
|
84
181
|
|
|
85
|
-
if (__DEV__)
|
|
86
|
-
assertValidElementProps(el)
|
|
87
|
-
}
|
|
182
|
+
if (__DEV__) assertValidElementProps(el)
|
|
88
183
|
const attrs = propsToElementAttributes(props)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (!voidElements.has(type)) {
|
|
92
|
-
if ("innerHTML" in props) {
|
|
93
|
-
stream.push(
|
|
94
|
-
String(
|
|
95
|
-
Signal.isSignal(props.innerHTML)
|
|
96
|
-
? props.innerHTML.peek()
|
|
97
|
-
: props.innerHTML
|
|
98
|
-
)
|
|
99
|
-
)
|
|
100
|
-
} else {
|
|
101
|
-
if (Array.isArray(children)) {
|
|
102
|
-
children.forEach((c, i) => renderToStream_internal(stream, c, el, i))
|
|
103
|
-
} else {
|
|
104
|
-
renderToStream_internal(stream, children, el, 0)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
184
|
+
ctx.write(`<${type}${attrs.length ? ` ${attrs}` : ""}>`)
|
|
107
185
|
|
|
108
|
-
|
|
186
|
+
if (voidElements.has(type)) return
|
|
187
|
+
|
|
188
|
+
if ("innerHTML" in props) {
|
|
189
|
+
ctx.write(
|
|
190
|
+
String(
|
|
191
|
+
Signal.isSignal(props.innerHTML)
|
|
192
|
+
? props.innerHTML.peek()
|
|
193
|
+
: props.innerHTML
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
} else if (Array.isArray(children)) {
|
|
197
|
+
children.forEach((c, i) => renderToStream_internal(ctx, c, el, i))
|
|
198
|
+
} else {
|
|
199
|
+
renderToStream_internal(ctx, children, el, 0)
|
|
109
200
|
}
|
|
201
|
+
ctx.write(`</${type}>`)
|
|
110
202
|
}
|