next_custom_transforms/transforms/
pure.rs1use 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}