1#![feature(min_specialization)]
9#![feature(arbitrary_self_types)]
10#![feature(arbitrary_self_types_pointers)]
11
12use std::fmt::Write;
13
14use anyhow::{Error, Result, bail};
15use turbo_rcstr::rcstr;
16use turbo_tasks::{ResolvedVc, ValueToString, Vc};
17use turbo_tasks_fs::{FileContent, FileJsonContent, glob::Glob};
18use turbopack_core::{
19 asset::{Asset, AssetContent},
20 chunk::{ChunkItem, ChunkType, ChunkableModule, ChunkingContext},
21 code_builder::CodeBuilder,
22 ident::AssetIdent,
23 module::Module,
24 module_graph::ModuleGraph,
25 source::Source,
26};
27use turbopack_ecmascript::{
28 chunk::{
29 EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable,
30 EcmascriptChunkType, EcmascriptExports,
31 },
32 runtime_functions::TURBOPACK_EXPORT_VALUE,
33};
34
35#[turbo_tasks::value]
36pub struct JsonModuleAsset {
37 source: ResolvedVc<Box<dyn Source>>,
38}
39
40#[turbo_tasks::value_impl]
41impl JsonModuleAsset {
42 #[turbo_tasks::function]
43 pub fn new(source: ResolvedVc<Box<dyn Source>>) -> Vc<Self> {
44 Self::cell(JsonModuleAsset { source })
45 }
46}
47
48#[turbo_tasks::value_impl]
49impl Module for JsonModuleAsset {
50 #[turbo_tasks::function]
51 fn ident(&self) -> Vc<AssetIdent> {
52 self.source.ident().with_modifier(rcstr!("json"))
53 }
54}
55
56#[turbo_tasks::value_impl]
57impl Asset for JsonModuleAsset {
58 #[turbo_tasks::function]
59 fn content(&self) -> Vc<AssetContent> {
60 self.source.content()
61 }
62}
63
64#[turbo_tasks::value_impl]
65impl ChunkableModule for JsonModuleAsset {
66 #[turbo_tasks::function]
67 fn as_chunk_item(
68 self: ResolvedVc<Self>,
69 _module_graph: Vc<ModuleGraph>,
70 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
71 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
72 Vc::upcast(JsonChunkItem::cell(JsonChunkItem {
73 module: self,
74 chunking_context,
75 }))
76 }
77}
78
79#[turbo_tasks::value_impl]
80impl EcmascriptChunkPlaceable for JsonModuleAsset {
81 #[turbo_tasks::function]
82 fn get_exports(&self) -> Vc<EcmascriptExports> {
83 EcmascriptExports::Value.cell()
84 }
85
86 #[turbo_tasks::function]
87 fn is_marked_as_side_effect_free(&self, _side_effect_free_packages: Vc<Glob>) -> Vc<bool> {
88 Vc::cell(true)
89 }
90}
91
92#[turbo_tasks::value]
93struct JsonChunkItem {
94 module: ResolvedVc<JsonModuleAsset>,
95 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
96}
97
98#[turbo_tasks::value_impl]
99impl ChunkItem for JsonChunkItem {
100 #[turbo_tasks::function]
101 fn asset_ident(&self) -> Vc<AssetIdent> {
102 self.module.ident()
103 }
104
105 #[turbo_tasks::function]
106 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
107 *self.chunking_context
108 }
109
110 #[turbo_tasks::function]
111 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
112 Ok(Vc::upcast(
113 Vc::<EcmascriptChunkType>::default().resolve().await?,
114 ))
115 }
116
117 #[turbo_tasks::function]
118 fn module(&self) -> Vc<Box<dyn Module>> {
119 Vc::upcast(*self.module)
120 }
121}
122
123#[turbo_tasks::value_impl]
124impl EcmascriptChunkItem for JsonChunkItem {
125 #[turbo_tasks::function]
126 async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
127 let content = self.module.content().file_content();
130 let data = content.parse_json().await?;
131 match &*data {
132 FileJsonContent::Content(data) => {
133 let data_str = data.to_string();
134
135 let mut code = CodeBuilder::default();
136
137 let source_code = if data_str.len() > 10_000 {
138 let js_str_content = serde_json::to_string(&data_str)?;
141 format!("{TURBOPACK_EXPORT_VALUE}(JSON.parse({js_str_content}));")
142 } else {
143 format!("{TURBOPACK_EXPORT_VALUE}({data_str});")
144 };
145
146 let source_code = source_code.into();
147 let source_map = serde_json::json!({
148 "version": 3,
149 "sources": [format!("turbopack:///{}", self.module.ident().path().to_string().await?)],
155 "sourcesContent": [&data_str],
156 "names": [],
157 "mappings": "AAAA",
160 })
161 .to_string()
162 .into();
163 code.push_source(&source_code, Some(source_map));
164
165 let code = code.build();
166 Ok(EcmascriptChunkItemContent {
167 source_map: Some(code.generate_source_map_ref(None)),
168 inner_code: code.into_source_code(),
169 ..Default::default()
170 }
171 .into())
172 }
173 FileJsonContent::Unparsable(e) => {
174 let mut message = "Unable to make a module from invalid JSON: ".to_string();
175 if let FileContent::Content(content) = &*content.await? {
176 let text = content.content().to_str()?;
177 e.write_with_content(&mut message, text.as_ref())?;
178 } else {
179 write!(message, "{e}")?;
180 }
181
182 Err(Error::msg(message))
183 }
184 FileJsonContent::NotFound => {
185 bail!(
186 "JSON file not found: {}",
187 self.module.ident().to_string().await?
188 );
189 }
190 }
191 }
192}