]>
Commit | Line | Data |
---|---|---|
5099ac24 | 1 | // Take a look at the license at the top of the repository in the LICENSE file. |
8faf50e0 | 2 | |
923072b8 | 3 | use crate::css::minify; |
8faf50e0 XL |
4 | |
5 | /*enum Element { | |
6 | /// Rule starting with `@`: | |
7 | /// | |
8 | /// * charset | |
9 | /// * font-face | |
10 | /// * import | |
11 | /// * keyframes | |
12 | /// * media | |
13 | AtRule(AtRule<'a>), | |
14 | /// Any "normal" CSS rule block. | |
15 | /// | |
16 | /// Contains the selector(s) and its content. | |
17 | ElementRule(Vec<&'a str>, Vec<Property<'a>>), | |
18 | } | |
19 | ||
20 | fn get_property<'a>(source: &'a str, iterator: &mut Peekable<CharIndices>, | |
21 | start_pos: &mut usize) -> Option<Property<'a>> { | |
22 | let mut end_pos = None; | |
23 | // First we get the property name. | |
24 | while let Some((pos, c)) = iterator.next() { | |
25 | if let Ok(c) = ReservedChar::try_from(c) { | |
26 | if c.is_useless() { | |
27 | continue | |
28 | } else if c == ReservedChar::OpenCurlyBrace { | |
29 | return None | |
30 | } else if c == ReservedChar::Colon { | |
31 | end_pos = Some(pos); | |
32 | break | |
33 | } else { // Invalid character. | |
34 | return None; | |
35 | } | |
36 | } else if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' { | |
37 | // everything's fine for now... | |
38 | } else { | |
39 | return None; // invalid character | |
40 | } | |
41 | } | |
42 | if end_pos.is_none() || end_pos == Some(*start_pos + 1) { | |
43 | return None; | |
44 | } | |
45 | while let Some((pos, c)) = iterator.next() { | |
46 | if let Ok(c) = ReservedChar::try_from(c) { | |
47 | if c == ReservedChar::DoubleQuote || c == ReservedChar::Quote { | |
48 | get_string(source, iterator, &mut 0, c) | |
49 | } else if c == ReservedChar::SemiColon { | |
50 | // we reached the end! | |
51 | let end_pos = end_pos.unwrap(); | |
52 | *start_pos = pos; | |
53 | return Property { | |
54 | name: &source[start_pos..end_pos], | |
55 | value: &source[end_pos..pos], | |
56 | } | |
57 | } | |
58 | } | |
59 | } | |
60 | None | |
61 | } | |
62 | ||
63 | enum Selector<'a> { | |
64 | Tag(&'a str), | |
65 | /// '.' | |
66 | Class(&'a str), | |
67 | /// '#' | |
68 | Id(&'a str), | |
69 | /// '<', '>', '(', ')', '+', ' ', '[', ']' | |
70 | Operator(char), | |
71 | } | |
72 | ||
73 | struct ElementRule<'a> { | |
74 | selectors: Vec<Selector<'a>>, | |
75 | properties: Vec<Property<'a>>, | |
76 | } | |
77 | ||
78 | fn get_element_rule<'a>(source: &'a str, iterator: &mut Peekable<CharIndices>, | |
79 | c: char) -> Option<Token<'a>> { | |
80 | let mut selectors = Vec::with_capacity(2); | |
81 | ||
82 | while let Some(s) = get_next_selector(source, iterator, c) { | |
83 | if !selectors.is_empty() || !s.empty_operator() { | |
84 | } | |
85 | selectors.push(s); | |
86 | } | |
87 | } | |
88 | ||
89 | fn get_media_query<'a>(source: &'a str, iterator: &mut Peekable<CharIndices>, | |
90 | start_pos: &mut usize) -> Option<Token<'a>> { | |
91 | while let Some((pos, c)) = iterator.next() { | |
92 | if c == '{' { | |
93 | ; | |
94 | } | |
95 | } | |
96 | None // An error occurred, sad life... | |
97 | } | |
98 | ||
99 | ||
100 | fn get_properties<'a>(source: &'a str, iterator: &mut Peekable<CharIndices>, | |
101 | start_pos: &mut usize) -> Vec<Property> { | |
102 | let mut ret = Vec::with_capacity(2); | |
103 | while let Some(property) = get_property(source, iterator, start_pos) { | |
104 | ret.push(property); | |
105 | } | |
106 | ret | |
107 | } | |
108 | ||
109 | pub struct Property<'a> { | |
110 | name: &'a str, | |
111 | value: &'a str, | |
112 | } | |
113 | ||
114 | pub enum AtRule<'a> { | |
115 | /// Contains the charset. Supposed to be the first rule in the style sheet and be present | |
116 | /// only once. | |
117 | Charset(&'a str), | |
118 | /// font-face rule. | |
119 | FontFace(Vec<Property<'a>>), | |
120 | /// Contains the import. | |
121 | Import(&'a str), | |
122 | /// Contains the rule and the block. | |
123 | Keyframes(&'a str, Tokens<'a>), | |
124 | /// Contains the rules and the block. | |
125 | Media(Vec<&'a str>, Tokens<'a>), | |
126 | } | |
127 | ||
128 | impl fmt::Display for AtRule { | |
129 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
130 | write!(f, "@{}", &match *self { | |
131 | AtRule::Charset(c) => format!("charset {};", c), | |
132 | AtRule::FontFace(t) => format!("font-face {{{}}};", t), | |
133 | AtRule::Import(i) => format!("import {};", i), | |
134 | AtRule::Keyframes(r, t) => format!("keyframes {} {{{}}}", r, t), | |
135 | AtRule::Media(r, t) => format!("media {} {{{}}}", r.join(" ").collect::<String>(), t), | |
136 | }) | |
137 | } | |
138 | }*/ | |
139 | ||
8faf50e0 XL |
140 | #[test] |
141 | fn check_minification() { | |
142 | let s = r#" | |
143 | /** Baguette! */ | |
144 | .b > p + div:hover { | |
145 | background: #fff; | |
146 | } | |
147 | ||
148 | a[target = "_blank"] { | |
17df50a5 | 149 | /* I like weird tests. */ |
8faf50e0 XL |
150 | border: 1px solid yellow ; |
151 | } | |
152 | "#; | |
153 | let expected = r#"/*! Baguette! */ | |
154 | .b>p+div:hover{background:#fff;}a[target="_blank"]{border:1px solid yellow;}"#; | |
923072b8 | 155 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
8faf50e0 XL |
156 | } |
157 | ||
158 | #[test] | |
159 | fn check_minification2() { | |
160 | let s = r#" | |
161 | h2, h3:not(.impl):not(.method):not(.type) { | |
162 | background-color: #0a042f !important; | |
163 | } | |
164 | ||
165 | :target { background: #494a3d; } | |
166 | ||
167 | .table-display tr td:first-child { | |
168 | float: right; | |
169 | } | |
170 | ||
171 | /* just some | |
172 | * long | |
173 | * | |
174 | * very | |
175 | * long | |
176 | * comment :) | |
177 | */ | |
178 | @media (max-width: 700px) { | |
179 | .theme-picker { | |
180 | left: 10px; | |
181 | top: 54px; | |
182 | z-index: 1; | |
183 | background-color: rgba(0, 0 , 0 , 0); | |
184 | font: 15px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; | |
185 | } | |
186 | }"#; | |
187 | let expected = "h2,h3:not(.impl):not(.method):not(.type){background-color:#0a042f !important;}\ | |
188 | :target{background:#494a3d;}.table-display tr td:first-child{float:right;}\ | |
189 | @media (max-width:700px){.theme-picker{left:10px;top:54px;z-index:1;\ | |
190 | background-color:rgba(0,0,0,0);font:15px \"SFMono-Regular\",Consolas,\ | |
191 | \"Liberation Mono\",Menlo,Courier,monospace;}}"; | |
923072b8 | 192 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
8faf50e0 | 193 | } |
b7449926 XL |
194 | |
195 | #[test] | |
196 | fn check_calc() { | |
197 | let s = ".foo { width: calc(100% - 34px); }"; | |
198 | let expected = ".foo{width:calc(100% - 34px);}"; | |
923072b8 | 199 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
b7449926 XL |
200 | } |
201 | ||
202 | #[test] | |
203 | fn check_spaces() { | |
204 | let s = ".line-numbers .line-highlighted { color: #0a042f !important; }"; | |
205 | let expected = ".line-numbers .line-highlighted{color:#0a042f !important;}"; | |
923072b8 | 206 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
b7449926 XL |
207 | } |
208 | ||
209 | #[test] | |
210 | fn check_space_after_paren() { | |
211 | let s = ".docblock:not(.type-decl) a:not(.srclink) {}"; | |
212 | let expected = ".docblock:not(.type-decl) a:not(.srclink){}"; | |
923072b8 | 213 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
b7449926 | 214 | } |
0bf4aa26 | 215 | |
6a06907d XL |
216 | #[test] |
217 | fn check_space_after_and() { | |
218 | let s = "@media only screen and (max-width : 600px) {}"; | |
219 | let expected = "@media only screen and (max-width:600px){}"; | |
923072b8 | 220 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
6a06907d XL |
221 | } |
222 | ||
5099ac24 FG |
223 | #[test] |
224 | fn check_space_after_or_not() { | |
225 | let s = "@supports not ((text-align-last: justify) or (-moz-text-align-last: justify)) {}"; | |
226 | let expected = "@supports not ((text-align-last:justify) or (-moz-text-align-last:justify)){}"; | |
923072b8 | 227 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
5099ac24 FG |
228 | } |
229 | ||
6a06907d XL |
230 | #[test] |
231 | fn check_space_after_brackets() { | |
232 | let s = "#main[data-behavior = \"1\"] {}"; | |
233 | let expected = "#main[data-behavior=\"1\"]{}"; | |
923072b8 | 234 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
6a06907d XL |
235 | |
236 | let s = "#main[data-behavior = \"1\"] .aclass"; | |
237 | let expected = "#main[data-behavior=\"1\"] .aclass"; | |
923072b8 | 238 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
6a06907d XL |
239 | |
240 | let s = "#main[data-behavior = \"1\"] ul.aclass"; | |
241 | let expected = "#main[data-behavior=\"1\"] ul.aclass"; | |
923072b8 | 242 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
6a06907d XL |
243 | } |
244 | ||
0bf4aa26 XL |
245 | #[test] |
246 | fn check_whitespaces_in_calc() { | |
247 | let s = ".foo { width: calc(130px + 10%); }"; | |
248 | let expected = ".foo{width:calc(130px + 10%);}"; | |
923072b8 | 249 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
0bf4aa26 XL |
250 | |
251 | let s = ".foo { width: calc(130px + (45% - 10% + (12 * 2px))); }"; | |
252 | let expected = ".foo{width:calc(130px + (45% - 10% + (12 * 2px)));}"; | |
923072b8 | 253 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
6a06907d | 254 | } |
17df50a5 XL |
255 | |
256 | #[test] | |
257 | fn check_weird_comments() { | |
258 | let s = ".test1 { | |
259 | font-weight: 30em; | |
260 | }/**/ | |
261 | .test2 { | |
262 | font-weight: 30em; | |
263 | }/**/ | |
264 | .test3 { | |
265 | font-weight: 30em; | |
266 | }/**/"; | |
267 | let expected = ".test1{font-weight:30em;}.test2{font-weight:30em;}.test3{font-weight:30em;}"; | |
923072b8 | 268 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
17df50a5 XL |
269 | } |
270 | ||
271 | #[test] | |
272 | fn check_slash_slash() { | |
273 | let s = "body { | |
274 | background-image: url(data:image/webp;base64,c//S4KP//ZZ/19Uj/UA==); | |
275 | }"; | |
276 | let expected = "body{background-image:url(data:image/webp;base64,c//S4KP//ZZ/19Uj/UA==);}"; | |
923072b8 | 277 | assert_eq!(minify(s).expect("minify failed").to_string(), expected); |
17df50a5 | 278 | } |
5e7ed085 FG |
279 | |
280 | #[test] | |
281 | fn issue_80() { | |
282 | assert_eq!( | |
923072b8 | 283 | minify("@import 'i';t{x: #fff;}").unwrap().to_string(), |
5e7ed085 FG |
284 | "@import 'i';t{x:#fff;}", |
285 | ); | |
286 | } |