// Take a look at the license at the top of the repository in the LICENSE file. use crate::css::minify; /*enum Element { /// Rule starting with `@`: /// /// * charset /// * font-face /// * import /// * keyframes /// * media AtRule(AtRule<'a>), /// Any "normal" CSS rule block. /// /// Contains the selector(s) and its content. ElementRule(Vec<&'a str>, Vec>), } fn get_property<'a>(source: &'a str, iterator: &mut Peekable, start_pos: &mut usize) -> Option> { let mut end_pos = None; // First we get the property name. while let Some((pos, c)) = iterator.next() { if let Ok(c) = ReservedChar::try_from(c) { if c.is_useless() { continue } else if c == ReservedChar::OpenCurlyBrace { return None } else if c == ReservedChar::Colon { end_pos = Some(pos); break } else { // Invalid character. return None; } } else if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' { // everything's fine for now... } else { return None; // invalid character } } if end_pos.is_none() || end_pos == Some(*start_pos + 1) { return None; } while let Some((pos, c)) = iterator.next() { if let Ok(c) = ReservedChar::try_from(c) { if c == ReservedChar::DoubleQuote || c == ReservedChar::Quote { get_string(source, iterator, &mut 0, c) } else if c == ReservedChar::SemiColon { // we reached the end! let end_pos = end_pos.unwrap(); *start_pos = pos; return Property { name: &source[start_pos..end_pos], value: &source[end_pos..pos], } } } } None } enum Selector<'a> { Tag(&'a str), /// '.' Class(&'a str), /// '#' Id(&'a str), /// '<', '>', '(', ')', '+', ' ', '[', ']' Operator(char), } struct ElementRule<'a> { selectors: Vec>, properties: Vec>, } fn get_element_rule<'a>(source: &'a str, iterator: &mut Peekable, c: char) -> Option> { let mut selectors = Vec::with_capacity(2); while let Some(s) = get_next_selector(source, iterator, c) { if !selectors.is_empty() || !s.empty_operator() { } selectors.push(s); } } fn get_media_query<'a>(source: &'a str, iterator: &mut Peekable, start_pos: &mut usize) -> Option> { while let Some((pos, c)) = iterator.next() { if c == '{' { ; } } None // An error occurred, sad life... } fn get_properties<'a>(source: &'a str, iterator: &mut Peekable, start_pos: &mut usize) -> Vec { let mut ret = Vec::with_capacity(2); while let Some(property) = get_property(source, iterator, start_pos) { ret.push(property); } ret } pub struct Property<'a> { name: &'a str, value: &'a str, } pub enum AtRule<'a> { /// Contains the charset. Supposed to be the first rule in the style sheet and be present /// only once. Charset(&'a str), /// font-face rule. FontFace(Vec>), /// Contains the import. Import(&'a str), /// Contains the rule and the block. Keyframes(&'a str, Tokens<'a>), /// Contains the rules and the block. Media(Vec<&'a str>, Tokens<'a>), } impl fmt::Display for AtRule { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "@{}", &match *self { AtRule::Charset(c) => format!("charset {};", c), AtRule::FontFace(t) => format!("font-face {{{}}};", t), AtRule::Import(i) => format!("import {};", i), AtRule::Keyframes(r, t) => format!("keyframes {} {{{}}}", r, t), AtRule::Media(r, t) => format!("media {} {{{}}}", r.join(" ").collect::(), t), }) } }*/ #[test] fn check_minification() { let s = r#" /** Baguette! */ .b > p + div:hover { background: #fff; } a[target = "_blank"] { /* I like weird tests. */ border: 1px solid yellow ; } "#; let expected = r#"/*! Baguette! */ .b>p+div:hover{background:#fff;}a[target="_blank"]{border:1px solid yellow;}"#; assert_eq!(minify(s).expect("minify failed").to_string(), expected); } #[test] fn check_minification2() { let s = r#" h2, h3:not(.impl):not(.method):not(.type) { background-color: #0a042f !important; } :target { background: #494a3d; } .table-display tr td:first-child { float: right; } /* just some * long * * very * long * comment :) */ @media (max-width: 700px) { .theme-picker { left: 10px; top: 54px; z-index: 1; background-color: rgba(0, 0 , 0 , 0); font: 15px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; } }"#; let expected = "h2,h3:not(.impl):not(.method):not(.type){background-color:#0a042f !important;}\ :target{background:#494a3d;}.table-display tr td:first-child{float:right;}\ @media (max-width:700px){.theme-picker{left:10px;top:54px;z-index:1;\ background-color:rgba(0,0,0,0);font:15px \"SFMono-Regular\",Consolas,\ \"Liberation Mono\",Menlo,Courier,monospace;}}"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); } #[test] fn check_calc() { let s = ".foo { width: calc(100% - 34px); }"; let expected = ".foo{width:calc(100% - 34px);}"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); } #[test] fn check_spaces() { let s = ".line-numbers .line-highlighted { color: #0a042f !important; }"; let expected = ".line-numbers .line-highlighted{color:#0a042f !important;}"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); } #[test] fn check_space_after_paren() { let s = ".docblock:not(.type-decl) a:not(.srclink) {}"; let expected = ".docblock:not(.type-decl) a:not(.srclink){}"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); } #[test] fn check_space_after_and() { let s = "@media only screen and (max-width : 600px) {}"; let expected = "@media only screen and (max-width:600px){}"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); } #[test] fn check_space_after_or_not() { let s = "@supports not ((text-align-last: justify) or (-moz-text-align-last: justify)) {}"; let expected = "@supports not ((text-align-last:justify) or (-moz-text-align-last:justify)){}"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); } #[test] fn check_space_after_brackets() { let s = "#main[data-behavior = \"1\"] {}"; let expected = "#main[data-behavior=\"1\"]{}"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); let s = "#main[data-behavior = \"1\"] .aclass"; let expected = "#main[data-behavior=\"1\"] .aclass"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); let s = "#main[data-behavior = \"1\"] ul.aclass"; let expected = "#main[data-behavior=\"1\"] ul.aclass"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); } #[test] fn check_whitespaces_in_calc() { let s = ".foo { width: calc(130px + 10%); }"; let expected = ".foo{width:calc(130px + 10%);}"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); let s = ".foo { width: calc(130px + (45% - 10% + (12 * 2px))); }"; let expected = ".foo{width:calc(130px + (45% - 10% + (12 * 2px)));}"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); } #[test] fn check_weird_comments() { let s = ".test1 { font-weight: 30em; }/**/ .test2 { font-weight: 30em; }/**/ .test3 { font-weight: 30em; }/**/"; let expected = ".test1{font-weight:30em;}.test2{font-weight:30em;}.test3{font-weight:30em;}"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); } #[test] fn check_slash_slash() { let s = "body { background-image: url(data:image/webp;base64,c//S4KP//ZZ/19Uj/UA==); }"; let expected = "body{background-image:url(data:image/webp;base64,c//S4KP//ZZ/19Uj/UA==);}"; assert_eq!(minify(s).expect("minify failed").to_string(), expected); } #[test] fn issue_80() { assert_eq!( minify("@import 'i';t{x: #fff;}").unwrap().to_string(), "@import 'i';t{x:#fff;}", ); }