eslint-plugin-unicorn 61.0.0 → 61.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/rules/prefer-classlist-toggle.js +48 -16
package/package.json
CHANGED
|
@@ -78,6 +78,40 @@ const getConditionText = (node, sourceCode, isNegative) => {
|
|
|
78
78
|
return text;
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
+
const isClassListMethodCall = (node, methods) =>
|
|
82
|
+
isMethodCall(node, {
|
|
83
|
+
methods,
|
|
84
|
+
argumentsLength: 1,
|
|
85
|
+
optionalCall: false,
|
|
86
|
+
optionalMember: false,
|
|
87
|
+
})
|
|
88
|
+
&& isClassList(node.callee.object);
|
|
89
|
+
|
|
90
|
+
const isSameElementAndClassName = (callExpressionA, callExpressionB) =>
|
|
91
|
+
isSameReference(callExpressionA.callee.object, callExpressionB.callee.object)
|
|
92
|
+
&& isSameReference(callExpressionA.arguments[0], callExpressionB.arguments[0]);
|
|
93
|
+
|
|
94
|
+
const getClassListContainsCall = (conditionNode, isNegative, addOrRemoveCall) => {
|
|
95
|
+
if (!isNegative) {
|
|
96
|
+
if (!(conditionNode.type === 'UnaryExpression' && conditionNode.operator === '!' && conditionNode.prefix)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return getClassListContainsCall(conditionNode.argument, !isNegative, addOrRemoveCall);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (conditionNode.type === 'ChainExpression') {
|
|
104
|
+
conditionNode = conditionNode.expression;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (
|
|
108
|
+
isClassListMethodCall(conditionNode, ['contains'])
|
|
109
|
+
&& isSameElementAndClassName(conditionNode, addOrRemoveCall)
|
|
110
|
+
) {
|
|
111
|
+
return conditionNode;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
81
115
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
82
116
|
const create = context => {
|
|
83
117
|
const {sourceCode} = context;
|
|
@@ -121,37 +155,30 @@ const create = context => {
|
|
|
121
155
|
|
|
122
156
|
// `element.classList.add('className');`
|
|
123
157
|
// `element.classList.remove('className');`
|
|
124
|
-
if (!clauses.every(node =>
|
|
125
|
-
isMethodCall(node, {
|
|
126
|
-
methods: ['add', 'remove'],
|
|
127
|
-
argumentsLength: 1,
|
|
128
|
-
optionalCall: false,
|
|
129
|
-
optionalMember: false,
|
|
130
|
-
})
|
|
131
|
-
&& isClassList(node.callee.object),
|
|
132
|
-
)) {
|
|
158
|
+
if (!clauses.every(node => isClassListMethodCall(node, ['add', 'remove']))) {
|
|
133
159
|
return;
|
|
134
160
|
}
|
|
135
161
|
|
|
136
162
|
const [consequent, alternate] = clauses;
|
|
137
163
|
if (
|
|
138
164
|
(consequent.callee.property.name === alternate.callee.property.name)
|
|
139
|
-
|| !
|
|
140
|
-
|| !isSameReference(consequent.arguments[0], alternate.arguments[0])
|
|
165
|
+
|| !isSameElementAndClassName(consequent, alternate)
|
|
141
166
|
) {
|
|
142
167
|
return;
|
|
143
168
|
}
|
|
144
169
|
|
|
145
170
|
/** @param {import('eslint').Rule.RuleFixer} fixer */
|
|
146
171
|
function * fix(fixer) {
|
|
147
|
-
const isOptional = consequent.callee.object.optional || alternate.callee.object.optional;
|
|
148
172
|
const elementText = getParenthesizedText(consequent.callee.object.object, sourceCode);
|
|
149
173
|
const classNameText = getParenthesizedText(consequent.arguments[0], sourceCode);
|
|
150
174
|
const isExpression = node.type === 'ConditionalExpression';
|
|
151
175
|
const isNegative = consequent.callee.property.name === 'remove';
|
|
152
|
-
const
|
|
176
|
+
const conditionNode = node.test;
|
|
177
|
+
const classListContainsCall = getClassListContainsCall(conditionNode, isNegative, consequent);
|
|
178
|
+
const conditionText = classListContainsCall ? '' : getConditionText(conditionNode, sourceCode, isNegative);
|
|
179
|
+
const isOptional = consequent.callee.object.optional || alternate.callee.object.optional || classListContainsCall?.callee.object.optional;
|
|
153
180
|
|
|
154
|
-
let text = `${elementText}${isOptional ? '?' : ''}.classList.toggle(${classNameText}
|
|
181
|
+
let text = `${elementText}${isOptional ? '?' : ''}.classList.toggle(${classNameText}${conditionText ? `, ${conditionText}` : ''})`;
|
|
155
182
|
|
|
156
183
|
if (!isExpression) {
|
|
157
184
|
text = `${text};`;
|
|
@@ -195,9 +222,14 @@ const create = context => {
|
|
|
195
222
|
/** @param {import('eslint').Rule.RuleFixer} fixer */
|
|
196
223
|
function * fix(fixer) {
|
|
197
224
|
const isNegative = conditionalExpression.consequent.value === 'remove';
|
|
198
|
-
const
|
|
225
|
+
const conditionNode = conditionalExpression.test;
|
|
226
|
+
const classListContainsCall = getClassListContainsCall(conditionNode, isNegative, callExpression);
|
|
227
|
+
const conditionText = classListContainsCall ? '' : getConditionText(conditionNode, sourceCode, isNegative);
|
|
228
|
+
|
|
229
|
+
if (conditionText) {
|
|
230
|
+
yield fixer.insertTextAfter(callExpression.arguments[0], `, ${conditionText}`);
|
|
231
|
+
}
|
|
199
232
|
|
|
200
|
-
yield fixer.insertTextAfter(callExpression.arguments[0], `, ${conditionText}`);
|
|
201
233
|
yield replaceMemberExpressionProperty(fixer, classListMethod, sourceCode, '.toggle');
|
|
202
234
|
}
|
|
203
235
|
|