feat(css/ast): Add raw to Url (#2389)

This commit is contained in:
Alexander Akait 2021-10-11 06:33:11 +03:00 committed by GitHub
parent 4458f9c74d
commit 2678c34488
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 462 additions and 112 deletions

14
Cargo.lock generated
View File

@ -2481,7 +2481,7 @@ dependencies = [
[[package]]
name = "swc_css"
version = "0.12.0"
version = "0.13.0"
dependencies = [
"swc_css_ast",
"swc_css_codegen",
@ -2492,7 +2492,7 @@ dependencies = [
[[package]]
name = "swc_css_ast"
version = "0.11.0"
version = "0.12.0"
dependencies = [
"is-macro",
"serde",
@ -2503,7 +2503,7 @@ dependencies = [
[[package]]
name = "swc_css_codegen"
version = "0.10.0"
version = "0.11.0"
dependencies = [
"auto_impl",
"bitflags",
@ -2529,7 +2529,7 @@ dependencies = [
[[package]]
name = "swc_css_parser"
version = "0.12.0"
version = "0.13.0"
dependencies = [
"bitflags",
"lexical",
@ -2545,7 +2545,7 @@ dependencies = [
[[package]]
name = "swc_css_utils"
version = "0.8.0"
version = "0.9.0"
dependencies = [
"swc_atoms 0.2.7",
"swc_common",
@ -2555,7 +2555,7 @@ dependencies = [
[[package]]
name = "swc_css_visit"
version = "0.10.0"
version = "0.11.0"
dependencies = [
"swc_atoms 0.2.7",
"swc_common",
@ -3120,7 +3120,7 @@ dependencies = [
[[package]]
name = "swc_stylis"
version = "0.9.0"
version = "0.10.0"
dependencies = [
"swc_atoms 0.2.7",
"swc_common",

View File

@ -6,11 +6,11 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_css"
repository = "https://github.com/swc-project/swc.git"
version = "0.12.0"
version = "0.13.0"
[dependencies]
swc_css_ast = {version = "0.11.0", path = "./ast"}
swc_css_codegen = {version = "0.10.0", path = "./codegen"}
swc_css_parser = {version = "0.12.0", path = "./parser"}
swc_css_utils = {version = "0.8.0", path = "./utils/"}
swc_css_visit = {version = "0.10.0", path = "./visit"}
swc_css_ast = {version = "0.12.0", path = "./ast"}
swc_css_codegen = {version = "0.11.0", path = "./codegen"}
swc_css_parser = {version = "0.13.0", path = "./parser"}
swc_css_utils = {version = "0.9.0", path = "./utils/"}
swc_css_visit = {version = "0.11.0", path = "./visit"}

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_css_ast"
repository = "https://github.com/swc-project/swc.git"
version = "0.11.0"
version = "0.12.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -58,6 +58,7 @@ pub enum Token {
/// `url(value)`
Url {
value: JsWord,
raw: JsWord,
},
/// `,`

View File

@ -159,6 +159,6 @@ pub struct AtTextValue {
#[ast_node("UrlValue")]
pub struct UrlValue {
pub span: Span,
pub url: JsWord,
pub raw: JsWord,
}

View File

@ -6,17 +6,17 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_css_codegen"
repository = "https://github.com/swc-project/swc.git"
version = "0.10.0"
version = "0.11.0"
[dependencies]
auto_impl = "0.4.1"
bitflags = "1.3.2"
swc_atoms = {version = "0.2.7", path = "../../atoms"}
swc_common = {version = "0.13.0", path = "../../common"}
swc_css_ast = {version = "0.11.0", path = "../ast/"}
swc_css_ast = {version = "0.12.0", path = "../ast/"}
swc_css_codegen_macros = {version = "0.2.0", path = "macros/"}
[dev-dependencies]
swc_css_parser = {version = "0.12.0", path = "../parser"}
swc_css_visit = {version = "0.10.0", path = "../visit"}
swc_css_parser = {version = "0.13.0", path = "../parser"}
swc_css_visit = {version = "0.11.0", path = "../visit"}
testing = {version = "0.14.0", path = "../../testing"}

View File

@ -506,10 +506,10 @@ where
Token::Str { raw, .. } => {
self.wr.write_raw(Some(span), &raw)?;
}
Token::Url { value } => {
Token::Url { raw, .. } => {
self.wr.write_ident(Some(span), "url", false)?;
punct!(self, "(");
self.wr.write_raw(None, &value)?;
self.wr.write_raw(None, &raw)?;
punct!(self, ")");
}
Token::Comma => {
@ -596,7 +596,7 @@ where
fn emit_url_value(&mut self, n: &UrlValue) -> Result {
keyword!(self, "url");
punct!(self, "(");
self.wr.write_raw(Some(n.span), &n.url)?;
self.wr.write_raw(Some(n.span), &n.raw)?;
punct!(self, ")");
}

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_css_parser"
repository = "https://github.com/swc-project/swc.git"
version = "0.12.0"
version = "0.13.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
@ -17,11 +17,11 @@ bitflags = "1.2.1"
lexical = "5.2.2"
swc_atoms = {version = "0.2.7", path = "../../atoms"}
swc_common = {version = "0.13.0", path = "../../common"}
swc_css_ast = {version = "0.11.0", path = "../ast"}
swc_css_ast = {version = "0.12.0", path = "../ast"}
unicode-xid = "0.2.2"
[dev-dependencies]
serde = "1.0.127"
serde_json = "1.0.66"
swc_css_visit = {version = "0.10.0", path = "../visit"}
swc_css_visit = {version = "0.11.0", path = "../visit"}
testing = {version = "0.14.0", path = "../../testing"}

View File

@ -236,24 +236,23 @@ where
}
}
/// Ported from `consumeIdentLike` of `esbuild`
fn read_ident_like(&mut self) -> LexResult<Token> {
let name = self.read_name()?;
if self.input.is_byte(b'(') {
if name.0.len() == 3 {
if name.0.to_ascii_lowercase() == js_word!("url") {
let pos = self.input.cur_pos();
// TODO: rewrite https://www.w3.org/TR/css-syntax-3/#consume-ident-like-token
if name.0.to_ascii_lowercase() == js_word!("url") && self.input.is_byte(b'(') {
let pos = self.input.cur_pos();
self.input.bump();
self.skip_ws()?;
self.input.bump();
let pos_whitespace = self.input.cur_pos();
self.skip_ws()?;
match self.input.cur() {
Some('"' | '\'') => self.input.reset_to(pos),
_ => {
return self.read_url();
}
}
match self.input.cur() {
Some('"' | '\'') => self.input.reset_to(pos),
_ => {
self.input.reset_to(pos_whitespace);
return self.read_url();
}
}
}
@ -348,51 +347,83 @@ where
})
}
/// Ported from `consumeURL` of `esbuild`.
fn read_url(&mut self) -> LexResult<Token> {
let mut url = String::new();
let mut value = String::new();
let mut raw = String::new();
let start_pos = self.input.cur_pos();
self.skip_ws()?;
let end_pos = self.input.cur_pos();
raw.push_str(&self.input.slice(start_pos, end_pos));
loop {
self.last_pos = None;
if self.input.eat_byte(b')') {
return Ok(Token::Url { value: url.into() });
}
match self.input.cur() {
Some(c) if c == ')' => {
self.input.bump();
if self.input.cur().is_none() {
return Err(ErrorKind::UnterminatedUrl);
}
match self.input.cur().unwrap() {
c if is_whitespace(c) => {
self.skip_ws()?;
if !self.input.eat_byte(b')') {
// TODO: break + Error recovery
return Err(ErrorKind::UnterminatedUrl);
}
return Ok(Token::Url { value: url.into() });
return Ok(Token::Url {
value: value.into(),
raw: raw.into(),
});
}
'"' | '\'' | '(' => {
// TODO: break + Error recovery
None => {
// TODO: This is a parse error. Return the <url-token>.
return Err(ErrorKind::Eof);
}
Some(c) if is_whitespace(c) => {
raw.push(c);
self.input.bump();
let start_pos = self.input.cur_pos();
self.skip_ws()?;
let end_pos = self.input.cur_pos();
raw.push_str(&self.input.slice(start_pos, end_pos));
let c = self.input.cur();
match c {
Some(c) if c == ')' => {
self.input.bump();
return Ok(Token::Url {
value: value.into(),
raw: raw.into(),
});
}
None => {
// TODO: This is a parse error. Return the <url-token>.
return Err(ErrorKind::Eof);
}
_ => {}
}
// TODO: break + Error recovery + 4.3.14. Consume the remnants of a bad url
return Err(ErrorKind::UnterminatedUrl);
}
'\\' => {
Some(c) if c == '"' || c == '\'' || c == '(' || is_non_printable(c) => {
// TODO: break + Error recovery + 4.3.14. Consume the remnants of a bad url
return Err(ErrorKind::UnterminatedUrl);
}
Some('\\') => {
if !self.is_valid_escape()? {
// TODO: break + Error recovery
// TODO: break + Error recovery + 4.3.14. Consume the remnants of a bad url
return Err(ErrorKind::InvalidEscape);
}
// TODO raw url
url.push(self.read_escape()?.0);
let escaped = self.read_escape()?;
value.push(escaped.0);
raw.push_str(&escaped.1);
}
c => {
url.push(c);
// TODO: Validate that c is a valid character for a URL.
Some(c) => {
value.push(c);
raw.push(c);
self.input.bump();
}
@ -725,3 +756,10 @@ fn is_whitespace(c: char) -> bool {
_ => false,
}
}
fn is_non_printable(c: char) -> bool {
match c {
'\x00'..='\x08' | '\x0B' | '\x0E'..='\x1F' | '\x7F' => true,
_ => false,
}
}

View File

@ -30,7 +30,11 @@ where
}
Token::Url { .. } => match bump!(self) {
Token::Url { value } => Ok(ImportSource::Url(UrlValue { span, url: value })),
Token::Url { value, raw } => Ok(ImportSource::Url(UrlValue {
span,
url: value,
raw,
})),
_ => {
unreachable!()
}

View File

@ -238,8 +238,7 @@ where
Token::Url { .. } => {
let url = match bump!(self) {
// TODO fix me
Token::Url { value, .. } => value,
Token::Url { value, raw } => (value, raw),
_ => {
unreachable!()
}
@ -247,7 +246,8 @@ where
return Ok(Value::Url(UrlValue {
span: span!(self, span.lo),
url,
url: url.0,
raw: url.1,
}));
}

View File

@ -98,7 +98,8 @@
"end": 80,
"ctxt": 0
},
"url": "./style.css"
"url": "./style.css",
"raw": "./style.css"
},
"condition": null
},
@ -193,7 +194,8 @@
"end": 180,
"ctxt": 0
},
"url": "./style.css"
"url": "./style.css",
"raw": "./style.css"
},
"condition": null
},

View File

@ -88,7 +88,8 @@
"end": 21,
"ctxt": 0
},
"url": "aج"
"url": "aج",
"raw": "a\\62c"
}
],
"important": null

View File

@ -20,7 +20,8 @@
"end": 20,
"ctxt": 0
},
"url": "foo.css"
"url": "foo.css",
"raw": "foo.css"
},
"condition": null
}

View File

@ -88,7 +88,8 @@
"end": 22,
"ctxt": 0
},
"url": "abc"
"url": "abc",
"raw": "a\\62 c"
}
],
"important": null

View File

@ -88,7 +88,8 @@
"end": 18,
"ctxt": 0
},
"url": ","
"url": ",",
"raw": "\\,"
}
],
"important": null

View File

@ -20,7 +20,8 @@
"end": 20,
"ctxt": 0
},
"url": "foo.css"
"url": "foo.css",
"raw": "foo.css"
},
"condition": null
}

View File

@ -88,7 +88,8 @@
"end": 19,
"ctxt": 0
},
"url": ","
"url": ",",
"raw": "\\2c"
}
],
"important": null

View File

@ -88,7 +88,8 @@
"end": 22,
"ctxt": 0
},
"url": "abc"
"url": "abc",
"raw": "\\61 bc"
}
],
"important": null

