kiru 0.47.0 → 0.48.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 (85) hide show
  1. package/dist/constants.d.ts +4 -2
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +5 -3
  4. package/dist/constants.js.map +1 -1
  5. package/dist/dom.d.ts.map +1 -1
  6. package/dist/dom.js +124 -52
  7. package/dist/dom.js.map +1 -1
  8. package/dist/element.js +2 -2
  9. package/dist/element.js.map +1 -1
  10. package/dist/hmr.d.ts +3 -2
  11. package/dist/hmr.d.ts.map +1 -1
  12. package/dist/hmr.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/memo.d.ts.map +1 -1
  18. package/dist/memo.js +1 -3
  19. package/dist/memo.js.map +1 -1
  20. package/dist/props.js +1 -1
  21. package/dist/props.js.map +1 -1
  22. package/dist/reconciler.d.ts.map +1 -1
  23. package/dist/reconciler.js +14 -11
  24. package/dist/reconciler.js.map +1 -1
  25. package/dist/renderToString.js +1 -1
  26. package/dist/renderToString.js.map +1 -1
  27. package/dist/scheduler.d.ts.map +1 -1
  28. package/dist/scheduler.js +37 -142
  29. package/dist/scheduler.js.map +1 -1
  30. package/dist/signals/base.d.ts +1 -1
  31. package/dist/signals/base.d.ts.map +1 -1
  32. package/dist/signals/base.js +2 -2
  33. package/dist/signals/base.js.map +1 -1
  34. package/dist/signals/computed.d.ts +1 -1
  35. package/dist/signals/computed.d.ts.map +1 -1
  36. package/dist/signals/computed.js +3 -3
  37. package/dist/signals/computed.js.map +1 -1
  38. package/dist/signals/effect.d.ts +1 -1
  39. package/dist/signals/effect.d.ts.map +1 -1
  40. package/dist/signals/effect.js +3 -2
  41. package/dist/signals/effect.js.map +1 -1
  42. package/dist/signals/globals.d.ts +1 -1
  43. package/dist/signals/globals.d.ts.map +1 -1
  44. package/dist/signals/jsx.d.ts +1 -1
  45. package/dist/signals/jsx.d.ts.map +1 -1
  46. package/dist/signals/types.d.ts +1 -1
  47. package/dist/signals/types.d.ts.map +1 -1
  48. package/dist/signals/utils.d.ts +1 -1
  49. package/dist/signals/utils.d.ts.map +1 -1
  50. package/dist/signals/utils.js +1 -1
  51. package/dist/signals/utils.js.map +1 -1
  52. package/dist/ssr/server.js +1 -1
  53. package/dist/ssr/server.js.map +1 -1
  54. package/dist/swr.d.ts +1 -1
  55. package/dist/swr.d.ts.map +1 -1
  56. package/dist/swr.js +1 -1
  57. package/dist/swr.js.map +1 -1
  58. package/dist/types.d.ts +0 -1
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/utils.d.ts +1 -10
  61. package/dist/utils.d.ts.map +1 -1
  62. package/dist/utils.js +3 -47
  63. package/dist/utils.js.map +1 -1
  64. package/package.json +1 -1
  65. package/src/constants.ts +7 -3
  66. package/src/dom.ts +130 -56
  67. package/src/element.ts +2 -2
  68. package/src/hmr.ts +4 -3
  69. package/src/index.ts +1 -1
  70. package/src/memo.ts +1 -3
  71. package/src/props.ts +1 -1
  72. package/src/reconciler.ts +14 -16
  73. package/src/renderToString.ts +1 -1
  74. package/src/scheduler.ts +47 -144
  75. package/src/signals/base.ts +4 -4
  76. package/src/signals/computed.ts +4 -4
  77. package/src/signals/effect.ts +4 -3
  78. package/src/signals/globals.ts +1 -1
  79. package/src/signals/jsx.ts +1 -1
  80. package/src/signals/types.ts +1 -1
  81. package/src/signals/utils.ts +1 -1
  82. package/src/ssr/server.ts +1 -1
  83. package/src/swr.ts +1 -1
  84. package/src/types.ts +0 -1
  85. package/src/utils.ts +1 -66
