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