kiru 0.52.4 → 0.54.0-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/dist/appContext.d.ts.map +1 -1
  2. package/dist/appContext.js +14 -11
  3. package/dist/appContext.js.map +1 -1
  4. package/dist/components/derive.d.ts +20 -0
  5. package/dist/components/derive.d.ts.map +1 -0
  6. package/dist/components/derive.js +57 -0
  7. package/dist/components/derive.js.map +1 -0
  8. package/dist/components/errorBoundary.d.ts +1 -1
  9. package/dist/components/errorBoundary.d.ts.map +1 -1
  10. package/dist/components/index.d.ts +1 -1
  11. package/dist/components/index.d.ts.map +1 -1
  12. package/dist/components/index.js +1 -1
  13. package/dist/components/index.js.map +1 -1
  14. package/dist/components/memo.d.ts +6 -3
  15. package/dist/components/memo.d.ts.map +1 -1
  16. package/dist/components/memo.js.map +1 -1
  17. package/dist/constants.d.ts +3 -3
  18. package/dist/constants.d.ts.map +1 -1
  19. package/dist/constants.js +3 -3
  20. package/dist/constants.js.map +1 -1
  21. package/dist/dom.d.ts.map +1 -1
  22. package/dist/dom.js +69 -66
  23. package/dist/dom.js.map +1 -1
  24. package/dist/element.d.ts +2 -2
  25. package/dist/element.d.ts.map +1 -1
  26. package/dist/element.js +20 -32
  27. package/dist/element.js.map +1 -1
  28. package/dist/globalContext.d.ts +6 -1
  29. package/dist/globalContext.d.ts.map +1 -1
  30. package/dist/globalContext.js +14 -2
  31. package/dist/globalContext.js.map +1 -1
  32. package/dist/globals.d.ts +1 -1
  33. package/dist/globals.d.ts.map +1 -1
  34. package/dist/globals.js.map +1 -1
  35. package/dist/hooks/usePromise.d.ts +4 -9
  36. package/dist/hooks/usePromise.d.ts.map +1 -1
  37. package/dist/hooks/usePromise.js +25 -28
  38. package/dist/hooks/usePromise.js.map +1 -1
  39. package/dist/hooks/useRef.d.ts +2 -2
  40. package/dist/hooks/useRef.d.ts.map +1 -1
  41. package/dist/jsx.d.ts +1 -1
  42. package/dist/jsx.d.ts.map +1 -1
  43. package/dist/reconciler.d.ts +1 -1
  44. package/dist/reconciler.d.ts.map +1 -1
  45. package/dist/reconciler.js +57 -117
  46. package/dist/reconciler.js.map +1 -1
  47. package/dist/recursiveRender.d.ts +4 -3
  48. package/dist/recursiveRender.d.ts.map +1 -1
  49. package/dist/recursiveRender.js +20 -19
  50. package/dist/recursiveRender.js.map +1 -1
  51. package/dist/renderToString.js +2 -2
  52. package/dist/renderToString.js.map +1 -1
  53. package/dist/router/client/index.d.ts +4 -2
  54. package/dist/router/client/index.d.ts.map +1 -1
  55. package/dist/router/client/index.js +49 -11
  56. package/dist/router/client/index.js.map +1 -1
  57. package/dist/router/context.d.ts +2 -0
  58. package/dist/router/context.d.ts.map +1 -1
  59. package/dist/router/context.js +5 -1
  60. package/dist/router/context.js.map +1 -1
  61. package/dist/router/fileRouterController.d.ts +3 -1
  62. package/dist/router/fileRouterController.d.ts.map +1 -1
  63. package/dist/router/fileRouterController.js +80 -9
  64. package/dist/router/fileRouterController.js.map +1 -1
  65. package/dist/router/globals.d.ts +3 -0
  66. package/dist/router/globals.d.ts.map +1 -1
  67. package/dist/router/globals.js +3 -0
  68. package/dist/router/globals.js.map +1 -1
  69. package/dist/router/guard.d.ts +17 -0
  70. package/dist/router/guard.d.ts.map +1 -0
  71. package/dist/router/guard.js +45 -0
  72. package/dist/router/guard.js.map +1 -0
  73. package/dist/router/head.d.ts.map +1 -1
  74. package/dist/router/head.js +5 -7
  75. package/dist/router/head.js.map +1 -1
  76. package/dist/router/index.d.ts +3 -2
  77. package/dist/router/index.d.ts.map +1 -1
  78. package/dist/router/index.js +2 -1
  79. package/dist/router/index.js.map +1 -1
  80. package/dist/router/{server → ssg}/index.d.ts +4 -3
  81. package/dist/router/ssg/index.d.ts.map +1 -0
  82. package/dist/router/{server → ssg}/index.js +7 -4
  83. package/dist/router/ssg/index.js.map +1 -0
  84. package/dist/router/ssr/index.d.ts +20 -0
  85. package/dist/router/ssr/index.d.ts.map +1 -0
  86. package/dist/router/ssr/index.js +160 -0
  87. package/dist/router/ssr/index.js.map +1 -0
  88. package/dist/router/types.d.ts +37 -4
  89. package/dist/router/types.d.ts.map +1 -1
  90. package/dist/router/types.internal.d.ts +4 -0
  91. package/dist/router/types.internal.d.ts.map +1 -1
  92. package/dist/router/utils/index.d.ts +9 -4
  93. package/dist/router/utils/index.d.ts.map +1 -1
  94. package/dist/router/utils/index.js +38 -6
  95. package/dist/router/utils/index.js.map +1 -1
  96. package/dist/signals/base.d.ts +1 -3
  97. package/dist/signals/base.d.ts.map +1 -1
  98. package/dist/signals/base.js +3 -3
  99. package/dist/signals/base.js.map +1 -1
  100. package/dist/signals/computed.d.ts +4 -2
  101. package/dist/signals/computed.d.ts.map +1 -1
  102. package/dist/signals/computed.js +29 -6
  103. package/dist/signals/computed.js.map +1 -1
  104. package/dist/signals/for.d.ts +10 -0
  105. package/dist/signals/for.d.ts.map +1 -0
  106. package/dist/signals/for.js +7 -0
  107. package/dist/signals/for.js.map +1 -0
  108. package/dist/signals/index.d.ts +1 -1
  109. package/dist/signals/index.js +1 -1
  110. package/dist/signals/types.d.ts +1 -1
  111. package/dist/signals/types.d.ts.map +1 -1
  112. package/dist/ssr/client.d.ts +1 -1
  113. package/dist/ssr/client.d.ts.map +1 -1
  114. package/dist/ssr/server.d.ts +1 -2
  115. package/dist/ssr/server.d.ts.map +1 -1
  116. package/dist/ssr/server.js +26 -29
  117. package/dist/ssr/server.js.map +1 -1
  118. package/dist/types.d.ts +13 -11
  119. package/dist/types.d.ts.map +1 -1
  120. package/dist/types.dom.d.ts +33 -32
  121. package/dist/types.dom.d.ts.map +1 -1
  122. package/dist/types.utils.d.ts +5 -1
  123. package/dist/types.utils.d.ts.map +1 -1
  124. package/dist/utils/format.d.ts +4 -3
  125. package/dist/utils/format.d.ts.map +1 -1
  126. package/dist/utils/format.js +8 -4
  127. package/dist/utils/format.js.map +1 -1
  128. package/dist/utils/index.d.ts +0 -1
  129. package/dist/utils/index.d.ts.map +1 -1
  130. package/dist/utils/index.js +0 -1
  131. package/dist/utils/index.js.map +1 -1
  132. package/dist/utils/promise.d.ts +16 -0
  133. package/dist/utils/promise.d.ts.map +1 -0
  134. package/dist/utils/promise.js +14 -0
  135. package/dist/utils/promise.js.map +1 -0
  136. package/dist/utils/runtime.d.ts +11 -2
  137. package/dist/utils/runtime.d.ts.map +1 -1
  138. package/dist/utils/runtime.js +26 -2
  139. package/dist/utils/runtime.js.map +1 -1
  140. package/dist/utils/vdom.d.ts +4 -4
  141. package/dist/utils/vdom.d.ts.map +1 -1
  142. package/dist/utils/vdom.js +18 -17
  143. package/dist/utils/vdom.js.map +1 -1
  144. package/dist/vNode.d.ts +4 -0
  145. package/dist/vNode.d.ts.map +1 -0
  146. package/dist/vNode.js +22 -0
  147. package/dist/vNode.js.map +1 -0
  148. package/package.json +8 -4
  149. package/src/appContext.ts +15 -13
  150. package/src/components/derive.ts +121 -0
  151. package/src/components/index.ts +1 -1
  152. package/src/components/memo.ts +3 -2
  153. package/src/constants.ts +4 -4
  154. package/src/dom.ts +71 -66
  155. package/src/element.ts +22 -35
  156. package/src/globalContext.ts +24 -3
  157. package/src/globals.ts +1 -1
  158. package/src/hooks/usePromise.ts +32 -41
  159. package/src/hooks/useRef.ts +2 -2
  160. package/src/reconciler.ts +87 -125
  161. package/src/recursiveRender.ts +25 -23
  162. package/src/renderToString.ts +3 -3
  163. package/src/router/client/index.ts +100 -14
  164. package/src/router/context.ts +7 -1
  165. package/src/router/fileRouterController.ts +137 -8
  166. package/src/router/globals.ts +4 -0
  167. package/src/router/guard.ts +72 -0
  168. package/src/router/head.ts +5 -7
  169. package/src/router/index.ts +12 -1
  170. package/src/router/{server → ssg}/index.ts +16 -9
  171. package/src/router/ssr/index.ts +247 -0
  172. package/src/router/types.internal.ts +5 -0
  173. package/src/router/types.ts +48 -4
  174. package/src/router/utils/index.ts +74 -8
  175. package/src/signals/base.ts +3 -3
  176. package/src/signals/computed.ts +43 -6
  177. package/src/signals/for.ts +25 -0
  178. package/src/signals/index.ts +1 -1
  179. package/src/signals/types.ts +5 -1
  180. package/src/ssr/client.ts +1 -1
  181. package/src/ssr/server.ts +31 -33
  182. package/src/types.dom.ts +40 -40
  183. package/src/types.ts +14 -11
  184. package/src/types.utils.ts +11 -1
  185. package/src/utils/format.ts +12 -4
  186. package/src/utils/index.ts +0 -2
  187. package/src/utils/promise.ts +26 -0
  188. package/src/utils/runtime.ts +30 -2
  189. package/src/utils/vdom.ts +20 -23
  190. package/src/vNode.ts +30 -0
  191. package/dist/components/suspense.d.ts +0 -24
  192. package/dist/components/suspense.d.ts.map +0 -1
  193. package/dist/components/suspense.js +0 -36
  194. package/dist/components/suspense.js.map +0 -1
  195. package/dist/router/server/index.d.ts.map +0 -1
  196. package/dist/router/server/index.js.map +0 -1
  197. package/dist/signals/jsx.d.ts +0 -17
  198. package/dist/signals/jsx.d.ts.map +0 -1
  199. package/dist/signals/jsx.js +0 -11
  200. package/dist/signals/jsx.js.map +0 -1
  201. package/src/components/suspense.ts +0 -72
  202. package/src/signals/jsx.ts +0 -46
