stylelint
Version:
A mighty, modern CSS linter.
143 lines (121 loc) • 3.52 kB
JavaScript
"use strict";
const isKeyframeRule = require("../../utils/isKeyframeRule");
const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule");
const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector");
const optionsMatches = require("../../utils/optionsMatches");
const parseSelector = require("../../utils/parseSelector");
const report = require("../../utils/report");
const resolvedNestedSelector = require("postcss-resolve-nested-selector");
const ruleMessages = require("../../utils/ruleMessages");
const validateOptions = require("../../utils/validateOptions");
const ruleName = "selector-no-qualifying-type";
const messages = ruleMessages(ruleName, {
rejected: "Unexpected qualifying type selector"
});
const selectorCharacters = ["#", ".", "["];
function isSelectorCharacters(value) {
return selectorCharacters.some(char => value.indexOf(char) !== -1);
}
function getRightNodes(node) {
const result = [];
let rightNode = node;
while ((rightNode = rightNode.next())) {
if (rightNode.type === "combinator") {
break;
}
if (
rightNode.type !== "id" &&
rightNode.type !== "class" &&
rightNode.type !== "attribute"
) {
continue;
}
result.push(rightNode);
}
return result;
}
const rule = function(enabled, options) {
return (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: enabled,
possible: [true, false]
},
{
actual: options,
possible: {
ignore: ["attribute", "class", "id"]
},
optional: true
}
);
if (!validOptions) {
return;
}
root.walkRules(rule => {
if (!isStandardSyntaxRule(rule)) {
return;
}
if (isKeyframeRule(rule)) {
return;
}
// Increasing performance
if (!isStandardSyntaxSelector(rule.selector)) {
return;
}
if (!isSelectorCharacters(rule.selector)) {
return;
}
function checkSelector(selectorAST) {
selectorAST.walkTags(selector => {
const selectorParent = selector.parent;
if (selectorParent.nodes.length === 1) {
return;
}
const selectorNodes = getRightNodes(selector);
const index = selector.sourceIndex;
selectorNodes.forEach(selectorNode => {
if (
selectorNode.type === "id" &&
!optionsMatches(options, "ignore", "id")
) {
complain(index);
}
if (
selectorNode.type === "class" &&
!optionsMatches(options, "ignore", "class")
) {
complain(index);
}
if (
selectorNode.type === "attribute" &&
!optionsMatches(options, "ignore", "attribute")
) {
complain(index);
}
});
});
}
resolvedNestedSelector(rule.selector, rule).forEach(resolvedSelector => {
if (!isStandardSyntaxSelector(resolvedSelector)) {
return;
}
parseSelector(resolvedSelector, result, rule, checkSelector);
});
function complain(index) {
report({
ruleName,
result,
node: rule,
message: messages.rejected,
index
});
}
});
};
};
rule.ruleName = ruleName;
rule.messages = messages;
module.exports = rule;