UNPKG

3.59 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 // TODO: Issue #4985
32 // eslint-disable-next-line no-shadow
33 root.walkRules((rule) => {
34 if (!isStandardSyntaxRule(rule)) {
35 return;
36 }
37
38 if (!rule.selector.includes('(')) {
39 return;
40 }
41
42 let hasFixed = false;
43 const selector = rule.raws.selector ? rule.raws.selector.raw : rule.selector;
44 const fixedSelector = parseSelector(selector, result, rule, (selectorTree) => {
45 selectorTree.walkPseudos((pseudoNode) => {
46 if (!pseudoNode.length) {
47 return;
48 }
49
50 const paramString = pseudoNode.map(String).join(',');
51 const nextCharIsSpace = paramString.startsWith(' ');
52 const openIndex =
53 pseudoNode.sourceIndex + stringifyProperty(pseudoNode, 'value').length + 1;
54
55 if (nextCharIsSpace && expectation === 'never') {
56 if (context.fix) {
57 hasFixed = true;
58 setFirstNodeSpaceBefore(pseudoNode, '');
59 } else {
60 complain(messages.rejectedOpening, openIndex);
61 }
62 }
63
64 if (!nextCharIsSpace && expectation === 'always') {
65 if (context.fix) {
66 hasFixed = true;
67 setFirstNodeSpaceBefore(pseudoNode, ' ');
68 } else {
69 complain(messages.expectedOpening, openIndex);
70 }
71 }
72
73 const prevCharIsSpace = paramString.endsWith(' ');
74 const closeIndex = openIndex + paramString.length - 1;
75
76 if (prevCharIsSpace && expectation === 'never') {
77 if (context.fix) {
78 hasFixed = true;
79 setLastNodeSpaceAfter(pseudoNode, '');
80 } else {
81 complain(messages.rejectedClosing, closeIndex);
82 }
83 }
84
85 if (!prevCharIsSpace && expectation === 'always') {
86 if (context.fix) {
87 hasFixed = true;
88 setLastNodeSpaceAfter(pseudoNode, ' ');
89 } else {
90 complain(messages.expectedClosing, closeIndex);
91 }
92 }
93 });
94 });
95
96 if (hasFixed) {
97 if (!rule.raws.selector) {
98 rule.selector = fixedSelector;
99 } else {
100 rule.raws.selector.raw = fixedSelector;
101 }
102 }
103
104 function complain(message, index) {
105 report({
106 message,
107 index,
108 result,
109 ruleName,
110 node: rule,
111 });
112 }
113 });
114 };
115}
116
117function setFirstNodeSpaceBefore(node, value) {
118 const target = node.first;
119
120 if (target.type === 'selector') {
121 setFirstNodeSpaceBefore(target, value);
122 } else {
123 target.spaces.before = value;
124 }
125}
126
127function setLastNodeSpaceAfter(node, value) {
128 const target = node.last;
129
130 if (target.type === 'selector') {
131 setLastNodeSpaceAfter(target, value);
132 } else {
133 target.spaces.after = value;
134 }
135}
136
137function stringifyProperty(node, propName) {
138 return (node.raws && node.raws[propName]) || node[propName];
139}
140
141rule.ruleName = ruleName;
142rule.messages = messages;
143module.exports = rule;