1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use data_encoding::{Specification, Encoding};
use lazy_static::lazy_static;
use crate::properties::css_modules::{Composes, ComposesFrom};
use parcel_selectors::SelectorList;
use crate::selector::Selectors;
use serde::Serialize;
use crate::error::PrinterError;
#[derive(PartialEq, Debug, Clone, Serialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum CssModuleReference {
Local {
name: String,
},
Global {
name: String
},
Dependency {
name: String,
specifier: String
}
}
#[derive(PartialEq, Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CssModuleExport {
pub name: String,
pub composes: Vec<CssModuleReference>,
pub is_referenced: bool
}
pub type CssModuleExports = HashMap<String, CssModuleExport>;
lazy_static! {
static ref ENCODER: Encoding = {
let mut spec = Specification::new();
spec.symbols.push_str("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-");
spec.encoding().unwrap()
};
}
pub(crate) struct CssModule<'a> {
pub hash: &'a str,
pub exports: &'a mut CssModuleExports
}
impl<'a> CssModule<'a> {
pub fn add_local(&mut self, exported: &str, local: &str) {
let hash = &self.hash;
self.exports.entry(exported.into())
.or_insert_with(|| CssModuleExport {
name: format!("{}_{}", local, hash),
composes: vec![],
is_referenced: false
});
}
pub fn reference(&mut self, name: &str) {
match self.exports.entry(name.into()) {
std::collections::hash_map::Entry::Occupied(mut entry) => {
entry.get_mut().is_referenced = true;
}
std::collections::hash_map::Entry::Vacant(entry) => {
entry.insert(CssModuleExport {
name: format!("{}_{}", name, self.hash),
composes: vec![],
is_referenced: true
});
}
}
}
pub fn handle_composes(&mut self, selectors: &SelectorList<Selectors>, composes: &Composes) -> Result<(), PrinterError> {
for sel in &selectors.0 {
if sel.len() == 1 {
match sel.iter_raw_match_order().next().unwrap() {
parcel_selectors::parser::Component::Class(ref id) => {
for name in &composes.names {
let reference = match &composes.from {
None => CssModuleReference::Local { name: format!("{}_{}", name.0, self.hash) },
Some(ComposesFrom::Global) => CssModuleReference::Global { name: name.0.clone() },
Some(ComposesFrom::File(file)) => CssModuleReference::Dependency {
name: name.0.clone(),
specifier: file.clone()
}
};
let export = self.exports.get_mut(&id.0).unwrap();
if !export.composes.contains(&reference) {
export.composes.push(reference);
}
}
continue;
}
_ => {}
}
}
return Err(PrinterError::InvalidComposesSelector(composes.loc))
}
Ok(())
}
}
pub(crate) fn hash(s: &str) -> String {
let mut hasher = DefaultHasher::new();
s.hash(&mut hasher);
let hash = hasher.finish() as u32;
ENCODER.encode(&hash.to_le_bytes())
}