package/src/dom.ts CHANGED
@@ -3,18 +3,18 @@ import {
3
3
  commitSnapshot,
4
4
  propFilters,
5
5
  propToHtmlAttr,
6
- postOrderApply,
7
6
  getVNodeAppContext,
8
7
  } from "./utils.js"
9
8
  import {
10
9
  booleanAttributes,
11
- FLAG_DELETION,
12
10
  FLAG_PLACEMENT,
13
11
  FLAG_UPDATE,
14
12
  FLAG_STATIC_DOM,
15
13
  svgTags,
14
+ FLAG_NOOP,
16
15
  } from "./constants.js"
17
- import { Signal, unwrap } from "./signals/index.js"
16
+ import { Signal } from "./signals/base.js"
17
+ import { unwrap } from "./signals/utils.js"
18
18
  import { renderMode } from "./globals.js"
19
19
  import { hydrationStack } from "./hydration.js"
20
20
  import { StyleObject } from "./types.dom.js"
@@ -34,7 +34,7 @@ export { commitWork, commitDeletion, createDom, hydrateDom }
34
34
  type VNode = Kiru.VNode
35
35
  type HostNode = {
36
36
  node: ElementVNode
37
- lastChild?: DomVNode
37
+ lastChild?: SomeDom
38
38
  }
