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/scheduler.ts CHANGED
@@ -1,48 +1,39 @@
1
- import type {
2
- ContextProviderNode,
3
- DomVNode,
4
- ErrorBoundaryNode,
5
- FunctionVNode,
6
- } from "./types.utils"
1
+ import type { DomVNode, ErrorBoundaryNode, FunctionVNode } from "./types.utils"
7
2
  import {
8
- $CONTEXT_PROVIDER,
9
3
  $ERROR_BOUNDARY,
10
- $MEMO,
11
4
  CONSECUTIVE_DIRTY_LIMIT,
12
5
  FLAG_DELETION,
13
6
  FLAG_DIRTY,
14
- FLAG_MEMO,
15
- FLAG_NOOP,
16
7
  } from "./constants.js"
17
8
  import {
9
+ captureFocus,
18
10
  commitDeletion,
19
11
  commitWork,
20
12
  createDom,
21
13
  hydrateDom,
22
- onAfterFlushDomChanges,
23
- onBeforeFlushDomChanges,
24
- } from "./dom.js"
25
- import { __DEV__ } from "./env.js"
26
- import { KiruError } from "./error.js"
27
- import { hookIndex, node, renderMode } from "./globals.js"
28
- import { hydrationStack } from "./hydration.js"
29
- import { reconcileChildren } from "./reconciler.js"
14
+ reinstateFocus,
15
+ } from "./dom/index.js"
30
16
  import {
31
17
  assertValidElementProps,
32
18
  latest,
33
19
  traverseApply,
34
20
  isExoticType,
35
- getVNodeAppContext,
21
+ getVNodeApp,
36
22
  findParentErrorBoundary,
37
23
  call,
24
+ propsChanged,
38
25
  } from "./utils/index.js"
39
- import type { AppContext } from "./appContext"
40
- import type { MemoFn } from "./components/memo"
26
+ import { __DEV__ } from "./env.js"
27
+ import { KiruError } from "./error.js"
28
+ import { node, postEffectCleanups, renderMode, setups } from "./globals.js"
29
+ import { hydrationStack } from "./hydration.js"
30
+ import { reconcileChildren } from "./reconciler.js"
41
31
  import { isHmrUpdate } from "./hmr.js"
32
+ import type { AppHandle } from "./appHandle"
42
33
 
43
34
  type VNode = Kiru.VNode
44
35
 
45
- let appCtx: AppContext | null
36
+ let app: AppHandle | null
46
37
  let treesInProgress: VNode[] = []
47
38
  let isRunningOrQueued = false
48
39
  let nextIdleEffects: (() => void)[] = []
@@ -51,8 +42,8 @@ let isImmediateEffectsMode = false
51
42
  let immediateEffectDirtiedRender = false
52
43
  let isRenderDirtied = false
53
44
  let consecutiveDirtyCount = 0
54
- let preEffects: Array<Function> = []
55
- let postEffects: Array<Function> = []
45
+ let preEffects: Kiru.LifecycleHookCallback[] = []
46
+ let postEffects: Kiru.LifecycleHookCallback[] = []
56
47
  let animationFrameHandle = -1
57
48
 
