Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Selectively load runtime CSS modules to avoid rule duplication (#4695)
### Description Implementing our style deduplication RFC. Next.js PR: vercel/next.js#48866 ### Testing Instructions Working on a test case now. link WEB-951 --------- Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com>
- Loading branch information
Showing
59 changed files
with
2,725 additions
and
581 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
163 changes: 163 additions & 0 deletions
163
crates/turbopack-css/src/chunk/single_item_chunk/chunk.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
use std::fmt::Write; | ||
|
||
use anyhow::Result; | ||
use turbo_tasks::{primitives::StringVc, ValueToString}; | ||
use turbo_tasks_fs::File; | ||
use turbopack_core::{ | ||
asset::{Asset, AssetContentVc, AssetVc}, | ||
chunk::{Chunk, ChunkItem, ChunkVc, ChunkingContext, ChunkingContextVc}, | ||
code_builder::{CodeBuilder, CodeVc}, | ||
ident::AssetIdentVc, | ||
introspect::{Introspectable, IntrospectableVc}, | ||
reference::AssetReferencesVc, | ||
source_map::{GenerateSourceMap, GenerateSourceMapVc, OptionSourceMapVc}, | ||
}; | ||
|
||
use super::source_map::SingleItemCssChunkSourceMapAssetReferenceVc; | ||
use crate::chunk::{CssChunkItem, CssChunkItemVc}; | ||
|
||
/// A CSS chunk that only contains a single item. This is used for selectively | ||
/// loading CSS modules that are part of a larger chunk in development mode, and | ||
/// avoiding rule duplication. | ||
#[turbo_tasks::value] | ||
pub struct SingleItemCssChunk { | ||
context: ChunkingContextVc, | ||
item: CssChunkItemVc, | ||
} | ||
|
||
#[turbo_tasks::value_impl] | ||
impl SingleItemCssChunkVc { | ||
/// Creates a new [`SingleItemCssChunkVc`]. | ||
#[turbo_tasks::function] | ||
pub fn new(context: ChunkingContextVc, item: CssChunkItemVc) -> Self { | ||
SingleItemCssChunk { context, item }.cell() | ||
} | ||
} | ||
|
||
#[turbo_tasks::value_impl] | ||
impl SingleItemCssChunkVc { | ||
#[turbo_tasks::function] | ||
async fn code(self) -> Result<CodeVc> { | ||
use std::io::Write; | ||
|
||
let this = self.await?; | ||
let mut code = CodeBuilder::default(); | ||
|
||
let id = &*this.item.id().await?; | ||
|
||
writeln!(code, "/* {} */", id)?; | ||
let content = this.item.content().await?; | ||
code.push_source( | ||
&content.inner_code, | ||
content.source_map.map(|sm| sm.as_generate_source_map()), | ||
); | ||
|
||
if *this | ||
.context | ||
.reference_chunk_source_maps(self.into()) | ||
.await? | ||
&& code.has_source_map() | ||
{ | ||
let chunk_path = self.path().await?; | ||
write!( | ||
code, | ||
"\n/*# sourceMappingURL={}.map*/", | ||
chunk_path.file_name() | ||
)?; | ||
} | ||
|
||
let c = code.build().cell(); | ||
Ok(c) | ||
} | ||
} | ||
|
||
#[turbo_tasks::value_impl] | ||
impl Chunk for SingleItemCssChunk { | ||
#[turbo_tasks::function] | ||
fn chunking_context(&self) -> ChunkingContextVc { | ||
self.context | ||
} | ||
} | ||
|
||
#[turbo_tasks::function] | ||
fn single_item_modifier() -> StringVc { | ||
StringVc::cell("single item css chunk".to_string()) | ||
} | ||
|
||
#[turbo_tasks::value_impl] | ||
impl Asset for SingleItemCssChunk { | ||
#[turbo_tasks::function] | ||
async fn ident(&self) -> Result<AssetIdentVc> { | ||
Ok(AssetIdentVc::from_path( | ||
self.context.chunk_path( | ||
self.item | ||
.asset_ident() | ||
.with_modifier(single_item_modifier()), | ||
".css", | ||
), | ||
)) | ||
} | ||
|
||
#[turbo_tasks::function] | ||
async fn content(self_vc: SingleItemCssChunkVc) -> Result<AssetContentVc> { | ||
let code = self_vc.code().await?; | ||
Ok(File::from(code.source_code().clone()).into()) | ||
} | ||
|
||
#[turbo_tasks::function] | ||
async fn references(self_vc: SingleItemCssChunkVc) -> Result<AssetReferencesVc> { | ||
let this = self_vc.await?; | ||
let mut references = Vec::new(); | ||
if *this | ||
.context | ||
.reference_chunk_source_maps(self_vc.into()) | ||
.await? | ||
{ | ||
references.push(SingleItemCssChunkSourceMapAssetReferenceVc::new(self_vc).into()); | ||
} | ||
Ok(AssetReferencesVc::cell(references)) | ||
} | ||
} | ||
|
||
#[turbo_tasks::value_impl] | ||
impl GenerateSourceMap for SingleItemCssChunk { | ||
#[turbo_tasks::function] | ||
fn generate_source_map(self_vc: SingleItemCssChunkVc) -> OptionSourceMapVc { | ||
self_vc.code().generate_source_map() | ||
} | ||
} | ||
|
||
#[turbo_tasks::function] | ||
fn introspectable_type() -> StringVc { | ||
StringVc::cell("single asset css chunk".to_string()) | ||
} | ||
|
||
#[turbo_tasks::function] | ||
fn entry_module_key() -> StringVc { | ||
StringVc::cell("entry module".to_string()) | ||
} | ||
|
||
#[turbo_tasks::value_impl] | ||
impl Introspectable for SingleItemCssChunk { | ||
#[turbo_tasks::function] | ||
fn ty(&self) -> StringVc { | ||
introspectable_type() | ||
} | ||
|
||
#[turbo_tasks::function] | ||
fn title(self_vc: SingleItemCssChunkVc) -> StringVc { | ||
self_vc.path().to_string() | ||
} | ||
|
||
#[turbo_tasks::function] | ||
async fn details(self_vc: SingleItemCssChunkVc) -> Result<StringVc> { | ||
let this = self_vc.await?; | ||
let mut details = String::new(); | ||
write!( | ||
details, | ||
"Chunk item: {}", | ||
this.item.asset_ident().to_string().await? | ||
)?; | ||
Ok(StringVc::cell(details)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pub(crate) mod chunk; | ||
pub(crate) mod reference; | ||
pub(crate) mod source_map; |
57 changes: 57 additions & 0 deletions
57
crates/turbopack-css/src/chunk/single_item_chunk/reference.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
use anyhow::Result; | ||
use turbo_tasks::{primitives::StringVc, ValueToString, ValueToStringVc}; | ||
use turbopack_core::{ | ||
chunk::{ | ||
ChunkItem, ChunkableAssetReference, ChunkableAssetReferenceVc, ChunkingContextVc, | ||
ChunkingType, ChunkingTypeOptionVc, | ||
}, | ||
reference::{AssetReference, AssetReferenceVc}, | ||
resolve::{ResolveResult, ResolveResultVc}, | ||
}; | ||
|
||
use super::chunk::SingleItemCssChunkVc; | ||
use crate::chunk::CssChunkItemVc; | ||
|
||
/// A reference to a [`SingleItemCssChunkVc`]. | ||
#[turbo_tasks::value] | ||
#[derive(Hash, Debug)] | ||
pub struct SingleItemCssChunkReference { | ||
context: ChunkingContextVc, | ||
item: CssChunkItemVc, | ||
} | ||
|
||
#[turbo_tasks::value_impl] | ||
impl SingleItemCssChunkReferenceVc { | ||
/// Creates a new [`SingleItemCssChunkReferenceVc`]. | ||
#[turbo_tasks::function] | ||
pub fn new(context: ChunkingContextVc, item: CssChunkItemVc) -> Self { | ||
Self::cell(SingleItemCssChunkReference { context, item }) | ||
} | ||
} | ||
|
||
#[turbo_tasks::value_impl] | ||
impl AssetReference for SingleItemCssChunkReference { | ||
#[turbo_tasks::function] | ||
fn resolve_reference(&self) -> ResolveResultVc { | ||
ResolveResult::asset(SingleItemCssChunkVc::new(self.context, self.item).into()).cell() | ||
} | ||
} | ||
|
||
#[turbo_tasks::value_impl] | ||
impl ValueToString for SingleItemCssChunkReference { | ||
#[turbo_tasks::function] | ||
async fn to_string(&self) -> Result<StringVc> { | ||
Ok(StringVc::cell(format!( | ||
"css single item chunk {}", | ||
self.item.asset_ident().to_string().await? | ||
))) | ||
} | ||
} | ||
|
||
#[turbo_tasks::value_impl] | ||
impl ChunkableAssetReference for SingleItemCssChunkReference { | ||
#[turbo_tasks::function] | ||
fn chunking_type(&self) -> Result<ChunkingTypeOptionVc> { | ||
Ok(ChunkingTypeOptionVc::cell(Some(ChunkingType::Separate))) | ||
} | ||
} |
Oops, something went wrong.