Skip to content

Commit 680aebe

Browse files
committed
feat(codegen): print shorthand for all { x } variants
closes #4340
1 parent d345b84 commit 680aebe

File tree

10 files changed

+170
-63
lines changed

10 files changed

+170
-63
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/oxc_codegen/src/gen.rs

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,18 +1079,10 @@ impl<'a, const MINIFY: bool> GenExpr<MINIFY> for ParenthesizedExpression<'a> {
10791079

10801080
impl<'a, const MINIFY: bool> Gen<MINIFY> for IdentifierReference<'a> {
10811081
fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) {
1082-
if let Some(mangler) = &p.mangler {
1083-
if let Some(reference_id) = self.reference_id.get() {
1084-
if let Some(name) = mangler.get_reference_name(reference_id) {
1085-
let name = CompactStr::new(name);
1086-
p.add_source_mapping_for_name(self.span, &name);
1087-
p.print_str(&name);
1088-
return;
1089-
}
1090-
}
1091-
}
1092-
p.add_source_mapping_for_name(self.span, &self.name);
1093-
p.print_str(&self.name);
1082+
let name = p.get_identifier_reference_name(self);
1083+
let name = CompactStr::new(name);
1084+
p.add_source_mapping_for_name(self.span, &name);
1085+
p.print_str(&name);
10941086
}
10951087
}
10961088

@@ -1103,7 +1095,10 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for IdentifierName<'a> {
11031095

11041096
impl<'a, const MINIFY: bool> Gen<MINIFY> for BindingIdentifier<'a> {
11051097
fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) {
1106-
p.print_symbol(self.span, self.symbol_id.get(), self.name.as_str());
1098+
let name = p.get_binding_identifier_name(self);
1099+
let name = CompactStr::new(name);
1100+
p.add_source_mapping_for_name(self.span, &name);
1101+
p.print_str(&name);
11071102
}
11081103
}
11091104

@@ -1514,7 +1509,6 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ObjectPropertyKind<'a> {
15141509
}
15151510
}
15161511

1517-
// TODO: only print shorthand if key value are the same.
15181512
impl<'a, const MINIFY: bool> Gen<MINIFY> for ObjectProperty<'a> {
15191513
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
15201514
if let Expression::FunctionExpression(func) = &self.value {
@@ -1559,15 +1553,29 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ObjectProperty<'a> {
15591553
return;
15601554
}
15611555
}
1556+
1557+
let mut shorthand = false;
1558+
if let PropertyKey::StaticIdentifier(key) = &self.key {
1559+
if let Expression::Identifier(ident) = &self.value {
1560+
if key.name == p.get_identifier_reference_name(ident) {
1561+
shorthand = true;
1562+
}
1563+
}
1564+
}
1565+
15621566
if self.computed {
15631567
p.print_char(b'[');
15641568
}
1565-
self.key.gen(p, ctx);
1569+
if !shorthand {
1570+
self.key.gen(p, ctx);
1571+
}
15661572
if self.computed {
15671573
p.print_char(b']');
15681574
}
1569-
p.print_colon();
1570-
p.print_soft_space();
1575+
if !shorthand {
1576+
p.print_colon();
1577+
p.print_soft_space();
1578+
}
15711579
self.value.gen_expr(p, Precedence::Assign, Context::default());
15721580
}
15731581
}
@@ -1925,7 +1933,17 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for AssignmentTargetProperty<'a> {
19251933

19261934
impl<'a, const MINIFY: bool> Gen<MINIFY> for AssignmentTargetPropertyIdentifier<'a> {
19271935
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
1928-
self.binding.gen(p, ctx);
1936+
let ident_name = p.get_identifier_reference_name(&self.binding);
1937+
if ident_name == self.binding.name.as_str() {
1938+
self.binding.gen(p, ctx);
1939+
} else {
1940+
// `({x: a} = y);`
1941+
let ident_name = CompactStr::new(ident_name);
1942+
p.print_str(self.binding.name.as_str());
1943+
p.print_colon();
1944+
p.print_soft_space();
1945+
p.print_str(ident_name.as_str());
1946+
}
19291947
if let Some(expr) = &self.init {
19301948
p.print_soft_space();
19311949
p.print_equal();
@@ -2535,19 +2553,32 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ObjectPattern<'a> {
25352553
}
25362554
}
25372555

