|
31 | 31 |
|
32 | 32 | # stdlib |
33 | 33 | from io import TextIOBase |
34 | | -from typing import IO, Any, Dict, Mapping, MutableMapping, Sequence, Union, cast |
| 34 | +from typing import IO, Any, Dict, Iterable, List, Mapping, MutableMapping, Sequence, Union, cast |
35 | 35 |
|
36 | 36 | # 3rd party |
| 37 | +import tinycss2 |
| 38 | +import tinycss2.ast |
37 | 39 | from domdf_python_tools.paths import PathPlus |
38 | 40 | from domdf_python_tools.typing import PathLike |
39 | 41 | from domdf_python_tools.words import TAB |
40 | 42 |
|
41 | | -try: |
42 | | - # 3rd party |
43 | | - import css_parser # type: ignore |
44 | | -except ImportError: # pragma: no cover |
45 | | - import cssutils as css_parser # type: ignore |
46 | | - |
47 | 43 | # this package |
48 | 44 | from dict2css.helpers import em, px, rem # noqa: F401 |
49 | 45 | from dict2css.serializer import CSSSerializer |
@@ -258,44 +254,46 @@ def loads(styles: str) -> MutableMapping[str, MutableMapping[str, Any]]: |
258 | 254 | :return: The style sheet as a dictionary. |
259 | 255 | """ |
260 | 256 |
|
261 | | - parser = css_parser.CSSParser(validate=False) |
262 | | - stylesheet: css_parser.css.CSSStyleSheet = parser.parseString(styles) |
| 257 | + stylesheet = tinycss2.parse_blocks_contents(styles, skip_comments=True, skip_whitespace=True) |
263 | 258 |
|
264 | 259 | styles_dict: MutableMapping[str, MutableMapping[str, Any]] = {} |
265 | 260 |
|
266 | | - def parse_style(style: css_parser.css.CSSStyleDeclaration) -> MutableMapping[str, Property]: |
| 261 | + def parse_style(style: List[tinycss2.ast.Node]) -> MutableMapping[str, Property]: |
267 | 262 | style_dict: Dict[str, Property] = {} |
268 | 263 |
|
269 | | - prop: css_parser.css.Property |
270 | | - for prop in style.children(): |
271 | | - if prop.priority: |
272 | | - style_dict[prop.name] = (prop.value, prop.priority) |
| 264 | + prop: Union[tinycss2.ast.ParseError, tinycss2.ast.Declaration] |
| 265 | + for prop in tinycss2.parse_declaration_list(style, skip_comments=True, skip_whitespace=True): |
| 266 | + if isinstance(prop, tinycss2.ast.ParseError): |
| 267 | + raise ValueError(prop) |
| 268 | + |
| 269 | + if prop.important: |
| 270 | + style_dict[prop.name.strip()] = (_serialize(prop.value), IMPORTANT) |
273 | 271 | else: |
274 | | - style_dict[prop.name] = prop.value |
| 272 | + style_dict[prop.name.strip()] = _serialize(prop.value) |
275 | 273 |
|
276 | 274 | return style_dict |
277 | 275 |
|
278 | | - rule: css_parser.css.CSSRule |
279 | | - for rule in stylesheet.cssRules: |
280 | | - if isinstance(rule, css_parser.css.CSSStyleRule): |
281 | | - styles_dict[rule.selectorText] = parse_style(rule.style) |
282 | | - |
283 | | - elif isinstance(rule, css_parser.css.CSSMediaRule): |
284 | | - styles_dict[f"@media {rule.media.mediaText}"] = {} |
| 276 | + rule: tinycss2.ast.Node |
| 277 | + for rule in stylesheet: |
| 278 | + if isinstance(rule, tinycss2.ast.QualifiedRule): |
| 279 | + styles_dict[_serialize(rule.prelude)] = parse_style(rule.content) |
285 | 280 |
|
286 | | - for child in rule.cssRules: |
287 | | - styles_dict[f"@media {rule.media.mediaText}"][child.selectorText] = parse_style(child.style) |
| 281 | + elif isinstance(rule, tinycss2.ast.AtRule): |
| 282 | + at_rule_styles = styles_dict[f"@{rule.at_keyword} {_serialize(rule.prelude)}"] = at_rule_styles |
288 | 283 |
|
289 | | - elif isinstance(rule, (css_parser.css.CSSComment)): # pragma: no cover |
290 | | - # Ignore these classes |
291 | | - pass |
| 284 | + for child in tinycss2.parse_blocks_contents(rule.content, skip_comments=True, skip_whitespace=True): |
| 285 | + at_rule_styles[_serialize(child.prelude)] = parse_style(child.content) |
292 | 286 |
|
293 | 287 | else: |
294 | 288 | raise NotImplementedError(rule) |
295 | 289 |
|
296 | 290 | return styles_dict |
297 | 291 |
|
298 | 292 |
|
| 293 | +def _serialize(nodes: Iterable[tinycss2.ast.Node]): |
| 294 | + return tinycss2.serialize(nodes).strip() |
| 295 | + |
| 296 | + |
299 | 297 | def load(fp: Union[PathLike, IO]) -> MutableMapping[str, MutableMapping[str, Any]]: |
300 | 298 | r""" |
301 | 299 | Parse a cascading style sheet from the given file and return its dictionary representation. |
|
0 commit comments