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();
62 let path = ident.path().await?;
63 let content = source.content().file_content();
64
65 let data = content.parse_json().await?;
67 let (code, extension) = match &*data {
68 FileJsonContent::Content(data) => {
69 let data_str = data.to_string();
70
71 let mut code = String::with_capacity(
74 data_str.len() + 100, );
77 code.push_str("\"use turbopack no side effects\";\n");
78
79 let extension = if this.use_esm {
80 code.push_str("export default ");
82 "mjs"
83 } else {
84 code.push_str("module.exports = ");
86 "cjs"
87 };
88 if data_str.len() > 10_000 {
91 code.push_str("JSON.parse(");
92 code.push_str(&serde_json::to_string(&data_str)?);
93 code.push(')');
94 } else {
95 code.push_str(&data_str);
96 }
97 code.push_str(";\n");
98 code.push_str(&inline_source_map_comment(&path.path, &data_str));
99
100 (code, extension)
101 }
102 FileJsonContent::Unparsable(e) => {
103 let resolved_source = source.to_resolved().await?;
104 let issue_source = IssueSource::from_unparsable_json(resolved_source, e);
105
106 CodeGenerationIssue {
107 severity: IssueSeverity::Error,
108 path: ident.path().owned().await?,
109 title: StyledString::Text(rcstr!("Unable to make a module from invalid JSON"))
110 .resolved_cell(),
111 message: StyledString::Text(e.message.clone()).resolved_cell(),
112 source: Some(issue_source),
113 }
114 .resolved_cell()
115 .emit();
116
117 let js_error_message = serde_json::to_string(&format!(
118 "Unable to make a module from invalid JSON: {}",
119 e.message
120 ))?;
121 (format!("throw new Error({js_error_message});"), "js")
122 }
123 FileJsonContent::NotFound => {
124 bail!("JSON file not found: {:?}", path);
128 }
129 };
130
131 let new_ident = ident.rename_as(format!("{}.[json].{}", path.path, extension).into());
132
133 Ok(Vc::upcast(VirtualSource::new_with_ident(
134 new_ident,
135 AssetContent::file(FileContent::Content(File::from(code)).cell()),
136 )))
137 }
138}