kiru 0.52.3 → 0.53.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 (150) 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/hooks/usePromise.d.ts +4 -9
  33. package/dist/hooks/usePromise.d.ts.map +1 -1
  34. package/dist/hooks/usePromise.js +25 -28
  35. package/dist/hooks/usePromise.js.map +1 -1
  36. package/dist/hooks/useRef.d.ts +2 -2
  37. package/dist/hooks/useRef.d.ts.map +1 -1
  38. package/dist/jsx.d.ts +1 -1
  39. package/dist/jsx.d.ts.map +1 -1
  40. package/dist/reconciler.d.ts +1 -1
  41. package/dist/reconciler.d.ts.map +1 -1
  42. package/dist/reconciler.js +57 -117
  43. package/dist/reconciler.js.map +1 -1
  44. package/dist/recursiveRender.d.ts +4 -3
  45. package/dist/recursiveRender.d.ts.map +1 -1
  46. package/dist/recursiveRender.js +20 -19
  47. package/dist/recursiveRender.js.map +1 -1
  48. package/dist/renderToString.js +2 -2
  49. package/dist/renderToString.js.map +1 -1
  50. package/dist/router/fileRouterController.d.ts +1 -1
  51. package/dist/router/fileRouterController.d.ts.map +1 -1
  52. package/dist/router/fileRouterController.js +11 -9
  53. package/dist/router/fileRouterController.js.map +1 -1
  54. package/dist/router/index.d.ts +1 -1
  55. package/dist/router/index.d.ts.map +1 -1
  56. package/dist/router/scrollStack.d.ts +0 -1
  57. package/dist/router/scrollStack.d.ts.map +1 -1
  58. package/dist/router/scrollStack.js +0 -3
  59. package/dist/router/scrollStack.js.map +1 -1
  60. package/dist/router/utils/index.d.ts +1 -1
  61. package/dist/router/utils/index.d.ts.map +1 -1
  62. package/dist/signals/base.d.ts +1 -3
  63. package/dist/signals/base.d.ts.map +1 -1
  64. package/dist/signals/base.js +3 -3
  65. package/dist/signals/base.js.map +1 -1
  66. package/dist/signals/computed.d.ts +4 -2
  67. package/dist/signals/computed.d.ts.map +1 -1
  68. package/dist/signals/computed.js +29 -6
  69. package/dist/signals/computed.js.map +1 -1
  70. package/dist/signals/for.d.ts +10 -0
  71. package/dist/signals/for.d.ts.map +1 -0
  72. package/dist/signals/for.js +7 -0
  73. package/dist/signals/for.js.map +1 -0
  74. package/dist/signals/index.d.ts +1 -1
  75. package/dist/signals/index.js +1 -1
  76. package/dist/signals/types.d.ts +1 -1
  77. package/dist/signals/types.d.ts.map +1 -1
  78. package/dist/ssr/server.js +13 -13
  79. package/dist/ssr/server.js.map +1 -1
  80. package/dist/types.d.ts +10 -11
  81. package/dist/types.d.ts.map +1 -1
  82. package/dist/types.dom.d.ts +33 -32
  83. package/dist/types.dom.d.ts.map +1 -1
  84. package/dist/types.utils.d.ts +5 -1
  85. package/dist/types.utils.d.ts.map +1 -1
  86. package/dist/utils/format.d.ts +2 -2
  87. package/dist/utils/format.d.ts.map +1 -1
  88. package/dist/utils/format.js +4 -3
  89. package/dist/utils/format.js.map +1 -1
  90. package/dist/utils/index.d.ts +0 -1
  91. package/dist/utils/index.d.ts.map +1 -1
  92. package/dist/utils/index.js +0 -1
  93. package/dist/utils/index.js.map +1 -1
  94. package/dist/utils/promise.d.ts +16 -0
  95. package/dist/utils/promise.d.ts.map +1 -0
  96. package/dist/utils/promise.js +14 -0
  97. package/dist/utils/promise.js.map +1 -0
  98. package/dist/utils/runtime.d.ts +10 -1
  99. package/dist/utils/runtime.d.ts.map +1 -1
  100. package/dist/utils/runtime.js +25 -1
  101. package/dist/utils/runtime.js.map +1 -1
  102. package/dist/utils/vdom.d.ts +4 -4
  103. package/dist/utils/vdom.d.ts.map +1 -1
  104. package/dist/utils/vdom.js +18 -17
  105. package/dist/utils/vdom.js.map +1 -1
  106. package/dist/vNode.d.ts +4 -0
  107. package/dist/vNode.d.ts.map +1 -0
  108. package/dist/vNode.js +22 -0
  109. package/dist/vNode.js.map +1 -0
  110. package/package.json +1 -1
  111. package/src/appContext.ts +15 -13
  112. package/src/components/derive.ts +121 -0
  113. package/src/components/index.ts +1 -1
  114. package/src/components/memo.ts +3 -2
  115. package/src/constants.ts +4 -4
  116. package/src/dom.ts +71 -66
  117. package/src/element.ts +22 -35
  118. package/src/globalContext.ts +24 -3
  119. package/src/hooks/usePromise.ts +32 -41
  120. package/src/hooks/useRef.ts +2 -2
  121. package/src/reconciler.ts +87 -125
  122. package/src/recursiveRender.ts +25 -23
  123. package/src/renderToString.ts +3 -3
  124. package/src/router/fileRouterController.ts +19 -9
  125. package/src/router/scrollStack.ts +23 -26
  126. package/src/signals/base.ts +3 -3
  127. package/src/signals/computed.ts +43 -6
  128. package/src/signals/for.ts +25 -0
  129. package/src/signals/index.ts +1 -1
  130. package/src/signals/types.ts +5 -1
  131. package/src/ssr/server.ts +15 -15
  132. package/src/types.dom.ts +40 -40
  133. package/src/types.ts +11 -11
  134. package/src/types.utils.ts +11 -1
  135. package/src/utils/format.ts +7 -4
  136. package/src/utils/index.ts +0 -2
  137. package/src/utils/promise.ts +26 -0
  138. package/src/utils/runtime.ts +29 -1
  139. package/src/utils/vdom.ts +20 -23
  140. package/src/vNode.ts +30 -0
  141. package/dist/components/suspense.d.ts +0 -24
  142. package/dist/components/suspense.d.ts.map +0 -1
  143. package/dist/components/suspense.js +0 -36
  144. package/dist/components/suspense.js.map +0 -1
  145. package/dist/signals/jsx.d.ts +0 -17
  146. package/dist/signals/jsx.d.ts.map +0 -1
  147. package/dist/signals/jsx.js +0 -11
  148. package/dist/signals/jsx.js.map +0 -1
  149. package/src/components/suspense.ts +0 -72
  150. package/src/signals/jsx.ts +0 -46
