feat(css/parser): Improve parsing of urls (#3362)

This commit is contained in:
Alexander Akait 2022-01-26 07:45:49 +03:00 committed by GitHub
parent ac2bb9b7bd
commit 50521d8ffa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1080 additions and 32 deletions

View File

@ -2,6 +2,7 @@ use crate::{
error::{Error, ErrorKind},
parser::{input::ParserInput, PResult, ParserConfig},
};
use std::char::REPLACEMENT_CHARACTER;
use swc_atoms::{js_word, JsWord};
use swc_common::{input::Input, BytePos, Span};
use swc_css_ast::{NumberType, Token, TokenAndSpan};
@ -700,22 +701,22 @@ where
// whitespace
Some(c) if is_whitespace(c) => {
let start_pos = self.input.cur_pos();
let mut whitespaces = String::new();
whitespaces.push(c);
// Consume as much whitespace as possible.
while let Some(c) = self.next() {
if is_whitespace(c) {
self.consume();
whitespaces.push(c);
} else {
break;
}
}
let end_pos = self.input.cur_pos();
raw.push(c);
// TODO: fix me
raw.push_str(self.input.slice(start_pos, end_pos));
raw.push_str(&whitespaces);
// if the next input code point is U+0029 RIGHT PARENTHESIS ()) or EOF, consume
// it and return the <url-token> (if EOF was encountered, this is a parse
@ -742,6 +743,8 @@ where
_ => {}
}
value.push_str(&whitespaces);
// otherwise, consume the remnants of a bad url, create a <bad-url-token>, and
// return it.
let remnants = self.read_bad_url_remnants()?;
@ -749,7 +752,6 @@ where
value.push_str(&remnants.0);
raw.push_str(&remnants.1);
// TODO check me
return Ok(Token::BadUrl {
name: name.0,
raw_name: name.1,
@ -865,8 +867,15 @@ where
// Interpret the hex digits as a hexadecimal number. If this number is zero, or
// is for a surrogate, or is greater than the maximum allowed code point, return
// U+FFFD REPLACEMENT CHARACTER (<28>).
// TODO: fix me
let hex = char::from_u32(hex).ok_or(ErrorKind::InvalidEscape)?;
let hex = match hex {
// If this number is zero
0 => REPLACEMENT_CHARACTER,
// or is for a surrogate
55296..=57343 => REPLACEMENT_CHARACTER,
// or is greater than the maximum allowed code point
1114112.. => REPLACEMENT_CHARACTER,
_ => char::from_u32(hex).unwrap_or_else(|| REPLACEMENT_CHARACTER),
};
// Otherwise, return the code point with that value.
Ok((hex, raw))
@ -874,7 +883,7 @@ where
// EOF
// This is a parse error. Return U+FFFD REPLACEMENT CHARACTER (<28>).
None => {
let value = '\u{FFFD}';
let value = REPLACEMENT_CHARACTER;
raw.push(value);

View File

@ -95,7 +95,7 @@
"end": 15,
"ctxt": 0
},
"value": "x\u0000",
"value": "x<EFBFBD>",
"raw": "x\\0 "
}
],

View File

@ -95,7 +95,7 @@
"end": 15,
"ctxt": 0
},
"value": "\u0000",
"value": "<EFBFBD>",
"raw": "'\\0'"
}
],

View File

@ -36,7 +36,7 @@
"end": 26,
"ctxt": 0
},
"value": "\u0000screen,screen\t",
"value": "<EFBFBD>screen,screen\t",
"raw": "\\0screen\\,screen\\9 "
},
"condition": null

View File

@ -40,4 +40,9 @@ a {
);
background: url(image.png\0001);
background: url(image.png\1);
background: url(image.png\D799);
background: url(image.png\E000);
background: url(image.png\10FFFF);
}

View File

