123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- module.exports = function(css){
- /**
- * Parse stylesheet.
- */
- function stylesheet() {
- return { stylesheet: { rules: rules() }};
- }
- /**
- * Opening brace.
- */
- function open() {
- return match(/^{\s*/);
- }
- /**
- * Closing brace.
- */
- function close() {
- return match(/^}\s*/);
- }
- /**
- * Parse ruleset.
- */
- function rules() {
- var node;
- var rules = [];
- whitespace();
- comments();
- while (css[0] != '}' && (node = atrule() || rule())) {
- comments();
- rules.push(node);
- }
- return rules;
- }
- /**
- * Match `re` and return captures.
- */
- function match(re) {
- var m = re.exec(css);
- if (!m) return;
- css = css.slice(m[0].length);
- return m;
- }
- /**
- * Parse whitespace.
- */
- function whitespace() {
- match(/^\s*/);
- }
- /**
- * Parse comments;
- */
- function comments() {
- while (comment()) ;
- }
- /**
- * Parse comment.
- */
- function comment() {
- if ('/' == css[0] && '*' == css[1]) {
- var i = 2;
- while ('*' != css[i] || '/' != css[i + 1]) ++i;
- i += 2;
- css = css.slice(i);
- whitespace();
- return true;
- }
- }
- /**
- * Parse selector.
- */
- function selector() {
- var m = match(/^([^{]+)/);
- if (!m) return;
- return m[0].trim().split(/\s*,\s*/);
- }
- /**
- * Parse declaration.
- */
- function declaration() {
- // prop
- var prop = match(/^(\*?[-\w]+)\s*/);
- if (!prop) return;
- prop = prop[0];
- // :
- if (!match(/^:\s*/)) return;
- // val
- var val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)\s*/);
- if (!val) return;
- val = val[0].trim();
- // ;
- match(/^[;\s]*/);
- return { property: prop, value: val };
- }
- /**
- * Parse keyframe.
- */
- function keyframe() {
- var m;
- var vals = [];
- while (m = match(/^(from|to|\d+%|\.\d+%|\d+\.\d+%)\s*/)) {
- vals.push(m[1]);
- match(/^,\s*/);
- }
- if (!vals.length) return;
- return {
- values: vals,
- declarations: declarations()
- };
- }
- /**
- * Parse keyframes.
- */
- function keyframes() {
- var m = match(/^@([-\w]+)?keyframes */);
- if (!m) return;
- var vendor = m[1];
- // identifier
- var m = match(/^([-\w]+)\s*/);
- if (!m) return;
- var name = m[1];
- if (!open()) return;
- comments();
- var frame;
- var frames = [];
- while (frame = keyframe()) {
- frames.push(frame);
- comments();
- }
- if (!close()) return;
- return {
- name: name,
- vendor: vendor,
- keyframes: frames
- };
- }
- /**
- * Parse media.
- */
- function media() {
- var m = match(/^@media *([^{]+)/);
- if (!m) return;
- var media = m[1].trim();
- if (!open()) return;
- comments();
- var style = rules();
- if (!close()) return;
- return { media: media, rules: style };
- }
- /**
- * Parse import
- */
- function atimport() {
- return _atrule('import')
- }
- /**
- * Parse charset
- */
- function atcharset() {
- return _atrule('charset');
- }
- /**
- * Parse non-block at-rules
- */
- function _atrule(name) {
- var m = match(new RegExp('^@' + name + ' *([^;\\n]+);\\s*'));
- if (!m) return;
- var ret = {}
- ret[name] = m[1].trim();
- return ret;
- }
- /**
- * Parse declarations.
- */
- function declarations() {
- var decls = [];
- if (!open()) return;
- comments();
-
- // declarations
- var decl;
- while (decl = declaration()) {
- decls.push(decl);
- comments();
- }
-
- if (!close()) return;
- return decls;
- }
- /**
- * Parse at rule.
- */
-
- function atrule() {
- return keyframes()
- || media()
- || atimport()
- || atcharset();
- }
- /**
- * Parse rule.
- */
-
- function rule() {
- var sel = selector();
- if (!sel) return;
- comments();
- return { selectors: sel, declarations: declarations() };
- }
-
- return stylesheet();
- };
|