Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for CSS client references #5397

Merged
merged 8 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions crates/turbopack-core/src/chunk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub(crate) mod containment_tree;
pub(crate) mod data;
pub(crate) mod evaluate;
pub mod optimize;
pub(crate) mod passthrough_asset;

use std::{
collections::HashSet,
Expand Down Expand Up @@ -32,6 +33,7 @@ pub use self::{
chunking_context::{ChunkingContext, ChunkingContextVc},
data::{ChunkData, ChunkDataOption, ChunkDataOptionVc, ChunkDataVc, ChunksData, ChunksDataVc},
evaluate::{EvaluatableAsset, EvaluatableAssetVc, EvaluatableAssets, EvaluatableAssetsVc},
passthrough_asset::{PassthroughAsset, PassthroughAssetVc},
};
use crate::{
asset::{Asset, AssetVc, AssetsVc},
Expand Down Expand Up @@ -283,6 +285,9 @@ where

#[derive(Eq, PartialEq, Clone, Hash)]
enum ChunkContentGraphNode<I> {
// An asset not placed in the current chunk, but whose references we will
// follow to find more graph nodes.
PassthroughAsset { asset: AssetVc },
// Chunk items that are placed into the current chunk
ChunkItem { item: I, ident: StringReadRef },
// Asset that is already available and doesn't need to be included
Expand Down Expand Up @@ -342,6 +347,11 @@ where
}
}

if PassthroughAssetVc::resolve_from(asset).await?.is_some() {
graph_nodes.push((None, ChunkContentGraphNode::PassthroughAsset { asset }));
continue;
}

let chunkable_asset = match ChunkableAssetVc::resolve_from(asset).await? {
Some(chunkable_asset) => chunkable_asset,
_ => {
Expand Down Expand Up @@ -504,24 +514,20 @@ where
}

fn edges(&mut self, node: &ChunkContentGraphNode<I>) -> Self::EdgesFuture {
let chunk_item = if let ChunkContentGraphNode::ChunkItem {
item: chunk_item, ..
} = node
{
Some(chunk_item.clone())
} else {
None
};
let node = node.clone();

let context = self.context;

async move {
let Some(chunk_item) = chunk_item else {
return Ok(vec![].into_iter().flatten());
let references = match node {
ChunkContentGraphNode::PassthroughAsset { asset } => asset.references(),
ChunkContentGraphNode::ChunkItem { item, .. } => item.references(),
_ => {
return Ok(vec![].into_iter().flatten());
}
};

Ok(chunk_item
.references()
Ok(references
.await?
.into_iter()
.map(|reference| reference_to_graph_nodes::<I>(context, *reference))
Expand Down Expand Up @@ -599,7 +605,8 @@ where

for graph_node in graph_nodes {
match graph_node {
ChunkContentGraphNode::AvailableAsset(_asset) => {}
ChunkContentGraphNode::AvailableAsset(_)
| ChunkContentGraphNode::PassthroughAsset { .. } => {}
ChunkContentGraphNode::ChunkItem { item, .. } => {
chunk_items.push(item);
}
Expand Down
6 changes: 6 additions & 0 deletions crates/turbopack-core/src/chunk/passthrough_asset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use crate::asset::{Asset, AssetVc};

/// An [Asset] that should never be placed into a chunk, but whose references
/// should still be followed.
#[turbo_tasks::value_trait]
pub trait PassthroughAsset: Asset {}
1 change: 0 additions & 1 deletion crates/turbopack-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ pub mod ident;
pub mod introspect;
pub mod issue;
pub mod package_json;
pub mod plugin;
pub mod proxied_asset;
pub mod reference;
pub mod reference_type;
Expand Down
16 changes: 16 additions & 0 deletions crates/turbopack-core/src/reference_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ pub enum EcmaScriptModulesReferenceSubType {
pub enum CssReferenceSubType {
AtImport,
Compose,
/// Reference from any asset to a CSS-parseable asset.
///
/// This marks the boundary between non-CSS and CSS assets. The Next.js App
/// Router implementation uses this to inject client references in-between
/// Global/Module CSS assets and the underlying CSS assets.
Internal,
Custom(u8),
Undefined,
}
Expand Down Expand Up @@ -151,4 +157,14 @@ impl ReferenceType {
ReferenceType::Undefined => true,
}
}

/// Returns true if this reference type is internal. This will be used in
/// combination with [`ModuleRuleCondition::Internal`] to determine if a
/// rule should be applied to an internal asset/reference.
pub fn is_internal(&self) -> bool {
matches!(
self,
ReferenceType::Internal(_) | ReferenceType::Css(CssReferenceSubType::Internal)
)
}
}
61 changes: 27 additions & 34 deletions crates/turbopack-css/src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ use crate::{
CssChunkPlaceable, CssChunkPlaceableVc, CssChunkVc, CssImport,
},
code_gen::{CodeGenerateable, CodeGenerateableVc},
parse::{parse, ParseResult, ParseResultSourceMap, ParseResultVc},
parse::{
parse_css, ParseCss, ParseCssResult, ParseCssResultSourceMap, ParseCssResultVc, ParseCssVc,
},
path_visitor::ApplyVisitors,
references::{
analyze_css_stylesheet, compose::CssModuleComposeReferenceVc,
Expand All @@ -48,54 +50,45 @@ fn modifier() -> StringVc {
#[turbo_tasks::value]
#[derive(Clone)]
pub struct CssModuleAsset {
pub source: AssetVc,
pub context: AssetContextVc,
pub transforms: CssInputTransformsVc,
pub ty: CssModuleAssetType,
source: AssetVc,
context: AssetContextVc,
transforms: CssInputTransformsVc,
ty: CssModuleAssetType,
}

#[turbo_tasks::value_impl]
impl CssModuleAssetVc {
/// Creates a new CSS asset. The CSS is treated as global CSS.
#[turbo_tasks::function]
pub fn new(source: AssetVc, context: AssetContextVc, transforms: CssInputTransformsVc) -> Self {
Self::cell(CssModuleAsset {
source,
context,
transforms,
ty: CssModuleAssetType::Global,
})
}

/// Creates a new CSS asset. The CSS is treated as CSS module.
/// Creates a new CSS asset.
#[turbo_tasks::function]
pub fn new_module(
pub fn new(
source: AssetVc,
context: AssetContextVc,
transforms: CssInputTransformsVc,
ty: CssModuleAssetType,
) -> Self {
Self::cell(CssModuleAsset {
source,
context,
transforms,
ty: CssModuleAssetType::Module,
ty,
})
}

/// Returns the parsed css.
#[turbo_tasks::function]
pub(crate) async fn parse(self) -> Result<ParseResultVc> {
let this = self.await?;
Ok(parse(this.source, Value::new(this.ty), this.transforms))
}

/// Retrns the asset ident of the source without the "css" modifier
#[turbo_tasks::function]
pub async fn source_ident(self) -> Result<AssetIdentVc> {
Ok(self.await?.source.ident())
}
}

#[turbo_tasks::value_impl]
impl ParseCss for CssModuleAsset {
#[turbo_tasks::function]
fn parse_css(&self) -> ParseCssResultVc {
parse_css(self.source, self.ty, self.transforms)
}
}

#[turbo_tasks::value_impl]
impl Asset for CssModuleAsset {
#[turbo_tasks::function]
Expand All @@ -115,7 +108,7 @@ impl Asset for CssModuleAsset {
Ok(analyze_css_stylesheet(
this.source,
self_vc.as_resolve_origin(),
Value::new(this.ty),
this.ty,
this.transforms,
))
}
Expand All @@ -137,7 +130,7 @@ impl ChunkableAsset for CssModuleAsset {
impl CssChunkPlaceable for CssModuleAsset {
#[turbo_tasks::function]
fn as_chunk_item(self_vc: CssModuleAssetVc, context: ChunkingContextVc) -> CssChunkItemVc {
ModuleChunkItemVc::cell(ModuleChunkItem {
CssModuleChunkItemVc::cell(CssModuleChunkItem {
module: self_vc,
context,
})
Expand All @@ -159,13 +152,13 @@ impl ResolveOrigin for CssModuleAsset {
}

#[turbo_tasks::value]
struct ModuleChunkItem {
struct CssModuleChunkItem {
module: CssModuleAssetVc,
context: ChunkingContextVc,
}

#[turbo_tasks::value_impl]
impl ChunkItem for ModuleChunkItem {
impl ChunkItem for CssModuleChunkItem {
#[turbo_tasks::function]
fn asset_ident(&self) -> AssetIdentVc {
self.module.ident()
Expand All @@ -178,7 +171,7 @@ impl ChunkItem for ModuleChunkItem {
}

#[turbo_tasks::value_impl]
impl CssChunkItem for ModuleChunkItem {
impl CssChunkItem for CssModuleChunkItem {
#[turbo_tasks::function]
async fn content(&self) -> Result<CssChunkItemContentVc> {
let references = &*self.module.references().await?;
Expand Down Expand Up @@ -236,9 +229,9 @@ impl CssChunkItem for ModuleChunkItem {
}
}

let parsed = self.module.parse().await?;
let parsed = self.module.parse_css().await?;

if let ParseResult::Ok {
if let ParseCssResult::Ok {
stylesheet,
source_map,
..
Expand Down Expand Up @@ -280,7 +273,7 @@ impl CssChunkItem for ModuleChunkItem {

code_gen.emit(&stylesheet)?;

let srcmap = ParseResultSourceMap::new(source_map.clone(), srcmap).cell();
let srcmap = ParseCssResultSourceMap::new(source_map.clone(), srcmap).cell();

Ok(CssChunkItemContent {
inner_code: code_string.into(),
Expand Down
5 changes: 3 additions & 2 deletions crates/turbopack-css/src/chunk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use self::{
};
use crate::{
embed::{CssEmbed, CssEmbeddable, CssEmbeddableVc},
parse::ParseResultSourceMapVc,
parse::ParseCssResultSourceMapVc,
util::stringify_js,
ImportAssetReferenceVc,
};
Expand Down Expand Up @@ -152,6 +152,7 @@ impl CssChunkContentVc {
let entry_placeable = CssChunkPlaceableVc::cast_from(entry);
let entry_item = entry_placeable.as_chunk_item(this.context);

// TODO(WEB-1261)
for external_import in expand_imports(&mut body, entry_item).await? {
external_imports.insert(external_import.await?.to_owned());
}
Expand Down Expand Up @@ -485,7 +486,7 @@ pub enum CssImport {
pub struct CssChunkItemContent {
pub inner_code: Rope,
pub imports: Vec<CssImport>,
pub source_map: Option<ParseResultSourceMapVc>,
pub source_map: Option<ParseCssResultSourceMapVc>,
}

#[turbo_tasks::value_trait]
Expand Down
1 change: 1 addition & 0 deletions crates/turbopack-css/src/chunk/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use turbopack_core::{chunk::ChunkItem, code_builder::CodeBuilder};
use super::{CssChunkItemVc, CssImport};
use crate::chunk::CssChunkItem;

// TODO(WEB-1261)
pub async fn expand_imports(
code: &mut CodeBuilder,
chunk_item: CssChunkItemVc,
Expand Down
73 changes: 73 additions & 0 deletions crates/turbopack-css/src/global_asset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use anyhow::{bail, Result};
use turbo_tasks::{primitives::StringVc, Value};
use turbopack_core::{
asset::{Asset, AssetContentVc, AssetVc},
chunk::{PassthroughAsset, PassthroughAssetVc},
context::{AssetContext, AssetContextVc},
ident::AssetIdentVc,
reference::AssetReferencesVc,
reference_type::{CssReferenceSubType, ReferenceType},
};

use crate::references::internal::InternalCssAssetReferenceVc;

#[turbo_tasks::value]
#[derive(Clone)]
pub struct GlobalCssAsset {
source: AssetVc,
context: AssetContextVc,
}

#[turbo_tasks::value_impl]
impl GlobalCssAssetVc {
/// Creates a new CSS asset. The CSS is treated as global CSS.
#[turbo_tasks::function]
pub fn new(source: AssetVc, context: AssetContextVc) -> Self {
Self::cell(GlobalCssAsset { source, context })
}
}

#[turbo_tasks::value_impl]
impl GlobalCssAssetVc {
#[turbo_tasks::function]
async fn inner(self) -> Result<AssetVc> {
let this = self.await?;
// The underlying CSS is processed through an internal CSS reference.
// This can then be picked up by other rules to treat CSS assets in
// a special way. For instance, in the Next App Router implementation,
// RSC CSS assets will be added to the client references manifest.
Ok(this.context.process(
this.source,
Value::new(ReferenceType::Css(CssReferenceSubType::Internal)),
))
}
}

#[turbo_tasks::value_impl]
impl Asset for GlobalCssAsset {
#[turbo_tasks::function]
fn ident(&self) -> AssetIdentVc {
self.source.ident().with_modifier(modifier())
}

#[turbo_tasks::function]
fn content(&self) -> Result<AssetContentVc> {
bail!("CSS global asset has no contents")
}

#[turbo_tasks::function]
fn references(self_vc: GlobalCssAssetVc) -> AssetReferencesVc {
AssetReferencesVc::cell(vec![
InternalCssAssetReferenceVc::new(self_vc.inner()).into()
])
}
}

#[turbo_tasks::function]
fn modifier() -> StringVc {
StringVc::cell("global css".to_string())
}

/// A GlobalAsset is a transparent wrapper around an actual CSS asset.
#[turbo_tasks::value_impl]
impl PassthroughAsset for GlobalCssAsset {}
Loading