@ -2,7 +2,7 @@
"type": "Stylesheet",
"span": {
"start": 0,
"end": 1043,
"end": 1227,
"ctxt": 0
},
"rules": [
@ -10,7 +10,7 @@
"type": "QualifiedRule",
"span": {
"start": 0,
"end": 1042,
"end": 1226,
"ctxt": 0
},
"prelude": {
@ -66,7 +66,7 @@
"type": "Block",
"span": {
"start": 2,
"end": 1042,
"end": 1226,
"ctxt": 0
},
"value": [
@ -1119,6 +1119,256 @@
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 1045,
"end": 1076,
"ctxt": 0
},
"property": {
"type": "Identifier",
"span": {
"start": 1045,
"end": 1055,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Url",
"span": {
"start": 1057,
"end": 1076,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 1057,
"end": 1060,
"ctxt": 0
},
"value": "url",
"raw": "url"
},
"value": {
"type": "UrlValueRaw",
"span": {
"start": 1061,
"end": 1075,
"ctxt": 0
},
"value": "image.png\u0001",
"raw": "image.png\\0001"
},
"modifiers": null
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 1082,
"end": 1110,
"ctxt": 0
},
"property": {
"type": "Identifier",
"span": {
"start": 1082,
"end": 1092,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Url",
"span": {
"start": 1094,
"end": 1110,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 1094,
"end": 1097,
"ctxt": 0
},
"value": "url",
"raw": "url"
},
"value": {
"type": "UrlValueRaw",
"span": {
"start": 1098,
"end": 1109,
"ctxt": 0
},
"value": "image.png\u0001",
"raw": "image.png\\1"
},
"modifiers": null
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 1116,
"end": 1147,
"ctxt": 0
},
"property": {
"type": "Identifier",
"span": {
"start": 1116,
"end": 1126,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Url",
"span": {
"start": 1128,
"end": 1147,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 1128,
"end": 1131,
"ctxt": 0
},
"value": "url",
"raw": "url"
},
"value": {
"type": "UrlValueRaw",
"span": {
"start": 1132,
"end": 1146,
"ctxt": 0
},
"value": "image.png힙",
"raw": "image.png\\D799"
},
"modifiers": null
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 1153,
"end": 1184,
"ctxt": 0
},
"property": {
"type": "Identifier",
"span": {
"start": 1153,
"end": 1163,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Url",
"span": {
"start": 1165,
"end": 1184,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 1165,
"end": 1168,
"ctxt": 0
},
"value": "url",
"raw": "url"
},
"value": {
"type": "UrlValueRaw",
"span": {
"start": 1169,
"end": 1183,
"ctxt": 0
},
"value": "image.png",
"raw": "image.png\\E000"
},
"modifiers": null
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 1190,
"end": 1223,
"ctxt": 0
},
"property": {
"type": "Identifier",
"span": {
"start": 1190,
"end": 1200,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Url",
"span": {
"start": 1202,
"end": 1223,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 1202,
"end": 1205,
"ctxt": 0
},
"value": "url",
"raw": "url"
},
"value": {
"type": "UrlValueRaw",
"span": {
"start": 1206,
"end": 1222,
"ctxt": 0
},
"value": "image.png􏿿",
"raw": "image.png\\10FFFF"
},
"modifiers": null
}
],
"important": null
}
]
}

View File

