kiru 0.54.4 → 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.
Files changed (381) hide show
  1. package/dist/{appContext.d.ts → appHandle.d.ts} +4 -4
  2. package/dist/appHandle.d.ts.map +1 -0
  3. package/dist/{appContext.js → appHandle.js} +13 -10
  4. package/dist/appHandle.js.map +1 -0
  5. package/dist/components/derive.d.ts +10 -8
  6. package/dist/components/derive.d.ts.map +1 -1
  7. package/dist/components/derive.js +50 -47
  8. package/dist/components/derive.js.map +1 -1
  9. package/dist/components/index.d.ts +0 -1
  10. package/dist/components/index.d.ts.map +1 -1
  11. package/dist/components/index.js +0 -1
  12. package/dist/components/index.js.map +1 -1
  13. package/dist/components/lazy.d.ts.map +1 -1
  14. package/dist/components/lazy.js +7 -6
  15. package/dist/components/lazy.js.map +1 -1
  16. package/dist/components/portal.d.ts.map +1 -1
  17. package/dist/components/portal.js +2 -3
  18. package/dist/components/portal.js.map +1 -1
  19. package/dist/components/transition.d.ts +3 -2
  20. package/dist/components/transition.d.ts.map +1 -1
  21. package/dist/components/transition.js +29 -30
  22. package/dist/components/transition.js.map +1 -1
  23. package/dist/constants.d.ts +1 -5
  24. package/dist/constants.d.ts.map +1 -1
  25. package/dist/constants.js +1 -5
  26. package/dist/constants.js.map +1 -1
  27. package/dist/context.d.ts +1 -1
  28. package/dist/context.d.ts.map +1 -1
  29. package/dist/context.js +25 -19
  30. package/dist/context.js.map +1 -1
  31. package/dist/devtools.d.ts +7 -0
  32. package/dist/devtools.d.ts.map +1 -0
  33. package/dist/devtools.js +19 -0
  34. package/dist/devtools.js.map +1 -0
  35. package/dist/dom/commit.d.ts +5 -0
  36. package/dist/dom/commit.d.ts.map +1 -0
  37. package/dist/dom/commit.js +94 -0
  38. package/dist/dom/commit.js.map +1 -0
  39. package/dist/dom/focus.d.ts +4 -0
  40. package/dist/dom/focus.d.ts.map +1 -0
  41. package/dist/dom/focus.js +32 -0
  42. package/dist/dom/focus.js.map +1 -0
  43. package/dist/dom/index.d.ts +4 -0
  44. package/dist/dom/index.d.ts.map +1 -0
  45. package/dist/dom/index.js +4 -0
  46. package/dist/dom/index.js.map +1 -0
  47. package/dist/dom/nodes.d.ts +12 -0
  48. package/dist/dom/nodes.d.ts.map +1 -0
  49. package/dist/dom/nodes.js +165 -0
  50. package/dist/dom/nodes.js.map +1 -0
  51. package/dist/dom/props.d.ts +8 -0
  52. package/dist/dom/props.d.ts.map +1 -0
  53. package/dist/dom/props.js +675 -0
  54. package/dist/dom/props.js.map +1 -0
  55. package/dist/env.d.ts +2 -0
  56. package/dist/env.d.ts.map +1 -1
  57. package/dist/env.js +2 -0
  58. package/dist/env.js.map +1 -1
  59. package/dist/globalContext.d.ts +17 -23
  60. package/dist/globalContext.d.ts.map +1 -1
  61. package/dist/globalContext.js +31 -53
  62. package/dist/globalContext.js.map +1 -1
  63. package/dist/globals.d.ts +21 -4
  64. package/dist/globals.d.ts.map +1 -1
  65. package/dist/globals.js +22 -5
  66. package/dist/globals.js.map +1 -1
  67. package/dist/headlessRender.d.ts +6 -0
  68. package/dist/headlessRender.d.ts.map +1 -0
  69. package/dist/{recursiveRender.js → headlessRender.js} +17 -16
  70. package/dist/headlessRender.js.map +1 -0
  71. package/dist/hmr.d.ts +21 -8
  72. package/dist/hmr.d.ts.map +1 -1
  73. package/dist/hmr.js +58 -37
  74. package/dist/hmr.js.map +1 -1
  75. package/dist/hooks/index.d.ts +4 -14
  76. package/dist/hooks/index.d.ts.map +1 -1
  77. package/dist/hooks/index.js +4 -14
  78. package/dist/hooks/index.js.map +1 -1
  79. package/dist/hooks/onBeforeMount.d.ts +9 -0
  80. package/dist/hooks/onBeforeMount.d.ts.map +1 -0
  81. package/dist/hooks/onBeforeMount.js +19 -0
  82. package/dist/hooks/onBeforeMount.js.map +1 -0
  83. package/dist/hooks/onCleanup.d.ts +8 -0
  84. package/dist/hooks/onCleanup.d.ts.map +1 -0
  85. package/dist/hooks/onCleanup.js +18 -0
  86. package/dist/hooks/onCleanup.js.map +1 -0
  87. package/dist/hooks/onMount.d.ts +9 -0
  88. package/dist/hooks/onMount.d.ts.map +1 -0
  89. package/dist/hooks/onMount.js +19 -0
  90. package/dist/hooks/onMount.js.map +1 -0
  91. package/dist/hooks/setup.d.ts +13 -0
  92. package/dist/hooks/setup.d.ts.map +1 -0
  93. package/dist/hooks/setup.js +54 -0
  94. package/dist/hooks/setup.js.map +1 -0
  95. package/dist/hooks/utils.d.ts +2 -63
  96. package/dist/hooks/utils.d.ts.map +1 -1
  97. package/dist/hooks/utils.js +17 -144
  98. package/dist/hooks/utils.js.map +1 -1
  99. package/dist/index.d.ts +9 -4
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +11 -8
  102. package/dist/index.js.map +1 -1
  103. package/dist/profiling.d.ts +15 -14
  104. package/dist/profiling.d.ts.map +1 -1
  105. package/dist/profiling.js +9 -4
  106. package/dist/profiling.js.map +1 -1
  107. package/dist/reconciler.d.ts.map +1 -1
  108. package/dist/reconciler.js +15 -28
  109. package/dist/reconciler.js.map +1 -1
  110. package/dist/ref.d.ts +4 -0
  111. package/dist/ref.d.ts.map +1 -0
  112. package/dist/ref.js +4 -0
  113. package/dist/ref.js.map +1 -0
  114. package/dist/renderToString.js +1 -1
  115. package/dist/renderToString.js.map +1 -1
  116. package/dist/router/context.d.ts.map +1 -1
  117. package/dist/router/context.js +1 -2
  118. package/dist/router/context.js.map +1 -1
  119. package/dist/router/fileRouter.d.ts +1 -1
  120. package/dist/router/fileRouter.d.ts.map +1 -1
  121. package/dist/router/fileRouter.js +17 -11
  122. package/dist/router/fileRouter.js.map +1 -1
  123. package/dist/router/fileRouterController.d.ts.map +1 -1
  124. package/dist/router/fileRouterController.js +68 -55
  125. package/dist/router/fileRouterController.js.map +1 -1
  126. package/dist/router/head.js +2 -2
  127. package/dist/router/head.js.map +1 -1
  128. package/dist/router/link.d.ts.map +1 -1
  129. package/dist/router/link.js +19 -23
  130. package/dist/router/link.js.map +1 -1
  131. package/dist/router/pageConfig.js +2 -2
  132. package/dist/router/pageConfig.js.map +1 -1
  133. package/dist/router/server/index.d.ts.map +1 -1
  134. package/dist/router/server/index.js +14 -11
  135. package/dist/router/server/index.js.map +1 -1
  136. package/dist/router/types.d.ts +11 -6
  137. package/dist/router/types.d.ts.map +1 -1
  138. package/dist/scheduler.d.ts +1 -0
  139. package/dist/scheduler.d.ts.map +1 -1
  140. package/dist/scheduler.js +91 -73
  141. package/dist/scheduler.js.map +1 -1
  142. package/dist/signals/base.d.ts +0 -1
  143. package/dist/signals/base.d.ts.map +1 -1
  144. package/dist/signals/base.js +14 -37
  145. package/dist/signals/base.js.map +1 -1
  146. package/dist/signals/computed.d.ts +0 -2
  147. package/dist/signals/computed.d.ts.map +1 -1
  148. package/dist/signals/computed.js +1 -40
  149. package/dist/signals/computed.js.map +1 -1
  150. package/dist/signals/effect.d.ts +15 -14
  151. package/dist/signals/effect.d.ts.map +1 -1
  152. package/dist/signals/effect.js +65 -37
  153. package/dist/signals/effect.js.map +1 -1
  154. package/dist/signals/globals.d.ts +0 -5
  155. package/dist/signals/globals.d.ts.map +1 -1
  156. package/dist/signals/globals.js +0 -6
  157. package/dist/signals/globals.js.map +1 -1
  158. package/dist/signals/index.d.ts +4 -4
  159. package/dist/signals/index.d.ts.map +1 -1
  160. package/dist/signals/index.js +4 -4
  161. package/dist/signals/index.js.map +1 -1
  162. package/dist/signals/{for.d.ts → jsx.d.ts} +8 -1
  163. package/dist/signals/jsx.d.ts.map +1 -0
  164. package/dist/signals/{for.js → jsx.js} +4 -1
  165. package/dist/signals/jsx.js.map +1 -0
  166. package/dist/signals/tracking.d.ts +24 -0
  167. package/dist/signals/tracking.d.ts.map +1 -0
  168. package/dist/signals/tracking.js +51 -0
  169. package/dist/signals/tracking.js.map +1 -0
  170. package/dist/signals/types.d.ts +1 -1
  171. package/dist/signals/types.d.ts.map +1 -1
  172. package/dist/signals/utils.d.ts +2 -1
  173. package/dist/signals/utils.d.ts.map +1 -1
  174. package/dist/signals/utils.js +9 -2
  175. package/dist/signals/utils.js.map +1 -1
  176. package/dist/ssr/client.d.ts +3 -3
  177. package/dist/ssr/client.d.ts.map +1 -1
  178. package/dist/ssr/client.js.map +1 -1
  179. package/dist/ssr/server.js +1 -1
  180. package/dist/ssr/server.js.map +1 -1
  181. package/dist/statefulPromise.d.ts +22 -0
  182. package/dist/statefulPromise.d.ts.map +1 -0
  183. package/dist/statefulPromise.js +94 -0
  184. package/dist/statefulPromise.js.map +1 -0
  185. package/dist/types.d.ts +40 -50
  186. package/dist/types.d.ts.map +1 -1
  187. package/dist/types.dom.d.ts +5 -8
  188. package/dist/types.dom.d.ts.map +1 -1
  189. package/dist/types.utils.d.ts +3 -4
  190. package/dist/types.utils.d.ts.map +1 -1
  191. package/dist/utils/format.d.ts.map +1 -1
  192. package/dist/utils/format.js +4 -1
  193. package/dist/utils/format.js.map +1 -1
  194. package/dist/utils/vdom.d.ts +8 -6
  195. package/dist/utils/vdom.d.ts.map +1 -1
  196. package/dist/utils/vdom.js +32 -9
  197. package/dist/utils/vdom.js.map +1 -1
  198. package/dist/viewTransitions.d.ts +7 -0
  199. package/dist/viewTransitions.d.ts.map +1 -0
  200. package/dist/viewTransitions.js +72 -0
  201. package/dist/viewTransitions.js.map +1 -0
  202. package/package.json +1 -1
  203. package/src/{appContext.ts → appHandle.ts} +22 -17
  204. package/src/components/derive.ts +74 -69
  205. package/src/components/index.ts +0 -1
  206. package/src/components/lazy.ts +10 -10
  207. package/src/components/portal.ts +2 -3
  208. package/src/components/transition.ts +33 -39
  209. package/src/constants.ts +0 -8
  210. package/src/context.ts +30 -23
  211. package/src/devtools.ts +18 -0
  212. package/src/dom/commit.ts +133 -0
  213. package/src/dom/focus.ts +34 -0
  214. package/src/dom/index.ts +3 -0
  215. package/src/dom/nodes.ts +204 -0
  216. package/src/dom/props.ts +818 -0
  217. package/src/env.ts +3 -0
  218. package/src/globalContext.ts +51 -85
  219. package/src/globals.ts +25 -6
  220. package/src/{recursiveRender.ts → headlessRender.ts} +18 -18
  221. package/src/hmr.ts +60 -42
  222. package/src/hooks/index.ts +4 -14
  223. package/src/hooks/onBeforeMount.ts +18 -0
  224. package/src/hooks/onCleanup.ts +21 -0
  225. package/src/hooks/onMount.ts +18 -0
  226. package/src/hooks/setup.ts +70 -0
  227. package/src/hooks/utils.ts +24 -239
  228. package/src/index.ts +17 -7
  229. package/src/profiling.ts +22 -20
  230. package/src/reconciler.ts +21 -33
  231. package/src/ref.ts +6 -0
  232. package/src/renderToString.ts +1 -1
  233. package/src/router/context.ts +1 -2
  234. package/src/router/fileRouter.ts +23 -13
  235. package/src/router/fileRouterController.ts +72 -64
  236. package/src/router/head.ts +2 -2
  237. package/src/router/link.ts +11 -25
  238. package/src/router/pageConfig.ts +2 -2
  239. package/src/router/server/index.ts +24 -13
  240. package/src/router/types.ts +15 -8
  241. package/src/scheduler.ts +116 -98
  242. package/src/signals/base.ts +13 -42
  243. package/src/signals/computed.ts +1 -62
  244. package/src/signals/effect.ts +93 -48
  245. package/src/signals/globals.ts +0 -7
  246. package/src/signals/index.ts +4 -4
  247. package/src/signals/{for.ts → jsx.ts} +10 -0
  248. package/src/signals/tracking.ts +70 -0
  249. package/src/signals/types.ts +1 -1
  250. package/src/signals/utils.ts +9 -1
  251. package/src/ssr/client.ts +4 -4
  252. package/src/ssr/server.ts +2 -2
  253. package/src/statefulPromise.ts +136 -0
  254. package/src/types.dom.ts +6 -10
  255. package/src/types.ts +51 -60
  256. package/src/types.utils.ts +3 -4
  257. package/src/utils/format.ts +3 -1
  258. package/src/utils/vdom.ts +44 -15
  259. package/src/viewTransitions.ts +89 -0
  260. package/dist/appContext.d.ts.map +0 -1
  261. package/dist/appContext.js.map +0 -1
  262. package/dist/components/memo.d.ts +0 -10
  263. package/dist/components/memo.d.ts.map +0 -1
  264. package/dist/components/memo.js +0 -23
  265. package/dist/components/memo.js.map +0 -1
  266. package/dist/dom.d.ts +0 -10
  267. package/dist/dom.d.ts.map +0 -1
  268. package/dist/dom.js +0 -634
  269. package/dist/dom.js.map +0 -1
  270. package/dist/form/index.d.ts +0 -4
  271. package/dist/form/index.d.ts.map +0 -1
  272. package/dist/form/index.js +0 -518
  273. package/dist/form/index.js.map +0 -1
  274. package/dist/form/types.d.ts +0 -122
  275. package/dist/form/types.d.ts.map +0 -1
  276. package/dist/form/types.js +0 -2
  277. package/dist/form/types.js.map +0 -1
  278. package/dist/form/utils.d.ts +0 -3
  279. package/dist/form/utils.d.ts.map +0 -1
  280. package/dist/form/utils.js +0 -16
  281. package/dist/form/utils.js.map +0 -1
  282. package/dist/hooks/useAsync.d.ts +0 -18
  283. package/dist/hooks/useAsync.d.ts.map +0 -1
  284. package/dist/hooks/useAsync.js +0 -96
  285. package/dist/hooks/useAsync.js.map +0 -1
  286. package/dist/hooks/useCallback.d.ts +0 -7
  287. package/dist/hooks/useCallback.d.ts.map +0 -1
  288. package/dist/hooks/useCallback.js +0 -30
  289. package/dist/hooks/useCallback.js.map +0 -1
  290. package/dist/hooks/useContext.d.ts +0 -7
  291. package/dist/hooks/useContext.d.ts.map +0 -1
  292. package/dist/hooks/useContext.js +0 -59
  293. package/dist/hooks/useContext.js.map +0 -1
  294. package/dist/hooks/useEffect.d.ts +0 -8
  295. package/dist/hooks/useEffect.d.ts.map +0 -1
  296. package/dist/hooks/useEffect.js +0 -34
  297. package/dist/hooks/useEffect.js.map +0 -1
  298. package/dist/hooks/useEffectEvent.d.ts +0 -8
  299. package/dist/hooks/useEffectEvent.d.ts.map +0 -1
  300. package/dist/hooks/useEffectEvent.js +0 -23
  301. package/dist/hooks/useEffectEvent.js.map +0 -1
  302. package/dist/hooks/useId.d.ts +0 -8
  303. package/dist/hooks/useId.d.ts.map +0 -1
  304. package/dist/hooks/useId.js +0 -35
  305. package/dist/hooks/useId.js.map +0 -1
  306. package/dist/hooks/useLayoutEffect.d.ts +0 -8
  307. package/dist/hooks/useLayoutEffect.d.ts.map +0 -1
  308. package/dist/hooks/useLayoutEffect.js +0 -34
  309. package/dist/hooks/useLayoutEffect.js.map +0 -1
  310. package/dist/hooks/useMemo.d.ts +0 -8
  311. package/dist/hooks/useMemo.d.ts.map +0 -1
  312. package/dist/hooks/useMemo.js +0 -31
  313. package/dist/hooks/useMemo.js.map +0 -1
  314. package/dist/hooks/usePromise.d.ts +0 -8
  315. package/dist/hooks/usePromise.d.ts.map +0 -1
  316. package/dist/hooks/usePromise.js +0 -90
  317. package/dist/hooks/usePromise.js.map +0 -1
  318. package/dist/hooks/useReducer.d.ts +0 -7
  319. package/dist/hooks/useReducer.d.ts.map +0 -1
  320. package/dist/hooks/useReducer.js +0 -44
  321. package/dist/hooks/useReducer.js.map +0 -1
  322. package/dist/hooks/useRef.d.ts +0 -10
  323. package/dist/hooks/useRef.d.ts.map +0 -1
  324. package/dist/hooks/useRef.js +0 -29
  325. package/dist/hooks/useRef.js.map +0 -1
  326. package/dist/hooks/useState.d.ts +0 -7
  327. package/dist/hooks/useState.d.ts.map +0 -1
  328. package/dist/hooks/useState.js +0 -54
  329. package/dist/hooks/useState.js.map +0 -1
  330. package/dist/hooks/useSyncExternalStore.d.ts +0 -8
  331. package/dist/hooks/useSyncExternalStore.d.ts.map +0 -1
  332. package/dist/hooks/useSyncExternalStore.js +0 -50
  333. package/dist/hooks/useSyncExternalStore.js.map +0 -1
  334. package/dist/hooks/useViewTransition.d.ts +0 -10
  335. package/dist/hooks/useViewTransition.d.ts.map +0 -1
  336. package/dist/hooks/useViewTransition.js +0 -27
  337. package/dist/hooks/useViewTransition.js.map +0 -1
  338. package/dist/recursiveRender.d.ts +0 -7
  339. package/dist/recursiveRender.d.ts.map +0 -1
  340. package/dist/recursiveRender.js.map +0 -1
  341. package/dist/signals/for.d.ts.map +0 -1
  342. package/dist/signals/for.js.map +0 -1
  343. package/dist/signals/watch.d.ts +0 -21
  344. package/dist/signals/watch.d.ts.map +0 -1
  345. package/dist/signals/watch.js +0 -86
  346. package/dist/signals/watch.js.map +0 -1
  347. package/dist/store.d.ts +0 -28
  348. package/dist/store.d.ts.map +0 -1
  349. package/dist/store.js +0 -166
  350. package/dist/store.js.map +0 -1
  351. package/dist/swr.d.ts +0 -63
  352. package/dist/swr.d.ts.map +0 -1
  353. package/dist/swr.js +0 -236
  354. package/dist/swr.js.map +0 -1
  355. package/dist/utils/promise.d.ts +0 -16
  356. package/dist/utils/promise.d.ts.map +0 -1
  357. package/dist/utils/promise.js +0 -14
  358. package/dist/utils/promise.js.map +0 -1
  359. package/src/components/memo.ts +0 -39
  360. package/src/dom.ts +0 -809
  361. package/src/form/index.ts +0 -676
  362. package/src/form/types.ts +0 -262
  363. package/src/form/utils.ts +0 -19
  364. package/src/hooks/useAsync.ts +0 -121
  365. package/src/hooks/useCallback.ts +0 -32
  366. package/src/hooks/useContext.ts +0 -79
  367. package/src/hooks/useEffect.ts +0 -40
  368. package/src/hooks/useEffectEvent.ts +0 -24
  369. package/src/hooks/useId.ts +0 -42
  370. package/src/hooks/useLayoutEffect.ts +0 -43
  371. package/src/hooks/useMemo.ts +0 -34
  372. package/src/hooks/usePromise.ts +0 -126
  373. package/src/hooks/useReducer.ts +0 -50
  374. package/src/hooks/useRef.ts +0 -40
  375. package/src/hooks/useState.ts +0 -62
  376. package/src/hooks/useSyncExternalStore.ts +0 -59
  377. package/src/hooks/useViewTransition.ts +0 -25
  378. package/src/signals/watch.ts +0 -139
  379. package/src/store.ts +0 -245
  380. package/src/swr.ts +0 -351
  381. package/src/utils/promise.ts +0 -26
