[BUG] Keyed list rows with value + input two-way binding reference undeclared variables (ReferenceError)
- Dominant language
- JavaScript
- Stars
- 1.2k
- Forks
- 47
- PR merge metrics
- No merged PRs in 30d
Description
### 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
```tsx
// App.tsx
import { Component } from '@geajs/core'
const items = [{ id: 'a', title: 'x' }]
export default class App extends Component {
template() {
return (
{items.map((item) => (
T
console.log(e)} />
))}
)
}
}
```
Render it (client render or SSR). The compiled row function is emitted as:
```js
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`):
1. 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;`
2. The optimizer then converts `reactiveValueRead(elN, ...)` into an inline one-shot write and adds `elN` to `plan.deadBindings`.
3. 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.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with collectPatchRowPlan, buildPatchWrite, and pruneExtractedCreateItemStatements in the keyed-list patch-plan optimizer. Use the supplied keyed .map() reproduction with value plus input binding, checking both client rendering and renderToString. Done means the generated row no longer references an undeclared variable and the reproduction renders without ReferenceError.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, vite
- Domain
- compilers, frontend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100