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
use crate::{
pass::Pass,
util::{alias_if_required, undefined, StmtLike},
};
use ast::*;
use std::mem::replace;
use swc_common::{Fold, FoldWith, DUMMY_SP};
#[cfg(test)]
mod tests;
pub fn nullish_coalescing() -> impl Pass + 'static {
NullishCoalescing::default()
}
#[derive(Debug, Default)]
struct NullishCoalescing {
vars: Vec<VarDeclarator>,
}
impl<T> Fold<Vec<T>> for NullishCoalescing
where
T: FoldWith<Self> + StmtLike,
{
fn fold(&mut self, stmts: Vec<T>) -> Vec<T> {
let mut buf = Vec::with_capacity(stmts.len() + 2);
for stmt in stmts {
let stmt = stmt.fold_with(self);
if !self.vars.is_empty() {
buf.push(T::from_stmt(Stmt::Decl(Decl::Var(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
decls: replace(&mut self.vars, Default::default()),
declare: false,
}))));
}
buf.push(stmt);
}
buf
}
}
impl Fold<Expr> for NullishCoalescing {
fn fold(&mut self, e: Expr) -> Expr {
let e = e.fold_children(self);
match e {
Expr::Bin(BinExpr {
span,
left,
op: op!("??"),
right,
}) => {
let (l, aliased) = alias_if_required(&left, "ref");
if aliased {
self.vars.push(VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(l.clone()),
init: None,
definite: false,
});
}
let var_expr = if aliased {
Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: PatOrExpr::Pat(box Pat::Ident(l.clone())),
right: left,
})
} else {
Expr::Ident(l.clone())
};
return Expr::Cond(CondExpr {
span,
test: box Expr::Bin(BinExpr {
span: DUMMY_SP,
left: box Expr::Bin(BinExpr {
span: DUMMY_SP,
left: box var_expr,
op: op!("!=="),
right: box Expr::Lit(Lit::Null(Null { span: DUMMY_SP })),
}),
op: op!("&&"),
right: box Expr::Bin(BinExpr {
span: DUMMY_SP,
left: box Expr::Ident(l.clone()),
op: op!("!=="),
right: undefined(DUMMY_SP),
}),
}),
cons: box Expr::Ident(l.clone()),
alt: right,
});
}
_ => {}
}
e
}
}