swc_ecma_codegen/
lib.rs

1#![recursion_limit = "1024"]
2#![deny(clippy::all)]
3#![deny(unused)]
4#![allow(clippy::match_like_matches_macro)]
5#![allow(clippy::nonminimal_bool)]
6#![allow(non_local_definitions)]
7
8use std::{borrow::Cow, fmt::Write, io, ops::Deref, str};
9
10use ascii::AsciiChar;
11use compact_str::{format_compact, CompactString};
12use memchr::memmem::Finder;
13use once_cell::sync::Lazy;
14use swc_atoms::Atom;
15use swc_common::{
16    comments::{CommentKind, Comments},
17    sync::Lrc,
18    BytePos, SourceMap, SourceMapper, Span, Spanned, DUMMY_SP,
19};
20use swc_ecma_ast::*;
21use swc_ecma_codegen_macros::emitter;
22
23pub use self::config::Config;
24use self::{text_writer::WriteJs, util::StartsWithAlphaNum};
25use crate::util::EndsWithAlphaNum;
26
27#[macro_use]
28pub mod macros;
29mod comments;
30mod config;
31mod decl;
32mod expr;
33mod jsx;
34mod stmt;
35#[cfg(test)]
36mod tests;
37pub mod text_writer;
38mod typescript;
39pub mod util;
40
41pub type Result = io::Result<()>;
42
43/// Generate a code from a syntax node using default options.
44pub fn to_code_default(
45    cm: Lrc<SourceMap>,
46    comments: Option<&dyn Comments>,
47    node: &impl Node,
48) -> String {
49    let mut buf = std::vec::Vec::new();
50    {
51        let mut emitter = Emitter {
52            cfg: Default::default(),
53            cm: cm.clone(),
54            comments,
55            wr: text_writer::JsWriter::new(cm, "\n", &mut buf, None),
56        };
57        node.emit_with(&mut emitter).unwrap();
58    }
59
60    String::from_utf8(buf).expect("codegen generated non-utf8 output")
61}
62
63/// Generate a code from a syntax node using default options.
64pub fn to_code_with_comments(comments: Option<&dyn Comments>, node: &impl Node) -> String {
65    to_code_default(Default::default(), comments, node)
66}
67
68/// Generate a code from a syntax node using default options.
69pub fn to_code(node: &impl Node) -> String {
70    to_code_with_comments(None, node)
71}
72
73pub trait Node: Spanned {
74    fn emit_with<W, S>(&self, e: &mut Emitter<'_, W, S>) -> Result
75    where
76        W: WriteJs,
77        S: SourceMapper + SourceMapperExt;
78}
79impl<N: Node> Node for Box<N> {
80    #[inline]
81    fn emit_with<W, S>(&self, e: &mut Emitter<'_, W, S>) -> Result
82    where
83        W: WriteJs,
84        S: SourceMapper + SourceMapperExt,
85    {
86        (**self).emit_with(e)
87    }
88}
89impl<N: Node> Node for &N {
90    #[inline]
91    fn emit_with<W, S>(&self, e: &mut Emitter<'_, W, S>) -> Result
92    where
93        W: WriteJs,
94        S: SourceMapper + SourceMapperExt,
95    {
96        (**self).emit_with(e)
97    }
98}
99
100pub struct Emitter<'a, W, S: SourceMapper>
101where
102    W: WriteJs,
103    S: SourceMapperExt,
104{
105    pub cfg: config::Config,
106    pub cm: Lrc<S>,
107    pub comments: Option<&'a dyn Comments>,
108    pub wr: W,
109}
110
111enum CowStr<'a> {
112    Borrowed(&'a str),
113    Owned(CompactString),
114}
115
116impl Deref for CowStr<'_> {
117    type Target = str;
118
119    fn deref(&self) -> &str {
120        match self {
121            CowStr::Borrowed(s) => s,
122            CowStr::Owned(s) => s.as_str(),
123        }
124    }
125}
126
127fn replace_close_inline_script(raw: &str) -> CowStr {
128    let chars = raw.as_bytes();
129    let pattern_len = 8; // </script>
130
131    let mut matched_indexes = chars
132        .iter()
133        .enumerate()
134        .filter(|(index, byte)| {
135            byte == &&b'<'
136                && index + pattern_len < chars.len()
137                && chars[index + 1..index + pattern_len].eq_ignore_ascii_case(b"/script")
138                && matches!(
139                    chars[index + pattern_len],
140                    b'>' | b' ' | b'\t' | b'\n' | b'\x0C' | b'\r'
141                )
142        })
143        .map(|(index, _)| index)
144        .peekable();
145
146    if matched_indexes.peek().is_none() {
147        return CowStr::Borrowed(raw);
148    }
149
150    let mut result = CompactString::new(raw);
151
152    for (offset, i) in matched_indexes.enumerate() {
153        result.insert(i + 1 + offset, '\\');
154    }
155
156    CowStr::Owned(result)
157}
158
159static NEW_LINE_TPL_REGEX: Lazy<regex::Regex> = Lazy::new(|| regex::Regex::new(r"\\n|\n").unwrap());
160
161impl<W, S: SourceMapper> Emitter<'_, W, S>
162where
163    W: WriteJs,
164    S: SourceMapperExt,
165{
166    #[emitter]
167    pub fn emit_program(&mut self, node: &Program) -> Result {
168        match *node {
169            Program::Module(ref m) => emit!(m),
170            Program::Script(ref s) => emit!(s),
171            // TODO: reenable once experimental_metadata breaking change is merged
172            // _ => unreachable!(),
173        }
174    }
175
176    #[emitter]
177    #[tracing::instrument(skip_all)]
178    pub fn emit_module(&mut self, node: &Module) -> Result {
179        self.emit_leading_comments_of_span(node.span(), false)?;
180
181        if node.body.is_empty() {
182            srcmap!(node, true);
183        }
184
185        if let Some(ref shebang) = node.shebang {
186            punct!("#!");
187            self.wr.write_str_lit(DUMMY_SP, shebang)?;
188            self.wr.write_line()?;
189        }
190        for stmt in &node.body {
191            emit!(stmt);
192        }
193
194        self.emit_trailing_comments_of_pos(node.span().hi, true, true)?;
195        if !self.cfg.omit_last_semi {
196            self.wr.commit_pending_semi()?;
197        }
198    }
199
200    #[emitter]
201    #[tracing::instrument(skip_all)]
202    pub fn emit_script(&mut self, node: &Script) -> Result {
203        self.emit_leading_comments_of_span(node.span(), false)?;
204
205        if node.body.is_empty() {
206            srcmap!(node, true);
207        }
208
209        if let Some(ref shebang) = node.shebang {
210            punct!("#!");
211            self.wr.write_str_lit(DUMMY_SP, shebang)?;
212            self.wr.write_line()?;
213        }
214        for stmt in &node.body {
215            emit!(stmt);
216        }
217
218        self.emit_trailing_comments_of_pos(node.span().hi, true, true)?;
219        if !self.cfg.omit_last_semi {
220            self.wr.commit_pending_semi()?;
221        }
222    }
223
224    #[emitter]
225    pub fn emit_module_item(&mut self, node: &ModuleItem) -> Result {
226        self.emit_leading_comments_of_span(node.span(), false)?;
227        match *node {
228            ModuleItem::Stmt(ref stmt) => emit!(stmt),
229            ModuleItem::ModuleDecl(ref decl) => emit!(decl),
230        }
231        self.emit_trailing_comments_of_pos(node.span().hi, true, true)?;
232    }
233
234    #[emitter]
235    fn emit_module_decl(&mut self, node: &ModuleDecl) -> Result {
236        self.emit_leading_comments_of_span(node.span(), false)?;
237
238        match *node {
239            ModuleDecl::Import(ref d) => emit!(d),
240            ModuleDecl::ExportDecl(ref d) => emit!(d),
241            ModuleDecl::ExportNamed(ref d) => emit!(d),
242            ModuleDecl::ExportDefaultDecl(ref d) => emit!(d),
243            ModuleDecl::ExportDefaultExpr(ref n) => emit!(n),
244            ModuleDecl::ExportAll(ref d) => emit!(d),
245            ModuleDecl::TsExportAssignment(ref n) => emit!(n),
246            ModuleDecl::TsImportEquals(ref n) => emit!(n),
247            ModuleDecl::TsNamespaceExport(ref n) => emit!(n),
248        }
249
250        self.emit_trailing_comments_of_pos(node.span().hi, true, true)?;
251
252        if !self.cfg.minify {
253            self.wr.write_line()?;
254        }
255    }
256
257    #[emitter]
258    fn emit_export_decl(&mut self, n: &ExportDecl) -> Result {
259        srcmap!(n, true);
260
261        match &n.decl {
262            Decl::Class(decl) => {
263                for dec in &decl.class.decorators {
264                    emit!(dec);
265                }
266
267                keyword!("export");
268
269                space!();
270                self.emit_class_decl_inner(decl, true)?;
271            }
272            _ => {
273                keyword!("export");
274
275                space!();
276                emit!(n.decl);
277            }
278        }
279    }
280
281    #[emitter]
282    fn emit_export_default_expr(&mut self, n: &ExportDefaultExpr) -> Result {
283        srcmap!(n, true);
284
285        keyword!("export");
286
287        space!();
288        keyword!("default");
289        {
290            let starts_with_alpha_num = n.expr.starts_with_alpha_num();
291            if starts_with_alpha_num {
292                space!();
293            } else {
294                formatting_space!();
295            }
296            emit!(n.expr);
297        }
298        semi!();
299
300        srcmap!(n, false);
301    }
302
303    #[emitter]
304    fn emit_export_default_decl(&mut self, n: &ExportDefaultDecl) -> Result {
305        self.emit_leading_comments_of_span(n.span(), false)?;
306
307        srcmap!(n, true);
308
309        keyword!("export");
310
311        space!();
312        keyword!("default");
313        space!();
314        match n.decl {
315            DefaultDecl::Class(ref n) => emit!(n),
316            DefaultDecl::Fn(ref n) => emit!(n),
317            DefaultDecl::TsInterfaceDecl(ref n) => emit!(n),
318        }
319    }
320
321    #[emitter]
322    fn emit_import(&mut self, n: &ImportDecl) -> Result {
323        self.emit_leading_comments_of_span(n.span(), false)?;
324
325        srcmap!(n, true);
326
327        keyword!("import");
328
329        if n.type_only {
330            space!();
331            keyword!("type");
332        }
333
334        match n.phase {
335            ImportPhase::Evaluation => {}
336            ImportPhase::Source => {
337                space!();
338                keyword!("source");
339            }
340            ImportPhase::Defer => {
341                space!();
342                keyword!("defer");
343            }
344        }
345
346        let starts_with_ident = !n.specifiers.is_empty()
347            && match &n.specifiers[0] {
348                ImportSpecifier::Default(_) => true,
349                _ => false,
350            };
351        if starts_with_ident {
352            space!();
353        } else {
354            formatting_space!();
355        }
356
357        let mut specifiers = Vec::new();
358        let mut emitted_default = false;
359        let mut emitted_ns = false;
360        for specifier in &n.specifiers {
361            match specifier {
362                ImportSpecifier::Named(ref s) => {
363                    specifiers.push(s);
364                }
365                ImportSpecifier::Default(ref s) => {
366                    emit!(s.local);
367                    emitted_default = true;
368                }
369                ImportSpecifier::Namespace(ref ns) => {
370                    if emitted_default {
371                        punct!(",");
372                        formatting_space!();
373                    }
374
375                    emitted_ns = true;
376
377                    assert!(n.specifiers.len() <= 2);
378                    punct!("*");
379                    formatting_space!();
380                    keyword!("as");
381                    space!();
382                    emit!(ns.local);
383                }
384            }
385        }
386
387        if specifiers.is_empty() {
388            if emitted_ns || emitted_default {
389                space!();
390                keyword!("from");
391                formatting_space!();
392            }
393        } else {
394            if emitted_default {
395                punct!(",");
396                formatting_space!();
397            }
398
399            punct!("{");
400            self.emit_list(
401                n.span(),
402                Some(&specifiers),
403                ListFormat::NamedImportsOrExportsElements,
404            )?;
405            punct!("}");
406            formatting_space!();
407
408            keyword!("from");
409            formatting_space!();
410        }
411
412        emit!(n.src);
413
414        if let Some(with) = &n.with {
415            formatting_space!();
416            if self.cfg.emit_assert_for_import_attributes {
417                keyword!("assert");
418            } else {
419                keyword!("with")
420            };
421            formatting_space!();
422            emit!(with);
423        }
424
425        semi!();
426
427        srcmap!(n, false);
428    }
429
430    #[emitter]
431    fn emit_import_specific(&mut self, node: &ImportNamedSpecifier) -> Result {
432        srcmap!(node, true);
433
434        if node.is_type_only {
435            keyword!("type");
436            space!();
437        }
438
439        if let Some(ref imported) = node.imported {
440            emit!(imported);
441            space!();
442            keyword!("as");
443            space!();
444        }
445
446        emit!(node.local);
447
448        srcmap!(node, false);
449    }
450
451    #[emitter]
452    fn emit_export_specifier(&mut self, node: &ExportSpecifier) -> Result {
453        match node {
454            ExportSpecifier::Default(..) => {
455                unimplemented!("codegen of `export default from 'foo';`")
456            }
457            ExportSpecifier::Namespace(ref node) => emit!(node),
458            ExportSpecifier::Named(ref node) => emit!(node),
459        }
460    }
461
462    #[emitter]
463    fn emit_namespace_export_specifier(&mut self, node: &ExportNamespaceSpecifier) -> Result {
464        self.emit_leading_comments_of_span(node.span(), false)?;
465
466        srcmap!(node, true);
467
468        punct!("*");
469        formatting_space!();
470        keyword!("as");
471        space!();
472        emit!(node.name);
473
474        srcmap!(node, false);
475    }
476
477    #[emitter]
478    fn emit_named_export_specifier(&mut self, node: &ExportNamedSpecifier) -> Result {
479        self.emit_leading_comments_of_span(node.span(), false)?;
480
481        srcmap!(node, true);
482
483        if node.is_type_only {
484            keyword!("type");
485            space!();
486        }
487
488        if let Some(exported) = &node.exported {
489            emit!(node.orig);
490            space!();
491            keyword!("as");
492            space!();
493            emit!(exported);
494        } else {
495            emit!(node.orig);
496        }
497        srcmap!(node, false);
498    }
499
500    #[emitter]
501    fn emit_named_export(&mut self, node: &NamedExport) -> Result {
502        self.emit_leading_comments_of_span(node.span(), false)?;
503
504        srcmap!(node, true);
505
506        struct Specifiers<'a> {
507            has_namespace_spec: bool,
508            namespace_spec: Option<&'a ExportNamespaceSpecifier>,
509            has_named_specs: bool,
510            named_specs: Vec<&'a ExportSpecifier>,
511        }
512        let Specifiers {
513            has_namespace_spec,
514            namespace_spec,
515            has_named_specs,
516            named_specs,
517        } = node.specifiers.iter().fold(
518            Specifiers {
519                has_namespace_spec: false,
520                namespace_spec: None,
521                has_named_specs: false,
522                named_specs: Vec::new(),
523            },
524            |mut result, s| match s {
525                ExportSpecifier::Namespace(spec) => {
526                    result.has_namespace_spec = true;
527                    // There can only be one namespace export specifier.
528                    if result.namespace_spec.is_none() {
529                        result.namespace_spec = Some(spec)
530                    }
531                    result
532                }
533                spec => {
534                    result.has_named_specs = true;
535                    result.named_specs.push(spec);
536                    result
537                }
538            },
539        );
540
541        keyword!("export");
542
543        if node.type_only {
544            space!();
545            keyword!("type");
546        }
547        formatting_space!();
548
549        if let Some(spec) = namespace_spec {
550            emit!(spec);
551            if has_named_specs {
552                punct!(",");
553                formatting_space!();
554            }
555        }
556        if has_named_specs || !has_namespace_spec {
557            punct!("{");
558            self.emit_list(
559                node.span,
560                Some(&named_specs),
561                ListFormat::NamedImportsOrExportsElements,
562            )?;
563            punct!("}");
564        }
565
566        if let Some(ref src) = node.src {
567            if has_named_specs || !has_namespace_spec {
568                formatting_space!();
569            } else if has_namespace_spec {
570                space!();
571            }
572            keyword!("from");
573            formatting_space!();
574            emit!(src);
575
576            if let Some(with) = &node.with {
577                formatting_space!();
578                if self.cfg.emit_assert_for_import_attributes {
579                    keyword!("assert");
580                } else {
581                    keyword!("with")
582                };
583                formatting_space!();
584                emit!(with);
585            }
586        }
587        semi!();
588
589        srcmap!(node, false);
590    }
591
592    #[emitter]
593    fn emit_export_all(&mut self, node: &ExportAll) -> Result {
594        self.emit_leading_comments_of_span(node.span(), false)?;
595
596        srcmap!(node, true);
597
598        keyword!("export");
599
600        if node.type_only {
601            space!();
602            keyword!("type");
603            space!();
604        } else {
605            formatting_space!();
606        }
607
608        punct!("*");
609        formatting_space!();
610        keyword!("from");
611        formatting_space!();
612        emit!(node.src);
613
614        if let Some(with) = &node.with {
615            formatting_space!();
616            if self.cfg.emit_assert_for_import_attributes {
617                keyword!("assert");
618            } else {
619                keyword!("with")
620            };
621            formatting_space!();
622            emit!(with);
623        }
624
625        semi!();
626
627        srcmap!(node, false);
628    }
629
630    #[emitter]
631
632    fn emit_lit(&mut self, node: &Lit) -> Result {
633        self.emit_leading_comments_of_span(node.span(), false)?;
634
635        srcmap!(node, true);
636
637        match *node {
638            Lit::Bool(Bool { value, .. }) => {
639                if value {
640                    keyword!("true")
641                } else {
642                    keyword!("false")
643                }
644            }
645            Lit::Null(Null { .. }) => keyword!("null"),
646            Lit::Str(ref s) => emit!(s),
647            Lit::BigInt(ref s) => emit!(s),
648            Lit::Num(ref n) => emit!(n),
649            Lit::Regex(ref n) => {
650                punct!("/");
651                self.wr.write_str(&n.exp)?;
652                punct!("/");
653                self.wr.write_str(&n.flags)?;
654            }
655            Lit::JSXText(ref n) => emit!(n),
656        }
657    }
658
659    fn emit_atom(&mut self, span: Span, value: &Atom) -> Result {
660        self.wr.write_str_lit(span, value)?;
661
662        Ok(())
663    }
664
665    #[emitter]
666
667    fn emit_str_lit(&mut self, node: &Str) -> Result {
668        self.wr.commit_pending_semi()?;
669
670        self.emit_leading_comments_of_span(node.span(), false)?;
671
672        srcmap!(node, true);
673
674        if &*node.value == "use strict"
675            && node.raw.is_some()
676            && node.raw.as_ref().unwrap().contains('\\')
677            && (!self.cfg.inline_script || !node.raw.as_ref().unwrap().contains("script"))
678        {
679            self.wr
680                .write_str_lit(DUMMY_SP, node.raw.as_ref().unwrap())?;
681
682            srcmap!(node, false);
683
684            return Ok(());
685        }
686
687        let target = self.cfg.target;
688
689        if !self.cfg.minify {
690            if let Some(raw) = &node.raw {
691                if (!self.cfg.ascii_only || raw.is_ascii())
692                    && (!self.cfg.inline_script || !node.raw.as_ref().unwrap().contains("script"))
693                {
694                    self.wr.write_str_lit(DUMMY_SP, raw)?;
695                    return Ok(());
696                }
697            }
698        }
699
700        let (quote_char, mut value) = get_quoted_utf16(&node.value, self.cfg.ascii_only, target);
701
702        if self.cfg.inline_script {
703            value = CowStr::Owned(
704                replace_close_inline_script(&value)
705                    .replace("\x3c!--", "\\x3c!--")
706                    .replace("--\x3e", "--\\x3e")
707                    .into(),
708            );
709        }
710
711        let quote_str = [quote_char.as_byte()];
712        let quote_str = unsafe {
713            // Safety: quote_char is valid ascii
714            str::from_utf8_unchecked(&quote_str)
715        };
716
717        self.wr.write_str(quote_str)?;
718        self.wr.write_str_lit(DUMMY_SP, &value)?;
719        self.wr.write_str(quote_str)?;
720
721        // srcmap!(node, false);
722    }
723
724    #[emitter]
725
726    fn emit_num_lit(&mut self, num: &Number) -> Result {
727        self.emit_num_lit_internal(num, false)?;
728    }
729
730    /// `1.toString` is an invalid property access,
731    /// should emit a dot after the literal if return true
732    fn emit_num_lit_internal(
733        &mut self,
734        num: &Number,
735        mut detect_dot: bool,
736    ) -> std::result::Result<bool, io::Error> {
737        self.wr.commit_pending_semi()?;
738
739        self.emit_leading_comments_of_span(num.span(), false)?;
740
741        // Handle infinity
742        if num.value.is_infinite() && num.raw.is_none() {
743            if num.value.is_sign_negative() {
744                self.wr.write_str_lit(num.span, "-")?;
745            }
746            self.wr.write_str_lit(num.span, "Infinity")?;
747
748            return Ok(false);
749        }
750
751        let mut striped_raw = None;
752        let mut value = String::default();
753
754        srcmap!(self, num, true);
755
756        if self.cfg.minify {
757            if num.value.is_infinite() && num.raw.is_some() {
758                self.wr.write_str_lit(DUMMY_SP, num.raw.as_ref().unwrap())?;
759            } else {
760                value = minify_number(num.value, &mut detect_dot);
761                self.wr.write_str_lit(DUMMY_SP, &value)?;
762            }
763        } else {
764            match &num.raw {
765                Some(raw) => {
766                    if raw.len() > 2 && self.cfg.target < EsVersion::Es2015 && {
767                        let slice = &raw.as_bytes()[..2];
768                        slice == b"0b" || slice == b"0o" || slice == b"0B" || slice == b"0O"
769                    } {
770                        if num.value.is_infinite() && num.raw.is_some() {
771                            self.wr.write_str_lit(DUMMY_SP, num.raw.as_ref().unwrap())?;
772                        } else {
773                            value = num.value.to_string();
774                            self.wr.write_str_lit(DUMMY_SP, &value)?;
775                        }
776                    } else if raw.len() > 2
777                        && self.cfg.target < EsVersion::Es2021
778                        && raw.contains('_')
779                    {
780                        let value = raw.replace('_', "");
781                        self.wr.write_str_lit(DUMMY_SP, &value)?;
782
783                        striped_raw = Some(value);
784                    } else {
785                        self.wr.write_str_lit(DUMMY_SP, raw)?;
786
787                        if !detect_dot {
788                            return Ok(false);
789                        }
790
791                        striped_raw = Some(raw.replace('_', ""));
792                    }
793                }
794                _ => {
795                    value = num.value.to_string();
796                    self.wr.write_str_lit(DUMMY_SP, &value)?;
797                }
798            }
799        }
800
801        // fast return
802        if !detect_dot {
803            return Ok(false);
804        }
805
806        Ok(striped_raw
807            .map(|raw| {
808                if raw.bytes().all(|c| c.is_ascii_digit()) {
809                    // Maybe legacy octal
810                    // Do we really need to support pre es5?
811                    let slice = raw.as_bytes();
812                    if slice.len() >= 2 && slice[0] == b'0' {
813                        return false;
814                    }
815
816                    return true;
817                }
818
819                false
820            })
821            .unwrap_or_else(|| {
822                let bytes = value.as_bytes();
823
824                if !bytes.contains(&b'.') && !bytes.contains(&b'e') {
825                    return true;
826                }
827
828                false
829            }))
830    }
831
832    #[emitter]
833    fn emit_big_lit(&mut self, v: &BigInt) -> Result {
834        self.emit_leading_comments_of_span(v.span, false)?;
835
836        if self.cfg.minify {
837            let value = if *v.value >= 10000000000000000_i64.into() {
838                format!("0x{}", v.value.to_str_radix(16))
839            } else if *v.value <= (-10000000000000000_i64).into() {
840                format!("-0x{}", (-*v.value.clone()).to_str_radix(16))
841            } else {
842                v.value.to_string()
843            };
844            self.wr.write_lit(v.span, &value)?;
845            self.wr.write_lit(v.span, "n")?;
846        } else {
847            match &v.raw {
848                Some(raw) => {
849                    if raw.len() > 2 && self.cfg.target < EsVersion::Es2021 && raw.contains('_') {
850                        self.wr.write_str_lit(v.span, &raw.replace('_', ""))?;
851                    } else {
852                        self.wr.write_str_lit(v.span, raw)?;
853                    }
854                }
855                _ => {
856                    self.wr.write_lit(v.span, &v.value.to_string())?;
857                    self.wr.write_lit(v.span, "n")?;
858                }
859            }
860        }
861    }
862
863    // fn emit_object_binding_pat(&mut self, node: &ObjectPat) -> Result {
864    //     self.wr.write_punct("{")?;
865    //     self.emit_list(
866    //         node.span(),
867    //         &node.props,
868    //         ListFormat::ObjectBindingPatternElements,
869    //     );
870    //     self.wr.write_punct("}")?;
871
872    //     Ok(())
873    // }
874
875    // fn emit_array_binding_pat(&mut self, node: &ArrayPat) -> Result {
876    //     self.wr.write_punct("[")?;
877    //     self.emit_list(
878    //         node.span(),
879    //         &node.elems,
880    //         ListFormat::ArrayBindingPatternElements,
881    //     );
882    //     self.wr.write_punct("]")?;
883
884    //     Ok(())
885    // }
886
887    #[emitter]
888    fn emit_callee(&mut self, node: &Callee) -> Result {
889        match *node {
890            Callee::Expr(ref e) => {
891                if let Expr::New(new) = &**e {
892                    self.emit_new(new, false)?;
893                } else {
894                    emit!(e);
895                }
896            }
897            Callee::Super(ref n) => emit!(n),
898            Callee::Import(ref n) => emit!(n),
899        }
900    }
901
902    #[emitter]
903    fn emit_super(&mut self, node: &Super) -> Result {
904        keyword!(node.span, "super");
905    }
906
907    #[emitter]
908    fn emit_import_callee(&mut self, node: &Import) -> Result {
909        keyword!(node.span, "import");
910        match node.phase {
911            ImportPhase::Source => {
912                punct!(".");
913                keyword!("source")
914            }
915            ImportPhase::Defer => {
916                punct!(".");
917                keyword!("defer")
918            }
919            _ => {}
920        }
921    }
922
923    #[emitter]
924
925    fn emit_expr(&mut self, node: &Expr) -> Result {
926        match node {
927            Expr::Array(ref n) => emit!(n),
928            Expr::Arrow(ref n) => emit!(n),
929            Expr::Assign(ref n) => emit!(n),
930            Expr::Await(ref n) => emit!(n),
931            Expr::Bin(ref n) => emit!(n),
932            Expr::Call(ref n) => emit!(n),
933            Expr::Class(ref n) => emit!(n),
934            Expr::Cond(ref n) => emit!(n),
935            Expr::Fn(ref n) => emit!(n),
936            Expr::Ident(ref n) => emit!(n),
937            Expr::Lit(ref n) => emit!(n),
938            Expr::Member(ref n) => emit!(n),
939            Expr::SuperProp(ref n) => emit!(n),
940            Expr::MetaProp(ref n) => emit!(n),
941            Expr::New(ref n) => emit!(n),
942            Expr::Object(ref n) => emit!(n),
943            Expr::Paren(ref n) => emit!(n),
944            Expr::Seq(ref n) => emit!(n),
945            Expr::TaggedTpl(ref n) => emit!(n),
946            Expr::This(ref n) => emit!(n),
947            Expr::Tpl(ref n) => emit!(n),
948            Expr::Unary(ref n) => emit!(n),
949            Expr::Update(ref n) => emit!(n),
950            Expr::Yield(ref n) => emit!(n),
951            Expr::PrivateName(ref n) => emit!(n),
952
953            Expr::JSXMember(ref n) => emit!(n),
954            Expr::JSXNamespacedName(ref n) => emit!(n),
955            Expr::JSXEmpty(ref n) => emit!(n),
956            Expr::JSXElement(ref n) => emit!(n),
957            Expr::JSXFragment(ref n) => emit!(n),
958
959            Expr::TsAs(ref n) => emit!(n),
960            Expr::TsNonNull(ref n) => emit!(n),
961            Expr::TsTypeAssertion(ref n) => emit!(n),
962            Expr::TsConstAssertion(ref n) => emit!(n),
963            Expr::TsInstantiation(ref n) => emit!(n),
964            Expr::OptChain(ref n) => emit!(n),
965            Expr::Invalid(ref n) => emit!(n),
966            Expr::TsSatisfies(n) => {
967                emit!(n)
968            }
969        }
970
971        if self.comments.is_some() {
972            self.emit_trailing_comments_of_pos(node.span().hi, true, true)?;
973        }
974    }
975
976    #[emitter]
977    fn emit_opt_chain(&mut self, n: &OptChainExpr) -> Result {
978        self.emit_leading_comments_of_span(n.span(), false)?;
979
980        match &*n.base {
981            OptChainBase::Member(ref e) => {
982                if let Expr::New(new) = &*e.obj {
983                    self.emit_new(new, false)?;
984                } else {
985                    emit!(e.obj);
986                }
987                if n.optional {
988                    punct!("?.");
989                } else if !e.prop.is_computed() {
990                    punct!(".");
991                }
992
993                match &e.prop {
994                    MemberProp::Computed(computed) => emit!(computed),
995                    MemberProp::Ident(i) => emit!(i),
996                    MemberProp::PrivateName(p) => emit!(p),
997                }
998            }
999            OptChainBase::Call(ref e) => {
1000                debug_assert!(!e.callee.is_new());
1001                emit!(e.callee);
1002
1003                if n.optional {
1004                    punct!("?.");
1005                }
1006
1007                punct!("(");
1008                self.emit_expr_or_spreads(n.span(), &e.args, ListFormat::CallExpressionArguments)?;
1009                punct!(")");
1010            }
1011        }
1012    }
1013
1014    #[emitter]
1015    fn emit_invalid(&mut self, n: &Invalid) -> Result {
1016        self.emit_leading_comments_of_span(n.span, false)?;
1017
1018        self.wr.write_str_lit(n.span, "<invalid>")?;
1019    }
1020
1021    #[emitter]
1022    fn emit_call_expr(&mut self, node: &CallExpr) -> Result {
1023        self.wr.commit_pending_semi()?;
1024
1025        self.emit_leading_comments_of_span(node.span(), false)?;
1026
1027        srcmap!(node, true);
1028
1029        emit!(node.callee);
1030
1031        if let Some(type_args) = &node.type_args {
1032            emit!(type_args);
1033        }
1034
1035        punct!("(");
1036        self.emit_expr_or_spreads(node.span(), &node.args, ListFormat::CallExpressionArguments)?;
1037        punct!(")");
1038
1039        // srcmap!(node, false);
1040    }
1041
1042    fn emit_new(&mut self, node: &NewExpr, should_ignore_empty_args: bool) -> Result {
1043        self.wr.commit_pending_semi()?;
1044
1045        self.emit_leading_comments_of_span(node.span(), false)?;
1046
1047        srcmap!(self, node, true);
1048
1049        keyword!(self, "new");
1050
1051        let starts_with_alpha_num = node.callee.starts_with_alpha_num();
1052
1053        if starts_with_alpha_num {
1054            space!(self);
1055        } else {
1056            formatting_space!(self);
1057        }
1058        emit!(self, node.callee);
1059
1060        if let Some(type_args) = &node.type_args {
1061            emit!(self, type_args);
1062        }
1063
1064        if let Some(ref args) = node.args {
1065            if !(self.cfg.minify && args.is_empty() && should_ignore_empty_args) {
1066                punct!(self, "(");
1067                self.emit_expr_or_spreads(node.span(), args, ListFormat::NewExpressionArguments)?;
1068                punct!(self, ")");
1069            }
1070        }
1071
1072        // srcmap!(self, node, false);
1073
1074        // if it's false, it means it doesn't come from emit_expr,
1075        // we need to compensate that
1076        if !should_ignore_empty_args && self.comments.is_some() {
1077            self.emit_trailing_comments_of_pos(node.span().hi, true, true)?;
1078        }
1079
1080        Ok(())
1081    }
1082
1083    #[emitter]
1084    fn emit_new_expr(&mut self, node: &NewExpr) -> Result {
1085        self.emit_new(node, true)?;
1086    }
1087
1088    #[emitter]
1089    fn emit_member_expr(&mut self, node: &MemberExpr) -> Result {
1090        self.emit_leading_comments_of_span(node.span(), false)?;
1091
1092        srcmap!(node, true);
1093
1094        let mut needs_2dots_for_property_access = false;
1095
1096        match &*node.obj {
1097            Expr::New(new) => {
1098                self.emit_new(new, false)?;
1099            }
1100            Expr::Lit(Lit::Num(num)) => {
1101                needs_2dots_for_property_access = self.emit_num_lit_internal(num, true)?;
1102            }
1103            _ => {
1104                emit!(node.obj);
1105            }
1106        }
1107
1108        match &node.prop {
1109            MemberProp::Computed(computed) => emit!(computed),
1110            MemberProp::Ident(ident) => {
1111                if needs_2dots_for_property_access {
1112                    if node.prop.span().lo() >= BytePos(2) {
1113                        self.emit_leading_comments(node.prop.span().lo() - BytePos(2), false)?;
1114                    }
1115                    punct!(".");
1116                }
1117                if node.prop.span().lo() >= BytePos(1) {
1118                    self.emit_leading_comments(node.prop.span().lo() - BytePos(1), false)?;
1119                }
1120                punct!(".");
1121                emit!(ident);
1122            }
1123            MemberProp::PrivateName(private) => {
1124                if needs_2dots_for_property_access {
1125                    if node.prop.span().lo() >= BytePos(2) {
1126                        self.emit_leading_comments(node.prop.span().lo() - BytePos(2), false)?;
1127                    }
1128                    punct!(".");
1129                }
1130                if node.prop.span().lo() >= BytePos(1) {
1131                    self.emit_leading_comments(node.prop.span().lo() - BytePos(1), false)?;
1132                }
1133                punct!(".");
1134                emit!(private);
1135            }
1136        }
1137
1138        srcmap!(node, false);
1139    }
1140
1141    #[emitter]
1142    fn emit_super_expr(&mut self, node: &SuperPropExpr) -> Result {
1143        self.emit_leading_comments_of_span(node.span(), false)?;
1144
1145        srcmap!(node, true);
1146
1147        emit!(node.obj);
1148
1149        match &node.prop {
1150            SuperProp::Computed(computed) => emit!(computed),
1151            SuperProp::Ident(i) => {
1152                if node.prop.span().lo() >= BytePos(1) {
1153                    self.emit_leading_comments(node.prop.span().lo() - BytePos(1), false)?;
1154                }
1155                punct!(".");
1156                emit!(i);
1157            }
1158        }
1159    }
1160
1161    #[emitter]
1162    fn emit_arrow_expr(&mut self, node: &ArrowExpr) -> Result {
1163        self.emit_leading_comments_of_span(node.span(), false)?;
1164
1165        srcmap!(node, true);
1166
1167        let space = !self.cfg.minify
1168            || match node.params.as_slice() {
1169                [Pat::Ident(_)] => true,
1170                _ => false,
1171            };
1172
1173        if node.is_async {
1174            keyword!("async");
1175            if space {
1176                space!();
1177            } else {
1178                formatting_space!();
1179            }
1180        }
1181        if node.is_generator {
1182            punct!("*")
1183        }
1184
1185        let parens = !self.cfg.minify
1186            || match node.params.as_slice() {
1187                [Pat::Ident(i)] => self.has_trailing_comment(i.span),
1188                _ => true,
1189            };
1190
1191        emit!(node.type_params);
1192
1193        if parens {
1194            punct!("(");
1195        }
1196
1197        self.emit_list(node.span, Some(&node.params), ListFormat::CommaListElements)?;
1198        if parens {
1199            punct!(")");
1200        }
1201
1202        if let Some(ty) = &node.return_type {
1203            punct!(":");
1204            formatting_space!();
1205            emit!(ty);
1206            formatting_space!();
1207        }
1208
1209        punct!("=>");
1210        emit!(node.body);
1211    }
1212
1213    #[emitter]
1214    fn emit_meta_prop_expr(&mut self, node: &MetaPropExpr) -> Result {
1215        if self.comments.is_some() {
1216            self.emit_leading_comments_of_span(node.span(), false)?;
1217        }
1218
1219        srcmap!(node, true);
1220
1221        match node.kind {
1222            MetaPropKind::ImportMeta => keyword!("import.meta"),
1223
1224            MetaPropKind::NewTarget => keyword!("new.target"),
1225        }
1226    }
1227
1228    #[emitter]
1229    fn emit_seq_expr(&mut self, node: &SeqExpr) -> Result {
1230        self.emit_leading_comments_of_span(node.span(), false)?;
1231
1232        srcmap!(node, true);
1233
1234        let mut first = true;
1235        //TODO: Indention
1236        for e in &node.exprs {
1237            if first {
1238                first = false
1239            } else {
1240                punct!(",");
1241                formatting_space!();
1242            }
1243
1244            emit!(e);
1245        }
1246    }
1247
1248    #[emitter]
1249    fn emit_assign_expr(&mut self, node: &AssignExpr) -> Result {
1250        self.emit_leading_comments_of_span(node.span(), false)?;
1251
1252        emit!(node.left);
1253        formatting_space!();
1254        operator!(node.op.as_str());
1255        formatting_space!();
1256        emit!(node.right);
1257    }
1258
1259    /// Prints operator and right node of a binary expression.
1260    #[inline(never)]
1261    fn emit_bin_expr_trailing(&mut self, node: &BinExpr) -> Result {
1262        // let indent_before_op = needs_indention(node, &node.left, node.op);
1263        // let indent_after_op = needs_indention(node, node.op, &node.right);
1264        let is_kwd_op = match node.op {
1265            op!("in") | op!("instanceof") => true,
1266            _ => false,
1267        };
1268
1269        let need_pre_space = if self.cfg.minify {
1270            if is_kwd_op {
1271                node.left.ends_with_alpha_num()
1272            } else {
1273                // space is mandatory to avoid outputting -->
1274                match *node.left {
1275                    Expr::Update(UpdateExpr {
1276                        prefix: false, op, ..
1277                    }) => matches!(
1278                        (op, node.op),
1279                        (op!("--"), op!(">") | op!(">>") | op!(">>>") | op!(">="))
1280                    ),
1281                    _ => false,
1282                }
1283            }
1284        } else {
1285            is_kwd_op
1286                || match *node.left {
1287                    Expr::Update(UpdateExpr { prefix: false, .. }) => true,
1288                    _ => false,
1289                }
1290        };
1291        if need_pre_space {
1292            space!(self);
1293        } else {
1294            formatting_space!(self);
1295        }
1296        operator!(self, node.op.as_str());
1297
1298        let need_post_space = if self.cfg.minify {
1299            if is_kwd_op {
1300                node.right.starts_with_alpha_num()
1301            } else if node.op == op!("/") {
1302                let span = node.right.span();
1303
1304                span.is_pure()
1305                    || self
1306                        .comments
1307                        .map_or(false, |comments| comments.has_leading(node.right.span().lo))
1308            } else {
1309                require_space_before_rhs(&node.right, &node.op)
1310            }
1311        } else {
1312            is_kwd_op
1313                || match *node.right {
1314                    Expr::Unary(..) | Expr::Update(UpdateExpr { prefix: true, .. }) => true,
1315                    _ => false,
1316                }
1317        };
1318        if need_post_space {
1319            space!(self);
1320        } else {
1321            formatting_space!(self);
1322        }
1323        emit!(self, node.right);
1324
1325        Ok(())
1326    }
1327
1328    #[emitter]
1329    fn emit_bin_expr(&mut self, node: &BinExpr) -> Result {
1330        self.emit_leading_comments_of_span(node.span(), false)?;
1331
1332        srcmap!(node, true);
1333
1334        {
1335            let mut left = Some(node);
1336            let mut lefts = Vec::new();
1337            while let Some(l) = left {
1338                lefts.push(l);
1339
1340                match &*l.left {
1341                    Expr::Bin(b) => {
1342                        left = Some(b);
1343                    }
1344                    _ => break,
1345                }
1346            }
1347
1348            let len = lefts.len();
1349
1350            for (i, left) in lefts.into_iter().rev().enumerate() {
1351                if i == 0 {
1352                    emit!(left.left);
1353                }
1354                // Check if it's last
1355                if i + 1 != len {
1356                    self.emit_bin_expr_trailing(left)?;
1357                }
1358            }
1359        }
1360
1361        self.emit_bin_expr_trailing(node)?;
1362    }
1363
1364    #[emitter]
1365    fn emit_decorator(&mut self, node: &Decorator) -> Result {
1366        self.emit_leading_comments_of_span(node.span(), false)?;
1367
1368        srcmap!(node, true);
1369
1370        punct!("@");
1371        emit!(node.expr);
1372        self.wr.write_line()?;
1373
1374        srcmap!(node, false);
1375    }
1376
1377    #[emitter]
1378    fn emit_class_expr(&mut self, node: &ClassExpr) -> Result {
1379        self.emit_leading_comments_of_span(node.span(), false)?;
1380
1381        srcmap!(node, true);
1382
1383        for dec in &node.class.decorators {
1384            emit!(dec);
1385        }
1386
1387        if node.class.is_abstract {
1388            keyword!("abstract");
1389            space!();
1390        }
1391
1392        keyword!("class");
1393
1394        if let Some(ref i) = node.ident {
1395            space!();
1396            emit!(i);
1397            emit!(node.class.type_params);
1398        }
1399
1400        self.emit_class_trailing(&node.class)?;
1401    }
1402
1403    #[emitter]
1404    fn emit_class_trailing(&mut self, node: &Class) -> Result {
1405        if node.super_class.is_some() {
1406            space!();
1407            keyword!("extends");
1408
1409            {
1410                let starts_with_alpha_num =
1411                    node.super_class.as_ref().unwrap().starts_with_alpha_num();
1412
1413                if starts_with_alpha_num {
1414                    space!();
1415                } else {
1416                    formatting_space!()
1417                }
1418            }
1419            emit!(node.super_class);
1420            emit!(node.super_type_params);
1421        }
1422
1423        if !node.implements.is_empty() {
1424            space!();
1425            keyword!("implements");
1426
1427            space!();
1428
1429            self.emit_list(
1430                node.span,
1431                Some(&node.implements),
1432                ListFormat::ClassHeritageClauses,
1433            )?;
1434        }
1435
1436        formatting_space!();
1437
1438        punct!("{");
1439
1440        self.emit_list(node.span, Some(&node.body), ListFormat::ClassMembers)?;
1441
1442        srcmap!(node, false, true);
1443        punct!("}");
1444    }
1445
1446    #[emitter]
1447
1448    fn emit_class_member(&mut self, node: &ClassMember) -> Result {
1449        match *node {
1450            ClassMember::Constructor(ref n) => emit!(n),
1451            ClassMember::ClassProp(ref n) => emit!(n),
1452            ClassMember::Method(ref n) => emit!(n),
1453            ClassMember::PrivateMethod(ref n) => emit!(n),
1454            ClassMember::PrivateProp(ref n) => emit!(n),
1455            ClassMember::TsIndexSignature(ref n) => emit!(n),
1456            ClassMember::Empty(ref n) => emit!(n),
1457            ClassMember::StaticBlock(ref n) => emit!(n),
1458            ClassMember::AutoAccessor(ref n) => emit!(n),
1459        }
1460    }
1461
1462    #[emitter]
1463    fn emit_auto_accessor(&mut self, n: &AutoAccessor) -> Result {
1464        self.emit_list(n.span, Some(&n.decorators), ListFormat::Decorators)?;
1465
1466        self.emit_accessibility(n.accessibility)?;
1467
1468        if n.is_static {
1469            keyword!("static");
1470            space!();
1471        }
1472
1473        if n.is_abstract {
1474            keyword!("abstract");
1475            space!();
1476        }
1477
1478        if n.is_override {
1479            keyword!("override");
1480            space!();
1481        }
1482
1483        keyword!("accessor");
1484        space!();
1485
1486        emit!(n.key);
1487
1488        if let Some(type_ann) = &n.type_ann {
1489            if n.definite {
1490                punct!("!");
1491            }
1492            punct!(":");
1493            space!();
1494            emit!(type_ann);
1495        }
1496
1497        if let Some(init) = &n.value {
1498            formatting_space!();
1499            punct!("=");
1500            formatting_space!();
1501            emit!(init);
1502        }
1503
1504        semi!();
1505    }
1506
1507    #[emitter]
1508    fn emit_key(&mut self, n: &Key) -> Result {
1509        match n {
1510            Key::Private(n) => emit!(n),
1511            Key::Public(n) => emit!(n),
1512        }
1513    }
1514
1515    #[emitter]
1516    fn emit_private_method(&mut self, n: &PrivateMethod) -> Result {
1517        self.emit_leading_comments_of_span(n.span(), false)?;
1518
1519        srcmap!(n, true);
1520
1521        if n.is_static {
1522            keyword!("static");
1523            space!();
1524        }
1525        match n.kind {
1526            MethodKind::Method => {
1527                if n.function.is_async {
1528                    keyword!("async");
1529                    space!();
1530                }
1531                if n.function.is_generator {
1532                    punct!("*");
1533                }
1534
1535                emit!(n.key);
1536            }
1537            MethodKind::Getter => {
1538                keyword!("get");
1539                space!();
1540
1541                emit!(n.key);
1542            }
1543            MethodKind::Setter => {
1544                keyword!("set");
1545                space!();
1546
1547                emit!(n.key);
1548            }
1549        }
1550
1551        self.emit_fn_trailing(&n.function)?;
1552    }
1553
1554    #[emitter]
1555    fn emit_bool(&mut self, n: &Bool) -> Result {
1556        self.emit_leading_comments_of_span(n.span(), false)?;
1557
1558        if n.value {
1559            keyword!(n.span, "true")
1560        } else {
1561            keyword!(n.span, "false")
1562        }
1563    }
1564
1565    #[emitter]
1566    fn emit_class_method(&mut self, n: &ClassMethod) -> Result {
1567        self.emit_leading_comments_of_span(n.span(), false)?;
1568
1569        self.emit_leading_comments_of_span(n.key.span(), false)?;
1570
1571        srcmap!(n, true);
1572
1573        for d in &n.function.decorators {
1574            emit!(d);
1575        }
1576
1577        self.emit_accessibility(n.accessibility)?;
1578
1579        if n.is_static {
1580            keyword!("static");
1581
1582            let starts_with_alpha_num = match n.kind {
1583                MethodKind::Method => {
1584                    if n.function.is_async {
1585                        true
1586                    } else if n.function.is_generator {
1587                        false
1588                    } else {
1589                        n.key.starts_with_alpha_num()
1590                    }
1591                }
1592                MethodKind::Getter => true,
1593                MethodKind::Setter => true,
1594            };
1595
1596            if starts_with_alpha_num {
1597                space!();
1598            } else {
1599                formatting_space!();
1600            }
1601        }
1602
1603        if n.is_abstract {
1604            keyword!("abstract");
1605            space!()
1606        }
1607
1608        if n.is_override {
1609            keyword!("override");
1610            space!()
1611        }
1612
1613        match n.kind {
1614            MethodKind::Method => {
1615                if n.function.is_async {
1616                    keyword!("async");
1617                    space!();
1618                }
1619                if n.function.is_generator {
1620                    punct!("*");
1621                }
1622
1623                emit!(n.key);
1624            }
1625            MethodKind::Getter => {
1626                keyword!("get");
1627
1628                if n.key.starts_with_alpha_num() {
1629                    space!();
1630                } else {
1631                    formatting_space!()
1632                }
1633
1634                emit!(n.key);
1635            }
1636            MethodKind::Setter => {
1637                keyword!("set");
1638
1639                if n.key.starts_with_alpha_num() {
1640                    space!();
1641                } else {
1642                    formatting_space!()
1643                }
1644
1645                emit!(n.key);
1646            }
1647        }
1648
1649        if n.is_optional {
1650            punct!("?");
1651        }
1652
1653        if let Some(type_params) = &n.function.type_params {
1654            emit!(type_params);
1655        }
1656
1657        punct!("(");
1658        self.emit_list(
1659            n.function.span,
1660            Some(&n.function.params),
1661            ListFormat::CommaListElements,
1662        )?;
1663
1664        punct!(")");
1665
1666        if let Some(ty) = &n.function.return_type {
1667            punct!(":");
1668            formatting_space!();
1669            emit!(ty);
1670        }
1671
1672        if let Some(body) = &n.function.body {
1673            formatting_space!();
1674            emit!(body);
1675        } else {
1676            formatting_semi!()
1677        }
1678    }
1679
1680    #[emitter]
1681    fn emit_private_prop(&mut self, n: &PrivateProp) -> Result {
1682        self.emit_leading_comments_of_span(n.span(), false)?;
1683
1684        srcmap!(n, true);
1685
1686        self.emit_list(n.span, Some(&n.decorators), ListFormat::Decorators)?;
1687
1688        self.emit_accessibility(n.accessibility)?;
1689
1690        if n.is_static {
1691            keyword!("static");
1692            space!();
1693        }
1694
1695        if n.is_override {
1696            keyword!("override");
1697            space!()
1698        }
1699
1700        if n.readonly {
1701            keyword!("readonly");
1702            space!();
1703        }
1704
1705        emit!(n.key);
1706
1707        if n.is_optional {
1708            punct!("?");
1709        }
1710
1711        if let Some(type_ann) = &n.type_ann {
1712            if n.definite {
1713                punct!("!");
1714            }
1715            punct!(":");
1716            space!();
1717            emit!(type_ann);
1718        }
1719
1720        if let Some(value) = &n.value {
1721            formatting_space!();
1722            punct!("=");
1723            formatting_space!();
1724
1725            if value.is_seq() {
1726                punct!("(");
1727                emit!(value);
1728                punct!(")");
1729            } else {
1730                emit!(value);
1731            }
1732        }
1733
1734        semi!();
1735
1736        srcmap!(n, false);
1737    }
1738
1739    #[emitter]
1740    fn emit_class_prop(&mut self, n: &ClassProp) -> Result {
1741        self.emit_leading_comments_of_span(n.span(), false)?;
1742        srcmap!(n, true);
1743
1744        for dec in &n.decorators {
1745            emit!(dec)
1746        }
1747
1748        if n.declare {
1749            keyword!("declare");
1750            space!();
1751        }
1752
1753        self.emit_accessibility(n.accessibility)?;
1754
1755        if n.is_static {
1756            keyword!("static");
1757            space!();
1758        }
1759
1760        if n.is_abstract {
1761            keyword!("abstract");
1762            space!()
1763        }
1764
1765        if n.is_override {
1766            keyword!("override");
1767            space!()
1768        }
1769
1770        if n.readonly {
1771            keyword!("readonly");
1772            space!()
1773        }
1774
1775        emit!(n.key);
1776
1777        if n.is_optional {
1778            punct!("?");
1779        }
1780
1781        if let Some(ty) = &n.type_ann {
1782            if n.definite {
1783                punct!("!");
1784            }
1785            punct!(":");
1786            space!();
1787            emit!(ty);
1788        }
1789
1790        if let Some(v) = &n.value {
1791            formatting_space!();
1792            punct!("=");
1793            formatting_space!();
1794
1795            if v.is_seq() {
1796                punct!("(");
1797                emit!(v);
1798                punct!(")");
1799            } else {
1800                emit!(v);
1801            }
1802        }
1803
1804        semi!();
1805
1806        srcmap!(n, false);
1807    }
1808
1809    fn emit_accessibility(&mut self, n: Option<Accessibility>) -> Result {
1810        if let Some(a) = n {
1811            match a {
1812                Accessibility::Public => keyword!(self, "public"),
1813                Accessibility::Protected => keyword!(self, "protected"),
1814                Accessibility::Private => keyword!(self, "private"),
1815            }
1816            space!(self);
1817        }
1818
1819        Ok(())
1820    }
1821
1822    #[emitter]
1823
1824    fn emit_class_constructor(&mut self, n: &Constructor) -> Result {
1825        self.emit_leading_comments_of_span(n.span(), false)?;
1826
1827        srcmap!(n, true);
1828
1829        self.emit_accessibility(n.accessibility)?;
1830
1831        keyword!("constructor");
1832        punct!("(");
1833        self.emit_list(n.span(), Some(&n.params), ListFormat::Parameters)?;
1834        punct!(")");
1835
1836        if let Some(body) = &n.body {
1837            emit!(body);
1838        } else {
1839            formatting_semi!();
1840        }
1841    }
1842
1843    #[emitter]
1844    fn emit_static_block(&mut self, n: &StaticBlock) -> Result {
1845        self.emit_leading_comments_of_span(n.span(), false)?;
1846
1847        srcmap!(n, true);
1848
1849        keyword!("static");
1850        emit!(n.body);
1851
1852        srcmap!(n, false);
1853    }
1854
1855    #[emitter]
1856    fn emit_prop_name(&mut self, node: &PropName) -> Result {
1857        match node {
1858            PropName::Ident(ident) => {
1859                // TODO: Use write_symbol when ident is a symbol.
1860                self.emit_leading_comments_of_span(ident.span, false)?;
1861
1862                // Source map
1863                self.wr.commit_pending_semi()?;
1864
1865                srcmap!(ident, true);
1866
1867                if self.cfg.ascii_only {
1868                    if self.wr.can_ignore_invalid_unicodes() {
1869                        self.wr.write_symbol(
1870                            DUMMY_SP,
1871                            &get_ascii_only_ident(&ident.sym, true, self.cfg.target),
1872                        )?;
1873                    } else {
1874                        self.wr.write_symbol(
1875                            DUMMY_SP,
1876                            &get_ascii_only_ident(
1877                                &handle_invalid_unicodes(&ident.sym),
1878                                true,
1879                                self.cfg.target,
1880                            ),
1881                        )?;
1882                    }
1883                } else {
1884                    emit!(ident);
1885                }
1886            }
1887            PropName::Str(ref n) => emit!(n),
1888            PropName::Num(ref n) => emit!(n),
1889            PropName::BigInt(ref n) => emit!(n),
1890            PropName::Computed(ref n) => emit!(n),
1891        }
1892    }
1893
1894    #[emitter]
1895    fn emit_computed_prop_name(&mut self, n: &ComputedPropName) -> Result {
1896        srcmap!(n, true);
1897
1898        punct!("[");
1899        emit!(n.expr);
1900        punct!("]");
1901
1902        srcmap!(n, false);
1903    }
1904
1905    #[emitter]
1906    fn emit_cond_expr(&mut self, node: &CondExpr) -> Result {
1907        self.emit_leading_comments_of_span(node.span(), false)?;
1908
1909        srcmap!(node, true);
1910
1911        emit!(node.test);
1912        formatting_space!();
1913        punct!("?");
1914        formatting_space!();
1915        emit!(node.cons);
1916        formatting_space!();
1917        punct!(":");
1918        formatting_space!();
1919        emit!(node.alt);
1920    }
1921
1922    #[emitter]
1923    fn emit_fn_expr(&mut self, n: &FnExpr) -> Result {
1924        self.emit_leading_comments_of_span(n.span(), false)?;
1925
1926        self.wr.commit_pending_semi()?;
1927
1928        srcmap!(n, true);
1929
1930        if n.function.is_async {
1931            keyword!("async");
1932            space!();
1933            keyword!("function");
1934        } else {
1935            keyword!("function");
1936        }
1937
1938        if n.function.is_generator {
1939            punct!("*");
1940        }
1941        if let Some(ref i) = n.ident {
1942            space!();
1943            emit!(i);
1944        }
1945
1946        self.emit_fn_trailing(&n.function)?;
1947    }
1948
1949    /// prints `(b){}` from `function a(b){}`
1950    #[emitter]
1951    fn emit_fn_trailing(&mut self, node: &Function) -> Result {
1952        if let Some(type_params) = &node.type_params {
1953            emit!(type_params);
1954        }
1955
1956        punct!("(");
1957        self.emit_list(node.span, Some(&node.params), ListFormat::CommaListElements)?;
1958        punct!(")");
1959
1960        if let Some(ty) = &node.return_type {
1961            punct!(":");
1962            formatting_space!();
1963            emit!(ty);
1964        }
1965
1966        if let Some(body) = &node.body {
1967            formatting_space!();
1968            self.emit_block_stmt_inner(body, true)?;
1969        } else {
1970            semi!();
1971        }
1972
1973        // srcmap!(node, false);
1974    }
1975
1976    #[emitter]
1977    fn emit_block_stmt_or_expr(&mut self, node: &BlockStmtOrExpr) -> Result {
1978        match node {
1979            BlockStmtOrExpr::BlockStmt(block) => {
1980                self.emit_block_stmt_inner(block, true)?;
1981            }
1982            BlockStmtOrExpr::Expr(expr) => {
1983                self.wr.increase_indent()?;
1984                emit!(expr);
1985                self.wr.decrease_indent()?;
1986            }
1987        }
1988    }
1989
1990    #[emitter]
1991    fn emit_this_expr(&mut self, node: &ThisExpr) -> Result {
1992        self.emit_leading_comments_of_span(node.span(), false)?;
1993
1994        keyword!(node.span, "this");
1995    }
1996
1997    #[emitter]
1998    fn emit_tpl_lit(&mut self, node: &Tpl) -> Result {
1999        debug_assert!(node.quasis.len() == node.exprs.len() + 1);
2000
2001        self.emit_leading_comments_of_span(node.span(), false)?;
2002
2003        srcmap!(node, true);
2004
2005        punct!("`");
2006
2007        for i in 0..(node.quasis.len() + node.exprs.len()) {
2008            if i % 2 == 0 {
2009                emit!(node.quasis[i / 2]);
2010            } else {
2011                punct!("${");
2012                emit!(node.exprs[i / 2]);
2013                punct!("}");
2014            }
2015        }
2016
2017        punct!("`");
2018
2019        srcmap!(node, false);
2020    }
2021
2022    #[emitter]
2023    fn emit_quasi(&mut self, node: &TplElement) -> Result {
2024        let raw = node.raw.replace("\r\n", "\n").replace('\r', "\n");
2025        if self.cfg.minify || (self.cfg.ascii_only && !node.raw.is_ascii()) {
2026            let v = get_template_element_from_raw(&raw, self.cfg.ascii_only);
2027            let span = node.span();
2028
2029            let mut last_offset_gen = 0;
2030            let mut last_offset_origin = 0;
2031            for ((offset_gen, _), mat) in v
2032                .match_indices('\n')
2033                .zip(NEW_LINE_TPL_REGEX.find_iter(&raw))
2034            {
2035                // If the string starts with a newline char, then adding a mark is redundant.
2036                // This catches both "no newlines" and "newline after several chars".
2037                if offset_gen != 0 {
2038                    self.wr
2039                        .add_srcmap(span.lo + BytePos(last_offset_origin as u32))?;
2040                }
2041
2042                self.wr
2043                    .write_str_lit(DUMMY_SP, &v[last_offset_gen..=offset_gen])?;
2044                last_offset_gen = offset_gen + 1;
2045                last_offset_origin = mat.end();
2046            }
2047            self.wr
2048                .add_srcmap(span.lo + BytePos(last_offset_origin as u32))?;
2049            self.wr.write_str_lit(DUMMY_SP, &v[last_offset_gen..])?;
2050            self.wr.add_srcmap(span.hi)?;
2051        } else {
2052            self.wr.write_str_lit(node.span(), &raw)?;
2053        }
2054    }
2055
2056    #[emitter]
2057    fn emit_tagged_tpl_lit(&mut self, node: &TaggedTpl) -> Result {
2058        self.emit_leading_comments_of_span(node.span(), false)?;
2059
2060        srcmap!(node, true);
2061
2062        if let Expr::New(new) = &*node.tag {
2063            self.emit_new(new, false)?;
2064        } else {
2065            emit!(node.tag);
2066        }
2067
2068        emit!(node.type_params);
2069        self.emit_template_for_tagged_template(&node.tpl)?;
2070
2071        srcmap!(node, false);
2072    }
2073
2074    fn emit_template_for_tagged_template(&mut self, node: &Tpl) -> Result {
2075        debug_assert!(node.quasis.len() == node.exprs.len() + 1);
2076
2077        self.emit_leading_comments_of_span(node.span(), false)?;
2078
2079        srcmap!(self, node, true);
2080
2081        punct!(self, "`");
2082
2083        for i in 0..(node.quasis.len() + node.exprs.len()) {
2084            if i % 2 == 0 {
2085                self.emit_template_element_for_tagged_template(&node.quasis[i / 2])?;
2086            } else {
2087                punct!(self, "${");
2088                emit!(self, node.exprs[i / 2]);
2089                punct!(self, "}");
2090            }
2091        }
2092
2093        punct!(self, "`");
2094
2095        srcmap!(self, node, false);
2096
2097        Ok(())
2098    }
2099
2100    fn emit_template_element_for_tagged_template(&mut self, node: &TplElement) -> Result {
2101        srcmap!(self, node, true);
2102
2103        self.wr.write_str_lit(DUMMY_SP, &node.raw)?;
2104
2105        srcmap!(self, node, false);
2106
2107        Ok(())
2108    }
2109
2110    #[emitter]
2111    fn emit_unary_expr(&mut self, n: &UnaryExpr) -> Result {
2112        self.emit_leading_comments_of_span(n.span(), false)?;
2113
2114        srcmap!(n, true);
2115
2116        let need_formatting_space = match n.op {
2117            op!("typeof") | op!("void") | op!("delete") => {
2118                keyword!(n.op.as_str());
2119
2120                true
2121            }
2122            op!(unary, "+") | op!(unary, "-") | op!("!") | op!("~") => {
2123                punct!(n.op.as_str());
2124                false
2125            }
2126        };
2127
2128        if should_emit_whitespace_before_operand(n) {
2129            space!();
2130        } else if need_formatting_space {
2131            formatting_space!();
2132        }
2133
2134        emit!(n.arg);
2135    }
2136
2137    #[emitter]
2138    fn emit_update_expr(&mut self, node: &UpdateExpr) -> Result {
2139        self.emit_leading_comments_of_span(node.span(), false)?;
2140
2141        srcmap!(node, true);
2142
2143        if node.prefix {
2144            operator!(node.op.as_str());
2145            //TODO: Check if we should use should_emit_whitespace_before_operand
2146            emit!(node.arg);
2147        } else {
2148            emit!(node.arg);
2149            operator!(node.op.as_str());
2150        }
2151    }
2152
2153    #[emitter]
2154    fn emit_yield_expr(&mut self, node: &YieldExpr) -> Result {
2155        self.emit_leading_comments_of_span(node.span(), false)?;
2156
2157        srcmap!(node, true);
2158
2159        keyword!("yield");
2160        if node.delegate {
2161            operator!("*");
2162        }
2163
2164        if let Some(ref arg) = node.arg {
2165            let need_paren = node
2166                .arg
2167                .as_deref()
2168                .map(|expr| self.has_leading_comment(expr))
2169                .unwrap_or(false);
2170            if need_paren {
2171                punct!("(")
2172            } else if !node.delegate && arg.starts_with_alpha_num() {
2173                space!()
2174            } else {
2175                formatting_space!()
2176            }
2177
2178            emit!(node.arg);
2179            if need_paren {
2180                punct!(")")
2181            }
2182        }
2183    }
2184
2185    fn emit_expr_or_spreads(
2186        &mut self,
2187        parent_node: Span,
2188        nodes: &[ExprOrSpread],
2189        format: ListFormat,
2190    ) -> Result {
2191        self.emit_list(parent_node, Some(nodes), format)
2192    }
2193
2194    #[emitter]
2195    fn emit_expr_or_spread(&mut self, node: &ExprOrSpread) -> Result {
2196        if let Some(span) = node.spread {
2197            self.emit_leading_comments_of_span(span, false)?;
2198
2199            punct!("...");
2200        }
2201
2202        emit!(node.expr);
2203    }
2204
2205    #[emitter]
2206    fn emit_await_expr(&mut self, n: &AwaitExpr) -> Result {
2207        self.emit_leading_comments_of_span(n.span(), false)?;
2208
2209        srcmap!(n, true);
2210
2211        keyword!("await");
2212        space!();
2213
2214        emit!(&n.arg);
2215    }
2216
2217    #[emitter]
2218    fn emit_array_lit(&mut self, node: &ArrayLit) -> Result {
2219        self.emit_leading_comments_of_span(node.span(), false)?;
2220
2221        srcmap!(node, true);
2222
2223        punct!("[");
2224        let mut format = ListFormat::ArrayLiteralExpressionElements;
2225        if let Some(None) = node.elems.last() {
2226            format |= ListFormat::ForceTrailingComma;
2227        }
2228
2229        self.emit_list(node.span(), Some(&node.elems), format)?;
2230        punct!("]");
2231
2232        srcmap!(node, false);
2233    }
2234
2235    #[emitter]
2236    fn emit_object_lit(&mut self, node: &ObjectLit) -> Result {
2237        self.emit_leading_comments_of_span(node.span(), false)?;
2238
2239        srcmap!(node, true);
2240
2241        punct!("{");
2242
2243        let emit_new_line = !self.cfg.minify
2244            && !(node.props.is_empty() && is_empty_comments(&node.span(), &self.comments));
2245
2246        if emit_new_line {
2247            self.wr.write_line()?;
2248        }
2249
2250        let mut list_format =
2251            ListFormat::ObjectLiteralExpressionProperties | ListFormat::CanSkipTrailingComma;
2252
2253        if !emit_new_line {
2254            list_format -= ListFormat::MultiLine | ListFormat::Indented;
2255        }
2256
2257        self.emit_list(node.span(), Some(&node.props), list_format)?;
2258
2259        if emit_new_line {
2260            self.wr.write_line()?;
2261        }
2262
2263        srcmap!(node, false, true);
2264        punct!("}");
2265    }
2266
2267    #[emitter]
2268    fn emit_prop(&mut self, node: &Prop) -> Result {
2269        match *node {
2270            Prop::Shorthand(ref n) => emit!(n),
2271            Prop::KeyValue(ref n) => emit!(n),
2272            Prop::Assign(ref n) => emit!(n),
2273            Prop::Getter(ref n) => emit!(n),
2274            Prop::Setter(ref n) => emit!(n),
2275            Prop::Method(ref n) => emit!(n),
2276        }
2277    }
2278
2279    #[emitter]
2280    fn emit_kv_prop(&mut self, node: &KeyValueProp) -> Result {
2281        self.emit_leading_comments_of_span(node.span(), false)?;
2282        let key_span = node.key.span();
2283        let value_span = node.value.span();
2284        if !key_span.is_dummy() {
2285            self.wr.add_srcmap(key_span.lo)?;
2286        }
2287        emit!(node.key);
2288        if !key_span.is_dummy() && value_span.is_dummy() {
2289            self.wr.add_srcmap(key_span.hi)?;
2290        }
2291        punct!(":");
2292        formatting_space!();
2293        if key_span.is_dummy() && !value_span.is_dummy() {
2294            self.wr.add_srcmap(value_span.lo)?;
2295        }
2296        emit!(node.value);
2297    }
2298
2299    #[emitter]
2300    fn emit_assign_prop(&mut self, node: &AssignProp) -> Result {
2301        self.emit_leading_comments_of_span(node.span(), false)?;
2302
2303        srcmap!(node, true);
2304
2305        emit!(node.key);
2306        punct!("=");
2307        emit!(node.value);
2308    }
2309
2310    #[emitter]
2311    fn emit_getter_prop(&mut self, node: &GetterProp) -> Result {
2312        self.emit_leading_comments_of_span(node.span(), false)?;
2313
2314        srcmap!(node, true);
2315
2316        keyword!("get");
2317
2318        let starts_with_alpha_num = match node.key {
2319            PropName::Str(_) | PropName::Computed(_) => false,
2320            _ => true,
2321        };
2322        if starts_with_alpha_num {
2323            space!();
2324        } else {
2325            formatting_space!();
2326        }
2327        emit!(node.key);
2328        formatting_space!();
2329        punct!("(");
2330        punct!(")");
2331        formatting_space!();
2332        emit!(node.body);
2333    }
2334
2335    #[emitter]
2336    fn emit_setter_prop(&mut self, node: &SetterProp) -> Result {
2337        self.emit_leading_comments_of_span(node.span(), false)?;
2338
2339        srcmap!(node, true);
2340
2341        keyword!("set");
2342
2343        let starts_with_alpha_num = match node.key {
2344            PropName::Str(_) | PropName::Computed(_) => false,
2345            _ => true,
2346        };
2347
2348        if starts_with_alpha_num {
2349            space!();
2350        } else {
2351            formatting_space!();
2352        }
2353
2354        emit!(node.key);
2355        formatting_space!();
2356
2357        punct!("(");
2358        if let Some(this) = &node.this_param {
2359            emit!(this);
2360            punct!(",");
2361
2362            formatting_space!();
2363        }
2364
2365        emit!(node.param);
2366
2367        punct!(")");
2368
2369        emit!(node.body);
2370    }
2371
2372    #[emitter]
2373    fn emit_method_prop(&mut self, node: &MethodProp) -> Result {
2374        self.emit_leading_comments_of_span(node.span(), false)?;
2375
2376        srcmap!(node, true);
2377
2378        if node.function.is_async {
2379            keyword!("async");
2380            space!();
2381        }
2382
2383        if node.function.is_generator {
2384            punct!("*");
2385        }
2386
2387        emit!(node.key);
2388        formatting_space!();
2389        // TODO
2390        self.emit_fn_trailing(&node.function)?;
2391    }
2392
2393    #[emitter]
2394    fn emit_paren_expr(&mut self, node: &ParenExpr) -> Result {
2395        self.wr.commit_pending_semi()?;
2396
2397        self.emit_leading_comments_of_span(node.span(), false)?;
2398
2399        srcmap!(node, true);
2400
2401        punct!("(");
2402        emit!(node.expr);
2403
2404        srcmap!(node, false, true);
2405        punct!(")");
2406    }
2407
2408    #[emitter]
2409    fn emit_private_name(&mut self, n: &PrivateName) -> Result {
2410        self.emit_leading_comments_of_span(n.span(), false)?;
2411
2412        srcmap!(n, true);
2413
2414        punct!("#");
2415        self.emit_ident_like(n.span, &n.name, false)?;
2416
2417        srcmap!(n, false);
2418    }
2419
2420    #[emitter]
2421    fn emit_binding_ident(&mut self, ident: &BindingIdent) -> Result {
2422        self.emit_ident_like(ident.span, &ident.sym, ident.optional)?;
2423
2424        if let Some(ty) = &ident.type_ann {
2425            punct!(":");
2426            formatting_space!();
2427            emit!(ty);
2428        }
2429
2430        // Call emitList directly since it could be an array of
2431        // TypeParameterDeclarations _or_ type arguments
2432
2433        // emitList(node, node.typeArguments, ListFormat::TypeParameters);
2434    }
2435
2436    #[emitter]
2437    fn emit_ident(&mut self, ident: &Ident) -> Result {
2438        self.emit_ident_like(ident.span, &ident.sym, ident.optional)?;
2439    }
2440
2441    #[emitter]
2442    fn emit_ident_name(&mut self, ident: &IdentName) -> Result {
2443        self.emit_ident_like(ident.span, &ident.sym, false)?;
2444    }
2445
2446    fn emit_ident_like(&mut self, span: Span, sym: &Atom, optional: bool) -> Result {
2447        // TODO: Use write_symbol when ident is a symbol.
2448        self.emit_leading_comments_of_span(span, false)?;
2449
2450        // Source map
2451        self.wr.commit_pending_semi()?;
2452
2453        srcmap!(self, span, true);
2454        // TODO: span
2455
2456        if self.cfg.ascii_only {
2457            if self.wr.can_ignore_invalid_unicodes() {
2458                self.wr
2459                    .write_symbol(DUMMY_SP, &get_ascii_only_ident(sym, false, self.cfg.target))?;
2460            } else {
2461                self.wr.write_symbol(
2462                    DUMMY_SP,
2463                    &get_ascii_only_ident(&handle_invalid_unicodes(sym), false, self.cfg.target),
2464                )?;
2465            }
2466        } else if self.wr.can_ignore_invalid_unicodes() {
2467            self.wr.write_symbol(DUMMY_SP, sym)?;
2468        } else {
2469            self.wr
2470                .write_symbol(DUMMY_SP, &handle_invalid_unicodes(sym))?;
2471        }
2472
2473        if optional {
2474            punct!(self, "?");
2475        }
2476
2477        // Call emitList directly since it could be an array of
2478        // TypeParameterDeclarations _or_ type arguments
2479
2480        // emitList(node, node.typeArguments, ListFormat::TypeParameters);
2481
2482        Ok(())
2483    }
2484
2485    fn emit_list<N: Node>(
2486        &mut self,
2487        parent_node: Span,
2488        children: Option<&[N]>,
2489        format: ListFormat,
2490    ) -> Result {
2491        self.emit_list5(
2492            parent_node,
2493            children,
2494            format,
2495            0,
2496            children.map(|c| c.len()).unwrap_or(0),
2497        )
2498    }
2499
2500    /// This method exists to reduce compile time.
2501    #[inline(never)]
2502    fn emit_first_of_list5(
2503        &mut self,
2504        parent_node: Span,
2505        children: Option<usize>,
2506        format: ListFormat,
2507        start: usize,
2508        count: usize,
2509    ) -> Option<Result> {
2510        if children.is_none() && format.contains(ListFormat::OptionalIfUndefined) {
2511            return Some(Ok(()));
2512        }
2513
2514        let is_empty = children.is_none() || start > children.unwrap() || count == 0;
2515        if is_empty && format.contains(ListFormat::OptionalIfEmpty) {
2516            return Some(Ok(()));
2517        }
2518
2519        if format.contains(ListFormat::BracketsMask) {
2520            if let Err(err) = self.wr.write_punct(None, format.opening_bracket()) {
2521                return Some(Err(err));
2522            }
2523
2524            if is_empty {
2525                if let Err(err) = self.emit_trailing_comments_of_pos(
2526                    {
2527                        // TODO: children.lo()
2528
2529                        parent_node.lo()
2530                    },
2531                    true,
2532                    false,
2533                ) {
2534                    return Some(Err(err));
2535                }
2536            }
2537        }
2538
2539        None
2540    }
2541
2542    /// This method exists to reduce compile time.
2543    #[inline(never)]
2544    fn emit_pre_child_for_list5(
2545        &mut self,
2546        parent_node: Span,
2547        format: ListFormat,
2548        previous_sibling: Option<Span>,
2549        child: Span,
2550        should_decrease_indent_after_emit: &mut bool,
2551        should_emit_intervening_comments: &mut bool,
2552    ) -> Result {
2553        // Write the delimiter if this is not the first node.
2554        if let Some(previous_sibling) = previous_sibling {
2555            // i.e
2556            //      function commentedParameters(
2557            //          /* Parameter a */
2558            //          a
2559            // /* End of parameter a */
2560            // -> this comment isn't considered to be trailing comment of parameter "a" due
2561            // to newline ,
2562            if format.contains(ListFormat::DelimitersMask)
2563                && previous_sibling.hi != parent_node.hi()
2564                && self.comments.is_some()
2565            {
2566                self.emit_leading_comments(previous_sibling.hi(), true)?;
2567            }
2568
2569            self.write_delim(format)?;
2570
2571            // Write either a line terminator or whitespace to separate the elements.
2572
2573            if self.cm.should_write_separating_line_terminator(
2574                Some(previous_sibling),
2575                Some(child),
2576                format,
2577            ) {
2578                // If a synthesized node in a single-line list starts on a new
2579                // line, we should increase the indent.
2580                if (format & (ListFormat::LinesMask | ListFormat::Indented))
2581                    == ListFormat::SingleLine
2582                    && !self.cfg.minify
2583                {
2584                    self.wr.increase_indent()?;
2585                    *should_decrease_indent_after_emit = true;
2586                }
2587
2588                if !self.cfg.minify {
2589                    self.wr.write_line()?;
2590                }
2591                *should_emit_intervening_comments = false;
2592            } else if format.contains(ListFormat::SpaceBetweenSiblings) {
2593                formatting_space!(self);
2594            }
2595        }
2596
2597        Ok(())
2598    }
2599
2600    /// This method exists to reduce compile time.
2601    #[inline(never)]
2602    fn emit_list_finisher_of_list5(
2603        &mut self,
2604        parent_node: Span,
2605        format: ListFormat,
2606        previous_sibling: Option<Span>,
2607        last_child: Option<Span>,
2608    ) -> Result {
2609        // Write a trailing comma, if requested.
2610        let has_trailing_comma = format.contains(ListFormat::ForceTrailingComma)
2611            || format.contains(ListFormat::AllowTrailingComma) && {
2612                if parent_node.is_dummy() {
2613                    false
2614                } else {
2615                    match self.cm.span_to_snippet(parent_node) {
2616                        Ok(snippet) => {
2617                            if snippet.len() < 3 {
2618                                false
2619                            } else {
2620                                let last_char = snippet.chars().last().unwrap();
2621                                snippet[..snippet.len() - last_char.len_utf8()]
2622                                    .trim()
2623                                    .ends_with(',')
2624                            }
2625                        }
2626                        _ => false,
2627                    }
2628                }
2629            };
2630
2631        if has_trailing_comma
2632            && format.contains(ListFormat::CommaDelimited)
2633            && (!self.cfg.minify || !format.contains(ListFormat::CanSkipTrailingComma))
2634        {
2635            punct!(self, ",");
2636            formatting_space!(self);
2637        }
2638
2639        {
2640            // Emit any trailing comment of the last element in the list
2641            // i.e
2642            //       var array = [...
2643            //          2
2644            //          /* end of element 2 */
2645            //       ];
2646
2647            let emit_trailing_comments = {
2648                // TODO:
2649                //
2650                // !(getEmitFlags(previousSibling).contains(EmitFlags::NoTrailingComments))
2651
2652                true
2653            };
2654
2655            if let Some(previous_sibling) = previous_sibling {
2656                if format.contains(ListFormat::DelimitersMask)
2657                    && previous_sibling.hi() != parent_node.hi()
2658                    && emit_trailing_comments
2659                    && self.comments.is_some()
2660                {
2661                    self.emit_leading_comments(previous_sibling.hi(), true)?;
2662                }
2663            }
2664        }
2665
2666        // Decrease the indent, if requested.
2667        if format.contains(ListFormat::Indented) && !self.cfg.minify {
2668            self.wr.decrease_indent()?;
2669        }
2670
2671        // Write the closing line terminator or closing whitespace.
2672        if self
2673            .cm
2674            .should_write_closing_line_terminator(parent_node, last_child, format)
2675        {
2676            if !self.cfg.minify {
2677                self.wr.write_line()?;
2678            }
2679        } else if format.contains(ListFormat::SpaceBetweenBraces) && !self.cfg.minify {
2680            self.wr.write_space()?;
2681        }
2682
2683        Ok(())
2684    }
2685
2686    /// This method exists to reduce compile time.
2687    #[inline(never)]
2688    fn emit_last_of_list5(
2689        &mut self,
2690        parent_node: Span,
2691        is_empty: bool,
2692        format: ListFormat,
2693        _start: usize,
2694        _count: usize,
2695    ) -> Result {
2696        if format.contains(ListFormat::BracketsMask) {
2697            if is_empty {
2698                self.emit_leading_comments(
2699                    {
2700                        //TODO: children.hi()
2701
2702                        parent_node.hi()
2703                    },
2704                    true,
2705                )?; // Emit leading comments within empty lists
2706            }
2707            self.wr.write_punct(None, format.closing_bracket())?;
2708        }
2709
2710        Ok(())
2711    }
2712
2713    fn emit_list5<N: Node>(
2714        &mut self,
2715        parent_node: Span,
2716        children: Option<&[N]>,
2717        format: ListFormat,
2718        start: usize,
2719        count: usize,
2720    ) -> Result {
2721        if let Some(result) =
2722            self.emit_first_of_list5(parent_node, children.map(|v| v.len()), format, start, count)
2723        {
2724            return result;
2725        }
2726
2727        let is_empty = children.is_none() || start > children.unwrap().len() || count == 0;
2728
2729        if is_empty {
2730            // Write a line terminator if the parent node was multi-line
2731
2732            if format.contains(ListFormat::MultiLine) {
2733                if !self.cfg.minify {
2734                    self.wr.write_line()?;
2735                }
2736            } else if format.contains(ListFormat::SpaceBetweenBraces)
2737                && !(format.contains(ListFormat::NoSpaceIfEmpty))
2738                && !self.cfg.minify
2739            {
2740                self.wr.write_space()?;
2741            }
2742        } else {
2743            let children = children.unwrap();
2744
2745            // Write the opening line terminator or leading whitespace.
2746            let may_emit_intervening_comments =
2747                !format.intersects(ListFormat::NoInterveningComments);
2748            let mut should_emit_intervening_comments = may_emit_intervening_comments;
2749            if self.cm.should_write_leading_line_terminator(
2750                parent_node,
2751                children.first().map(|v| v.span()),
2752                format,
2753            ) {
2754                if !self.cfg.minify {
2755                    self.wr.write_line()?;
2756                }
2757                should_emit_intervening_comments = false;
2758            } else if format.contains(ListFormat::SpaceBetweenBraces) && !self.cfg.minify {
2759                self.wr.write_space()?;
2760            }
2761
2762            // Increase the indent, if requested.
2763            if format.contains(ListFormat::Indented) && !self.cfg.minify {
2764                self.wr.increase_indent()?;
2765            }
2766
2767            // Emit each child.
2768            let mut previous_sibling: Option<Span> = None;
2769            let mut should_decrease_indent_after_emit = false;
2770            for i in 0..count {
2771                let child = &children[start + i];
2772
2773                self.emit_pre_child_for_list5(
2774                    parent_node,
2775                    format,
2776                    previous_sibling,
2777                    child.span(),
2778                    &mut should_decrease_indent_after_emit,
2779                    &mut should_emit_intervening_comments,
2780                )?;
2781
2782                child.emit_with(self)?;
2783
2784                // Emit this child.
2785                if should_emit_intervening_comments {
2786                    if self.comments.is_some() {
2787                        let comment_range = child.comment_range();
2788                        self.emit_trailing_comments_of_pos(comment_range.hi(), false, true)?;
2789                    }
2790                } else {
2791                    should_emit_intervening_comments = may_emit_intervening_comments;
2792                }
2793
2794                if should_decrease_indent_after_emit {
2795                    self.wr.decrease_indent()?;
2796                    should_decrease_indent_after_emit = false;
2797                }
2798
2799                previous_sibling = Some(child.span());
2800            }
2801
2802            self.emit_list_finisher_of_list5(
2803                parent_node,
2804                format,
2805                previous_sibling,
2806                children.last().map(|v| v.span()),
2807            )?;
2808        }
2809
2810        // self.handlers.onAfterEmitNodeArray(children);
2811
2812        self.emit_last_of_list5(parent_node, is_empty, format, start, count)?;
2813        Ok(())
2814    }
2815}
2816
2817/// Patterns
2818impl<W, S: SourceMapper> Emitter<'_, W, S>
2819where
2820    W: WriteJs,
2821    S: SourceMapperExt,
2822{
2823    #[emitter]
2824    fn emit_param(&mut self, node: &Param) -> Result {
2825        self.emit_leading_comments_of_span(node.span(), false)?;
2826
2827        srcmap!(node, true);
2828
2829        self.emit_list(node.span, Some(&node.decorators), ListFormat::Decorators)?;
2830
2831        emit!(node.pat);
2832
2833        srcmap!(node, false);
2834    }
2835
2836    #[emitter]
2837    fn emit_pat(&mut self, node: &Pat) -> Result {
2838        match node {
2839            Pat::Array(ref n) => emit!(n),
2840            Pat::Assign(ref n) => emit!(n),
2841            Pat::Expr(ref n) => emit!(n),
2842            Pat::Ident(ref n) => emit!(n),
2843            Pat::Object(ref n) => emit!(n),
2844            Pat::Rest(ref n) => emit!(n),
2845            Pat::Invalid(n) => emit!(n),
2846        }
2847
2848        if self.comments.is_some() {
2849            self.emit_trailing_comments_of_pos(node.span().hi, true, true)?;
2850        }
2851    }
2852
2853    #[emitter]
2854    fn emit_rest_pat(&mut self, node: &RestPat) -> Result {
2855        self.emit_leading_comments_of_span(node.span(), false)?;
2856
2857        punct!(node.dot3_token, "...");
2858        emit!(node.arg);
2859
2860        if let Some(type_ann) = &node.type_ann {
2861            punct!(":");
2862            formatting_space!();
2863            emit!(type_ann);
2864        }
2865    }
2866
2867    #[emitter]
2868    fn emit_prop_or_spread(&mut self, node: &PropOrSpread) -> Result {
2869        match *node {
2870            PropOrSpread::Prop(ref n) => emit!(n),
2871            PropOrSpread::Spread(ref n) => emit!(n),
2872        }
2873    }
2874
2875    #[emitter]
2876    fn emit_spread_element(&mut self, node: &SpreadElement) -> Result {
2877        if self.comments.is_some() {
2878            self.emit_leading_comments_of_span(node.span(), false)?;
2879        }
2880
2881        srcmap!(node, true);
2882
2883        punct!("...");
2884        emit!(node.expr);
2885
2886        srcmap!(node, false);
2887    }
2888
2889    #[emitter]
2890    fn emit_assign_target(&mut self, node: &AssignTarget) -> Result {
2891        match *node {
2892            AssignTarget::Simple(ref n) => emit!(n),
2893            AssignTarget::Pat(ref n) => emit!(n),
2894        }
2895    }
2896
2897    #[emitter]
2898    fn emit_simple_assign_target(&mut self, node: &SimpleAssignTarget) -> Result {
2899        match node {
2900            SimpleAssignTarget::Ident(n) => emit!(n),
2901            SimpleAssignTarget::Member(n) => emit!(n),
2902            SimpleAssignTarget::Invalid(n) => emit!(n),
2903            SimpleAssignTarget::SuperProp(n) => emit!(n),
2904            SimpleAssignTarget::Paren(n) => emit!(n),
2905            SimpleAssignTarget::OptChain(n) => emit!(n),
2906            SimpleAssignTarget::TsAs(n) => emit!(n),
2907            SimpleAssignTarget::TsNonNull(n) => emit!(n),
2908            SimpleAssignTarget::TsSatisfies(n) => emit!(n),
2909            SimpleAssignTarget::TsTypeAssertion(n) => emit!(n),
2910            SimpleAssignTarget::TsInstantiation(n) => emit!(n),
2911        }
2912    }
2913
2914    #[emitter]
2915    fn emit_assign_target_pat(&mut self, node: &AssignTargetPat) -> Result {
2916        match node {
2917            AssignTargetPat::Array(n) => emit!(n),
2918            AssignTargetPat::Object(n) => emit!(n),
2919            AssignTargetPat::Invalid(n) => emit!(n),
2920        }
2921    }
2922
2923    #[emitter]
2924    fn emit_array_pat(&mut self, node: &ArrayPat) -> Result {
2925        self.emit_leading_comments_of_span(node.span(), false)?;
2926
2927        srcmap!(node, true);
2928
2929        punct!("[");
2930
2931        let mut format = ListFormat::ArrayBindingPatternElements;
2932
2933        if let Some(None) = node.elems.last() {
2934            format |= ListFormat::ForceTrailingComma;
2935        }
2936
2937        self.emit_list(node.span(), Some(&node.elems), format)?;
2938        punct!("]");
2939        if node.optional {
2940            punct!("?");
2941        }
2942
2943        if let Some(type_ann) = &node.type_ann {
2944            punct!(":");
2945            space!();
2946            emit!(type_ann);
2947        }
2948
2949        srcmap!(node, false);
2950    }
2951
2952    #[emitter]
2953    fn emit_assign_pat(&mut self, node: &AssignPat) -> Result {
2954        self.emit_leading_comments_of_span(node.span(), false)?;
2955
2956        srcmap!(node, true);
2957
2958        emit!(node.left);
2959        formatting_space!();
2960        punct!("=");
2961        formatting_space!();
2962        emit!(node.right);
2963
2964        srcmap!(node, false);
2965    }
2966
2967    #[emitter]
2968    fn emit_object_pat(&mut self, node: &ObjectPat) -> Result {
2969        self.emit_leading_comments_of_span(node.span(), false)?;
2970
2971        srcmap!(node, true);
2972        punct!("{");
2973
2974        self.emit_list(
2975            node.span(),
2976            Some(&node.props),
2977            ListFormat::ObjectBindingPatternElements | ListFormat::CanSkipTrailingComma,
2978        )?;
2979
2980        punct!("}");
2981
2982        if node.optional {
2983            punct!("?");
2984        }
2985
2986        if let Some(type_ann) = &node.type_ann {
2987            punct!(":");
2988            space!();
2989            emit!(type_ann);
2990        }
2991
2992        srcmap!(node, false);
2993    }
2994
2995    #[emitter]
2996    fn emit_object_pat_prop(&mut self, node: &ObjectPatProp) -> Result {
2997        match *node {
2998            ObjectPatProp::KeyValue(ref node) => emit!(node),
2999            ObjectPatProp::Assign(ref node) => emit!(node),
3000            ObjectPatProp::Rest(ref node) => emit!(node),
3001        }
3002    }
3003
3004    #[emitter]
3005    fn emit_object_kv_pat(&mut self, node: &KeyValuePatProp) -> Result {
3006        self.emit_leading_comments_of_span(node.span(), false)?;
3007
3008        srcmap!(node, true);
3009
3010        emit!(node.key);
3011        punct!(":");
3012        formatting_space!();
3013        emit!(node.value);
3014
3015        srcmap!(node, false);
3016    }
3017
3018    #[emitter]
3019    fn emit_object_assign_pat(&mut self, node: &AssignPatProp) -> Result {
3020        self.emit_leading_comments_of_span(node.span(), false)?;
3021
3022        srcmap!(node, true);
3023
3024        emit!(node.key);
3025        if let Some(value) = &node.value {
3026            formatting_space!();
3027            punct!("=");
3028            formatting_space!();
3029            emit!(value);
3030        }
3031
3032        srcmap!(node, false);
3033    }
3034
3035    #[emitter]
3036    fn emit_for_head(&mut self, node: &ForHead) -> Result {
3037        match node {
3038            ForHead::Pat(n) => emit!(n),
3039            ForHead::VarDecl(n) => emit!(n),
3040            ForHead::UsingDecl(n) => emit!(n),
3041        }
3042    }
3043}
3044
3045/// Statements
3046impl<W, S: SourceMapper> Emitter<'_, W, S>
3047where
3048    W: WriteJs,
3049    S: SourceMapperExt,
3050{
3051    #[emitter]
3052    fn emit_stmt(&mut self, node: &Stmt) -> Result {
3053        match node {
3054            Stmt::Expr(ref e) => emit!(e),
3055            Stmt::Block(ref e) => {
3056                emit!(e);
3057                return Ok(());
3058            }
3059            Stmt::Empty(ref e) => emit!(e),
3060            Stmt::Debugger(ref e) => emit!(e),
3061            Stmt::With(ref e) => emit!(e),
3062            Stmt::Return(ref e) => emit!(e),
3063            Stmt::Labeled(ref e) => emit!(e),
3064            Stmt::Break(ref e) => emit!(e),
3065            Stmt::Continue(ref e) => emit!(e),
3066            Stmt::If(ref e) => emit!(e),
3067            Stmt::Switch(ref e) => emit!(e),
3068            Stmt::Throw(ref e) => emit!(e),
3069            Stmt::Try(ref e) => emit!(e),
3070            Stmt::While(ref e) => emit!(e),
3071            Stmt::DoWhile(ref e) => emit!(e),
3072            Stmt::For(ref e) => emit!(e),
3073            Stmt::ForIn(ref e) => emit!(e),
3074            Stmt::ForOf(ref e) => emit!(e),
3075            Stmt::Decl(Decl::Var(e)) => {
3076                emit!(e);
3077                semi!();
3078            }
3079            Stmt::Decl(e @ Decl::Using(..)) => {
3080                emit!(e);
3081                semi!();
3082            }
3083            Stmt::Decl(ref e) => emit!(e),
3084        }
3085        if self.comments.is_some() {
3086            self.emit_trailing_comments_of_pos(node.span().hi(), true, true)?;
3087        }
3088
3089        if !self.cfg.minify {
3090            self.wr.write_line()?;
3091        }
3092    }
3093
3094    #[emitter]
3095
3096    fn emit_expr_stmt(&mut self, e: &ExprStmt) -> Result {
3097        self.emit_leading_comments_of_span(e.span, false)?;
3098
3099        emit!(e.expr);
3100
3101        semi!();
3102    }
3103
3104    #[emitter]
3105
3106    fn emit_block_stmt(&mut self, node: &BlockStmt) -> Result {
3107        self.emit_block_stmt_inner(node, false)?;
3108    }
3109
3110    fn emit_block_stmt_inner(&mut self, node: &BlockStmt, skip_first_src_map: bool) -> Result {
3111        self.emit_leading_comments_of_span(node.span(), false)?;
3112
3113        if !skip_first_src_map {
3114            srcmap!(self, node, true);
3115        }
3116        punct!(self, "{");
3117
3118        let emit_new_line = !self.cfg.minify
3119            && !(node.stmts.is_empty() && is_empty_comments(&node.span(), &self.comments));
3120
3121        let mut list_format = ListFormat::MultiLineBlockStatements;
3122
3123        if !emit_new_line {
3124            list_format -= ListFormat::MultiLine | ListFormat::Indented;
3125        }
3126
3127        self.emit_list(node.span(), Some(&node.stmts), list_format)?;
3128
3129        self.emit_leading_comments_of_span(node.span(), true)?;
3130
3131        srcmap!(self, node, false, true);
3132        punct!(self, "}");
3133
3134        Ok(())
3135    }
3136
3137    #[emitter]
3138    fn emit_empty_stmt(&mut self, node: &EmptyStmt) -> Result {
3139        self.emit_leading_comments_of_span(node.span(), false)?;
3140
3141        self.wr.write_punct(None, ";")?;
3142    }
3143
3144    #[emitter]
3145    fn emit_debugger_stmt(&mut self, node: &DebuggerStmt) -> Result {
3146        self.wr.commit_pending_semi()?;
3147
3148        self.emit_leading_comments_of_span(node.span(), false)?;
3149
3150        keyword!(node.span, "debugger");
3151        semi!();
3152    }
3153
3154    #[emitter]
3155    fn emit_with_stmt(&mut self, node: &WithStmt) -> Result {
3156        self.wr.commit_pending_semi()?;
3157
3158        srcmap!(node, true);
3159
3160        keyword!("with");
3161        formatting_space!();
3162
3163        punct!("(");
3164        emit!(node.obj);
3165        punct!(")");
3166
3167        emit!(node.body);
3168    }
3169
3170    fn has_trailing_comment(&self, span: Span) -> bool {
3171        if let Some(cmt) = self.comments {
3172            let hi = span.hi;
3173
3174            if cmt.has_trailing(hi) {
3175                return true;
3176            }
3177        }
3178
3179        false
3180    }
3181
3182    fn simple_assign_target_has_leading_comment(&self, arg: &SimpleAssignTarget) -> bool {
3183        match arg {
3184            SimpleAssignTarget::Ident(i) => {
3185                span_has_leading_comment(self.comments.as_ref().unwrap(), i.span)
3186            }
3187            SimpleAssignTarget::Member(m) => {
3188                if self.has_leading_comment(&m.obj) {
3189                    return true;
3190                }
3191
3192                false
3193            }
3194
3195            SimpleAssignTarget::SuperProp(m) => {
3196                if span_has_leading_comment(self.comments.as_ref().unwrap(), m.span) {
3197                    return true;
3198                }
3199
3200                false
3201            }
3202
3203            _ => false,
3204        }
3205    }
3206
3207    fn has_leading_comment(&self, arg: &Expr) -> bool {
3208        let cmt = if let Some(cmt) = self.comments {
3209            if span_has_leading_comment(cmt, arg.span()) {
3210                return true;
3211            }
3212
3213            cmt
3214        } else {
3215            return false;
3216        };
3217
3218        match arg {
3219            Expr::Call(c) => {
3220                let has_leading = match &c.callee {
3221                    Callee::Super(callee) => span_has_leading_comment(cmt, callee.span),
3222                    Callee::Import(callee) => span_has_leading_comment(cmt, callee.span),
3223                    Callee::Expr(callee) => self.has_leading_comment(callee),
3224                };
3225
3226                if has_leading {
3227                    return true;
3228                }
3229            }
3230
3231            Expr::Member(m) => {
3232                if self.has_leading_comment(&m.obj) {
3233                    return true;
3234                }
3235            }
3236
3237            Expr::SuperProp(m) => {
3238                if span_has_leading_comment(cmt, m.span) {
3239                    return true;
3240                }
3241            }
3242
3243            Expr::Bin(e) => {
3244                if self.has_leading_comment(&e.left) {
3245                    return true;
3246                }
3247            }
3248
3249            Expr::Cond(e) => {
3250                if self.has_leading_comment(&e.test) {
3251                    return true;
3252                }
3253            }
3254
3255            Expr::Seq(e) => {
3256                if let Some(e) = e.exprs.first() {
3257                    if self.has_leading_comment(e) {
3258                        return true;
3259                    }
3260                }
3261            }
3262
3263            Expr::Assign(e) => {
3264                let lo = e.span.lo;
3265
3266                if cmt.has_leading(lo) {
3267                    return true;
3268                }
3269
3270                let has_leading = match &e.left {
3271                    AssignTarget::Simple(e) => self.simple_assign_target_has_leading_comment(e),
3272
3273                    AssignTarget::Pat(p) => match p {
3274                        AssignTargetPat::Array(a) => span_has_leading_comment(cmt, a.span),
3275                        AssignTargetPat::Object(o) => span_has_leading_comment(cmt, o.span),
3276                        AssignTargetPat::Invalid(..) => false,
3277                    },
3278                };
3279
3280                if has_leading {
3281                    return true;
3282                }
3283            }
3284
3285            Expr::OptChain(e) => match &*e.base {
3286                OptChainBase::Member(m) => {
3287                    if self.has_leading_comment(&m.obj) {
3288                        return true;
3289                    }
3290                }
3291                OptChainBase::Call(c) => {
3292                    if self.has_leading_comment(&c.callee) {
3293                        return true;
3294                    }
3295                }
3296            },
3297
3298            _ => {}
3299        }
3300
3301        false
3302    }
3303
3304    #[emitter]
3305    fn emit_return_stmt(&mut self, n: &ReturnStmt) -> Result {
3306        self.wr.commit_pending_semi()?;
3307
3308        self.emit_leading_comments_of_span(n.span, false)?;
3309
3310        srcmap!(n, true);
3311
3312        keyword!("return");
3313
3314        if let Some(arg) = n.arg.as_deref() {
3315            let need_paren = n
3316                .arg
3317                .as_deref()
3318                .map(|expr| self.has_leading_comment(expr))
3319                .unwrap_or(false);
3320            if need_paren {
3321                punct!("(");
3322            } else if arg.starts_with_alpha_num() {
3323                space!();
3324            } else {
3325                formatting_space!();
3326            }
3327
3328            emit!(arg);
3329            if need_paren {
3330                punct!(")");
3331            }
3332        }
3333
3334        semi!();
3335    }
3336
3337    #[emitter]
3338    fn emit_labeled_stmt(&mut self, node: &LabeledStmt) -> Result {
3339        self.wr.commit_pending_semi()?;
3340
3341        emit!(node.label);
3342
3343        // TODO: Comment
3344        punct!(":");
3345        formatting_space!();
3346
3347        emit!(node.body);
3348    }
3349
3350    #[emitter]
3351    fn emit_break_stmt(&mut self, n: &BreakStmt) -> Result {
3352        self.wr.commit_pending_semi()?;
3353
3354        srcmap!(n, true);
3355
3356        keyword!("break");
3357
3358        if let Some(ref label) = n.label {
3359            space!();
3360            emit!(label);
3361        }
3362
3363        semi!();
3364    }
3365
3366    #[emitter]
3367    fn emit_continue_stmt(&mut self, n: &ContinueStmt) -> Result {
3368        self.wr.commit_pending_semi()?;
3369
3370        srcmap!(n, true);
3371
3372        keyword!("continue");
3373
3374        if let Some(ref label) = n.label {
3375            space!();
3376            emit!(label);
3377        }
3378
3379        semi!();
3380    }
3381
3382    #[emitter]
3383    fn emit_if_stmt(&mut self, n: &IfStmt) -> Result {
3384        self.emit_leading_comments_of_span(n.span(), false)?;
3385
3386        self.wr.commit_pending_semi()?;
3387
3388        srcmap!(n, true);
3389
3390        keyword!("if");
3391
3392        formatting_space!();
3393        punct!("(");
3394        emit!(n.test);
3395        punct!(")");
3396        formatting_space!();
3397
3398        let is_cons_block = match *n.cons {
3399            Stmt::Block(..) => true,
3400            _ => false,
3401        };
3402
3403        emit!(n.cons);
3404
3405        if let Some(ref alt) = n.alt {
3406            if is_cons_block {
3407                formatting_space!();
3408            }
3409            keyword!("else");
3410            if alt.starts_with_alpha_num() {
3411                space!();
3412            } else {
3413                formatting_space!();
3414            }
3415            emit!(alt);
3416        }
3417    }
3418
3419    #[emitter]
3420    fn emit_switch_stmt(&mut self, n: &SwitchStmt) -> Result {
3421        self.wr.commit_pending_semi()?;
3422
3423        self.emit_leading_comments_of_span(n.span(), false)?;
3424
3425        srcmap!(n, true);
3426
3427        keyword!("switch");
3428
3429        punct!("(");
3430        emit!(n.discriminant);
3431        punct!(")");
3432
3433        punct!("{");
3434        self.emit_list(n.span(), Some(&n.cases), ListFormat::CaseBlockClauses)?;
3435
3436        srcmap!(n, false, true);
3437        punct!("}");
3438    }
3439
3440    #[emitter]
3441    fn emit_catch_clause(&mut self, n: &CatchClause) -> Result {
3442        self.emit_leading_comments_of_span(n.span(), false)?;
3443
3444        srcmap!(n, true);
3445
3446        keyword!("catch");
3447
3448        formatting_space!();
3449
3450        if let Some(param) = &n.param {
3451            punct!("(");
3452            emit!(param);
3453            punct!(")");
3454        }
3455
3456        formatting_space!();
3457
3458        emit!(n.body);
3459    }
3460
3461    #[emitter]
3462    fn emit_switch_case(&mut self, n: &SwitchCase) -> Result {
3463        self.emit_leading_comments_of_span(n.span(), false)?;
3464
3465        srcmap!(n, true);
3466
3467        if let Some(ref test) = n.test {
3468            keyword!("case");
3469
3470            let starts_with_alpha_num = test.starts_with_alpha_num();
3471
3472            if starts_with_alpha_num {
3473                space!();
3474            } else {
3475                formatting_space!();
3476            }
3477
3478            emit!(test);
3479        } else {
3480            keyword!("default");
3481        }
3482
3483        let emit_as_single_stmt = n.cons.len() == 1 && {
3484            // treat synthesized nodes as located on the same line for emit purposes
3485            n.is_synthesized()
3486                || n.cons[0].is_synthesized()
3487                || self
3488                    .cm
3489                    .is_on_same_line(n.span().lo(), n.cons[0].span().lo())
3490        };
3491
3492        let mut format = ListFormat::CaseOrDefaultClauseStatements;
3493        if emit_as_single_stmt {
3494            punct!(":");
3495            space!();
3496            format &= !(ListFormat::MultiLine | ListFormat::Indented);
3497        } else {
3498            punct!(":");
3499        }
3500        self.emit_list(n.span(), Some(&n.cons), format)?;
3501    }
3502
3503    #[emitter]
3504    fn emit_throw_stmt(&mut self, n: &ThrowStmt) -> Result {
3505        self.emit_leading_comments_of_span(n.span(), false)?;
3506
3507        srcmap!(n, true);
3508
3509        keyword!("throw");
3510
3511        {
3512            let need_paren = self.has_leading_comment(&n.arg);
3513            if need_paren {
3514                punct!("(");
3515            } else if n.arg.starts_with_alpha_num() {
3516                space!();
3517            } else {
3518                formatting_space!();
3519            }
3520
3521            emit!(n.arg);
3522            if need_paren {
3523                punct!(")");
3524            }
3525        }
3526        semi!();
3527    }
3528
3529    #[emitter]
3530
3531    fn emit_try_stmt(&mut self, n: &TryStmt) -> Result {
3532        self.emit_leading_comments_of_span(n.span(), false)?;
3533
3534        self.wr.commit_pending_semi()?;
3535
3536        srcmap!(n, true);
3537
3538        keyword!("try");
3539
3540        formatting_space!();
3541        emit!(n.block);
3542
3543        if let Some(ref catch) = n.handler {
3544            formatting_space!();
3545            emit!(catch);
3546        }
3547
3548        if let Some(ref finally) = n.finalizer {
3549            formatting_space!();
3550            keyword!("finally");
3551            // space!();
3552            emit!(finally);
3553        }
3554    }
3555
3556    #[emitter]
3557    fn emit_while_stmt(&mut self, node: &WhileStmt) -> Result {
3558        self.wr.commit_pending_semi()?;
3559
3560        self.emit_leading_comments_of_span(node.span(), false)?;
3561
3562        srcmap!(node, true);
3563
3564        keyword!("while");
3565
3566        punct!("(");
3567        emit!(node.test);
3568        punct!(")");
3569
3570        emit!(node.body);
3571    }
3572
3573    #[emitter]
3574    fn emit_do_while_stmt(&mut self, node: &DoWhileStmt) -> Result {
3575        self.wr.commit_pending_semi()?;
3576
3577        self.emit_leading_comments_of_span(node.span(), false)?;
3578
3579        srcmap!(node, true);
3580
3581        keyword!("do");
3582        if node.body.starts_with_alpha_num() {
3583            space!();
3584        } else {
3585            formatting_space!()
3586        }
3587        emit!(node.body);
3588
3589        keyword!("while");
3590
3591        formatting_space!();
3592
3593        punct!("(");
3594        emit!(node.test);
3595        punct!(")");
3596
3597        if self.cfg.target <= EsVersion::Es5 {
3598            semi!();
3599        }
3600
3601        srcmap!(node, false);
3602    }
3603
3604    #[emitter]
3605    fn emit_for_stmt(&mut self, n: &ForStmt) -> Result {
3606        self.wr.commit_pending_semi()?;
3607
3608        self.emit_leading_comments_of_span(n.span(), false)?;
3609
3610        srcmap!(n, true);
3611
3612        keyword!("for");
3613
3614        punct!("(");
3615        opt!(n.init);
3616        self.wr.write_punct(None, ";")?;
3617        opt_leading_space!(n.test);
3618        self.wr.write_punct(None, ";")?;
3619        opt_leading_space!(n.update);
3620        punct!(")");
3621
3622        emit!(n.body);
3623    }
3624
3625    #[emitter]
3626    fn emit_for_in_stmt(&mut self, n: &ForInStmt) -> Result {
3627        self.wr.commit_pending_semi()?;
3628
3629        self.emit_leading_comments_of_span(n.span(), false)?;
3630
3631        srcmap!(n, true);
3632
3633        keyword!("for");
3634
3635        punct!("(");
3636        emit!(n.left);
3637
3638        if n.left.ends_with_alpha_num() {
3639            space!();
3640        } else {
3641            formatting_space!();
3642        }
3643        keyword!("in");
3644
3645        {
3646            let starts_with_alpha_num = n.right.starts_with_alpha_num();
3647
3648            if starts_with_alpha_num {
3649                space!();
3650            } else {
3651                formatting_space!()
3652            }
3653            emit!(n.right);
3654        }
3655
3656        punct!(")");
3657
3658        emit!(n.body);
3659    }
3660
3661    #[emitter]
3662    fn emit_for_of_stmt(&mut self, n: &ForOfStmt) -> Result {
3663        self.wr.commit_pending_semi()?;
3664
3665        self.emit_leading_comments_of_span(n.span(), false)?;
3666
3667        srcmap!(n, true);
3668
3669        keyword!("for");
3670
3671        if n.is_await {
3672            space!();
3673            keyword!("await");
3674        }
3675        formatting_space!();
3676        punct!("(");
3677        emit!(n.left);
3678        if n.left.ends_with_alpha_num() {
3679            space!();
3680        } else {
3681            formatting_space!();
3682        }
3683        keyword!("of");
3684
3685        {
3686            let starts_with_alpha_num = n.right.starts_with_alpha_num();
3687
3688            if starts_with_alpha_num {
3689                space!();
3690            } else {
3691                formatting_space!()
3692            }
3693            emit!(n.right);
3694        }
3695        punct!(")");
3696        emit!(n.body);
3697    }
3698
3699    #[emitter]
3700    pub fn emit_module_export_name(&mut self, node: &ModuleExportName) -> Result {
3701        match *node {
3702            ModuleExportName::Ident(ref ident) => emit!(ident),
3703            ModuleExportName::Str(ref s) => emit!(s),
3704        }
3705    }
3706}
3707
3708impl<W, S: SourceMapper> Emitter<'_, W, S>
3709where
3710    W: WriteJs,
3711    S: SourceMapperExt,
3712{
3713    fn write_delim(&mut self, f: ListFormat) -> Result {
3714        match f & ListFormat::DelimitersMask {
3715            ListFormat::None => {}
3716            ListFormat::CommaDelimited => self.wr.write_punct(None, ",")?,
3717            ListFormat::BarDelimited => {
3718                if !self.cfg.minify {
3719                    self.wr.write_space()?;
3720                }
3721                self.wr.write_punct(None, "|")?;
3722            }
3723            ListFormat::AmpersandDelimited => {
3724                if !self.cfg.minify {
3725                    self.wr.write_space()?;
3726                }
3727                self.wr.write_punct(None, "&")?;
3728            }
3729            _ => unreachable!(),
3730        }
3731
3732        Ok(())
3733    }
3734
3735    #[emitter]
3736    fn emit_var_decl_or_expr(&mut self, node: &VarDeclOrExpr) -> Result {
3737        match *node {
3738            VarDeclOrExpr::Expr(ref node) => emit!(node),
3739            VarDeclOrExpr::VarDecl(ref node) => emit!(node),
3740        }
3741    }
3742}
3743
3744/// In some cases, we need to emit a space between the operator and the operand.
3745/// One obvious case is when the operator is an identifier, like delete or
3746/// typeof. We also need to do this for plus and minus expressions in certain
3747/// cases. Specifically, consider the following two cases (parens are just for
3748/// clarity of exposition, and not part of the source code):
3749///
3750///  (+(+1))
3751///  (+(++1))
3752///
3753/// We need to emit a space in both cases. In the first case, the absence of a
3754/// space will make the resulting expression a prefix increment operation. And
3755/// in the second, it will make the resulting expression a prefix increment
3756/// whose operand is a plus expression - (++(+x)) The same is true of minus of
3757/// course.
3758fn should_emit_whitespace_before_operand(node: &UnaryExpr) -> bool {
3759    match *node {
3760        UnaryExpr {
3761            op: op!("void"), ..
3762        }
3763        | UnaryExpr {
3764            op: op!("typeof"), ..
3765        }
3766        | UnaryExpr {
3767            op: op!("delete"), ..
3768        } => return node.arg.starts_with_alpha_num(),
3769        _ => {}
3770    }
3771
3772    match &*node.arg {
3773        Expr::Update(UpdateExpr {
3774            op: op!("++"),
3775            prefix: true,
3776            ..
3777        })
3778        | Expr::Unary(UnaryExpr {
3779            op: op!(unary, "+"),
3780            ..
3781        }) if node.op == op!(unary, "+") => true,
3782        Expr::Update(UpdateExpr {
3783            op: op!("--"),
3784            prefix: true,
3785            ..
3786        })
3787        | Expr::Unary(UnaryExpr {
3788            op: op!(unary, "-"),
3789            ..
3790        }) if node.op == op!(unary, "-") => true,
3791
3792        Expr::Lit(Lit::Num(v)) if v.value.is_sign_negative() && node.op == op!(unary, "-") => true,
3793
3794        _ => false,
3795    }
3796}
3797
3798impl<N> Node for Option<N>
3799where
3800    N: Node,
3801{
3802    fn emit_with<W, S>(&self, e: &mut Emitter<'_, W, S>) -> Result
3803    where
3804        W: WriteJs,
3805        S: SourceMapper + SourceMapperExt,
3806    {
3807        match *self {
3808            Some(ref n) => n.emit_with(e),
3809            None => Ok(()),
3810        }
3811    }
3812}
3813
3814fn get_template_element_from_raw(s: &str, ascii_only: bool) -> String {
3815    fn read_escaped(
3816        radix: u32,
3817        len: Option<usize>,
3818        buf: &mut String,
3819        iter: impl Iterator<Item = char>,
3820    ) {
3821        let mut v = 0;
3822        let mut pending = None;
3823
3824        for (i, c) in iter.enumerate() {
3825            if let Some(len) = len {
3826                if i == len {
3827                    pending = Some(c);
3828                    break;
3829                }
3830            }
3831
3832            match c.to_digit(radix) {
3833                None => {
3834                    pending = Some(c);
3835                    break;
3836                }
3837                Some(d) => {
3838                    v = v * radix + d;
3839                }
3840            }
3841        }
3842
3843        match radix {
3844            16 => {
3845                match v {
3846                    0 => match pending {
3847                        Some('1'..='9') => write!(buf, "\\x00").unwrap(),
3848                        _ => write!(buf, "\\0").unwrap(),
3849                    },
3850                    1..=15 => write!(buf, "\\x0{:x}", v).unwrap(),
3851                    // '\x20'..='\x7e'
3852                    32..=126 => {
3853                        let c = char::from_u32(v);
3854
3855                        match c {
3856                            Some(c) => write!(buf, "{}", c).unwrap(),
3857                            _ => {
3858                                unreachable!()
3859                            }
3860                        }
3861                    }
3862                    // '\x10'..='\x1f'
3863                    // '\u{7f}'..='\u{ff}'
3864                    _ => {
3865                        write!(buf, "\\x{:x}", v).unwrap();
3866                    }
3867                }
3868            }
3869
3870            _ => unreachable!(),
3871        }
3872
3873        if let Some(pending) = pending {
3874            buf.push(pending);
3875        }
3876    }
3877
3878    let mut buf = String::with_capacity(s.len());
3879    let mut iter = s.chars().peekable();
3880
3881    let mut is_dollar_prev = false;
3882
3883    while let Some(c) = iter.next() {
3884        let unescape = match c {
3885            '\\' => match iter.next() {
3886                Some(c) => match c {
3887                    'n' => Some('\n'),
3888                    't' => Some('\t'),
3889                    'x' => {
3890                        read_escaped(16, Some(2), &mut buf, &mut iter);
3891
3892                        None
3893                    }
3894                    // TODO handle `\u1111` and `\u{1111}` too
3895                    // Source - https://github.com/eslint/eslint/blob/main/lib/rules/no-useless-escape.js
3896                    '\u{2028}' | '\u{2029}' => None,
3897                    // `\t` and `\h` are special cases, because they can be replaced on real
3898                    // characters `\xXX` can be replaced on character
3899                    '\\' | 'r' | 'v' | 'b' | 'f' | 'u' | '\r' | '\n' | '`' | '0'..='7' => {
3900                        buf.push('\\');
3901                        buf.push(c);
3902
3903                        None
3904                    }
3905                    '$' if iter.peek() == Some(&'{') => {
3906                        buf.push('\\');
3907                        buf.push('$');
3908
3909                        None
3910                    }
3911                    '{' if is_dollar_prev => {
3912                        buf.push('\\');
3913                        buf.push('{');
3914
3915                        is_dollar_prev = false;
3916
3917                        None
3918                    }
3919                    _ => Some(c),
3920                },
3921                None => Some('\\'),
3922            },
3923            _ => Some(c),
3924        };
3925
3926        match unescape {
3927            Some(c @ '$') => {
3928                is_dollar_prev = true;
3929
3930                buf.push(c);
3931            }
3932            Some('\x00') => {
3933                let next = iter.peek();
3934
3935                match next {
3936                    Some('1'..='9') => buf.push_str("\\x00"),
3937                    _ => buf.push_str("\\0"),
3938                }
3939            }
3940            // Octal doesn't supported in template literals, except in tagged templates, but
3941            // we don't use this for tagged templates, they are printing as is
3942            Some('\u{0008}') => buf.push_str("\\b"),
3943            Some('\u{000c}') => buf.push_str("\\f"),
3944            Some('\n') => buf.push('\n'),
3945            // `\r` is impossible here, because it was removed on parser stage
3946            Some('\u{000b}') => buf.push_str("\\v"),
3947            Some('\t') => buf.push('\t'),
3948            // Print `"` and `'` without quotes
3949            Some(c @ '\x20'..='\x7e') => {
3950                buf.push(c);
3951            }
3952            Some(c @ '\u{7f}'..='\u{ff}') => {
3953                let _ = write!(buf, "\\x{:x}", c as u8);
3954            }
3955            Some('\u{2028}') => {
3956                buf.push_str("\\u2028");
3957            }
3958            Some('\u{2029}') => {
3959                buf.push_str("\\u2029");
3960            }
3961            Some('\u{FEFF}') => {
3962                buf.push_str("\\uFEFF");
3963            }
3964            // TODO(kdy1): Surrogate pairs
3965            Some(c) => {
3966                if !ascii_only || c.is_ascii() {
3967                    buf.push(c);
3968                } else {
3969                    buf.extend(c.escape_unicode().map(|c| {
3970                        if c == 'u' {
3971                            c
3972                        } else {
3973                            c.to_ascii_uppercase()
3974                        }
3975                    }));
3976                }
3977            }
3978            None => {}
3979        }
3980    }
3981
3982    buf
3983}
3984
3985fn get_ascii_only_ident(sym: &str, may_need_quote: bool, target: EsVersion) -> CowStr {
3986    if sym.is_ascii() {
3987        return CowStr::Borrowed(sym);
3988    }
3989
3990    let mut first = true;
3991    let mut buf = CompactString::with_capacity(sym.len() + 8);
3992    let mut iter = sym.chars().peekable();
3993    let mut need_quote = false;
3994
3995    while let Some(c) = iter.next() {
3996        match c {
3997            '\x00' => {
3998                if may_need_quote {
3999                    need_quote = true;
4000                    let _ = write!(buf, "\\x00");
4001                } else {
4002                    let _ = write!(buf, "\\u0000");
4003                }
4004            }
4005            '\u{0008}' => buf.push_str("\\b"),
4006            '\u{000c}' => buf.push_str("\\f"),
4007            '\n' => buf.push_str("\\n"),
4008            '\r' => buf.push_str("\\r"),
4009            '\u{000b}' => buf.push_str("\\v"),
4010            '\t' => buf.push('\t'),
4011            '\\' => {
4012                let next = iter.peek();
4013
4014                match next {
4015                    // TODO fix me - workaround for surrogate pairs
4016                    Some('u') => {
4017                        let mut inner_iter = iter.clone();
4018
4019                        inner_iter.next();
4020
4021                        let mut is_curly = false;
4022                        let mut next = inner_iter.peek();
4023
4024                        if next == Some(&'{') {
4025                            is_curly = true;
4026
4027                            inner_iter.next();
4028                            next = inner_iter.peek();
4029                        }
4030
4031                        if let Some(c @ 'D' | c @ 'd') = next {
4032                            let mut inner_buf = String::new();
4033
4034                            inner_buf.push('\\');
4035                            inner_buf.push('u');
4036
4037                            if is_curly {
4038                                inner_buf.push('{');
4039                            }
4040
4041                            inner_buf.push(*c);
4042
4043                            inner_iter.next();
4044
4045                            let mut is_valid = true;
4046
4047                            for _ in 0..3 {
4048                                let c = inner_iter.next();
4049
4050                                match c {
4051                                    Some('0'..='9') | Some('a'..='f') | Some('A'..='F') => {
4052                                        inner_buf.push(c.unwrap());
4053                                    }
4054                                    _ => {
4055                                        is_valid = false;
4056
4057                                        break;
4058                                    }
4059                                }
4060                            }
4061
4062                            if is_curly {
4063                                inner_buf.push('}');
4064                            }
4065
4066                            if is_valid {
4067                                buf.push_str(&inner_buf);
4068
4069                                let end = if is_curly { 7 } else { 5 };
4070
4071                                for _ in 0..end {
4072                                    iter.next();
4073                                }
4074                            }
4075                        } else {
4076                            buf.push_str("\\\\");
4077                        }
4078                    }
4079                    _ => {
4080                        buf.push_str("\\\\");
4081                    }
4082                }
4083            }
4084            '\'' => {
4085                buf.push('\'');
4086            }
4087            '"' => {
4088                buf.push('"');
4089            }
4090            '\x01'..='\x0f' if !first => {
4091                if may_need_quote {
4092                    need_quote = true;
4093                    let _ = write!(buf, "\\x{:x}", c as u8);
4094                } else {
4095                    let _ = write!(buf, "\\u00{:x}", c as u8);
4096                }
4097            }
4098            '\x10'..='\x1f' if !first => {
4099                if may_need_quote {
4100                    need_quote = true;
4101                    let _ = write!(buf, "\\x{:x}", c as u8);
4102                } else {
4103                    let _ = write!(buf, "\\u00{:x}", c as u8);
4104                }
4105            }
4106            '\x20'..='\x7e' => {
4107                buf.push(c);
4108            }
4109            '\u{7f}'..='\u{ff}' => {
4110                if may_need_quote {
4111                    need_quote = true;
4112                    let _ = write!(buf, "\\x{:x}", c as u8);
4113                } else {
4114                    let _ = write!(buf, "\\u00{:x}", c as u8);
4115                }
4116            }
4117            '\u{2028}' => {
4118                buf.push_str("\\u2028");
4119            }
4120            '\u{2029}' => {
4121                buf.push_str("\\u2029");
4122            }
4123            '\u{FEFF}' => {
4124                buf.push_str("\\uFEFF");
4125            }
4126            _ => {
4127                if c.is_ascii() {
4128                    buf.push(c);
4129                } else if c > '\u{FFFF}' {
4130                    // if we've got this far the char isn't reserved and if the callee has specified
4131                    // we should output unicode for non-ascii chars then we have
4132                    // to make sure we output unicode that is safe for the target
4133                    // Es5 does not support code point escapes and so surrograte formula must be
4134                    // used
4135                    if target <= EsVersion::Es5 {
4136                        // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
4137                        let h = ((c as u32 - 0x10000) / 0x400) + 0xd800;
4138                        let l = (c as u32 - 0x10000) % 0x400 + 0xdc00;
4139
4140                        let _ = write!(buf, r#""\u{:04X}\u{:04X}""#, h, l);
4141                    } else {
4142                        let _ = write!(buf, "\\u{{{:04X}}}", c as u32);
4143                    }
4144                } else {
4145                    let _ = write!(buf, "\\u{:04X}", c as u16);
4146                }
4147            }
4148        }
4149        first = false;
4150    }
4151
4152    if need_quote {
4153        CowStr::Owned(format_compact!("\"{}\"", buf))
4154    } else {
4155        CowStr::Owned(buf)
4156    }
4157}
4158
4159/// Returns `(quote_char, value)`
4160fn get_quoted_utf16(v: &str, ascii_only: bool, target: EsVersion) -> (AsciiChar, CowStr) {
4161    // Fast path: If the string is ASCII and doesn't need escaping, we can avoid
4162    // allocation
4163    if v.is_ascii() {
4164        let mut needs_escaping = false;
4165        let mut single_quote_count = 0;
4166        let mut double_quote_count = 0;
4167
4168        for &b in v.as_bytes() {
4169            match b {
4170                b'\'' => single_quote_count += 1,
4171                b'"' => double_quote_count += 1,
4172                // Control characters and backslash need escaping
4173                0..=0x1f | b'\\' => {
4174                    needs_escaping = true;
4175                    break;
4176                }
4177                _ => {}
4178            }
4179        }
4180
4181        if !needs_escaping {
4182            let quote_char = if double_quote_count > single_quote_count {
4183                AsciiChar::Apostrophe
4184            } else {
4185                AsciiChar::Quotation
4186            };
4187
4188            // If there are no quotes to escape, we can return the original string
4189            if (quote_char == AsciiChar::Apostrophe && single_quote_count == 0)
4190                || (quote_char == AsciiChar::Quotation && double_quote_count == 0)
4191            {
4192                return (quote_char, CowStr::Borrowed(v));
4193            }
4194        }
4195    }
4196
4197    // Slow path: Original implementation for strings that need processing
4198    // Count quotes first to determine which quote character to use
4199    let (mut single_quote_count, mut double_quote_count) = (0, 0);
4200    for c in v.chars() {
4201        match c {
4202            '\'' => single_quote_count += 1,
4203            '"' => double_quote_count += 1,
4204            _ => {}
4205        }
4206    }
4207
4208    // Pre-calculate capacity to avoid reallocations
4209    let quote_char = if double_quote_count > single_quote_count {
4210        AsciiChar::Apostrophe
4211    } else {
4212        AsciiChar::Quotation
4213    };
4214    let escape_char = if quote_char == AsciiChar::Apostrophe {
4215        AsciiChar::Apostrophe
4216    } else {
4217        AsciiChar::Quotation
4218    };
4219    let escape_count = if quote_char == AsciiChar::Apostrophe {
4220        single_quote_count
4221    } else {
4222        double_quote_count
4223    };
4224
4225    // Add 1 for each escaped quote
4226    let capacity = v.len() + escape_count;
4227    let mut buf = CompactString::with_capacity(capacity);
4228
4229    let mut iter = v.chars().peekable();
4230    while let Some(c) = iter.next() {
4231        match c {
4232            '\x00' => {
4233                if target < EsVersion::Es5 || matches!(iter.peek(), Some('0'..='9')) {
4234                    buf.push_str("\\x00");
4235                } else {
4236                    buf.push_str("\\0");
4237                }
4238            }
4239            '\u{0008}' => buf.push_str("\\b"),
4240            '\u{000c}' => buf.push_str("\\f"),
4241            '\n' => buf.push_str("\\n"),
4242            '\r' => buf.push_str("\\r"),
4243            '\u{000b}' => buf.push_str("\\v"),
4244            '\t' => buf.push('\t'),
4245            '\\' => {
4246                let next = iter.peek();
4247                match next {
4248                    Some('u') => {
4249                        let mut inner_iter = iter.clone();
4250                        inner_iter.next();
4251
4252                        let mut is_curly = false;
4253                        let mut next = inner_iter.peek();
4254
4255                        if next == Some(&'{') {
4256                            is_curly = true;
4257                            inner_iter.next();
4258                            next = inner_iter.peek();
4259                        } else if next != Some(&'D') && next != Some(&'d') {
4260                            buf.push('\\');
4261                        }
4262
4263                        if let Some(c @ 'D' | c @ 'd') = next {
4264                            let mut inner_buf = String::with_capacity(8);
4265                            inner_buf.push('\\');
4266                            inner_buf.push('u');
4267
4268                            if is_curly {
4269                                inner_buf.push('{');
4270                            }
4271
4272                            inner_buf.push(*c);
4273                            inner_iter.next();
4274
4275                            let mut is_valid = true;
4276                            for _ in 0..3 {
4277                                match inner_iter.next() {
4278                                    Some(c @ '0'..='9') | Some(c @ 'a'..='f')
4279                                    | Some(c @ 'A'..='F') => {
4280                                        inner_buf.push(c);
4281                                    }
4282                                    _ => {
4283                                        is_valid = false;
4284                                        break;
4285                                    }
4286                                }
4287                            }
4288
4289                            if is_curly {
4290                                inner_buf.push('}');
4291                            }
4292
4293                            let range = if is_curly {
4294                                3..(inner_buf.len() - 1)
4295                            } else {
4296                                2..6
4297                            };
4298
4299                            if is_valid {
4300                                let val_str = &inner_buf[range];
4301                                if let Ok(v) = u32::from_str_radix(val_str, 16) {
4302                                    if v > 0xffff {
4303                                        buf.push_str(&inner_buf);
4304                                        let end = if is_curly { 7 } else { 5 };
4305                                        for _ in 0..end {
4306                                            iter.next();
4307                                        }
4308                                    } else if (0xd800..=0xdfff).contains(&v) {
4309                                        buf.push('\\');
4310                                    } else {
4311                                        buf.push_str("\\\\");
4312                                    }
4313                                } else {
4314                                    buf.push_str("\\\\");
4315                                }
4316                            } else {
4317                                buf.push_str("\\\\");
4318                            }
4319                        } else if is_curly {
4320                            buf.push_str("\\\\");
4321                        } else {
4322                            buf.push('\\');
4323                        }
4324                    }
4325                    _ => buf.push_str("\\\\"),
4326                }
4327            }
4328            c if c == escape_char => {
4329                buf.push('\\');
4330                buf.push(c);
4331            }
4332            '\x01'..='\x0f' => {
4333                buf.push_str("\\x0");
4334                write!(&mut buf, "{:x}", c as u8).unwrap();
4335            }
4336            '\x10'..='\x1f' => {
4337                buf.push_str("\\x");
4338                write!(&mut buf, "{:x}", c as u8).unwrap();
4339            }
4340            '\x20'..='\x7e' => buf.push(c),
4341            '\u{7f}'..='\u{ff}' => {
4342                if ascii_only || target <= EsVersion::Es5 {
4343                    buf.push_str("\\x");
4344                    write!(&mut buf, "{:x}", c as u8).unwrap();
4345                } else {
4346                    buf.push(c);
4347                }
4348            }
4349            '\u{2028}' => buf.push_str("\\u2028"),
4350            '\u{2029}' => buf.push_str("\\u2029"),
4351            '\u{FEFF}' => buf.push_str("\\uFEFF"),
4352            c => {
4353                if c.is_ascii() {
4354                    buf.push(c);
4355                } else if c > '\u{FFFF}' {
4356                    if target <= EsVersion::Es5 {
4357                        let h = ((c as u32 - 0x10000) / 0x400) + 0xd800;
4358                        let l = (c as u32 - 0x10000) % 0x400 + 0xdc00;
4359                        write!(&mut buf, "\\u{:04X}\\u{:04X}", h, l).unwrap();
4360                    } else if ascii_only {
4361                        write!(&mut buf, "\\u{{{:04X}}}", c as u32).unwrap();
4362                    } else {
4363                        buf.push(c);
4364                    }
4365                } else if ascii_only {
4366                    write!(&mut buf, "\\u{:04X}", c as u16).unwrap();
4367                } else {
4368                    buf.push(c);
4369                }
4370            }
4371        }
4372    }
4373
4374    (quote_char, CowStr::Owned(buf))
4375}
4376
4377fn handle_invalid_unicodes(s: &str) -> Cow<str> {
4378    static NEEDLE: Lazy<Finder> = Lazy::new(|| Finder::new("\\\0"));
4379    if NEEDLE.find(s.as_bytes()).is_none() {
4380        return Cow::Borrowed(s);
4381    }
4382
4383    Cow::Owned(s.replace("\\\0", "\\"))
4384}
4385
4386fn require_space_before_rhs(rhs: &Expr, op: &BinaryOp) -> bool {
4387    match rhs {
4388        Expr::Lit(Lit::Num(v)) if v.value.is_sign_negative() && *op == op!(bin, "-") => true,
4389
4390        Expr::Update(UpdateExpr {
4391            prefix: true,
4392            op: update,
4393            ..
4394        }) => matches!(
4395            (op, update),
4396            (op!(bin, "-"), op!("--")) | (op!(bin, "+"), op!("++"))
4397        ),
4398
4399        // space is mandatory to avoid outputting <!--
4400        Expr::Unary(UnaryExpr {
4401            op: op!("!"), arg, ..
4402        }) if *op == op!("<") || *op == op!("<<") => {
4403            if let Expr::Update(UpdateExpr { op: op!("--"), .. }) = &**arg {
4404                true
4405            } else {
4406                false
4407            }
4408        }
4409
4410        Expr::Unary(UnaryExpr { op: unary, .. }) => matches!(
4411            (op, unary),
4412            (op!(bin, "-"), op!(unary, "-")) | (op!(bin, "+"), op!(unary, "+"))
4413        ),
4414
4415        Expr::Bin(BinExpr { left, .. }) => require_space_before_rhs(left, op),
4416
4417        _ => false,
4418    }
4419}
4420
4421fn is_empty_comments(span: &Span, comments: &Option<&dyn Comments>) -> bool {
4422    span.is_dummy() || comments.map_or(true, |c| !c.has_leading(span.span_hi() - BytePos(1)))
4423}
4424
4425fn minify_number(num: f64, detect_dot: &mut bool) -> String {
4426    // ddddd -> 0xhhhh
4427    // len(0xhhhh) == len(ddddd)
4428    // 10000000 <= num <= 0xffffff
4429    'hex: {
4430        if num.fract() == 0.0 && num.abs() <= u64::MAX as f64 {
4431            let int = num.abs() as u64;
4432
4433            if int < 10000000 {
4434                break 'hex;
4435            }
4436
4437            // use scientific notation
4438            if int % 1000 == 0 {
4439                break 'hex;
4440            }
4441
4442            *detect_dot = false;
4443            return format!(
4444                "{}{:#x}",
4445                if num.is_sign_negative() { "-" } else { "" },
4446                int
4447            );
4448        }
4449    }
4450
4451    let mut num = num.to_string();
4452
4453    if num.contains(".") {
4454        *detect_dot = false;
4455    }
4456
4457    if let Some(num) = num.strip_prefix("0.") {
4458        let cnt = clz(num);
4459        if cnt > 2 {
4460            return format!("{}e-{}", &num[cnt..], num.len());
4461        }
4462        return format!(".{}", num);
4463    }
4464
4465    if let Some(num) = num.strip_prefix("-0.") {
4466        let cnt = clz(num);
4467        if cnt > 2 {
4468            return format!("-{}e-{}", &num[cnt..], num.len());
4469        }
4470        return format!("-.{}", num);
4471    }
4472
4473    if num.ends_with("000") {
4474        *detect_dot = false;
4475
4476        let cnt = num
4477            .as_bytes()
4478            .iter()
4479            .rev()
4480            .skip(3)
4481            .take_while(|&&c| c == b'0')
4482            .count()
4483            + 3;
4484
4485        num.truncate(num.len() - cnt);
4486        num.push('e');
4487        num.push_str(&cnt.to_string());
4488    }
4489
4490    num
4491}
4492
4493fn clz(s: &str) -> usize {
4494    s.as_bytes().iter().take_while(|&&c| c == b'0').count()
4495}
4496
4497fn span_has_leading_comment(cmt: &dyn Comments, span: Span) -> bool {
4498    let lo = span.lo;
4499
4500    // see #415
4501    if let Some(cmt) = cmt.get_leading(lo) {
4502        if cmt.iter().any(|cmt| {
4503            cmt.kind == CommentKind::Line
4504                || cmt
4505                    .text
4506                    .chars()
4507                    // https://tc39.es/ecma262/#table-line-terminator-code-points
4508                    .any(|c| c == '\n' || c == '\r' || c == '\u{2028}' || c == '\u{2029}')
4509        }) {
4510            return true;
4511        }
4512    }
4513
4514    false
4515}