dashersw / dashersw/gea

[BUG] Keyed list rows with value + input two-way binding reference undeclared variables (ReferenceError)

Open
#87 0 comments 1 reaction 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.