@ -6,8 +6,8 @@ error: Stylesheet
3 | | background-image: url("./image (1).jpg");
4 | | background-image: url('./image (1).jpg');
... |
42 | | );
43 | | }
47 | | background: url(image.png\10FFFF);
48 | | }
| |__^
error: Rule
@ -18,8 +18,8 @@ error: Rule
3 | | background-image: url("./image (1).jpg");
4 | | background-image: url('./image (1).jpg');
... |
42 | | );
43 | | }
47 | | background: url(image.png\10FFFF);
48 | | }
| |_^
error: QualifiedRule
@ -30,8 +30,8 @@ error: QualifiedRule
3 | | background-image: url("./image (1).jpg");
4 | | background-image: url('./image (1).jpg');
... |
42 | | );
43 | | }
47 | | background: url(image.png\10FFFF);
48 | | }
| |_^
error: SelectorList
@ -73,8 +73,8 @@ error: Block
3 | | background-image: url("./image (1).jpg");
4 | | background-image: url('./image (1).jpg');
... |
42 | | );
43 | | }
47 | | background: url(image.png\10FFFF);
48 | | }
| |_^
error: Declaration
@ -1049,3 +1049,213 @@ error: UrlValueRaw
42 | | );
| |_______^
error: Declaration
--> $DIR/tests/fixture/function/url/input.css:43:5
|
43 | background: url(image.png\0001);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: Ident
--> $DIR/tests/fixture/function/url/input.css:43:5
|
43 | background: url(image.png\0001);
| ^^^^^^^^^^
error: Value
--> $DIR/tests/fixture/function/url/input.css:43:17
|
43 | background: url(image.png\0001);
| ^^^^^^^^^^^^^^^^^^^
error: Url
--> $DIR/tests/fixture/function/url/input.css:43:17
|
43 | background: url(image.png\0001);
| ^^^^^^^^^^^^^^^^^^^
error: Ident
--> $DIR/tests/fixture/function/url/input.css:43:17
|
43 | background: url(image.png\0001);
| ^^^
error: UrlValue
--> $DIR/tests/fixture/function/url/input.css:43:21
|
43 | background: url(image.png\0001);
| ^^^^^^^^^^^^^^
error: UrlValueRaw
--> $DIR/tests/fixture/function/url/input.css:43:21
|
43 | background: url(image.png\0001);
| ^^^^^^^^^^^^^^
error: Declaration
--> $DIR/tests/fixture/function/url/input.css:44:5
|
44 | background: url(image.png\1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: Ident
--> $DIR/tests/fixture/function/url/input.css:44:5
|
44 | background: url(image.png\1);
| ^^^^^^^^^^
error: Value
--> $DIR/tests/fixture/function/url/input.css:44:17
|
44 | background: url(image.png\1);
| ^^^^^^^^^^^^^^^^
error: Url
--> $DIR/tests/fixture/function/url/input.css:44:17
|
44 | background: url(image.png\1);
| ^^^^^^^^^^^^^^^^
error: Ident
--> $DIR/tests/fixture/function/url/input.css:44:17
|
44 | background: url(image.png\1);
| ^^^
error: UrlValue
--> $DIR/tests/fixture/function/url/input.css:44:21
|
44 | background: url(image.png\1);
| ^^^^^^^^^^^
error: UrlValueRaw
--> $DIR/tests/fixture/function/url/input.css:44:21
|
44 | background: url(image.png\1);
| ^^^^^^^^^^^
error: Declaration
--> $DIR/tests/fixture/function/url/input.css:45:5
|
45 | background: url(image.png\D799);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: Ident
--> $DIR/tests/fixture/function/url/input.css:45:5
|
45 | background: url(image.png\D799);
| ^^^^^^^^^^
error: Value
--> $DIR/tests/fixture/function/url/input.css:45:17
|
45 | background: url(image.png\D799);
| ^^^^^^^^^^^^^^^^^^^
error: Url
--> $DIR/tests/fixture/function/url/input.css:45:17
|
45 | background: url(image.png\D799);
| ^^^^^^^^^^^^^^^^^^^
error: Ident
--> $DIR/tests/fixture/function/url/input.css:45:17
|
45 | background: url(image.png\D799);
| ^^^
error: UrlValue
--> $DIR/tests/fixture/function/url/input.css:45:21
|
45 | background: url(image.png\D799);
| ^^^^^^^^^^^^^^
error: UrlValueRaw
--> $DIR/tests/fixture/function/url/input.css:45:21
|
45 | background: url(image.png\D799);
| ^^^^^^^^^^^^^^
error: Declaration
--> $DIR/tests/fixture/function/url/input.css:46:5
|
46 | background: url(image.png\E000);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: Ident
--> $DIR/tests/fixture/function/url/input.css:46:5
|
46 | background: url(image.png\E000);
| ^^^^^^^^^^
error: Value
--> $DIR/tests/fixture/function/url/input.css:46:17
|
46 | background: url(image.png\E000);
| ^^^^^^^^^^^^^^^^^^^
error: Url
--> $DIR/tests/fixture/function/url/input.css:46:17
|
46 | background: url(image.png\E000);
| ^^^^^^^^^^^^^^^^^^^
error: Ident
--> $DIR/tests/fixture/function/url/input.css:46:17
|
46 | background: url(image.png\E000);
| ^^^
error: UrlValue
--> $DIR/tests/fixture/function/url/input.css:46:21
|
46 | background: url(image.png\E000);
| ^^^^^^^^^^^^^^
error: UrlValueRaw
--> $DIR/tests/fixture/function/url/input.css:46:21
|
46 | background: url(image.png\E000);
| ^^^^^^^^^^^^^^
error: Declaration
--> $DIR/tests/fixture/function/url/input.css:47:5
|
47 | background: url(image.png\10FFFF);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: Ident
--> $DIR/tests/fixture/function/url/input.css:47:5
|
47 | background: url(image.png\10FFFF);
| ^^^^^^^^^^
error: Value
--> $DIR/tests/fixture/function/url/input.css:47:17
|
47 | background: url(image.png\10FFFF);
| ^^^^^^^^^^^^^^^^^^^^^
error: Url
--> $DIR/tests/fixture/function/url/input.css:47:17
|
47 | background: url(image.png\10FFFF);
| ^^^^^^^^^^^^^^^^^^^^^
error: Ident
--> $DIR/tests/fixture/function/url/input.css:47:17
|
47 | background: url(image.png\10FFFF);
| ^^^
error: UrlValue
--> $DIR/tests/fixture/function/url/input.css:47:21
|
47 | background: url(image.png\10FFFF);
| ^^^^^^^^^^^^^^^^
error: UrlValueRaw
--> $DIR/tests/fixture/function/url/input.css:47:21
|
47 | background: url(image.png\10FFFF);
| ^^^^^^^^^^^^^^^^

View File

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

View File

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

View File

@ -0,0 +1,135 @@
{
"type": "Stylesheet",
"span": {
"start": 0,
"end": 46,
"ctxt": 0
},
"rules": [
{
"type": "QualifiedRule",
"span": {
"start": 0,
"end": 45,
"ctxt": 0
},
"prelude": {
"type": "SelectorList",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"children": [
{
"type": "ComplexSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"children": [
{
"type": "CompoundSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": {
"type": "TypeSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"prefix": null,
"name": {
"type": "Identifier",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"value": "div",
"raw": "div"
}
},
"subclassSelectors": []
}
]
}
]
},
"block": {
"type": "Block",
"span": {
"start": 4,
"end": 45,
"ctxt": 0
},
"value": [
{
"type": "Tokens",
"span": {
"start": 10,
"end": 42,
"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": {
"value": " "
}
}
},
{
"span": {
"start": 22,
"end": 42,
"ctxt": 0
},
"token": {
"BadUrl": {
"name": "url",
"raw_name": "url",
"value": "image.png\\\n ",
"raw_value": "image.png\\\n "
}
}
}
]
}
]
}
}
]
}

