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/lexer/Lexer.js b/node_modules/css-tree/lib/lexer/Lexer.js
new file mode 100644
index 0000000..9484881
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/Lexer.js
@@ -0,0 +1,466 @@
+var SyntaxReferenceError = require('./error').SyntaxReferenceError;
+var SyntaxMatchError = require('./error').SyntaxMatchError;
+var names = require('../utils/names');
+var generic = require('./generic');
+var parse = require('../definition-syntax/parse');
+var generate = require('../definition-syntax/generate');
+var walk = require('../definition-syntax/walk');
+var prepareTokens = require('./prepare-tokens');
+var buildMatchGraph = require('./match-graph').buildMatchGraph;
+var matchAsTree = require('./match').matchAsTree;
+var trace = require('./trace');
+var search = require('./search');
+var getStructureFromConfig = require('./structure').getStructureFromConfig;
+var cssWideKeywords = buildMatchGraph('inherit | initial | unset');
+var cssWideKeywordsWithExpression = buildMatchGraph('inherit | initial | unset | <-ms-legacy-expression>');
+
+function dumpMapSyntax(map, compact, syntaxAsAst) {
+ var result = {};
+
+ for (var name in map) {
+ if (map[name].syntax) {
+ result[name] = syntaxAsAst
+ ? map[name].syntax
+ : generate(map[name].syntax, { compact: compact });
+ }
+ }
+
+ return result;
+}
+
+function dumpAtruleMapSyntax(map, compact, syntaxAsAst) {
+ const result = {};
+
+ for (const [name, atrule] of Object.entries(map)) {
+ result[name] = {
+ prelude: atrule.prelude && (
+ syntaxAsAst
+ ? atrule.prelude.syntax
+ : generate(atrule.prelude.syntax, { compact })
+ ),
+ descriptors: atrule.descriptors && dumpMapSyntax(atrule.descriptors, compact, syntaxAsAst)
+ };
+ }
+
+ return result;
+}
+
+function valueHasVar(tokens) {
+ for (var i = 0; i < tokens.length; i++) {
+ if (tokens[i].value.toLowerCase() === 'var(') {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function buildMatchResult(match, error, iterations) {
+ return {
+ matched: match,
+ iterations: iterations,
+ error: error,
+ getTrace: trace.getTrace,
+ isType: trace.isType,
+ isProperty: trace.isProperty,
+ isKeyword: trace.isKeyword
+ };
+}
+
+function matchSyntax(lexer, syntax, value, useCommon) {
+ var tokens = prepareTokens(value, lexer.syntax);
+ var result;
+
+ if (valueHasVar(tokens)) {
+ return buildMatchResult(null, new Error('Matching for a tree with var() is not supported'));
+ }
+
+ if (useCommon) {
+ result = matchAsTree(tokens, lexer.valueCommonSyntax, lexer);
+ }
+
+ if (!useCommon || !result.match) {
+ result = matchAsTree(tokens, syntax.match, lexer);
+ if (!result.match) {
+ return buildMatchResult(
+ null,
+ new SyntaxMatchError(result.reason, syntax.syntax, value, result),
+ result.iterations
+ );
+ }
+ }
+
+ return buildMatchResult(result.match, null, result.iterations);
+}
+
+var Lexer = function(config, syntax, structure) {
+ this.valueCommonSyntax = cssWideKeywords;
+ this.syntax = syntax;
+ this.generic = false;
+ this.atrules = {};
+ this.properties = {};
+ this.types = {};
+ this.structure = structure || getStructureFromConfig(config);
+
+ if (config) {
+ if (config.types) {
+ for (var name in config.types) {
+ this.addType_(name, config.types[name]);
+ }
+ }
+
+ if (config.generic) {
+ this.generic = true;
+ for (var name in generic) {
+ this.addType_(name, generic[name]);
+ }
+ }
+
+ if (config.atrules) {
+ for (var name in config.atrules) {
+ this.addAtrule_(name, config.atrules[name]);
+ }
+ }
+
+ if (config.properties) {
+ for (var name in config.properties) {
+ this.addProperty_(name, config.properties[name]);
+ }
+ }
+ }
+};
+
+Lexer.prototype = {
+ structure: {},
+ checkStructure: function(ast) {
+ function collectWarning(node, message) {
+ warns.push({
+ node: node,
+ message: message
+ });
+ }
+
+ var structure = this.structure;
+ var warns = [];
+
+ this.syntax.walk(ast, function(node) {
+ if (structure.hasOwnProperty(node.type)) {
+ structure[node.type].check(node, collectWarning);
+ } else {
+ collectWarning(node, 'Unknown node type `' + node.type + '`');
+ }
+ });
+
+ return warns.length ? warns : false;
+ },
+
+ createDescriptor: function(syntax, type, name, parent = null) {
+ var ref = {
+ type: type,
+ name: name
+ };
+ var descriptor = {
+ type: type,
+ name: name,
+ parent: parent,
+ syntax: null,
+ match: null
+ };
+
+ if (typeof syntax === 'function') {
+ descriptor.match = buildMatchGraph(syntax, ref);
+ } else {
+ if (typeof syntax === 'string') {
+ // lazy parsing on first access
+ Object.defineProperty(descriptor, 'syntax', {
+ get: function() {
+ Object.defineProperty(descriptor, 'syntax', {
+ value: parse(syntax)
+ });
+
+ return descriptor.syntax;
+ }
+ });
+ } else {
+ descriptor.syntax = syntax;
+ }
+
+ // lazy graph build on first access
+ Object.defineProperty(descriptor, 'match', {
+ get: function() {
+ Object.defineProperty(descriptor, 'match', {
+ value: buildMatchGraph(descriptor.syntax, ref)
+ });
+
+ return descriptor.match;
+ }
+ });
+ }
+
+ return descriptor;
+ },
+ addAtrule_: function(name, syntax) {
+ if (!syntax) {
+ return;
+ }
+
+ this.atrules[name] = {
+ type: 'Atrule',
+ name: name,
+ prelude: syntax.prelude ? this.createDescriptor(syntax.prelude, 'AtrulePrelude', name) : null,
+ descriptors: syntax.descriptors
+ ? Object.keys(syntax.descriptors).reduce((res, descName) => {
+ res[descName] = this.createDescriptor(syntax.descriptors[descName], 'AtruleDescriptor', descName, name);
+ return res;
+ }, {})
+ : null
+ };
+ },
+ addProperty_: function(name, syntax) {
+ if (!syntax) {
+ return;
+ }
+
+ this.properties[name] = this.createDescriptor(syntax, 'Property', name);
+ },
+ addType_: function(name, syntax) {
+ if (!syntax) {
+ return;
+ }
+
+ this.types[name] = this.createDescriptor(syntax, 'Type', name);
+
+ if (syntax === generic['-ms-legacy-expression']) {
+ this.valueCommonSyntax = cssWideKeywordsWithExpression;
+ }
+ },
+
+ checkAtruleName: function(atruleName) {
+ if (!this.getAtrule(atruleName)) {
+ return new SyntaxReferenceError('Unknown at-rule', '@' + atruleName);
+ }
+ },
+ checkAtrulePrelude: function(atruleName, prelude) {
+ let error = this.checkAtruleName(atruleName);
+
+ if (error) {
+ return error;
+ }
+
+ var atrule = this.getAtrule(atruleName);
+
+ if (!atrule.prelude && prelude) {
+ return new SyntaxError('At-rule `@' + atruleName + '` should not contain a prelude');
+ }
+
+ if (atrule.prelude && !prelude) {
+ return new SyntaxError('At-rule `@' + atruleName + '` should contain a prelude');
+ }
+ },
+ checkAtruleDescriptorName: function(atruleName, descriptorName) {
+ let error = this.checkAtruleName(atruleName);
+
+ if (error) {
+ return error;
+ }
+
+ var atrule = this.getAtrule(atruleName);
+ var descriptor = names.keyword(descriptorName);
+
+ if (!atrule.descriptors) {
+ return new SyntaxError('At-rule `@' + atruleName + '` has no known descriptors');
+ }
+
+ if (!atrule.descriptors[descriptor.name] &&
+ !atrule.descriptors[descriptor.basename]) {
+ return new SyntaxReferenceError('Unknown at-rule descriptor', descriptorName);
+ }
+ },
+ checkPropertyName: function(propertyName) {
+ var property = names.property(propertyName);
+
+ // don't match syntax for a custom property
+ if (property.custom) {
+ return new Error('Lexer matching doesn\'t applicable for custom properties');
+ }
+
+ if (!this.getProperty(propertyName)) {
+ return new SyntaxReferenceError('Unknown property', propertyName);
+ }
+ },
+
+ matchAtrulePrelude: function(atruleName, prelude) {
+ var error = this.checkAtrulePrelude(atruleName, prelude);
+
+ if (error) {
+ return buildMatchResult(null, error);
+ }
+
+ if (!prelude) {
+ return buildMatchResult(null, null);
+ }
+
+ return matchSyntax(this, this.getAtrule(atruleName).prelude, prelude, false);
+ },
+ matchAtruleDescriptor: function(atruleName, descriptorName, value) {
+ var error = this.checkAtruleDescriptorName(atruleName, descriptorName);
+
+ if (error) {
+ return buildMatchResult(null, error);
+ }
+
+ var atrule = this.getAtrule(atruleName);
+ var descriptor = names.keyword(descriptorName);
+
+ return matchSyntax(this, atrule.descriptors[descriptor.name] || atrule.descriptors[descriptor.basename], value, false);
+ },
+ matchDeclaration: function(node) {
+ if (node.type !== 'Declaration') {
+ return buildMatchResult(null, new Error('Not a Declaration node'));
+ }
+
+ return this.matchProperty(node.property, node.value);
+ },
+ matchProperty: function(propertyName, value) {
+ var error = this.checkPropertyName(propertyName);
+
+ if (error) {
+ return buildMatchResult(null, error);
+ }
+
+ return matchSyntax(this, this.getProperty(propertyName), value, true);
+ },
+ matchType: function(typeName, value) {
+ var typeSyntax = this.getType(typeName);
+
+ if (!typeSyntax) {
+ return buildMatchResult(null, new SyntaxReferenceError('Unknown type', typeName));
+ }
+
+ return matchSyntax(this, typeSyntax, value, false);
+ },
+ match: function(syntax, value) {
+ if (typeof syntax !== 'string' && (!syntax || !syntax.type)) {
+ return buildMatchResult(null, new SyntaxReferenceError('Bad syntax'));
+ }
+
+ if (typeof syntax === 'string' || !syntax.match) {
+ syntax = this.createDescriptor(syntax, 'Type', 'anonymous');
+ }
+
+ return matchSyntax(this, syntax, value, false);
+ },
+
+ findValueFragments: function(propertyName, value, type, name) {
+ return search.matchFragments(this, value, this.matchProperty(propertyName, value), type, name);
+ },
+ findDeclarationValueFragments: function(declaration, type, name) {
+ return search.matchFragments(this, declaration.value, this.matchDeclaration(declaration), type, name);
+ },
+ findAllFragments: function(ast, type, name) {
+ var result = [];
+
+ this.syntax.walk(ast, {
+ visit: 'Declaration',
+ enter: function(declaration) {
+ result.push.apply(result, this.findDeclarationValueFragments(declaration, type, name));
+ }.bind(this)
+ });
+
+ return result;
+ },
+
+ getAtrule: function(atruleName, fallbackBasename = true) {
+ var atrule = names.keyword(atruleName);
+ var atruleEntry = atrule.vendor && fallbackBasename
+ ? this.atrules[atrule.name] || this.atrules[atrule.basename]
+ : this.atrules[atrule.name];
+
+ return atruleEntry || null;
+ },
+ getAtrulePrelude: function(atruleName, fallbackBasename = true) {
+ const atrule = this.getAtrule(atruleName, fallbackBasename);
+
+ return atrule && atrule.prelude || null;
+ },
+ getAtruleDescriptor: function(atruleName, name) {
+ return this.atrules.hasOwnProperty(atruleName) && this.atrules.declarators
+ ? this.atrules[atruleName].declarators[name] || null
+ : null;
+ },
+ getProperty: function(propertyName, fallbackBasename = true) {
+ var property = names.property(propertyName);
+ var propertyEntry = property.vendor && fallbackBasename
+ ? this.properties[property.name] || this.properties[property.basename]
+ : this.properties[property.name];
+
+ return propertyEntry || null;
+ },
+ getType: function(name) {
+ return this.types.hasOwnProperty(name) ? this.types[name] : null;
+ },
+
+ validate: function() {
+ function validate(syntax, name, broken, descriptor) {
+ if (broken.hasOwnProperty(name)) {
+ return broken[name];
+ }
+
+ broken[name] = false;
+ if (descriptor.syntax !== null) {
+ walk(descriptor.syntax, function(node) {
+ if (node.type !== 'Type' && node.type !== 'Property') {
+ return;
+ }
+
+ var map = node.type === 'Type' ? syntax.types : syntax.properties;
+ var brokenMap = node.type === 'Type' ? brokenTypes : brokenProperties;
+
+ if (!map.hasOwnProperty(node.name) || validate(syntax, node.name, brokenMap, map[node.name])) {
+ broken[name] = true;
+ }
+ }, this);
+ }
+ }
+
+ var brokenTypes = {};
+ var brokenProperties = {};
+
+ for (var key in this.types) {
+ validate(this, key, brokenTypes, this.types[key]);
+ }
+
+ for (var key in this.properties) {
+ validate(this, key, brokenProperties, this.properties[key]);
+ }
+
+ brokenTypes = Object.keys(brokenTypes).filter(function(name) {
+ return brokenTypes[name];
+ });
+ brokenProperties = Object.keys(brokenProperties).filter(function(name) {
+ return brokenProperties[name];
+ });
+
+ if (brokenTypes.length || brokenProperties.length) {
+ return {
+ types: brokenTypes,
+ properties: brokenProperties
+ };
+ }
+
+ return null;
+ },
+ dump: function(syntaxAsAst, pretty) {
+ return {
+ generic: this.generic,
+ types: dumpMapSyntax(this.types, !pretty, syntaxAsAst),
+ properties: dumpMapSyntax(this.properties, !pretty, syntaxAsAst),
+ atrules: dumpAtruleMapSyntax(this.atrules, !pretty, syntaxAsAst)
+ };
+ },
+ toString: function() {
+ return JSON.stringify(this.dump());
+ }
+};
+
+module.exports = Lexer;
diff --git a/node_modules/css-tree/lib/lexer/error.js b/node_modules/css-tree/lib/lexer/error.js
new file mode 100644
index 0000000..2a6af15
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/error.js
@@ -0,0 +1,127 @@
+const createCustomError = require('../utils/createCustomError');
+const generate = require('../definition-syntax/generate');
+const defaultLoc = { offset: 0, line: 1, column: 1 };
+
+function locateMismatch(matchResult, node) {
+ const tokens = matchResult.tokens;
+ const longestMatch = matchResult.longestMatch;
+ const mismatchNode = longestMatch < tokens.length ? tokens[longestMatch].node || null : null;
+ const badNode = mismatchNode !== node ? mismatchNode : null;
+ let mismatchOffset = 0;
+ let mismatchLength = 0;
+ let entries = 0;
+ let css = '';
+ let start;
+ let end;
+
+ for (let i = 0; i < tokens.length; i++) {
+ const token = tokens[i].value;
+
+ if (i === longestMatch) {
+ mismatchLength = token.length;
+ mismatchOffset = css.length;
+ }
+
+ if (badNode !== null && tokens[i].node === badNode) {
+ if (i <= longestMatch) {
+ entries++;
+ } else {
+ entries = 0;
+ }
+ }
+
+ css += token;
+ }
+
+ if (longestMatch === tokens.length || entries > 1) { // last
+ start = fromLoc(badNode || node, 'end') || buildLoc(defaultLoc, css);
+ end = buildLoc(start);
+ } else {
+ start = fromLoc(badNode, 'start') ||
+ buildLoc(fromLoc(node, 'start') || defaultLoc, css.slice(0, mismatchOffset));
+ end = fromLoc(badNode, 'end') ||
+ buildLoc(start, css.substr(mismatchOffset, mismatchLength));
+ }
+
+ return {
+ css,
+ mismatchOffset,
+ mismatchLength,
+ start,
+ end
+ };
+}
+
+function fromLoc(node, point) {
+ const value = node && node.loc && node.loc[point];
+
+ if (value) {
+ return 'line' in value ? buildLoc(value) : value;
+ }
+
+ return null;
+}
+
+function buildLoc({ offset, line, column }, extra) {
+ const loc = {
+ offset,
+ line,
+ column
+ };
+
+ if (extra) {
+ const lines = extra.split(/\n|\r\n?|\f/);
+
+ loc.offset += extra.length;
+ loc.line += lines.length - 1;
+ loc.column = lines.length === 1 ? loc.column + extra.length : lines.pop().length + 1;
+ }
+
+ return loc;
+}
+
+const SyntaxReferenceError = function(type, referenceName) {
+ const error = createCustomError(
+ 'SyntaxReferenceError',
+ type + (referenceName ? ' `' + referenceName + '`' : '')
+ );
+
+ error.reference = referenceName;
+
+ return error;
+};
+
+const SyntaxMatchError = function(message, syntax, node, matchResult) {
+ const error = createCustomError('SyntaxMatchError', message);
+ const {
+ css,
+ mismatchOffset,
+ mismatchLength,
+ start,
+ end
+ } = locateMismatch(matchResult, node);
+
+ error.rawMessage = message;
+ error.syntax = syntax ? generate(syntax) : '<generic>';
+ error.css = css;
+ error.mismatchOffset = mismatchOffset;
+ error.mismatchLength = mismatchLength;
+ error.message = message + '\n' +
+ ' syntax: ' + error.syntax + '\n' +
+ ' value: ' + (css || '<empty string>') + '\n' +
+ ' --------' + new Array(error.mismatchOffset + 1).join('-') + '^';
+
+ Object.assign(error, start);
+ error.loc = {
+ source: (node && node.loc && node.loc.source) || '<unknown>',
+ start,
+ end
+ };
+
+ return error;
+};
+
+module.exports = {
+ SyntaxReferenceError,
+ SyntaxMatchError
+};
diff --git a/node_modules/css-tree/lib/lexer/generic-an-plus-b.js b/node_modules/css-tree/lib/lexer/generic-an-plus-b.js
new file mode 100644
index 0000000..7b8a818
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/generic-an-plus-b.js
@@ -0,0 +1,236 @@
+var isDigit = require('../tokenizer').isDigit;
+var cmpChar = require('../tokenizer').cmpChar;
+var TYPE = require('../tokenizer').TYPE;
+
+var DELIM = TYPE.Delim;
+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 isDelim(token, code) {
+ return token !== null && token.type === DELIM && token.value.charCodeAt(0) === code;
+}
+
+function skipSC(token, offset, getNextToken) {
+ while (token !== null && (token.type === WHITESPACE || token.type === COMMENT)) {
+ token = getNextToken(++offset);
+ }
+
+ return offset;
+}
+
+function checkInteger(token, valueOffset, disallowSign, offset) {
+ if (!token) {
+ return 0;
+ }
+
+ var code = token.value.charCodeAt(valueOffset);
+
+ if (code === PLUSSIGN || code === HYPHENMINUS) {
+ if (disallowSign) {
+ // Number sign is not allowed
+ return 0;
+ }
+ valueOffset++;
+ }
+
+ for (; valueOffset < token.value.length; valueOffset++) {
+ if (!isDigit(token.value.charCodeAt(valueOffset))) {
+ // Integer is expected
+ return 0;
+ }
+ }
+
+ return offset + 1;
+}
+
+// ... <signed-integer>
+// ... ['+' | '-'] <signless-integer>
+function consumeB(token, offset_, getNextToken) {
+ var sign = false;
+ var offset = skipSC(token, offset_, getNextToken);
+
+ token = getNextToken(offset);
+
+ if (token === null) {
+ return offset_;
+ }
+
+ if (token.type !== NUMBER) {
+ if (isDelim(token, PLUSSIGN) || isDelim(token, HYPHENMINUS)) {
+ sign = true;
+ offset = skipSC(getNextToken(++offset), offset, getNextToken);
+ token = getNextToken(offset);
+
+ if (token === null && token.type !== NUMBER) {
+ return 0;
+ }
+ } else {
+ return offset_;
+ }
+ }
+
+ if (!sign) {
+ var code = token.value.charCodeAt(0);
+ if (code !== PLUSSIGN && code !== HYPHENMINUS) {
+ // Number sign is expected
+ return 0;
+ }
+ }
+
+ return checkInteger(token, sign ? 0 : 1, sign, offset);
+}
+
+// An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb
+module.exports = function anPlusB(token, getNextToken) {
+ /* eslint-disable brace-style*/
+ var offset = 0;
+
+ if (!token) {
+ return 0;
+ }
+
+ // <integer>
+ if (token.type === NUMBER) {
+ return checkInteger(token, 0, ALLOW_SIGN, offset); // b
+ }
+
+ // -n
+ // -n <signed-integer>
+ // -n ['+' | '-'] <signless-integer>
+ // -n- <signless-integer>
+ // <dashndashdigit-ident>
+ else if (token.type === IDENT && token.value.charCodeAt(0) === HYPHENMINUS) {
+ // expect 1st char is N
+ if (!cmpChar(token.value, 1, N)) {
+ return 0;
+ }
+
+ switch (token.value.length) {
+ // -n
+ // -n <signed-integer>
+ // -n ['+' | '-'] <signless-integer>
+ case 2:
+ return consumeB(getNextToken(++offset), offset, getNextToken);
+
+ // -n- <signless-integer>
+ case 3:
+ if (token.value.charCodeAt(2) !== HYPHENMINUS) {
+ return 0;
+ }
+
+ offset = skipSC(getNextToken(++offset), offset, getNextToken);
+ token = getNextToken(offset);
+
+ return checkInteger(token, 0, DISALLOW_SIGN, offset);
+
+ // <dashndashdigit-ident>
+ default:
+ if (token.value.charCodeAt(2) !== HYPHENMINUS) {
+ return 0;
+ }
+
+ return checkInteger(token, 3, DISALLOW_SIGN, offset);
+ }
+ }
+
+ // '+'? n
+ // '+'? n <signed-integer>
+ // '+'? n ['+' | '-'] <signless-integer>
+ // '+'? n- <signless-integer>
+ // '+'? <ndashdigit-ident>
+ else if (token.type === IDENT || (isDelim(token, PLUSSIGN) && getNextToken(offset + 1).type === IDENT)) {
+ // just ignore a plus
+ if (token.type !== IDENT) {
+ token = getNextToken(++offset);
+ }
+
+ if (token === null || !cmpChar(token.value, 0, N)) {
+ return 0;
+ }
+
+ switch (token.value.length) {
+ // '+'? n
+ // '+'? n <signed-integer>
+ // '+'? n ['+' | '-'] <signless-integer>
+ case 1:
+ return consumeB(getNextToken(++offset), offset, getNextToken);
+
+ // '+'? n- <signless-integer>
+ case 2:
+ if (token.value.charCodeAt(1) !== HYPHENMINUS) {
+ return 0;
+ }
+
+ offset = skipSC(getNextToken(++offset), offset, getNextToken);
+ token = getNextToken(offset);
+
+ return checkInteger(token, 0, DISALLOW_SIGN, offset);
+
+ // '+'? <ndashdigit-ident>
+ default:
+ if (token.value.charCodeAt(1) !== HYPHENMINUS) {
+ return 0;
+ }
+
+ return checkInteger(token, 2, DISALLOW_SIGN, offset);
+ }
+ }
+
+ // <ndashdigit-dimension>
+ // <ndash-dimension> <signless-integer>
+ // <n-dimension>
+ // <n-dimension> <signed-integer>
+ // <n-dimension> ['+' | '-'] <signless-integer>
+ else if (token.type === DIMENSION) {
+ var code = token.value.charCodeAt(0);
+ var sign = code === PLUSSIGN || code === HYPHENMINUS ? 1 : 0;
+
+ for (var i = sign; i < token.value.length; i++) {
+ if (!isDigit(token.value.charCodeAt(i))) {
+ break;
+ }
+ }
+
+ if (i === sign) {
+ // Integer is expected
+ return 0;
+ }
+
+ if (!cmpChar(token.value, i, N)) {
+ return 0;
+ }
+
+ // <n-dimension>
+ // <n-dimension> <signed-integer>
+ // <n-dimension> ['+' | '-'] <signless-integer>
+ if (i + 1 === token.value.length) {
+ return consumeB(getNextToken(++offset), offset, getNextToken);
+ } else {
+ if (token.value.charCodeAt(i + 1) !== HYPHENMINUS) {
+ return 0;
+ }
+
+ // <ndash-dimension> <signless-integer>
+ if (i + 2 === token.value.length) {
+ offset = skipSC(getNextToken(++offset), offset, getNextToken);
+ token = getNextToken(offset);
+
+ return checkInteger(token, 0, DISALLOW_SIGN, offset);
+ }
+ // <ndashdigit-dimension>
+ else {
+ return checkInteger(token, i + 2, DISALLOW_SIGN, offset);
+ }
+ }
+ }
+
+ return 0;
+};
diff --git a/node_modules/css-tree/lib/lexer/generic-urange.js b/node_modules/css-tree/lib/lexer/generic-urange.js
new file mode 100644
index 0000000..2556d70
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/generic-urange.js
@@ -0,0 +1,159 @@
+var isHexDigit = require('../tokenizer').isHexDigit;
+var cmpChar = require('../tokenizer').cmpChar;
+var TYPE = require('../tokenizer').TYPE;
+
+var IDENT = TYPE.Ident;
+var DELIM = TYPE.Delim;
+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 isDelim(token, code) {
+ return token !== null && token.type === DELIM && token.value.charCodeAt(0) === code;
+}
+
+function startsWith(token, code) {
+ return token.value.charCodeAt(0) === code;
+}
+
+function hexSequence(token, offset, allowDash) {
+ for (var pos = offset, hexlen = 0; pos < token.value.length; pos++) {
+ var code = token.value.charCodeAt(pos);
+
+ if (code === HYPHENMINUS && allowDash && hexlen !== 0) {
+ if (hexSequence(token, offset + hexlen + 1, false) > 0) {
+ return 6; // dissallow following question marks
+ }
+
+ return 0; // dash at the ending of a hex sequence is not allowed
+ }
+
+ if (!isHexDigit(code)) {
+ return 0; // not a hex digit
+ }
+
+ if (++hexlen > 6) {
+ return 0; // too many hex digits
+ };
+ }
+
+ return hexlen;
+}
+
+function withQuestionMarkSequence(consumed, length, getNextToken) {
+ if (!consumed) {
+ return 0; // nothing consumed
+ }
+
+ while (isDelim(getNextToken(length), QUESTIONMARK)) {
+ if (++consumed > 6) {
+ return 0; // too many question marks
+ }
+
+ length++;
+ }
+
+ return length;
+}
+
+// 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 '+' '?'+
+module.exports = function urange(token, getNextToken) {
+ var length = 0;
+
+ // should start with `u` or `U`
+ if (token === null || token.type !== IDENT || !cmpChar(token.value, 0, U)) {
+ return 0;
+ }
+
+ token = getNextToken(++length);
+ if (token === null) {
+ return 0;
+ }
+
+ // u '+' <ident-token> '?'*
+ // u '+' '?'+
+ if (isDelim(token, PLUSSIGN)) {
+ token = getNextToken(++length);
+ if (token === null) {
+ return 0;
+ }
+
+ if (token.type === IDENT) {
+ // u '+' <ident-token> '?'*
+ return withQuestionMarkSequence(hexSequence(token, 0, true), ++length, getNextToken);
+ }
+
+ if (isDelim(token, QUESTIONMARK)) {
+ // u '+' '?'+
+ return withQuestionMarkSequence(1, ++length, getNextToken);
+ }
+
+ // Hex digit or question mark is expected
+ return 0;
+ }
+
+ // u <number-token> '?'*
+ // u <number-token> <dimension-token>
+ // u <number-token> <number-token>
+ if (token.type === NUMBER) {
+ if (!startsWith(token, PLUSSIGN)) {
+ return 0;
+ }
+
+ var consumedHexLength = hexSequence(token, 1, true);
+ if (consumedHexLength === 0) {
+ return 0;
+ }
+
+ token = getNextToken(++length);
+ if (token === null) {
+ // u <number-token> <eof>
+ return length;
+ }
+
+ if (token.type === DIMENSION || token.type === NUMBER) {
+ // u <number-token> <dimension-token>
+ // u <number-token> <number-token>
+ if (!startsWith(token, HYPHENMINUS) || !hexSequence(token, 1, false)) {
+ return 0;
+ }
+
+ return length + 1;
+ }
+
+ // u <number-token> '?'*
+ return withQuestionMarkSequence(consumedHexLength, length, getNextToken);
+ }
+
+ // u <dimension-token> '?'*
+ if (token.type === DIMENSION) {
+ if (!startsWith(token, PLUSSIGN)) {
+ return 0;
+ }
+
+ return withQuestionMarkSequence(hexSequence(token, 1, true), ++length, getNextToken);
+ }
+
+ return 0;
+};
diff --git a/node_modules/css-tree/lib/lexer/generic.js b/node_modules/css-tree/lib/lexer/generic.js
new file mode 100644
index 0000000..c5b733a
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/generic.js
@@ -0,0 +1,585 @@
+var tokenizer = require('../tokenizer');
+var isIdentifierStart = tokenizer.isIdentifierStart;
+var isHexDigit = tokenizer.isHexDigit;
+var isDigit = tokenizer.isDigit;
+var cmpStr = tokenizer.cmpStr;
+var consumeNumber = tokenizer.consumeNumber;
+var TYPE = tokenizer.TYPE;
+var anPlusB = require('./generic-an-plus-b');
+var urange = require('./generic-urange');
+
+var cssWideKeywords = ['unset', 'initial', 'inherit'];
+var calcFunctionNames = ['calc(', '-moz-calc(', '-webkit-calc('];
+
+// https://www.w3.org/TR/css-values-3/#lengths
+var LENGTH = {
+ // absolute length units
+ 'px': true,
+ 'mm': true,
+ 'cm': true,
+ 'in': true,
+ 'pt': true,
+ 'pc': true,
+ 'q': true,
+
+ // relative length units
+ 'em': true,
+ 'ex': true,
+ 'ch': true,
+ 'rem': true,
+
+ // viewport-percentage lengths
+ 'vh': true,
+ 'vw': true,
+ 'vmin': true,
+ 'vmax': true,
+ 'vm': true
+};
+
+var ANGLE = {
+ 'deg': true,
+ 'grad': true,
+ 'rad': true,
+ 'turn': true
+};
+
+var TIME = {
+ 's': true,
+ 'ms': true
+};
+
+var FREQUENCY = {
+ 'hz': true,
+ 'khz': true
+};
+
+// https://www.w3.org/TR/css-values-3/#resolution (https://drafts.csswg.org/css-values/#resolution)
+var RESOLUTION = {
+ 'dpi': true,
+ 'dpcm': true,
+ 'dppx': true,
+ 'x': true // https://github.com/w3c/csswg-drafts/issues/461
+};
+
+// https://drafts.csswg.org/css-grid/#fr-unit
+var FLEX = {
+ 'fr': true
+};
+
+// https://www.w3.org/TR/css3-speech/#mixing-props-voice-volume
+var DECIBEL = {
+ 'db': true
+};
+
+// https://www.w3.org/TR/css3-speech/#voice-props-voice-pitch
+var SEMITONES = {
+ 'st': true
+};
+
+// safe char code getter
+function charCode(str, index) {
+ return index < str.length ? str.charCodeAt(index) : 0;
+}
+
+function eqStr(actual, expected) {
+ return cmpStr(actual, 0, actual.length, expected);
+}
+
+function eqStrAny(actual, expected) {
+ for (var i = 0; i < expected.length; i++) {
+ if (eqStr(actual, expected[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// IE postfix hack, i.e. 123\0 or 123px\9
+function isPostfixIeHack(str, offset) {
+ if (offset !== str.length - 2) {
+ return false;
+ }
+
+ return (
+ str.charCodeAt(offset) === 0x005C && // U+005C REVERSE SOLIDUS (\)
+ isDigit(str.charCodeAt(offset + 1))
+ );
+}
+
+function outOfRange(opts, value, numEnd) {
+ if (opts && opts.type === 'Range') {
+ var num = Number(
+ numEnd !== undefined && numEnd !== value.length
+ ? value.substr(0, numEnd)
+ : value
+ );
+
+ if (isNaN(num)) {
+ return true;
+ }
+
+ if (opts.min !== null && num < opts.min) {
+ return true;
+ }
+
+ if (opts.max !== null && num > opts.max) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function consumeFunction(token, getNextToken) {
+ var startIdx = token.index;
+ var length = 0;
+
+ // balanced token consuming
+ do {
+ length++;
+
+ if (token.balance <= startIdx) {
+ break;
+ }
+ } while (token = getNextToken(length));
+
+ return length;
+}
+
+// TODO: implement
+// can be used wherever <length>, <frequency>, <angle>, <time>, <percentage>, <number>, or <integer> values are allowed
+// https://drafts.csswg.org/css-values/#calc-notation
+function calc(next) {
+ return function(token, getNextToken, opts) {
+ if (token === null) {
+ return 0;
+ }
+
+ if (token.type === TYPE.Function && eqStrAny(token.value, calcFunctionNames)) {
+ return consumeFunction(token, getNextToken);
+ }
+
+ return next(token, getNextToken, opts);
+ };
+}
+
+function tokenType(expectedTokenType) {
+ return function(token) {
+ if (token === null || token.type !== expectedTokenType) {
+ return 0;
+ }
+
+ return 1;
+ };
+}
+
+function func(name) {
+ name = name + '(';
+
+ return function(token, getNextToken) {
+ if (token !== null && eqStr(token.value, name)) {
+ return consumeFunction(token, getNextToken);
+ }
+
+ return 0;
+ };
+}
+
+// =========================
+// Complex types
+//
+
+// https://drafts.csswg.org/css-values-4/#custom-idents
+// 4.2. Author-defined Identifiers: the <custom-ident> type
+// Some properties accept arbitrary author-defined identifiers as a component value.
+// This generic data type is denoted by <custom-ident>, and represents any valid CSS identifier
+// that would not be misinterpreted as a pre-defined keyword in that property’s value definition.
+//
+// See also: https://developer.mozilla.org/en-US/docs/Web/CSS/custom-ident
+function customIdent(token) {
+ if (token === null || token.type !== TYPE.Ident) {
+ return 0;
+ }
+
+ var name = token.value.toLowerCase();
+
+ // The CSS-wide keywords are not valid <custom-ident>s
+ if (eqStrAny(name, cssWideKeywords)) {
+ return 0;
+ }
+
+ // The default keyword is reserved and is also not a valid <custom-ident>
+ if (eqStr(name, 'default')) {
+ return 0;
+ }
+
+ // TODO: ignore property specific keywords (as described https://developer.mozilla.org/en-US/docs/Web/CSS/custom-ident)
+ // Specifications using <custom-ident> must specify clearly what other keywords
+ // are excluded from <custom-ident>, if any—for example by saying that any pre-defined keywords
+ // in that property’s value definition are excluded. Excluded keywords are excluded
+ // in all ASCII case permutations.
+
+ return 1;
+}
+
+// https://drafts.csswg.org/css-variables/#typedef-custom-property-name
+// A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS), like --foo.
+// The <custom-property-name> production corresponds to this: it’s defined as any valid identifier
+// that starts with two dashes, except -- itself, which is reserved for future use by CSS.
+// NOTE: Current implementation treat `--` as a valid name since most (all?) major browsers treat it as valid.
+function customPropertyName(token) {
+ // ... defined as any valid identifier
+ if (token === null || token.type !== TYPE.Ident) {
+ return 0;
+ }
+
+ // ... that starts with two dashes (U+002D HYPHEN-MINUS)
+ if (charCode(token.value, 0) !== 0x002D || charCode(token.value, 1) !== 0x002D) {
+ return 0;
+ }
+
+ return 1;
+}
+
+// https://drafts.csswg.org/css-color-4/#hex-notation
+// The syntax of a <hex-color> is a <hash-token> token whose value consists of 3, 4, 6, or 8 hexadecimal digits.
+// In other words, a hex color is written as a hash character, "#", followed by some number of digits 0-9 or
+// letters a-f (the case of the letters doesn’t matter - #00ff00 is identical to #00FF00).
+function hexColor(token) {
+ if (token === null || token.type !== TYPE.Hash) {
+ return 0;
+ }
+
+ var length = token.value.length;
+
+ // valid values (length): #rgb (4), #rgba (5), #rrggbb (7), #rrggbbaa (9)
+ if (length !== 4 && length !== 5 && length !== 7 && length !== 9) {
+ return 0;
+ }
+
+ for (var i = 1; i < length; i++) {
+ if (!isHexDigit(token.value.charCodeAt(i))) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+function idSelector(token) {
+ if (token === null || token.type !== TYPE.Hash) {
+ return 0;
+ }
+
+ if (!isIdentifierStart(charCode(token.value, 1), charCode(token.value, 2), charCode(token.value, 3))) {
+ return 0;
+ }
+
+ return 1;
+}
+
+// https://drafts.csswg.org/css-syntax/#any-value
+// It represents the entirety of what a valid declaration can have as its value.
+function declarationValue(token, getNextToken) {
+ if (!token) {
+ return 0;
+ }
+
+ var length = 0;
+ var level = 0;
+ var startIdx = token.index;
+
+ // The <declaration-value> production matches any sequence of one or more tokens,
+ // so long as the sequence ...
+ scan:
+ do {
+ switch (token.type) {
+ // ... does not contain <bad-string-token>, <bad-url-token>,
+ case TYPE.BadString:
+ case TYPE.BadUrl:
+ break scan;
+
+ // ... unmatched <)-token>, <]-token>, or <}-token>,
+ case TYPE.RightCurlyBracket:
+ case TYPE.RightParenthesis:
+ case TYPE.RightSquareBracket:
+ if (token.balance > token.index || token.balance < startIdx) {
+ break scan;
+ }
+
+ level--;
+ break;
+
+ // ... or top-level <semicolon-token> tokens
+ case TYPE.Semicolon:
+ if (level === 0) {
+ break scan;
+ }
+
+ break;
+
+ // ... or <delim-token> tokens with a value of "!"
+ case TYPE.Delim:
+ if (token.value === '!' && level === 0) {
+ break scan;
+ }
+
+ break;
+
+ case TYPE.Function:
+ case TYPE.LeftParenthesis:
+ case TYPE.LeftSquareBracket:
+ case TYPE.LeftCurlyBracket:
+ level++;
+ break;
+ }
+
+ length++;
+
+ // until balance closing
+ if (token.balance <= startIdx) {
+ break;
+ }
+ } while (token = getNextToken(length));
+
+ return length;
+}
+
+// https://drafts.csswg.org/css-syntax/#any-value
+// The <any-value> production is identical to <declaration-value>, but also
+// allows top-level <semicolon-token> tokens and <delim-token> tokens
+// with a value of "!". It represents the entirety of what valid CSS can be in any context.
+function anyValue(token, getNextToken) {
+ if (!token) {
+ return 0;
+ }
+
+ var startIdx = token.index;
+ var length = 0;
+
+ // The <any-value> production matches any sequence of one or more tokens,
+ // so long as the sequence ...
+ scan:
+ do {
+ switch (token.type) {
+ // ... does not contain <bad-string-token>, <bad-url-token>,
+ case TYPE.BadString:
+ case TYPE.BadUrl:
+ break scan;
+
+ // ... unmatched <)-token>, <]-token>, or <}-token>,
+ case TYPE.RightCurlyBracket:
+ case TYPE.RightParenthesis:
+ case TYPE.RightSquareBracket:
+ if (token.balance > token.index || token.balance < startIdx) {
+ break scan;
+ }
+
+ break;
+ }
+
+ length++;
+
+ // until balance closing
+ if (token.balance <= startIdx) {
+ break;
+ }
+ } while (token = getNextToken(length));
+
+ return length;
+}
+
+// =========================
+// Dimensions
+//
+
+function dimension(type) {
+ return function(token, getNextToken, opts) {
+ if (token === null || token.type !== TYPE.Dimension) {
+ return 0;
+ }
+
+ var numberEnd = consumeNumber(token.value, 0);
+
+ // check unit
+ if (type !== null) {
+ // check for IE postfix hack, i.e. 123px\0 or 123px\9
+ var reverseSolidusOffset = token.value.indexOf('\\', numberEnd);
+ var unit = reverseSolidusOffset === -1 || !isPostfixIeHack(token.value, reverseSolidusOffset)
+ ? token.value.substr(numberEnd)
+ : token.value.substring(numberEnd, reverseSolidusOffset);
+
+ if (type.hasOwnProperty(unit.toLowerCase()) === false) {
+ return 0;
+ }
+ }
+
+ // check range if specified
+ if (outOfRange(opts, token.value, numberEnd)) {
+ return 0;
+ }
+
+ return 1;
+ };
+}
+
+// =========================
+// Percentage
+//
+
+// §5.5. Percentages: the <percentage> type
+// https://drafts.csswg.org/css-values-4/#percentages
+function percentage(token, getNextToken, opts) {
+ // ... corresponds to the <percentage-token> production
+ if (token === null || token.type !== TYPE.Percentage) {
+ return 0;
+ }
+
+ // check range if specified
+ if (outOfRange(opts, token.value, token.value.length - 1)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+// =========================
+// Numeric
+//
+
+// https://drafts.csswg.org/css-values-4/#numbers
+// The value <zero> represents a literal number with the value 0. Expressions that merely
+// evaluate to a <number> with the value 0 (for example, calc(0)) do not match <zero>;
+// only literal <number-token>s do.
+function zero(next) {
+ if (typeof next !== 'function') {
+ next = function() {
+ return 0;
+ };
+ }
+
+ return function(token, getNextToken, opts) {
+ if (token !== null && token.type === TYPE.Number) {
+ if (Number(token.value) === 0) {
+ return 1;
+ }
+ }
+
+ return next(token, getNextToken, opts);
+ };
+}
+
+// § 5.3. Real Numbers: the <number> type
+// https://drafts.csswg.org/css-values-4/#numbers
+// Number values are denoted by <number>, and represent real numbers, possibly with a fractional component.
+// ... It corresponds to the <number-token> production
+function number(token, getNextToken, opts) {
+ if (token === null) {
+ return 0;
+ }
+
+ var numberEnd = consumeNumber(token.value, 0);
+ var isNumber = numberEnd === token.value.length;
+ if (!isNumber && !isPostfixIeHack(token.value, numberEnd)) {
+ return 0;
+ }
+
+ // check range if specified
+ if (outOfRange(opts, token.value, numberEnd)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+// §5.2. Integers: the <integer> type
+// https://drafts.csswg.org/css-values-4/#integers
+function integer(token, getNextToken, opts) {
+ // ... corresponds to a subset of the <number-token> production
+ if (token === null || token.type !== TYPE.Number) {
+ return 0;
+ }
+
+ // The first digit of an integer may be immediately preceded by `-` or `+` to indicate the integer’s sign.
+ var i = token.value.charCodeAt(0) === 0x002B || // U+002B PLUS SIGN (+)
+ token.value.charCodeAt(0) === 0x002D ? 1 : 0; // U+002D HYPHEN-MINUS (-)
+
+ // When written literally, an integer is one or more decimal digits 0 through 9 ...
+ for (; i < token.value.length; i++) {
+ if (!isDigit(token.value.charCodeAt(i))) {
+ return 0;
+ }
+ }
+
+ // check range if specified
+ if (outOfRange(opts, token.value, i)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+module.exports = {
+ // token types
+ 'ident-token': tokenType(TYPE.Ident),
+ 'function-token': tokenType(TYPE.Function),
+ 'at-keyword-token': tokenType(TYPE.AtKeyword),
+ 'hash-token': tokenType(TYPE.Hash),
+ 'string-token': tokenType(TYPE.String),
+ 'bad-string-token': tokenType(TYPE.BadString),
+ 'url-token': tokenType(TYPE.Url),
+ 'bad-url-token': tokenType(TYPE.BadUrl),
+ 'delim-token': tokenType(TYPE.Delim),
+ 'number-token': tokenType(TYPE.Number),
+ 'percentage-token': tokenType(TYPE.Percentage),
+ 'dimension-token': tokenType(TYPE.Dimension),
+ 'whitespace-token': tokenType(TYPE.WhiteSpace),
+ 'CDO-token': tokenType(TYPE.CDO),
+ 'CDC-token': tokenType(TYPE.CDC),
+ 'colon-token': tokenType(TYPE.Colon),
+ 'semicolon-token': tokenType(TYPE.Semicolon),
+ 'comma-token': tokenType(TYPE.Comma),
+ '[-token': tokenType(TYPE.LeftSquareBracket),
+ ']-token': tokenType(TYPE.RightSquareBracket),
+ '(-token': tokenType(TYPE.LeftParenthesis),
+ ')-token': tokenType(TYPE.RightParenthesis),
+ '{-token': tokenType(TYPE.LeftCurlyBracket),
+ '}-token': tokenType(TYPE.RightCurlyBracket),
+
+ // token type aliases
+ 'string': tokenType(TYPE.String),
+ 'ident': tokenType(TYPE.Ident),
+
+ // complex types
+ 'custom-ident': customIdent,
+ 'custom-property-name': customPropertyName,
+ 'hex-color': hexColor,
+ 'id-selector': idSelector, // element( <id-selector> )
+ 'an-plus-b': anPlusB,
+ 'urange': urange,
+ 'declaration-value': declarationValue,
+ 'any-value': anyValue,
+
+ // dimensions
+ 'dimension': calc(dimension(null)),
+ 'angle': calc(dimension(ANGLE)),
+ 'decibel': calc(dimension(DECIBEL)),
+ 'frequency': calc(dimension(FREQUENCY)),
+ 'flex': calc(dimension(FLEX)),
+ 'length': calc(zero(dimension(LENGTH))),
+ 'resolution': calc(dimension(RESOLUTION)),
+ 'semitones': calc(dimension(SEMITONES)),
+ 'time': calc(dimension(TIME)),
+
+ // percentage
+ 'percentage': calc(percentage),
+
+ // numeric
+ 'zero': zero(),
+ 'number': calc(number),
+ 'integer': calc(integer),
+
+ // old IE stuff
+ '-ms-legacy-expression': func('expression')
+};
diff --git a/node_modules/css-tree/lib/lexer/index.js b/node_modules/css-tree/lib/lexer/index.js
new file mode 100644
index 0000000..e29f392
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/index.js
@@ -0,0 +1,3 @@
+module.exports = {
+ Lexer: require('./Lexer')
+};
diff --git a/node_modules/css-tree/lib/lexer/match-graph.js b/node_modules/css-tree/lib/lexer/match-graph.js
new file mode 100644
index 0000000..3d27704
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/match-graph.js
@@ -0,0 +1,455 @@
+var parse = require('../definition-syntax/parse');
+
+var MATCH = { type: 'Match' };
+var MISMATCH = { type: 'Mismatch' };
+var DISALLOW_EMPTY = { type: 'DisallowEmpty' };
+var LEFTPARENTHESIS = 40; // (
+var RIGHTPARENTHESIS = 41; // )
+
+function createCondition(match, thenBranch, elseBranch) {
+ // reduce node count
+ if (thenBranch === MATCH && elseBranch === MISMATCH) {
+ return match;
+ }
+
+ if (match === MATCH && thenBranch === MATCH && elseBranch === MATCH) {
+ return match;
+ }
+
+ if (match.type === 'If' && match.else === MISMATCH && thenBranch === MATCH) {
+ thenBranch = match.then;
+ match = match.match;
+ }
+
+ return {
+ type: 'If',
+ match: match,
+ then: thenBranch,
+ else: elseBranch
+ };
+}
+
+function isFunctionType(name) {
+ return (
+ name.length > 2 &&
+ name.charCodeAt(name.length - 2) === LEFTPARENTHESIS &&
+ name.charCodeAt(name.length - 1) === RIGHTPARENTHESIS
+ );
+}
+
+function isEnumCapatible(term) {
+ return (
+ term.type === 'Keyword' ||
+ term.type === 'AtKeyword' ||
+ term.type === 'Function' ||
+ term.type === 'Type' && isFunctionType(term.name)
+ );
+}
+
+function buildGroupMatchGraph(combinator, terms, atLeastOneTermMatched) {
+ switch (combinator) {
+ case ' ':
+ // Juxtaposing components means that all of them must occur, in the given order.
+ //
+ // a b c
+ // =
+ // match a
+ // then match b
+ // then match c
+ // then MATCH
+ // else MISMATCH
+ // else MISMATCH
+ // else MISMATCH
+ var result = MATCH;
+
+ for (var i = terms.length - 1; i >= 0; i--) {
+ var term = terms[i];
+
+ result = createCondition(
+ term,
+ result,
+ MISMATCH
+ );
+ };
+
+ return result;
+
+ case '|':
+ // A bar (|) separates two or more alternatives: exactly one of them must occur.
+ //
+ // a | b | c
+ // =
+ // match a
+ // then MATCH
+ // else match b
+ // then MATCH
+ // else match c
+ // then MATCH
+ // else MISMATCH
+
+ var result = MISMATCH;
+ var map = null;
+
+ for (var i = terms.length - 1; i >= 0; i--) {
+ var term = terms[i];
+
+ // reduce sequence of keywords into a Enum
+ if (isEnumCapatible(term)) {
+ if (map === null && i > 0 && isEnumCapatible(terms[i - 1])) {
+ map = Object.create(null);
+ result = createCondition(
+ {
+ type: 'Enum',
+ map: map
+ },
+ MATCH,
+ result
+ );
+ }
+
+ if (map !== null) {
+ var key = (isFunctionType(term.name) ? term.name.slice(0, -1) : term.name).toLowerCase();
+ if (key in map === false) {
+ map[key] = term;
+ continue;
+ }
+ }
+ }
+
+ map = null;
+
+ // create a new conditonal node
+ result = createCondition(
+ term,
+ MATCH,
+ result
+ );
+ };
+
+ return result;
+
+ case '&&':
+ // A double ampersand (&&) separates two or more components,
+ // all of which must occur, in any order.
+
+ // Use MatchOnce for groups with a large number of terms,
+ // since &&-groups produces at least N!-node trees
+ if (terms.length > 5) {
+ return {
+ type: 'MatchOnce',
+ terms: terms,
+ all: true
+ };
+ }
+
+ // Use a combination tree for groups with small number of terms
+ //
+ // a && b && c
+ // =
+ // match a
+ // then [b && c]
+ // else match b
+ // then [a && c]
+ // else match c
+ // then [a && b]
+ // else MISMATCH
+ //
+ // a && b
+ // =
+ // match a
+ // then match b
+ // then MATCH
+ // else MISMATCH
+ // else match b
+ // then match a
+ // then MATCH
+ // else MISMATCH
+ // else MISMATCH
+ var result = MISMATCH;
+
+ for (var i = terms.length - 1; i >= 0; i--) {
+ var term = terms[i];
+ var thenClause;
+
+ if (terms.length > 1) {
+ thenClause = buildGroupMatchGraph(
+ combinator,
+ terms.filter(function(newGroupTerm) {
+ return newGroupTerm !== term;
+ }),
+ false
+ );
+ } else {
+ thenClause = MATCH;
+ }
+
+ result = createCondition(
+ term,
+ thenClause,
+ result
+ );
+ };
+
+ return result;
+
+ case '||':
+ // A double bar (||) separates two or more options:
+ // one or more of them must occur, in any order.
+
+ // Use MatchOnce for groups with a large number of terms,
+ // since ||-groups produces at least N!-node trees
+ if (terms.length > 5) {
+ return {
+ type: 'MatchOnce',
+ terms: terms,
+ all: false
+ };
+ }
+
+ // Use a combination tree for groups with small number of terms
+ //
+ // a || b || c
+ // =
+ // match a
+ // then [b || c]
+ // else match b
+ // then [a || c]
+ // else match c
+ // then [a || b]
+ // else MISMATCH
+ //
+ // a || b
+ // =
+ // match a
+ // then match b
+ // then MATCH
+ // else MATCH
+ // else match b
+ // then match a
+ // then MATCH
+ // else MATCH
+ // else MISMATCH
+ var result = atLeastOneTermMatched ? MATCH : MISMATCH;
+
+ for (var i = terms.length - 1; i >= 0; i--) {
+ var term = terms[i];
+ var thenClause;
+
+ if (terms.length > 1) {
+ thenClause = buildGroupMatchGraph(
+ combinator,
+ terms.filter(function(newGroupTerm) {
+ return newGroupTerm !== term;
+ }),
+ true
+ );
+ } else {
+ thenClause = MATCH;
+ }
+
+ result = createCondition(
+ term,
+ thenClause,
+ result
+ );
+ };
+
+ return result;
+ }
+}
+
+function buildMultiplierMatchGraph(node) {
+ var result = MATCH;
+ var matchTerm = buildMatchGraph(node.term);
+
+ if (node.max === 0) {
+ // disable repeating of empty match to prevent infinite loop
+ matchTerm = createCondition(
+ matchTerm,
+ DISALLOW_EMPTY,
+ MISMATCH
+ );
+
+ // an occurrence count is not limited, make a cycle;
+ // to collect more terms on each following matching mismatch
+ result = createCondition(
+ matchTerm,
+ null, // will be a loop
+ MISMATCH
+ );
+
+ result.then = createCondition(
+ MATCH,
+ MATCH,
+ result // make a loop
+ );
+
+ if (node.comma) {
+ result.then.else = createCondition(
+ { type: 'Comma', syntax: node },
+ result,
+ MISMATCH
+ );
+ }
+ } else {
+ // create a match node chain for [min .. max] interval with optional matches
+ for (var i = node.min || 1; i <= node.max; i++) {
+ if (node.comma && result !== MATCH) {
+ result = createCondition(
+ { type: 'Comma', syntax: node },
+ result,
+ MISMATCH
+ );
+ }
+
+ result = createCondition(
+ matchTerm,
+ createCondition(
+ MATCH,
+ MATCH,
+ result
+ ),
+ MISMATCH
+ );
+ }
+ }
+
+ if (node.min === 0) {
+ // allow zero match
+ result = createCondition(
+ MATCH,
+ MATCH,
+ result
+ );
+ } else {
+ // create a match node chain to collect [0 ... min - 1] required matches
+ for (var i = 0; i < node.min - 1; i++) {
+ if (node.comma && result !== MATCH) {
+ result = createCondition(
+ { type: 'Comma', syntax: node },
+ result,
+ MISMATCH
+ );
+ }
+
+ result = createCondition(
+ matchTerm,
+ result,
+ MISMATCH
+ );
+ }
+ }
+
+ return result;
+}
+
+function buildMatchGraph(node) {
+ if (typeof node === 'function') {
+ return {
+ type: 'Generic',
+ fn: node
+ };
+ }
+
+ switch (node.type) {
+ case 'Group':
+ var result = buildGroupMatchGraph(
+ node.combinator,
+ node.terms.map(buildMatchGraph),
+ false
+ );
+
+ if (node.disallowEmpty) {
+ result = createCondition(
+ result,
+ DISALLOW_EMPTY,
+ MISMATCH
+ );
+ }
+
+ return result;
+
+ case 'Multiplier':
+ return buildMultiplierMatchGraph(node);
+
+ case 'Type':
+ case 'Property':
+ return {
+ type: node.type,
+ name: node.name,
+ syntax: node
+ };
+
+ case 'Keyword':
+ return {
+ type: node.type,
+ name: node.name.toLowerCase(),
+ syntax: node
+ };
+
+ case 'AtKeyword':
+ return {
+ type: node.type,
+ name: '@' + node.name.toLowerCase(),
+ syntax: node
+ };
+
+ case 'Function':
+ return {
+ type: node.type,
+ name: node.name.toLowerCase() + '(',
+ syntax: node
+ };
+
+ case 'String':
+ // convert a one char length String to a Token
+ if (node.value.length === 3) {
+ return {
+ type: 'Token',
+ value: node.value.charAt(1),
+ syntax: node
+ };
+ }
+
+ // otherwise use it as is
+ return {
+ type: node.type,
+ value: node.value.substr(1, node.value.length - 2).replace(/\\'/g, '\''),
+ syntax: node
+ };
+
+ case 'Token':
+ return {
+ type: node.type,
+ value: node.value,
+ syntax: node
+ };
+
+ case 'Comma':
+ return {
+ type: node.type,
+ syntax: node
+ };
+
+ default:
+ throw new Error('Unknown node type:', node.type);
+ }
+}
+
+module.exports = {
+ MATCH: MATCH,
+ MISMATCH: MISMATCH,
+ DISALLOW_EMPTY: DISALLOW_EMPTY,
+ buildMatchGraph: function(syntaxTree, ref) {
+ if (typeof syntaxTree === 'string') {
+ syntaxTree = parse(syntaxTree);
+ }
+
+ return {
+ type: 'MatchGraph',
+ match: buildMatchGraph(syntaxTree),
+ syntax: ref || null,
+ source: syntaxTree
+ };
+ }
+};
diff --git a/node_modules/css-tree/lib/lexer/match.js b/node_modules/css-tree/lib/lexer/match.js
new file mode 100644
index 0000000..333ed7f
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/match.js
@@ -0,0 +1,639 @@
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var matchGraph = require('./match-graph');
+var MATCH = matchGraph.MATCH;
+var MISMATCH = matchGraph.MISMATCH;
+var DISALLOW_EMPTY = matchGraph.DISALLOW_EMPTY;
+var TYPE = require('../tokenizer/const').TYPE;
+
+var STUB = 0;
+var TOKEN = 1;
+var OPEN_SYNTAX = 2;
+var CLOSE_SYNTAX = 3;
+
+var EXIT_REASON_MATCH = 'Match';
+var EXIT_REASON_MISMATCH = 'Mismatch';
+var EXIT_REASON_ITERATION_LIMIT = 'Maximum iteration number exceeded (please fill an issue on https://github.com/csstree/csstree/issues)';
+
+var ITERATION_LIMIT = 15000;
+var totalIterationCount = 0;
+
+function reverseList(list) {
+ var prev = null;
+ var next = null;
+ var item = list;
+
+ while (item !== null) {
+ next = item.prev;
+ item.prev = prev;
+ prev = item;
+ item = next;
+ }
+
+ return prev;
+}
+
+function areStringsEqualCaseInsensitive(testStr, referenceStr) {
+ if (testStr.length !== referenceStr.length) {
+ return false;
+ }
+
+ for (var i = 0; i < testStr.length; i++) {
+ var testCode = testStr.charCodeAt(i);
+ var referenceCode = referenceStr.charCodeAt(i);
+
+ // testCode.toLowerCase() for U+0041 LATIN CAPITAL LETTER A (A) .. U+005A LATIN CAPITAL LETTER Z (Z).
+ if (testCode >= 0x0041 && testCode <= 0x005A) {
+ testCode = testCode | 32;
+ }
+
+ if (testCode !== referenceCode) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function isContextEdgeDelim(token) {
+ if (token.type !== TYPE.Delim) {
+ return false;
+ }
+
+ // Fix matching for unicode-range: U+30??, U+FF00-FF9F
+ // Probably we need to check out previous match instead
+ return token.value !== '?';
+}
+
+function isCommaContextStart(token) {
+ if (token === null) {
+ return true;
+ }
+
+ return (
+ token.type === TYPE.Comma ||
+ token.type === TYPE.Function ||
+ token.type === TYPE.LeftParenthesis ||
+ token.type === TYPE.LeftSquareBracket ||
+ token.type === TYPE.LeftCurlyBracket ||
+ isContextEdgeDelim(token)
+ );
+}
+
+function isCommaContextEnd(token) {
+ if (token === null) {
+ return true;
+ }
+
+ return (
+ token.type === TYPE.RightParenthesis ||
+ token.type === TYPE.RightSquareBracket ||
+ token.type === TYPE.RightCurlyBracket ||
+ token.type === TYPE.Delim
+ );
+}
+
+function internalMatch(tokens, state, syntaxes) {
+ function moveToNextToken() {
+ do {
+ tokenIndex++;
+ token = tokenIndex < tokens.length ? tokens[tokenIndex] : null;
+ } while (token !== null && (token.type === TYPE.WhiteSpace || token.type === TYPE.Comment));
+ }
+
+ function getNextToken(offset) {
+ var nextIndex = tokenIndex + offset;
+
+ return nextIndex < tokens.length ? tokens[nextIndex] : null;
+ }
+
+ function stateSnapshotFromSyntax(nextState, prev) {
+ return {
+ nextState: nextState,
+ matchStack: matchStack,
+ syntaxStack: syntaxStack,
+ thenStack: thenStack,
+ tokenIndex: tokenIndex,
+ prev: prev
+ };
+ }
+
+ function pushThenStack(nextState) {
+ thenStack = {
+ nextState: nextState,
+ matchStack: matchStack,
+ syntaxStack: syntaxStack,
+ prev: thenStack
+ };
+ }
+
+ function pushElseStack(nextState) {
+ elseStack = stateSnapshotFromSyntax(nextState, elseStack);
+ }
+
+ function addTokenToMatch() {
+ matchStack = {
+ type: TOKEN,
+ syntax: state.syntax,
+ token: token,
+ prev: matchStack
+ };
+
+ moveToNextToken();
+ syntaxStash = null;
+
+ if (tokenIndex > longestMatch) {
+ longestMatch = tokenIndex;
+ }
+ }
+
+ function openSyntax() {
+ syntaxStack = {
+ syntax: state.syntax,
+ opts: state.syntax.opts || (syntaxStack !== null && syntaxStack.opts) || null,
+ prev: syntaxStack
+ };
+
+ matchStack = {
+ type: OPEN_SYNTAX,
+ syntax: state.syntax,
+ token: matchStack.token,
+ prev: matchStack
+ };
+ }
+
+ function closeSyntax() {
+ if (matchStack.type === OPEN_SYNTAX) {
+ matchStack = matchStack.prev;
+ } else {
+ matchStack = {
+ type: CLOSE_SYNTAX,
+ syntax: syntaxStack.syntax,
+ token: matchStack.token,
+ prev: matchStack
+ };
+ }
+
+ syntaxStack = syntaxStack.prev;
+ }
+
+ var syntaxStack = null;
+ var thenStack = null;
+ var elseStack = null;
+
+ // null – stashing allowed, nothing stashed
+ // false – stashing disabled, nothing stashed
+ // anithing else – fail stashable syntaxes, some syntax stashed
+ var syntaxStash = null;
+
+ var iterationCount = 0; // count iterations and prevent infinite loop
+ var exitReason = null;
+
+ var token = null;
+ var tokenIndex = -1;
+ var longestMatch = 0;
+ var matchStack = {
+ type: STUB,
+ syntax: null,
+ token: null,
+ prev: null
+ };
+
+ moveToNextToken();
+
+ while (exitReason === null && ++iterationCount < ITERATION_LIMIT) {
+ // function mapList(list, fn) {
+ // var result = [];
+ // while (list) {
+ // result.unshift(fn(list));
+ // list = list.prev;
+ // }
+ // return result;
+ // }
+ // console.log('--\n',
+ // '#' + iterationCount,
+ // require('util').inspect({
+ // match: mapList(matchStack, x => x.type === TOKEN ? x.token && x.token.value : x.syntax ? ({ [OPEN_SYNTAX]: '<', [CLOSE_SYNTAX]: '</' }[x.type] || x.type) + '!' + x.syntax.name : null),
+ // token: token && token.value,
+ // tokenIndex,
+ // syntax: syntax.type + (syntax.id ? ' #' + syntax.id : '')
+ // }, { depth: null })
+ // );
+ switch (state.type) {
+ case 'Match':
+ if (thenStack === null) {
+ // turn to MISMATCH when some tokens left unmatched
+ if (token !== null) {
+ // doesn't mismatch if just one token left and it's an IE hack
+ if (tokenIndex !== tokens.length - 1 || (token.value !== '\\0' && token.value !== '\\9')) {
+ state = MISMATCH;
+ break;
+ }
+ }
+
+ // break the main loop, return a result - MATCH
+ exitReason = EXIT_REASON_MATCH;
+ break;
+ }
+
+ // go to next syntax (`then` branch)
+ state = thenStack.nextState;
+
+ // check match is not empty
+ if (state === DISALLOW_EMPTY) {
+ if (thenStack.matchStack === matchStack) {
+ state = MISMATCH;
+ break;
+ } else {
+ state = MATCH;
+ }
+ }
+
+ // close syntax if needed
+ while (thenStack.syntaxStack !== syntaxStack) {
+ closeSyntax();
+ }
+
+ // pop stack
+ thenStack = thenStack.prev;
+ break;
+
+ case 'Mismatch':
+ // when some syntax is stashed
+ if (syntaxStash !== null && syntaxStash !== false) {
+ // there is no else branches or a branch reduce match stack
+ if (elseStack === null || tokenIndex > elseStack.tokenIndex) {
+ // restore state from the stash
+ elseStack = syntaxStash;
+ syntaxStash = false; // disable stashing
+ }
+ } else if (elseStack === null) {
+ // no else branches -> break the main loop
+ // return a result - MISMATCH
+ exitReason = EXIT_REASON_MISMATCH;
+ break;
+ }
+
+ // go to next syntax (`else` branch)
+ state = elseStack.nextState;
+
+ // restore all the rest stack states
+ thenStack = elseStack.thenStack;
+ syntaxStack = elseStack.syntaxStack;
+ matchStack = elseStack.matchStack;
+ tokenIndex = elseStack.tokenIndex;
+ token = tokenIndex < tokens.length ? tokens[tokenIndex] : null;
+
+ // pop stack
+ elseStack = elseStack.prev;
+ break;
+
+ case 'MatchGraph':
+ state = state.match;
+ break;
+
+ case 'If':
+ // IMPORTANT: else stack push must go first,
+ // since it stores the state of thenStack before changes
+ if (state.else !== MISMATCH) {
+ pushElseStack(state.else);
+ }
+
+ if (state.then !== MATCH) {
+ pushThenStack(state.then);
+ }
+
+ state = state.match;
+ break;
+
+ case 'MatchOnce':
+ state = {
+ type: 'MatchOnceBuffer',
+ syntax: state,
+ index: 0,
+ mask: 0
+ };
+ break;
+
+ case 'MatchOnceBuffer':
+ var terms = state.syntax.terms;
+
+ if (state.index === terms.length) {
+ // no matches at all or it's required all terms to be matched
+ if (state.mask === 0 || state.syntax.all) {
+ state = MISMATCH;
+ break;
+ }
+
+ // a partial match is ok
+ state = MATCH;
+ break;
+ }
+
+ // all terms are matched
+ if (state.mask === (1 << terms.length) - 1) {
+ state = MATCH;
+ break;
+ }
+
+ for (; state.index < terms.length; state.index++) {
+ var matchFlag = 1 << state.index;
+
+ if ((state.mask & matchFlag) === 0) {
+ // IMPORTANT: else stack push must go first,
+ // since it stores the state of thenStack before changes
+ pushElseStack(state);
+ pushThenStack({
+ type: 'AddMatchOnce',
+ syntax: state.syntax,
+ mask: state.mask | matchFlag
+ });
+
+ // match
+ state = terms[state.index++];
+ break;
+ }
+ }
+ break;
+
+ case 'AddMatchOnce':
+ state = {
+ type: 'MatchOnceBuffer',
+ syntax: state.syntax,
+ index: 0,
+ mask: state.mask
+ };
+ break;
+
+ case 'Enum':
+ if (token !== null) {
+ var name = token.value.toLowerCase();
+
+ // drop \0 and \9 hack from keyword name
+ if (name.indexOf('\\') !== -1) {
+ name = name.replace(/\\[09].*$/, '');
+ }
+
+ if (hasOwnProperty.call(state.map, name)) {
+ state = state.map[name];
+ break;
+ }
+ }
+
+ state = MISMATCH;
+ break;
+
+ case 'Generic':
+ var opts = syntaxStack !== null ? syntaxStack.opts : null;
+ var lastTokenIndex = tokenIndex + Math.floor(state.fn(token, getNextToken, opts));
+
+ if (!isNaN(lastTokenIndex) && lastTokenIndex > tokenIndex) {
+ while (tokenIndex < lastTokenIndex) {
+ addTokenToMatch();
+ }
+
+ state = MATCH;
+ } else {
+ state = MISMATCH;
+ }
+
+ break;
+
+ case 'Type':
+ case 'Property':
+ var syntaxDict = state.type === 'Type' ? 'types' : 'properties';
+ var dictSyntax = hasOwnProperty.call(syntaxes, syntaxDict) ? syntaxes[syntaxDict][state.name] : null;
+
+ if (!dictSyntax || !dictSyntax.match) {
+ throw new Error(
+ 'Bad syntax reference: ' +
+ (state.type === 'Type'
+ ? '<' + state.name + '>'
+ : '<\'' + state.name + '\'>')
+ );
+ }
+
+ // stash a syntax for types with low priority
+ if (syntaxStash !== false && token !== null && state.type === 'Type') {
+ var lowPriorityMatching =
+ // https://drafts.csswg.org/css-values-4/#custom-idents
+ // When parsing positionally-ambiguous keywords in a property value, a <custom-ident> production
+ // can only claim the keyword if no other unfulfilled production can claim it.
+ (state.name === 'custom-ident' && token.type === TYPE.Ident) ||
+
+ // https://drafts.csswg.org/css-values-4/#lengths
+ // ... if a `0` could be parsed as either a <number> or a <length> in a property (such as line-height),
+ // it must parse as a <number>
+ (state.name === 'length' && token.value === '0');
+
+ if (lowPriorityMatching) {
+ if (syntaxStash === null) {
+ syntaxStash = stateSnapshotFromSyntax(state, elseStack);
+ }
+
+ state = MISMATCH;
+ break;
+ }
+ }
+
+ openSyntax();
+ state = dictSyntax.match;
+ break;
+
+ case 'Keyword':
+ var name = state.name;
+
+ if (token !== null) {
+ var keywordName = token.value;
+
+ // drop \0 and \9 hack from keyword name
+ if (keywordName.indexOf('\\') !== -1) {
+ keywordName = keywordName.replace(/\\[09].*$/, '');
+ }
+
+ if (areStringsEqualCaseInsensitive(keywordName, name)) {
+ addTokenToMatch();
+ state = MATCH;
+ break;
+ }
+ }
+
+ state = MISMATCH;
+ break;
+
+ case 'AtKeyword':
+ case 'Function':
+ if (token !== null && areStringsEqualCaseInsensitive(token.value, state.name)) {
+ addTokenToMatch();
+ state = MATCH;
+ break;
+ }
+
+ state = MISMATCH;
+ break;
+
+ case 'Token':
+ if (token !== null && token.value === state.value) {
+ addTokenToMatch();
+ state = MATCH;
+ break;
+ }
+
+ state = MISMATCH;
+ break;
+
+ case 'Comma':
+ if (token !== null && token.type === TYPE.Comma) {
+ if (isCommaContextStart(matchStack.token)) {
+ state = MISMATCH;
+ } else {
+ addTokenToMatch();
+ state = isCommaContextEnd(token) ? MISMATCH : MATCH;
+ }
+ } else {
+ state = isCommaContextStart(matchStack.token) || isCommaContextEnd(token) ? MATCH : MISMATCH;
+ }
+
+ break;
+
+ case 'String':
+ var string = '';
+
+ for (var lastTokenIndex = tokenIndex; lastTokenIndex < tokens.length && string.length < state.value.length; lastTokenIndex++) {
+ string += tokens[lastTokenIndex].value;
+ }
+
+ if (areStringsEqualCaseInsensitive(string, state.value)) {
+ while (tokenIndex < lastTokenIndex) {
+ addTokenToMatch();
+ }
+
+ state = MATCH;
+ } else {
+ state = MISMATCH;
+ }
+
+ break;
+
+ default:
+ throw new Error('Unknown node type: ' + state.type);
+ }
+ }
+
+ totalIterationCount += iterationCount;
+
+ switch (exitReason) {
+ case null:
+ console.warn('[csstree-match] BREAK after ' + ITERATION_LIMIT + ' iterations');
+ exitReason = EXIT_REASON_ITERATION_LIMIT;
+ matchStack = null;
+ break;
+
+ case EXIT_REASON_MATCH:
+ while (syntaxStack !== null) {
+ closeSyntax();
+ }
+ break;
+
+ default:
+ matchStack = null;
+ }
+
+ return {
+ tokens: tokens,
+ reason: exitReason,
+ iterations: iterationCount,
+ match: matchStack,
+ longestMatch: longestMatch
+ };
+}
+
+function matchAsList(tokens, matchGraph, syntaxes) {
+ var matchResult = internalMatch(tokens, matchGraph, syntaxes || {});
+
+ if (matchResult.match !== null) {
+ var item = reverseList(matchResult.match).prev;
+
+ matchResult.match = [];
+
+ while (item !== null) {
+ switch (item.type) {
+ case STUB:
+ break;
+
+ case OPEN_SYNTAX:
+ case CLOSE_SYNTAX:
+ matchResult.match.push({
+ type: item.type,
+ syntax: item.syntax
+ });
+ break;
+
+ default:
+ matchResult.match.push({
+ token: item.token.value,
+ node: item.token.node
+ });
+ break;
+ }
+
+ item = item.prev;
+ }
+ }
+
+ return matchResult;
+}
+
+function matchAsTree(tokens, matchGraph, syntaxes) {
+ var matchResult = internalMatch(tokens, matchGraph, syntaxes || {});
+
+ if (matchResult.match === null) {
+ return matchResult;
+ }
+
+ var item = matchResult.match;
+ var host = matchResult.match = {
+ syntax: matchGraph.syntax || null,
+ match: []
+ };
+ var hostStack = [host];
+
+ // revert a list and start with 2nd item since 1st is a stub item
+ item = reverseList(item).prev;
+
+ // build a tree
+ while (item !== null) {
+ switch (item.type) {
+ case OPEN_SYNTAX:
+ host.match.push(host = {
+ syntax: item.syntax,
+ match: []
+ });
+ hostStack.push(host);
+ break;
+
+ case CLOSE_SYNTAX:
+ hostStack.pop();
+ host = hostStack[hostStack.length - 1];
+ break;
+
+ default:
+ host.match.push({
+ syntax: item.syntax || null,
+ token: item.token.value,
+ node: item.token.node
+ });
+ }
+
+ item = item.prev;
+ }
+
+ return matchResult;
+}
+
+module.exports = {
+ matchAsList: matchAsList,
+ matchAsTree: matchAsTree,
+ getTotalIterationCount: function() {
+ return totalIterationCount;
+ }
+};
diff --git a/node_modules/css-tree/lib/lexer/prepare-tokens.js b/node_modules/css-tree/lib/lexer/prepare-tokens.js
new file mode 100644
index 0000000..0a9d65e
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/prepare-tokens.js
@@ -0,0 +1,73 @@
+var tokenize = require('../tokenizer');
+var TokenStream = require('../common/TokenStream');
+var tokenStream = new TokenStream();
+var astToTokens = {
+ decorator: function(handlers) {
+ var curNode = null;
+ var prev = { len: 0, node: null };
+ var nodes = [prev];
+ var buffer = '';
+
+ return {
+ children: handlers.children,
+ node: function(node) {
+ var tmp = curNode;
+ curNode = node;
+ handlers.node.call(this, node);
+ curNode = tmp;
+ },
+ chunk: function(chunk) {
+ buffer += chunk;
+ if (prev.node !== curNode) {
+ nodes.push({
+ len: chunk.length,
+ node: curNode
+ });
+ } else {
+ prev.len += chunk.length;
+ }
+ },
+ result: function() {
+ return prepareTokens(buffer, nodes);
+ }
+ };
+ }
+};
+
+function prepareTokens(str, nodes) {
+ var tokens = [];
+ var nodesOffset = 0;
+ var nodesIndex = 0;
+ var currentNode = nodes ? nodes[nodesIndex].node : null;
+
+ tokenize(str, tokenStream);
+
+ while (!tokenStream.eof) {
+ if (nodes) {
+ while (nodesIndex < nodes.length && nodesOffset + nodes[nodesIndex].len <= tokenStream.tokenStart) {
+ nodesOffset += nodes[nodesIndex++].len;
+ currentNode = nodes[nodesIndex].node;
+ }
+ }
+
+ tokens.push({
+ type: tokenStream.tokenType,
+ value: tokenStream.getTokenValue(),
+ index: tokenStream.tokenIndex, // TODO: remove it, temporary solution
+ balance: tokenStream.balance[tokenStream.tokenIndex], // TODO: remove it, temporary solution
+ node: currentNode
+ });
+ tokenStream.next();
+ // console.log({ ...tokens[tokens.length - 1], node: undefined });
+ }
+
+ return tokens;
+}
+
+module.exports = function(value, syntax) {
+ if (typeof value === 'string') {
+ return prepareTokens(value, null);
+ }
+
+ return syntax.generate(value, astToTokens);
+};
diff --git a/node_modules/css-tree/lib/lexer/search.js b/node_modules/css-tree/lib/lexer/search.js
new file mode 100644
index 0000000..7e270ab
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/search.js
@@ -0,0 +1,65 @@
+var List = require('../common/List');
+
+function getFirstMatchNode(matchNode) {
+ if ('node' in matchNode) {
+ return matchNode.node;
+ }
+
+ return getFirstMatchNode(matchNode.match[0]);
+}
+
+function getLastMatchNode(matchNode) {
+ if ('node' in matchNode) {
+ return matchNode.node;
+ }
+
+ return getLastMatchNode(matchNode.match[matchNode.match.length - 1]);
+}
+
+function matchFragments(lexer, ast, match, type, name) {
+ function findFragments(matchNode) {
+ if (matchNode.syntax !== null &&
+ matchNode.syntax.type === type &&
+ matchNode.syntax.name === name) {
+ var start = getFirstMatchNode(matchNode);
+ var end = getLastMatchNode(matchNode);
+
+ lexer.syntax.walk(ast, function(node, item, list) {
+ if (node === start) {
+ var nodes = new List();
+
+ do {
+ nodes.appendData(item.data);
+
+ if (item.data === end) {
+ break;
+ }
+
+ item = item.next;
+ } while (item !== null);
+
+ fragments.push({
+ parent: list,
+ nodes: nodes
+ });
+ }
+ });
+ }
+
+ if (Array.isArray(matchNode.match)) {
+ matchNode.match.forEach(findFragments);
+ }
+ }
+
+ var fragments = [];
+
+ if (match.matched !== null) {
+ findFragments(match.matched);
+ }
+
+ return fragments;
+}
+
+module.exports = {
+ matchFragments: matchFragments
+};
diff --git a/node_modules/css-tree/lib/lexer/structure.js b/node_modules/css-tree/lib/lexer/structure.js
new file mode 100644
index 0000000..e1239dd
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/structure.js
@@ -0,0 +1,163 @@
+var List = require('../common/List');
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+function isValidNumber(value) {
+ // Number.isInteger(value) && value >= 0
+ return (
+ typeof value === 'number' &&
+ isFinite(value) &&
+ Math.floor(value) === value &&
+ value >= 0
+ );
+}
+
+function isValidLocation(loc) {
+ return (
+ Boolean(loc) &&
+ isValidNumber(loc.offset) &&
+ isValidNumber(loc.line) &&
+ isValidNumber(loc.column)
+ );
+}
+
+function createNodeStructureChecker(type, fields) {
+ return function checkNode(node, warn) {
+ if (!node || node.constructor !== Object) {
+ return warn(node, 'Type of node should be an Object');
+ }
+
+ for (var key in node) {
+ var valid = true;
+
+ if (hasOwnProperty.call(node, key) === false) {
+ continue;
+ }
+
+ if (key === 'type') {
+ if (node.type !== type) {
+ warn(node, 'Wrong node type `' + node.type + '`, expected `' + type + '`');
+ }
+ } else if (key === 'loc') {
+ if (node.loc === null) {
+ continue;
+ } else if (node.loc && node.loc.constructor === Object) {
+ if (typeof node.loc.source !== 'string') {
+ key += '.source';
+ } else if (!isValidLocation(node.loc.start)) {
+ key += '.start';
+ } else if (!isValidLocation(node.loc.end)) {
+ key += '.end';
+ } else {
+ continue;
+ }
+ }
+
+ valid = false;
+ } else if (fields.hasOwnProperty(key)) {
+ for (var i = 0, valid = false; !valid && i < fields[key].length; i++) {
+ var fieldType = fields[key][i];
+
+ switch (fieldType) {
+ case String:
+ valid = typeof node[key] === 'string';
+ break;
+
+ case Boolean:
+ valid = typeof node[key] === 'boolean';
+ break;
+
+ case null:
+ valid = node[key] === null;
+ break;
+
+ default:
+ if (typeof fieldType === 'string') {
+ valid = node[key] && node[key].type === fieldType;
+ } else if (Array.isArray(fieldType)) {
+ valid = node[key] instanceof List;
+ }
+ }
+ }
+ } else {
+ warn(node, 'Unknown field `' + key + '` for ' + type + ' node type');
+ }
+
+ if (!valid) {
+ warn(node, 'Bad value for `' + type + '.' + key + '`');
+ }
+ }
+
+ for (var key in fields) {
+ if (hasOwnProperty.call(fields, key) &&
+ hasOwnProperty.call(node, key) === false) {
+ warn(node, 'Field `' + type + '.' + key + '` is missed');
+ }
+ }
+ };
+}
+
+function processStructure(name, nodeType) {
+ var structure = nodeType.structure;
+ var fields = {
+ type: String,
+ loc: true
+ };
+ var docs = {
+ type: '"' + name + '"'
+ };
+
+ for (var key in structure) {
+ if (hasOwnProperty.call(structure, key) === false) {
+ continue;
+ }
+
+ var docsTypes = [];
+ var fieldTypes = fields[key] = Array.isArray(structure[key])
+ ? structure[key].slice()
+ : [structure[key]];
+
+ for (var i = 0; i < fieldTypes.length; i++) {
+ var fieldType = fieldTypes[i];
+ if (fieldType === String || fieldType === Boolean) {
+ docsTypes.push(fieldType.name);
+ } else if (fieldType === null) {
+ docsTypes.push('null');
+ } else if (typeof fieldType === 'string') {
+ docsTypes.push('<' + fieldType + '>');
+ } else if (Array.isArray(fieldType)) {
+ docsTypes.push('List'); // TODO: use type enum
+ } else {
+ throw new Error('Wrong value `' + fieldType + '` in `' + name + '.' + key + '` structure definition');
+ }
+ }
+
+ docs[key] = docsTypes.join(' | ');
+ }
+
+ return {
+ docs: docs,
+ check: createNodeStructureChecker(name, fields)
+ };
+}
+
+module.exports = {
+ getStructureFromConfig: function(config) {
+ var structure = {};
+
+ if (config.node) {
+ for (var name in config.node) {
+ if (hasOwnProperty.call(config.node, name)) {
+ var nodeType = config.node[name];
+
+ if (nodeType.structure) {
+ structure[name] = processStructure(name, nodeType);
+ } else {
+ throw new Error('Missed `structure` field in `' + name + '` node type definition');
+ }
+ }
+ }
+ }
+
+ return structure;
+ }
+};
diff --git a/node_modules/css-tree/lib/lexer/trace.js b/node_modules/css-tree/lib/lexer/trace.js
new file mode 100644
index 0000000..3a45e53
--- /dev/null
+++ b/node_modules/css-tree/lib/lexer/trace.js
@@ -0,0 +1,79 @@
+function getTrace(node) {
+ function shouldPutToTrace(syntax) {
+ if (syntax === null) {
+ return false;
+ }
+
+ return (
+ syntax.type === 'Type' ||
+ syntax.type === 'Property' ||
+ syntax.type === 'Keyword'
+ );
+ }
+
+ function hasMatch(matchNode) {
+ if (Array.isArray(matchNode.match)) {
+ // use for-loop for better perfomance
+ for (var i = 0; i < matchNode.match.length; i++) {
+ if (hasMatch(matchNode.match[i])) {
+ if (shouldPutToTrace(matchNode.syntax)) {
+ result.unshift(matchNode.syntax);
+ }
+
+ return true;
+ }
+ }
+ } else if (matchNode.node === node) {
+ result = shouldPutToTrace(matchNode.syntax)
+ ? [matchNode.syntax]
+ : [];
+
+ return true;
+ }
+
+ return false;
+ }
+
+ var result = null;
+
+ if (this.matched !== null) {
+ hasMatch(this.matched);
+ }
+
+ return result;
+}
+
+function testNode(match, node, fn) {
+ var trace = getTrace.call(match, node);
+
+ if (trace === null) {
+ return false;
+ }
+
+ return trace.some(fn);
+}
+
+function isType(node, type) {
+ return testNode(this, node, function(matchNode) {
+ return matchNode.type === 'Type' && matchNode.name === type;
+ });
+}
+
+function isProperty(node, property) {
+ return testNode(this, node, function(matchNode) {
+ return matchNode.type === 'Property' && matchNode.name === property;
+ });
+}
+
+function isKeyword(node) {
+ return testNode(this, node, function(matchNode) {
+ return matchNode.type === 'Keyword';
+ });
+}
+
+module.exports = {
+ getTrace: getTrace,
+ isType: isType,
+ isProperty: isProperty,
+ isKeyword: isKeyword
+};