forked from parcel-bundler/lightningcss
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcss_modules.rs
More file actions
104 lines (91 loc) · 3.09 KB
/
css_modules.rs
File metadata and controls
104 lines (91 loc) · 3.09 KB
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
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, Eq, Hash, Debug, Clone, Serialize)]
#[serde(tag = "type", content = "value", rename_all = "lowercase")]
pub enum CssModuleExport {
Local(String),
Dependency {
name: String,
specifier: String
}
}
pub type CssModuleExports = HashMap<String, Vec<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_export(&mut self, name: String, export: CssModuleExport) {
match self.exports.entry(name) {
std::collections::hash_map::Entry::Occupied(mut entry) => {
if !entry.get().contains(&export) {
entry.get_mut().push(export);
}
}
std::collections::hash_map::Entry::Vacant(entry) => {
let mut items = Vec::new();
if !items.contains(&export) {
items.push(export);
}
entry.insert(items);
}
}
}
pub fn add_local(&mut self, exported: &str, local: &str) {
let local = CssModuleExport::Local(format!("{}_{}", local, self.hash));
self.add_export(exported.into(), local);
}
pub fn add_global(&mut self, exported: &str, global: &str) {
self.add_export(exported.into(), CssModuleExport::Local(global.into()))
}
pub fn add_dependency(&mut self, exported: &str, name: &str, specifier: &str) {
let dependency = CssModuleExport::Dependency {
name: name.into(),
specifier: specifier.into()
};
self.add_export(exported.into(), dependency)
}
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 {
match &composes.from {
None => self.add_local(&id.0, &name.0),
Some(ComposesFrom::Global) => self.add_global(&id.0, &name.0),
Some(ComposesFrom::File(file)) => self.add_dependency(&id.0, &name.0, &file)
}
}
continue;
}
_ => {}
}
}
// The composes property can only be used within a simple class selector.
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())
}