swc_estree_compat/babelify/
jsx.rs1use 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 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}