Repository: oxc-project/oxc (2026-03-27T18:27:10Z)

Issue:
minifier: inlining variables in object when string key is encountered
i noticed a bug with logic used to inline variables, that is present only when object with property of type string is present before variable that should be inlined

```ts
var o = "test-b", l = {
	"test-a": "test-a",
	testb: o,
};
export { l };
/// out
var o = "test-b", l = {
	"test-a": "test-a",
	testb: o
};
export { l };
```
[play](https://playground.oxc.rs/?options=%7B%22run%22%3A%7B%22lint%22%3Atrue%2C%22formatter%22%3Afalse%2C%22transform%22%3Afalse%2C%22isolatedDeclarations%22%3Afalse%2C%22whitespace%22%3Afalse%2C%22mangle%22%3Afalse%2C%22compress%22%3Atrue%2C%22scope%22%3Atrue%2C%22symbol%22%3Atrue%2C%22cfg%22%3Afalse%7D%2C%22parser%22%3A%7B%22extension%22%3A%22tsx%22%2C%22allowReturnOutsideFunction%22%3Atrue%2C%22preserveParens%22%3Atrue%2C%22allowV8Intrinsics%22%3Atrue%2C%22semanticErrors%22%3Atrue%7D%2C%22linter%22%3A%7B%7D%2C%22formatter%22%3A%7B%22useTabs%22%3Afalse%2C%22tabWidth%22%3A2%2C%22endOfLine%22%3A%22lf%22%2C%22printWidth%22%3A80%2C%22singleQuote%22%3Afalse%2C%22jsxSingleQuote%22%3Afalse%2C%22quoteProps%22%3A%22as-needed%22%2C%22trailingComma%22%3A%22all%22%2C%22semi%22%3Atrue%2C%22arrowParens%22%3A%22always%22%2C%22bracketSpacing%22%3Atrue%2C%22bracketSameLine%22%3Afalse%2C%22objectWrap%22%3A%22preserve%22%2C%22singleAttributePerLine%22%3Afalse%7D%2C%22transformer%22%3A%7B%22target%22%3A%22es2015%22%2C%22useDefineForClassFields%22%3Atrue%2C%22experimentalDecorators%22%3Atrue%2C%22emitDecoratorMetadata%22%3Atrue%7D%2C%22isolatedDeclarations%22%3A%7B%22stripInternal%22%3Afalse%7D%2C%22codegen%22%3A%7B%22normal%22%3Atrue%2C%22jsdoc%22%3Atrue%2C%22annotation%22%3Atrue%2C%22legal%22%3Atrue%7D%2C%22compress%22%3A%7B%7D%2C%22mangle%22%3A%7B%22topLevel%22%3Atrue%2C%22keepNames%22%3Afalse%7D%2C%22controlFlow%22%3A%7B%22verbose%22%3Afalse%7D%2C%22inject%22%3A%7B%22inject%22%3A%7B%7D%7D%2C%22define%22%3A%7B%22define%22%3A%7B%7D%7D%7D&code=var+o+%3D+%22test-b%22%2C+l+%3D+%7B%0A%09%22test-a%22%3A+%22string%22%2C%0A%09testb%3A+o%2C%0A%7D%3B%0Aexport+%7B+l+%7D%3B)

this issue is present only when `"test-a"` string is enoutered and it works correctly when `"` can be removed
```ts
var o = "test-b", l = {
	"testa": "test-a",
	testb: o,
};
export { l };
//// out
var l = {
	testa: "test-a",
	testb: "test-b"
};
export { l };
```


Discussion:

Patch:
diff --git a/crates/oxc_minifier/src/peephole/minimize_statements.rs b/crates/oxc_minifier/src/peephole/minimize_statements.rs
index 4987d2c3cd821..c40f9bb0327be 100644
--- a/crates/oxc_minifier/src/peephole/minimize_statements.rs
+++ b/crates/oxc_minifier/src/peephole/minimize_statements.rs
@@ -1720,33 +1720,8 @@ impl<'a> PeepholeOptimizations {
             Expression::ObjectExpression(obj_expr) => {
                 for prop in &mut obj_expr.properties {
                     match prop {
-                        ObjectPropertyKind::ObjectProperty(prop) => match prop.key {
-                            PropertyKey::StaticIdentifier(_)
-                            | PropertyKey::PrivateIdentifier(_) => {
-                                if let Some(changed) =
-                                    Self::substitute_single_use_symbol_in_expression(
-                                        &mut prop.value,
-                                        search_for,
-                                        replacement,
-                                        replacement_has_side_effect,
-                                        ctx,
-                                    )
-                                {
-                                    if prop.shorthand && prop.key.is_specific_id("__proto__") {
-                                        // { __proto__ } -> { ['__proto__']: value }
-                                        prop.computed = true;
-                                        prop.key =
-                                            PropertyKey::from(ctx.ast.expression_string_literal(
-                                                prop.key.span(),
-                                                "__proto__",
-                                                None,
-                                            ));
-                                    }
-                                    prop.shorthand = false;
-                                    return Some(changed);
-                                }
-                            }
-                            match_expression!(PropertyKey) => {
+                        ObjectPropertyKind::ObjectProperty(prop) => {
+                            if prop.computed {
                                 if let Some(changed) =
                                     Self::substitute_single_use_symbol_in_expression(
                                         prop.key.to_expression_mut(),
@@ -1761,7 +1736,28 @@ impl<'a> PeepholeOptimizations {
                                 // Stop now because computed keys have side effects
                                 return Some(false);
                             }
-                        },
+
+                            if let Some(changed) = Self::substitute_single_use_symbol_in_expression(
+                                &mut prop.value,
+                                search_for,
+                                replacement,
+                                replacement_has_side_effect,
+                                ctx,
+                            ) {
+                                if prop.shorthand && prop.key.is_specific_id("__proto__") {
+                                    // { __proto__ } -> { ['__proto__']: value }
+                                    prop.computed = true;
+                                    prop.key =
+                                        PropertyKey::from(ctx.ast.expression_string_literal(
+                                            prop.key.span(),
+                                            "__proto__",
+                                            None,
+                                        ));
+                                }
+                                prop.shorthand = false;
+                                return Some(changed);
+                            }
+                        }
                         ObjectPropertyKind::SpreadProperty(prop) => {
                             if let Some(changed) = Self::substitute_single_use_symbol_in_expression(
                                 &mut prop.argument,
diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap
index 6f3517a6140cd..f91e84bc5f209 100644
--- a/tasks/minsize/minsize.snap
+++ b/tasks/minsize/minsize.snap
@@ -21,7 +21,7 @@ Original   | minified   | minified   | gzip       | gzip       | Iterations | Fi
 
 3.20 MB    | 1.00 MB    | 1.01 MB    | 322.57 kB  | 331.56 kB  |          3 | echarts.js 
 
-6.69 MB    | 2.22 MB    | 2.31 MB    | 458.43 kB  | 488.28 kB  |          4 | antd.js    
+6.69 MB    | 2.22 MB    | 2.31 MB    | 458.42 kB  | 488.28 kB  |          4 | antd.js    
 
 10.95 MB   | 3.33 MB    | 3.49 MB    | 853.27 kB  | 915.50 kB  |          4 | typescript.js 
 
diff --git a/tasks/track_memory_allocations/allocs_minifier.snap b/tasks/track_memory_allocations/allocs_minifier.snap
index 0358f433335e3..c2724e1491b9a 100644
--- a/tasks/track_memory_allocations/allocs_minifier.snap
+++ b/tasks/track_memory_allocations/allocs_minifier.snap
@@ -2,13 +2,13 @@ File                       | File size      || Sys allocs     | Sys reallocs   |
 -------------------------------------------------------------------------------------------------------------------------------------------
 checker.ts                 | 2.92 MB        ||            333 |             37 ||         152659 |          28244
 
-cal.com.tsx                | 1.06 MB        ||          20242 |             46 ||          37144 |           4583
+cal.com.tsx                | 1.06 MB        ||          20242 |             46 ||          37147 |           4570
 
 RadixUIAdoptionSection.jsx | 2.52 kB        ||             34 |              6 ||             30 |              6
 
 pdf.mjs                    | 567.30 kB      ||           3994 |            417 ||          47464 |           7734
 
-antd.js                    | 6.69 MB        ||           7751 |           1657 ||         331671 |          69344
+antd.js                    | 6.69 MB        ||           7751 |           1657 ||         331673 |          69344
 
 binder.ts                  | 193.08 kB      ||             78 |             23 ||           7075 |            824
 


Test patch:
diff --git a/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs b/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs
index 216a7f854022a..3dd4fc265148e 100644
--- a/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs
+++ b/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs
@@ -329,6 +329,10 @@ fn keep_exposed_variables() {
     test("var x = foo; x()", "foo()");
     test_script_same("var x = foo; x()");
     test_script("{ let x = foo; x() }", "foo()");
+    test(
+        "var b = 'b', l = { '-a': 'a', b }; export { l }",
+        "var l = { '-a': 'a', b: 'b' }; export { l }",
+    );
 }
 
 #[test]