kiru 1.0.1 → 1.1.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.
Files changed (145) hide show
  1. package/dist/appHandle.js +2 -2
  2. package/dist/appHandle.js.map +1 -1
  3. package/dist/components/lazy.d.ts.map +1 -1
  4. package/dist/components/lazy.js +2 -2
  5. package/dist/components/lazy.js.map +1 -1
  6. package/dist/components/transition.js +1 -5
  7. package/dist/components/transition.js.map +1 -1
  8. package/dist/devtools.d.ts.map +1 -1
  9. package/dist/devtools.js +6 -2
  10. package/dist/devtools.js.map +1 -1
  11. package/dist/dom/commit.d.ts +5 -0
  12. package/dist/dom/commit.d.ts.map +1 -0
  13. package/dist/dom/commit.js +94 -0
  14. package/dist/dom/commit.js.map +1 -0
  15. package/dist/dom/focus.d.ts +4 -0
  16. package/dist/dom/focus.d.ts.map +1 -0
  17. package/dist/dom/focus.js +32 -0
  18. package/dist/dom/focus.js.map +1 -0
  19. package/dist/dom/index.d.ts +4 -0
  20. package/dist/dom/index.d.ts.map +1 -0
  21. package/dist/dom/index.js +4 -0
  22. package/dist/dom/index.js.map +1 -0
  23. package/dist/dom/nodes.d.ts +12 -0
  24. package/dist/dom/nodes.d.ts.map +1 -0
  25. package/dist/dom/nodes.js +166 -0
  26. package/dist/dom/nodes.js.map +1 -0
  27. package/dist/dom/props.d.ts +8 -0
  28. package/dist/dom/props.d.ts.map +1 -0
  29. package/dist/dom/props.js +675 -0
  30. package/dist/dom/props.js.map +1 -0
  31. package/dist/env.d.ts +2 -0
  32. package/dist/env.d.ts.map +1 -1
  33. package/dist/env.js +2 -0
  34. package/dist/env.js.map +1 -1
  35. package/dist/globalContext.d.ts +3 -8
  36. package/dist/globalContext.d.ts.map +1 -1
  37. package/dist/globalContext.js +4 -16
  38. package/dist/globalContext.js.map +1 -1
  39. package/dist/globals.d.ts +21 -1
  40. package/dist/globals.d.ts.map +1 -1
  41. package/dist/globals.js +22 -2
  42. package/dist/globals.js.map +1 -1
  43. package/dist/hmr.d.ts +17 -2
  44. package/dist/hmr.d.ts.map +1 -1
  45. package/dist/hmr.js +31 -5
  46. package/dist/hmr.js.map +1 -1
  47. package/dist/hooks/index.d.ts +1 -0
  48. package/dist/hooks/index.d.ts.map +1 -1
  49. package/dist/hooks/index.js +1 -0
  50. package/dist/hooks/index.js.map +1 -1
  51. package/dist/hooks/onBeforeMount.d.ts +1 -1
  52. package/dist/hooks/onBeforeMount.d.ts.map +1 -1
  53. package/dist/hooks/onBeforeMount.js +10 -3
  54. package/dist/hooks/onBeforeMount.js.map +1 -1
  55. package/dist/hooks/onCleanup.d.ts +1 -1
  56. package/dist/hooks/onCleanup.d.ts.map +1 -1
  57. package/dist/hooks/onCleanup.js +7 -4
  58. package/dist/hooks/onCleanup.js.map +1 -1
  59. package/dist/hooks/onMount.d.ts +2 -2
  60. package/dist/hooks/onMount.d.ts.map +1 -1
  61. package/dist/hooks/onMount.js +11 -4
  62. package/dist/hooks/onMount.js.map +1 -1
  63. package/dist/hooks/setup.d.ts +13 -0
  64. package/dist/hooks/setup.d.ts.map +1 -0
  65. package/dist/hooks/setup.js +54 -0
  66. package/dist/hooks/setup.js.map +1 -0
  67. package/dist/hooks/utils.d.ts +2 -3
  68. package/dist/hooks/utils.d.ts.map +1 -1
  69. package/dist/hooks/utils.js +9 -14
  70. package/dist/hooks/utils.js.map +1 -1
  71. package/dist/index.d.ts +1 -0
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +4 -3
  74. package/dist/index.js.map +1 -1
  75. package/dist/reconciler.js +3 -3
  76. package/dist/reconciler.js.map +1 -1
  77. package/dist/router/head.js +2 -2
  78. package/dist/router/head.js.map +1 -1
  79. package/dist/router/pageConfig.js +2 -2
  80. package/dist/router/pageConfig.js.map +1 -1
  81. package/dist/scheduler.js +62 -57
  82. package/dist/scheduler.js.map +1 -1
  83. package/dist/signals/base.js +3 -3
  84. package/dist/signals/base.js.map +1 -1
  85. package/dist/signals/effect.d.ts.map +1 -1
  86. package/dist/signals/effect.js +6 -6
  87. package/dist/signals/effect.js.map +1 -1
  88. package/dist/signals/tracking.d.ts +3 -2
  89. package/dist/signals/tracking.d.ts.map +1 -1
  90. package/dist/signals/tracking.js.map +1 -1
  91. package/dist/statefulPromise.js +2 -2
  92. package/dist/statefulPromise.js.map +1 -1
  93. package/dist/types.d.ts +5 -1
  94. package/dist/types.d.ts.map +1 -1
  95. package/dist/types.dom.d.ts +1 -1
  96. package/dist/types.dom.d.ts.map +1 -1
  97. package/dist/utils/format.d.ts.map +1 -1
  98. package/dist/utils/format.js +4 -1
  99. package/dist/utils/format.js.map +1 -1
  100. package/dist/utils/vdom.d.ts +2 -2
  101. package/dist/utils/vdom.d.ts.map +1 -1
  102. package/dist/utils/vdom.js +2 -2
  103. package/dist/utils/vdom.js.map +1 -1
  104. package/dist/viewTransitions.d.ts.map +1 -1
  105. package/dist/viewTransitions.js +2 -1
  106. package/dist/viewTransitions.js.map +1 -1
  107. package/package.json +1 -1
  108. package/src/appHandle.ts +2 -2
  109. package/src/components/lazy.ts +5 -6
  110. package/src/components/transition.ts +2 -6
  111. package/src/devtools.ts +4 -2
  112. package/src/dom/commit.ts +133 -0
  113. package/src/dom/focus.ts +34 -0
  114. package/src/dom/index.ts +3 -0
  115. package/src/dom/nodes.ts +205 -0
  116. package/src/dom/props.ts +818 -0
  117. package/src/env.ts +3 -0
  118. package/src/globalContext.ts +7 -24
  119. package/src/globals.ts +25 -2
  120. package/src/hmr.ts +32 -5
  121. package/src/hooks/index.ts +1 -0
  122. package/src/hooks/onBeforeMount.ts +9 -3
  123. package/src/hooks/onCleanup.ts +10 -4
  124. package/src/hooks/onMount.ts +10 -4
  125. package/src/hooks/setup.ts +70 -0
  126. package/src/hooks/utils.ts +14 -19
  127. package/src/index.ts +4 -2
  128. package/src/reconciler.ts +3 -3
  129. package/src/router/head.ts +2 -2
  130. package/src/router/pageConfig.ts +2 -2
  131. package/src/scheduler.ts +79 -64
  132. package/src/signals/base.ts +3 -3
  133. package/src/signals/effect.ts +5 -7
  134. package/src/signals/tracking.ts +3 -2
  135. package/src/statefulPromise.ts +2 -2
  136. package/src/types.dom.ts +2 -2
  137. package/src/types.ts +7 -1
  138. package/src/utils/format.ts +3 -1
  139. package/src/utils/vdom.ts +2 -2
  140. package/src/viewTransitions.ts +2 -1
  141. package/dist/dom.d.ts +0 -10
  142. package/dist/dom.d.ts.map +0 -1
  143. package/dist/dom.js +0 -601
  144. package/dist/dom.js.map +0 -1
  145. 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?.subscribe(callback)
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
+ }
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ export { commitDeletion, commitWork } from "./commit.js"
2
+ export { reinstateFocus, captureFocus } from "./focus.js"
3
+ export { createDom, hydrateDom } from "./nodes.js"
@@ -0,0 +1,205 @@
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
+ import { updateDomProps } from "./props.js"
14
+
15
+ export { createDom, hydrateDom, getDomParent, placeDom }
16
+
17
+ type VNode = Kiru.VNode
18
+
19
+ export type HostNode = {
20
+ node: ElementVNode
21
+ lastChild?: SomeDom
22
+ }
23
+
24
+ function createDom(vNode: DomVNode): SomeDom {
25
+ const t = vNode.type
26
+ const dom =
27
+ t == "#text"
28
+ ? createTextNode(vNode)
29
+ : svgTags.has(t)
30
+ ? document.createElementNS("http://www.w3.org/2000/svg", t)
31
+ : document.createElement(t)
32
+
33
+ return dom
34
+ }
35
+
36
+ function hydrateDom(vNode: VNode) {
37
+ const dom =
38
+ vNode.type === "#text"
39
+ ? getOrCreateTextNode(vNode)
40
+ : hydrationStack.getCurrentChild()
41
+
42
+ hydrationStack.bumpChildIndex()
43
+
44
+ if (!dom) {
45
+ throw new KiruError({
46
+ message: `Hydration mismatch - no node found`,
47
+ vNode,
48
+ })
49
+ }
50
+ let nodeName = dom.nodeName
51
+ if (!svgTags.has(nodeName)) {
52
+ nodeName = nodeName.toLowerCase()
53
+ }
54
+ if ((vNode.type as string) !== nodeName) {
55
+ throw new KiruError({
56
+ message: `Hydration mismatch - expected node of type ${vNode.type.toString()} but received ${nodeName}`,
57
+ vNode,
58
+ })
59
+ }
60
+ vNode.dom = dom
61
+ if (vNode.type !== "#text" && !(vNode.flags & FLAG_STATIC_DOM)) {
62
+ updateDomProps(vNode as DomVNode)
63
+ return
64
+ }
65
+ if (Signal.isSignal(vNode.props.nodeValue)) {
66
+ subTextNode(vNode, dom as Text, vNode.props.nodeValue)
67
+ }
68
+
69
+ let prev = vNode
70
+ let sibling = vNode.sibling
71
+ while (sibling && sibling.type === "#text") {
72
+ const sib = sibling
73
+ hydrationStack.bumpChildIndex()
74
+ const prevText = String(unwrap(prev.props.nodeValue) ?? "")
75
+ const dom = (prev.dom as Text).splitText(prevText.length)
76
+ sib.dom = dom
77
+ if (Signal.isSignal(sib.props.nodeValue)) {
78
+ subTextNode(sib, dom, sib.props.nodeValue)
79
+ }
80
+ prev = sibling
81
+ sibling = sibling.sibling
82
+ }
83
+ }
84
+
85
+ function getDomParent(vNode: VNode): ElementVNode {
86
+ let parentNode: VNode | null = vNode.parent
87
+ let parentNodeElement = parentNode?.dom
88
+ while (parentNode && !parentNodeElement) {
89
+ parentNode = parentNode.parent
90
+ parentNodeElement = parentNode?.dom
91
+ }
92
+
93
+ if (!parentNodeElement || !parentNode) {
94
+ if (!vNode.parent && vNode.dom) {
95
+ return vNode as ElementVNode
96
+ }
97
+
98
+ throw new KiruError({
99
+ message: "No DOM parent found while attempting to place node.",
100
+ vNode: vNode,
101
+ })
102
+ }
103
+ return parentNode as ElementVNode
104
+ }
105
+
106
+ function placeDom(vNode: DomVNode, hostNode: HostNode) {
107
+ const { node: parentVNodeWithDom, lastChild } = hostNode
108
+ const dom = vNode.dom
109
+ if (lastChild) {
110
+ lastChild.after(dom)
111
+ return
112
+ }
113
+ const nextSiblingDom = getNextSiblingDom(vNode, parentVNodeWithDom)
114
+ if (nextSiblingDom) {
115
+ parentVNodeWithDom.dom.insertBefore(dom, nextSiblingDom)
116
+ return
117
+ }
118
+
119
+ parentVNodeWithDom.dom.appendChild(dom)
120
+ }
121
+
122
+ function getNextSiblingDom(vNode: VNode, parent: ElementVNode): MaybeDom {
123
+ let node: VNode | null = vNode
124
+
125
+ while (node) {
126
+ let sibling = node.sibling
127
+
128
+ while (sibling) {
129
+ if (!(sibling.flags & (FLAG_PLACEMENT | FLAG_STATIC_DOM))) {
130
+ const dom = findFirstHostDom(sibling)
131
+ if (dom?.isConnected) return dom
132
+ }
133
+ sibling = sibling.sibling
134
+ }
135
+
136
+ node = node.parent
137
+ if (!node || node.flags & FLAG_STATIC_DOM || node === parent) {
138
+ return
139
+ }
140
+ }
141
+
142
+ return
143
+ }
144
+
145
+ function findFirstHostDom(vNode: VNode): MaybeDom {
146
+ let node: VNode | null = vNode
147
+ while (node) {
148
+ if (node.dom) return node.dom
149
+ if (node.flags & FLAG_STATIC_DOM) return
150
+ node = node.child
151
+ }
152
+ return
153
+ }
154
+
155
+ function getOrCreateTextNode(vNode: VNode): MaybeDom {
156
+ const sig = vNode.props.nodeValue
157
+ if (!Signal.isSignal(sig)) {
158
+ return hydrationStack.getCurrentChild()
159
+ }
160
+
161
+ const value = sig.peek()
162
+ if (isValidTextChild(value)) {
163
+ return hydrationStack.getCurrentChild()
164
+ }
165
+
166
+ const dom = createSignalTextNode(vNode, sig)
167
+ const currentChild = hydrationStack.getCurrentChild()
168
+
169
+ if (!currentChild) {
170
+ return hydrationStack.getCurrentParent().appendChild(dom)
171
+ }
172
+
173
+ currentChild.before(dom)
174
+ return dom
175
+ }
176
+
177
+ function subTextNode(vNode: VNode, textNode: Text, signal: Signal<string>) {
178
+ const cleanup = signal.subscribe((value, prev) => {
179
+ if (value === prev) return
180
+ textNode.nodeValue = value
181
+ if (__DEV__ && isBrowser) {
182
+ window.__kiru?.profilingContext?.emit(
183
+ "signalTextUpdate",
184
+ getVNodeApp(vNode)!
185
+ )
186
+ }
187
+ })
188
+ registerVNodeCleanup(vNode, "nodeValue", cleanup)
189
+ }
190
+
191
+ function createTextNode(vNode: VNode): Text {
192
+ const { nodeValue } = vNode.props
193
+ if (Signal.isSignal(nodeValue)) {
194
+ return createSignalTextNode(vNode, nodeValue)
195
+ }
196
+
197
+ return document.createTextNode(nodeValue)
198
+ }
199
+
200
+ function createSignalTextNode(vNode: VNode, nodeValue: Signal<string>): Text {
201
+ const value = nodeValue.peek() ?? ""
202
+ const textNode = document.createTextNode(value)
203
+ subTextNode(vNode, textNode, nodeValue)
204
+ return textNode
205
+ }