swc_estree_compat/babelify/
jsx.rs

1use copyless::BoxHelper;
2use swc_common::{BytePos, Span, Spanned};
3use swc_ecma_ast::{
4    JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXAttrValue, JSXClosingElement, JSXClosingFragment,
5    JSXElement, JSXElementChild, JSXElementName, JSXEmptyExpr, JSXExpr, JSXExprContainer,
6    JSXFragment, JSXMemberExpr, JSXNamespacedName, JSXObject, JSXOpeningElement,
7    JSXOpeningFragment, JSXSpreadChild, JSXText,
8};
9use swc_estree_ast::{
10    flavor::Flavor, JSXAttrName as BabelJSXAttrName, JSXAttrVal, JSXAttribute,
11    JSXClosingElement as BabelJSXClosingElement, JSXClosingFragment as BabelJSXClosingFragment,
12    JSXElement as BabelJSXElement, JSXElementChild as BabelJSXElementChild,
13    JSXElementName as BabelJSXElementName, JSXEmptyExpression, JSXExprContainerExpr,
14    JSXExpressionContainer, JSXFragment as BabelJSXFragment, JSXMemberExprObject,
15    JSXMemberExpression, JSXNamespacedName as BabelJSXNamespacedName, JSXOpeningElAttr,
16    JSXOpeningElement as BabelJSXOpeningElement, JSXOpeningFragment as BabelJSXOpeningFragment,
17    JSXSpreadAttribute, JSXSpreadChild as BabelJSXSpreadChild, JSXText as BabelJSXText,
18};
19
20use crate::babelify::{Babelify, Context};
21
22impl Babelify for JSXObject {
23    type Output = JSXMemberExprObject;
24
25    fn babelify(self, ctx: &Context) -> Self::Output {
26        match self {
27            JSXObject::JSXMemberExpr(e) => JSXMemberExprObject::Expr(e.babelify(ctx)),
28            JSXObject::Ident(i) => JSXMemberExprObject::Id(i.babelify(ctx).into()),
29            #[cfg(swc_ast_unknown)]
30            _ => panic!("unable to access unknown nodes"),
31        }
32    }
33}
34
35impl Babelify for JSXMemberExpr {
36    type Output = JSXMemberExpression;
37
38    fn babelify(self, ctx: &Context) -> Self::Output {
39        JSXMemberExpression {
40            base: ctx.base(self.span()),
41            object: Box::alloc().init(self.obj.babelify(ctx)),
42            property: self.prop.babelify(ctx).into(),
43        }
44    }
45}
46
47impl Babelify for JSXNamespacedName {
48    type Output = BabelJSXNamespacedName;
49
50    fn babelify(self, ctx: &Context) -> Self::Output {
51        BabelJSXNamespacedName {
52            base: ctx.base(self.span()),
53            namespace: self.ns.babelify(ctx).into(),
54            name: self.name.babelify(ctx).into(),
55        }
56    }
57}
58
59impl Babelify for JSXEmptyExpr {
60    type Output = JSXEmptyExpression;
61
62    fn babelify(self, ctx: &Context) -> Self::Output {
63        JSXEmptyExpression {
64            base: ctx.base(self.span),
65        }
66    }
67}
68
69impl Babelify for JSXExprContainer {
70    type Output = JSXExpressionContainer;
71
72    fn babelify(self, ctx: &Context) -> Self::Output {
73        JSXExpressionContainer {
74            base: ctx.base(self.span),
75            expression: self.expr.babelify(ctx),
76        }
77    }
78}
79
80impl Babelify for JSXExpr {
81    type Output = JSXExprContainerExpr;
82
83    fn babelify(self, ctx: &Context) -> Self::Output {
84        match self {
85            JSXExpr::JSXEmptyExpr(e) => JSXExprContainerExpr::Empty(e.babelify(ctx)),
86            JSXExpr::Expr(e) => {
87                JSXExprContainerExpr::Expr(Box::alloc().init(e.babelify(ctx).into()))
88            }
89            #[cfg(swc_ast_unknown)]
90            _ => panic!("unable to access unknown nodes"),
91        }
92    }
93}
94
95impl Babelify for JSXSpreadChild {
96    type Output = BabelJSXSpreadChild;
97
98    fn babelify(self, ctx: &Context) -> Self::Output {
99        BabelJSXSpreadChild {
100            base: ctx.base(self.span),
101            expression: Box::alloc().init(self.expr.babelify(ctx).into()),
102        }
103    }
104}
105
106impl Babelify for JSXElementName {
107    type Output = BabelJSXElementName;
108
109    fn babelify(self, ctx: &Context) -> Self::Output {
110        match self {
111            JSXElementName::Ident(i) => BabelJSXElementName::Id(i.babelify(ctx).into()),
112            JSXElementName::JSXMemberExpr(e) => BabelJSXElementName::Expr(e.babelify(ctx)),
113            JSXElementName::JSXNamespacedName(n) => BabelJSXElementName::Name(n.babelify(ctx)),
114            #[cfg(swc_ast_unknown)]
115            _ => panic!("unable to access unknown nodes"),
116        }
117    }
118}
119
120impl Babelify for JSXOpeningElement {
121    type Output = BabelJSXOpeningElement;
122
123    fn babelify(self, ctx: &Context) -> Self::Output {
124        BabelJSXOpeningElement {
125            base: ctx.base(self.span),
126            name: self.name.babelify(ctx),
127            attributes: self.attrs.babelify(ctx),
128            self_closing: self.self_closing,
129            type_parameters: self.type_args.map(|arg| arg.babelify(ctx).into()),
130        }
131    }
132}
133
134impl Babelify for JSXAttrOrSpread {
135    type Output = JSXOpeningElAttr;
136
137    fn babelify(self, ctx: &Context) -> Self::Output {
138        match self {
139            JSXAttrOrSpread::JSXAttr(a) => JSXOpeningElAttr::Attr(a.babelify(ctx)),
140            JSXAttrOrSpread::SpreadElement(spread) => {
141                // For JSX spread elements, babel includes the curly braces, and swc
142                // does not. So we extend the span to include the braces here.
143                let span = extend_spread_span_to_braces(spread.span(), ctx);
144                JSXOpeningElAttr::Spread(JSXSpreadAttribute {
145                    base: ctx.base(span),
146                    argument: Box::alloc().init(spread.expr.babelify(ctx).into()),
147                })
148            }
149            #[cfg(swc_ast_unknown)]
150            _ => panic!("unable to access unknown nodes"),
151        }
152    }
153}
154
155fn extend_spread_span_to_braces(sp: Span, ctx: &Context) -> Span {
156    let mut span = sp;
157    let _ = ctx.cm.with_span_to_prev_source(sp, |prev_source| {
158        let mut num_chars = 0;
159        for c in prev_source.chars().rev() {
160            num_chars += 1;
161            if c == '{' {
162                span = span.with_lo(span.lo - BytePos(num_chars));
163            } else if !c.is_whitespace() {
164                break;
165            }
166        }
167    });
168
169    let _ = ctx.cm.with_span_to_next_source(sp, |next_source| {
170        let mut num_chars = 0;
171        for c in next_source.chars() {
172            num_chars += 1;
173            if c == '}' {
174                span = span.with_hi(span.hi + BytePos(num_chars));
175            } else if !c.is_whitespace() {
176                break;
177            }
178        }
179    });
180
181    span
182}
183
184impl Babelify for JSXClosingElement {
185    type Output = BabelJSXClosingElement;
186
187    fn babelify(self, ctx: &Context) -> Self::Output {
188        BabelJSXClosingElement {
189            base: ctx.base(self.span),
190            name: self.name.babelify(ctx),
191        }
192    }
193}
194
195impl Babelify for JSXAttr {
196    type Output = JSXAttribute;
197
198    fn babelify(self, ctx: &Context) -> Self::Output {
199        JSXAttribute {
200            base: ctx.base(self.span),
201            name: self.name.babelify(ctx),
202            value: self.value.map(|val| val.babelify(ctx)),
203        }
204    }
205}
206
207impl Babelify for JSXAttrName {
208    type Output = BabelJSXAttrName;
209
210    fn babelify(self, ctx: &Context) -> Self::Output {
211        match self {
212            JSXAttrName::Ident(i) => BabelJSXAttrName::Id(i.babelify(ctx).into()),
213            JSXAttrName::JSXNamespacedName(n) => BabelJSXAttrName::Name(n.babelify(ctx)),
214            #[cfg(swc_ast_unknown)]
215            _ => panic!("unable to access unknown nodes"),
216        }
217    }
218}
219
220impl Babelify for JSXAttrValue {
221    type Output = JSXAttrVal;
222
223    fn babelify(self, ctx: &Context) -> Self::Output {
224        match self {
225            JSXAttrValue::Str(s) => JSXAttrVal::String(s.babelify(ctx)),
226            JSXAttrValue::JSXExprContainer(e) => JSXAttrVal::Expr(e.babelify(ctx)),
227            JSXAttrValue::JSXElement(e) => JSXAttrVal::Element(e.babelify(ctx)),
228            JSXAttrValue::JSXFragment(f) => JSXAttrVal::Fragment(f.babelify(ctx)),
229            #[cfg(swc_ast_unknown)]
230            _ => panic!("unable to access unknown nodes"),
231        }
232    }
233}
234
235impl Babelify for JSXText {
236    type Output = BabelJSXText;
237
238    fn babelify(self, ctx: &Context) -> Self::Output {
239        BabelJSXText {
240            base: ctx.base(self.span),
241            value: self.value,
242        }
243    }
244}
245
246impl Babelify for JSXElement {
247    type Output = BabelJSXElement;
248
249    fn babelify(self, ctx: &Context) -> Self::Output {
250        let self_closing = match Flavor::current() {
251            Flavor::Babel => None,
252            Flavor::Acorn { .. } => Some(self.closing.is_some()),
253        };
254        BabelJSXElement {
255            base: ctx.base(self.span),
256            opening_element: self.opening.babelify(ctx),
257            closing_element: self.closing.map(|el| el.babelify(ctx)),
258            children: self.children.babelify(ctx),
259            self_closing,
260        }
261    }
262}
263
264impl Babelify for JSXElementChild {
265    type Output = BabelJSXElementChild;
266
267    fn babelify(self, ctx: &Context) -> Self::Output {
268        match self {
269            JSXElementChild::JSXText(t) => BabelJSXElementChild::Text(t.babelify(ctx)),
270            JSXElementChild::JSXExprContainer(e) => BabelJSXElementChild::Expr(e.babelify(ctx)),
271            JSXElementChild::JSXSpreadChild(s) => BabelJSXElementChild::Spread(s.babelify(ctx)),
272            JSXElementChild::JSXElement(e) => BabelJSXElementChild::Element(e.babelify(ctx)),
273            JSXElementChild::JSXFragment(f) => BabelJSXElementChild::Fragment(f.babelify(ctx)),
274            #[cfg(swc_ast_unknown)]
275            _ => panic!("unable to access unknown nodes"),
276        }
277    }
278}
279
280impl Babelify for JSXFragment {
281    type Output = BabelJSXFragment;
282
283    fn babelify(self, ctx: &Context) -> Self::Output {
284        BabelJSXFragment {
285            base: ctx.base(self.span),
286            opening_fragment: self.opening.babelify(ctx),
287            closing_fragment: self.closing.babelify(ctx),
288            children: self.children.babelify(ctx),
289        }
290    }
291}
292
293impl Babelify for JSXOpeningFragment {
294    type Output = BabelJSXOpeningFragment;
295
296    fn babelify(self, ctx: &Context) -> Self::Output {
297        BabelJSXOpeningFragment {
298            base: ctx.base(self.span),
299        }
300    }
301}
302
303impl Babelify for JSXClosingFragment {
304    type Output = BabelJSXClosingFragment;
305
306    fn babelify(self, ctx: &Context) -> Self::Output {
307        BabelJSXClosingFragment {
308            base: ctx.base(self.span),
309        }
310    }
311}