UNPKG

2.39 kBJavaScriptView Raw
1'use strict';
2
3const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
4const keywordSets = require('../../reference/keywordSets');
5const report = require('../../utils/report');
6const ruleMessages = require('../../utils/ruleMessages');
7const styleSearch = require('style-search');
8const validateOptions = require('../../utils/validateOptions');
9
10const ruleName = 'selector-pseudo-element-colon-notation';
11
12const messages = ruleMessages(ruleName, {
13 expected: (q) => `Expected ${q} colon pseudo-element notation`,
14});
15
16function rule(expectation, options, context) {
17 return (root, result) => {
18 const validOptions = validateOptions(result, ruleName, {
19 actual: expectation,
20 possible: ['single', 'double'],
21 });
22
23 if (!validOptions) {
24 return;
25 }
26
27 root.walkRules((rule) => {
28 if (!isStandardSyntaxRule(rule)) {
29 return;
30 }
31
32 const selector = rule.selector;
33
34 // get out early if no pseudo elements or classes
35 if (!selector.includes(':')) {
36 return;
37 }
38
39 const fixPositions = [];
40
41 // match only level 1 and 2 pseudo elements
42 const pseudoElementsWithColons = Array.from(keywordSets.levelOneAndTwoPseudoElements).map(
43 (x) => `:${x}`,
44 );
45
46 styleSearch({ source: selector.toLowerCase(), target: pseudoElementsWithColons }, (match) => {
47 const prevCharIsColon = selector[match.startIndex - 1] === ':';
48
49 if (expectation === 'single' && !prevCharIsColon) {
50 return;
51 }
52
53 if (expectation === 'double' && prevCharIsColon) {
54 return;
55 }
56
57 if (context.fix) {
58 fixPositions.unshift({ rule, startIndex: match.startIndex });
59
60 return;
61 }
62
63 report({
64 message: messages.expected(expectation),
65 node: rule,
66 index: match.startIndex,
67 result,
68 ruleName,
69 });
70 });
71
72 if (fixPositions.length) {
73 // If expecting : then we found :: so remove one of the colons
74 // If expecting :: then we found : so add one extra colon
75 const expectedSingle = expectation === 'single';
76 const offset = expectedSingle ? 1 : 0;
77 const extraColon = expectedSingle ? '' : ':';
78
79 fixPositions.forEach((fixPosition) => {
80 rule.selector =
81 rule.selector.substring(0, fixPosition.startIndex - offset) +
82 extraColon +
83 rule.selector.substring(fixPosition.startIndex);
84 });
85 }
86 });
87 };
88}
89
90rule.ruleName = ruleName;
91rule.messages = messages;
92module.exports = rule;