Skip to content

Commit

Permalink
feat(plugin-css): should handle css layer supports media (#9221)
Browse files Browse the repository at this point in the history
  • Loading branch information
JSerFeng authored Feb 11, 2025
1 parent 2adb783 commit decbbd7
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 25 deletions.
53 changes: 52 additions & 1 deletion crates/rspack_plugin_css/src/dependency/import.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fmt::Display;

use rspack_cacheable::{cacheable, cacheable_dyn};
use rspack_core::{
AsContextDependency, Compilation, Dependency, DependencyCategory, DependencyId, DependencyRange,
Expand All @@ -11,16 +13,47 @@ pub struct CssImportDependency {
id: DependencyId,
request: String,
range: DependencyRange,
media: Option<String>,
supports: Option<String>,
layer: Option<CssLayer>,
}

#[cacheable]
#[derive(Debug, Clone)]
pub enum CssLayer {
Anonymous,
Named(String),
}

impl CssImportDependency {
pub fn new(request: String, range: DependencyRange) -> Self {
pub fn new(
request: String,
range: DependencyRange,
media: Option<String>,
supports: Option<String>,
layer: Option<CssLayer>,
) -> Self {
Self {
id: DependencyId::new(),
request,
range,
media,
supports,
layer,
}
}

pub fn media(&self) -> Option<&str> {
self.media.as_deref()
}

pub fn supports(&self) -> Option<&str> {
self.supports.as_deref()
}

pub fn layer(&self) -> Option<&CssLayer> {
self.layer.as_ref()
}
}

#[cacheable_dyn]
Expand Down Expand Up @@ -61,6 +94,24 @@ impl ModuleDependency for CssImportDependency {
}
}

#[derive(Clone)]
pub struct CssMedia(pub String);

#[derive(Clone)]
pub struct CssSupports(pub String);

impl Display for CssMedia {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}

impl Display for CssSupports {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}

#[cacheable_dyn]
impl DependencyTemplate for CssImportDependency {
fn apply(
Expand Down
76 changes: 59 additions & 17 deletions crates/rspack_plugin_css/src/parser_and_generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use rspack_core::{
rspack_sources::{BoxSource, ConcatSource, RawStringSource, ReplaceSource, Source, SourceExt},
BuildMetaDefaultObject, BuildMetaExportsType, ChunkGraph, Compilation, ConstDependency,
CssExportsConvention, Dependency, DependencyId, DependencyRange, DependencyTemplate,
GenerateContext, LocalIdentName, Module, ModuleDependency, ModuleGraph, ModuleIdentifier,
ModuleType, NormalModule, ParseContext, ParseResult, ParserAndGenerator, RuntimeSpec, SourceType,
TemplateContext, UsageState,
DependencyType, GenerateContext, LocalIdentName, Module, ModuleDependency, ModuleGraph,
ModuleIdentifier, ModuleType, NormalModule, ParseContext, ParseResult, ParserAndGenerator,
RuntimeSpec, SourceType, TemplateContext, UsageState,
};
use rspack_core::{ModuleInitFragments, RuntimeGlobals};
use rspack_error::{
Expand All @@ -26,14 +26,6 @@ use rspack_error::{
use rspack_util::ext::DynHash;
use rustc_hash::{FxHashMap, FxHashSet};

use crate::{
dependency::CssSelfReferenceLocalIdentDependency,
utils::{css_modules_exports_to_string, LocalIdentOptions},
};
use crate::{
dependency::CssSelfReferenceLocalIdentReplacement,
utils::{export_locals_convention, unescape},
};
use crate::{
dependency::{
CssComposeDependency, CssExportDependency, CssImportDependency, CssLocalIdentDependency,
Expand All @@ -44,6 +36,14 @@ use crate::{
replace_module_request_prefix,
},
};
use crate::{
dependency::{CssLayer, CssSelfReferenceLocalIdentReplacement, CssSupports},
utils::{export_locals_convention, unescape},
};
use crate::{
dependency::{CssMedia, CssSelfReferenceLocalIdentDependency},
utils::{css_modules_exports_to_string, LocalIdentOptions},
};

static REGEX_IS_MODULES: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"\.module(s)?\.[^.]+$").expect("Invalid regex"));
Expand Down Expand Up @@ -184,7 +184,13 @@ impl ParserAndGenerator for CssParserAndGenerator {
dependencies.push(dep.clone());
code_generation_dependencies.push(dep);
}
css_module_lexer::Dependency::Import { request, range, .. } => {
css_module_lexer::Dependency::Import {
request,
range,
media,
supports,
layer,
} => {
if request.is_empty() {
presentational_dependencies.push(Box::new(ConstDependency::new(
range.start,
Expand All @@ -204,6 +210,15 @@ impl ParserAndGenerator for CssParserAndGenerator {
dependencies.push(Box::new(CssImportDependency::new(
request.to_string(),
DependencyRange::new(range.start, range.end),
media.map(|s| s.to_string()),
supports.map(|s| s.to_string()),
layer.map(|s| {
if s.is_empty() {
CssLayer::Anonymous
} else {
CssLayer::Named(s.to_string())
}
}),
)));
}
css_module_lexer::Dependency::Replace { content, range } => presentational_dependencies
Expand Down Expand Up @@ -476,17 +491,44 @@ impl ParserAndGenerator for CssParserAndGenerator {
data: generate_context.data,
};

let module_graph = compilation.get_module_graph();
module.get_dependencies().iter().for_each(|id| {
if let Some(dependency) = compilation
.get_module_graph()
let dep = module_graph
.dependency_by_id(id)
.expect("should have dependency")
.as_dependency_template()
{
.expect("should have dependency");
if let Some(dependency) = dep.as_dependency_template() {
dependency.apply(&mut source, &mut context)
}
});

for conn in module_graph.get_incoming_connections(&module.identifier()) {
let Some(dep) = module_graph.dependency_by_id(&conn.dependency_id) else {
continue;
};

if matches!(dep.dependency_type(), DependencyType::CssImport) {
let Some(css_import_dep) = dep.downcast_ref::<CssImportDependency>() else {
panic!(
"dependency with type DependencyType::CssImport should only be CssImportDependency"
);
};

if let Some(media) = css_import_dep.media() {
let media = CssMedia(media.to_string());
context.data.insert(media);
}

if let Some(supports) = css_import_dep.supports() {
let supports = CssSupports(supports.to_string());
context.data.insert(supports);
}

if let Some(layer) = css_import_dep.layer() {
context.data.insert(layer.clone());
}
}
}

if let Some(dependencies) = module.get_presentational_dependencies() {
dependencies
.iter()
Expand Down
49 changes: 42 additions & 7 deletions crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#![allow(clippy::comparison_chain)]

use std::borrow::Cow;
use std::hash::Hash;
use std::sync::Arc;

use async_trait::async_trait;
use rayon::prelude::*;
use rspack_collections::DatabaseItem;
use rspack_core::rspack_sources::{BoxSource, CachedSource, ReplaceSource};
use rspack_core::rspack_sources::{BoxSource, CachedSource, RawSource, ReplaceSource};
use rspack_core::{
get_css_chunk_filename_template,
rspack_sources::{ConcatSource, RawStringSource, Source, SourceExt},
Expand All @@ -25,6 +26,7 @@ use rspack_hook::plugin_hook;
use rspack_plugin_runtime::is_enabled_for_chunk;
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};

use crate::dependency::{CssLayer, CssMedia, CssSupports};
use crate::parser_and_generator::{CodeGenerationDataUnusedLocalIdent, CssParserAndGenerator};
use crate::runtime::CssLoadingRuntimeModule;
use crate::utils::AUTO_PUBLIC_PATH_PLACEHOLDER;
Expand Down Expand Up @@ -131,11 +133,13 @@ impl CssPlugin {
.code_generation_results
.get(module_id, Some(chunk.runtime()));

Ok(
code_gen_result
.get(&SourceType::Css)
.map(|source| (CssModuleDebugInfo { module: *module }, source)),
)
Ok(code_gen_result.get(&SourceType::Css).map(|source| {
(
CssModuleDebugInfo { module: *module },
&code_gen_result.data,
source,
)
}))
})
.collect::<Result<Vec<_>>>()?;

Expand All @@ -147,11 +151,42 @@ impl CssPlugin {
.flatten()
.fold(
ConcatSource::default,
|mut acc, (debug_info, cur_source)| {
|mut acc, (debug_info, data, cur_source)| {
let (start, end) = Self::render_module_debug_info(compilation, &debug_info);
acc.add(start);

let mut num_close_bracket = 0;

// TODO: use PrefixSource to create indent
if let Some(media) = data.get::<CssMedia>() {
num_close_bracket += 1;
acc.add(RawSource::from(format!("@media {}{{\n", media)));
}

if let Some(supports) = data.get::<CssSupports>() {
num_close_bracket += 1;
acc.add(RawSource::from(format!("@supports ({}) {{\n", supports)));
}

if let Some(layer) = data.get::<CssLayer>() {
num_close_bracket += 1;
acc.add(RawSource::from(format!(
"@layer{} {{\n",
if let CssLayer::Named(layer) = &layer {
Cow::Owned(format!(" {}", layer))
} else {
Cow::Borrowed("")
}
)));
}

acc.add(cur_source.clone());

for _ in 0..num_close_bracket {
acc.add(RawStringSource::from_static("\n}"));
}
acc.add(RawStringSource::from_static("\n"));

acc.add(end);
acc
},
Expand Down
27 changes: 27 additions & 0 deletions packages/rspack-test-tools/tests/__snapshots__/Config.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,33 @@ exports[`config config/chunk-index/available-modules-order-index exported tests
}
`;
exports[`config config/css/at-import exported tests at-import-in-the-top 1`] = `
@import url("https://fonts.googleapis.com/css2?family=Inter");
@media (prefers-color-scheme: dark){
.b {
color: red;
}
}
@supports (display: grid) {
@layer {
.c {
color: pink;
}
}
}
.a {
color: blue;
}
`;
exports[`config config/css/at-import-in-the-top exported tests at-import-in-the-top 1`] = `
@import url("https://fonts.googleapis.com/css2?family=Roboto");
@import url("https://fonts.googleapis.com/css2?family=Inter");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,33 @@ exports[`new-code-splitting config cases new-code-splitting config cases/chunk-i
}
`;
exports[`new-code-splitting config cases new-code-splitting config cases/css/at-import exported tests at-import-in-the-top 1`] = `
@import url("https://fonts.googleapis.com/css2?family=Inter");
@media (prefers-color-scheme: dark){
.b {
color: red;
}
}
@supports (display: grid) {
@layer {
.c {
color: pink;
}
}
}
.a {
color: blue;
}
`;
exports[`new-code-splitting config cases new-code-splitting config cases/css/at-import-in-the-top exported tests at-import-in-the-top 1`] = `
@import url("https://fonts.googleapis.com/css2?family=Roboto");
@import url("https://fonts.googleapis.com/css2?family=Inter");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@import url("./b.css") (prefers-color-scheme: dark);

@import url("./c.css") layer supports(display: grid);

.a {
color: blue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import url("https://fonts.googleapis.com/css2?family=Inter");

.b {
color: red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.c {
color: pink;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require("./a.css");
const fs = __non_webpack_require__("fs");
const path = __non_webpack_require__("path");

it("at-import-in-the-top", async () => {
const css = await fs.promises.readFile(
path.resolve(__dirname, "bundle0.css"),
"utf-8"
);

expect(css).toMatchSnapshot();
});
Loading

2 comments on commit decbbd7

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented on decbbd7 Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Benchmark detail: Open

Name Base (2025-02-11 9d82335) Current Change
10000_big_production-mode_disable-minimize + exec 37.7 s ± 637 ms 39.9 s ± 968 ms +5.70 %
10000_development-mode + exec 1.88 s ± 89 ms 1.88 s ± 159 ms +0.23 %
10000_development-mode_hmr + exec 685 ms ± 7.4 ms 695 ms ± 29 ms +1.46 %
10000_production-mode + exec 2.28 s ± 51 ms 2.36 s ± 187 ms +3.49 %
10000_production-mode_persistent-cold + exec 2.47 s ± 72 ms 2.47 s ± 53 ms +0.15 %
10000_production-mode_persistent-hot + exec 1.65 s ± 61 ms 1.66 s ± 69 ms +0.68 %
arco-pro_development-mode + exec 1.78 s ± 145 ms 1.76 s ± 119 ms -1.09 %
arco-pro_development-mode_hmr + exec 389 ms ± 2.1 ms 388 ms ± 3.1 ms -0.25 %
arco-pro_production-mode + exec 3.59 s ± 217 ms 3.65 s ± 99 ms +1.73 %
arco-pro_production-mode_generate-package-json-webpack-plugin + exec 3.72 s ± 229 ms 3.69 s ± 140 ms -0.69 %
arco-pro_production-mode_persistent-cold + exec 3.85 s ± 164 ms 3.79 s ± 87 ms -1.56 %
arco-pro_production-mode_persistent-hot + exec 2.36 s ± 99 ms 2.39 s ± 115 ms +1.53 %
arco-pro_production-mode_traverse-chunk-modules + exec 3.63 s ± 69 ms 3.62 s ± 138 ms -0.26 %
large-dyn-imports_development-mode + exec 2.09 s ± 24 ms 2.14 s ± 103 ms +2.37 %
large-dyn-imports_production-mode + exec 2.15 s ± 71 ms 2.17 s ± 33 ms +0.82 %
threejs_development-mode_10x + exec 1.54 s ± 25 ms 1.54 s ± 18 ms +0.29 %
threejs_development-mode_10x_hmr + exec 779 ms ± 7 ms 793 ms ± 34 ms +1.81 %
threejs_production-mode_10x + exec 5.2 s ± 32 ms 5.26 s ± 137 ms +1.07 %
threejs_production-mode_10x_persistent-cold + exec 5.27 s ± 65 ms 5.37 s ± 270 ms +1.85 %
threejs_production-mode_10x_persistent-hot + exec 4.53 s ± 220 ms 4.54 s ± 264 ms +0.16 %
10000_big_production-mode_disable-minimize + rss memory 8706 MiB ± 32.4 MiB 8705 MiB ± 57.9 MiB -0.01 %
10000_development-mode + rss memory 649 MiB ± 12.6 MiB 647 MiB ± 16 MiB -0.25 %
10000_development-mode_hmr + rss memory 1344 MiB ± 112 MiB 1273 MiB ± 157 MiB -5.32 %
10000_production-mode + rss memory 621 MiB ± 21.8 MiB 632 MiB ± 23.4 MiB +1.76 %
10000_production-mode_persistent-cold + rss memory 746 MiB ± 24.7 MiB 732 MiB ± 30.2 MiB -1.89 %
10000_production-mode_persistent-hot + rss memory 717 MiB ± 23.3 MiB 727 MiB ± 31.3 MiB +1.38 %
arco-pro_development-mode + rss memory 569 MiB ± 17.5 MiB 551 MiB ± 27.6 MiB -3.25 %
arco-pro_development-mode_hmr + rss memory 643 MiB ± 49.1 MiB 634 MiB ± 64.4 MiB -1.41 %
arco-pro_production-mode + rss memory 714 MiB ± 28.8 MiB 704 MiB ± 33.5 MiB -1.36 %
arco-pro_production-mode_generate-package-json-webpack-plugin + rss memory 732 MiB ± 22.4 MiB 718 MiB ± 20.3 MiB -2.00 %
arco-pro_production-mode_persistent-cold + rss memory 853 MiB ± 45.5 MiB 827 MiB ± 15.9 MiB -3.02 %
arco-pro_production-mode_persistent-hot + rss memory 708 MiB ± 27.8 MiB 700 MiB ± 37.8 MiB -1.16 %
arco-pro_production-mode_traverse-chunk-modules + rss memory 727 MiB ± 48.2 MiB 712 MiB ± 15.3 MiB -2.04 %
large-dyn-imports_development-mode + rss memory 644 MiB ± 5.26 MiB 639 MiB ± 4.55 MiB -0.79 %
large-dyn-imports_production-mode + rss memory 526 MiB ± 7.84 MiB 520 MiB ± 8.08 MiB -1.25 %
threejs_development-mode_10x + rss memory 553 MiB ± 16.3 MiB 544 MiB ± 26.3 MiB -1.66 %
threejs_development-mode_10x_hmr + rss memory 1145 MiB ± 140 MiB 1131 MiB ± 125 MiB -1.21 %
threejs_production-mode_10x + rss memory 828 MiB ± 50.8 MiB 818 MiB ± 32 MiB -1.20 %
threejs_production-mode_10x_persistent-cold + rss memory 960 MiB ± 14.5 MiB 932 MiB ± 54.7 MiB -2.87 %
threejs_production-mode_10x_persistent-hot + rss memory 877 MiB ± 54.8 MiB 859 MiB ± 50 MiB -2.13 %

Threshold exceeded: ["10000_big_production-mode_disable-minimize + exec"]

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented on decbbd7 Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Ecosystem CI detail: Open

suite result
modernjs ❌ failure
rspress ✅ success
rslib ✅ success
rsbuild ❌ failure
rsdoctor ❌ failure
examples ✅ success
devserver ✅ success
nuxt ✅ success

Please sign in to comment.