eslint-plugin-unicorn 61.0.0 → 61.0.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-unicorn",
3
- "version": "61.0.0",
3
+ "version": "61.0.2",
4
4
  "description": "More than 100 powerful ESLint rules",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/eslint-plugin-unicorn",
@@ -53,6 +53,8 @@ const shouldIgnore = node => {
53
53
  || name === 'add'
54
54
  // `set.has(undefined)`
55
55
  || name === 'has'
56
+ // `set.delete(undefined)`
57
+ || name === 'delete'
56
58
 
57
59
  // `map.set(foo, undefined)`
58
60
  || name === 'set'
@@ -35,9 +35,8 @@ const getProblem = (valueNode, fix, reportNode) => {
35
35
  messageId: MESSAGE_ID_ERROR,
36
36
  };
37
37
 
38
- const shouldUseSuggestion = valueNode.type === 'IfStatement'
39
- ? false
40
- : !(isExpressionStatement(valueNode) || isExpressionStatement(valueNode.parent));
38
+ const shouldUseSuggestion = valueNode.type !== 'IfStatement'
39
+ && !(isExpressionStatement(valueNode) || isExpressionStatement(valueNode.parent));
41
40
 
42
41
  if (shouldUseSuggestion) {
43
42
  problem.suggest = [
@@ -78,6 +77,40 @@ const getConditionText = (node, sourceCode, isNegative) => {
78
77
  return text;
79
78
  };
80
79
 
80
+ const isClassListMethodCall = (node, methods) =>
81
+ isMethodCall(node, {
82
+ methods,
83
+ argumentsLength: 1,
84
+ optionalCall: false,
85
+ optionalMember: false,
86
+ })
87
+ && isClassList(node.callee.object);
88
+
89
+ const isSameElementAndClassName = (callExpressionA, callExpressionB) =>
90
+ isSameReference(callExpressionA.callee.object, callExpressionB.callee.object)
91
+ && isSameReference(callExpressionA.arguments[0], callExpressionB.arguments[0]);
92
+
93
+ const getClassListContainsCall = (conditionNode, isNegative, addOrRemoveCall) => {
94
+ if (!isNegative) {
95
+ if (conditionNode.type === 'UnaryExpression' && conditionNode.operator === '!' && conditionNode.prefix) {
96
+ return getClassListContainsCall(conditionNode.argument, !isNegative, addOrRemoveCall);
97
+ }
98
+
99
+ return;
100
+ }
101
+
102
+ if (conditionNode.type === 'ChainExpression') {
103
+ conditionNode = conditionNode.expression;
104
+ }
105
+
106
+ if (
107
+ isClassListMethodCall(conditionNode, ['contains'])
108
+ && isSameElementAndClassName(conditionNode, addOrRemoveCall)
109
+ ) {
110
+ return conditionNode;
111
+ }
112
+ };
113
+
81
114
  /** @param {import('eslint').Rule.RuleContext} context */
82
115
  const create = context => {
83
116
  const {sourceCode} = context;
@@ -121,37 +154,30 @@ const create = context => {
121
154
 
122
155
  // `element.classList.add('className');`
123
156
  // `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
- )) {
157
+ if (!clauses.every(node => isClassListMethodCall(node, ['add', 'remove']))) {
133
158
  return;
134
159
  }
135
160
 
136
161
  const [consequent, alternate] = clauses;
137
162
  if (
138
163
  (consequent.callee.property.name === alternate.callee.property.name)
139
- || !isSameReference(consequent.callee.object, alternate.callee.object)
140
- || !isSameReference(consequent.arguments[0], alternate.arguments[0])
164
+ || !isSameElementAndClassName(consequent, alternate)
141
165
  ) {
142
166
  return;
143
167
  }
144
168
 
145
169
  /** @param {import('eslint').Rule.RuleFixer} fixer */
146
170
  function * fix(fixer) {
147
- const isOptional = consequent.callee.object.optional || alternate.callee.object.optional;
148
171
  const elementText = getParenthesizedText(consequent.callee.object.object, sourceCode);
149
172
  const classNameText = getParenthesizedText(consequent.arguments[0], sourceCode);
150
173
  const isExpression = node.type === 'ConditionalExpression';
151
174
  const isNegative = consequent.callee.property.name === 'remove';
152
- const conditionText = getConditionText(node.test, sourceCode, isNegative);
175
+ const conditionNode = node.test;
176
+ const classListContainsCall = getClassListContainsCall(conditionNode, isNegative, consequent);
177
+ const conditionText = classListContainsCall ? '' : getConditionText(conditionNode, sourceCode, isNegative);
178
+ const isOptional = consequent.callee.object.optional || alternate.callee.object.optional || classListContainsCall?.callee.object.optional;
153
179
 
154
- let text = `${elementText}${isOptional ? '?' : ''}.classList.toggle(${classNameText}, ${conditionText})`;
180
+ let text = `${elementText}${isOptional ? '?' : ''}.classList.toggle(${classNameText}${conditionText ? `, ${conditionText}` : ''})`;
155
181
 
156
182
  if (!isExpression) {
157
183
  text = `${text};`;
@@ -195,9 +221,14 @@ const create = context => {
195
221
  /** @param {import('eslint').Rule.RuleFixer} fixer */
196
222
  function * fix(fixer) {
197
223
  const isNegative = conditionalExpression.consequent.value === 'remove';
198
- const conditionText = getConditionText(conditionalExpression.test, sourceCode, isNegative);
224
+ const conditionNode = conditionalExpression.test;
225
+ const classListContainsCall = getClassListContainsCall(conditionNode, isNegative, callExpression);
226
+ const conditionText = classListContainsCall ? '' : getConditionText(conditionNode, sourceCode, isNegative);
227
+
228
+ if (conditionText) {
229
+ yield fixer.insertTextAfter(callExpression.arguments[0], `, ${conditionText}`);
230
+ }
199
231
 
200
- yield fixer.insertTextAfter(callExpression.arguments[0], `, ${conditionText}`);
201
232
  yield replaceMemberExpressionProperty(fixer, classListMethod, sourceCode, '.toggle');
202
233
  }
203
234
 
@@ -85,8 +85,11 @@ export default function simpleArraySearchRule({method, replacement}) {
85
85
 
86
86
  const callbackScope = scopeManager.acquire(callback);
87
87
  if (
88
+ // Can't use scopeManager in vue template
89
+ // https://github.com/vuejs/vue-eslint-parser/issues/263
90
+ !callbackScope
88
91
  // `parameter` is used somewhere else
89
- findVariable(callbackScope, parameter).references.some(({identifier}) => identifier !== parameterInBinaryExpression)
92
+ || findVariable(callbackScope, parameter).references.some(({identifier}) => identifier !== parameterInBinaryExpression)
90
93
  || isFunctionSelfUsedInside(callback, callbackScope)
91
94
  ) {
92
95
  return;
@@ -1,3 +1,4 @@
1
+ import {isMemberExpression} from './ast/index.js';
1
2
  import {switchCallExpressionToNewExpression} from './fix/index.js';
2
3
 
3
4
  const messageId = 'throw-new-error';
@@ -23,6 +24,17 @@ const create = context => ({
23
24
  return;
24
25
  }
25
26
 
27
+ // https://github.com/sindresorhus/eslint-plugin-unicorn/issues/2654 (Effect library)
28
+ if (
29
+ isMemberExpression(callee, {
30
+ object: 'Data',
31
+ property: 'TaggedError',
32
+ computed: false,
33
+ })
34
+ ) {
35
+ return;
36
+ }
37
+
26
38
  return {
27
39
  node,
28
40
  messageId,