styled_components/utils/
analyzer.rs

1use swc_ecma_ast::*;
2use swc_ecma_visit::{noop_visit_type, visit_pass, Visit, VisitWith};
3
4use super::State;
5use crate::Config;
6
7pub fn analyzer<'a>(config: &'a Config, state: &'a mut State) -> impl 'a + Pass {
8    visit_pass(Analyzer { config, state })
9}
10
11pub fn analyze(config: &Config, program: &Program) -> State {
12    let mut state = State::default();
13
14    let mut v = Analyzer {
15        config,
16        state: &mut state,
17    };
18
19    program.visit_with(&mut v);
20
21    state
22}
23
24struct Analyzer<'a> {
25    config: &'a Config,
26    state: &'a mut State,
27}
28
29impl Visit for Analyzer<'_> {
30    noop_visit_type!(fail);
31
32    fn visit_var_declarator(&mut self, v: &VarDeclarator) {
33        v.visit_children_with(self);
34
35        if let (
36            Pat::Ident(name),
37            Some(Expr::Call(CallExpr {
38                callee: Callee::Expr(callee),
39                args,
40                ..
41            })),
42        ) = (&v.name, v.init.as_deref())
43        {
44            if let Expr::Ident(callee) = &**callee {
45                if &*callee.sym == "require" && args.len() == 1 && args[0].spread.is_none() {
46                    if let Expr::Lit(Lit::Str(v)) = &*args[0].expr {
47                        let is_styled = if self.config.top_level_import_paths.is_empty() {
48                            &*v.value == "styled-components"
49                                || v.value.starts_with("styled-components/")
50                        } else {
51                            self.config.top_level_import_paths.contains(&v.value)
52                        };
53
54                        if is_styled {
55                            self.state.styled_required = Some(name.id.to_id());
56                            self.state.unresolved_ctxt = Some(callee.ctxt);
57                        }
58                    }
59                }
60            }
61        }
62    }
63
64    fn visit_import_decl(&mut self, i: &ImportDecl) {
65        let is_styled = if self.config.top_level_import_paths.is_empty() {
66            &*i.src.value == "styled-components" || i.src.value.starts_with("styled-components/")
67        } else {
68            self.config.top_level_import_paths.contains(&i.src.value)
69        };
70
71        if is_styled {
72            for s in &i.specifiers {
73                match s {
74                    ImportSpecifier::Named(s) => {
75                        let imported = s
76                            .imported
77                            .as_ref()
78                            .map(|v| match v {
79                                ModuleExportName::Ident(v) => &*v.sym,
80                                ModuleExportName::Str(v) => &*v.value,
81                            })
82                            .unwrap_or(&*s.local.sym);
83                        self.state
84                            .imported_local_named
85                            .insert(imported.to_string(), s.local.to_id());
86                    }
87                    ImportSpecifier::Default(s) => {
88                        self.state.imported_local_name = Some(s.local.to_id());
89                    }
90                    ImportSpecifier::Namespace(s) => {
91                        self.state.imported_local_ns = Some(s.local.to_id());
92                    }
93                }
94            }
95        }
96    }
97}