1 |
|
2 |
|
3 | 'use strict';
|
4 |
|
5 | const _ = require('lodash');
|
6 | const declarationValueIndex = require('../../utils/declarationValueIndex');
|
7 | const isSingleLineString = require('../../utils/isSingleLineString');
|
8 | const isStandardSyntaxFunction = require('../../utils/isStandardSyntaxFunction');
|
9 | const report = require('../../utils/report');
|
10 | const ruleMessages = require('../../utils/ruleMessages');
|
11 | const validateOptions = require('../../utils/validateOptions');
|
12 | const valueParser = require('postcss-value-parser');
|
13 |
|
14 | const ruleName = 'function-parentheses-space-inside';
|
15 |
|
16 | const messages = ruleMessages(ruleName, {
|
17 | expectedOpening: 'Expected single space after "("',
|
18 | rejectedOpening: 'Unexpected whitespace after "("',
|
19 | expectedClosing: 'Expected single space before ")"',
|
20 | rejectedClosing: 'Unexpected whitespace before ")"',
|
21 | expectedOpeningSingleLine: 'Expected single space after "(" in a single-line function',
|
22 | rejectedOpeningSingleLine: 'Unexpected whitespace after "(" in a single-line function',
|
23 | expectedClosingSingleLine: 'Expected single space before ")" in a single-line function',
|
24 | rejectedClosingSingleLine: 'Unexpected whitespace before ")" in a single-line function',
|
25 | });
|
26 |
|
27 | function rule(expectation, options, context) {
|
28 | return (root, result) => {
|
29 | const validOptions = validateOptions(result, ruleName, {
|
30 | actual: expectation,
|
31 | possible: ['always', 'never', 'always-single-line', 'never-single-line'],
|
32 | });
|
33 |
|
34 | if (!validOptions) {
|
35 | return;
|
36 | }
|
37 |
|
38 | root.walkDecls((decl) => {
|
39 | if (!decl.value.includes('(')) {
|
40 | return;
|
41 | }
|
42 |
|
43 | let hasFixed = false;
|
44 | const declValue = _.get(decl, 'raws.value.raw', decl.value);
|
45 | const parsedValue = valueParser(declValue);
|
46 |
|
47 | parsedValue.walk((valueNode) => {
|
48 | if (valueNode.type !== 'function') {
|
49 | return;
|
50 | }
|
51 |
|
52 | if (!isStandardSyntaxFunction(valueNode)) {
|
53 | return;
|
54 | }
|
55 |
|
56 |
|
57 | if (!valueNode.nodes.length) {
|
58 | return;
|
59 | }
|
60 |
|
61 | const functionString = valueParser.stringify(valueNode);
|
62 | const isSingleLine = isSingleLineString(functionString);
|
63 |
|
64 |
|
65 |
|
66 | const openingIndex = valueNode.sourceIndex + valueNode.value.length + 1;
|
67 |
|
68 | if (expectation === 'always' && valueNode.before !== ' ') {
|
69 | if (context.fix) {
|
70 | hasFixed = true;
|
71 | valueNode.before = ' ';
|
72 | } else {
|
73 | complain(messages.expectedOpening, openingIndex);
|
74 | }
|
75 | }
|
76 |
|
77 | if (expectation === 'never' && valueNode.before !== '') {
|
78 | if (context.fix) {
|
79 | hasFixed = true;
|
80 | valueNode.before = '';
|
81 | } else {
|
82 | complain(messages.rejectedOpening, openingIndex);
|
83 | }
|
84 | }
|
85 |
|
86 | if (isSingleLine && expectation === 'always-single-line' && valueNode.before !== ' ') {
|
87 | if (context.fix) {
|
88 | hasFixed = true;
|
89 | valueNode.before = ' ';
|
90 | } else {
|
91 | complain(messages.expectedOpeningSingleLine, openingIndex);
|
92 | }
|
93 | }
|
94 |
|
95 | if (isSingleLine && expectation === 'never-single-line' && valueNode.before !== '') {
|
96 | if (context.fix) {
|
97 | hasFixed = true;
|
98 | valueNode.before = '';
|
99 | } else {
|
100 | complain(messages.rejectedOpeningSingleLine, openingIndex);
|
101 | }
|
102 | }
|
103 |
|
104 |
|
105 |
|
106 | const closingIndex = valueNode.sourceIndex + functionString.length - 2;
|
107 |
|
108 | if (expectation === 'always' && valueNode.after !== ' ') {
|
109 | if (context.fix) {
|
110 | hasFixed = true;
|
111 | valueNode.after = ' ';
|
112 | } else {
|
113 | complain(messages.expectedClosing, closingIndex);
|
114 | }
|
115 | }
|
116 |
|
117 | if (expectation === 'never' && valueNode.after !== '') {
|
118 | if (context.fix) {
|
119 | hasFixed = true;
|
120 | valueNode.after = '';
|
121 | } else {
|
122 | complain(messages.rejectedClosing, closingIndex);
|
123 | }
|
124 | }
|
125 |
|
126 | if (isSingleLine && expectation === 'always-single-line' && valueNode.after !== ' ') {
|
127 | if (context.fix) {
|
128 | hasFixed = true;
|
129 | valueNode.after = ' ';
|
130 | } else {
|
131 | complain(messages.expectedClosingSingleLine, closingIndex);
|
132 | }
|
133 | }
|
134 |
|
135 | if (isSingleLine && expectation === 'never-single-line' && valueNode.after !== '') {
|
136 | if (context.fix) {
|
137 | hasFixed = true;
|
138 | valueNode.after = '';
|
139 | } else {
|
140 | complain(messages.rejectedClosingSingleLine, closingIndex);
|
141 | }
|
142 | }
|
143 | });
|
144 |
|
145 | if (hasFixed) {
|
146 | if (!decl.raws.value) {
|
147 | decl.value = parsedValue.toString();
|
148 | } else {
|
149 | decl.raws.value.raw = parsedValue.toString();
|
150 | }
|
151 | }
|
152 |
|
153 | function complain(message, offset) {
|
154 | report({
|
155 | ruleName,
|
156 | result,
|
157 | message,
|
158 | node: decl,
|
159 | index: declarationValueIndex(decl) + offset,
|
160 | });
|
161 | }
|
162 | });
|
163 | };
|
164 | }
|
165 |
|
166 | rule.ruleName = ruleName;
|
167 | rule.messages = messages;
|
168 | module.exports = rule;
|