package/src/ssr/server.ts CHANGED
@@ -1,71 +1,69 @@
1
- import { Readable } from "node:stream"
2
1
  import { Fragment } from "../element.js"
3
2
  import { renderMode } from "../globals.js"
4
- import { PREFETCHED_DATA_EVENT } from "../constants.js"
3
+ import { STREAMED_DATA_EVENT } from "../constants.js"
5
4
  import { __DEV__ } from "../env.js"
6
- import { recursiveRender, RecursiveRenderContext } from "../recursiveRender.js"
5
+ import { headlessRender, HeadlessRenderContext } from "../recursiveRender.js"
7
6
 
8
- const PREFETCH_EVENTS_SETUP = `
7
+ const STREAMED_DATA_SETUP = `
9
8
  <script type="text/javascript">
10
9
  const d = document,
11
- m = (window["${PREFETCHED_DATA_EVENT}"] ??= new Map());
12
- d.querySelectorAll("[x-data]").forEach((p) => {
10
+ m = (window["${STREAMED_DATA_EVENT}"] ??= new Map());
11
+ d.querySelectorAll("[k-data]").forEach((p) => {
13
12
  const id = p.getAttribute("id");
14
13
  const { data, error } = JSON.parse(p.innerHTML);
15
14
  m.set(id, { data, error });
16
- const event = new CustomEvent("${PREFETCHED_DATA_EVENT}", { detail: { id, data, error } });
15
+ const event = new CustomEvent("${STREAMED_DATA_EVENT}", { detail: { id, data, error } });
17
16
  window.dispatchEvent(event);
18
17
  p.remove();
19
18
  });
20
19
  d.currentScript.remove()
21
20
  </script>
22
- `
21
+ `.replace(/\s+/g, " ")
23
22
 
24
23
  export function renderToReadableStream(element: JSX.Element): {
25
24
  immediate: string
26
- stream: Readable
25
+ stream: ReadableStream | null
27
26
  } {
28
- const stream = new Readable({ read() {} })
29
- const rootNode = Fragment({ children: element })
30
- const prefetchPromises = new Set<Kiru.StatefulPromise<unknown>>()
31
- const pendingWritePromises: Promise<unknown>[] = []
27
+ const streamPromises = new Set<Kiru.StatefulPromise<unknown>>()
28
+ const dataPromises: Promise<string>[] = []
29
+ let stream: ReadableStream | null = null
32
30
 
33
31
  let immediate = ""
34
32
 
35
- const ctx: RecursiveRenderContext = {
33
+ const ctx: HeadlessRenderContext = {
36
34
  write: (chunk) => (immediate += chunk),
37
- onPending(data) {
35
+ onStreamData(data) {
38
36
  for (const promise of data) {
39
- if (prefetchPromises.has(promise)) continue
40
- prefetchPromises.add(promise)
37
+ if (streamPromises.has(promise)) continue
38
+ streamPromises.add(promise)
41
39
 
42
- const writePromise = promise
40
+ const dataPromise = promise
43
41
  .then(() => ({ data: promise.value }))
44
42
  .catch(() => ({ error: promise.error?.message }))
45
- .then((value) => {
46
- const content = JSON.stringify(value)
47
- stream.push(
48
- `<script id="${promise.id}" x-data type="application/json">${content}</script>`
49
- )
50
- })
51
-
52
- pendingWritePromises.push(writePromise)
43
+ .then(
44
+ (value, content = JSON.stringify(value)) =>
45
+ `<script id="${promise.id}" k-data type="application/json">${content}</script>`
46
+ )
47
+ dataPromises.push(dataPromise)
53
48
  }
54
49
  },
55
50
  }
56
51
 
57
52
  const prev = renderMode.current
58
53
  renderMode.current = "stream"
59
- recursiveRender(ctx, rootNode, null, 0)
54
+ headlessRender(ctx, Fragment({ children: element }), null, 0)
60
55
  renderMode.current = prev
61
56
 
62
- if (pendingWritePromises.length > 0) {
63
- Promise.all(pendingWritePromises).then(() => {
64
- stream.push(PREFETCH_EVENTS_SETUP)
65
- stream.push(null)
57
+ if (dataPromises.length > 0) {
58
+ stream = new ReadableStream({
59
+ async pull(controller) {
60
+ for await (const chunk of dataPromises) {
61
+ controller.enqueue(chunk)
62
+ }
63
+ controller.enqueue(STREAMED_DATA_SETUP)
64
+ controller.close()
65
+ },
66
66
  })
67
- } else {
68
- stream.push(null)
69
67
  }
70
68
 
71
69
  return { immediate, stream }
package/src/types.dom.ts CHANGED
@@ -352,6 +352,8 @@ type NoChildElementElement =
352
352
  | HTMLTrackElement
353
353
  | HTMLTextAreaElement
354
354
 
355
+ type DomElement = Element
356
+
355
357
  declare global {
356
358
  namespace Kiru {
357
359
  type DOMEvent<E = Event, C = unknown, T = unknown> = Omit<
@@ -368,98 +370,96 @@ declare global {
368
370
  bivarianceHack(event: E): void
369
371
  }["bivarianceHack"]
370
372
 
371
- interface BaseEvent<T extends Element = Element>
373
+ interface BaseEvent<T extends DomElement = DomElement>
372
374
  extends DOMEvent<Event, T> {}
373
375
 
374
- interface AnimationEvent<T extends Element = Element>
376
+ interface AnimationEvent<T extends DomElement = DomElement>
375
377
  extends DOMEvent<NativeAnimationEvent, T> {}
376
378
 
377
- interface ClipboardEvent<T extends Element = Element>
379
+ interface ClipboardEvent<T extends DomElement = DomElement>
378
380
  extends DOMEvent<NativeClipboardEvent, T> {}
379
381
 
380
- interface CompositionEvent<T extends Element = Element>
382
+ interface CompositionEvent<T extends DomElement = DomElement>
381
383
  extends DOMEvent<NativeCompositionEvent, T> {}
382
384
 
383
- interface DragEvent<T extends Element = Element>
385
+ interface DragEvent<T extends DomElement = DomElement>
384
386
  extends DOMEvent<NativeDragEvent, T> {}
385
387
 
386
- interface FocusEvent<T extends Element = Element>
388
+ interface FocusEvent<T extends DomElement = DomElement>
387
389
  extends DOMEvent<NativeFocusEvent, T> {}
388
390
 
389
- interface FormEvent<T extends Element = Element>
391
+ interface FormEvent<T extends DomElement = DomElement>
390
392
  extends DOMEvent<Event, T> {}
391
393
 
392
- interface KeyboardEvent<T extends Element = Element>
394
+ interface KeyboardEvent<T extends DomElement = DomElement>
393
395
  extends DOMEvent<NativeKeyboardEvent, T> {}
394
396
 
395
- interface MouseEvent<T extends Element = Element>
397
+ interface MouseEvent<T extends DomElement = DomElement>
396
398
  extends DOMEvent<NativeMouseEvent, T> {}
397
399
 
398
- interface PointerEvent<T extends Element = Element>
400
+ interface PointerEvent<T extends DomElement = DomElement>
399
401
  extends DOMEvent<NativePointerEvent, T> {}
400
402
 
401
- interface SubmitEvent<T extends Element = Element>
403
+ interface SubmitEvent<T extends DomElement = DomElement>
402
404
  extends DOMEvent<NativeSubmitEvent, T> {}
403
405
 
404
- interface TouchEvent<T extends Element = Element>
406
+ interface TouchEvent<T extends DomElement = DomElement>
405
407
  extends DOMEvent<NativeTouchEvent, T> {}
406
408
 
407
- interface ToggleEvent<T extends Element = Element>
409
+ interface ToggleEvent<T extends DomElement = DomElement>
408
410
  extends DOMEvent<NativeToggleEvent, T> {}
409
411
 
410
- interface TransitionEvent<T extends Element = Element>
412
+ interface TransitionEvent<T extends DomElement = DomElement>
411
413
  extends DOMEvent<NativeTransitionEvent, T> {}
412
414
 
413
- interface UIEvent<T extends Element = Element>
415
+ interface UIEvent<T extends DomElement = DomElement>
414
416
  extends DOMEvent<NativeUIEvent, T> {}
415
417
 
416
- interface WheelEvent<T extends Element = Element>
418
+ interface WheelEvent<T extends DomElement = DomElement>
417
419
  extends DOMEvent<NativeWheelEvent, T> {}
418
420
 
419
- type BaseEventHandler<T extends Element = Element> = EventHandler<
421
+ type BaseEventHandler<T extends DomElement = DomElement> = EventHandler<
420
422
  BaseEvent<T>
421
423
  >
422
424
 
423
- type ClipboardEventHandler<T extends Element = Element> = EventHandler<
424
- ClipboardEvent<T>
425
- >
426
- type CompositionEventHandler<T extends Element = Element> = EventHandler<
427
- CompositionEvent<T>
428
- >
429
- type DragEventHandler<T extends Element = Element> = EventHandler<
425
+ type ClipboardEventHandler<T extends DomElement = DomElement> =
426
+ EventHandler<ClipboardEvent<T>>
427
+ type CompositionEventHandler<T extends DomElement = DomElement> =
428
+ EventHandler<CompositionEvent<T>>
429
+ type DragEventHandler<T extends DomElement = DomElement> = EventHandler<
430
430
  DragEvent<T>
431
431
  >
432
- type FocusEventHandler<T extends Element = Element> = EventHandler<
432
+ type FocusEventHandler<T extends DomElement = DomElement> = EventHandler<
433
433
  FocusEvent<T>
434
434
  >
435
- type FormEventHandler<T extends Element = Element> = EventHandler<
435
+ type FormEventHandler<T extends DomElement = DomElement> = EventHandler<
436
436
  FormEvent<T>
437
437
  >
438
- type KeyboardEventHandler<T extends Element = Element> = EventHandler<
438
+ type KeyboardEventHandler<T extends DomElement = DomElement> = EventHandler<
439
439
  KeyboardEvent<T>
440
440
  >
441
- type MouseEventHandler<T extends Element = Element> = EventHandler<
441
+ type MouseEventHandler<T extends DomElement = DomElement> = EventHandler<
442
442
  MouseEvent<T>
443
443
  >
444
- type TouchEventHandler<T extends Element = Element> = EventHandler<
444
+ type TouchEventHandler<T extends DomElement = DomElement> = EventHandler<
445
445
  TouchEvent<T>
446
446
  >
447
- type PointerEventHandler<T extends Element = Element> = EventHandler<
447
+ type PointerEventHandler<T extends DomElement = DomElement> = EventHandler<
448
448
  PointerEvent<T>
449
449
  >
450
- type UIEventHandler<T extends Element = Element> = EventHandler<UIEvent<T>>
451
- type WheelEventHandler<T extends Element = Element> = EventHandler<
452
- WheelEvent<T>
450
+ type UIEventHandler<T extends DomElement = DomElement> = EventHandler<
451
+ UIEvent<T>
453
452
  >
454
- type AnimationEventHandler<T extends Element = Element> = EventHandler<
455
- AnimationEvent<T>
453
+ type WheelEventHandler<T extends DomElement = DomElement> = EventHandler<
454
+ WheelEvent<T>
456
455
  >
457
- type ToggleEventHandler<T extends Element = Element> = EventHandler<
456
+ type AnimationEventHandler<T extends DomElement = DomElement> =
457
+ EventHandler<AnimationEvent<T>>
458
+ type ToggleEventHandler<T extends DomElement = DomElement> = EventHandler<
458
459
  ToggleEvent<T>
459
460
  >
460
- type TransitionEventHandler<T extends Element = Element> = EventHandler<
461
- TransitionEvent<T>
462
- >
461
+ type TransitionEventHandler<T extends DomElement = DomElement> =
462
+ EventHandler<TransitionEvent<T>>
463
463
 
464
464
  type CustomEventAttributes = {
465
465
  [Key in keyof Kiru.CustomEvents as `on:${Key}`]?: (
@@ -467,7 +467,7 @@ declare global {
467
467
  ) => void
468
468
  }
469
469
 
470
- interface EventAttributes<T extends Element = Element>
470
+ interface EventAttributes<T extends DomElement = DomElement>
471
471
  extends CustomEventAttributes {
472
472
  // Clipboard Events
473
473
  oncopy?: ClipboardEventHandler<T> | undefined
package/src/types.ts CHANGED
@@ -103,7 +103,7 @@ declare global {
103
103
 
104
104
  type Element =
105
105
  | Element[]
106
- | Kiru.VNode
106
+ | Kiru.Element
107
107
  | PrimitiveChild
108
108
  | Kiru.Signal<PrimitiveChild>
109
109
 
@@ -117,6 +117,8 @@ declare global {
117
117
  }
118
118
  }
119
119
  namespace Kiru {
120
+ interface RequestContext {}
121
+
120
122
  interface CustomEvents {}
121
123
 
122
124
  interface ProviderProps<T> {
@@ -156,16 +158,13 @@ declare global {
156
158
  }
157
159
  }
158
160
  interface RefObject<T> {
159
- readonly current: T | null
160
- }
161
- interface MutableRefObject<T> {
162
161
  current: T
163
162
  }
164
163
  type RefCallback<T> = {
165
164
  bivarianceHack(instance: T | null): void
166
165
  }["bivarianceHack"]
167
166
 
168
- type Ref<T> = RefCallback<T> | RefObject<T> | null | undefined
167
+ type Ref<T> = RefCallback<T> | RefObject<T>
169
168
 
170
169
  interface PromiseState<T> {
171
170
  id: string
@@ -176,6 +175,7 @@ declare global {
176
175
 
177
176
  interface StatefulPromise<T> extends Promise<T>, PromiseState<T> {}
178
177
 
178
+ type HydrationMode = "static" | "dynamic"
179
179
  type RenderMode = "dom" | "hydrate" | "string" | "stream"
180
180
 
181
181
  type StateSetter<T> = T | ((prev: T) => T)
@@ -187,25 +187,27 @@ declare global {
187
187
  | typeof $CONTEXT_PROVIDER
188
188
  | typeof $ERROR_BOUNDARY
189
189
 
190
- interface VNode {
191
- app?: AppContext
192
- dom?: SomeDom
193
- lastChildDom?: SomeDom
190
+ interface Element {
194
191
  type: Function | ExoticSymbol | "#text" | (string & {})
192
+ key: JSX.ElementKey | null
195
193
  props: {
196
194
  [key: string]: any
197
195
  children?: unknown
198
- key?: JSX.ElementKey
199
196
  ref?: Kiru.Ref<unknown>
200
197
  }
198
+ }
199
+
200
+ interface VNode extends Element {
201
+ app?: AppContext
202
+ dom?: SomeDom
201
203
  index: number
202
204
  depth: number
205
+ flags: number
203
206
  parent: VNode | null
204
207
  child: VNode | null
205
208
  sibling: VNode | null
206
209
  prev: VNodeSnapshot | null
207
210
  deletions: VNode[] | null
208
- flags: number
209
211
  hooks?: Hook<unknown>[]
210
212
  subs?: Set<Function>
211
213
  cleanups?: Record<string, Function>
@@ -223,6 +225,7 @@ declare global {
223
225
  }
224
226
  interface VNodeSnapshot {
225
227
  props: Kiru.VNode["props"]
228
+ key: Kiru.VNode["key"]
226
229
  memoizedProps: Kiru.VNode["memoizedProps"]
227
230
  index: number
228
231
  }
@@ -8,7 +8,7 @@ export type MaybeElement = SomeElement | undefined
8
8
  export type MaybeDom = SomeDom | undefined
9
9
 
10
10
  export interface FunctionVNode extends Kiru.VNode {
11
- type: (...args: any) => JSX.Element
11
+ type: (props: Record<string, unknown>) => JSX.Element
12
12
  }
13
13
 
14
14
  export interface ElementVNode extends Kiru.VNode {
@@ -65,3 +65,13 @@ export type AsyncTaskState<T, E extends Error = Error> =
65
65
  export type Guard<T, K extends keyof T> = {
66
66
  [P in K]: T[P]
67
67
  }
68
+
69
+ export type ArrayHas<T extends any[], U> =
70
+ // does the union of element types intersect U?
71
+ Extract<T[number], U> extends never ? false : true
72
+
73
+ export type RecordHas<T extends Record<string, any>, U> = [
74
+ Extract<T[keyof T], U>
75
+ ] extends [never]
76
+ ? false
77
+ : true
@@ -2,6 +2,7 @@ import { unwrap } from "../signals/utils.js"
2
2
  import { booleanAttributes, snakeCaseAttributes } from "../constants.js"
3
3
 
4
4
  export {
5
+ toArray,
5
6
  className,
6
7
  encodeHtmlEntities,
7
8
  propFilters,
@@ -20,6 +21,10 @@ const REGEX_DBLQT = /"/g
20
21
  const REGEX_SLASH = /\//g
21
22
  const REGEX_ALPHA_UPPER = /[A-Z]/g
22
23
 
24
+ function toArray<T>(value: T | T[]): T[] {
25
+ return Array.isArray(value) ? value : [value]
26
+ }
27
+
23
28
  function className(...classes: (string | false | null | undefined)[]): string {
24
29
  return classes.filter(Boolean).join(" ")
25
30
  }
@@ -34,11 +39,14 @@ function encodeHtmlEntities(text: string): string {
34
39
  .replace(REGEX_SLASH, "&#47;")
35
40
  }
36
41
 
42
+ const internalProps = new Set(["children", "ref", "key", "innerHTML"])
37
43
  const propFilters = {
38
- internalProps: ["children", "ref", "key", "innerHTML"],
44
+ isInternalProp: (
45
+ key: string
46
+ ): key is "children" | "ref" | "key" | "innerHTML" => internalProps.has(key),
39
47
  isEvent: (key: string) => key.startsWith("on"),
40
- isProperty: (key: string) =>
41
- !propFilters.internalProps.includes(key) && !propFilters.isEvent(key),
48
+ isStringRenderableProperty: (key: string) =>
49
+ !internalProps.has(key) && !propFilters.isEvent(key),
42
50
  }
43
51
 
44
52
  function propToHtmlAttr(key: string): string {
@@ -110,7 +118,7 @@ function propsToElementAttributes(props: Record<string, unknown>): string {
110
118
  if (!!val) attrs.push(`style="${stylePropToString(val)}"`)
111
119
  }
112
120
 
113
- const keys = Object.keys(rest).filter(propFilters.isProperty)
121
+ const keys = Object.keys(rest).filter(propFilters.isStringRenderableProperty)
114
122
  for (let i = 0; i < keys.length; i++) {
115
123
  let k = keys[i]
116
124
  let val = unwrap(props[k])
@@ -4,5 +4,3 @@ export * from "./format.js"
4
4
  export * from "./runtime.js"
5
5
  export * from "./vdom.js"
6
6
  export * from "./generateId.js"
7
-
8
- export const noop = Object.freeze(() => {})
@@ -0,0 +1,26 @@
1
+ import { $STREAM_DATA } from "../constants.js"
2
+
3
+ export interface StreamDataThrowValue {
4
+ [$STREAM_DATA]: {
5
+ fallback?: JSX.Element
6
+ data: Kiru.StatefulPromise<unknown>[]
7
+ }
8
+ }
9
+
10
+ /**
11
+ * Returns true if the value is a {@link StreamDataThrowValue}
12
+ */
13
+ export function isStreamDataThrowValue(
14
+ value: unknown
15
+ ): value is StreamDataThrowValue {
16
+ return typeof value === "object" && !!value && $STREAM_DATA in value
17
+ }
18
+
19
+ /**
20
+ * Returns true if the value is a {@link Kiru.StatefulPromise}
21
+ */
22
+ export function isStatefulPromise(
23
+ thing: unknown
24
+ ): thing is Kiru.StatefulPromise<unknown> {
25
+ return thing instanceof Promise && "id" in thing && "state" in thing
26
+ }
@@ -1,7 +1,10 @@
1
+ import { Signal } from "../signals/base.js"
1
2
  import { __DEV__ } from "../env.js"
2
3
  import { renderMode } from "../globals.js"
3
4
 
4
- export { latest, sideEffectsEnabled }
5
+ export { noop, latest, composeRefs, setRef, sideEffectsEnabled }
6
+
7
+ const noop = Object.freeze(() => {})
5
8
 
6
9
  /**
7
10
  * This is a no-op in production. It is used to get the latest
@@ -18,7 +21,32 @@ function latest<T extends Exclude<object, null>>(thing: T): T {
18
21
  }
19
22
 
20
23
  /**
21
- * Returns false if called during "stream" or "string" render modes.
24
+ * Composes multiple refs into a single ref callback.
25
+ */
26
+
27
+ function composeRefs<T>(...refs: Array<Kiru.Ref<T>>): Kiru.RefCallback<T> {
28
+ return (value: T) => {
29
+ refs.forEach((ref) => setRef(ref, value))
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Sets the value of a ref.
35
+ */
36
+ function setRef<T>(ref: Kiru.Ref<T>, value: T): void {
37
+ if (typeof ref === "function") {
38
+ ref(value)
39
+ return
40
+ }
41
+ if (Signal.isSignal(ref)) {
42
+ ref.value = value
43
+ return
44
+ }
45
+ ref.current = value
46
+ }
47
+
48
+ /**
49
+ * Returns true if called during 'dom' or 'hydrate' mode.
22
50
  */
23
51
  function sideEffectsEnabled(): boolean {
24
52
  return renderMode.current === "dom" || renderMode.current === "hydrate"
package/src/utils/vdom.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { Signal } from "../signals/base.js"
2
1
  import {
3
2
  FLAG_DELETION,
4
3
  $FRAGMENT,
@@ -12,10 +11,12 @@ import { KiruError } from "../error.js"
12
11
  import { node } from "../globals.js"
13
12
  import type { AppContext } from "../appContext.js"
14
13
  import type { ErrorBoundaryNode } from "../types.utils.js"
14
+ import { isMemoFn } from "../components/memo.js"
15
15
 
16
16
  export {
17
- cloneVNode,
17
+ cloneElement,
18
18
  isVNodeDeleted,
19
+ isElement,
19
20
  isVNode,
20
21
  isValidTextChild,
21
22
  isExoticType,
@@ -31,17 +32,16 @@ export {
31
32
  findParent,
32
33
  findParentErrorBoundary,
33
34
  assertValidElementProps,
34
- isValidElementKeyProp,
35
- isValidElementRefProp,
35
+ normalizeElementKey,
36
36
  }
37
37
 
38
- function cloneVNode(vNode: Kiru.VNode): Kiru.VNode {
38
+ function cloneElement(vNode: Kiru.VNode): Kiru.Element {
39
39
  const children = vNode.props.children
40
40
  let clonedChildren: unknown
41
41
  if (isVNode(children)) {
42
- clonedChildren = cloneVNode(children)
42
+ clonedChildren = cloneElement(children)
43
43
  } else if (Array.isArray(children)) {
44
- clonedChildren = children.map((c) => (isVNode(c) ? cloneVNode(c) : c))
44
+ clonedChildren = children.map((c) => (isVNode(c) ? cloneElement(c) : c))
45
45
  }
46
46
 
47
47
  return createElement(vNode.type, { ...vNode.props, children: clonedChildren })
@@ -55,6 +55,10 @@ function isVNode(thing: unknown): thing is Kiru.VNode {
55
55
  return typeof thing === "object" && thing !== null && "type" in thing
56
56
  }
57
57
 
58
+ function isElement(thing: unknown): thing is Kiru.Element {
59
+ return typeof thing === "object" && thing !== null && "type" in thing
60
+ }
61
+
58
62
  function isValidTextChild(thing: unknown): thing is string | number | bigint {
59
63
  return (
60
64
  (typeof thing === "string" && thing !== "") ||
@@ -84,11 +88,7 @@ function isLazy(vNode: Kiru.VNode): boolean {
84
88
  }
85
89
 
86
90
  function isMemo(vNode: Kiru.VNode): boolean {
87
- return (
88
- typeof vNode.type === "function" &&
89
- "displayName" in vNode.type &&
90
- vNode.type.displayName === "Kiru.memo"
91
- )
91
+ return typeof vNode.type === "function" && isMemoFn(vNode.type)
92
92
  }
93
93
 
94
94
  function isContextProvider(
@@ -116,10 +116,11 @@ function getVNodeAppContext(vNode: Kiru.VNode): AppContext | null {
116
116
  function commitSnapshot(vNode: Kiru.VNode): void {
117
117
  const {
118
118
  props: { children, ...props },
119
+ key,
119
120
  memoizedProps,
120
121
  index,
121
122
  } = vNode
122
- vNode.prev = { props, memoizedProps, index }
123
+ vNode.prev = { props, key, memoizedProps, index }
123
124
  vNode.flags &= ~(FLAG_UPDATE | FLAG_PLACEMENT | FLAG_DELETION)
124
125
  }
125
126
 
@@ -187,14 +188,10 @@ function assertValidElementProps(vNode: Kiru.VNode) {
187
188
  }
188
189
  }
189
190
 
190
- function isValidElementKeyProp(thing: unknown): thing is string | number {
191
- return typeof thing === "string" || typeof thing === "number"
192
- }
193
-
194
- function isValidElementRefProp(thing: unknown): thing is Kiru.Ref<any> {
195
- return (
196
- typeof thing === "function" ||
197
- (typeof thing === "object" && !!thing && "current" in thing) ||
198
- Signal.isSignal(thing)
199
- )
191
+ function normalizeElementKey(thing: unknown): JSX.ElementKey | null {
192
+ if (thing === undefined) return null
193
+ if (typeof thing === "string" || typeof thing === "number") {
194
+ return thing
195
+ }
196
+ return null
200
197
  }
package/src/vNode.ts ADDED
@@ -0,0 +1,30 @@
1
+ import { $FRAGMENT } from "./constants.js"
2
+ import { Fragment } from "./element.js"
3
+
4
+ type VNode = Kiru.VNode
5
+
6
+ export function createVNode(
7
+ type: VNode["type"],
8
+ parent: VNode | null = null,
9
+ props: VNode["props"] = {},
10
+ key: VNode["key"] = null,
11
+ index = 0
12
+ ): VNode {
13
+ if ((type as any) === Fragment) {
14
+ type = $FRAGMENT
15
+ }
16
+ const depth = parent ? parent.depth + 1 : 0
17
+ return {
18
+ type,
19
+ key,
20
+ props,
21
+ parent,
22
+ index,
23
+ depth,
24
+ flags: 0,
25
+ child: null,
26
+ sibling: null,
27
+ prev: null,
28
+ deletions: null,
29
+ }
30
+ }
@@ -1,24 +0,0 @@
1
- import { $SUSPENSE_THROW } from "../constants.js";
2
- export { Suspense, isSuspenseThrowValue };
3
- export type { SuspenseProps };
4
- type StatefulPromiseValues<T extends readonly Kiru.StatefulPromise<unknown>[]> = {
5
- [I in keyof T]: T[I] extends Kiru.StatefulPromise<infer V> ? V : never;
6
- };
7
- type SuspenseChildrenArgs<T extends Kiru.StatefulPromise<any> | Kiru.StatefulPromise<any>[]> = T extends Kiru.StatefulPromise<any>[] ? StatefulPromiseValues<T> : [T extends Kiru.StatefulPromise<infer V> ? V : never];
8
- interface SuspenseProps<T extends Kiru.StatefulPromise<any> | Kiru.StatefulPromise<any>[]> {
9
- data: T;
10
- children: (...data: SuspenseChildrenArgs<T>) => JSX.Element;
11
- fallback?: JSX.Element;
12
- }
13
- declare function Suspense<const T extends Kiru.StatefulPromise<unknown> | Kiru.StatefulPromise<unknown>[]>({ data, children, fallback }: SuspenseProps<T>): JSX.Element;
14
- interface SuspenseThrowValue {
15
- [$SUSPENSE_THROW]: {
16
- fallback?: JSX.Element;
17
- pending: Kiru.StatefulPromise<unknown>[];
18
- };
19
- }
20
- /**
21
- * Returns true if the value was thrown by a Suspense component.
22
- */
23
- declare function isSuspenseThrowValue(value: unknown): value is SuspenseThrowValue;
24
- //# sourceMappingURL=suspense.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"suspense.d.ts","sourceRoot":"","sources":["../../src/components/suspense.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAKjD,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,CAAA;AACzC,YAAY,EAAE,aAAa,EAAE,CAAA;AAE7B,KAAK,qBAAqB,CAAC,CAAC,SAAS,SAAS,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,IAC3E;KACG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK;CACvE,CAAA;AAEH,KAAK,oBAAoB,CACvB,CAAC,SAAS,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,IAC/D,CAAC,SAAS,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,GACrC,qBAAqB,CAAC,CAAC,CAAC,GACxB,CAAC,CAAC,SAAS,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,CAAA;AAEzD,UAAU,aAAa,CACrB,CAAC,SAAS,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE;IAEjE,IAAI,EAAE,CAAC,CAAA;IACP,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IAC3D,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACvB;AAED,iBAAS,QAAQ,CACf,KAAK,CAAC,CAAC,SACH,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,GAC7B,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,EACnC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC,eA0B/C;AAED,UAAU,kBAAkB;IAC1B,CAAC,eAAe,CAAC,EAAE;QACjB,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;QACtB,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,CAAA;KACzC,CAAA;CACF;AAED;;GAEG;AACH,iBAAS,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,CAEzE"}