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, .. })) => {
125 if value.fract() == 0.0 {
126 Value::Number((value as i64).into())
127 } else {
128 match serde_json::Number::from_f64(value) {
129 Some(n) => Value::Number(n),
130 None => Value::Number((value as i64).into()),
131 }
132 }
133 }
134 Expr::Lit(Lit::Null(..)) => Value::Null,
135 Expr::Lit(Lit::Bool(v)) => Value::Bool(v.value),
136 Expr::Tpl(Tpl { quasis, .. }) => Value::String(match quasis.first() {
137 Some(TplElement {
138 cooked: Some(value),
139 ..
140 }) => value.to_string(),
141 _ => String::new(),
142 }),
143 _ => unreachable!("jsonify: Expr {:?} cannot be converted to json", e),
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use swc_ecma_transforms_testing::test;
150
151 use super::*;
152
153 test!(
154 ::swc_ecma_parser::Syntax::default(),
155 |_| json_parse(0),
156 simple_object,
157 "let a = {b: 'foo'}"
158 );
159
160 test!(
161 ::swc_ecma_parser::Syntax::default(),
162 |_| json_parse(0),
163 simple_arr,
164 "let a = ['foo']"
165 );
166
167 test!(
168 ::swc_ecma_parser::Syntax::default(),
169 |_| json_parse(0),
170 empty_object,
171 "const a = {};"
172 );
173
174 test!(
175 ::swc_ecma_parser::Syntax::default(),
176 |_| json_parse(15),
177 min_cost_15,
178 "const a = { b: 1, c: 2 };"
179 );
180
181 test!(
182 ::swc_ecma_parser::Syntax::default(),
183 |_| json_parse(0),
184 min_cost_0,
185 "const a = { b: 1, c: 2 };"
186 );
187
188 test!(
189 ::swc_ecma_parser::Syntax::default(),
190 |_| json_parse(0),
191 spread,
192 "const a = { ...a, b: 1 };"
193 );
194
195 test!(
196 ::swc_ecma_parser::Syntax::default(),
197 |_| json_parse(0),
198 object_method,
199 "const a = {
200 method(arg) {
201 return arg;
202 },
203 b: 1
204 };"
205 );
206
207 test!(
208 ::swc_ecma_parser::Syntax::default(),
209 |_| json_parse(0),
210 computed_property,
211 r#"const a = { b : "b_val", ["c"]: "c_val" };"#
212 );
213
214 test!(
215 ::swc_ecma_parser::Syntax::default(),
216 |_| json_parse(0),
217 invalid_numeric_key,
218 r#"const a ={ 77777777777777777.1: "foo" };"#
219 );
220
221 test!(
222 ::swc_ecma_parser::Syntax::default(),
223 |_| json_parse(0),
224 string,
225 r#"const a = { b: "b_val" };"#
226 );
227
228 test!(
229 ::swc_ecma_parser::Syntax::default(),
230 |_| json_parse(0),
231 string_single_quote_1,
232 r#"const a = { b: "'abc'" };"#,
233 ok_if_code_eq
234 );
235
236 test!(
237 ::swc_ecma_parser::Syntax::default(),
238 |_| json_parse(0),
239 string_single_quote_2,
240 r#"const a = { b: "ab\'c" };"#,
241 ok_if_code_eq
242 );
243
244 test!(
245 ::swc_ecma_parser::Syntax::default(),
246 |_| json_parse(0),
247 number,
248 "const a = { b: 1 };"
249 );
250
251 test!(
252 ::swc_ecma_parser::Syntax::default(),
253 |_| json_parse(0),
254 decimal_number,
255 "const a = { b: 24.0197, c: 0.0, d: 1.0 };"
256 );
257
258 test!(
259 ::swc_ecma_parser::Syntax::default(),
260 |_| json_parse(0),
261 null,
262 "const a = { b: null };"
263 );
264
265 test!(
266 ::swc_ecma_parser::Syntax::default(),
267 |_| json_parse(0),
268 boolean,
269 "const a = { b: false };"
270 );
271
272 test!(
273 ::swc_ecma_parser::Syntax::default(),
274 |_| json_parse(0),
275 array,
276 "const a = { b: [1, 'b_val', null] };"
277 );
278
279 test!(
280 ::swc_ecma_parser::Syntax::default(),
281 |_| json_parse(0),
282 nested_array,
283 "const a = { b: [1, ['b_val', { a: 1 }], null] };"
284 );
285
286 test!(
287 ::swc_ecma_parser::Syntax::default(),
288 |_| json_parse(0),
289 object,
290 "const a = { b: { c: 1 } };"
291 );
292
293 test!(
294 ::swc_ecma_parser::Syntax::default(),
295 |_| json_parse(0),
296 object_numeric_keys,
297 r#"const a = { 1: "123", 23: 45, b: "b_val" };"#
298 );
299 test!(
300 ::swc_ecma_parser::Syntax::default(),
301 |_| json_parse(0),
302 tpl,
303 r"const a = [`\x22\x21\x224`];"
304 );
305 test!(
306 ::swc_ecma_parser::Syntax::default(),
307 |_| json_parse(0),
308 tpl2,
309 r#"const a = [`1${b}2`];"#
310 );
311 test!(
312 ::swc_ecma_parser::Syntax::default(),
313 |_| json_parse(0),
314 tpl3,
315 r#"const a = [`1${0}2`];"#
316 );
317}