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,83 @@
1
+ 'use strict';
2
+ const {isParenthesized} = require('@eslint-community/eslint-utils');
3
+ const shouldAddParenthesesToMemberExpressionObject = require('./utils/should-add-parentheses-to-member-expression-object.js');
4
+ const {fixSpaceAroundKeyword} = require('./fix/index.js');
5
+
6
+ const MESSAGE_ID = 'no-unreadable-array-destructuring';
7
+ const messages = {
8
+ [MESSAGE_ID]: 'Array destructuring may not contain consecutive ignored values.',
9
+ };
10
+
11
+ const isCommaFollowedWithComma = (element, index, array) =>
12
+ element === null && array[index + 1] === null;
13
+
14
+ /** @param {import('eslint').Rule.RuleContext} context */
15
+ const create = context => {
16
+ const {sourceCode} = context;
17
+
18
+ return {
19
+ ArrayPattern(node) {
20
+ const {elements, parent} = node;
21
+
22
+ if (
23
+ elements.length < 3
24
+ || !elements.some((element, index, elements) => isCommaFollowedWithComma(element, index, elements))) {
25
+ return;
26
+ }
27
+
28
+ const problem = {
29
+ node,
30
+ messageId: MESSAGE_ID,
31
+ };
32
+
33
+ const nonNullElements = elements.filter(node => node !== null);
34
+ if (
35
+ parent.type === 'VariableDeclarator'
36
+ && parent.id === node
37
+ && parent.init !== null
38
+ && nonNullElements.length === 1
39
+ ) {
40
+ const [element] = nonNullElements;
41
+
42
+ if (element.type !== 'AssignmentPattern') {
43
+ problem.fix = function * (fixer) {
44
+ const index = elements.indexOf(element);
45
+ const isSlice = element.type === 'RestElement';
46
+ const variable = isSlice ? element.argument : element;
47
+
48
+ yield fixer.replaceText(node, sourceCode.getText(variable));
49
+
50
+ const code = isSlice ? `.slice(${index})` : `[${index}]`;
51
+ const array = parent.init;
52
+ if (
53
+ !isParenthesized(array, sourceCode)
54
+ && shouldAddParenthesesToMemberExpressionObject(array, sourceCode)
55
+ ) {
56
+ yield fixer.insertTextBefore(array, '(');
57
+ yield fixer.insertTextAfter(parent, `)${code}`);
58
+ } else {
59
+ yield fixer.insertTextAfter(parent, code);
60
+ }
61
+
62
+ yield * fixSpaceAroundKeyword(fixer, node, sourceCode);
63
+ };
64
+ }
65
+ }
66
+
67
+ return problem;
68
+ },
69
+ };
70
+ };
71
+
72
+ /** @type {import('eslint').Rule.RuleModule} */
73
+ module.exports = {
74
+ create,
75
+ meta: {
76
+ type: 'suggestion',
77
+ docs: {
78
+ description: 'Disallow unreadable array destructuring.',
79
+ },
80
+ fixable: 'code',
81
+ messages,
82
+ },
83
+ };
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+ const {
3
+ isParenthesized,
4
+ getParenthesizedRange,
5
+ toLocation,
6
+ } = require('./utils/index.js');
7
+
8
+ const MESSAGE_ID_ERROR = 'no-unreadable-iife';
9
+ const messages = {
10
+ [MESSAGE_ID_ERROR]: 'IIFE with parenthesized arrow function body is considered unreadable.',
11
+ };
12
+
13
+ /** @param {import('eslint').Rule.RuleContext} context */
14
+ const create = context => ({
15
+ CallExpression(callExpression) {
16
+ const {sourceCode} = context;
17
+
18
+ if (
19
+ callExpression.callee.type !== 'ArrowFunctionExpression'
20
+ || callExpression.callee.body.type === 'BlockStatement'
21
+ || !isParenthesized(callExpression.callee.body, sourceCode)
22
+ ) {
23
+ return;
24
+ }
25
+
26
+ return {
27
+ node: callExpression,
28
+ loc: toLocation(getParenthesizedRange(callExpression.callee.body, sourceCode), sourceCode),
29
+ messageId: MESSAGE_ID_ERROR,
30
+ };
31
+ },
32
+ });
33
+
34
+ /** @type {import('eslint').Rule.RuleModule} */
35
+ module.exports = {
36
+ create,
37
+ meta: {
38
+ type: 'suggestion',
39
+ docs: {
40
+ description: 'Disallow unreadable IIFEs.',
41
+ },
42
+ hasSuggestions: false,
43
+ messages,
44
+ },
45
+ };
@@ -0,0 +1,238 @@
1
+ 'use strict';
2
+ const getScopes = require('./utils/get-scopes.js');
3
+
4
+ const MESSAGE_ID = 'no-unused-properties';
5
+ const messages = {
6
+ [MESSAGE_ID]: 'Property `{{name}}` is defined but never used.',
7
+ };
8
+
9
+ const getDeclaratorOrPropertyValue = declaratorOrProperty =>
10
+ declaratorOrProperty.init
11
+ || declaratorOrProperty.value;
12
+
13
+ const isMemberExpressionCall = memberExpression =>
14
+ memberExpression.parent.type === 'CallExpression'
15
+ && memberExpression.parent.callee === memberExpression;
16
+
17
+ const isMemberExpressionAssignment = memberExpression =>
18
+ memberExpression.parent.type === 'AssignmentExpression';
19
+
20
+ const isMemberExpressionComputedBeyondPrediction = memberExpression =>
21
+ memberExpression.computed
22
+ && memberExpression.property.type !== 'Literal';
23
+
24
+ const specialProtoPropertyKey = {
25
+ type: 'Identifier',
26
+ name: '__proto__',
27
+ };
28
+
29
+ const propertyKeysEqual = (keyA, keyB) => {
30
+ if (keyA.type === 'Identifier') {
31
+ if (keyB.type === 'Identifier') {
32
+ return keyA.name === keyB.name;
33
+ }
34
+
35
+ if (keyB.type === 'Literal') {
36
+ return keyA.name === keyB.value;
37
+ }
38
+ }
39
+
40
+ if (keyA.type === 'Literal') {
41
+ if (keyB.type === 'Identifier') {
42
+ return keyA.value === keyB.name;
43
+ }
44
+
45
+ if (keyB.type === 'Literal') {
46
+ return keyA.value === keyB.value;
47
+ }
48
+ }
49
+
50
+ return false;
51
+ };
52
+
53
+ const objectPatternMatchesObjectExprPropertyKey = (pattern, key) =>
54
+ pattern.properties.some(property => {
55
+ if (property.type === 'RestElement') {
56
+ return true;
57
+ }
58
+
59
+ return propertyKeysEqual(property.key, key);
60
+ });
61
+
62
+ const isLeafDeclaratorOrProperty = declaratorOrProperty => {
63
+ const value = getDeclaratorOrPropertyValue(declaratorOrProperty);
64
+
65
+ if (!value) {
66
+ return true;
67
+ }
68
+
69
+ if (value.type !== 'ObjectExpression') {
70
+ return true;
71
+ }
72
+
73
+ return false;
74
+ };
75
+
76
+ const isUnusedVariable = variable => {
77
+ const hasReadReference = variable.references.some(reference => reference.isRead());
78
+ return !hasReadReference;
79
+ };
80
+
81
+ /** @param {import('eslint').Rule.RuleContext} context */
82
+ const create = context => {
83
+ const {sourceCode} = context;
84
+ const getPropertyDisplayName = property => {
85
+ if (property.key.type === 'Identifier') {
86
+ return property.key.name;
87
+ }
88
+
89
+ if (property.key.type === 'Literal') {
90
+ return property.key.value;
91
+ }
92
+
93
+ return sourceCode.getText(property.key);
94
+ };
95
+
96
+ const checkProperty = (property, references, path) => {
97
+ if (references.length === 0) {
98
+ context.report({
99
+ node: property,
100
+ messageId: MESSAGE_ID,
101
+ data: {
102
+ name: getPropertyDisplayName(property),
103
+ },
104
+ });
105
+ return;
106
+ }
107
+
108
+ checkObject(property, references, path);
109
+ };
110
+
111
+ const checkProperties = (objectExpression, references, path = []) => {
112
+ for (const property of objectExpression.properties) {
113
+ const {key} = property;
114
+
115
+ if (!key) {
116
+ continue;
117
+ }
118
+
119
+ if (propertyKeysEqual(key, specialProtoPropertyKey)) {
120
+ continue;
121
+ }
122
+
123
+ const nextPath = [...path, key];
124
+
125
+ const nextReferences = references
126
+ .map(reference => {
127
+ const {parent} = reference.identifier;
128
+
129
+ if (reference.init) {
130
+ if (
131
+ parent.type === 'VariableDeclarator'
132
+ && parent.parent.type === 'VariableDeclaration'
133
+ && parent.parent.parent.type === 'ExportNamedDeclaration'
134
+ ) {
135
+ return {identifier: parent};
136
+ }
137
+
138
+ return;
139
+ }
140
+
141
+ if (parent.type === 'MemberExpression') {
142
+ if (
143
+ isMemberExpressionAssignment(parent)
144
+ || isMemberExpressionCall(parent)
145
+ || isMemberExpressionComputedBeyondPrediction(parent)
146
+ || propertyKeysEqual(parent.property, key)
147
+ ) {
148
+ return {identifier: parent};
149
+ }
150
+
151
+ return;
152
+ }
153
+
154
+ if (
155
+ parent.type === 'VariableDeclarator'
156
+ && parent.id.type === 'ObjectPattern'
157
+ ) {
158
+ if (objectPatternMatchesObjectExprPropertyKey(parent.id, key)) {
159
+ return {identifier: parent};
160
+ }
161
+
162
+ return;
163
+ }
164
+
165
+ if (
166
+ parent.type === 'AssignmentExpression'
167
+ && parent.left.type === 'ObjectPattern'
168
+ ) {
169
+ if (objectPatternMatchesObjectExprPropertyKey(parent.left, key)) {
170
+ return {identifier: parent};
171
+ }
172
+
173
+ return;
174
+ }
175
+
176
+ return reference;
177
+ })
178
+ .filter(Boolean);
179
+
180
+ checkProperty(property, nextReferences, nextPath);
181
+ }
182
+ };
183
+
184
+ const checkObject = (declaratorOrProperty, references, path) => {
185
+ if (isLeafDeclaratorOrProperty(declaratorOrProperty)) {
186
+ return;
187
+ }
188
+
189
+ const value = getDeclaratorOrPropertyValue(declaratorOrProperty);
190
+
191
+ checkProperties(value, references, path);
192
+ };
193
+
194
+ const checkVariable = variable => {
195
+ if (variable.defs.length !== 1) {
196
+ return;
197
+ }
198
+
199
+ if (isUnusedVariable(variable)) {
200
+ return;
201
+ }
202
+
203
+ const [definition] = variable.defs;
204
+
205
+ checkObject(definition.node, variable.references);
206
+ };
207
+
208
+ const checkVariables = scope => {
209
+ for (const variable of scope.variables) {
210
+ checkVariable(variable);
211
+ }
212
+ };
213
+
214
+ return {
215
+ 'Program:exit'(program) {
216
+ const scopes = getScopes(sourceCode.getScope(program));
217
+ for (const scope of scopes) {
218
+ if (scope.type === 'global') {
219
+ continue;
220
+ }
221
+
222
+ checkVariables(scope);
223
+ }
224
+ },
225
+ };
226
+ };
227
+
228
+ /** @type {import('eslint').Rule.RuleModule} */
229
+ module.exports = {
230
+ create,
231
+ meta: {
232
+ type: 'suggestion',
233
+ docs: {
234
+ description: 'Disallow unused object properties.',
235
+ },
236
+ messages,
237
+ },
238
+ };
@@ -0,0 +1,68 @@
1
+ 'use strict';
2
+ const {
3
+ isParenthesized,
4
+ getParenthesizedRange,
5
+ } = require('./utils/parentheses.js');
6
+ const {removeParentheses} = require('./fix/index.js');
7
+ const shouldAddParenthesesToSpreadElementArgument = require('./utils/should-add-parentheses-to-spread-element-argument.js');
8
+
9
+ const MESSAGE_ID = 'no-useless-fallback-in-spread';
10
+ const messages = {
11
+ [MESSAGE_ID]: 'The empty object is useless.',
12
+ };
13
+
14
+ /** @param {import('eslint').Rule.RuleContext} context */
15
+ const create = context => ({
16
+ ObjectExpression(node) {
17
+ if (!(
18
+ node.properties.length === 0
19
+ && node.parent.type === 'LogicalExpression'
20
+ && node.parent.right === node
21
+ && (node.parent.operator === '||' || node.parent.operator === '??')
22
+ && node.parent.parent.type === 'SpreadElement'
23
+ && node.parent.parent.argument === node.parent
24
+ && node.parent.parent.parent.type === 'ObjectExpression'
25
+ && node.parent.parent.parent.properties.includes(node.parent.parent)
26
+ )) {
27
+ return;
28
+ }
29
+
30
+ return {
31
+ node,
32
+ messageId: MESSAGE_ID,
33
+ /** @param {import('eslint').Rule.RuleFixer} fixer */
34
+ * fix(fixer) {
35
+ const {sourceCode} = context;
36
+ const logicalExpression = node.parent;
37
+ const {left} = logicalExpression;
38
+ const isLeftObjectParenthesized = isParenthesized(left, sourceCode);
39
+ const [, start] = isLeftObjectParenthesized
40
+ ? getParenthesizedRange(left, sourceCode)
41
+ : left.range;
42
+ const [, end] = logicalExpression.range;
43
+
44
+ yield fixer.removeRange([start, end]);
45
+
46
+ if (
47
+ isLeftObjectParenthesized
48
+ || !shouldAddParenthesesToSpreadElementArgument(left)
49
+ ) {
50
+ yield * removeParentheses(logicalExpression, fixer, sourceCode);
51
+ }
52
+ },
53
+ };
54
+ },
55
+ });
56
+
57
+ /** @type {import('eslint').Rule.RuleModule} */
58
+ module.exports = {
59
+ create,
60
+ meta: {
61
+ type: 'suggestion',
62
+ docs: {
63
+ description: 'Disallow useless fallback when spreading in object literals.',
64
+ },
65
+ fixable: 'code',
66
+ messages,
67
+ },
68
+ };
@@ -0,0 +1,152 @@
1
+ 'use strict';
2
+ const {isMethodCall, isMemberExpression} = require('./ast/index.js');
3
+ const {
4
+ getParenthesizedRange,
5
+ isSameReference,
6
+ isLogicalExpression,
7
+ } = require('./utils/index.js');
8
+
9
+ const messages = {
10
+ 'non-zero': 'The non-empty check is useless as `Array#some()` returns `false` for an empty array.',
11
+ zero: 'The empty check is useless as `Array#every()` returns `true` for an empty array.',
12
+ };
13
+
14
+ // We assume the user already follows `unicorn/explicit-length-check`. These are allowed in that rule.
15
+ const isLengthCompareZero = node =>
16
+ node.type === 'BinaryExpression'
17
+ && node.right.type === 'Literal'
18
+ && node.right.raw === '0'
19
+ && isMemberExpression(node.left, {property: 'length', optional: false})
20
+ && isLogicalExpression(node.parent);
21
+
22
+ function flatLogicalExpression(node) {
23
+ return [node.left, node.right].flatMap(child =>
24
+ child.type === 'LogicalExpression' && child.operator === node.operator
25
+ ? flatLogicalExpression(child)
26
+ : [child],
27
+ );
28
+ }
29
+
30
+ /** @param {import('eslint').Rule.RuleContext} context */
31
+ const create = context => {
32
+ const logicalExpressions = [];
33
+ const zeroLengthChecks = new Set();
34
+ const nonZeroLengthChecks = new Set();
35
+ const arraySomeCalls = new Set();
36
+ const arrayEveryCalls = new Set();
37
+
38
+ function isUselessLengthCheckNode({node, operator, siblings}) {
39
+ return (
40
+ (
41
+ operator === '||'
42
+ && zeroLengthChecks.has(node)
43
+ && siblings.some(condition =>
44
+ arrayEveryCalls.has(condition)
45
+ && isSameReference(node.left.object, condition.callee.object),
46
+ )
47
+ )
48
+ || (
49
+ operator === '&&'
50
+ && nonZeroLengthChecks.has(node)
51
+ && siblings.some(condition =>
52
+ arraySomeCalls.has(condition)
53
+ && isSameReference(node.left.object, condition.callee.object),
54
+ )
55
+ )
56
+ );
57
+ }
58
+
59
+ function getUselessLengthCheckNode(logicalExpression) {
60
+ const {operator} = logicalExpression;
61
+ return flatLogicalExpression(logicalExpression)
62
+ .filter((node, index, conditions) => isUselessLengthCheckNode({
63
+ node,
64
+ operator,
65
+ siblings: [
66
+ conditions[index - 1],
67
+ conditions[index + 1],
68
+ ].filter(Boolean),
69
+ }));
70
+ }
71
+
72
+ return {
73
+ BinaryExpression(node) {
74
+ if (isLengthCompareZero(node)) {
75
+ const {operator} = node;
76
+ if (operator === '===') {
77
+ zeroLengthChecks.add(node);
78
+ } else if (operator === '>' || operator === '!==') {
79
+ nonZeroLengthChecks.add(node);
80
+ }
81
+ }
82
+ },
83
+ CallExpression(node) {
84
+ if (
85
+ isMethodCall(node, {
86
+ optionalCall: false,
87
+ optionalMember: false,
88
+ computed: false,
89
+ })
90
+ && node.callee.property.type === 'Identifier'
91
+ ) {
92
+ if (node.callee.property.name === 'some') {
93
+ arraySomeCalls.add(node);
94
+ } else if (node.callee.property.name === 'every') {
95
+ arrayEveryCalls.add(node);
96
+ }
97
+ }
98
+ },
99
+ LogicalExpression(node) {
100
+ if (isLogicalExpression(node)) {
101
+ logicalExpressions.push(node);
102
+ }
103
+ },
104
+ * 'Program:exit'() {
105
+ const nodes = new Set(
106
+ logicalExpressions.flatMap(logicalExpression =>
107
+ getUselessLengthCheckNode(logicalExpression),
108
+ ),
109
+ );
110
+
111
+ for (const node of nodes) {
112
+ yield {
113
+ loc: {
114
+ start: node.left.property.loc.start,
115
+ end: node.loc.end,
116
+ },
117
+ messageId: zeroLengthChecks.has(node) ? 'zero' : 'non-zero',
118
+ /** @param {import('eslint').Rule.RuleFixer} fixer */
119
+ fix(fixer) {
120
+ const {sourceCode} = context;
121
+ const {left, right} = node.parent;
122
+ const leftRange = getParenthesizedRange(left, sourceCode);
123
+ const rightRange = getParenthesizedRange(right, sourceCode);
124
+ const range = [];
125
+ if (left === node) {
126
+ range[0] = leftRange[0];
127
+ range[1] = rightRange[0];
128
+ } else {
129
+ range[0] = leftRange[1];
130
+ range[1] = rightRange[1];
131
+ }
132
+
133
+ return fixer.removeRange(range);
134
+ },
135
+ };
136
+ }
137
+ },
138
+ };
139
+ };
140
+
141
+ /** @type {import('eslint').Rule.RuleModule} */
142
+ module.exports = {
143
+ create,
144
+ meta: {
145
+ type: 'suggestion',
146
+ docs: {
147
+ description: 'Disallow useless array length check.',
148
+ },
149
+ fixable: 'code',
150
+ messages,
151
+ },
152
+ };