@kudzujs/core 0.17.1 → 0.17.2
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/README.md +1 -1
- package/framework/binding-runtime.js +32 -33
- package/framework/list-runtime.js +8 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Kudzu compiles ordinary React-shaped TypeScript and TSX into complete static HTM
|
|
|
14
14
|
|
|
15
15
|
> Experimental `0.17.x`: the compiler API and supported TSX surface may change.
|
|
16
16
|
|
|
17
|
-
**Current release: [0.17.
|
|
17
|
+
**Current release: [0.17.2](https://github.com/kudzujs/kudzu/releases/tag/v0.17.2).** Delayed evaluators cannot regain authority after DOM or source-state release, including same-node remount and state-ID reuse. Keyed conditions now settle correctly when row data and parent selection change in one turn. Native Chrome regressions cover both fixes; static siblings remain JavaScript-free. Generator `create-kudzu@0.1.163` targets core `^0.17.2`. Output sizes have mixed deltas; no speed improvement is claimed. Restoration latency and AI-cost/1.0 gates remain unresolved.
|
|
18
18
|
|
|
19
19
|
- [Documentation](https://kudzujs.cloud/docs)
|
|
20
20
|
- [Installation guide](https://kudzujs.cloud/docs#install)
|
|
@@ -5,7 +5,6 @@ import { serializeStyle } from "./style.js"
|
|
|
5
5
|
const imports = new Map()
|
|
6
6
|
const bindingTargets = new Map()
|
|
7
7
|
const conditionTargets = globalThis.__KUDZU_CONDITIONS__ && new Map()
|
|
8
|
-
const mountedConditions = globalThis.__KUDZU_CONDITIONS__ && new WeakSet()
|
|
9
8
|
const bindingRegistrations = new WeakMap()
|
|
10
9
|
const conditionRegistrations = globalThis.__KUDZU_CONDITIONS__ && new WeakMap()
|
|
11
10
|
const textDescriptors = globalThis.__KUDZU_TEXT_BINDINGS__ && typeof document !== "undefined" ? JSON.parse(document.body.dataset.kTextBindings ?? "[]") : []
|
|
@@ -43,7 +42,7 @@ function commitBindings(id) {
|
|
|
43
42
|
if (bindings) {
|
|
44
43
|
for (const binding of bindings) {
|
|
45
44
|
if (!binding.node.isConnected) bindings.delete(binding)
|
|
46
|
-
else patchBinding(binding.node, binding.target, binding.read())
|
|
45
|
+
else if (binding.read) patchBinding(binding.node, binding.target, binding.read())
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
if (!globalThis.__KUDZU_CONDITIONS__) return
|
|
@@ -83,15 +82,7 @@ function mountBindings(root) {
|
|
|
83
82
|
patchBinding(node, target, binding.read())
|
|
84
83
|
continue
|
|
85
84
|
}
|
|
86
|
-
|
|
87
|
-
if (!node.isConnected) return
|
|
88
|
-
const binding = { node, target, read: evaluator.read }
|
|
89
|
-
for (const id of evaluator.stateIds) {
|
|
90
|
-
register(bindingTargets, id, binding)
|
|
91
|
-
registrations.push([id, binding])
|
|
92
|
-
}
|
|
93
|
-
patchBinding(node, target, binding.read())
|
|
94
|
-
}).catch(error => console.error(error))
|
|
85
|
+
mountBindingEvaluator(node, target, descriptor, registrations)
|
|
95
86
|
}
|
|
96
87
|
}
|
|
97
88
|
if (globalThis.__KUDZU_TEXT_BINDINGS__) {
|
|
@@ -108,47 +99,56 @@ function mountBindings(root) {
|
|
|
108
99
|
patchBinding(node, "text", binding.read())
|
|
109
100
|
continue
|
|
110
101
|
}
|
|
111
|
-
|
|
112
|
-
if (!node.isConnected) return
|
|
113
|
-
const binding = { node, target: "text", read: evaluator.read }
|
|
114
|
-
for (const id of evaluator.stateIds) {
|
|
115
|
-
register(bindingTargets, id, binding)
|
|
116
|
-
registrations.push([id, binding])
|
|
117
|
-
}
|
|
118
|
-
patchBinding(node, "text", binding.read())
|
|
119
|
-
}).catch(error => console.error(error))
|
|
102
|
+
mountBindingEvaluator(node, "text", descriptor, registrations)
|
|
120
103
|
}
|
|
121
104
|
}
|
|
122
105
|
}
|
|
123
106
|
|
|
107
|
+
function mountBindingEvaluator(node, target, descriptor, registrations) {
|
|
108
|
+
const binding = { node, target }
|
|
109
|
+
// Register dependencies before loading so state release also revokes pending work.
|
|
110
|
+
for (const id of bindingStateIds(descriptor)) {
|
|
111
|
+
register(bindingTargets, id, binding)
|
|
112
|
+
registrations.push([id, binding])
|
|
113
|
+
}
|
|
114
|
+
loadEvaluator(descriptor).then(evaluator => {
|
|
115
|
+
if (!node.isConnected || bindingRegistrations.get(node) !== registrations) return
|
|
116
|
+
binding.read = evaluator.read
|
|
117
|
+
patchBinding(node, target, binding.read())
|
|
118
|
+
}).catch(error => console.error(error))
|
|
119
|
+
}
|
|
120
|
+
|
|
124
121
|
function mountConditions(root) {
|
|
125
122
|
for (const start of matching(root, "template[data-k-if]")) {
|
|
126
|
-
if (
|
|
127
|
-
|
|
123
|
+
if (conditionRegistrations.has(start)) continue
|
|
124
|
+
// Reserve membership while the evaluator loads, before subscriptions exist.
|
|
125
|
+
conditionRegistrations.set(start, undefined)
|
|
128
126
|
const descriptor = JSON.parse(start.dataset.kIf)
|
|
129
127
|
const end = findEnd(start, descriptor.id)
|
|
130
128
|
const truthy = globalThis.__KUDZU_SVG_CONDITIONS__ && descriptor.svg ? start.dataset.kSvgTrue : start.content.querySelector("template[data-k-true]")
|
|
131
129
|
const falsy = globalThis.__KUDZU_SVG_CONDITIONS__ && descriptor.svg ? start.dataset.kSvgFalse : start.content.querySelector("template[data-k-false]")
|
|
132
130
|
if (!end || !truthy || !falsy) continue
|
|
133
131
|
const condition = { start, end, truthy, falsy, svg: globalThis.__KUDZU_SVG_CONDITIONS__ && descriptor.svg, kind: descriptor.kind, current: conditionKey(descriptor.kind, descriptor.initial), mount: globalThis.__KUDZU_CONDITION_MOUNTS__ !== false && descriptor.mount, owned: globalThis.__KUDZU_CONDITION_STATE__ !== false && descriptor.owned }
|
|
132
|
+
const registrations = []
|
|
133
|
+
const registration = { condition, registrations }
|
|
134
|
+
conditionRegistrations.set(start, registration)
|
|
135
|
+
for (const id of descriptor.state ? [descriptor.state] : bindingStateIds(descriptor)) {
|
|
136
|
+
register(conditionTargets, id, condition)
|
|
137
|
+
registrations.push([id, condition])
|
|
138
|
+
}
|
|
134
139
|
if (globalThis.__KUDZU_CONDITION_STATE__ !== false) mountConditionStates(condition, Boolean(descriptor.initial), false)
|
|
135
140
|
const mount = evaluator => {
|
|
136
|
-
if (!start.isConnected) return
|
|
141
|
+
if (!start.isConnected || conditionRegistrations.get(start) !== registration) return
|
|
137
142
|
condition.read = evaluator.read
|
|
138
|
-
const registrations = []
|
|
139
|
-
for (const id of evaluator.stateIds) {
|
|
140
|
-
register(conditionTargets, id, condition)
|
|
141
|
-
registrations.push([id, condition])
|
|
142
|
-
}
|
|
143
|
-
conditionRegistrations.set(start, { condition, registrations })
|
|
144
143
|
updateCondition(condition)
|
|
145
144
|
}
|
|
146
|
-
if (descriptor.state) mount({ read: () => browserState.get(descriptor.state)
|
|
145
|
+
if (descriptor.state) mount({ read: () => browserState.get(descriptor.state) })
|
|
147
146
|
else loadEvaluator(descriptor).then(mount).catch(error => console.error(error))
|
|
148
147
|
}
|
|
149
148
|
}
|
|
150
149
|
|
|
151
150
|
function updateCondition(condition) {
|
|
151
|
+
if (!condition.read) return
|
|
152
152
|
const value = condition.read()
|
|
153
153
|
const next = conditionKey(condition.kind, value)
|
|
154
154
|
if (next === condition.current) return
|
|
@@ -170,7 +170,7 @@ function updateCondition(condition) {
|
|
|
170
170
|
const select = globalThis.__KUDZU_VALUE_BINDINGS__ !== false && condition.start.closest("select[data-k-bind-value]")
|
|
171
171
|
if (select) {
|
|
172
172
|
for (const binding of new Set((bindingRegistrations.get(select) ?? []).map(([, entry]) => entry))) {
|
|
173
|
-
patchBinding(binding.node, binding.target, binding.read())
|
|
173
|
+
if (binding.read) patchBinding(binding.node, binding.target, binding.read())
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
}
|
|
@@ -194,7 +194,6 @@ function unmountConditions(root) {
|
|
|
194
194
|
for (const [id, condition] of registration?.registrations ?? []) conditionTargets.get(id)?.delete(condition)
|
|
195
195
|
if (globalThis.__KUDZU_CONDITION_STATE__ !== false && registration?.condition) unmountConditionStates(registration.condition, registration.condition.current === "true")
|
|
196
196
|
conditionRegistrations.delete(start)
|
|
197
|
-
mountedConditions.delete(start)
|
|
198
197
|
}
|
|
199
198
|
}
|
|
200
199
|
|
|
@@ -210,7 +209,7 @@ function releaseBindings(id) {
|
|
|
210
209
|
const registration = conditionRegistrations.get(condition.start)
|
|
211
210
|
for (const [conditionId, entry] of registration?.registrations ?? []) conditionTargets.get(conditionId)?.delete(entry)
|
|
212
211
|
if (globalThis.__KUDZU_CONDITION_STATE__ !== false) unmountConditionStates(condition, condition.current === "true")
|
|
213
|
-
conditionRegistrations.
|
|
212
|
+
conditionRegistrations.set(condition.start, undefined)
|
|
214
213
|
}
|
|
215
214
|
conditionTargets.delete(id)
|
|
216
215
|
}
|
|
@@ -650,13 +650,6 @@ function fillListParts(root, parts, item, revision, index = 0, previous) {
|
|
|
650
650
|
}
|
|
651
651
|
}
|
|
652
652
|
fillListExpressions(root, parts, item, revision, index)
|
|
653
|
-
if (__KUDZU_LIST_CONDITIONS__) {
|
|
654
|
-
for (const [marker, descriptor] of parts.conditions) {
|
|
655
|
-
evaluate(descriptor, item, index).then(value => {
|
|
656
|
-
if (revisions.get(root) === revision && marker.isConnected) updateListCondition(marker, descriptor.kind, value, item, index)
|
|
657
|
-
}).catch(error => console.error(error))
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
653
|
}
|
|
661
654
|
|
|
662
655
|
function fillListExpressions(root, parts, item, revision, index) {
|
|
@@ -676,6 +669,14 @@ function fillListExpressions(root, parts, item, revision, index) {
|
|
|
676
669
|
}
|
|
677
670
|
}
|
|
678
671
|
}
|
|
672
|
+
// Parent-state commits invalidate the shared row revision, including pending conditions.
|
|
673
|
+
if (__KUDZU_LIST_CONDITIONS__) {
|
|
674
|
+
for (const [marker, descriptor] of parts.conditions) {
|
|
675
|
+
evaluate(descriptor, item, index).then(value => {
|
|
676
|
+
if (revisions.get(root) === revision && marker.isConnected) updateListCondition(marker, descriptor.kind, value, item, index)
|
|
677
|
+
}).catch(error => console.error(error))
|
|
678
|
+
}
|
|
679
|
+
}
|
|
679
680
|
}
|
|
680
681
|
|
|
681
682
|
function updateListExpressions(list) {
|