swc_estree_compat/babelify/
module.rs1use serde::{Deserialize, Serialize};
2use swc_common::{comments::Comment, Span};
3use swc_ecma_ast::{Module, ModuleItem, Program, Script};
4use swc_ecma_visit::{Visit, VisitWith};
5use swc_estree_ast::{
6 flavor::Flavor, BaseNode, File, InterpreterDirective, LineCol, Loc, ModuleDeclaration,
7 Program as BabelProgram, SrcType, Statement,
8};
9use swc_node_comments::SwcComments;
10
11use crate::babelify::{Babelify, Context};
12
13impl Babelify for Program {
14 type Output = File;
15
16 fn babelify(self, ctx: &Context) -> Self::Output {
17 let comments = extract_all_comments(&self, ctx);
18 let program = match self {
19 Program::Module(module) => module.babelify(ctx),
20 Program::Script(script) => script.babelify(ctx),
21 #[cfg(swc_ast_unknown)]
24 _ => panic!("unable to access unknown nodes"),
25 };
26
27 File {
28 base: BaseNode {
29 leading_comments: Default::default(),
30 inner_comments: Default::default(),
31 trailing_comments: Default::default(),
32 start: program.base.start,
33 end: program.base.end,
34 loc: program.base.loc,
35 range: if matches!(Flavor::current(), Flavor::Acorn { .. }) {
36 match (program.base.start, program.base.end) {
37 (Some(start), Some(end)) => Some([start, end]),
38 _ => None,
39 }
40 } else {
41 None
42 },
43 },
44 program,
45 comments: Some(ctx.convert_comments(comments)),
46 tokens: Default::default(),
47 }
48 }
49}
50
51impl Babelify for Module {
52 type Output = BabelProgram;
53
54 fn babelify(self, ctx: &Context) -> Self::Output {
55 let span = if has_comment_first_line(self.span, ctx) {
56 self.span.with_lo(ctx.fm.start_pos)
57 } else {
58 self.span
59 };
60 BabelProgram {
61 base: base_with_trailing_newline(span, ctx),
62 source_type: SrcType::Module,
63 body: self
64 .body
65 .into_iter()
66 .map(|stmt| stmt.babelify(ctx).into())
67 .collect(),
68 interpreter: self.shebang.map(|s| InterpreterDirective {
69 base: ctx.base(extract_shebang_span(span, ctx)),
70 value: s,
71 }),
72 directives: Default::default(),
73 source_file: Default::default(),
74 comments: Default::default(),
75 }
76 }
77}
78
79impl Babelify for Script {
80 type Output = BabelProgram;
81
82 fn babelify(self, ctx: &Context) -> Self::Output {
83 let span = if has_comment_first_line(self.span, ctx) {
84 self.span.with_lo(ctx.fm.start_pos)
85 } else {
86 self.span
87 };
88 BabelProgram {
89 base: base_with_trailing_newline(span, ctx),
90 source_type: SrcType::Script,
91 body: self.body.babelify(ctx),
92 interpreter: self.shebang.map(|s| InterpreterDirective {
93 base: ctx.base(extract_shebang_span(span, ctx)),
94 value: s,
95 }),
96 directives: Default::default(),
97 source_file: Default::default(),
98 comments: Default::default(),
99 }
100 }
101}
102
103fn base_with_trailing_newline(span: Span, ctx: &Context) -> BaseNode {
108 let mut base = ctx.base(span);
109
110 base.end = base.end.map(|num| num + 1);
111 base.loc = base.loc.map(|loc| Loc {
112 end: LineCol {
113 line: loc.end.line + 1,
114 column: 0,
115 },
116 ..loc
117 });
118 base.range = base.range.map(|range| [range[0], range[1] + 1]);
119
120 base
121}
122
123fn has_comment_first_line(sp: Span, ctx: &Context) -> bool {
128 if let Some(comments) = ctx.comments.leading.get(&sp.hi) {
129 !comments
130 .first()
131 .map(|c| c.span.lo == ctx.fm.start_pos)
132 .unwrap_or(false)
133 } else {
134 true
135 }
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub enum ModuleItemOutput {
140 ModuleDecl(ModuleDeclaration),
141 Stmt(Statement),
142}
143
144impl Babelify for ModuleItem {
145 type Output = ModuleItemOutput;
146
147 fn parallel(cnt: usize) -> bool {
148 cnt >= 32
149 }
150
151 fn babelify(self, ctx: &Context) -> Self::Output {
152 match self {
153 ModuleItem::ModuleDecl(d) => ModuleItemOutput::ModuleDecl(d.babelify(ctx).into()),
154 ModuleItem::Stmt(s) => ModuleItemOutput::Stmt(s.babelify(ctx)),
155 #[cfg(swc_ast_unknown)]
156 _ => panic!("unable to access unknown nodes"),
157 }
158 }
159}
160
161impl From<ModuleItemOutput> for Statement {
162 fn from(m: ModuleItemOutput) -> Self {
163 match m {
164 ModuleItemOutput::Stmt(stmt) => stmt,
165 ModuleItemOutput::ModuleDecl(decl) => match decl {
166 ModuleDeclaration::ExportAll(e) => Statement::ExportAllDecl(e),
167 ModuleDeclaration::ExportDefault(e) => Statement::ExportDefaultDecl(e),
168 ModuleDeclaration::ExportNamed(e) => Statement::ExportNamedDecl(e),
169 ModuleDeclaration::Import(i) => Statement::ImportDecl(i),
170 },
171 }
172 }
173}
174
175fn extract_shebang_span(span: Span, ctx: &Context) -> Span {
176 ctx.cm.span_take_while(span, |ch| *ch != '\n')
177}
178
179fn extract_all_comments(program: &Program, ctx: &Context) -> Vec<Comment> {
180 let mut collector = CommentCollector {
181 comments: ctx.comments.clone(),
182 collected: Vec::new(),
183 };
184 program.visit_with(&mut collector);
185 collector.collected
186}
187
188struct CommentCollector {
189 comments: SwcComments,
190 collected: Vec<Comment>,
191}
192
193impl Visit for CommentCollector {
194 fn visit_span(&mut self, sp: &Span) {
195 let mut span_comments: Vec<Comment> = Vec::new();
196 if let Some(comments) = self.comments.leading.get(&sp.lo) {
200 for comment in comments.iter() {
201 if !self.collected.contains(comment) {
202 span_comments.push(comment.clone());
203 }
204 }
205 }
206
207 if let Some(comments) = self.comments.trailing.get(&sp.hi) {
208 for comment in comments.iter() {
209 if !self.collected.contains(comment) {
210 span_comments.push(comment.clone());
211 }
212 }
213 }
214 self.collected.append(&mut span_comments);
215 }
216}