Skip to main content

rolldown_plugin_utils/
data_to_esm.rs

1use std::fmt::Write as _;
2
3use rolldown_utils::concat_string;
4use serde_json::Value;
5
6use super::constants::THRESHOLD_SIZE;
7
8#[inline]
9fn serialize_value(value: &Value) -> Result<String, serde_json::Error> {
10  let value_as_string = serde_json::to_string(value)?;
11  if value_as_string.len() > THRESHOLD_SIZE && value.is_object() {
12    let value = serde_json::to_string(&value_as_string)?;
13    Ok(concat_string!("/*#__PURE__*/ JSON.parse(", value, ")"))
14  } else {
15    Ok(value_as_string)
16  }
17}
18
19pub fn data_to_esm(data: &Value, named_exports: bool) -> String {
20  if !named_exports || !data.is_object() {
21    return concat_string!("export default ", data.to_string(), ";\n");
22  }
23
24  let data = data.as_object().unwrap();
25  if data.is_empty() {
26    return "export default {};\n".to_string();
27  }
28
29  let mut named_export_code = String::new();
30  let mut default_object_code = String::new();
31  for (key, value) in data {
32    let value = serialize_value(value).expect("Invalid JSON value");
33    if rolldown_utils::ecmascript::is_validate_assignee_identifier_name(key) {
34      writeln!(named_export_code, "export const {key} = {value};").unwrap();
35      writeln!(default_object_code, "  {key},").unwrap();
36    } else {
37      let key = serde_json::to_string(key).unwrap();
38      writeln!(default_object_code, "  {key}: {value},").unwrap();
39    }
40  }
41
42  // Remove the trailing ",\n"
43  default_object_code.truncate(default_object_code.len() - 2);
44
45  concat_string!(named_export_code, "export default {\n", default_object_code, "\n};")
46}
47
48#[cfg(test)]
49mod test {
50  use super::data_to_esm;
51
52  #[test]
53  fn to_esm_named_exports_object() {
54    let data = serde_json::json!({"name": "name"});
55    assert_eq!(
56      "export const name = \"name\";\nexport default {\n  name\n};",
57      data_to_esm(&data, true)
58    );
59  }
60
61  #[test]
62  fn to_esm_named_exports_literal() {
63    let data = serde_json::json!(1);
64    assert_eq!("export default 1;\n", data_to_esm(&data, true));
65  }
66
67  #[test]
68  fn to_esm_named_exports_forbidden_ident() {
69    let data = serde_json::json!({"true": true, "\\\"\n": 1234, "eval": 0, "arguments": 0});
70    assert_eq!(
71      "export default {\n  \"true\": true,\n  \"\\\\\\\"\\n\": 1234,\n  \"eval\": 0,\n  \"arguments\": 0\n};",
72      data_to_esm(&data, true)
73    );
74  }
75
76  #[test]
77  fn to_esm_named_exports_multiple_fields() {
78    let data = serde_json::json!({"foo": "foo", "bar": "bar"});
79    assert_eq!(
80      "export const foo = \"foo\";\nexport const bar = \"bar\";\nexport default {\n  foo,\n  bar\n};",
81      data_to_esm(&data, true)
82    );
83  }
84}