Skip to content

Commit

Permalink
Bundle CSS modules dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett committed Sep 4, 2022
1 parent e3ad301 commit ca503cf
Show file tree
Hide file tree
Showing 10 changed files with 500 additions and 98 deletions.
397 changes: 361 additions & 36 deletions src/bundler.rs

Large diffs are not rendered by default.

147 changes: 109 additions & 38 deletions src/css_modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,12 @@ impl<'i> Pattern<'i> {
write(s)?;
}
Segment::Name => {
write(path.file_stem().unwrap().to_str().unwrap())?;
let stem = path.file_stem().unwrap().to_str().unwrap();
if stem.contains('.') {
write(&stem.replace('.', "-"))?;
} else {
write(stem)?;
}
}
Segment::Local => {
write(local)?;
Expand Down Expand Up @@ -210,54 +215,70 @@ lazy_static! {

pub(crate) struct CssModule<'a, 'b, 'c> {
pub config: &'a Config<'b>,
pub path: &'c Path,
pub hash: String,
pub exports: &'a mut CssModuleExports,
pub sources: Vec<&'c Path>,
pub hashes: Vec<String>,
pub exports_by_source_index: Vec<CssModuleExports>,
pub references: &'a mut HashMap<String, CssModuleReference>,
}

impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
pub fn new(
config: &'a Config<'b>,
filename: &'c str,
exports: &'a mut CssModuleExports,
sources: &'c Vec<String>,
references: &'a mut HashMap<String, CssModuleReference>,
) -> Self {
Self {
config,
path: Path::new(filename),
hash: hash(filename, matches!(config.pattern.segments[0], Segment::Hash)),
exports,
sources: sources.iter().map(|filename| Path::new(filename)).collect(),
hashes: sources
.iter()
.map(|source| hash(&source, matches!(config.pattern.segments[0], Segment::Hash)))
.collect(),
exports_by_source_index: sources.iter().map(|_| HashMap::new()).collect(),
references,
}
}

pub fn add_local(&mut self, exported: &str, local: &str) {
self.exports.entry(exported.into()).or_insert_with(|| CssModuleExport {
name: self
.config
.pattern
.write_to_string(String::new(), &self.hash, &self.path, local)
.unwrap(),
composes: vec![],
is_referenced: false,
});
pub fn add_local(&mut self, exported: &str, local: &str, source_index: u32) {
self.exports_by_source_index[source_index as usize]
.entry(exported.into())
.or_insert_with(|| CssModuleExport {
name: self
.config
.pattern
.write_to_string(
String::new(),
&self.hashes[source_index as usize],
&self.sources[source_index as usize],
local,
)
.unwrap(),
composes: vec![],
is_referenced: false,
});
}

pub fn add_dashed(&mut self, local: &str) {
self.exports.entry(local.into()).or_insert_with(|| CssModuleExport {
name: self
.config
.pattern
.write_to_string("--".into(), &self.hash, &self.path, &local[2..])
.unwrap(),
composes: vec![],
is_referenced: false,
});
pub fn add_dashed(&mut self, local: &str, source_index: u32) {
self.exports_by_source_index[source_index as usize]
.entry(local.into())
.or_insert_with(|| CssModuleExport {
name: self
.config
.pattern
.write_to_string(
"--".into(),
&self.hashes[source_index as usize],
&self.sources[source_index as usize],
&local[2..],
)
.unwrap(),
composes: vec![],
is_referenced: false,
});
}

pub fn reference(&mut self, name: &str) {
match self.exports.entry(name.into()) {
pub fn reference(&mut self, name: &str, source_index: u32) {
match self.exports_by_source_index[source_index as usize].entry(name.into()) {
std::collections::hash_map::Entry::Occupied(mut entry) => {
entry.get_mut().is_referenced = true;
}
Expand All @@ -266,7 +287,12 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
name: self
.config
.pattern
.write_to_string(String::new(), &self.hash, &self.path, name)
.write_to_string(
String::new(),
&self.hashes[source_index as usize],
&self.sources[source_index as usize],
name,
)
.unwrap(),
composes: vec![],
is_referenced: true,
Expand All @@ -275,7 +301,7 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
}
}

pub fn reference_dashed(&mut self, name: &str, from: &Option<Specifier>) -> Option<String> {
pub fn reference_dashed(&mut self, name: &str, from: &Option<Specifier>, source_index: u32) -> Option<String> {
let (reference, key) = match from {
Some(Specifier::Global) => return Some(name[2..].into()),
Some(Specifier::File(file)) => (
Expand All @@ -285,9 +311,23 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
},
file.as_ref(),
),
Some(Specifier::SourceIndex(source_index)) => {
return Some(
self
.config
.pattern
.write_to_string(
String::new(),
&self.hashes[*source_index as usize],
&self.sources[*source_index as usize],
&name[2..],
)
.unwrap(),
)
}
None => {
// Local export. Mark as used.
match self.exports.entry(name.into()) {
match self.exports_by_source_index[source_index as usize].entry(name.into()) {
std::collections::hash_map::Entry::Occupied(mut entry) => {
entry.get_mut().is_referenced = true;
}
Expand All @@ -296,7 +336,12 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
name: self
.config
.pattern
.write_to_string("--".into(), &self.hash, &self.path, name)
.write_to_string(
"--".into(),
&self.hashes[source_index as usize],
&self.sources[source_index as usize],
name,
)
.unwrap(),
composes: vec![],
is_referenced: true,
Expand All @@ -307,7 +352,10 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
}
};

let hash = hash(&format!("{}_{}_{}", self.hash, name, key), false);
let hash = hash(
&format!("{}_{}_{}", self.hashes[source_index as usize], name, key),
false,
);
let name = format!("--{}", hash);

self.references.insert(name.clone(), reference);
Expand All @@ -318,6 +366,7 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
&mut self,
selectors: &SelectorList<Selectors>,
composes: &Composes,
source_index: u32,
) -> Result<(), PrinterErrorKind> {
for sel in &selectors.0 {
if sel.len() == 1 {
Expand All @@ -329,9 +378,29 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
name: self
.config
.pattern
.write_to_string(String::new(), &self.hash, &self.path, name.0.as_ref())
.write_to_string(
String::new(),
&self.hashes[source_index as usize],
&self.sources[source_index as usize],
name.0.as_ref(),
)
.unwrap(),
},
Some(Specifier::SourceIndex(dep_source_index)) => {
if let Some(entry) =
self.exports_by_source_index[*dep_source_index as usize].get(&name.0.as_ref().to_owned())
{
let name = entry.name.clone();
let composes = entry.composes.clone();
let export = self.exports_by_source_index[source_index as usize]
.get_mut(&id.0.as_ref().to_owned())
.unwrap();

export.composes.push(CssModuleReference::Local { name });
export.composes.extend(composes);
}
continue;
}
Some(Specifier::Global) => CssModuleReference::Global {
name: name.0.as_ref().into(),
},
Expand All @@ -341,7 +410,9 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
},
};

let export = self.exports.get_mut(&id.0.as_ref().to_owned()).unwrap();
let export = self.exports_by_source_index[source_index as usize]
.get_mut(&id.0.as_ref().to_owned())
.unwrap();
if !export.composes.contains(&reference) {
export.composes.push(reference);
}
Expand Down
28 changes: 16 additions & 12 deletions src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,20 +226,22 @@ impl<'a, 'b, 'c, W: std::fmt::Write + Sized> Printer<'a, 'b, 'c, W> {
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.hash, &css_module.path, ident, |s| {
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);
css_module.add_local(&ident, &ident, self.loc.source_index);
} else {
serialize_identifier(ident, self)?;
}
Expand All @@ -253,16 +255,18 @@ impl<'a, 'b, 'c, W: std::fmt::Write + Sized> Printer<'a, 'b, 'c, W> {
match &mut self.css_module {
Some(css_module) if css_module.config.dashed_idents => {
let dest = &mut self.dest;
css_module
.config
.pattern
.write(&css_module.hash, &css_module.path, &ident[2..], |s| {
css_module.config.pattern.write(
&css_module.hashes[self.loc.source_index as usize],
&css_module.sources[self.loc.source_index as usize],
&ident[2..],
|s| {
self.col += s.len() as u32;
serialize_name(s, dest)
})?;
},
)?;

if is_declaration {
css_module.add_dashed(ident);
css_module.add_dashed(ident, self.loc.source_index);
}
}
_ => {
Expand Down
2 changes: 1 addition & 1 deletion src/properties/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl<'i> ToCss for AnimationName<'i> {
AnimationName::None => dest.write_str("none"),
AnimationName::Ident(s) => {
if let Some(css_module) = &mut dest.css_module {
css_module.reference(&s.0)
css_module.reference(&s.0, dest.loc.source_index)
}
s.to_css(dest)
}
Expand Down
3 changes: 3 additions & 0 deletions src/properties/css_modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub enum Specifier<'i> {
/// The referenced name comes from the specified file.
#[cfg_attr(feature = "serde", serde(borrow))]
File(CowArcStr<'i>),
/// The referenced name comes from a source index (used during bundling).
SourceIndex(u32),
}

impl<'i> Parse<'i> for Composes<'i> {
Expand Down Expand Up @@ -119,6 +121,7 @@ impl<'i> ToCss for Specifier<'i> {
match self {
Specifier::Global => dest.write_str("global")?,
Specifier::File(file) => serialize_string(&file, dest)?,
Specifier::SourceIndex(..) => {}
}
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion src/properties/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl ToCss for CounterStyle<'_> {
CounterStyle::Predefined(style) => style.to_css(dest),
CounterStyle::Name(name) => {
if let Some(css_module) = &mut dest.css_module {
css_module.reference(&name.0)
css_module.reference(&name.0, dest.loc.source_index)
}
name.to_css(dest)
}
Expand Down
2 changes: 2 additions & 0 deletions src/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ pub(crate) struct MinifyContext<'a, 'i> {
pub handler_context: &'a mut PropertyHandlerContext<'i, 'a>,
pub unused_symbols: &'a HashSet<String>,
pub custom_media: Option<HashMap<CowArcStr<'i>, CustomMediaRule<'i>>>,
pub css_modules: bool,
}

impl<'i> CssRuleList<'i> {
Expand Down Expand Up @@ -455,6 +456,7 @@ fn merge_style_rules<'i>(
&& last_style_rule.is_compatible(*context.targets)
&& style.rules.0.is_empty()
&& last_style_rule.rules.0.is_empty()
&& (!context.css_modules || style.loc.source_index == last_style_rule.loc.source_index)
{
last_style_rule
.declarations
Expand Down
2 changes: 1 addition & 1 deletion src/rules/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ impl<'a, 'i> StyleRule<'i> {

if let Some(css_module) = &mut dest.css_module {
css_module
.handle_composes(&self.selectors, &composes)
.handle_composes(&self.selectors, &composes, self.loc.source_index)
.map_err(|e| dest.error(e, composes.loc))?;
continue;
}
Expand Down
13 changes: 5 additions & 8 deletions src/stylesheet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ impl<'i, 'o> StyleSheet<'i, 'o> {
handler_context: &mut context,
unused_symbols: &options.unused_symbols,
custom_media,
css_modules: self.options.css_modules.is_some(),
};

self.rules.minify(&mut ctx, false).map_err(|e| Error {
Expand All @@ -201,22 +202,18 @@ impl<'i, 'o> StyleSheet<'i, 'o> {
printer.sources = Some(&self.sources);

if let Some(config) = &self.options.css_modules {
let mut exports = HashMap::new();
let mut references = HashMap::new();
printer.css_module = Some(CssModule::new(
config,
printer.filename(),
&mut exports,
&mut references,
));
printer.css_module = Some(CssModule::new(config, &self.sources, &mut references));

self.rules.to_css(&mut printer)?;
printer.newline()?;

Ok(ToCssResult {
dependencies: printer.dependencies,
exports: Some(std::mem::take(
&mut printer.css_module.unwrap().exports_by_source_index[0],
)),
code: dest,
exports: Some(exports),
references: Some(references),
})
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/values/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ impl<'i> ToCss for DashedIdentReference<'i> {
{
match &mut dest.css_module {
Some(css_module) if css_module.config.dashed_idents => {
if let Some(name) = css_module.reference_dashed(&self.ident.0, &self.from) {
if let Some(name) = css_module.reference_dashed(&self.ident.0, &self.from, dest.loc.source_index) {
dest.write_str("--")?;
serialize_name(&name, dest)?;
return Ok(());
Expand Down

0 comments on commit ca503cf

Please sign in to comment.