Skip to content

Commit

Permalink
feat(codegen): print shorthand for all { x } variants
Browse files Browse the repository at this point in the history
closes #4340
  • Loading branch information
Boshen committed Jul 20, 2024
1 parent d345b84 commit 4e7dadb
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 36 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 53 additions & 22 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1079,18 +1079,10 @@ impl<'a, const MINIFY: bool> GenExpr<MINIFY> for ParenthesizedExpression<'a> {

impl<'a, const MINIFY: bool> Gen<MINIFY> for IdentifierReference<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) {
if let Some(mangler) = &p.mangler {
if let Some(reference_id) = self.reference_id.get() {
if let Some(name) = mangler.get_reference_name(reference_id) {
let name = CompactStr::new(name);
p.add_source_mapping_for_name(self.span, &name);
p.print_str(&name);
return;
}
}
}
p.add_source_mapping_for_name(self.span, &self.name);
p.print_str(&self.name);
let name = p.get_identifier_reference_name(self);
let name = CompactStr::new(name);
p.add_source_mapping_for_name(self.span, &name);
p.print_str(&name);
}
}

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

impl<'a, const MINIFY: bool> Gen<MINIFY> for BindingIdentifier<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) {
p.print_symbol(self.span, self.symbol_id.get(), self.name.as_str());
let name = p.get_binding_identifier_name(self);
let name = CompactStr::new(name);
p.add_source_mapping_for_name(self.span, &name);
p.print_str(&name);
}
}

Expand Down Expand Up @@ -1514,7 +1509,6 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ObjectPropertyKind<'a> {
}
}

// TODO: only print shorthand if key value are the same.
impl<'a, const MINIFY: bool> Gen<MINIFY> for ObjectProperty<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
if let Expression::FunctionExpression(func) = &self.value {
Expand Down Expand Up @@ -1559,15 +1553,29 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ObjectProperty<'a> {
return;
}
}

let mut shorthand = false;
if let PropertyKey::StaticIdentifier(key) = &self.key {
if let Expression::Identifier(ident) = &self.value {
if key.name == p.get_identifier_reference_name(ident) {
shorthand = true;
}
}
}

if self.computed {
p.print_char(b'[');
}
self.key.gen(p, ctx);
if !shorthand {
self.key.gen(p, ctx);
}
if self.computed {
p.print_char(b']');
}
p.print_colon();
p.print_soft_space();
if !shorthand {
p.print_colon();
p.print_soft_space();
}
self.value.gen_expr(p, Precedence::Assign, Context::default());
}
}
Expand Down Expand Up @@ -1925,7 +1933,17 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for AssignmentTargetProperty<'a> {

impl<'a, const MINIFY: bool> Gen<MINIFY> for AssignmentTargetPropertyIdentifier<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
self.binding.gen(p, ctx);
let ident_name = p.get_identifier_reference_name(&self.binding);
if ident_name == self.binding.name.as_str() {
self.binding.gen(p, ctx);
} else {
// `({x: a} = y);`
let ident_name = CompactStr::new(ident_name);
p.print_str(self.binding.name.as_str());
p.print_colon();
p.print_soft_space();
p.print_str(ident_name.as_str());
}
if let Some(expr) = &self.init {
p.print_soft_space();
p.print_equal();
Expand Down Expand Up @@ -2535,19 +2553,32 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ObjectPattern<'a> {
}
}

// TODO: only print shorthand if key value are the same.
impl<'a, const MINIFY: bool> Gen<MINIFY> for BindingProperty<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
p.add_source_mapping(self.span.start);
if self.computed {
p.print_char(b'[');
}
self.key.gen(p, ctx);

let mut shorthand = false;
if let PropertyKey::StaticIdentifier(key) = &self.key {
if let BindingPatternKind::BindingIdentifier(ident) = &self.value.kind {
if key.name == p.get_binding_identifier_name(ident) {
shorthand = true;
}
}
}

if !shorthand {
self.key.gen(p, ctx);
}
if self.computed {
p.print_char(b']');
}
p.print_colon();
p.print_soft_space();
if !shorthand {
p.print_colon();
p.print_soft_space();
}
self.value.gen(p, ctx);
}
}
Expand Down
32 changes: 20 additions & 12 deletions crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@ use std::{borrow::Cow, ops::Range};
use rustc_hash::FxHashMap;

use oxc_ast::{
ast::{BlockStatement, Directive, Expression, Program, Statement},
ast::{
BindingIdentifier, BlockStatement, Directive, Expression, IdentifierReference, Program,
Statement,
},
Comment, Trivias,
};
use oxc_mangler::Mangler;
use oxc_span::{CompactStr, Span};
use oxc_span::Span;
use oxc_syntax::{
identifier::is_identifier_part,
operator::{BinaryOperator, UnaryOperator, UpdateOperator},
precedence::Precedence,
symbol::SymbolId,
};