2538-
// TODO: only print shorthand if key value are the same.
25392556
impl<'a, const MINIFY: bool> Gen<MINIFY> for BindingProperty<'a> {
25402557
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
25412558
p.add_source_mapping(self.span.start);
25422559
if self.computed {
25432560
p.print_char(b'[');
25442561
}
2545-
self.key.gen(p, ctx);
2562+
2563+
let mut shorthand = false;
2564+
if let PropertyKey::StaticIdentifier(key) = &self.key {
2565+
if let BindingPatternKind::BindingIdentifier(ident) = &self.value.kind {
2566+
if key.name == p.get_binding_identifier_name(ident) {
2567+
shorthand = true;
2568+
}
2569+
}
2570+
}
2571+
2572+
if !shorthand {
2573+
self.key.gen(p, ctx);
2574+
}
25462575
if self.computed {
25472576
p.print_char(b']');
25482577
}
2549-
p.print_colon();
2550-
p.print_soft_space();
2578+
if !shorthand {
2579+
p.print_colon();
2580+
p.print_soft_space();
2581+
}
25512582
self.value.gen(p, ctx);
25522583
}
25532584
}

crates/oxc_codegen/src/lib.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,18 @@ use std::{borrow::Cow, ops::Range};
1414
use rustc_hash::FxHashMap;
1515

1616
use oxc_ast::{
17-
ast::{BlockStatement, Directive, Expression, Program, Statement},
17+
ast::{
18+
BindingIdentifier, BlockStatement, Directive, Expression, IdentifierReference, Program,
19+
Statement,
20+
},
1821
Comment, Trivias,
1922
};
2023
use oxc_mangler::Mangler;
21-
use oxc_span::{CompactStr, Span};
24+
use oxc_span::Span;
2225
use oxc_syntax::{
2326
identifier::is_identifier_part,
2427
operator::{BinaryOperator, UnaryOperator, UpdateOperator},
2528
precedence::Precedence,
26-
symbol::SymbolId,
2729
};
2830

2931
pub use crate::{
@@ -425,19 +427,25 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
425427
}
426428
}
427429

