CSSLint and parserlib (#646)
* CSSLint: add mask-image https://drafts.fxtf.org/css-masking-1/#the-mask-image * CSSLint: update <image> type https://drafts.csswg.org/css-images-3/#typedef-image * CodeMirror CSS mode: add 'mask-image' * CodeMirror CSS mode: add CSS Round Display L1 https://www.w3.org/TR/css-round-display-1/ * CSSLint: CSS Round Display L1 (ED 2018-09-26) https://drafts.csswg.org/css-round-display/ * CSSLint: CSS Environment Variables L1 (ED 2018-08-03) https://drafts.csswg.org/css-env-1/ * CSSLint: parts of CSS Overflow Module L3 (WD 2018-07-31) only overflow-* properties are added since the rest seem tentative https://www.w3.org/TR/css-overflow-3/ * CSSLint: Selectors L4 :is() supersedes :matches() https://drafts.csswg.org/selectors-4/#matches * CSSLint: Text Decoration L3 (CR 2018-06-26) https://drafts.csswg.org/css-text-decor-3/ * CSSLint: fix '&&' in grammarParser consequences: * fixed text-shadow * fixed <display-listitem> * switched to a string in <shadow> * CSSLint: fix definition for 'rotate' * CSSLint: fix applyEmbeddedOverrides * CSSLint: update definition for 'rotate' * CSSLint: reset parserlib cache when inline overrides change * CSSLint: code cosmetics * CSSLint: fixup d5971e9c * CSSLint: code cosmetics * CSSLint: start ignoring from the comment's line number
This commit is contained in:
parent
cdc7f98150
commit
55189f1fdd
|
@ -157,10 +157,18 @@
|
||||||
'text-align-all': true,
|
'text-align-all': true,
|
||||||
|
|
||||||
'contain': true,
|
'contain': true,
|
||||||
|
'mask-image': true,
|
||||||
'mix-blend-mode': true,
|
'mix-blend-mode': true,
|
||||||
|
'rotate': true,
|
||||||
'isolation': true,
|
'isolation': true,
|
||||||
'zoom': true,
|
'zoom': true,
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/css-round-display-1/
|
||||||
|
'border-boundary': true,
|
||||||
|
'shape': true,
|
||||||
|
'shape-inside': true,
|
||||||
|
'viewport-fit': true,
|
||||||
|
|
||||||
// nonstandard https://compat.spec.whatwg.org/
|
// nonstandard https://compat.spec.whatwg.org/
|
||||||
'box-reflect': true,
|
'box-reflect': true,
|
||||||
'text-fill-color': true,
|
'text-fill-color': true,
|
||||||
|
@ -171,6 +179,7 @@
|
||||||
});
|
});
|
||||||
Object.assign(CodeMirror.mimeModes['text/css'].valueKeywords, {
|
Object.assign(CodeMirror.mimeModes['text/css'].valueKeywords, {
|
||||||
'isolate': true,
|
'isolate': true,
|
||||||
|
'rect': true,
|
||||||
'recto': true,
|
'recto': true,
|
||||||
'verso': true,
|
'verso': true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,11 +34,11 @@ class Reporter {
|
||||||
* verification back to the main API.
|
* verification back to the main API.
|
||||||
* @class Reporter
|
* @class Reporter
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {String[]} lines The text lines of the source.
|
* @param {String[]} lines - The text lines of the source.
|
||||||
* @param {Object} ruleset The set of rules to work with, including if
|
* @param {Object} ruleset - The set of rules to work with, including if
|
||||||
* they are errors or warnings.
|
* they are errors or warnings.
|
||||||
* @param {Object} explicitly allowed lines
|
* @param {Object} allow - explicitly allowed lines
|
||||||
* @param {[][]} ingore list of line ranges to be ignored
|
* @param {[][]} ingore - list of line ranges to be ignored
|
||||||
*/
|
*/
|
||||||
constructor(lines, ruleset, allow, ignore) {
|
constructor(lines, ruleset, allow, ignore) {
|
||||||
this.messages = [];
|
this.messages = [];
|
||||||
|
@ -126,6 +126,9 @@ var CSSLint = (() => {
|
||||||
};
|
};
|
||||||
const rules = [];
|
const rules = [];
|
||||||
|
|
||||||
|
// previous CSSLint overrides are used to decide whether the parserlib's cache should be reset
|
||||||
|
let prevOverrides;
|
||||||
|
|
||||||
return Object.assign(new parserlib.util.EventTarget(), {
|
return Object.assign(new parserlib.util.EventTarget(), {
|
||||||
|
|
||||||
addRule(rule) {
|
addRule(rule) {
|
||||||
|
@ -193,8 +196,13 @@ var CSSLint = (() => {
|
||||||
rules[id] &&
|
rules[id] &&
|
||||||
rules[id].init(parser, reporter));
|
rules[id].init(parser, reporter));
|
||||||
|
|
||||||
|
// TODO: when ruleset is unchanged we can try to invalidate only line ranges in 'allow' and 'ignore'
|
||||||
|
const newOvr = [ruleset, allow, ignore];
|
||||||
|
const reuseCache = !prevOverrides || JSON.stringify(prevOverrides) === JSON.stringify(newOvr);
|
||||||
|
prevOverrides = newOvr;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
parser.parse(text, {reuseCache: true});
|
parser.parse(text, {reuseCache});
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
reporter.error('Fatal error, cannot continue: ' + ex.message, ex.line, ex.col, {});
|
reporter.error('Fatal error, cannot continue: ' + ex.message, ex.line, ex.col, {});
|
||||||
}
|
}
|
||||||
|
@ -219,17 +227,50 @@ var CSSLint = (() => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Example 1:
|
||||||
|
|
||||||
|
/* csslint ignore:start */
|
||||||
|
// the chunk of code where errors won't be reported
|
||||||
|
// the chunk's start is hardwired to the line of the opening comment
|
||||||
|
// the chunk's end is hardwired to the line of the closing comment
|
||||||
|
/* csslint ignore:end */
|
||||||
|
|
||||||
|
// Example 2:
|
||||||
|
|
||||||
|
/* csslint allow:rulename1,rulename2,... */
|
||||||
|
// allows to break the specified rules on the next single line of code
|
||||||
|
|
||||||
|
// Example 3:
|
||||||
|
|
||||||
|
/* csslint rulename1 */
|
||||||
|
/* csslint rulename2:N */
|
||||||
|
/* csslint rulename3:N, rulename4:N */
|
||||||
|
|
||||||
|
// entire code is affected;
|
||||||
|
// comments futher down the code extend/override previous comments of this kind
|
||||||
|
// values for N:
|
||||||
|
// "2" or "true" means "error"
|
||||||
|
// "1" or nothing means "warning" - note in this case ":" can also be omitted
|
||||||
|
// "0" or "false" means "ignore"
|
||||||
|
// (the quotes are added here for convenience, don't put them in the actual comments)
|
||||||
|
|
||||||
function applyEmbeddedOverrides(text, ruleset, allow, ignore) {
|
function applyEmbeddedOverrides(text, ruleset, allow, ignore) {
|
||||||
let ignoreStart = null;
|
let ignoreStart = null;
|
||||||
let ignoreEnd = null;
|
let ignoreEnd = null;
|
||||||
let lineno = 0;
|
let lineno = 0;
|
||||||
|
let eol = -1;
|
||||||
|
let m;
|
||||||
|
|
||||||
for (let eol = 0, m; (m = RX_EMBEDDED.exec(text)); lineno++) {
|
while ((m = RX_EMBEDDED.exec(text))) {
|
||||||
eol = (text.indexOf('\n', eol) + 1 || text.length + 1) - 1;
|
// account for the lines between the previous and current match
|
||||||
if (eol < m.index) continue;
|
while (eol <= m.index) {
|
||||||
|
eol = text.indexOf('\n', eol + 1);
|
||||||
|
if (eol < 0) eol = text.length;
|
||||||
|
lineno++;
|
||||||
|
}
|
||||||
|
|
||||||
const ovr = m[1].toLowerCase();
|
const ovr = m[1].toLowerCase();
|
||||||
const cmd = ovr.split(':', 1);
|
const cmd = ovr.split(':', 1)[0];
|
||||||
const i = cmd.length + 1;
|
const i = cmd.length + 1;
|
||||||
|
|
||||||
switch (cmd.trim()) {
|
switch (cmd.trim()) {
|
||||||
|
@ -246,15 +287,13 @@ var CSSLint = (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'ignore':
|
case 'ignore':
|
||||||
if (ovr.lastIndexOf('start', i) > 0) {
|
if (ovr.includes('start')) {
|
||||||
if (ignoreStart === null) {
|
ignoreStart = ignoreStart || lineno;
|
||||||
ignoreStart = lineno;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ovr.lastIndexOf('end', i) > 0) {
|
if (ovr.includes('end')) {
|
||||||
ignoreEnd = lineno;
|
ignoreEnd = lineno;
|
||||||
if (ignoreStart !== null && ignoreEnd !== null) {
|
if (ignoreStart && ignoreEnd) {
|
||||||
ignore.push([ignoreStart, ignoreEnd]);
|
ignore.push([ignoreStart, ignoreEnd]);
|
||||||
ignoreStart = ignoreEnd = null;
|
ignoreStart = ignoreEnd = null;
|
||||||
}
|
}
|
||||||
|
@ -273,7 +312,7 @@ var CSSLint = (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close remaining ignore block, if any
|
// Close remaining ignore block, if any
|
||||||
if (ignoreStart !== null) {
|
if (ignoreStart) {
|
||||||
ignore.push([ignoreStart, lineno]);
|
ignore.push([ignoreStart, lineno]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,7 @@ self.parserlib = (() => {
|
||||||
'border-bottom-right-radius': '<x-one-radius>',
|
'border-bottom-right-radius': '<x-one-radius>',
|
||||||
'border-bottom-style': '<border-style>',
|
'border-bottom-style': '<border-style>',
|
||||||
'border-bottom-width': '<border-width>',
|
'border-bottom-width': '<border-width>',
|
||||||
|
'border-boundary': 'none | parent | display',
|
||||||
'border-inline-color': '<color>{1,2}',
|
'border-inline-color': '<color>{1,2}',
|
||||||
'border-inline-end': '<border-shorthand>',
|
'border-inline-end': '<border-shorthand>',
|
||||||
'border-inline-end-color': '<color>',
|
'border-inline-end-color': '<color>',
|
||||||
|
@ -467,6 +468,7 @@ self.parserlib = (() => {
|
||||||
'marquee-speed': 1,
|
'marquee-speed': 1,
|
||||||
'marquee-style': 1,
|
'marquee-style': 1,
|
||||||
'mask': 1,
|
'mask': 1,
|
||||||
|
'mask-image': '[ none | <image> | <uri> ]#',
|
||||||
'max-height': 'none | <width-height>',
|
'max-height': 'none | <width-height>',
|
||||||
'max-width': 'none | <width-height>',
|
'max-width': 'none | <width-height>',
|
||||||
'min-height': 'auto | <width-height>',
|
'min-height': 'auto | <width-height>',
|
||||||
|
@ -496,7 +498,9 @@ self.parserlib = (() => {
|
||||||
'outline-offset': '<length>',
|
'outline-offset': '<length>',
|
||||||
'outline-style': '<border-style> | auto',
|
'outline-style': '<border-style> | auto',
|
||||||
'outline-width': '<border-width>',
|
'outline-width': '<border-width>',
|
||||||
'overflow': '<overflow>',
|
'overflow': '<overflow>{1,2}',
|
||||||
|
'overflow-block': '<overflow>',
|
||||||
|
'overflow-inline': '<overflow>',
|
||||||
'overflow-style': 1,
|
'overflow-style': 1,
|
||||||
'overflow-wrap': 'normal | break-word',
|
'overflow-wrap': 'normal | break-word',
|
||||||
'overflow-x': '<overflow>',
|
'overflow-x': '<overflow>',
|
||||||
|
@ -549,7 +553,7 @@ self.parserlib = (() => {
|
||||||
'rest-before': 1,
|
'rest-before': 1,
|
||||||
'richness': 1,
|
'richness': 1,
|
||||||
'right': '<width>',
|
'right': '<width>',
|
||||||
'rotate': 'none | <number>{3}? <angle>',
|
'rotate': 'none | [ x | y | z | <number>{3} ]? && <angle>',
|
||||||
'rotation': 1,
|
'rotation': 1,
|
||||||
'rotation-point': 1,
|
'rotation-point': 1,
|
||||||
'row-gap': '<row-gap>',
|
'row-gap': '<row-gap>',
|
||||||
|
@ -560,6 +564,7 @@ self.parserlib = (() => {
|
||||||
|
|
||||||
// S
|
// S
|
||||||
'scale': 'none | <number>{1,3}',
|
'scale': 'none | <number>{1,3}',
|
||||||
|
'shape-inside': 'auto | outside-shape | [ <basic-shape> || shape-box ] | <image> | display',
|
||||||
'shape-rendering': 'auto | optimizeSpeed | crispEdges | geometricPrecision',
|
'shape-rendering': 'auto | optimizeSpeed | crispEdges | geometricPrecision',
|
||||||
'size': 1,
|
'size': 1,
|
||||||
'speak': 'normal | none | spell-out',
|
'speak': 'normal | none | spell-out',
|
||||||
|
@ -598,15 +603,18 @@ self.parserlib = (() => {
|
||||||
'text-decoration-skip': 'none | [ objects || [ spaces | [ leading-spaces || trailing-spaces ] ] || ' +
|
'text-decoration-skip': 'none | [ objects || [ spaces | [ leading-spaces || trailing-spaces ] ] || ' +
|
||||||
'edges || box-decoration ]',
|
'edges || box-decoration ]',
|
||||||
'text-decoration-style': '<text-decoration-style>',
|
'text-decoration-style': '<text-decoration-style>',
|
||||||
'text-emphasis': 1,
|
'text-emphasis': '<text-emphasis-style> || <color>',
|
||||||
|
'text-emphasis-style': '<text-emphasis-style>',
|
||||||
|
'text-emphasis-position': '[ over | under ] && [ right | left ]?',
|
||||||
'text-height': 1,
|
'text-height': 1,
|
||||||
'text-indent': '<length> | <percentage>',
|
'text-indent': '<length> | <percentage>',
|
||||||
'text-justify': 'auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida',
|
'text-justify': 'auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida',
|
||||||
'text-outline': 1,
|
'text-outline': 1,
|
||||||
'text-overflow': 'clip | ellipsis',
|
'text-overflow': 'clip | ellipsis',
|
||||||
'text-rendering': 'auto | optimizeSpeed | optimizeLegibility | geometricPrecision',
|
'text-rendering': 'auto | optimizeSpeed | optimizeLegibility | geometricPrecision',
|
||||||
'text-shadow': 'none | [ [ <color> && <length>{2,3} ] | <length>{2,3} ]#',
|
'text-shadow': 'none | [ <color>? && <length>{2,3} ]#',
|
||||||
'text-transform': 'capitalize | uppercase | lowercase | none',
|
'text-transform': 'capitalize | uppercase | lowercase | none',
|
||||||
|
'text-underline-position': 'auto | [ under || [ left | right ] ]',
|
||||||
'text-wrap': 'normal | none | avoid',
|
'text-wrap': 'normal | none | avoid',
|
||||||
'top': '<width>',
|
'top': '<width>',
|
||||||
'touch-action': 'auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation',
|
'touch-action': 'auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation',
|
||||||
|
@ -689,7 +697,7 @@ self.parserlib = (() => {
|
||||||
|
|
||||||
'<basic-shape>': 'inset() | circle() | ellipse() | polygon()',
|
'<basic-shape>': 'inset() | circle() | ellipse() | polygon()',
|
||||||
|
|
||||||
'<bg-image>': '<image> | <gradient> | none',
|
'<bg-image>': '<image> | none',
|
||||||
|
|
||||||
'<blend-mode>': 'normal | multiply | screen | overlay | darken | lighten | color-dodge | ' +
|
'<blend-mode>': 'normal | multiply | screen | overlay | darken | lighten | color-dodge | ' +
|
||||||
'color-burn | hard-light | soft-light | difference | exclusion | hue | ' +
|
'color-burn | hard-light | soft-light | difference | exclusion | hue | ' +
|
||||||
|
@ -773,7 +781,7 @@ self.parserlib = (() => {
|
||||||
return this['<ident>'](part) && !this['<generic-family>'](part);
|
return this['<ident>'](part) && !this['<generic-family>'](part);
|
||||||
},
|
},
|
||||||
|
|
||||||
'<image>': '<uri>',
|
'<image>': '<uri> | <gradient> | cross-fade()',
|
||||||
|
|
||||||
'<inflexible-breadth>': '<length-percentage> | min-content | max-content | auto',
|
'<inflexible-breadth>': '<length-percentage> | min-content | max-content | auto',
|
||||||
|
|
||||||
|
@ -832,7 +840,7 @@ self.parserlib = (() => {
|
||||||
return this['<number>'](part) && part.value >= 0 && part.value <= 1;
|
return this['<number>'](part) && part.value >= 0 && part.value <= 1;
|
||||||
},
|
},
|
||||||
|
|
||||||
'<overflow>': 'visible | hidden | scroll | auto',
|
'<overflow>': 'visible | hidden | clip | scroll | auto',
|
||||||
|
|
||||||
'<overflow-position>': 'unsafe | safe',
|
'<overflow-position>': 'unsafe | safe',
|
||||||
|
|
||||||
|
@ -882,9 +890,12 @@ self.parserlib = (() => {
|
||||||
if (part.tokenType === Tokens.USO_VAR) return true;
|
if (part.tokenType === Tokens.USO_VAR) return true;
|
||||||
if (part.type !== 'function' || !part.expr) return false;
|
if (part.type !== 'function' || !part.expr) return false;
|
||||||
const subparts = part.expr.parts;
|
const subparts = part.expr.parts;
|
||||||
return subparts.length &&
|
if (!subparts.length) return false;
|
||||||
lower(part.name) === 'var' &&
|
const name = lower(part.name);
|
||||||
subparts[0].type === 'custom-property' && (
|
return (
|
||||||
|
name === 'var' && subparts[0].type === 'custom-property' ||
|
||||||
|
name === 'env' && subparts[0].type === 'identifier'
|
||||||
|
) && (
|
||||||
subparts.length === 1 ||
|
subparts.length === 1 ||
|
||||||
subparts[1].text === ','
|
subparts[1].text === ','
|
||||||
);
|
);
|
||||||
|
@ -1039,18 +1050,16 @@ self.parserlib = (() => {
|
||||||
'<hsl-color>': '[ <number> | <angle> ] <percentage>{2} [ / <nonnegative-number-or-percentage> ]? | ' +
|
'<hsl-color>': '[ <number> | <angle> ] <percentage>{2} [ / <nonnegative-number-or-percentage> ]? | ' +
|
||||||
'[ <number> | <angle> ] , <percentage>#{2} [ , <nonnegative-number-or-percentage> ]?',
|
'[ <number> | <angle> ] , <percentage>#{2} [ , <nonnegative-number-or-percentage> ]?',
|
||||||
|
|
||||||
// inset? && [ <length>{2,4} && <color>? ]
|
'<shadow>': 'inset? && [ <length>{2,4} && <color>? ]',
|
||||||
'<shadow>': Matcher =>
|
|
||||||
Matcher.many(
|
|
||||||
[true],
|
|
||||||
Matcher.cast('<length>').braces(2, 4),
|
|
||||||
'inset',
|
|
||||||
'<color>'),
|
|
||||||
|
|
||||||
'<single-timing-function>': 'linear | <cubic-bezier-timing-function> | <step-timing-function> | frames()',
|
'<single-timing-function>': 'linear | <cubic-bezier-timing-function> | <step-timing-function> | frames()',
|
||||||
|
|
||||||
'<text-decoration-line>': 'none | [ underline || overline || line-through || blink ]',
|
'<text-decoration-line>': 'none | [ underline || overline || line-through || blink ]',
|
||||||
|
|
||||||
|
'<text-emphasis-style>': 'none | ' +
|
||||||
|
'[ [ filled | open ] || [ dot | circle | double-circle | triangle | sesame ] ] | ' +
|
||||||
|
'<string>',
|
||||||
|
|
||||||
'<track-list>': '[ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?',
|
'<track-list>': '[ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?',
|
||||||
|
|
||||||
'<track-repeat>': 'repeat( [ <positive-integer> ] , [ <line-names>? <track-size> ]+ <line-names>? )',
|
'<track-repeat>': 'repeat( [ <positive-integer> ] , [ <line-names>? <track-size> ]+ <line-names>? )',
|
||||||
|
@ -1408,6 +1417,7 @@ self.parserlib = (() => {
|
||||||
{name: 'NOT'},
|
{name: 'NOT'},
|
||||||
{name: 'ANY', text: ['any', '-webkit-any', '-moz-any']},
|
{name: 'ANY', text: ['any', '-webkit-any', '-moz-any']},
|
||||||
{name: 'MATCHES'},
|
{name: 'MATCHES'},
|
||||||
|
{name: 'IS'},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Defined in CSS3 Paged Media
|
* Defined in CSS3 Paged Media
|
||||||
|
@ -1881,7 +1891,8 @@ self.parserlib = (() => {
|
||||||
const p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND;
|
const p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND;
|
||||||
const s = ms.map((m, i) => {
|
const s = ms.map((m, i) => {
|
||||||
if (required !== false && !required[i]) {
|
if (required !== false && !required[i]) {
|
||||||
return m.toString(Matcher.prec.MOD) + '?';
|
const str = m.toString(Matcher.prec.MOD);
|
||||||
|
return str.endsWith('?') ? str : str + '?';
|
||||||
}
|
}
|
||||||
return m.toString(p);
|
return m.toString(p);
|
||||||
}).join(required === false ? ' || ' : ' && ');
|
}).join(required === false ? ' || ' : ' && ');
|
||||||
|
@ -1919,10 +1930,22 @@ self.parserlib = (() => {
|
||||||
function andand() {
|
function andand() {
|
||||||
// andand = seq ( " && " seq)*
|
// andand = seq ( " && " seq)*
|
||||||
const m = [seq()];
|
const m = [seq()];
|
||||||
|
let reqPrev = !isOptional(m[0]);
|
||||||
|
const required = [reqPrev];
|
||||||
while (reader.readMatch(' && ')) {
|
while (reader.readMatch(' && ')) {
|
||||||
m.push(seq());
|
const item = seq();
|
||||||
|
const req = !isOptional(item);
|
||||||
|
// Matcher.many apparently can't handle optional items first
|
||||||
|
if (req && !reqPrev) {
|
||||||
|
m.unshift(item);
|
||||||
|
required.unshift(req);
|
||||||
|
} else {
|
||||||
|
m.push(item);
|
||||||
|
required.push(req);
|
||||||
|
reqPrev = req;
|
||||||
}
|
}
|
||||||
return m.length === 1 ? m[0] : Matcher.andand.apply(Matcher, m);
|
}
|
||||||
|
return m.length === 1 ? m[0] : Matcher.many(required, ...m);
|
||||||
}
|
}
|
||||||
|
|
||||||
function seq() {
|
function seq() {
|
||||||
|
@ -1977,6 +2000,10 @@ self.parserlib = (() => {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isOptional(item) {
|
||||||
|
return !Array.isArray(item.options) && item.toString().endsWith('?');
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
@ -2663,7 +2690,7 @@ self.parserlib = (() => {
|
||||||
known.add(value.text);
|
known.add(value.text);
|
||||||
|
|
||||||
function throwEndExpected(token, force) {
|
function throwEndExpected(token, force) {
|
||||||
if (force || token.name !== 'var' || token.type !== 'function') {
|
if (force || (token.name !== 'var' && token.name !== 'env') || token.type !== 'function') {
|
||||||
throw new ValidationError(`Expected end of value but found '${token.text}'.`, token);
|
throw new ValidationError(`Expected end of value but found '${token.text}'.`, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3012,11 +3039,13 @@ self.parserlib = (() => {
|
||||||
/*
|
/*
|
||||||
* Potential tokens:
|
* Potential tokens:
|
||||||
* - ANY
|
* - ANY
|
||||||
|
* - IS
|
||||||
|
* - MATCHES
|
||||||
* - NOT
|
* - NOT
|
||||||
* - CHAR
|
* - CHAR
|
||||||
*/
|
*/
|
||||||
case ':':
|
case ':':
|
||||||
return this.notOrAnyOrMatchesToken(c, pos);
|
return this.notOrIsToken(c, pos);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Potential tokens:
|
* Potential tokens:
|
||||||
|
@ -3224,16 +3253,20 @@ self.parserlib = (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOT
|
// NOT
|
||||||
|
// IS
|
||||||
// ANY
|
// ANY
|
||||||
// MATCHES
|
// MATCHES
|
||||||
// CHAR
|
// CHAR
|
||||||
notOrAnyOrMatchesToken(first, pos) {
|
notOrIsToken(first, pos) {
|
||||||
|
// first is always ':'
|
||||||
const reader = this._reader;
|
const reader = this._reader;
|
||||||
const func = reader.readMatch(/(not|(-(moz|webkit)-)?any|matches)\(/iy);
|
const func = reader.readMatch(/(not|is|(-(moz|webkit)-)?(any|matches))\(/iy);
|
||||||
if (func) {
|
if (func) {
|
||||||
|
const lcase = func[0].toLowerCase();
|
||||||
const type =
|
const type =
|
||||||
func.startsWith('n') || func.startsWith('N') ? Tokens.NOT :
|
lcase === 'n' ? Tokens.NOT :
|
||||||
func.startsWith('m') || func.startsWith('M') ? Tokens.MATCHES : Tokens.ANY;
|
lcase === 'i' ? Tokens.IS :
|
||||||
|
lcase === 'm' ? Tokens.MATCHES : Tokens.ANY;
|
||||||
return this.createToken(type, first + func, pos);
|
return this.createToken(type, first + func, pos);
|
||||||
}
|
}
|
||||||
return this.charToken(first, pos);
|
return this.charToken(first, pos);
|
||||||
|
@ -4239,14 +4272,25 @@ self.parserlib = (() => {
|
||||||
|
|
||||||
_viewport() {
|
_viewport() {
|
||||||
const stream = this._tokenStream;
|
const stream = this._tokenStream;
|
||||||
stream.mustMatch(Tokens.VIEWPORT_SYM);
|
const start = stream.mustMatch(Tokens.VIEWPORT_SYM);
|
||||||
|
|
||||||
this.fire('startviewport');
|
// only viewport-fit is allowed but we're reusing MediaQuery syntax unit,
|
||||||
|
// and accept anything for the sake of simplicity since the spec isn't yet final:
|
||||||
|
// https://drafts.csswg.org/css-round-display/#extending-viewport-rule
|
||||||
|
const descriptors = this._mediaQueryList();
|
||||||
|
|
||||||
|
this.fire({
|
||||||
|
type: 'startviewport',
|
||||||
|
descriptors,
|
||||||
|
}, start);
|
||||||
|
|
||||||
this._ws();
|
this._ws();
|
||||||
this._readDeclarations();
|
this._readDeclarations();
|
||||||
|
|
||||||
this.fire('endviewport');
|
this.fire({
|
||||||
|
type: 'endviewport',
|
||||||
|
descriptors,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_document() {
|
_document() {
|
||||||
|
@ -4664,13 +4708,13 @@ self.parserlib = (() => {
|
||||||
return value.length ? value : null;
|
return value.length ? value : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_anyOrMatches() {
|
_is() {
|
||||||
const stream = this._tokenStream;
|
const stream = this._tokenStream;
|
||||||
if (!stream.match([Tokens.ANY, Tokens.MATCHES])) return null;
|
if (!stream.match([Tokens.IS, Tokens.ANY, Tokens.MATCHES])) return null;
|
||||||
|
|
||||||
let arg;
|
let arg;
|
||||||
const start = stream._token;
|
const start = stream._token;
|
||||||
const type = start.type === Tokens.ANY ? 'any' : 'matches';
|
const type = lower(Tokens[start.type].name);
|
||||||
const value =
|
const value =
|
||||||
start.value +
|
start.value +
|
||||||
this._ws() +
|
this._ws() +
|
||||||
|
@ -5380,6 +5424,7 @@ self.parserlib = (() => {
|
||||||
[Tokens.MEDIA_SYM, Parser.prototype._media],
|
[Tokens.MEDIA_SYM, Parser.prototype._media],
|
||||||
[Tokens.SUPPORTS_SYM, Parser.prototype._supports],
|
[Tokens.SUPPORTS_SYM, Parser.prototype._supports],
|
||||||
[Tokens.DOCUMENT_SYM, Parser.prototype._documentMisplaced],
|
[Tokens.DOCUMENT_SYM, Parser.prototype._documentMisplaced],
|
||||||
|
[Tokens.VIEWPORT_SYM, Parser.prototype._viewport],
|
||||||
]),
|
]),
|
||||||
|
|
||||||
media: new Map([
|
media: new Map([
|
||||||
|
@ -5396,8 +5441,9 @@ self.parserlib = (() => {
|
||||||
[Tokens.DOT, Parser.prototype._class],
|
[Tokens.DOT, Parser.prototype._class],
|
||||||
[Tokens.LBRACKET, Parser.prototype._attrib],
|
[Tokens.LBRACKET, Parser.prototype._attrib],
|
||||||
[Tokens.COLON, Parser.prototype._pseudo],
|
[Tokens.COLON, Parser.prototype._pseudo],
|
||||||
[Tokens.ANY, Parser.prototype._anyOrMatches],
|
[Tokens.IS, Parser.prototype._is],
|
||||||
[Tokens.MATCHES, Parser.prototype._anyOrMatches],
|
[Tokens.ANY, Parser.prototype._is],
|
||||||
|
[Tokens.MATCHES, Parser.prototype._is],
|
||||||
[Tokens.NOT, Parser.prototype._negation],
|
[Tokens.NOT, Parser.prototype._negation],
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user