kiru 0.46.0 → 0.47.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 (62) hide show
  1. package/dist/appContext.d.ts +6 -17
  2. package/dist/appContext.d.ts.map +1 -1
  3. package/dist/appContext.js +39 -95
  4. package/dist/appContext.js.map +1 -1
  5. package/dist/constants.d.ts +2 -1
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/constants.js +2 -1
  8. package/dist/constants.js.map +1 -1
  9. package/dist/context.d.ts.map +1 -1
  10. package/dist/context.js +0 -2
  11. package/dist/context.js.map +1 -1
  12. package/dist/dom.d.ts.map +1 -1
  13. package/dist/dom.js +10 -13
  14. package/dist/dom.js.map +1 -1
  15. package/dist/hmr.d.ts +1 -2
  16. package/dist/hmr.d.ts.map +1 -1
  17. package/dist/hmr.js +0 -2
  18. package/dist/hmr.js.map +1 -1
  19. package/dist/index.d.ts +0 -3
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +0 -20
  22. package/dist/index.js.map +1 -1
  23. package/dist/portal.d.ts +2 -5
  24. package/dist/portal.d.ts.map +1 -1
  25. package/dist/portal.js +22 -22
  26. package/dist/portal.js.map +1 -1
  27. package/dist/profiling.d.ts +1 -1
  28. package/dist/renderToString.d.ts +1 -1
  29. package/dist/renderToString.d.ts.map +1 -1
  30. package/dist/renderToString.js +3 -6
  31. package/dist/renderToString.js.map +1 -1
  32. package/dist/scheduler.d.ts +11 -2
  33. package/dist/scheduler.d.ts.map +1 -1
  34. package/dist/scheduler.js +59 -71
  35. package/dist/scheduler.js.map +1 -1
  36. package/dist/ssr/client.d.ts +1 -2
  37. package/dist/ssr/client.d.ts.map +1 -1
  38. package/dist/ssr/client.js +6 -4
  39. package/dist/ssr/client.js.map +1 -1
  40. package/dist/ssr/server.d.ts +1 -1
  41. package/dist/ssr/server.d.ts.map +1 -1
  42. package/dist/ssr/server.js +24 -27
  43. package/dist/ssr/server.js.map +1 -1
  44. package/dist/swr.d.ts +1 -1
  45. package/dist/swr.d.ts.map +1 -1
  46. package/dist/swr.js +1 -1
  47. package/dist/swr.js.map +1 -1
  48. package/dist/utils.js +1 -1
  49. package/package.json +1 -1
  50. package/src/appContext.ts +50 -117
  51. package/src/constants.ts +2 -0
  52. package/src/context.ts +0 -1
  53. package/src/dom.ts +10 -10
  54. package/src/hmr.ts +1 -3
  55. package/src/index.ts +0 -40
  56. package/src/portal.ts +22 -26
  57. package/src/renderToString.ts +3 -9
  58. package/src/scheduler.ts +59 -74
  59. package/src/ssr/client.ts +13 -19
  60. package/src/ssr/server.ts +25 -35
  61. package/src/swr.ts +1 -1
  62. package/src/utils.ts +1 -1
package/src/scheduler.ts CHANGED
@@ -31,7 +31,7 @@ let appCtx: AppContext | null
31
31
  let nextUnitOfWork: VNode | null = null
32
32
  let treesInProgress: VNode[] = []
33
33
  let currentTreeIndex = 0
34
- let isRunning = false
34
+ let isRunningOrQueued = false
35
35
  let nextIdleEffects: (() => void)[] = []
36
36
  let deletions: VNode[] = []
37
37
  let isImmediateEffectsMode = false
@@ -41,23 +41,34 @@ let consecutiveDirtyCount = 0
41
41
  let pendingContextChanges = new Set<ContextProviderNode<any>>()
42
42
  let preEffects: Array<Function> = []
43
43
  let postEffects: Array<Function> = []
