eslint-plugin-unicorn-ts 0.0.1-security → 50.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.

Potentially problematic release.


This version of eslint-plugin-unicorn-ts might be problematic. Click here for more details.

Files changed (214) hide show
  1. package/configs/all.js +6 -0
  2. package/configs/flat-config-base.js +10 -0
  3. package/configs/legacy-config-base.js +10 -0
  4. package/configs/recommended.js +117 -0
  5. package/index.js +91 -0
  6. package/license +9 -0
  7. package/package.json +186 -4
  8. package/readme.md +356 -0
  9. package/rules/ast/call-or-new-expression.js +127 -0
  10. package/rules/ast/function-types.js +5 -0
  11. package/rules/ast/index.js +39 -0
  12. package/rules/ast/is-arrow-function-body.js +7 -0
  13. package/rules/ast/is-empty-node.js +20 -0
  14. package/rules/ast/is-expression-statement.js +11 -0
  15. package/rules/ast/is-function.js +8 -0
  16. package/rules/ast/is-member-expression.js +101 -0
  17. package/rules/ast/is-method-call.js +65 -0
  18. package/rules/ast/is-reference-identifier.js +156 -0
  19. package/rules/ast/is-static-require.js +14 -0
  20. package/rules/ast/is-undefined.js +7 -0
  21. package/rules/ast/literal.js +29 -0
  22. package/rules/better-regex.js +144 -0
  23. package/rules/catch-error-name.js +136 -0
  24. package/rules/consistent-destructuring.js +168 -0
  25. package/rules/consistent-function-scoping.js +223 -0
  26. package/rules/custom-error-definition.js +215 -0
  27. package/rules/empty-brace-spaces.js +72 -0
  28. package/rules/error-message.js +104 -0
  29. package/rules/escape-case.js +63 -0
  30. package/rules/expiring-todo-comments.js +580 -0
  31. package/rules/explicit-length-check.js +229 -0
  32. package/rules/filename-case.js +258 -0
  33. package/rules/fix/add-parenthesizes-to-return-or-throw-expression.js +21 -0
  34. package/rules/fix/append-argument.js +20 -0
  35. package/rules/fix/extend-fix-range.js +15 -0
  36. package/rules/fix/fix-space-around-keywords.js +35 -0
  37. package/rules/fix/index.js +23 -0
  38. package/rules/fix/remove-argument.js +32 -0
  39. package/rules/fix/remove-member-expression-property.js +11 -0
  40. package/rules/fix/remove-method-call.js +20 -0
  41. package/rules/fix/remove-parentheses.js +11 -0
  42. package/rules/fix/remove-spaces-after.js +14 -0
  43. package/rules/fix/rename-variable.js +9 -0
  44. package/rules/fix/replace-argument.js +8 -0
  45. package/rules/fix/replace-node-or-token-and-spaces-before.js +21 -0
  46. package/rules/fix/replace-reference-identifier.js +35 -0
  47. package/rules/fix/replace-string-literal.js +11 -0
  48. package/rules/fix/replace-string-raw.js +14 -0
  49. package/rules/fix/replace-template-element.js +11 -0
  50. package/rules/fix/switch-call-expression-to-new-expression.js +18 -0
  51. package/rules/fix/switch-new-expression-to-call-expression.js +34 -0
  52. package/rules/import-style.js +364 -0
  53. package/rules/new-for-builtins.js +85 -0
  54. package/rules/no-abusive-eslint-disable.js +48 -0
  55. package/rules/no-array-callback-reference.js +256 -0
  56. package/rules/no-array-for-each.js +473 -0
  57. package/rules/no-array-method-this-argument.js +188 -0
  58. package/rules/no-array-push-push.js +144 -0
  59. package/rules/no-array-reduce.js +126 -0
  60. package/rules/no-await-expression-member.js +90 -0
  61. package/rules/no-console-spaces.js +86 -0
  62. package/rules/no-document-cookie.js +25 -0
  63. package/rules/no-empty-file.js +57 -0
  64. package/rules/no-for-loop.js +427 -0
  65. package/rules/no-hex-escape.js +46 -0
  66. package/rules/no-instanceof-array.js +65 -0
  67. package/rules/no-invalid-remove-event-listener.js +60 -0
  68. package/rules/no-keyword-prefix.js +199 -0
  69. package/rules/no-lonely-if.js +151 -0
  70. package/rules/no-negated-condition.js +144 -0
  71. package/rules/no-nested-ternary.js +58 -0
  72. package/rules/no-new-array.js +104 -0
  73. package/rules/no-new-buffer.js +98 -0
  74. package/rules/no-null.js +153 -0
  75. package/rules/no-object-as-default-parameter.js +50 -0
  76. package/rules/no-process-exit.js +104 -0
  77. package/rules/no-static-only-class.js +224 -0
  78. package/rules/no-thenable.js +198 -0
  79. package/rules/no-this-assignment.js +38 -0
  80. package/rules/no-typeof-undefined.js +143 -0
  81. package/rules/no-unnecessary-await.js +107 -0
  82. package/rules/no-unnecessary-polyfills.js +176 -0
  83. package/rules/no-unreadable-array-destructuring.js +83 -0
  84. package/rules/no-unreadable-iife.js +45 -0
  85. package/rules/no-unused-properties.js +238 -0
  86. package/rules/no-useless-fallback-in-spread.js +68 -0
  87. package/rules/no-useless-length-check.js +152 -0
  88. package/rules/no-useless-promise-resolve-reject.js +212 -0
  89. package/rules/no-useless-spread.js +381 -0
  90. package/rules/no-useless-switch-case.js +71 -0
  91. package/rules/no-useless-undefined.js +301 -0
  92. package/rules/no-zero-fractions.js +79 -0
  93. package/rules/number-literal-case.js +52 -0
  94. package/rules/numeric-separators-style.js +181 -0
  95. package/rules/prefer-add-event-listener.js +188 -0
  96. package/rules/prefer-array-find.js +423 -0
  97. package/rules/prefer-array-flat-map.js +82 -0
  98. package/rules/prefer-array-flat.js +279 -0
  99. package/rules/prefer-array-index-of.js +32 -0
  100. package/rules/prefer-array-some.js +157 -0
  101. package/rules/prefer-at.js +374 -0
  102. package/rules/prefer-blob-reading-methods.js +45 -0
  103. package/rules/prefer-code-point.js +67 -0
  104. package/rules/prefer-date-now.js +135 -0
  105. package/rules/prefer-default-parameters.js +219 -0
  106. package/rules/prefer-dom-node-append.js +48 -0
  107. package/rules/prefer-dom-node-dataset.js +120 -0
  108. package/rules/prefer-dom-node-remove.js +122 -0
  109. package/rules/prefer-dom-node-text-content.js +75 -0
  110. package/rules/prefer-event-target.js +117 -0
  111. package/rules/prefer-export-from.js +413 -0
  112. package/rules/prefer-includes.js +98 -0
  113. package/rules/prefer-json-parse-buffer.js +159 -0
  114. package/rules/prefer-keyboard-event-key.js +186 -0
  115. package/rules/prefer-logical-operator-over-ternary.js +159 -0
  116. package/rules/prefer-math-trunc.js +109 -0
  117. package/rules/prefer-modern-dom-apis.js +141 -0
  118. package/rules/prefer-modern-math-apis.js +212 -0
  119. package/rules/prefer-module.js +349 -0
  120. package/rules/prefer-native-coercion-functions.js +185 -0
  121. package/rules/prefer-negative-index.js +213 -0
  122. package/rules/prefer-node-protocol.js +61 -0
  123. package/rules/prefer-number-properties.js +126 -0
  124. package/rules/prefer-object-from-entries.js +252 -0
  125. package/rules/prefer-optional-catch-binding.js +75 -0
  126. package/rules/prefer-prototype-methods.js +88 -0
  127. package/rules/prefer-query-selector.js +135 -0
  128. package/rules/prefer-reflect-apply.js +97 -0
  129. package/rules/prefer-regexp-test.js +156 -0
  130. package/rules/prefer-set-has.js +186 -0
  131. package/rules/prefer-set-size.js +103 -0
  132. package/rules/prefer-spread.js +529 -0
  133. package/rules/prefer-string-replace-all.js +145 -0
  134. package/rules/prefer-string-slice.js +182 -0
  135. package/rules/prefer-string-starts-ends-with.js +199 -0
  136. package/rules/prefer-string-trim-start-end.js +44 -0
  137. package/rules/prefer-switch.js +344 -0
  138. package/rules/prefer-ternary.js +282 -0
  139. package/rules/prefer-top-level-await.js +152 -0
  140. package/rules/prefer-type-error.js +151 -0
  141. package/rules/prevent-abbreviations.js +645 -0
  142. package/rules/relative-url-style.js +168 -0
  143. package/rules/require-array-join-separator.js +63 -0
  144. package/rules/require-number-to-fixed-digits-argument.js +54 -0
  145. package/rules/require-post-message-target-origin.js +71 -0
  146. package/rules/shared/abbreviations.js +262 -0
  147. package/rules/shared/dom-events.js +275 -0
  148. package/rules/shared/event-keys.js +52 -0
  149. package/rules/shared/negative-index.js +46 -0
  150. package/rules/shared/simple-array-search-rule.js +128 -0
  151. package/rules/shared/typed-array.js +16 -0
  152. package/rules/string-content.js +187 -0
  153. package/rules/switch-case-braces.js +109 -0
  154. package/rules/template-indent.js +219 -0
  155. package/rules/text-encoding-identifier-case.js +108 -0
  156. package/rules/throw-new-error.js +53 -0
  157. package/rules/utils/array-or-object-prototype-property.js +63 -0
  158. package/rules/utils/assert-token.js +32 -0
  159. package/rules/utils/avoid-capture.js +146 -0
  160. package/rules/utils/boolean.js +92 -0
  161. package/rules/utils/builtins.js +36 -0
  162. package/rules/utils/cartesian-product-samples.js +24 -0
  163. package/rules/utils/create-deprecated-rules.js +25 -0
  164. package/rules/utils/escape-string.js +26 -0
  165. package/rules/utils/escape-template-element-raw.js +6 -0
  166. package/rules/utils/get-ancestor.js +20 -0
  167. package/rules/utils/get-builtin-rule.js +7 -0
  168. package/rules/utils/get-call-expression-arguments-text.js +21 -0
  169. package/rules/utils/get-class-head-location.js +22 -0
  170. package/rules/utils/get-documentation-url.js +10 -0
  171. package/rules/utils/get-indent-string.js +11 -0
  172. package/rules/utils/get-previous-node.js +24 -0
  173. package/rules/utils/get-references.js +9 -0
  174. package/rules/utils/get-scopes.js +14 -0
  175. package/rules/utils/get-switch-case-head-location.js +21 -0
  176. package/rules/utils/get-variable-identifiers.js +7 -0
  177. package/rules/utils/global-reference-tracker.js +72 -0
  178. package/rules/utils/has-optional-chain-element.js +21 -0
  179. package/rules/utils/has-same-range.js +7 -0
  180. package/rules/utils/index.js +53 -0
  181. package/rules/utils/is-function-self-used-inside.js +43 -0
  182. package/rules/utils/is-left-hand-side.js +22 -0
  183. package/rules/utils/is-logical-expression.js +16 -0
  184. package/rules/utils/is-method-named.js +9 -0
  185. package/rules/utils/is-new-expression-with-parentheses.js +26 -0
  186. package/rules/utils/is-node-matches.js +53 -0
  187. package/rules/utils/is-node-value-not-dom-node.js +21 -0
  188. package/rules/utils/is-node-value-not-function.js +42 -0
  189. package/rules/utils/is-number.js +224 -0
  190. package/rules/utils/is-object-method.js +11 -0
  191. package/rules/utils/is-on-same-line.js +7 -0
  192. package/rules/utils/is-same-identifier.js +8 -0
  193. package/rules/utils/is-same-reference.js +173 -0
  194. package/rules/utils/is-shadowed.js +33 -0
  195. package/rules/utils/is-shorthand-export-local.js +9 -0
  196. package/rules/utils/is-shorthand-import-local.js +9 -0
  197. package/rules/utils/is-shorthand-property-assignment-pattern-left.js +10 -0
  198. package/rules/utils/is-shorthand-property-value.js +8 -0
  199. package/rules/utils/is-value-not-usable.js +5 -0
  200. package/rules/utils/lodash.js +1589 -0
  201. package/rules/utils/needs-semicolon.js +114 -0
  202. package/rules/utils/numeric.js +53 -0
  203. package/rules/utils/parentheses.js +73 -0
  204. package/rules/utils/resolve-variable-name.js +20 -0
  205. package/rules/utils/rule.js +190 -0
  206. package/rules/utils/should-add-parentheses-to-conditional-expression-child.js +17 -0
  207. package/rules/utils/should-add-parentheses-to-expression-statement-expression.js +26 -0
  208. package/rules/utils/should-add-parentheses-to-logical-expression-child.js +47 -0
  209. package/rules/utils/should-add-parentheses-to-member-expression-object.js +47 -0
  210. package/rules/utils/should-add-parentheses-to-new-expression-callee.js +32 -0
  211. package/rules/utils/should-add-parentheses-to-spread-element-argument.js +22 -0
  212. package/rules/utils/singular.js +18 -0
  213. package/rules/utils/to-location.js +21 -0
  214. package/README.md +0 -5
