eslint-plugin-unicorn 67.0.0 → 69.0.0

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.
Files changed (192) hide show
  1. package/index.js +4 -0
  2. package/package.json +8 -3
  3. package/readme.md +165 -16
  4. package/rules/ast/is-empty-node.js +1 -5
  5. package/rules/ast/is-empty-object-expression.js +2 -2
  6. package/rules/better-dom-traversing.js +17 -8
  7. package/rules/class-reference-in-static-methods.js +14 -2
  8. package/rules/comment-content.js +136 -18
  9. package/rules/consistent-assert.js +1 -1
  10. package/rules/consistent-boolean-name.js +44 -5
  11. package/rules/consistent-compound-words.js +7 -5
  12. package/rules/consistent-conditional-object-spread.js +360 -0
  13. package/rules/consistent-destructuring.js +5 -0
  14. package/rules/consistent-existence-index-check.js +1 -1
  15. package/rules/consistent-json-file-read.js +4 -16
  16. package/rules/consistent-optional-chaining.js +3 -20
  17. package/rules/consistent-tuple-labels.js +49 -0
  18. package/rules/custom-error-definition.js +26 -7
  19. package/rules/default-export-style.js +569 -0
  20. package/rules/expiring-todo-comments.js +1 -1
  21. package/rules/explicit-length-check.js +7 -32
  22. package/rules/import-style.js +8 -6
  23. package/rules/index.js +50 -1
  24. package/rules/logical-assignment-operators.js +17 -4
  25. package/rules/{prevent-abbreviations.js → name-replacements.js} +11 -9
  26. package/rules/new-for-builtins.js +39 -5
  27. package/rules/no-accessor-recursion.js +7 -0
  28. package/rules/no-accidental-bitwise-operator.js +97 -0
  29. package/rules/no-array-callback-reference.js +1 -7
  30. package/rules/no-array-concat-in-loop.js +45 -0
  31. package/rules/no-array-front-mutation.js +64 -0
  32. package/rules/no-array-sort-for-min-max.js +225 -0
  33. package/rules/no-array-splice.js +78 -64
  34. package/rules/no-boolean-sort-comparator.js +215 -0
  35. package/rules/no-break-in-nested-loop.js +33 -0
  36. package/rules/no-chained-comparison.js +116 -0
  37. package/rules/no-collection-bracket-access.js +165 -0
  38. package/rules/no-computed-property-existence-check.js +6 -0
  39. package/rules/no-confusing-array-splice.js +2 -21
  40. package/rules/no-confusing-array-with.js +2 -20
  41. package/rules/no-constant-zero-expression.js +91 -0
  42. package/rules/no-declarations-before-early-exit.js +29 -36
  43. package/rules/no-double-comparison.js +117 -0
  44. package/rules/no-duplicate-if-branches.js +143 -0
  45. package/rules/no-duplicate-logical-operands.js +151 -0
  46. package/rules/no-for-each.js +11 -12
  47. package/rules/no-for-loop.js +3 -15
  48. package/rules/no-impossible-length-comparison.js +166 -0
  49. package/rules/no-incorrect-query-selector.js +8 -4
  50. package/rules/no-incorrect-template-string-interpolation.js +106 -43
  51. package/rules/no-instanceof-builtins.js +73 -14
  52. package/rules/no-invalid-argument-count.js +110 -54
  53. package/rules/no-invalid-character-comparison.js +121 -0
  54. package/rules/no-invalid-fetch-options.js +3 -1
  55. package/rules/no-invalid-well-known-symbol-methods.js +205 -0
  56. package/rules/no-keyword-prefix.js +14 -4
  57. package/rules/no-late-current-target-access.js +13 -107
  58. package/rules/no-late-event-control.js +127 -0
  59. package/rules/no-loop-iterable-mutation.js +541 -0
  60. package/rules/no-manually-wrapped-comments.js +10 -2
  61. package/rules/no-misrefactored-assignment.js +102 -0
  62. package/rules/no-named-default.js +35 -2
  63. package/rules/no-nonstandard-builtin-properties.js +1383 -0
  64. package/rules/no-object-methods-with-collections.js +4 -289
  65. package/rules/no-process-exit.js +16 -12
  66. package/rules/no-redundant-comparison.js +5 -12
  67. package/rules/no-return-array-push.js +20 -5
  68. package/rules/no-selector-as-dom-name.js +150 -0
  69. package/rules/no-thenable.js +27 -11
  70. package/rules/no-top-level-side-effects.js +8 -1
  71. package/rules/no-uncalled-method.js +2 -8
  72. package/rules/no-undeclared-class-members.js +8 -6
  73. package/rules/no-unnecessary-boolean-comparison.js +131 -0
  74. package/rules/no-unnecessary-global-this.js +36 -1
  75. package/rules/no-unnecessary-splice.js +15 -20
  76. package/rules/no-unreadable-for-of-expression.js +2 -1
  77. package/rules/no-unreadable-object-destructuring.js +15 -0
  78. package/rules/no-unsafe-property-key.js +10 -15
  79. package/rules/no-unsafe-string-replacement.js +31 -0
  80. package/rules/no-unused-array-method-return.js +11 -2
  81. package/rules/no-unused-properties.js +4 -3
  82. package/rules/no-useless-boolean-cast.js +58 -1
  83. package/rules/no-useless-coercion.js +71 -15
  84. package/rules/no-useless-compound-assignment.js +110 -0
  85. package/rules/no-useless-concat.js +27 -3
  86. package/rules/no-useless-delete-check.js +353 -0
  87. package/rules/no-useless-else.js +27 -62
  88. package/rules/no-useless-error-capture-stack-trace.js +1 -1
  89. package/rules/no-useless-fallback-in-spread.js +7 -118
  90. package/rules/no-useless-iterator-to-array.js +165 -115
  91. package/rules/no-useless-logical-operand.js +248 -0
  92. package/rules/no-useless-undefined.js +11 -28
  93. package/rules/no-xor-as-exponentiation.js +59 -0
  94. package/rules/prefer-abort-signal-timeout.js +395 -0
  95. package/rules/prefer-aggregate-error.js +478 -0
  96. package/rules/prefer-array-find.js +95 -5
  97. package/rules/prefer-array-flat-map.js +9 -21
  98. package/rules/prefer-array-flat.js +1 -24
  99. package/rules/prefer-array-from-async.js +273 -0
  100. package/rules/prefer-array-iterable-methods.js +157 -0
  101. package/rules/prefer-array-some.js +56 -31
  102. package/rules/prefer-at.js +6 -30
  103. package/rules/prefer-await.js +29 -0
  104. package/rules/prefer-boolean-return.js +224 -0
  105. package/rules/prefer-code-point.js +58 -8
  106. package/rules/prefer-continue.js +377 -0
  107. package/rules/prefer-default-parameters.js +10 -6
  108. package/rules/prefer-dispose.js +1 -5
  109. package/rules/prefer-dom-node-replace-children.js +353 -0
  110. package/rules/prefer-early-return.js +160 -30
  111. package/rules/prefer-else-if.js +50 -53
  112. package/rules/prefer-error-is-error.js +181 -0
  113. package/rules/prefer-event-target.js +3 -13
  114. package/rules/prefer-flat-math-min-max.js +103 -0
  115. package/rules/prefer-global-this.js +1 -1
  116. package/rules/prefer-has-check.js +1 -4
  117. package/rules/prefer-hoisting-branch-code.js +424 -0
  118. package/rules/prefer-identifier-import-export-specifiers.js +6 -11
  119. package/rules/prefer-includes-over-repeated-comparisons.js +5 -1
  120. package/rules/prefer-includes.js +6 -0
  121. package/rules/prefer-iterator-concat.js +21 -1
  122. package/rules/prefer-logical-operator-over-ternary.js +12 -20
  123. package/rules/prefer-math-constants.js +103 -0
  124. package/rules/prefer-math-min-max.js +1 -5
  125. package/rules/prefer-minimal-ternary.js +41 -13
  126. package/rules/prefer-modern-dom-apis.js +1 -45
  127. package/rules/prefer-node-protocol.js +1 -0
  128. package/rules/prefer-number-properties.js +2 -5
  129. package/rules/prefer-object-from-entries.js +6 -1
  130. package/rules/prefer-object-iterable-methods.js +36 -11
  131. package/rules/prefer-observer-apis.js +498 -0
  132. package/rules/prefer-optional-catch-binding.js +30 -24
  133. package/rules/prefer-private-class-fields.js +2 -8
  134. package/rules/prefer-promise-try.js +238 -0
  135. package/rules/prefer-promise-with-resolvers.js +248 -0
  136. package/rules/prefer-query-selector.js +79 -17
  137. package/rules/prefer-queue-microtask.js +7 -9
  138. package/rules/prefer-regexp-escape.js +263 -0
  139. package/rules/prefer-set-methods.js +337 -0
  140. package/rules/prefer-set-size.js +10 -2
  141. package/rules/prefer-short-arrow-method.js +3 -3
  142. package/rules/prefer-single-replace.js +218 -0
  143. package/rules/prefer-spread.js +14 -21
  144. package/rules/prefer-string-replace-all.js +5 -0
  145. package/rules/prefer-structured-clone.js +2 -2
  146. package/rules/prefer-switch.js +1 -0
  147. package/rules/prefer-ternary.js +4 -2
  148. package/rules/prefer-toggle-attribute.js +293 -0
  149. package/rules/prefer-type-literal-last.js +22 -54
  150. package/rules/prefer-unary-minus.js +103 -0
  151. package/rules/prefer-url-can-parse.js +308 -0
  152. package/rules/prefer-url-search-parameters.js +411 -0
  153. package/rules/prefer-while-loop-condition.js +252 -0
  154. package/rules/require-passive-events.js +11 -4
  155. package/rules/require-proxy-trap-boolean-return.js +40 -126
  156. package/rules/rule/to-eslint-listener.js +5 -3
  157. package/rules/shared/array-concat-in-loop.js +151 -0
  158. package/rules/shared/identifier-checks.js +4 -16
  159. package/rules/shared/late-event-handler.js +82 -0
  160. package/rules/shared/{abbreviations.js → name-replacements.js} +63 -0
  161. package/rules/shared/no-array-mutate-rule.js +19 -2
  162. package/rules/shared/regexp-escape.js +81 -0
  163. package/rules/shared/splice-replacements.js +1 -18
  164. package/rules/string-content.js +11 -4
  165. package/rules/template-indent.js +2 -8
  166. package/rules/throw-new-error.js +7 -0
  167. package/rules/utils/block-scope.js +42 -0
  168. package/rules/utils/builtin-collection-type.js +267 -0
  169. package/rules/utils/builtins.js +50 -1
  170. package/rules/utils/comments.js +16 -0
  171. package/rules/utils/comparison.js +17 -0
  172. package/rules/utils/contains-suspension-point.js +35 -0
  173. package/rules/utils/get-const-variable-initializer.js +31 -0
  174. package/rules/utils/global-reference-tracker.js +14 -1
  175. package/rules/utils/has-optional-chain-element.js +36 -4
  176. package/rules/utils/index.js +38 -2
  177. package/rules/utils/is-boolean.js +26 -1
  178. package/rules/utils/is-event.js +127 -0
  179. package/rules/utils/is-function-self-used-inside.js +1 -5
  180. package/rules/utils/is-weak-map.js +21 -0
  181. package/rules/utils/is-weak-set.js +21 -0
  182. package/rules/utils/length-or-size.js +53 -0
  183. package/rules/utils/member-expression.js +18 -0
  184. package/rules/utils/needs-semicolon.js +1 -5
  185. package/rules/utils/numeric.js +20 -0
  186. package/rules/utils/should-add-parentheses-to-logical-expression-child.js +8 -14
  187. package/rules/utils/should-add-parentheses-to-member-expression-object.js +1 -5
  188. package/rules/utils/should-add-parentheses-to-unary-expression.js +8 -2
  189. package/rules/utils/should-report-replace-children-receiver.js +210 -0
  190. package/rules/utils/track-branch-exits.js +79 -0
  191. package/rules/utils/type-helpers.js +166 -8
  192. package/rules/utils/types.js +22 -0