44
-
45
- export function nextIdle(fn: () => void, wakeUpIfIdle = true) {
46
- nextIdleEffects.push(fn)
47
- if (wakeUpIfIdle) wake()
44
+ let animationFrameHandle = -1
45
+
46
+ /**
47
+ * Runs a function after any existing work has been completed,
48
+ * or immediately if the scheduler is already idle.
49
+ */
50
+ export function nextIdle(fn: () => void) {
51
+ if (isRunningOrQueued) {
52
+ nextIdleEffects.push(fn)
53
+ return
54
+ }
55
+ fn()
48
56
  }
49
57
 
58
+ /**
59
+ * Syncronously flushes any pending work.
60
+ */
50
61
  export function flushSync() {
51
- workLoop()
62
+ if (!isRunningOrQueued) return
63
+ window.cancelAnimationFrame(animationFrameHandle)
64
+ doWork()
52
65
  }
53
66
 
67
+ /**
68
+ * Queues a node for an update. Has no effect if the node is already deleted or marked for deletion.
69
+ */
54
70
  export function requestUpdate(vNode: VNode): void {
55
71
  if (vNode.flags & FLAG_DELETION) return
56
- if (__DEV__) {
57
- // if (options?.debug?.onRequestUpdate) {
58
- // options.debug.onRequestUpdate(vNode)
59
- // }
60
- }
61
72
  if (renderMode.current === "hydrate") {
62
73
  return nextIdle(() => {
63
74
  vNode.flags & FLAG_DELETION || queueUpdate(vNode)
@@ -66,28 +77,17 @@ export function requestUpdate(vNode: VNode): void {
66
77
  queueUpdate(vNode)
67
78
  }
68
79
 
69
- export function requestDelete(vNode: VNode): void {
70
- if (vNode.flags & FLAG_DELETION) return
71
- if (renderMode.current === "hydrate") {
72
- return nextIdle(() => {
73
- vNode.flags & FLAG_DELETION || queueDelete(vNode)
74
- })
75
- }
76
- queueDelete(vNode)
77
- }
78
-
79
- function queueWorkLoop() {
80
- queueMicrotask(workLoop)
80
+ function queueBeginWork() {
81
+ if (isRunningOrQueued) return
82
+ isRunningOrQueued = true
83
+ animationFrameHandle = window.requestAnimationFrame(doWork)
81
84
  }
82
85
 
83
- function wake() {
84
- if (isRunning) return
85
- isRunning = true
86
- queueWorkLoop()
87
- }
88
-
89
- function sleep() {
90
- isRunning = false
86
+ function onWorkFinished() {
87
+ isRunningOrQueued = false
88
+ while (nextIdleEffects.length) {
89
+ nextIdleEffects.shift()!()
90
+ }
91
91
  }
92
92
 
93
93
  function queueUpdate(vNode: VNode) {
@@ -113,7 +113,7 @@ function queueUpdate(vNode: VNode) {
113
113
  if (nextUnitOfWork === null) {
114
114
  treesInProgress.push(vNode)
115
115
  nextUnitOfWork = vNode
116
- return wake()
116
+ return queueBeginWork()
117
117
  }
118
118
 
119
119
  for (let i = 0; i < treesInProgress.length; i++) {
@@ -198,7 +198,7 @@ function queueDelete(vNode: VNode) {
198
198
  deletions.push(vNode)
199
199
  }
200
200
 
201
- function workLoop(): void {
201
+ function doWork(): void {
202
202
  if (__DEV__) {
203
203
  const n = nextUnitOfWork ?? deletions[0] ?? treesInProgress[0]
204
204
  if (n) {
@@ -216,55 +216,40 @@ function workLoop(): void {
216
216
  queueBlockedContextDependencyRoots()
217
217
  }
218
218
 
219
- if (!nextUnitOfWork && (deletions.length || treesInProgress.length)) {
220
- while (deletions.length) {
221
- commitDeletion(deletions.shift()!)
222
- }
223
- const workRoots = [...treesInProgress]
224
- treesInProgress.length = 0
225
- currentTreeIndex = 0
226
- for (const root of workRoots) {
227
- commitWork(root)
228
- }
229
-
230
- isImmediateEffectsMode = true
231
- flushEffects(preEffects)
232
- isImmediateEffectsMode = false
219
+ while (deletions.length) {
220
+ commitDeletion(deletions.shift()!)
221
+ }
222
+ const workRoots = [...treesInProgress]
223
+ treesInProgress.length = 0
224
+ currentTreeIndex = 0
225
+ for (const root of workRoots) {
226
+ commitWork(root)
227
+ }
233
228
 
234
- if (immediateEffectDirtiedRender) {
235
- checkForTooManyConsecutiveDirtyRenders()
236
- flushEffects(postEffects)
237
- immediateEffectDirtiedRender = false
238
- consecutiveDirtyCount++
239
- if (__DEV__) {
240
- window.__kiru?.profilingContext?.endTick(appCtx!)
241
- window.__kiru?.profilingContext?.emit("updateDirtied", appCtx!)
242
- }
243
- return flushSync()
244
- }
245
- consecutiveDirtyCount = 0
229
+ isImmediateEffectsMode = true
230
+ flushEffects(preEffects)
231
+ isImmediateEffectsMode = false
246
232
 
233
+ if (immediateEffectDirtiedRender) {
234
+ checkForTooManyConsecutiveDirtyRenders()
247
235
  flushEffects(postEffects)
236
+ immediateEffectDirtiedRender = false
237
+ consecutiveDirtyCount++
248
238
  if (__DEV__) {
249
- window.__kiru!.emit("update", appCtx!)
250
- window.__kiru?.profilingContext?.emit("update", appCtx!)
239
+ window.__kiru?.profilingContext?.endTick(appCtx!)
240
+ window.__kiru?.profilingContext?.emit("updateDirtied", appCtx!)
251
241
  }
242
+ return flushSync()
252
243
  }
244
+ consecutiveDirtyCount = 0
253
245
 
254
- if (!nextUnitOfWork) {
255
- sleep()
256
- while (nextIdleEffects.length) {
257
- nextIdleEffects.shift()!()
258
- }
259
- if (__DEV__) {
260
- if (appCtx) {
261
- window.__kiru?.profilingContext?.endTick(appCtx)
262
- }
263
- }
264
- return
246
+ onWorkFinished()
247
+ flushEffects(postEffects)
248
+ if (__DEV__) {
249
+ window.__kiru!.emit("update", appCtx!)
250
+ window.__kiru?.profilingContext?.emit("update", appCtx!)
251
+ window.__kiru?.profilingContext?.endTick(appCtx!)
265
252
  }
266
-
267
- queueWorkLoop()
268
253
  }
269
254
 
270
255
  function queueBlockedContextDependencyRoots(): VNode | null {
package/src/ssr/client.ts CHANGED
@@ -3,27 +3,21 @@ import { hydrationStack } from "../hydration.js"
3
3
  import { renderMode } from "../globals.js"
4
4
  import { mount } from "../index.js"
5
5
 
6
- export function hydrate<T extends Record<string, unknown>>(
7
- appFunc: (props: T) => JSX.Element,
8
- container: AppContextOptions,
9
- appProps?: T
10
- ): Promise<AppContext<T>>
11
-
12
- export function hydrate<T extends Record<string, unknown>>(
13
- appFunc: (props: T) => JSX.Element,
6
+ export function hydrate(
7
+ children: JSX.Element,
14
8
  container: HTMLElement,
15
- appProps?: T
16
- ): Promise<AppContext<T>>
17
-
18
- export function hydrate<T extends Record<string, unknown>>(
19
- appFunc: (props: T) => JSX.Element,
20
- optionsOrRoot: HTMLElement | AppContextOptions,
21
- appProps = {} as T
22
- ) {
9
+ options?: AppContextOptions
10
+ ): AppContext {
23
11
  hydrationStack.clear()
12
+
24
13
  const prevRenderMode = renderMode.current
25
14
  renderMode.current = "hydrate"
26
- return mount(appFunc, optionsOrRoot as any, appProps).finally(() => {
27
- renderMode.current = prevRenderMode
28
- })
15
+ hydrationStack.captureEvents(container)
16
+
17
+ const app = mount(children, container, options)
18
+
19
+ renderMode.current = prevRenderMode
20
+ hydrationStack.releaseEvents(container)
21
+
22
+ return app
29
23
  }
package/src/ssr/server.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Readable } from "node:stream"
2
2
  import { Fragment } from "../element.js"
3
- import { AppContext, createAppContext } from "../appContext.js"
4
3
  import { renderMode, node } from "../globals.js"
5
4
  import {
6
5
  isVNode,
@@ -8,36 +7,27 @@ import {
8
7
  propsToElementAttributes,
9
8
  isExoticType,
10
9
  } from "../utils.js"
11
- import { Signal } from "../signals/base.js"
10
+ import { Signal } from "../signals/index.js"
12
11
  import { $HYDRATION_BOUNDARY, voidElements } from "../constants.js"
13
12
  import { assertValidElementProps } from "../props.js"
14
13
  import { HYDRATION_BOUNDARY_MARKER } from "./hydrationBoundary.js"
15
14
  import { __DEV__ } from "../env.js"
16
15
 
17
- type RequestState = {
18
- stream: Readable
19
- ctx: AppContext
20
- }
21
-
22
- export function renderToReadableStream<T extends Record<string, unknown>>(
23
- appFunc: (props: T) => JSX.Element,
24
- appProps = {} as T
25
- ): Readable {
16
+ export function renderToReadableStream(element: JSX.Element): Readable {
26
17
  const prev = renderMode.current
27
18
  renderMode.current = "stream"
28
- const state: RequestState = {
29
- stream: new Readable(),
30
- ctx: createAppContext(appFunc, appProps, { rootType: Fragment }),
31
- }
32
- renderToStream_internal(state, state.ctx.rootNode, null, 0)
33
- state.stream.push(null)
19
+ const stream = new Readable()
20
+ const rootNode = Fragment({ children: element })
21
+
22
+ renderToStream_internal(stream, rootNode, null, 0)
23
+ stream.push(null)
34
24
  renderMode.current = prev
35
25
 
36
- return state.stream
26
+ return stream
37
27
  }
38
28
 
39
29
  function renderToStream_internal(
40
- state: RequestState,
30
+ stream: Readable,
41
31
  el: unknown,
42
32
  parent: Kiru.VNode | null,
43
33
  idx: number
@@ -46,23 +36,23 @@ function renderToStream_internal(
46
36
  if (el === undefined) return
47
37
  if (typeof el === "boolean") return
48
38
  if (typeof el === "string") {
49
- state.stream.push(encodeHtmlEntities(el))
39
+ stream.push(encodeHtmlEntities(el))
50
40
  return
51
41
  }
52
42
  if (typeof el === "number" || typeof el === "bigint") {
53
- state.stream.push(el.toString())
43
+ stream.push(el.toString())
54
44
  return
55
45
  }
56
46
  if (el instanceof Array) {
57
- el.forEach((c, i) => renderToStream_internal(state, c, parent, i))
47
+ el.forEach((c, i) => renderToStream_internal(stream, c, parent, i))
58
48
  return
59
49
  }
60
50
  if (Signal.isSignal(el)) {
61
- state.stream.push(String(el.peek()))
51
+ stream.push(String(el.peek()))
62
52
  return
63
53
  }
64
54
  if (!isVNode(el)) {
65
- state.stream.push(String(el))
55
+ stream.push(String(el))
66
56
  return
67
57
  }
68
58
  el.parent = parent
@@ -72,35 +62,35 @@ function renderToStream_internal(
72
62
  const children = props.children
73
63
  const type = el.type
74
64
  if (type === "#text") {
75
- state.stream.push(encodeHtmlEntities(props.nodeValue ?? ""))
65
+ stream.push(encodeHtmlEntities(props.nodeValue ?? ""))
76
66
  return
77
67
  }
78
68
  if (isExoticType(type)) {
79
69
  if (type === $HYDRATION_BOUNDARY) {
80
- state.stream.push(`<!--${HYDRATION_BOUNDARY_MARKER}-->`)
81
- renderToStream_internal(state, children, el, idx)
82
- state.stream.push(`<!--/${HYDRATION_BOUNDARY_MARKER}-->`)
70
+ stream.push(`<!--${HYDRATION_BOUNDARY_MARKER}-->`)
71
+ renderToStream_internal(stream, children, el, idx)
72
+ stream.push(`<!--/${HYDRATION_BOUNDARY_MARKER}-->`)
83
73
  return
84
74
  }
85
- return renderToStream_internal(state, children, el, idx)
75
+ return renderToStream_internal(stream, children, el, idx)
86
76
  }
87
77
 
88
78
  if (typeof type !== "string") {
89
79
  node.current = el
90
80
  const res = type(props)
91
81
  node.current = null
92
- return renderToStream_internal(state, res, parent, idx)
82
+ return renderToStream_internal(stream, res, parent, idx)
93
83
  }
94
84
 
95
85
  if (__DEV__) {
96
86
  assertValidElementProps(el)
97
87
  }
98
88
  const attrs = propsToElementAttributes(props)
99
- state.stream.push(`<${type}${attrs.length ? ` ${attrs}` : ""}>`)
89
+ stream.push(`<${type}${attrs.length ? ` ${attrs}` : ""}>`)
100
90
 
101
91
  if (!voidElements.has(type)) {
102
92
  if ("innerHTML" in props) {
103
- state.stream.push(
93
+ stream.push(
104
94
  String(
105
95
  Signal.isSignal(props.innerHTML)
106
96
  ? props.innerHTML.peek()
@@ -109,12 +99,12 @@ function renderToStream_internal(
109
99
  )
110
100
  } else {
111
101
  if (Array.isArray(children)) {
112
- children.forEach((c, i) => renderToStream_internal(state, c, el, i))
102
+ children.forEach((c, i) => renderToStream_internal(stream, c, el, i))
113
103
  } else {
114
- renderToStream_internal(state, children, el, 0)
104
+ renderToStream_internal(stream, children, el, 0)
115
105
  }
116
106
  }
117
107
 
118
- state.stream.push(`</${type}>`)
108
+ stream.push(`</${type}>`)
119
109
  }
120
110
  }
package/src/swr.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { __DEV__ } from "./env.js"
2
2
  import { useHook } from "./hooks/utils.js"
3
- import { Signal } from "./signals/base.js"
3
+ import { Signal } from "./signals/index.js"
4
4
  import {
5
5
  noop,
6
6
  deepCompare,
package/src/utils.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  FLAG_UPDATE,
11
11
  REGEX_UNIT,
12
12
  } from "./constants.js"
13
- import { unwrap } from "./signals/utils.js"
13
+ import { unwrap } from "./signals/index.js"
14
14
  import { __DEV__ } from "./env.js"
15
15
  import type { AppContext } from "./appContext"
16
16