javascript - 将 CSS 结构解析为对象
问题描述
我正在尝试解析一些 CSS 的结构以用作 Node.js 中 renderkid 的输入,有点(但不一样)就像Parsing CSS in JavaScript / jQuery一样。
例如,我想要这个 CSS
body { font-size: 10px; }
html { font-size: 11px; }
html, body { font-size: 12px; }
@media only screen and (max-width: 600px) {
body {
background-color: lightblue;
}
}
被解析为这个对象:
{
body: {
"font-size": "12px"
},
html: {
"font-size": "12px"
},
"@media only screen and (max-width: 600px)": {
body: {
"font-size": "12px"
},
}
}
为了避免通过编写 CSS 解析器重新发明轮子,我使用了流行的css
包,它返回提供的 CSS 的 AST,在这种情况下看起来像这样:
{
"type": "stylesheet",
"stylesheet": {
"rules": [{
"type": "rule",
"selectors": ["body"],
"declarations": [{
"type": "declaration",
"property": "font-size",
"value": "10px",
"position": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 1,
"column": 23
}
}
}],
"position": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 26
}
}
}, {
"type": "rule",
"selectors": ["html"],
"declarations": [{
"type": "declaration",
"property": "font-size",
"value": "11px",
"position": {
"start": {
"line": 2,
"column": 8
},
"end": {
"line": 2,
"column": 23
}
}
}],
"position": {
"start": {
"line": 2,
"column": 1
},
"end": {
"line": 2,
"column": 26
}
}
}, {
"type": "rule",
"selectors": ["html", "body"],
"declarations": [{
"type": "declaration",
"property": "font-size",
"value": "12px",
"position": {
"start": {
"line": 3,
"column": 14
},
"end": {
"line": 3,
"column": 29
}
}
}],
"position": {
"start": {
"line": 3,
"column": 1
},
"end": {
"line": 3,
"column": 32
}
}
}, {
"type": "media",
"media": "only screen and (max-width: 600px)",
"rules": [{
"type": "rule",
"selectors": ["body"],
"declarations": [{
"type": "declaration",
"property": "background-color",
"value": "lightblue",
"position": {
"start": {
"line": 6,
"column": 5
},
"end": {
"line": 6,
"column": 32
}
}
}],
"position": {
"start": {
"line": 5,
"column": 3
},
"end": {
"line": 7,
"column": 4
}
}
}],
"position": {
"start": {
"line": 4,
"column": 1
},
"end": {
"line": 8,
"column": 2
}
}
}],
"parsingErrors": []
}
}
目前,我设法想出了这段代码:
"use strict"
const {
parse: parseCSS,
} = require("css")
const _ = require("lodash")
const pickToObject = (array, ...keys) => _.fromPairs(array.map((val) => [val[keys[0]], val[keys[1]]]))
module.exports = (css) => _.merge(...parseCSS(css).stylesheet.rules.map(({
declarations,
selectors,
type,
media,
rules,
}) => {
if (type === "rule") return _.fromPairs(selectors.map((selector) => [selector, pickToObject(declarations, "property", "value")]))
if (type === "media") {
return _.fromPairs([
[`@media ${media}`, _.merge(...rules.map(({
selectors,
declarations,
}) => _.fromPairs(selectors.map((selector) => [selector, pickToObject(declarations, "property", "value")]))))],
])
}
return undefined
}))
但是,我不确定如何进一步优化它。
澄清一下:我需要创建一个可以处理任何有效 CSS 的规范函数——这意味着不能简单地从 AST 中获取值。
解决方案
使用reduce解决:
let styles = {
"type": "stylesheet",
"stylesheet": {
"rules": [{
"type": "rule",
"selectors": ["body"],
"declarations": [{
"type": "declaration",
"property": "background-color",
"value": "black",
"position": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 1,
"column": 32
}
}
}],
"position": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 33
}
}
}],
"parsingErrors": []
}
}
let parsedStyle = styles.stylesheet.rules.reduce((parsed, rule) => {
parsed[rule['selectors']] = rule['declarations'].reduce((rr, r) => {
rr[r['property']] = r['value'];return rr
}, {})
return parsed
}, {})
console.log(parsedStyle)
推荐阅读
- angular - 使用服务 Angular 6 重复 Http 请求
- php - php-fpm 与 libapache2-mod-fcgid 和/或 libapache2-mod-fastcgi 之间的区别
- bazel - 如何在 Bazel v0.19+ 中使用 JAR 工具?
- selenium - 无法访问 org.openqa.selenium.WrapsDriver Selenide + TestContainers
- android - 通过单击从 textview 打开 url
- excel - vba excel到powerpoint ppPasteHTML不起作用
- java - 使用 WebTarget 时指定原始查询字符串
- python - 程序如何在 Python 中同时执行两个动作?
- python - 优化中的多个目标
- tensorflow - 如何解释卷积神经网络中的梯度范数?