39
39
  type PlacementScope = {
40
40
  parent: VNode
@@ -550,7 +550,7 @@ function placeDom(vNode: DomVNode, hostNode: HostNode) {
550
550
  const { node: parentVNodeWithDom, lastChild } = hostNode
551
551
  const dom = vNode.dom
552
552
  if (lastChild) {
553
- lastChild.dom.after(dom)
553
+ lastChild.after(dom)
554
554
  return
555
555
  }
556
556
  // TODO: we can probably skip the 'next sibling search' if we're appending
@@ -607,72 +607,124 @@ function commitWork(vNode: VNode) {
607
607
  handlePrePlacementFocusPersistence()
608
608
 
609
609
  const hostNodes: HostNode[] = []
610
- let currentHostNode: HostNode
610
+ let currentHostNode: HostNode | undefined
611
611
  const placementScopes: PlacementScope[] = []
612
612
  let currentPlacementScope: PlacementScope | undefined
613
613
 
614
- postOrderApply(vNode, {
615
- onDescent: (node) => {
616
- if (node.dom) {
617
- // collect host nodes as we go
618
- currentHostNode = { node: node as ElementVNode }
614
+ const root = vNode
615
+ const rootChild = root.child
616
+
617
+ const onAscent = (node: VNode) => {
618
+ let inheritsPlacement = false
619
+ if (currentPlacementScope?.child === node) {
620
+ currentPlacementScope.active = true
621
+ inheritsPlacement = true
622
+ }
623
+ if (node.dom) {
624
+ if (!currentHostNode) {
625
+ currentHostNode = { node: getDomParent(node) }
619
626
  hostNodes.push(currentHostNode)
627
+ }
628
+ if (!(node.flags & FLAG_STATIC_DOM)) {
629
+ commitDom(node as DomVNode, currentHostNode, inheritsPlacement)
630
+ }
631
+ }
632
+ commitSnapshot(node)
633
+ }
620
634
 
621
- if (node.prev && "innerHTML" in node.prev.props) {
622
- /**
623
- * We need to update innerHTML during descent in cases
624
- * where we previously set innerHTML on this element but
625
- * now we provide children. Setting innerHTML _after_
626
- * appending children will yeet em into the abyss.
627
- */
628
- delete node.props.innerHTML
629
- setInnerHTML(node.dom as SomeElement, "")
630
- // remove innerHTML from prev to prevent our ascension pass from doing this again
631
- delete node.prev.props.innerHTML
632
- }
635
+ if (!rootChild) {
636
+ onAscent(root)
637
+ return
638
+ }
633
639
 
634
- if (currentPlacementScope?.active) {
635
- currentPlacementScope.child = node
636
- // prevent scope applying to descendants of this element node
637
- currentPlacementScope.active = false
638
- }
639
- } else if (node.flags & FLAG_PLACEMENT) {
640
- currentPlacementScope = { parent: node, active: true }
641
- placementScopes.push(currentPlacementScope)
640
+ const onDescent = (node: VNode) => {
641
+ if (node.dom) {
642
+ // collect host nodes as we go
643
+ currentHostNode = { node: node as ElementVNode }
644
+ hostNodes.push(currentHostNode)
645
+
646
+ if (node.prev && "innerHTML" in node.prev.props) {
647
+ /**
648
+ * We need to update innerHTML during descent in cases
649
+ * where we previously set innerHTML on this element but
650
+ * now we provide children. Setting innerHTML _after_
651
+ * appending children will yeet em into the abyss.
652
+ */
653
+ delete node.props.innerHTML
654
+ setInnerHTML(node.dom as SomeElement, "")
655
+ // remove innerHTML from prev to prevent our ascension pass from doing this again
656
+ delete node.prev.props.innerHTML
642
657
  }
643
- },
644
- onAscent: (node) => {
645
- let inheritsPlacement = false
646
- if (currentPlacementScope?.child === node) {
647
- currentPlacementScope.active = true
648
- inheritsPlacement = true
649
- }
650
- if (node.flags & FLAG_DELETION) {
651
- return commitDeletion(node)
658
+
659
+ if (currentPlacementScope?.active) {
660
+ currentPlacementScope.child = node
661
+ // prevent scope applying to descendants of this element node
662
+ currentPlacementScope.active = false
652
663
  }
653
- if (node.dom) {
654
- if (!currentHostNode) {
655
- currentHostNode = { node: getDomParent(node) }
656
- hostNodes.push(currentHostNode)
657
- }
658
- if (!(node.flags & FLAG_STATIC_DOM)) {
659
- commitDom(node as DomVNode, currentHostNode, inheritsPlacement)
664
+ } else if (node.flags & FLAG_PLACEMENT) {
665
+ currentPlacementScope = { parent: node, active: true }
666
+ placementScopes.push(currentPlacementScope)
667
+ }
668
+ }
669
+
670
+ onDescent(root)
671
+ let branch = rootChild
672
+ while (branch) {
673
+ let c = branch
674
+ while (c) {
675
+ if (!c.child) break
676
+ if (c.flags & FLAG_NOOP) {
677
+ if (c.flags & FLAG_PLACEMENT) {
678
+ const directDomChildren = findDirectDomChildren(c)
679
+ if (directDomChildren.length === 0) {
680
+ break
681
+ }
682
+ if (!currentHostNode) {
683
+ currentHostNode = { node: getDomParent(c) }
684
+ hostNodes.push(currentHostNode)
685
+ }
686
+ const { node, lastChild } = currentHostNode
687
+ if (lastChild) {
688
+ lastChild.after(...directDomChildren)
689
+ } else {
690
+ const nextSiblingDom = getNextSiblingDom(c, node)
691
+ const parentDom = node.dom
692
+ if (nextSiblingDom) {
693
+ const [first, ...rest] = directDomChildren
694
+ parentDom.insertBefore(first, nextSiblingDom)
695
+ first.after(...rest)
696
+ } else {
697
+ parentDom.append(...directDomChildren)
698
+ }
699
+ }
700
+ currentHostNode.lastChild =
701
+ directDomChildren[directDomChildren.length - 1]
660
702
  }
703
+ break
661
704
  }
662
- commitSnapshot(node)
663
- },
664
- onBeforeAscent(node) {
665
- if (currentPlacementScope?.parent === node) {
705
+ onDescent(c)
706
+ c = c.child
707
+ }
708
+
709
+ while (c && c !== root) {
710
+ onAscent(c)
711
+ if (c.sibling) {
712
+ branch = c.sibling
713
+ break
714
+ }
715
+ if (currentPlacementScope?.parent === c) {
666
716
  placementScopes.pop()
667
717
  currentPlacementScope = placementScopes[placementScopes.length - 1]
668
718
  }
669
- if (currentHostNode?.node === node.parent) {
719
+ if (currentHostNode?.node === c.parent) {
670
720
  hostNodes.pop()
671
721
  currentHostNode = hostNodes[hostNodes.length - 1]
672
722
  }
673
- },
674
- })
675
-
723
+ c = c.parent!
724
+ }
725
+ if (c === root) break
726
+ }
727
+ onAscent(root)
676
728
  handlePostPlacementFocusPersistence()
677
729
  }
678
730
 
@@ -691,7 +743,7 @@ function commitDom(
691
743
  if (!vNode.prev || vNode.flags & FLAG_UPDATE) {
692
744
  updateDom(vNode)
693
745
  }
694
- hostNode.lastChild = vNode
746
+ hostNode.lastChild = vNode.dom
695
747
  }
696
748
 
697
749
  function commitDeletion(vNode: VNode) {
@@ -729,3 +781,25 @@ function commitDeletion(vNode: VNode) {
729
781
 
730
782
  vNode.parent = null
731
783
  }
784
+
785
+ function findDirectDomChildren(vNode: VNode): SomeDom[] {
786
+ const domChildren: SomeDom[] = []
787
+
788
+ function collectDomNodes(node: VNode | null): void {
789
+ while (node) {
790
+ if (node.dom) {
791
+ // Found a DOM node, add it to the collection
792
+ domChildren.push(node.dom)
793
+ } else if (node.child) {
794
+ // This is a component or fragment, traverse its children
795
+ collectDomNodes(node.child)
796
+ }
797
+ node = node.sibling
798
+ }
799
+ }
800
+
801
+ // Start collecting from the memo's children
802
+ collectDomNodes(vNode.child)
803
+
804
+ return domChildren
805
+ }
package/src/element.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $FRAGMENT, $MEMO } from "./constants.js"
1
+ import { $FRAGMENT, $MEMO, FLAG_MEMO } from "./constants.js"
2
2
  import { isMemoFn } from "./memo.js"
3
3
  import { isValidElementKeyProp, isValidElementRefProp } from "./props.js"
4
4
 
@@ -23,7 +23,7 @@ export function createElement<T extends Kiru.VNode["type"]>(
23
23
  deletions: null,
24
24
  }
25
25
  if (isMemoFn(type)) {
26
- node.isMemoized = true
26
+ node.flags |= FLAG_MEMO
27
27
  node.arePropsEqual = type[$MEMO].arePropsEqual
28
28
  }
29
29
 
package/src/hmr.ts CHANGED
@@ -1,10 +1,11 @@
1
- import type { Store } from "./store"
2
1
  import { $HMR_ACCEPT } from "./constants.js"
3
2
  import { __DEV__ } from "./env.js"
4
3
  import { traverseApply } from "./utils.js"
5
4
  import { requestUpdate } from "./scheduler.js"
6
- import { Signal, type WatchEffect } from "./signals/index.js"
7
- import type { AppContext } from "./appContext"
5
+ import { Signal } from "./signals/base.js"
6
+ import type { WatchEffect } from "./signals/watch.js"
7
+ import type { Store } from "./store.js"
8
+ import type { AppContext } from "./appContext.js"
8
9
 
9
10
  export type HMRAccept<T = {}> = {
10
11
  provide: () => T
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ import { createKiruGlobalContext } from "./globalContext.js"
2
2
  import { __DEV__ } from "./env.js"
3
3
 
4
4
  export type * from "./types"
5
+ export * from "./signals/index.js"
5
6
  export * from "./appContext.js"
6
7
  export * from "./context.js"
7
8
  export * from "./cloneVNode.js"
@@ -11,7 +12,6 @@ export * from "./lazy.js"
11
12
  export { memo } from "./memo.js"
12
13
  export * from "./portal.js"
13
14
  export * from "./renderToString.js"
14
- export * from "./signals/index.js"
15
15
  export { nextIdle, flushSync, requestUpdate } from "./scheduler.js"
16
16
  export * from "./store.js"
17
17
  export * from "./transition.js"
package/src/memo.ts CHANGED
@@ -41,8 +41,6 @@ export function memo<T extends Record<string, unknown> = {}>(
41
41
 
42
42
  export function isMemoFn(fn: any): fn is MemoFn {
43
43
  return (
44
- typeof fn === "function" &&
45
- fn[$MEMO] &&
46
- typeof fn[$MEMO].arePropsEqual === "function"
44
+ typeof fn === "function" && typeof fn[$MEMO]?.arePropsEqual === "function"
47
45
  )
48
46
  }
package/src/props.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { KiruError } from "./error.js"
2
- import { Signal } from "./signals/index.js"
2
+ import { Signal } from "./signals/base.js"
3
3
 
4
4
  export function assertValidElementProps(vNode: Kiru.VNode) {
5
5
  if ("children" in vNode.props && vNode.props.innerHTML) {
package/src/reconciler.ts CHANGED
@@ -1,11 +1,6 @@
1
- import {
2
- $FRAGMENT,
3
- FLAG_HAS_MEMO_ANCESTOR,
4
- FLAG_PLACEMENT,
5
- FLAG_UPDATE,
6
- } from "./constants.js"
1
+ import { $FRAGMENT, FLAG_PLACEMENT, FLAG_UPDATE } from "./constants.js"
7
2
  import { getVNodeAppContext, isVNode, latest } from "./utils.js"
8
- import { Signal } from "./signals/index.js"
3
+ import { Signal } from "./signals/base.js"
9
4
  import { __DEV__ } from "./env.js"
10
5
  import { createElement, Fragment } from "./element.js"
11
6
  import type { AppContext } from "./appContext.js"
@@ -31,7 +26,6 @@ export function reconcileChildren(parent: VNode, children: unknown) {
31
26
  }
32
27
 
33
28
  function reconcileSingleChild(parent: VNode, child: unknown) {
34
- const deletions: VNode[] = (parent.deletions = [])
35
29
  const oldChild = parent.child
36
30
  if (oldChild === null) {
37
31
  return createChild(parent, child)
@@ -59,13 +53,12 @@ function reconcileSingleChild(parent: VNode, child: unknown) {
59
53
  }
60
54
  placeChild(newNode, 0, 0)
61
55
  }
62
- existingChildren.forEach((child) => deletions.push(child))
56
+ existingChildren.forEach((child) => deleteChild(parent, child))
63
57
  return newNode
64
58
  }
65
59
  }
66
60
 
67
61
  function reconcileChildrenArray(parent: VNode, children: unknown[]) {
68
- const deletions: VNode[] = (parent.deletions = [])
69
62
  let resultingChild: VNode | null = null
70
63
  let prevNewChild: VNode | null = null
71
64
 
@@ -89,7 +82,7 @@ function reconcileChildrenArray(parent: VNode, children: unknown[]) {
89
82
  break
90
83
  }
91
84
  if (oldChild && !newChild.prev) {
92
- deletions.push(oldChild)
85
+ deleteChild(parent, oldChild)
93
86
  }
94
87
  lastPlacedIndex = placeChild(newChild, lastPlacedIndex, newIdx)
95
88
  if (prevNewChild === null) {
@@ -150,7 +143,7 @@ function reconcileChildrenArray(parent: VNode, children: unknown[]) {
150
143
  }
151
144
  }
152
145
 
153
- existingChildren.forEach((child) => deletions.push(child))
146
+ existingChildren.forEach((child) => deleteChild(parent, child))
154
147
  return resultingChild
155
148
  }
156
149
 
@@ -459,9 +452,6 @@ function propsChanged(oldProps: VNode["props"], newProps: VNode["props"]) {
459
452
  function setParent(child: VNode, parent: VNode) {
460
453
  child.parent = parent
461
454
  child.depth = parent.depth + 1
462
- if (parent.isMemoized || parent.flags & FLAG_HAS_MEMO_ANCESTOR) {
463
- child.flags |= FLAG_HAS_MEMO_ANCESTOR
464
- }
465
455
  }
466
456
 
467
457
  function dev_emitUpdateNode() {
@@ -489,9 +479,17 @@ function mapRemainingChildren(child: VNode | null) {
489
479
  return map
490
480
  }
491
481
 
482
+ function deleteChild(parent: VNode, child: VNode) {
483
+ if (parent.deletions === null) {
484
+ parent.deletions = [child]
485
+ } else {
486
+ parent.deletions.push(child)
487
+ }
488
+ }
489
+
492
490
  function deleteRemainingChildren(parent: VNode, child: VNode | null) {
493
491
  while (child) {
494
- parent.deletions!.push(child)
492
+ deleteChild(parent, child)
495
493
  child = child.sibling
496
494
  }
497
495
  }
@@ -6,7 +6,7 @@ import {
6
6
  propsToElementAttributes,
7
7
  isExoticType,
8
8
  } from "./utils.js"
9
- import { Signal } from "./signals/index.js"
9
+ import { Signal } from "./signals/base.js"
10
10
  import { $HYDRATION_BOUNDARY, voidElements } from "./constants.js"
11
11
  import { assertValidElementProps } from "./props.js"
12
12
  import { HYDRATION_BOUNDARY_MARKER } from "./ssr/hydrationBoundary.js"