View File

@ -20,7 +20,8 @@
"end": 13,
"ctxt": 0
},
"url": ""
"url": "",
"raw": ""
},
"condition": null
}

View File

@ -88,7 +88,8 @@
"end": 21,
"ctxt": 0
},
"url": "憼"
"url": "憼",
"raw": "\\61bc"
}
],
"important": null

View File

@ -18,4 +18,26 @@ a {
background-image: url( ' ' );
background-image: url(./image.png);
background-image: url( ./image.png );
}
background-image: url( ./image\32.png );
background-image: url(
./image\32.png
);
background-image: url(
./image\32.png
);
background-image: url(
./image\32.png
);
}

View File

@ -2,7 +2,7 @@
"type": "Stylesheet",
"span": {
"start": 0,
"end": 783,
"end": 1043,
"ctxt": 0
},
"rules": [
@ -10,7 +10,7 @@
"type": "StyleRule",
"span": {
"start": 0,
"end": 783,
"end": 1042,
"ctxt": 0
},
"selectors": [
@ -59,7 +59,7 @@
"type": "DeclBlock",
"span": {
"start": 2,
"end": 783,
"end": 1042,
"ctxt": 0
},
"items": [
@ -88,7 +88,8 @@
"end": 109,
"ctxt": 0
},
"url": "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
"url": "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
"raw": "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
}
],
"important": null
@ -475,7 +476,8 @@
"end": 488,
"ctxt": 0
},
"url": ""
"url": "",
"raw": ""
}
],
"important": null
@ -505,7 +507,8 @@
"end": 520,
"ctxt": 0
},
"url": ""
"url": "",
"raw": " "
}
],
"important": null
@ -790,7 +793,8 @@
"end": 734,
"ctxt": 0
},
"url": "./image.png"
"url": "./image.png",
"raw": "./image.png"
}
],
"important": null
@ -820,7 +824,132 @@
"end": 780,
"ctxt": 0
},
"url": "./image.png"
"url": "./image.png",
"raw": " ./image.png "
}
],
"important": null
},
{
"type": "Property",
"span": {
"start": 786,
"end": 829,
"ctxt": 0
},
"name": {
"type": "Text",
"span": {
"start": 786,
"end": 802,
"ctxt": 0
},
"value": "background-image",
"raw": "background-image"
},
"values": [
{
"type": "UrlValue",
"span": {
"start": 804,
"end": 829,
"ctxt": 0
},
"url": "./image2.png",
"raw": " ./image\\32.png "
}
],
"important": null
},
{
"type": "Property",
"span": {
"start": 835,
"end": 888,
"ctxt": 0
},
"name": {
"type": "Text",
"span": {
"start": 835,
"end": 851,
"ctxt": 0
},
"value": "background-image",
"raw": "background-image"
},
"values": [
{
"type": "UrlValue",
"span": {
"start": 853,
"end": 888,
"ctxt": 0
},
"url": "./image2.png",
"raw": " \n ./image\\32.png \n "
}
],
"important": null
},
{
"type": "Property",
"span": {
"start": 894,
"end": 974,
"ctxt": 0
},
"name": {
"type": "Text",
"span": {
"start": 894,
"end": 910,
"ctxt": 0
},
"value": "background-image",
"raw": "background-image"
},
"values": [
{
"type": "UrlValue",
"span": {
"start": 912,
"end": 974,
"ctxt": 0
},
"url": "./image2.png",
"raw": "\n \n \n \n ./image\\32.png\n \n \n \n "
}
],
"important": null
},
{
"type": "Property",
"span": {
"start": 980,
"end": 1039,
"ctxt": 0
},
"name": {
"type": "Text",
"span": {
"start": 980,
"end": 996,
"ctxt": 0
},
"value": "background-image",
"raw": "background-image"
},
"values": [
{
"type": "UrlValue",
"span": {
"start": 998,
"end": 1039,
"ctxt": 0
},
"url": "./image2.png",
"raw": " \n\n\n\n ./image\\32.png\n\n\n\n "
}
],
"important": null

View File

@ -6,9 +6,9 @@ error: Stylesheet
3 | | background-image: url("./image (1).jpg");
4 | | background-image: url('./image (1).jpg');
... |
20 | | background-image: url( ./image.png );
21 | | }
| |_^
42 | | );
43 | | }
| |__^
error: Rule
--> $DIR/tests/fixture/function/url/input.css:1:1
@ -18,8 +18,8 @@ error: Rule
3 | | background-image: url("./image (1).jpg");
4 | | background-image: url('./image (1).jpg');
... |
20 | | background-image: url( ./image.png );
21 | | }
42 | | );
43 | | }
| |_^
error: StyleRule
@ -30,8 +30,8 @@ error: StyleRule
3 | | background-image: url("./image (1).jpg");
4 | | background-image: url('./image (1).jpg');
... |
20 | | background-image: url( ./image.png );
21 | | }
42 | | );
43 | | }
| |_^
error: ComplexSelector
@ -67,8 +67,8 @@ error: DeclBlock
3 | | background-image: url("./image (1).jpg");
4 | | background-image: url('./image (1).jpg');
... |
20 | | background-image: url( ./image.png );
21 | | }
42 | | );
43 | | }
| |_^
error: Property
@ -703,3 +703,147 @@ error: UrlValue
20 | background-image: url( ./image.png );
| ^^^^^^^^^^^^^^^^^^^^^^
error: Property
--> $DIR/tests/fixture/function/url/input.css:21:5
|
21 | background-image: url( ./image\32.png );
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: Text
--> $DIR/tests/fixture/function/url/input.css:21:5
|
21 | background-image: url( ./image\32.png );
| ^^^^^^^^^^^^^^^^
error: Value
--> $DIR/tests/fixture/function/url/input.css:21:23
|
21 | background-image: url( ./image\32.png );
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: UrlValue
--> $DIR/tests/fixture/function/url/input.css:21:23
|
21 | background-image: url( ./image\32.png );
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: Property
--> $DIR/tests/fixture/function/url/input.css:22:5
|
22 | / background-image: url(
23 | | ./image\32.png
24 | | );
| |_____^
error: Text
--> $DIR/tests/fixture/function/url/input.css:22:5
|
22 | background-image: url(
| ^^^^^^^^^^^^^^^^
error: Value
--> $DIR/tests/fixture/function/url/input.css:22:23
|
22 | background-image: url(
| _______________________^
23 | | ./image\32.png
24 | | );
| |_____^
error: UrlValue
--> $DIR/tests/fixture/function/url/input.css:22:23
|
22 | background-image: url(
| _______________________^
23 | | ./image\32.png
24 | | );
| |_____^
error: Property
--> $DIR/tests/fixture/function/url/input.css:25:5
|
25 | / background-image: url(
26 | |
27 | |
28 | |
... |
32 | |
33 | | );
| |_____^
error: Text
--> $DIR/tests/fixture/function/url/input.css:25:5
|
25 | background-image: url(
| ^^^^^^^^^^^^^^^^
error: Value
--> $DIR/tests/fixture/function/url/input.css:25:23
|
25 | background-image: url(
| _______________________^
26 | |
27 | |
28 | |
... |
32 | |
33 | | );
| |_____^
error: UrlValue
--> $DIR/tests/fixture/function/url/input.css:25:23
|
25 | background-image: url(
| _______________________^
26 | |
27 | |
28 | |
... |
32 | |
33 | | );
| |_____^
error: Property
--> $DIR/tests/fixture/function/url/input.css:34:5
|
34 | / background-image: url(
35 | |
36 | |
37 | |
... |
41 | |
42 | | );
| |________^
error: Text
--> $DIR/tests/fixture/function/url/input.css:34:5
|
34 | background-image: url(
| ^^^^^^^^^^^^^^^^
error: Value
--> $DIR/tests/fixture/function/url/input.css:34:23
|
34 | background-image: url(
| _______________________^
35 | |
36 | |
37 | |
... |
41 | |
42 | | );
| |________^
error: UrlValue
--> $DIR/tests/fixture/function/url/input.css:34:23
|
34 | background-image: url(
| _______________________^
35 | |
36 | |
37 | |
... |
41 | |
42 | | );
| |________^

View File

@ -122,7 +122,8 @@
"end": 38,
"ctxt": 0
},
"url": "foo.jpg"
"url": "foo.jpg",
"raw": "foo.jpg"
},
{
"type": "UnitValue",

View File

@ -6,18 +6,18 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_stylis"
repository = "https://github.com/swc-project/swc.git"
version = "0.9.0"
version = "0.10.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
swc_atoms = {version = "0.2.7", path = "../../atoms"}
swc_common = {version = "0.13.0", path = "../../common"}
swc_css_ast = {version = "0.11.0", path = "../ast"}
swc_css_utils = {version = "0.8.0", path = "../utils/"}
swc_css_visit = {version = "0.10.0", path = "../visit"}
swc_css_ast = {version = "0.12.0", path = "../ast"}
swc_css_utils = {version = "0.9.0", path = "../utils/"}
swc_css_visit = {version = "0.11.0", path = "../visit"}
[dev-dependencies]
swc_css_codegen = {version = "0.10.0", path = "../codegen"}
swc_css_parser = {version = "0.12.0", path = "../parser"}
swc_css_codegen = {version = "0.11.0", path = "../codegen"}
swc_css_parser = {version = "0.13.0", path = "../parser"}
testing = {version = "0.14.0", path = "../../testing"}

View File

@ -6,11 +6,11 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_css_utils"
repository = "https://github.com/swc-project/swc.git"
version = "0.8.0"
version = "0.9.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
swc_atoms = {version = "0.2.7", path = "../../atoms"}
swc_common = {version = "0.13.0", path = "../../common"}
swc_css_ast = {version = "0.11.0", path = "../ast"}
swc_css_visit = {version = "0.10.0", path = "../visit"}
swc_css_ast = {version = "0.12.0", path = "../ast"}
swc_css_visit = {version = "0.11.0", path = "../visit"}

View File

@ -6,12 +6,12 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_css_visit"
repository = "https://github.com/swc-project/swc.git"
version = "0.10.0"
version = "0.11.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
swc_atoms = {version = "0.2.7", path = "../../atoms"}
swc_common = {version = "0.13.0", path = "../../common"}
swc_css_ast = {version = "0.11.0", path = "../ast/"}
swc_css_ast = {version = "0.12.0", path = "../ast/"}
swc_visit = {version = "0.2.6", path = "../../visit"}

View File

@ -167,8 +167,8 @@ define!({
pub struct UrlValue {
pub span: Span,
pub url: JsWord,
pub raw: JsWord,
}
pub struct ComplexSelector {