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