.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / rules / no-descending-specificity / index.js
diff --git a/.config/coc/extensions/node_modules/coc-prettier/node_modules/stylelint/lib/rules/no-descending-specificity/index.js b/.config/coc/extensions/node_modules/coc-prettier/node_modules/stylelint/lib/rules/no-descending-specificity/index.js
new file mode 100644 (file)
index 0000000..2f1e6b6
--- /dev/null
@@ -0,0 +1,118 @@
+"use strict";
+
+const _ = require("lodash");
+const findAtRuleContext = require("../../utils/findAtRuleContext");
+const isCustomPropertySet = require("../../utils/isCustomPropertySet");
+const keywordSets = require("../../reference/keywordSets");
+const nodeContextLookup = require("../../utils/nodeContextLookup");
+const parseSelector = require("../../utils/parseSelector");
+const report = require("../../utils/report");
+const resolvedNestedSelector = require("postcss-resolve-nested-selector");
+const ruleMessages = require("../../utils/ruleMessages");
+const specificity = require("specificity");
+const validateOptions = require("../../utils/validateOptions");
+
+const ruleName = "no-descending-specificity";
+
+const messages = ruleMessages(ruleName, {
+  rejected: (b, a) => `Expected selector "${b}" to come before selector "${a}"`
+});
+
+const rule = function(actual) {
+  return (root, result) => {
+    const validOptions = validateOptions(result, ruleName, { actual });
+    if (!validOptions) {
+      return;
+    }
+
+    const selectorContextLookup = nodeContextLookup();
+
+    root.walkRules(rule => {
+      // Ignore custom property set `--foo: {};`
+      if (isCustomPropertySet(rule)) {
+        return;
+      }
+
+      const comparisonContext = selectorContextLookup.getContext(
+        rule,
+        findAtRuleContext(rule)
+      );
+
+      rule.selectors.forEach(selector => {
+        const trimSelector = selector.trim();
+        // Ignore `.selector, { }`
+        if (trimSelector === "") {
+          return;
+        }
+
+        // The edge-case of duplicate selectors will act acceptably
+        const index = rule.selector.indexOf(trimSelector);
+        // Resolve any nested selectors before checking
+        resolvedNestedSelector(selector, rule).forEach(resolvedSelector => {
+          parseSelector(resolvedSelector, result, rule, s =>
+            checkSelector(s, rule, index, comparisonContext)
+          );
+        });
+      });
+    });
+
+    function checkSelector(selectorNode, rule, sourceIndex, comparisonContext) {
+      const selector = selectorNode.toString();
+      const referenceSelectorNode = lastCompoundSelectorWithoutPseudoClasses(
+        selectorNode
+      );
+      const selectorSpecificity = specificity.calculate(selector)[0]
+        .specificityArray;
+      const entry = { selector, specificity: selectorSpecificity };
+
+      if (!comparisonContext.has(referenceSelectorNode)) {
+        comparisonContext.set(referenceSelectorNode, [entry]);
+        return;
+      }
+
+      const priorComparableSelectors = comparisonContext.get(
+        referenceSelectorNode
+      );
+
+      priorComparableSelectors.forEach(priorEntry => {
+        if (
+          specificity.compare(selectorSpecificity, priorEntry.specificity) ===
+          -1
+        ) {
+          report({
+            ruleName,
+            result,
+            node: rule,
+            message: messages.rejected(selector, priorEntry.selector),
+            index: sourceIndex
+          });
+        }
+      });
+
+      priorComparableSelectors.push(entry);
+    }
+  };
+};
+
+function lastCompoundSelectorWithoutPseudoClasses(selectorNode) {
+  const nodesAfterLastCombinator = _.last(
+    selectorNode.nodes[0].split(node => {
+      return node.type === "combinator";
+    })
+  );
+
+  const nodesWithoutPseudoClasses = nodesAfterLastCombinator
+    .filter(node => {
+      return (
+        node.type !== "pseudo" ||
+        keywordSets.pseudoElements.has(node.value.replace(/:/g, ""))
+      );
+    })
+    .join("");
+
+  return nodesWithoutPseudoClasses.toString();
+}
+
+rule.ruleName = ruleName;
+rule.messages = messages;
+module.exports = rule;