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