View File

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

View File

@ -0,0 +1,3 @@
a {
background-image: url( ./image.jpg a );
}

View File

@ -0,0 +1,135 @@
{
"type": "Stylesheet",
"span": {
"start": 0,
"end": 54,
"ctxt": 0
},
"rules": [
{
"type": "QualifiedRule",
"span": {
"start": 0,
"end": 53,
"ctxt": 0
},
"prelude": {
"type": "SelectorList",
"span": {
"start": 0,
"end": 1,
"ctxt": 0
},
"children": [
{
"type": "ComplexSelector",
"span": {
"start": 0,
"end": 1,
"ctxt": 0
},
"children": [
{
"type": "CompoundSelector",
"span": {
"start": 0,
"end": 1,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": {
"type": "TypeSelector",
"span": {
"start": 0,
"end": 1,
"ctxt": 0
},
"prefix": null,
"name": {
"type": "Identifier",
"span": {
"start": 0,
"end": 1,
"ctxt": 0
},
"value": "a",
"raw": "a"
}
},
"subclassSelectors": []
}
]
}
]
},
"block": {
"type": "Block",
"span": {
"start": 2,
"end": 53,
"ctxt": 0
},
"value": [
{
"type": "Tokens",
"span": {
"start": 8,
"end": 50,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 8,
"end": 24,
"ctxt": 0
},
"token": {
"Ident": {
"value": "background-image",
"raw": "background-image"
}
}
},
{
"span": {
"start": 24,
"end": 25,
"ctxt": 0
},
"token": "Colon"
},
{
"span": {
"start": 25,
"end": 26,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
},
{
"span": {
"start": 26,
"end": 50,
"ctxt": 0
},
"token": {
"BadUrl": {
"name": "url",
"raw_name": "url",
"value": "./image.jpg a ",
"raw_value": "./image.jpg a "
}
}
}
]
}
]
}
}
]
}

View File

