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