use oxc_allocator::{Box, Vec}; #[allow(clippy::wildcard_imports)] use oxc_ast::ast::*; use oxc_syntax::{ identifier::{LS, PS}, operator::{BinaryOperator, UnaryOperator}, precedence::{GetPrecedence, Precedence}, NumberBase, }; use super::{Codegen, Context, Operator, Separator}; pub trait Gen { fn gen(&self, _p: &mut Codegen<{ MINIFY }>, _ctx: Context) {} } pub trait GenExpr { fn gen_expr(&self, _p: &mut Codegen<{ MINIFY }>, _precedence: Precedence, _ctx: Context) {} } impl<'a, const MINIFY: bool, T> Gen for Box<'a, T> where T: Gen, { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { (**self).gen(p, ctx); } } impl<'a, const MINIFY: bool, T> GenExpr for Box<'a, T> where T: GenExpr, { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { (**self).gen_expr(p, precedence, ctx); } } impl<'a, const MINIFY: bool> Gen for Program<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if let Some(hashbang) = &self.hashbang { hashbang.gen(p, ctx); } print_directives_and_statements(p, &self.directives, &self.body, ctx); } } fn print_directives_and_statements( p: &mut Codegen<{ MINIFY }>, directives: &[Directive], statements: &[Statement<'_>], ctx: Context, ) { print_directives_and_statements_with_semicolon_order(p, directives, statements, ctx, false); } fn print_directives_and_statements_with_semicolon_order( p: &mut Codegen<{ MINIFY }>, directives: &[Directive], statements: &[Statement<'_>], ctx: Context, print_semicolon_first: bool, ) { if directives.is_empty() { if let Some(Statement::ExpressionStatement(s)) = statements.first() { if matches!(s.expression.get_inner_expression(), Expression::StringLiteral(_)) { p.print_semicolon(); } } } else { for directive in directives { directive.gen(p, ctx); } } for stmt in statements { if print_semicolon_first { p.print_semicolon_if_needed(); stmt.gen(p, ctx); } else { stmt.gen(p, ctx); p.print_semicolon_if_needed(); } } } impl Gen for Hashbang { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(b"#!"); p.print_str(self.value.as_bytes()); } } impl Gen for Directive { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { // Use the string value instead of the raw self.directive because it can cannot escaped values. print_str(self.expression.value.as_str(), p); p.print_semicolon(); } } impl<'a, const MINIFY: bool> Gen for Statement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::BlockStatement(stmt) => stmt.gen(p, ctx), Self::BreakStatement(stmt) => stmt.gen(p, ctx), Self::ContinueStatement(stmt) => stmt.gen(p, ctx), Self::DebuggerStatement(stmt) => stmt.gen(p, ctx), Self::Declaration(decl) => decl.gen(p, ctx), Self::DoWhileStatement(stmt) => stmt.gen(p, ctx), Self::EmptyStatement(stmt) => stmt.gen(p, ctx), Self::ExpressionStatement(stmt) => stmt.gen(p, ctx), Self::ForInStatement(stmt) => stmt.gen(p, ctx), Self::ForOfStatement(stmt) => stmt.gen(p, ctx), Self::ForStatement(stmt) => stmt.gen(p, ctx), Self::IfStatement(stmt) => stmt.gen(p, ctx), Self::LabeledStatement(stmt) => stmt.gen(p, ctx), Self::ModuleDeclaration(decl) => decl.gen(p, ctx), Self::ReturnStatement(stmt) => stmt.gen(p, ctx), Self::SwitchStatement(stmt) => stmt.gen(p, ctx), Self::ThrowStatement(stmt) => stmt.gen(p, ctx), Self::TryStatement(stmt) => stmt.gen(p, ctx), Self::WhileStatement(stmt) => stmt.gen(p, ctx), Self::WithStatement(stmt) => stmt.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for ExpressionStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_indent(); p.start_of_stmt = p.code_len(); self.expression.gen_expr(p, Precedence::lowest(), Context::default()); if self.expression.is_specific_id("let") { p.print_semicolon(); } else { p.print_semicolon_after_statement(); } } } impl<'a, const MINIFY: bool> Gen for IfStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); print_if(self, p, ctx); } } fn print_if( if_stmt: &IfStatement<'_>, p: &mut Codegen<{ MINIFY }>, ctx: Context, ) { p.print_str(b"if"); p.print_soft_space(); p.print(b'('); if_stmt.test.gen_expr(p, Precedence::lowest(), Context::default()); p.print(b')'); p.print_soft_space(); match &if_stmt.consequent { Statement::BlockStatement(block) => { p.print_block1(block, ctx); } stmt if wrap_to_avoid_ambiguous_else(stmt) => { p.print_block_start(); stmt.gen(p, ctx); p.needs_semicolon = false; p.print_block_end(); } stmt => { stmt.gen(p, ctx); } } if if_stmt.alternate.is_some() { p.print_soft_space(); } else { p.print_soft_newline(); } if let Some(alternate) = if_stmt.alternate.as_ref() { p.print_semicolon_if_needed(); p.print_space_before_identifier(); p.print_str(b"else "); match alternate { Statement::BlockStatement(block) => { p.print_block1(block, ctx); p.print_soft_newline(); } Statement::IfStatement(if_stmt) => { print_if(if_stmt, p, ctx); } _ => { p.print_soft_newline(); p.indent(); alternate.gen(p, ctx); p.dedent(); } } } } // fn wrap_to_avoid_ambiguous_else(stmt: &Statement) -> bool { let mut current = stmt; loop { current = match current { Statement::IfStatement(Box(IfStatement { alternate, .. })) => { if let Some(stmt) = &alternate { stmt } else { return true; } } Statement::ForStatement(Box(ForStatement { body, .. })) | Statement::ForOfStatement(Box(ForOfStatement { body, .. })) | Statement::ForInStatement(Box(ForInStatement { body, .. })) | Statement::WhileStatement(Box(WhileStatement { body, .. })) | Statement::WithStatement(Box(WithStatement { body, .. })) | Statement::LabeledStatement(Box(LabeledStatement { body, .. })) => body, _ => return false, } } } impl<'a, const MINIFY: bool> Gen for BlockStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); p.print_block1(self, ctx); p.print_soft_newline(); } } impl<'a, const MINIFY: bool> Gen for ForStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); p.print_str(b"for"); p.print_soft_space(); p.print(b'('); if let Some(init) = self.init.as_ref() { let ctx = Context::empty(); match init { ForStatementInit::UsingDeclaration(decl) => decl.gen(p, ctx), ForStatementInit::Expression(expr) => { expr.gen_expr(p, Precedence::lowest(), ctx); } ForStatementInit::VariableDeclaration(var) => var.gen(p, ctx), } } p.print_semicolon(); p.print_soft_space(); if let Some(test) = self.test.as_ref() { test.gen_expr(p, Precedence::lowest(), Context::default()); } p.print_semicolon(); p.print_soft_space(); if let Some(update) = self.update.as_ref() { update.gen_expr(p, Precedence::lowest(), Context::default()); } p.print(b')'); self.body.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for ForInStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); p.print_str(b"for"); p.print_soft_space(); p.print(b'('); self.left.gen(p, ctx); p.print_soft_space(); p.print_space_before_identifier(); p.print_str(b"in"); p.print_hard_space(); self.right.gen_expr(p, Precedence::lowest(), Context::default()); p.print(b')'); p.print_soft_space(); self.body.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for ForOfStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); p.print_str(b"for"); p.print_soft_space(); if self.r#await { p.print_str(b" await"); } p.print(b'('); self.left.gen(p, ctx); p.print_soft_space(); p.print_space_before_identifier(); p.print_str(b"of "); p.print_soft_space(); self.right.gen_expr(p, Precedence::Assign, Context::default()); p.print(b')'); p.print_soft_space(); self.body.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for ForStatementLeft<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match &self { ForStatementLeft::UsingDeclaration(var) => var.gen(p, ctx), ForStatementLeft::VariableDeclaration(var) => var.gen(p, ctx), ForStatementLeft::AssignmentTarget(target) => target.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for WhileStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); p.print_str(b"while"); p.print(b'('); self.test.gen_expr(p, Precedence::lowest(), Context::default()); p.print(b')'); self.body.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for DoWhileStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); p.print_str(b"do "); if let Statement::BlockStatement(block) = &self.body { p.print_block1(block, ctx); } else { p.print_soft_newline(); p.indent(); self.body.gen(p, ctx); p.print_semicolon_if_needed(); p.dedent(); p.print_indent(); } p.print_str(b"while"); p.print(b'('); self.test.gen_expr(p, Precedence::lowest(), Context::default()); p.print(b')'); p.print_semicolon_after_statement(); } } impl Gen for EmptyStatement { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_indent(); p.print_semicolon(); } } impl Gen for ContinueStatement { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); p.print_str(b"continue"); if let Some(label) = &self.label { p.print_hard_space(); label.gen(p, ctx); } p.print_semicolon_after_statement(); } } impl Gen for BreakStatement { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); p.print_str(b"break"); if let Some(label) = &self.label { p.print_hard_space(); label.gen(p, ctx); } p.print_semicolon_after_statement(); } } impl<'a, const MINIFY: bool> Gen for SwitchStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); p.print_str(b"switch"); p.print(b'('); self.discriminant.gen_expr(p, Precedence::lowest(), Context::default()); p.print(b')'); p.print_block_start(); for case in &self.cases { case.gen(p, ctx); } p.print_block_end(); p.print_soft_newline(); p.needs_semicolon = false; } } impl<'a, const MINIFY: bool> Gen for SwitchCase<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_semicolon_if_needed(); p.print_indent(); match &self.test { Some(test) => { p.print_str(b"case"); p.print_hard_space(); test.gen_expr(p, Precedence::lowest(), Context::default()); } None => p.print_str(b"default"), } p.print_colon(); p.print_soft_newline(); p.indent(); for item in &self.consequent { p.print_semicolon_if_needed(); item.gen(p, ctx); } p.dedent(); } } impl<'a, const MINIFY: bool> Gen for ReturnStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_indent(); p.print_str(b"return"); if let Some(arg) = &self.argument { p.print_hard_space(); arg.gen_expr(p, Precedence::lowest(), Context::default()); } p.print_semicolon_after_statement(); } } impl<'a, const MINIFY: bool> Gen for LabeledStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); self.label.gen(p, ctx); p.print_colon(); self.body.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for TryStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); p.print_str(b"try"); p.print_block1(&self.block, ctx); if let Some(handler) = &self.handler { p.print_str(b"catch"); if let Some(param) = &handler.param { p.print_str(b"("); param.gen(p, ctx); p.print_str(b")"); } p.print_block1(&handler.body, ctx); } if let Some(finalizer) = &self.finalizer { p.print_str(b"finally"); p.print_block1(finalizer, ctx); } } } impl<'a, const MINIFY: bool> Gen for ThrowStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_indent(); p.print_str(b"throw "); self.argument.gen_expr(p, Precedence::lowest(), Context::default()); p.print_semicolon_after_statement(); } } impl<'a, const MINIFY: bool> Gen for WithStatement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_indent(); p.print_str(b"with"); p.print(b'('); self.object.gen_expr(p, Precedence::lowest(), Context::default()); p.print(b')'); self.body.gen(p, ctx); } } impl Gen for DebuggerStatement { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_indent(); p.print_str(b"debugger"); p.print_semicolon_after_statement(); } } impl<'a, const MINIFY: bool> Gen for ModuleDeclaration<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::ImportDeclaration(decl) => decl.gen(p, ctx), Self::ExportAllDeclaration(decl) => decl.gen(p, ctx), Self::ExportDefaultDeclaration(decl) => decl.gen(p, ctx), Self::ExportNamedDeclaration(decl) => decl.gen(p, ctx), _ => p.needs_semicolon = false, } } } impl<'a, const MINIFY: bool> Gen for Declaration<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::VariableDeclaration(decl) => { // Codegen is not intended to be used as a code formatting tool, so we need filter out the TypeScript syntax here. if !decl.is_typescript_syntax() { p.print_indent(); decl.gen(p, ctx); p.print_semicolon_after_statement(); } } Self::FunctionDeclaration(decl) => { if !decl.is_typescript_syntax() { p.print_indent(); p.print_space_before_identifier(); decl.gen(p, ctx); p.print_soft_newline(); } } Self::ClassDeclaration(decl) => { if !decl.is_typescript_syntax() { p.print_indent(); p.print_space_before_identifier(); decl.gen(p, ctx); p.print_soft_newline(); } } Self::UsingDeclaration(declaration) => { p.print_space_before_identifier(); declaration.gen(p, ctx); p.print_soft_newline(); } _ => {} } } } impl<'a, const MINIFY: bool> Gen for UsingDeclaration<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if self.is_await { p.print_str(b"await"); p.print_soft_space(); } p.print_str(b"using"); p.print_soft_space(); p.print_list(&self.declarations, ctx); p.print_semicolon_after_statement(); } } impl<'a, const MINIFY: bool> Gen for VariableDeclaration<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_str(match self.kind { VariableDeclarationKind::Const => b"const", VariableDeclarationKind::Let => b"let", VariableDeclarationKind::Var => b"var", }); if !self.declarations.is_empty() { p.print_hard_space(); } p.print_list(&self.declarations, ctx); } } impl<'a, const MINIFY: bool> Gen for VariableDeclarator<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.id.gen(p, ctx); if let Some(init) = &self.init { p.print_soft_space(); p.print_equal(); p.print_soft_space(); init.gen_expr(p, Precedence::Assign, ctx); } } } impl<'a, const MINIFY: bool> Gen for Function<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if self.is_typescript_syntax() { return; } let n = p.code_len(); let wrap = self.is_expression() && (p.start_of_stmt == n || p.start_of_default_export == n); p.wrap(wrap, |p| { if self.r#async { p.print_str(b"async "); } p.print_str(b"function"); if self.generator { p.print(b'*'); p.print_soft_space(); } if let Some(id) = &self.id { p.print_space_before_identifier(); id.gen(p, ctx); } p.print(b'('); self.params.gen(p, ctx); p.print(b')'); p.print_soft_space(); if let Some(body) = &self.body { body.gen(p, ctx); } }); } } impl<'a, const MINIFY: bool> Gen for FunctionBody<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_block_start(); print_directives_and_statements_with_semicolon_order( p, &self.directives, &self.statements, ctx, true, ); p.print_block_end(); p.needs_semicolon = false; } } impl<'a, const MINIFY: bool> Gen for FormalParameter<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.decorators.gen(p, ctx); self.pattern.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for FormalParameters<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_list(&self.items, ctx); if let Some(rest) = &self.rest { if !self.items.is_empty() { p.print_comma(); } rest.gen(p, ctx); } } } impl<'a, const MINIFY: bool> Gen for ImportDeclaration<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_str(b"import "); if let Some(specifiers) = &self.specifiers { if specifiers.is_empty() { p.print(b'\''); p.print_str(self.source.value.as_bytes()); p.print(b'\''); self.with_clause.gen(p, ctx); p.print_semicolon_after_statement(); return; } let mut in_block = false; for (index, specifier) in specifiers.iter().enumerate() { match specifier { ImportDeclarationSpecifier::ImportDefaultSpecifier(spec) => { if in_block { p.print_str(b"},"); in_block = false; } else if index != 0 { p.print_comma(); } spec.local.gen(p, ctx); } ImportDeclarationSpecifier::ImportNamespaceSpecifier(spec) => { if in_block { p.print_str(b"},"); in_block = false; } else if index != 0 { p.print_comma(); } p.print_str(b"* as "); spec.local.gen(p, ctx); } ImportDeclarationSpecifier::ImportSpecifier(spec) => { if in_block { p.print_comma(); } else { if index != 0 { p.print_comma(); } in_block = true; p.print(b'{'); } let imported_name = match &spec.imported { ModuleExportName::Identifier(identifier) => { identifier.gen(p, ctx); identifier.name.as_bytes() } ModuleExportName::StringLiteral(literal) => { literal.gen(p, ctx); literal.value.as_bytes() } }; let local_name = spec.local.name.as_bytes(); if imported_name != local_name { p.print_str(b" as "); spec.local.gen(p, ctx); } } } } if in_block { p.print(b'}'); } p.print_str(b" from "); } self.source.gen(p, ctx); self.with_clause.gen(p, ctx); p.print_semicolon_after_statement(); } } impl<'a, const MINIFY: bool> Gen for Option> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if let Some(with_clause) = self { with_clause.gen(p, ctx); } } } impl<'a, const MINIFY: bool> Gen for WithClause<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.attributes_keyword.gen(p, ctx); p.print_block(&self.with_entries, Separator::Comma, ctx); } } impl Gen for ImportAttribute { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match &self.key { ImportAttributeKey::Identifier(identifier) => { p.print_str(identifier.name.as_bytes()); } ImportAttributeKey::StringLiteral(literal) => literal.gen(p, ctx), }; p.print_colon(); self.value.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for ExportNamedDeclaration<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if self.is_typescript_syntax() { return; } p.print_str(b"export "); match &self.declaration { Some(decl) => decl.gen(p, ctx), None => { p.print(b'{'); if !self.specifiers.is_empty() { p.print_soft_space(); p.print_list(&self.specifiers, ctx); p.print_soft_space(); } p.print(b'}'); if let Some(source) = &self.source { p.print_soft_space(); p.print_str(b"from"); p.print_soft_space(); source.gen(p, ctx); } p.needs_semicolon = true; } } } } impl Gen for ExportSpecifier { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.local.gen(p, ctx); if self.local.name() != self.exported.name() { p.print_str(b" as "); self.exported.gen(p, ctx); } } } impl Gen for ModuleExportName { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::Identifier(identifier) => { p.print_str(identifier.name.as_bytes()); } Self::StringLiteral(literal) => literal.gen(p, ctx), }; } } impl<'a, const MINIFY: bool> Gen for ExportAllDeclaration<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if self.is_typescript_syntax() { return; } p.print_str(b"export"); p.print(b'*'); if let Some(exported) = &self.exported { p.print_str(b"as "); exported.gen(p, ctx); } p.print_str(b" from"); self.source.gen(p, ctx); self.with_clause.gen(p, ctx); p.print_semicolon_after_statement(); } } impl<'a, const MINIFY: bool> Gen for ExportDefaultDeclaration<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if self.is_typescript_syntax() { return; } p.print_str(b"export default "); self.declaration.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for ExportDefaultDeclarationKind<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::Expression(expr) => { p.start_of_default_export = p.code_len(); expr.gen_expr(p, Precedence::Assign, Context::default()); p.print_semicolon_after_statement(); } Self::FunctionDeclaration(fun) => fun.gen(p, ctx), Self::ClassDeclaration(class) => { if !class.is_typescript_syntax() { class.gen(p, ctx); p.print_soft_newline(); } } _ => {} } } } impl<'a, const MINIFY: bool> GenExpr for Expression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { match self { Self::BooleanLiteral(lit) => lit.gen(p, ctx), Self::NullLiteral(lit) => lit.gen(p, ctx), Self::NumberLiteral(lit) => lit.gen(p, ctx), Self::BigintLiteral(lit) => lit.gen(p, ctx), Self::RegExpLiteral(lit) => lit.gen(p, ctx), Self::StringLiteral(lit) => lit.gen(p, ctx), Self::Identifier(ident) => ident.gen(p, ctx), Self::ThisExpression(expr) => expr.gen(p, ctx), Self::MemberExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::CallExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::ArrayExpression(expr) => expr.gen(p, ctx), Self::ObjectExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::FunctionExpression(expr) => expr.gen(p, ctx), Self::ArrowExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::YieldExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::UpdateExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::UnaryExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::BinaryExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::PrivateInExpression(expr) => expr.gen(p, ctx), Self::LogicalExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::ConditionalExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::AssignmentExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::SequenceExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::ImportExpression(expr) => expr.gen(p, ctx), Self::TemplateLiteral(literal) => literal.gen(p, ctx), Self::TaggedTemplateExpression(expr) => expr.gen(p, ctx), Self::Super(sup) => sup.gen(p, ctx), Self::AwaitExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::ChainExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::NewExpression(expr) => expr.gen_expr(p, precedence, ctx), Self::MetaProperty(expr) => expr.gen(p, ctx), Self::ClassExpression(expr) => expr.gen(p, ctx), Self::JSXElement(el) => el.gen(p, ctx), Self::JSXFragment(fragment) => fragment.gen(p, ctx), Self::ParenthesizedExpression(e) => e.expression.gen_expr(p, precedence, ctx), Self::TSAsExpression(e) => e.expression.gen_expr(p, precedence, ctx), Self::TSSatisfiesExpression(e) => e.expression.gen_expr(p, precedence, ctx), Self::TSTypeAssertion(e) => e.expression.gen_expr(p, precedence, ctx), Self::TSNonNullExpression(e) => e.expression.gen_expr(p, precedence, ctx), Self::TSInstantiationExpression(e) => e.expression.gen_expr(p, precedence, ctx), } } } impl Gen for IdentifierReference { 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) { // p.print_str(name.clone().as_bytes()); // return; // } // } // } p.print_str(self.name.as_bytes()); } } impl Gen for IdentifierName { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(self.name.as_bytes()); } } impl Gen for BindingIdentifier { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_symbol(self.symbol_id.get(), &self.name); } } impl Gen for LabelIdentifier { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(self.name.as_bytes()); } } impl Gen for BooleanLiteral { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(self.as_str().as_bytes()); } } impl Gen for NullLiteral { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_space_before_identifier(); p.print_str(b"null"); } } // Need a space before "." if it could be parsed as a decimal point. fn need_space_before_dot(bytes: &[u8], p: &mut Codegen<{ MINIFY }>) { if !bytes.iter().any(|&b| matches!(b, b'.' | b'e' | b'x')) { p.need_space_before_dot = p.code_len(); } } impl<'a, const MINIFY: bool> Gen for NumberLiteral<'a> { #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { if MINIFY || self.raw.is_empty() { p.print_space_before_identifier(); let abs_value = self.value.abs(); if self.value.is_sign_negative() { p.print_space_before_operator(Operator::Unary(UnaryOperator::UnaryNegation)); p.print_str(b"-"); } let result = if self.base == NumberBase::Float { print_non_negative_float(abs_value, p) } else { let value = abs_value as u64; // If integers less than 1000, we know that exponential notation will always be longer than // the integer representation. This is not the case for 1000 which is "1e3". if value < 1000 { format!("{value}") } else if (1_000_000_000_000..=0xFFFF_FFFF_FFFF_F800).contains(&value) { let hex = format!("{value:#x}"); let result = print_non_negative_float(abs_value, p); if hex.len() < result.len() { hex } else { result } } else { print_non_negative_float(abs_value, p) } }; let bytes = result.as_bytes(); p.print_str(bytes); need_space_before_dot(bytes, p); } else { let bytes = self.raw.as_bytes(); p.print_str(bytes); need_space_before_dot(bytes, p); }; } } // TODO: refactor this with less allocations fn print_non_negative_float(value: f64, _p: &Codegen<{ MINIFY }>) -> String { let mut result = value.to_string(); let chars = result.as_bytes(); let len = chars.len(); let dot = chars.iter().position(|&c| c == b'.'); let u8_to_string = |num: &[u8]| { // SAFETY: criteria of `from_utf8_unchecked`.are met. unsafe { String::from_utf8_unchecked(num.to_vec()) } }; if dot == Some(1) && chars[0] == b'0' { // Strip off the leading zero when minifying // "0.5" => ".5" let stripped_result = &chars[1..]; // after stripping the leading zero, the after dot position will be start from 1 let after_dot = 1; // Try using an exponent // "0.001" => "1e-3" if stripped_result[after_dot] == b'0' { let mut i = after_dot + 1; while stripped_result[i] == b'0' { i += 1; } let remaining = &stripped_result[i..]; let exponent = format!("-{}", remaining.len() - after_dot + i); // Only switch if it's actually shorter if stripped_result.len() > remaining.len() + 1 + exponent.len() { result = format!("{}e{}", u8_to_string(remaining), exponent); } else { result = u8_to_string(stripped_result); } } else { result = u8_to_string(stripped_result); } } else if chars[len - 1] == b'0' { // Simplify numbers ending with "0" by trying to use an exponent // "1000" => "1e3" let mut i = len - 1; while i > 0 && chars[i - 1] == b'0' { i -= 1; } let remaining = &chars[0..i]; let exponent = format!("{}", chars.len() - i); // Only switch if it's actually shorter if chars.len() > remaining.len() + 1 + exponent.len() { result = format!("{}e{}", u8_to_string(remaining), exponent); } else { result = u8_to_string(chars); } } result } impl Gen for BigintLiteral { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { use num_bigint::Sign; if self.value.sign() == Sign::Minus { p.print_space_before_operator(Operator::Unary(UnaryOperator::UnaryNegation)); } p.print_str(self.value.to_string().as_bytes()); p.print(b'n'); } } impl Gen for RegExpLiteral { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { let last = p.peek_nth(0); // Avoid forming a single-line comment or "(s: &str, p: &mut Codegen<{ MINIFY }>) { p.print(b'\''); for c in s.chars() { match c { // Allow `U+2028` and `U+2029` in string literals // // LS => p.print_str(b"\\u2028"), PS => p.print_str(b"\\u2029"), '\u{a0}' => { p.print_str(b"\\xA0"); } _ => p.print_str(c.escape_default().to_string().as_bytes()), } } p.print(b'\''); } impl Gen for StringLiteral { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { print_str(self.value.as_str(), p); } } impl Gen for ThisExpression { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_space_before_identifier(); p.print_str(b"this"); } } impl<'a, const MINIFY: bool> GenExpr for MemberExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { p.wrap(precedence > self.precedence(), |p| match self { Self::ComputedMemberExpression(expr) => { expr.gen_expr(p, self.precedence(), ctx.and_in(true)); } Self::StaticMemberExpression(expr) => expr.gen_expr(p, self.precedence(), ctx), Self::PrivateFieldExpression(expr) => expr.gen_expr(p, self.precedence(), ctx), }); } } impl<'a, const MINIFY: bool> GenExpr for ComputedMemberExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, _precedence: Precedence, ctx: Context) { self.object.gen_expr(p, Precedence::Postfix, ctx); if self.optional { p.print_str(b"?."); } p.print(b'['); self.expression.gen_expr(p, Precedence::lowest(), ctx); p.print(b']'); } } impl<'a, const MINIFY: bool> GenExpr for StaticMemberExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, _precedence: Precedence, ctx: Context) { self.object.gen_expr(p, Precedence::Postfix, ctx); if self.optional { p.print(b'?'); } else if p.need_space_before_dot == p.code_len() { // `0.toExponential()` is invalid, add a space before the dot, `0 .toExponential()` is valid p.print_hard_space(); } p.print(b'.'); self.property.gen(p, ctx); } } impl<'a, const MINIFY: bool> GenExpr for PrivateFieldExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, _precedence: Precedence, ctx: Context) { self.object.gen_expr(p, Precedence::Postfix, ctx); if self.optional { p.print_str(b"?"); } p.print(b'.'); self.field.gen(p, ctx); } } impl<'a, const MINIFY: bool> GenExpr for CallExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { p.wrap(precedence > self.precedence(), |p| { self.callee.gen_expr(p, self.precedence(), ctx); if self.optional { p.print_str(b"?."); } p.print(b'('); p.print_list(&self.arguments, ctx); p.print(b')'); }); } } impl<'a, const MINIFY: bool> Gen for Argument<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::SpreadElement(elem) => elem.gen(p, ctx), Self::Expression(elem) => elem.gen_expr(p, Precedence::Assign, Context::default()), } } } impl<'a, const MINIFY: bool> Gen for ArrayExpressionElement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::Expression(expr) => expr.gen_expr(p, Precedence::Assign, Context::default()), Self::SpreadElement(elem) => elem.gen(p, ctx), Self::Elision(_span) => {} } } } impl<'a, const MINIFY: bool> Gen for SpreadElement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_ellipsis(); self.argument.gen_expr(p, Precedence::Assign, Context::default()); } } impl<'a, const MINIFY: bool> Gen for ArrayExpression<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print(b'['); p.print_list(&self.elements, ctx); if self.trailing_comma.is_some() { p.print_comma(); } p.print(b']'); } } impl<'a, const MINIFY: bool> GenExpr for ObjectExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, _precedence: Precedence, ctx: Context) { let n = p.code_len(); let is_multi_line = !self.properties.is_empty(); p.wrap(p.start_of_stmt == n || p.start_of_arrow_expr == n, |p| { p.print(b'{'); if is_multi_line { p.indent(); } for (i, item) in self.properties.iter().enumerate() { if i != 0 { p.print_comma(); } p.print_soft_newline(); p.print_indent(); item.gen(p, ctx); } if is_multi_line { p.print_soft_newline(); p.dedent(); p.print_indent(); } p.print(b'}'); }); } } impl<'a, const MINIFY: bool> Gen for ObjectPropertyKind<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::ObjectProperty(prop) => prop.gen(p, ctx), Self::SpreadProperty(elem) => elem.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for ObjectProperty<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if let Expression::FunctionExpression(func) = &self.value { let is_accessor = match &self.kind { PropertyKind::Init => false, PropertyKind::Get => { p.print_str(b"get "); true } PropertyKind::Set => { p.print_str(b"set "); true } }; if self.method || is_accessor { if func.r#async { p.print_str(b"async "); } if func.generator { p.print_str(b"*"); } if self.computed { p.print(b'['); } self.key.gen(p, ctx); if self.computed { p.print(b']'); } p.print(b'('); func.params.gen(p, ctx); p.print(b')'); if let Some(body) = &func.body { body.gen(p, ctx); } return; } } if self.computed { p.print(b'['); } self.key.gen(p, ctx); if self.computed { p.print(b']'); } p.print_colon(); self.value.gen_expr(p, Precedence::Assign, Context::default()); } } impl<'a, const MINIFY: bool> Gen for PropertyKey<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::Identifier(ident) => ident.gen(p, ctx), Self::PrivateIdentifier(ident) => ident.gen(p, ctx), Self::Expression(expr) => expr.gen_expr(p, Precedence::Assign, Context::default()), } } } impl<'a, const MINIFY: bool> GenExpr for ArrowExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { p.wrap(precedence > Precedence::Assign, |p| { if self.r#async { p.print_str(b"async"); p.print_soft_space(); } // No wrap for `a => {}` let nowrap = self.params.rest.is_none() && self.params.items.len() == 1 && self.params.items[0].pattern.kind.is_binding_identifier(); if nowrap && self.r#async { p.print_soft_space(); } p.wrap(!nowrap, |p| { self.params.gen(p, ctx); }); p.print_soft_space(); p.print_str(b"=>"); p.print_soft_space(); if self.expression { if let Statement::ExpressionStatement(stmt) = &self.body.statements[0] { p.start_of_arrow_expr = p.code_len(); stmt.expression.gen_expr(p, Precedence::Assign, ctx); } } else { self.body.gen(p, ctx); } }); } } impl<'a, const MINIFY: bool> GenExpr for YieldExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { p.wrap(precedence >= self.precedence(), |p| { p.print_space_before_identifier(); p.print_str(b"yield"); if self.delegate { p.print(b'*'); } if let Some(argument) = self.argument.as_ref() { if !self.delegate { p.print_hard_space(); } argument.gen_expr(p, Precedence::Assign, ctx); } }); } } impl<'a, const MINIFY: bool> GenExpr for UpdateExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { let operator = self.operator.as_str().as_bytes(); p.wrap(precedence > self.precedence(), |p| { if self.prefix { p.print_space_before_operator(self.operator.into()); p.print_str(operator); p.prev_op = Some(self.operator.into()); p.prev_op_end = p.code().len(); self.argument.gen_expr(p, Precedence::Prefix, ctx); } else { self.argument.gen_expr(p, Precedence::Postfix, ctx); p.print_str(operator); p.prev_op = Some(self.operator.into()); p.prev_op_end = p.code().len(); } }); } } impl<'a, const MINIFY: bool> GenExpr for UnaryExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { p.wrap(precedence > self.precedence() || precedence == Precedence::Exponential, |p| { let operator = self.operator.as_str().as_bytes(); if self.operator.is_keyword() { p.print_space_before_identifier(); p.print_str(operator); p.print_hard_space(); } else { p.print_space_before_operator(self.operator.into()); p.print_str(operator); p.prev_op = Some(self.operator.into()); p.prev_op_end = p.code().len(); } self.argument.gen_expr(p, Precedence::Prefix, ctx); }); } } impl<'a, const MINIFY: bool> GenExpr for BinaryExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { let wrap_in = self.operator == BinaryOperator::In && !ctx.has_in(); let wrap = precedence > self.precedence() || wrap_in; p.wrap(wrap, |p| { self.left.gen_expr(p, self.precedence(), ctx); if self.operator.is_keyword() { p.print_space_before_identifier(); } self.operator.gen(p, ctx); p.print_soft_space(); self.right.gen_expr(p, self.precedence(), ctx.union_in_if(wrap)); }); } } impl Gen for BinaryOperator { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { let operator = self.as_str().as_bytes(); if self.is_keyword() { p.print_str(operator); p.print_hard_space(); } else { let op: Operator = (*self).into(); p.print_space_before_operator(op); p.print_str(operator); p.prev_op = Some(op); p.prev_op_end = p.code().len(); } } } impl<'a, const MINIFY: bool> Gen for PrivateInExpression<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.left.gen(p, ctx); p.print_str(b" in "); self.right.gen_expr(p, Precedence::Shift, Context::default()); } } impl<'a, const MINIFY: bool> GenExpr for LogicalExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { // Logical expressions and coalesce expressions cannot be mixed (Syntax Error). let mixed = matches!( (precedence, self.precedence()), (Precedence::Coalesce, Precedence::LogicalAnd | Precedence::LogicalOr) ); p.wrap(mixed || (precedence > self.precedence()), |p| { self.left.gen_expr(p, self.precedence(), ctx); p.print_soft_space(); p.print_str(self.operator.as_str().as_bytes()); p.print_soft_space(); self.right.gen_expr(p, self.precedence(), ctx); }); } } impl<'a, const MINIFY: bool> GenExpr for ConditionalExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { let wrap = precedence > self.precedence(); p.wrap(wrap, |p| { self.test.gen_expr(p, self.precedence(), ctx); p.print_soft_space(); p.print(b'?'); p.print_soft_space(); self.consequent.gen_expr(p, Precedence::Assign, ctx.and_in(true)); p.print_soft_space(); p.print(b':'); p.print_soft_space(); self.alternate.gen_expr(p, Precedence::Assign, ctx.union_in_if(wrap)); }); } } impl<'a, const MINIFY: bool> GenExpr for AssignmentExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { // Destructuring assignment let n = p.code_len(); let wrap = (p.start_of_stmt == n || p.start_of_arrow_expr == n) && matches!( self.left, AssignmentTarget::AssignmentTargetPattern( AssignmentTargetPattern::ObjectAssignmentTarget(_) ) ); p.wrap(wrap || precedence > self.precedence(), |p| { self.left.gen(p, ctx); p.print_soft_space(); p.print_str(self.operator.as_str().as_bytes()); p.print_soft_space(); self.right.gen_expr(p, Precedence::Assign, ctx); }); } } impl<'a, const MINIFY: bool> Gen for AssignmentTarget<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::SimpleAssignmentTarget(target) => { target.gen_expr(p, Precedence::Assign, Context::default()); } Self::AssignmentTargetPattern(pat) => pat.gen(p, ctx), } } } impl<'a, const MINIFY: bool> GenExpr for SimpleAssignmentTarget<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { match self { Self::AssignmentTargetIdentifier(ident) => ident.gen(p, ctx), Self::MemberAssignmentTarget(member_expr) => { member_expr.gen_expr(p, precedence, ctx); } Self::TSAsExpression(e) => e.expression.gen_expr(p, precedence, ctx), Self::TSSatisfiesExpression(e) => e.expression.gen_expr(p, precedence, ctx), Self::TSNonNullExpression(e) => e.expression.gen_expr(p, precedence, ctx), Self::TSTypeAssertion(e) => e.expression.gen_expr(p, precedence, ctx), } } } impl<'a, const MINIFY: bool> Gen for AssignmentTargetPattern<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::ArrayAssignmentTarget(target) => target.gen(p, ctx), Self::ObjectAssignmentTarget(target) => target.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for ArrayAssignmentTarget<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print(b'['); p.print_list(&self.elements, ctx); if let Some(target) = &self.rest { p.print_comma(); p.print_ellipsis(); target.gen(p, ctx); } if self.trailing_comma.is_some() { p.print_comma(); } p.print(b']'); } } impl<'a, const MINIFY: bool> Gen for Option> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if let Some(arg) = self { arg.gen(p, ctx); } } } impl<'a, const MINIFY: bool> Gen for ObjectAssignmentTarget<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print(b'{'); p.print_list(&self.properties, ctx); if let Some(target) = &self.rest { if !self.properties.is_empty() { p.print_comma(); } p.print_ellipsis(); target.gen(p, ctx); } p.print(b'}'); } } impl<'a, const MINIFY: bool> Gen for AssignmentTargetMaybeDefault<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::AssignmentTarget(target) => target.gen(p, ctx), Self::AssignmentTargetWithDefault(target) => target.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for AssignmentTargetWithDefault<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.binding.gen(p, ctx); p.print_equal(); self.init.gen_expr(p, Precedence::Assign, Context::default()); } } impl<'a, const MINIFY: bool> Gen for AssignmentTargetProperty<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::AssignmentTargetPropertyIdentifier(ident) => ident.gen(p, ctx), Self::AssignmentTargetPropertyProperty(prop) => prop.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for AssignmentTargetPropertyIdentifier<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.binding.gen(p, ctx); if let Some(expr) = &self.init { p.print_equal(); expr.gen_expr(p, Precedence::Assign, Context::default()); } } } impl<'a, const MINIFY: bool> Gen for AssignmentTargetPropertyProperty<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match &self.name { PropertyKey::Identifier(ident) => { ident.gen(p, ctx); } PropertyKey::PrivateIdentifier(ident) => { ident.gen(p, ctx); } PropertyKey::Expression(expr) => { p.print(b'['); expr.gen_expr(p, Precedence::Assign, Context::default()); p.print(b']'); } } p.print_colon(); self.binding.gen(p, ctx); } } impl<'a, const MINIFY: bool> GenExpr for SequenceExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, _ctx: Context) { p.wrap(precedence > self.precedence(), |p| { p.print_expressions(&self.expressions, Precedence::Assign, Context::default()); }); } } impl<'a, const MINIFY: bool> Gen for ImportExpression<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(b"import("); self.source.gen_expr(p, Precedence::Assign, Context::default()); if !self.arguments.is_empty() { p.print_comma(); p.print_expressions(&self.arguments, Precedence::Assign, Context::default()); } p.print(b')'); } } impl<'a, const MINIFY: bool> Gen for TemplateLiteral<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print(b'`'); let mut expressions = self.expressions.iter(); for quasi in &self.quasis { p.print_str(quasi.value.raw.as_bytes()); if let Some(expr) = expressions.next() { p.print_str(b"${"); expr.gen_expr(p, Precedence::lowest(), Context::default()); p.print(b'}'); } } p.print(b'`'); } } impl<'a, const MINIFY: bool> Gen for TaggedTemplateExpression<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.tag.gen_expr(p, Precedence::Call, Context::default()); self.quasi.gen(p, ctx); } } impl Gen for Super { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(b"super"); } } impl<'a, const MINIFY: bool> GenExpr for AwaitExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { p.wrap(precedence > self.precedence(), |p| { p.print_str(b"await "); self.argument.gen_expr(p, self.precedence(), ctx); }); } } impl<'a, const MINIFY: bool> GenExpr for ChainExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { match &self.expression { ChainElement::CallExpression(expr) => expr.gen_expr(p, precedence, ctx), ChainElement::MemberExpression(expr) => expr.gen_expr(p, precedence, ctx), } } } impl<'a, const MINIFY: bool> GenExpr for NewExpression<'a> { fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) { p.wrap(precedence > self.precedence(), |p| { p.print_str(b"new "); self.callee.gen_expr(p, self.precedence(), ctx); p.wrap(true, |p| { p.print_list(&self.arguments, ctx); }); }); } } impl Gen for MetaProperty { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.meta.gen(p, ctx); p.print(b'.'); self.property.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for Class<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if self.is_declare() { return; } let n = p.code_len(); let wrap = self.is_expression() && (p.start_of_stmt == n || p.start_of_default_export == n); p.wrap(wrap, |p| { self.decorators.gen(p, ctx); p.print_str(b"class"); if let Some(id) = &self.id { p.print_hard_space(); id.gen(p, ctx); } if let Some(super_class) = self.super_class.as_ref() { p.print_str(b" extends "); super_class.gen_expr(p, Precedence::Call, Context::default()); } p.print_soft_space(); p.print_block_start(); for item in &self.body.body { if item.is_typescript_syntax() { continue; } p.print_indent(); p.print_semicolon_if_needed(); item.gen(p, ctx); if matches!( item, ClassElement::PropertyDefinition(_) | ClassElement::AccessorProperty(_) ) { p.print_semicolon_after_statement(); } p.print_soft_newline(); } p.print_block_end(); p.needs_semicolon = false; }); } } impl<'a, const MINIFY: bool> Gen for ClassElement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::StaticBlock(elem) => elem.gen(p, ctx), Self::MethodDefinition(elem) => elem.gen(p, ctx), Self::PropertyDefinition(elem) => elem.gen(p, ctx), Self::AccessorProperty(elem) => elem.gen(p, ctx), _ => {} } } } impl Gen for JSXIdentifier { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(self.name.as_bytes()); } } impl<'a, const MINIFY: bool> Gen for JSXMemberExpressionObject<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::Identifier(ident) => ident.gen(p, ctx), Self::MemberExpression(member_expr) => member_expr.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for JSXMemberExpression<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.object.gen(p, ctx); p.print(b'.'); self.property.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for JSXElementName<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::Identifier(identifier) => identifier.gen(p, ctx), Self::NamespacedName(namespaced_name) => namespaced_name.gen(p, ctx), Self::MemberExpression(member_expr) => member_expr.gen(p, ctx), } } } impl Gen for JSXNamespacedName { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.namespace.gen(p, ctx); p.print(b'.'); self.property.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for JSXAttributeName<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::Identifier(ident) => ident.gen(p, ctx), Self::NamespacedName(namespaced_name) => namespaced_name.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for JSXAttribute<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.name.gen(p, ctx); if let Some(value) = &self.value { p.print(b'='); value.gen(p, ctx); } } } impl Gen for JSXEmptyExpression { fn gen(&self, _: &mut Codegen<{ MINIFY }>, _ctx: Context) {} } impl<'a, const MINIFY: bool> Gen for JSXExpression<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::Expression(expr) => expr.gen_expr(p, Precedence::lowest(), Context::default()), Self::EmptyExpression(expr) => expr.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for JSXExpressionContainer<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print(b'{'); self.expression.gen(p, ctx); p.print(b'}'); } } impl<'a, const MINIFY: bool> Gen for JSXAttributeValue<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::Fragment(fragment) => fragment.gen(p, ctx), Self::Element(el) => el.gen(p, ctx), Self::StringLiteral(lit) => lit.gen(p, ctx), Self::ExpressionContainer(expr_container) => expr_container.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for JSXSpreadAttribute<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(b"{..."); self.argument.gen_expr(p, Precedence::lowest(), Context::default()); p.print(b'}'); } } impl<'a, const MINIFY: bool> Gen for JSXAttributeItem<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::Attribute(attr) => attr.gen(p, ctx), Self::SpreadAttribute(spread_attr) => spread_attr.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for JSXOpeningElement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_str(b"<"); self.name.gen(p, ctx); for attr in &self.attributes { p.print_hard_space(); attr.gen(p, ctx); } if self.self_closing { p.print_str(b"/>"); } else { p.print(b'>'); } } } impl<'a, const MINIFY: bool> Gen for JSXClosingElement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_str(b"'); } } impl<'a, const MINIFY: bool> Gen for JSXElement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.opening_element.gen(p, ctx); for child in &self.children { child.gen(p, ctx); } if let Some(closing_element) = &self.closing_element { closing_element.gen(p, ctx); } } } impl Gen for JSXOpeningFragment { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(b"<>"); } } impl Gen for JSXClosingFragment { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(b""); } } impl Gen for JSXText { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(self.value.as_bytes()); } } impl<'a, const MINIFY: bool> Gen for JSXSpreadChild<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print_str(b"..."); self.expression.gen_expr(p, Precedence::lowest(), Context::default()); } } impl<'a, const MINIFY: bool> Gen for JSXChild<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { Self::Fragment(fragment) => fragment.gen(p, ctx), Self::Element(el) => el.gen(p, ctx), Self::Spread(spread) => { spread.expression.gen_expr(p, Precedence::lowest(), Context::default()); } Self::ExpressionContainer(expr_container) => expr_container.gen(p, ctx), Self::Text(text) => text.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for JSXFragment<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.opening_fragment.gen(p, ctx); for child in &self.children { child.gen(p, ctx); } self.closing_fragment.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for StaticBlock<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_str(b"static"); p.print_block_start(); for stmt in &self.body { p.print_semicolon_if_needed(); stmt.gen(p, ctx); } p.print_block_end(); p.needs_semicolon = false; } } impl<'a, const MINIFY: bool> Gen for MethodDefinition<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if self.value.is_typescript_syntax() { return; } self.decorators.gen(p, ctx); if self.r#static { p.print_str(b"static "); } match &self.kind { MethodDefinitionKind::Constructor | MethodDefinitionKind::Method => {} MethodDefinitionKind::Get => p.print_str(b"get "), MethodDefinitionKind::Set => p.print_str(b"set "), } if self.value.r#async { p.print_str(b"async "); } if self.value.generator { p.print_str(b"*"); } if self.computed { p.print(b'['); } self.key.gen(p, ctx); if self.computed { p.print(b']'); } p.print(b'('); self.value.params.gen(p, ctx); p.print(b')'); if let Some(body) = &self.value.body { body.gen(p, ctx); } } } impl<'a, const MINIFY: bool> Gen for PropertyDefinition<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.decorators.gen(p, ctx); if self.r#static { p.print_str(b"static "); } if self.computed { p.print(b'['); } self.key.gen(p, ctx); if self.computed { p.print(b']'); } if let Some(value) = &self.value { p.print_equal(); value.gen_expr(p, Precedence::Assign, Context::default()); } } } impl<'a, const MINIFY: bool> Gen for AccessorProperty<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if self.r#static { p.print_str(b"static "); } p.print_str(b"accessor "); if self.computed { p.print(b'['); } self.key.gen(p, ctx); if self.computed { p.print(b']'); } if let Some(value) = &self.value { p.print_equal(); value.gen_expr(p, Precedence::Assign, Context::default()); } } } impl Gen for PrivateIdentifier { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.print(b'#'); p.print_str(self.name.as_bytes()); } } impl<'a, const MINIFY: bool> Gen for BindingPattern<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match &self.kind { BindingPatternKind::BindingIdentifier(ident) => ident.gen(p, ctx), BindingPatternKind::ObjectPattern(pattern) => pattern.gen(p, ctx), BindingPatternKind::ArrayPattern(pattern) => pattern.gen(p, ctx), BindingPatternKind::AssignmentPattern(pattern) => pattern.gen(p, ctx), } } } impl<'a, const MINIFY: bool> Gen for ObjectPattern<'a> { fn gen(&self, p: &mut Codegen, ctx: Context) { p.print(b'{'); p.print_list(&self.properties, ctx); if let Some(rest) = &self.rest { if !self.properties.is_empty() { p.print_comma(); } rest.gen(p, ctx); } p.print(b'}'); } } impl<'a, const MINIFY: bool> Gen for BindingProperty<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if self.computed { p.print(b'['); } self.key.gen(p, ctx); if self.computed { p.print(b']'); } p.print(b':'); self.value.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for RestElement<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_ellipsis(); self.argument.gen(p, ctx); } } impl<'a, const MINIFY: bool> Gen for ArrayPattern<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print(b'['); for (index, item) in self.elements.iter().enumerate() { if index != 0 { p.print_comma(); } if let Some(item) = item { item.gen(p, ctx); } if index == self.elements.len() - 1 && (item.is_none() || self.rest.is_some()) { p.print_comma(); } } if let Some(rest) = &self.rest { rest.gen(p, ctx); } p.print(b']'); } } impl<'a, const MINIFY: bool> Gen for AssignmentPattern<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { self.left.gen(p, ctx); p.print_equal(); self.right.gen_expr(p, Precedence::Assign, Context::default()); } } impl<'a, const MINIFY: bool> Gen for Vec<'a, Decorator<'a>> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { for decorator in self { decorator.gen(p, ctx); p.print_hard_space(); } } } impl<'a, const MINIFY: bool> Gen for Decorator<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { fn need_wrap(expr: &Expression) -> bool { match expr { // "@foo" Expression::Identifier(_) => false, Expression::MemberExpression(member_expr) => { // "@foo.bar" // "@(foo['bar'])" matches!(&**member_expr, MemberExpression::ComputedMemberExpression(_)) } Expression::CallExpression(call_expr) => need_wrap(&call_expr.callee), // "@(foo + bar)" // "@(() => {})" _ => true, } } p.print(b'@'); let wrap = need_wrap(&self.expression); p.wrap(wrap, |p| { self.expression.gen_expr(p, Precedence::Assign, Context::default()); }); } }