package/src/dom.ts DELETED
@@ -1,809 +0,0 @@
1
- import {
2
- traverseApply,
3
- commitSnapshot,
4
- propFilters,
5
- propToHtmlAttr,
6
- getVNodeAppContext,
7
- setRef,
8
- isValidTextChild,
9
- } from "./utils/index.js"
10
- import {
11
- booleanAttributes,
12
- FLAG_PLACEMENT,
13
- FLAG_UPDATE,
14
- FLAG_STATIC_DOM,
15
- svgTags,
16
- FLAG_NOOP,
17
- EVENT_PREFIX_REGEX,
18
- } from "./constants.js"
19
- import { Signal } from "./signals/base.js"
20
- import { unwrap } from "./signals/utils.js"
21
- import { renderMode } from "./globals.js"
22
- import { hydrationStack } from "./hydration.js"
23
- import { StyleObject } from "./types.dom.js"
24
- import { __DEV__ } from "./env.js"
25
- import { KiruError } from "./error.js"
26
- import type {
27
- DomVNode,
28
- ElementVNode,
29
- MaybeDom,
30
- SomeDom,
31
- SomeElement,
32
- } from "./types.utils"
33
- import type { AppContext } from "./appContext.js"
34
-
35
- export {
36
- commitWork,
37
- onBeforeFlushDomChanges,
38
- onAfterFlushDomChanges,
39
- commitDeletion,
40
- createDom,
41
- hydrateDom,
42
- }
43
-
44
- type VNode = Kiru.VNode
45
- type HostNode = {
46
- node: ElementVNode
47
- lastChild?: SomeDom
48
- }
49
-
50
- let persistingFocus = false
51
- let didBlurActiveElement = false
52
- const placementBlurHandler = (event: Event) => {
53
- event.preventDefault()
54
- event.stopPropagation()
55
- didBlurActiveElement = true
56
- }
57
-
58
- let currentActiveElement: Element | null = null
59
- function onBeforeFlushDomChanges() {
60
- persistingFocus = true
61
- currentActiveElement = document.activeElement
62
- if (currentActiveElement && currentActiveElement !== document.body) {
63
- currentActiveElement.addEventListener("blur", placementBlurHandler)
64
- }
65
- }
66
-
67
- function onAfterFlushDomChanges() {
68
- if (didBlurActiveElement) {
69
- currentActiveElement!.removeEventListener("blur", placementBlurHandler)
70
- if (currentActiveElement!.isConnected) {
71
- ;(currentActiveElement as any).focus()
72
- }
73
- didBlurActiveElement = false
74
- }
75
- persistingFocus = false
76
- }
77
-
78
- function createDom(vNode: DomVNode): SomeDom {
79
- const t = vNode.type
80
- const dom =
81
- t == "#text"
82
- ? createTextNode(vNode)
83
- : svgTags.has(t)
84
- ? document.createElementNS("http://www.w3.org/2000/svg", t)
85
- : document.createElement(t)
86
-
87
- return dom
88
- }
89
- function createTextNode(vNode: VNode): Text {
90
- const { nodeValue } = vNode.props
91
- if (Signal.isSignal(nodeValue)) {
92
- return createSignalTextNode(vNode, nodeValue)
93
- }
94
-
95
- return document.createTextNode(nodeValue)
96
- }
97
-
98
- function createSignalTextNode(vNode: VNode, nodeValue: Signal<string>): Text {
99
- const value = nodeValue.peek() ?? ""
100
- const textNode = document.createTextNode(value)
101
- subTextNode(vNode, textNode, nodeValue)
102
- return textNode
103
- }
104
-
105
- function wrapFocusEventHandler(callback: (event: FocusEvent) => void) {
106
- return (event: FocusEvent) => {
107
- if (persistingFocus) {
108
- event.preventDefault()
109
- event.stopPropagation()
110
- return
111
- }
112
- callback(event)
113
- }
114
- }
115
-
116
- interface VNodeEventListenerObjects {
117
- [key: string]: EventListenerObject
118
- }
119
- const eventListenerObjects = new WeakMap<VNode, VNodeEventListenerObjects>()
120
-
121
- function updateDom(vNode: DomVNode) {
122
- const { dom, prev, props, cleanups } = vNode
123
- const prevProps = prev?.props ?? {}
124
- const nextProps = props ?? {}
125
- const isHydration = renderMode.current === "hydrate"
126
-
127
- // TEXT NODE SHORT-PATH
128
- if (dom instanceof Text) {
129
- const nextVal = nextProps.nodeValue
130
- if (!Signal.isSignal(nextVal) && dom.nodeValue !== nextVal) {
131
- dom.nodeValue = nextVal
132
- }
133
- return
134
- }
135
-
136
- const keys: string[] = []
137
- for (const k in prevProps) keys.push(k)
138
- for (const k in nextProps) {
139
- if (!(k in prevProps)) keys.push(k)
140
- }
141
-
142
- let events: VNodeEventListenerObjects | undefined
143
- for (let i = 0; i < keys.length; i++) {
144
- const key = keys[i]
145
- const prevVal = prevProps[key]
146
- const nextVal = nextProps[key]
147
-
148
- if (propFilters.isEvent(key)) {
149
- events ??= eventListenerObjects.get(vNode)
150
- if (!events) eventListenerObjects.set(vNode, (events = {}))
151
-
152
- if (prevVal !== nextVal || isHydration) {
153
- const evtName = key.replace(EVENT_PREFIX_REGEX, "")
154
- const evtListenerObj = events[evtName]
155
-
156
- if (!nextVal) {
157
- if (evtListenerObj) {
158
- dom.removeEventListener(evtName, evtListenerObj)
159
- delete events[evtName]
160
- }
161
- continue
162
- }
163
-
164
- let handleEvent = nextVal.bind(void 0)
165
- if (evtName === "focus" || evtName === "blur") {
166
- handleEvent = wrapFocusEventHandler(handleEvent)
167
- }
168
-
169
- if (evtListenerObj) {
170
- evtListenerObj.handleEvent = handleEvent
171
- continue
172
- }
173
-
174
- dom.addEventListener(evtName, (events[evtName] = { handleEvent }))
175
- }
176
- continue
177
- }
178
-
179
- if (propFilters.isInternalProp(key) && key !== "innerHTML") {
180
- continue
181
- }
182
-
183
- if (prevVal === nextVal) {
184
- continue
185
- }
186
-
187
- if (Signal.isSignal(prevVal) && cleanups) {
188
- const disposer = cleanups[key]
189
- if (disposer) {
190
- disposer()
191
- delete cleanups[key]
192
- }
193
- }
194
-
195
- if (Signal.isSignal(nextVal)) {
196
- setSignalProp(vNode, dom, key, nextVal, prevVal)
197
- continue
198
- }
199
-
200
- setProp(dom, key, nextVal, prevVal)
201
- }
202
-
203
- const prevRef = prevProps.ref
204
- const nextRef = nextProps.ref
205
- if (prevRef !== nextRef) {
206
- if (prevRef) setRef(prevRef, null)
207
- if (nextRef) setRef(nextRef, dom)
208
- }
209
- }
210
-
211
- function getSelectElementValue(dom: HTMLSelectElement) {
212
- if (dom.multiple) {
213
- return Array.from(dom.selectedOptions).map((option) => option.value)
214
- }
215
- return dom.value
216
- }
217
-
218
- function setSelectElementValue(dom: HTMLSelectElement, value: any) {
219
- if (!dom.multiple || value === undefined || value === null || value === "") {
220
- dom.value = value
221
- return
222
- }
223
- Array.from(dom.options).forEach((option) => {
224
- option.selected = value.indexOf(option.value) > -1
225
- })
226
- }
227
-
228
- const bindAttrToEventMap: Record<string, string> = {
229
- value: "input",
230
- checked: "change",
231
- open: "toggle",
232
- volume: "volumechange",
233
- playbackRate: "ratechange",
234
- currentTime: "timeupdate",
235
- }
236
- const numericValueInputTypes = new Set(["progress", "meter", "number", "range"])
237
-
238
- function setSignalProp(
239
- vNode: VNode,
240
- dom: Exclude<SomeDom, Text>,
241
- key: string,
242
- signal: Signal<any>,
243
- prevValue: unknown
244
- ) {
245
- const [modifier, attr] = key.split(":")
246
- const cleanups = (vNode.cleanups ??= {})
247
- if (modifier !== "bind") {
248
- cleanups[key] = signal.subscribe((value, prev) => {
249
- if (value === prev) return
250
- setProp(dom, key, value, prev)
251
- if (__DEV__) {
252
- emitSignalAttrUpdate(vNode)
253
- }
254
- })
255
- } else {
256
- const evtName = bindAttrToEventMap[attr]
257
- if (!evtName) {
258
- if (__DEV__) {
259
- console.error(
260
- `[kiru]: ${attr} is not a valid element binding attribute.`
261
- )
262
- }
263
- return
264
- }
265
- cleanups[key] = bindElementProp(vNode, dom, attr, evtName, signal)
266
- }
267
-
268
- const value = signal.peek()
269
- const prev = unwrap(prevValue)
270
- if (value !== prev) {
271
- setProp(dom, attr ?? modifier, value, prev)
272
- }
273
- }
274
-
275
- function createElementValueReader(
276
- dom: Exclude<SomeDom, Text>,
277
- signal: Signal<any>
278
- ) {
279
- if (dom instanceof HTMLInputElement) {
280
- return createInputValueReader(dom, signal)
281
- }
282
- if (dom instanceof HTMLSelectElement) {
283
- return () => getSelectElementValue(dom)
284
- }
285
- return () => (dom as any).value
286
- }
287
-
288
- function bindElementProp(
289
- vNode: VNode,
290
- dom: Exclude<SomeDom, Text>,
291
- attr: string,
292
- evtName: string,
293
- signal: Signal<any>
294
- ): () => void {
295
- const writeToSignal = (val: any) => {
296
- signal.sneak(val)
297
- signal.notify((sub) => sub !== updateFromSignal)
298
- }
299
-
300
- const writeToElement =
301
- dom instanceof HTMLSelectElement && attr === "value"
302
- ? (value: any) => setSelectElementValue(dom, value)
303
- : (value: any) => ((dom as any)[attr] = value)
304
-
305
- const updateFromSignal = (value: any) => {
306
- writeToElement(value)
307
- if (__DEV__) {
308
- emitSignalAttrUpdate(vNode)
309
- }
310
- }
311
-
312
- let evtHandler: EventListener
313
- if (attr === "value") {
314
- const readValue = createElementValueReader(dom, signal)
315
- evtHandler = () => writeToSignal(readValue())
316
- } else {
317
- evtHandler = () => {
318
- const val = (dom as any)[attr]
319
- /**
320
- * the 'timeupdate' event is fired when the currentTime property is
321
- * set (from code OR playback), so we need to prevent unnecessary
322
- * signal updates to avoid a feedback loop when there are multiple
323
- * elements with the same signal bound to 'currentTime'
324
- */
325
- if (attr === "currentTime" && signal.peek() === val) return
326
- writeToSignal(val)
327
- }
328
- }
329
-
330
- dom.addEventListener(evtName, evtHandler)
331
- const unsub = signal.subscribe(updateFromSignal)
332
-
333
- return () => {
334
- dom.removeEventListener(evtName, evtHandler)
335
- unsub()
336
- }
337
- }
338
-
339
- function createInputValueReader(
340
- dom: HTMLInputElement,
341
- signal: Signal<any>
342
- ): () => any {
343
- const t = dom.type
344
- const v = signal.peek()
345
-
346
- if (t === "date" && v instanceof Date) {
347
- return () => dom.valueAsDate
348
- }
349
-
350
- if (numericValueInputTypes.has(t) && typeof v === "number") {
351
- return () => dom.valueAsNumber
352
- }
353
-
354
- return () => dom.value
355
- }
356
-
357
- function emitSignalAttrUpdate(vNode: VNode) {
358
- window.__kiru.profilingContext?.emit(
359
- "signalAttrUpdate",
360
- getVNodeAppContext(vNode)!
361
- )
362
- }
363
-
364
- function subTextNode(vNode: VNode, textNode: Text, signal: Signal<string>) {
365
- ;(vNode.cleanups ??= {}).nodeValue = signal.subscribe((value, prev) => {
366
- if (value === prev) return
367
- textNode.nodeValue = value
368
- if (__DEV__) {
369
- window.__kiru.profilingContext?.emit(
370
- "signalTextUpdate",
371
- getVNodeAppContext(vNode)!
372
- )
373
- }
374
- })
375
- }
376
-
377
- /**
378
- * Creates and inserts an empty signal-bound text node into
379
- * the dom tree if the signal value is null or undefined.
380
- */
381
- function getOrCreateTextNode(vNode: VNode): MaybeDom {
382
- const sig = vNode.props.nodeValue
383
- if (!Signal.isSignal(sig)) {
384
- return hydrationStack.getCurrentChild()
385
- }
386
-
387
- const value = sig.peek()
388
- if (isValidTextChild(value)) {
389
- return hydrationStack.getCurrentChild()
390
- }
391
-
392
- const dom = createSignalTextNode(vNode, sig)
393
- const currentChild = hydrationStack.getCurrentChild()
394
-
395
- if (!currentChild) {
396
- return hydrationStack.getCurrentParent().appendChild(dom)
397
- }
398
-
399
- currentChild.before(dom)
400
- return dom
401
- }
402
-
403
- function hydrateDom(vNode: VNode) {
404
- const dom =
405
- vNode.type === "#text"
406
- ? getOrCreateTextNode(vNode)
407
- : hydrationStack.getCurrentChild()
408
-
409
- hydrationStack.bumpChildIndex()
410
-
411
- if (!dom) {
412
- throw new KiruError({
413
- message: `Hydration mismatch - no node found`,
414
- vNode,
415
- })
416
- }
417
- let nodeName = dom.nodeName
418
- if (!svgTags.has(nodeName)) {
419
- nodeName = nodeName.toLowerCase()
420
- }
421
- if ((vNode.type as string) !== nodeName) {
422
- throw new KiruError({
423
- message: `Hydration mismatch - expected node of type ${vNode.type.toString()} but received ${nodeName}`,
424
- vNode,
425
- })
426
- }
427
- vNode.dom = dom
428
- if (vNode.type !== "#text" && !(vNode.flags & FLAG_STATIC_DOM)) {
429
- updateDom(vNode as DomVNode)
430
- return
431
- }
432
- if (Signal.isSignal(vNode.props.nodeValue)) {
433
- subTextNode(vNode, dom as Text, vNode.props.nodeValue)
434
- }
435
-
436
- let prev = vNode
437
- let sibling = vNode.sibling
438
- while (sibling && sibling.type === "#text") {
439
- const sib = sibling
440
- hydrationStack.bumpChildIndex()
441
- const prevText = String(unwrap(prev.props.nodeValue) ?? "")
442
- const dom = (prev.dom as Text).splitText(prevText.length)
443
- sib.dom = dom
444
- if (Signal.isSignal(sib.props.nodeValue)) {
445
- subTextNode(sib, dom, sib.props.nodeValue)
446
- }
447
- prev = sibling
448
- sibling = sibling.sibling
449
- }
450
- }
451
-
452
- function handleAttributeRemoval(
453
- element: Element,
454
- key: string,
455
- value: unknown,
456
- isBoolAttr = false
457
- ) {
458
- if (value === null) {
459
- element.removeAttribute(key)
460
- return true
461
- }
462
- switch (typeof value) {
463
- case "undefined":
464
- case "function":
465
- case "symbol": {
466
- element.removeAttribute(key)
467
- return true
468
- }
469
- case "boolean": {
470
- if (isBoolAttr && !value) {
471
- element.removeAttribute(key)
472
- return true
473
- }
474
- }
475
- }
476
-
477
- return false
478
- }
479
-
480
- function setDomAttribute(element: Element, key: string, value: unknown) {
481
- const isBoolAttr = booleanAttributes.has(key)
482
-
483
- if (handleAttributeRemoval(element, key, value, isBoolAttr)) return
484
-
485
- element.setAttribute(
486
- key,
487
- isBoolAttr && typeof value === "boolean" ? "" : String(value)
488
- )
489
- }
490
-
491
- const explicitValueElementTags = ["INPUT", "TEXTAREA"]
492
-
493
- const needsExplicitValueSet = (
494
- element: SomeElement
495
- ): element is HTMLInputElement | HTMLTextAreaElement => {
496
- return explicitValueElementTags.indexOf(element.nodeName) > -1
497
- }
498
-
499
- function setProp(
500
- element: SomeElement,
501
- key: string,
502
- value: unknown,
503
- prev: unknown
504
- ) {
505
- switch (key) {
506
- case "style":
507
- return setStyleProp(element, value, prev)
508
- case "className":
509
- return setClassName(element, value)
510
- case "innerHTML":
511
- return setInnerHTML(element, value)
512
- case "muted":
513
- ;(element as HTMLMediaElement).muted = Boolean(value)
514
- return
515
- case "value":
516
- if (element.nodeName === "SELECT") {
517
- return setSelectElementValue(element as HTMLSelectElement, value)
518
- }
519
- const strVal = value === undefined || value === null ? "" : String(value)
520
- if (needsExplicitValueSet(element)) {
521
- element.value = strVal
522
- return
523
- }
524
- element.setAttribute("value", strVal)
525
- return
526
- case "checked":
527
- if (element.nodeName === "INPUT") {
528
- ;(element as HTMLInputElement).checked = Boolean(value)
529
- return
530
- }
531
- element.setAttribute("checked", String(value))
532
- return
533
- default:
534
- return setDomAttribute(element, propToHtmlAttr(key), value)
535
- }
536
- }
537
-
538
- function setInnerHTML(element: SomeElement, value: unknown) {
539
- if (value === null || value === undefined || typeof value === "boolean") {
540
- element.innerHTML = ""
541
- return
542
- }
543
- element.innerHTML = String(value)
544
- }
545
-
546
- function setClassName(element: SomeElement, value: unknown) {
547
- const val = unwrap(value)
548
- if (!val) {
549
- return element.removeAttribute("class")
550
- }
551
- element.setAttribute("class", val as string)
552
- }
553
-
554
- function setStyleProp(element: SomeElement, value: unknown, prev: unknown) {
555
- if (handleAttributeRemoval(element, "style", value)) return
556
-
557
- if (typeof value === "string") {
558
- element.setAttribute("style", value)
559
- return
560
- }
561
-
562
- let prevStyle: StyleObject = {}
563
- if (typeof prev === "string") {
564
- element.setAttribute("style", "")
565
- } else if (typeof prev === "object" && !!prev) {
566
- prevStyle = prev as StyleObject
567
- }
568
-
569
- const nextStyle = value as StyleObject
570
- const keys = new Set([
571
- ...Object.keys(prevStyle),
572
- ...Object.keys(nextStyle),
573
- ]) as Set<keyof StyleObject>
574
-
575
- keys.forEach((k) => {
576
- const prev = prevStyle[k]
577
- const next = nextStyle[k]
578
- if (prev === next) return
579
-
580
- if (next === undefined) {
581
- element.style[k as any] = ""
582
- return
583
- }
584
-
585
- element.style[k as any] = next as any
586
- })
587
- }
588
-
589
- function getDomParent(vNode: VNode): ElementVNode {
590
- let parentNode: VNode | null = vNode.parent
591
- let parentNodeElement = parentNode?.dom
592
- while (parentNode && !parentNodeElement) {
593
- parentNode = parentNode.parent
594
- parentNodeElement = parentNode?.dom
595
- }
596
-
597
- if (!parentNodeElement || !parentNode) {
598
- // handle app entry
599
- if (!vNode.parent && vNode.dom) {
600
- return vNode as ElementVNode
601
- }
602
-
603
- throw new KiruError({
604
- message: "No DOM parent found while attempting to place node.",
605
- vNode: vNode,
606
- })
607
- }
608
- return parentNode as ElementVNode
609
- }
610
-
611
- function placeDom(vNode: DomVNode, hostNode: HostNode) {
612
- const { node: parentVNodeWithDom, lastChild } = hostNode
613
- const dom = vNode.dom
614
- if (lastChild) {
615
- lastChild.after(dom)
616
- return
617
- }
618
- // TODO: we can probably skip the 'next sibling search' if we're appending
619
- const nextSiblingDom = getNextSiblingDom(vNode, parentVNodeWithDom)
620
- if (nextSiblingDom) {
621
- parentVNodeWithDom.dom.insertBefore(dom, nextSiblingDom)
622
- return
623
- }
624
-
625
- parentVNodeWithDom.dom.appendChild(dom)
626
- }
627
-
628
- function getNextSiblingDom(vNode: VNode, parent: ElementVNode): MaybeDom {
629
- let node: VNode | null = vNode
630
-
631
- while (node) {
632
- let sibling = node.sibling
633
-
634
- while (sibling) {
635
- // Skip unmounted, to-be-placed & static nodes
636
- if (!(sibling.flags & (FLAG_PLACEMENT | FLAG_STATIC_DOM))) {
637
- // Descend into the child to find host dom
638
- const dom = findFirstHostDom(sibling)
639
- if (dom?.isConnected) return dom
640
- }
641
- sibling = sibling.sibling
642
- }
643
-
644
- // Move up to parent — but don't escape portal boundary
645
- node = node.parent
646
- if (!node || node.flags & FLAG_STATIC_DOM || node === parent) {
647
- return
648
- }
649
- }
650
-
651
- return
652
- }
653
-
654
- function findFirstHostDom(vNode: VNode): MaybeDom {
655
- let node: VNode | null = vNode
656
-
657
- while (node) {
658
- if (node.dom) return node.dom
659
- if (node.flags & FLAG_STATIC_DOM) return // Don't descend into portals
660
- node = node.child
661
- }
662
- return
663
- }
664
-
665
- function commitWork(vNode: VNode) {
666
- if (renderMode.current === "hydrate") {
667
- return traverseApply(vNode, commitSnapshot)
668
- }
669
-
670
- const host: HostNode = {
671
- node: vNode.dom ? (vNode as ElementVNode) : getDomParent(vNode),
672
- }
673
- commitWork_impl(vNode, host, (vNode.flags & FLAG_PLACEMENT) > 0)
674
- if (vNode.dom && !(vNode.flags & FLAG_STATIC_DOM)) {
675
- commitDom(vNode as DomVNode, host, false)
676
- }
677
- commitSnapshot(vNode)
678
- }
679
-
680
- function commitWork_impl(
681
- vNode: VNode,
682
- currentHostNode: HostNode,
683
- inheritsPlacement: boolean
684
- ) {
685
- let child: VNode | null = vNode.child
686
- while (child) {
687
- if (child.flags & FLAG_NOOP) {
688
- if (child.flags & FLAG_PLACEMENT) {
689
- placeAndCommitNoopChildren(child, currentHostNode)
690
- }
691
- commitSnapshot(child)
692
- child = child.sibling
693
- continue
694
- }
695
-
696
- if (child.dom) {
697
- commitWork_impl(child, { node: child as ElementVNode }, false)
698
- if (!(child.flags & FLAG_STATIC_DOM)) {
699
- commitDom(child as DomVNode, currentHostNode, inheritsPlacement)
700
- }
701
- } else {
702
- commitWork_impl(
703
- child,
704
- currentHostNode,
705
- (child.flags & FLAG_PLACEMENT) > 0 || inheritsPlacement
706
- )
707
- }
708
-
709
- commitSnapshot(child)
710
- child = child.sibling
711
- }
712
- }
713
-
714
- function commitDom(
715
- vNode: DomVNode,
716
- hostNode: HostNode,
717
- inheritsPlacement: boolean
718
- ) {
719
- if (
720
- inheritsPlacement ||
721
- !vNode.dom.isConnected ||
722
- vNode.flags & FLAG_PLACEMENT
723
- ) {
724
- placeDom(vNode, hostNode)
725
- }
726
- if (!vNode.prev || vNode.flags & FLAG_UPDATE) {
727
- updateDom(vNode)
728
- }
729
- hostNode.lastChild = vNode.dom
730
- }
731
-
732
- function commitDeletion(vNode: VNode) {
733
- if (vNode === vNode.parent?.child) {
734
- vNode.parent.child = vNode.sibling
735
- }
736
- let ctx: AppContext
737
- if (__DEV__) {
738
- ctx = getVNodeAppContext(vNode)!
739
- }
740
- traverseApply(vNode, (node) => {
741
- const {
742
- hooks,
743
- subs,
744
- cleanups,
745
- dom,
746
- props: { ref },
747
- } = node
748
-
749
- subs?.forEach((unsub) => unsub())
750
- if (cleanups) Object.values(cleanups).forEach((c) => c())
751
- while (hooks?.length) hooks.pop()!.cleanup?.()
752
-
753
- if (__DEV__) {
754
- window.__kiru.profilingContext?.emit("removeNode", ctx)
755
- if (dom instanceof Element) {
756
- delete dom.__kiruNode
757
- }
758
- }
759
-
760
- if (dom) {
761
- if (dom.isConnected && !(node.flags & FLAG_STATIC_DOM)) {
762
- dom.remove()
763
- }
764
- if (ref) {
765
- setRef(ref, null)
766
- }
767
- delete node.dom
768
- }
769
- })
770
-
771
- vNode.parent = null
772
- }
773
-
774
- function placeAndCommitNoopChildren(
775
- parent: VNode,
776
- currentHostNode: HostNode
777
- ): void {
778
- if (!parent.child) return
779
-
780
- const domChildren: SomeDom[] = []
781
- collectDomNodes(parent.child, domChildren)
782
- if (domChildren.length === 0) return
783
-
784
- const { node, lastChild } = currentHostNode
785
- if (lastChild) {
786
- lastChild.after(...domChildren)
787
- } else {
788
- const nextSiblingDom = getNextSiblingDom(parent, node)
789
- const parentDom = node.dom
790
- if (nextSiblingDom) {
791
- nextSiblingDom.before(...domChildren)
792
- } else {
793
- parentDom.append(...domChildren)
794
- }
795
- }
796
- currentHostNode.lastChild = domChildren[domChildren.length - 1]
797
- }
798
-
799
- function collectDomNodes(firstChild: VNode, children: SomeDom[]): void {
800
- let child: VNode | null = firstChild
801
- while (child) {
802
- if (child.dom) {
803
- children.push(child.dom)
804
- } else if (child.child) {
805
- collectDomNodes(child.child, children)
806
- }
807
- child = child.sibling
808
- }
809
- }