fix(transform): enable enum_eval for transformSync and vite TS transform#9325
Conversation
✅ Deploy Preview for rolldown-rs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
enum_eval for transformSync and vite TS transform
ff3cbfd to
ece7fed
Compare
How to use the Graphite Merge QueueAdd the label graphite: merge-when-ready to this PR to add it to 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. |
Merging this PR will not alter performance
Comparing Footnotes
|
Merge activity
|
…ansform (#9325) Related: oxc-project/oxc#22252 ## Summary Fixes the bug where `transformSync`, the vite TS transform plugin, and the bundler (when `define` triggers a program mutation) emitted a corrupt reverse-mapping line for string-enum alias members. For input ```ts enum Theme { Light = "Light", Dark = "Dark", Default = Theme.Light, } ``` rolldown produced ```js Theme[Theme["Default"] = Theme.Light] = "Default"; ``` which overwrites `Theme["Light"]`'s reverse mapping with `"Default"`. Expected (matching `tsc`): ```js Theme["Default"] = "Light"; ``` ## Cause The oxc TS transformer's enum lowering looks up each member's constant value via `Scoping::get_enum_member_value` (`crates/oxc_transformer/src/typescript/enum.rs:310`). That table is only populated when the `SemanticBuilder` is built with `with_enum_eval(true)`. When it isn't, every member returns `None`, so the transformer takes the non-string-init branch and emits the `Foo[Foo["x"] = init] = "x"` reverse-mapping form regardless of whether `init` is statically a string. The bundler's initial build (`crates/rolldown/src/utils/pre_process_ecma_ast.rs:66`) already enables `enum_eval`, but four other call sites that hand a `Scoping` to `Transformer::build_with_scoping` did not: - `transformSync` (`crates/rolldown_common/src/utils/enhanced_transform.rs`) — initial build and the fallback used after `ReplaceGlobalDefines` consumes the scoping. - The vite TS transform plugin (`crates/rolldown_plugin_vite_transform/src/lib.rs`). - The bundler's `recreate_scoping` (`crates/rolldown/src/utils/pre_process_ecma_ast.rs:238`), which feeds the transformer when the define plugin has consumed the original scoping. ## Fix Add `.with_enum_eval(true)` to all four call sites: - `crates/rolldown_common/src/utils/enhanced_transform.rs` — initial build before transformer. - `crates/rolldown_common/src/utils/enhanced_transform.rs` — fallback rebuild when `ReplaceGlobalDefines` consumed the original scoping. - `crates/rolldown_plugin_vite_transform/src/lib.rs` — vite TS transform plugin. - `crates/rolldown/src/utils/pre_process_ecma_ast.rs` — `recreate_scoping`, the fallback used by step 3 (transformer) when define has consumed scoping. Later callers of `recreate_scoping` (inject / DCE / PreProcessor) run after enums have been lowered to IIFEs, so the eval pass is a no-op there. Closes #9312 ## Test plan - [x] Added two `transformSync` regression tests in `packages/rolldown/tests/utils/transform.test.ts` (string-enum alias forward-only, numeric-enum alias reverse-mapping retained). - [x] Added a bundler fixture under `crates/rolldown/tests/rolldown/issues/9312/` covering define + string-enum-alias. - [x] All 24 existing enum-related Rust integration tests pass. - [x] All 38 define-related tests pass. - [x] `cargo clippy -p rolldown_common -p rolldown_plugin_vite_transform -- -D warnings` clean. - [x] Verified the issue's exact `transformSync` repro (string, numeric, mixed enums) matches `tsc` output.
d0b9526 to
ef5796f
Compare
…9326) ## Summary Centralize the `with_enum_eval(true)` invariant required for any `Scoping` that will be passed to `Transformer::build_with_scoping`. After #9325 the same option needed to be set at five call sites; missing it at any one of them recreates the string-enum-alias bug. Wrapping it in a single helper makes the invariant a property of the function name instead of a footgun spread across crates. ## Cause Five call sites that hand a `Scoping` to the transformer were each spelling out `SemanticBuilder::new().with_enum_eval(true)` (or worse, omitting the flag and silently producing the buggy reverse-mapping form for string-enum aliases). Adding a new transformer entry point in the future would require remembering this same invariant. ## Fix Add `pub fn semantic_builder_for_transform()` in `crates/rolldown_ecmascript/src/ecma_ast/helpers.rs`, re-exported from `rolldown_ecmascript::*`. The doc comment on the helper captures the WHY (`get_enum_member_value` is only populated when `enum_eval` is on; without it, string-enum aliases get the corrupt `Foo[Foo["x"] = init] = "x"` form). Migrate all five call sites to start from the helper: - `crates/rolldown/src/utils/pre_process_ecma_ast.rs` — initial bundler build (chains `with_check_syntax_error`). - `crates/rolldown/src/utils/pre_process_ecma_ast.rs::recreate_scoping` — post-define fallback (chains `with_stats`). - `crates/rolldown_common/src/utils/enhanced_transform.rs` — initial build for `transformSync`. - `crates/rolldown_common/src/utils/enhanced_transform.rs` — fallback rebuild after `ReplaceGlobalDefines`. - `crates/rolldown_plugin_vite_transform/src/lib.rs` — vite TS transform plugin. The post-transformer rebuild for `InjectGlobalVariables` (`enhanced_transform.rs:406`) is intentionally left as a bare `SemanticBuilder::new()` — enum_eval is semantically irrelevant after enums have been lowered. Adds `rolldown_ecmascript = { workspace = true }` to `crates/rolldown_plugin_vite_transform/Cargo.toml` so the plugin can reach the helper. ## Test plan - [x] No new test surface — covered by the regression tests already in #9325 (Rust fixture `/issues/9312/`, TS unit tests in `packages/rolldown/tests/utils/transform.test.ts`). - [x] All 24 enum-related Rust tests, 38 define-related tests, and 49 `transform.test.ts` cases pass. - [x] `cargo clippy -p rolldown_ecmascript -p rolldown_common -p rolldown_plugin_vite_transform -- -D warnings` clean.
## [1.0.1] - 2026-05-13 ### 🚀 Features - experimental/lazy-barrel: advice on oversized barrel modules (#9236) by @shulaoda - rolldown: inline optional-chain enum access (#9379) by @Dunqing - chunk-optimization: dedupe already-loaded dynamic deps (#9305) by @IWANABETHATGUY - binding: call moduleParsed hook in ParallelJsPlugin (#9318) by @jaehafe ### 🐛 Bug Fixes - transform: enable `enum_eval` for `transformSync` and vite TS transform (#9325) by @Dunqing - error: remove severity prefix from diagnostic messages (#9262) by @Kyujenius - deps: pin pnpm to 10.23.0 to work around catalog mismatch on Netlify (#9364) by @shulaoda - ci: pin mimalloc-safe to 0.1.58 (#9361) by @shulaoda - dev/lazy: fix exports of lazy requests in lazy chunks (#9249) by @h-a-n-a - rolldown_plugin_vite_resolve: handle errors in `resolveSubpathImports` callback (#9355) by @sapphi-red - rolldown_plugin_lazy_compilation: use loadExports for fetched proxy to preserve original export names (#9132) by @h-a-n-a - common: include offending index in HybridIndexVec panic message (#9296) by @SAY-5 ### 🚜 Refactor - ecmascript: extract semantic_builder_for_transform helper (#9326) by @Dunqing - test: extract reusable static-import-cycle helper (#9332) by @IWANABETHATGUY ### 📚 Documentation - clarify scope of `topLevelVar` (#9380) by @IWANABETHATGUY - meta/design: add ast-mutation design doc (#9338) by @hyf0 - feat: add ai policy in contribution guide (#9315) by @mdong1909 ### ⚡ Performance - binding: enable mimalloc v3 to reduce idle memory (#9349) by @shulaoda ### 🧪 Testing - mcs: cover require() in `$initial` group (#9376) by @hyf0 - add regression for CJS facade chunk merge into entry (#9351) by @IWANABETHATGUY ### ⚙️ Miscellaneous Tasks - switch prepare-release to manual dispatch with version input (#9383) by @shulaoda - migrate `@rolldown/pluginutils` to `rolldown/plugins` (#9317) by @shulaoda - deps: pin libmimalloc-sys2 to 0.1.54 (#9372) by @shulaoda - replace `igorskyflyer/action-readfile` with `cat` (#9369) by @sapphi-red - deps: update test262 submodule for tests (#9371) by @rolldown-guard[bot] - use app token for test dep update PRs (#9368) by @sapphi-red - replace some actions with gh commands (#9367) by @sapphi-red - replace action-semantic-pull-request with inline regex (#9366) by @sapphi-red - remove pull_request_target workflows (#9188) by @Boshen - deps: upgrade oxc to 0.130.0 (#9360) by @shulaoda - deps: update github actions (major) (#9348) by @renovate[bot] - deps: update github actions (#9341) by @renovate[bot] - deps: update rust crates (#9344) by @renovate[bot] - deps: update crate-ci/typos action to v1.46.1 (#9357) by @renovate[bot] - deps: update npm packages (#9343) by @renovate[bot] - deps: update pnpm to v10.33.4 (#9347) by @renovate[bot] - deps: update dependency rolldown-plugin-dts to ^0.25.0 (#9346) by @renovate[bot] - .claude: add rolldown-repl encoder, rename decode skill (#9352) by @IWANABETHATGUY - deps: update crate-ci/typos action to v1.46.0 (#9345) by @renovate[bot] - deps: update napi to v3.8.6 (#9342) by @renovate[bot] - deps: update dependency vite-plus to v0.1.20 (#9340) by @renovate[bot] - enable rollup chunking-form test (#9335) by @IWANABETHATGUY - typo: fix typo in watcher options comment (#9324) by @thescripted ### ❤️ New Contributors * @Kyujenius made their first contribution in [#9262](#9262) * @SAY-5 made their first contribution in [#9296](#9296) * @thescripted made their first contribution in [#9324](#9324) Co-authored-by: shulaoda <[email protected]>
…ansform (#9325) Related: oxc-project/oxc#22252 ## Summary Fixes the bug where `transformSync`, the vite TS transform plugin, and the bundler (when `define` triggers a program mutation) emitted a corrupt reverse-mapping line for string-enum alias members. For input ```ts enum Theme { Light = "Light", Dark = "Dark", Default = Theme.Light, } ``` rolldown produced ```js Theme[Theme["Default"] = Theme.Light] = "Default"; ``` which overwrites `Theme["Light"]`'s reverse mapping with `"Default"`. Expected (matching `tsc`): ```js Theme["Default"] = "Light"; ``` ## Cause The oxc TS transformer's enum lowering looks up each member's constant value via `Scoping::get_enum_member_value` (`crates/oxc_transformer/src/typescript/enum.rs:310`). That table is only populated when the `SemanticBuilder` is built with `with_enum_eval(true)`. When it isn't, every member returns `None`, so the transformer takes the non-string-init branch and emits the `Foo[Foo["x"] = init] = "x"` reverse-mapping form regardless of whether `init` is statically a string. The bundler's initial build (`crates/rolldown/src/utils/pre_process_ecma_ast.rs:66`) already enables `enum_eval`, but four other call sites that hand a `Scoping` to `Transformer::build_with_scoping` did not: - `transformSync` (`crates/rolldown_common/src/utils/enhanced_transform.rs`) — initial build and the fallback used after `ReplaceGlobalDefines` consumes the scoping. - The vite TS transform plugin (`crates/rolldown_plugin_vite_transform/src/lib.rs`). - The bundler's `recreate_scoping` (`crates/rolldown/src/utils/pre_process_ecma_ast.rs:238`), which feeds the transformer when the define plugin has consumed the original scoping. ## Fix Add `.with_enum_eval(true)` to all four call sites: - `crates/rolldown_common/src/utils/enhanced_transform.rs` — initial build before transformer. - `crates/rolldown_common/src/utils/enhanced_transform.rs` — fallback rebuild when `ReplaceGlobalDefines` consumed the original scoping. - `crates/rolldown_plugin_vite_transform/src/lib.rs` — vite TS transform plugin. - `crates/rolldown/src/utils/pre_process_ecma_ast.rs` — `recreate_scoping`, the fallback used by step 3 (transformer) when define has consumed scoping. Later callers of `recreate_scoping` (inject / DCE / PreProcessor) run after enums have been lowered to IIFEs, so the eval pass is a no-op there. Closes #9312 ## Test plan - [x] Added two `transformSync` regression tests in `packages/rolldown/tests/utils/transform.test.ts` (string-enum alias forward-only, numeric-enum alias reverse-mapping retained). - [x] Added a bundler fixture under `crates/rolldown/tests/rolldown/issues/9312/` covering define + string-enum-alias. - [x] All 24 existing enum-related Rust integration tests pass. - [x] All 38 define-related tests pass. - [x] `cargo clippy -p rolldown_common -p rolldown_plugin_vite_transform -- -D warnings` clean. - [x] Verified the issue's exact `transformSync` repro (string, numeric, mixed enums) matches `tsc` output.
…9326) ## Summary Centralize the `with_enum_eval(true)` invariant required for any `Scoping` that will be passed to `Transformer::build_with_scoping`. After #9325 the same option needed to be set at five call sites; missing it at any one of them recreates the string-enum-alias bug. Wrapping it in a single helper makes the invariant a property of the function name instead of a footgun spread across crates. ## Cause Five call sites that hand a `Scoping` to the transformer were each spelling out `SemanticBuilder::new().with_enum_eval(true)` (or worse, omitting the flag and silently producing the buggy reverse-mapping form for string-enum aliases). Adding a new transformer entry point in the future would require remembering this same invariant. ## Fix Add `pub fn semantic_builder_for_transform()` in `crates/rolldown_ecmascript/src/ecma_ast/helpers.rs`, re-exported from `rolldown_ecmascript::*`. The doc comment on the helper captures the WHY (`get_enum_member_value` is only populated when `enum_eval` is on; without it, string-enum aliases get the corrupt `Foo[Foo["x"] = init] = "x"` form). Migrate all five call sites to start from the helper: - `crates/rolldown/src/utils/pre_process_ecma_ast.rs` — initial bundler build (chains `with_check_syntax_error`). - `crates/rolldown/src/utils/pre_process_ecma_ast.rs::recreate_scoping` — post-define fallback (chains `with_stats`). - `crates/rolldown_common/src/utils/enhanced_transform.rs` — initial build for `transformSync`. - `crates/rolldown_common/src/utils/enhanced_transform.rs` — fallback rebuild after `ReplaceGlobalDefines`. - `crates/rolldown_plugin_vite_transform/src/lib.rs` — vite TS transform plugin. The post-transformer rebuild for `InjectGlobalVariables` (`enhanced_transform.rs:406`) is intentionally left as a bare `SemanticBuilder::new()` — enum_eval is semantically irrelevant after enums have been lowered. Adds `rolldown_ecmascript = { workspace = true }` to `crates/rolldown_plugin_vite_transform/Cargo.toml` so the plugin can reach the helper. ## Test plan - [x] No new test surface — covered by the regression tests already in #9325 (Rust fixture `/issues/9312/`, TS unit tests in `packages/rolldown/tests/utils/transform.test.ts`). - [x] All 24 enum-related Rust tests, 38 define-related tests, and 49 `transform.test.ts` cases pass. - [x] `cargo clippy -p rolldown_ecmascript -p rolldown_common -p rolldown_plugin_vite_transform -- -D warnings` clean.
## [1.0.1] - 2026-05-13 ### 🚀 Features - experimental/lazy-barrel: advice on oversized barrel modules (#9236) by @shulaoda - rolldown: inline optional-chain enum access (#9379) by @Dunqing - chunk-optimization: dedupe already-loaded dynamic deps (#9305) by @IWANABETHATGUY - binding: call moduleParsed hook in ParallelJsPlugin (#9318) by @jaehafe ### 🐛 Bug Fixes - transform: enable `enum_eval` for `transformSync` and vite TS transform (#9325) by @Dunqing - error: remove severity prefix from diagnostic messages (#9262) by @Kyujenius - deps: pin pnpm to 10.23.0 to work around catalog mismatch on Netlify (#9364) by @shulaoda - ci: pin mimalloc-safe to 0.1.58 (#9361) by @shulaoda - dev/lazy: fix exports of lazy requests in lazy chunks (#9249) by @h-a-n-a - rolldown_plugin_vite_resolve: handle errors in `resolveSubpathImports` callback (#9355) by @sapphi-red - rolldown_plugin_lazy_compilation: use loadExports for fetched proxy to preserve original export names (#9132) by @h-a-n-a - common: include offending index in HybridIndexVec panic message (#9296) by @SAY-5 ### 🚜 Refactor - ecmascript: extract semantic_builder_for_transform helper (#9326) by @Dunqing - test: extract reusable static-import-cycle helper (#9332) by @IWANABETHATGUY ### 📚 Documentation - clarify scope of `topLevelVar` (#9380) by @IWANABETHATGUY - meta/design: add ast-mutation design doc (#9338) by @hyf0 - feat: add ai policy in contribution guide (#9315) by @mdong1909 ### ⚡ Performance - binding: enable mimalloc v3 to reduce idle memory (#9349) by @shulaoda ### 🧪 Testing - mcs: cover require() in `$initial` group (#9376) by @hyf0 - add regression for CJS facade chunk merge into entry (#9351) by @IWANABETHATGUY ### ⚙️ Miscellaneous Tasks - switch prepare-release to manual dispatch with version input (#9383) by @shulaoda - migrate `@rolldown/pluginutils` to `rolldown/plugins` (#9317) by @shulaoda - deps: pin libmimalloc-sys2 to 0.1.54 (#9372) by @shulaoda - replace `igorskyflyer/action-readfile` with `cat` (#9369) by @sapphi-red - deps: update test262 submodule for tests (#9371) by @rolldown-guard[bot] - use app token for test dep update PRs (#9368) by @sapphi-red - replace some actions with gh commands (#9367) by @sapphi-red - replace action-semantic-pull-request with inline regex (#9366) by @sapphi-red - remove pull_request_target workflows (#9188) by @Boshen - deps: upgrade oxc to 0.130.0 (#9360) by @shulaoda - deps: update github actions (major) (#9348) by @renovate[bot] - deps: update github actions (#9341) by @renovate[bot] - deps: update rust crates (#9344) by @renovate[bot] - deps: update crate-ci/typos action to v1.46.1 (#9357) by @renovate[bot] - deps: update npm packages (#9343) by @renovate[bot] - deps: update pnpm to v10.33.4 (#9347) by @renovate[bot] - deps: update dependency rolldown-plugin-dts to ^0.25.0 (#9346) by @renovate[bot] - .claude: add rolldown-repl encoder, rename decode skill (#9352) by @IWANABETHATGUY - deps: update crate-ci/typos action to v1.46.0 (#9345) by @renovate[bot] - deps: update napi to v3.8.6 (#9342) by @renovate[bot] - deps: update dependency vite-plus to v0.1.20 (#9340) by @renovate[bot] - enable rollup chunking-form test (#9335) by @IWANABETHATGUY - typo: fix typo in watcher options comment (#9324) by @thescripted ### ❤️ New Contributors * @Kyujenius made their first contribution in [#9262](#9262) * @SAY-5 made their first contribution in [#9296](#9296) * @thescripted made their first contribution in [#9324](#9324) Co-authored-by: shulaoda <[email protected]>

