Skip to content

Commit

Permalink
Add granular CSS Modules options (#739)
Browse files Browse the repository at this point in the history
  • Loading branch information
timneutkens authored May 15, 2024
1 parent 445def9 commit 83839a9
Show file tree
Hide file tree
Showing 12 changed files with 299 additions and 64 deletions.
23 changes: 23 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ assert_cmd = "2.0"
assert_fs = "1.0"
predicates = "2.1"
serde_json = "1"
pretty_assertions = "1.4.0"

[[test]]
name = "cli_integration_tests"
Expand Down
9 changes: 9 additions & 0 deletions napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,9 @@ enum CssModulesOption {
struct CssModulesConfig {
pattern: Option<String>,
dashed_idents: Option<bool>,
animation: Option<bool>,
grid: Option<bool>,
custom_idents: Option<bool>,
}

#[cfg(feature = "bundler")]
Expand Down Expand Up @@ -713,6 +716,9 @@ fn compile<'i>(
Default::default()
},
dashed_idents: c.dashed_idents.unwrap_or_default(),
animation: c.animation.unwrap_or(true),
grid: c.grid.unwrap_or(true),
custom_idents: c.custom_idents.unwrap_or(true),
}),
}
} else {
Expand Down Expand Up @@ -840,6 +846,9 @@ fn compile_bundle<
Default::default()
},
dashed_idents: c.dashed_idents.unwrap_or_default(),
animation: c.animation.unwrap_or(true),
grid: c.grid.unwrap_or(true),
custom_idents: c.custom_idents.unwrap_or(true),
}),
}
} else {
Expand Down
23 changes: 22 additions & 1 deletion src/css_modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,34 @@ use std::hash::{Hash, Hasher};
use std::path::Path;

/// Configuration for CSS modules.
#[derive(Default, Clone, Debug)]
#[derive(Clone, Debug)]
pub struct Config<'i> {
/// The name pattern to use when renaming class names and other identifiers.
/// Default is `[hash]_[local]`.
pub pattern: Pattern<'i>,
/// Whether to rename dashed identifiers, e.g. custom properties.
pub dashed_idents: bool,
/// Whether to scope animation names.
/// Default is `true`.
pub animation: bool,
/// Whether to scope grid names.
/// Default is `true`.
pub grid: bool,
/// Whether to scope custom identifiers
/// Default is `true`.
pub custom_idents: bool,
}

impl<'i> Default for Config<'i> {
fn default() -> Self {
Config {
pattern: Default::default(),
dashed_idents: Default::default(),
animation: true,
grid: true,
custom_idents: true,
}
}
}

