Fix JSX transform panic when a bare `key` prop precedes `key` with a value (#31350)
### What does this PR do?
Fixes a panic in the JSX transform when a JSX element has a bare `key`
prop (no value) followed by a `key` prop with a value:
```
panic: removal index (is 1) should be < len (is 1)
```
**Repro** (found by fuzzing, 22-byte input):
```js
new Bun.Transpiler({ loader: "jsx", target: "node", minifyWhitespace: true })
.transformSync('<div key key=""></div>');
```
### Cause
In `parse_jsx.rs`, a bare `key` prop is skipped with a `"key" prop
ignored` warning and is never appended to the props list — but the prop
index counter `i` still advanced. A later `key=...` then recorded
`key_prop_i = i`, which is one past the prop's actual position in the
list. During the automatic-runtime JSX transform, the key extraction
calls `properties.ordered_remove(key_prop_index)`, which removes past
the end of the list and panics.
### Fix
Only advance the counter when a prop is actually appended to `props`, so
`key_prop_index` (and `first_spread_prop_i`) always match real positions
in the props list. The bare `key` keeps its existing behavior (warning +
ignored), and `key="duplicate"` is extracted as the key argument as
usual.
### Verification
- Before: the repro (and the new test's subprocess) panics with `removal
index (is 1) should be < len (is 1)`.
- After: `<div key key="duplicate"></div>` transforms to `jsxDEV("div",
{}, "duplicate", ...)`; the variant with an extra prop in between (`<div
key className="x" key="duplicate">`) also transforms correctly.
- New test in `test/bundler/transpiler/transpiler.test.js` runs the
transform in a subprocess (so a crash surfaces as a test failure) and
asserts the exact output; it fails on the previous build and passes with
this change.
- Full `test/bundler/transpiler/transpiler.test.js` suite passes (147
pass / 0 fail). R
robobun committed
dedf657ae0847b7af6f846d560a498adcf7e3a16
Parent: 0deaf0b
Committed by GitHub <noreply@github.com>
on 5/24/2026, 11:56:43 PM