next_custom_transforms/transforms/
pure.rs

1use swc_core::{
2    common::{comments::Comments, errors::HANDLER, util::take::Take, Span, Spanned, DUMMY_SP},
3    ecma::{
4        ast::{CallExpr, Callee, EmptyStmt, Expr, Module, ModuleDecl, ModuleItem, Stmt},
5        visit::{noop_visit_mut_type, VisitMut, VisitMutWith},
6    },
7};
8
9use crate::transforms::import_analyzer::ImportMap;
10
11pub fn pure_magic<C>(comments: C) -> PureTransform<C>
12where
13    C: Comments,
14{
15    PureTransform {
16        imports: Default::default(),
17        comments,
18    }
19}
20
21pub struct PureTransform<C>
22where
23    C: Comments,
24{
25    imports: ImportMap,
26    comments: C,
27}
28
29const MODULE: &str = "next/dist/build/swc/helpers";
30const FN_NAME: &str = "__nextjs_pure";
31
32impl<C> VisitMut for PureTransform<C>
33where
34    C: Comments,
35{
36    fn visit_mut_expr(&mut self, e: &mut Expr) {
37        e.visit_mut_children_with(self);
38
39        if let Expr::Call(CallExpr {
40            span,
41            callee: Callee::Expr(callee),
42            args,
43            ..
44        }) = e
45        {
46            if !self.imports.is_import(callee, MODULE, FN_NAME) {
47                return;
48            }
49
50            if args.len() != 1 {
51                HANDLER.with(|handler| {
52                    handler
53                        .struct_span_err(*span, "markAsPure() does not support multiple arguments")
54                        .emit();
55                });
56                return;
57            }
58
59            *e = *args[0].expr.take();
60
61            let mut lo = e.span().lo;
62            if lo.is_dummy() {
63                lo = Span::dummy_with_cmt().lo;
64            }
65
66            self.comments.add_pure_comment(lo);
67        }
68    }
69
70    fn visit_mut_module(&mut self, m: &mut Module) {
71        self.imports = ImportMap::analyze(m);
72
73        m.visit_mut_children_with(self);
74    }
75
76    fn visit_mut_module_item(&mut self, m: &mut ModuleItem) {
77        if let ModuleItem::ModuleDecl(ModuleDecl::Import(import)) = m {
78            if import.src.value == MODULE {
79                *m = ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }));
80                return;
81            }
82        }
83
84        m.visit_mut_children_with(self);
85    }
86
87    noop_visit_mut_type!();
88}