/
hook_optimizer.rs
120 lines (111 loc) · 3.43 KB
/
hook_optimizer.rs
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use swc_atoms::JsWord;
use swc_common::DUMMY_SP;
use swc_ecmascript::ast::{
ArrayPat, Callee, Decl, Expr, Ident, ImportDecl, ImportSpecifier, KeyValuePatProp, Number,
ObjectPat, ObjectPatProp, Pat, PropName, VarDecl, VarDeclarator,
};
use swc_ecmascript::visit::{Fold, FoldWith};
pub fn hook_optimizer() -> impl Fold {
HookOptimizer::default()
}
#[derive(Debug, Default)]
struct HookOptimizer {
hooks: Vec<JsWord>,
}
impl Fold for HookOptimizer {
// Find hooks imported from react/preact
fn fold_import_decl(&mut self, decl: ImportDecl) -> ImportDecl {
let ImportDecl {
ref src,
ref specifiers,
..
} = decl;
if &src.value == "react" || &src.value == "preact/hooks" {
for specifier in specifiers {
if let ImportSpecifier::Named(named_specifier) = specifier {
if named_specifier.local.sym.starts_with("use") {
self.hooks.push(named_specifier.local.sym.clone())
}
}
}
}
decl
}
// Transform array desctructing to object destructuring for relevant hooks
fn fold_decl(&mut self, node: Decl) -> Decl {
let node = node.fold_children_with(self);
match node {
Decl::Var(VarDecl {
decls,
span,
kind,
declare,
}) => {
let mut new_decls = Vec::with_capacity(decls.len());
for decl in decls {
new_decls.push(self.get_decl(decl));
}
Decl::Var(VarDecl {
decls: new_decls,
span,
kind,
declare,
})
}
_ => node,
}
}
}
impl HookOptimizer {
fn get_decl(&mut self, decl: VarDeclarator) -> VarDeclarator {
let VarDeclarator {
name,
init,
span,
definite,
} = &decl;
let init_clone = init.clone();
if let Pat::Array(a) = name {
if let Expr::Call(c) = &*init.as_deref().unwrap() {
if let Callee::Expr(i) = &c.callee {
if let Expr::Ident(Ident { sym, .. }) = &**i {
if self.hooks.contains(sym) {
let name = get_object_pattern(a);
return VarDeclarator {
name,
init: init_clone,
span: *span,
definite: *definite,
};
}
}
}
}
}
decl
}
}
fn get_object_pattern(array_pattern: &ArrayPat) -> Pat {
let props: Vec<ObjectPatProp> = array_pattern
.elems
.iter()
.enumerate()
.filter_map(|(i, elem)| {
elem.as_ref().map(|elem| {
ObjectPatProp::KeyValue(KeyValuePatProp {
key: PropName::Num(Number {
value: i as f64,
span: DUMMY_SP,
}),
value: Box::new(elem.clone()),
})
})
})
.collect();
Pat::Object(ObjectPat {
props,
span: DUMMY_SP,
optional: false,
type_ann: None,
})
}