eslint-plugin-unicorn
Version:
More than 300 powerful ESLint rules
95 lines (80 loc) • 2.45 kB
JavaScript
import {ident} from '@eslint/css-tree';
import standardPseudoSelectors from './shared/standard-pseudo-selectors.js';
/**
@import * as ESLint from 'eslint';
*/
const MESSAGE_ID = 'no-unknown-pseudo-selectors';
const messages = {
[MESSAGE_ID]: 'Unknown pseudo-selector \'{{selector}}\'.',
};
const toAsciiLowerCase = string => string.replaceAll(/[A-Z]/g, character => character.toLowerCase());
const getPseudoSelectorKey = pseudoSelector => {
const colonCount = pseudoSelector.startsWith('::') ? 2 : 1;
return `${colonCount}:${toAsciiLowerCase(ident.decode(pseudoSelector.slice(colonCount)))}`;
};
const standardPseudoSelectorKeys = new Set(standardPseudoSelectors.map(pseudoSelector => getPseudoSelectorKey(pseudoSelector)));
const getPseudoSelector = node => `${node.type === 'PseudoElementSelector' ? '::' : ':'}${node.name}`;
const isCustomSelector = node => node.type === 'PseudoClassSelector' && ident.decode(node.name).startsWith('--');
const getProblem = (node, allowedPseudoSelectorKeys) => {
if (isCustomSelector(node)) {
return;
}
const pseudoSelector = getPseudoSelector(node);
const pseudoSelectorKey = getPseudoSelectorKey(pseudoSelector);
if (
standardPseudoSelectorKeys.has(pseudoSelectorKey)
|| allowedPseudoSelectorKeys.has(pseudoSelectorKey)
) {
return;
}
return {
node,
messageId: MESSAGE_ID,
data: {selector: pseudoSelector},
};
};
/**
@param {ESLint.Rule.RuleContext} context
*/
const create = context => {
const [{allow}] = context.options;
const allowedPseudoSelectorKeys = new Set(allow.map(pseudoSelector => getPseudoSelectorKey(pseudoSelector)));
context.on('PseudoClassSelector', node => getProblem(node, allowedPseudoSelectorKeys));
context.on('PseudoElementSelector', node => getProblem(node, allowedPseudoSelectorKeys));
};
/**
@type {ESLint.Rule.RuleModule}
*/
const config = {
create,
meta: {
type: 'problem',
docs: {
description: 'Disallow unknown pseudo-class and pseudo-element selectors.',
recommended: false,
},
schema: [
{
type: 'object',
additionalProperties: false,
properties: {
allow: {
type: 'array',
uniqueItems: true,
items: {
type: 'string',
pattern: String.raw`^:{1,2}(?:\\.|[^\\:()])+$`,
},
description: 'Additional pseudo-selectors to allow.',
},
},
},
],
defaultOptions: [{allow: []}],
messages,
languages: [
'css/css',
],
},
};
export default config;