fix(react_compiler): keep imports referenced only by a computed key#23586
Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Merge activity
|
…23586) ## Problem When the React Compiler hoists an object into a memo cache slot, a computed key like `{ [CSS_VAR]: x }` keeps referencing the imported `CSS_VAR`. But `convert_expression_to_property_key` built **every** identifier key as a static property name, ignoring the `computed` flag. Semantic analysis then never linked the key to the import, so the TypeScript transform's import elision dropped the import — while codegen still emitted `[CSS_VAR]`. The result is output with a dangling reference (a runtime `ReferenceError`). ```tsx // input import { iconSizeVar } from "./styles.css"; export function Box({ size }) { const style = { [iconSizeVar]: size + "px" }; return <div style={style} />; } // before: the compiled body still reads `[iconSizeVar]`, but the import is gone ❌ ``` ## Fix Thread `computed` through `convert_expression_to_property_key` and build computed keys from the expression, so the identifier stays an `IdentifierReference`, semantic analysis links it to the binding, and import elision keeps the import. ## How it was found / impact By diffing oxc's React Compiler output against `babel-plugin-react-compiler` across the `oxc-ecosystem-ci` repos. **56 real-world files** (in `kibana`, `posthog`, `affine`, …) emitted code referencing a dropped import — all UPPER_SNAKE config constants or vanilla-extract CSS vars used as dynamic keys. This fixes every one of them, with **0 regressions** in the ~42k-file corpus. --- 🤖 Developed with [Claude Code](https://claude.com/claude-code) (AI-assisted).
68a6d8b to
20375f9
Compare
Merging this PR will not alter performance
Comparing Footnotes
|
### 💥 BREAKING CHANGES - 7a76cd3 estree: [**BREAKING**] Make whether to include TS fields a runtime option (#23574) (overlookmotel) - e7b6b68 estree: [**BREAKING**] `ESTree` config use methods not consts (#23573) (overlookmotel) ### 🚀 Features - 556cc6d data_structures: Add `CodeBuffer::as_str` method (#23571) (overlookmotel) - 38c4b06 parser: Add friendly error for adjacent JSX elements (#23378) (sapphi-red) - 53509a8 minifier: Treeshake pure typed arrays and Set/Map array literals (#23469) (Dunqing) - 09762d9 minifier: Inline const value for read-only vars (#22593) (Dunqing) ### 🐛 Bug Fixes - 20375f9 react_compiler: Keep imports referenced only by a computed key (#23586) (Boshen) - 31bfd9b minifier: Keep Object introspection calls on a possible Proxy (#23483) (Dunqing) - 837a395 parser: Treat a line comment after ':' as leading, not trailing (#23515) (Dunqing) - e409fe0 minifier: Keep `new Map`/`WeakSet`/`WeakMap` with a string argument (#23470) (Dunqing) - ae02b4e ci/parser: Use `minimal` for vitest reporter (#23457) (camc314) ### ⚡ Performance - cf24329 mangler: Compile slot sort once instead of per CAPACITY (#23577) (Boshen) - 4058a6a parser: Reduce code bloat from verify_modifiers monomorphization (#23576) (Boshen) - 053b0c1 estree: Remove pointless `mem::take` (#23572) (overlookmotel) - dfb52b6 transformer: Pre-size statement vecs in TS enum & namespace lowering (#23516) (Yunfei He) - 970e09a minifier: Compute template-literal inline checks in a single pass (#23467) (Yunfei He) - 3170c0e semantic,mangler,minifier: Fix `Semantic::stats` node count and reuse stats in mangler builds (#23352) (Boshen) - d1fa6e0 minifier: Evaluate ternary branches once in minimize_conditional_expression (#23479) (Yunfei He) - 3fa8051 transformer: Pre-size JSX props vec to attribute count (#23466) (Yunfei He) - 488b382 react_compiler: Borrow binding names in prefilter instead of allocating (#23471) (Yunfei He) - bcb3894 minifier: Incremental scoping refresh, delete LiveUsageCollector (#23197) (Dunqing) ### 📚 Documentation - f68641e data_structures: Improve docs on safety contract (#23575) (overlookmotel) Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
…23586) ## Problem When the React Compiler hoists an object into a memo cache slot, a computed key like `{ [CSS_VAR]: x }` keeps referencing the imported `CSS_VAR`. But `convert_expression_to_property_key` built **every** identifier key as a static property name, ignoring the `computed` flag. Semantic analysis then never linked the key to the import, so the TypeScript transform's import elision dropped the import — while codegen still emitted `[CSS_VAR]`. The result is output with a dangling reference (a runtime `ReferenceError`). ```tsx // input import { iconSizeVar } from "./styles.css"; export function Box({ size }) { const style = { [iconSizeVar]: size + "px" }; return <div style={style} />; } // before: the compiled body still reads `[iconSizeVar]`, but the import is gone ❌ ``` ## Fix Thread `computed` through `convert_expression_to_property_key` and build computed keys from the expression, so the identifier stays an `IdentifierReference`, semantic analysis links it to the binding, and import elision keeps the import. ## How it was found / impact By diffing oxc's React Compiler output against `babel-plugin-react-compiler` across the `oxc-ecosystem-ci` repos. **56 real-world files** (in `kibana`, `posthog`, `affine`, …) emitted code referencing a dropped import — all UPPER_SNAKE config constants or vanilla-extract CSS vars used as dynamic keys. This fixes every one of them, with **0 regressions** in the ~42k-file corpus. --- 🤖 Developed with [Claude Code](https://claude.com/claude-code) (AI-assisted).
### 💥 BREAKING CHANGES - 7a76cd3 estree: [**BREAKING**] Make whether to include TS fields a runtime option (#23574) (overlookmotel) - e7b6b68 estree: [**BREAKING**] `ESTree` config use methods not consts (#23573) (overlookmotel) ### 🚀 Features - 556cc6d data_structures: Add `CodeBuffer::as_str` method (#23571) (overlookmotel) - 38c4b06 parser: Add friendly error for adjacent JSX elements (#23378) (sapphi-red) - 53509a8 minifier: Treeshake pure typed arrays and Set/Map array literals (#23469) (Dunqing) - 09762d9 minifier: Inline const value for read-only vars (#22593) (Dunqing) ### 🐛 Bug Fixes - 20375f9 react_compiler: Keep imports referenced only by a computed key (#23586) (Boshen) - 31bfd9b minifier: Keep Object introspection calls on a possible Proxy (#23483) (Dunqing) - 837a395 parser: Treat a line comment after ':' as leading, not trailing (#23515) (Dunqing) - e409fe0 minifier: Keep `new Map`/`WeakSet`/`WeakMap` with a string argument (#23470) (Dunqing) - ae02b4e ci/parser: Use `minimal` for vitest reporter (#23457) (camc314) ### ⚡ Performance - cf24329 mangler: Compile slot sort once instead of per CAPACITY (#23577) (Boshen) - 4058a6a parser: Reduce code bloat from verify_modifiers monomorphization (#23576) (Boshen) - 053b0c1 estree: Remove pointless `mem::take` (#23572) (overlookmotel) - dfb52b6 transformer: Pre-size statement vecs in TS enum & namespace lowering (#23516) (Yunfei He) - 970e09a minifier: Compute template-literal inline checks in a single pass (#23467) (Yunfei He) - 3170c0e semantic,mangler,minifier: Fix `Semantic::stats` node count and reuse stats in mangler builds (#23352) (Boshen) - d1fa6e0 minifier: Evaluate ternary branches once in minimize_conditional_expression (#23479) (Yunfei He) - 3fa8051 transformer: Pre-size JSX props vec to attribute count (#23466) (Yunfei He) - 488b382 react_compiler: Borrow binding names in prefilter instead of allocating (#23471) (Yunfei He) - bcb3894 minifier: Incremental scoping refresh, delete LiveUsageCollector (#23197) (Dunqing) ### 📚 Documentation - f68641e data_structures: Improve docs on safety contract (#23575) (overlookmotel) Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>

Problem
When the React Compiler hoists an object into a memo cache slot, a computed key like
{ [CSS_VAR]: x }keeps referencing the importedCSS_VAR. Butconvert_expression_to_property_keybuilt every identifier key as a static property name, ignoring thecomputedflag. Semantic analysis then never linked the key to the import, so the TypeScript transform's import elision dropped the import — while codegen still emitted[CSS_VAR]. The result is output with a dangling reference (a runtimeReferenceError).Fix
Thread
computedthroughconvert_expression_to_property_keyand build computed keys from the expression, so the identifier stays anIdentifierReference, semantic analysis links it to the binding, and import elision keeps the import.How it was found / impact
By diffing oxc's React Compiler output against
babel-plugin-react-compileracross theoxc-ecosystem-cirepos. 56 real-world files (inkibana,posthog,affine, …) emitted code referencing a dropped import — all UPPER_SNAKE config constants or vanilla-extract CSS vars used as dynamic keys. This fixes every one of them, with 0 regressions in the ~42k-file corpus.🤖 Developed with Claude Code (AI-assisted).