2018年更新:此问题是在PostCSS存在之前提出的,我可能会使用它。
我想将一块CSS解析成AST,以便在某些CSS指令中添加前缀。
是否有适用于JavaScript或Node的CSS解析器可以做到这一点?
我已经在NPM上搜索了。我找到的唯一有用的结果是parser-lib,但它是基于流的,并且看起来我需要为每个CSS节点编写自己的发射器。
更新:我还发现了JSCSSP,但它没有文档......
2018年更新:此问题是在PostCSS存在之前提出的,我可能会使用它。
我想将一块CSS解析成AST,以便在某些CSS指令中添加前缀。
是否有适用于JavaScript或Node的CSS解析器可以做到这一点?
我已经在NPM上搜索了。我找到的唯一有用的结果是parser-lib,但它是基于流的,并且看起来我需要为每个CSS节点编写自己的发射器。
更新:我还发现了JSCSSP,但它没有文档......
更新: 我之前提到了JSCSSP,但它有缺陷并且似乎已经被放弃了。显然,NPM上的css模块是最好的选择:
css = require 'css'
input = '''
body {
font-family: sans-serif;
}
#thing.foo p.bar {
font-weight: bold;
}
'''
obj = css.parse input
sheet = obj.stylesheet
for rule in sheet.rules
rule.selectors = ('#XXX ' + s for s in rule.selectors)
console.log css.stringify(obj)
输出:
#XXX body {
font-family: sans-serif;
}
#XXX #thing.foo p.bar {
font-weight: bold;
}
这是我们的开源CSS解析器css.js
以下是一个简单的解析示例:
<script type="text/javascript">
var cssString = ' .someSelector { margin:40px 10px; padding:5px}';
//initialize parser object
var parser = new cssjs();
//parse css string
var parsed = parser.parseCSS(cssString);
console.log(parsed);
</script>
var newCSSString = parser.getCSSForEditor(parsed);
我们的CSS解析器的主要特点如下:
值得一提的是LESS。虽然它主要是CSS的一个扩展(非常好用),但LESS解析器确实让你可以访问AST。
纯CSS样式表也是合法的LESS样式表,因此您可以从现有的CSS样式表开始,逐渐过渡到LESS的扩展。
不需要使用外部CSS解析器,我们可以使用本地CSS解析器
var sheetRef=document.getElementsByTagName("style")[0];
console.log("----------------list of rules--------------");
for (var i=0; i<sheetRef.sheet.cssRules.length; i++){
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
if (sheet.cssRules.length > 0) {
//console.log(sheet.cssRules[i]);
console.log(sheet.cssRules[i].selectorText);
console.log(sheet.cssRules[i].cssText);
}}
.red{
color:red;
}
插入规则
var sheetRef=document.getElementsByTagName("style")[0];
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
sheet.insertRule('.foo{color:red;}', 0);
移除规则 除了IE 9之前的版本外,适用于所有浏览器。
var sheetRef=document.getElementsByTagName("style")[0];
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
sheet.removeRule (0);
删除规则 所有浏览器,除了IE 9及之前版本
var sheetRef=document.getElementsByTagName("style")[0];
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
sheet.deleteRule (0);
添加媒体
function AddScreenMedia () {
var styleTag = document.getElementsByTagName("style")[0];
// the style sheet in the style tag
var sheet = styleTag.sheet ? styleTag.sheet : styleTag.styleSheet;
if (sheet.cssRules) { // all browsers, except IE before version 9
var rule = sheet.cssRules[0];
var mediaList = rule.media;
alert ("The media types before adding the screen media type: " + mediaList.mediaText);
mediaList.appendMedium ("screen");
alert ("The media types after adding the screen media type: " + mediaList.mediaText);
}
else { // Internet Explorer before version 9
// note: the rules collection does not contain the at-rules
alert ("Your browser does not support this example!");
}
}
@media print {
body {
font-size: 13px;
color: #FF0000;
}
}
some text
<button onclick="AddScreenMedia ();">Add screen media</button>
获取规则
var sheetRef=document.getElementsByTagName("style")[0];
console.log("----------------list of rules--------------");
for (var i=0; i<sheetRef.sheet.cssRules.length; i++){
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
if (sheet.cssRules.length > 0) {
//console.log(sheet.cssRules[i]);
console.log(sheet.cssRules[i].selectorText);
console.log(sheet.cssRules[i].cssText);
console.log(sheet.cssRules[i].style.color)
console.log(sheet.cssRules[i].style.background)
console.log(sheet.cssRules[i].style)
}}
.red{
color:red;
background:orange;
}
<h1>red</h1>
background: var(--my-color)
,它会将其拆分为长手属性,并丢弃变量。请参考https://bugs.chromium.org/p/chromium/issues/detail?id=1218159#c13,浏览器开发人员指出了规范中的遗漏问题。 - EoghanM最终我使用了这个库,它非常轻量级适合我的实现(在 Kemal Dağ
的回答中提供)。其他选项对于我追求的客户端实现而言过于沉重。
https://github.com/jotform/css.js
a paid nerd
的原始答案一直很好,直到我遇到媒体查询为止。
我必须添加一些递归,这就是最终结果。
对于 TypeScript,请谅解。
private scopeCSS(css: string): CSS.Stylesheet {
let ast: CSS.Stylesheet = CSS.parse(css);
let stylesheet: CSS.StyleRules|undefined = ast.stylesheet;
if (stylesheet) {
let rules: Array<CSS.Rule|CSS.Media> = stylesheet.rules;
let prefix = `[data-id='sticky-container-${this.parent.id}']`;
// Append our container scope to rules
// Recursive rule appender
let ruleAppend = (rules: Array<CSS.Rule|CSS.Media>) => {
rules.forEach(rule => {
let cssRule = <CSS.Rule>rule;
let mediaRule = <CSS.Media>rule;
if (cssRule.selectors !== undefined) {
cssRule.selectors = cssRule.selectors.map(selector => `${prefix} ${selector}`);
}
if (mediaRule.rules !== undefined) {
ruleAppend(mediaRule.rules);
}
});
};
ruleAppend(rules);
}
return ast;
}
function scopeCSS(css, prefix) {
var ast = CSS.parse(css);
var stylesheet = ast.stylesheet;
if (stylesheet) {
var rules = stylesheet.rules;
// Append our container scope to rules
// Recursive rule appender
var ruleAppend = function(rules) {
rules.forEach(function(rule) {
if (rule.selectors !== undefined) {
rule.selectors = rule.selectors.map(function(selector) {
return prefix + " " + selector;
});
}
if (rule.rules !== undefined) {
ruleAppend(rule.rules);
}
});
};
ruleAppend(rules);
}
return ast;
}
非常简单,只有一个函数。
function parseCSStxt(cssXTX){
var p2=[], p1=cssXTX.split("}");
p1.forEach(element => {
var rtmp=element.split("{") ;
if( rtmp.length>1 && Array.isArray(rtmp) ){
var s=rtmp[0].split(",");
var v=rtmp[1].split(";");
const notNil = (i) => !(typeof i === 'undefined' || i === null || i=='');
s = s.filter(notNil);
v = v.filter(notNil);
p2.push( {s,v} );
}
});
console.log(p2);
}
parseCSStxt( ".cls-1,.cls-5{fill:none;}.cls-1,.cls-2,.cls-5,.cls-6{stroke:#000;}.cls-1{stroke-linecap:round;stroke-linejoin:round;}.cls-1,.cls-2,.cls-6{stroke-width:4px;}.cls-2{fill:#ffad17;}.cls-2,.cls-5,.cls-6{stroke-miterlimit:10;}.cls-3{fill:#d86212;}.cls-4{fill:#87270e;}.cls-5{stroke-width:2px;}.cls-6{fill:#006d31;}" );
parseCSStxt("@keyframes foo {0% {color: orange} 100% {color:black}}")
是有效的 CSS,但这段代码无法对其进行有意义的解析。 - AKXbackground-image: url('svg+xml;123{你明白我的意思}');
。 - EoghanM