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,199 @@
1
+ 'use strict';
2
+ const isShorthandPropertyAssignmentPatternLeft = require('./utils/is-shorthand-property-assignment-pattern-left.js');
3
+
4
+ const MESSAGE_ID = 'noKeywordPrefix';
5
+ const messages = {
6
+ [MESSAGE_ID]: 'Do not prefix identifiers with keyword `{{keyword}}`.',
7
+ };
8
+
9
+ const prepareOptions = ({
10
+ disallowedPrefixes,
11
+ checkProperties = true,
12
+ onlyCamelCase = true,
13
+ } = {}) => ({
14
+ disallowedPrefixes: (disallowedPrefixes || [
15
+ 'new',
16
+ 'class',
17
+ ]),
18
+ checkProperties,
19
+ onlyCamelCase,
20
+ });
21
+
22
+ function findKeywordPrefix(name, options) {
23
+ return options.disallowedPrefixes.find(keyword => {
24
+ const suffix = options.onlyCamelCase ? '[A-Z]' : '.';
25
+ const regex = new RegExp(`^${keyword}${suffix}`);
26
+ return name.match(regex);
27
+ });
28
+ }
29
+
30
+ function checkMemberExpression(report, node, options) {
31
+ const {name, parent} = node;
32
+ const keyword = findKeywordPrefix(name, options);
33
+ const effectiveParent = parent.type === 'MemberExpression' ? parent.parent : parent;
34
+
35
+ if (!options.checkProperties) {
36
+ return;
37
+ }
38
+
39
+ if (parent.object.type === 'Identifier' && parent.object.name === name && Boolean(keyword)) {
40
+ report(node, keyword);
41
+ } else if (
42
+ effectiveParent.type === 'AssignmentExpression'
43
+ && Boolean(keyword)
44
+ && (effectiveParent.right.type !== 'MemberExpression' || effectiveParent.left.type === 'MemberExpression')
45
+ && effectiveParent.left.property.name === name
46
+ ) {
47
+ report(node, keyword);
48
+ }
49
+ }
50
+
51
+ function checkObjectPattern(report, node, options) {
52
+ const {name, parent} = node;
53
+ const keyword = findKeywordPrefix(name, options);
54
+
55
+ /* c8 ignore next 3 */
56
+ if (parent.shorthand && parent.value.left && Boolean(keyword)) {
57
+ report(node, keyword);
58
+ }
59
+
60
+ const assignmentKeyEqualsValue = parent.key.name === parent.value.name;
61
+
62
+ if (Boolean(keyword) && parent.computed) {
63
+ report(node, keyword);
64
+ }
65
+
66
+ // Prevent checking right hand side of destructured object
67
+ if (parent.key === node && parent.value !== node) {
68
+ return true;
69
+ }
70
+
71
+ const valueIsInvalid = parent.value.name && Boolean(keyword);
72
+
73
+ // Ignore destructuring if the option is set, unless a new identifier is created
74
+ if (valueIsInvalid && !assignmentKeyEqualsValue) {
75
+ report(node, keyword);
76
+ }
77
+
78
+ return false;
79
+ }
80
+
81
+ // Core logic copied from:
82
+ // https://github.com/eslint/eslint/blob/master/lib/rules/camelcase.js
83
+ const create = context => {
84
+ const options = prepareOptions(context.options[0]);
85
+
86
+ // Contains reported nodes to avoid reporting twice on destructuring with shorthand notation
87
+ const reported = [];
88
+ const ALLOWED_PARENT_TYPES = new Set(['CallExpression', 'NewExpression']);
89
+
90
+ function report(node, keyword) {
91
+ if (!reported.includes(node)) {
92
+ reported.push(node);
93
+ context.report({
94
+ node,
95
+ messageId: MESSAGE_ID,
96
+ data: {
97
+ name: node.name,
98
+ keyword,
99
+ },
100
+ });
101
+ }
102
+ }
103
+
104
+ return {
105
+ Identifier(node) {
106
+ const {name, parent} = node;
107
+ const keyword = findKeywordPrefix(name, options);
108
+ const effectiveParent = parent.type === 'MemberExpression' ? parent.parent : parent;
109
+
110
+ if (parent.type === 'MemberExpression') {
111
+ checkMemberExpression(report, node, options);
112
+ } else if (
113
+ parent.type === 'Property'
114
+ || parent.type === 'AssignmentPattern'
115
+ ) {
116
+ if (parent.parent.type === 'ObjectPattern') {
117
+ const finished = checkObjectPattern(report, node, options);
118
+ if (finished) {
119
+ return;
120
+ }
121
+ }
122
+
123
+ if (
124
+ !options.checkProperties
125
+ ) {
126
+ return;
127
+ }
128
+
129
+ // Don't check right hand side of AssignmentExpression to prevent duplicate warnings
130
+ if (
131
+ Boolean(keyword)
132
+ && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)
133
+ && !(parent.right === node)
134
+ && !isShorthandPropertyAssignmentPatternLeft(node)
135
+ ) {
136
+ report(node, keyword);
137
+ }
138
+
139
+ // Check if it's an import specifier
140
+ } else if (
141
+ [
142
+ 'ImportSpecifier',
143
+ 'ImportNamespaceSpecifier',
144
+ 'ImportDefaultSpecifier',
145
+ ].includes(parent.type)
146
+ ) {
147
+ // Report only if the local imported identifier is invalid
148
+ if (Boolean(keyword) && parent.local?.name === name) {
149
+ report(node, keyword);
150
+ }
151
+
152
+ // Report anything that is invalid that isn't a CallExpression
153
+ } else if (
154
+ Boolean(keyword)
155
+ && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)
156
+ ) {
157
+ report(node, keyword);
158
+ }
159
+ },
160
+ };
161
+ };
162
+
163
+ const schema = [
164
+ {
165
+ type: 'object',
166
+ additionalProperties: false,
167
+ properties: {
168
+ disallowedPrefixes: {
169
+ type: 'array',
170
+ items: [
171
+ {
172
+ type: 'string',
173
+ },
174
+ ],
175
+ minItems: 0,
176
+ uniqueItems: true,
177
+ },
178
+ checkProperties: {
179
+ type: 'boolean',
180
+ },
181
+ onlyCamelCase: {
182
+ type: 'boolean',
183
+ },
184
+ },
185
+ },
186
+ ];
187
+
188
+ /** @type {import('eslint').Rule.RuleModule} */
189
+ module.exports = {
190
+ create,
191
+ meta: {
192
+ type: 'suggestion',
193
+ docs: {
194
+ description: 'Disallow identifiers starting with `new` or `class`.',
195
+ },
196
+ schema,
197
+ messages,
198
+ },
199
+ };
@@ -0,0 +1,151 @@
1
+ 'use strict';
2
+ const {isParenthesized, isNotSemicolonToken} = require('@eslint-community/eslint-utils');
3
+ const {needsSemicolon} = require('./utils/index.js');
4
+ const {removeSpacesAfter} = require('./fix/index.js');
5
+
6
+ const MESSAGE_ID = 'no-lonely-if';
7
+ const messages = {
8
+ [MESSAGE_ID]: 'Unexpected `if` as the only statement in a `if` block without `else`.',
9
+ };
10
+
11
+ const isIfStatementWithoutAlternate = node => node.type === 'IfStatement' && !node.alternate;
12
+
13
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table
14
+ // Lower precedence than `&&`
15
+ const needParenthesis = node => (
16
+ (node.type === 'LogicalExpression' && (node.operator === '||' || node.operator === '??'))
17
+ || node.type === 'ConditionalExpression'
18
+ || node.type === 'AssignmentExpression'
19
+ || node.type === 'YieldExpression'
20
+ || node.type === 'SequenceExpression'
21
+ );
22
+
23
+ function getIfStatementTokens(node, sourceCode) {
24
+ const tokens = {};
25
+
26
+ tokens.ifToken = sourceCode.getFirstToken(node);
27
+ tokens.openingParenthesisToken = sourceCode.getFirstToken(node, 1);
28
+
29
+ const {consequent} = node;
30
+ tokens.closingParenthesisToken = sourceCode.getTokenBefore(consequent);
31
+
32
+ if (consequent.type === 'BlockStatement') {
33
+ tokens.openingBraceToken = sourceCode.getFirstToken(consequent);
34
+ tokens.closingBraceToken = sourceCode.getLastToken(consequent);
35
+ }
36
+
37
+ return tokens;
38
+ }
39
+
40
+ function fix(innerIfStatement, sourceCode) {
41
+ return function * (fixer) {
42
+ const outerIfStatement = (
43
+ innerIfStatement.parent.type === 'BlockStatement'
44
+ ? innerIfStatement.parent
45
+ : innerIfStatement
46
+ ).parent;
47
+ const outer = {
48
+ ...outerIfStatement,
49
+ ...getIfStatementTokens(outerIfStatement, sourceCode),
50
+ };
51
+ const inner = {
52
+ ...innerIfStatement,
53
+ ...getIfStatementTokens(innerIfStatement, sourceCode),
54
+ };
55
+
56
+ // Remove inner `if` token
57
+ yield fixer.remove(inner.ifToken);
58
+ yield removeSpacesAfter(inner.ifToken, sourceCode, fixer);
59
+
60
+ // Remove outer `{}`
61
+ if (outer.openingBraceToken) {
62
+ yield fixer.remove(outer.openingBraceToken);
63
+ yield removeSpacesAfter(outer.openingBraceToken, sourceCode, fixer);
64
+ yield fixer.remove(outer.closingBraceToken);
65
+
66
+ const tokenBefore = sourceCode.getTokenBefore(outer.closingBraceToken, {includeComments: true});
67
+ yield removeSpacesAfter(tokenBefore, sourceCode, fixer);
68
+ }
69
+
70
+ // Add new `()`
71
+ yield fixer.insertTextBefore(outer.openingParenthesisToken, '(');
72
+ yield fixer.insertTextAfter(
73
+ inner.closingParenthesisToken,
74
+ `)${inner.consequent.type === 'EmptyStatement' ? '' : ' '}`,
75
+ );
76
+
77
+ // Add ` && `
78
+ yield fixer.insertTextAfter(outer.closingParenthesisToken, ' && ');
79
+
80
+ // Remove `()` if `test` don't need it
81
+ for (const {test, openingParenthesisToken, closingParenthesisToken} of [outer, inner]) {
82
+ if (
83
+ isParenthesized(test, sourceCode)
84
+ || !needParenthesis(test)
85
+ ) {
86
+ yield fixer.remove(openingParenthesisToken);
87
+ yield fixer.remove(closingParenthesisToken);
88
+ }
89
+
90
+ yield removeSpacesAfter(closingParenthesisToken, sourceCode, fixer);
91
+ }
92
+
93
+ // If the `if` statement has no block, and is not followed by a semicolon,
94
+ // make sure that fixing the issue would not change semantics due to ASI.
95
+ // Similar logic https://github.com/eslint/eslint/blob/2124e1b5dad30a905dc26bde9da472bf622d3f50/lib/rules/no-lonely-if.js#L61-L77
96
+ if (inner.consequent.type !== 'BlockStatement') {
97
+ const lastToken = sourceCode.getLastToken(inner.consequent);
98
+ if (isNotSemicolonToken(lastToken)) {
99
+ const nextToken = sourceCode.getTokenAfter(outer);
100
+ if (nextToken && needsSemicolon(lastToken, sourceCode, nextToken.value)) {
101
+ yield fixer.insertTextBefore(nextToken, ';');
102
+ }
103
+ }
104
+ }
105
+ };
106
+ }
107
+
108
+ /** @param {import('eslint').Rule.RuleContext} context */
109
+ const create = context => ({
110
+ IfStatement(ifStatement) {
111
+ if (!(
112
+ isIfStatementWithoutAlternate(ifStatement)
113
+ && (
114
+ // `if (a) { if (b) {} }`
115
+ (
116
+ ifStatement.parent.type === 'BlockStatement'
117
+ && ifStatement.parent.body.length === 1
118
+ && ifStatement.parent.body[0] === ifStatement
119
+ && isIfStatementWithoutAlternate(ifStatement.parent.parent)
120
+ && ifStatement.parent.parent.consequent === ifStatement.parent
121
+ )
122
+ // `if (a) if (b) {}`
123
+ || (
124
+ isIfStatementWithoutAlternate(ifStatement.parent)
125
+ && ifStatement.parent.consequent === ifStatement
126
+ )
127
+ )
128
+ )) {
129
+ return;
130
+ }
131
+
132
+ return {
133
+ node: ifStatement,
134
+ messageId: MESSAGE_ID,
135
+ fix: fix(ifStatement, context.sourceCode),
136
+ };
137
+ },
138
+ });
139
+
140
+ /** @type {import('eslint').Rule.RuleModule} */
141
+ module.exports = {
142
+ create,
143
+ meta: {
144
+ type: 'suggestion',
145
+ docs: {
146
+ description: 'Disallow `if` statements as the only statement in `if` blocks without `else`.',
147
+ },
148
+ fixable: 'code',
149
+ messages,
150
+ },
151
+ };
@@ -0,0 +1,144 @@
1
+ /*
2
+ Based on ESLint builtin `no-negated-condition` rule
3
+ https://github.com/eslint/eslint/blob/5c39425fc55ecc0b97bbd07ac22654c0eb4f789c/lib/rules/no-negated-condition.js
4
+ */
5
+ 'use strict';
6
+ const {
7
+ removeParentheses,
8
+ fixSpaceAroundKeyword,
9
+ addParenthesizesToReturnOrThrowExpression,
10
+ } = require('./fix/index.js');
11
+ const {
12
+ getParenthesizedRange,
13
+ isParenthesized,
14
+ } = require('./utils/parentheses.js');
15
+ const isOnSameLine = require('./utils/is-on-same-line.js');
16
+ const needsSemicolon = require('./utils/needs-semicolon.js');
17
+
18
+ const MESSAGE_ID = 'no-negated-condition';
19
+ const messages = {
20
+ [MESSAGE_ID]: 'Unexpected negated condition.',
21
+ };
22
+
23
+ function * convertNegatedCondition(fixer, node, sourceCode) {
24
+ const {test} = node;
25
+ if (test.type === 'UnaryExpression') {
26
+ const token = sourceCode.getFirstToken(test);
27
+
28
+ if (node.type === 'IfStatement') {
29
+ yield * removeParentheses(test.argument, fixer, sourceCode);
30
+ }
31
+
32
+ yield fixer.remove(token);
33
+ return;
34
+ }
35
+
36
+ const token = sourceCode.getTokenAfter(
37
+ test.left,
38
+ token => token.type === 'Punctuator' && token.value === test.operator,
39
+ );
40
+
41
+ yield fixer.replaceText(token, '=' + token.value.slice(1));
42
+ }
43
+
44
+ function * swapConsequentAndAlternate(fixer, node, sourceCode) {
45
+ const isIfStatement = node.type === 'IfStatement';
46
+ const [consequent, alternate] = [
47
+ node.consequent,
48
+ node.alternate,
49
+ ].map(node => {
50
+ const range = getParenthesizedRange(node, sourceCode);
51
+ let text = sourceCode.text.slice(...range);
52
+ // `if (!a) b(); else c()` can't fix to `if (!a) c() else b();`
53
+ if (isIfStatement && node.type !== 'BlockStatement') {
54
+ text = `{${text}}`;
55
+ }
56
+
57
+ return {
58
+ range,
59
+ text,
60
+ };
61
+ });
62
+
63
+ if (consequent.text === alternate.text) {
64
+ return;
65
+ }
66
+
67
+ yield fixer.replaceTextRange(consequent.range, alternate.text);
68
+ yield fixer.replaceTextRange(alternate.range, consequent.text);
69
+ }
70
+
71
+ /** @param {import('eslint').Rule.RuleContext} context */
72
+ const create = context => {
73
+ context.on(['IfStatement', 'ConditionalExpression'], node => {
74
+ if (
75
+ node.type === 'IfStatement'
76
+ && (
77
+ !node.alternate
78
+ || node.alternate.type === 'IfStatement'
79
+ )
80
+ ) {
81
+ return;
82
+ }
83
+
84
+ const {test} = node;
85
+
86
+ if (!(
87
+ (test.type === 'UnaryExpression' && test.operator === '!')
88
+ || (test.type === 'BinaryExpression' && (test.operator === '!=' || test.operator === '!=='))
89
+ )) {
90
+ return;
91
+ }
92
+
93
+ return {
94
+ node: test,
95
+ messageId: MESSAGE_ID,
96
+ /** @param {import('eslint').Rule.RuleFixer} fixer */
97
+ * fix(fixer) {
98
+ const {sourceCode} = context;
99
+ yield * convertNegatedCondition(fixer, node, sourceCode);
100
+ yield * swapConsequentAndAlternate(fixer, node, sourceCode);
101
+
102
+ if (
103
+ node.type !== 'ConditionalExpression'
104
+ || test.type !== 'UnaryExpression'
105
+ ) {
106
+ return;
107
+ }
108
+
109
+ yield * fixSpaceAroundKeyword(fixer, node, sourceCode);
110
+
111
+ const {parent} = node;
112
+ const [firstToken, secondToken] = sourceCode.getFirstTokens(test, 2);
113
+ if (
114
+ (parent.type === 'ReturnStatement' || parent.type === 'ThrowStatement')
115
+ && parent.argument === node
116
+ && !isOnSameLine(firstToken, secondToken)
117
+ && !isParenthesized(node, sourceCode)
118
+ && !isParenthesized(test, sourceCode)
119
+ ) {
120
+ yield * addParenthesizesToReturnOrThrowExpression(fixer, parent, sourceCode);
121
+ return;
122
+ }
123
+
124
+ const tokenBefore = sourceCode.getTokenBefore(node);
125
+ if (needsSemicolon(tokenBefore, sourceCode, secondToken.value)) {
126
+ yield fixer.insertTextBefore(node, ';');
127
+ }
128
+ },
129
+ };
130
+ });
131
+ };
132
+
133
+ /** @type {import('eslint').Rule.RuleModule} */
134
+ module.exports = {
135
+ create,
136
+ meta: {
137
+ type: 'suggestion',
138
+ docs: {
139
+ description: 'Disallow negated conditions.',
140
+ },
141
+ fixable: 'code',
142
+ messages,
143
+ },
144
+ };
@@ -0,0 +1,58 @@
1
+ 'use strict';
2
+ const {isParenthesized} = require('@eslint-community/eslint-utils');
3
+
4
+ const MESSAGE_ID_TOO_DEEP = 'too-deep';
5
+ const MESSAGE_ID_SHOULD_PARENTHESIZED = 'should-parenthesized';
6
+ const messages = {
7
+ [MESSAGE_ID_TOO_DEEP]: 'Do not nest ternary expressions.',
8
+ [MESSAGE_ID_SHOULD_PARENTHESIZED]: 'Nest ternary expression should be parenthesized.',
9
+ };
10
+
11
+ /** @param {import('eslint').Rule.RuleContext} context */
12
+ const create = context => ({
13
+ ConditionalExpression(node) {
14
+ if ([
15
+ node.test,
16
+ node.consequent,
17
+ node.alternate,
18
+ ].some(node => node.type === 'ConditionalExpression')) {
19
+ return;
20
+ }
21
+
22
+ const {sourceCode} = context;
23
+ const ancestors = sourceCode.getAncestors(node).reverse();
24
+ const nestLevel = ancestors.findIndex(node => node.type !== 'ConditionalExpression');
25
+
26
+ if (nestLevel === 1 && !isParenthesized(node, sourceCode)) {
27
+ return {
28
+ node,
29
+ messageId: MESSAGE_ID_SHOULD_PARENTHESIZED,
30
+ fix: fixer => [
31
+ fixer.insertTextBefore(node, '('),
32
+ fixer.insertTextAfter(node, ')'),
33
+ ],
34
+ };
35
+ }
36
+
37
+ // Nesting more than one level not allowed
38
+ if (nestLevel > 1) {
39
+ return {
40
+ node: nestLevel > 2 ? ancestors[nestLevel - 3] : node,
41
+ messageId: MESSAGE_ID_TOO_DEEP,
42
+ };
43
+ }
44
+ },
45
+ });
46
+
47
+ /** @type {import('eslint').Rule.RuleModule} */
48
+ module.exports = {
49
+ create,
50
+ meta: {
51
+ type: 'suggestion',
52
+ docs: {
53
+ description: 'Disallow nested ternary expressions.',
54
+ },
55
+ fixable: 'code',
56
+ messages,
57
+ },
58
+ };
@@ -0,0 +1,104 @@
1
+ 'use strict';
2
+ const {isParenthesized, getStaticValue} = require('@eslint-community/eslint-utils');
3
+ const needsSemicolon = require('./utils/needs-semicolon.js');
4
+ const isNumber = require('./utils/is-number.js');
5
+ const {isNewExpression} = require('./ast/index.js');
6
+
7
+ const MESSAGE_ID_ERROR = 'error';
8
+ const MESSAGE_ID_LENGTH = 'array-length';
9
+ const MESSAGE_ID_ONLY_ELEMENT = 'only-element';
10
+ const MESSAGE_ID_SPREAD = 'spread';
11
+ const messages = {
12
+ [MESSAGE_ID_ERROR]: 'Do not use `new Array()`.',
13
+ [MESSAGE_ID_LENGTH]: 'The argument is the length of array.',
14
+ [MESSAGE_ID_ONLY_ELEMENT]: 'The argument is the only element of array.',
15
+ [MESSAGE_ID_SPREAD]: 'Spread the argument.',
16
+ };
17
+
18
+ function getProblem(context, node) {
19
+ if (
20
+ !isNewExpression(node, {
21
+ name: 'Array',
22
+ argumentsLength: 1,
23
+ allowSpreadElement: true,
24
+ })
25
+ ) {
26
+ return;
27
+ }
28
+
29
+ const problem = {
30
+ node,
31
+ messageId: MESSAGE_ID_ERROR,
32
+ };
33
+
34
+ const [argumentNode] = node.arguments;
35
+
36
+ const {sourceCode} = context;
37
+ let text = sourceCode.getText(argumentNode);
38
+ if (isParenthesized(argumentNode, sourceCode)) {
39
+ text = `(${text})`;
40
+ }
41
+
42
+ const maybeSemiColon = needsSemicolon(sourceCode.getTokenBefore(node), sourceCode, '[')
43
+ ? ';'
44
+ : '';
45
+
46
+ // We are not sure how many `arguments` passed
47
+ if (argumentNode.type === 'SpreadElement') {
48
+ problem.suggest = [
49
+ {
50
+ messageId: MESSAGE_ID_SPREAD,
51
+ fix: fixer => fixer.replaceText(node, `${maybeSemiColon}[${text}]`),
52
+ },
53
+ ];
54
+ return problem;
55
+ }
56
+
57
+ const fromLengthText = `Array.from(${text === 'length' ? '{length}' : `{length: ${text}}`})`;
58
+ const scope = sourceCode.getScope(node);
59
+ if (isNumber(argumentNode, scope)) {
60
+ problem.fix = fixer => fixer.replaceText(node, fromLengthText);
61
+ return problem;
62
+ }
63
+
64
+ const onlyElementText = `${maybeSemiColon}[${text}]`;
65
+ const result = getStaticValue(argumentNode, scope);
66
+ if (result !== null && typeof result.value !== 'number') {
67
+ problem.fix = fixer => fixer.replaceText(node, onlyElementText);
68
+ return problem;
69
+ }
70
+
71
+ // We don't know the argument is number or not
72
+ problem.suggest = [
73
+ {
74
+ messageId: MESSAGE_ID_LENGTH,
75
+ fix: fixer => fixer.replaceText(node, fromLengthText),
76
+ },
77
+ {
78
+ messageId: MESSAGE_ID_ONLY_ELEMENT,
79
+ fix: fixer => fixer.replaceText(node, onlyElementText),
80
+ },
81
+ ];
82
+ return problem;
83
+ }
84
+
85
+ /** @param {import('eslint').Rule.RuleContext} context */
86
+ const create = context => ({
87
+ NewExpression(node) {
88
+ return getProblem(context, node);
89
+ },
90
+ });
91
+
92
+ /** @type {import('eslint').Rule.RuleModule} */
93
+ module.exports = {
94
+ create,
95
+ meta: {
96
+ type: 'suggestion',
97
+ docs: {
98
+ description: 'Disallow `new Array()`.',
99
+ },
100
+ fixable: 'code',
101
+ hasSuggestions: true,
102
+ messages,
103
+ },
104
+ };