1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//! Port of https://github.com/styled-components/babel-plugin-styled-components/blob/4e2eb388d9c90f2921c306c760657d059d01a518/src/visitors/templateLiterals/transpile.js

use std::{cell::RefCell, iter, rc::Rc};

use swc_common::{util::take::Take, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};

use crate::utils::State;

pub fn template_literals(state: Rc<RefCell<State>>) -> impl Fold + VisitMut {
    as_folder(TemplateLiterals { state })
}

#[derive(Debug)]
struct TemplateLiterals {
    state: Rc<RefCell<State>>,
}

impl VisitMut for TemplateLiterals {
    noop_visit_mut_type!();

    fn visit_mut_expr(&mut self, expr: &mut Expr) {
        expr.visit_mut_children_with(self);

        let Expr::TaggedTpl(tagged) = expr else {
            return;
        };
        if !self.state.borrow().is_styled(&tagged.tag)
            && !self.state.borrow().is_helper(&tagged.tag)
        {
            return;
        }

        expr.map_with_mut(|expr| {
            let tagged = expr.expect_tagged_tpl();

            let quasis = tagged
                .tpl
                .quasis
                .into_iter()
                .map(|q| {
                    Expr::Lit(Lit::Str(Str {
                        span: q.span,
                        value: q.cooked.unwrap_or(q.raw),
                        raw: None,
                    }))
                })
                .map(ExprOrSpread::from)
                .map(Some);
            let exprs = tagged.tpl.exprs.into_iter().map(ExprOrSpread::from);
            let args = iter::once(
                Expr::Array(ArrayLit {
                    span: DUMMY_SP,
                    elems: quasis.collect(),
                })
                .into(),
            )
            .chain(exprs)
            .collect();

            Expr::Call(CallExpr {
                span: tagged.span,
                callee: tagged.tag.into(),
                args,
                type_args: tagged.type_params,
            })
        });
    }
}