The goal of this specification is to allow authors access to the engine's parser. There are two over-arching use cases:
Desires for these APIs:
var background = window.cssParse.rule("background: green");
console.log(background.styleMap.get("background").value) // "green"
var styles = window.cssParse.ruleSet(".foo { background: green; margin: 5px; }");
console.log(styles.length) // 5
console.log(styles[0].styleMap.get("margin-top").value) // 5
console.log(styles[0].styleMap.get("margin-top").type) // "px"
const style = fetch("style.css")
.then(response=>CSS.parseStylesheet(response.body));
style.then(console.log);
/* example of the object once we have it more refined */
);
<length> || <color>
", for example. Right now your only choice is to give it the grammar "*"
which just gives you a string that you have to parse yourself. Similar with custom functions (--conic-gradient(...)
) and custom at-rules (@--svg
) - these will rarely fit in our limited set of allowed grammars.<img sizes>
. If you wanted to add support for the h
descriptor to sizes
, you currently have to write your own full-feature CSS parser. (sizes
is pretty complex; you shouldn't skimp.) Better to let the engine parse it as "generic CSS", then you can recognize the parts you need and do image selection yourself.Here is an example of some JS code that is wanting to parse out various CSS types and also seperate out the values from their units.
function parseValues(value,propertyName) {
// Trim value on the edges
value = value.trim();
// Normalize letter-casing
value = value.toLowerCase();
// Map colors to a standard value (eg: white, blue, yellow)
if (isKeywordColor(value)) { return "<color-keyword>"; }
value = value.replace(/[#][0-9a-fA-F]+/g, '#xxyyzz');
// Escapce identifiers containing numbers
var numbers = ['ZERO','ONE','TWO','THREE','FOUR','FIVE','SIX','SEVEN','EIGHT','NINE'];
value = value.replace(
/([_a-z][-_a-z]|[_a-df-z])[0-9]+[-_a-z0-9]*/g,
s=>numbers.reduce(
(m,nstr,nint)=>m.replace(RegExp(nint,'g'),nstr),
s
)
);
// Remove any digits eg: 55px -> px, 1.5 -> 0.0, 1 -> 0
value = value.replace(/(?:[+]|[-]|)(?:(?:[0-9]+)(?:[.][0-9]+|)|(?:[.][0-9]+))(?:[e](?:[+]|[-]|)(?:[0-9]+))?(%|e[a-z]+|[a-df-z][a-z]*)/g, "$1");
value = value.replace(/(?:[+]|[-]|)(?:[0-9]+)(?:[.][0-9]+)(?:[e](?:[+]|[-]|)(?:[0-9]+))?/g, " <float> ");
value = value.replace(/(?:[+]|[-]|)(?:[.][0-9]+)(?:[e](?:[+]|[-]|)(?:[0-9]+))?/g, " <float> ");
value = value.replace(/(?:[+]|[-]|)(?:[0-9]+)(?:[e](?:[+]|[-]|)(?:[0-9]+))/g, " <float> ");
value = value.replace(/(?:[+]|[-]|)(?:[0-9]+)/g, " <int> ");
// Unescapce identifiers containing numbers
value = numbers.reduce(
(m,nstr,nint)=>m.replace(RegExp(nstr,'g'),nint),
value
)
// Remove quotes
value = value.replace(/('|‘|’|")/g, "");
//
switch(propertyName) {
case 'counter-increment':
case 'counter-reset':
// Anonymize the user identifier
value = value.replace(/[-_a-zA-Z0-9]+/g,' <custom-ident> ');
break;
case 'grid':
case 'grid-template':
case 'grid-template-rows':
case 'grid-template-columns':
case 'grid-template-areas':
// Anonymize line names
value = value.replace(/\[[-_a-zA-Z0-9 ]+\]/g,' <line-names> ');
break;
case '--var':
// Replace (...), {...} and [...]
value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, " <parentheses-block> ");
value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, " <parentheses-block> ");
value = value.replace(/\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]*)\])*\])*\])*\])*\]/g, " <curly-brackets-block> ");
value = value.replace(/\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]*)\])*\])*\])*\])*\]/g, " <curly-brackets-block> ");
value = value.replace(/\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]*)\})*\})*\})*\})*\}/g, " <square-brackets-block> ");
value = value.replace(/\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]*)\})*\})*\})*\})*\}/g, " <square-brackets-block> ");
break;
}
return value.trim();
}