CSSLint: fixes and tweaks to reduce memory consumption
* _hexcolor - actually validate length * all number regexps - support +- in the exponent * readCount - single slice * readMatch - use sticky regexps or string comparison on a chunk of pattern length * readComment - single sticky match & slice * readWhile - collect in an array, then join * PropertyValuePart - do native num/str checks first * isXXX - do a string comparison first
This commit is contained in:
parent
1406cae6c5
commit
d3c0999541
|
@ -363,7 +363,7 @@ Matcher.parse = function(str) {
|
||||||
seq = function() {
|
seq = function() {
|
||||||
// seq = mod ( " " mod)*
|
// seq = mod ( " " mod)*
|
||||||
var m = [ mod() ];
|
var m = [ mod() ];
|
||||||
while (reader.readMatch(/^ (?![&|\]])/) !== null) {
|
while (reader.readMatch(/\s(?![&|\]])/y) !== null) {
|
||||||
m.push(mod());
|
m.push(mod());
|
||||||
}
|
}
|
||||||
return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m);
|
return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m);
|
||||||
|
@ -379,11 +379,11 @@ Matcher.parse = function(str) {
|
||||||
return m.plus();
|
return m.plus();
|
||||||
} else if (reader.readMatch("#") !== null) {
|
} else if (reader.readMatch("#") !== null) {
|
||||||
return m.hash();
|
return m.hash();
|
||||||
} else if (reader.readMatch(/^\{\s*/) !== null) {
|
} else if (reader.readMatch(/\{\s*/y) !== null) {
|
||||||
var min = eat(/^\d+/);
|
var min = eat(/\d+/y);
|
||||||
eat(/^\s*,\s*/);
|
eat(/\s*,\s*/y);
|
||||||
var max = eat(/^\d+/);
|
var max = eat(/\d+/y);
|
||||||
eat(/^\s*\}/);
|
eat(/\s*\}/y);
|
||||||
return m.braces(+min, +max);
|
return m.braces(+min, +max);
|
||||||
}
|
}
|
||||||
return m;
|
return m;
|
||||||
|
@ -395,7 +395,7 @@ Matcher.parse = function(str) {
|
||||||
eat(" ]");
|
eat(" ]");
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
return Matcher.fromType(eat(/^[^ ?*+#{]+/));
|
return Matcher.fromType(eat(/[^ ?*+#{]+/y));
|
||||||
};
|
};
|
||||||
result = expr();
|
result = expr();
|
||||||
if (!reader.eof()) {
|
if (!reader.eof()) {
|
||||||
|
@ -2956,7 +2956,7 @@ Parser.prototype = function() {
|
||||||
_hexcolor: function() {
|
_hexcolor: function() {
|
||||||
/*
|
/*
|
||||||
* There is a constraint on the color that it must
|
* There is a constraint on the color that it must
|
||||||
* have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
|
* have either 3,4 or 6,8 hex-digits (i.e., [0-9a-fA-F])
|
||||||
* after the "#"; e.g., "#000" is OK, but "#abcd" is not.
|
* after the "#"; e.g., "#000" is OK, but "#abcd" is not.
|
||||||
*
|
*
|
||||||
* hexcolor
|
* hexcolor
|
||||||
|
@ -2969,12 +2969,11 @@ Parser.prototype = function() {
|
||||||
color;
|
color;
|
||||||
|
|
||||||
if (tokenStream.match(Tokens.HASH)) {
|
if (tokenStream.match(Tokens.HASH)) {
|
||||||
|
|
||||||
//need to do some validation here
|
|
||||||
|
|
||||||
token = tokenStream.token();
|
token = tokenStream.token();
|
||||||
color = token.value;
|
color = token.value;
|
||||||
if (!/#[a-f0-9]{3,6}/i.test(color)) {
|
const len = color.length;
|
||||||
|
if (len !== 4 && len !== 5 && len !== 7 && len !== 9 ||
|
||||||
|
!/^#([a-f\d]{3}(?:[a-f\d](?:[a-f\d]{2}){0,2})?)$/i.test(color)) {
|
||||||
throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
|
throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
|
||||||
}
|
}
|
||||||
this._readWhitespace();
|
this._readWhitespace();
|
||||||
|
@ -4191,10 +4190,13 @@ function PropertyValuePart(text, line, col, optionalHint) {
|
||||||
|
|
||||||
//figure out what type of data it is
|
//figure out what type of data it is
|
||||||
|
|
||||||
var temp;
|
let temp;
|
||||||
|
const num = parseFloat(text);
|
||||||
|
const isNum = !isNaN(num);
|
||||||
|
const func = text.slice(0, Math.max(0, text.indexOf('('))).toLowerCase();
|
||||||
|
|
||||||
//it is a measurement?
|
//it is a measurement?
|
||||||
if (/^([+\-]?[\d.]+(?:e\d+)?)([a-z]+)$/i.test(text)) { //dimension
|
if (isNum && /^([+-]?[\d.]+(?:e[+-]?\d+)?)([a-z]+)$/i.test(text)) { //dimension
|
||||||
this.type = "dimension";
|
this.type = "dimension";
|
||||||
this.value = +RegExp.$1;
|
this.value = +RegExp.$1;
|
||||||
this.units = RegExp.$2;
|
this.units = RegExp.$2;
|
||||||
|
@ -4251,20 +4253,17 @@ function PropertyValuePart(text, line, col, optionalHint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//percentage
|
//percentage
|
||||||
} else if (text.endsWith('%') && /^([+\-]?[\d.]+(?:e\d+)?)%$/i.test(text)) {
|
} else if (isNum && text.endsWith('%') && !isNaN(Number(text.slice(0, -1)))) {
|
||||||
this.type = "percentage";
|
this.type = "percentage";
|
||||||
this.value = +RegExp.$1;
|
this.value = num;
|
||||||
//integer
|
|
||||||
} else if (/^([+\-]?\d+(?:e\d+)?)$/i.test(text)) {
|
//integer or number
|
||||||
this.type = "integer";
|
} else if (isNum && !isNaN(Number(text))) {
|
||||||
this.value = +RegExp.$1;
|
this.type = text.includes('.') ? "number" : "integer";
|
||||||
//number
|
this.value = num;
|
||||||
} else if (/^([+\-]?[\d.]+(?:e\d+)?)$/i.test(text)) {
|
|
||||||
this.type = "number";
|
|
||||||
this.value = +RegExp.$1;
|
|
||||||
|
|
||||||
//hexcolor
|
//hexcolor
|
||||||
} else if (text[0] === '#' && /^#([a-f\d]{3}(?:[a-f\d](?:[a-f\d]{2}(?:[a-f\d]{2})?)?)?)\b/i.test(text)) {
|
} else if (text[0] === '#' && /^#([a-f\d]{3}(?:[a-f\d](?:[a-f\d]{2}){0,2})?)\b/i.test(text)) {
|
||||||
this.type = "color";
|
this.type = "color";
|
||||||
temp = RegExp.$1;
|
temp = RegExp.$1;
|
||||||
if (temp.length <= 4) {
|
if (temp.length <= 4) {
|
||||||
|
@ -4282,7 +4281,8 @@ function PropertyValuePart(text, line, col, optionalHint) {
|
||||||
this.alpha = parseInt(temp.substr(6, 2), 16);
|
this.alpha = parseInt(temp.substr(6, 2), 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (/^rgba?\(\s*(.*?)\s*\)/i.test(text)) {
|
|
||||||
|
} else if ((func === 'rgb' || func === 'rgba') && /^rgba?\(\s*(.*?)\s*\)/i.test(text)) {
|
||||||
const str = RegExp.$1;
|
const str = RegExp.$1;
|
||||||
const commaSep = str.includes(',');
|
const commaSep = str.includes(',');
|
||||||
let [r, s1, g, s2, b, s3 = '', a] = commaSep ? str.split(/(\s*,\s*)/) : str.split(/(\s+(?:\/\s*)?|\s*\/\s*)/);
|
let [r, s1, g, s2, b, s3 = '', a] = commaSep ? str.split(/(\s*,\s*)/) : str.split(/(\s+(?:\/\s*)?|\s*\/\s*)/);
|
||||||
|
@ -4304,7 +4304,7 @@ function PropertyValuePart(text, line, col, optionalHint) {
|
||||||
if (a !== undefined) this.alpha = a;
|
if (a !== undefined) this.alpha = a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (/hsla?\(\s*(.*?)\s*\)/i.test(text)) {
|
} else if ((func === 'hsl' || func === 'hsla') && /hsla?\(\s*(.*?)\s*\)/i.test(text)) {
|
||||||
const str = RegExp.$1;
|
const str = RegExp.$1;
|
||||||
const commaSep = str.includes(',');
|
const commaSep = str.includes(',');
|
||||||
let [h, s1, s, s2, l, s3 = '', a] = commaSep ? str.split(/(\s*,\s*)/) : str.split(/(\s+(?:\/\s*)?|\s*\/\s*)/);
|
let [h, s1, s, s2, l, s3 = '', a] = commaSep ? str.split(/(\s*,\s*)/) : str.split(/(\s+(?:\/\s*)?|\s*\/\s*)/);
|
||||||
|
@ -4326,29 +4326,36 @@ function PropertyValuePart(text, line, col, optionalHint) {
|
||||||
if (a !== undefined) this.alpha = a;
|
if (a !== undefined) this.alpha = a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (/^url\(("([^\\"]|\\.)*")\)/i.test(text)) { //URI
|
|
||||||
|
} else if (func === 'url' && /^url\(("([^\\"]|\\.)*")\)/i.test(text)) { //URI
|
||||||
// generated by TokenStream.readURI, so always double-quoted.
|
// generated by TokenStream.readURI, so always double-quoted.
|
||||||
this.type = "uri";
|
this.type = "uri";
|
||||||
this.uri = PropertyValuePart.parseString(RegExp.$1);
|
this.uri = PropertyValuePart.parseString(RegExp.$1);
|
||||||
} else if (/^([^(]+)\(/i.test(text)) {
|
|
||||||
|
} else if (func && /^([^(]+)\(/i.test(text)) {
|
||||||
this.type = "function";
|
this.type = "function";
|
||||||
this.name = RegExp.$1;
|
this.name = RegExp.$1;
|
||||||
this.value = text;
|
this.value = text;
|
||||||
} else if (/^"([^\n\r\f\\"]|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*"/i.test(text)) { //double-quoted string
|
|
||||||
|
} else if (text[0] === '"' && /^"([^\n\r\f\\"]|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*"/i.test(text)) { //double-quoted string
|
||||||
this.type = "string";
|
this.type = "string";
|
||||||
this.value = PropertyValuePart.parseString(text);
|
this.value = PropertyValuePart.parseString(text);
|
||||||
} else if (/^'([^\n\r\f\\']|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*'/i.test(text)) { //single-quoted string
|
|
||||||
|
} else if (text[0] === "'" && /^'([^\n\r\f\\']|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*'/i.test(text)) { //single-quoted string
|
||||||
this.type = "string";
|
this.type = "string";
|
||||||
this.value = PropertyValuePart.parseString(text);
|
this.value = PropertyValuePart.parseString(text);
|
||||||
} else if (Colors[text.toLowerCase()]) { //named color
|
|
||||||
|
} else if (!isNum && !func && Colors[text.toLowerCase()]) { //named color
|
||||||
this.type = "color";
|
this.type = "color";
|
||||||
temp = Colors[text.toLowerCase()].substring(1);
|
temp = Colors[text.toLowerCase()].substring(1);
|
||||||
this.red = parseInt(temp.substring(0, 2), 16);
|
this.red = parseInt(temp.substring(0, 2), 16);
|
||||||
this.green = parseInt(temp.substring(2, 4), 16);
|
this.green = parseInt(temp.substring(2, 4), 16);
|
||||||
this.blue = parseInt(temp.substring(4, 6), 16);
|
this.blue = parseInt(temp.substring(4, 6), 16);
|
||||||
|
|
||||||
} else if (/^[,\/]$/.test(text)) {
|
} else if (/^[,\/]$/.test(text)) {
|
||||||
this.type = "operator";
|
this.type = "operator";
|
||||||
this.value = text;
|
this.value = text;
|
||||||
|
|
||||||
} else if (/^-?[a-z_\u00A0-\uFFFF][a-z0-9\-_\u00A0-\uFFFF]*$/i.test(text)) {
|
} else if (/^-?[a-z_\u00A0-\uFFFF][a-z0-9\-_\u00A0-\uFFFF]*$/i.test(text)) {
|
||||||
this.type = "identifier";
|
this.type = "identifier";
|
||||||
this.value = text;
|
this.value = text;
|
||||||
|
@ -4737,31 +4744,36 @@ var h = /^[0-9a-fA-F]$/,
|
||||||
|
|
||||||
|
|
||||||
function isHexDigit(c) {
|
function isHexDigit(c) {
|
||||||
return c !== null && h.test(c);
|
return c !== null && (c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F');
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDigit(c) {
|
function isDigit(c) {
|
||||||
return c !== null && /\d/.test(c);
|
return c !== null && c >= '0' && c <= '9';
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWhitespace(c) {
|
function isWhitespace(c) {
|
||||||
return c !== null && whitespace.test(c);
|
return c !== null && (c === ' ' || c === '\t' || c === '\n' || c === '\f' || c === '\r');
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNewLine(c) {
|
function isNewLine(c) {
|
||||||
return c !== null && nl.test(c);
|
return c !== null && (c === '\n' || c === '\r\n' || c === '\r' || c === '\f');
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNameStart(c) {
|
function isNameStart(c) {
|
||||||
return c !== null && /[a-z_\u00A0-\uFFFF\\]/i.test(c);
|
return c !== null && (
|
||||||
|
c >= 'a' && c <= 'z' ||
|
||||||
|
c >= 'A' && c <= 'Z' ||
|
||||||
|
c === '_' ||
|
||||||
|
c === '\\' ||
|
||||||
|
c >= '\u00A0' && c <= '\uFFFF');
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNameChar(c) {
|
function isNameChar(c) {
|
||||||
return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
|
return c !== null && (c === '-' || c >= '0' && c <= '9' || isNameStart(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIdentStart(c) {
|
function isIdentStart(c) {
|
||||||
return c !== null && (isNameStart(c) || /\-\\/.test(c));
|
return c !== null && (c === '-' || isNameStart(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
function mix(receiver, supplier) {
|
function mix(receiver, supplier) {
|
||||||
|
@ -5587,8 +5599,8 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
|
||||||
},
|
},
|
||||||
readNumber: function(first) {
|
readNumber: function(first) {
|
||||||
const tail = this._reader.readMatch(
|
const tail = this._reader.readMatch(
|
||||||
first === "." ? /^\d+(e[+-]?\d+)?/ :
|
first === "." ? /\d+(e[+-]?\d+)?/y :
|
||||||
/^(\d*\.\d+|\d+\.?\d*)(e[+-]?\d+)?/);
|
/(\d*\.\d+|\d+\.?\d*)(e[+-]?\d+)?/y);
|
||||||
return first + (tail || '');
|
return first + (tail || '');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -5736,28 +5748,9 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
|
||||||
},
|
},
|
||||||
|
|
||||||
readComment: function(first) {
|
readComment: function(first) {
|
||||||
var reader = this._reader,
|
return first +
|
||||||
comment = first || "",
|
this._reader.readCount(first ? 1 : 2) +
|
||||||
c = reader.read();
|
this._reader.readMatch(/([^*]|\*(?!\/))*(\*\/|$)/y);
|
||||||
|
|
||||||
if (c === "*") {
|
|
||||||
while (c) {
|
|
||||||
comment += c;
|
|
||||||
|
|
||||||
//look for end of comment
|
|
||||||
if (comment.length > 2 && c === "*" && reader.peek() === "/") {
|
|
||||||
comment += reader.read();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
c = reader.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
return comment;
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6962,15 +6955,15 @@ StringReader.prototype = {
|
||||||
*/
|
*/
|
||||||
readWhile: function(filter) {
|
readWhile: function(filter) {
|
||||||
|
|
||||||
var buffer = "",
|
var buffer = [],
|
||||||
c = this.peek();
|
c = this.peek();
|
||||||
|
|
||||||
while (c !== null && filter(c)) {
|
while (c !== null && filter(c)) {
|
||||||
buffer += this.read();
|
buffer.push(this.read());
|
||||||
c = this.peek();
|
c = this.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer;
|
return buffer.join('');
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -6996,21 +6989,17 @@ StringReader.prototype = {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var source = this._input.substring(this._cursor),
|
|
||||||
value = null;
|
|
||||||
|
|
||||||
// if it's a string, just do a straight match
|
|
||||||
if (typeof matcher === "string") {
|
if (typeof matcher === "string") {
|
||||||
if (source.slice(0, matcher.length) === matcher) {
|
if (this._input.substr(this._cursor, matcher.length) === matcher) {
|
||||||
value = this.readCount(matcher.length);
|
return this.readCount(matcher.length);
|
||||||
}
|
}
|
||||||
} else if (matcher instanceof RegExp) {
|
} else if (matcher instanceof RegExp) {
|
||||||
if (matcher.test(source)) {
|
if (matcher.test(this._input.substr(this._cursor))) {
|
||||||
value = this.readCount(RegExp.lastMatch.length);
|
return this.readCount(RegExp.lastMatch.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -7022,13 +7011,16 @@ StringReader.prototype = {
|
||||||
* @method readCount
|
* @method readCount
|
||||||
*/
|
*/
|
||||||
readCount: function(count) {
|
readCount: function(count) {
|
||||||
var buffer = "";
|
const len = this._input.length;
|
||||||
|
if (this._cursor >= len) return null;
|
||||||
while (count--) {
|
const text = this._input.substr(this._cursor, count);
|
||||||
buffer += this.read();
|
this._cursor = Math.min(this._cursor + count, len);
|
||||||
|
let prev = -1;
|
||||||
|
for (let i = 0; (i = text.indexOf('\n', i)) >= 0; prev = i, i++) {
|
||||||
|
this._line++;
|
||||||
}
|
}
|
||||||
|
this._col = prev < 0 ? this._col + count : count - prev;
|
||||||
return buffer;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user