kiru 1.0.1 → 1.1.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/appHandle.js +2 -2
- package/dist/appHandle.js.map +1 -1
- package/dist/components/lazy.d.ts.map +1 -1
- package/dist/components/lazy.js +2 -2
- package/dist/components/lazy.js.map +1 -1
- package/dist/components/transition.js +1 -5
- package/dist/components/transition.js.map +1 -1
- package/dist/devtools.d.ts.map +1 -1
- package/dist/devtools.js +6 -2
- package/dist/devtools.js.map +1 -1
- package/dist/dom/commit.d.ts +5 -0
- package/dist/dom/commit.d.ts.map +1 -0
- package/dist/dom/commit.js +94 -0
- package/dist/dom/commit.js.map +1 -0
- package/dist/dom/focus.d.ts +4 -0
- package/dist/dom/focus.d.ts.map +1 -0
- package/dist/dom/focus.js +32 -0
- package/dist/dom/focus.js.map +1 -0
- package/dist/dom/index.d.ts +4 -0
- package/dist/dom/index.d.ts.map +1 -0
- package/dist/dom/index.js +4 -0
- package/dist/dom/index.js.map +1 -0
- package/dist/dom/nodes.d.ts +12 -0
- package/dist/dom/nodes.d.ts.map +1 -0
- package/dist/dom/nodes.js +165 -0
- package/dist/dom/nodes.js.map +1 -0
- package/dist/dom/props.d.ts +8 -0
- package/dist/dom/props.d.ts.map +1 -0
- package/dist/dom/props.js +675 -0
- package/dist/dom/props.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +2 -0
- package/dist/env.js.map +1 -1
- package/dist/globalContext.d.ts +3 -8
- package/dist/globalContext.d.ts.map +1 -1
- package/dist/globalContext.js +4 -16
- package/dist/globalContext.js.map +1 -1
- package/dist/globals.d.ts +21 -1
- package/dist/globals.d.ts.map +1 -1
- package/dist/globals.js +22 -2
- package/dist/globals.js.map +1 -1
- package/dist/hmr.d.ts +17 -2
- package/dist/hmr.d.ts.map +1 -1
- package/dist/hmr.js +31 -5
- package/dist/hmr.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/onBeforeMount.d.ts +1 -1
- package/dist/hooks/onBeforeMount.d.ts.map +1 -1
- package/dist/hooks/onBeforeMount.js +10 -3
- package/dist/hooks/onBeforeMount.js.map +1 -1
- package/dist/hooks/onCleanup.d.ts +1 -1
- package/dist/hooks/onCleanup.d.ts.map +1 -1
- package/dist/hooks/onCleanup.js +7 -4
- package/dist/hooks/onCleanup.js.map +1 -1
- package/dist/hooks/onMount.d.ts +2 -2
- package/dist/hooks/onMount.d.ts.map +1 -1
- package/dist/hooks/onMount.js +11 -4
- package/dist/hooks/onMount.js.map +1 -1
- package/dist/hooks/setup.d.ts +13 -0
- package/dist/hooks/setup.d.ts.map +1 -0
- package/dist/hooks/setup.js +54 -0
- package/dist/hooks/setup.js.map +1 -0
- package/dist/hooks/utils.d.ts +2 -3
- package/dist/hooks/utils.d.ts.map +1 -1
- package/dist/hooks/utils.js +9 -14
- package/dist/hooks/utils.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/reconciler.js +3 -3
- package/dist/reconciler.js.map +1 -1
- package/dist/router/head.js +2 -2
- package/dist/router/head.js.map +1 -1
- package/dist/router/pageConfig.js +2 -2
- package/dist/router/pageConfig.js.map +1 -1
- package/dist/scheduler.js +62 -57
- package/dist/scheduler.js.map +1 -1
- package/dist/signals/base.js +3 -3
- package/dist/signals/base.js.map +1 -1
- package/dist/signals/effect.d.ts.map +1 -1
- package/dist/signals/effect.js +6 -6
- package/dist/signals/effect.js.map +1 -1
- package/dist/signals/tracking.d.ts +3 -2
- package/dist/signals/tracking.d.ts.map +1 -1
- package/dist/signals/tracking.js.map +1 -1
- package/dist/statefulPromise.js +2 -2
- package/dist/statefulPromise.js.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.dom.d.ts +1 -1
- package/dist/types.dom.d.ts.map +1 -1
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +4 -1
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/vdom.d.ts +2 -2
- 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/dist/viewTransitions.d.ts.map +1 -1
- package/dist/viewTransitions.js +2 -1
- package/dist/viewTransitions.js.map +1 -1
- package/package.json +1 -1
- package/src/appHandle.ts +2 -2
- package/src/components/lazy.ts +5 -6
- package/src/components/transition.ts +2 -6
- package/src/devtools.ts +4 -2
- package/src/dom/commit.ts +133 -0
- package/src/dom/focus.ts +34 -0
- package/src/dom/index.ts +3 -0
- package/src/dom/nodes.ts +204 -0
- package/src/dom/props.ts +818 -0
- package/src/env.ts +3 -0
- package/src/globalContext.ts +7 -24
- package/src/globals.ts +25 -2
- package/src/hmr.ts +32 -5
- package/src/hooks/index.ts +1 -0
- package/src/hooks/onBeforeMount.ts +9 -3
- package/src/hooks/onCleanup.ts +10 -4
- package/src/hooks/onMount.ts +10 -4
- package/src/hooks/setup.ts +70 -0
- package/src/hooks/utils.ts +14 -19
- package/src/index.ts +4 -2
- package/src/reconciler.ts +3 -3
- package/src/router/head.ts +2 -2
- package/src/router/pageConfig.ts +2 -2
- package/src/scheduler.ts +79 -64
- package/src/signals/base.ts +3 -3
- package/src/signals/effect.ts +5 -7
- package/src/signals/tracking.ts +3 -2
- package/src/statefulPromise.ts +2 -2
- package/src/types.dom.ts +2 -2
- package/src/types.ts +7 -1
- package/src/utils/format.ts +3 -1
- package/src/utils/vdom.ts +2 -2
- package/src/viewTransitions.ts +2 -1
- package/dist/dom.d.ts +0 -10
- package/dist/dom.d.ts.map +0 -1
- package/dist/dom.js +0 -601
- package/dist/dom.js.map +0 -1
- package/src/dom.ts +0 -775
package/src/devtools.ts
CHANGED
|
@@ -2,15 +2,17 @@ import type { DebuggerEntry } from "./globalContext"
|
|
|
2
2
|
|
|
3
3
|
export namespace DevTools {
|
|
4
4
|
export const track = (signal: Kiru.Signal<unknown>, label?: string) => {
|
|
5
|
+
if (!("window" in globalThis)) return
|
|
5
6
|
window.__kiru.devtools?.track(signal, label)
|
|
6
7
|
}
|
|
7
8
|
export const untrack = (signal: Kiru.Signal<unknown>) => {
|
|
9
|
+
if (!("window" in globalThis)) return
|
|
8
10
|
window.__kiru.devtools?.untrack(signal)
|
|
9
11
|
}
|
|
10
12
|
export const subscribe = (
|
|
11
13
|
callback: (entries: Set<DebuggerEntry>) => void
|
|
12
14
|
) => {
|
|
13
|
-
if (!window.__kiru.devtools) return () => {}
|
|
14
|
-
return window.__kiru.devtools
|
|
15
|
+
if (!("window" in globalThis) || !window.__kiru.devtools) return () => {}
|
|
16
|
+
return window.__kiru.devtools.subscribe(callback)
|
|
15
17
|
}
|
|
16
18
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
traverseApply,
|
|
3
|
+
commitSnapshot,
|
|
4
|
+
getVNodeApp,
|
|
5
|
+
setRef,
|
|
6
|
+
call,
|
|
7
|
+
} from "../utils/index.js"
|
|
8
|
+
import { FLAG_PLACEMENT, FLAG_STATIC_DOM, FLAG_UPDATE } from "../constants.js"
|
|
9
|
+
import { __DEV__ } from "../env.js"
|
|
10
|
+
import { postEffectCleanups, renderMode } from "../globals.js"
|
|
11
|
+
import { isHmrUpdate } from "../hmr.js"
|
|
12
|
+
import { unmountDomProps, updateDomProps } from "./props.js"
|
|
13
|
+
import { HostNode, getDomParent, placeDom } from "./nodes.js"
|
|
14
|
+
import type { AppHandle } from "../appHandle.js"
|
|
15
|
+
import type { DomVNode, ElementVNode } from "../types.utils"
|
|
16
|
+
|
|
17
|
+
export { commitWork, commitDeletion }
|
|
18
|
+
|
|
19
|
+
type VNode = Kiru.VNode
|
|
20
|
+
|
|
21
|
+
function commitWork(vNode: VNode) {
|
|
22
|
+
if (renderMode.current === "hydrate") {
|
|
23
|
+
return traverseApply(vNode, commitSnapshot)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const host: HostNode = {
|
|
27
|
+
node: vNode.dom ? (vNode as ElementVNode) : getDomParent(vNode),
|
|
28
|
+
}
|
|
29
|
+
commitWork_impl(vNode, host, (vNode.flags & FLAG_PLACEMENT) > 0)
|
|
30
|
+
if (vNode.dom && !(vNode.flags & FLAG_STATIC_DOM)) {
|
|
31
|
+
commitDom(vNode as DomVNode, host, false)
|
|
32
|
+
}
|
|
33
|
+
commitSnapshot(vNode)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function commitWork_impl(
|
|
37
|
+
vNode: VNode,
|
|
38
|
+
currentHostNode: HostNode,
|
|
39
|
+
inheritsPlacement: boolean
|
|
40
|
+
) {
|
|
41
|
+
let child: VNode | null = vNode.child
|
|
42
|
+
while (child) {
|
|
43
|
+
if (child.dom) {
|
|
44
|
+
commitWork_impl(child, { node: child as ElementVNode }, false)
|
|
45
|
+
if (!(child.flags & FLAG_STATIC_DOM)) {
|
|
46
|
+
commitDom(child as DomVNode, currentHostNode, inheritsPlacement)
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
commitWork_impl(
|
|
50
|
+
child,
|
|
51
|
+
currentHostNode,
|
|
52
|
+
(child.flags & FLAG_PLACEMENT) > 0 || inheritsPlacement
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
commitSnapshot(child)
|
|
57
|
+
child = child.sibling
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function commitDom(
|
|
62
|
+
vNode: DomVNode,
|
|
63
|
+
hostNode: HostNode,
|
|
64
|
+
inheritsPlacement: boolean
|
|
65
|
+
) {
|
|
66
|
+
if (
|
|
67
|
+
inheritsPlacement ||
|
|
68
|
+
!vNode.dom.isConnected ||
|
|
69
|
+
vNode.flags & FLAG_PLACEMENT
|
|
70
|
+
) {
|
|
71
|
+
placeDom(vNode, hostNode)
|
|
72
|
+
}
|
|
73
|
+
// During HMR we want to fully unmount previous props (events, signal
|
|
74
|
+
// subscriptions, style listeners, refs, etc.) before applying the new ones,
|
|
75
|
+
// so that we don't merge stale props with the new shape.
|
|
76
|
+
if (__DEV__ && vNode.prev && isHmrUpdate()) {
|
|
77
|
+
const { dom, prev, cleanups } = vNode
|
|
78
|
+
unmountDomProps(vNode, dom, prev.props, cleanups)
|
|
79
|
+
vNode.prev = null
|
|
80
|
+
}
|
|
81
|
+
if (!vNode.prev || vNode.flags & FLAG_UPDATE) {
|
|
82
|
+
updateDomProps(vNode)
|
|
83
|
+
}
|
|
84
|
+
hostNode.lastChild = vNode.dom
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function commitDeletion(vNode: VNode) {
|
|
88
|
+
if (vNode === vNode.parent?.child) {
|
|
89
|
+
vNode.parent.child = vNode.sibling
|
|
90
|
+
}
|
|
91
|
+
let app: AppHandle
|
|
92
|
+
if (__DEV__) {
|
|
93
|
+
app = getVNodeApp(vNode)!
|
|
94
|
+
}
|
|
95
|
+
traverseApply(vNode, (node) => {
|
|
96
|
+
const {
|
|
97
|
+
subs,
|
|
98
|
+
cleanups,
|
|
99
|
+
dom,
|
|
100
|
+
props: { ref },
|
|
101
|
+
hooks,
|
|
102
|
+
} = node
|
|
103
|
+
|
|
104
|
+
subs?.forEach((unsub) => unsub())
|
|
105
|
+
if (cleanups) Object.values(cleanups).forEach((c) => c())
|
|
106
|
+
if (hooks) {
|
|
107
|
+
const { preCleanups, postCleanups } = hooks
|
|
108
|
+
|
|
109
|
+
postEffectCleanups.push(...postCleanups)
|
|
110
|
+
preCleanups.forEach(call)
|
|
111
|
+
preCleanups.length = postCleanups.length = 0
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (__DEV__) {
|
|
115
|
+
window.__kiru.profilingContext?.emit("removeNode", app)
|
|
116
|
+
if (dom instanceof Element) {
|
|
117
|
+
delete dom.__kiruNode
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (dom) {
|
|
122
|
+
if (dom.isConnected && !(node.flags & FLAG_STATIC_DOM)) {
|
|
123
|
+
dom.remove()
|
|
124
|
+
}
|
|
125
|
+
if (ref) {
|
|
126
|
+
setRef(ref, null)
|
|
127
|
+
}
|
|
128
|
+
delete node.dom
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
vNode.parent = null
|
|
133
|
+
}
|
package/src/dom/focus.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
let focussedElement: HTMLElement | null = null
|
|
2
|
+
|
|
3
|
+
export function captureFocus() {
|
|
4
|
+
const el = document.activeElement
|
|
5
|
+
if (el === document.body || !(el instanceof HTMLElement)) {
|
|
6
|
+
return
|
|
7
|
+
}
|
|
8
|
+
el.addEventListener("blur", placementBlurHandler)
|
|
9
|
+
focussedElement = el
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function reinstateFocus() {
|
|
13
|
+
if (focussedElement) {
|
|
14
|
+
focussedElement.removeEventListener("blur", placementBlurHandler)
|
|
15
|
+
if (focussedElement.isConnected) focussedElement.focus()
|
|
16
|
+
focussedElement = null
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function wrapFocusEventHandler(callback: (event: FocusEvent) => void) {
|
|
21
|
+
return (event: FocusEvent) => {
|
|
22
|
+
if (focussedElement) {
|
|
23
|
+
event.preventDefault()
|
|
24
|
+
event.stopPropagation()
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
callback(event)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function placementBlurHandler(event: FocusEvent) {
|
|
32
|
+
event.preventDefault()
|
|
33
|
+
event.stopPropagation()
|
|
34
|
+
}
|
package/src/dom/index.ts
ADDED
package/src/dom/nodes.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { svgTags, FLAG_PLACEMENT, FLAG_STATIC_DOM } from "../constants.js"
|
|
2
|
+
import { Signal } from "../signals/base.js"
|
|
3
|
+
import { unwrap } from "../signals/utils.js"
|
|
4
|
+
import { hydrationStack } from "../hydration.js"
|
|
5
|
+
import {
|
|
6
|
+
getVNodeApp,
|
|
7
|
+
isValidTextChild,
|
|
8
|
+
registerVNodeCleanup,
|
|
9
|
+
} from "../utils/index.js"
|
|
10
|
+
import { KiruError } from "../error.js"
|
|
11
|
+
import { __DEV__, isBrowser } from "../env.js"
|
|
12
|
+
import type { DomVNode, ElementVNode, MaybeDom, SomeDom } from "../types.utils"
|
|
13
|
+
|
|
14
|
+
export { createDom, hydrateDom, getDomParent, placeDom }
|
|
15
|
+
|
|
16
|
+
type VNode = Kiru.VNode
|
|
17
|
+
|
|
18
|
+
export type HostNode = {
|
|
19
|
+
node: ElementVNode
|
|
20
|
+
lastChild?: SomeDom
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function createDom(vNode: DomVNode): SomeDom {
|
|
24
|
+
const t = vNode.type
|
|
25
|
+
const dom =
|
|
26
|
+
t == "#text"
|
|
27
|
+
? createTextNode(vNode)
|
|
28
|
+
: svgTags.has(t)
|
|
29
|
+
? document.createElementNS("http://www.w3.org/2000/svg", t)
|
|
30
|
+
: document.createElement(t)
|
|
31
|
+
|
|
32
|
+
return dom
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function hydrateDom(vNode: VNode) {
|
|
36
|
+
const dom =
|
|
37
|
+
vNode.type === "#text"
|
|
38
|
+
? getOrCreateTextNode(vNode)
|
|
39
|
+
: hydrationStack.getCurrentChild()
|
|
40
|
+
|
|
41
|
+
hydrationStack.bumpChildIndex()
|
|
42
|
+
|
|
43
|
+
if (!dom) {
|
|
44
|
+
throw new KiruError({
|
|
45
|
+
message: `Hydration mismatch - no node found`,
|
|
46
|
+
vNode,
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
let nodeName = dom.nodeName
|
|
50
|
+
if (!svgTags.has(nodeName)) {
|
|
51
|
+
nodeName = nodeName.toLowerCase()
|
|
52
|
+
}
|
|
53
|
+
if ((vNode.type as string) !== nodeName) {
|
|
54
|
+
throw new KiruError({
|
|
55
|
+
message: `Hydration mismatch - expected node of type ${vNode.type.toString()} but received ${nodeName}`,
|
|
56
|
+
vNode,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
vNode.dom = dom
|
|
60
|
+
if (vNode.type !== "#text" && !(vNode.flags & FLAG_STATIC_DOM)) {
|
|
61
|
+
// updateDom is called later during commit phase
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
if (Signal.isSignal(vNode.props.nodeValue)) {
|
|
65
|
+
subTextNode(vNode, dom as Text, vNode.props.nodeValue)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let prev = vNode
|
|
69
|
+
let sibling = vNode.sibling
|
|
70
|
+
while (sibling && sibling.type === "#text") {
|
|
71
|
+
const sib = sibling
|
|
72
|
+
hydrationStack.bumpChildIndex()
|
|
73
|
+
const prevText = String(unwrap(prev.props.nodeValue) ?? "")
|
|
74
|
+
const dom = (prev.dom as Text).splitText(prevText.length)
|
|
75
|
+
sib.dom = dom
|
|
76
|
+
if (Signal.isSignal(sib.props.nodeValue)) {
|
|
77
|
+
subTextNode(sib, dom, sib.props.nodeValue)
|
|
78
|
+
}
|
|
79
|
+
prev = sibling
|
|
80
|
+
sibling = sibling.sibling
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getDomParent(vNode: VNode): ElementVNode {
|
|
85
|
+
let parentNode: VNode | null = vNode.parent
|
|
86
|
+
let parentNodeElement = parentNode?.dom
|
|
87
|
+
while (parentNode && !parentNodeElement) {
|
|
88
|
+
parentNode = parentNode.parent
|
|
89
|
+
parentNodeElement = parentNode?.dom
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!parentNodeElement || !parentNode) {
|
|
93
|
+
if (!vNode.parent && vNode.dom) {
|
|
94
|
+
return vNode as ElementVNode
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
throw new KiruError({
|
|
98
|
+
message: "No DOM parent found while attempting to place node.",
|
|
99
|
+
vNode: vNode,
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
return parentNode as ElementVNode
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function placeDom(vNode: DomVNode, hostNode: HostNode) {
|
|
106
|
+
const { node: parentVNodeWithDom, lastChild } = hostNode
|
|
107
|
+
const dom = vNode.dom
|
|
108
|
+
if (lastChild) {
|
|
109
|
+
lastChild.after(dom)
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
const nextSiblingDom = getNextSiblingDom(vNode, parentVNodeWithDom)
|
|
113
|
+
if (nextSiblingDom) {
|
|
114
|
+
parentVNodeWithDom.dom.insertBefore(dom, nextSiblingDom)
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
parentVNodeWithDom.dom.appendChild(dom)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getNextSiblingDom(vNode: VNode, parent: ElementVNode): MaybeDom {
|
|
122
|
+
let node: VNode | null = vNode
|
|
123
|
+
|
|
124
|
+
while (node) {
|
|
125
|
+
let sibling = node.sibling
|
|
126
|
+
|
|
127
|
+
while (sibling) {
|
|
128
|
+
if (!(sibling.flags & (FLAG_PLACEMENT | FLAG_STATIC_DOM))) {
|
|
129
|
+
const dom = findFirstHostDom(sibling)
|
|
130
|
+
if (dom?.isConnected) return dom
|
|
131
|
+
}
|
|
132
|
+
sibling = sibling.sibling
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
node = node.parent
|
|
136
|
+
if (!node || node.flags & FLAG_STATIC_DOM || node === parent) {
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function findFirstHostDom(vNode: VNode): MaybeDom {
|
|
145
|
+
let node: VNode | null = vNode
|
|
146
|
+
while (node) {
|
|
147
|
+
if (node.dom) return node.dom
|
|
148
|
+
if (node.flags & FLAG_STATIC_DOM) return
|
|
149
|
+
node = node.child
|
|
150
|
+
}
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function getOrCreateTextNode(vNode: VNode): MaybeDom {
|
|
155
|
+
const sig = vNode.props.nodeValue
|
|
156
|
+
if (!Signal.isSignal(sig)) {
|
|
157
|
+
return hydrationStack.getCurrentChild()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const value = sig.peek()
|
|
161
|
+
if (isValidTextChild(value)) {
|
|
162
|
+
return hydrationStack.getCurrentChild()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const dom = createSignalTextNode(vNode, sig)
|
|
166
|
+
const currentChild = hydrationStack.getCurrentChild()
|
|
167
|
+
|
|
168
|
+
if (!currentChild) {
|
|
169
|
+
return hydrationStack.getCurrentParent().appendChild(dom)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
currentChild.before(dom)
|
|
173
|
+
return dom
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function subTextNode(vNode: VNode, textNode: Text, signal: Signal<string>) {
|
|
177
|
+
const cleanup = signal.subscribe((value, prev) => {
|
|
178
|
+
if (value === prev) return
|
|
179
|
+
textNode.nodeValue = value
|
|
180
|
+
if (__DEV__ && isBrowser) {
|
|
181
|
+
window.__kiru?.profilingContext?.emit(
|
|
182
|
+
"signalTextUpdate",
|
|
183
|
+
getVNodeApp(vNode)!
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
registerVNodeCleanup(vNode, "nodeValue", cleanup)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function createTextNode(vNode: VNode): Text {
|
|
191
|
+
const { nodeValue } = vNode.props
|
|
192
|
+
if (Signal.isSignal(nodeValue)) {
|
|
193
|
+
return createSignalTextNode(vNode, nodeValue)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return document.createTextNode(nodeValue)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function createSignalTextNode(vNode: VNode, nodeValue: Signal<string>): Text {
|
|
200
|
+
const value = nodeValue.peek() ?? ""
|
|
201
|
+
const textNode = document.createTextNode(value)
|
|
202
|
+
subTextNode(vNode, textNode, nodeValue)
|
|
203
|
+
return textNode
|
|
204
|
+
}
|