also check prefixed pseudos
This commit is contained in:
parent
02530fcbf4
commit
681dfdc62b
|
@ -1145,76 +1145,85 @@ CSSLint.addRule['known-properties'] = [{
|
||||||
}];
|
}];
|
||||||
|
|
||||||
CSSLint.addRule['known-pseudos'] = [{
|
CSSLint.addRule['known-pseudos'] = [{
|
||||||
name: 'Require use of known pseudo selectors except when vendor-prefixed',
|
name: 'Require use of known pseudo selectors',
|
||||||
url: 'https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements',
|
url: 'https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements',
|
||||||
browsers: 'All',
|
browsers: 'All',
|
||||||
_data: {
|
}, (rule, parser, reporter) => {
|
||||||
// 1 = requires ":"
|
// 1 = requires ":"
|
||||||
// 2 = requires "::"
|
// 2 = requires "::"
|
||||||
// 1+2 = allows both ":" and "::"
|
const Func = 4; // must be :function()
|
||||||
// 4 = requires "("
|
const FuncToo = 8; // both :function() and :non-function
|
||||||
// 8 = allows both "(" and bare name
|
const WK = 0x10;
|
||||||
'active': 1,
|
const Moz = 0x20;
|
||||||
'after': 1 + 2,
|
const definitions = {
|
||||||
'any-link': 1,
|
// elements
|
||||||
'autofill': 1,
|
'after': 1 + 2, // also allows ":"
|
||||||
'backdrop': 2,
|
'backdrop': 2,
|
||||||
'before': 1 + 2,
|
'before': 1 + 2, // also allows ":"
|
||||||
'blank': 1,
|
|
||||||
'checked': 1,
|
|
||||||
'cue': 2,
|
'cue': 2,
|
||||||
'cue-region': 2,
|
'cue-region': 2,
|
||||||
'current': 1 + 8,
|
'file-selector-button': 2,
|
||||||
|
'first-letter': 1 + 2, // also allows ":"
|
||||||
|
'first-line': 1 + 2, // also allows ":"
|
||||||
|
'grammar-error': 2,
|
||||||
|
'highlight': 2 + Func,
|
||||||
|
'marker': 2,
|
||||||
|
'part': 2 + Func,
|
||||||
|
'placeholder': 2 + Moz,
|
||||||
|
'selection': 2 + Moz,
|
||||||
|
'slotted': 2 + Func,
|
||||||
|
'spelling-error': 2,
|
||||||
|
'target-text': 2,
|
||||||
|
// classes
|
||||||
|
'active': 1,
|
||||||
|
'any-link': 1 + Moz + WK,
|
||||||
|
'autofill': 1 + WK,
|
||||||
|
'blank': 1,
|
||||||
|
'checked': 1,
|
||||||
|
'current': 1 + FuncToo,
|
||||||
'default': 1,
|
'default': 1,
|
||||||
'defined': 1,
|
'defined': 1,
|
||||||
'dir': 1 + 4,
|
'dir': 1 + Func,
|
||||||
'disabled': 1,
|
'disabled': 1,
|
||||||
'drop': 1,
|
'drop': 1,
|
||||||
'empty': 1,
|
'empty': 1,
|
||||||
'enabled': 1,
|
'enabled': 1,
|
||||||
'file-selector-button': 2,
|
|
||||||
'first': 1,
|
'first': 1,
|
||||||
'first-child': 1,
|
'first-child': 1,
|
||||||
'first-letter': 1 + 2,
|
|
||||||
'first-line': 1 + 2,
|
|
||||||
'first-of-type': 1,
|
'first-of-type': 1,
|
||||||
'focus': 1,
|
'focus': 1,
|
||||||
'focus-visible': 1,
|
'focus-visible': 1,
|
||||||
'focus-within': 1,
|
'focus-within': 1,
|
||||||
'fullscreen': 1,
|
'fullscreen': 1,
|
||||||
'future': 1,
|
'future': 1,
|
||||||
'grammar-error': 2,
|
'has': 1 + Func,
|
||||||
'has': 1 + 4,
|
'host': 1 + FuncToo,
|
||||||
'host': 1 + 8,
|
'host-context': 1 + Func,
|
||||||
'host-context': 1 + 4,
|
|
||||||
'hover': 1,
|
'hover': 1,
|
||||||
'in-range': 1,
|
'in-range': 1,
|
||||||
'indeterminate': 1,
|
'indeterminate': 1,
|
||||||
'invalid': 1,
|
'invalid': 1,
|
||||||
'is': 1 + 4,
|
'is': 1 + Func,
|
||||||
'lang': 1 + 4,
|
'lang': 1 + Func,
|
||||||
'last-child': 1,
|
'last-child': 1,
|
||||||
'last-of-type': 1,
|
'last-of-type': 1,
|
||||||
'left': 1,
|
'left': 1,
|
||||||
'link': 1,
|
'link': 1,
|
||||||
'local-link': 1,
|
'local-link': 1,
|
||||||
'marker': 2,
|
'not': 1 + Func,
|
||||||
'not': 1 + 4,
|
'nth-child': 1 + Func,
|
||||||
'nth-child': 1 + 4,
|
'nth-col': 1 + Func,
|
||||||
'nth-col': 1 + 4,
|
'nth-last-child': 1 + Func,
|
||||||
'nth-last-child': 1 + 4,
|
'nth-last-col': 1 + Func,
|
||||||
'nth-last-col': 1 + 4,
|
'nth-last-of-type': 1 + Func,
|
||||||
'nth-last-of-type': 1 + 4,
|
'nth-of-type': 1 + Func,
|
||||||
'nth-of-type': 1 + 4,
|
|
||||||
'only-child': 1,
|
'only-child': 1,
|
||||||
'only-of-type': 1,
|
'only-of-type': 1,
|
||||||
'optional': 1,
|
'optional': 1,
|
||||||
'out-of-range': 1,
|
'out-of-range': 1,
|
||||||
'part': 2 + 4,
|
|
||||||
'past': 1,
|
'past': 1,
|
||||||
'paused': 1,
|
'paused': 1,
|
||||||
'picture-in-picture': 1,
|
'picture-in-picture': 1,
|
||||||
'placeholder': 2,
|
|
||||||
'placeholder-shown': 1,
|
'placeholder-shown': 1,
|
||||||
'playing': 1,
|
'playing': 1,
|
||||||
'read-only': 1,
|
'read-only': 1,
|
||||||
|
@ -1223,47 +1232,129 @@ CSSLint.addRule['known-pseudos'] = [{
|
||||||
'right': 1,
|
'right': 1,
|
||||||
'root': 1,
|
'root': 1,
|
||||||
'scope': 1,
|
'scope': 1,
|
||||||
'selection': 2,
|
'state': 1 + Func,
|
||||||
'slotted': 2 + 4,
|
|
||||||
'spelling-error': 2,
|
|
||||||
'state': 1 + 4,
|
|
||||||
'target': 1,
|
'target': 1,
|
||||||
'target-text': 2,
|
|
||||||
'target-within': 1,
|
'target-within': 1,
|
||||||
'user-invalid': 1,
|
'user-invalid': 1,
|
||||||
'valid': 1,
|
'valid': 1,
|
||||||
'visited': 1,
|
'visited': 1,
|
||||||
'where': 1 + 4,
|
'where': 1 + Func,
|
||||||
// used with ::-webkit-scrollbar selectors
|
'xr-overlay': 1,
|
||||||
|
// ::-webkit-scrollbar specific classes
|
||||||
|
'corner-present': 1,
|
||||||
'decrement': 1,
|
'decrement': 1,
|
||||||
|
'double-button': 1,
|
||||||
|
'end': 1,
|
||||||
'horizontal': 1,
|
'horizontal': 1,
|
||||||
'increment': 1,
|
'increment': 1,
|
||||||
|
'no-button': 1,
|
||||||
'single-button': 1,
|
'single-button': 1,
|
||||||
|
'start': 1,
|
||||||
'vertical': 1,
|
'vertical': 1,
|
||||||
},
|
'window-inactive': 1 + Moz,
|
||||||
}, (rule, parser, reporter) => {
|
};
|
||||||
const definitions = rule._data;
|
const definitionsPrefixed = {
|
||||||
const rxColons = /^:+/;
|
'any': 1 + Func + Moz + WK,
|
||||||
const rxPseudoVendorPrefix = /^(::?)-(webkit|moz|ms|o)-/i;
|
'calendar-picker-indicator': 2 + WK,
|
||||||
|
'clear-button': 2 + WK,
|
||||||
|
'color-swatch': 2 + WK,
|
||||||
|
'color-swatch-wrapper': 2 + WK,
|
||||||
|
'date-and-time-value': 2 + WK,
|
||||||
|
'datetime-edit': 2 + WK,
|
||||||
|
'datetime-edit-ampm-field': 2 + WK,
|
||||||
|
'datetime-edit-day-field': 2 + WK,
|
||||||
|
'datetime-edit-fields-wrapper': 2 + WK,
|
||||||
|
'datetime-edit-hour-field': 2 + WK,
|
||||||
|
'datetime-edit-millisecond-field': 2 + WK,
|
||||||
|
'datetime-edit-minute-field': 2 + WK,
|
||||||
|
'datetime-edit-month-field': 2 + WK,
|
||||||
|
'datetime-edit-second-field': 2 + WK,
|
||||||
|
'datetime-edit-text': 2 + WK,
|
||||||
|
'datetime-edit-week-field': 2 + WK,
|
||||||
|
'datetime-edit-year-field': 2 + WK,
|
||||||
|
'drag': 1 + WK,
|
||||||
|
'drag-over': 1 + Moz,
|
||||||
|
'file-upload-button': 2 + WK,
|
||||||
|
'focus-inner': 2 + Moz,
|
||||||
|
'focusring': 1 + Moz,
|
||||||
|
'full-page-media': 1 + WK,
|
||||||
|
'full-screen': 1 + Moz + WK,
|
||||||
|
'full-screen-ancestor': 1 + Moz + WK,
|
||||||
|
'inner-spin-button': 2 + WK,
|
||||||
|
'input-placeholder': 1 + 2 + WK + Moz,
|
||||||
|
'loading': 1 + Moz,
|
||||||
|
'media-controls': 2 + WK,
|
||||||
|
'media-controls-current-time-display': 2 + WK,
|
||||||
|
'media-controls-enclosure': 2 + WK,
|
||||||
|
'media-controls-fullscreen-button': 2 + WK,
|
||||||
|
'media-controls-mute-button': 2 + WK,
|
||||||
|
'media-controls-overlay-enclosure': 2 + WK,
|
||||||
|
'media-controls-overlay-play-button': 2 + WK,
|
||||||
|
'media-controls-panel': 2 + WK,
|
||||||
|
'media-controls-play-button': 2 + WK,
|
||||||
|
'media-controls-time-remaining-display': 2 + WK,
|
||||||
|
'media-controls-timeline': 2 + WK,
|
||||||
|
'media-controls-timeline-container': 2 + WK,
|
||||||
|
'media-controls-toggle-closed-captions-button': 2 + WK,
|
||||||
|
'media-controls-volume-slider': 2 + WK,
|
||||||
|
'media-slider-container': 2 + WK,
|
||||||
|
'media-slider-thumb': 2 + WK,
|
||||||
|
'media-text-track-container': 2 + WK,
|
||||||
|
'media-text-track-display': 2 + WK,
|
||||||
|
'media-text-track-region': 2 + WK,
|
||||||
|
'media-text-track-region-container': 2 + WK,
|
||||||
|
'meter-bar': 2 + WK,
|
||||||
|
'meter-even-less-good-value': 2 + WK,
|
||||||
|
'meter-inner-element': 2 + WK,
|
||||||
|
'meter-optimum-value': 2 + WK,
|
||||||
|
'meter-suboptimum-value': 2 + WK,
|
||||||
|
'progress-bar': 2 + WK,
|
||||||
|
'progress-inner-element': 2 + WK,
|
||||||
|
'progress-value': 2 + WK,
|
||||||
|
'resizer': 2 + WK,
|
||||||
|
'scrollbar': 2 + WK,
|
||||||
|
'scrollbar-button': 2 + WK,
|
||||||
|
'scrollbar-corner': 2 + WK,
|
||||||
|
'scrollbar-thumb': 2 + WK,
|
||||||
|
'scrollbar-track': 2 + WK,
|
||||||
|
'scrollbar-track-piece': 2 + WK,
|
||||||
|
'search-cancel-button': 2 + WK,
|
||||||
|
'slider-container': 2 + WK,
|
||||||
|
'slider-runnable-track': 2 + WK,
|
||||||
|
'slider-thumb': 2 + WK,
|
||||||
|
'textfield-decoration-container': 2 + WK,
|
||||||
|
};
|
||||||
|
const rx = /^(:+)(?:-(\w+)-)?([^(]+)(\()?/i;
|
||||||
|
const allowsFunc = Func + FuncToo;
|
||||||
|
const allowsPrefix = WK + Moz;
|
||||||
const {lower} = parserlib.util;
|
const {lower} = parserlib.util;
|
||||||
const checkSelector = ({parts}) => {
|
const checkSelector = ({parts}) => {
|
||||||
let text;
|
|
||||||
for (const {modifiers} of parts || []) {
|
for (const {modifiers} of parts || []) {
|
||||||
if (!modifiers) continue;
|
if (!modifiers) continue;
|
||||||
for (const mod of modifiers) {
|
for (const mod of modifiers) {
|
||||||
if (mod.type === 'pseudo' && !rxPseudoVendorPrefix.test(text = mod.text)) {
|
if (mod.type === 'pseudo') {
|
||||||
const i = text.indexOf('(');
|
const {text} = mod;
|
||||||
const colons = text.match(rxColons)[0].length;
|
const [all, colons, prefix, name, paren] = rx.exec(lower(text)) || 0;
|
||||||
const def = definitions[lower(text.slice(colons, i < 0 ? 99 : i))];
|
const defPrefixed = definitionsPrefixed[name];
|
||||||
|
const def = definitions[name] || defPrefixed;
|
||||||
for (const err of !def ? ['Unknown pseudo'] : [
|
for (const err of !def ? ['Unknown pseudo'] : [
|
||||||
colons > 1
|
colons.length > 1
|
||||||
? !(def & 2) && 'Must use : in'
|
? !(def & 2) && 'Must use : in'
|
||||||
: !(def & 1) && 'Must use :: in',
|
: !(def & 1) && all !== ':-moz-placeholder' && 'Must use :: in',
|
||||||
i < 0
|
paren
|
||||||
? (def & 4) && 'Must use ( after'
|
? !(def & allowsFunc) && 'Unexpected ( in'
|
||||||
: !(def & (4 + 8)) && 'Unexpected ( in',
|
: (def & Func) && 'Must use ( after',
|
||||||
|
prefix ?
|
||||||
|
(
|
||||||
|
!(def & allowsPrefix) ||
|
||||||
|
prefix === 'webkit' && !(def & WK) ||
|
||||||
|
prefix === 'moz' && !(def & Moz)
|
||||||
|
) && 'Unexpected prefix in'
|
||||||
|
: defPrefixed && `Must use ${
|
||||||
|
(def & WK) && (def & Moz) && '-webkit- or -moz-' ||
|
||||||
|
(def & WK) && '-webkit-' || '-moz-'} prefix in`,
|
||||||
]) {
|
]) {
|
||||||
if (err) reporter.report(`${err} ${i < 0 ? text : text.slice(0, i + 1)}`, mod, rule);
|
if (err) reporter.report(`${err} ${text.slice(0, all.length)}`, mod, rule);
|
||||||
}
|
}
|
||||||
} else if (mod.args) {
|
} else if (mod.args) {
|
||||||
mod.args.forEach(checkSelector);
|
mod.args.forEach(checkSelector);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user