Add packages to optimize svgs dynamically

These packages will be used to dynamically optimize SVG images
during the build.

R=jacktfranklin@chromium.org

Bug: 1216402
Change-Id: I04e95aa7d79c9d67beaf8a7861182c52b16b7d0f
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2939992
Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org>
diff --git a/node_modules/css-tree/lib/syntax/atrule/font-face.js b/node_modules/css-tree/lib/syntax/atrule/font-face.js
new file mode 100644
index 0000000..ea33ad9
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/atrule/font-face.js
@@ -0,0 +1,8 @@
+module.exports = {
+    parse: {
+        prelude: null,
+        block: function() {
+            return this.Block(true);
+        }
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/atrule/import.js b/node_modules/css-tree/lib/syntax/atrule/import.js
new file mode 100644
index 0000000..58bda97
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/atrule/import.js
@@ -0,0 +1,40 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var STRING = TYPE.String;
+var IDENT = TYPE.Ident;
+var URL = TYPE.Url;
+var FUNCTION = TYPE.Function;
+var LEFTPARENTHESIS = TYPE.LeftParenthesis;
+
+module.exports = {
+    parse: {
+        prelude: function() {
+            var children = this.createList();
+
+            this.scanner.skipSC();
+
+            switch (this.scanner.tokenType) {
+                case STRING:
+                    children.push(this.String());
+                    break;
+
+                case URL:
+                case FUNCTION:
+                    children.push(this.Url());
+                    break;
+
+                default:
+                    this.error('String or url() is expected');
+            }
+
+            if (this.lookupNonWSType(0) === IDENT ||
+                this.lookupNonWSType(0) === LEFTPARENTHESIS) {
+                children.push(this.WhiteSpace());
+                children.push(this.MediaQueryList());
+            }
+
+            return children;
+        },
+        block: null
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/atrule/index.js b/node_modules/css-tree/lib/syntax/atrule/index.js
new file mode 100644
index 0000000..5098cfb
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/atrule/index.js
@@ -0,0 +1,7 @@
+module.exports = {
+    'font-face': require('./font-face'),
+    'import': require('./import'),
+    'media': require('./media'),
+    'page': require('./page'),
+    'supports': require('./supports')
+};
diff --git a/node_modules/css-tree/lib/syntax/atrule/media.js b/node_modules/css-tree/lib/syntax/atrule/media.js
new file mode 100644
index 0000000..f148346
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/atrule/media.js
@@ -0,0 +1,12 @@
+module.exports = {
+    parse: {
+        prelude: function() {
+            return this.createSingleNodeList(
+                this.MediaQueryList()
+            );
+        },
+        block: function() {
+            return this.Block(false);
+        }
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/atrule/page.js b/node_modules/css-tree/lib/syntax/atrule/page.js
new file mode 100644
index 0000000..aa2229f
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/atrule/page.js
@@ -0,0 +1,12 @@
+module.exports = {
+    parse: {
+        prelude: function() {
+            return this.createSingleNodeList(
+                this.SelectorList()
+            );
+        },
+        block: function() {
+            return this.Block(true);
+        }
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/atrule/supports.js b/node_modules/css-tree/lib/syntax/atrule/supports.js
new file mode 100644
index 0000000..75fa1d8
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/atrule/supports.js
@@ -0,0 +1,89 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var WHITESPACE = TYPE.WhiteSpace;
+var COMMENT = TYPE.Comment;
+var IDENT = TYPE.Ident;
+var FUNCTION = TYPE.Function;
+var COLON = TYPE.Colon;
+var LEFTPARENTHESIS = TYPE.LeftParenthesis;
+
+function consumeRaw() {
+    return this.createSingleNodeList(
+        this.Raw(this.scanner.tokenIndex, null, false)
+    );
+}
+
+function parentheses() {
+    this.scanner.skipSC();
+
+    if (this.scanner.tokenType === IDENT &&
+        this.lookupNonWSType(1) === COLON) {
+        return this.createSingleNodeList(
+            this.Declaration()
+        );
+    }
+
+    return readSequence.call(this);
+}
+
+function readSequence() {
+    var children = this.createList();
+    var space = null;
+    var child;
+
+    this.scanner.skipSC();
+
+    scan:
+    while (!this.scanner.eof) {
+        switch (this.scanner.tokenType) {
+            case WHITESPACE:
+                space = this.WhiteSpace();
+                continue;
+
+            case COMMENT:
+                this.scanner.next();
+                continue;
+
+            case FUNCTION:
+                child = this.Function(consumeRaw, this.scope.AtrulePrelude);
+                break;
+
+            case IDENT:
+                child = this.Identifier();
+                break;
+
+            case LEFTPARENTHESIS:
+                child = this.Parentheses(parentheses, this.scope.AtrulePrelude);
+                break;
+
+            default:
+                break scan;
+        }
+
+        if (space !== null) {
+            children.push(space);
+            space = null;
+        }
+
+        children.push(child);
+    }
+
+    return children;
+}
+
+module.exports = {
+    parse: {
+        prelude: function() {
+            var children = readSequence.call(this);
+
+            if (this.getFirstListNode(children) === null) {
+                this.error('Condition is expected');
+            }
+
+            return children;
+        },
+        block: function() {
+            return this.Block(false);
+        }
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/config/lexer.js b/node_modules/css-tree/lib/syntax/config/lexer.js
new file mode 100644
index 0000000..2c58b0f
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/config/lexer.js
@@ -0,0 +1,9 @@
+var data = require('../../../data');
+
+module.exports = {
+    generic: true,
+    types: data.types,
+    atrules: data.atrules,
+    properties: data.properties,
+    node: require('../node')
+};
diff --git a/node_modules/css-tree/lib/syntax/config/mix.js b/node_modules/css-tree/lib/syntax/config/mix.js
new file mode 100644
index 0000000..8eb57ec
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/config/mix.js
@@ -0,0 +1,140 @@
+const hasOwnProperty = Object.prototype.hasOwnProperty;
+const shape = {
+    generic: true,
+    types: appendOrAssign,
+    atrules: {
+        prelude: appendOrAssignOrNull,
+        descriptors: appendOrAssignOrNull
+    },
+    properties: appendOrAssign,
+    parseContext: assign,
+    scope: deepAssign,
+    atrule: ['parse'],
+    pseudo: ['parse'],
+    node: ['name', 'structure', 'parse', 'generate', 'walkContext']
+};
+
+function isObject(value) {
+    return value && value.constructor === Object;
+}
+
+function copy(value) {
+    return isObject(value)
+        ? Object.assign({}, value)
+        : value;
+}
+
+function assign(dest, src) {
+    return Object.assign(dest, src);
+}
+
+function deepAssign(dest, src) {
+    for (const key in src) {
+        if (hasOwnProperty.call(src, key)) {
+            if (isObject(dest[key])) {
+                deepAssign(dest[key], copy(src[key]));
+            } else {
+                dest[key] = copy(src[key]);
+            }
+        }
+    }
+
+    return dest;
+}
+
+function append(a, b) {
+    if (typeof b === 'string' && /^\s*\|/.test(b)) {
+        return typeof a === 'string'
+            ? a + b
+            : b.replace(/^\s*\|\s*/, '');
+    }
+
+    return b || null;
+}
+
+function appendOrAssign(a, b) {
+    if (typeof b === 'string') {
+        return append(a, b);
+    }
+
+    const result = Object.assign({}, a);
+    for (let key in b) {
+        if (hasOwnProperty.call(b, key)) {
+            result[key] = append(hasOwnProperty.call(a, key) ? a[key] : undefined, b[key]);
+        }
+    }
+
+    return result;
+}
+
+function appendOrAssignOrNull(a, b) {
+    const result = appendOrAssign(a, b);
+
+    return !isObject(result) || Object.keys(result).length
+        ? result
+        : null;
+}
+
+function mix(dest, src, shape) {
+    for (const key in shape) {
+        if (hasOwnProperty.call(shape, key) === false) {
+            continue;
+        }
+
+        if (shape[key] === true) {
+            if (key in src) {
+                if (hasOwnProperty.call(src, key)) {
+                    dest[key] = copy(src[key]);
+                }
+            }
+        } else if (shape[key]) {
+            if (typeof shape[key] === 'function') {
+                const fn = shape[key];
+                dest[key] = fn({}, dest[key]);
+                dest[key] = fn(dest[key] || {}, src[key]);
+            } else if (isObject(shape[key])) {
+                const result = {};
+
+                for (let name in dest[key]) {
+                    result[name] = mix({}, dest[key][name], shape[key]);
+                }
+
+                for (let name in src[key]) {
+                    result[name] = mix(result[name] || {}, src[key][name], shape[key]);
+                }
+
+                dest[key] = result;
+            } else if (Array.isArray(shape[key])) {
+                const res = {};
+                const innerShape = shape[key].reduce(function(s, k) {
+                    s[k] = true;
+                    return s;
+                }, {});
+
+                for (const [name, value] of Object.entries(dest[key] || {})) {
+                    res[name] = {};
+                    if (value) {
+                        mix(res[name], value, innerShape);
+                    }
+                }
+
+                for (const name in src[key]) {
+                    if (hasOwnProperty.call(src[key], name)) {
+                        if (!res[name]) {
+                            res[name] = {};
+                        }
+
+                        if (src[key] && src[key][name]) {
+                            mix(res[name], src[key][name], innerShape);
+                        }
+                    }
+                }
+
+                dest[key] = res;
+            }
+        }
+    }
+    return dest;
+}
+
+module.exports = (dest, src) => mix(dest, src, shape);
diff --git a/node_modules/css-tree/lib/syntax/config/parser.js b/node_modules/css-tree/lib/syntax/config/parser.js
new file mode 100644
index 0000000..88d2921
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/config/parser.js
@@ -0,0 +1,25 @@
+module.exports = {
+    parseContext: {
+        default: 'StyleSheet',
+        stylesheet: 'StyleSheet',
+        atrule: 'Atrule',
+        atrulePrelude: function(options) {
+            return this.AtrulePrelude(options.atrule ? String(options.atrule) : null);
+        },
+        mediaQueryList: 'MediaQueryList',
+        mediaQuery: 'MediaQuery',
+        rule: 'Rule',
+        selectorList: 'SelectorList',
+        selector: 'Selector',
+        block: function() {
+            return this.Block(true);
+        },
+        declarationList: 'DeclarationList',
+        declaration: 'Declaration',
+        value: 'Value'
+    },
+    scope: require('../scope'),
+    atrule: require('../atrule'),
+    pseudo: require('../pseudo'),
+    node: require('../node')
+};
diff --git a/node_modules/css-tree/lib/syntax/config/walker.js b/node_modules/css-tree/lib/syntax/config/walker.js
new file mode 100644
index 0000000..3a030de
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/config/walker.js
@@ -0,0 +1,3 @@
+module.exports = {
+    node: require('../node')
+};
diff --git a/node_modules/css-tree/lib/syntax/create.js b/node_modules/css-tree/lib/syntax/create.js
new file mode 100644
index 0000000..68b6d16
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/create.js
@@ -0,0 +1,77 @@
+var List = require('../common/List');
+var SyntaxError = require('../common/SyntaxError');
+var TokenStream = require('../common/TokenStream');
+var Lexer = require('../lexer/Lexer');
+var definitionSyntax = require('../definition-syntax');
+var tokenize = require('../tokenizer');
+var createParser = require('../parser/create');
+var createGenerator = require('../generator/create');
+var createConvertor = require('../convertor/create');
+var createWalker = require('../walker/create');
+var clone = require('../utils/clone');
+var names = require('../utils/names');
+var mix = require('./config/mix');
+
+function createSyntax(config) {
+    var parse = createParser(config);
+    var walk = createWalker(config);
+    var generate = createGenerator(config);
+    var convert = createConvertor(walk);
+
+    var syntax = {
+        List: List,
+        SyntaxError: SyntaxError,
+        TokenStream: TokenStream,
+        Lexer: Lexer,
+
+        vendorPrefix: names.vendorPrefix,
+        keyword: names.keyword,
+        property: names.property,
+        isCustomProperty: names.isCustomProperty,
+
+        definitionSyntax: definitionSyntax,
+        lexer: null,
+        createLexer: function(config) {
+            return new Lexer(config, syntax, syntax.lexer.structure);
+        },
+
+        tokenize: tokenize,
+        parse: parse,
+        walk: walk,
+        generate: generate,
+
+        find: walk.find,
+        findLast: walk.findLast,
+        findAll: walk.findAll,
+
+        clone: clone,
+        fromPlainObject: convert.fromPlainObject,
+        toPlainObject: convert.toPlainObject,
+
+        createSyntax: function(config) {
+            return createSyntax(mix({}, config));
+        },
+        fork: function(extension) {
+            var base = mix({}, config); // copy of config
+            return createSyntax(
+                typeof extension === 'function'
+                    ? extension(base, Object.assign)
+                    : mix(base, extension)
+            );
+        }
+    };
+
+    syntax.lexer = new Lexer({
+        generic: true,
+        types: config.types,
+        atrules: config.atrules,
+        properties: config.properties,
+        node: config.node
+    }, syntax);
+
+    return syntax;
+};
+
+exports.create = function(config) {
+    return createSyntax(mix({}, config));
+};
diff --git a/node_modules/css-tree/lib/syntax/function/expression.js b/node_modules/css-tree/lib/syntax/function/expression.js
new file mode 100644
index 0000000..e76f631
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/function/expression.js
@@ -0,0 +1,7 @@
+// legacy IE function
+// expression( <any-value> )
+module.exports = function() {
+    return this.createSingleNodeList(
+        this.Raw(this.scanner.tokenIndex, null, false)
+    );
+};
diff --git a/node_modules/css-tree/lib/syntax/function/var.js b/node_modules/css-tree/lib/syntax/function/var.js
new file mode 100644
index 0000000..ba55b48
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/function/var.js
@@ -0,0 +1,43 @@
+var TYPE = require('../../tokenizer').TYPE;
+var rawMode = require('../node/Raw').mode;
+
+var COMMA = TYPE.Comma;
+var WHITESPACE = TYPE.WhiteSpace;
+
+// var( <ident> , <value>? )
+module.exports = function() {
+    var children = this.createList();
+
+    this.scanner.skipSC();
+
+    // NOTE: Don't check more than a first argument is an ident, rest checks are for lexer
+    children.push(this.Identifier());
+
+    this.scanner.skipSC();
+
+    if (this.scanner.tokenType === COMMA) {
+        children.push(this.Operator());
+
+        const startIndex = this.scanner.tokenIndex;
+        const value = this.parseCustomProperty
+            ? this.Value(null)
+            : this.Raw(this.scanner.tokenIndex, rawMode.exclamationMarkOrSemicolon, false);
+
+        if (value.type === 'Value' && value.children.isEmpty()) {
+            for (let offset = startIndex - this.scanner.tokenIndex; offset <= 0; offset++) {
+                if (this.scanner.lookupType(offset) === WHITESPACE) {
+                    value.children.appendData({
+                        type: 'WhiteSpace',
+                        loc: null,
+                        value: ' '
+                    });
+                    break;
+                }
+            }
+        }
+
+        children.push(value);
+    }
+
+    return children;
+};
diff --git a/node_modules/css-tree/lib/syntax/index.js b/node_modules/css-tree/lib/syntax/index.js
new file mode 100644
index 0000000..065ea04
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/index.js
@@ -0,0 +1,21 @@
+function merge() {
+    var dest = {};
+
+    for (var i = 0; i < arguments.length; i++) {
+        var src = arguments[i];
+        for (var key in src) {
+            dest[key] = src[key];
+        }
+    }
+
+    return dest;
+}
+
+module.exports = require('./create').create(
+    merge(
+        require('./config/lexer'),
+        require('./config/parser'),
+        require('./config/walker')
+    )
+);
+module.exports.version = require('../../package.json').version;
diff --git a/node_modules/css-tree/lib/syntax/node/AnPlusB.js b/node_modules/css-tree/lib/syntax/node/AnPlusB.js
new file mode 100644
index 0000000..d33d21a
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/AnPlusB.js
@@ -0,0 +1,297 @@
+var cmpChar = require('../../tokenizer').cmpChar;
+var isDigit = require('../../tokenizer').isDigit;
+var TYPE = require('../../tokenizer').TYPE;
+
+var WHITESPACE = TYPE.WhiteSpace;
+var COMMENT = TYPE.Comment;
+var IDENT = TYPE.Ident;
+var NUMBER = TYPE.Number;
+var DIMENSION = TYPE.Dimension;
+var PLUSSIGN = 0x002B;    // U+002B PLUS SIGN (+)
+var HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
+var N = 0x006E;           // U+006E LATIN SMALL LETTER N (n)
+var DISALLOW_SIGN = true;
+var ALLOW_SIGN = false;
+
+function checkInteger(offset, disallowSign) {
+    var pos = this.scanner.tokenStart + offset;
+    var code = this.scanner.source.charCodeAt(pos);
+
+    if (code === PLUSSIGN || code === HYPHENMINUS) {
+        if (disallowSign) {
+            this.error('Number sign is not allowed');
+        }
+        pos++;
+    }
+
+    for (; pos < this.scanner.tokenEnd; pos++) {
+        if (!isDigit(this.scanner.source.charCodeAt(pos))) {
+            this.error('Integer is expected', pos);
+        }
+    }
+}
+
+function checkTokenIsInteger(disallowSign) {
+    return checkInteger.call(this, 0, disallowSign);
+}
+
+function expectCharCode(offset, code) {
+    if (!cmpChar(this.scanner.source, this.scanner.tokenStart + offset, code)) {
+        var msg = '';
+
+        switch (code) {
+            case N:
+                msg = 'N is expected';
+                break;
+            case HYPHENMINUS:
+                msg = 'HyphenMinus is expected';
+                break;
+        }
+
+        this.error(msg, this.scanner.tokenStart + offset);
+    }
+}
+
+// ... <signed-integer>
+// ... ['+' | '-'] <signless-integer>
+function consumeB() {
+    var offset = 0;
+    var sign = 0;
+    var type = this.scanner.tokenType;
+
+    while (type === WHITESPACE || type === COMMENT) {
+        type = this.scanner.lookupType(++offset);
+    }
+
+    if (type !== NUMBER) {
+        if (this.scanner.isDelim(PLUSSIGN, offset) ||
+            this.scanner.isDelim(HYPHENMINUS, offset)) {
+            sign = this.scanner.isDelim(PLUSSIGN, offset) ? PLUSSIGN : HYPHENMINUS;
+
+            do {
+                type = this.scanner.lookupType(++offset);
+            } while (type === WHITESPACE || type === COMMENT);
+
+            if (type !== NUMBER) {
+                this.scanner.skip(offset);
+                checkTokenIsInteger.call(this, DISALLOW_SIGN);
+            }
+        } else {
+            return null;
+        }
+    }
+
+    if (offset > 0) {
+        this.scanner.skip(offset);
+    }
+
+    if (sign === 0) {
+        type = this.scanner.source.charCodeAt(this.scanner.tokenStart);
+        if (type !== PLUSSIGN && type !== HYPHENMINUS) {
+            this.error('Number sign is expected');
+        }
+    }
+
+    checkTokenIsInteger.call(this, sign !== 0);
+    return sign === HYPHENMINUS ? '-' + this.consume(NUMBER) : this.consume(NUMBER);
+}
+
+// An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb
+module.exports = {
+    name: 'AnPlusB',
+    structure: {
+        a: [String, null],
+        b: [String, null]
+    },
+    parse: function() {
+        /* eslint-disable brace-style*/
+        var start = this.scanner.tokenStart;
+        var a = null;
+        var b = null;
+
+        // <integer>
+        if (this.scanner.tokenType === NUMBER) {
+            checkTokenIsInteger.call(this, ALLOW_SIGN);
+            b = this.consume(NUMBER);
+        }
+
+        // -n
+        // -n <signed-integer>
+        // -n ['+' | '-'] <signless-integer>
+        // -n- <signless-integer>
+        // <dashndashdigit-ident>
+        else if (this.scanner.tokenType === IDENT && cmpChar(this.scanner.source, this.scanner.tokenStart, HYPHENMINUS)) {
+            a = '-1';
+
+            expectCharCode.call(this, 1, N);
+
+            switch (this.scanner.getTokenLength()) {
+                // -n
+                // -n <signed-integer>
+                // -n ['+' | '-'] <signless-integer>
+                case 2:
+                    this.scanner.next();
+                    b = consumeB.call(this);
+                    break;
+
+                // -n- <signless-integer>
+                case 3:
+                    expectCharCode.call(this, 2, HYPHENMINUS);
+
+                    this.scanner.next();
+                    this.scanner.skipSC();
+
+                    checkTokenIsInteger.call(this, DISALLOW_SIGN);
+
+                    b = '-' + this.consume(NUMBER);
+                    break;
+
+                // <dashndashdigit-ident>
+                default:
+                    expectCharCode.call(this, 2, HYPHENMINUS);
+                    checkInteger.call(this, 3, DISALLOW_SIGN);
+                    this.scanner.next();
+
+                    b = this.scanner.substrToCursor(start + 2);
+            }
+        }
+
+        // '+'? n
+        // '+'? n <signed-integer>
+        // '+'? n ['+' | '-'] <signless-integer>
+        // '+'? n- <signless-integer>
+        // '+'? <ndashdigit-ident>
+        else if (this.scanner.tokenType === IDENT || (this.scanner.isDelim(PLUSSIGN) && this.scanner.lookupType(1) === IDENT)) {
+            var sign = 0;
+            a = '1';
+
+            // just ignore a plus
+            if (this.scanner.isDelim(PLUSSIGN)) {
+                sign = 1;
+                this.scanner.next();
+            }
+
+            expectCharCode.call(this, 0, N);
+
+            switch (this.scanner.getTokenLength()) {
+                // '+'? n
+                // '+'? n <signed-integer>
+                // '+'? n ['+' | '-'] <signless-integer>
+                case 1:
+                    this.scanner.next();
+                    b = consumeB.call(this);
+                    break;
+
+                // '+'? n- <signless-integer>
+                case 2:
+                    expectCharCode.call(this, 1, HYPHENMINUS);
+
+                    this.scanner.next();
+                    this.scanner.skipSC();
+
+                    checkTokenIsInteger.call(this, DISALLOW_SIGN);
+
+                    b = '-' + this.consume(NUMBER);
+                    break;
+
+                // '+'? <ndashdigit-ident>
+                default:
+                    expectCharCode.call(this, 1, HYPHENMINUS);
+                    checkInteger.call(this, 2, DISALLOW_SIGN);
+                    this.scanner.next();
+
+                    b = this.scanner.substrToCursor(start + sign + 1);
+            }
+        }
+
+        // <ndashdigit-dimension>
+        // <ndash-dimension> <signless-integer>
+        // <n-dimension>
+        // <n-dimension> <signed-integer>
+        // <n-dimension> ['+' | '-'] <signless-integer>
+        else if (this.scanner.tokenType === DIMENSION) {
+            var code = this.scanner.source.charCodeAt(this.scanner.tokenStart);
+            var sign = code === PLUSSIGN || code === HYPHENMINUS;
+
+            for (var i = this.scanner.tokenStart + sign; i < this.scanner.tokenEnd; i++) {
+                if (!isDigit(this.scanner.source.charCodeAt(i))) {
+                    break;
+                }
+            }
+
+            if (i === this.scanner.tokenStart + sign) {
+                this.error('Integer is expected', this.scanner.tokenStart + sign);
+            }
+
+            expectCharCode.call(this, i - this.scanner.tokenStart, N);
+            a = this.scanner.source.substring(start, i);
+
+            // <n-dimension>
+            // <n-dimension> <signed-integer>
+            // <n-dimension> ['+' | '-'] <signless-integer>
+            if (i + 1 === this.scanner.tokenEnd) {
+                this.scanner.next();
+                b = consumeB.call(this);
+            } else {
+                expectCharCode.call(this, i - this.scanner.tokenStart + 1, HYPHENMINUS);
+
+                // <ndash-dimension> <signless-integer>
+                if (i + 2 === this.scanner.tokenEnd) {
+                    this.scanner.next();
+                    this.scanner.skipSC();
+                    checkTokenIsInteger.call(this, DISALLOW_SIGN);
+                    b = '-' + this.consume(NUMBER);
+                }
+                // <ndashdigit-dimension>
+                else {
+                    checkInteger.call(this, i - this.scanner.tokenStart + 2, DISALLOW_SIGN);
+                    this.scanner.next();
+                    b = this.scanner.substrToCursor(i + 1);
+                }
+            }
+        } else {
+            this.error();
+        }
+
+        if (a !== null && a.charCodeAt(0) === PLUSSIGN) {
+            a = a.substr(1);
+        }
+
+        if (b !== null && b.charCodeAt(0) === PLUSSIGN) {
+            b = b.substr(1);
+        }
+
+        return {
+            type: 'AnPlusB',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            a: a,
+            b: b
+        };
+    },
+    generate: function(node) {
+        var a = node.a !== null && node.a !== undefined;
+        var b = node.b !== null && node.b !== undefined;
+
+        if (a) {
+            this.chunk(
+                node.a === '+1' ? '+n' : // eslint-disable-line operator-linebreak, indent
+                node.a ===  '1' ?  'n' : // eslint-disable-line operator-linebreak, indent
+                node.a === '-1' ? '-n' : // eslint-disable-line operator-linebreak, indent
+                node.a + 'n'             // eslint-disable-line operator-linebreak, indent
+            );
+
+            if (b) {
+                b = String(node.b);
+                if (b.charAt(0) === '-' || b.charAt(0) === '+') {
+                    this.chunk(b.charAt(0));
+                    this.chunk(b.substr(1));
+                } else {
+                    this.chunk('+');
+                    this.chunk(b);
+                }
+            }
+        } else {
+            this.chunk(String(node.b));
+        }
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Atrule.js b/node_modules/css-tree/lib/syntax/node/Atrule.js
new file mode 100644
index 0000000..284499f
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Atrule.js
@@ -0,0 +1,107 @@
+var TYPE = require('../../tokenizer').TYPE;
+var rawMode = require('./Raw').mode;
+
+var ATKEYWORD = TYPE.AtKeyword;
+var SEMICOLON = TYPE.Semicolon;
+var LEFTCURLYBRACKET = TYPE.LeftCurlyBracket;
+var RIGHTCURLYBRACKET = TYPE.RightCurlyBracket;
+
+function consumeRaw(startToken) {
+    return this.Raw(startToken, rawMode.leftCurlyBracketOrSemicolon, true);
+}
+
+function isDeclarationBlockAtrule() {
+    for (var offset = 1, type; type = this.scanner.lookupType(offset); offset++) {
+        if (type === RIGHTCURLYBRACKET) {
+            return true;
+        }
+
+        if (type === LEFTCURLYBRACKET ||
+            type === ATKEYWORD) {
+            return false;
+        }
+    }
+
+    return false;
+}
+
+module.exports = {
+    name: 'Atrule',
+    structure: {
+        name: String,
+        prelude: ['AtrulePrelude', 'Raw', null],
+        block: ['Block', null]
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var name;
+        var nameLowerCase;
+        var prelude = null;
+        var block = null;
+
+        this.eat(ATKEYWORD);
+
+        name = this.scanner.substrToCursor(start + 1);
+        nameLowerCase = name.toLowerCase();
+        this.scanner.skipSC();
+
+        // parse prelude
+        if (this.scanner.eof === false &&
+            this.scanner.tokenType !== LEFTCURLYBRACKET &&
+            this.scanner.tokenType !== SEMICOLON) {
+            if (this.parseAtrulePrelude) {
+                prelude = this.parseWithFallback(this.AtrulePrelude.bind(this, name), consumeRaw);
+
+                // turn empty AtrulePrelude into null
+                if (prelude.type === 'AtrulePrelude' && prelude.children.head === null) {
+                    prelude = null;
+                }
+            } else {
+                prelude = consumeRaw.call(this, this.scanner.tokenIndex);
+            }
+
+            this.scanner.skipSC();
+        }
+
+        switch (this.scanner.tokenType) {
+            case SEMICOLON:
+                this.scanner.next();
+                break;
+
+            case LEFTCURLYBRACKET:
+                if (this.atrule.hasOwnProperty(nameLowerCase) &&
+                    typeof this.atrule[nameLowerCase].block === 'function') {
+                    block = this.atrule[nameLowerCase].block.call(this);
+                } else {
+                    // TODO: should consume block content as Raw?
+                    block = this.Block(isDeclarationBlockAtrule.call(this));
+                }
+
+                break;
+        }
+
+        return {
+            type: 'Atrule',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            name: name,
+            prelude: prelude,
+            block: block
+        };
+    },
+    generate: function(node) {
+        this.chunk('@');
+        this.chunk(node.name);
+
+        if (node.prelude !== null) {
+            this.chunk(' ');
+            this.node(node.prelude);
+        }
+
+        if (node.block) {
+            this.node(node.block);
+        } else {
+            this.chunk(';');
+        }
+    },
+    walkContext: 'atrule'
+};
diff --git a/node_modules/css-tree/lib/syntax/node/AtrulePrelude.js b/node_modules/css-tree/lib/syntax/node/AtrulePrelude.js
new file mode 100644
index 0000000..f9f21ff
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/AtrulePrelude.js
@@ -0,0 +1,51 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var SEMICOLON = TYPE.Semicolon;
+var LEFTCURLYBRACKET = TYPE.LeftCurlyBracket;
+
+module.exports = {
+    name: 'AtrulePrelude',
+    structure: {
+        children: [[]]
+    },
+    parse: function(name) {
+        var children = null;
+
+        if (name !== null) {
+            name = name.toLowerCase();
+        }
+
+        this.scanner.skipSC();
+
+        if (this.atrule.hasOwnProperty(name) &&
+            typeof this.atrule[name].prelude === 'function') {
+            // custom consumer
+            children = this.atrule[name].prelude.call(this);
+        } else {
+            // default consumer
+            children = this.readSequence(this.scope.AtrulePrelude);
+        }
+
+        this.scanner.skipSC();
+
+        if (this.scanner.eof !== true &&
+            this.scanner.tokenType !== LEFTCURLYBRACKET &&
+            this.scanner.tokenType !== SEMICOLON) {
+            this.error('Semicolon or block is expected');
+        }
+
+        if (children === null) {
+            children = this.createList();
+        }
+
+        return {
+            type: 'AtrulePrelude',
+            loc: this.getLocationFromList(children),
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.children(node);
+    },
+    walkContext: 'atrulePrelude'
+};
diff --git a/node_modules/css-tree/lib/syntax/node/AttributeSelector.js b/node_modules/css-tree/lib/syntax/node/AttributeSelector.js
new file mode 100644
index 0000000..656446c
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/AttributeSelector.js
@@ -0,0 +1,165 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var IDENT = TYPE.Ident;
+var STRING = TYPE.String;
+var COLON = TYPE.Colon;
+var LEFTSQUAREBRACKET = TYPE.LeftSquareBracket;
+var RIGHTSQUAREBRACKET = TYPE.RightSquareBracket;
+var DOLLARSIGN = 0x0024;       // U+0024 DOLLAR SIGN ($)
+var ASTERISK = 0x002A;         // U+002A ASTERISK (*)
+var EQUALSSIGN = 0x003D;       // U+003D EQUALS SIGN (=)
+var CIRCUMFLEXACCENT = 0x005E; // U+005E (^)
+var VERTICALLINE = 0x007C;     // U+007C VERTICAL LINE (|)
+var TILDE = 0x007E;            // U+007E TILDE (~)
+
+function getAttributeName() {
+    if (this.scanner.eof) {
+        this.error('Unexpected end of input');
+    }
+
+    var start = this.scanner.tokenStart;
+    var expectIdent = false;
+    var checkColon = true;
+
+    if (this.scanner.isDelim(ASTERISK)) {
+        expectIdent = true;
+        checkColon = false;
+        this.scanner.next();
+    } else if (!this.scanner.isDelim(VERTICALLINE)) {
+        this.eat(IDENT);
+    }
+
+    if (this.scanner.isDelim(VERTICALLINE)) {
+        if (this.scanner.source.charCodeAt(this.scanner.tokenStart + 1) !== EQUALSSIGN) {
+            this.scanner.next();
+            this.eat(IDENT);
+        } else if (expectIdent) {
+            this.error('Identifier is expected', this.scanner.tokenEnd);
+        }
+    } else if (expectIdent) {
+        this.error('Vertical line is expected');
+    }
+
+    if (checkColon && this.scanner.tokenType === COLON) {
+        this.scanner.next();
+        this.eat(IDENT);
+    }
+
+    return {
+        type: 'Identifier',
+        loc: this.getLocation(start, this.scanner.tokenStart),
+        name: this.scanner.substrToCursor(start)
+    };
+}
+
+function getOperator() {
+    var start = this.scanner.tokenStart;
+    var code = this.scanner.source.charCodeAt(start);
+
+    if (code !== EQUALSSIGN &&        // =
+        code !== TILDE &&             // ~=
+        code !== CIRCUMFLEXACCENT &&  // ^=
+        code !== DOLLARSIGN &&        // $=
+        code !== ASTERISK &&          // *=
+        code !== VERTICALLINE         // |=
+    ) {
+        this.error('Attribute selector (=, ~=, ^=, $=, *=, |=) is expected');
+    }
+
+    this.scanner.next();
+
+    if (code !== EQUALSSIGN) {
+        if (!this.scanner.isDelim(EQUALSSIGN)) {
+            this.error('Equal sign is expected');
+        }
+
+        this.scanner.next();
+    }
+
+    return this.scanner.substrToCursor(start);
+}
+
+// '[' <wq-name> ']'
+// '[' <wq-name> <attr-matcher> [ <string-token> | <ident-token> ] <attr-modifier>? ']'
+module.exports = {
+    name: 'AttributeSelector',
+    structure: {
+        name: 'Identifier',
+        matcher: [String, null],
+        value: ['String', 'Identifier', null],
+        flags: [String, null]
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var name;
+        var matcher = null;
+        var value = null;
+        var flags = null;
+
+        this.eat(LEFTSQUAREBRACKET);
+        this.scanner.skipSC();
+
+        name = getAttributeName.call(this);
+        this.scanner.skipSC();
+
+        if (this.scanner.tokenType !== RIGHTSQUAREBRACKET) {
+            // avoid case `[name i]`
+            if (this.scanner.tokenType !== IDENT) {
+                matcher = getOperator.call(this);
+
+                this.scanner.skipSC();
+
+                value = this.scanner.tokenType === STRING
+                    ? this.String()
+                    : this.Identifier();
+
+                this.scanner.skipSC();
+            }
+
+            // attribute flags
+            if (this.scanner.tokenType === IDENT) {
+                flags = this.scanner.getTokenValue();
+                this.scanner.next();
+
+                this.scanner.skipSC();
+            }
+        }
+
+        this.eat(RIGHTSQUAREBRACKET);
+
+        return {
+            type: 'AttributeSelector',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            name: name,
+            matcher: matcher,
+            value: value,
+            flags: flags
+        };
+    },
+    generate: function(node) {
+        var flagsPrefix = ' ';
+
+        this.chunk('[');
+        this.node(node.name);
+
+        if (node.matcher !== null) {
+            this.chunk(node.matcher);
+
+            if (node.value !== null) {
+                this.node(node.value);
+
+                // space between string and flags is not required
+                if (node.value.type === 'String') {
+                    flagsPrefix = '';
+                }
+            }
+        }
+
+        if (node.flags !== null) {
+            this.chunk(flagsPrefix);
+            this.chunk(node.flags);
+        }
+
+        this.chunk(']');
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Block.js b/node_modules/css-tree/lib/syntax/node/Block.js
new file mode 100644
index 0000000..b4fc6ba
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Block.js
@@ -0,0 +1,91 @@
+var TYPE = require('../../tokenizer').TYPE;
+var rawMode = require('./Raw').mode;
+
+var WHITESPACE = TYPE.WhiteSpace;
+var COMMENT = TYPE.Comment;
+var SEMICOLON = TYPE.Semicolon;
+var ATKEYWORD = TYPE.AtKeyword;
+var LEFTCURLYBRACKET = TYPE.LeftCurlyBracket;
+var RIGHTCURLYBRACKET = TYPE.RightCurlyBracket;
+
+function consumeRaw(startToken) {
+    return this.Raw(startToken, null, true);
+}
+function consumeRule() {
+    return this.parseWithFallback(this.Rule, consumeRaw);
+}
+function consumeRawDeclaration(startToken) {
+    return this.Raw(startToken, rawMode.semicolonIncluded, true);
+}
+function consumeDeclaration() {
+    if (this.scanner.tokenType === SEMICOLON) {
+        return consumeRawDeclaration.call(this, this.scanner.tokenIndex);
+    }
+
+    var node = this.parseWithFallback(this.Declaration, consumeRawDeclaration);
+
+    if (this.scanner.tokenType === SEMICOLON) {
+        this.scanner.next();
+    }
+
+    return node;
+}
+
+module.exports = {
+    name: 'Block',
+    structure: {
+        children: [[
+            'Atrule',
+            'Rule',
+            'Declaration'
+        ]]
+    },
+    parse: function(isDeclaration) {
+        var consumer = isDeclaration ? consumeDeclaration : consumeRule;
+
+        var start = this.scanner.tokenStart;
+        var children = this.createList();
+
+        this.eat(LEFTCURLYBRACKET);
+
+        scan:
+        while (!this.scanner.eof) {
+            switch (this.scanner.tokenType) {
+                case RIGHTCURLYBRACKET:
+                    break scan;
+
+                case WHITESPACE:
+                case COMMENT:
+                    this.scanner.next();
+                    break;
+
+                case ATKEYWORD:
+                    children.push(this.parseWithFallback(this.Atrule, consumeRaw));
+                    break;
+
+                default:
+                    children.push(consumer.call(this));
+            }
+        }
+
+        if (!this.scanner.eof) {
+            this.eat(RIGHTCURLYBRACKET);
+        }
+
+        return {
+            type: 'Block',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.chunk('{');
+        this.children(node, function(prev) {
+            if (prev.type === 'Declaration') {
+                this.chunk(';');
+            }
+        });
+        this.chunk('}');
+    },
+    walkContext: 'block'
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Brackets.js b/node_modules/css-tree/lib/syntax/node/Brackets.js
new file mode 100644
index 0000000..a81c474
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Brackets.js
@@ -0,0 +1,34 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var LEFTSQUAREBRACKET = TYPE.LeftSquareBracket;
+var RIGHTSQUAREBRACKET = TYPE.RightSquareBracket;
+
+module.exports = {
+    name: 'Brackets',
+    structure: {
+        children: [[]]
+    },
+    parse: function(readSequence, recognizer) {
+        var start = this.scanner.tokenStart;
+        var children = null;
+
+        this.eat(LEFTSQUAREBRACKET);
+
+        children = readSequence.call(this, recognizer);
+
+        if (!this.scanner.eof) {
+            this.eat(RIGHTSQUAREBRACKET);
+        }
+
+        return {
+            type: 'Brackets',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.chunk('[');
+        this.children(node);
+        this.chunk(']');
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/CDC.js b/node_modules/css-tree/lib/syntax/node/CDC.js
new file mode 100644
index 0000000..885b258
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/CDC.js
@@ -0,0 +1,19 @@
+var CDC = require('../../tokenizer').TYPE.CDC;
+
+module.exports = {
+    name: 'CDC',
+    structure: [],
+    parse: function() {
+        var start = this.scanner.tokenStart;
+
+        this.eat(CDC); // -->
+
+        return {
+            type: 'CDC',
+            loc: this.getLocation(start, this.scanner.tokenStart)
+        };
+    },
+    generate: function() {
+        this.chunk('-->');
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/CDO.js b/node_modules/css-tree/lib/syntax/node/CDO.js
new file mode 100644
index 0000000..f701be7
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/CDO.js
@@ -0,0 +1,19 @@
+var CDO = require('../../tokenizer').TYPE.CDO;
+
+module.exports = {
+    name: 'CDO',
+    structure: [],
+    parse: function() {
+        var start = this.scanner.tokenStart;
+
+        this.eat(CDO); // <!--
+
+        return {
+            type: 'CDO',
+            loc: this.getLocation(start, this.scanner.tokenStart)
+        };
+    },
+    generate: function() {
+        this.chunk('<!--');
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/ClassSelector.js b/node_modules/css-tree/lib/syntax/node/ClassSelector.js
new file mode 100644
index 0000000..c84d69b
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/ClassSelector.js
@@ -0,0 +1,29 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var IDENT = TYPE.Ident;
+var FULLSTOP = 0x002E; // U+002E FULL STOP (.)
+
+// '.' ident
+module.exports = {
+    name: 'ClassSelector',
+    structure: {
+        name: String
+    },
+    parse: function() {
+        if (!this.scanner.isDelim(FULLSTOP)) {
+            this.error('Full stop is expected');
+        }
+
+        this.scanner.next();
+
+        return {
+            type: 'ClassSelector',
+            loc: this.getLocation(this.scanner.tokenStart - 1, this.scanner.tokenEnd),
+            name: this.consume(IDENT)
+        };
+    },
+    generate: function(node) {
+        this.chunk('.');
+        this.chunk(node.name);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Combinator.js b/node_modules/css-tree/lib/syntax/node/Combinator.js
new file mode 100644
index 0000000..308ca81
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Combinator.js
@@ -0,0 +1,55 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var IDENT = TYPE.Ident;
+var PLUSSIGN = 0x002B;        // U+002B PLUS SIGN (+)
+var SOLIDUS = 0x002F;         // U+002F SOLIDUS (/)
+var GREATERTHANSIGN = 0x003E; // U+003E GREATER-THAN SIGN (>)
+var TILDE = 0x007E;           // U+007E TILDE (~)
+
+// + | > | ~ | /deep/
+module.exports = {
+    name: 'Combinator',
+    structure: {
+        name: String
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var code = this.scanner.source.charCodeAt(this.scanner.tokenStart);
+
+        switch (code) {
+            case GREATERTHANSIGN:
+            case PLUSSIGN:
+            case TILDE:
+                this.scanner.next();
+                break;
+
+            case SOLIDUS:
+                this.scanner.next();
+
+                if (this.scanner.tokenType !== IDENT || this.scanner.lookupValue(0, 'deep') === false) {
+                    this.error('Identifier `deep` is expected');
+                }
+
+                this.scanner.next();
+
+                if (!this.scanner.isDelim(SOLIDUS)) {
+                    this.error('Solidus is expected');
+                }
+
+                this.scanner.next();
+                break;
+
+            default:
+                this.error('Combinator is expected');
+        }
+
+        return {
+            type: 'Combinator',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            name: this.scanner.substrToCursor(start)
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.name);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Comment.js b/node_modules/css-tree/lib/syntax/node/Comment.js
new file mode 100644
index 0000000..9f0e2f4
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Comment.js
@@ -0,0 +1,36 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var COMMENT = TYPE.Comment;
+var ASTERISK = 0x002A;        // U+002A ASTERISK (*)
+var SOLIDUS = 0x002F;         // U+002F SOLIDUS (/)
+
+// '/*' .* '*/'
+module.exports = {
+    name: 'Comment',
+    structure: {
+        value: String
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var end = this.scanner.tokenEnd;
+
+        this.eat(COMMENT);
+
+        if ((end - start + 2) >= 2 &&
+            this.scanner.source.charCodeAt(end - 2) === ASTERISK &&
+            this.scanner.source.charCodeAt(end - 1) === SOLIDUS) {
+            end -= 2;
+        }
+
+        return {
+            type: 'Comment',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            value: this.scanner.source.substring(start + 2, end)
+        };
+    },
+    generate: function(node) {
+        this.chunk('/*');
+        this.chunk(node.value);
+        this.chunk('*/');
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Declaration.js b/node_modules/css-tree/lib/syntax/node/Declaration.js
new file mode 100644
index 0000000..0792836
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Declaration.js
@@ -0,0 +1,169 @@
+var isCustomProperty = require('../../utils/names').isCustomProperty;
+var TYPE = require('../../tokenizer').TYPE;
+var rawMode = require('./Raw').mode;
+
+var IDENT = TYPE.Ident;
+var HASH = TYPE.Hash;
+var COLON = TYPE.Colon;
+var SEMICOLON = TYPE.Semicolon;
+var DELIM = TYPE.Delim;
+var WHITESPACE = TYPE.WhiteSpace;
+var EXCLAMATIONMARK = 0x0021; // U+0021 EXCLAMATION MARK (!)
+var NUMBERSIGN = 0x0023;      // U+0023 NUMBER SIGN (#)
+var DOLLARSIGN = 0x0024;      // U+0024 DOLLAR SIGN ($)
+var AMPERSAND = 0x0026;       // U+0026 ANPERSAND (&)
+var ASTERISK = 0x002A;        // U+002A ASTERISK (*)
+var PLUSSIGN = 0x002B;        // U+002B PLUS SIGN (+)
+var SOLIDUS = 0x002F;         // U+002F SOLIDUS (/)
+
+function consumeValueRaw(startToken) {
+    return this.Raw(startToken, rawMode.exclamationMarkOrSemicolon, true);
+}
+
+function consumeCustomPropertyRaw(startToken) {
+    return this.Raw(startToken, rawMode.exclamationMarkOrSemicolon, false);
+}
+
+function consumeValue() {
+    var startValueToken = this.scanner.tokenIndex;
+    var value = this.Value();
+
+    if (value.type !== 'Raw' &&
+        this.scanner.eof === false &&
+        this.scanner.tokenType !== SEMICOLON &&
+        this.scanner.isDelim(EXCLAMATIONMARK) === false &&
+        this.scanner.isBalanceEdge(startValueToken) === false) {
+        this.error();
+    }
+
+    return value;
+}
+
+module.exports = {
+    name: 'Declaration',
+    structure: {
+        important: [Boolean, String],
+        property: String,
+        value: ['Value', 'Raw']
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var startToken = this.scanner.tokenIndex;
+        var property = readProperty.call(this);
+        var customProperty = isCustomProperty(property);
+        var parseValue = customProperty ? this.parseCustomProperty : this.parseValue;
+        var consumeRaw = customProperty ? consumeCustomPropertyRaw : consumeValueRaw;
+        var important = false;
+        var value;
+
+        this.scanner.skipSC();
+        this.eat(COLON);
+
+        const valueStart = this.scanner.tokenIndex;
+
+        if (!customProperty) {
+            this.scanner.skipSC();
+        }
+
+        if (parseValue) {
+            value = this.parseWithFallback(consumeValue, consumeRaw);
+        } else {
+            value = consumeRaw.call(this, this.scanner.tokenIndex);
+        }
+
+        if (customProperty && value.type === 'Value' && value.children.isEmpty()) {
+            for (let offset = valueStart - this.scanner.tokenIndex; offset <= 0; offset++) {
+                if (this.scanner.lookupType(offset) === WHITESPACE) {
+                    value.children.appendData({
+                        type: 'WhiteSpace',
+                        loc: null,
+                        value: ' '
+                    });
+                    break;
+                }
+            }
+        }
+
+        if (this.scanner.isDelim(EXCLAMATIONMARK)) {
+            important = getImportant.call(this);
+            this.scanner.skipSC();
+        }
+
+        // Do not include semicolon to range per spec
+        // https://drafts.csswg.org/css-syntax/#declaration-diagram
+
+        if (this.scanner.eof === false &&
+            this.scanner.tokenType !== SEMICOLON &&
+            this.scanner.isBalanceEdge(startToken) === false) {
+            this.error();
+        }
+
+        return {
+            type: 'Declaration',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            important: important,
+            property: property,
+            value: value
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.property);
+        this.chunk(':');
+        this.node(node.value);
+
+        if (node.important) {
+            this.chunk(node.important === true ? '!important' : '!' + node.important);
+        }
+    },
+    walkContext: 'declaration'
+};
+
+function readProperty() {
+    var start = this.scanner.tokenStart;
+    var prefix = 0;
+
+    // hacks
+    if (this.scanner.tokenType === DELIM) {
+        switch (this.scanner.source.charCodeAt(this.scanner.tokenStart)) {
+            case ASTERISK:
+            case DOLLARSIGN:
+            case PLUSSIGN:
+            case NUMBERSIGN:
+            case AMPERSAND:
+                this.scanner.next();
+                break;
+
+            // TODO: not sure we should support this hack
+            case SOLIDUS:
+                this.scanner.next();
+                if (this.scanner.isDelim(SOLIDUS)) {
+                    this.scanner.next();
+                }
+                break;
+        }
+    }
+
+    if (prefix) {
+        this.scanner.skip(prefix);
+    }
+
+    if (this.scanner.tokenType === HASH) {
+        this.eat(HASH);
+    } else {
+        this.eat(IDENT);
+    }
+
+    return this.scanner.substrToCursor(start);
+}
+
+// ! ws* important
+function getImportant() {
+    this.eat(DELIM);
+    this.scanner.skipSC();
+
+    var important = this.consume(IDENT);
+
+    // store original value in case it differ from `important`
+    // for better original source restoring and hacks like `!ie` support
+    return important === 'important' ? true : important;
+}
diff --git a/node_modules/css-tree/lib/syntax/node/DeclarationList.js b/node_modules/css-tree/lib/syntax/node/DeclarationList.js
new file mode 100644
index 0000000..084b5c6
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/DeclarationList.js
@@ -0,0 +1,49 @@
+var TYPE = require('../../tokenizer').TYPE;
+var rawMode = require('./Raw').mode;
+
+var WHITESPACE = TYPE.WhiteSpace;
+var COMMENT = TYPE.Comment;
+var SEMICOLON = TYPE.Semicolon;
+
+function consumeRaw(startToken) {
+    return this.Raw(startToken, rawMode.semicolonIncluded, true);
+}
+
+module.exports = {
+    name: 'DeclarationList',
+    structure: {
+        children: [[
+            'Declaration'
+        ]]
+    },
+    parse: function() {
+        var children = this.createList();
+
+        scan:
+        while (!this.scanner.eof) {
+            switch (this.scanner.tokenType) {
+                case WHITESPACE:
+                case COMMENT:
+                case SEMICOLON:
+                    this.scanner.next();
+                    break;
+
+                default:
+                    children.push(this.parseWithFallback(this.Declaration, consumeRaw));
+            }
+        }
+
+        return {
+            type: 'DeclarationList',
+            loc: this.getLocationFromList(children),
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.children(node, function(prev) {
+            if (prev.type === 'Declaration') {
+                this.chunk(';');
+            }
+        });
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Dimension.js b/node_modules/css-tree/lib/syntax/node/Dimension.js
new file mode 100644
index 0000000..186f6ee
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Dimension.js
@@ -0,0 +1,29 @@
+var consumeNumber = require('../../tokenizer/utils').consumeNumber;
+var TYPE = require('../../tokenizer').TYPE;
+
+var DIMENSION = TYPE.Dimension;
+
+module.exports = {
+    name: 'Dimension',
+    structure: {
+        value: String,
+        unit: String
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var numberEnd = consumeNumber(this.scanner.source, start);
+
+        this.eat(DIMENSION);
+
+        return {
+            type: 'Dimension',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            value: this.scanner.source.substring(start, numberEnd),
+            unit: this.scanner.source.substring(numberEnd, this.scanner.tokenStart)
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.value);
+        this.chunk(node.unit);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Function.js b/node_modules/css-tree/lib/syntax/node/Function.js
new file mode 100644
index 0000000..9e6b420
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Function.js
@@ -0,0 +1,40 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var RIGHTPARENTHESIS = TYPE.RightParenthesis;
+
+// <function-token> <sequence> )
+module.exports = {
+    name: 'Function',
+    structure: {
+        name: String,
+        children: [[]]
+    },
+    parse: function(readSequence, recognizer) {
+        var start = this.scanner.tokenStart;
+        var name = this.consumeFunctionName();
+        var nameLowerCase = name.toLowerCase();
+        var children;
+
+        children = recognizer.hasOwnProperty(nameLowerCase)
+            ? recognizer[nameLowerCase].call(this, recognizer)
+            : readSequence.call(this, recognizer);
+
+        if (!this.scanner.eof) {
+            this.eat(RIGHTPARENTHESIS);
+        }
+
+        return {
+            type: 'Function',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            name: name,
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.name);
+        this.chunk('(');
+        this.children(node);
+        this.chunk(')');
+    },
+    walkContext: 'function'
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Hash.js b/node_modules/css-tree/lib/syntax/node/Hash.js
new file mode 100644
index 0000000..5632636
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Hash.js
@@ -0,0 +1,26 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var HASH = TYPE.Hash;
+
+// '#' ident
+module.exports = {
+    name: 'Hash',
+    structure: {
+        value: String
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+
+        this.eat(HASH);
+
+        return {
+            type: 'Hash',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            value: this.scanner.substrToCursor(start + 1)
+        };
+    },
+    generate: function(node) {
+        this.chunk('#');
+        this.chunk(node.value);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/IdSelector.js b/node_modules/css-tree/lib/syntax/node/IdSelector.js
new file mode 100644
index 0000000..b35fd96
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/IdSelector.js
@@ -0,0 +1,27 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var HASH = TYPE.Hash;
+
+// <hash-token>
+module.exports = {
+    name: 'IdSelector',
+    structure: {
+        name: String
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+
+        // TODO: check value is an ident
+        this.eat(HASH);
+
+        return {
+            type: 'IdSelector',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            name: this.scanner.substrToCursor(start + 1)
+        };
+    },
+    generate: function(node) {
+        this.chunk('#');
+        this.chunk(node.name);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Identifier.js b/node_modules/css-tree/lib/syntax/node/Identifier.js
new file mode 100644
index 0000000..3fce2b4
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Identifier.js
@@ -0,0 +1,20 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var IDENT = TYPE.Ident;
+
+module.exports = {
+    name: 'Identifier',
+    structure: {
+        name: String
+    },
+    parse: function() {
+        return {
+            type: 'Identifier',
+            loc: this.getLocation(this.scanner.tokenStart, this.scanner.tokenEnd),
+            name: this.consume(IDENT)
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.name);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/MediaFeature.js b/node_modules/css-tree/lib/syntax/node/MediaFeature.js
new file mode 100644
index 0000000..bdc7bc2
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/MediaFeature.js
@@ -0,0 +1,76 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var IDENT = TYPE.Ident;
+var NUMBER = TYPE.Number;
+var DIMENSION = TYPE.Dimension;
+var LEFTPARENTHESIS = TYPE.LeftParenthesis;
+var RIGHTPARENTHESIS = TYPE.RightParenthesis;
+var COLON = TYPE.Colon;
+var DELIM = TYPE.Delim;
+
+module.exports = {
+    name: 'MediaFeature',
+    structure: {
+        name: String,
+        value: ['Identifier', 'Number', 'Dimension', 'Ratio', null]
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var name;
+        var value = null;
+
+        this.eat(LEFTPARENTHESIS);
+        this.scanner.skipSC();
+
+        name = this.consume(IDENT);
+        this.scanner.skipSC();
+
+        if (this.scanner.tokenType !== RIGHTPARENTHESIS) {
+            this.eat(COLON);
+            this.scanner.skipSC();
+
+            switch (this.scanner.tokenType) {
+                case NUMBER:
+                    if (this.lookupNonWSType(1) === DELIM) {
+                        value = this.Ratio();
+                    } else {
+                        value = this.Number();
+                    }
+
+                    break;
+
+                case DIMENSION:
+                    value = this.Dimension();
+                    break;
+
+                case IDENT:
+                    value = this.Identifier();
+
+                    break;
+
+                default:
+                    this.error('Number, dimension, ratio or identifier is expected');
+            }
+
+            this.scanner.skipSC();
+        }
+
+        this.eat(RIGHTPARENTHESIS);
+
+        return {
+            type: 'MediaFeature',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            name: name,
+            value: value
+        };
+    },
+    generate: function(node) {
+        this.chunk('(');
+        this.chunk(node.name);
+        if (node.value !== null) {
+            this.chunk(':');
+            this.node(node.value);
+        }
+        this.chunk(')');
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/MediaQuery.js b/node_modules/css-tree/lib/syntax/node/MediaQuery.js
new file mode 100644
index 0000000..e0c74ce
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/MediaQuery.js
@@ -0,0 +1,68 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var WHITESPACE = TYPE.WhiteSpace;
+var COMMENT = TYPE.Comment;
+var IDENT = TYPE.Ident;
+var LEFTPARENTHESIS = TYPE.LeftParenthesis;
+
+module.exports = {
+    name: 'MediaQuery',
+    structure: {
+        children: [[
+            'Identifier',
+            'MediaFeature',
+            'WhiteSpace'
+        ]]
+    },
+    parse: function() {
+        this.scanner.skipSC();
+
+        var children = this.createList();
+        var child = null;
+        var space = null;
+
+        scan:
+        while (!this.scanner.eof) {
+            switch (this.scanner.tokenType) {
+                case COMMENT:
+                    this.scanner.next();
+                    continue;
+
+                case WHITESPACE:
+                    space = this.WhiteSpace();
+                    continue;
+
+                case IDENT:
+                    child = this.Identifier();
+                    break;
+
+                case LEFTPARENTHESIS:
+                    child = this.MediaFeature();
+                    break;
+
+                default:
+                    break scan;
+            }
+
+            if (space !== null) {
+                children.push(space);
+                space = null;
+            }
+
+            children.push(child);
+        }
+
+        if (child === null) {
+            this.error('Identifier or parenthesis is expected');
+        }
+
+        return {
+            type: 'MediaQuery',
+            loc: this.getLocationFromList(children),
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.children(node);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/MediaQueryList.js b/node_modules/css-tree/lib/syntax/node/MediaQueryList.js
new file mode 100644
index 0000000..a25a965
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/MediaQueryList.js
@@ -0,0 +1,36 @@
+var COMMA = require('../../tokenizer').TYPE.Comma;
+
+module.exports = {
+    name: 'MediaQueryList',
+    structure: {
+        children: [[
+            'MediaQuery'
+        ]]
+    },
+    parse: function(relative) {
+        var children = this.createList();
+
+        this.scanner.skipSC();
+
+        while (!this.scanner.eof) {
+            children.push(this.MediaQuery(relative));
+
+            if (this.scanner.tokenType !== COMMA) {
+                break;
+            }
+
+            this.scanner.next();
+        }
+
+        return {
+            type: 'MediaQueryList',
+            loc: this.getLocationFromList(children),
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.children(node, function() {
+            this.chunk(',');
+        });
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Nth.js b/node_modules/css-tree/lib/syntax/node/Nth.js
new file mode 100644
index 0000000..43835cc
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Nth.js
@@ -0,0 +1,51 @@
+module.exports = {
+    name: 'Nth',
+    structure: {
+        nth: ['AnPlusB', 'Identifier'],
+        selector: ['SelectorList', null]
+    },
+    parse: function(allowOfClause) {
+        this.scanner.skipSC();
+
+        var start = this.scanner.tokenStart;
+        var end = start;
+        var selector = null;
+        var query;
+
+        if (this.scanner.lookupValue(0, 'odd') || this.scanner.lookupValue(0, 'even')) {
+            query = this.Identifier();
+        } else {
+            query = this.AnPlusB();
+        }
+
+        this.scanner.skipSC();
+
+        if (allowOfClause && this.scanner.lookupValue(0, 'of')) {
+            this.scanner.next();
+
+            selector = this.SelectorList();
+
+            if (this.needPositions) {
+                end = this.getLastListNode(selector.children).loc.end.offset;
+            }
+        } else {
+            if (this.needPositions) {
+                end = query.loc.end.offset;
+            }
+        }
+
+        return {
+            type: 'Nth',
+            loc: this.getLocation(start, end),
+            nth: query,
+            selector: selector
+        };
+    },
+    generate: function(node) {
+        this.node(node.nth);
+        if (node.selector !== null) {
+            this.chunk(' of ');
+            this.node(node.selector);
+        }
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Number.js b/node_modules/css-tree/lib/syntax/node/Number.js
new file mode 100644
index 0000000..1c01936
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Number.js
@@ -0,0 +1,18 @@
+var NUMBER = require('../../tokenizer').TYPE.Number;
+
+module.exports = {
+    name: 'Number',
+    structure: {
+        value: String
+    },
+    parse: function() {
+        return {
+            type: 'Number',
+            loc: this.getLocation(this.scanner.tokenStart, this.scanner.tokenEnd),
+            value: this.consume(NUMBER)
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.value);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Operator.js b/node_modules/css-tree/lib/syntax/node/Operator.js
new file mode 100644
index 0000000..d94259a
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Operator.js
@@ -0,0 +1,21 @@
+// '/' | '*' | ',' | ':' | '+' | '-'
+module.exports = {
+    name: 'Operator',
+    structure: {
+        value: String
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+
+        this.scanner.next();
+
+        return {
+            type: 'Operator',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            value: this.scanner.substrToCursor(start)
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.value);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Parentheses.js b/node_modules/css-tree/lib/syntax/node/Parentheses.js
new file mode 100644
index 0000000..19efe6f
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Parentheses.js
@@ -0,0 +1,34 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var LEFTPARENTHESIS = TYPE.LeftParenthesis;
+var RIGHTPARENTHESIS = TYPE.RightParenthesis;
+
+module.exports = {
+    name: 'Parentheses',
+    structure: {
+        children: [[]]
+    },
+    parse: function(readSequence, recognizer) {
+        var start = this.scanner.tokenStart;
+        var children = null;
+
+        this.eat(LEFTPARENTHESIS);
+
+        children = readSequence.call(this, recognizer);
+
+        if (!this.scanner.eof) {
+            this.eat(RIGHTPARENTHESIS);
+        }
+
+        return {
+            type: 'Parentheses',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.chunk('(');
+        this.children(node);
+        this.chunk(')');
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Percentage.js b/node_modules/css-tree/lib/syntax/node/Percentage.js
new file mode 100644
index 0000000..99b7641
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Percentage.js
@@ -0,0 +1,27 @@
+var consumeNumber = require('../../tokenizer/utils').consumeNumber;
+var TYPE = require('../../tokenizer').TYPE;
+
+var PERCENTAGE = TYPE.Percentage;
+
+module.exports = {
+    name: 'Percentage',
+    structure: {
+        value: String
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var numberEnd = consumeNumber(this.scanner.source, start);
+
+        this.eat(PERCENTAGE);
+
+        return {
+            type: 'Percentage',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            value: this.scanner.source.substring(start, numberEnd)
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.value);
+        this.chunk('%');
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/PseudoClassSelector.js b/node_modules/css-tree/lib/syntax/node/PseudoClassSelector.js
new file mode 100644
index 0000000..282e236
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/PseudoClassSelector.js
@@ -0,0 +1,61 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var IDENT = TYPE.Ident;
+var FUNCTION = TYPE.Function;
+var COLON = TYPE.Colon;
+var RIGHTPARENTHESIS = TYPE.RightParenthesis;
+
+// : [ <ident> | <function-token> <any-value>? ) ]
+module.exports = {
+    name: 'PseudoClassSelector',
+    structure: {
+        name: String,
+        children: [['Raw'], null]
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var children = null;
+        var name;
+        var nameLowerCase;
+
+        this.eat(COLON);
+
+        if (this.scanner.tokenType === FUNCTION) {
+            name = this.consumeFunctionName();
+            nameLowerCase = name.toLowerCase();
+
+            if (this.pseudo.hasOwnProperty(nameLowerCase)) {
+                this.scanner.skipSC();
+                children = this.pseudo[nameLowerCase].call(this);
+                this.scanner.skipSC();
+            } else {
+                children = this.createList();
+                children.push(
+                    this.Raw(this.scanner.tokenIndex, null, false)
+                );
+            }
+
+            this.eat(RIGHTPARENTHESIS);
+        } else {
+            name = this.consume(IDENT);
+        }
+
+        return {
+            type: 'PseudoClassSelector',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            name: name,
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.chunk(':');
+        this.chunk(node.name);
+
+        if (node.children !== null) {
+            this.chunk('(');
+            this.children(node);
+            this.chunk(')');
+        }
+    },
+    walkContext: 'function'
+};
diff --git a/node_modules/css-tree/lib/syntax/node/PseudoElementSelector.js b/node_modules/css-tree/lib/syntax/node/PseudoElementSelector.js
new file mode 100644
index 0000000..b736cea
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/PseudoElementSelector.js
@@ -0,0 +1,62 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var IDENT = TYPE.Ident;
+var FUNCTION = TYPE.Function;
+var COLON = TYPE.Colon;
+var RIGHTPARENTHESIS = TYPE.RightParenthesis;
+
+// :: [ <ident> | <function-token> <any-value>? ) ]
+module.exports = {
+    name: 'PseudoElementSelector',
+    structure: {
+        name: String,
+        children: [['Raw'], null]
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var children = null;
+        var name;
+        var nameLowerCase;
+
+        this.eat(COLON);
+        this.eat(COLON);
+
+        if (this.scanner.tokenType === FUNCTION) {
+            name = this.consumeFunctionName();
+            nameLowerCase = name.toLowerCase();
+
+            if (this.pseudo.hasOwnProperty(nameLowerCase)) {
+                this.scanner.skipSC();
+                children = this.pseudo[nameLowerCase].call(this);
+                this.scanner.skipSC();
+            } else {
+                children = this.createList();
+                children.push(
+                    this.Raw(this.scanner.tokenIndex, null, false)
+                );
+            }
+
+            this.eat(RIGHTPARENTHESIS);
+        } else {
+            name = this.consume(IDENT);
+        }
+
+        return {
+            type: 'PseudoElementSelector',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            name: name,
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.chunk('::');
+        this.chunk(node.name);
+
+        if (node.children !== null) {
+            this.chunk('(');
+            this.children(node);
+            this.chunk(')');
+        }
+    },
+    walkContext: 'function'
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Ratio.js b/node_modules/css-tree/lib/syntax/node/Ratio.js
new file mode 100644
index 0000000..940fee5
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Ratio.js
@@ -0,0 +1,66 @@
+var isDigit = require('../../tokenizer').isDigit;
+var TYPE = require('../../tokenizer').TYPE;
+
+var NUMBER = TYPE.Number;
+var DELIM = TYPE.Delim;
+var SOLIDUS = 0x002F;  // U+002F SOLIDUS (/)
+var FULLSTOP = 0x002E; // U+002E FULL STOP (.)
+
+// Terms of <ratio> should be a positive numbers (not zero or negative)
+// (see https://drafts.csswg.org/mediaqueries-3/#values)
+// However, -o-min-device-pixel-ratio takes fractional values as a ratio's term
+// and this is using by various sites. Therefore we relax checking on parse
+// to test a term is unsigned number without an exponent part.
+// Additional checking may be applied on lexer validation.
+function consumeNumber() {
+    this.scanner.skipWS();
+
+    var value = this.consume(NUMBER);
+
+    for (var i = 0; i < value.length; i++) {
+        var code = value.charCodeAt(i);
+        if (!isDigit(code) && code !== FULLSTOP) {
+            this.error('Unsigned number is expected', this.scanner.tokenStart - value.length + i);
+        }
+    }
+
+    if (Number(value) === 0) {
+        this.error('Zero number is not allowed', this.scanner.tokenStart - value.length);
+    }
+
+    return value;
+}
+
+// <positive-integer> S* '/' S* <positive-integer>
+module.exports = {
+    name: 'Ratio',
+    structure: {
+        left: String,
+        right: String
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var left = consumeNumber.call(this);
+        var right;
+
+        this.scanner.skipWS();
+
+        if (!this.scanner.isDelim(SOLIDUS)) {
+            this.error('Solidus is expected');
+        }
+        this.eat(DELIM);
+        right = consumeNumber.call(this);
+
+        return {
+            type: 'Ratio',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            left: left,
+            right: right
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.left);
+        this.chunk('/');
+        this.chunk(node.right);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Raw.js b/node_modules/css-tree/lib/syntax/node/Raw.js
new file mode 100644
index 0000000..a47deb4
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Raw.js
@@ -0,0 +1,87 @@
+var tokenizer = require('../../tokenizer');
+var TYPE = tokenizer.TYPE;
+
+var WhiteSpace = TYPE.WhiteSpace;
+var Semicolon = TYPE.Semicolon;
+var LeftCurlyBracket = TYPE.LeftCurlyBracket;
+var Delim = TYPE.Delim;
+var EXCLAMATIONMARK = 0x0021; // U+0021 EXCLAMATION MARK (!)
+
+function getOffsetExcludeWS() {
+    if (this.scanner.tokenIndex > 0) {
+        if (this.scanner.lookupType(-1) === WhiteSpace) {
+            return this.scanner.tokenIndex > 1
+                ? this.scanner.getTokenStart(this.scanner.tokenIndex - 1)
+                : this.scanner.firstCharOffset;
+        }
+    }
+
+    return this.scanner.tokenStart;
+}
+
+// 0, 0, false
+function balanceEnd() {
+    return 0;
+}
+
+// LEFTCURLYBRACKET, 0, false
+function leftCurlyBracket(tokenType) {
+    return tokenType === LeftCurlyBracket ? 1 : 0;
+}
+
+// LEFTCURLYBRACKET, SEMICOLON, false
+function leftCurlyBracketOrSemicolon(tokenType) {
+    return tokenType === LeftCurlyBracket || tokenType === Semicolon ? 1 : 0;
+}
+
+// EXCLAMATIONMARK, SEMICOLON, false
+function exclamationMarkOrSemicolon(tokenType, source, offset) {
+    if (tokenType === Delim && source.charCodeAt(offset) === EXCLAMATIONMARK) {
+        return 1;
+    }
+
+    return tokenType === Semicolon ? 1 : 0;
+}
+
+// 0, SEMICOLON, true
+function semicolonIncluded(tokenType) {
+    return tokenType === Semicolon ? 2 : 0;
+}
+
+module.exports = {
+    name: 'Raw',
+    structure: {
+        value: String
+    },
+    parse: function(startToken, mode, excludeWhiteSpace) {
+        var startOffset = this.scanner.getTokenStart(startToken);
+        var endOffset;
+
+        this.scanner.skip(
+            this.scanner.getRawLength(startToken, mode || balanceEnd)
+        );
+
+        if (excludeWhiteSpace && this.scanner.tokenStart > startOffset) {
+            endOffset = getOffsetExcludeWS.call(this);
+        } else {
+            endOffset = this.scanner.tokenStart;
+        }
+
+        return {
+            type: 'Raw',
+            loc: this.getLocation(startOffset, endOffset),
+            value: this.scanner.source.substring(startOffset, endOffset)
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.value);
+    },
+
+    mode: {
+        default: balanceEnd,
+        leftCurlyBracket: leftCurlyBracket,
+        leftCurlyBracketOrSemicolon: leftCurlyBracketOrSemicolon,
+        exclamationMarkOrSemicolon: exclamationMarkOrSemicolon,
+        semicolonIncluded: semicolonIncluded
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Rule.js b/node_modules/css-tree/lib/syntax/node/Rule.js
new file mode 100644
index 0000000..2f1aaa2
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Rule.js
@@ -0,0 +1,54 @@
+var TYPE = require('../../tokenizer').TYPE;
+var rawMode = require('./Raw').mode;
+
+var LEFTCURLYBRACKET = TYPE.LeftCurlyBracket;
+
+function consumeRaw(startToken) {
+    return this.Raw(startToken, rawMode.leftCurlyBracket, true);
+}
+
+function consumePrelude() {
+    var prelude = this.SelectorList();
+
+    if (prelude.type !== 'Raw' &&
+        this.scanner.eof === false &&
+        this.scanner.tokenType !== LEFTCURLYBRACKET) {
+        this.error();
+    }
+
+    return prelude;
+}
+
+module.exports = {
+    name: 'Rule',
+    structure: {
+        prelude: ['SelectorList', 'Raw'],
+        block: ['Block']
+    },
+    parse: function() {
+        var startToken = this.scanner.tokenIndex;
+        var startOffset = this.scanner.tokenStart;
+        var prelude;
+        var block;
+
+        if (this.parseRulePrelude) {
+            prelude = this.parseWithFallback(consumePrelude, consumeRaw);
+        } else {
+            prelude = consumeRaw.call(this, startToken);
+        }
+
+        block = this.Block(true);
+
+        return {
+            type: 'Rule',
+            loc: this.getLocation(startOffset, this.scanner.tokenStart),
+            prelude: prelude,
+            block: block
+        };
+    },
+    generate: function(node) {
+        this.node(node.prelude);
+        this.node(node.block);
+    },
+    walkContext: 'rule'
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Selector.js b/node_modules/css-tree/lib/syntax/node/Selector.js
new file mode 100644
index 0000000..a1b17f3
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Selector.js
@@ -0,0 +1,32 @@
+module.exports = {
+    name: 'Selector',
+    structure: {
+        children: [[
+            'TypeSelector',
+            'IdSelector',
+            'ClassSelector',
+            'AttributeSelector',
+            'PseudoClassSelector',
+            'PseudoElementSelector',
+            'Combinator',
+            'WhiteSpace'
+        ]]
+    },
+    parse: function() {
+        var children = this.readSequence(this.scope.Selector);
+
+        // nothing were consumed
+        if (this.getFirstListNode(children) === null) {
+            this.error('Selector is expected');
+        }
+
+        return {
+            type: 'Selector',
+            loc: this.getLocationFromList(children),
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.children(node);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/SelectorList.js b/node_modules/css-tree/lib/syntax/node/SelectorList.js
new file mode 100644
index 0000000..70dd7c5
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/SelectorList.js
@@ -0,0 +1,39 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var COMMA = TYPE.Comma;
+
+module.exports = {
+    name: 'SelectorList',
+    structure: {
+        children: [[
+            'Selector',
+            'Raw'
+        ]]
+    },
+    parse: function() {
+        var children = this.createList();
+
+        while (!this.scanner.eof) {
+            children.push(this.Selector());
+
+            if (this.scanner.tokenType === COMMA) {
+                this.scanner.next();
+                continue;
+            }
+
+            break;
+        }
+
+        return {
+            type: 'SelectorList',
+            loc: this.getLocationFromList(children),
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.children(node, function() {
+            this.chunk(',');
+        });
+    },
+    walkContext: 'selector'
+};
diff --git a/node_modules/css-tree/lib/syntax/node/String.js b/node_modules/css-tree/lib/syntax/node/String.js
new file mode 100644
index 0000000..d3b8cc1
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/String.js
@@ -0,0 +1,18 @@
+var STRING = require('../../tokenizer').TYPE.String;
+
+module.exports = {
+    name: 'String',
+    structure: {
+        value: String
+    },
+    parse: function() {
+        return {
+            type: 'String',
+            loc: this.getLocation(this.scanner.tokenStart, this.scanner.tokenEnd),
+            value: this.consume(STRING)
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.value);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/StyleSheet.js b/node_modules/css-tree/lib/syntax/node/StyleSheet.js
new file mode 100644
index 0000000..db332cf
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/StyleSheet.js
@@ -0,0 +1,81 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var WHITESPACE = TYPE.WhiteSpace;
+var COMMENT = TYPE.Comment;
+var ATKEYWORD = TYPE.AtKeyword;
+var CDO = TYPE.CDO;
+var CDC = TYPE.CDC;
+var EXCLAMATIONMARK = 0x0021; // U+0021 EXCLAMATION MARK (!)
+
+function consumeRaw(startToken) {
+    return this.Raw(startToken, null, false);
+}
+
+module.exports = {
+    name: 'StyleSheet',
+    structure: {
+        children: [[
+            'Comment',
+            'CDO',
+            'CDC',
+            'Atrule',
+            'Rule',
+            'Raw'
+        ]]
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var children = this.createList();
+        var child;
+
+        scan:
+        while (!this.scanner.eof) {
+            switch (this.scanner.tokenType) {
+                case WHITESPACE:
+                    this.scanner.next();
+                    continue;
+
+                case COMMENT:
+                    // ignore comments except exclamation comments (i.e. /*! .. */) on top level
+                    if (this.scanner.source.charCodeAt(this.scanner.tokenStart + 2) !== EXCLAMATIONMARK) {
+                        this.scanner.next();
+                        continue;
+                    }
+
+                    child = this.Comment();
+                    break;
+
+                case CDO: // <!--
+                    child = this.CDO();
+                    break;
+
+                case CDC: // -->
+                    child = this.CDC();
+                    break;
+
+                // CSS Syntax Module Level 3
+                // §2.2 Error handling
+                // At the "top level" of a stylesheet, an <at-keyword-token> starts an at-rule.
+                case ATKEYWORD:
+                    child = this.parseWithFallback(this.Atrule, consumeRaw);
+                    break;
+
+                // Anything else starts a qualified rule ...
+                default:
+                    child = this.parseWithFallback(this.Rule, consumeRaw);
+            }
+
+            children.push(child);
+        }
+
+        return {
+            type: 'StyleSheet',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.children(node);
+    },
+    walkContext: 'stylesheet'
+};
diff --git a/node_modules/css-tree/lib/syntax/node/TypeSelector.js b/node_modules/css-tree/lib/syntax/node/TypeSelector.js
new file mode 100644
index 0000000..c8a692c
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/TypeSelector.js
@@ -0,0 +1,53 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var IDENT = TYPE.Ident;
+var ASTERISK = 0x002A;     // U+002A ASTERISK (*)
+var VERTICALLINE = 0x007C; // U+007C VERTICAL LINE (|)
+
+function eatIdentifierOrAsterisk() {
+    if (this.scanner.tokenType !== IDENT &&
+        this.scanner.isDelim(ASTERISK) === false) {
+        this.error('Identifier or asterisk is expected');
+    }
+
+    this.scanner.next();
+}
+
+// ident
+// ident|ident
+// ident|*
+// *
+// *|ident
+// *|*
+// |ident
+// |*
+module.exports = {
+    name: 'TypeSelector',
+    structure: {
+        name: String
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+
+        if (this.scanner.isDelim(VERTICALLINE)) {
+            this.scanner.next();
+            eatIdentifierOrAsterisk.call(this);
+        } else {
+            eatIdentifierOrAsterisk.call(this);
+
+            if (this.scanner.isDelim(VERTICALLINE)) {
+                this.scanner.next();
+                eatIdentifierOrAsterisk.call(this);
+            }
+        }
+
+        return {
+            type: 'TypeSelector',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            name: this.scanner.substrToCursor(start)
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.name);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/UnicodeRange.js b/node_modules/css-tree/lib/syntax/node/UnicodeRange.js
new file mode 100644
index 0000000..f3ca607
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/UnicodeRange.js
@@ -0,0 +1,173 @@
+var isHexDigit = require('../../tokenizer').isHexDigit;
+var cmpChar = require('../../tokenizer').cmpChar;
+var TYPE = require('../../tokenizer').TYPE;
+var NAME = require('../../tokenizer').NAME;
+
+var IDENT = TYPE.Ident;
+var NUMBER = TYPE.Number;
+var DIMENSION = TYPE.Dimension;
+var PLUSSIGN = 0x002B;     // U+002B PLUS SIGN (+)
+var HYPHENMINUS = 0x002D;  // U+002D HYPHEN-MINUS (-)
+var QUESTIONMARK = 0x003F; // U+003F QUESTION MARK (?)
+var U = 0x0075;            // U+0075 LATIN SMALL LETTER U (u)
+
+function eatHexSequence(offset, allowDash) {
+    for (var pos = this.scanner.tokenStart + offset, len = 0; pos < this.scanner.tokenEnd; pos++) {
+        var code = this.scanner.source.charCodeAt(pos);
+
+        if (code === HYPHENMINUS && allowDash && len !== 0) {
+            if (eatHexSequence.call(this, offset + len + 1, false) === 0) {
+                this.error();
+            }
+
+            return -1;
+        }
+
+        if (!isHexDigit(code)) {
+            this.error(
+                allowDash && len !== 0
+                    ? 'HyphenMinus' + (len < 6 ? ' or hex digit' : '') + ' is expected'
+                    : (len < 6 ? 'Hex digit is expected' : 'Unexpected input'),
+                pos
+            );
+        }
+
+        if (++len > 6) {
+            this.error('Too many hex digits', pos);
+        };
+    }
+
+    this.scanner.next();
+    return len;
+}
+
+function eatQuestionMarkSequence(max) {
+    var count = 0;
+
+    while (this.scanner.isDelim(QUESTIONMARK)) {
+        if (++count > max) {
+            this.error('Too many question marks');
+        }
+
+        this.scanner.next();
+    }
+}
+
+function startsWith(code) {
+    if (this.scanner.source.charCodeAt(this.scanner.tokenStart) !== code) {
+        this.error(NAME[code] + ' is expected');
+    }
+}
+
+// https://drafts.csswg.org/css-syntax/#urange
+// Informally, the <urange> production has three forms:
+// U+0001
+//      Defines a range consisting of a single code point, in this case the code point "1".
+// U+0001-00ff
+//      Defines a range of codepoints between the first and the second value, in this case
+//      the range between "1" and "ff" (255 in decimal) inclusive.
+// U+00??
+//      Defines a range of codepoints where the "?" characters range over all hex digits,
+//      in this case defining the same as the value U+0000-00ff.
+// In each form, a maximum of 6 digits is allowed for each hexadecimal number (if you treat "?" as a hexadecimal digit).
+//
+// <urange> =
+//   u '+' <ident-token> '?'* |
+//   u <dimension-token> '?'* |
+//   u <number-token> '?'* |
+//   u <number-token> <dimension-token> |
+//   u <number-token> <number-token> |
+//   u '+' '?'+
+function scanUnicodeRange() {
+    var hexLength = 0;
+
+    // u '+' <ident-token> '?'*
+    // u '+' '?'+
+    if (this.scanner.isDelim(PLUSSIGN)) {
+        this.scanner.next();
+
+        if (this.scanner.tokenType === IDENT) {
+            hexLength = eatHexSequence.call(this, 0, true);
+            if (hexLength > 0) {
+                eatQuestionMarkSequence.call(this, 6 - hexLength);
+            }
+            return;
+        }
+
+        if (this.scanner.isDelim(QUESTIONMARK)) {
+            this.scanner.next();
+            eatQuestionMarkSequence.call(this, 5);
+            return;
+        }
+
+        this.error('Hex digit or question mark is expected');
+        return;
+    }
+
+    // u <number-token> '?'*
+    // u <number-token> <dimension-token>
+    // u <number-token> <number-token>
+    if (this.scanner.tokenType === NUMBER) {
+        startsWith.call(this, PLUSSIGN);
+        hexLength = eatHexSequence.call(this, 1, true);
+
+        if (this.scanner.isDelim(QUESTIONMARK)) {
+            eatQuestionMarkSequence.call(this, 6 - hexLength);
+            return;
+        }
+
+        if (this.scanner.tokenType === DIMENSION ||
+            this.scanner.tokenType === NUMBER) {
+            startsWith.call(this, HYPHENMINUS);
+            eatHexSequence.call(this, 1, false);
+            return;
+        }
+
+        return;
+    }
+
+    // u <dimension-token> '?'*
+    if (this.scanner.tokenType === DIMENSION) {
+        startsWith.call(this, PLUSSIGN);
+        hexLength = eatHexSequence.call(this, 1, true);
+
+        if (hexLength > 0) {
+            eatQuestionMarkSequence.call(this, 6 - hexLength);
+        }
+
+        return;
+    }
+
+    this.error();
+}
+
+module.exports = {
+    name: 'UnicodeRange',
+    structure: {
+        value: String
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+
+        // U or u
+        if (!cmpChar(this.scanner.source, start, U)) {
+            this.error('U is expected');
+        }
+
+        if (!cmpChar(this.scanner.source, start + 1, PLUSSIGN)) {
+            this.error('Plus sign is expected');
+        }
+
+        this.scanner.next();
+        scanUnicodeRange.call(this);
+
+        return {
+            type: 'UnicodeRange',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            value: this.scanner.substrToCursor(start)
+        };
+    },
+    generate: function(node) {
+        this.chunk(node.value);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Url.js b/node_modules/css-tree/lib/syntax/node/Url.js
new file mode 100644
index 0000000..91c6f59
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Url.js
@@ -0,0 +1,69 @@
+var isWhiteSpace = require('../../tokenizer').isWhiteSpace;
+var cmpStr = require('../../tokenizer').cmpStr;
+var TYPE = require('../../tokenizer').TYPE;
+
+var FUNCTION = TYPE.Function;
+var URL = TYPE.Url;
+var RIGHTPARENTHESIS = TYPE.RightParenthesis;
+
+// <url-token> | <function-token> <string> )
+module.exports = {
+    name: 'Url',
+    structure: {
+        value: ['String', 'Raw']
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var value;
+
+        switch (this.scanner.tokenType) {
+            case URL:
+                var rawStart = start + 4;
+                var rawEnd = this.scanner.tokenEnd - 1;
+
+                while (rawStart < rawEnd && isWhiteSpace(this.scanner.source.charCodeAt(rawStart))) {
+                    rawStart++;
+                }
+
+                while (rawStart < rawEnd && isWhiteSpace(this.scanner.source.charCodeAt(rawEnd - 1))) {
+                    rawEnd--;
+                }
+
+                value = {
+                    type: 'Raw',
+                    loc: this.getLocation(rawStart, rawEnd),
+                    value: this.scanner.source.substring(rawStart, rawEnd)
+                };
+
+                this.eat(URL);
+                break;
+
+            case FUNCTION:
+                if (!cmpStr(this.scanner.source, this.scanner.tokenStart, this.scanner.tokenEnd, 'url(')) {
+                    this.error('Function name must be `url`');
+                }
+
+                this.eat(FUNCTION);
+                this.scanner.skipSC();
+                value = this.String();
+                this.scanner.skipSC();
+                this.eat(RIGHTPARENTHESIS);
+                break;
+
+            default:
+                this.error('Url or Function is expected');
+        }
+
+        return {
+            type: 'Url',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            value: value
+        };
+    },
+    generate: function(node) {
+        this.chunk('url');
+        this.chunk('(');
+        this.node(node.value);
+        this.chunk(')');
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/Value.js b/node_modules/css-tree/lib/syntax/node/Value.js
new file mode 100644
index 0000000..6dcb265
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/Value.js
@@ -0,0 +1,19 @@
+module.exports = {
+    name: 'Value',
+    structure: {
+        children: [[]]
+    },
+    parse: function() {
+        var start = this.scanner.tokenStart;
+        var children = this.readSequence(this.scope.Value);
+
+        return {
+            type: 'Value',
+            loc: this.getLocation(start, this.scanner.tokenStart),
+            children: children
+        };
+    },
+    generate: function(node) {
+        this.children(node);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/WhiteSpace.js b/node_modules/css-tree/lib/syntax/node/WhiteSpace.js
new file mode 100644
index 0000000..0c6a363
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/WhiteSpace.js
@@ -0,0 +1,26 @@
+var WHITESPACE = require('../../tokenizer').TYPE.WhiteSpace;
+var SPACE = Object.freeze({
+    type: 'WhiteSpace',
+    loc: null,
+    value: ' '
+});
+
+module.exports = {
+    name: 'WhiteSpace',
+    structure: {
+        value: String
+    },
+    parse: function() {
+        this.eat(WHITESPACE);
+        return SPACE;
+
+        // return {
+        //     type: 'WhiteSpace',
+        //     loc: this.getLocation(this.scanner.tokenStart, this.scanner.tokenEnd),
+        //     value: this.consume(WHITESPACE)
+        // };
+    },
+    generate: function(node) {
+        this.chunk(node.value);
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/node/index.js b/node_modules/css-tree/lib/syntax/node/index.js
new file mode 100644
index 0000000..182b2cf
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/node/index.js
@@ -0,0 +1,42 @@
+module.exports = {
+    AnPlusB: require('./AnPlusB'),
+    Atrule: require('./Atrule'),
+    AtrulePrelude: require('./AtrulePrelude'),
+    AttributeSelector: require('./AttributeSelector'),
+    Block: require('./Block'),
+    Brackets: require('./Brackets'),
+    CDC: require('./CDC'),
+    CDO: require('./CDO'),
+    ClassSelector: require('./ClassSelector'),
+    Combinator: require('./Combinator'),
+    Comment: require('./Comment'),
+    Declaration: require('./Declaration'),
+    DeclarationList: require('./DeclarationList'),
+    Dimension: require('./Dimension'),
+    Function: require('./Function'),
+    Hash: require('./Hash'),
+    Identifier: require('./Identifier'),
+    IdSelector: require('./IdSelector'),
+    MediaFeature: require('./MediaFeature'),
+    MediaQuery: require('./MediaQuery'),
+    MediaQueryList: require('./MediaQueryList'),
+    Nth: require('./Nth'),
+    Number: require('./Number'),
+    Operator: require('./Operator'),
+    Parentheses: require('./Parentheses'),
+    Percentage: require('./Percentage'),
+    PseudoClassSelector: require('./PseudoClassSelector'),
+    PseudoElementSelector: require('./PseudoElementSelector'),
+    Ratio: require('./Ratio'),
+    Raw: require('./Raw'),
+    Rule: require('./Rule'),
+    Selector: require('./Selector'),
+    SelectorList: require('./SelectorList'),
+    String: require('./String'),
+    StyleSheet: require('./StyleSheet'),
+    TypeSelector: require('./TypeSelector'),
+    UnicodeRange: require('./UnicodeRange'),
+    Url: require('./Url'),
+    Value: require('./Value'),
+    WhiteSpace: require('./WhiteSpace')
+};
diff --git a/node_modules/css-tree/lib/syntax/pseudo/common/nth.js b/node_modules/css-tree/lib/syntax/pseudo/common/nth.js
new file mode 100644
index 0000000..c201e7e
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/common/nth.js
@@ -0,0 +1,9 @@
+var DISALLOW_OF_CLAUSE = false;
+
+module.exports = {
+    parse: function nth() {
+        return this.createSingleNodeList(
+            this.Nth(DISALLOW_OF_CLAUSE)
+        );
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/pseudo/common/nthWithOfClause.js b/node_modules/css-tree/lib/syntax/pseudo/common/nthWithOfClause.js
new file mode 100644
index 0000000..527bd1a
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/common/nthWithOfClause.js
@@ -0,0 +1,9 @@
+var ALLOW_OF_CLAUSE = true;
+
+module.exports = {
+    parse: function nthWithOfClause() {
+        return this.createSingleNodeList(
+            this.Nth(ALLOW_OF_CLAUSE)
+        );
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/pseudo/common/selectorList.js b/node_modules/css-tree/lib/syntax/pseudo/common/selectorList.js
new file mode 100644
index 0000000..7f87d08
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/common/selectorList.js
@@ -0,0 +1,7 @@
+module.exports = {
+    parse: function selectorList() {
+        return this.createSingleNodeList(
+            this.SelectorList()
+        );
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/pseudo/dir.js b/node_modules/css-tree/lib/syntax/pseudo/dir.js
new file mode 100644
index 0000000..10f7232
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/dir.js
@@ -0,0 +1,7 @@
+module.exports = {
+    parse: function() {
+        return this.createSingleNodeList(
+            this.Identifier()
+        );
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/pseudo/has.js b/node_modules/css-tree/lib/syntax/pseudo/has.js
new file mode 100644
index 0000000..0e6b583
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/has.js
@@ -0,0 +1,7 @@
+module.exports = {
+    parse: function() {
+        return this.createSingleNodeList(
+            this.SelectorList()
+        );
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/pseudo/index.js b/node_modules/css-tree/lib/syntax/pseudo/index.js
new file mode 100644
index 0000000..eb68aef
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/index.js
@@ -0,0 +1,12 @@
+module.exports = {
+    'dir': require('./dir'),
+    'has': require('./has'),
+    'lang': require('./lang'),
+    'matches': require('./matches'),
+    'not': require('./not'),
+    'nth-child': require('./nth-child'),
+    'nth-last-child': require('./nth-last-child'),
+    'nth-last-of-type': require('./nth-last-of-type'),
+    'nth-of-type': require('./nth-of-type'),
+    'slotted': require('./slotted')
+};
diff --git a/node_modules/css-tree/lib/syntax/pseudo/lang.js b/node_modules/css-tree/lib/syntax/pseudo/lang.js
new file mode 100644
index 0000000..10f7232
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/lang.js
@@ -0,0 +1,7 @@
+module.exports = {
+    parse: function() {
+        return this.createSingleNodeList(
+            this.Identifier()
+        );
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/pseudo/matches.js b/node_modules/css-tree/lib/syntax/pseudo/matches.js
new file mode 100644
index 0000000..43aa085
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/matches.js
@@ -0,0 +1 @@
+module.exports = require('./common/selectorList');
diff --git a/node_modules/css-tree/lib/syntax/pseudo/not.js b/node_modules/css-tree/lib/syntax/pseudo/not.js
new file mode 100644
index 0000000..43aa085
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/not.js
@@ -0,0 +1 @@
+module.exports = require('./common/selectorList');
diff --git a/node_modules/css-tree/lib/syntax/pseudo/nth-child.js b/node_modules/css-tree/lib/syntax/pseudo/nth-child.js
new file mode 100644
index 0000000..ed62028
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/nth-child.js
@@ -0,0 +1 @@
+module.exports = require('./common/nthWithOfClause');
diff --git a/node_modules/css-tree/lib/syntax/pseudo/nth-last-child.js b/node_modules/css-tree/lib/syntax/pseudo/nth-last-child.js
new file mode 100644
index 0000000..ed62028
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/nth-last-child.js
@@ -0,0 +1 @@
+module.exports = require('./common/nthWithOfClause');
diff --git a/node_modules/css-tree/lib/syntax/pseudo/nth-last-of-type.js b/node_modules/css-tree/lib/syntax/pseudo/nth-last-of-type.js
new file mode 100644
index 0000000..9417189
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/nth-last-of-type.js
@@ -0,0 +1 @@
+module.exports = require('./common/nth');
diff --git a/node_modules/css-tree/lib/syntax/pseudo/nth-of-type.js b/node_modules/css-tree/lib/syntax/pseudo/nth-of-type.js
new file mode 100644
index 0000000..9417189
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/nth-of-type.js
@@ -0,0 +1 @@
+module.exports = require('./common/nth');
diff --git a/node_modules/css-tree/lib/syntax/pseudo/slotted.js b/node_modules/css-tree/lib/syntax/pseudo/slotted.js
new file mode 100644
index 0000000..9719f44
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/pseudo/slotted.js
@@ -0,0 +1,7 @@
+module.exports = {
+    parse: function compoundSelector() {
+        return this.createSingleNodeList(
+            this.Selector()
+        );
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/scope/atrulePrelude.js b/node_modules/css-tree/lib/syntax/scope/atrulePrelude.js
new file mode 100644
index 0000000..a4bcab6
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/scope/atrulePrelude.js
@@ -0,0 +1,3 @@
+module.exports = {
+    getNode: require('./default')
+};
diff --git a/node_modules/css-tree/lib/syntax/scope/default.js b/node_modules/css-tree/lib/syntax/scope/default.js
new file mode 100644
index 0000000..a1c6a31
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/scope/default.js
@@ -0,0 +1,87 @@
+var cmpChar = require('../../tokenizer').cmpChar;
+var cmpStr = require('../../tokenizer').cmpStr;
+var TYPE = require('../../tokenizer').TYPE;
+
+var IDENT = TYPE.Ident;
+var STRING = TYPE.String;
+var NUMBER = TYPE.Number;
+var FUNCTION = TYPE.Function;
+var URL = TYPE.Url;
+var HASH = TYPE.Hash;
+var DIMENSION = TYPE.Dimension;
+var PERCENTAGE = TYPE.Percentage;
+var LEFTPARENTHESIS = TYPE.LeftParenthesis;
+var LEFTSQUAREBRACKET = TYPE.LeftSquareBracket;
+var COMMA = TYPE.Comma;
+var DELIM = TYPE.Delim;
+var NUMBERSIGN = 0x0023;  // U+0023 NUMBER SIGN (#)
+var ASTERISK = 0x002A;    // U+002A ASTERISK (*)
+var PLUSSIGN = 0x002B;    // U+002B PLUS SIGN (+)
+var HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
+var SOLIDUS = 0x002F;     // U+002F SOLIDUS (/)
+var U = 0x0075;           // U+0075 LATIN SMALL LETTER U (u)
+
+module.exports = function defaultRecognizer(context) {
+    switch (this.scanner.tokenType) {
+        case HASH:
+            return this.Hash();
+
+        case COMMA:
+            context.space = null;
+            context.ignoreWSAfter = true;
+            return this.Operator();
+
+        case LEFTPARENTHESIS:
+            return this.Parentheses(this.readSequence, context.recognizer);
+
+        case LEFTSQUAREBRACKET:
+            return this.Brackets(this.readSequence, context.recognizer);
+
+        case STRING:
+            return this.String();
+
+        case DIMENSION:
+            return this.Dimension();
+
+        case PERCENTAGE:
+            return this.Percentage();
+
+        case NUMBER:
+            return this.Number();
+
+        case FUNCTION:
+            return cmpStr(this.scanner.source, this.scanner.tokenStart, this.scanner.tokenEnd, 'url(')
+                ? this.Url()
+                : this.Function(this.readSequence, context.recognizer);
+
+        case URL:
+            return this.Url();
+
+        case IDENT:
+            // check for unicode range, it should start with u+ or U+
+            if (cmpChar(this.scanner.source, this.scanner.tokenStart, U) &&
+                cmpChar(this.scanner.source, this.scanner.tokenStart + 1, PLUSSIGN)) {
+                return this.UnicodeRange();
+            } else {
+                return this.Identifier();
+            }
+
+        case DELIM:
+            var code = this.scanner.source.charCodeAt(this.scanner.tokenStart);
+
+            if (code === SOLIDUS ||
+                code === ASTERISK ||
+                code === PLUSSIGN ||
+                code === HYPHENMINUS) {
+                return this.Operator(); // TODO: replace with Delim
+            }
+
+            // TODO: produce a node with Delim node type
+
+            if (code === NUMBERSIGN) {
+                this.error('Hex or identifier is expected', this.scanner.tokenStart + 1);
+            }
+
+            break;
+    }
+};
diff --git a/node_modules/css-tree/lib/syntax/scope/index.js b/node_modules/css-tree/lib/syntax/scope/index.js
new file mode 100644
index 0000000..d217ff8
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/scope/index.js
@@ -0,0 +1,5 @@
+module.exports = {
+    AtrulePrelude: require('./atrulePrelude'),
+    Selector: require('./selector'),
+    Value: require('./value')
+};
diff --git a/node_modules/css-tree/lib/syntax/scope/selector.js b/node_modules/css-tree/lib/syntax/scope/selector.js
new file mode 100644
index 0000000..7cb990b
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/scope/selector.js
@@ -0,0 +1,80 @@
+var TYPE = require('../../tokenizer').TYPE;
+
+var DELIM = TYPE.Delim;
+var IDENT = TYPE.Ident;
+var DIMENSION = TYPE.Dimension;
+var PERCENTAGE = TYPE.Percentage;
+var NUMBER = TYPE.Number;
+var HASH = TYPE.Hash;
+var COLON = TYPE.Colon;
+var LEFTSQUAREBRACKET = TYPE.LeftSquareBracket;
+var NUMBERSIGN = 0x0023;      // U+0023 NUMBER SIGN (#)
+var ASTERISK = 0x002A;        // U+002A ASTERISK (*)
+var PLUSSIGN = 0x002B;        // U+002B PLUS SIGN (+)
+var SOLIDUS = 0x002F;         // U+002F SOLIDUS (/)
+var FULLSTOP = 0x002E;        // U+002E FULL STOP (.)
+var GREATERTHANSIGN = 0x003E; // U+003E GREATER-THAN SIGN (>)
+var VERTICALLINE = 0x007C;    // U+007C VERTICAL LINE (|)
+var TILDE = 0x007E;           // U+007E TILDE (~)
+
+function getNode(context) {
+    switch (this.scanner.tokenType) {
+        case LEFTSQUAREBRACKET:
+            return this.AttributeSelector();
+
+        case HASH:
+            return this.IdSelector();
+
+        case COLON:
+            if (this.scanner.lookupType(1) === COLON) {
+                return this.PseudoElementSelector();
+            } else {
+                return this.PseudoClassSelector();
+            }
+
+        case IDENT:
+            return this.TypeSelector();
+
+        case NUMBER:
+        case PERCENTAGE:
+            return this.Percentage();
+
+        case DIMENSION:
+            // throws when .123ident
+            if (this.scanner.source.charCodeAt(this.scanner.tokenStart) === FULLSTOP) {
+                this.error('Identifier is expected', this.scanner.tokenStart + 1);
+            }
+            break;
+
+        case DELIM:
+            var code = this.scanner.source.charCodeAt(this.scanner.tokenStart);
+
+            switch (code) {
+                case PLUSSIGN:
+                case GREATERTHANSIGN:
+                case TILDE:
+                    context.space = null;
+                    context.ignoreWSAfter = true;
+                    return this.Combinator();
+
+                case SOLIDUS:  // /deep/
+                    return this.Combinator();
+
+                case FULLSTOP:
+                    return this.ClassSelector();
+
+                case ASTERISK:
+                case VERTICALLINE:
+                    return this.TypeSelector();
+
+                case NUMBERSIGN:
+                    return this.IdSelector();
+            }
+
+            break;
+    }
+};
+
+module.exports = {
+    getNode: getNode
+};
diff --git a/node_modules/css-tree/lib/syntax/scope/value.js b/node_modules/css-tree/lib/syntax/scope/value.js
new file mode 100644
index 0000000..0403eb8
--- /dev/null
+++ b/node_modules/css-tree/lib/syntax/scope/value.js
@@ -0,0 +1,5 @@
+module.exports = {
+    getNode: require('./default'),
+    'expression': require('../function/expression'),
+    'var': require('../function/var')
+};