1use swc_common::{SourceMapper, Spanned};
2use swc_ecma_ast::*;
3use swc_ecma_codegen_macros::node_impl;
4
5use super::{Emitter, Result};
6#[cfg(swc_ast_unknown)]
7use crate::unknown_error;
8use crate::{
9 scope_helpers::{for_each_param_binding, for_each_var_decl_binding},
10 text_writer::{BindingStorage, ScopeKind, WriteJs},
11};
12
13impl<W, S: SourceMapper> Emitter<'_, W, S>
14where
15 W: WriteJs,
16 S: SourceMapperExt,
17{
18 pub(super) fn emit_class_decl_inner(
19 &mut self,
20 node: &ClassDecl,
21 skip_decorators: bool,
22 ) -> Result {
23 self.emit_leading_comments_of_span(node.span(), false)?;
24
25 srcmap!(self, node, true);
26
27 if node.declare {
28 keyword!(self, "declare");
29 space!(self);
30 }
31
32 if !skip_decorators {
33 for dec in &node.class.decorators {
34 emit!(self, dec);
35 }
36 }
37
38 if node.class.is_abstract {
39 keyword!(self, "abstract");
40 space!(self);
41 }
42
43 keyword!(self, "class");
44 space!(self);
45 emit!(self, node.ident);
46 emit!(self, node.class.type_params);
47
48 self.emit_class_trailing(&node.class)?;
49
50 Ok(())
51 }
52
53 fn emit_var_decl_inner(&mut self, node: &VarDecl) -> Result {
54 self.emit_leading_comments_of_span(node.span, false)?;
55
56 self.wr.commit_pending_semi()?;
57
58 srcmap!(self, node, true);
59
60 if self.scope_tracking_enabled() {
61 let storage = if matches!(node.kind, VarDeclKind::Var) {
62 BindingStorage::Hoisted
63 } else {
64 BindingStorage::Lexical
65 };
66 let mut names = vec![];
67 for_each_var_decl_binding(node, &mut |name| names.push(name.to_string()));
68 for name in names {
69 self.add_scope_variable(&name, Some(&name), storage)?;
70 }
71 }
72
73 if node.declare {
74 keyword!(self, "declare");
75 space!(self);
76 }
77
78 keyword!(self, node.kind.as_str());
79
80 let starts_with_ident = match node.decls.first() {
81 Some(VarDeclarator {
82 name: Pat::Array(..) | Pat::Rest(..) | Pat::Object(..),
83 ..
84 }) => false,
85 _ => true,
86 };
87 if starts_with_ident {
88 space!(self);
89 } else {
90 formatting_space!(self);
91 }
92
93 self.emit_list(
94 node.span(),
95 Some(&node.decls),
96 ListFormat::VariableDeclarationList,
97 )?;
98
99 Ok(())
100 }
101}
102
103#[node_impl]
104impl MacroNode for Decl {
105 fn emit(&mut self, emitter: &mut Macro) -> Result {
106 match self {
107 Decl::Class(n) => emit!(n),
108 Decl::Fn(n) => emit!(n),
109 Decl::Var(n) => {
110 emitter.emit_var_decl_inner(n)?;
111 formatting_semi!(emitter);
112 srcmap!(emitter, self, false);
113 }
114 Decl::Using(n) => emit!(n),
115 Decl::TsEnum(n) => emit!(n),
116 Decl::TsInterface(n) => emit!(n),
117 Decl::TsModule(n) => emit!(n),
118 Decl::TsTypeAlias(n) => emit!(n),
119 #[cfg(swc_ast_unknown)]
120 _ => return Err(unknown_error()),
121 }
122
123 Ok(())
124 }
125}
126
127#[node_impl]
128impl MacroNode for ClassDecl {
129 fn emit(&mut self, emitter: &mut Macro) -> Result {
130 let name = self.ident.sym.as_ref();
131 emitter.add_scope_variable(name, Some(name), BindingStorage::Lexical)?;
132 emitter.emit_class_decl_inner(self, false)?;
133 Ok(())
134 }
135}
136
137#[node_impl]
138impl MacroNode for UsingDecl {
139 fn emit(&mut self, emitter: &mut Macro) -> Result {
140 emitter.emit_leading_comments_of_span(self.span(), false)?;
141
142 if self.is_await {
143 keyword!(emitter, "await");
144 space!(emitter);
145 }
146
147 keyword!(emitter, "using");
148 space!(emitter);
149
150 emitter.emit_list(
151 self.span,
152 Some(&self.decls),
153 ListFormat::VariableDeclarationList,
154 )?;
155
156 Ok(())
157 }
158}
159
160#[node_impl]
161impl MacroNode for FnDecl {
162 fn emit(&mut self, emitter: &mut Macro) -> Result {
163 emitter.emit_leading_comments_of_span(self.span(), false)?;
164
165 emitter.wr.commit_pending_semi()?;
166
167 srcmap!(emitter, self, true);
168 let fn_name = self.ident.sym.as_ref();
169 emitter.add_scope_variable(fn_name, Some(fn_name), BindingStorage::Hoisted)?;
170 emitter.start_scope(
171 Some(fn_name),
172 ScopeKind::Function,
173 true,
174 false,
175 Some(self.function.span),
176 )?;
177 if emitter.scope_tracking_enabled() {
178 let mut names = vec![];
179 for_each_param_binding(&self.function.params, &mut |name| {
180 names.push(name.to_string())
181 });
182 for name in names {
183 emitter.add_scope_variable(&name, Some(&name), BindingStorage::Lexical)?;
184 }
185 }
186
187 if self.declare {
188 keyword!(emitter, "declare");
189 space!(emitter);
190 }
191
192 if self.function.is_async {
193 keyword!(emitter, "async");
194 space!(emitter);
195 }
196
197 keyword!(emitter, "function");
198 if self.function.is_generator {
199 punct!(emitter, "*");
200 formatting_space!(emitter);
201 } else {
202 space!(emitter);
203 }
204
205 emit!(self.ident);
206
207 emitter.emit_fn_trailing(&self.function)?;
208 emitter.end_scope()?;
209
210 Ok(())
211 }
212}
213
214#[node_impl]
215impl MacroNode for VarDecl {
216 fn emit(&mut self, emitter: &mut Macro) -> Result {
217 emitter.emit_var_decl_inner(self)?;
218 Ok(())
219 }
220}
221
222#[node_impl]
223impl MacroNode for VarDeclarator {
224 fn emit(&mut self, emitter: &mut Macro) -> Result {
225 emitter.emit_leading_comments_of_span(self.span(), false)?;
226
227 srcmap!(emitter, self, true);
228
229 emit!(self.name);
230
231 if let Some(ref init) = self.init {
232 formatting_space!(emitter);
233 punct!(emitter, "=");
234 formatting_space!(emitter);
235 emit!(init);
236 }
237
238 Ok(())
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use crate::tests::assert_min;
245
246 #[test]
247 fn issue_275() {
248 assert_min(
249 "function* foo(){
250 yield getServiceHosts()
251 }",
252 "function*foo(){yield getServiceHosts()}",
253 );
254 }
255
256 #[test]
257 fn issue_1764() {
258 assert_min(
259 "class Hoge {};
260class HogeFuga extends Hoge {};",
261 "class Hoge{};class HogeFuga extends Hoge{};",
262 );
263 }
264
265 #[test]
266 fn single_argument_arrow_expression() {
267 assert_min("function* f(){ yield x => x}", "function*f(){yield x=>x}");
268 assert_min(
269 "function* f(){ yield ({x}) => x}",
270 "function*f(){yield({x})=>x}",
271 );
272 }
273
274 #[test]
275 fn class_static_block() {
276 assert_min("class Foo { static { 1 + 1; }}", "class Foo{static{1+1}}");
277 }
278}