swc_ecma_transforms_optimization/
json_parse.rs1use serde_json::Value;
2use swc_common::{util::take::Take, Spanned, DUMMY_SP};
3use swc_ecma_ast::*;
4use swc_ecma_transforms_base::perf::Parallel;
5use swc_ecma_utils::{calc_literal_cost, member_expr, ExprFactory};
6use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
7
8pub fn json_parse(min_cost: usize) -> impl Pass {
31 visit_mut_pass(JsonParse { min_cost })
32}
33
34struct JsonParse {
35 pub min_cost: usize,
36}
37
38impl Parallel for JsonParse {
39 fn create(&self) -> Self {
40 JsonParse {
41 min_cost: self.min_cost,
42 }
43 }
44
45 fn merge(&mut self, _: Self) {}
46}
47
48impl Default for JsonParse {
49 fn default() -> Self {
50 JsonParse { min_cost: 1024 }
51 }
52}
53
54impl VisitMut for JsonParse {
55 noop_visit_mut_type!(fail);
56
57 fn visit_mut_expr(&mut self, expr: &mut Expr) {
59 if self.min_cost == usize::MAX {
60 return;
61 }
62
63 let e = match expr {
64 Expr::Array(..) | Expr::Object(..) => {
65 let (is_lit, cost) = calc_literal_cost(&*expr, false);
66 if is_lit && cost >= self.min_cost {
67 let value =
68 serde_json::to_string(&jsonify(expr.take())).unwrap_or_else(|err| {
69 unreachable!("failed to serialize serde_json::Value as json: {}", err)
70 });
71
72 *expr = CallExpr {
73 span: expr.span(),
74 callee: member_expr!(Default::default(), DUMMY_SP, JSON.parse).as_callee(),
75 args: vec![Lit::Str(Str {
76 span: DUMMY_SP,
77 raw: None,
78 value: value.into(),
79 })
80 .as_arg()],
81 ..Default::default()
82 }
83 .into();
84 return;
85 }
86
87 expr
88 }
89 _ => expr,
90 };
91
92 e.visit_mut_children_with(self)
93 }
94}
95
96fn jsonify(e: Expr) -> Value {
97 match e {
98 Expr::Object(obj) => Value::Object(
99 obj.props
100 .into_iter()
101 .map(|v| match v {
102 PropOrSpread::Prop(p) if p.is_key_value() => p.key_value().unwrap(),
103 _ => unreachable!(),
104 })
105 .map(|p: KeyValueProp| {
106 let value = jsonify(*p.value);
107 let key = match p.key {
108 PropName::Str(s) => s.value.to_string(),
109 PropName::Ident(id) => id.sym.to_string(),
110 PropName::Num(n) => format!("{}", n.value),
111 _ => unreachable!(),
112 };
113 (key, value)
114 })
115 .collect(),
116 ),
117 Expr::Array(arr) => Value::Array(
118 arr.elems
119 .into_iter()
120 .map(|v| jsonify(*v.unwrap().expr))
121 .collect(),
122 ),
123 Expr::Lit(Lit::Str(Str { value, .. })) => Value::String(value.to_string()),
124 Expr::Lit(Lit::Num(Number { value, .. })) => Value::Number((value as i64).into()),
125 Expr::Lit(Lit::Null(..)) => Value::Null,
126 Expr::Lit(Lit::Bool(v)) => Value::Bool(v.value),
127 Expr::Tpl(Tpl { quasis, .. }) => Value::String(match quasis.first() {
128 Some(TplElement {
129 cooked: Some(value),
130 ..
131 }) => value.to_string(),
132 _ => String::new(),
133 }),
134 _ => unreachable!("jsonify: Expr {:?} cannot be converted to json", e),
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use swc_ecma_transforms_testing::test;
141
142 use super::*;
143
144 test!(
145 ::swc_ecma_parser::Syntax::default(),
146 |_| json_parse(0),
147 simple_object,
148 "let a = {b: 'foo'}"
149 );
150
151 test!(
152 ::swc_ecma_parser::Syntax::default(),
153 |_| json_parse(0),
154 simple_arr,
155 "let a = ['foo']"
156 );
157
158 test!(
159 ::swc_ecma_parser::Syntax::default(),
160 |_| json_parse(0),
161 empty_object,
162 "const a = {};"
163 );
164
165 test!(
166 ::swc_ecma_parser::Syntax::default(),
167 |_| json_parse(15),
168 min_cost_15,
169 "const a = { b: 1, c: 2 };"
170 );
171
172 test!(
173 ::swc_ecma_parser::Syntax::default(),
174 |_| json_parse(0),
175 min_cost_0,
176 "const a = { b: 1, c: 2 };"
177 );
178
179 test!(
180 ::swc_ecma_parser::Syntax::default(),
181 |_| json_parse(0),
182 spread,
183 "const a = { ...a, b: 1 };"
184 );
185
186 test!(
187 ::swc_ecma_parser::Syntax::default(),
188 |_| json_parse(0),
189 object_method,
190 "const a = {
191 method(arg) {
192 return arg;
193 },
194 b: 1
195 };"
196 );
197
198 test!(
199 ::swc_ecma_parser::Syntax::default(),
200 |_| json_parse(0),
201 computed_property,
202 r#"const a = { b : "b_val", ["c"]: "c_val" };"#
203 );
204
205 test!(
206 ::swc_ecma_parser::Syntax::default(),
207 |_| json_parse(0),
208 invalid_numeric_key,
209 r#"const a ={ 77777777777777777.1: "foo" };"#
210 );
211
212 test!(
213 ::swc_ecma_parser::Syntax::default(),
214 |_| json_parse(0),
215 string,
216 r#"const a = { b: "b_val" };"#
217 );
218
219 test!(
220 ::swc_ecma_parser::Syntax::default(),
221 |_| json_parse(0),
222 string_single_quote_1,
223 r#"const a = { b: "'abc'" };"#,
224 ok_if_code_eq
225 );
226
227 test!(
228 ::swc_ecma_parser::Syntax::default(),
229 |_| json_parse(0),
230 string_single_quote_2,
231 r#"const a = { b: "ab\'c" };"#,
232 ok_if_code_eq
233 );
234
235 test!(
236 ::swc_ecma_parser::Syntax::default(),
237 |_| json_parse(0),
238 number,
239 "const a = { b: 1 };"
240 );
241
242 test!(
243 ::swc_ecma_parser::Syntax::default(),
244 |_| json_parse(0),
245 null,
246 "const a = { b: null };"
247 );
248
249 test!(
250 ::swc_ecma_parser::Syntax::default(),
251 |_| json_parse(0),
252 boolean,
253 "const a = { b: false };"
254 );
255
256 test!(
257 ::swc_ecma_parser::Syntax::default(),
258 |_| json_parse(0),
259 array,
260 "const a = { b: [1, 'b_val', null] };"
261 );
262
263 test!(
264 ::swc_ecma_parser::Syntax::default(),
265 |_| json_parse(0),
266 nested_array,
267 "const a = { b: [1, ['b_val', { a: 1 }], null] };"
268 );
269
270 test!(
271 ::swc_ecma_parser::Syntax::default(),
272 |_| json_parse(0),
273 object,
274 "const a = { b: { c: 1 } };"
275 );
276
277 test!(
278 ::swc_ecma_parser::Syntax::default(),
279 |_| json_parse(0),
280 object_numeric_keys,
281 r#"const a = { 1: "123", 23: 45, b: "b_val" };"#
282 );
283 test!(
284 ::swc_ecma_parser::Syntax::default(),
285 |_| json_parse(0),
286 tpl,
287 r"const a = [`\x22\x21\x224`];"
288 );
289 test!(
290 ::swc_ecma_parser::Syntax::default(),
291 |_| json_parse(0),
292 tpl2,
293 r#"const a = [`1${b}2`];"#
294 );
295 test!(
296 ::swc_ecma_parser::Syntax::default(),
297 |_| json_parse(0),
298 tpl3,
299 r#"const a = [`1${0}2`];"#
300 );
301}