/// A CSS modules class name pattern.
Expand Down
132 changes: 132 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ mod tests {
use crate::vendor_prefix::VendorPrefix;
use cssparser::SourceLocation;
use indoc::indoc;
use pretty_assertions::assert_eq;
use std::collections::HashMap;

fn test(source: &str, expected: &str) {
Expand Down Expand Up @@ -23053,6 +23054,97 @@ mod tests {
Default::default(),
);

css_modules_test(
r#"
.foo {
color: red;
}

#id {
animation: 2s test;
}

@keyframes test {
from { color: red }
to { color: yellow }
}
"#,
indoc! {r#"
.EgL3uq_foo {
color: red;
}

#EgL3uq_id {
animation: 2s test;
}

@keyframes test {
from {
color: red;
}

to {
color: #ff0;
}
}
"#},
map! {
"foo" => "EgL3uq_foo",
"id" => "EgL3uq_id"
},
HashMap::new(),
crate::css_modules::Config {
animation: false,
// custom_idents: false,
..Default::default()
},
);

css_modules_test(
r#"
@counter-style circles {
symbols: Ⓐ Ⓑ Ⓒ;
}

ul {
list-style: circles;
}

ol {
list-style-type: none;
}

li {
list-style-type: disc;
}
"#,
indoc! {r#"
@counter-style circles {
symbols: Ⓐ Ⓑ Ⓒ;
}

ul {
list-style: circles;
}

ol {
list-style-type: none;
}

li {
list-style-type: disc;
}
"#},
map! {
"circles" => "EgL3uq_circles" referenced: true
},
HashMap::new(),
crate::css_modules::Config {
custom_idents: false,
..Default::default()
},
);

#[cfg(feature = "grid")]
css_modules_test(
r#"
Expand Down Expand Up @@ -23135,6 +23227,46 @@ mod tests {
Default::default(),
);

#[cfg(feature = "grid")]
css_modules_test(
r#"
.grid {
grid-template-areas: "foo";
}

.foo {
grid-area: foo;
}

.bar {
grid-column-start: foo-start;
}
"#,
indoc! {r#"
.EgL3uq_grid {
grid-template-areas: "foo";
}

.EgL3uq_foo {
grid-area: foo;
}

.EgL3uq_bar {
grid-column-start: foo-start;
}
"#},
map! {
"foo" => "EgL3uq_foo",
"grid" => "EgL3uq_grid",
"bar" => "EgL3uq_bar"
},
HashMap::new(),
crate::css_modules::Config {
grid: false,
..Default::default()
},
);

css_modules_test(
r#"
test {
Expand Down
44 changes: 23 additions & 21 deletions src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,30 +267,32 @@ impl<'a, 'b, 'c, W: std::fmt::Write + Sized> Printer<'a, 'b, 'c, W> {
/// Writes a CSS identifier to the underlying destination, escaping it
/// as appropriate. If the `css_modules` option was enabled, then a hash
/// is added, and the mapping is added to the CSS module.
pub fn write_ident(&mut self, ident: &str) -> Result<(), PrinterError> {
if let Some(css_module) = &mut self.css_module {
let dest = &mut self.dest;
let mut first = true;
css_module.config.pattern.write(
&css_module.hashes[self.loc.source_index as usize],
&css_module.sources[self.loc.source_index as usize],
ident,
|s| {
self.col += s.len() as u32;
if first {
first = false;
serialize_identifier(s, dest)
} else {
serialize_name(s, dest)
}
},
)?;
pub fn write_ident(&mut self, ident: &str, handle_css_module: bool) -> Result<(), PrinterError> {
if handle_css_module {
if let Some(css_module) = &mut self.css_module {
let dest = &mut self.dest;
let mut first = true;
css_module.config.pattern.write(
&css_module.hashes[self.loc.source_index as usize],
&css_module.sources[self.loc.source_index as usize],
ident,
|s| {
self.col += s.len() as u32;
if first {
first = false;
serialize_identifier(s, dest)
} else {
serialize_name(s, dest)
}
},
)?;

css_module.add_local(&ident, &ident, self.loc.source_index);
} else {
serialize_identifier(ident, self)?;
css_module.add_local(&ident, &ident, self.loc.source_index);
return Ok(());
}
}

serialize_identifier(ident, self)?;
Ok(())
}

Expand Down
19 changes: 13 additions & 6 deletions src/properties/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,24 @@ impl<'i> ToCss for AnimationName<'i> {
where
W: std::fmt::Write,
{
let css_module_animation_enabled =
dest.css_module.as_ref().map_or(false, |css_module| css_module.config.animation);

match self {
AnimationName::None => dest.write_str("none"),
AnimationName::Ident(s) => {
if let Some(css_module) = &mut dest.css_module {
css_module.reference(&s.0, dest.loc.source_index)
if css_module_animation_enabled {
if let Some(css_module) = &mut dest.css_module {
css_module.reference(&s.0, dest.loc.source_index)
}
}
s.to_css(dest)
s.to_css_with_options(dest, css_module_animation_enabled)
}
AnimationName::String(s) => {
if let Some(css_module) = &mut dest.css_module {
css_module.reference(&s, dest.loc.source_index)
if css_module_animation_enabled {
if let Some(css_module) = &mut dest.css_module {
css_module.reference(&s, dest.loc.source_index)
}
}

// CSS-wide keywords and `none` cannot remove quotes.
Expand All @@ -78,7 +85,7 @@ impl<'i> ToCss for AnimationName<'i> {
Ok(())
},
_ => {
dest.write_ident(s.as_ref())
dest.write_ident(s.as_ref(), css_module_animation_enabled)
}
}
}
Expand Down
Loading

0 comments on commit 83839a9

Please sign in to comment.