turbopack_ecmascript/
json_source_transform.rs1use anyhow::{Result, bail};
2use turbo_rcstr::rcstr;
3use turbo_tasks::Vc;
4use turbo_tasks_fs::{File, FileContent, FileJsonContent};
5use turbopack_core::{
6 asset::{Asset, AssetContent},
7 context::AssetContext,
8 issue::{IssueExt, IssueSeverity, IssueSource, StyledString, code_gen::CodeGenerationIssue},
9 source::Source,
10 source_transform::SourceTransform,
11 virtual_source::VirtualSource,
12};
13
14use crate::utils::inline_source_map_comment;
15
16#[turbo_tasks::value]
30pub struct JsonSourceTransform {
31 use_esm: bool,
34}
35
36#[turbo_tasks::value_impl]
37impl JsonSourceTransform {
38 #[turbo_tasks::function]
40 pub fn new_cjs() -> Vc<Self> {
41 JsonSourceTransform { use_esm: false }.cell()
42 }
43
44 #[turbo_tasks::function]
47 pub fn new_esm() -> Vc<Self> {
48 JsonSourceTransform { use_esm: true }.cell()
49 }
50}
51
52#[turbo_tasks::value_impl]
53impl SourceTransform for JsonSourceTransform {
54 #[turbo_tasks::function]
55 async fn transform(
56 self: Vc<Self>,
57 source: Vc<Box<dyn Source>>,
58 _asset_context: Vc<Box<dyn AssetContext>>,
59 ) -> Result<Vc<Box<dyn Source>>> {
60 let this = self.await?;
61 let ident = source.ident().owned().await?;
62 let content = source.content().file_content();
63
64 let data = content.parse_json().await?;
66 let (code, rename_pattern) = match &*data {
67 FileJsonContent::Content(data) => {
68 let data_str = data.to_string();
69
70 let mut code = String::with_capacity(
73 data_str.len() + 100, );
76 code.push_str("\"use turbopack no side effects\";\n");
77
78 let rename_pattern = if this.use_esm {
79 code.push_str("export default ");
81 "*.[json].mjs"
82 } else {
83 code.push_str("module.exports = ");
85 "*.[json].cjs"
86 };
87 if data_str.len() > 10_000 {
90 code.push_str("JSON.parse(");
91 code.push_str(&serde_json::to_string(&data_str)?);
92 code.push(')');
93 } else {
94 code.push_str(&data_str);
95 }
96 code.push_str(";\n");
97 code.push_str(&inline_source_map_comment(&ident.path.path, &data_str));
98
99 (code, rename_pattern)
100 }
101 FileJsonContent::Unparsable(e) => {
102 let resolved_source = source.to_resolved().await?;
103 let issue_source = IssueSource::from_unparsable_json(resolved_source, e);
104
105 CodeGenerationIssue {
106 severity: IssueSeverity::Error,
107 path: ident.path.clone(),
108 title: StyledString::Text(rcstr!("Unable to make a module from invalid JSON"))
109 .resolved_cell(),
110 message: StyledString::Text(e.message.clone()).resolved_cell(),
111 source: Some(issue_source),
112 }
113 .resolved_cell()
114 .emit();
115
116 let js_error_message = serde_json::to_string(&format!(
117 "Unable to make a module from invalid JSON: {}",
118 e.message
119 ))?;
120 (
121 format!("throw new Error({js_error_message});"),
122 "*.[json].js",
123 )
124 }
125 FileJsonContent::NotFound => {
126 bail!("JSON file not found: {:?}", ident.path);
130 }
131 };
132
133 let new_ident = ident.rename_as(rename_pattern).into_vc();
134
135 Ok(Vc::upcast(VirtualSource::new_with_ident(
136 new_ident,
137 AssetContent::file(FileContent::Content(File::from(code)).cell()),
138 )))
139 }
140}