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