58
49
  /**
@@ -94,21 +85,16 @@ export function requestUpdate(vNode: VNode): void {
94
85
  queueUpdate(vNode)
95
86
  }
96
87
 
97
- function queueBeginWork(): void {
98
- if (isRunningOrQueued) return
99
- isRunningOrQueued = true
100
- animationFrameHandle = window.requestAnimationFrame(doWork)
101
- }
102
-
103
- function onWorkFinished(): void {
104
- isRunningOrQueued = false
105
- while (nextIdleEffects.length) {
106
- nextIdleEffects.shift()!()
88
+ export function useRequestUpdate(): () => void {
89
+ const n = node.current
90
+ if (!n) {
91
+ throw new Error("useRequestUpdate must be called inside a Kiru component")
107
92
  }
93
+ return () => requestUpdate(n)
108
94
  }
109
95
 
110
96
  function queueUpdate(vNode: VNode): void {
111
- // In immediate effect mode (useLayoutEffect), immediately mark the render as dirty
97
+ // In immediate effect mode (onBeforeMount), immediately mark the render as dirty
112
98
  if (isImmediateEffectsMode) {
113
99
  immediateEffectDirtiedRender = true
114
100
  }
@@ -116,7 +102,7 @@ function queueUpdate(vNode: VNode): void {
116
102
  // If this node is currently being rendered, just mark it dirty
117
103
  if (node.current === vNode) {
118
104
  if (__DEV__) {
119
- window.__kiru.profilingContext?.emit("updateDirtied", appCtx!)
105
+ window.__kiru.profilingContext?.emit("updateDirtied", app!)
120
106
  }
121
107
  isRenderDirtied = true
122
108
  return
@@ -127,7 +113,12 @@ function queueUpdate(vNode: VNode): void {
127
113
 
128
114
  if (!treesInProgress.length) {
129
115
  treesInProgress.push(vNode)
130
- return queueBeginWork()
116
+
117
+ if (!isRunningOrQueued) {
118
+ isRunningOrQueued = true
119
+ animationFrameHandle = window.requestAnimationFrame(doWork)
120
+ }
121
+ return
131
122
  }
132
123
 
133
124
  treesInProgress.push(vNode)
@@ -146,16 +137,16 @@ function doWork(): void {
146
137
  if (__DEV__) {
147
138
  const n = deletions[0] ?? treesInProgress[0]
148
139
  if (n) {
149
- appCtx = getVNodeAppContext(n)!
150
- window.__kiru.profilingContext?.beginTick(appCtx)
140
+ app = getVNodeApp(n)!
141
+ window.__kiru.profilingContext?.beginTick(app)
151
142
  } else {
152
- appCtx = null
143
+ app = null
153
144
  }
154
145
  }
155
146
 
156
147
  let len = 1
157
148
 
158
- onBeforeFlushDomChanges()
149
+ captureFocus()
159
150
  while (treesInProgress.length) {
160
151
  if (treesInProgress.length > len) {
161
152
  treesInProgress.sort(depthSort)
@@ -178,7 +169,7 @@ function doWork(): void {
178
169
  currentWorkRoot.flags &= ~FLAG_DIRTY
179
170
  }
180
171
  }
181
- onAfterFlushDomChanges()
172
+ reinstateFocus()
182
173
 
183
174
  isImmediateEffectsMode = true
184
175
  flushEffects(preEffects)
@@ -190,19 +181,25 @@ function doWork(): void {
190
181
  immediateEffectDirtiedRender = false
191
182
  consecutiveDirtyCount++
192
183
  if (__DEV__) {
193
- window.__kiru.profilingContext?.endTick(appCtx!)
194
- window.__kiru.profilingContext?.emit("updateDirtied", appCtx!)
184
+ window.__kiru.profilingContext?.endTick(app!)
185
+ window.__kiru.profilingContext?.emit("updateDirtied", app!)
195
186
  }
196
187
  return flushSync()
197
188
  }
198
189
  consecutiveDirtyCount = 0
199
190
 
200
- onWorkFinished()
201
- queueMicrotask(() => flushEffects(postEffects))
191
+ isRunningOrQueued = false
192
+ while (nextIdleEffects.length) {
193
+ nextIdleEffects.shift()!()
194
+ }
195
+ queueMicrotask(() => {
196
+ flushEffects(postEffectCleanups)
197
+ flushEffects(postEffects)
198
+ })
202
199
  if (__DEV__) {
203
- window.__kiru.emit("update", appCtx!)
204
- window.__kiru.profilingContext?.emit("update", appCtx!)
205
- window.__kiru.profilingContext?.endTick(appCtx!)
200
+ window.__kiru.emit("update", app!)
201
+ window.__kiru.profilingContext?.emit("update", app!)
202
+ window.__kiru.profilingContext?.endTick(app!)
206
203
  }
207
204
  }
208
205
 
@@ -221,13 +218,12 @@ function performUnitOfWork(vNode: VNode): VNode | null {
221
218
  let nextNode: VNode | null = vNode
222
219
  while (nextNode) {
223
220
  // queue effects upon ascent
224
- if (nextNode.immediateEffects) {
225
- preEffects.push(...nextNode.immediateEffects)
226
- nextNode.immediateEffects = undefined
227
- }
228
- if (nextNode.effects) {
229
- postEffects.push(...nextNode.effects)
230
- nextNode.effects = undefined
221
+ const { hooks } = nextNode
222
+ if (hooks) {
223
+ preEffects.push(...hooks.pre)
224
+ postEffects.push(...hooks.post)
225
+ hooks.pre.length = 0
226
+ hooks.post.length = 0
231
227
  }
232
228
 
233
229
  if (nextNode === currentWorkRoot) return null
@@ -246,8 +242,13 @@ function performUnitOfWork(vNode: VNode): VNode | null {
246
242
 
247
243
  function updateVNode(vNode: VNode): VNode | null {
248
244
  const { type, props, prev, flags } = vNode
245
+
249
246
  if (__DEV__ && isHmrUpdate()) {
250
- } else if ((flags & FLAG_DIRTY) === 0 && props === prev?.props) {
247
+ } else if (
248
+ prev &&
249
+ (flags & FLAG_DIRTY) === 0 &&
250
+ (prev.props === props || !propsChanged(prev.props, props))
251
+ ) {
251
252
  return null
252
253
  }
253
254
  try {
@@ -262,7 +263,7 @@ function updateVNode(vNode: VNode): VNode | null {
262
263
  if (__DEV__) {
263
264
  window.__kiru.emit(
264
265
  "error",
265
- appCtx!,
266
+ app!,
266
267
  error instanceof Error ? error : new Error(String(error))
267
268
  )
268
269
  }
@@ -302,16 +303,7 @@ function updateExoticComponent(vNode: VNode): VNode | null {
302
303
  const { props, type } = vNode
303
304
  let children = props.children
304
305
 
305
- if (type === $CONTEXT_PROVIDER) {
306
- const {
307
- props: { dependents, value },
308
- prev,
309
- } = vNode as ContextProviderNode<unknown>
310
-
311
- if (dependents.size && prev && prev.props.value !== value) {
312
- dependents.forEach(queueUpdate)
313
- }
314
- } else if (type === $ERROR_BOUNDARY) {
306
+ if (type === $ERROR_BOUNDARY) {
315
307
  const n = vNode as ErrorBoundaryNode
316
308
  const { error } = n
317
309
  if (error) {
@@ -328,18 +320,11 @@ function updateExoticComponent(vNode: VNode): VNode | null {
328
320
  }
329
321
 
330
322
  function updateFunctionComponent(vNode: FunctionVNode): VNode | null {
331
- const { type, props, subs, prev, flags } = vNode
332
- if (flags & FLAG_MEMO) {
333
- if (
334
- prev &&
335
- (type as MemoFn)[$MEMO](prev.props, props) &&
336
- !(__DEV__ && isHmrUpdate())
337
- ) {
338
- vNode.flags |= FLAG_NOOP
339
- return null
340
- }
341
- vNode.flags &= ~FLAG_NOOP
342
- }
323
+ const { type, props, subs } = vNode
324
+
325
+ /** Only sync prop-derived signals when update came from parent (new props), not from internal subscription (e.g. signal). */
326
+ const shouldSyncProps = (vNode.flags & FLAG_DIRTY) === 0
327
+
343
328
  try {
344
329
  node.current = vNode
345
330
  let newChild
@@ -347,7 +332,6 @@ function updateFunctionComponent(vNode: FunctionVNode): VNode | null {
347
332
  do {
348
333
  vNode.flags &= ~FLAG_DIRTY
349
334
  isRenderDirtied = false
350
- hookIndex.current = 0
351
335
 
352
336
  /**
353
337
  * remove previous signal subscriptions (if any) every render.
@@ -363,23 +347,26 @@ function updateFunctionComponent(vNode: FunctionVNode): VNode | null {
363
347
  subs.clear()
364
348
  }
365
349
 
366
- if (__DEV__) {
367
- newChild = latest(type)(props)
368
-
369
- if (isHmrUpdate() && vNode.hooks && vNode.hookSig) {
370
- const len = vNode.hooks.length
371
- if (hookIndex.current < len) {
372
- // clean up any hooks that were removed
373
- for (let i = hookIndex.current; i < len; i++) {
374
- const hook = vNode.hooks[i]
375
- hook.cleanup?.()
376
- }
377
- vNode.hooks.length = hookIndex.current
378
- vNode.hookSig.length = hookIndex.current
379
- }
350
+ if (__DEV__ && isHmrUpdate()) {
351
+ const { hooks, cleanups } = vNode
352
+ if (cleanups) {
353
+ Object.values(cleanups).forEach(call)
354
+ delete vNode.cleanups
355
+ }
356
+ if (hooks) {
357
+ const { preCleanups, postCleanups } = hooks
358
+ preCleanups.forEach(call)
359
+ postCleanups.forEach(call)
360
+ preCleanups.length = postCleanups.length = 0
380
361
  }
362
+ delete vNode.propSyncs
363
+ delete vNode.render
364
+ }
365
+
366
+ newChild = renderFunctionComponent(vNode, type, props, shouldSyncProps)
381
367
 
382
- if (++renderTryCount > CONSECUTIVE_DIRTY_LIMIT) {
368
+ if (++renderTryCount > CONSECUTIVE_DIRTY_LIMIT) {
369
+ if (__DEV__) {
383
370
  throw new KiruError({
384
371
  message:
385
372
  "Too many re-renders. Kiru limits the number of renders to prevent an infinite loop.",
@@ -387,9 +374,8 @@ function updateFunctionComponent(vNode: FunctionVNode): VNode | null {
387
374
  vNode,
388
375
  })
389
376
  }
390
- continue
377
+ break
391
378
  }
392
- newChild = type(props)
393
379
  } while (isRenderDirtied)
394
380
 
395
381
  return (vNode.child = reconcileChildren(vNode, newChild))
@@ -398,6 +384,38 @@ function updateFunctionComponent(vNode: FunctionVNode): VNode | null {
398
384
  }
399
385
  }
400
386
 
387
+ function renderFunctionComponent(
388
+ vNode: FunctionVNode,
389
+ type: Function,
390
+ props: Record<string, unknown>,
391
+ shouldSyncProps: boolean
392
+ ): unknown {
393
+ const { render, propSyncs } = vNode
394
+
395
+ if (render) {
396
+ if (shouldSyncProps) {
397
+ const p = { ...props }
398
+ propSyncs?.forEach((sync) => sync(p))
399
+ }
400
+ return render(props)
401
+ }
402
+
403
+ let newChild = latest(type)(props)
404
+ if (typeof newChild === "function") {
405
+ vNode.subs?.forEach(call) // unsub from signals observered during setup
406
+ vNode.render = newChild
407
+ if (shouldSyncProps) {
408
+ const p = { ...props }
409
+ propSyncs?.forEach((sync) => sync(p))
410
+ }
411
+ newChild = newChild(props)
412
+ } else if (__DEV__ && setups.has(vNode)) {
413
+ throw new Error("setup() must not be called inside a render function")
414
+ }
415
+
416
+ return newChild
417
+ }
418
+
401
419
  function updateHostComponent(vNode: DomVNode): VNode | null {
402
420
  const { props, type } = vNode
403
421
  if (__DEV__) {
@@ -3,13 +3,14 @@ import {
3
3
  safeStringify,
4
4
  sideEffectsEnabled,
5
5
  generateRandomID,
6
+ registerVNodeCleanup,
6
7
  } from "../utils/index.js"
7
8
  import { $HMR_ACCEPT, $SIGNAL } from "../constants.js"
8
- import { __DEV__ } from "../env.js"
9
+ import { __DEV__, isBrowser } from "../env.js"
9
10
  import { node } from "../globals.js"
10
- import { useHook } from "../hooks/utils.js"
11
11
  import { requestUpdate } from "../scheduler.js"
12
- import { tracking, signalSubsMap } from "./globals.js"
12
+ import { signalSubsMap } from "./globals.js"
13
+ import { tracking } from "./tracking.js"
13
14
  import type { SignalSubscriber, ReadonlySignal } from "./types.js"
14
15
  import type { HMRAccept } from "../hmr.js"
15
16
 
@@ -38,6 +39,7 @@ export class Signal<T> {
38
39
  return this as Signal<any>
39
40
  },
40
41
  inject: (prev) => {
42
+ if (isBrowser) window.__kiru.devtools?.untrack(prev)
41
43
  signalSubsMap.get(this.$id)?.clear?.()
42
44
  signalSubsMap.delete(this.$id)
43
45
  this.$id = prev.$id
@@ -56,6 +58,11 @@ export class Signal<T> {
56
58
  } else {
57
59
  this.$subs = new Set()
58
60
  }
61
+
62
+ const n = node.current
63
+ if (n) {
64
+ registerVNodeCleanup(n, this.$id, Signal.dispose.bind(null, this))
65
+ }
59
66
  }
60
67
 
61
68
  get value() {
@@ -175,6 +182,8 @@ export class Signal<T> {
175
182
  }
176
183
 
177
184
  static entangle<T>(signal: Signal<T>) {
185
+ if (tracking.enabled === false) return
186
+
178
187
  const vNode = node.current
179
188
  const trackedSignalObservations = tracking.current()
180
189
  if (trackedSignalObservations) {
@@ -192,6 +201,7 @@ export class Signal<T> {
192
201
  signal.$isDisposed = true
193
202
  if (__DEV__) {
194
203
  signalSubsMap.delete(signal.$id)
204
+ if (isBrowser) window.__kiru.devtools?.untrack(signal)
195
205
  return
196
206
  }
197
207
  signal.$subs!.clear()
@@ -201,42 +211,3 @@ export class Signal<T> {
201
211
  export const signal = <T>(initial: T, displayName?: string) => {
202
212
  return new Signal(initial, displayName)
203
213
  }
204
-
205
- export const useSignal = <T>(initial: T, displayName?: string) => {
206
- return useHook(
207
- "useSignal",
208
- { signal: null! as Signal<T> },
209
- ({ hook, isInit, isHMR }) => {
210
- if (__DEV__) {
211
- if (isInit) {
212
- hook.dev = {
213
- devtools: {
214
- get: () => ({
215
- displayName: hook.signal.displayName,
216
- value: hook.signal.peek(),
217
- }),
218
- set: ({ value }) => {
219
- hook.signal.value = value
220
- },
221
- },
222
- initialArgs: [initial, displayName],
223
- }
224
- }
225
- if (isHMR) {
226
- const [v, name] = hook.dev!.initialArgs
227
- if (v !== initial || name !== displayName) {
228
- hook.cleanup?.()
229
- isInit = true
230
- hook.dev!.initialArgs = [initial, displayName]
231
- }
232
- }
233
- }
234
-
235
- if (isInit) {
236
- hook.cleanup = () => Signal.dispose(hook.signal)
237
- hook.signal = new Signal(initial, displayName)
238
- }
239
- return hook.signal
240
- }
241
- )
242
- }
@@ -1,9 +1,8 @@
1
1
  import { __DEV__ } from "../env.js"
2
2
  import { $HMR_ACCEPT } from "../constants.js"
3
- import { depsRequireChange, useHook } from "../hooks/utils.js"
4
3
  import { call, latest } from "../utils/index.js"
5
4
  import { effectQueue, signalSubsMap } from "./globals.js"
6
- import { executeWithTracking } from "./effect.js"
5
+ import { executeWithTracking } from "./tracking.js"
7
6
  import { Signal } from "./base.js"
8
7
  import type { HMRAccept } from "../hmr.js"
9
8
 
@@ -131,63 +130,3 @@ export function computed<T>(
131
130
  ): ComputedSignal<T> {
132
131
  return new ComputedSignal(getter, displayName)
133
132
  }
134
-
135
- export function useComputed<T>(
136
- getter: (prev?: T) => T,
137
- displayName?: string
138
- ): ComputedSignal<T>
139
-
140
- export function useComputed<T>(
141
- getter: (prev?: T) => T,
142
- deps?: unknown[],
143
- displayName?: string
144
- ): ComputedSignal<T>
145
-
146
- export function useComputed<T>(
147
- getter: (prev?: T) => T,
148
- depsOrDisplayName?: string | unknown[],
149
- displayName?: string
150
- ) {
151
- return useHook(
152
- "useComputedSignal",
153
- {
154
- signal: null! as ComputedSignal<T>,
155
- deps: void 0 as unknown[] | undefined,
156
- },
157
- ({ hook, isInit, isHMR }) => {
158
- if (__DEV__) {
159
- hook.dev = {
160
- devtools: {
161
- get: () => ({
162
- displayName: hook.signal.displayName,
163
- value: hook.signal.peek(),
164
- }),
165
- },
166
- }
167
- if (isHMR) {
168
- // useComputed is always considered side-effecty, so we need to re-run
169
- hook.cleanup?.()
170
- isInit = true
171
- }
172
- }
173
- if (isInit) {
174
- if (typeof depsOrDisplayName === "string") {
175
- displayName = depsOrDisplayName
176
- depsOrDisplayName = void 0
177
- }
178
- hook.deps = depsOrDisplayName
179
- hook.cleanup = () => ComputedSignal.dispose(hook.signal)
180
- hook.signal = computed(getter, displayName)
181
- } else if (
182
- hook.deps &&
183
- typeof depsOrDisplayName !== "string" &&
184
- depsRequireChange(hook.deps, depsOrDisplayName)
185
- ) {
186
- hook.deps = depsOrDisplayName
187
- ComputedSignal.updateGetter(hook.signal, getter)
188
- }
189
-
190
- return hook.signal
191
- }
192
- )
193
- }
@@ -1,61 +1,106 @@
1
- import { node } from "../globals.js"
2
- import { sideEffectsEnabled } from "../utils/index.js"
3
- import { tracking, effectQueue } from "./globals.js"
4
- import { tick } from "./utils.js"
1
+ import { __DEV__, isBrowser } from "../env.js"
2
+ import { effectQueue } from "./globals.js"
3
+ import { executeWithTracking } from "./tracking.js"
4
+ import {
5
+ latest,
6
+ generateRandomID,
7
+ call,
8
+ registerVNodeCleanup,
9
+ sideEffectsEnabled,
10
+ } from "../utils/index.js"
5
11
  import type { Signal } from "./base.js"
6
12
  import type { SignalValues } from "./types.js"
13
+ import { node } from "../globals.js"
7
14
 
8
- type TrackedExecutionContext<T, Deps extends readonly Signal<unknown>[]> = {
9
- id: string
10
- subs: Map<string, Function>
11
- fn: (...values: SignalValues<Deps>) => T
12
- deps?: Deps
13
- onDepChanged: () => void
14
- }
15
-
16
- /**
17
- * Executes an effect function with dependency tracking enabled, and manages
18
- * the effect's subscriptions.
19
- * @param ctx - The execution context
20
- * @returns The result of the effect function
21
- */
22
- export function executeWithTracking<T, Deps extends readonly Signal<unknown>[]>(
23
- ctx: TrackedExecutionContext<T, Deps>
24
- ): T {
25
- const { id, subs, fn, deps = [], onDepChanged } = ctx
26
- let observations: Map<string, Signal<unknown>> | undefined
27
-
28
- effectQueue.delete(id)
29
- const isServer = !!node.current && !sideEffectsEnabled()
30
-
31
- if (!isServer) {
32
- observations = new Map<string, Signal<unknown>>()
33
- tracking.stack.push(observations)
34
- }
15
+ type EffectCallbackReturn = (() => void) | void
35
16
 
36
- const result = fn(...(deps.map((s) => s.value) as SignalValues<Deps>))
17
+ export class Effect<const Deps extends readonly Signal<unknown>[] = []> {
18
+ protected id: string
19
+ protected callback: (...values: SignalValues<Deps>) => EffectCallbackReturn
20
+ protected deps?: Deps
21
+ protected unsubs: Map<string, Function>
22
+ protected cleanup: (() => void) | null
23
+ protected isRunning?: boolean
37
24
 
38
- if (!isServer) {
39
- for (const [id, unsub] of subs) {
40
- if (observations!.has(id)) continue
41
- unsub()
42
- subs.delete(id)
25
+ constructor(
26
+ callback: (...values: SignalValues<Deps>) => EffectCallbackReturn,
27
+ deps?: Deps
28
+ ) {
29
+ this.id = generateRandomID()
30
+ this.callback = callback
31
+ this.deps = deps
32
+ this.unsubs = new Map()
33
+ this.isRunning = false
34
+ this.cleanup = null
35
+ if (__DEV__ && isBrowser) {
36
+ window.__kiru.HMRContext!.moduleEffects.push(this)
37
+ }
38
+ const n = node.current
39
+ if (n) {
40
+ if (!sideEffectsEnabled()) return // prevent side effects in non-browser environments
41
+ registerVNodeCleanup(n, this.id, this.stop.bind(this))
43
42
  }
43
+ this.start()
44
+ }
44
45
 
45
- const effect = () => {
46
- if (!effectQueue.size) {
47
- queueMicrotask(tick)
48
- }
49
- effectQueue.set(id, onDepChanged)
46
+ start() {
47
+ if (this.isRunning) {
48
+ return
50
49
  }
51
50
 
52
- for (const [id, sig] of observations!) {
53
- if (subs.has(id)) continue
54
- const unsub = sig.subscribe(effect)
55
- subs.set(id, unsub)
51
+ this.isRunning = true
52
+
53
+ // postpone execution during HMR
54
+ if (__DEV__ && isBrowser && window.__kiru.HMRContext?.isReplacement()) {
55
+ return queueMicrotask(() => {
56
+ if (this.isRunning) {
57
+ Effect.run(this as Effect)
58
+ }
59
+ })
56
60
  }
57
- tracking.stack.pop()
61
+ Effect.run(this as Effect)
58
62
  }
59
63
 
60
- return result
64
+ stop() {
65
+ effectQueue.delete(this.id)
66
+ this.unsubs.forEach(call)
67
+ this.unsubs.clear()
68
+ this.cleanup?.()
69
+ this.cleanup = null
70
+ this.isRunning = false
71
+ }
72
+
73
+ private static run(watchEffect: Effect) {
74
+ const effect = latest(watchEffect)
75
+ const { id, callback: getter, unsubs: subs, deps } = effect
76
+
77
+ effect.cleanup =
78
+ executeWithTracking({
79
+ id,
80
+ subs,
81
+ fn: getter,
82
+ deps,
83
+ onDepChanged: () => {
84
+ effect.cleanup?.()
85
+ Effect.run(effect)
86
+ },
87
+ }) ?? null
88
+ }
89
+ }
90
+
91
+ export function effect(callback: () => EffectCallbackReturn): Effect
92
+ export function effect<const Deps extends readonly Signal<unknown>[]>(
93
+ dependencies: Deps,
94
+ callback: (...values: SignalValues<Deps>) => EffectCallbackReturn
95
+ ): Effect<Deps>
96
+ export function effect<const Deps extends readonly Signal<unknown>[]>(
97
+ depsOrGetter: Deps | (() => EffectCallbackReturn),
98
+ callback?: (...values: SignalValues<Deps>) => EffectCallbackReturn
99
+ ): Effect<Deps> | Effect {
100
+ if (typeof depsOrGetter === "function") {
101
+ return new Effect<[]>(depsOrGetter)
102
+ }
103
+ const dependencies = depsOrGetter
104
+ const effectGetter = callback!
105
+ return new Effect(effectGetter, dependencies)
61
106
  }