1use anyhow::Result;
2use turbo_rcstr::rcstr;
3use turbo_tasks::{IntoTraitRef, ResolvedVc, TryJoinIterExt, ValueToString, Vc};
4use turbo_tasks_fs::{FileContent, FileSystemPath};
5use turbopack_core::{
6 chunk::{ChunkItem, ChunkType, ChunkableModule, ChunkingContext, MinifyType},
7 context::AssetContext,
8 environment::Environment,
9 ident::AssetIdent,
10 module::{Module, ModuleSideEffects, StyleModule, StyleType},
11 module_graph::ModuleGraph,
12 output::{OutputAssetsReference, OutputAssetsWithReferenced},
13 reference::{ModuleReference, ModuleReferences},
14 reference_type::ImportContext,
15 resolve::origin::ResolveOrigin,
16 source::{OptionSource, Source},
17 source_map::GenerateSourceMap,
18};
19
20use crate::{
21 CssModuleAssetType,
22 chunk::{CssChunkItem, CssChunkItemContent, CssChunkPlaceable, CssChunkType, CssImport},
23 code_gen::CodeGenerateable,
24 process::{
25 CssWithPlaceholderResult, FinalCssResult, ParseCss, ParseCssResult, ProcessCss,
26 finalize_css, parse_css, process_css_with_placeholder,
27 },
28 references::{
29 compose::CssModuleComposeReference, import::ImportAssetReference, url::ReferencedAsset,
30 },
31};
32
33#[turbo_tasks::value]
34#[derive(Clone)]
35pub struct CssModuleAsset {
37 source: ResolvedVc<Box<dyn Source>>,
38 asset_context: ResolvedVc<Box<dyn AssetContext>>,
39 import_context: Option<ResolvedVc<ImportContext>>,
40 ty: CssModuleAssetType,
41 environment: Option<ResolvedVc<Environment>>,
42}
43
44#[turbo_tasks::value_impl]
45impl CssModuleAsset {
46 #[turbo_tasks::function]
48 pub fn new(
49 source: ResolvedVc<Box<dyn Source>>,
50 asset_context: ResolvedVc<Box<dyn AssetContext>>,
51 ty: CssModuleAssetType,
52 import_context: Option<ResolvedVc<ImportContext>>,
53 environment: Option<ResolvedVc<Environment>>,
54 ) -> Vc<Self> {
55 Self::cell(CssModuleAsset {
56 source,
57 asset_context,
58 import_context,
59 ty,
60 environment,
61 })
62 }
63
64 #[turbo_tasks::function]
66 pub fn source_ident(&self) -> Vc<AssetIdent> {
67 self.source.ident()
68 }
69}
70
71#[turbo_tasks::value_impl]
72impl ParseCss for CssModuleAsset {
73 #[turbo_tasks::function]
74 async fn parse_css(self: Vc<Self>) -> Result<Vc<ParseCssResult>> {
75 let this = self.await?;
76
77 Ok(parse_css(
78 *this.source,
79 Vc::upcast(self),
80 this.import_context.map(|v| *v),
81 this.ty,
82 this.environment.as_deref().copied(),
83 ))
84 }
85}
86
87#[turbo_tasks::value_impl]
88impl ProcessCss for CssModuleAsset {
89 #[turbo_tasks::function]
90 async fn get_css_with_placeholder(self: Vc<Self>) -> Result<Vc<CssWithPlaceholderResult>> {
91 let this = self.await?;
92 let parse_result = self.parse_css();
93
94 Ok(process_css_with_placeholder(
95 parse_result,
96 this.environment.as_deref().copied(),
97 ))
98 }
99
100 #[turbo_tasks::function]
101 async fn finalize_css(
102 self: Vc<Self>,
103 chunking_context: Vc<Box<dyn ChunkingContext>>,
104 minify_type: MinifyType,
105 ) -> Result<Vc<FinalCssResult>> {
106 let process_result = self.get_css_with_placeholder();
107
108 let this = self.await?;
109 let origin_source_map =
110 match ResolvedVc::try_sidecast::<Box<dyn GenerateSourceMap>>(this.source) {
111 Some(gsm) => gsm.generate_source_map(),
112 None => FileContent::NotFound.cell(),
113 };
114 Ok(finalize_css(
115 process_result,
116 chunking_context,
117 minify_type,
118 origin_source_map,
119 this.environment.as_deref().copied(),
120 ))
121 }
122}
123
124#[turbo_tasks::value_impl]
125impl Module for CssModuleAsset {
126 #[turbo_tasks::function]
127 async fn ident(&self) -> Result<Vc<AssetIdent>> {
128 let mut ident = self
129 .source
130 .ident()
131 .with_modifier(rcstr!("css"))
132 .with_layer(self.asset_context.into_trait_ref().await?.layer());
133 if let Some(import_context) = self.import_context {
134 ident = ident.with_modifier(import_context.modifier().owned().await?)
135 }
136 Ok(ident)
137 }
138
139 #[turbo_tasks::function]
140 fn source(&self) -> Vc<OptionSource> {
141 Vc::cell(Some(self.source))
142 }
143
144 #[turbo_tasks::function]
145 async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
146 let result = self.parse_css().await?;
147 match &*result {
150 ParseCssResult::Ok { references, .. } => Ok(**references),
151 ParseCssResult::Unparsable => Ok(ModuleReferences::empty()),
152 ParseCssResult::NotFound => Ok(ModuleReferences::empty()),
153 }
154 }
155 #[turbo_tasks::function]
156 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
157 ModuleSideEffects::SideEffectful.cell()
159 }
160}
161
162#[turbo_tasks::value_impl]
163impl StyleModule for CssModuleAsset {
164 #[turbo_tasks::function]
165 fn style_type(&self) -> Vc<StyleType> {
166 match self.ty {
167 CssModuleAssetType::Default => StyleType::GlobalStyle.cell(),
168 CssModuleAssetType::Module => StyleType::IsolatedStyle.cell(),
169 }
170 }
171}
172
173#[turbo_tasks::value_impl]
174impl ChunkableModule for CssModuleAsset {
175 #[turbo_tasks::function]
176 fn as_chunk_item(
177 self: ResolvedVc<Self>,
178 module_graph: ResolvedVc<ModuleGraph>,
179 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
180 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
181 Vc::upcast(CssModuleChunkItem::cell(CssModuleChunkItem {
182 module: self,
183 module_graph,
184 chunking_context,
185 }))
186 }
187}
188
189#[turbo_tasks::value_impl]
190impl CssChunkPlaceable for CssModuleAsset {}
191
192#[turbo_tasks::value_impl]
193impl ResolveOrigin for CssModuleAsset {
194 #[turbo_tasks::function]
195 fn origin_path(&self) -> Vc<FileSystemPath> {
196 self.source.ident().path()
197 }
198
199 #[turbo_tasks::function]
200 fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
201 *self.asset_context
202 }
203}
204
205#[turbo_tasks::value]
206struct CssModuleChunkItem {
207 module: ResolvedVc<CssModuleAsset>,
208 module_graph: ResolvedVc<ModuleGraph>,
209 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
210}
211
212#[turbo_tasks::value_impl]
213impl OutputAssetsReference for CssModuleChunkItem {
214 #[turbo_tasks::function]
215 async fn references(&self) -> Result<Vc<OutputAssetsWithReferenced>> {
216 let mut references = Vec::new();
217 if let ParseCssResult::Ok { url_references, .. } = &*self.module.parse_css().await? {
218 for (_, reference) in url_references.await? {
219 if let ReferencedAsset::Some(asset) = *reference
220 .get_referenced_asset(*self.chunking_context)
221 .await?
222 {
223 references.push(asset);
224 }
225 }
226 }
227 Ok(OutputAssetsWithReferenced::from_assets(Vc::cell(
228 references,
229 )))
230 }
231}
232
233#[turbo_tasks::value_impl]
234impl ChunkItem for CssModuleChunkItem {
235 #[turbo_tasks::function]
236 fn asset_ident(&self) -> Vc<AssetIdent> {
237 self.module.ident()
238 }
239
240 #[turbo_tasks::function]
241 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
242 *self.chunking_context
243 }
244
245 #[turbo_tasks::function]
246 fn ty(&self) -> Vc<Box<dyn ChunkType>> {
247 Vc::upcast(Vc::<CssChunkType>::default())
248 }
249
250 #[turbo_tasks::function]
251 fn module(&self) -> Vc<Box<dyn Module>> {
252 Vc::upcast(*self.module)
253 }
254}
255
256#[turbo_tasks::value_impl]
257impl CssChunkItem for CssModuleChunkItem {
258 #[turbo_tasks::function]
259 async fn content(&self) -> Result<Vc<CssChunkItemContent>> {
260 let references = &*self.module.references().await?;
261 let mut imports = vec![];
262 let chunking_context = self.chunking_context;
263
264 for reference in references.iter() {
265 if let Some(import_ref) =
266 ResolvedVc::try_downcast_type::<ImportAssetReference>(*reference)
267 {
268 for &module in import_ref
269 .resolve_reference()
270 .resolve()
271 .await?
272 .primary_modules()
273 .await?
274 .iter()
275 {
276 if let Some(placeable) =
277 ResolvedVc::try_downcast::<Box<dyn CssChunkPlaceable>>(module)
278 {
279 let item = placeable.as_chunk_item(*self.module_graph, *chunking_context);
280 if let Some(css_item) =
281 Vc::try_resolve_downcast::<Box<dyn CssChunkItem>>(item).await?
282 {
283 imports.push(CssImport::Internal(
284 import_ref,
285 css_item.to_resolved().await?,
286 ));
287 }
288 }
289 }
290 } else if let Some(compose_ref) =
291 ResolvedVc::try_downcast_type::<CssModuleComposeReference>(*reference)
292 {
293 for &module in compose_ref
294 .resolve_reference()
295 .resolve()
296 .await?
297 .primary_modules()
298 .await?
299 .iter()
300 {
301 if let Some(placeable) =
302 ResolvedVc::try_downcast::<Box<dyn CssChunkPlaceable>>(module)
303 {
304 let item = placeable.as_chunk_item(*self.module_graph, *chunking_context);
305 if let Some(css_item) =
306 Vc::try_resolve_downcast::<Box<dyn CssChunkItem>>(item).await?
307 {
308 imports.push(CssImport::Composes(css_item.to_resolved().await?));
309 }
310 }
311 }
312 }
313 }
314
315 let mut code_gens = Vec::new();
316 for r in references.iter() {
317 if let Some(code_gen) = ResolvedVc::try_sidecast::<Box<dyn CodeGenerateable>>(*r) {
318 code_gens.push(code_gen.code_generation(*chunking_context));
319 }
320 }
321 let code_gens = code_gens.into_iter().try_join().await?;
323 let code_gens = code_gens.iter().map(|cg| &**cg).collect::<Vec<_>>();
324 for code_gen in code_gens {
326 for import in &code_gen.imports {
327 imports.push(import.clone());
328 }
329 }
330
331 let result = self
332 .module
333 .finalize_css(*chunking_context, *chunking_context.minify_type().await?)
334 .await?;
335
336 if let FinalCssResult::Ok {
337 output_code,
338 source_map,
339 } = &*result
340 {
341 Ok(CssChunkItemContent {
342 inner_code: output_code.to_owned().into(),
343 imports,
344 import_context: self.module.await?.import_context,
345 source_map: *source_map,
346 }
347 .cell())
348 } else {
349 Ok(CssChunkItemContent {
350 inner_code: format!(
351 "/* unparsable {} */",
352 self.module.ident().to_string().await?
353 )
354 .into(),
355 imports: vec![],
356 import_context: None,
357 source_map: FileContent::NotFound.resolved_cell(),
358 }
359 .cell())
360 }
361 }
362}