swc_estree_compat/babelify/
mod.rs1use serde::{de::DeserializeOwned, Serialize};
2use swc_common::{
3 comments::{CommentKind, Comments},
4 sync::Lrc,
5 BytePos, SourceFile, SourceMap, Span,
6};
7use swc_ecma_ast::Class;
8use swc_estree_ast::{flavor::Flavor, BaseComment, BaseNode, Comment, CommentType, LineCol, Loc};
9use swc_node_comments::SwcComments;
10
11mod class;
12mod decl;
13mod expr;
14mod function;
15mod ident;
16mod jsx;
17mod lit;
18mod module;
19mod module_decl;
20mod operators;
21mod pat;
22mod prop;
23mod stmt;
24mod typescript;
25
26#[derive(Clone)]
27pub struct Context {
28 pub fm: Lrc<SourceFile>,
29 pub cm: Lrc<SourceMap>,
30 pub comments: SwcComments,
31}
32
33impl Context {
34 fn offset(&self, span: Span) -> (Option<u32>, Option<u32>) {
36 if span.is_dummy() {
37 return (None, None);
38 }
39
40 let (start, end) = self.cm.span_to_char_offset(&self.fm, span);
41
42 (Some(start), Some(end))
43 }
44
45 fn line_col(&self, pos: BytePos) -> Option<LineCol> {
46 let loc = self.cm.lookup_char_pos_with(self.fm.clone(), pos);
47
48 Some(LineCol {
49 line: loc.line,
50 column: loc.col.0,
51 })
52 }
53
54 fn loc(&self, span: Span) -> Option<Loc> {
55 if span.is_dummy() {
56 return None;
57 }
58 if !Flavor::current().emit_loc() {
59 return None;
60 }
61
62 let start = self.line_col(span.lo)?;
63 let end = self.line_col(span.hi)?;
64
65 Some(Loc { start, end })
66 }
67
68 fn convert_comments(&self, comments: Vec<swc_common::comments::Comment>) -> Vec<Comment> {
69 comments
70 .into_iter()
71 .map(|c| {
72 let (start, end) = self.offset(c.span);
73 let loc = self.loc(c.span).unwrap_or_else(Loc::dummy);
74
75 let comment = BaseComment {
76 type_: match c.kind {
77 CommentKind::Line => CommentType::Line,
78 CommentKind::Block => CommentType::Block,
79 },
80 value: c.text,
81 start: start.unwrap_or_default(),
82 end: end.unwrap_or_default(),
83 loc,
84 };
85 match c.kind {
86 CommentKind::Line => Comment::Line(comment),
87 CommentKind::Block => Comment::Block(comment),
88 }
89 })
90 .collect()
91 }
92
93 fn base(&self, span: Span) -> BaseNode {
98 let leading_comments = self
99 .comments
100 .take_leading(span.lo)
101 .map(|v| self.convert_comments(v))
102 .unwrap_or_default();
103 let trailing_comments = self
104 .comments
105 .take_trailing(span.hi)
106 .map(|v| self.convert_comments(v))
107 .unwrap_or_default();
108
109 let (start, end) = self.offset(span);
110
111 let loc = self.loc(span);
112
113 BaseNode {
114 leading_comments,
115 inner_comments: Default::default(),
117 trailing_comments,
118 start,
119 end,
120 loc,
121 range: if matches!(Flavor::current(), Flavor::Acorn { .. }) {
122 match (start, end) {
123 (Some(start), Some(end)) => Some([start, end]),
124 _ => None,
125 }
126 } else {
127 None
128 },
129 }
130 }
131}
132
133pub trait Babelify: Send + Sync {
134 type Output: Serialize + DeserializeOwned + Send + Sync;
135
136 fn parallel(_cnt: usize) -> bool {
137 false
138 }
139
140 fn babelify(self, ctx: &Context) -> Self::Output;
141}
142
143impl<T> Babelify for Vec<T>
144where
145 T: Babelify,
146{
147 type Output = Vec<T::Output>;
148
149 fn babelify(self, ctx: &Context) -> Self::Output {
150 self.into_iter().map(|v| v.babelify(ctx)).collect()
151 }
152}
153
154impl<T> Babelify for Option<T>
155where
156 T: Babelify,
157{
158 type Output = Option<T::Output>;
159
160 fn babelify(self, ctx: &Context) -> Self::Output {
161 self.map(|v| v.babelify(ctx))
162 }
163}
164
165impl<T> Babelify for Box<T>
166where
167 T: Babelify,
168{
169 type Output = T::Output;
170
171 fn babelify(self, ctx: &Context) -> Self::Output {
172 (*self).babelify(ctx)
173 }
174}
175
176fn extract_class_body_span(class: &Class, ctx: &Context) -> Span {
177 let sp = ctx.cm.span_take_while(class.span, |ch| *ch != '{');
178 class.span.with_lo(sp.hi())
179}