@ -0,0 +1,6 @@
error: Expected Declaration value
--> $DIR/tests/recovery/bad-url-token/whitespace-in-middle/input.css:2:23
|
2 | background-image: url( ./image.jpg a );
| ^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -121,7 +121,7 @@
"BadUrl": {
"name": "url",
"raw_name": "url",
"value": "image.png",
"value": "image.\n png",
"raw_value": "image.\n png"
}
}

View File

@ -1,4 +1,10 @@
div {
--foo: "http://www.example.com/pinkish.gif";
background: url(var(--foo));
background: url(image.png\999999);
background: url(image.png\0);
background: url(image.png\D800);
background: url(image.png\DFFF);
background: url(image.png\11FFFF);
color: red;
}

View File

@ -2,7 +2,7 @@
"type": "Stylesheet",
"span": {
"start": 0,
"end": 90,
"end": 292,
"ctxt": 0
},
"rules": [
@ -10,7 +10,7 @@
"type": "QualifiedRule",
"span": {
"start": 0,
"end": 89,
"end": 291,
"ctxt": 0
},
"prelude": {
@ -66,7 +66,7 @@
"type": "Block",
"span": {
"start": 4,
"end": 89,
"end": 291,
"ctxt": 0
},
"value": [
@ -191,6 +191,287 @@
"token": "RParen"
}
]
},
{
"type": "Declaration",
"span": {
"start": 92,
"end": 125,
"ctxt": 0
},
"property": {
"type": "Identifier",
"span": {
"start": 92,
"end": 102,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Url",
"span": {
"start": 104,
"end": 125,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 104,
"end": 107,
"ctxt": 0
},
"value": "url",
"raw": "url"
},
"value": {
"type": "UrlValueRaw",
"span": {
"start": 108,
"end": 124,
"ctxt": 0
},
"value": "image.png<6E>",
"raw": "image.png\\999999"
},
"modifiers": null
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 131,
"end": 159,
"ctxt": 0
},
"property": {
"type": "Identifier",
"span": {
"start": 131,
"end": 141,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Url",
"span": {
"start": 143,
"end": 159,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 143,
"end": 146,
"ctxt": 0
},
"value": "url",
"raw": "url"
},
"value": {
"type": "UrlValueRaw",
"span": {
"start": 147,
"end": 158,
"ctxt": 0
},
"value": "image.png<6E>",
"raw": "image.png\\0"
},
"modifiers": null
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 165,
"end": 196,
"ctxt": 0
},
"property": {
"type": "Identifier",
"span": {
"start": 165,
"end": 175,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Url",
"span": {
"start": 177,
"end": 196,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 177,
"end": 180,
"ctxt": 0
},
"value": "url",
"raw": "url"
},
"value": {
"type": "UrlValueRaw",
"span": {
"start": 181,
"end": 195,
"ctxt": 0
},
"value": "image.png<6E>",
"raw": "image.png\\D800"
},
"modifiers": null
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 202,
"end": 233,
"ctxt": 0
},
"property": {
"type": "Identifier",
"span": {
"start": 202,
"end": 212,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Url",
"span": {
"start": 214,
"end": 233,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 214,
"end": 217,
"ctxt": 0
},
"value": "url",
"raw": "url"
},
"value": {
"type": "UrlValueRaw",
"span": {
"start": 218,
"end": 232,
"ctxt": 0
},
"value": "image.png<6E>",
"raw": "image.png\\DFFF"
},
"modifiers": null
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 239,
"end": 272,
"ctxt": 0
},
"property": {
"type": "Identifier",
"span": {
"start": 239,
"end": 249,
"ctxt": 0
},
"value": "background",
"raw": "background"
},
"value": [
{
"type": "Url",
"span": {
"start": 251,
"end": 272,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 251,
"end": 254,
"ctxt": 0
},
"value": "url",
"raw": "url"
},
"value": {
"type": "UrlValueRaw",
"span": {
"start": 255,
"end": 271,
"ctxt": 0
},
"value": "image.png<6E>",
"raw": "image.png\\11FFFF"
},
"modifiers": null
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 278,
"end": 288,
"ctxt": 0
},
"property": {
"type": "Identifier",
"span": {
"start": 278,
"end": 283,
"ctxt": 0
},
"value": "color",
"raw": "color"
},
"value": [
{
"type": "Identifier",
"span": {
"start": 285,
"end": 288,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
}
]
}