Reputation: 31512
Update 2018: This question was asked long before PostCSS existed, and I would have probably used that.
I'd like to parse a blob of CSS into an AST so I can add prefixes to certain CSS directives.
Is there a CSS parser for JavaScript or Node that will do this?
I've searched NPM. The only useful result I've found is parser-lib, but it's stream-based and it looks like I'll need to write my own emitter for every CSS node.
Update: I also found JSCSSP, but it has no documentation...
Upvotes: 28
Views: 23155
Reputation: 153
Very simple, one function.
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;}" );
Upvotes: 0
Reputation: 10897
No need use external css parser,we can use native css parser
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;
}
To Insert Rule
var sheetRef=document.getElementsByTagName("style")[0];
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
sheet.insertRule('.foo{color:red;}', 0);
To Remove Rule all browsers, except IE before version 9
var sheetRef=document.getElementsByTagName("style")[0];
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
sheet.removeRule (0);
To Delete Rule all browsers, except IE before version 9
var sheetRef=document.getElementsByTagName("style")[0];
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
sheet.deleteRule (0);
To add Media
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>
To get rules
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>
Upvotes: 5
Reputation: 14823
I ended up using this library which was light enough for my implementation (provided in Kemal Dağ
's answer). Other options were too heavy for the client-side implementation I was after.
https://github.com/jotform/css.js
a paid nerd
's original answer worked great until I hit media queries.
I had to add some recursion and this is what I ended up with.
Forgive me for the 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;
}
Upvotes: 2
Reputation: 2763
Here is our open source CSS parser css.js
Here is a simple parsing example :
<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>
To stringify parsed data structure into CSS string after editing
var newCSSString = parser.getCSSForEditor(parsed);
Main features of our CSS parser is :
Upvotes: 6
Reputation: 144912
Also worth mentioning is LESS. While it is primarily a (fantastic) extension to CSS, the LESS parser does give you access to the AST.
A pure CSS stylesheet is also a valid LESS stylesheet, so you can start with what you have now and ease in to LESS' extensions.
Upvotes: 5
Reputation: 31512
Update: I previously mentioned JSCSSP, which is buggy seems to be abandoned. Obviously enough, the css module on NPM is the best:
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)
Output:
#XXX body {
font-family: sans-serif;
}
#XXX #thing.foo p.bar {
font-weight: bold;
}
Upvotes: 12
Reputation: 6012
http://www.glazman.org/JSCSSP/
sheet
is sort of like an AST.
Upvotes: 1