Related: oxc-project/oxc#22252
Summary
Fixes the bug where
transformSync, the vite TS transform plugin, and the bundler (whendefinetriggers a program mutation) emitted a corrupt reverse-mapping line for string-enum alias members. For inputrolldown produced
which overwrites
Theme["Light"]'s reverse mapping with"Default". Expected (matchingtsc):Cause
The oxc TS transformer's enum lowering looks up each member's constant value via
Scoping::get_enum_member_value(crates/oxc_transformer/src/typescript/enum.rs:310). That table is only populated when theSemanticBuilderis built withwith_enum_eval(true). When it isn't, every member returnsNone, so the transformer takes the non-string-init branch and emits theFoo[Foo["x"] = init] = "x"reverse-mapping form regardless of whetherinitis statically a string.The bundler's initial build (
crates/rolldown/src/utils/pre_process_ecma_ast.rs:66) already enablesenum_eval, but four other call sites that hand aScopingtoTransformer::build_with_scopingdid not:transformSync(crates/rolldown_common/src/utils/enhanced_transform.rs) — initial build and the fallback used afterReplaceGlobalDefinesconsumes the scoping.crates/rolldown_plugin_vite_transform/src/lib.rs).recreate_scoping(crates/rolldown/src/utils/pre_process_ecma_ast.rs:238), which feeds the transformer when the define plugin has consumed the original scoping.Fix
Add
.with_enum_eval(true)to all four call sites:crates/rolldown_common/src/utils/enhanced_transform.rs— initial build before transformer.crates/rolldown_common/src/utils/enhanced_transform.rs— fallback rebuild whenReplaceGlobalDefinesconsumed the original scoping.crates/rolldown_plugin_vite_transform/src/lib.rs— vite TS transform plugin.crates/rolldown/src/utils/pre_process_ecma_ast.rs—recreate_scoping, the fallback used by step 3 (transformer) when define has consumed scoping. Later callers ofrecreate_scoping(inject / DCE / PreProcessor) run after enums have been lowered to IIFEs, so the eval pass is a no-op there.Closes #9312
Test plan
transformSyncregression tests inpackages/rolldown/tests/utils/transform.test.ts(string-enum alias forward-only, numeric-enum alias reverse-mapping retained).crates/rolldown/tests/rolldown/issues/9312/covering define + string-enum-alias.cargo clippy -p rolldown_common -p rolldown_plugin_vite_transform -- -D warningsclean.transformSyncrepro (string, numeric, mixed enums) matchestscoutput.