@vidact/runtime 0.2.0-beta.7 → 0.2.0-beta.9
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/CHANGELOG.md +8 -0
- package/dist/actions.js +1 -1
- package/dist/compiled/core.d.ts +17 -5
- package/dist/compiled/core.d.ts.map +1 -1
- package/dist/compiled/core.js +529 -149
- package/dist/compiled/core.js.map +1 -1
- package/dist/direct-dom.d.ts.map +1 -1
- package/dist/direct-dom.js +102 -11
- package/dist/direct-dom.js.map +1 -1
- package/dist/dom/events.d.ts +4 -1
- package/dist/dom/events.d.ts.map +1 -1
- package/dist/dom/events.js +66 -1
- package/dist/dom/events.js.map +1 -1
- package/dist/dom/namespace.js +1 -1
- package/dist/hydration-bridge.d.ts +4 -4
- package/dist/hydration-bridge.d.ts.map +1 -1
- package/dist/hydration-bridge.js.map +1 -1
- package/dist/hydration.d.ts +2 -2
- package/dist/hydration.d.ts.map +1 -1
- package/dist/hydration.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/keyed-list.d.ts +6 -2
- package/dist/keyed-list.d.ts.map +1 -1
- package/dist/keyed-list.js +122 -11
- package/dist/keyed-list.js.map +1 -1
- package/dist/metadata.js +1 -1
- package/dist/spread.js +2 -2
- package/dist/state-slot.d.ts +12 -1
- package/dist/state-slot.d.ts.map +1 -1
- package/dist/state-slot.js +54 -24
- package/dist/state-slot.js.map +1 -1
- package/package.json +3 -3
- package/src/compiled/core.ts +752 -172
- package/src/direct-dom.ts +136 -10
- package/src/dom/events.ts +86 -0
- package/src/hydration-bridge.ts +10 -4
- package/src/hydration.ts +5 -3
- package/src/index.ts +1 -0
- package/src/keyed-list.ts +168 -22
- package/src/state-slot.ts +83 -27
package/src/compiled/core.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { COMPILED_DELEGATED_EVENT_INVOKE, retainCompiledEventRoot } from '../dom/events.ts'
|
|
1
2
|
import {
|
|
2
3
|
currentIntrinsicNamespace,
|
|
3
4
|
withIntrinsicNamespace,
|
|
@@ -51,7 +52,13 @@ import {
|
|
|
51
52
|
unionSources,
|
|
52
53
|
type SourceMask,
|
|
53
54
|
} from '../source-mask.ts'
|
|
54
|
-
import {
|
|
55
|
+
import {
|
|
56
|
+
createReadOnlyStateSlot,
|
|
57
|
+
createStateSlot,
|
|
58
|
+
replaceReadOnlyStateSlot,
|
|
59
|
+
type ReadOnlyStateCell,
|
|
60
|
+
type StateSlot,
|
|
61
|
+
} from '../state-slot.ts'
|
|
55
62
|
import { isSuspension, subscribeResource, useAsync } from './async-resource.ts'
|
|
56
63
|
import {
|
|
57
64
|
BINDING,
|
|
@@ -78,7 +85,28 @@ let retainedUiEnabled = typeof __VIDACT_RETAINED_UI__ !== 'undefined' && __VIDAC
|
|
|
78
85
|
let profilingEnabled = false
|
|
79
86
|
let activeOwnerCount = 0
|
|
80
87
|
let createdOwnerCount = 0
|
|
88
|
+
let createdUpdaterCount = 0
|
|
89
|
+
let createdSchedulerPlanCount = 0
|
|
81
90
|
const noop = (): void => {}
|
|
91
|
+
const COMPILED_EVENT_HANDLER = Symbol('vidact.compiled-event')
|
|
92
|
+
const COMPILED_INLINE_EVENT_OWNER = Symbol('vidact.compiled-event.owner')
|
|
93
|
+
const UNSET_BINDING = Symbol(DEV ? 'Vidact.UnsetBinding' : undefined)
|
|
94
|
+
|
|
95
|
+
type CompiledEventHandler = ((...arguments_: never[]) => void) & {
|
|
96
|
+
[COMPILED_EVENT_HANDLER]: true
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
type CompiledInlineEventHandler<Arguments extends unknown[]> = ((
|
|
100
|
+
...arguments_: Arguments
|
|
101
|
+
) => void) & {
|
|
102
|
+
[COMPILED_INLINE_EVENT_OWNER]: Owner
|
|
103
|
+
[COMPILED_DELEGATED_EVENT_INVOKE]: (...arguments_: Arguments) => void
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type CompiledEventMetadata = {
|
|
107
|
+
[COMPILED_EVENT_HANDLER]?: true
|
|
108
|
+
[COMPILED_DELEGATED_EVENT_INVOKE]?: (...arguments_: never[]) => void
|
|
109
|
+
}
|
|
82
110
|
|
|
83
111
|
type ContextFrame = {
|
|
84
112
|
readonly context: CompiledContext<unknown>
|
|
@@ -104,7 +132,7 @@ type PortalPublication = readonly [commit: () => void, rollback: () => void]
|
|
|
104
132
|
|
|
105
133
|
type Owner = [
|
|
106
134
|
disposed: boolean,
|
|
107
|
-
cleanups:
|
|
135
|
+
cleanups: Array<() => void>,
|
|
108
136
|
context: ContextFrame | null,
|
|
109
137
|
rootIdentity: RootIdentity,
|
|
110
138
|
errorBoundary: ErrorBoundaryHandler | null,
|
|
@@ -145,11 +173,13 @@ type RetainedResource = {
|
|
|
145
173
|
readonly phase: number
|
|
146
174
|
}
|
|
147
175
|
|
|
176
|
+
type FlushTask = (() => void) | { readonly [1]: (sources?: SourceMask) => void }
|
|
177
|
+
|
|
148
178
|
type RetainedConnection = {
|
|
149
179
|
readonly parent: RetainedConnection | null
|
|
150
180
|
readonly children: Set<RetainedConnection>
|
|
151
181
|
readonly resources: Set<RetainedResource>
|
|
152
|
-
readonly deferredFlushes: Map<
|
|
182
|
+
readonly deferredFlushes: Map<FlushTask, CancelScheduledTask>
|
|
153
183
|
readonly afterFlush: Set<() => void>
|
|
154
184
|
visible: boolean
|
|
155
185
|
connected: boolean
|
|
@@ -189,9 +219,18 @@ export function deferred(render: () => CompiledRenderValue): StructuralBinding {
|
|
|
189
219
|
return [
|
|
190
220
|
STRUCTURAL,
|
|
191
221
|
(parent, before) => {
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
194
|
-
|
|
222
|
+
const value = render()
|
|
223
|
+
if (ownedStructuralBinding(value)?.[2] === 'slot') {
|
|
224
|
+
insertValue(parent, value, before)
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
const hydrated = borrowHydrationSlotRange(parent, true)
|
|
228
|
+
if (hydrated === undefined) {
|
|
229
|
+
insertValue(parent, value, before)
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
claimHydrationSlotRange(parent)
|
|
233
|
+
insertValue(parent, value, hydrated[1])
|
|
195
234
|
},
|
|
196
235
|
'slot',
|
|
197
236
|
]
|
|
@@ -466,7 +505,7 @@ export function recordCompiledDebugValue<Value>(
|
|
|
466
505
|
const remove = subscribeBinding(valueInput, () => {
|
|
467
506
|
frame.values[index] = read()
|
|
468
507
|
})
|
|
469
|
-
owner[1].
|
|
508
|
+
owner[1].push(remove)
|
|
470
509
|
}
|
|
471
510
|
}
|
|
472
511
|
|
|
@@ -823,6 +862,7 @@ function createPortalStructural(
|
|
|
823
862
|
const portalOwner = withRootIdentity(rootIdentity, () =>
|
|
824
863
|
withContextFrame(context, createOwner),
|
|
825
864
|
)
|
|
865
|
+
let releaseEventRoot: (() => void) | undefined
|
|
826
866
|
try {
|
|
827
867
|
withOwner(portalOwner, () => insertValue(fragment, children, end))
|
|
828
868
|
} catch (error) {
|
|
@@ -831,12 +871,15 @@ function createPortalStructural(
|
|
|
831
871
|
throw error
|
|
832
872
|
}
|
|
833
873
|
const rollback = (): void => {
|
|
874
|
+
releaseEventRoot?.()
|
|
875
|
+
releaseEventRoot = undefined
|
|
834
876
|
removeBetween(start, end)
|
|
835
877
|
start.remove()
|
|
836
878
|
end.remove()
|
|
837
879
|
}
|
|
838
880
|
const publication: PortalPublication = [
|
|
839
881
|
() => {
|
|
882
|
+
releaseEventRoot = retainCompiledEventRoot(container)
|
|
840
883
|
container.insertBefore(fragment, null)
|
|
841
884
|
try {
|
|
842
885
|
commitRangeRefs(start, end)
|
|
@@ -906,7 +949,7 @@ let transactionDepth = 0
|
|
|
906
949
|
let disposalCascadeDepth = 0
|
|
907
950
|
let drainingFlushes = false
|
|
908
951
|
let activePublication: PublicationOperation[] | null = null
|
|
909
|
-
const scheduledFlushes = new Set<
|
|
952
|
+
const scheduledFlushes = new Set<FlushTask>()
|
|
910
953
|
const disposalCascadeOwners = new Set<Owner>()
|
|
911
954
|
const scopeOwners = new WeakMap<CompiledScope, Owner>()
|
|
912
955
|
const scopeNamespaces = new WeakMap<CompiledScope, IntrinsicNamespace>()
|
|
@@ -916,6 +959,22 @@ const componentCommitOwners = new WeakMap<Comment, Owner>()
|
|
|
916
959
|
const pendingInsertionCommits = new WeakMap<Owner, Set<() => void>>()
|
|
917
960
|
const pendingOwnerCommits = new WeakMap<Owner, Set<() => void>>()
|
|
918
961
|
const pendingRootPortals = new WeakMap<RootIdentity, Set<PortalPublication>>()
|
|
962
|
+
let pendingRefCount = 0
|
|
963
|
+
let pendingInsertionOwnerCount = 0
|
|
964
|
+
let pendingOwnerCommitCount = 0
|
|
965
|
+
let visitedPublishedNodeCount = 0
|
|
966
|
+
|
|
967
|
+
function setPendingRef(element: Element, pending: PendingRef): void {
|
|
968
|
+
if (!pendingRefs.has(element)) pendingRefCount += 1
|
|
969
|
+
pendingRefs.set(element, pending)
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
function deletePendingRef(element: Element, expected?: PendingRef): boolean {
|
|
973
|
+
if (expected !== undefined && pendingRefs.get(element) !== expected) return false
|
|
974
|
+
if (!pendingRefs.delete(element)) return false
|
|
975
|
+
pendingRefCount -= 1
|
|
976
|
+
return true
|
|
977
|
+
}
|
|
919
978
|
|
|
920
979
|
export function createCompiledScope(): CompiledScope {
|
|
921
980
|
return createScope(wideSourceOperations)
|
|
@@ -925,6 +984,171 @@ export function createNarrowCompiledScope(): CompiledScope {
|
|
|
925
984
|
return createScope(narrowSourceOperations)
|
|
926
985
|
}
|
|
927
986
|
|
|
987
|
+
type CompilerKeyedUpdater = [
|
|
988
|
+
reads: SourceMask,
|
|
989
|
+
writes: SourceMask | undefined,
|
|
990
|
+
run: (active: SourceMask) => void,
|
|
991
|
+
active: boolean,
|
|
992
|
+
errorOwner: Owner,
|
|
993
|
+
context: ContextFrame | null,
|
|
994
|
+
]
|
|
995
|
+
|
|
996
|
+
/** Compiler-emitted map callbacks only need a compact, narrow row scheduler. */
|
|
997
|
+
class CompilerKeyedScope {
|
|
998
|
+
readonly owner = createOwner()
|
|
999
|
+
readonly updaters: Array<CompilerKeyedUpdater | undefined> = []
|
|
1000
|
+
pending: SourceMask = 0
|
|
1001
|
+
batchDepth = 0
|
|
1002
|
+
flushing = false
|
|
1003
|
+
hasWriter = false
|
|
1004
|
+
cachedUpdaterOrder: number[] | undefined;
|
|
1005
|
+
|
|
1006
|
+
[1](sources?: SourceMask): void {
|
|
1007
|
+
if (this.owner[0]) return
|
|
1008
|
+
if (sources === undefined) {
|
|
1009
|
+
this.flush()
|
|
1010
|
+
return
|
|
1011
|
+
}
|
|
1012
|
+
if (narrowSourceOperations[0](sources)) return
|
|
1013
|
+
const wasIdle = narrowSourceOperations[0](this.pending)
|
|
1014
|
+
this.pending = narrowSourceOperations[2](this.pending, sources)
|
|
1015
|
+
if (wasIdle && this.batchDepth === 0) this.schedule()
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
[3](): void {
|
|
1019
|
+
if (this.owner[0]) return
|
|
1020
|
+
try {
|
|
1021
|
+
disposeOwner(this.owner)
|
|
1022
|
+
} finally {
|
|
1023
|
+
this.pending = 0
|
|
1024
|
+
for (const updater of this.updaters) {
|
|
1025
|
+
if (updater !== undefined) updater[3] = false
|
|
1026
|
+
}
|
|
1027
|
+
this.updaters.length = 0
|
|
1028
|
+
this.cachedUpdaterOrder = undefined
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
constructor() {
|
|
1033
|
+
const scope = this as unknown as CompiledScope
|
|
1034
|
+
scopeOwners.set(scope, this.owner)
|
|
1035
|
+
scopeNamespaces.set(scope, currentIntrinsicNamespace())
|
|
1036
|
+
activeScopeCollector?.add(scope)
|
|
1037
|
+
if (activeScopeCollector !== null) activeConstructionOwner = this.owner
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
[0](reads: SourceMask, run: (active: SourceMask) => void, writes?: SourceMask): () => void {
|
|
1041
|
+
if (this.owner[0]) {
|
|
1042
|
+
throw new Error(DEV ? 'cannot add an updater to a disposed scope' : 'V002')
|
|
1043
|
+
}
|
|
1044
|
+
const errorOwner = activeOwner ?? this.owner
|
|
1045
|
+
const entry: CompilerKeyedUpdater = [
|
|
1046
|
+
reads,
|
|
1047
|
+
writes,
|
|
1048
|
+
run,
|
|
1049
|
+
true,
|
|
1050
|
+
errorOwner,
|
|
1051
|
+
activeContextFrame ?? errorOwner[2],
|
|
1052
|
+
]
|
|
1053
|
+
if (DEV) createdUpdaterCount += 1
|
|
1054
|
+
const index = this.updaters.length
|
|
1055
|
+
this.updaters.push(entry)
|
|
1056
|
+
this.hasWriter ||= writes !== undefined
|
|
1057
|
+
this.cachedUpdaterOrder = undefined
|
|
1058
|
+
if (activeOwner === this.owner) return noop
|
|
1059
|
+
const remove = (): void => {
|
|
1060
|
+
if (this.updaters[index] !== entry) return
|
|
1061
|
+
entry[3] = false
|
|
1062
|
+
this.updaters[index] = undefined
|
|
1063
|
+
this.cachedUpdaterOrder = undefined
|
|
1064
|
+
}
|
|
1065
|
+
activeOwner?.[1].push(remove)
|
|
1066
|
+
return remove
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
[2]<T>(operation: () => T): T {
|
|
1070
|
+
this.batchDepth += 1
|
|
1071
|
+
transactionDepth += 1
|
|
1072
|
+
try {
|
|
1073
|
+
return operation()
|
|
1074
|
+
} finally {
|
|
1075
|
+
this.batchDepth -= 1
|
|
1076
|
+
if (this.batchDepth === 0 && !narrowSourceOperations[0](this.pending)) this.schedule()
|
|
1077
|
+
transactionDepth -= 1
|
|
1078
|
+
if (transactionDepth === 0) drainFlushes()
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
private schedule(): void {
|
|
1083
|
+
if (retainedUiEnabled) scheduleOwnerFlush(this.owner, this)
|
|
1084
|
+
else scheduleFlush(this)
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
private flush(): void {
|
|
1088
|
+
if (this.flushing || narrowSourceOperations[0](this.pending)) return
|
|
1089
|
+
const operation = (): void => {
|
|
1090
|
+
this.flushing = true
|
|
1091
|
+
try {
|
|
1092
|
+
let pass = 0
|
|
1093
|
+
while (!narrowSourceOperations[0](this.pending)) {
|
|
1094
|
+
pass += 1
|
|
1095
|
+
if (pass > MAX_FLUSH_PASSES) {
|
|
1096
|
+
this.pending = 0
|
|
1097
|
+
throw new Error(DEV ? 'Vidact compiled scope did not stabilize' : 'V001')
|
|
1098
|
+
}
|
|
1099
|
+
let active = this.pending
|
|
1100
|
+
this.pending = 0
|
|
1101
|
+
const order = this.hasWriter
|
|
1102
|
+
? (this.cachedUpdaterOrder ??= topologicalUpdaterOrder(
|
|
1103
|
+
this.updaters as ReadonlyArray<CompiledUpdater | undefined>,
|
|
1104
|
+
narrowSourceOperations,
|
|
1105
|
+
))
|
|
1106
|
+
: undefined
|
|
1107
|
+
const count = order?.length ?? this.updaters.length
|
|
1108
|
+
for (let position = 0; position < count; position += 1) {
|
|
1109
|
+
const index = order?.[position] ?? position
|
|
1110
|
+
const updater = this.updaters[index]
|
|
1111
|
+
if (
|
|
1112
|
+
updater === undefined ||
|
|
1113
|
+
!updater[3] ||
|
|
1114
|
+
!narrowSourceOperations[1](active, updater[0])
|
|
1115
|
+
) {
|
|
1116
|
+
continue
|
|
1117
|
+
}
|
|
1118
|
+
try {
|
|
1119
|
+
const invoke = () =>
|
|
1120
|
+
withOwner(updater[4], () =>
|
|
1121
|
+
withContextFrame(updater[5], () =>
|
|
1122
|
+
withScopeNamespace(this as unknown as CompiledScope, () => updater[2](active)),
|
|
1123
|
+
),
|
|
1124
|
+
)
|
|
1125
|
+
if (profilingEnabled) measureProfileWork(this.owner, 'updater', invoke)
|
|
1126
|
+
else invoke()
|
|
1127
|
+
} catch (error) {
|
|
1128
|
+
if (!routeOwnerError(updater[4], error)) {
|
|
1129
|
+
failedOwner = updater[4]
|
|
1130
|
+
throw error
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
if (updater[1] !== undefined) {
|
|
1134
|
+
active = narrowSourceOperations[2](active, updater[1])
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
} finally {
|
|
1139
|
+
this.flushing = false
|
|
1140
|
+
}
|
|
1141
|
+
if (retainedUiEnabled) notifyRetainedFlush(this.owner[5] ?? null)
|
|
1142
|
+
}
|
|
1143
|
+
if (profilingEnabled) measureProfileWork(this.owner, 'scheduler', operation, true)
|
|
1144
|
+
else operation()
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
function createCompilerKeyedScope(): CompiledScope {
|
|
1149
|
+
return new CompilerKeyedScope() as unknown as CompiledScope
|
|
1150
|
+
}
|
|
1151
|
+
|
|
928
1152
|
export function runCompiledTransaction<T>(operation: () => T): T {
|
|
929
1153
|
transactionDepth += 1
|
|
930
1154
|
try {
|
|
@@ -939,12 +1163,13 @@ function createScope(operations: SourceOperations): CompiledScope {
|
|
|
939
1163
|
const namespace = currentIntrinsicNamespace()
|
|
940
1164
|
const owner = createOwner()
|
|
941
1165
|
const updaters: Array<CompiledUpdater | undefined> = []
|
|
942
|
-
|
|
943
|
-
|
|
1166
|
+
let freeUpdaterIndexes: number[] | undefined
|
|
1167
|
+
let addedDuringFlush: Set<CompiledUpdater> | undefined
|
|
944
1168
|
let batchDepth = 0
|
|
945
1169
|
let flushing = false
|
|
946
1170
|
let pending: SourceMask = 0
|
|
947
1171
|
let cachedUpdaterOrder: number[] | undefined
|
|
1172
|
+
let hasWriter = false
|
|
948
1173
|
|
|
949
1174
|
const flush = (): void => {
|
|
950
1175
|
if (owner[0] || flushing) return
|
|
@@ -952,7 +1177,9 @@ function createScope(operations: SourceOperations): CompiledScope {
|
|
|
952
1177
|
flushing = true
|
|
953
1178
|
try {
|
|
954
1179
|
let pass = 0
|
|
955
|
-
const updaterOrder =
|
|
1180
|
+
const updaterOrder = hasWriter
|
|
1181
|
+
? (cachedUpdaterOrder ??= topologicalUpdaterOrder(updaters, operations))
|
|
1182
|
+
: undefined
|
|
956
1183
|
while (!operations[0](pending)) {
|
|
957
1184
|
pass += 1
|
|
958
1185
|
if (pass > MAX_FLUSH_PASSES) {
|
|
@@ -962,11 +1189,13 @@ function createScope(operations: SourceOperations): CompiledScope {
|
|
|
962
1189
|
|
|
963
1190
|
let active = pending
|
|
964
1191
|
pending = 0
|
|
965
|
-
|
|
1192
|
+
const updaterCount = updaterOrder?.length ?? updaters.length
|
|
1193
|
+
for (let position = 0; position < updaterCount; position += 1) {
|
|
1194
|
+
const index = updaterOrder?.[position] ?? position
|
|
966
1195
|
const updater = updaters[index]
|
|
967
1196
|
if (
|
|
968
1197
|
updater === undefined ||
|
|
969
|
-
addedDuringFlush
|
|
1198
|
+
addedDuringFlush?.has(updater) ||
|
|
970
1199
|
!updater[3] ||
|
|
971
1200
|
!operations[1](active, updater[0])
|
|
972
1201
|
) {
|
|
@@ -979,10 +1208,10 @@ function createScope(operations: SourceOperations): CompiledScope {
|
|
|
979
1208
|
}
|
|
980
1209
|
if (updater[1] !== undefined) active = operations[2](active, updater[1])
|
|
981
1210
|
}
|
|
982
|
-
addedDuringFlush
|
|
1211
|
+
addedDuringFlush?.clear()
|
|
983
1212
|
}
|
|
984
1213
|
} finally {
|
|
985
|
-
addedDuringFlush
|
|
1214
|
+
addedDuringFlush?.clear()
|
|
986
1215
|
flushing = false
|
|
987
1216
|
}
|
|
988
1217
|
if (retainedUiEnabled) notifyRetainedFlush(owner[5] ?? null)
|
|
@@ -1015,25 +1244,28 @@ function createScope(operations: SourceOperations): CompiledScope {
|
|
|
1015
1244
|
},
|
|
1016
1245
|
true,
|
|
1017
1246
|
]
|
|
1018
|
-
|
|
1247
|
+
if (DEV) createdUpdaterCount += 1
|
|
1248
|
+
const reusableIndex = freeUpdaterIndexes?.pop()
|
|
1019
1249
|
const index = reusableIndex ?? updaters.length
|
|
1020
1250
|
updaters[index] = entry
|
|
1251
|
+
hasWriter ||= writes !== undefined
|
|
1021
1252
|
cachedUpdaterOrder = undefined
|
|
1022
|
-
if (flushing) addedDuringFlush.add(entry)
|
|
1253
|
+
if (flushing) (addedDuringFlush ??= new Set()).add(entry)
|
|
1023
1254
|
const remove = (): void => {
|
|
1024
1255
|
if (updaters[index] !== entry) return
|
|
1025
1256
|
entry[3] = false
|
|
1026
1257
|
updaters[index] = undefined
|
|
1027
1258
|
cachedUpdaterOrder = undefined
|
|
1028
|
-
freeUpdaterIndexes.push(index)
|
|
1259
|
+
;(freeUpdaterIndexes ??= []).push(index)
|
|
1029
1260
|
}
|
|
1030
|
-
if (activeOwner !== null && activeOwner !== owner) activeOwner[1].
|
|
1261
|
+
if (activeOwner !== null && activeOwner !== owner) activeOwner[1].push(remove)
|
|
1031
1262
|
return remove
|
|
1032
1263
|
},
|
|
1033
1264
|
(sources) => {
|
|
1034
1265
|
if (owner[0] || operations[0](sources)) return
|
|
1266
|
+
const wasIdle = operations[0](pending)
|
|
1035
1267
|
pending = operations[2](pending, sources)
|
|
1036
|
-
if (batchDepth === 0) {
|
|
1268
|
+
if (wasIdle && batchDepth === 0) {
|
|
1037
1269
|
if (retainedUiEnabled) scheduleOwnerFlush(owner, flush)
|
|
1038
1270
|
else scheduleFlush(flush)
|
|
1039
1271
|
}
|
|
@@ -1063,8 +1295,9 @@ function createScope(operations: SourceOperations): CompiledScope {
|
|
|
1063
1295
|
if (updater !== undefined) updater[3] = false
|
|
1064
1296
|
}
|
|
1065
1297
|
updaters.length = 0
|
|
1066
|
-
|
|
1067
|
-
|
|
1298
|
+
hasWriter = false
|
|
1299
|
+
if (freeUpdaterIndexes !== undefined) freeUpdaterIndexes.length = 0
|
|
1300
|
+
addedDuringFlush?.clear()
|
|
1068
1301
|
}
|
|
1069
1302
|
},
|
|
1070
1303
|
]
|
|
@@ -1079,9 +1312,16 @@ function topologicalUpdaterOrder(
|
|
|
1079
1312
|
updaters: ReadonlyArray<CompiledUpdater | undefined>,
|
|
1080
1313
|
operations: SourceOperations,
|
|
1081
1314
|
): number[] {
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1315
|
+
if (DEV) createdSchedulerPlanCount += 1
|
|
1316
|
+
const activeIndexes: number[] = []
|
|
1317
|
+
let hasWriter = false
|
|
1318
|
+
for (let index = 0; index < updaters.length; index += 1) {
|
|
1319
|
+
const updater = updaters[index]
|
|
1320
|
+
if (updater === undefined || !updater[3]) continue
|
|
1321
|
+
activeIndexes.push(index)
|
|
1322
|
+
if (updater[1] !== undefined) hasWriter = true
|
|
1323
|
+
}
|
|
1324
|
+
if (!hasWriter) return activeIndexes
|
|
1085
1325
|
const readersBySource = new Map<number, number[]>()
|
|
1086
1326
|
for (const readerIndex of activeIndexes) {
|
|
1087
1327
|
operations[3](updaters[readerIndex]![0], (sourceIndex) => {
|
|
@@ -1239,7 +1479,7 @@ export function createCompiledMemo<T>(
|
|
|
1239
1479
|
removeUpdater()
|
|
1240
1480
|
throw new Error(DEV ? 'createCompiledMemo received an unknown scope' : 'V003')
|
|
1241
1481
|
}
|
|
1242
|
-
owner[1].
|
|
1482
|
+
owner[1].push(removeUpdater)
|
|
1243
1483
|
return slot
|
|
1244
1484
|
}
|
|
1245
1485
|
|
|
@@ -1363,7 +1603,7 @@ export function createCompiledExternalStore<T>(
|
|
|
1363
1603
|
removeStoreUpdater()
|
|
1364
1604
|
throw error
|
|
1365
1605
|
}
|
|
1366
|
-
owner[1].
|
|
1606
|
+
owner[1].push(() => {
|
|
1367
1607
|
removeStoreUpdater()
|
|
1368
1608
|
deactivateStore()
|
|
1369
1609
|
})
|
|
@@ -1381,7 +1621,7 @@ export function createCompiledExternalStore<T>(
|
|
|
1381
1621
|
disposeResource()
|
|
1382
1622
|
throw error
|
|
1383
1623
|
}
|
|
1384
|
-
owner[1].
|
|
1624
|
+
owner[1].push(() => {
|
|
1385
1625
|
removeStoreUpdater()
|
|
1386
1626
|
disposeResource()
|
|
1387
1627
|
})
|
|
@@ -1483,6 +1723,42 @@ export function useCallback<T extends Function>(callback: T, _dependencies: read
|
|
|
1483
1723
|
return callback
|
|
1484
1724
|
}
|
|
1485
1725
|
|
|
1726
|
+
const ownedBlockFacades = new WeakMap<StructuralBinding, RenderableProtocol>()
|
|
1727
|
+
const ownedFacadeBlocks = new WeakMap<RenderableProtocol, StructuralBinding>()
|
|
1728
|
+
|
|
1729
|
+
function ownedStructuralBinding(value: unknown): StructuralBinding | undefined {
|
|
1730
|
+
if (isStructuralBinding(value)) return value
|
|
1731
|
+
return isRenderableProtocol(value) ? ownedFacadeBlocks.get(value) : undefined
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
/**
|
|
1735
|
+
* Owned blocks are arrays internally, which user-level data flow must never
|
|
1736
|
+
* observe: dependency code inspects children with `Array.isArray` and spreads
|
|
1737
|
+
* them. Prop reads wrap a block in a stable renderable facade that
|
|
1738
|
+
* materializes back to the block when rendered.
|
|
1739
|
+
*/
|
|
1740
|
+
function sanitizeCompiledPropValue<T>(value: T): T {
|
|
1741
|
+
if (!isStructuralBinding(value)) return value
|
|
1742
|
+
let facade = ownedBlockFacades.get(value)
|
|
1743
|
+
if (facade === undefined) {
|
|
1744
|
+
const capability = { props: {} } as unknown as RenderableProtocol
|
|
1745
|
+
Object.defineProperty(capability, RENDERABLE, {
|
|
1746
|
+
configurable: false,
|
|
1747
|
+
enumerable: false,
|
|
1748
|
+
value: {
|
|
1749
|
+
identity: value,
|
|
1750
|
+
input: {},
|
|
1751
|
+
reconcile: false,
|
|
1752
|
+
construct: () => value,
|
|
1753
|
+
},
|
|
1754
|
+
})
|
|
1755
|
+
facade = capability
|
|
1756
|
+
ownedBlockFacades.set(value, facade)
|
|
1757
|
+
ownedFacadeBlocks.set(facade, value)
|
|
1758
|
+
}
|
|
1759
|
+
return facade as T
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1486
1762
|
export function createCompiledProp<T>(
|
|
1487
1763
|
scope: CompiledScope,
|
|
1488
1764
|
sourceMask: SourceMask,
|
|
@@ -1493,7 +1769,9 @@ export function createCompiledProp<T>(
|
|
|
1493
1769
|
const upstream = isCompiledBinding(input) ? input : undefined
|
|
1494
1770
|
const read = (): T => {
|
|
1495
1771
|
const value = upstream === undefined ? (input as T) : upstream[1]()
|
|
1496
|
-
return
|
|
1772
|
+
return sanitizeCompiledPropValue(
|
|
1773
|
+
value === undefined && fallback !== undefined ? fallback() : value,
|
|
1774
|
+
)
|
|
1497
1775
|
}
|
|
1498
1776
|
const slot = createStateSlot<T>(scope[1], sourceMask, read(), assertWritable)
|
|
1499
1777
|
if (upstream !== undefined) {
|
|
@@ -1502,7 +1780,7 @@ export function createCompiledProp<T>(
|
|
|
1502
1780
|
if (owner === undefined) {
|
|
1503
1781
|
throw new Error(DEV ? 'createCompiledProp received an unknown scope' : 'V003')
|
|
1504
1782
|
}
|
|
1505
|
-
owner[1].
|
|
1783
|
+
owner[1].push(remove)
|
|
1506
1784
|
}
|
|
1507
1785
|
return slot
|
|
1508
1786
|
}
|
|
@@ -1518,7 +1796,10 @@ export function createCompiledRestProp(
|
|
|
1518
1796
|
Object.entries(input).filter(([name]) => !excluded.has(name))
|
|
1519
1797
|
const read = (): Record<string, unknown> =>
|
|
1520
1798
|
Object.fromEntries(
|
|
1521
|
-
entries().map(([name, value]) => [
|
|
1799
|
+
entries().map(([name, value]) => [
|
|
1800
|
+
name,
|
|
1801
|
+
sanitizeCompiledPropValue(isCompiledBinding(value) ? value[1]() : value),
|
|
1802
|
+
]),
|
|
1522
1803
|
)
|
|
1523
1804
|
const slot = createStateSlot(scope[1], sourceMask, read(), stateWriteGuard(scope))
|
|
1524
1805
|
const publish = (): void => {
|
|
@@ -1545,7 +1826,7 @@ export function createCompiledRestProp(
|
|
|
1545
1826
|
if (owner === undefined) {
|
|
1546
1827
|
throw new Error(DEV ? 'createCompiledRestProp received an unknown scope' : 'V003')
|
|
1547
1828
|
}
|
|
1548
|
-
owner[1].
|
|
1829
|
+
owner[1].push(() => {
|
|
1549
1830
|
for (const remove of removers) remove()
|
|
1550
1831
|
})
|
|
1551
1832
|
return slot
|
|
@@ -1605,8 +1886,9 @@ export function compiledEvent<Arguments extends unknown[]>(
|
|
|
1605
1886
|
throw new Error(DEV ? 'compiledEvent received an unknown scope' : 'V003')
|
|
1606
1887
|
}
|
|
1607
1888
|
const errorOwner = activeOwner ?? owner
|
|
1608
|
-
|
|
1609
|
-
|
|
1889
|
+
const listener = (...arguments_: Arguments) => {
|
|
1890
|
+
if (errorOwner[0]) return
|
|
1891
|
+
return runOwnerTask(errorOwner, () =>
|
|
1610
1892
|
withOwner(errorOwner, () =>
|
|
1611
1893
|
scope[2](() => {
|
|
1612
1894
|
const current = isCompiledBinding(handler) ? handler[1]() : handler
|
|
@@ -1615,6 +1897,43 @@ export function compiledEvent<Arguments extends unknown[]>(
|
|
|
1615
1897
|
}),
|
|
1616
1898
|
),
|
|
1617
1899
|
)
|
|
1900
|
+
}
|
|
1901
|
+
;(listener as unknown as CompiledEventHandler)[COMPILED_EVENT_HANDLER] = true
|
|
1902
|
+
return listener
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
export function compiledInlineEvent<Arguments extends unknown[]>(
|
|
1906
|
+
scope: CompiledScope,
|
|
1907
|
+
handler: (...arguments_: Arguments) => void,
|
|
1908
|
+
): (...arguments_: Arguments) => void {
|
|
1909
|
+
const owner = scopeOwners.get(scope)
|
|
1910
|
+
if (owner === undefined) {
|
|
1911
|
+
throw new Error(DEV ? 'compiledInlineEvent received an unknown scope' : 'V003')
|
|
1912
|
+
}
|
|
1913
|
+
const listener = handler as unknown as CompiledInlineEventHandler<Arguments>
|
|
1914
|
+
listener[COMPILED_INLINE_EVENT_OWNER] = activeOwner ?? owner
|
|
1915
|
+
listener[COMPILED_DELEGATED_EVENT_INVOKE] = invokeCompiledInlineEvent
|
|
1916
|
+
return listener
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
function invokeCompiledInlineEvent<Arguments extends unknown[]>(
|
|
1920
|
+
this: CompiledInlineEventHandler<Arguments>,
|
|
1921
|
+
...arguments_: Arguments
|
|
1922
|
+
): void {
|
|
1923
|
+
const owner = this[COMPILED_INLINE_EVENT_OWNER]
|
|
1924
|
+
if (owner[0]) return
|
|
1925
|
+
return runOwnerTask(owner, () =>
|
|
1926
|
+
withOwner(owner, () => runCompiledTransaction(() => this(...arguments_))),
|
|
1927
|
+
)
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
export function isCompiledEventHandler(value: unknown): value is EventListener {
|
|
1931
|
+
if (typeof value !== 'function') return false
|
|
1932
|
+
const handler = value as typeof value & CompiledEventMetadata
|
|
1933
|
+
return (
|
|
1934
|
+
handler[COMPILED_EVENT_HANDLER] === true ||
|
|
1935
|
+
typeof handler[COMPILED_DELEGATED_EVENT_INVOKE] === 'function'
|
|
1936
|
+
)
|
|
1618
1937
|
}
|
|
1619
1938
|
|
|
1620
1939
|
export interface CompiledTaskController {
|
|
@@ -1716,7 +2035,12 @@ export function choose(
|
|
|
1716
2035
|
return structural(
|
|
1717
2036
|
scope,
|
|
1718
2037
|
(parent, before) => {
|
|
1719
|
-
const
|
|
2038
|
+
const range = structuralRange(parent, before, 'choice', true, true)
|
|
2039
|
+
const markerlessHydration = range === undefined
|
|
2040
|
+
const start = range?.[0] ?? document.createComment(DEV ? 'vidact:choice' : '')
|
|
2041
|
+
const end = range?.[1] ?? document.createComment(DEV ? '/vidact:choice' : '')
|
|
2042
|
+
const hydrated = range?.[2] ?? true
|
|
2043
|
+
const ownsMarkers = range?.[3] ?? true
|
|
1720
2044
|
let selected = -1
|
|
1721
2045
|
let branchOwner: Owner | null = null
|
|
1722
2046
|
let branchNodes: readonly Node[] = []
|
|
@@ -1726,15 +2050,28 @@ export function choose(
|
|
|
1726
2050
|
const next =
|
|
1727
2051
|
mode === 'not-nullish' ? (value === null || value === undefined ? 1 : 0) : value ? 0 : 1
|
|
1728
2052
|
if (next === selected) return
|
|
1729
|
-
const currentParent = rangeParent(start, end, 'choice block')
|
|
1730
2053
|
const nextOwner = createOwner()
|
|
1731
|
-
const
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
2054
|
+
const render = next === 0 ? consequent : alternate
|
|
2055
|
+
let nodes: readonly Node[]
|
|
2056
|
+
if (markerlessHydration && selected === -1) {
|
|
2057
|
+
const [first, after, hydratedNodes] = stageMarkerlessHydrationRender(
|
|
2058
|
+
parent,
|
|
2059
|
+
before,
|
|
2060
|
+
render,
|
|
2061
|
+
nextOwner,
|
|
2062
|
+
)
|
|
2063
|
+
parent.insertBefore(start, first)
|
|
2064
|
+
parent.insertBefore(end, after)
|
|
2065
|
+
nodes = hydratedNodes
|
|
2066
|
+
} else {
|
|
2067
|
+
const currentParent = rangeParent(start, end, 'choice block')
|
|
2068
|
+
const [fragment, stagedNodes] =
|
|
2069
|
+
hydrated && selected === -1
|
|
2070
|
+
? withHydrationInsertion(currentParent, end, () => stageRender(render, nextOwner))
|
|
2071
|
+
: stageRender(render, nextOwner)
|
|
2072
|
+
currentParent.insertBefore(fragment, end)
|
|
2073
|
+
nodes = stagedNodes
|
|
2074
|
+
}
|
|
1738
2075
|
try {
|
|
1739
2076
|
commitPublishedNodes(nodes)
|
|
1740
2077
|
} catch (error) {
|
|
@@ -1762,8 +2099,10 @@ export function choose(
|
|
|
1762
2099
|
try {
|
|
1763
2100
|
disposePublished(branchOwner, branchNodes)
|
|
1764
2101
|
} finally {
|
|
1765
|
-
|
|
1766
|
-
|
|
2102
|
+
if (ownsMarkers) {
|
|
2103
|
+
start.remove()
|
|
2104
|
+
end.remove()
|
|
2105
|
+
}
|
|
1767
2106
|
}
|
|
1768
2107
|
})
|
|
1769
2108
|
},
|
|
@@ -1850,9 +2189,21 @@ export function keyed<T, K>(
|
|
|
1850
2189
|
values: () => readonly T[],
|
|
1851
2190
|
key: (value: T, index: number) => K,
|
|
1852
2191
|
render: (value: StateSlot<T>, index: StateSlot<number>, itemScope: CompiledScope) => RenderValue,
|
|
1853
|
-
|
|
2192
|
+
trackIndexOrAdditionalScope?: boolean | CompiledScope,
|
|
2193
|
+
additionalScopeOrReads?: CompiledScope | SourceMask,
|
|
1854
2194
|
additionalReads?: SourceMask,
|
|
1855
2195
|
): StructuralBinding {
|
|
2196
|
+
const compilerManaged = typeof trackIndexOrAdditionalScope === 'boolean'
|
|
2197
|
+
const trackIndex =
|
|
2198
|
+
typeof trackIndexOrAdditionalScope === 'boolean' ? trackIndexOrAdditionalScope : true
|
|
2199
|
+
const additionalScope =
|
|
2200
|
+
typeof trackIndexOrAdditionalScope === 'boolean'
|
|
2201
|
+
? (additionalScopeOrReads as CompiledScope | undefined)
|
|
2202
|
+
: trackIndexOrAdditionalScope
|
|
2203
|
+
const resolvedAdditionalReads =
|
|
2204
|
+
typeof trackIndexOrAdditionalScope === 'boolean'
|
|
2205
|
+
? additionalReads
|
|
2206
|
+
: (additionalScopeOrReads as SourceMask | undefined)
|
|
1856
2207
|
return structural(
|
|
1857
2208
|
scope,
|
|
1858
2209
|
(parent, before) => {
|
|
@@ -1863,27 +2214,56 @@ export function keyed<T, K>(
|
|
|
1863
2214
|
{
|
|
1864
2215
|
key,
|
|
1865
2216
|
render(value, index) {
|
|
1866
|
-
const
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
const
|
|
2217
|
+
const itemScope = compilerManaged
|
|
2218
|
+
? createCompilerKeyedScope()
|
|
2219
|
+
: createNarrowCompiledScope()
|
|
2220
|
+
const owner = scopeOwners.get(itemScope)!
|
|
2221
|
+
const valueSlot = compilerManaged
|
|
2222
|
+
? createReadOnlyStateSlot(itemScope, itemSource, value)
|
|
2223
|
+
: createCompiledState(itemScope, itemSource, value)
|
|
2224
|
+
const indexSlot = trackIndex
|
|
2225
|
+
? compilerManaged
|
|
2226
|
+
? createReadOnlyStateSlot(itemScope, indexSource, index)
|
|
2227
|
+
: createCompiledState(itemScope, indexSource, index)
|
|
2228
|
+
: undefined
|
|
1870
2229
|
try {
|
|
1871
2230
|
const nodes = withOwner(owner, () => {
|
|
1872
|
-
|
|
1873
|
-
|
|
2231
|
+
const rendered = render(
|
|
2232
|
+
valueSlot as StateSlot<T>,
|
|
2233
|
+
indexSlot as StateSlot<number>,
|
|
2234
|
+
itemScope,
|
|
2235
|
+
)
|
|
2236
|
+
return !isHydrating() &&
|
|
2237
|
+
rendered instanceof Node &&
|
|
2238
|
+
!(rendered instanceof DocumentFragment)
|
|
2239
|
+
? [rendered]
|
|
2240
|
+
: materialize(rendered)
|
|
1874
2241
|
})
|
|
1875
2242
|
return [
|
|
1876
2243
|
nodes,
|
|
1877
2244
|
(nextValue: T, nextIndex: number) => {
|
|
2245
|
+
if (
|
|
2246
|
+
Object.is(valueSlot.get(), nextValue) &&
|
|
2247
|
+
(!trackIndex || indexSlot!.get() === nextIndex)
|
|
2248
|
+
) {
|
|
2249
|
+
return
|
|
2250
|
+
}
|
|
1878
2251
|
itemScope[2](() => {
|
|
1879
|
-
|
|
1880
|
-
|
|
2252
|
+
if (compilerManaged) {
|
|
2253
|
+
replaceReadOnlyStateSlot(valueSlot as ReadOnlyStateCell<T>, nextValue)
|
|
2254
|
+
if (indexSlot !== undefined) {
|
|
2255
|
+
replaceReadOnlyStateSlot(indexSlot as ReadOnlyStateCell<number>, nextIndex)
|
|
2256
|
+
}
|
|
2257
|
+
} else {
|
|
2258
|
+
;(valueSlot as StateSlot<T>).set(nextValue)
|
|
2259
|
+
;(indexSlot as StateSlot<number> | undefined)?.set(nextIndex)
|
|
2260
|
+
}
|
|
1881
2261
|
})
|
|
1882
2262
|
},
|
|
1883
|
-
|
|
2263
|
+
itemScope,
|
|
1884
2264
|
] as const
|
|
1885
2265
|
} catch (error) {
|
|
1886
|
-
|
|
2266
|
+
itemScope[3]()
|
|
1887
2267
|
throw error
|
|
1888
2268
|
}
|
|
1889
2269
|
},
|
|
@@ -1900,7 +2280,13 @@ export function keyed<T, K>(
|
|
|
1900
2280
|
}
|
|
1901
2281
|
}
|
|
1902
2282
|
update()
|
|
1903
|
-
const removeUpdater = subscribe(
|
|
2283
|
+
const removeUpdater = subscribe(
|
|
2284
|
+
scope,
|
|
2285
|
+
reads,
|
|
2286
|
+
update,
|
|
2287
|
+
additionalScope,
|
|
2288
|
+
resolvedAdditionalReads,
|
|
2289
|
+
)
|
|
1904
2290
|
onCleanup(() => {
|
|
1905
2291
|
removeUpdater()
|
|
1906
2292
|
list.dispose()
|
|
@@ -1988,7 +2374,7 @@ export function compiledRoot(
|
|
|
1988
2374
|
componentCommitOwners.set(end, owner)
|
|
1989
2375
|
const fragment = document.createDocumentFragment()
|
|
1990
2376
|
if (hydratedRange === undefined) fragment.append(start, end)
|
|
1991
|
-
owner[1].
|
|
2377
|
+
owner[1].push(() => {
|
|
1992
2378
|
try {
|
|
1993
2379
|
removeBetween(start, end)
|
|
1994
2380
|
} finally {
|
|
@@ -2052,7 +2438,7 @@ export function adoptCompiledRoot(root: unknown): void {
|
|
|
2052
2438
|
if (range === undefined) return
|
|
2053
2439
|
const owner = scopeOwners.get(range[2])
|
|
2054
2440
|
if (activeOwner !== null && owner !== undefined && activeOwner !== owner) {
|
|
2055
|
-
activeOwner[1].
|
|
2441
|
+
activeOwner[1].push(range[2][3])
|
|
2056
2442
|
}
|
|
2057
2443
|
}
|
|
2058
2444
|
|
|
@@ -2093,7 +2479,9 @@ export function queueElementRef(element: Element, value: unknown): void {
|
|
|
2093
2479
|
if (!isRefValue(value)) {
|
|
2094
2480
|
throw new TypeError(DEV ? 'ref must be a callback or an object with current' : 'V006')
|
|
2095
2481
|
}
|
|
2096
|
-
|
|
2482
|
+
const pending: PendingRef = [activeOwner, value]
|
|
2483
|
+
setPendingRef(element, pending)
|
|
2484
|
+
activeOwner?.[1].push(() => deletePendingRef(element, pending))
|
|
2097
2485
|
}
|
|
2098
2486
|
|
|
2099
2487
|
export function useImperativeHandle<T>(
|
|
@@ -2117,7 +2505,7 @@ export function useImperativeHandle<T>(
|
|
|
2117
2505
|
DEV ? 'reactive useImperativeHandle dependencies are not supported yet' : 'V014',
|
|
2118
2506
|
)
|
|
2119
2507
|
}
|
|
2120
|
-
queueOwnerCommit(owner, () => owner[1].
|
|
2508
|
+
queueOwnerCommit(owner, () => owner[1].push(attachRef(ref, create())))
|
|
2121
2509
|
}
|
|
2122
2510
|
|
|
2123
2511
|
type EffectResult = void | (() => void)
|
|
@@ -2154,7 +2542,7 @@ export function useInsertionEffect(
|
|
|
2154
2542
|
? readProfiledEffectCleanup(owner, create)
|
|
2155
2543
|
: readEffectCleanup(create())
|
|
2156
2544
|
})
|
|
2157
|
-
lifetimeOwner[1].
|
|
2545
|
+
lifetimeOwner[1].push(() => cleanup())
|
|
2158
2546
|
return
|
|
2159
2547
|
}
|
|
2160
2548
|
let cleanup = noop
|
|
@@ -2181,7 +2569,7 @@ export function useInsertionEffect(
|
|
|
2181
2569
|
queueInsertionCommit(owner, () => {
|
|
2182
2570
|
if (!lifetimeOwner[0]) activate()
|
|
2183
2571
|
})
|
|
2184
|
-
lifetimeOwner[1].
|
|
2572
|
+
lifetimeOwner[1].push(disposeResource)
|
|
2185
2573
|
}
|
|
2186
2574
|
|
|
2187
2575
|
export function useEffect(create: () => EffectResult, dependencies?: readonly unknown[]): void {
|
|
@@ -2221,7 +2609,7 @@ function registerEffect(
|
|
|
2221
2609
|
run()
|
|
2222
2610
|
}
|
|
2223
2611
|
})
|
|
2224
|
-
lifetimeOwner[1].
|
|
2612
|
+
lifetimeOwner[1].push(() => {
|
|
2225
2613
|
generation += 1
|
|
2226
2614
|
if (passive) (DEV ? scheduleTask : queueMicrotask)(() => runOwnerTask(lifetimeOwner, cleanup))
|
|
2227
2615
|
else cleanup()
|
|
@@ -2270,7 +2658,7 @@ function registerEffect(
|
|
|
2270
2658
|
queueOwnerCommit(owner, () => {
|
|
2271
2659
|
if (!lifetimeOwner[0]) activate()
|
|
2272
2660
|
})
|
|
2273
|
-
lifetimeOwner[1].
|
|
2661
|
+
lifetimeOwner[1].push(disposeResource)
|
|
2274
2662
|
}
|
|
2275
2663
|
|
|
2276
2664
|
export function compiledLayoutEffect(
|
|
@@ -2319,7 +2707,7 @@ export function compiledInsertionEffect(
|
|
|
2319
2707
|
const removeUpdater = subscribe(scope, reads, () =>
|
|
2320
2708
|
stagePublication([noop, noop, undefined, run, -20]),
|
|
2321
2709
|
)
|
|
2322
|
-
lifetimeOwner[1].
|
|
2710
|
+
lifetimeOwner[1].push(() => {
|
|
2323
2711
|
removeUpdater()
|
|
2324
2712
|
cleanup()
|
|
2325
2713
|
})
|
|
@@ -2372,7 +2760,7 @@ export function compiledInsertionEffect(
|
|
|
2372
2760
|
const removeUpdater = subscribe(scope, reads, () => {
|
|
2373
2761
|
if (connected) stagePublication([noop, noop, undefined, run, -20])
|
|
2374
2762
|
})
|
|
2375
|
-
lifetimeOwner[1].
|
|
2763
|
+
lifetimeOwner[1].push(() => {
|
|
2376
2764
|
removeUpdater()
|
|
2377
2765
|
disposeResource()
|
|
2378
2766
|
})
|
|
@@ -2428,7 +2816,7 @@ export function compiledEffect(
|
|
|
2428
2816
|
if (passive) schedule()
|
|
2429
2817
|
else stagePublication([noop, noop, undefined, run, 20])
|
|
2430
2818
|
})
|
|
2431
|
-
lifetimeOwner[1].
|
|
2819
|
+
lifetimeOwner[1].push(() => {
|
|
2432
2820
|
generation += 1
|
|
2433
2821
|
removeUpdater()
|
|
2434
2822
|
if (passive) (DEV ? scheduleTask : queueMicrotask)(() => runOwnerTask(lifetimeOwner, cleanup))
|
|
@@ -2501,7 +2889,7 @@ export function compiledEffect(
|
|
|
2501
2889
|
if (passive) schedule()
|
|
2502
2890
|
else stagePublication([noop, noop, undefined, run, 20])
|
|
2503
2891
|
})
|
|
2504
|
-
lifetimeOwner[1].
|
|
2892
|
+
lifetimeOwner[1].push(() => {
|
|
2505
2893
|
removeUpdater()
|
|
2506
2894
|
disposeResource()
|
|
2507
2895
|
})
|
|
@@ -2620,7 +3008,7 @@ export function compiledImperativeHandle<T>(
|
|
|
2620
3008
|
])
|
|
2621
3009
|
}
|
|
2622
3010
|
const removeUpdater = subscribe(scope, reads, update)
|
|
2623
|
-
owner[1].
|
|
3011
|
+
owner[1].push(() => {
|
|
2624
3012
|
removeUpdater()
|
|
2625
3013
|
cleanup()
|
|
2626
3014
|
})
|
|
@@ -2631,6 +3019,7 @@ function queueOwnerCommit(owner: Owner, commit: () => void): void {
|
|
|
2631
3019
|
if (commits === undefined) {
|
|
2632
3020
|
commits = new Set()
|
|
2633
3021
|
pendingOwnerCommits.set(owner, commits)
|
|
3022
|
+
pendingOwnerCommitCount += 1
|
|
2634
3023
|
}
|
|
2635
3024
|
commits.add(commit)
|
|
2636
3025
|
}
|
|
@@ -2640,6 +3029,7 @@ function queueInsertionCommit(owner: Owner, commit: () => void): void {
|
|
|
2640
3029
|
if (commits === undefined) {
|
|
2641
3030
|
commits = new Set()
|
|
2642
3031
|
pendingInsertionCommits.set(owner, commits)
|
|
3032
|
+
pendingInsertionOwnerCount += 1
|
|
2643
3033
|
}
|
|
2644
3034
|
commits.add(commit)
|
|
2645
3035
|
}
|
|
@@ -2670,7 +3060,7 @@ export function mountCompiledRef(element: Element, value: CompiledBinding<unknow
|
|
|
2670
3060
|
// oxlint-disable-next-line unicorn/consistent-function-scoping -- This placeholder is replaced when the ref attaches.
|
|
2671
3061
|
let cleanup = noop
|
|
2672
3062
|
const pending: PendingRef = [activeOwner, current, (attached) => (cleanup = attached)]
|
|
2673
|
-
|
|
3063
|
+
setPendingRef(element, pending)
|
|
2674
3064
|
|
|
2675
3065
|
const removeUpdater = subscribeBinding(value, () => {
|
|
2676
3066
|
const next = value[1]()
|
|
@@ -2729,7 +3119,7 @@ export function mountCompiledRef(element: Element, value: CompiledBinding<unknow
|
|
|
2729
3119
|
})
|
|
2730
3120
|
onCleanup(() => {
|
|
2731
3121
|
removeUpdater()
|
|
2732
|
-
|
|
3122
|
+
deletePendingRef(element, pending)
|
|
2733
3123
|
cleanup()
|
|
2734
3124
|
})
|
|
2735
3125
|
}
|
|
@@ -2814,6 +3204,7 @@ export function mountCompiled(
|
|
|
2814
3204
|
throw new Error(DEV ? 'mountCompiled received an unknown component result' : 'V007')
|
|
2815
3205
|
}
|
|
2816
3206
|
const previous = [...host.childNodes]
|
|
3207
|
+
const releaseEventRoot = retainCompiledEventRoot(host)
|
|
2817
3208
|
try {
|
|
2818
3209
|
root[1](host, previous[0] ?? null)
|
|
2819
3210
|
const rootOwner = scopeOwners.get(range[2])
|
|
@@ -2829,6 +3220,7 @@ export function mountCompiled(
|
|
|
2829
3220
|
for (const node of previous) host.removeChild(node)
|
|
2830
3221
|
}
|
|
2831
3222
|
} catch (error) {
|
|
3223
|
+
releaseEventRoot()
|
|
2832
3224
|
try {
|
|
2833
3225
|
range[2][3]()
|
|
2834
3226
|
} catch {
|
|
@@ -2838,8 +3230,17 @@ export function mountCompiled(
|
|
|
2838
3230
|
rootIdentity.onUncaughtError(error)
|
|
2839
3231
|
return { dispose: noop }
|
|
2840
3232
|
}
|
|
3233
|
+
let disposed = false
|
|
2841
3234
|
return {
|
|
2842
|
-
dispose:
|
|
3235
|
+
dispose: () => {
|
|
3236
|
+
if (disposed) return
|
|
3237
|
+
disposed = true
|
|
3238
|
+
try {
|
|
3239
|
+
range[2][3]()
|
|
3240
|
+
} finally {
|
|
3241
|
+
releaseEventRoot()
|
|
3242
|
+
}
|
|
3243
|
+
},
|
|
2843
3244
|
}
|
|
2844
3245
|
}
|
|
2845
3246
|
|
|
@@ -2859,12 +3260,43 @@ export function mountCompiledBinding(parent: Node, value: CompiledBinding<unknow
|
|
|
2859
3260
|
mountCompiledBindingBefore(parent, value, null)
|
|
2860
3261
|
}
|
|
2861
3262
|
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
3263
|
+
type CompiledPropApply<T> =
|
|
3264
|
+
| ((next: T) => void | (() => void))
|
|
3265
|
+
| ((element: Element, name: string, next: T) => void | (() => void))
|
|
3266
|
+
|
|
3267
|
+
function applyCompiledPropValue<T>(
|
|
3268
|
+
apply: CompiledPropApply<T>,
|
|
3269
|
+
element: Element | undefined,
|
|
3270
|
+
name: string | undefined,
|
|
3271
|
+
next: T,
|
|
3272
|
+
): void | (() => void) {
|
|
3273
|
+
return element === undefined
|
|
3274
|
+
? (apply as (next: T) => void | (() => void))(next)
|
|
3275
|
+
: (apply as (element: Element, name: string, next: T) => void | (() => void))(
|
|
3276
|
+
element,
|
|
3277
|
+
name!,
|
|
3278
|
+
next,
|
|
3279
|
+
)
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3282
|
+
export function mountCompiledProp<T>(
|
|
3283
|
+
value: CompiledBinding<T>,
|
|
3284
|
+
apply: (next: T) => void | (() => void),
|
|
3285
|
+
): void
|
|
3286
|
+
export function mountCompiledProp<T>(
|
|
3287
|
+
value: CompiledBinding<T>,
|
|
3288
|
+
apply: (element: Element, name: string, next: T) => void | (() => void),
|
|
3289
|
+
element: Element,
|
|
3290
|
+
name: string,
|
|
3291
|
+
): void
|
|
3292
|
+
export function mountCompiledProp<T>(
|
|
3293
|
+
value: CompiledBinding<T>,
|
|
3294
|
+
apply: CompiledPropApply<T>,
|
|
3295
|
+
element?: Element,
|
|
3296
|
+
name?: string,
|
|
2865
3297
|
): void {
|
|
2866
3298
|
let current = value[1]()
|
|
2867
|
-
let cleanup = apply
|
|
3299
|
+
let cleanup = applyCompiledPropValue(apply, element, name, current)
|
|
2868
3300
|
const removeUpdater = subscribeBinding(value, () => {
|
|
2869
3301
|
const next = value[1]()
|
|
2870
3302
|
if (Object.is(next, current)) return
|
|
@@ -2874,10 +3306,10 @@ export function mountCompiledProp(
|
|
|
2874
3306
|
stagePublication([
|
|
2875
3307
|
() => {
|
|
2876
3308
|
try {
|
|
2877
|
-
nextCleanup = apply
|
|
3309
|
+
nextCleanup = applyCompiledPropValue(apply, element, name, next)
|
|
2878
3310
|
} catch (error) {
|
|
2879
3311
|
try {
|
|
2880
|
-
apply
|
|
3312
|
+
applyCompiledPropValue(apply, element, name, previous)
|
|
2881
3313
|
} catch {
|
|
2882
3314
|
// Preserve the setter error that aborted publication.
|
|
2883
3315
|
}
|
|
@@ -2891,7 +3323,7 @@ export function mountCompiledProp(
|
|
|
2891
3323
|
() => {
|
|
2892
3324
|
if (!committedNext) return
|
|
2893
3325
|
nextCleanup?.()
|
|
2894
|
-
cleanup = apply
|
|
3326
|
+
cleanup = applyCompiledPropValue(apply, element, name, previous)
|
|
2895
3327
|
current = previous
|
|
2896
3328
|
committedNext = false
|
|
2897
3329
|
},
|
|
@@ -2968,14 +3400,40 @@ function structuralRange(
|
|
|
2968
3400
|
parent: Node,
|
|
2969
3401
|
before: Node | null,
|
|
2970
3402
|
label: string,
|
|
2971
|
-
|
|
3403
|
+
borrowOuter?: boolean,
|
|
3404
|
+
allowMarkerlessHydration?: false,
|
|
3405
|
+
): readonly [start: Comment, end: Comment, hydrated: boolean, ownsMarkers: boolean]
|
|
3406
|
+
function structuralRange(
|
|
3407
|
+
parent: Node,
|
|
3408
|
+
before: Node | null,
|
|
3409
|
+
label: string,
|
|
3410
|
+
borrowOuter: boolean,
|
|
3411
|
+
allowMarkerlessHydration: true,
|
|
3412
|
+
): readonly [start: Comment, end: Comment, hydrated: boolean, ownsMarkers: boolean] | undefined
|
|
3413
|
+
function structuralRange(
|
|
3414
|
+
parent: Node,
|
|
3415
|
+
before: Node | null,
|
|
3416
|
+
label: string,
|
|
3417
|
+
borrowOuter = false,
|
|
3418
|
+
allowMarkerlessHydration = false,
|
|
3419
|
+
): readonly [start: Comment, end: Comment, hydrated: boolean, ownsMarkers: boolean] | undefined {
|
|
3420
|
+
const current = borrowHydrationSlotRange(parent, true)
|
|
3421
|
+
if (current !== undefined) {
|
|
3422
|
+
claimHydrationSlotRange(parent)
|
|
3423
|
+
return [current[0], current[1], true, true]
|
|
3424
|
+
}
|
|
3425
|
+
if (borrowOuter) {
|
|
3426
|
+
const outer = borrowHydrationSlotRange(parent, false)
|
|
3427
|
+
if (outer !== undefined) return [outer[0], outer[1], true, false]
|
|
3428
|
+
}
|
|
3429
|
+
if (allowMarkerlessHydration && hydrationCursor(parent) !== undefined) return undefined
|
|
2972
3430
|
const hydrated = claimHydrationSlotRange(parent)
|
|
2973
|
-
if (hydrated !== undefined) return [hydrated[0], hydrated[1], true]
|
|
3431
|
+
if (hydrated !== undefined) return [hydrated[0], hydrated[1], true, true]
|
|
2974
3432
|
const start = document.createComment(DEV ? `vidact:${label}` : '')
|
|
2975
3433
|
const end = document.createComment(DEV ? `/vidact:${label}` : '')
|
|
2976
3434
|
parent.insertBefore(start, before)
|
|
2977
3435
|
parent.insertBefore(end, before)
|
|
2978
|
-
return [start, end, false]
|
|
3436
|
+
return [start, end, false, true]
|
|
2979
3437
|
}
|
|
2980
3438
|
|
|
2981
3439
|
function subscribe(
|
|
@@ -2985,12 +3443,29 @@ function subscribe(
|
|
|
2985
3443
|
additionalScope?: CompiledScope,
|
|
2986
3444
|
additionalReads?: SourceMask,
|
|
2987
3445
|
): () => void {
|
|
2988
|
-
|
|
2989
|
-
if (
|
|
2990
|
-
|
|
2991
|
-
|
|
3446
|
+
let first: (() => void) | undefined
|
|
3447
|
+
if (!isEmptySources(reads)) {
|
|
3448
|
+
const remove = scope[0](reads, run)
|
|
3449
|
+
if (remove !== noop) first = remove
|
|
3450
|
+
}
|
|
3451
|
+
let second: (() => void) | undefined
|
|
3452
|
+
if (
|
|
3453
|
+
additionalScope !== undefined &&
|
|
3454
|
+
additionalReads !== undefined &&
|
|
3455
|
+
!isEmptySources(additionalReads)
|
|
3456
|
+
) {
|
|
3457
|
+
const additionalRun =
|
|
3458
|
+
scopeNamespaces.get(additionalScope) === scopeNamespaces.get(scope)
|
|
3459
|
+
? run
|
|
3460
|
+
: () => withScopeNamespace(scope, run)
|
|
3461
|
+
const remove = additionalScope[0](additionalReads, additionalRun)
|
|
3462
|
+
if (remove !== noop) second = remove
|
|
3463
|
+
}
|
|
3464
|
+
if (first === undefined) return second ?? noop
|
|
3465
|
+
if (second === undefined) return first
|
|
2992
3466
|
return () => {
|
|
2993
|
-
|
|
3467
|
+
first()
|
|
3468
|
+
second()
|
|
2994
3469
|
}
|
|
2995
3470
|
}
|
|
2996
3471
|
|
|
@@ -3033,7 +3508,7 @@ function createOwner(
|
|
|
3033
3508
|
if (profilingEnabled) {
|
|
3034
3509
|
const owner: Owner = [
|
|
3035
3510
|
false,
|
|
3036
|
-
|
|
3511
|
+
[],
|
|
3037
3512
|
context,
|
|
3038
3513
|
rootIdentity,
|
|
3039
3514
|
boundary,
|
|
@@ -3044,8 +3519,8 @@ function createOwner(
|
|
|
3044
3519
|
return owner
|
|
3045
3520
|
}
|
|
3046
3521
|
const owner: Owner = retainedUiEnabled
|
|
3047
|
-
? [false,
|
|
3048
|
-
: [false,
|
|
3522
|
+
? [false, [], context, rootIdentity, boundary, retainedConnection]
|
|
3523
|
+
: [false, [], context, rootIdentity, boundary]
|
|
3049
3524
|
if (DEV) owner[7] = activeConstructionOwner?.[7] ?? activeProfileName
|
|
3050
3525
|
return owner
|
|
3051
3526
|
}
|
|
@@ -3388,7 +3863,7 @@ function withRootIdentity<T>(rootIdentity: RootIdentity | null, operation: () =>
|
|
|
3388
3863
|
}
|
|
3389
3864
|
|
|
3390
3865
|
function onCleanup(cleanup: () => void): void {
|
|
3391
|
-
activeOwner?.[1].
|
|
3866
|
+
activeOwner?.[1].push(cleanup)
|
|
3392
3867
|
}
|
|
3393
3868
|
|
|
3394
3869
|
function disposeOwner(owner: Owner): void {
|
|
@@ -3397,21 +3872,21 @@ function disposeOwner(owner: Owner): void {
|
|
|
3397
3872
|
disposalCascadeDepth += 1
|
|
3398
3873
|
disposalCascadeOwners.add(owner)
|
|
3399
3874
|
if (DEV) activeOwnerCount -= 1
|
|
3400
|
-
pendingInsertionCommits.delete(owner)
|
|
3401
|
-
pendingOwnerCommits.delete(owner)
|
|
3402
|
-
const cleanups = [...owner[1]]
|
|
3403
|
-
owner[1].clear()
|
|
3875
|
+
if (pendingInsertionCommits.delete(owner)) pendingInsertionOwnerCount -= 1
|
|
3876
|
+
if (pendingOwnerCommits.delete(owner)) pendingOwnerCommitCount -= 1
|
|
3404
3877
|
let firstError: unknown
|
|
3405
3878
|
let hasError = false
|
|
3406
3879
|
try {
|
|
3407
|
-
for (let
|
|
3880
|
+
for (let remaining = owner[1].length; remaining > 0; remaining -= 1) {
|
|
3881
|
+
const cleanup = owner[1].pop()
|
|
3408
3882
|
try {
|
|
3409
|
-
|
|
3883
|
+
cleanup?.()
|
|
3410
3884
|
} catch (error) {
|
|
3411
3885
|
if (!hasError) firstError = error
|
|
3412
3886
|
hasError = true
|
|
3413
3887
|
}
|
|
3414
3888
|
}
|
|
3889
|
+
owner[1].length = 0
|
|
3415
3890
|
} finally {
|
|
3416
3891
|
disposalCascadeDepth -= 1
|
|
3417
3892
|
if (disposalCascadeDepth === 0) disposalCascadeOwners.clear()
|
|
@@ -3423,8 +3898,50 @@ function disposeOwner(owner: Owner): void {
|
|
|
3423
3898
|
export function readCompiledOwnerMetrics(): {
|
|
3424
3899
|
readonly active: number
|
|
3425
3900
|
readonly created: number
|
|
3901
|
+
readonly updaters: number
|
|
3902
|
+
readonly schedulerPlans: number
|
|
3426
3903
|
} {
|
|
3427
|
-
return {
|
|
3904
|
+
return {
|
|
3905
|
+
active: activeOwnerCount,
|
|
3906
|
+
created: createdOwnerCount,
|
|
3907
|
+
updaters: createdUpdaterCount,
|
|
3908
|
+
schedulerPlans: createdSchedulerPlanCount,
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
|
|
3912
|
+
/** @internal Test-only publication traversal evidence. */
|
|
3913
|
+
export function readCompiledPublicationMetrics(): { readonly visitedNodes: number } {
|
|
3914
|
+
return { visitedNodes: visitedPublishedNodeCount }
|
|
3915
|
+
}
|
|
3916
|
+
|
|
3917
|
+
interface MountedBindingState {
|
|
3918
|
+
start: Comment | null
|
|
3919
|
+
end: Comment | null
|
|
3920
|
+
current: unknown
|
|
3921
|
+
currentOwner: Owner | null
|
|
3922
|
+
text: Text | null
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3925
|
+
function ensureMountedBindingRange(state: MountedBindingState): readonly [Comment, Comment] {
|
|
3926
|
+
if (state.start !== null && state.end !== null) return [state.start, state.end]
|
|
3927
|
+
const anchor = state.text
|
|
3928
|
+
const parent = anchor?.parentNode
|
|
3929
|
+
if (anchor === null || parent === null || parent === undefined) {
|
|
3930
|
+
throw new Error(DEV ? 'markerless binding text is detached' : 'V010')
|
|
3931
|
+
}
|
|
3932
|
+
state.start = document.createComment(DEV ? 'vidact:binding' : '')
|
|
3933
|
+
state.end = document.createComment(DEV ? '/vidact:binding' : '')
|
|
3934
|
+
parent.insertBefore(state.start, anchor)
|
|
3935
|
+
parent.insertBefore(state.end, anchor.nextSibling)
|
|
3936
|
+
return [state.start, state.end]
|
|
3937
|
+
}
|
|
3938
|
+
|
|
3939
|
+
function clearMountedBinding(state: MountedBindingState): void {
|
|
3940
|
+
const [start, end] = ensureMountedBindingRange(state)
|
|
3941
|
+
const owner = state.currentOwner
|
|
3942
|
+
state.currentOwner = null
|
|
3943
|
+
state.text = null
|
|
3944
|
+
disposeRange(owner, start, end)
|
|
3428
3945
|
}
|
|
3429
3946
|
|
|
3430
3947
|
function mountCompiledBindingBefore(
|
|
@@ -3432,144 +3949,160 @@ function mountCompiledBindingBefore(
|
|
|
3432
3949
|
value: CompiledBinding<unknown>,
|
|
3433
3950
|
before: Node | null,
|
|
3434
3951
|
): void {
|
|
3435
|
-
const unset = Symbol(DEV ? 'Vidact.UnsetBinding' : undefined)
|
|
3436
3952
|
const initial = value[1]()
|
|
3437
3953
|
const scalarInitial = isScalarRenderValue(initial)
|
|
3954
|
+
const structuralInitial = ownedStructuralBinding(initial)
|
|
3438
3955
|
const hydratedTextRange = scalarInitial
|
|
3439
3956
|
? claimHydrationTextRange(parent, toText(initial))
|
|
3440
3957
|
: undefined
|
|
3441
3958
|
const borrowedHydrationRange =
|
|
3442
3959
|
isHydrating() && !scalarInitial
|
|
3443
|
-
? borrowHydrationSlotRange(parent,
|
|
3960
|
+
? borrowHydrationSlotRange(parent, structuralInitial?.[2] === 'slot')
|
|
3444
3961
|
: undefined
|
|
3445
3962
|
const hydratedStructuralRange =
|
|
3446
3963
|
isHydrating() && !scalarInitial
|
|
3447
3964
|
? (borrowedHydrationRange ?? claimHydrationSlotRange(parent))
|
|
3448
3965
|
: undefined
|
|
3449
3966
|
const hydratedRange = hydratedTextRange ?? hydratedStructuralRange
|
|
3450
|
-
const
|
|
3451
|
-
|
|
3452
|
-
|
|
3967
|
+
const markerlessScalar = hydratedRange === undefined && scalarInitial && !isHydrating()
|
|
3968
|
+
let start = hydratedRange?.[0] ?? null
|
|
3969
|
+
let end = hydratedRange?.[1] ?? null
|
|
3970
|
+
if (!markerlessScalar && hydratedRange === undefined) {
|
|
3971
|
+
start = document.createComment(DEV ? 'vidact:binding' : '')
|
|
3972
|
+
end = document.createComment(DEV ? '/vidact:binding' : '')
|
|
3453
3973
|
parent.insertBefore(start, before)
|
|
3454
3974
|
parent.insertBefore(end, before)
|
|
3455
3975
|
}
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
const input = binding(
|
|
3463
|
-
value[2],
|
|
3464
|
-
value[3],
|
|
3465
|
-
() => {
|
|
3466
|
-
const next = value[1]()
|
|
3467
|
-
if (!isRenderableProtocol(next) || !Object.is(renderableIdentity(next), identity)) {
|
|
3468
|
-
return previous
|
|
3469
|
-
}
|
|
3470
|
-
previous = renderablePropsSnapshot(next)
|
|
3471
|
-
return previous
|
|
3472
|
-
},
|
|
3473
|
-
value[4],
|
|
3474
|
-
value[5],
|
|
3475
|
-
)
|
|
3476
|
-
return materializeRenderableWithInput(renderable, input)
|
|
3976
|
+
const state: MountedBindingState = {
|
|
3977
|
+
start,
|
|
3978
|
+
end,
|
|
3979
|
+
current: hydratedTextRange === undefined && !markerlessScalar ? UNSET_BINDING : initial,
|
|
3980
|
+
currentOwner: null,
|
|
3981
|
+
text: hydratedTextRange?.[2] ?? null,
|
|
3477
3982
|
}
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
currentOwner = null
|
|
3482
|
-
text = null
|
|
3483
|
-
disposeRange(owner, start, end)
|
|
3983
|
+
if (markerlessScalar) {
|
|
3984
|
+
state.text = document.createTextNode(toText(initial))
|
|
3985
|
+
parent.insertBefore(state.text, before)
|
|
3484
3986
|
}
|
|
3485
3987
|
const update = (): void => {
|
|
3486
3988
|
const next = value[1]()
|
|
3487
|
-
if (current !==
|
|
3989
|
+
if (state.current !== UNSET_BINDING && Object.is(next, state.current)) return
|
|
3488
3990
|
if (
|
|
3489
|
-
current !==
|
|
3490
|
-
isRenderableProtocol(current) &&
|
|
3991
|
+
state.current !== UNSET_BINDING &&
|
|
3992
|
+
isRenderableProtocol(state.current) &&
|
|
3491
3993
|
isRenderableProtocol(next) &&
|
|
3492
|
-
canReconcileRenderable(current) &&
|
|
3994
|
+
canReconcileRenderable(state.current) &&
|
|
3493
3995
|
canReconcileRenderable(next) &&
|
|
3494
|
-
Object.is(renderableIdentity(current), renderableIdentity(next))
|
|
3996
|
+
Object.is(renderableIdentity(state.current), renderableIdentity(next))
|
|
3495
3997
|
) {
|
|
3496
|
-
current = next
|
|
3998
|
+
state.current = next
|
|
3497
3999
|
return
|
|
3498
4000
|
}
|
|
3499
|
-
const currentParent = rangeParent(start, end, 'binding range')
|
|
3500
|
-
|
|
3501
4001
|
if (isScalarRenderValue(next)) {
|
|
3502
4002
|
const content = toText(next)
|
|
3503
|
-
if (text !== null) {
|
|
3504
|
-
const target = text
|
|
4003
|
+
if (state.text !== null) {
|
|
4004
|
+
const target = state.text
|
|
3505
4005
|
const previousContent = target.data
|
|
3506
|
-
const previous = current
|
|
4006
|
+
const previous = state.current
|
|
3507
4007
|
stagePublication([
|
|
3508
4008
|
() => {
|
|
3509
4009
|
if (target.data !== content) target.data = content
|
|
3510
|
-
current = next
|
|
4010
|
+
state.current = next
|
|
3511
4011
|
},
|
|
3512
4012
|
() => {
|
|
3513
4013
|
if (target.data !== previousContent) target.data = previousContent
|
|
3514
|
-
current = previous
|
|
4014
|
+
state.current = previous
|
|
3515
4015
|
},
|
|
3516
4016
|
])
|
|
3517
4017
|
return
|
|
3518
4018
|
}
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
currentParent
|
|
3522
|
-
|
|
4019
|
+
clearMountedBinding(state)
|
|
4020
|
+
const [rangeStart, rangeEnd] = ensureMountedBindingRange(state)
|
|
4021
|
+
const currentParent = rangeParent(rangeStart, rangeEnd, 'binding range')
|
|
4022
|
+
state.text = document.createTextNode(content)
|
|
4023
|
+
currentParent.insertBefore(state.text, rangeEnd)
|
|
4024
|
+
state.current = next
|
|
3523
4025
|
return
|
|
3524
4026
|
}
|
|
3525
4027
|
|
|
4028
|
+
const [rangeStart, rangeEnd] = ensureMountedBindingRange(state)
|
|
4029
|
+
const currentParent = rangeParent(rangeStart, rangeEnd, 'binding range')
|
|
3526
4030
|
const nextOwner = createOwner()
|
|
3527
4031
|
const stagedValue =
|
|
3528
4032
|
isRenderableProtocol(next) && canReconcileRenderable(next)
|
|
3529
|
-
?
|
|
4033
|
+
? stableRenderableBinding(value, next)
|
|
3530
4034
|
: (next as RenderValue)
|
|
3531
4035
|
const [fragment, staged] = stageValue(stagedValue as RenderValue, nextOwner)
|
|
3532
4036
|
try {
|
|
3533
|
-
|
|
4037
|
+
clearMountedBinding(state)
|
|
3534
4038
|
} catch (error) {
|
|
3535
4039
|
disposeOwner(nextOwner)
|
|
3536
4040
|
throw error
|
|
3537
4041
|
}
|
|
3538
|
-
currentOwner = nextOwner
|
|
3539
|
-
currentParent.insertBefore(fragment,
|
|
4042
|
+
state.currentOwner = nextOwner
|
|
4043
|
+
currentParent.insertBefore(fragment, rangeEnd)
|
|
3540
4044
|
commitPublishedNodes(staged)
|
|
3541
|
-
current = next
|
|
4045
|
+
state.current = next
|
|
3542
4046
|
}
|
|
3543
4047
|
|
|
3544
4048
|
if (hydratedStructuralRange !== undefined) {
|
|
4049
|
+
const rangeEnd = hydratedStructuralRange[1]
|
|
3545
4050
|
const nextOwner = createOwner()
|
|
3546
|
-
const [fragment, staged] = withHydrationInsertion(parent,
|
|
4051
|
+
const [fragment, staged] = withHydrationInsertion(parent, rangeEnd, () =>
|
|
3547
4052
|
stageRender(
|
|
3548
4053
|
() =>
|
|
3549
4054
|
(isRenderableProtocol(initial) && canReconcileRenderable(initial)
|
|
3550
|
-
?
|
|
4055
|
+
? stableRenderableBinding(value, initial)
|
|
3551
4056
|
: initial) as RenderValue,
|
|
3552
4057
|
nextOwner,
|
|
3553
4058
|
),
|
|
3554
4059
|
)
|
|
3555
|
-
parent.insertBefore(fragment,
|
|
4060
|
+
parent.insertBefore(fragment, rangeEnd)
|
|
3556
4061
|
commitPublishedNodes(staged)
|
|
3557
|
-
currentOwner = nextOwner
|
|
3558
|
-
current = initial
|
|
3559
|
-
} else if (hydratedRange === undefined) {
|
|
4062
|
+
state.currentOwner = nextOwner
|
|
4063
|
+
state.current = initial
|
|
4064
|
+
} else if (hydratedRange === undefined && !markerlessScalar) {
|
|
3560
4065
|
update()
|
|
3561
4066
|
}
|
|
3562
4067
|
const removeUpdater = subscribeBinding(value, update)
|
|
3563
4068
|
onCleanup(() => {
|
|
3564
4069
|
removeUpdater()
|
|
3565
|
-
|
|
4070
|
+
if (state.start === null || state.end === null) {
|
|
4071
|
+
state.text?.remove()
|
|
4072
|
+
state.text = null
|
|
4073
|
+
return
|
|
4074
|
+
}
|
|
4075
|
+
clearMountedBinding(state)
|
|
3566
4076
|
if (borrowedHydrationRange === undefined) {
|
|
3567
|
-
start.remove()
|
|
3568
|
-
end.remove()
|
|
4077
|
+
state.start.remove()
|
|
4078
|
+
state.end.remove()
|
|
3569
4079
|
}
|
|
3570
4080
|
})
|
|
3571
4081
|
}
|
|
3572
4082
|
|
|
4083
|
+
function stableRenderableBinding(
|
|
4084
|
+
value: CompiledBinding<unknown>,
|
|
4085
|
+
renderable: RenderableProtocol,
|
|
4086
|
+
): unknown {
|
|
4087
|
+
const identity = renderableIdentity(renderable)
|
|
4088
|
+
let previous = renderablePropsSnapshot(renderable)
|
|
4089
|
+
const input = binding(
|
|
4090
|
+
value[2],
|
|
4091
|
+
value[3],
|
|
4092
|
+
() => {
|
|
4093
|
+
const next = value[1]()
|
|
4094
|
+
if (!isRenderableProtocol(next) || !Object.is(renderableIdentity(next), identity)) {
|
|
4095
|
+
return previous
|
|
4096
|
+
}
|
|
4097
|
+
previous = renderablePropsSnapshot(next)
|
|
4098
|
+
return previous
|
|
4099
|
+
},
|
|
4100
|
+
value[4],
|
|
4101
|
+
value[5],
|
|
4102
|
+
)
|
|
4103
|
+
return materializeRenderableWithInput(renderable, input)
|
|
4104
|
+
}
|
|
4105
|
+
|
|
3573
4106
|
function materialize(value: RenderValue): Node[] {
|
|
3574
4107
|
const insertionPoint = hydrationInsertionPoint()
|
|
3575
4108
|
if (insertionPoint !== undefined) {
|
|
@@ -3643,6 +4176,40 @@ function stageRender(
|
|
|
3643
4176
|
}
|
|
3644
4177
|
}
|
|
3645
4178
|
|
|
4179
|
+
function stageMarkerlessHydrationRender(
|
|
4180
|
+
parent: Node,
|
|
4181
|
+
before: Node | null,
|
|
4182
|
+
render: () => CompiledRenderValue,
|
|
4183
|
+
owner: Owner,
|
|
4184
|
+
): readonly [first: Node | null, after: Node | null, nodes: readonly Node[]] {
|
|
4185
|
+
const first = hydrationCursor(parent)
|
|
4186
|
+
if (first === undefined) {
|
|
4187
|
+
throw new Error('markerless hydration render requires active hydration')
|
|
4188
|
+
}
|
|
4189
|
+
try {
|
|
4190
|
+
withHydrationInsertion(parent, before, () =>
|
|
4191
|
+
withOwner(owner, () => insertValue(parent, render(), before)),
|
|
4192
|
+
)
|
|
4193
|
+
} catch (error) {
|
|
4194
|
+
try {
|
|
4195
|
+
disposeOwner(owner)
|
|
4196
|
+
} catch {
|
|
4197
|
+
// Preserve the render or hydration error after every registered cleanup ran.
|
|
4198
|
+
}
|
|
4199
|
+
throw error
|
|
4200
|
+
}
|
|
4201
|
+
const after = hydrationCursor(parent)
|
|
4202
|
+
if (after === undefined) {
|
|
4203
|
+
disposeOwner(owner)
|
|
4204
|
+
throw new Error('markerless hydration render lost its hydration cursor')
|
|
4205
|
+
}
|
|
4206
|
+
const nodes: Node[] = []
|
|
4207
|
+
for (let node = first; node !== null && node !== after; node = node.nextSibling) {
|
|
4208
|
+
nodes.push(node)
|
|
4209
|
+
}
|
|
4210
|
+
return [first, after, nodes]
|
|
4211
|
+
}
|
|
4212
|
+
|
|
3646
4213
|
function insertValue(
|
|
3647
4214
|
parent: Node,
|
|
3648
4215
|
value: RenderValue,
|
|
@@ -3792,10 +4359,13 @@ function isRefValue(value: unknown): value is RefValue {
|
|
|
3792
4359
|
}
|
|
3793
4360
|
|
|
3794
4361
|
function claimPendingRefOwners(root: Node): void {
|
|
4362
|
+
if (pendingRefCount === 0) return
|
|
3795
4363
|
visitNodes(root, (node) => {
|
|
3796
4364
|
if (!(node instanceof Element)) return
|
|
3797
4365
|
const pending = pendingRefs.get(node)
|
|
3798
|
-
if (pending
|
|
4366
|
+
if (pending === undefined || pending[0] !== null) return
|
|
4367
|
+
pending[0] = activeOwner
|
|
4368
|
+
activeOwner?.[1].push(() => deletePendingRef(node, pending))
|
|
3799
4369
|
})
|
|
3800
4370
|
}
|
|
3801
4371
|
|
|
@@ -3813,9 +4383,15 @@ function commitPublishedNodesNow(nodes: readonly Node[]): void {
|
|
|
3813
4383
|
if (publicationRoot instanceof DocumentFragment && !(publicationRoot instanceof ShadowRoot)) {
|
|
3814
4384
|
return
|
|
3815
4385
|
}
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
4386
|
+
if (pendingInsertionOwnerCount > 0) {
|
|
4387
|
+
for (const node of nodes) commitNodeInsertions(node)
|
|
4388
|
+
}
|
|
4389
|
+
if (pendingRefCount > 0) {
|
|
4390
|
+
for (const node of nodes) commitNodeRefs(node)
|
|
4391
|
+
}
|
|
4392
|
+
if (pendingOwnerCommitCount > 0) {
|
|
4393
|
+
for (const node of nodes) commitNodeResources(node)
|
|
4394
|
+
}
|
|
3819
4395
|
}
|
|
3820
4396
|
|
|
3821
4397
|
function commitNodeInsertions(root: Node): void {
|
|
@@ -3829,12 +4405,12 @@ function commitNodeRefs(root: Node): void {
|
|
|
3829
4405
|
if (node instanceof Element) {
|
|
3830
4406
|
const pending = pendingRefs.get(node)
|
|
3831
4407
|
if (pending !== undefined) {
|
|
3832
|
-
|
|
4408
|
+
deletePendingRef(node, pending)
|
|
3833
4409
|
const owner = pending[0] ?? activeOwner
|
|
3834
4410
|
commitContextInsertions(owner?.[2] ?? null)
|
|
3835
4411
|
commitOwnerInsertions(owner ?? undefined)
|
|
3836
4412
|
const cleanup = attachRef(pending[1], node)
|
|
3837
|
-
if (pending[2] === undefined) owner?.[1].
|
|
4413
|
+
if (pending[2] === undefined) owner?.[1].push(cleanup)
|
|
3838
4414
|
else pending[2](cleanup)
|
|
3839
4415
|
}
|
|
3840
4416
|
}
|
|
@@ -3858,6 +4434,7 @@ function commitOwnerInsertions(owner: Owner | undefined): void {
|
|
|
3858
4434
|
const commits = pendingInsertionCommits.get(owner)
|
|
3859
4435
|
if (commits === undefined) return
|
|
3860
4436
|
pendingInsertionCommits.delete(owner)
|
|
4437
|
+
pendingInsertionOwnerCount -= 1
|
|
3861
4438
|
for (const commit of commits) commit()
|
|
3862
4439
|
}
|
|
3863
4440
|
|
|
@@ -3866,6 +4443,7 @@ function commitOwnerResources(owner: Owner | undefined): void {
|
|
|
3866
4443
|
const commits = pendingOwnerCommits.get(owner)
|
|
3867
4444
|
if (commits === undefined) return
|
|
3868
4445
|
pendingOwnerCommits.delete(owner)
|
|
4446
|
+
pendingOwnerCommitCount -= 1
|
|
3869
4447
|
for (const commit of commits) commit()
|
|
3870
4448
|
}
|
|
3871
4449
|
|
|
@@ -3880,6 +4458,7 @@ function commitRangeRefs(start: Node, end: Node): void {
|
|
|
3880
4458
|
}
|
|
3881
4459
|
|
|
3882
4460
|
function visitNodes(root: Node, visit: (node: Node) => void): void {
|
|
4461
|
+
visitedPublishedNodeCount += 1
|
|
3883
4462
|
visit(root)
|
|
3884
4463
|
for (const child of root.childNodes) visitNodes(child, visit)
|
|
3885
4464
|
}
|
|
@@ -3896,12 +4475,12 @@ function attachRef<T>(value: RefValue<T>, target: T): () => void {
|
|
|
3896
4475
|
}
|
|
3897
4476
|
}
|
|
3898
4477
|
|
|
3899
|
-
function scheduleFlush(flush:
|
|
4478
|
+
function scheduleFlush(flush: FlushTask): void {
|
|
3900
4479
|
scheduledFlushes.add(flush)
|
|
3901
4480
|
if (transactionDepth === 0) drainFlushes()
|
|
3902
4481
|
}
|
|
3903
4482
|
|
|
3904
|
-
function scheduleOwnerFlush(owner: Owner, flush:
|
|
4483
|
+
function scheduleOwnerFlush(owner: Owner, flush: FlushTask): void {
|
|
3905
4484
|
const connection = owner[5]
|
|
3906
4485
|
if (connection === null || connection === undefined || connection.connected) {
|
|
3907
4486
|
scheduleFlush(flush)
|
|
@@ -3920,7 +4499,7 @@ function drainFlushes(): void {
|
|
|
3920
4499
|
drainingFlushes = true
|
|
3921
4500
|
const publication: PublicationOperation[] = []
|
|
3922
4501
|
activePublication = publication
|
|
3923
|
-
const runs = new Map<
|
|
4502
|
+
const runs = new Map<FlushTask, number>()
|
|
3924
4503
|
let committing = false
|
|
3925
4504
|
let failure: unknown
|
|
3926
4505
|
let failed = false
|
|
@@ -3935,7 +4514,8 @@ function drainFlushes(): void {
|
|
|
3935
4514
|
throw new Error(DEV ? 'Vidact compiled scopes did not stabilize' : 'V011')
|
|
3936
4515
|
}
|
|
3937
4516
|
runs.set(flush, runCount)
|
|
3938
|
-
flush()
|
|
4517
|
+
if (typeof flush === 'function') flush()
|
|
4518
|
+
else flush[1]()
|
|
3939
4519
|
}
|
|
3940
4520
|
activePublication = null
|
|
3941
4521
|
committing = true
|