swc_ecma_codegen/text_writer/
semicolon.rs1use swc_common::{BytePos, Span, DUMMY_SP};
2
3use super::{BindingStorage, Result, ScopeKind, WriteJs};
4
5pub fn omit_trailing_semi<W: WriteJs>(w: W) -> impl WriteJs {
6 OmitTrailingSemi {
7 inner: w,
8 pending_semi: None,
9 }
10}
11
12#[derive(Debug, Clone)]
13struct OmitTrailingSemi<W: WriteJs> {
14 inner: W,
15 pending_semi: Option<Span>,
16}
17
18macro_rules! with_semi {
19 (
20 $fn_name:ident
21 (
22 $(
23 $arg_name:ident
24 :
25 $arg_ty:ty
26 ),*
27 )
28 ) => {
29 fn $fn_name(&mut self, $($arg_name: $arg_ty),* ) -> Result {
30 self.commit_pending_semi()?;
31
32 self.inner.$fn_name( $($arg_name),* )
33 }
34 };
35}
36
37impl<W: WriteJs> WriteJs for OmitTrailingSemi<W> {
38 with_semi!(increase_indent());
39
40 with_semi!(decrease_indent());
41
42 with_semi!(write_space());
43
44 with_semi!(write_comment(s: &str));
45
46 with_semi!(write_keyword(span: Option<Span>, s: &'static str));
47
48 with_semi!(write_operator(span: Option<Span>, s: &str));
49
50 with_semi!(write_param(s: &str));
51
52 with_semi!(write_property(s: &str));
53
54 with_semi!(write_line());
55
56 with_semi!(write_lit(span: Span, s: &str));
57
58 with_semi!(write_str_lit(span: Span, s: &str));
59
60 with_semi!(write_str(s: &str));
61
62 with_semi!(write_symbol(span: Span, s: &str));
63
64 fn write_semi(&mut self, span: Option<Span>) -> Result {
65 self.pending_semi = Some(span.unwrap_or(DUMMY_SP));
66 Ok(())
67 }
68
69 fn write_punct(
70 &mut self,
71 span: Option<Span>,
72 s: &'static str,
73 commit_pending_semi: bool,
74 ) -> Result {
75 if commit_pending_semi {
76 self.commit_pending_semi()?;
77 } else {
78 self.pending_semi = None;
79 }
80 self.inner.write_punct(span, s, commit_pending_semi)
81 }
82
83 #[inline]
84 fn care_about_srcmap(&self) -> bool {
85 self.inner.care_about_srcmap()
86 }
87
88 #[inline]
89 fn add_srcmap(&mut self, pos: BytePos) -> Result {
90 self.inner.add_srcmap(pos)
91 }
92
93 fn commit_pending_semi(&mut self) -> Result {
94 if let Some(span) = self.pending_semi {
95 self.inner.write_semi(Some(span))?;
96 self.pending_semi = None;
97 }
98 Ok(())
99 }
100
101 #[inline(always)]
102 fn can_ignore_invalid_unicodes(&mut self) -> bool {
103 self.inner.can_ignore_invalid_unicodes()
104 }
105
106 #[inline(always)]
107 fn has_scope_tracking(&self) -> bool {
108 self.inner.has_scope_tracking()
109 }
110
111 #[inline]
112 fn start_scope(
113 &mut self,
114 name: Option<&str>,
115 kind: ScopeKind,
116 is_stack_frame: bool,
117 is_hidden: bool,
118 original_span: Option<Span>,
119 ) -> Result {
120 self.inner
121 .start_scope(name, kind, is_stack_frame, is_hidden, original_span)
122 }
123
124 #[inline]
125 fn end_scope(&mut self) -> Result {
126 self.inner.end_scope()
127 }
128
129 #[inline]
130 fn add_scope_variable(
131 &mut self,
132 name: &str,
133 expression: Option<&str>,
134 storage: BindingStorage,
135 ) -> Result {
136 self.inner.add_scope_variable(name, expression, storage)
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use std::sync::Arc;
143
144 use swc_common::SourceMap;
145
146 use crate::text_writer::{basic_impl::JsWriter, BindingStorage, ScopeKind, WriteJs};
147
148 #[test]
149 fn forwards_scope_operations() {
150 let source_map = Arc::new(SourceMap::default());
151 let mut out = vec![];
152 let mut scopes = vec![];
153 {
154 let writer =
155 JsWriter::new_with_scopes(source_map, "\n", &mut out, None, Some(&mut scopes));
156 let mut writer = super::omit_trailing_semi(writer);
157
158 writer
159 .start_scope(None, ScopeKind::Function, true, false, None)
160 .unwrap();
161 writer
162 .add_scope_variable("x", Some("x"), BindingStorage::Lexical)
163 .unwrap();
164 writer.end_scope().unwrap();
165 }
166
167 assert_eq!(scopes.len(), 1);
168 assert_eq!(scopes[0].bindings.len(), 1);
169 assert_eq!(scopes[0].bindings[0].name, "x");
170 }
171}