shady-css-parser
Version:
A fast, small and flexible CSS parser.
905 lines • 40.1 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
define("shady-css/common", ["require", "exports"], function (require, exports) {
"use strict";
/**
* A set of common RegExp matchers for tokenizing CSS.
* @constant
* @type {object}
* @default
*/
var matcher = {
whitespace: /\s/,
whitespaceGreedy: /(\s+)/g,
commentGreedy: /(\*\/)/g,
boundary: /[\(\)\{\}'"@;:\s]/,
stringBoundary: /['"]/
};
exports.matcher = matcher;
/**
* An enumeration of Node types.
* @constant
* @type {object}
* @default
*/
var nodeType = {
stylesheet: 'stylesheet',
comment: 'comment',
atRule: 'atRule',
ruleset: 'ruleset',
expression: 'expression',
declaration: 'declaration',
rulelist: 'rulelist',
discarded: 'discarded'
};
exports.nodeType = nodeType;
});
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
define("shady-css/token", ["require", "exports"], function (require, exports) {
"use strict";
/**
* An enumeration of Token types.
* @type {object}
* @default
* @static
*/
var TokenType;
(function (TokenType) {
TokenType[TokenType["none"] = 0] = "none";
TokenType[TokenType["whitespace"] = (Math.pow(2, 0))] = "whitespace";
TokenType[TokenType["string"] = (Math.pow(2, 1))] = "string";
TokenType[TokenType["comment"] = (Math.pow(2, 2))] = "comment";
TokenType[TokenType["word"] = (Math.pow(2, 3))] = "word";
TokenType[TokenType["boundary"] = (Math.pow(2, 4))] = "boundary";
TokenType[TokenType["propertyBoundary"] = (Math.pow(2, 5))] = "propertyBoundary";
// Special cases for boundary:
TokenType[TokenType["openParenthesis"] = (Math.pow(2, 6)) | TokenType.boundary] = "openParenthesis";
TokenType[TokenType["closeParenthesis"] = (Math.pow(2, 7)) | TokenType.boundary] = "closeParenthesis";
TokenType[TokenType["at"] = (Math.pow(2, 8)) | TokenType.boundary] = "at";
TokenType[TokenType["openBrace"] = (Math.pow(2, 9)) | TokenType.boundary] = "openBrace";
// [};] are property boundaries:
TokenType[TokenType["closeBrace"] = (Math.pow(2, 10)) | TokenType.propertyBoundary | TokenType.boundary] = "closeBrace";
TokenType[TokenType["semicolon"] = (Math.pow(2, 11)) | TokenType.propertyBoundary | TokenType.boundary] = "semicolon";
// : is a chimaeric abomination:
// foo:bar{}
// foo:bar;
TokenType[TokenType["colon"] = (Math.pow(2, 12)) | TokenType.boundary | TokenType.word] = "colon";
// TODO: are these two boundaries? I mean, sometimes they are I guess? Or
// maybe they shouldn't exist in the boundaryTokenTypes map.
TokenType[TokenType["hyphen"] = (Math.pow(2, 13))] = "hyphen";
TokenType[TokenType["underscore"] = (Math.pow(2, 14))] = "underscore";
})(TokenType || (TokenType = {}));
;
/**
* Class that describes individual tokens as produced by the Tokenizer.
*/
var Token = (function () {
/**
* Create a Token instance.
* @param {number} type The lexical type of the Token.
* @param {number} start The start index of the text corresponding to the
* Token in the CSS text.
* @param {number} end The end index of the text corresponding to the Token
* in the CSS text.
*/
function Token(type, start, end) {
if (start === void 0) { start = undefined; }
if (end === void 0) { end = undefined; }
this.type = type;
this.start = start;
this.end = end;
this.previous = null;
this.next = null;
}
/**
* Test if the Token matches a given numeric type. Types match if the bitwise
* AND of the Token's type and the argument type are equivalent to the
* argument type.
* @param {number} type The numeric type to test for equivalency with the
* Token.
*/
Token.prototype.is = function (type) {
return (this.type & type) === type;
};
return Token;
}());
Token.type = TokenType;
exports.Token = Token;
/**
* A mapping of boundary token text to their corresponding types.
* @type {object}
* @default
* @const
*/
var boundaryTokenTypes = {
'(': Token.type.openParenthesis,
')': Token.type.closeParenthesis,
':': Token.type.colon,
'@': Token.type.at,
'{': Token.type.openBrace,
'}': Token.type.closeBrace,
';': Token.type.semicolon,
'-': Token.type.hyphen,
'_': Token.type.underscore
};
exports.boundaryTokenTypes = boundaryTokenTypes;
});
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
define("shady-css/tokenizer", ["require", "exports", "shady-css/common", "shady-css/token"], function (require, exports, common_1, token_1) {
"use strict";
var currentToken = Symbol('currentToken');
var cursorToken = Symbol('cursorToken');
var getNextToken = Symbol('getNextToken');
/**
* Class that implements tokenization of significant lexical features of the
* CSS syntax.
*/
var Tokenizer = (function () {
/**
* Create a Tokenizer instance.
* @param {string} cssText The raw CSS string to be tokenized.
*
*/
function Tokenizer(cssText) {
this.cssText = cssText;
/**
* Tracks the position of the tokenizer in the source string.
* Also the default head of the Token linked list.
* @type {!Token}
* @private
*/
this[cursorToken] = new token_1.Token(token_1.Token.type.none, 0, 0);
/**
* Holds a reference to a Token that is "next" in the source string, often
* due to having been peeked at.
* @type {?Token}
* @readonly
*/
this[currentToken] = null;
}
Object.defineProperty(Tokenizer.prototype, "offset", {
get: function () {
return this[cursorToken].end;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Tokenizer.prototype, "currentToken", {
/**
* The current token that will be returned by a call to `advance`. This
* reference is useful for "peeking" at the next token ahead in the sequence.
* If the entire CSS text has been tokenized, the `currentToken` will be null.
* @type {Token}
*/
get: function () {
if (this[currentToken] == null) {
this[currentToken] = this[getNextToken]();
}
return this[currentToken];
},
enumerable: true,
configurable: true
});
/**
* Advance the Tokenizer to the next token in the sequence.
* @return {Token} The current token prior to the call to `advance`, or null
* if the entire CSS text has been tokenized.
*/
Tokenizer.prototype.advance = function () {
var token;
if (this[currentToken] != null) {
token = this[currentToken];
this[currentToken] = null;
}
else {
token = this[getNextToken]();
}
return token;
};
/**
* Extract a slice from the CSS text, using two tokens to represent the range
* of text to be extracted. The extracted text will include all text between
* the start index of the first token and the offset index of the second token
* (or the offset index of the first token if the second is not provided).
* @param {Token} startToken The token that represents the beginning of the
* text range to be extracted.
* @param {Token} endToken The token that represents the end of the text range
* to be extracted. Defaults to the startToken if no endToken is provided.
* @return {string} The substring of the CSS text corresponding to the
* startToken and endToken.
*/
Tokenizer.prototype.slice = function (startToken, endToken) {
endToken = endToken || startToken;
return this.cssText.substring(startToken.start, endToken.end);
};
/**
* Flush all tokens from the Tokenizer.
* @return {array} An array of all tokens corresponding to the CSS text.
*/
Tokenizer.prototype.flush = function () {
var tokens = [];
while (this.currentToken) {
tokens.push(this.advance());
}
return tokens;
};
/**
* Extract the next token from the CSS text and advance the Tokenizer.
* @return {Token} A Token instance, or null if the entire CSS text has beeen
* tokenized.
*/
Tokenizer.prototype[getNextToken] = function () {
var character = this.cssText[this.offset];
var token;
this[currentToken] = null;
if (this.offset >= this.cssText.length) {
return null;
}
else if (common_1.matcher.whitespace.test(character)) {
token = this.tokenizeWhitespace(this.offset);
}
else if (common_1.matcher.stringBoundary.test(character)) {
token = this.tokenizeString(this.offset);
}
else if (character === '/' && this.cssText[this.offset + 1] === '*') {
token = this.tokenizeComment(this.offset);
}
else if (common_1.matcher.boundary.test(character)) {
token = this.tokenizeBoundary(this.offset);
}
else {
token = this.tokenizeWord(this.offset);
}
token.previous = this[cursorToken];
this[cursorToken].next = token;
this[cursorToken] = token;
return token;
};
/**
* Tokenize a string starting at a given offset in the CSS text. A string is
* any span of text that is wrapped by eclusively paired, non-escaped matching
* quotation marks.
* @param {number} offset An offset in the CSS text.
* @return {Token} A string Token instance.
*/
Tokenizer.prototype.tokenizeString = function (offset) {
var quotation = this.cssText[offset];
var escaped = false;
var start = offset;
var character;
while (character = this.cssText[++offset]) {
if (escaped) {
escaped = false;
continue;
}
if (character === quotation) {
++offset;
break;
}
if (character === '\\') {
escaped = true;
}
}
return new token_1.Token(token_1.Token.type.string, start, offset);
};
/**
* Tokenize a word starting at a given offset in the CSS text. A word is any
* span of text that is not whitespace, is not a string, is not a comment and
* is not a structural delimiter (such as braces and semicolon).
* @param {offset} number An offset in the CSS text.
* @return {Token} A word Token instance.
*/
Tokenizer.prototype.tokenizeWord = function (offset) {
var start = offset;
var character;
// TODO(cdata): change to greedy regex match?
while ((character = this.cssText[offset]) &&
!common_1.matcher.boundary.test(character)) {
offset++;
}
return new token_1.Token(token_1.Token.type.word, start, offset);
};
/**
* Tokenize whitespace starting at a given offset in the CSS text. Whitespace
* is any span of text made up of consecutive spaces, tabs, newlines and other
* single whitespace characters.
* @param {offset} number An offset in the CSS text.
* @return {Token} A whitespace Token instance.
*/
Tokenizer.prototype.tokenizeWhitespace = function (offset) {
var start = offset;
common_1.matcher.whitespaceGreedy.lastIndex = offset;
var match = common_1.matcher.whitespaceGreedy.exec(this.cssText);
if (match != null && match.index === offset) {
offset = common_1.matcher.whitespaceGreedy.lastIndex;
}
return new token_1.Token(token_1.Token.type.whitespace, start, offset);
};
/**
* Tokenize a comment starting at a given offset in the CSS text. A comment is
* any span of text beginning with the two characters / and *, and ending with
* a matching counterpart pair of consecurtive characters (* and /).
* @param {offset} number An offset in the CSS text.
* @return {Token} A comment Token instance.
*/
Tokenizer.prototype.tokenizeComment = function (offset) {
var start = offset;
common_1.matcher.commentGreedy.lastIndex = offset;
var match = common_1.matcher.commentGreedy.exec(this.cssText);
if (match == null) {
offset = this.cssText.length;
}
else {
offset = common_1.matcher.commentGreedy.lastIndex;
}
return new token_1.Token(token_1.Token.type.comment, start, offset);
};
/**
* Tokenize a boundary at a given offset in the CSS text. A boundary is any
* single structurally significant character. These characters include braces,
* semicolons, the "at" symbol and others.
* @param {offset} number An offset in the CSS text.
* @return {Token} A boundary Token instance.
*/
Tokenizer.prototype.tokenizeBoundary = function (offset) {
// TODO(cdata): Evaluate if this is faster than a switch statement:
var type = token_1.boundaryTokenTypes[this.cssText[offset]] || token_1.Token.type.boundary;
return new token_1.Token(type, offset, offset + 1);
};
return Tokenizer;
}());
exports.Tokenizer = Tokenizer;
});
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
define("shady-css/node-factory", ["require", "exports", "shady-css/common"], function (require, exports, common_2) {
"use strict";
/**
* Class used for generating nodes in a CSS AST. Extend this class to implement
* visitors to different nodes while the tree is being generated, and / or
* custom node generation.
*/
var NodeFactory = (function () {
function NodeFactory() {
}
/**
* Creates a Stylesheet node.
* @param {array} rules The list of rules that appear at the top
* level of the stylesheet.
* @return {object} A Stylesheet node.
*/
NodeFactory.prototype.stylesheet = function (rules) {
return { type: common_2.nodeType.stylesheet, rules: rules };
};
/**
* Creates an At Rule node.
* @param {string} name The "name" of the At Rule (e.g., `charset`)
* @param {string} parameters The "parameters" of the At Rule (e.g., `utf8`)
* @param {object=} rulelist The Rulelist node (if any) of the At Rule.
* @return {object} An At Rule node.
*/
NodeFactory.prototype.atRule = function (name, parameters, rulelist) {
return { type: common_2.nodeType.atRule, name: name, parameters: parameters, rulelist: rulelist };
};
/**
* Creates a Comment node.
* @param {string} value The full text content of the comment, including
* opening and closing comment signature.
* @return {object} A Comment node.
*/
NodeFactory.prototype.comment = function (value) {
return { type: common_2.nodeType.comment, value: value };
};
/**
* Creates a Rulelist node.
* @param {array} rules An array of the Rule nodes found within the Ruleset.
* @return {object} A Rulelist node.
*/
NodeFactory.prototype.rulelist = function (rules) {
return { type: common_2.nodeType.rulelist, rules: rules };
};
/**
* Creates a Ruleset node.
* @param {string} selector The selector that corresponds to the Selector
* (e.g., `#foo > .bar`).
* @param {object} rulelist The Rulelist node that corresponds to the Selector.
* @return {object} A Selector node.
*/
NodeFactory.prototype.ruleset = function (selector, rulelist) {
return { type: common_2.nodeType.ruleset, selector: selector, rulelist: rulelist };
};
/**
* Creates a Declaration node.
* @param {string} name The property name of the Declaration (e.g., `color`).
* @param {object} value Either an Expression node, or a Rulelist node, that
* corresponds to the value of the Declaration.
* @return {object} A Declaration node.
*/
NodeFactory.prototype.declaration = function (name, value) {
return { type: common_2.nodeType.declaration, name: name, value: value };
};
/**
* Creates an Expression node.
* @param {string} text The full text content of the expression (e.g.,
* `url(img.jpg)`)
* @return {object} An Expression node.
*/
NodeFactory.prototype.expression = function (text) {
return { type: common_2.nodeType.expression, text: text };
};
/**
* Creates a Discarded node. Discarded nodes contain content that was not
* parseable (usually due to typos, or otherwise unrecognized syntax).
* @param {string} text The text content that is discarded.
* @return {object} A Discarded node.
*/
NodeFactory.prototype.discarded = function (text) {
return { type: common_2.nodeType.discarded, text: text };
};
return NodeFactory;
}());
exports.NodeFactory = NodeFactory;
});
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
define("shady-css/node-visitor", ["require", "exports"], function (require, exports) {
"use strict";
var path = Symbol('path');
/**
* Class that implements a visitor pattern for ASTs produced by the Parser.
* Extend the NodeVisitor class to implement useful tree traversal operations
* such as stringification.
*/
var NodeVisitor = (function () {
/**
* Create a NodeVisitor instance.
*/
function NodeVisitor() {
this[path] = [];
}
Object.defineProperty(NodeVisitor.prototype, "path", {
/**
* A list of nodes that corresponds to the current path through an AST being
* visited, leading to where the currently visited node will be found.
* @type {array}
*/
get: function () {
return this[path];
},
enumerable: true,
configurable: true
});
/**
* Visit a node. The visited node will be added to the `path` before it is
* visited, and will be removed after it is visited. Nodes are "visited" by
* calling a method on the NodeVisitor instance that matches the node's type,
* if one is available on the NodeVisitor instance.
* @param {object} node The node to be visited.
* @return The return value of the method visiting the node, if any.
*/
NodeVisitor.prototype.visit = function (node) {
var result;
if (this[node.type]) {
this[path].push(node);
result = this[node.type](node);
this[path].pop();
}
return result;
};
return NodeVisitor;
}());
exports.NodeVisitor = NodeVisitor;
});
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
define("shady-css/stringifier", ["require", "exports", "shady-css/common", "shady-css/node-visitor"], function (require, exports, common_3, node_visitor_1) {
"use strict";
/**
* Class that implements basic stringification of an AST produced by the Parser.
*/
var Stringifier = (function (_super) {
__extends(Stringifier, _super);
function Stringifier() {
return _super !== null && _super.apply(this, arguments) || this;
}
/**
* Stringify an AST such as one produced by a Parser.
* @param {object} ast A node object representing the root of an AST.
* @return {string} The stringified CSS corresponding to the AST.
*/
Stringifier.prototype.stringify = function (ast) {
return this.visit(ast) || '';
};
/**
* Visit and stringify a Stylesheet node.
* @param {object} stylesheet A Stylesheet node.
* @return {string} The stringified CSS of the Stylesheet.
*/
Stringifier.prototype[common_3.nodeType.stylesheet] = function (stylesheet) {
var rules = '';
for (var i = 0; i < stylesheet.rules.length; ++i) {
rules += this.visit(stylesheet.rules[i]);
}
return rules;
};
/**
* Visit and stringify an At Rule node.
* @param {object} atRule An At Rule node.
* @return {string} The stringified CSS of the At Rule.
*/
Stringifier.prototype[common_3.nodeType.atRule] = function (atRule) {
return "@" + atRule.name +
(atRule.parameters ? " " + atRule.parameters : '') +
(atRule.rulelist ? "" + this.visit(atRule.rulelist) : ';');
};
/**
* Visit and stringify a Rulelist node.
* @param {object} rulelist A Rulelist node.
* @return {string} The stringified CSS of the Rulelist.
*/
Stringifier.prototype[common_3.nodeType.rulelist] = function (rulelist) {
var rules = '{';
for (var i = 0; i < rulelist.rules.length; ++i) {
rules += this.visit(rulelist.rules[i]);
}
return rules + '}';
};
/**
* Visit and stringify a Comment node.
* @param {object} comment A Comment node.
* @return {string} The stringified CSS of the Comment.
*/
Stringifier.prototype[common_3.nodeType.comment] = function (comment) {
return "" + comment.value;
};
/**
* Visit and stringify a Seletor node.
* @param {object} ruleset A Ruleset node.
* @return {string} The stringified CSS of the Ruleset.
*/
Stringifier.prototype[common_3.nodeType.ruleset] = function (ruleset) {
return "" + ruleset.selector + this.visit(ruleset.rulelist);
};
/**
* Visit and stringify a Declaration node.
* @param {object} declaration A Declaration node.
* @return {string} The stringified CSS of the Declaration.
*/
Stringifier.prototype[common_3.nodeType.declaration] = function (declaration) {
return declaration.value != null ?
declaration.name + ":" + this.visit(declaration.value) + ";" :
declaration.name + ";";
};
/**
* Visit and stringify an Expression node.
* @param {object} expression An Expression node.
* @return {string} The stringified CSS of the Expression.
*/
Stringifier.prototype[common_3.nodeType.expression] = function (expression) {
return "" + expression.text;
};
/**
* Visit a discarded node.
* @param {object} discarded A Discarded node.
* @return {string} An empty string, since Discarded nodes are discarded.
*/
Stringifier.prototype[common_3.nodeType.discarded] = function (discarded) {
return '';
};
return Stringifier;
}(node_visitor_1.NodeVisitor));
exports.Stringifier = Stringifier;
});
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
define("shady-css/parser", ["require", "exports", "shady-css/tokenizer", "shady-css/token", "shady-css/node-factory"], function (require, exports, tokenizer_1, token_2, node_factory_1) {
"use strict";
/**
* Class that implements a shady CSS parser.
*/
var Parser = (function () {
/**
* Create a Parser instance. When creating a Parser instance, a specialized
* NodeFactory can be supplied to implement streaming analysis and
* manipulation of the CSS AST.
*/
function Parser(nodeFactory) {
if (nodeFactory === void 0) { nodeFactory = new node_factory_1.NodeFactory(); }
this.nodeFactory = nodeFactory;
}
/**
* Parse CSS and generate an AST.
* @param {string} cssText The CSS to parse.
* @return {object} A CSS AST containing nodes that correspond to those
* generated by the Parser's NodeFactory.
*/
Parser.prototype.parse = function (cssText) {
return this.parseStylesheet(new tokenizer_1.Tokenizer(cssText));
};
/**
* Consumes tokens from a Tokenizer to parse a Stylesheet node.
* @param {Tokenizer} tokenizer A Tokenizer instance.
* @return {object} A Stylesheet node.
*/
Parser.prototype.parseStylesheet = function (tokenizer) {
return this.nodeFactory.stylesheet(this.parseRules(tokenizer));
};
/**
* Consumes tokens from a Tokenizer to parse a sequence of rules.
* @param {Tokenizer} tokenizer A Tokenizer instance.
* @return {array} A list of nodes corresponding to rules. For a parser
* configured with a basic NodeFactory, any of Comment, AtRule, Ruleset,
* Declaration and Discarded nodes may be present in the list.
*/
Parser.prototype.parseRules = function (tokenizer) {
var rules = [];
while (tokenizer.currentToken) {
var rule = this.parseRule(tokenizer);
if (rule) {
rules.push(rule);
}
}
return rules;
};
/**
* Consumes tokens from a Tokenizer to parse a single rule.
* @param {Tokenizer} tokenizer A Tokenizer instance.
* @return {object} If the current token in the Tokenizer is whitespace,
* returns null. Otherwise, returns the next parseable node.
*/
Parser.prototype.parseRule = function (tokenizer) {
// Trim leading whitespace:
if (tokenizer.currentToken.is(token_2.Token.type.whitespace)) {
tokenizer.advance();
return null;
}
else if (tokenizer.currentToken.is(token_2.Token.type.comment)) {
return this.parseComment(tokenizer);
}
else if (tokenizer.currentToken.is(token_2.Token.type.word)) {
return this.parseDeclarationOrRuleset(tokenizer);
}
else if (tokenizer.currentToken.is(token_2.Token.type.propertyBoundary)) {
return this.parseUnknown(tokenizer);
}
else if (tokenizer.currentToken.is(token_2.Token.type.at)) {
return this.parseAtRule(tokenizer);
}
else {
return this.parseUnknown(tokenizer);
}
};
/**
* Consumes tokens from a Tokenizer to parse a Comment node.
* @param {Tokenizer} tokenizer A Tokenizer instance.
* @return {object} A Comment node.
*/
Parser.prototype.parseComment = function (tokenizer) {
return this.nodeFactory.comment(tokenizer.slice(tokenizer.advance()));
};
/**
* Consumes tokens from a Tokenizer through the next boundary token to
* produce a Discarded node. This supports graceful recovery from many
* malformed CSS conditions.
* @param {Tokenizer} tokenizer A Tokenizer instance.
* @return {object} A Discarded node.
*/
Parser.prototype.parseUnknown = function (tokenizer) {
var start = tokenizer.advance();
var end;
while (tokenizer.currentToken &&
tokenizer.currentToken.is(token_2.Token.type.boundary)) {
end = tokenizer.advance();
}
return this.nodeFactory.discarded(tokenizer.slice(start, end));
};
/**
* Consumes tokens from a Tokenizer to parse an At Rule node.
* @param {Tokenizer} tokenizer A Tokenizer instance.
* @return {object} An At Rule node.
*/
Parser.prototype.parseAtRule = function (tokenizer) {
var name = '';
var rulelist = null;
var parametersStart = null;
var parametersEnd = null;
while (tokenizer.currentToken) {
if (tokenizer.currentToken.is(token_2.Token.type.whitespace)) {
tokenizer.advance();
}
else if (!name && tokenizer.currentToken.is(token_2.Token.type.at)) {
// Discard the @:
tokenizer.advance();
var start = tokenizer.currentToken;
var end = void 0;
while (tokenizer.currentToken &&
tokenizer.currentToken.is(token_2.Token.type.word)) {
end = tokenizer.advance();
}
name = tokenizer.slice(start, end);
}
else if (tokenizer.currentToken.is(token_2.Token.type.openBrace)) {
rulelist = this.parseRulelist(tokenizer);
break;
}
else if (tokenizer.currentToken.is(token_2.Token.type.propertyBoundary)) {
tokenizer.advance();
break;
}
else {
if (parametersStart == null) {
parametersStart = tokenizer.advance();
}
else {
parametersEnd = tokenizer.advance();
}
}
}
return this.nodeFactory.atRule(name, parametersStart ? tokenizer.slice(parametersStart, parametersEnd) : '', rulelist);
};
/**
* Consumes tokens from a Tokenizer to produce a Rulelist node.
* @param {Tokenizer} tokenizer A Tokenizer instance.
* @return {object} A Rulelist node.
*/
Parser.prototype.parseRulelist = function (tokenizer) {
var rules = [];
// Take the opening { boundary:
tokenizer.advance();
while (tokenizer.currentToken) {
if (tokenizer.currentToken.is(token_2.Token.type.closeBrace)) {
tokenizer.advance();
break;
}
else {
var rule = this.parseRule(tokenizer);
if (rule) {
rules.push(rule);
}
}
}
return this.nodeFactory.rulelist(rules);
};
/**
* Consumes tokens from a Tokenizer instance to produce a Declaration node or
* a Ruleset node, as appropriate.
* @param {Tokenizer} tokenizer A Tokenizer node.
* @return {object} One of a Declaration or Ruleset node.
*/
Parser.prototype.parseDeclarationOrRuleset = function (tokenizer) {
var ruleStart = null;
var ruleEnd = null;
var colon = null;
while (tokenizer.currentToken) {
if (tokenizer.currentToken.is(token_2.Token.type.whitespace)) {
tokenizer.advance();
}
else if (tokenizer.currentToken.is(token_2.Token.type.openParenthesis)) {
while (tokenizer.currentToken &&
!tokenizer.currentToken.is(token_2.Token.type.closeParenthesis)) {
tokenizer.advance();
}
}
else if (tokenizer.currentToken.is(token_2.Token.type.openBrace) ||
tokenizer.currentToken.is(token_2.Token.type.propertyBoundary)) {
break;
}
else {
if (tokenizer.currentToken.is(token_2.Token.type.colon)) {
colon = tokenizer.currentToken;
}
if (!ruleStart) {
ruleStart = tokenizer.advance();
ruleEnd = ruleStart;
}
else {
ruleEnd = tokenizer.advance();
}
}
}
// A ruleset never contains or ends with a semi-colon.
if (tokenizer.currentToken.is(token_2.Token.type.propertyBoundary)) {
var declarationName = tokenizer.slice(ruleStart, colon ? colon.previous : ruleEnd);
// TODO(cdata): is .trim() bad for performance?
var expressionValue = colon && tokenizer.slice(colon.next, ruleEnd).trim();
if (tokenizer.currentToken.is(token_2.Token.type.semicolon)) {
tokenizer.advance();
}
return this.nodeFactory.declaration(declarationName, expressionValue && this.nodeFactory.expression(expressionValue));
}
else if (colon && colon === ruleEnd) {
var rulelist = this.parseRulelist(tokenizer);
if (tokenizer.currentToken.is(token_2.Token.type.semicolon)) {
tokenizer.advance();
}
return this.nodeFactory.declaration(tokenizer.slice(ruleStart, ruleEnd.previous), rulelist);
}
else {
return this.nodeFactory.ruleset(tokenizer.slice(ruleStart, ruleEnd), this.parseRulelist(tokenizer));
}
};
return Parser;
}());
exports.Parser = Parser;
});
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
define("shady-css", ["require", "exports", "shady-css/common", "shady-css/token", "shady-css/tokenizer", "shady-css/node-factory", "shady-css/node-visitor", "shady-css/stringifier", "shady-css/parser"], function (require, exports, common_4, token_3, tokenizer_2, node_factory_2, node_visitor_2, stringifier_1, parser_1) {
"use strict";
exports.nodeType = common_4.nodeType;
exports.Token = token_3.Token;
exports.Tokenizer = tokenizer_2.Tokenizer;
exports.NodeFactory = node_factory_2.NodeFactory;
exports.NodeVisitor = node_visitor_2.NodeVisitor;
exports.Stringifier = stringifier_1.Stringifier;
exports.Parser = parser_1.Parser;
});
//# sourceMappingURL=shady-css.concat.js.map