将 CSS 结构解析为对象



我正在尝试解析一些CSS的结构,以用作Node.js中renderkid的输入,有点(但与(在JavaScript/jQuery中解析CSS一样。

例如,我想要这个 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 中获取值不是一种选择。

使用javascript解析给定的样式对象非常简单:

var 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": []
}
};
var parsedStyles = {};
styles.stylesheet.rules.forEach(rule => {
parsedStyles[rule['selectors']] = getAllStyles(rule['declarations']);
})
function getAllStyles(declarations) {
	var styles = {}
	declarations.forEach(declaration => {
styles[declaration['property']] = declaration['value'];
});

return styles;
}
console.log(parsedStyles);

使用 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)

你可以使用仅标头的css解析器 https://github.com/srgank/css_parser #include "cparsecss.hpp" #include #include

int main()
{
string css =
" h1{ "
" color: white; "
" text-align: center; "
" } "
" "
" p{ "
" font-family: verdana; "
" font-size: 20px; "
" } "
" "
" b2{ "
" font-family: verdana; "
" font-size: 20px; "
" } "
;
CParseCSS p;  
p.parse(css);  
typeListData data = p.getData();  

for (size_t i = 0; i < data.size(); i++) {  
cout << data.at(i).selector << " " << data.at(i).name << " " << data.at(i).value << endl;  
}  
}

结果:

h1 color white
h1 text-align center
p font-family verdana
p font-size 20px
b2 font-family verdana
b2 font-size 20px

最新更新