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, AssetContent},
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 Asset for JsonModuleAsset {
69 #[turbo_tasks::function]
70 fn content(&self) -> Vc<AssetContent> {
71 self.source.content()
72 }
73}
74
75#[turbo_tasks::value_impl]
76impl ChunkableModule for JsonModuleAsset {
77 #[turbo_tasks::function]
78 fn as_chunk_item(
79 self: ResolvedVc<Self>,
80 _module_graph: Vc<ModuleGraph>,
81 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
82 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
83 Vc::upcast(JsonChunkItem::cell(JsonChunkItem {
84 module: self,
85 chunking_context,
86 }))
87 }
88}
89
90#[turbo_tasks::value_impl]
91impl EcmascriptChunkPlaceable for JsonModuleAsset {
92 #[turbo_tasks::function]
93 fn get_exports(&self) -> Vc<EcmascriptExports> {
94 EcmascriptExports::Value.cell()
95 }
96}
97
98#[turbo_tasks::value]
99struct JsonChunkItem {
100 module: ResolvedVc<JsonModuleAsset>,
101 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
102}
103
104#[turbo_tasks::value_impl]
105impl OutputAssetsReference for JsonChunkItem {}
106
107#[turbo_tasks::value_impl]
108impl ChunkItem for JsonChunkItem {
109 #[turbo_tasks::function]
110 fn asset_ident(&self) -> Vc<AssetIdent> {
111 self.module.ident()
112 }
113
114 #[turbo_tasks::function]
115 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
116 *self.chunking_context
117 }
118
119 #[turbo_tasks::function]
120 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
121 Ok(Vc::upcast(
122 Vc::<EcmascriptChunkType>::default().resolve().await?,
123 ))
124 }
125
126 #[turbo_tasks::function]
127 fn module(&self) -> Vc<Box<dyn Module>> {
128 Vc::upcast(*self.module)
129 }
130}
131
132#[turbo_tasks::value_impl]
133impl EcmascriptChunkItem for JsonChunkItem {
134 #[turbo_tasks::function]
135 async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
136 let content = self.module.content().file_content();
139 let data = content.parse_json().await?;
140 match &*data {
141 FileJsonContent::Content(data) => {
142 let data_str = data.to_string();
143
144 let mut code = CodeBuilder::default();
145
146 let source_code = if data_str.len() > 10_000 {
147 let js_str_content = serde_json::to_string(&data_str)?;
150 format!("{TURBOPACK_EXPORT_VALUE}(JSON.parse({js_str_content}));")
151 } else {
152 format!("{TURBOPACK_EXPORT_VALUE}({data_str});")
153 };
154
155 let source_code = source_code.into();
156 let source_map = serde_json::json!({
157 "version": 3,
158 "sources": [format!("turbopack:///{}", self.module.ident().path().to_string().await?)],
164 "sourcesContent": [&data_str],
165 "names": [],
166 "mappings": "AAAA",
169 })
170 .to_string()
171 .into();
172 code.push_source(&source_code, Some(source_map));
173
174 let code = code.build();
175 Ok(EcmascriptChunkItemContent {
176 source_map: Some(code.generate_source_map_ref(None)),
177 inner_code: code.into_source_code(),
178 ..Default::default()
179 }
180 .cell())
181 }
182 FileJsonContent::Unparsable(e) => {
183 let mut message = "Unable to make a module from invalid JSON: ".to_string();
184 if let FileContent::Content(content) = &*content.await? {
185 let text = content.content().to_str()?;
186 e.write_with_content(&mut message, text.as_ref())?;
187 } else {
188 write!(message, "{e}")?;
189 }
190
191 Err(Error::msg(message))
192 }
193 FileJsonContent::NotFound => {
194 bail!(
195 "JSON file not found: {}",
196 self.module.ident().to_string().await?
197 );
198 }
199 }
200 }
201}