Skip to content

Commit ca503cf

Browse files
committed
Bundle CSS modules dependencies
1 parent e3ad301 commit ca503cf

File tree

10 files changed

+500
-98
lines changed

10 files changed

+500
-98
lines changed

src/bundler.rs

Lines changed: 361 additions & 36 deletions
Large diffs are not rendered by default.

src/css_modules.rs

Lines changed: 109 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,12 @@ impl<'i> Pattern<'i> {
114114
write(s)?;
115115
}
116116
Segment::Name => {
117-
write(path.file_stem().unwrap().to_str().unwrap())?;
117+
let stem = path.file_stem().unwrap().to_str().unwrap();
118+
if stem.contains('.') {
119+
write(&stem.replace('.', "-"))?;
120+
} else {
121+
write(stem)?;
122+
}
118123
}
119124
Segment::Local => {
120125
write(local)?;
@@ -210,54 +215,70 @@ lazy_static! {
210215

211216
pub(crate) struct CssModule<'a, 'b, 'c> {
212217
pub config: &'a Config<'b>,
213-
pub path: &'c Path,
214-
pub hash: String,
215-
pub exports: &'a mut CssModuleExports,
218+
pub sources: Vec<&'c Path>,
219+
pub hashes: Vec<String>,
220+
pub exports_by_source_index: Vec<CssModuleExports>,
216221
pub references: &'a mut HashMap<String, CssModuleReference>,
217222
}
218223

219224
impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
220225
pub fn new(
221226
config: &'a Config<'b>,
222-
filename: &'c str,
223-
exports: &'a mut CssModuleExports,
227+
sources: &'c Vec<String>,
224228
references: &'a mut HashMap<String, CssModuleReference>,
225229
) -> Self {
226230
Self {
227231
config,
228-
path: Path::new(filename),
229-
hash: hash(filename, matches!(config.pattern.segments[0], Segment::Hash)),
230-
exports,
232+
sources: sources.iter().map(|filename| Path::new(filename)).collect(),
233+
hashes: sources
234+
.iter()
235+
.map(|source| hash(&source, matches!(config.pattern.segments[0], Segment::Hash)))
236+
.collect(),
237+
exports_by_source_index: sources.iter().map(|_| HashMap::new()).collect(),
231238
references,
232239
}
233240
}
234241

235-
pub fn add_local(&mut self, exported: &str, local: &str) {
236-
self.exports.entry(exported.into()).or_insert_with(|| CssModuleExport {
237-
name: self
238-
.config
239-
.pattern
240-
.write_to_string(String::new(), &self.hash, &self.path, local)
241-
.unwrap(),
242-
composes: vec![],
243-
is_referenced: false,
244-
});
242+
pub fn add_local(&mut self, exported: &str, local: &str, source_index: u32) {
243+
self.exports_by_source_index[source_index as usize]
244+
.entry(exported.into())
245+
.or_insert_with(|| CssModuleExport {
246+
name: self
247+
.config
248+
.pattern
249+
.write_to_string(
250+
String::new(),
251+
&self.hashes[source_index as usize],
252+
&self.sources[source_index as usize],
253+
local,
254+
)
255+
.unwrap(),
256+
composes: vec![],
257+
is_referenced: false,
258+
});
245259
}
246260

247-
pub fn add_dashed(&mut self, local: &str) {
248-
self.exports.entry(local.into()).or_insert_with(|| CssModuleExport {
249-
name: self
250-
.config
251-
.pattern
252-
.write_to_string("--".into(), &self.hash, &self.path, &local[2..])
253-
.unwrap(),
254-
composes: vec![],
255-
is_referenced: false,
256-
});
261+
pub fn add_dashed(&mut self, local: &str, source_index: u32) {
262+
self.exports_by_source_index[source_index as usize]
263+
.entry(local.into())
264+
.or_insert_with(|| CssModuleExport {
265+
name: self
266+
.config
267+
.pattern
268+
.write_to_string(
269+
"--".into(),
270+
&self.hashes[source_index as usize],
271+
&self.sources[source_index as usize],
272+
&local[2..],
273+
)
274+
.unwrap(),
275+
composes: vec![],
276+
is_referenced: false,
277+
});
257278
}
258279

259-
pub fn reference(&mut self, name: &str) {
260-
match self.exports.entry(name.into()) {
280+
pub fn reference(&mut self, name: &str, source_index: u32) {
281+
match self.exports_by_source_index[source_index as usize].entry(name.into()) {
261282
std::collections::hash_map::Entry::Occupied(mut entry) => {
262283
entry.get_mut().is_referenced = true;
263284
}
@@ -266,7 +287,12 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
266287
name: self
267288
.config
268289
.pattern
269-
.write_to_string(String::new(), &self.hash, &self.path, name)
290+
.write_to_string(
291+
String::new(),
292+
&self.hashes[source_index as usize],
293+
&self.sources[source_index as usize],
294+
name,
295+
)
270296
.unwrap(),
271297
composes: vec![],
272298
is_referenced: true,
@@ -275,7 +301,7 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
275301
}
276302
}
277303

278-
pub fn reference_dashed(&mut self, name: &str, from: &Option<Specifier>) -> Option<String> {
304+
pub fn reference_dashed(&mut self, name: &str, from: &Option<Specifier>, source_index: u32) -> Option<String> {
279305
let (reference, key) = match from {
280306
Some(Specifier::Global) => return Some(name[2..].into()),
281307
Some(Specifier::File(file)) => (
@@ -285,9 +311,23 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
285311
},
286312
file.as_ref(),
287313
),
314+
Some(Specifier::SourceIndex(source_index)) => {
315+
return Some(
316+
self
317+
.config
318+
.pattern
319+
.write_to_string(
320+
String::new(),
321+
&self.hashes[*source_index as usize],
322+
&self.sources[*source_index as usize],
323+
&name[2..],
324+
)
325+
.unwrap(),
326+
)
327+
}
288328
None => {
289329
// Local export. Mark as used.
290-
match self.exports.entry(name.into()) {
330+
match self.exports_by_source_index[source_index as usize].entry(name.into()) {
291331
std::collections::hash_map::Entry::Occupied(mut entry) => {
292332
entry.get_mut().is_referenced = true;
293333
}
@@ -296,7 +336,12 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
296336
name: self
297337
.config
298338
.pattern
299-
.write_to_string("--".into(), &self.hash, &self.path, name)
339+
.write_to_string(
340+
"--".into(),
341+
&self.hashes[source_index as usize],
342+
&self.sources[source_index as usize],
343+
name,
344+
)
300345
.unwrap(),
301346
composes: vec![],
302347
is_referenced: true,
@@ -307,7 +352,10 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
307352
}
308353
};
309354

310-
let hash = hash(&format!("{}_{}_{}", self.hash, name, key), false);
355+
let hash = hash(
356+
&format!("{}_{}_{}", self.hashes[source_index as usize], name, key),
357+
false,
358+
);
311359
let name = format!("--{}", hash);
312360

313361
self.references.insert(name.clone(), reference);
@@ -318,6 +366,7 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
318366
&mut self,
319367
selectors: &SelectorList<Selectors>,
320368
composes: &Composes,
369+
source_index: u32,
321370
) -> Result<(), PrinterErrorKind> {
322371
for sel in &selectors.0 {
323372
if sel.len() == 1 {
@@ -329,9 +378,29 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
329378
name: self
330379
.config
331380
.pattern
332-
.write_to_string(String::new(), &self.hash, &self.path, name.0.as_ref())
381+
.write_to_string(
382+
String::new(),
383+
&self.hashes[source_index as usize],
384+
&self.sources[source_index as usize],
385+
name.0.as_ref(),
386+
)
333387
.unwrap(),
334388
},
389+
Some(Specifier::SourceIndex(dep_source_index)) => {
390+
if let Some(entry) =
391+
self.exports_by_source_index[*dep_source_index as usize].get(&name.0.as_ref().to_owned())
392+
{
393+
let name = entry.name.clone();
394+
let composes = entry.composes.clone();
395+
let export = self.exports_by_source_index[source_index as usize]
396+
.get_mut(&id.0.as_ref().to_owned())
397+
.unwrap();
398+
399+
export.composes.push(CssModuleReference::Local { name });
400+
export.composes.extend(composes);
401+
}
402+
continue;
403+
}
335404
Some(Specifier::Global) => CssModuleReference::Global {
336405
name: name.0.as_ref().into(),
337406
},
@@ -341,7 +410,9 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
341410
},
342411
};
343412

344-
let export = self.exports.get_mut(&id.0.as_ref().to_owned()).unwrap();
413+
let export = self.exports_by_source_index[source_index as usize]
414+
.get_mut(&id.0.as_ref().to_owned())
415+
.unwrap();
345416
if !export.composes.contains(&reference) {
346417
export.composes.push(reference);
347418
}

src/printer.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -226,20 +226,22 @@ impl<'a, 'b, 'c, W: std::fmt::Write + Sized> Printer<'a, 'b, 'c, W> {
226226
if let Some(css_module) = &mut self.css_module {
227227
let dest = &mut self.dest;
228228
let mut first = true;
229-
css_module
230-
.config
231-
.pattern
232-
.write(&css_module.hash, &css_module.path, ident, |s| {
229+
css_module.config.pattern.write(
230+
&css_module.hashes[self.loc.source_index as usize],
231+
&css_module.sources[self.loc.source_index as usize],
232+
ident,
233+
|s| {
233234
self.col += s.len() as u32;
234235
if first {
235236
first = false;
236237
serialize_identifier(s, dest)
237238
} else {
238239
serialize_name(s, dest)
239240
}
240-
})?;
241+
},
242+
)?;
241243

242-
css_module.add_local(&ident, &ident);
244+
css_module.add_local(&ident, &ident, self.loc.source_index);
243245
} else {
244246
serialize_identifier(ident, self)?;
245247
}
@@ -253,16 +255,18 @@ impl<'a, 'b, 'c, W: std::fmt::Write + Sized> Printer<'a, 'b, 'c, W> {
253255
match &mut self.css_module {
254256
Some(css_module) if css_module.config.dashed_idents => {
255257
let dest = &mut self.dest;
256-
css_module
257-
.config
258-
.pattern
259-
.write(&css_module.hash, &css_module.path, &ident[2..], |s| {
258+
css_module.config.pattern.write(
259+
&css_module.hashes[self.loc.source_index as usize],
260+
&css_module.sources[self.loc.source_index as usize],
261+
&ident[2..],
262+
|s| {
260263
self.col += s.len() as u32;
261264
serialize_name(s, dest)
262-
})?;
265+
},
266+
)?;
263267

264268
if is_declaration {
265-
css_module.add_dashed(ident);
269+
css_module.add_dashed(ident, self.loc.source_index);
266270
}
267271
}
268272
_ => {

src/properties/animation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl<'i> ToCss for AnimationName<'i> {
5555
AnimationName::None => dest.write_str("none"),
5656
AnimationName::Ident(s) => {
5757
if let Some(css_module) = &mut dest.css_module {
58-
css_module.reference(&s.0)
58+
css_module.reference(&s.0, dest.loc.source_index)
5959
}
6060
s.to_css(dest)
6161
}

src/properties/css_modules.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub enum Specifier<'i> {
3737
/// The referenced name comes from the specified file.
3838
#[cfg_attr(feature = "serde", serde(borrow))]
3939
File(CowArcStr<'i>),
40+
/// The referenced name comes from a source index (used during bundling).
41+
SourceIndex(u32),
4042
}
4143

4244
impl<'i> Parse<'i> for Composes<'i> {
@@ -119,6 +121,7 @@ impl<'i> ToCss for Specifier<'i> {
119121
match self {
120122
Specifier::Global => dest.write_str("global")?,
121123
Specifier::File(file) => serialize_string(&file, dest)?,
124+
Specifier::SourceIndex(..) => {}
122125
}
123126
Ok(())
124127
}

src/properties/list.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ impl ToCss for CounterStyle<'_> {
188188
CounterStyle::Predefined(style) => style.to_css(dest),
189189
CounterStyle::Name(name) => {
190190
if let Some(css_module) = &mut dest.css_module {
191-
css_module.reference(&name.0)
191+
css_module.reference(&name.0, dest.loc.source_index)
192192
}
193193
name.to_css(dest)
194194
}

src/rules/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ pub(crate) struct MinifyContext<'a, 'i> {
245245
pub handler_context: &'a mut PropertyHandlerContext<'i, 'a>,
246246
pub unused_symbols: &'a HashSet<String>,
247247
pub custom_media: Option<HashMap<CowArcStr<'i>, CustomMediaRule<'i>>>,
248+
pub css_modules: bool,
248249
}
249250

250251
impl<'i> CssRuleList<'i> {
@@ -455,6 +456,7 @@ fn merge_style_rules<'i>(
455456
&& last_style_rule.is_compatible(*context.targets)
456457
&& style.rules.0.is_empty()
457458
&& last_style_rule.rules.0.is_empty()
459+
&& (!context.css_modules || style.loc.source_index == last_style_rule.loc.source_index)
458460
{
459461
last_style_rule
460462
.declarations

src/rules/style.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ impl<'a, 'i> StyleRule<'i> {
233233

234234
if let Some(css_module) = &mut dest.css_module {
235235
css_module
236-
.handle_composes(&self.selectors, &composes)
236+
.handle_composes(&self.selectors, &composes, self.loc.source_index)
237237
.map_err(|e| dest.error(e, composes.loc))?;
238238
continue;
239239
}

src/stylesheet.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ impl<'i, 'o> StyleSheet<'i, 'o> {
179179
handler_context: &mut context,
180180
unused_symbols: &options.unused_symbols,
181181
custom_media,
182+
css_modules: self.options.css_modules.is_some(),
182183
};
183184

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

203204
if let Some(config) = &self.options.css_modules {
204-
let mut exports = HashMap::new();
205205
let mut references = HashMap::new();
206-
printer.css_module = Some(CssModule::new(
207-
config,
208-
printer.filename(),
209-
&mut exports,
210-
&mut references,
211-
));
206+
printer.css_module = Some(CssModule::new(config, &self.sources, &mut references));
212207

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

216211
Ok(ToCssResult {
217212
dependencies: printer.dependencies,
213+
exports: Some(std::mem::take(
214+
&mut printer.css_module.unwrap().exports_by_source_index[0],
215+
)),
218216
code: dest,
219-
exports: Some(exports),
220217
references: Some(references),
221218
})
222219
} else {

src/values/ident.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ impl<'i> ToCss for DashedIdentReference<'i> {
122122
{
123123
match &mut dest.css_module {
124124
Some(css_module) if css_module.config.dashed_idents => {
125-
if let Some(name) = css_module.reference_dashed(&self.ident.0, &self.from) {
125+
if let Some(name) = css_module.reference_dashed(&self.ident.0, &self.from, dest.loc.source_index) {
126126
dest.write_str("--")?;
127127
serialize_name(&name, dest)?;
128128
return Ok(());

0 commit comments

Comments
 (0)