Skip to content

Commit cdf6064

Browse files
committed
Improve @import parsing
1 parent 19e8a3d commit cdf6064

File tree

5 files changed

+84
-45
lines changed

5 files changed

+84
-45
lines changed

src/lib.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,6 @@ fn compile(code: &str, minify: bool, targets: Option<Browsers>) -> Result<String
9999
};
100100
// println!("{:?}", rule);
101101
let rule = match rule {
102-
parser::CssRule::Import(import) => {
103-
parser::CssRule::Import(parser::ImportRule {
104-
media: import.media,
105-
url: "test".into()
106-
})
107-
},
108102
parser::CssRule::Keyframes(mut keyframes) => {
109103
for keyframe in keyframes.keyframes.iter_mut() {
110104
keyframe.declarations.minify(&mut handler, &mut important_handler);
@@ -3858,6 +3852,19 @@ mod tests {
38583852
minify_test("@namespace toto url(http://toto.example.org);", "@namespace toto \"http://toto.example.org\";");
38593853
}
38603854

3855+
#[test]
3856+
fn test_import() {
3857+
minify_test("@import url(foo.css);", "@import \"foo.css\";");
3858+
minify_test("@import \"foo.css\";", "@import \"foo.css\";");
3859+
minify_test("@import url(foo.css) print;", "@import \"foo.css\" print;");
3860+
minify_test("@import \"foo.css\" print;", "@import \"foo.css\" print;");
3861+
minify_test("@import \"foo.css\" screen and (orientation: landscape);", "@import \"foo.css\" screen and (orientation:landscape);");
3862+
minify_test("@import url(foo.css) supports(display: flex);", "@import \"foo.css\" supports(display: flex);");
3863+
minify_test("@import url(foo.css) supports(display: flex) print;", "@import \"foo.css\" supports(display: flex) print;");
3864+
minify_test("@import url(foo.css) supports(not (display: flex));", "@import \"foo.css\" supports(not (display: flex));");
3865+
minify_test("@import url(foo.css) supports((display: flex));", "@import \"foo.css\" supports(display: flex);");
3866+
}
3867+
38613868
#[test]
38623869
fn test_prefixes() {
38633870
prefix_test(

src/parser.rs

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ use crate::printer::Printer;
77
use crate::traits::{Parse, ToCss};
88
use std::fmt::Write;
99
use crate::selector::{Selectors, SelectorParser};
10-
use crate::rules::keyframes::{KeyframeListParser, KeyframesRule};
11-
use crate::rules::font_face::{FontFaceRule, FontFaceDeclarationParser};
12-
use crate::rules::page::{PageSelector, PageRule};
13-
use crate::rules::supports::{SupportsCondition, SupportsRule};
14-
use crate::rules::counter_style::CounterStyleRule;
15-
use crate::rules::namespace::NamespaceRule;
10+
use crate::rules::{
11+
keyframes::{KeyframeListParser, KeyframesRule},
12+
font_face::{FontFaceRule, FontFaceDeclarationParser},
13+
page::{PageSelector, PageRule},
14+
supports::{SupportsCondition, SupportsRule},
15+
counter_style::CounterStyleRule,
16+
namespace::NamespaceRule,
17+
import::ImportRule
18+
};
1619
use crate::values::ident::CustomIdent;
1720
use crate::declaration::{Declaration, DeclarationHandler};
1821
use crate::properties::VendorPrefix;
@@ -99,7 +102,7 @@ pub enum AtRulePrelude {
99102
/// A @document rule, with its conditional.
100103
Document,//(DocumentCondition),
101104
/// A @import rule prelude.
102-
Import(String, MediaList),//(CssUrl, Arc<Locked<MediaList>>),
105+
Import(String, MediaList, Option<SupportsCondition>),
103106
/// A @namespace rule prelude.
104107
Namespace(Option<String>, String),
105108
}
@@ -118,28 +121,30 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser {
118121
match_ignore_ascii_case! { &*name,
119122
"import" => {
120123
let url_string = input.expect_url_or_string()?.as_ref().to_owned();
124+
let supports = if input.try_parse(|input| input.expect_function_matching("supports")).is_ok() {
125+
Some(input.parse_nested_block(|input| {
126+
input.try_parse(SupportsCondition::parse).or_else(|_| SupportsCondition::parse_declaration(input))
127+
})?)
128+
} else {
129+
None
130+
};
121131
let media = MediaList::parse(input);
122-
return Ok(AtRuleType::WithoutBlock(AtRulePrelude::Import(url_string, media)));
132+
return Ok(AtRuleType::WithoutBlock(AtRulePrelude::Import(url_string, media, supports)));
123133
},
124134
"namespace" => {
125135
let prefix = input.try_parse(|input| input.expect_ident_cloned()).map(|v| v.as_ref().to_owned()).ok();
126136
let namespace = input.expect_url_or_string()?.as_ref().to_owned();
127137
let prelude = AtRulePrelude::Namespace(prefix, namespace);
128138
return Ok(AtRuleType::WithoutBlock(prelude));
129139
},
130-
// // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
131-
// // anything left is invalid.
132-
// "charset" => {
133-
// self.dom_error = Some(RulesMutateError::HierarchyRequest);
134-
// return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule))
135-
// },
140+
// @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
141+
// anything left is invalid.
142+
"charset" => {
143+
return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name)))
144+
},
136145
_ => {}
137146
}
138147

139-
// if !self.check_state(State::Body) {
140-
// return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
141-
// }
142-
143148
AtRuleParser::parse_prelude(&mut self.nested(), name, input)
144149
}
145150

@@ -161,9 +166,10 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser {
161166
start: &ParserState,
162167
) -> Self::AtRule {
163168
let rule = match prelude {
164-
AtRulePrelude::Import(url, media) => {
169+
AtRulePrelude::Import(url, media, supports) => {
165170
CssRule::Import(ImportRule {
166171
url,
172+
supports,
167173
media
168174
})
169175
},
@@ -232,21 +238,6 @@ impl ToCss for MediaRule {
232238
}
233239
}
234240

235-
#[derive(Debug, PartialEq)]
236-
pub struct ImportRule {
237-
pub url: String,
238-
pub media: MediaList
239-
}
240-
241-
impl ToCss for ImportRule {
242-
fn to_css<W>(&self, dest: &mut Printer<W>) -> fmt::Result where W: fmt::Write {
243-
dest.write_str("@import ")?;
244-
serialize_string(&self.url, dest)?;
245-
// dest.write_str(&self.media)?;
246-
dest.write_str(";")
247-
}
248-
}
249-
250241
#[derive(Debug, PartialEq)]
251242
pub struct StyleRule {
252243
pub selectors: SelectorList<Selectors>,

src/rules/import.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use cssparser::*;
2+
use crate::traits::ToCss;
3+
use crate::printer::Printer;
4+
use std::fmt::Write;
5+
use crate::media_query::MediaList;
6+
use super::supports::SupportsCondition;
7+
8+
/// https://drafts.csswg.org/css-cascade/#at-import
9+
#[derive(Debug, PartialEq)]
10+
pub struct ImportRule {
11+
pub url: String,
12+
pub supports: Option<SupportsCondition>,
13+
pub media: MediaList
14+
}
15+
16+
impl ToCss for ImportRule {
17+
fn to_css<W>(&self, dest: &mut Printer<W>) -> std::fmt::Result where W: std::fmt::Write {
18+
dest.write_str("@import ")?;
19+
serialize_string(&self.url, dest)?;
20+
if let Some(supports) = &self.supports {
21+
dest.write_str(" supports")?;
22+
if matches!(supports, SupportsCondition::Declaration(_) | SupportsCondition::Parens(_)) {
23+
supports.to_css(dest)?;
24+
} else {
25+
dest.write_char('(')?;
26+
supports.to_css(dest)?;
27+
dest.write_char(')')?;
28+
}
29+
}
30+
if !self.media.media_queries.is_empty() {
31+
dest.write_char(' ')?;
32+
self.media.to_css(dest)?;
33+
}
34+
dest.write_str(";")
35+
}
36+
}

src/rules/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ pub mod page;
44
pub mod supports;
55
pub mod counter_style;
66
pub mod namespace;
7+
pub mod import;

src/rules/supports.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,7 @@ impl SupportsCondition {
123123
return Ok(SupportsCondition::Parens(Box::new(condition)))
124124
}
125125

126-
let pos = input.position();
127-
input.expect_ident()?;
128-
input.expect_colon()?;
129-
input.expect_no_error_token()?;
130-
Ok(SupportsCondition::Declaration(input.slice_from(pos).to_owned()))
126+
Self::parse_declaration(input)
131127
})
132128
});
133129
if res.is_ok() {
@@ -140,6 +136,14 @@ impl SupportsCondition {
140136
input.parse_nested_block(|input| input.expect_no_error_token().map_err(|err| err.into()))?;
141137
Ok(SupportsCondition::Unknown(input.slice_from(pos).to_owned()))
142138
}
139+
140+
pub fn parse_declaration<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ()>> {
141+
let pos = input.position();
142+
input.expect_ident()?;
143+
input.expect_colon()?;
144+
input.expect_no_error_token()?;
145+
Ok(SupportsCondition::Declaration(input.slice_from(pos).to_owned()))
146+
}
143147
}
144148

145149
impl ToCss for SupportsCondition {

0 commit comments

Comments
 (0)