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