@kudzujs/core 0.4.8 → 0.4.10
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 +39 -26
- package/framework/README.md +1 -1
- package/framework/build.mjs +66 -4
- package/framework/core.d.ts +1 -0
- package/framework/core.mjs +4 -1
- package/framework/list-runtime.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -160,6 +160,17 @@ Logical state persists across branch switches, while uncontrolled DOM state rese
|
|
|
160
160
|
|
|
161
161
|
Reactive conditional DOM currently targets the HTML namespace and is rejected inside SVG or MathML.
|
|
162
162
|
|
|
163
|
+
Top-level immutable JSX locals can hold static or state-dependent branches:
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
const menu = open ? <MenuBar /> : <p>Menu dormant</p>
|
|
167
|
+
const content = open && menu
|
|
168
|
+
|
|
169
|
+
return <main>{content}</main>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Kudzu compiles the local initializer to the same bounded DOM ranges as an inline condition. Reassigned, block-scoped, and keyed-list alias locals remain unsupported.
|
|
173
|
+
|
|
163
174
|
## Keyed Lists
|
|
164
175
|
|
|
165
176
|
Map local array state directly to one keyed JSX element per item:
|
|
@@ -175,6 +186,7 @@ const [items, setItems] = useState([
|
|
|
175
186
|
key={item.id}
|
|
176
187
|
className={item.done ? "done" : "active"}
|
|
177
188
|
aria-label={`${item.name} item`}
|
|
189
|
+
style={{ opacity: item.done ? 0.5 : 1 }}
|
|
178
190
|
>
|
|
179
191
|
{item.name.toUpperCase()}
|
|
180
192
|
<button onClick={() => setItems(items.filter(entry => entry.id !== item.id))}>Remove</button>
|
|
@@ -182,9 +194,9 @@ const [items, setItems] = useState([
|
|
|
182
194
|
)}</ul>
|
|
183
195
|
```
|
|
184
196
|
|
|
185
|
-
Kudzu emits initial items as static HTML, then adds, removes, updates, and moves keyed elements directly. Existing keys move without remounting, preserving uncontrolled descendant state. Direct `item.<field>` reads use compact markers; derived item expressions compile to external ESM evaluators. Item-local handlers use direct DOM listeners and receive the latest JSON-safe item for their key, including after updates, additions, and reorders. The item remains stored once in shared list state; handler descriptors carry a placeholder that the list runtime fills when mounting or updating the keyed root.
|
|
197
|
+
Kudzu emits initial items as static HTML, then adds, removes, updates, styles, and moves keyed elements directly. Existing keys move without remounting, preserving uncontrolled descendant state. Direct `item.<field>` reads use compact markers; derived item expressions compile to external ESM evaluators. Item-local handlers use direct DOM listeners and receive the latest JSON-safe item for their key, including after updates, additions, and reorders. The item remains stored once in shared list state; handler descriptors carry a placeholder that the list runtime fills when mounting or updating the keyed root.
|
|
186
198
|
|
|
187
|
-
Each item must be an ordinary plain object with a unique string or finite-number key; nested data may contain only JSON-safe arrays, ordinary plain objects, and primitive values. Null-prototype objects are rejected to preserve JSON round-trip parity. The current syntax requires a direct local-state `.map`, one identifier callback parameter, one intrinsic JSX root, and `key={item.<field>}`. Derived expressions must be pure and synchronous: item reads, literals, operators, templates, approved read-only string/array methods, deterministic `Math` methods, and `String`/`Number`/`Boolean` conversion are supported. Component state, locals, imported helpers, browser globals, Promise values, mutation, arbitrary calls, and prototype-sensitive properties are rejected. Nested conditions or lists, item spreads, component tags,
|
|
199
|
+
Each item must be an ordinary plain object with a unique string or finite-number key; nested data may contain only JSON-safe arrays, ordinary plain objects, and primitive values. Null-prototype objects are rejected to preserve JSON round-trip parity. The current syntax requires a direct local-state `.map`, one identifier callback parameter, one intrinsic JSX root, and `key={item.<field>}`. Derived expressions must be pure and synchronous: item reads, literals, operators, templates, approved read-only string/array methods, deterministic `Math` methods, and `String`/`Number`/`Boolean` conversion are supported. Component state, locals, imported helpers, browser globals, Promise values, mutation, arbitrary calls, and prototype-sensitive properties are rejected. Nested conditions or lists, item spreads, component tags, refs, and `dangerouslySetInnerHTML` remain unsupported. Keyed rows must be placed inside an explicit `<tbody>`, `<thead>`, or `<tfoot>`.
|
|
188
200
|
|
|
189
201
|
## Normal JavaScript
|
|
190
202
|
|
|
@@ -249,11 +261,12 @@ Supported:
|
|
|
249
261
|
- Object DOM refs in native event handlers
|
|
250
262
|
- Controlled `value` and `checked` form properties
|
|
251
263
|
- Conditional child `&&` and ternary DOM patches
|
|
264
|
+
- Top-level immutable JSX locals
|
|
252
265
|
- Direct keyed local-state lists
|
|
253
266
|
|
|
254
267
|
Not implemented yet:
|
|
255
268
|
|
|
256
|
-
-
|
|
269
|
+
- Block-scoped JSX locals and non-direct list item expressions
|
|
257
270
|
- Server actions and request-time SSR
|
|
258
271
|
- Imported client helpers and React package islands
|
|
259
272
|
- HMR and framework DevTools
|
|
@@ -268,13 +281,13 @@ Same counter with initial value `7` and increment/decrement buttons:
|
|
|
268
281
|
|
|
269
282
|
| Framework | Initial content | Initial JS gzip | Total output | Clean build |
|
|
270
283
|
|---|---:|---:|---:|---:|
|
|
271
|
-
| Kudzu | Yes | 393 B | 1.1 KB | **
|
|
272
|
-
| Astro | Yes | **158 B** | **365 B** |
|
|
273
|
-
| Svelte CSR | No | 10.5 KB | 26.9 KB |
|
|
274
|
-
| Qwik CSR | No | 20.6 KB | 57.8 KB |
|
|
275
|
-
| Vue CSR | No | 24.0 KB | 60.3 KB |
|
|
276
|
-
| React CSR | No | 59.2 KB | 189.0 KB |
|
|
277
|
-
| Next.js | Yes | 182.1 KB | 652.2 KB |
|
|
284
|
+
| Kudzu | Yes | 393 B | 1.1 KB | **383 ms** |
|
|
285
|
+
| Astro | Yes | **158 B** | **365 B** | 914 ms |
|
|
286
|
+
| Svelte CSR | No | 10.5 KB | 26.9 KB | 892 ms |
|
|
287
|
+
| Qwik CSR | No | 20.6 KB | 57.8 KB | 606 ms |
|
|
288
|
+
| Vue CSR | No | 24.0 KB | 60.3 KB | 815 ms |
|
|
289
|
+
| React CSR | No | 59.2 KB | 189.0 KB | 1044 ms |
|
|
290
|
+
| Next.js | Yes | 182.1 KB | 652.2 KB | 3030 ms |
|
|
278
291
|
|
|
279
292
|
Astro produces the smallest hand-authored counter. Kudzu's advantage in this fixture is React-shaped state code with a sub-1 KB runtime, not the smallest possible JavaScript.
|
|
280
293
|
|
|
@@ -284,13 +297,13 @@ Same content and CSS across every fixture:
|
|
|
284
297
|
|
|
285
298
|
| Framework | Initial content | Initial JS gzip | Total output | Clean build |
|
|
286
299
|
|---|---:|---:|---:|---:|
|
|
287
|
-
| Kudzu | Yes | **0 B** | 3.2 KB | **
|
|
288
|
-
| Astro | Yes | **0 B** | **3.0 KB** |
|
|
289
|
-
| Svelte CSR | No | 10.2 KB | 27.2 KB |
|
|
290
|
-
| Qwik CSR | No | 20.2 KB | 59.6 KB |
|
|
291
|
-
| Vue CSR | No | 24.2 KB | 62.3 KB |
|
|
292
|
-
| React CSR | No | 59.8 KB | 192.3 KB |
|
|
293
|
-
| Next.js | Yes | 182.6 KB | 663.6 KB |
|
|
300
|
+
| Kudzu | Yes | **0 B** | 3.2 KB | **409 ms** |
|
|
301
|
+
| Astro | Yes | **0 B** | **3.0 KB** | 1053 ms |
|
|
302
|
+
| Svelte CSR | No | 10.2 KB | 27.2 KB | 863 ms |
|
|
303
|
+
| Qwik CSR | No | 20.2 KB | 59.6 KB | 611 ms |
|
|
304
|
+
| Vue CSR | No | 24.2 KB | 62.3 KB | 782 ms |
|
|
305
|
+
| React CSR | No | 59.8 KB | 192.3 KB | 1062 ms |
|
|
306
|
+
| Next.js | Yes | 182.6 KB | 663.6 KB | 3118 ms |
|
|
294
307
|
|
|
295
308
|
### 1,000-item Keyed List
|
|
296
309
|
|
|
@@ -298,15 +311,15 @@ The list starts with 1,000 keyed items, then updates every label, reverses the o
|
|
|
298
311
|
|
|
299
312
|
| Framework | Initial content | Initial JS gzip | Total output | Build | Update | Reverse | Remove | Add | Operations total |
|
|
300
313
|
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
301
|
-
| Astro | Yes | **324 B** | **43.6 KB** |
|
|
302
|
-
| Kudzu | Yes | 5.0 KB | 60.3 KB | **
|
|
303
|
-
| Vue CSR | No | 24.3 KB | 61.3 KB |
|
|
304
|
-
|
|
|
305
|
-
|
|
|
306
|
-
| Qwik CSR | No | 22.2 KB | 64.1 KB |
|
|
307
|
-
| Svelte CSR | No | 12.9 KB | 33.1 KB |
|
|
308
|
-
|
|
309
|
-
Astro is the hand-authored native DOM baseline in the interactive fixtures. React, Vue, Svelte, and Qwik used client-rendered fixtures, while Kudzu and Astro emitted initial HTML; Qwik therefore did not exercise its SSR resumability advantage. In this run Kudzu's keyed-list operations total 67.4 ms,
|
|
314
|
+
| Astro | Yes | **324 B** | **43.6 KB** | 862 ms | **3.5 ms** | 24.8 ms | **8.4 ms** | **17.9 ms** | **54.6 ms** |
|
|
315
|
+
| Kudzu | Yes | 5.0 KB | 60.3 KB | **435 ms** | 6.1 ms | 29.0 ms | 12.8 ms | 19.5 ms | 67.4 ms |
|
|
316
|
+
| Vue CSR | No | 24.3 KB | 61.3 KB | 789 ms | 9.3 ms | 32.1 ms | 10.6 ms | 18.6 ms | 70.6 ms |
|
|
317
|
+
| React CSR | No | 59.3 KB | 189.4 KB | 1049 ms | 8.8 ms | 34.7 ms | 13.5 ms | 18.2 ms | 75.2 ms |
|
|
318
|
+
| Next.js | Yes | 182.2 KB | 695.2 KB | 3029 ms | 7.0 ms | 37.2 ms | 12.9 ms | 21.9 ms | 79.0 ms |
|
|
319
|
+
| Qwik CSR | No | 22.2 KB | 64.1 KB | 623 ms | 13.4 ms | **21.7 ms** | 33.6 ms | 28.9 ms | 97.6 ms |
|
|
320
|
+
| Svelte CSR | No | 12.9 KB | 33.1 KB | 870 ms | 5.6 ms | 61.3 ms | 15.6 ms | 18.3 ms | 100.8 ms |
|
|
321
|
+
|
|
322
|
+
Astro is the hand-authored native DOM baseline in the interactive fixtures. React, Vue, Svelte, and Qwik used client-rendered fixtures, while Kudzu and Astro emitted initial HTML; Qwik therefore did not exercise its SSR resumability advantage. In this run Kudzu's keyed-list operations total 67.4 ms, 12.8 ms behind Astro and 7.8 ms ahead of React across all four operations.
|
|
310
323
|
|
|
311
324
|
Benchmark snapshot collected on July 22, 2026 with Node 24.14.0 on an Intel i5-9500. These results compare the selected one-page fixtures, not ecosystem maturity, browser interaction speed beyond the listed operations, or each framework's full rendering options. Build times vary with machine load and filesystem cache.
|
|
312
325
|
|
package/framework/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
- `runtime.js`: command-only runtime for direct state-to-text patches.
|
|
7
7
|
- `shared-runtime.js`: command runtime with capability commit and DOM lifecycle hooks, emitted only when needed.
|
|
8
8
|
- `binding-runtime.js`: optional generic attributes, form properties, and conditional range patches.
|
|
9
|
-
- `list-runtime.js`: optional keyed list validation, external item-expression evaluation, dynamic item-handler scopes, moves, and cleanup.
|
|
9
|
+
- `list-runtime.js`: optional keyed list validation, external item-expression evaluation, dynamic styles and item-handler scopes, moves, and cleanup.
|
|
10
10
|
- `serialization.js`: capture deserialization shared by binding and native handlers.
|
|
11
11
|
- `native-runtime.js`: optional runtime for normal synchronous and asynchronous ESM handlers.
|
|
12
12
|
- `dev-state.js`: dev-only, short-lived logical-state snapshot validation and restoration.
|
package/framework/build.mjs
CHANGED
|
@@ -37,6 +37,7 @@ export async function build({ quiet = false, minify = true } = {}) {
|
|
|
37
37
|
let behaviorCount = 0
|
|
38
38
|
let bindingCount = 0
|
|
39
39
|
let listCount = 0
|
|
40
|
+
let listStyleCount = 0
|
|
40
41
|
let stateSeedCount = 0
|
|
41
42
|
const plans = []
|
|
42
43
|
const hasStyles = await exists(join(sourceDirectory, "style.css"))
|
|
@@ -58,6 +59,7 @@ export async function build({ quiet = false, minify = true } = {}) {
|
|
|
58
59
|
if (result.hasBehaviors) behaviorCount++
|
|
59
60
|
if (result.hasBindings) bindingCount++
|
|
60
61
|
if (result.hasLists) listCount++
|
|
62
|
+
if (result.hasListStyles) listStyleCount++
|
|
61
63
|
if (result.hasStateSeed) stateSeedCount++
|
|
62
64
|
}
|
|
63
65
|
|
|
@@ -73,8 +75,8 @@ export async function build({ quiet = false, minify = true } = {}) {
|
|
|
73
75
|
await writeJavaScript(join(assetsDirectory, "kudzu.js"), runtime, minify)
|
|
74
76
|
}
|
|
75
77
|
if (bindingCount || hasNativeHandlers) await writeJavaScript(join(assetsDirectory, "kudzu-serialization.js"), await readFile(new URL("./serialization.js", import.meta.url), "utf8"), minify)
|
|
78
|
+
if (bindingCount || listStyleCount) await writeJavaScript(join(assetsDirectory, "kudzu-style.js"), await readFile(new URL("./style.js", import.meta.url), "utf8"), minify)
|
|
76
79
|
if (bindingCount) {
|
|
77
|
-
await writeJavaScript(join(assetsDirectory, "kudzu-style.js"), await readFile(new URL("./style.js", import.meta.url), "utf8"), minify)
|
|
78
80
|
const bindingRuntime = (await readFile(new URL("./binding-runtime.js", import.meta.url), "utf8"))
|
|
79
81
|
.replace('"./shared-runtime.js"', '"./kudzu.js"')
|
|
80
82
|
.replace('"./serialization.js"', '"./kudzu-serialization.js"')
|
|
@@ -82,8 +84,16 @@ export async function build({ quiet = false, minify = true } = {}) {
|
|
|
82
84
|
await writeJavaScript(join(assetsDirectory, "kudzu-binding.js"), bindingRuntime, minify)
|
|
83
85
|
}
|
|
84
86
|
if (listCount) {
|
|
85
|
-
|
|
87
|
+
let listRuntime = (await readFile(new URL("./list-runtime.js", import.meta.url), "utf8"))
|
|
86
88
|
.replace('"./shared-runtime.js"', '"./kudzu.js"')
|
|
89
|
+
const stylePatch = ` if (target === "style") {
|
|
90
|
+
const style = serializeStyle(value)
|
|
91
|
+
if (style) node.setAttribute("style", style)
|
|
92
|
+
else node.removeAttribute("style")
|
|
93
|
+
return
|
|
94
|
+
}`
|
|
95
|
+
listRuntime = listRuntime.replace(" /* list-style */", listStyleCount ? stylePatch : "")
|
|
96
|
+
if (listStyleCount) listRuntime = `import { serializeStyle } from "./kudzu-style.js"\n${listRuntime}`
|
|
87
97
|
await writeJavaScript(join(assetsDirectory, "kudzu-list.js"), listRuntime, minify)
|
|
88
98
|
}
|
|
89
99
|
if (hasNativeHandlers) {
|
|
@@ -312,6 +322,8 @@ function createKudzuTransformer(nativeHandlers, reactiveBindings, listExpression
|
|
|
312
322
|
const factory = context.factory
|
|
313
323
|
const settersByFunction = new Map()
|
|
314
324
|
const functions = new Map()
|
|
325
|
+
const jsxLocalDeclarations = new Map()
|
|
326
|
+
const jsxLocalsByFunction = new Map()
|
|
315
327
|
const listValues = new WeakMap()
|
|
316
328
|
const listEventItems = new WeakMap()
|
|
317
329
|
let usesBehavior = false
|
|
@@ -336,9 +348,29 @@ function createKudzuTransformer(nativeHandlers, reactiveBindings, listExpression
|
|
|
336
348
|
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))) {
|
|
337
349
|
functions.set(node.name.text, node.initializer)
|
|
338
350
|
}
|
|
351
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && isTopLevelConst(node)) {
|
|
352
|
+
const owner = nearestFunction(node)
|
|
353
|
+
const declarations = jsxLocalDeclarations.get(owner) ?? new Map()
|
|
354
|
+
declarations.set(node.name.text, node.initializer)
|
|
355
|
+
jsxLocalDeclarations.set(owner, declarations)
|
|
356
|
+
}
|
|
339
357
|
ts.forEachChild(node, collect)
|
|
340
358
|
}
|
|
341
359
|
collect(sourceFile)
|
|
360
|
+
for (const [owner, declarations] of jsxLocalDeclarations) {
|
|
361
|
+
const names = new Set()
|
|
362
|
+
let changed = true
|
|
363
|
+
while (changed) {
|
|
364
|
+
changed = false
|
|
365
|
+
for (const [name, initializer] of declarations) {
|
|
366
|
+
if (!names.has(name) && isJsxLocalValue(initializer, names)) {
|
|
367
|
+
names.add(name)
|
|
368
|
+
changed = true
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
jsxLocalsByFunction.set(owner, names)
|
|
373
|
+
}
|
|
342
374
|
|
|
343
375
|
const visitor = node => {
|
|
344
376
|
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text.startsWith(".")) {
|
|
@@ -359,6 +391,21 @@ function createKudzuTransformer(nativeHandlers, reactiveBindings, listExpression
|
|
|
359
391
|
return factory.updateVariableDeclaration(node, node.name, node.exclamationToken, node.type, initializer)
|
|
360
392
|
}
|
|
361
393
|
|
|
394
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && jsxLocalsByFunction.get(nearestFunction(node))?.has(node.name.text) && referencesIdentifier(nearestFunction(node).body, node.name.text)) {
|
|
395
|
+
const parts = conditionalParts(node.initializer)
|
|
396
|
+
if (parts) {
|
|
397
|
+
const setters = settersForNode(node, settersByFunction)
|
|
398
|
+
const usedStates = referencedStateNames(parts.condition, setters)
|
|
399
|
+
const captures = captureNames(parts.condition, parts.condition, setters)
|
|
400
|
+
if (usedStates.size || captures.size) {
|
|
401
|
+
usesBehavior = true
|
|
402
|
+
usesConditional = true
|
|
403
|
+
const compiled = compileConditional(parts.kind, parts.condition, ts.visitNode(parts.truthy, visitor), ts.visitNode(parts.falsy, visitor), setters, factory, context, reactiveBindings, handlerUrl)
|
|
404
|
+
return factory.updateVariableDeclaration(node, node.name, node.exclamationToken, node.type, compiled)
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
362
409
|
if (ts.isJsxExpression(node) && node.expression && listValues.has(node.expression)) {
|
|
363
410
|
return factory.updateJsxExpression(node, compileListValue(node.expression, listValues.get(node.expression), factory, listExpressions, handlerUrl))
|
|
364
411
|
}
|
|
@@ -488,7 +535,7 @@ function validateKeyedList(parts, sourceFile, setters, listValues, listEventItem
|
|
|
488
535
|
const field = directProperty(expression, parts.item)
|
|
489
536
|
const isRootKey = ts.isJsxAttribute(node.parent) && node.parent.name.getText() === "key"
|
|
490
537
|
if (field && ["__proto__", "constructor", "prototype"].includes(field)) fail(node, `Keyed list item property "${field}" is not supported`)
|
|
491
|
-
if (field && ts.isJsxAttribute(node.parent) && ["
|
|
538
|
+
if (field && ts.isJsxAttribute(node.parent) && ["ref", "dangerouslysetinnerhtml"].includes(node.parent.name.getText().toLowerCase())) fail(node, `Keyed list item ${node.parent.name.getText()} is not supported`)
|
|
492
539
|
if (isRootKey) return
|
|
493
540
|
if (field) {
|
|
494
541
|
listValues.set(node.expression, { field })
|
|
@@ -496,7 +543,7 @@ function validateKeyedList(parts, sourceFile, setters, listValues, listEventItem
|
|
|
496
543
|
}
|
|
497
544
|
if (referencesIdentifier(expression, parts.item)) {
|
|
498
545
|
validateListExpression(expression, parts.item, node, fail)
|
|
499
|
-
if (ts.isJsxAttribute(node.parent) && ["
|
|
546
|
+
if (ts.isJsxAttribute(node.parent) && ["ref", "dangerouslysetinnerhtml"].includes(node.parent.name.getText().toLowerCase())) fail(node, `Keyed list item ${node.parent.name.getText()} is not supported`)
|
|
500
547
|
listValues.set(node.expression, { item: parts.item })
|
|
501
548
|
return
|
|
502
549
|
}
|
|
@@ -608,6 +655,21 @@ function unwrapExpression(node) {
|
|
|
608
655
|
return ts.isParenthesizedExpression(node) ? unwrapExpression(node.expression) : node
|
|
609
656
|
}
|
|
610
657
|
|
|
658
|
+
function isTopLevelConst(node) {
|
|
659
|
+
const list = node.parent
|
|
660
|
+
const statement = list?.parent
|
|
661
|
+
const owner = nearestFunction(node)
|
|
662
|
+
return ts.isVariableDeclarationList(list) && (list.flags & ts.NodeFlags.Const) !== 0 && ts.isVariableStatement(statement) && statement.parent === owner?.body
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function isJsxLocalValue(expression, known) {
|
|
666
|
+
const value = unwrapExpression(expression)
|
|
667
|
+
if (ts.isJsxElement(value) || ts.isJsxSelfClosingElement(value) || ts.isJsxFragment(value)) return true
|
|
668
|
+
if (ts.isIdentifier(value)) return known.has(value.text)
|
|
669
|
+
const parts = conditionalParts(value)
|
|
670
|
+
return Boolean(parts && (isJsxLocalValue(parts.truthy, known) || isJsxLocalValue(parts.falsy, known)))
|
|
671
|
+
}
|
|
672
|
+
|
|
611
673
|
function compileReactiveBinding(expression, setters, factory, context, reactiveBindings, handlerUrl) {
|
|
612
674
|
return factory.createCallExpression(factory.createIdentifier("__kBinding"), undefined, compileReactiveExpression(expression, setters, factory, context, reactiveBindings, handlerUrl))
|
|
613
675
|
}
|
package/framework/core.d.ts
CHANGED
package/framework/core.mjs
CHANGED
|
@@ -197,7 +197,7 @@ function serializeCapture(name, value, seen) {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
export async function renderPage(component, metadata = {}) {
|
|
200
|
-
renderContext = { nextState: 0, nextRef: 0, nextCondition: 0, nextList: 0, conditionDepth: 0, listDepth: 0, listRoot: undefined, listTemplate: false, listFields: undefined, states: {}, textStates: new Set(), conditionStates: new Set(), events: [], bindings: [], conditions: [], lists: [], hasBehaviors: false, hasNativeBehaviors: false, hasBindings: false, hasLists: false }
|
|
200
|
+
renderContext = { nextState: 0, nextRef: 0, nextCondition: 0, nextList: 0, conditionDepth: 0, listDepth: 0, listRoot: undefined, listTemplate: false, listFields: undefined, states: {}, textStates: new Set(), conditionStates: new Set(), events: [], bindings: [], conditions: [], lists: [], hasBehaviors: false, hasNativeBehaviors: false, hasBindings: false, hasLists: false, hasListStyles: false }
|
|
201
201
|
|
|
202
202
|
try {
|
|
203
203
|
const body = await renderNode({ type: component, props: {} })
|
|
@@ -235,6 +235,7 @@ export async function renderPage(component, metadata = {}) {
|
|
|
235
235
|
hasBehaviors: renderContext.hasBehaviors,
|
|
236
236
|
hasBindings: renderContext.hasBindings,
|
|
237
237
|
hasLists: renderContext.hasLists,
|
|
238
|
+
hasListStyles: renderContext.hasListStyles,
|
|
238
239
|
hasStateSeed: initialState.length > 0,
|
|
239
240
|
plan: {
|
|
240
241
|
states: Object.entries(renderContext.states).map(([id, state]) => ({ id, ...state })),
|
|
@@ -397,11 +398,13 @@ async function renderNode(node, namespace, selectValue = noSelectValue) {
|
|
|
397
398
|
if (value?.[listFieldMarker]) {
|
|
398
399
|
attributes += renderAttribute(name, value.value)
|
|
399
400
|
listAttributes.push([name, value.field])
|
|
401
|
+
if (name === "style") renderContext.hasListStyles = true
|
|
400
402
|
continue
|
|
401
403
|
}
|
|
402
404
|
if (value?.[listExpressionMarker]) {
|
|
403
405
|
attributes += renderAttribute(name, value.value)
|
|
404
406
|
listExpressionAttributes.push([name, value.module, value.handler])
|
|
407
|
+
if (name === "style") renderContext.hasListStyles = true
|
|
405
408
|
continue
|
|
406
409
|
}
|
|
407
410
|
if (value?.[signalMarker] || value?.[bindingMarker]) {
|