swc_ecma_transforms_typescript/
typescript.rs1use std::mem;
2
3use rustc_hash::FxHashSet;
4use swc_atoms::atom;
5use swc_common::{comments::Comments, sync::Lrc, util::take::Take, Mark, SourceMap, Span, Spanned};
6use swc_ecma_ast::*;
7use swc_ecma_transforms_react::{parse_expr_for_jsx, JsxDirectives};
8use swc_ecma_visit::{visit_mut_pass, VisitMut, VisitMutWith};
9
10pub use crate::config::*;
11use crate::{strip_import_export::StripImportExport, strip_type::StripType, transform::transform};
12
13macro_rules! static_str {
14 ($s:expr) => {
15 $s.into()
16 };
17}
18
19pub fn typescript(config: Config, unresolved_mark: Mark, top_level_mark: Mark) -> impl Pass {
20 debug_assert_ne!(unresolved_mark, top_level_mark);
21
22 visit_mut_pass(TypeScript {
23 config,
24 unresolved_mark,
25 top_level_mark,
26 id_usage: Default::default(),
27 })
28}
29
30pub fn strip(unresolved_mark: Mark, top_level_mark: Mark) -> impl Pass {
31 typescript(Config::default(), unresolved_mark, top_level_mark)
32}
33
34pub(crate) struct TypeScript {
35 pub config: Config,
36 pub unresolved_mark: Mark,
37 pub top_level_mark: Mark,
38
39 id_usage: FxHashSet<Id>,
40}
41
42impl VisitMut for TypeScript {
43 fn visit_mut_program(&mut self, n: &mut Program) {
44 let was_module = n.as_module().and_then(|m| self.get_last_module_span(m));
45
46 if !self.config.verbatim_module_syntax {
47 n.visit_mut_with(&mut StripImportExport {
48 import_not_used_as_values: self.config.import_not_used_as_values,
49 usage_info: mem::take(&mut self.id_usage).into(),
50 ..Default::default()
51 });
52 }
53
54 n.visit_mut_with(&mut StripType::default());
55
56 n.mutate(transform(
57 self.unresolved_mark,
58 self.top_level_mark,
59 self.config.import_export_assign_config,
60 self.config.ts_enum_is_mutable,
61 self.config.verbatim_module_syntax,
62 self.config.native_class_properties,
63 ));
64
65 if let Some(span) = was_module {
66 let module = n.as_mut_module().unwrap();
67 Self::restore_esm_ctx(module, span);
68 }
69 }
70
71 fn visit_mut_script(&mut self, _: &mut Script) {
72 #[cfg(debug_assertions)]
73 unreachable!("Use Program as entry");
74 #[cfg(not(debug_assertions))]
75 unreachable!();
76 }
77
78 fn visit_mut_module(&mut self, _: &mut Module) {
79 #[cfg(debug_assertions)]
80 unreachable!("Use Program as entry");
81 #[cfg(not(debug_assertions))]
82 unreachable!();
83 }
84}
85
86impl TypeScript {
87 fn get_last_module_span(&self, n: &Module) -> Option<Span> {
88 if self.config.no_empty_export {
89 return None;
90 }
91
92 n.body
93 .iter()
94 .rev()
95 .find(|m| m.is_es_module_decl())
96 .map(Spanned::span)
97 }
98
99 fn restore_esm_ctx(n: &mut Module, span: Span) {
100 if n.body.iter().any(ModuleItem::is_es_module_decl) {
101 return;
102 }
103
104 n.body.push(
105 NamedExport {
106 span,
107 ..NamedExport::dummy()
108 }
109 .into(),
110 );
111 }
112}
113
114trait EsModuleDecl {
115 fn is_es_module_decl(&self) -> bool;
116}
117
118impl EsModuleDecl for ModuleDecl {
119 fn is_es_module_decl(&self) -> bool {
120 match self {
123 ModuleDecl::Import(..)
124 | ModuleDecl::ExportDecl(..)
125 | ModuleDecl::ExportNamed(..)
126 | ModuleDecl::ExportDefaultDecl(..)
127 | ModuleDecl::ExportDefaultExpr(..)
128 | ModuleDecl::ExportAll(..) => true,
129
130 ModuleDecl::TsImportEquals(..)
131 | ModuleDecl::TsExportAssignment(..)
132 | ModuleDecl::TsNamespaceExport(..) => false,
133 #[cfg(swc_ast_unknown)]
134 _ => panic!("unable to access unknown nodes"),
135 }
136 }
137}
138
139impl EsModuleDecl for ModuleItem {
140 fn is_es_module_decl(&self) -> bool {
141 self.as_module_decl()
142 .is_some_and(ModuleDecl::is_es_module_decl)
143 }
144}
145
146pub fn tsx<C>(
147 cm: Lrc<SourceMap>,
148 config: Config,
149 tsx_config: TsxConfig,
150 comments: C,
151 unresolved_mark: Mark,
152 top_level_mark: Mark,
153) -> impl Pass
154where
155 C: Comments,
156{
157 visit_mut_pass(TypeScriptReact {
158 config,
159 tsx_config,
160 id_usage: Default::default(),
161 comments,
162 cm,
163 top_level_mark,
164 unresolved_mark,
165 })
166}
167
168fn id_for_jsx(e: &Expr) -> Option<Id> {
172 match e {
173 Expr::Ident(i) => Some(i.to_id()),
174 Expr::Member(MemberExpr { obj, .. }) => Some(id_for_jsx(obj)).flatten(),
175 Expr::Lit(Lit::Null(..)) => Some((atom!("null"), Default::default())),
176 _ => None,
177 }
178}
179
180struct TypeScriptReact<C>
181where
182 C: Comments,
183{
184 config: Config,
185 tsx_config: TsxConfig,
186 id_usage: FxHashSet<Id>,
187 comments: C,
188 cm: Lrc<SourceMap>,
189 top_level_mark: Mark,
190 unresolved_mark: Mark,
191}
192
193impl<C> VisitMut for TypeScriptReact<C>
194where
195 C: Comments,
196{
197 fn visit_mut_module(&mut self, n: &mut Module) {
198 if !self.config.verbatim_module_syntax {
203 let pragma = parse_expr_for_jsx(
204 &self.cm,
205 "pragma",
206 self.tsx_config
207 .pragma
208 .clone()
209 .unwrap_or_else(|| static_str!("React.createElement")),
210 self.top_level_mark,
211 );
212
213 let pragma_frag = parse_expr_for_jsx(
214 &self.cm,
215 "pragma",
216 self.tsx_config
217 .pragma_frag
218 .clone()
219 .unwrap_or_else(|| static_str!("React.Fragment")),
220 self.top_level_mark,
221 );
222
223 let pragma_id = id_for_jsx(&pragma).unwrap();
224 let pragma_frag_id = id_for_jsx(&pragma_frag).unwrap();
225
226 self.id_usage.insert(pragma_id);
227 self.id_usage.insert(pragma_frag_id);
228 }
229
230 if !self.config.verbatim_module_syntax {
231 let span = if n.shebang.is_some() {
232 n.span
233 .with_lo(n.body.first().map(|s| s.span_lo()).unwrap_or(n.span.lo))
234 } else {
235 n.span
236 };
237
238 let JsxDirectives {
239 pragma,
240 pragma_frag,
241 ..
242 } = self.comments.with_leading(span.lo, |comments| {
243 JsxDirectives::from_comments(&self.cm, span, comments, self.top_level_mark)
244 });
245
246 if let Some(pragma) = pragma {
247 if let Some(pragma_id) = id_for_jsx(&pragma) {
248 self.id_usage.insert(pragma_id);
249 }
250 }
251
252 if let Some(pragma_frag) = pragma_frag {
253 if let Some(pragma_frag_id) = id_for_jsx(&pragma_frag) {
254 self.id_usage.insert(pragma_frag_id);
255 }
256 }
257 }
258 }
259
260 fn visit_mut_script(&mut self, _: &mut Script) {
261 }
263
264 fn visit_mut_program(&mut self, n: &mut Program) {
265 n.visit_mut_children_with(self);
266
267 n.visit_mut_with(&mut TypeScript {
268 config: mem::take(&mut self.config),
269 unresolved_mark: self.unresolved_mark,
270 top_level_mark: self.top_level_mark,
271 id_usage: mem::take(&mut self.id_usage),
272 });
273 }
274}