428-
#[allow(clippy::needless_pass_by_value)]
429-
fn print_symbol(&mut self, span: Span, symbol_id: Option<SymbolId>, fallback: &str) {
430+
fn get_identifier_reference_name(&self, reference: &IdentifierReference<'a>) -> &str {
430431
if let Some(mangler) = &self.mangler {
431-
if let Some(symbol_id) = symbol_id {
432+
if let Some(reference_id) = reference.reference_id.get() {
433+
if let Some(name) = mangler.get_reference_name(reference_id) {
434+
return name;
435+
}
436+
}
437+
}
438+
reference.name.as_str()
439+
}
440+
441+
fn get_binding_identifier_name(&self, ident: &BindingIdentifier<'a>) -> &str {
442+
if let Some(mangler) = &self.mangler {
443+
if let Some(symbol_id) = ident.symbol_id.get() {
432444
let name = mangler.get_symbol_name(symbol_id);
433-
let name = CompactStr::new(name);
434-
self.add_source_mapping_for_name(span, &name);
435-
self.print_str(&name);
436-
return;
445+
return name;
437446
}
438447
}
439-
self.add_source_mapping_for_name(span, fallback);
440-
self.print_str(fallback);
448+
ident.name.as_str()
441449
}
442450

443451
fn print_space_before_operator(&mut self, next: Operator) {

crates/oxc_codegen/tests/integration/unit.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ fn for_stmt() {
126126
// );
127127
}
128128

129+
#[test]
130+
fn shorthand() {
131+
test("let _ = { x }", "let _ = {x};\n");
132+
test("let { x } = y", "let { x } = y;\n");
133+
test("({ x } = y)", "({x} = y);\n");
134+
}
135+
129136
#[test]
130137
fn unicode_escape() {
131138
test("console.log('你好');", "console.log('你好');\n");

crates/oxc_isolated_declarations/tests/snapshots/function-parameters.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ input_file: crates/oxc_isolated_declarations/tests/fixtures/function-parameters.
77
export declare function fnDeclGood(p?: T, rParam?: string): void;
88
export declare function fnDeclGood2(p?: T, rParam?: number): void;
99
export declare function fooGood([a, b]?: any[]): number;
10-
export declare const fooGood2: ({ a: a, b: b }?: object) => number;
11-
export declare function fooGood3({ a: a, b: [{ c: c }] }: object): void;
10+
export declare const fooGood2: ({ a, b }?: object) => number;
11+
export declare function fooGood3({ a, b: [{ c }] }: object): void;
1212
export declare function fnDeclBad<T>(p: T, rParam: T, r2: T): void;
1313
export declare function fnDeclBad2<T>(p: T, r2: T): void;
1414
export declare function fnDeclBad3<T>(p: T, rParam?: T, r2: T): void;

crates/oxc_mangler/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,10 @@ oxc_ast = { workspace = true }
2626
oxc_semantic = { workspace = true }
2727
oxc_index = { workspace = true }
2828
itertools = { workspace = true }
29+
30+
[dev-dependencies]
31+
oxc_codegen = { workspace = true }
32+
oxc_parser = { workspace = true }
33+
oxc_span = { workspace = true }
34+
oxc_allocator = { workspace = true }
35+
insta = { workspace = true }
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
mod tester;
2+
3+
use std::fmt::Write;
4+
5+
use tester::mangle;
6+
7+
#[test]
8+
fn mangler() {
9+
let cases = [
10+
"function foo(a) {a}",
11+
"function foo(a) { let _ = { x } }",
12+
"function foo(a) { let { x } = y }",
13+
"var x; function foo(a) { ({ x } = y) }",
14+
];
15+
16+
let snapshot = cases.into_iter().fold(String::new(), |mut w, case| {
17+
write!(w, "{case}\n{}\n", mangle(case)).unwrap();
18+
w
19+
});
20+
21+
insta::with_settings!({ prepend_module_to_snapshot => false, omit_expression => true }, {
22+
insta::assert_snapshot!("mangler", snapshot);
23+
});
24+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
source: crates/oxc_mangler/tests/integration/main.rs
3+
---
4+
function foo(a) {a}
5+
function a(b) {
6+
b;
7+
}
8+
9+
function foo(a) { let _ = { x } }
10+
function a(b) {
11+
let c = {x};
12+
}
13+
14+
function foo(a) { let { x } = y }
15+
function a(b) {
16+
let { x: c } = y;
17+
}
18+
19+
var x; function foo(a) { ({ x } = y) }
20+
var a;
21+
function b(c) {
22+
({x: a} = y);
23+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use oxc_allocator::Allocator;
2+
use oxc_codegen::CodeGenerator;
3+
use oxc_mangler::ManglerBuilder;
4+
use oxc_parser::Parser;
5+
use oxc_span::SourceType;
6+
7+
pub fn mangle(source_text: &str) -> String {
8+
let allocator = Allocator::default();
9+
let source_type = SourceType::default().with_module(true);
10+
let ret = Parser::new(&allocator, source_text, source_type).parse();
11+
let program = ret.program;
12+
let mangler = ManglerBuilder::default().build(&program);
13+
CodeGenerator::new().with_mangler(Some(mangler)).build(&program).source_text
14+
}

tasks/coverage/codegen_sourcemap.snap

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -714,15 +714,11 @@ Invalid Character `[`
714714
(160:1-162:0) "};\n" --> (90:44-91:2) "e};\n\t"
715715
(162:0-162:4) "\nvar" --> (91:2-91:6) "\tvar"
716716
(162:4-162:27) " ReactSharedInternals =" --> (91:6-91:29) " ReactSharedInternals ="
717-
(162:27-163:2) " {\n " --> (91:29-92:3) " {\n\t\t"
718-
(163:2-163:26) " ReactCurrentDispatcher:" --> (92:3-92:27) "\tReactCurrentDispatcher:"
719-
(163:26-164:2) " ReactCurrentDispatcher,\n " --> (92:27-93:3) " ReactCurrentDispatcher,\n\t\t"
720-
(164:2-164:27) " ReactCurrentBatchConfig:" --> (93:3-93:28) "\tReactCurrentBatchConfig:"
721-
(164:27-165:2) " ReactCurrentBatchConfig,\n " --> (93:28-94:3) " ReactCurrentBatchConfig,\n\t\t"
722-
(165:2-165:21) " ReactCurrentOwner:" --> (94:3-94:22) "\tReactCurrentOwner:"
723-
(165:21-166:2) " ReactCurrentOwner,\n " --> (94:22-95:3) " ReactCurrentOwner,\n\t\t"
724-
(166:2-166:24) " IsSomeRendererActing:" --> (95:3-95:25) "\tIsSomeRendererActing:"
725-
(166:24-168:2) " IsSomeRendererActing,\n // Used by renderers to avoid bundling object-assign twice in UMD bundles:\n " --> (95:25-96:3) " IsSomeRendererActing,\n\t\t"
717+
(162:27-163:26) " {\n ReactCurrentDispatcher:" --> (91:29-92:3) " {\n\t\t"
718+
(163:26-164:27) " ReactCurrentDispatcher,\n ReactCurrentBatchConfig:" --> (92:3-93:3) "\tReactCurrentDispatcher,\n\t\t"
719+
(164:27-165:21) " ReactCurrentBatchConfig,\n ReactCurrentOwner:" --> (93:3-94:3) "\tReactCurrentBatchConfig,\n\t\t"
720+
(165:21-166:24) " ReactCurrentOwner,\n IsSomeRendererActing:" --> (94:3-95:3) "\tReactCurrentOwner,\n\t\t"
721+
(166:24-168:2) " IsSomeRendererActing,\n // Used by renderers to avoid bundling object-assign twice in UMD bundles:\n " --> (95:3-96:3) "\tIsSomeRendererActing,\n\t\t"
726722
(168:2-168:10) " assign:" --> (96:3-96:11) "\tassign:"
727723
(168:10-169:1) " _assign\n" --> (96:11-97:2) " _assign\n\t"
728724
(169:1-171:0) "};\n" --> (97:2-98:2) "\t};\n\t"
@@ -1593,15 +1589,11 @@ Invalid Character `[`
15931589
(649:6-649:16) " element =" --> (347:7-347:17) " element ="
15941590
(649:16-651:4) " {\n // This tag allows us to uniquely identify this as a React Element\n " --> (347:17-348:4) " {\n\t\t\t"
15951591
(651:4-651:14) " $$typeof:" --> (348:4-348:14) "\t$$typeof:"
1596-
(651:14-653:4) " REACT_ELEMENT_TYPE,\n // Built-in properties that belong on the element\n " --> (348:14-349:4) " REACT_ELEMENT_TYPE,\n\t\t\t"
1597-
(653:4-653:10) " type:" --> (349:4-349:10) "\ttype:"
1598-
(653:10-654:4) " type,\n " --> (349:10-350:4) " type,\n\t\t\t"
1599-
(654:4-654:9) " key:" --> (350:4-350:9) "\tkey:"
1600-
(654:9-655:4) " key,\n " --> (350:9-351:4) " key,\n\t\t\t"
1601-
(655:4-655:9) " ref:" --> (351:4-351:9) "\tref:"
1602-
(655:9-656:4) " ref,\n " --> (351:9-352:4) " ref,\n\t\t\t"
1603-
(656:4-656:11) " props:" --> (352:4-352:11) "\tprops:"
1604-
(656:11-658:4) " props,\n // Record the component responsible for creating this element.\n " --> (352:11-353:4) " props,\n\t\t\t"
1592+
(651:14-653:10) " REACT_ELEMENT_TYPE,\n // Built-in properties that belong on the element\n type:" --> (348:14-349:4) " REACT_ELEMENT_TYPE,\n\t\t\t"
1593+
(653:10-654:9) " type,\n key:" --> (349:4-350:4) "\ttype,\n\t\t\t"
1594+
(654:9-655:9) " key,\n ref:" --> (350:4-351:4) "\tkey,\n\t\t\t"
1595+
(655:9-656:11) " ref,\n props:" --> (351:4-352:4) "\tref,\n\t\t\t"
1596+
(656:11-658:4) " props,\n // Record the component responsible for creating this element.\n " --> (352:4-353:4) "\tprops,\n\t\t\t"
16051597
(658:4-658:12) " _owner:" --> (353:4-353:12) "\t_owner:"
16061598
(658:12-659:3) " owner\n " --> (353:12-354:3) " owner\n\t\t"
16071599
(659:3-661:2) "};\n\n " --> (354:3-355:3) "\t};\n\t\t"
@@ -3096,9 +3088,8 @@ Invalid Character `[`
30963088
(1387:6-1387:20) " elementType =" --> (828:7-828:21) " elementType ="
30973089
(1387:20-1388:4) " {\n " --> (828:21-829:4) " {\n\t\t\t"
30983090
(1388:4-1388:14) " $$typeof:" --> (829:4-829:14) "\t$$typeof:"
3099-
(1388:14-1389:4) " REACT_FORWARD_REF_TYPE,\n " --> (829:14-830:4) " REACT_FORWARD_REF_TYPE,\n\t\t\t"
3100-
(1389:4-1389:12) " render:" --> (830:4-830:12) "\trender:"
3101-
(1389:12-1390:3) " render\n " --> (830:12-831:3) " render\n\t\t"
3091+
(1388:14-1389:12) " REACT_FORWARD_REF_TYPE,\n render:" --> (829:14-830:4) " REACT_FORWARD_REF_TYPE,\n\t\t\t"
3092+
(1389:12-1390:3) " render\n " --> (830:4-831:3) "\trender\n\t\t"
31023093
(1390:3-1392:2) "};\n\n " --> (831:3-832:3) "\t};\n\t\t"
31033094
(1392:2-1393:4) " {\n " --> (832:3-833:4) "\t{\n\t\t\t"
31043095
(1393:4-1393:8) " var" --> (833:4-833:8) "\tvar"
@@ -3244,9 +3235,8 @@ Invalid Character `[`
32443235
(1443:6-1443:20) " elementType =" --> (871:7-871:21) " elementType ="
32453236
(1443:20-1444:4) " {\n " --> (871:21-872:4) " {\n\t\t\t"
32463237
(1444:4-1444:14) " $$typeof:" --> (872:4-872:14) "\t$$typeof:"
3247-
(1444:14-1445:4) " REACT_MEMO_TYPE,\n " --> (872:14-873:4) " REACT_MEMO_TYPE,\n\t\t\t"
3248-
(1445:4-1445:10) " type:" --> (873:4-873:10) "\ttype:"
3249-
(1445:10-1446:4) " type,\n " --> (873:10-874:4) " type,\n\t\t\t"
3238+
(1444:14-1445:10) " REACT_MEMO_TYPE,\n type:" --> (872:14-873:4) " REACT_MEMO_TYPE,\n\t\t\t"
3239+
(1445:10-1446:4) " type,\n " --> (873:4-874:4) "\ttype,\n\t\t\t"
32503240
(1446:4-1446:13) " compare:" --> (874:4-874:13) "\tcompare:"
32513241
(1446:13-1446:25) " compare ===" --> (874:13-874:25) " compare ==="
32523242
(1446:25-1446:37) " undefined ?" --> (874:25-874:37) " undefined ?"
@@ -5179,9 +5169,8 @@ Invalid Character `[`
51795169
(2301:2-2301:11) " forEach:" --> (1456:3-1456:12) "\tforEach:"
51805170
(2301:11-2302:2) " forEachChildren,\n " --> (1456:12-1457:3) " forEachChildren,\n\t\t"
51815171
(2302:2-2302:9) " count:" --> (1457:3-1457:10) "\tcount:"
5182-
(2302:9-2303:2) " countChildren,\n " --> (1457:10-1458:3) " countChildren,\n\t\t"
5183-
(2303:2-2303:11) " toArray:" --> (1458:3-1458:12) "\ttoArray:"
5184-
(2303:11-2304:2) " toArray,\n " --> (1458:12-1459:3) " toArray,\n\t\t"
5172+
(2302:9-2303:11) " countChildren,\n toArray:" --> (1457:10-1458:3) " countChildren,\n\t\t"
5173+
(2303:11-2304:2) " toArray,\n " --> (1458:3-1459:3) "\ttoArray,\n\t\t"
51855174
(2304:2-2304:8) " only:" --> (1459:3-1459:9) "\tonly:"
51865175
(2304:8-2305:1) " onlyChild\n" --> (1459:9-1460:2) " onlyChild\n\t"
51875176
(2305:1-2307:0) "};\n" --> (1460:2-1461:0) "\t};"

0 commit comments

Comments
 (0)