@@ -0,0 +1,569 @@
1
+ import {findVariable} from '@eslint-community/eslint-utils';
2
+ import {
3
+ getComments,
4
+ getParenthesizedText,
5
+ hasUnsafeArrowConversionReference,
6
+ } from './utils/index.js';
7
+
8
+ const MESSAGE_ID_INLINE = 'default-export-style/inline';
9
+ const MESSAGE_ID_SEPARATE = 'default-export-style/separate';
10
+ const MESSAGE_ID_SUGGESTION_INLINE = 'default-export-style/suggestion-inline';
11
+ const MESSAGE_ID_SUGGESTION_SEPARATE = 'default-export-style/suggestion-separate';
12
+
13
+ const STYLE_INLINE = 'inline';
14
+ const STYLE_SEPARATE = 'separate';
15
+ const STYLE_IGNORE = 'ignore';
16
+
17
+ const KIND_CLASS = 'class';
18
+ const KIND_FUNCTION = 'function';
19
+
20
+ const messages = {
21
+ [MESSAGE_ID_INLINE]: 'Prefer declaring this default-exported {{kind}} inline.',
22
+ [MESSAGE_ID_SEPARATE]: 'Prefer declaring this default-exported {{kind}} as a separate {{style}}.',
23
+ [MESSAGE_ID_SUGGESTION_INLINE]: 'Use an inline default {{kind}} declaration.',
24
+ [MESSAGE_ID_SUGGESTION_SEPARATE]: 'Use a separate const arrow function default export.',
25
+ };
26
+
27
+ const defaultOptions = {
28
+ functions: STYLE_INLINE,
29
+ classes: STYLE_INLINE,
30
+ };
31
+
32
+ const createStyleSchema = () => ({
33
+ enum: [
34
+ STYLE_INLINE,
35
+ STYLE_SEPARATE,
36
+ STYLE_IGNORE,
37
+ ],
38
+ });
39
+
40
+ const isExportedIdentifier = (exportDeclaration, name) =>
41
+ exportDeclaration?.type === 'ExportDefaultDeclaration'
42
+ && exportDeclaration.declaration.type === 'Identifier'
43
+ && exportDeclaration.declaration.name === name;
44
+
45
+ const getName = node => node.id?.name;
46
+
47
+ const getKind = node => {
48
+ switch (node.type) {
49
+ case 'ClassDeclaration': {
50
+ return KIND_CLASS;
51
+ }
52
+
53
+ case 'FunctionDeclaration': {
54
+ return KIND_FUNCTION;
55
+ }
56
+
57
+ // No default
58
+ }
59
+ };
60
+
61
+ const getOptionKey = kind => kind === KIND_CLASS ? 'classes' : 'functions';
62
+
63
+ const hasCommentsBetween = (context, left, right) => {
64
+ const [, start] = context.sourceCode.getRange(left);
65
+ const [end] = context.sourceCode.getRange(right);
66
+
67
+ return getComments(context).some(comment => {
68
+ const [commentStart, commentEnd] = context.sourceCode.getRange(comment);
69
+ return commentStart >= start && commentEnd <= end;
70
+ });
71
+ };
72
+
73
+ const hasTrailingComment = (context, node) => {
74
+ const [, end] = context.sourceCode.getRange(node);
75
+ const nodeEndLine = context.sourceCode.getLoc(node).end.line;
76
+
77
+ return getComments(context).some(comment =>
78
+ context.sourceCode.getRange(comment)[0] >= end
79
+ && context.sourceCode.getLoc(comment).start.line === nodeEndLine,
80
+ );
81
+ };
82
+
83
+ const isBindingReassigned = (scope, identifier) =>
84
+ findVariable(scope, identifier)?.references.some(reference =>
85
+ !reference.init && reference.isWrite()) ?? false;
86
+
87
+ const hasTypeScriptSyntax = node => {
88
+ if (!node) {
89
+ return false;
90
+ }
91
+
92
+ return Boolean(
93
+ node.type.startsWith('TS')
94
+ || node.typeAnnotation
95
+ || node.typeParameters
96
+ || node.returnType
97
+ || node.implements?.length > 0
98
+ || node.superTypeArguments
99
+ || node.accessibility
100
+ || node.readonly
101
+ || node.override
102
+ || node.definite
103
+ || (
104
+ node.optional
105
+ && node.type !== 'MemberExpression'
106
+ && node.type !== 'CallExpression'
107
+ )
108
+ || node.abstract
109
+ || node.declare
110
+ || node.decorators?.length > 0,
111
+ );
112
+ };
113
+
114
+ const hasTypeScriptSyntaxInTree = (node, visitorKeys) => {
115
+ if (hasTypeScriptSyntax(node)) {
116
+ return true;
117
+ }
118
+
119
+ for (const key of visitorKeys[node.type] ?? []) {
120
+ const value = node[key];
121
+
122
+ if (Array.isArray(value)) {
123
+ if (value.some(child => child?.type && hasTypeScriptSyntaxInTree(child, visitorKeys))) {
124
+ return true;
125
+ }
126
+
127
+ continue;
128
+ }
129
+
130
+ if (value?.type && hasTypeScriptSyntaxInTree(value, visitorKeys)) {
131
+ return true;
132
+ }
133
+ }
134
+
135
+ return false;
136
+ };
137
+
138
+ const hasSafeParametersForArrowConversion = (functionNode, context) =>
139
+ functionNode.params.every(parameter => !hasUnsafeArrowConversionReference(parameter, context.sourceCode.visitorKeys));
140
+
141
+ const hasOtherReferences = (scope, identifier, allowedReferenceIdentifier) =>
142
+ findVariable(scope, identifier)?.references.some(reference =>
143
+ !reference.init
144
+ && reference.identifier !== allowedReferenceIdentifier,
145
+ ) ?? false;
146
+
147
+ const hasCommentsOutsideFunction = (context, node, functionNode) =>
148
+ context.sourceCode.getCommentsInside(node).length > context.sourceCode.getCommentsInside(functionNode).length;
149
+
150
+ const getVariableArrowFunction = (node, context) => {
151
+ if (
152
+ node.type !== 'VariableDeclaration'
153
+ || node.kind !== 'const'
154
+ || node.declarations.length !== 1
155
+ ) {
156
+ return;
157
+ }
158
+
159
+ const [declarator] = node.declarations;
160
+
161
+ if (
162
+ declarator.id.type !== 'Identifier'
163
+ || hasTypeScriptSyntax(declarator.id)
164
+ || declarator.init?.type !== 'ArrowFunctionExpression'
165
+ || hasTypeScriptSyntaxInTree(declarator.init, context.sourceCode.visitorKeys)
166
+ ) {
167
+ return;
168
+ }
169
+
170
+ return declarator;
171
+ };
172
+
173
+ const getSeparateDeclaration = (statement, context) => {
174
+ const name = getName(statement);
175
+
176
+ if (name) {
177
+ const kind = getKind(statement);
178
+ return kind && {kind, name, declaration: statement};
179
+ }
180
+
181
+ const arrowFunctionDeclarator = getVariableArrowFunction(statement, context);
182
+
183
+ if (arrowFunctionDeclarator) {
184
+ return {
185
+ kind: KIND_FUNCTION,
186
+ name: arrowFunctionDeclarator.id.name,
187
+ declaration: statement,
188
+ functionNode: arrowFunctionDeclarator.init,
189
+ };
190
+ }
191
+ };
192
+
193
+ const canConvertFunctionToArrow = (functionNode, context) =>
194
+ functionNode.type === 'FunctionDeclaration'
195
+ && !functionNode.generator
196
+ && !hasTypeScriptSyntaxInTree(functionNode, context.sourceCode.visitorKeys)
197
+ && context.sourceCode.getCommentsInside(functionNode).length === 0
198
+ && hasSafeParametersForArrowConversion(functionNode, context)
199
+ && !hasUnsafeArrowConversionReference(functionNode.body, context.sourceCode.visitorKeys);
200
+
201
+ const canConvertArrowToFunction = (functionNode, context) =>
202
+ functionNode.type === 'ArrowFunctionExpression'
203
+ && !hasTypeScriptSyntaxInTree(functionNode, context.sourceCode.visitorKeys)
204
+ && context.sourceCode.getCommentsInside(functionNode).length === 0
205
+ && hasSafeParametersForArrowConversion(functionNode, context)
206
+ && !hasUnsafeArrowConversionReference(functionNode.body, context.sourceCode.visitorKeys);
207
+
208
+ const getParametersText = (functionNode, sourceCode) =>
209
+ functionNode.params.map(parameter => sourceCode.getText(parameter)).join(', ');
210
+
211
+ const getArrowFunctionBodyText = (functionNode, context) =>
212
+ functionNode.body.type === 'BlockStatement'
213
+ ? context.sourceCode.getText(functionNode.body)
214
+ : `{ return ${getParenthesizedText(functionNode.body, context)}; }`;
215
+
216
+ const getInlineFunctionText = (name, functionNode, context) =>
217
+ `export default ${functionNode.async ? 'async ' : ''}function ${name}(${getParametersText(functionNode, context.sourceCode)}) ${getArrowFunctionBodyText(functionNode, context)}`;
218
+
219
+ const getSeparateArrowFunctionText = (name, functionNode, context) =>
220
+ `const ${name} = ${functionNode.async ? 'async ' : ''}(${getParametersText(functionNode, context.sourceCode)}) => ${context.sourceCode.getText(functionNode.body)};\nexport default ${name};`;
221
+
222
+ const fixSeparateToInline = (declaration, exportDeclaration, context) => function * (fixer) {
223
+ yield fixer.insertTextBefore(declaration, 'export default ');
224
+ yield fixer.removeRange([
225
+ context.sourceCode.getRange(declaration)[1],
226
+ context.sourceCode.getRange(exportDeclaration)[1],
227
+ ]);
228
+ };
229
+
230
+ const fixInlineClassToSeparate = (exportDeclaration, context) => fixer => {
231
+ const {declaration} = exportDeclaration;
232
+ return fixer.replaceText(
233
+ exportDeclaration,
234
+ `${context.sourceCode.getText(declaration)}\nexport default ${declaration.id.name};`,
235
+ );
236
+ };
237
+
238
+ const suggestArrowToInlineFunction = ({
239
+ context,
240
+ declaration,
241
+ exportDeclaration,
242
+ name,
243
+ scope,
244
+ }) => {
245
+ const [{id, init: functionNode}] = declaration.declarations;
246
+
247
+ if (
248
+ context.sourceCode.getCommentsInside(exportDeclaration).length > 0
249
+ || hasTrailingComment(context, exportDeclaration)
250
+ || hasCommentsOutsideFunction(context, declaration, functionNode)
251
+ || hasOtherReferences(scope, id, exportDeclaration.declaration)
252
+ || !canConvertArrowToFunction(functionNode, context)
253
+ ) {
254
+ return;
255
+ }
256
+
257
+ return [
258
+ {
259
+ messageId: MESSAGE_ID_SUGGESTION_INLINE,
260
+ data: {
261
+ kind: KIND_FUNCTION,
262
+ },
263
+ fix: fixer => fixer.replaceTextRange(
264
+ [
265
+ context.sourceCode.getRange(declaration)[0],
266
+ context.sourceCode.getRange(exportDeclaration)[1],
267
+ ],
268
+ getInlineFunctionText(name, functionNode, context),
269
+ ),
270
+ },
271
+ ];
272
+ };
273
+
274
+ const suggestFunctionToSeparateArrow = ({
275
+ context,
276
+ declaration,
277
+ exportDeclaration,
278
+ name,
279
+ scope,
280
+ rangeStartNode = declaration,
281
+ }) => {
282
+ if (
283
+ context.sourceCode.getCommentsInside(exportDeclaration).length > context.sourceCode.getCommentsInside(declaration).length
284
+ || hasOtherReferences(
285
+ scope,
286
+ declaration.id,
287
+ exportDeclaration.declaration.type === 'Identifier' ? exportDeclaration.declaration : undefined,
288
+ )
289
+ || !canConvertFunctionToArrow(declaration, context)
290
+ ) {
291
+ return;
292
+ }
293
+
294
+ return [
295
+ {
296
+ messageId: MESSAGE_ID_SUGGESTION_SEPARATE,
297
+ fix: fixer => fixer.replaceTextRange(
298
+ [
299
+ context.sourceCode.getRange(rangeStartNode)[0],
300
+ context.sourceCode.getRange(exportDeclaration)[1],
301
+ ],
302
+ getSeparateArrowFunctionText(name, declaration, context),
303
+ ),
304
+ },
305
+ ];
306
+ };
307
+
308
+ const reportSeparateDeclaration = ({
309
+ context,
310
+ declaration,
311
+ exportDeclaration,
312
+ expectedStyle,
313
+ kind,
314
+ name,
315
+ scope,
316
+ }) => {
317
+ if (
318
+ expectedStyle === STYLE_IGNORE
319
+ || hasCommentsBetween(context, declaration, exportDeclaration)
320
+ || isBindingReassigned(scope, exportDeclaration.declaration)
321
+ ) {
322
+ return;
323
+ }
324
+
325
+ if (
326
+ kind === KIND_FUNCTION
327
+ && declaration.type === 'FunctionDeclaration'
328
+ && declaration.generator
329
+ ) {
330
+ return;
331
+ }
332
+
333
+ if (
334
+ kind === KIND_FUNCTION
335
+ && declaration.type === 'FunctionDeclaration'
336
+ && hasTypeScriptSyntaxInTree(declaration, context.sourceCode.visitorKeys)
337
+ ) {
338
+ return;
339
+ }
340
+
341
+ if (expectedStyle === STYLE_INLINE) {
342
+ if (declaration.type === 'VariableDeclaration') {
343
+ return {
344
+ node: exportDeclaration,
345
+ messageId: MESSAGE_ID_INLINE,
346
+ data: {kind},
347
+ suggest: suggestArrowToInlineFunction({
348
+ context,
349
+ declaration,
350
+ exportDeclaration,
351
+ name,
352
+ scope,
353
+ }),
354
+ };
355
+ }
356
+
357
+ if (
358
+ kind === KIND_CLASS
359
+ && hasTypeScriptSyntaxInTree(declaration, context.sourceCode.visitorKeys)
360
+ ) {
361
+ return;
362
+ }
363
+
364
+ if (
365
+ context.sourceCode.getCommentsInside(exportDeclaration).length > 0
366
+ || hasTrailingComment(context, exportDeclaration)
367
+ ) {
368
+ return {
369
+ node: exportDeclaration,
370
+ messageId: MESSAGE_ID_INLINE,
371
+ data: {kind},
372
+ };
373
+ }
374
+
375
+ return {
376
+ node: exportDeclaration,
377
+ messageId: MESSAGE_ID_INLINE,
378
+ data: {kind},
379
+ fix: fixSeparateToInline(declaration, exportDeclaration, context),
380
+ };
381
+ }
382
+
383
+ if (
384
+ expectedStyle === STYLE_SEPARATE
385
+ && kind === KIND_FUNCTION
386
+ && declaration.type === 'FunctionDeclaration'
387
+ ) {
388
+ return {
389
+ node: exportDeclaration,
390
+ messageId: MESSAGE_ID_SEPARATE,
391
+ data: {
392
+ kind,
393
+ style: 'const arrow function',
394
+ },
395
+ suggest: suggestFunctionToSeparateArrow({
396
+ context,
397
+ declaration,
398
+ exportDeclaration,
399
+ name,
400
+ scope,
401
+ }),
402
+ };
403
+ }
404
+ };
405
+
406
+ const reportInlineDeclaration = ({
407
+ context,
408
+ exportDeclaration,
409
+ expectedStyle,
410
+ kind,
411
+ }) => {
412
+ const {declaration} = exportDeclaration;
413
+ const name = getName(declaration);
414
+ const exportScope = context.sourceCode.getScope(exportDeclaration);
415
+
416
+ if (
417
+ !name
418
+ || expectedStyle === STYLE_IGNORE
419
+ || expectedStyle === STYLE_INLINE
420
+ || isBindingReassigned(exportScope, name)
421
+ ) {
422
+ return;
423
+ }
424
+
425
+ if (
426
+ kind === KIND_FUNCTION
427
+ && (
428
+ declaration.generator
429
+ || hasTypeScriptSyntaxInTree(declaration, context.sourceCode.visitorKeys)
430
+ )
431
+ ) {
432
+ return;
433
+ }
434
+
435
+ if (kind === KIND_FUNCTION) {
436
+ return {
437
+ node: exportDeclaration,
438
+ messageId: MESSAGE_ID_SEPARATE,
439
+ data: {
440
+ kind,
441
+ style: 'const arrow function',
442
+ },
443
+ suggest: suggestFunctionToSeparateArrow({
444
+ context,
445
+ declaration,
446
+ exportDeclaration,
447
+ name,
448
+ scope: exportScope,
449
+ rangeStartNode: exportDeclaration,
450
+ }),
451
+ };
452
+ }
453
+
454
+ if (
455
+ kind === KIND_CLASS
456
+ && hasTypeScriptSyntaxInTree(declaration, context.sourceCode.visitorKeys)
457
+ ) {
458
+ return;
459
+ }
460
+
461
+ if (
462
+ context.sourceCode.getCommentsInside(exportDeclaration).length
463
+ > context.sourceCode.getCommentsInside(declaration).length
464
+ ) {
465
+ return {
466
+ node: exportDeclaration,
467
+ messageId: MESSAGE_ID_SEPARATE,
468
+ data: {
469
+ kind,
470
+ style: 'class declaration',
471
+ },
472
+ };
473
+ }
474
+
475
+ return {
476
+ node: exportDeclaration,
477
+ messageId: MESSAGE_ID_SEPARATE,
478
+ data: {
479
+ kind,
480
+ style: 'class declaration',
481
+ },
482
+ fix: fixInlineClassToSeparate(exportDeclaration, context),
483
+ };
484
+ };
485
+
486
+ /** @param {import('eslint').Rule.RuleContext} context */
487
+ const create = context => {
488
+ const options = context.options[0];
489
+
490
+ context.on('Program', program => {
491
+ for (const [index, statement] of program.body.entries()) {
492
+ if (statement.type === 'ExportDefaultDeclaration') {
493
+ const {declaration} = statement;
494
+ const kind = getKind(declaration);
495
+ const name = getName(declaration);
496
+
497
+ if (!kind || !name) {
498
+ continue;
499
+ }
500
+
501
+ const problem = reportInlineDeclaration({
502
+ context,
503
+ exportDeclaration: statement,
504
+ expectedStyle: options[getOptionKey(kind)],
505
+ kind,
506
+ });
507
+
508
+ if (problem) {
509
+ return problem;
510
+ }
511
+
512
+ continue;
513
+ }
514
+
515
+ const nextStatement = program.body[index + 1];
516
+ const separateDeclaration = getSeparateDeclaration(statement, context);
517
+
518
+ if (
519
+ !separateDeclaration
520
+ || !isExportedIdentifier(nextStatement, separateDeclaration.name)
521
+ ) {
522
+ continue;
523
+ }
524
+
525
+ const problem = reportSeparateDeclaration({
526
+ context,
527
+ exportDeclaration: nextStatement,
528
+ expectedStyle: options[getOptionKey(separateDeclaration.kind)],
529
+ scope: context.sourceCode.getScope(nextStatement),
530
+ ...separateDeclaration,
531
+ });
532
+
533
+ if (problem) {
534
+ return problem;
535
+ }
536
+ }
537
+ });
538
+ };
539
+
540
+ /** @type {import('eslint').Rule.RuleModule} */
541
+ const config = {
542
+ create,
543
+ meta: {
544
+ type: 'suggestion',
545
+ docs: {
546
+ description: 'Enforce consistent default export declarations.',
547
+ recommended: true,
548
+ },
549
+ fixable: 'code',
550
+ hasSuggestions: true,
551
+ schema: [
552
+ {
553
+ type: 'object',
554
+ properties: {
555
+ functions: createStyleSchema(),
556
+ classes: createStyleSchema(),
557
+ },
558
+ additionalProperties: false,
559
+ },
560
+ ],
561
+ defaultOptions: [defaultOptions],
562
+ messages,
563
+ languages: [
564
+ 'js/js',
565
+ ],
566
+ },
567
+ };
568
+
569
+ export default config;
@@ -210,7 +210,7 @@ function getPackageHelpers(dirname) {
210
210
  const DEPENDENCY_INCLUSION_RE = /^[+-]\s*\S{2,}/;
211
211
  const VERSION_COMPARISON_RE = /^(?<name>\S{2,})@(?<condition>>|>=)(?<version>\d+(?:\.\d+){0,2}(?:-[\d\-a-z]+(?:\.[\d\-a-z]+)*)?(?:\+[\d\-a-z]+(?:\.[\d\-a-z]+)*)?)/i;
212
212
  const PKG_VERSION_RE = /^(?<condition>>|>=)(?<version>\d+(?:\.\d+){0,2}(?:-[\d\-a-z]+(?:\.[\d\-a-z]+)*)?(?:\+[\d\-a-z]+(?:\.[\d\-a-z]+)*)?)\s*$/;
213
- const ISO8601_DATE = /\d{4}-\d{2}-\d{2}/;
213
+ const ISO8601_DATE = /^\d{4}-\d{2}-\d{2}$/;
214
214
 
215
215
  function createArgumentGroup(arguments_) {
216
216
  const groups = {};
@@ -1,4 +1,3 @@
1
- import {getStaticValue} from '@eslint-community/eslint-utils';
2
1
  import {
3
2
  isParenthesized,
4
3
  checkVueTemplate,
@@ -9,9 +8,12 @@ import {
9
8
  isSameReference,
10
9
  isTypeScriptExpressionWrapper,
11
10
  unwrapTypeScriptExpression,
11
+ hasSameObjectShapePropertyCheck,
12
+ isKnownNonCollectionLengthOrSize,
13
+ isLengthOrSizeMemberExpression,
12
14
  } from './utils/index.js';
13
15
  import {fixSpaceAroundKeyword} from './fix/index.js';
14
- import {isLiteral, isMemberExpression} from './ast/index.js';
16
+ import {isLiteral} from './ast/index.js';
15
17
 
16
18
  const TYPE_NON_ZERO = 'non-zero';
17
19
  const TYPE_ZERO = 'zero';
@@ -51,15 +53,6 @@ const zeroStyle = {
51
53
  test: node => isCompareRight(node, '===', 0),
52
54
  };
53
55
 
54
- const shapeProperties = new Set(['depth', 'height', 'width']);
55
-
56
- function isLengthOrSizeMemberExpression(node) {
57
- return isMemberExpression(node, {
58
- properties: ['length', 'size'],
59
- optional: false,
60
- });
61
- }
62
-
63
56
  function getLengthCheckParent(node, allowTypeScriptExpression) {
64
57
  node = node.parent;
65
58
  if (allowTypeScriptExpression) {
@@ -89,23 +82,6 @@ function getLogicalExpressionOperands(node) {
89
82
  : [child]);
90
83
  }
91
84
 
92
- function hasSameObjectShapePropertyCheck({node, lengthNode}) {
93
- const root = getLogicalExpressionRoot(node);
94
- if (
95
- root.type !== 'LogicalExpression'
96
- || root.operator !== '&&'
97
- ) {
98
- return false;
99
- }
100
-
101
- return getLogicalExpressionOperands(root).some(operand =>
102
- operand !== node
103
- && isMemberExpression(operand, {computed: false, optional: false})
104
- && operand.property.type === 'Identifier'
105
- && shapeProperties.has(operand.property.name)
106
- && isSameReference(operand.object, lengthNode.object));
107
- }
108
-
109
85
  function getLengthCheckMemberExpression(node) {
110
86
  if (node.type === 'UnaryExpression' && node.operator === '!') {
111
87
  return getLengthCheckMemberExpression(node.argument);
@@ -268,9 +244,8 @@ function create(context) {
268
244
  }
269
245
 
270
246
  const lengthNode = memberExpression;
271
- const staticValue = getStaticValue(lengthNode, sourceCode.getScope(lengthNode));
272
- if (staticValue && (!Number.isSafeInteger(staticValue.value) || staticValue.value < 0)) {
273
- // Ignore known, non-positive-integer length properties.
247
+ if (isKnownNonCollectionLengthOrSize(lengthNode, context)) {
248
+ // Ignore known non-cardinality length or size properties.
274
249
  return;
275
250
  }
276
251
 
@@ -298,7 +273,7 @@ function create(context) {
298
273
  if (node) {
299
274
  if (
300
275
  (node === lengthNode && isLengthGuardedByNonZeroCheck(lengthNode, context))
301
- || hasSameObjectShapePropertyCheck({node, lengthNode})
276
+ || hasSameObjectShapePropertyCheck({node, lengthOrSizeNode: lengthNode})
302
277
  ) {
303
278
  return;
304
279
  }
@@ -53,14 +53,16 @@ const getActualExportDeclarationStyles = exportDeclaration => {
53
53
  const styles = new Set();
54
54
 
55
55
  for (const specifier of specifiers) {
56
- if (specifier.type === 'ExportSpecifier') {
57
- if (specifier.exported.type === 'Identifier' && specifier.exported.name === 'default') {
58
- styles.add('default');
59
- continue;
60
- }
56
+ if (specifier.type !== 'ExportSpecifier') {
57
+ continue;
58
+ }
61
59
 
62
- styles.add('named');
60
+ if (specifier.exported.type === 'Identifier' && specifier.exported.name === 'default') {
61
+ styles.add('default');
62
+ continue;
63
63
  }
64
+
65
+ styles.add('named');
64
66
  }
65
67
 
66
68
  return [...styles];