Describe the bug
Any element inside a keyed .map() row that has both value={...} and an input={...} handler (the standard two-way form binding) generates a keyed-row template that references undeclared variables, throwing ReferenceError at runtime — in the browser and during SSR (renderToString).
Minimal reproduction
// App.tsx
import { Component } from '@geajs/core'
const items = [{ id: 'a', title: 'x' }]
export default class App extends Component {
template() {
return (
<div>
{items.map((item) => (
<article key={item.id}>
<label>
<span>T</span>
<input value={item.title} input={(e: Event) => console.log(e)} />
</label>
</article>
))}
</div>
)
}
}
Render it (client render or SSR). The compiled row function is emitted as:
const __ki_L0 = (item, idx, d) => {
const root = (_tpl1_root || (_tpl1_root = _tpl1_create())).cloneNode(true);
const el0 = root;
const evt2 = el1; // ← el1 was never declared
evt2.__onct_input = __hm_0;
{
root.firstChild.childNodes[1].value = item.title;
}
root[GEA_DOM_ITEM] = item;
return root;
};
→ ReferenceError: el1 is not defined
Triggering patterns (all confirmed):
| Variant |
Result |
input with value + input handler |
BROKEN |
input with value only |
ok |
input with input handler only |
ok |
button + input(value+input) |
BROKEN |
Root cause
In the keyed-list patch-plan optimizer (collectPatchRowPlan → buildPatchWrite → pruneExtractedCreateItemStatements):
- When an input element has both a
value slot and an input event slot on the same walk, emitWalkCapture declares the value slot as elN and emits the event var as an alias: const evtM = elN;
- The optimizer then converts
reactiveValueRead(elN, ...) into an inline one-shot write and adds elN to plan.deadBindings.
- The prune step removes
const elN = ... but does not check that the surviving alias declaration const evtM = elN; still references it.
Suggested fix: when adding names to deadBindings, skip names that are aliased by other surviving declarations (or rewrite aliases to raw walks before pruning).
Affected versions
Reproduced with @geajs/vite-plugin@1.4.1 and also @geajs/vite-plugin@1.2.3, with @geajs/core@1.4.0.
Impact
Every keyed list row using the common value + input two-way pattern crashes as soon as the row renders. This blocks both client rendering and SSR for any form-heavy app.
Describe the bug
Any element inside a keyed
.map()row that has bothvalue={...}and aninput={...}handler (the standard two-way form binding) generates a keyed-row template that references undeclared variables, throwingReferenceErrorat runtime — in the browser and during SSR (renderToString).Minimal reproduction
Render it (client render or SSR). The compiled row function is emitted as:
→
ReferenceError: el1 is not definedTriggering patterns (all confirmed):
value+inputhandlervalueonlyinputhandler onlyvalue+input)Root cause
In the keyed-list patch-plan optimizer (
collectPatchRowPlan→buildPatchWrite→pruneExtractedCreateItemStatements):valueslot and aninputevent slot on the same walk,emitWalkCapturedeclares the value slot aselNand emits the event var as an alias:const evtM = elN;reactiveValueRead(elN, ...)into an inline one-shot write and addselNtoplan.deadBindings.const elN = ...but does not check that the surviving alias declarationconst evtM = elN;still references it.Suggested fix: when adding names to
deadBindings, skip names that are aliased by other surviving declarations (or rewrite aliases to raw walks before pruning).Affected versions
Reproduced with
@geajs/vite-plugin@1.4.1and also@geajs/vite-plugin@1.2.3, with@geajs/core@1.4.0.Impact
Every keyed list row using the common
value+inputtwo-way pattern crashes as soon as the row renders. This blocks both client rendering and SSR for any form-heavy app.