rolldown_common/ecmascript/
json_to_program.rs1use arcstr::ArcStr;
2use oxc::{
3 allocator::Allocator,
4 ast::AstBuilder,
5 span::{SPAN, SourceType},
6};
7use rolldown_ecmascript::EcmaAst;
8use serde_json::Value;
9
10pub fn json_value_to_ecma_ast(value: &Value) -> EcmaAst {
30 let source = ArcStr::from("");
31 let allocator = Allocator::default();
32
33 EcmaAst::from_allocator_and_source(source, allocator, |allocator| {
34 let builder = AstBuilder::new(allocator);
35 let expr = json_value_to_expression(value, builder);
36 let stmt = builder.statement_expression(SPAN, expr);
37
38 builder.program(
39 SPAN,
40 SourceType::default().with_module(true),
41 "",
42 builder.vec(),
43 None,
44 builder.vec(),
45 builder.vec1(stmt),
46 )
47 })
48}
49
50pub fn json_value_to_expression<'a>(
61 value: &Value,
62 builder: AstBuilder<'a>,
63) -> oxc::ast::ast::Expression<'a> {
64 match value {
65 Value::Null => builder.expression_null_literal(SPAN),
66
67 Value::Bool(b) => builder.expression_boolean_literal(SPAN, *b),
68
69 Value::Number(n) => {
70 let f = n.as_f64().expect("JSON numbers are always representable as f64");
73 builder.expression_numeric_literal(SPAN, f, None, oxc::ast::ast::NumberBase::Decimal)
74 }
75
76 Value::String(s) => builder.expression_string_literal(SPAN, builder.str(s), None),
77
78 Value::Array(arr) => {
79 let elements = builder.vec_from_iter(arr.iter().map(|item| {
80 let expr = json_value_to_expression(item, builder);
81 oxc::ast::ast::ArrayExpressionElement::from(expr)
82 }));
83 builder.expression_array(SPAN, elements)
84 }
85
86 Value::Object(obj) => {
87 let properties = builder.vec_from_iter(obj.iter().map(|(key, val)| {
88 let key_expr = builder.expression_string_literal(SPAN, builder.str(key), None);
89 let value_expr = json_value_to_expression(val, builder);
90
91 builder.object_property_kind_object_property(
92 SPAN,
93 oxc::ast::ast::PropertyKind::Init,
94 oxc::ast::ast::PropertyKey::from(key_expr),
95 value_expr,
96 false, false, false, )
100 }));
101 builder.expression_object(SPAN, properties)
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use insta::assert_snapshot;
109 use oxc::codegen::Codegen;
110
111 use super::*;
112
113 fn to_code(value: &Value) -> String {
114 let ast = json_value_to_ecma_ast(value);
115 Codegen::new().build(ast.program()).code
116 }
117
118 #[test]
119 fn test_primitives() {
120 assert_snapshot!(to_code(&serde_json::json!(null)), @"null;");
121 assert_snapshot!(to_code(&serde_json::json!(true)), @"true;");
122 assert_snapshot!(to_code(&serde_json::json!(false)), @"false;");
123 assert_snapshot!(to_code(&serde_json::json!(42)), @"42;");
124 assert_snapshot!(to_code(&serde_json::json!(3.5)), @"3.5;");
125 assert_snapshot!(to_code(&serde_json::json!(-17)), @"-17;");
126 assert_snapshot!(to_code(&serde_json::json!(0)), @"0;");
127 assert_snapshot!(to_code(&serde_json::json!("hello")), @r#"("hello");"#);
128 assert_snapshot!(to_code(&serde_json::json!("")), @r#"("");"#);
129 assert_snapshot!(to_code(&serde_json::json!("with \"quotes\"")), @r#"("with \"quotes\"");"#);
130 }
131
132 #[test]
136 fn test_large_integer_precision_loss() {
137 let json: Value = serde_json::from_str(r#"{ "v": 9007199254740995 }"#).unwrap();
140 assert_snapshot!(to_code(&json), @r#"({ "v": 9007199254740996 });"#);
141 }
142
143 #[test]
144 fn test_array() {
145 assert_snapshot!(to_code(&serde_json::json!([])), @"[];");
146 assert_snapshot!(to_code(&serde_json::json!([1, 2, 3])), @r"
147 [
148 1,
149 2,
150 3
151 ];
152 ");
153 assert_snapshot!(to_code(&serde_json::json!(["a", "b"])), @r#"["a", "b"];"#);
154 assert_snapshot!(to_code(&serde_json::json!([1, "mixed", true, null])), @r#"
155 [
156 1,
157 "mixed",
158 true,
159 null
160 ];
161 "#);
162 }
163
164 #[test]
165 fn test_object() {
166 assert_snapshot!(to_code(&serde_json::json!({})), @"({});");
167 assert_snapshot!(to_code(&serde_json::json!({"a": 1})), @r#"({ "a": 1 });"#);
168 assert_snapshot!(to_code(&serde_json::json!({"key with spaces": 1})), @r#"({ "key with spaces": 1 });"#);
169 assert_snapshot!(to_code(&serde_json::json!({"true": 1})), @r#"({ "true": 1 });"#);
170 assert_snapshot!(to_code(&serde_json::from_str::<Value>(r#"{"a": 1, "a": 2}"#).unwrap()), @r#"({ "a": 2 });"#);
172 }
173
174 #[test]
176 fn test_float_17_significant_digits() {
177 let inputs = [
178 114.351_437_992_579_97_f64,
179 406.314_867_132_489_95_f64,
180 163.414_980_184_984_98_f64,
181 364.094_987_249_009_9_f64,
182 ];
183 let json: Value = serde_json::from_str(
184 r"[114.35143799257997, 406.31486713248995, 163.41498018498498, 364.09498724900986]",
185 )
186 .unwrap();
187 let code: String = to_code(&json).chars().filter(|c| !c.is_whitespace()).collect();
188 let expected = format!("[{}];", inputs.map(|v| v.to_string()).join(","));
189 assert_eq!(code, expected);
190 }
191
192 #[test]
193 fn test_nested() {
194 assert_snapshot!(to_code(&serde_json::json!({
195 "name": "test",
196 "values": [1, 2, 3],
197 "nested": {
198 "deep": true
199 }
200 })), @r#"
201 ({
202 "name": "test",
203 "values": [
204 1,
205 2,
206 3
207 ],
208 "nested": { "deep": true }
209 });
210 "#);
211 }
212}