kiru 0.47.1 → 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.
- package/dist/constants.d.ts +3 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +5 -4
- package/dist/constants.js.map +1 -1
- package/dist/dom.d.ts.map +1 -1
- package/dist/dom.js +122 -51
- package/dist/dom.js.map +1 -1
- package/dist/reconciler.d.ts.map +1 -1
- package/dist/reconciler.js +13 -10
- package/dist/reconciler.js.map +1 -1
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +35 -140
- package/dist/scheduler.js.map +1 -1
- package/dist/utils.d.ts +1 -10
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -48
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +6 -4
- package/src/dom.ts +128 -55
- package/src/reconciler.ts +13 -16
- package/src/scheduler.ts +44 -142
- package/src/utils.ts +0 -67
package/src/dom.ts
CHANGED
|
@@ -3,16 +3,15 @@ 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
16
|
import { Signal } from "./signals/base.js"
|
|
18
17
|
import { unwrap } from "./signals/utils.js"
|
|
@@ -35,7 +34,7 @@ export { commitWork, commitDeletion, createDom, hydrateDom }
|
|
|
35
34
|
type VNode = Kiru.VNode
|
|
36
35
|
type HostNode = {
|
|
37
36
|
node: ElementVNode
|
|
38
|
-
lastChild?:
|
|
37
|
+
lastChild?: SomeDom
|
|
39
38
|
}
|
|
40
39
|
type PlacementScope = {
|
|
41
40
|
parent: VNode
|
|
@@ -551,7 +550,7 @@ function placeDom(vNode: DomVNode, hostNode: HostNode) {
|
|
|
551
550
|
const { node: parentVNodeWithDom, lastChild } = hostNode
|
|
552
551
|
const dom = vNode.dom
|
|
553
552
|
if (lastChild) {
|
|
554
|
-
lastChild.
|
|
553
|
+
lastChild.after(dom)
|
|
555
554
|
return
|
|
556
555
|
}
|
|
557
556
|
// TODO: we can probably skip the 'next sibling search' if we're appending
|
|
@@ -608,72 +607,124 @@ function commitWork(vNode: VNode) {
|
|
|
608
607
|
handlePrePlacementFocusPersistence()
|
|
609
608
|
|
|
610
609
|
const hostNodes: HostNode[] = []
|
|
611
|
-
let currentHostNode: HostNode
|
|
610
|
+
let currentHostNode: HostNode | undefined
|
|
612
611
|
const placementScopes: PlacementScope[] = []
|
|
613
612
|
let currentPlacementScope: PlacementScope | undefined
|
|
614
613
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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) }
|
|
620
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
|
+
}
|
|
621
634
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
* now we provide children. Setting innerHTML _after_
|
|
627
|
-
* appending children will yeet em into the abyss.
|
|
628
|
-
*/
|
|
629
|
-
delete node.props.innerHTML
|
|
630
|
-
setInnerHTML(node.dom as SomeElement, "")
|
|
631
|
-
// remove innerHTML from prev to prevent our ascension pass from doing this again
|
|
632
|
-
delete node.prev.props.innerHTML
|
|
633
|
-
}
|
|
635
|
+
if (!rootChild) {
|
|
636
|
+
onAscent(root)
|
|
637
|
+
return
|
|
638
|
+
}
|
|
634
639
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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
|
|
643
657
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
currentPlacementScope.active =
|
|
649
|
-
inheritsPlacement = true
|
|
650
|
-
}
|
|
651
|
-
if (node.flags & FLAG_DELETION) {
|
|
652
|
-
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
|
|
653
663
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
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]
|
|
661
702
|
}
|
|
703
|
+
break
|
|
662
704
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
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) {
|
|
667
716
|
placementScopes.pop()
|
|
668
717
|
currentPlacementScope = placementScopes[placementScopes.length - 1]
|
|
669
718
|
}
|
|
670
|
-
if (currentHostNode?.node ===
|
|
719
|
+
if (currentHostNode?.node === c.parent) {
|
|
671
720
|
hostNodes.pop()
|
|
672
721
|
currentHostNode = hostNodes[hostNodes.length - 1]
|
|
673
722
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
723
|
+
c = c.parent!
|
|
724
|
+
}
|
|
725
|
+
if (c === root) break
|
|
726
|
+
}
|
|
727
|
+
onAscent(root)
|
|
677
728
|
handlePostPlacementFocusPersistence()
|
|
678
729
|
}
|
|
679
730
|
|
|
@@ -692,7 +743,7 @@ function commitDom(
|
|
|
692
743
|
if (!vNode.prev || vNode.flags & FLAG_UPDATE) {
|
|
693
744
|
updateDom(vNode)
|
|
694
745
|
}
|
|
695
|
-
hostNode.lastChild = vNode
|
|
746
|
+
hostNode.lastChild = vNode.dom
|
|
696
747
|
}
|
|
697
748
|
|
|
698
749
|
function commitDeletion(vNode: VNode) {
|
|
@@ -730,3 +781,25 @@ function commitDeletion(vNode: VNode) {
|
|
|
730
781
|
|
|
731
782
|
vNode.parent = null
|
|
732
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/reconciler.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
$FRAGMENT,
|
|
3
|
-
FLAG_HAS_MEMO_ANCESTOR,
|
|
4
|
-
FLAG_MEMO,
|
|
5
|
-
FLAG_PLACEMENT,
|
|
6
|
-
FLAG_UPDATE,
|
|
7
|
-
} from "./constants.js"
|
|
1
|
+
import { $FRAGMENT, FLAG_PLACEMENT, FLAG_UPDATE } from "./constants.js"
|
|
8
2
|
import { getVNodeAppContext, isVNode, latest } from "./utils.js"
|
|
9
3
|
import { Signal } from "./signals/base.js"
|
|
10
4
|
import { __DEV__ } from "./env.js"
|
|
@@ -32,7 +26,6 @@ export function reconcileChildren(parent: VNode, children: unknown) {
|
|
|
32
26
|
}
|
|
33
27
|
|
|
34
28
|
function reconcileSingleChild(parent: VNode, child: unknown) {
|
|
35
|
-
const deletions: VNode[] = (parent.deletions = [])
|
|
36
29
|
const oldChild = parent.child
|
|
37
30
|
if (oldChild === null) {
|
|
38
31
|
return createChild(parent, child)
|
|
@@ -60,13 +53,12 @@ function reconcileSingleChild(parent: VNode, child: unknown) {
|
|
|
60
53
|
}
|
|
61
54
|
placeChild(newNode, 0, 0)
|
|
62
55
|
}
|
|
63
|
-
existingChildren.forEach((child) =>
|
|
56
|
+
existingChildren.forEach((child) => deleteChild(parent, child))
|
|
64
57
|
return newNode
|
|
65
58
|
}
|
|
66
59
|
}
|
|
67
60
|
|
|
68
61
|
function reconcileChildrenArray(parent: VNode, children: unknown[]) {
|
|
69
|
-
const deletions: VNode[] = (parent.deletions = [])
|
|
70
62
|
let resultingChild: VNode | null = null
|
|
71
63
|
let prevNewChild: VNode | null = null
|
|
72
64
|
|
|
@@ -90,7 +82,7 @@ function reconcileChildrenArray(parent: VNode, children: unknown[]) {
|
|
|
90
82
|
break
|
|
91
83
|
}
|
|
92
84
|
if (oldChild && !newChild.prev) {
|
|
93
|
-
|
|
85
|
+
deleteChild(parent, oldChild)
|
|
94
86
|
}
|
|
95
87
|
lastPlacedIndex = placeChild(newChild, lastPlacedIndex, newIdx)
|
|
96
88
|
if (prevNewChild === null) {
|
|
@@ -151,7 +143,7 @@ function reconcileChildrenArray(parent: VNode, children: unknown[]) {
|
|
|
151
143
|
}
|
|
152
144
|
}
|
|
153
145
|
|
|
154
|
-
existingChildren.forEach((child) =>
|
|
146
|
+
existingChildren.forEach((child) => deleteChild(parent, child))
|
|
155
147
|
return resultingChild
|
|
156
148
|
}
|
|
157
149
|
|
|
@@ -460,9 +452,6 @@ function propsChanged(oldProps: VNode["props"], newProps: VNode["props"]) {
|
|
|
460
452
|
function setParent(child: VNode, parent: VNode) {
|
|
461
453
|
child.parent = parent
|
|
462
454
|
child.depth = parent.depth + 1
|
|
463
|
-
if (parent.flags & (FLAG_MEMO | FLAG_HAS_MEMO_ANCESTOR)) {
|
|
464
|
-
child.flags |= FLAG_HAS_MEMO_ANCESTOR
|
|
465
|
-
}
|
|
466
455
|
}
|
|
467
456
|
|
|
468
457
|
function dev_emitUpdateNode() {
|
|
@@ -490,9 +479,17 @@ function mapRemainingChildren(child: VNode | null) {
|
|
|
490
479
|
return map
|
|
491
480
|
}
|
|
492
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
|
+
|
|
493
490
|
function deleteRemainingChildren(parent: VNode, child: VNode | null) {
|
|
494
491
|
while (child) {
|
|
495
|
-
parent
|
|
492
|
+
deleteChild(parent, child)
|
|
496
493
|
child = child.sibling
|
|
497
494
|
}
|
|
498
495
|
}
|
package/src/scheduler.ts
CHANGED
|
@@ -7,7 +7,9 @@ import {
|
|
|
7
7
|
$CONTEXT_PROVIDER,
|
|
8
8
|
CONSECUTIVE_DIRTY_LIMIT,
|
|
9
9
|
FLAG_DELETION,
|
|
10
|
+
FLAG_DIRTY,
|
|
10
11
|
FLAG_MEMO,
|
|
12
|
+
FLAG_NOOP,
|
|
11
13
|
} from "./constants.js"
|
|
12
14
|
import { commitDeletion, commitWork, createDom, hydrateDom } from "./dom.js"
|
|
13
15
|
import { __DEV__ } from "./env.js"
|
|
@@ -17,10 +19,8 @@ import { hydrationStack } from "./hydration.js"
|
|
|
17
19
|
import { assertValidElementProps } from "./props.js"
|
|
18
20
|
import { reconcileChildren } from "./reconciler.js"
|
|
19
21
|
import {
|
|
20
|
-
willMemoBlockUpdate,
|
|
21
22
|
latest,
|
|
22
23
|
traverseApply,
|
|
23
|
-
vNodeContains,
|
|
24
24
|
isExoticType,
|
|
25
25
|
getVNodeAppContext,
|
|
26
26
|
} from "./utils.js"
|
|
@@ -29,9 +29,7 @@ import type { AppContext } from "./appContext"
|
|
|
29
29
|
type VNode = Kiru.VNode
|
|
30
30
|
|
|
31
31
|
let appCtx: AppContext | null
|
|
32
|
-
let nextUnitOfWork: VNode | null = null
|
|
33
32
|
let treesInProgress: VNode[] = []
|
|
34
|
-
let currentTreeIndex = 0
|
|
35
33
|
let isRunningOrQueued = false
|
|
36
34
|
let nextIdleEffects: (() => void)[] = []
|
|
37
35
|
let deletions: VNode[] = []
|
|
@@ -39,7 +37,6 @@ let isImmediateEffectsMode = false
|
|
|
39
37
|
let immediateEffectDirtiedRender = false
|
|
40
38
|
let isRenderDirtied = false
|
|
41
39
|
let consecutiveDirtyCount = 0
|
|
42
|
-
let pendingContextChanges = new Set<ContextProviderNode<any>>()
|
|
43
40
|
let preEffects: Array<Function> = []
|
|
44
41
|
let postEffects: Array<Function> = []
|
|
45
42
|
let animationFrameHandle = -1
|
|
@@ -106,91 +103,14 @@ function queueUpdate(vNode: VNode) {
|
|
|
106
103
|
return
|
|
107
104
|
}
|
|
108
105
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return
|
|
112
|
-
}
|
|
106
|
+
if (vNode.flags & (FLAG_DIRTY | FLAG_DELETION)) return
|
|
107
|
+
vNode.flags |= FLAG_DIRTY
|
|
113
108
|
|
|
114
|
-
if (
|
|
109
|
+
if (!treesInProgress.length) {
|
|
115
110
|
treesInProgress.push(vNode)
|
|
116
|
-
nextUnitOfWork = vNode
|
|
117
111
|
return queueBeginWork()
|
|
118
112
|
}
|
|
119
113
|
|
|
120
|
-
for (let i = 0; i < treesInProgress.length; i++) {
|
|
121
|
-
const tree = treesInProgress[i]
|
|
122
|
-
if (tree !== vNode) continue
|
|
123
|
-
if (i < currentTreeIndex) {
|
|
124
|
-
// It was already processed; requeue it to the end
|
|
125
|
-
currentTreeIndex--
|
|
126
|
-
treesInProgress.splice(i, 1)
|
|
127
|
-
treesInProgress.push(tree)
|
|
128
|
-
}
|
|
129
|
-
return
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Check if this node is a descendant of any trees already queued
|
|
133
|
-
for (let i = 0; i < treesInProgress.length; i++) {
|
|
134
|
-
const tree = treesInProgress[i]
|
|
135
|
-
if (!vNodeContains(tree, vNode)) continue
|
|
136
|
-
|
|
137
|
-
if (i === currentTreeIndex) {
|
|
138
|
-
// It's a child of the currently worked-on tree
|
|
139
|
-
// If it's deeper within the same tree, we can skip
|
|
140
|
-
if (vNodeContains(nextUnitOfWork, vNode)) return
|
|
141
|
-
// If it's not in the current work subtree, move back up to it
|
|
142
|
-
nextUnitOfWork = vNode
|
|
143
|
-
} else if (i < currentTreeIndex) {
|
|
144
|
-
// It's a descendant of an already processed tree; treat as a new update
|
|
145
|
-
treesInProgress.push(vNode)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Check if this node contains any of the currently queued trees
|
|
152
|
-
let didReplaceTree = false
|
|
153
|
-
let shouldQueueAtEnd = false
|
|
154
|
-
for (let i = 0; i < treesInProgress.length; ) {
|
|
155
|
-
const tree = treesInProgress[i]
|
|
156
|
-
if (!vNodeContains(vNode, tree)) {
|
|
157
|
-
i++
|
|
158
|
-
continue
|
|
159
|
-
}
|
|
160
|
-
// This node contains another update root, replace it
|
|
161
|
-
|
|
162
|
-
if (i === currentTreeIndex) {
|
|
163
|
-
if (!didReplaceTree) {
|
|
164
|
-
treesInProgress.splice(i, 1, vNode)
|
|
165
|
-
nextUnitOfWork = vNode
|
|
166
|
-
didReplaceTree = true
|
|
167
|
-
i++ // advance past replaced node
|
|
168
|
-
} else {
|
|
169
|
-
treesInProgress.splice(i, 1)
|
|
170
|
-
// no increment
|
|
171
|
-
}
|
|
172
|
-
} else if (i < currentTreeIndex) {
|
|
173
|
-
currentTreeIndex--
|
|
174
|
-
treesInProgress.splice(i, 1)
|
|
175
|
-
if (!didReplaceTree) {
|
|
176
|
-
shouldQueueAtEnd = true
|
|
177
|
-
didReplaceTree = true
|
|
178
|
-
}
|
|
179
|
-
// no increment
|
|
180
|
-
} else {
|
|
181
|
-
// i > currentTreeIndex
|
|
182
|
-
treesInProgress.splice(i, 1)
|
|
183
|
-
if (!didReplaceTree) {
|
|
184
|
-
shouldQueueAtEnd = true
|
|
185
|
-
didReplaceTree = true
|
|
186
|
-
}
|
|
187
|
-
// no increment
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (!shouldQueueAtEnd && didReplaceTree) {
|
|
191
|
-
return
|
|
192
|
-
}
|
|
193
|
-
// If it doesn't overlap with any queued tree, queue as new independent update root
|
|
194
114
|
treesInProgress.push(vNode)
|
|
195
115
|
}
|
|
196
116
|
|
|
@@ -199,9 +119,13 @@ function queueDelete(vNode: VNode) {
|
|
|
199
119
|
deletions.push(vNode)
|
|
200
120
|
}
|
|
201
121
|
|
|
122
|
+
const depthSort = (a: VNode, b: VNode) => b.depth - a.depth
|
|
123
|
+
|
|
124
|
+
let currentWorkRoot: VNode | null = null
|
|
125
|
+
|
|
202
126
|
function doWork(): void {
|
|
203
127
|
if (__DEV__) {
|
|
204
|
-
const n =
|
|
128
|
+
const n = deletions[0] ?? treesInProgress[0]
|
|
205
129
|
if (n) {
|
|
206
130
|
appCtx = getVNodeAppContext(n)!
|
|
207
131
|
window.__kiru?.profilingContext?.beginTick(appCtx)
|
|
@@ -210,21 +134,29 @@ function doWork(): void {
|
|
|
210
134
|
}
|
|
211
135
|
}
|
|
212
136
|
|
|
213
|
-
|
|
214
|
-
nextUnitOfWork =
|
|
215
|
-
performUnitOfWork(nextUnitOfWork) ??
|
|
216
|
-
treesInProgress[++currentTreeIndex] ??
|
|
217
|
-
queueBlockedContextDependencyRoots()
|
|
218
|
-
}
|
|
137
|
+
let len = 1
|
|
219
138
|
|
|
220
|
-
while (
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
139
|
+
while (treesInProgress.length) {
|
|
140
|
+
if (treesInProgress.length > len) {
|
|
141
|
+
treesInProgress.sort(depthSort)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
currentWorkRoot = treesInProgress.shift()!
|
|
145
|
+
len = treesInProgress.length
|
|
146
|
+
|
|
147
|
+
const flags = currentWorkRoot.flags
|
|
148
|
+
if (flags & FLAG_DELETION) continue
|
|
149
|
+
if (flags & FLAG_DIRTY) {
|
|
150
|
+
let n: VNode | void = currentWorkRoot
|
|
151
|
+
while ((n = performUnitOfWork(n))) {}
|
|
152
|
+
|
|
153
|
+
while (deletions.length) {
|
|
154
|
+
commitDeletion(deletions.pop()!)
|
|
155
|
+
}
|
|
156
|
+
commitWork(currentWorkRoot)
|
|
157
|
+
|
|
158
|
+
currentWorkRoot.flags &= ~FLAG_DIRTY
|
|
159
|
+
}
|
|
228
160
|
}
|
|
229
161
|
|
|
230
162
|
isImmediateEffectsMode = true
|
|
@@ -253,39 +185,6 @@ function doWork(): void {
|
|
|
253
185
|
}
|
|
254
186
|
}
|
|
255
187
|
|
|
256
|
-
function queueBlockedContextDependencyRoots(): VNode | null {
|
|
257
|
-
if (pendingContextChanges.size === 0) return null
|
|
258
|
-
|
|
259
|
-
// TODO: it's possible that a 'job' created by this process is
|
|
260
|
-
// blocked by a parent memo after a queueUpdate -> replaceTree action.
|
|
261
|
-
// To prevent this, we might need to add these to a distinct queue.
|
|
262
|
-
const jobRoots: VNode[] = []
|
|
263
|
-
pendingContextChanges.forEach((provider) => {
|
|
264
|
-
provider.props.dependents.forEach((dep) => {
|
|
265
|
-
if (!willMemoBlockUpdate(provider, dep)) return
|
|
266
|
-
for (let i = 0; i < jobRoots.length; i++) {
|
|
267
|
-
const root = jobRoots[i]
|
|
268
|
-
if (vNodeContains(root, dep)) {
|
|
269
|
-
if (willMemoBlockUpdate(root, dep)) {
|
|
270
|
-
// root is a parent of dep and there's a memo between them, prevent consolidation and queue as new root
|
|
271
|
-
break
|
|
272
|
-
}
|
|
273
|
-
return
|
|
274
|
-
}
|
|
275
|
-
if (vNodeContains(dep, root)) {
|
|
276
|
-
jobRoots[i] = dep
|
|
277
|
-
return
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
jobRoots.push(dep)
|
|
281
|
-
})
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
pendingContextChanges.clear()
|
|
285
|
-
treesInProgress.push(...jobRoots)
|
|
286
|
-
return jobRoots[0] ?? null
|
|
287
|
-
}
|
|
288
|
-
|
|
289
188
|
function performUnitOfWork(vNode: VNode): VNode | void {
|
|
290
189
|
let renderChild = true
|
|
291
190
|
try {
|
|
@@ -293,15 +192,15 @@ function performUnitOfWork(vNode: VNode): VNode | void {
|
|
|
293
192
|
if (typeof vNode.type === "string") {
|
|
294
193
|
updateHostComponent(vNode as DomVNode)
|
|
295
194
|
} else if (isExoticType(vNode.type)) {
|
|
296
|
-
if (vNode
|
|
195
|
+
if (vNode?.type === $CONTEXT_PROVIDER) {
|
|
297
196
|
const asProvider = vNode as ContextProviderNode<any>
|
|
298
|
-
const {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
) {
|
|
304
|
-
|
|
197
|
+
const {
|
|
198
|
+
props: { dependents, value },
|
|
199
|
+
prev,
|
|
200
|
+
} = asProvider
|
|
201
|
+
|
|
202
|
+
if (dependents.size && prev && prev.props.value !== value) {
|
|
203
|
+
dependents.forEach(queueUpdate)
|
|
305
204
|
}
|
|
306
205
|
}
|
|
307
206
|
vNode.child = reconcileChildren(vNode, props.children)
|
|
@@ -351,7 +250,7 @@ function performUnitOfWork(vNode: VNode): VNode | void {
|
|
|
351
250
|
nextNode.effects = undefined
|
|
352
251
|
}
|
|
353
252
|
|
|
354
|
-
if (nextNode ===
|
|
253
|
+
if (nextNode === currentWorkRoot) return
|
|
355
254
|
if (nextNode.sibling) {
|
|
356
255
|
return nextNode.sibling
|
|
357
256
|
}
|
|
@@ -372,14 +271,17 @@ function updateFunctionComponent(vNode: FunctionVNode) {
|
|
|
372
271
|
vNode.arePropsEqual!(prev.memoizedProps, props) &&
|
|
373
272
|
!vNode.hmrUpdated
|
|
374
273
|
) {
|
|
274
|
+
vNode.flags |= FLAG_NOOP
|
|
375
275
|
return false
|
|
376
276
|
}
|
|
277
|
+
vNode.flags &= ~FLAG_NOOP
|
|
377
278
|
}
|
|
378
279
|
try {
|
|
379
280
|
node.current = vNode
|
|
380
281
|
let newChild
|
|
381
282
|
let renderTryCount = 0
|
|
382
283
|
do {
|
|
284
|
+
vNode.flags &= ~FLAG_DIRTY
|
|
383
285
|
isRenderDirtied = false
|
|
384
286
|
hookIndex.current = 0
|
|
385
287
|
|
package/src/utils.ts
CHANGED
|
@@ -5,10 +5,8 @@ import {
|
|
|
5
5
|
$HYDRATION_BOUNDARY,
|
|
6
6
|
booleanAttributes,
|
|
7
7
|
FLAG_DELETION,
|
|
8
|
-
FLAG_HAS_MEMO_ANCESTOR,
|
|
9
8
|
FLAG_PLACEMENT,
|
|
10
9
|
FLAG_UPDATE,
|
|
11
|
-
FLAG_MEMO,
|
|
12
10
|
REGEX_UNIT,
|
|
13
11
|
} from "./constants.js"
|
|
14
12
|
import { unwrap } from "./signals/utils.js"
|
|
@@ -25,12 +23,10 @@ export {
|
|
|
25
23
|
isExoticType,
|
|
26
24
|
isVNodeDeleted,
|
|
27
25
|
vNodeContains,
|
|
28
|
-
willMemoBlockUpdate,
|
|
29
26
|
getCurrentVNode,
|
|
30
27
|
getVNodeAppContext,
|
|
31
28
|
commitSnapshot,
|
|
32
29
|
traverseApply,
|
|
33
|
-
postOrderApply,
|
|
34
30
|
findParent,
|
|
35
31
|
propToHtmlAttr,
|
|
36
32
|
propValueToHtmlAttrValue,
|
|
@@ -158,26 +154,6 @@ function vNodeContains(haystack: VNode, needle: VNode): boolean {
|
|
|
158
154
|
return false
|
|
159
155
|
}
|
|
160
156
|
|
|
161
|
-
function willMemoBlockUpdate(root: VNode, target: VNode): boolean {
|
|
162
|
-
let node: VNode | null = target
|
|
163
|
-
|
|
164
|
-
while (node && node !== root && node.flags & FLAG_HAS_MEMO_ANCESTOR) {
|
|
165
|
-
const parent = node.parent
|
|
166
|
-
if (!parent) return false
|
|
167
|
-
if (
|
|
168
|
-
parent.flags & FLAG_MEMO &&
|
|
169
|
-
parent.prev?.memoizedProps &&
|
|
170
|
-
parent.arePropsEqual!(parent.prev.memoizedProps, parent.props)
|
|
171
|
-
) {
|
|
172
|
-
return true
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
node = node.parent
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return false
|
|
179
|
-
}
|
|
180
|
-
|
|
181
157
|
function traverseApply(vNode: VNode, func: (node: VNode) => void): void {
|
|
182
158
|
let applyToSiblings = false
|
|
183
159
|
const nodes: VNode[] = [vNode]
|
|
@@ -190,49 +166,6 @@ function traverseApply(vNode: VNode, func: (node: VNode) => void): void {
|
|
|
190
166
|
while (nodes.length) apply(nodes.shift()!)
|
|
191
167
|
}
|
|
192
168
|
|
|
193
|
-
function postOrderApply(
|
|
194
|
-
tree: VNode,
|
|
195
|
-
callbacks: {
|
|
196
|
-
/** called upon traversing to the next parent, and on the root */
|
|
197
|
-
onAscent: (vNode: VNode) => void
|
|
198
|
-
/** called before traversing to the next parent */
|
|
199
|
-
onBeforeAscent?: (vNode: VNode) => void
|
|
200
|
-
/** called before traversing to the next child */
|
|
201
|
-
onDescent?: (vNode: VNode) => void
|
|
202
|
-
}
|
|
203
|
-
): void {
|
|
204
|
-
const root = tree
|
|
205
|
-
const rootChild = root.child
|
|
206
|
-
if (!rootChild) {
|
|
207
|
-
callbacks.onAscent(root)
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
callbacks.onDescent?.(root)
|
|
212
|
-
let branch = rootChild
|
|
213
|
-
while (branch) {
|
|
214
|
-
let c = branch
|
|
215
|
-
while (c) {
|
|
216
|
-
if (!c.child) break
|
|
217
|
-
callbacks.onDescent?.(c)
|
|
218
|
-
c = c.child
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
while (c && c !== root) {
|
|
222
|
-
callbacks.onAscent(c)
|
|
223
|
-
if (c.sibling) {
|
|
224
|
-
branch = c.sibling
|
|
225
|
-
break
|
|
226
|
-
}
|
|
227
|
-
callbacks.onBeforeAscent?.(c)
|
|
228
|
-
c = c.parent!
|
|
229
|
-
}
|
|
230
|
-
if (c === root) break
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
callbacks.onAscent(root)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
169
|
function findParent(vNode: Kiru.VNode, predicate: (n: Kiru.VNode) => boolean) {
|
|
237
170
|
let n: Kiru.VNode | null = vNode.parent
|
|
238
171
|
while (n) {
|