package/src/element.ts CHANGED
@@ -1,46 +1,29 @@
1
- import { $FRAGMENT, $MEMO, FLAG_MEMO } from "./constants.js"
2
- import { isMemoFn } from "./components/memo.js"
3
- import { isValidElementKeyProp, isValidElementRefProp } from "./utils/index.js"
1
+ import { $FRAGMENT } from "./constants.js"
2
+ import { normalizeElementKey } from "./utils/index.js"
4
3
 
5
4
  export function createElement<T extends Kiru.VNode["type"]>(
6
5
  type: T,
7
6
  props: null | Record<string, unknown> = null,
8
7
  ...children: unknown[]
9
- ): Kiru.VNode {
10
- if ((type as any) === Fragment) {
11
- return Fragment({ children: children as any, ...props })
12
- }
13
- const node: Kiru.VNode = {
14
- type,
15
- flags: 0,
16
- index: 0,
17
- depth: 0,
18
- props: {},
19
- parent: null,
20
- sibling: null,
21
- child: null,
22
- prev: null,
23
- deletions: null,
24
- }
25
- if (isMemoFn(type)) {
26
- node.flags |= FLAG_MEMO
27
- node.arePropsEqual = type[$MEMO].arePropsEqual
8
+ ): Kiru.Element {
9
+ if ((type as unknown) === Fragment) {
10
+ type = $FRAGMENT as T
28
11
  }
12
+ const p = props === null ? {} : props
13
+ const key = normalizeElementKey(p.key)
29
14
 
30
- if (props !== null) {
31
- const { key, ref, ...rest } = props
32
- if (isValidElementKeyProp(key)) node.props.key = key.toString()
33
- if (isValidElementRefProp(ref)) node.props.ref = ref
34
- Object.assign(node.props, rest)
15
+ const len = children.length
16
+ if (len === 1) {
17
+ p.children = children[0]
18
+ } else if (len > 1) {
19
+ p.children = children
35
20
  }
36
21
 
37
- const _children =
38
- children.length === 1 ? children[0] : children.length > 1 ? children : null
39
- if (_children !== null) {
40
- node.props.children = _children
22
+ return {
23
+ type,
24
+ key,
25
+ props: p,
41
26
  }
42
-
43
- return node
44
27
  }
45
28
 
46
29
  export function Fragment({
@@ -49,6 +32,10 @@ export function Fragment({
49
32
  }: {
50
33
  children: JSX.Children
51
34
  key?: JSX.ElementKey
52
- }): Kiru.VNode {
53
- return createElement($FRAGMENT, key ? { key } : null, children)
35
+ }): Kiru.Element {
36
+ return {
37
+ type: $FRAGMENT,
38
+ key: normalizeElementKey(key),
39
+ props: { children },
40
+ }
54
41
  }
@@ -6,6 +6,7 @@ import type { FileRouterController } from "./router/fileRouterController"
6
6
  import type { AppContext } from "./appContext"
7
7
  import type { Store } from "./store"
8
8
  import type { SWRCache } from "./swr"
9
+ import type { requestUpdate } from "./index.js"
9
10
 
10
11
  export { createKiruGlobalContext, type GlobalKiruEvent, type KiruGlobalContext }
11
12
 
@@ -56,7 +57,7 @@ function createReactiveMap<V>(): ReactiveMap<V> {
56
57
  type Evt =
57
58
  | {
58
59
  name: "mount"
59
- data?: undefined
60
+ data?: typeof requestUpdate
60
61
  }
61
62
  | {
62
63
  name: "unmount"
@@ -73,6 +74,10 @@ type Evt =
73
74
 
74
75
  type GlobalKiruEvent = Evt["name"]
75
76
 
77
+ interface SchedulerInterface {
78
+ requestUpdate: (vNode: Kiru.VNode) => void
79
+ }
80
+
76
81
  interface KiruGlobalContext {
77
82
  readonly apps: AppContext[]
78
83
  emit<T extends Evt>(event: T["name"], ctx: AppContext, data?: T["data"]): void
@@ -91,10 +96,15 @@ interface KiruGlobalContext {
91
96
  fileRouterInstance?: {
92
97
  current: FileRouterController | null
93
98
  }
99
+ getSchedulerInterface?: (app: AppContext) => SchedulerInterface | null
94
100
  }
95
101
 
96
102
  function createKiruGlobalContext(): KiruGlobalContext {
97
103
  const contexts = new Set<AppContext>()
104
+ const contextToSchedulerInterface = new WeakMap<
105
+ AppContext,
106
+ SchedulerInterface
107
+ >()
98
108
  const listeners = new Map<
99
109
  GlobalKiruEvent,
100
110
  Set<(ctx: AppContext, data?: Evt["data"]) => void>
@@ -134,14 +144,25 @@ function createKiruGlobalContext(): KiruGlobalContext {
134
144
  }
135
145
 
136
146
  // Initialize event listeners
137
- on("mount", (ctx) => contexts.add(ctx))
138
- on("unmount", (ctx) => contexts.delete(ctx))
147
+ on("mount", (ctx, requestUpdate) => {
148
+ contexts.add(ctx)
149
+ if (requestUpdate && typeof requestUpdate === "function") {
150
+ contextToSchedulerInterface.set(ctx, { requestUpdate })
151
+ }
152
+ })
153
+ on("unmount", (ctx) => {
154
+ contexts.delete(ctx)
155
+ contextToSchedulerInterface.delete(ctx)
156
+ })
139
157
 
140
158
  if (__DEV__) {
141
159
  globalContext.HMRContext = createHMRContext()
142
160
  globalContext.profilingContext = createProfilingContext()
143
161
  globalContext.stores = createReactiveMap()
144
162
  globalContext.fileRouterInstance = fileRouterInstance
163
+ globalContext.getSchedulerInterface = (app) => {
164
+ return contextToSchedulerInterface.get(app) ?? null
165
+ }
145
166
  }
146
167
 
147
168
  return globalContext
@@ -1,43 +1,36 @@
1
- import { PREFETCHED_DATA_EVENT } from "../constants.js"
1
+ import { STREAMED_DATA_EVENT } from "../constants.js"
2
2
  import { __DEV__ } from "../env.js"
3
3
  import { hydrationMode, renderMode } from "../globals.js"
4
- import { requestUpdate } from "../scheduler.js"
5
4
  import { Signal, useSignal } from "../signals/base.js"
6
5
  import { cleanupHook, depsRequireChange, useHook } from "./utils.js"
7
6
  import { useId } from "./useId.js"
8
7
 
9
8
  export { usePromise }
10
- export type { UsePromiseCallbackContext, UsePromiseState }
11
9
 
12
10
  const nodeToPromiseIndex = new WeakMap<Kiru.VNode, number>()
13
11
 
14
- interface UsePromiseCallbackContext {
15
- signal: AbortSignal
16
- }
17
-
18
- interface UsePromiseState<T> {
19
- data: Kiru.StatefulPromise<T>
12
+ type UsePromiseResult<T> = Kiru.StatefulPromise<T> & {
20
13
  refresh: () => void
21
- pending: Signal<boolean>
14
+ isPending: Signal<boolean>
22
15
  }
23
16
 
24
17
  function usePromise<T>(
25
- callback: (ctx: UsePromiseCallbackContext) => Promise<T>,
18
+ callback: (signal: AbortSignal) => Promise<T>,
26
19
  deps: unknown[]
27
- ): UsePromiseState<T> {
20
+ ): UsePromiseResult<T> {
28
21
  const id = useId()
29
- const pending = useSignal(true)
22
+ const isPending = useSignal(true)
30
23
 
31
24
  return useHook(
32
25
  "usePromise",
33
26
  {} as {
34
- promise: Kiru.StatefulPromise<T>
27
+ promise: UsePromiseResult<T>
35
28
  abortController?: AbortController
36
29
  deps?: unknown[]
37
30
  },
38
- ({ hook, isInit, vNode }) => {
31
+ ({ hook, isInit, vNode, update }) => {
39
32
  if (isInit || depsRequireChange(deps, hook.deps)) {
40
- pending.value = true
33
+ isPending.value = true
41
34
  hook.deps = deps
42
35
  cleanupHook(hook)
43
36
 
@@ -59,77 +52,75 @@ function usePromise<T>(
59
52
  ) {
60
53
  // if we're hydrating and the hydration mode is not static,
61
54
  // we need to resolve the promise from cache/event
62
- promise = resolvePrefetchedPromise<T>(promiseId, controller.signal)
55
+ promise = resolveDeferredPromise<T>(promiseId, controller.signal)
63
56
  } else {
64
57
  // dom / stream / (hydrate + static)
65
- promise = callback({ signal: controller.signal })
58
+ promise = callback(controller.signal)
66
59
  }
67
60
 
68
61
  const state: Kiru.PromiseState<T> = { id: promiseId, state: "pending" }
69
- const statefulPromise = (hook.promise = Object.assign(promise, state))
62
+ const statefulPromise: Kiru.StatefulPromise<T> = (hook.promise =
63
+ Object.assign(promise, state, {
64
+ isPending,
65
+ refresh: () => {
66
+ hook.deps = undefined
67
+ update()
68
+ },
69
+ }))
70
70
 
71
71
  statefulPromise
72
72
  .then((value) => {
73
73
  statefulPromise.state = "fulfilled"
74
74
  statefulPromise.value = value
75
+ isPending.value = false
75
76
  })
76
77
  .catch((error) => {
77
78
  statefulPromise.state = "rejected"
78
79
  statefulPromise.error =
79
80
  error instanceof Error ? error : new Error(error)
80
81
  })
81
- .finally(() => {
82
- pending.value = false
83
- })
84
- }
85
- return {
86
- data: hook.promise,
87
- refresh: () => {
88
- hook.deps = undefined
89
- requestUpdate(vNode)
90
- },
91
- pending,
92
82
  }
83
+ return hook.promise
93
84
  }
94
85
  )
95
86
  }
96
87
 
97
- interface PrefetchedPromiseEventDetail<T> {
88
+ interface DeferredPromiseEventDetail<T> {
98
89
  id: string
99
90
  data?: T
100
91
  error?: string
101
92
  }
102
93
 
103
- function resolvePrefetchedPromise<T>(
94
+ function resolveDeferredPromise<T>(
104
95
  id: string,
105
96
  signal: AbortSignal
106
97
  ): Promise<T> {
107
98
  return new Promise<T>((resolve, reject) => {
108
- const prefetchCache: Map<string, { data?: T; error?: string }> = // @ts-ignore
109
- (window[PREFETCHED_DATA_EVENT] ??= new Map())
99
+ const deferralCache: Map<string, { data?: T; error?: string }> = // @ts-ignore
100
+ (window[STREAMED_DATA_EVENT] ??= new Map())
110
101
 
111
- const existing = prefetchCache.get(id)
102
+ const existing = deferralCache.get(id)
112
103
  if (existing) {
113
104
  const { data, error } = existing
114
- prefetchCache.delete(id)
105
+ deferralCache.delete(id)
115
106
  if (error) return reject(error)
116
107
  return resolve(data!)
117
108
  }
118
109
 
119
110
  const onDataEvent = (event: Event) => {
120
- const { detail } = event as CustomEvent<PrefetchedPromiseEventDetail<T>>
111
+ const { detail } = event as CustomEvent<DeferredPromiseEventDetail<T>>
121
112
  if (detail.id === id) {
122
- prefetchCache.delete(id)
123
- window.removeEventListener(PREFETCHED_DATA_EVENT, onDataEvent)
113
+ deferralCache.delete(id)
114
+ window.removeEventListener(STREAMED_DATA_EVENT, onDataEvent)
124
115
  const { data, error } = detail
125
116
  if (error) return reject(error)
126
117
  resolve(data!)
127
118
  }
128
119
  }
129
120
 
130
- window.addEventListener(PREFETCHED_DATA_EVENT, onDataEvent)
121
+ window.addEventListener(STREAMED_DATA_EVENT, onDataEvent)
131
122
  signal.addEventListener("abort", () => {
132
- window.removeEventListener(PREFETCHED_DATA_EVENT, onDataEvent)
123
+ window.removeEventListener(STREAMED_DATA_EVENT, onDataEvent)
133
124
  reject()
134
125
  })
135
126
  })
@@ -8,9 +8,9 @@ import { useHook } from "./utils.js"
8
8
  *
9
9
  * @see https://kirujs.dev/docs/hooks/useRef
10
10
  */
11
- export function useRef<T>(initialValue: T): Kiru.MutableRefObject<T>
11
+ export function useRef<T>(initialValue: T): Kiru.RefObject<T>
12
12
  export function useRef<T>(initialValue: T | null): Kiru.RefObject<T>
13
- export function useRef<T = undefined>(): Kiru.MutableRefObject<T | undefined>
13
+ export function useRef<T = undefined>(): Kiru.RefObject<T | undefined>
14
14
  export function useRef<T>(initialValue?: T | null) {
15
15
  if (!sideEffectsEnabled()) return { current: initialValue }
16
16
  return useHook(