feat(css): Add BadUrl token (#2426)

swc_css_ast:
 - Add `BadUrl` to `Token`.

swc_css_parser:
 - Implement error recovery for bad urls.
This commit is contained in:
Alexander Akait 2021-10-15 16:36:54 +03:00 committed by GitHub
parent dcf42771ad
commit c2ce89c0fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 768 additions and 45 deletions

14
Cargo.lock generated
View File

@ -2500,7 +2500,7 @@ dependencies = [
[[package]]
name = "swc_css"
version = "0.16.0"
version = "0.17.0"
dependencies = [
"swc_css_ast",
"swc_css_codegen",
@ -2511,7 +2511,7 @@ dependencies = [
[[package]]
name = "swc_css_ast"
version = "0.15.0"
version = "0.16.0"
dependencies = [
"is-macro",
"serde",
@ -2522,7 +2522,7 @@ dependencies = [
[[package]]
name = "swc_css_codegen"
version = "0.14.0"
version = "0.15.0"
dependencies = [
"auto_impl",
"bitflags",
@ -2548,7 +2548,7 @@ dependencies = [
[[package]]
name = "swc_css_parser"
version = "0.16.0"
version = "0.17.0"
dependencies = [
"bitflags",
"lexical",
@ -2564,7 +2564,7 @@ dependencies = [
[[package]]
name = "swc_css_utils"
version = "0.12.0"
version = "0.13.0"
dependencies = [
"swc_atoms 0.2.8",
"swc_common",
@ -2574,7 +2574,7 @@ dependencies = [
[[package]]
name = "swc_css_visit"
version = "0.14.0"
version = "0.15.0"
dependencies = [
"swc_atoms 0.2.8",
"swc_common",
@ -3142,7 +3142,7 @@ dependencies = [
[[package]]
name = "swc_stylis"
version = "0.13.0"
version = "0.14.0"
dependencies = [
"swc_atoms 0.2.8",
"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.16.0"
version = "0.17.0"
[dependencies]
swc_css_ast = {version = "0.15.0", path = "./ast"}
swc_css_codegen = {version = "0.14.0", path = "./codegen"}
swc_css_parser = {version = "0.16.0", path = "./parser"}
swc_css_utils = {version = "0.12.0", path = "./utils/"}
swc_css_visit = {version = "0.14.0", path = "./visit"}
swc_css_ast = {version = "0.16.0", path = "./ast"}
swc_css_codegen = {version = "0.15.0", path = "./codegen"}
swc_css_parser = {version = "0.17.0", path = "./parser"}
swc_css_utils = {version = "0.13.0", path = "./utils/"}
swc_css_visit = {version = "0.15.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.15.0"
version = "0.16.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -52,7 +52,11 @@ pub enum Token {
raw: JsWord,
},
// TODO BadUrl
BadUrl {
value: JsWord,
raw: JsWord,
},
Delim {
value: char,
},

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.14.0"
version = "0.15.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.15.0", path = "../ast/"}
swc_css_ast = {version = "0.16.0", path = "../ast/"}
swc_css_codegen_macros = {version = "0.2.0", path = "macros/"}
[dev-dependencies]
swc_css_parser = {version = "0.16.0", path = "../parser"}
swc_css_visit = {version = "0.14.0", path = "../visit"}
swc_css_parser = {version = "0.17.0", path = "../parser"}
swc_css_visit = {version = "0.15.0", path = "../visit"}
testing = {version = "0.14.0", path = "../../testing"}

View File

@ -512,6 +512,12 @@ where
self.wr.write_raw(None, &raw)?;
punct!(self, ")");
}
Token::BadUrl { raw, .. } => {
self.wr.write_ident(Some(span), "url", false)?;
punct!(self, "(");
self.wr.write_raw(None, &raw)?;
punct!(self, ")");
}
Token::Comma => {
punct!(self, span, ",");
}

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.16.0"
version = "0.17.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.15.0", path = "../ast"}
swc_css_ast = {version = "0.16.0", path = "../ast"}
unicode-xid = "0.2.2"
[dev-dependencies]
serde = "1.0.127"
serde_json = "1.0.66"
swc_css_visit = {version = "0.14.0", path = "../visit"}
swc_css_visit = {version = "0.15.0", path = "../visit"}
testing = {version = "0.14.0", path = "../../testing"}

View File

@ -450,6 +450,39 @@ where
})
}
fn read_bad_url_remnants(&mut self) -> LexResult<(String, String)> {
let mut value = String::new();
let mut raw = String::new();
loop {
match self.input.cur() {
Some(c) if c == ')' => {
self.input.bump();
break;
}
Some(c) => {
if self.is_valid_escape().unwrap() {
let escaped = self.read_escape()?;
value.push(escaped.0);
raw.push_str(&escaped.1);
} else {
value.push(c);
raw.push(c);
self.input.bump();
}
}
None => {
break;
}
}
}
return Ok((value, raw));
}
fn read_url(&mut self) -> LexResult<Token> {
let mut value = String::new();
let mut raw = String::new();
@ -503,25 +536,46 @@ where
_ => {}
}
// TODO: break + Error recovery + 4.3.14. Consume the remnants of a bad url
return Err(ErrorKind::UnterminatedUrl);
let remnants = self.read_bad_url_remnants()?;
value.push_str(&remnants.0);
raw.push_str(&remnants.1);
return Ok(Token::BadUrl {
value: value.into(),
raw: raw.into(),
});
}
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);
let remnants = self.read_bad_url_remnants()?;
value.push_str(&remnants.0);
raw.push_str(&remnants.1);
return Ok(Token::BadUrl {
value: value.into(),
raw: raw.into(),
});
}
Some('\\') => {
if !self.is_valid_escape()? {
// TODO: break + Error recovery + 4.3.14. Consume the remnants of a bad url
return Err(ErrorKind::InvalidEscape);
if self.is_valid_escape()? {
let escaped = self.read_escape()?;
value.push(escaped.0);
raw.push_str(&escaped.1);
} else {
let remnants = self.read_bad_url_remnants()?;
value.push_str(&remnants.0);
raw.push_str(&remnants.1);
return Ok(Token::BadUrl {
value: value.into(),
raw: raw.into(),
});
}
let escaped = self.read_escape()?;
value.push(escaped.0);
raw.push_str(&escaped.1);
}
Some(c) => {

View File

@ -0,0 +1,4 @@
div {
background: url(image.png\999999);
color: red;
}

View File

@ -0,0 +1,4 @@
div {
background: url(image".png);
color: red;
}

View File

@ -0,0 +1,153 @@
{
"type": "Stylesheet",
"span": {
"start": 0,
"end": 57,
"ctxt": 0
},
"rules": [
{
"type": "StyleRule",
"span": {
"start": 0,
"end": 56,
"ctxt": 0
},
"selectors": [
{
"type": "ComplexSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"selectors": [
{
"type": "CompoundSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"hasNestPrefix": false,
"combinator": null,
"typeSelector": {
"type": "NamespacedName",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"prefix": null,
"name": {
"type": "Text",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"value": "div",
"raw": "div"
}
},
"subclassSelectors": []
}
]
}
],
"block": {
"type": "DeclBlock",
"span": {
"start": 4,
"end": 56,
"ctxt": 0
},
"items": [
{
"type": "Tokens",
"span": {
"start": 10,
"end": 37,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 10,
"end": 20,
"ctxt": 0
},
"token": {
"Ident": {
"value": "background",
"raw": "background"
}
}
},
{
"span": {
"start": 20,
"end": 21,
"ctxt": 0
},
"token": "Colon"
},
{
"span": {
"start": 21,
"end": 22,
"ctxt": 0
},
"token": "WhiteSpace"
},
{
"span": {
"start": 22,
"end": 37,
"ctxt": 0
},
"token": {
"BadUrl": {
"value": "image\".png",
"raw": "image\".png"
}
}
}
]
},
{
"type": "Property",
"span": {
"start": 43,
"end": 53,
"ctxt": 0
},
"name": {
"type": "Text",
"span": {
"start": 43,
"end": 48,
"ctxt": 0
},
"value": "color",
"raw": "color"
},
"values": [
{
"type": "Text",
"span": {
"start": 50,
"end": 53,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
}
]
}
}
]
}

View File

@ -0,0 +1,6 @@
error: Expected Property value
--> $DIR/tests/recovery/bad-url-token/double-quotes/input.css:2:17
|
2 | background: url(image".png);
| ^^^^^^^^^^^^^^^

View File

@ -0,0 +1,4 @@
div {
background: url(image(.png);
color: red;
}

View File

@ -0,0 +1,153 @@
{
"type": "Stylesheet",
"span": {
"start": 0,
"end": 57,
"ctxt": 0
},
"rules": [
{
"type": "StyleRule",
"span": {
"start": 0,
"end": 56,
"ctxt": 0
},
"selectors": [
{
"type": "ComplexSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"selectors": [
{
"type": "CompoundSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"hasNestPrefix": false,
"combinator": null,
"typeSelector": {
"type": "NamespacedName",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"prefix": null,
"name": {
"type": "Text",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"value": "div",
"raw": "div"
}
},
"subclassSelectors": []
}
]
}
],
"block": {
"type": "DeclBlock",
"span": {
"start": 4,
"end": 56,
"ctxt": 0
},
"items": [
{
"type": "Tokens",
"span": {
"start": 10,
"end": 37,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 10,
"end": 20,
"ctxt": 0
},
"token": {
"Ident": {
"value": "background",
"raw": "background"
}
}
},
{
"span": {
"start": 20,
"end": 21,
"ctxt": 0
},
"token": "Colon"
},
{
"span": {
"start": 21,
"end": 22,
"ctxt": 0
},
"token": "WhiteSpace"
},
{
"span": {
"start": 22,
"end": 37,
"ctxt": 0
},
"token": {
"BadUrl": {
"value": "image(.png",
"raw": "image(.png"
}
}
}
]
},
{
"type": "Property",
"span": {
"start": 43,
"end": 53,
"ctxt": 0
},
"name": {
"type": "Text",
"span": {
"start": 43,
"end": 48,
"ctxt": 0
},
"value": "color",
"raw": "color"
},
"values": [
{
"type": "Text",
"span": {
"start": 50,
"end": 53,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
}
]
}
}
]
}

View File

@ -0,0 +1,6 @@
error: Expected Property value
--> $DIR/tests/recovery/bad-url-token/left-parenthesis/input.css:2:17
|
2 | background: url(image(.png);
| ^^^^^^^^^^^^^^^

View File

@ -0,0 +1,4 @@
div {
background: url(image'.png);
color: red;
}

View File

@ -0,0 +1,153 @@
{
"type": "Stylesheet",
"span": {
"start": 0,
"end": 57,
"ctxt": 0
},
"rules": [
{
"type": "StyleRule",
"span": {
"start": 0,
"end": 56,
"ctxt": 0
},
"selectors": [
{
"type": "ComplexSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"selectors": [
{
"type": "CompoundSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"hasNestPrefix": false,
"combinator": null,
"typeSelector": {
"type": "NamespacedName",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"prefix": null,
"name": {
"type": "Text",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"value": "div",
"raw": "div"
}
},
"subclassSelectors": []
}
]
}
],
"block": {
"type": "DeclBlock",
"span": {
"start": 4,
"end": 56,
"ctxt": 0
},
"items": [
{
"type": "Tokens",
"span": {
"start": 10,
"end": 37,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 10,
"end": 20,
"ctxt": 0
},
"token": {
"Ident": {
"value": "background",
"raw": "background"
}
}
},
{
"span": {
"start": 20,
"end": 21,
"ctxt": 0
},
"token": "Colon"
},
{
"span": {
"start": 21,
"end": 22,
"ctxt": 0
},
"token": "WhiteSpace"
},
{
"span": {
"start": 22,
"end": 37,
"ctxt": 0
},
"token": {
"BadUrl": {
"value": "image'.png",
"raw": "image'.png"
}
}
}
]
},
{
"type": "Property",
"span": {
"start": 43,
"end": 53,
"ctxt": 0
},
"name": {
"type": "Text",
"span": {
"start": 43,
"end": 48,
"ctxt": 0
},
"value": "color",
"raw": "color"
},
"values": [
{
"type": "Text",
"span": {
"start": 50,
"end": 53,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
}
]
}
}
]
}

View File

@ -0,0 +1,6 @@
error: Expected Property value
--> $DIR/tests/recovery/bad-url-token/single-quotes/input.css:2:17
|
2 | background: url(image'.png);
| ^^^^^^^^^^^^^^^

View File

@ -0,0 +1,5 @@
div {
background: url(image.
png);
color: red;
}

View File

@ -0,0 +1,153 @@
{
"type": "Stylesheet",
"span": {
"start": 0,
"end": 61,
"ctxt": 0
},
"rules": [
{
"type": "StyleRule",
"span": {
"start": 0,
"end": 60,
"ctxt": 0
},
"selectors": [
{
"type": "ComplexSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"selectors": [
{
"type": "CompoundSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"hasNestPrefix": false,
"combinator": null,
"typeSelector": {
"type": "NamespacedName",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"prefix": null,
"name": {
"type": "Text",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"value": "div",
"raw": "div"
}
},
"subclassSelectors": []
}
]
}
],
"block": {
"type": "DeclBlock",
"span": {
"start": 4,
"end": 60,
"ctxt": 0
},
"items": [
{
"type": "Tokens",
"span": {
"start": 10,
"end": 41,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 10,
"end": 20,
"ctxt": 0
},
"token": {
"Ident": {
"value": "background",
"raw": "background"
}
}
},
{
"span": {
"start": 20,
"end": 21,
"ctxt": 0
},
"token": "Colon"
},
{
"span": {
"start": 21,
"end": 22,
"ctxt": 0
},
"token": "WhiteSpace"
},
{
"span": {
"start": 22,
"end": 41,
"ctxt": 0
},
"token": {
"BadUrl": {
"value": "image.png",
"raw": "image.\n png"
}
}
}
]
},
{
"type": "Property",
"span": {
"start": 47,
"end": 57,
"ctxt": 0
},
"name": {
"type": "Text",
"span": {
"start": 47,
"end": 52,
"ctxt": 0
},
"value": "color",
"raw": "color"
},
"values": [
{
"type": "Text",
"span": {
"start": 54,
"end": 57,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
}
]
}
}
]
}

View File

@ -0,0 +1,8 @@
error: Expected Property value
--> $DIR/tests/recovery/bad-url-token/whitespace/input.css:2:17
|
2 | background: url(image.
| _________________^
3 | | png);
| |________^

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.13.0"
version = "0.14.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.15.0", path = "../ast"}
swc_css_utils = {version = "0.12.0", path = "../utils/"}
swc_css_visit = {version = "0.14.0", path = "../visit"}
swc_css_ast = {version = "0.16.0", path = "../ast"}
swc_css_utils = {version = "0.13.0", path = "../utils/"}
swc_css_visit = {version = "0.15.0", path = "../visit"}
[dev-dependencies]
swc_css_codegen = {version = "0.14.0", path = "../codegen"}
swc_css_parser = {version = "0.16.0", path = "../parser"}
swc_css_codegen = {version = "0.15.0", path = "../codegen"}
swc_css_parser = {version = "0.17.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.12.0"
version = "0.13.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.15.0", path = "../ast"}
swc_css_visit = {version = "0.14.0", path = "../visit"}
swc_css_ast = {version = "0.16.0", path = "../ast"}
swc_css_visit = {version = "0.15.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.14.0"
version = "0.15.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.15.0", path = "../ast/"}
swc_css_ast = {version = "0.16.0", path = "../ast/"}
swc_visit = {version = "0.2.6", path = "../../visit"}