pub use crate::{
Expand Down Expand Up @@ -425,19 +427,25 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
}
}

#[allow(clippy::needless_pass_by_value)]
fn print_symbol(&mut self, span: Span, symbol_id: Option<SymbolId>, fallback: &str) {
fn get_identifier_reference_name(&self, reference: &IdentifierReference<'a>) -> &str {
if let Some(mangler) = &self.mangler {
if let Some(symbol_id) = symbol_id {
if let Some(reference_id) = reference.reference_id.get() {
if let Some(name) = mangler.get_reference_name(reference_id) {
return name;
}
}
}
reference.name.as_str()
}

fn get_binding_identifier_name(&self, ident: &BindingIdentifier<'a>) -> &str {
if let Some(mangler) = &self.mangler {
if let Some(symbol_id) = ident.symbol_id.get() {
let name = mangler.get_symbol_name(symbol_id);
let name = CompactStr::new(name);
self.add_source_mapping_for_name(span, &name);
self.print_str(&name);
return;
return name;
}
}
self.add_source_mapping_for_name(span, fallback);
self.print_str(fallback);
ident.name.as_str()
}

fn print_space_before_operator(&mut self, next: Operator) {
Expand Down
7 changes: 7 additions & 0 deletions crates/oxc_codegen/tests/integration/unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ fn for_stmt() {
// );
}

#[test]
fn shorthand() {
test("let _ = { x }", "let _ = {x};\n");
test("let { x } = y", "let { x } = y;\n");
test("({ x } = y)", "({x} = y);\n");
}

#[test]
fn unicode_escape() {
test("console.log('你好');", "console.log('你好');\n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ input_file: crates/oxc_isolated_declarations/tests/fixtures/function-parameters.
export declare function fnDeclGood(p?: T, rParam?: string): void;
export declare function fnDeclGood2(p?: T, rParam?: number): void;
export declare function fooGood([a, b]?: any[]): number;
export declare const fooGood2: ({ a: a, b: b }?: object) => number;
export declare function fooGood3({ a: a, b: [{ c: c }] }: object): void;
export declare const fooGood2: ({ a, b }?: object) => number;
export declare function fooGood3({ a, b: [{ c }] }: object): void;
export declare function fnDeclBad<T>(p: T, rParam: T, r2: T): void;
export declare function fnDeclBad2<T>(p: T, r2: T): void;
export declare function fnDeclBad3<T>(p: T, rParam?: T, r2: T): void;
Expand Down
7 changes: 7 additions & 0 deletions crates/oxc_mangler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,10 @@ oxc_ast = { workspace = true }
oxc_semantic = { workspace = true }
oxc_index = { workspace = true }
itertools = { workspace = true }

[dev-dependencies]
oxc_codegen = { workspace = true }
oxc_parser = { workspace = true }
oxc_span = { workspace = true }
oxc_allocator = { workspace = true }
insta = { workspace = true }
24 changes: 24 additions & 0 deletions crates/oxc_mangler/tests/integration/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
mod tester;

use std::fmt::Write;

use tester::mangle;

#[test]
fn mangler() {
let cases = [
"function foo(a) {a}",
"function foo(a) { let _ = { x } }",
"function foo(a) { let { x } = y }",
"var x; function foo(a) { ({ x } = y) }",
];

let snapshot = cases.into_iter().fold(String::new(), |mut w, case| {
write!(w, "{case}\n{}\n", mangle(case)).unwrap();
w
});

insta::with_settings!({ prepend_module_to_snapshot => false, omit_expression => true }, {
insta::assert_snapshot!("mangler", snapshot);
});
}
23 changes: 23 additions & 0 deletions crates/oxc_mangler/tests/integration/snapshots/mangler.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
source: crates/oxc_mangler/tests/integration/main.rs
---
function foo(a) {a}
function a(b) {
b;
}

function foo(a) { let _ = { x } }
function a(b) {
let c = {x};
}

function foo(a) { let { x } = y }
function a(b) {
let { x: c } = y;
}

var x; function foo(a) { ({ x } = y) }
var a;
function b(c) {
({x: a} = y);
}
14 changes: 14 additions & 0 deletions crates/oxc_mangler/tests/integration/tester.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use oxc_allocator::Allocator;
use oxc_codegen::CodeGenerator;
use oxc_mangler::ManglerBuilder;
use oxc_parser::Parser;
use oxc_span::SourceType;

pub fn mangle(source_text: &str) -> String {
let allocator = Allocator::default();
let source_type = SourceType::default().with_module(true);
let ret = Parser::new(&allocator, source_text, source_type).parse();
let program = ret.program;
let mangler = ManglerBuilder::default().build(&program);
CodeGenerator::new().with_mangler(Some(mangler)).build(&program).source_text
}

0 comments on commit 4e7dadb

Please sign in to comment.