UNPKG

3.06 kBJavaScriptView Raw
1'use strict';
2
3const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
4const report = require('../../utils/report');
5const ruleMessages = require('../../utils/ruleMessages');
6const styleSearch = require('style-search');
7const validateOptions = require('../../utils/validateOptions');
8const whitespaceChecker = require('../../utils/whitespaceChecker');
9
10const ruleName = 'selector-list-comma-newline-after';
11
12const messages = ruleMessages(ruleName, {
13 expectedAfter: () => 'Expected newline after ","',
14 expectedAfterMultiLine: () => 'Expected newline after "," in a multi-line list',
15 rejectedAfterMultiLine: () => 'Unexpected whitespace after "," in a multi-line list',
16});
17
18function rule(expectation, options, context) {
19 const checker = whitespaceChecker('newline', expectation, messages);
20
21 return (root, result) => {
22 const validOptions = validateOptions(result, ruleName, {
23 actual: expectation,
24 possible: ['always', 'always-multi-line', 'never-multi-line'],
25 });
26
27 if (!validOptions) {
28 return;
29 }
30
31 root.walkRules((rule) => {
32 if (!isStandardSyntaxRule(rule)) {
33 return;
34 }
35
36 // Get raw selector so we can allow end-of-line comments, e.g.
37 // a, /* comment */
38 // b {}
39 const selector = rule.raws.selector ? rule.raws.selector.raw : rule.selector;
40
41 const fixIndices = [];
42
43 styleSearch(
44 {
45 source: selector,
46 target: ',',
47 functionArguments: 'skip',
48 },
49 (match) => {
50 const nextChars = selector.substr(match.endIndex, selector.length - match.endIndex);
51
52 // If there's a // comment, that means there has to be a newline
53 // ending the comment so we're fine
54 if (/^\s+\/\//.test(nextChars)) {
55 return;
56 }
57
58 // If there are spaces and then a comment begins, look for the newline
59 const indextoCheckAfter = /^\s+\/\*/.test(nextChars)
60 ? selector.indexOf('*/', match.endIndex) + 1
61 : match.startIndex;
62
63 checker.afterOneOnly({
64 source: selector,
65 index: indextoCheckAfter,
66 err: (m) => {
67 if (context.fix) {
68 fixIndices.push(indextoCheckAfter + 1);
69
70 return;
71 }
72
73 report({
74 message: m,
75 node: rule,
76 index: match.startIndex,
77 result,
78 ruleName,
79 });
80 },
81 });
82 },
83 );
84
85 if (fixIndices.length) {
86 let fixedSelector = selector;
87
88 fixIndices
89 .sort((a, b) => b - a)
90 .forEach((index) => {
91 const beforeSelector = fixedSelector.slice(0, index);
92 let afterSelector = fixedSelector.slice(index);
93
94 if (expectation.startsWith('always')) {
95 afterSelector = context.newline + afterSelector;
96 } else if (expectation.startsWith('never-multi-line')) {
97 afterSelector = afterSelector.replace(/^\s*/, '');
98 }
99
100 fixedSelector = beforeSelector + afterSelector;
101 });
102
103 if (rule.raws.selector) {
104 rule.raws.selector.raw = fixedSelector;
105 } else {
106 rule.selector = fixedSelector;
107 }
108 }
109 });
110 };
111}
112
113rule.ruleName = ruleName;
114rule.messages = messages;
115module.exports = rule;