UNPKG

3 kBJavaScriptView Raw
1'use strict';
2
3const isKeyframeRule = require('../../utils/isKeyframeRule');
4const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
5const isStandardSyntaxSelector = require('../../utils/isStandardSyntaxSelector');
6const optionsMatches = require('../../utils/optionsMatches');
7const parseSelector = require('../../utils/parseSelector');
8const report = require('../../utils/report');
9const resolvedNestedSelector = require('postcss-resolve-nested-selector');
10const ruleMessages = require('../../utils/ruleMessages');
11const validateOptions = require('../../utils/validateOptions');
12
13const ruleName = 'selector-no-qualifying-type';
14
15const messages = ruleMessages(ruleName, {
16 rejected: 'Unexpected qualifying type selector',
17});
18
19const selectorCharacters = ['#', '.', '['];
20
21function isSelectorCharacters(value) {
22 return selectorCharacters.some((char) => value.includes(char));
23}
24
25function getRightNodes(node) {
26 const result = [];
27 let rightNode = node;
28
29 while ((rightNode = rightNode.next())) {
30 if (rightNode.type === 'combinator') {
31 break;
32 }
33
34 if (rightNode.type !== 'id' && rightNode.type !== 'class' && rightNode.type !== 'attribute') {
35 continue;
36 }
37
38 result.push(rightNode);
39 }
40
41 return result;
42}
43
44function rule(enabled, options) {
45 return (root, result) => {
46 const validOptions = validateOptions(
47 result,
48 ruleName,
49 {
50 actual: enabled,
51 possible: [true, false],
52 },
53 {
54 actual: options,
55 possible: {
56 ignore: ['attribute', 'class', 'id'],
57 },
58 optional: true,
59 },
60 );
61
62 if (!validOptions) {
63 return;
64 }
65
66 root.walkRules((rule) => {
67 if (!isStandardSyntaxRule(rule)) {
68 return;
69 }
70
71 if (isKeyframeRule(rule)) {
72 return;
73 }
74
75 if (!isSelectorCharacters(rule.selector)) {
76 return;
77 }
78
79 function checkSelector(selectorAST) {
80 selectorAST.walkTags((selector) => {
81 const selectorParent = selector.parent;
82
83 if (selectorParent.nodes.length === 1) {
84 return;
85 }
86
87 const selectorNodes = getRightNodes(selector);
88 const index = selector.sourceIndex;
89
90 selectorNodes.forEach((selectorNode) => {
91 if (selectorNode.type === 'id' && !optionsMatches(options, 'ignore', 'id')) {
92 complain(index);
93 }
94
95 if (selectorNode.type === 'class' && !optionsMatches(options, 'ignore', 'class')) {
96 complain(index);
97 }
98
99 if (
100 selectorNode.type === 'attribute' &&
101 !optionsMatches(options, 'ignore', 'attribute')
102 ) {
103 complain(index);
104 }
105 });
106 });
107 }
108
109 resolvedNestedSelector(rule.selector, rule).forEach((resolvedSelector) => {
110 if (!isStandardSyntaxSelector(resolvedSelector)) {
111 return;
112 }
113
114 parseSelector(resolvedSelector, result, rule, checkSelector);
115 });
116
117 function complain(index) {
118 report({
119 ruleName,
120 result,
121 node: rule,
122 message: messages.rejected,
123 index,
124 });
125 }
126 });
127 };
128}
129
130rule.ruleName = ruleName;
131rule.messages = messages;
132module.exports = rule;