@@ -0,0 +1,427 @@
1
+ 'use strict';
2
+ const {isClosingParenToken, getStaticValue} = require('@eslint-community/eslint-utils');
3
+ const avoidCapture = require('./utils/avoid-capture.js');
4
+ const getScopes = require('./utils/get-scopes.js');
5
+ const singular = require('./utils/singular.js');
6
+ const toLocation = require('./utils/to-location.js');
7
+ const getReferences = require('./utils/get-references.js');
8
+ const {isLiteral} = require('./ast/index.js');
9
+
10
+ const MESSAGE_ID = 'no-for-loop';
11
+ const messages = {
12
+ [MESSAGE_ID]: 'Use a `for-of` loop instead of this `for` loop.',
13
+ };
14
+
15
+ const defaultElementName = 'element';
16
+ const isLiteralZero = node => isLiteral(node, 0);
17
+ const isLiteralOne = node => isLiteral(node, 1);
18
+
19
+ const isIdentifierWithName = (node, name) => node?.type === 'Identifier' && node.name === name;
20
+
21
+ const getIndexIdentifierName = forStatement => {
22
+ const {init: variableDeclaration} = forStatement;
23
+
24
+ if (
25
+ !variableDeclaration
26
+ || variableDeclaration.type !== 'VariableDeclaration'
27
+ ) {
28
+ return;
29
+ }
30
+
31
+ if (variableDeclaration.declarations.length !== 1) {
32
+ return;
33
+ }
34
+
35
+ const [variableDeclarator] = variableDeclaration.declarations;
36
+
37
+ if (!isLiteralZero(variableDeclarator.init)) {
38
+ return;
39
+ }
40
+
41
+ if (variableDeclarator.id.type !== 'Identifier') {
42
+ return;
43
+ }
44
+
45
+ return variableDeclarator.id.name;
46
+ };
47
+
48
+ const getStrictComparisonOperands = binaryExpression => {
49
+ if (binaryExpression.operator === '<') {
50
+ return {
51
+ lesser: binaryExpression.left,
52
+ greater: binaryExpression.right,
53
+ };
54
+ }
55
+
56
+ if (binaryExpression.operator === '>') {
57
+ return {
58
+ lesser: binaryExpression.right,
59
+ greater: binaryExpression.left,
60
+ };
61
+ }
62
+ };
63
+
64
+ const getArrayIdentifierFromBinaryExpression = (binaryExpression, indexIdentifierName) => {
65
+ const operands = getStrictComparisonOperands(binaryExpression);
66
+
67
+ if (!operands) {
68
+ return;
69
+ }
70
+
71
+ const {lesser, greater} = operands;
72
+
73
+ if (!isIdentifierWithName(lesser, indexIdentifierName)) {
74
+ return;
75
+ }
76
+
77
+ if (greater.type !== 'MemberExpression') {
78
+ return;
79
+ }
80
+
81
+ if (
82
+ greater.object.type !== 'Identifier'
83
+ || greater.property.type !== 'Identifier'
84
+ ) {
85
+ return;
86
+ }
87
+
88
+ if (greater.property.name !== 'length') {
89
+ return;
90
+ }
91
+
92
+ return greater.object;
93
+ };
94
+
95
+ const getArrayIdentifier = (forStatement, indexIdentifierName) => {
96
+ const {test} = forStatement;
97
+
98
+ if (!test || test.type !== 'BinaryExpression') {
99
+ return;
100
+ }
101
+
102
+ return getArrayIdentifierFromBinaryExpression(test, indexIdentifierName);
103
+ };
104
+
105
+ const isLiteralOnePlusIdentifierWithName = (node, identifierName) => {
106
+ if (node?.type === 'BinaryExpression' && node.operator === '+') {
107
+ return (isIdentifierWithName(node.left, identifierName) && isLiteralOne(node.right))
108
+ || (isIdentifierWithName(node.right, identifierName) && isLiteralOne(node.left));
109
+ }
110
+
111
+ return false;
112
+ };
113
+
114
+ const checkUpdateExpression = (forStatement, indexIdentifierName) => {
115
+ const {update} = forStatement;
116
+
117
+ if (!update) {
118
+ return false;
119
+ }
120
+
121
+ if (update.type === 'UpdateExpression') {
122
+ return update.operator === '++' && isIdentifierWithName(update.argument, indexIdentifierName);
123
+ }
124
+
125
+ if (
126
+ update.type === 'AssignmentExpression'
127
+ && isIdentifierWithName(update.left, indexIdentifierName)
128
+ ) {
129
+ if (update.operator === '+=') {
130
+ return isLiteralOne(update.right);
131
+ }
132
+
133
+ if (update.operator === '=') {
134
+ return isLiteralOnePlusIdentifierWithName(update.right, indexIdentifierName);
135
+ }
136
+ }
137
+
138
+ return false;
139
+ };
140
+
141
+ const isOnlyArrayOfIndexVariableRead = (arrayReferences, indexIdentifierName) => arrayReferences.every(reference => {
142
+ const node = reference.identifier.parent;
143
+
144
+ if (node.type !== 'MemberExpression') {
145
+ return false;
146
+ }
147
+
148
+ if (node.property.name !== indexIdentifierName) {
149
+ return false;
150
+ }
151
+
152
+ if (
153
+ node.parent.type === 'AssignmentExpression'
154
+ && node.parent.left === node
155
+ ) {
156
+ return false;
157
+ }
158
+
159
+ return true;
160
+ });
161
+
162
+ const getRemovalRange = (node, sourceCode) => {
163
+ const declarationNode = node.parent;
164
+
165
+ if (declarationNode.declarations.length === 1) {
166
+ const {line} = declarationNode.loc.start;
167
+ const lineText = sourceCode.lines[line - 1];
168
+
169
+ const isOnlyNodeOnLine = lineText.trim() === sourceCode.getText(declarationNode);
170
+
171
+ return isOnlyNodeOnLine ? [
172
+ sourceCode.getIndexFromLoc({line, column: 0}),
173
+ sourceCode.getIndexFromLoc({line: line + 1, column: 0}),
174
+ ] : declarationNode.range;
175
+ }
176
+
177
+ const index = declarationNode.declarations.indexOf(node);
178
+
179
+ if (index === 0) {
180
+ return [
181
+ node.range[0],
182
+ declarationNode.declarations[1].range[0],
183
+ ];
184
+ }
185
+
186
+ return [
187
+ declarationNode.declarations[index - 1].range[1],
188
+ node.range[1],
189
+ ];
190
+ };
191
+
192
+ const resolveIdentifierName = (name, scope) => {
193
+ while (scope) {
194
+ const variable = scope.set.get(name);
195
+
196
+ if (variable) {
197
+ return variable;
198
+ }
199
+
200
+ scope = scope.upper;
201
+ }
202
+ };
203
+
204
+ const scopeContains = (ancestor, descendant) => {
205
+ while (descendant) {
206
+ if (descendant === ancestor) {
207
+ return true;
208
+ }
209
+
210
+ descendant = descendant.upper;
211
+ }
212
+
213
+ return false;
214
+ };
215
+
216
+ const nodeContains = (ancestor, descendant) => {
217
+ while (descendant) {
218
+ if (descendant === ancestor) {
219
+ return true;
220
+ }
221
+
222
+ descendant = descendant.parent;
223
+ }
224
+
225
+ return false;
226
+ };
227
+
228
+ const isIndexVariableUsedElsewhereInTheLoopBody = (indexVariable, bodyScope, arrayIdentifierName) => {
229
+ const inBodyReferences = indexVariable.references.filter(reference => scopeContains(bodyScope, reference.from));
230
+
231
+ const referencesOtherThanArrayAccess = inBodyReferences.filter(reference => {
232
+ const node = reference.identifier.parent;
233
+
234
+ if (node.type !== 'MemberExpression') {
235
+ return true;
236
+ }
237
+
238
+ if (node.object.name !== arrayIdentifierName) {
239
+ return true;
240
+ }
241
+
242
+ return false;
243
+ });
244
+
245
+ return referencesOtherThanArrayAccess.length > 0;
246
+ };
247
+
248
+ const isIndexVariableAssignedToInTheLoopBody = (indexVariable, bodyScope) =>
249
+ indexVariable.references
250
+ .filter(reference => scopeContains(bodyScope, reference.from))
251
+ .some(inBodyReference => inBodyReference.isWrite());
252
+
253
+ const someVariablesLeakOutOfTheLoop = (forStatement, variables, forScope) =>
254
+ variables.some(
255
+ variable => !variable.references.every(
256
+ reference => scopeContains(forScope, reference.from) || nodeContains(forStatement, reference.identifier),
257
+ ),
258
+ );
259
+
260
+ const getReferencesInChildScopes = (scope, name) =>
261
+ getReferences(scope).filter(reference => reference.identifier.name === name);
262
+
263
+ /** @param {import('eslint').Rule.RuleContext} context */
264
+ const create = context => {
265
+ const {sourceCode} = context;
266
+ const {scopeManager, text: sourceCodeText} = sourceCode;
267
+
268
+ return {
269
+ ForStatement(node) {
270
+ const indexIdentifierName = getIndexIdentifierName(node);
271
+
272
+ if (!indexIdentifierName) {
273
+ return;
274
+ }
275
+
276
+ const arrayIdentifier = getArrayIdentifier(node, indexIdentifierName);
277
+ if (!arrayIdentifier) {
278
+ return;
279
+ }
280
+
281
+ const arrayIdentifierName = arrayIdentifier.name;
282
+
283
+ const scope = sourceCode.getScope(node);
284
+ const staticResult = getStaticValue(arrayIdentifier, scope);
285
+ if (staticResult && !Array.isArray(staticResult.value)) {
286
+ // Bail out if we can tell that the array variable has a non-array value (i.e. we're looping through the characters of a string constant).
287
+ return;
288
+ }
289
+
290
+ if (!checkUpdateExpression(node, indexIdentifierName)) {
291
+ return;
292
+ }
293
+
294
+ if (!node.body || node.body.type !== 'BlockStatement') {
295
+ return;
296
+ }
297
+
298
+ const forScope = scopeManager.acquire(node);
299
+ const bodyScope = scopeManager.acquire(node.body);
300
+
301
+ if (!bodyScope) {
302
+ return;
303
+ }
304
+
305
+ const indexVariable = resolveIdentifierName(indexIdentifierName, bodyScope);
306
+
307
+ if (isIndexVariableAssignedToInTheLoopBody(indexVariable, bodyScope)) {
308
+ return;
309
+ }
310
+
311
+ const arrayReferences = getReferencesInChildScopes(bodyScope, arrayIdentifierName);
312
+
313
+ if (arrayReferences.length === 0) {
314
+ return;
315
+ }
316
+
317
+ if (!isOnlyArrayOfIndexVariableRead(arrayReferences, indexIdentifierName)) {
318
+ return;
319
+ }
320
+
321
+ const [start] = node.range;
322
+ const [, end] = sourceCode.getTokenBefore(node.body, isClosingParenToken).range;
323
+
324
+ const problem = {
325
+ loc: toLocation([start, end], sourceCode),
326
+ messageId: MESSAGE_ID,
327
+ };
328
+
329
+ const elementReference = arrayReferences.find(reference => {
330
+ const node = reference.identifier.parent;
331
+
332
+ if (node.parent.type !== 'VariableDeclarator') {
333
+ return false;
334
+ }
335
+
336
+ return true;
337
+ });
338
+ const elementNode = elementReference?.identifier.parent.parent;
339
+ const elementIdentifierName = elementNode?.id.name;
340
+ const elementVariable = elementIdentifierName && resolveIdentifierName(elementIdentifierName, bodyScope);
341
+
342
+ const shouldFix = !someVariablesLeakOutOfTheLoop(node, [indexVariable, elementVariable].filter(Boolean), forScope);
343
+
344
+ if (shouldFix) {
345
+ problem.fix = function * (fixer) {
346
+ const shouldGenerateIndex = isIndexVariableUsedElsewhereInTheLoopBody(indexVariable, bodyScope, arrayIdentifierName);
347
+
348
+ const index = indexIdentifierName;
349
+ const element = elementIdentifierName
350
+ || avoidCapture(singular(arrayIdentifierName) || defaultElementName, getScopes(bodyScope));
351
+ const array = arrayIdentifierName;
352
+
353
+ let declarationElement = element;
354
+ let declarationType = 'const';
355
+ let removeDeclaration = true;
356
+ let typeAnnotation;
357
+
358
+ if (elementNode) {
359
+ if (elementNode.id.type === 'ObjectPattern' || elementNode.id.type === 'ArrayPattern') {
360
+ removeDeclaration = arrayReferences.length === 1;
361
+ }
362
+
363
+ if (removeDeclaration) {
364
+ declarationType = element.type === 'VariableDeclarator' ? elementNode.kind : elementNode.parent.kind;
365
+ if (elementNode.id.typeAnnotation && shouldGenerateIndex) {
366
+ declarationElement = sourceCodeText.slice(elementNode.id.range[0], elementNode.id.typeAnnotation.range[0]).trim();
367
+ typeAnnotation = sourceCode.getText(
368
+ elementNode.id.typeAnnotation,
369
+ -1, // Skip leading `:`
370
+ ).trim();
371
+ } else {
372
+ declarationElement = sourceCode.getText(elementNode.id);
373
+ }
374
+ }
375
+ }
376
+
377
+ const parts = [declarationType];
378
+ if (shouldGenerateIndex) {
379
+ parts.push(` [${index}, ${declarationElement}]`);
380
+ if (typeAnnotation) {
381
+ parts.push(`: [number, ${typeAnnotation}]`);
382
+ }
383
+
384
+ parts.push(` of ${array}.entries()`);
385
+ } else {
386
+ parts.push(` ${declarationElement} of ${array}`);
387
+ }
388
+
389
+ const replacement = parts.join('');
390
+
391
+ yield fixer.replaceTextRange([
392
+ node.init.range[0],
393
+ node.update.range[1],
394
+ ], replacement);
395
+
396
+ for (const reference of arrayReferences) {
397
+ if (reference !== elementReference) {
398
+ yield fixer.replaceText(reference.identifier.parent, element);
399
+ }
400
+ }
401
+
402
+ if (elementNode) {
403
+ yield removeDeclaration
404
+ ? fixer.removeRange(getRemovalRange(elementNode, sourceCode))
405
+ : fixer.replaceText(elementNode.init, element);
406
+ }
407
+ };
408
+ }
409
+
410
+ return problem;
411
+ },
412
+ };
413
+ };
414
+
415
+ /** @type {import('eslint').Rule.RuleModule} */
416
+ module.exports = {
417
+ create,
418
+ meta: {
419
+ type: 'suggestion',
420
+ docs: {
421
+ description: 'Do not use a `for` loop that can be replaced with a `for-of` loop.',
422
+ },
423
+ fixable: 'code',
424
+ hasSuggestions: true,
425
+ messages,
426
+ },
427
+ };
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+ const {replaceTemplateElement} = require('./fix/index.js');
3
+ const {isStringLiteral, isRegexLiteral} = require('./ast/index.js');
4
+
5
+ const MESSAGE_ID = 'no-hex-escape';
6
+ const messages = {
7
+ [MESSAGE_ID]: 'Use Unicode escapes instead of hexadecimal escapes.',
8
+ };
9
+
10
+ function checkEscape(context, node, value) {
11
+ const fixedValue = value.replaceAll(/(?<=(?:^|[^\\])(?:\\\\)*\\)x/g, 'u00');
12
+
13
+ if (value !== fixedValue) {
14
+ return {
15
+ node,
16
+ messageId: MESSAGE_ID,
17
+ fix: fixer =>
18
+ node.type === 'TemplateElement'
19
+ ? replaceTemplateElement(fixer, node, fixedValue)
20
+ : fixer.replaceText(node, fixedValue),
21
+ };
22
+ }
23
+ }
24
+
25
+ /** @param {import('eslint').Rule.RuleContext} context */
26
+ const create = context => ({
27
+ Literal(node) {
28
+ if (isStringLiteral(node) || isRegexLiteral(node)) {
29
+ return checkEscape(context, node, node.raw);
30
+ }
31
+ },
32
+ TemplateElement: node => checkEscape(context, node, node.value.raw),
33
+ });
34
+
35
+ /** @type {import('eslint').Rule.RuleModule} */
36
+ module.exports = {
37
+ create,
38
+ meta: {
39
+ type: 'suggestion',
40
+ docs: {
41
+ description: 'Enforce the use of Unicode escapes instead of hexadecimal escapes.',
42
+ },
43
+ fixable: 'code',
44
+ messages,
45
+ },
46
+ };
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+ const {checkVueTemplate} = require('./utils/rule.js');
3
+ const {getParenthesizedRange} = require('./utils/parentheses.js');
4
+ const {replaceNodeOrTokenAndSpacesBefore, fixSpaceAroundKeyword} = require('./fix/index.js');
5
+
6
+ const isInstanceofToken = token => token.value === 'instanceof' && token.type === 'Keyword';
7
+
8
+ const MESSAGE_ID = 'no-instanceof-array';
9
+ const messages = {
10
+ [MESSAGE_ID]: 'Use `Array.isArray()` instead of `instanceof Array`.',
11
+ };
12
+
13
+ /** @param {import('eslint').Rule.RuleContext} context */
14
+ const create = context => {
15
+ const {sourceCode} = context;
16
+
17
+ return {
18
+ BinaryExpression(node) {
19
+ if (!(
20
+ node.operator === 'instanceof'
21
+ && node.right.type === 'Identifier'
22
+ && node.right.name === 'Array'
23
+ )) {
24
+ return;
25
+ }
26
+
27
+ const {left, right} = node;
28
+ let tokenStore = sourceCode;
29
+ let instanceofToken = tokenStore.getTokenAfter(left, isInstanceofToken);
30
+ if (!instanceofToken && sourceCode.parserServices.getTemplateBodyTokenStore) {
31
+ tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore();
32
+ instanceofToken = tokenStore.getTokenAfter(left, isInstanceofToken);
33
+ }
34
+
35
+ return {
36
+ node: instanceofToken,
37
+ messageId: MESSAGE_ID,
38
+ /** @param {import('eslint').Rule.RuleFixer} fixer */
39
+ * fix(fixer) {
40
+ yield * fixSpaceAroundKeyword(fixer, node, sourceCode);
41
+
42
+ const range = getParenthesizedRange(left, tokenStore);
43
+ yield fixer.insertTextBeforeRange(range, 'Array.isArray(');
44
+ yield fixer.insertTextAfterRange(range, ')');
45
+
46
+ yield * replaceNodeOrTokenAndSpacesBefore(instanceofToken, '', fixer, sourceCode, tokenStore);
47
+ yield * replaceNodeOrTokenAndSpacesBefore(right, '', fixer, sourceCode, tokenStore);
48
+ },
49
+ };
50
+ },
51
+ };
52
+ };
53
+
54
+ /** @type {import('eslint').Rule.RuleModule} */
55
+ module.exports = {
56
+ create: checkVueTemplate(create),
57
+ meta: {
58
+ type: 'suggestion',
59
+ docs: {
60
+ description: 'Require `Array.isArray()` instead of `instanceof Array`.',
61
+ },
62
+ fixable: 'code',
63
+ messages,
64
+ },
65
+ };
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+ const {getFunctionHeadLocation} = require('@eslint-community/eslint-utils');
3
+ const {isMethodCall} = require('./ast/index.js');
4
+
5
+ const MESSAGE_ID = 'no-invalid-remove-event-listener';
6
+ const messages = {
7
+ [MESSAGE_ID]: 'The listener argument should be a function reference.',
8
+ };
9
+
10
+ /** @param {import('eslint').Rule.RuleContext} context */
11
+ const create = context => ({
12
+ CallExpression(callExpression) {
13
+ if (!(
14
+ isMethodCall(callExpression, {
15
+ method: 'removeEventListener',
16
+ minimumArguments: 2,
17
+ optionalCall: false,
18
+ optionalMember: false,
19
+ })
20
+ && callExpression.arguments[0].type !== 'SpreadElement'
21
+ && (
22
+ callExpression.arguments[1].type === 'FunctionExpression'
23
+ || callExpression.arguments[1].type === 'ArrowFunctionExpression'
24
+ || isMethodCall(callExpression.arguments[1], {
25
+ method: 'bind',
26
+ optionalCall: false,
27
+ optionalMember: false,
28
+ })
29
+ )
30
+ )) {
31
+ return;
32
+ }
33
+
34
+ const [, listener] = callExpression.arguments;
35
+ if (['ArrowFunctionExpression', 'FunctionExpression'].includes(listener.type)) {
36
+ return {
37
+ node: listener,
38
+ loc: getFunctionHeadLocation(listener, context.sourceCode),
39
+ messageId: MESSAGE_ID,
40
+ };
41
+ }
42
+
43
+ return {
44
+ node: listener.callee.property,
45
+ messageId: MESSAGE_ID,
46
+ };
47
+ },
48
+ });
49
+
50
+ /** @type {import('eslint').Rule.RuleModule} */
51
+ module.exports = {
52
+ create,
53
+ meta: {
54
+ type: 'problem',
55
+ docs: {
56
+ description: 'Prevent calling `EventTarget#removeEventListener()` with the result of an expression.',
57
+ },
58
+ messages,
59
+ },
60
+ };