swc_estree_compat/babelify/
mod.rs

1use 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    /// Byte offset starting from the 0. (counted separately for each file)
35    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    /// Creates a [BaseNode] from `span`.
94    ///
95    /// Note that we removes comment from the comment map because `.babelify`
96    /// takes `self`. (not reference)
97    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            // TODO(kdy1): Use this field.
116            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}