swc_ecma_transforms_react/jsx_self/
mod.rs

1use swc_common::DUMMY_SP;
2use swc_ecma_ast::*;
3use swc_ecma_transforms_base::perf::Parallel;
4use swc_ecma_utils::quote_ident;
5use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
6
7#[cfg(test)]
8mod tests;
9
10/// `@babel/plugin-transform-react-jsx-self`
11///
12/// Add a __self prop to all JSX Elements
13pub fn jsx_self(dev: bool) -> impl Pass {
14    visit_mut_pass(JsxSelf {
15        dev,
16        ctx: Default::default(),
17    })
18}
19
20/// See <https://github.com/babel/babel/blob/1bdb1a4175ed1fc40751fb84dc4ad1900260f28f/packages/babel-plugin-transform-react-jsx-self/src/index.ts#L27>
21#[derive(Clone, Copy, Default)]
22struct Context {
23    in_constructor: bool,
24    in_derived_class: bool,
25}
26
27#[derive(Clone, Copy)]
28struct JsxSelf {
29    dev: bool,
30    ctx: Context,
31}
32
33impl JsxSelf {
34    fn with_in_constructor<N: VisitMutWith<JsxSelf>>(&mut self, in_constructor: bool, n: &mut N) {
35        let old = self.ctx;
36        self.ctx.in_constructor = in_constructor;
37        n.visit_mut_children_with(self);
38        self.ctx = old;
39    }
40}
41
42impl Parallel for JsxSelf {
43    fn create(&self) -> Self {
44        *self
45    }
46
47    fn merge(&mut self, _: Self) {}
48}
49
50impl VisitMut for JsxSelf {
51    noop_visit_mut_type!();
52
53    fn visit_mut_class(&mut self, n: &mut Class) {
54        let old = self.ctx;
55        self.ctx.in_derived_class = n.super_class.is_some();
56        n.visit_mut_children_with(self);
57        self.ctx = old;
58    }
59
60    fn visit_mut_fn_decl(&mut self, n: &mut FnDecl) {
61        self.with_in_constructor(false, n);
62    }
63
64    fn visit_mut_fn_expr(&mut self, n: &mut FnExpr) {
65        self.with_in_constructor(false, n);
66    }
67
68    fn visit_mut_prop(&mut self, n: &mut Prop) {
69        match n {
70            Prop::Getter(_) | Prop::Setter(_) | Prop::Method(_) => {
71                self.with_in_constructor(false, n)
72            }
73            _ => n.visit_mut_children_with(self),
74        }
75    }
76
77    fn visit_mut_class_member(&mut self, n: &mut ClassMember) {
78        match n {
79            ClassMember::Constructor(_) => self.with_in_constructor(true, n),
80            ClassMember::Method(_)
81            | ClassMember::PrivateMethod(_)
82            | ClassMember::StaticBlock(_) => self.with_in_constructor(false, n),
83            _ => n.visit_mut_children_with(self),
84        }
85    }
86
87    fn visit_mut_jsx_opening_element(&mut self, n: &mut JSXOpeningElement) {
88        if !self.dev {
89            return;
90        }
91
92        // https://github.com/babel/babel/blob/1bdb1a4175ed1fc40751fb84dc4ad1900260f28f/packages/babel-plugin-transform-react-jsx-self/src/index.ts#L50
93        if self.ctx.in_constructor && self.ctx.in_derived_class {
94            return;
95        }
96
97        n.attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr {
98            span: DUMMY_SP,
99            name: JSXAttrName::Ident(quote_ident!("__self")),
100            value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
101                span: DUMMY_SP,
102                expr: JSXExpr::Expr(Box::new(ThisExpr { span: DUMMY_SP }.into())),
103            })),
104        }));
105    }
106}