UNPKG

3.53 kBJavaScriptView Raw
1// @ts-nocheck
2
3'use strict';
4
5const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
6const parseSelector = require('../../utils/parseSelector');
7const report = require('../../utils/report');
8const ruleMessages = require('../../utils/ruleMessages');
9const validateOptions = require('../../utils/validateOptions');
10
11const ruleName = 'selector-pseudo-class-parentheses-space-inside';
12
13const messages = ruleMessages(ruleName, {
14 expectedOpening: 'Expected single space after "("',
15 rejectedOpening: 'Unexpected whitespace after "("',
16 expectedClosing: 'Expected single space before ")"',
17 rejectedClosing: 'Unexpected whitespace before ")"',
18});
19
20function rule(expectation, options, context) {
21 return (root, result) => {
22 const validOptions = validateOptions(result, ruleName, {
23 actual: expectation,
24 possible: ['always', 'never'],
25 });
26
27 if (!validOptions) {
28 return;
29 }
30
31 root.walkRules((rule) => {
32 if (!isStandardSyntaxRule(rule)) {
33 return;
34 }
35
36 if (!rule.selector.includes('(')) {
37 return;
38 }
39
40 let hasFixed = false;
41 const selector = rule.raws.selector ? rule.raws.selector.raw : rule.selector;
42 const fixedSelector = parseSelector(selector, result, rule, (selectorTree) => {
43 selectorTree.walkPseudos((pseudoNode) => {
44 if (!pseudoNode.length) {
45 return;
46 }
47
48 const paramString = pseudoNode.map(String).join(',');
49 const nextCharIsSpace = paramString.startsWith(' ');
50 const openIndex =
51 pseudoNode.sourceIndex + stringifyProperty(pseudoNode, 'value').length + 1;
52
53 if (nextCharIsSpace && expectation === 'never') {
54 if (context.fix) {
55 hasFixed = true;
56 setFirstNodeSpaceBefore(pseudoNode, '');
57 } else {
58 complain(messages.rejectedOpening, openIndex);
59 }
60 }
61
62 if (!nextCharIsSpace && expectation === 'always') {
63 if (context.fix) {
64 hasFixed = true;
65 setFirstNodeSpaceBefore(pseudoNode, ' ');
66 } else {
67 complain(messages.expectedOpening, openIndex);
68 }
69 }
70
71 const prevCharIsSpace = paramString.endsWith(' ');
72 const closeIndex = openIndex + paramString.length - 1;
73
74 if (prevCharIsSpace && expectation === 'never') {
75 if (context.fix) {
76 hasFixed = true;
77 setLastNodeSpaceAfter(pseudoNode, '');
78 } else {
79 complain(messages.rejectedClosing, closeIndex);
80 }
81 }
82
83 if (!prevCharIsSpace && expectation === 'always') {
84 if (context.fix) {
85 hasFixed = true;
86 setLastNodeSpaceAfter(pseudoNode, ' ');
87 } else {
88 complain(messages.expectedClosing, closeIndex);
89 }
90 }
91 });
92 });
93
94 if (hasFixed) {
95 if (!rule.raws.selector) {
96 rule.selector = fixedSelector;
97 } else {
98 rule.raws.selector.raw = fixedSelector;
99 }
100 }
101
102 function complain(message, index) {
103 report({
104 message,
105 index,
106 result,
107 ruleName,
108 node: rule,
109 });
110 }
111 });
112 };
113}
114
115function setFirstNodeSpaceBefore(node, value) {
116 const target = node.first;
117
118 if (target.type === 'selector') {
119 setFirstNodeSpaceBefore(target, value);
120 } else {
121 target.spaces.before = value;
122 }
123}
124
125function setLastNodeSpaceAfter(node, value) {
126 const target = node.last;
127
128 if (target.type === 'selector') {
129 setLastNodeSpaceAfter(target, value);
130 } else {
131 target.spaces.after = value;
132 }
133}
134
135function stringifyProperty(node, propName) {
136 return (node.raws && node.raws[propName]) || node[propName];
137}
138
139rule.ruleName = ruleName;
140rule.messages = messages;
141module.exports = rule;