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 .owned()
141 .await?
142 .with_modifier(rcstr!("css"))
143 .with_layer(self.asset_context.into_trait_ref().await?.layer());
144 if let Some(import_context) = self.import_context {
145 ident = ident.with_modifier(import_context.modifier().owned().await?)
146 }
147 Ok(ident.into_vc())
148 }
149
150 #[turbo_tasks::function]
151 fn source(&self) -> Vc<OptionSource> {
152 Vc::cell(Some(self.source))
153 }
154
155 #[turbo_tasks::function]
156 async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
157 let result = self.parse_css().await?;
158 match &*result {
161 ParseCssResult::Ok { references, .. } => Ok(**references),
162 ParseCssResult::Unparsable => Ok(ModuleReferences::empty()),
163 ParseCssResult::NotFound => Ok(ModuleReferences::empty()),
164 }
165 }
166 #[turbo_tasks::function]
167 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
168 ModuleSideEffects::SideEffectful.cell()
170 }
171}
172
173#[turbo_tasks::value_impl]
174impl StyleModule for CssModule {
175 #[turbo_tasks::function]
176 fn style_type(&self) -> Vc<StyleType> {
177 match self.ty {
178 CssModuleType::Default => StyleType::GlobalStyle.cell(),
179 CssModuleType::Module => StyleType::IsolatedStyle.cell(),
180 }
181 }
182}
183
184#[turbo_tasks::value_impl]
185impl ChunkableModule for CssModule {
186 #[turbo_tasks::function]
187 fn as_chunk_item(
188 self: ResolvedVc<Self>,
189 module_graph: ResolvedVc<ModuleGraph>,
190 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
191 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
192 Vc::upcast(CssModuleChunkItem::cell(CssModuleChunkItem {
193 module: self,
194 module_graph,
195 chunking_context,
196 }))
197 }
198}
199
200#[turbo_tasks::value_impl]
201impl CssChunkPlaceable for CssModule {}
202
203#[turbo_tasks::value_impl]
204impl ResolveOrigin for CssModule {
205 #[turbo_tasks::function]
206 async fn origin_path(&self) -> Result<Vc<FileSystemPath>> {
207 Ok(self.source.ident().await?.path.clone().cell())
208 }
209
210 #[turbo_tasks::function]
211 fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
212 *self.asset_context
213 }
214}
215
216#[turbo_tasks::value]
217struct CssModuleChunkItem {
218 module: ResolvedVc<CssModule>,
219 module_graph: ResolvedVc<ModuleGraph>,
220 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
221}
222
223#[turbo_tasks::value_impl]
224impl OutputAssetsReference for CssModuleChunkItem {
225 #[turbo_tasks::function]
226 async fn references(&self) -> Result<Vc<OutputAssetsWithReferenced>> {
227 let mut references = Vec::new();
228 if let ParseCssResult::Ok { url_references, .. } = &*self.module.parse_css().await? {
229 for (_, reference) in &*url_references.await? {
230 if let ReferencedAsset::Some(asset) = *reference
231 .get_referenced_asset(*self.chunking_context)
232 .await?
233 {
234 references.push(asset);
235 }
236 }
237 }
238 Ok(OutputAssetsWithReferenced::from_assets(Vc::cell(
239 references,
240 )))
241 }
242}
243
244#[turbo_tasks::value_impl]
245impl ChunkItem for CssModuleChunkItem {
246 #[turbo_tasks::function]
247 fn asset_ident(&self) -> Vc<AssetIdent> {
248 self.module.ident()
249 }
250
251 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
252 *self.chunking_context
253 }
254
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 .await?
280 .primary_modules()
281 .await?
282 .iter()
283 {
284 if let Some(placeable) =
285 ResolvedVc::try_downcast::<Box<dyn CssChunkPlaceable>>(module)
286 {
287 let item = placeable.as_chunk_item(*self.module_graph, *chunking_context);
288 if let Some(css_item) = ResolvedVc::try_downcast::<Box<dyn CssChunkItem>>(
289 item.to_resolved().await?,
290 ) {
291 imports.push(CssImport::Internal(import_ref, css_item));
292 }
293 }
294 }
295 } else if let Some(compose_ref) =
296 ResolvedVc::try_downcast_type::<CssModuleComposeReference>(*reference)
297 {
298 for &module in compose_ref
299 .resolve_reference()
300 .await?
301 .primary_modules()
302 .await?
303 .iter()
304 {
305 if let Some(placeable) =
306 ResolvedVc::try_downcast::<Box<dyn CssChunkPlaceable>>(module)
307 {
308 let item = placeable.as_chunk_item(*self.module_graph, *chunking_context);
309 if let Some(css_item) = ResolvedVc::try_downcast::<Box<dyn CssChunkItem>>(
310 item.to_resolved().await?,
311 ) {
312 imports.push(CssImport::Composes(css_item));
313 }
314 }
315 }
316 }
317 }
318
319 let mut code_gens = Vec::new();
320 for r in references.iter() {
321 if let Some(code_gen) = ResolvedVc::try_sidecast::<Box<dyn CodeGenerateable>>(*r) {
322 code_gens.push(code_gen.code_generation(*chunking_context));
323 }
324 }
325 let code_gens = code_gens.into_iter().try_join().await?;
327 let code_gens = code_gens.iter().map(|cg| &**cg).collect::<Vec<_>>();
328 for code_gen in code_gens {
330 for import in &code_gen.imports {
331 imports.push(import.clone());
332 }
333 }
334
335 let result = self
336 .module
337 .finalize_css(*chunking_context, *chunking_context.minify_type().await?)
338 .await?;
339
340 if let FinalCssResult::Ok {
341 output_code,
342 source_map,
343 } = &*result
344 {
345 Ok(CssChunkItemContent {
346 inner_code: output_code.to_owned().into(),
347 imports,
348 import_context: self.module.await?.import_context,
349 source_map: *source_map,
350 }
351 .cell())
352 } else {
353 Ok(CssChunkItemContent {
354 inner_code: turbofmt!("/* unparsable {} */", self.module.ident())
355 .await?
356 .to_string()
357 .into(),
358 imports: vec![],
359 import_context: None,
360 source_map: FileContent::NotFound.resolved_cell(),
361 }
362 .cell())
363 }
364 }
365}