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,98 @@
1
+ 'use strict';
2
+ const {getStaticValue} = require('@eslint-community/eslint-utils');
3
+ const {switchNewExpressionToCallExpression} = require('./fix/index.js');
4
+ const isNumber = require('./utils/is-number.js');
5
+ const {isNewExpression} = require('./ast/index.js');
6
+
7
+ const ERROR = 'error';
8
+ const ERROR_UNKNOWN = 'error-unknown';
9
+ const SUGGESTION = 'suggestion';
10
+ const messages = {
11
+ [ERROR]: '`new Buffer()` is deprecated, use `Buffer.{{method}}()` instead.',
12
+ [ERROR_UNKNOWN]: '`new Buffer()` is deprecated, use `Buffer.alloc()` or `Buffer.from()` instead.',
13
+ [SUGGESTION]: 'Switch to `Buffer.{{replacement}}()`.',
14
+ };
15
+
16
+ const inferMethod = (bufferArguments, scope) => {
17
+ if (bufferArguments.length !== 1) {
18
+ return 'from';
19
+ }
20
+
21
+ const [firstArgument] = bufferArguments;
22
+ if (firstArgument.type === 'SpreadElement') {
23
+ return;
24
+ }
25
+
26
+ if (firstArgument.type === 'ArrayExpression' || firstArgument.type === 'TemplateLiteral') {
27
+ return 'from';
28
+ }
29
+
30
+ if (isNumber(firstArgument, scope)) {
31
+ return 'alloc';
32
+ }
33
+
34
+ const staticResult = getStaticValue(firstArgument, scope);
35
+ if (staticResult) {
36
+ const {value} = staticResult;
37
+ if (
38
+ typeof value === 'string'
39
+ || Array.isArray(value)
40
+ ) {
41
+ return 'from';
42
+ }
43
+ }
44
+ };
45
+
46
+ function fix(node, sourceCode, method) {
47
+ return function * (fixer) {
48
+ yield fixer.insertTextAfter(node.callee, `.${method}`);
49
+ yield * switchNewExpressionToCallExpression(node, sourceCode, fixer);
50
+ };
51
+ }
52
+
53
+ /** @param {import('eslint').Rule.RuleContext} context */
54
+ const create = context => {
55
+ const {sourceCode} = context;
56
+ return {
57
+ NewExpression(node) {
58
+ if (!isNewExpression(node, {name: 'Buffer'})) {
59
+ return;
60
+ }
61
+
62
+ const method = inferMethod(node.arguments, sourceCode.getScope(node));
63
+
64
+ if (method) {
65
+ return {
66
+ node,
67
+ messageId: ERROR,
68
+ data: {method},
69
+ fix: fix(node, sourceCode, method),
70
+ };
71
+ }
72
+
73
+ return {
74
+ node,
75
+ messageId: ERROR_UNKNOWN,
76
+ suggest: ['from', 'alloc'].map(replacement => ({
77
+ messageId: SUGGESTION,
78
+ data: {replacement},
79
+ fix: fix(node, sourceCode, replacement),
80
+ })),
81
+ };
82
+ },
83
+ };
84
+ };
85
+
86
+ /** @type {import('eslint').Rule.RuleModule} */
87
+ module.exports = {
88
+ create,
89
+ meta: {
90
+ type: 'problem',
91
+ docs: {
92
+ description: 'Enforce the use of `Buffer.from()` and `Buffer.alloc()` instead of the deprecated `new Buffer()`.',
93
+ },
94
+ fixable: 'code',
95
+ hasSuggestions: true,
96
+ messages,
97
+ },
98
+ };
@@ -0,0 +1,153 @@
1
+ 'use strict';
2
+ const {
3
+ isMethodCall,
4
+ isCallExpression,
5
+ isLiteral,
6
+ } = require('./ast/index.js');
7
+
8
+ const ERROR_MESSAGE_ID = 'error';
9
+ const SUGGESTION_REPLACE_MESSAGE_ID = 'replace';
10
+ const SUGGESTION_REMOVE_MESSAGE_ID = 'remove';
11
+ const messages = {
12
+ [ERROR_MESSAGE_ID]: 'Use `undefined` instead of `null`.',
13
+ [SUGGESTION_REPLACE_MESSAGE_ID]: 'Replace `null` with `undefined`.',
14
+ [SUGGESTION_REMOVE_MESSAGE_ID]: 'Remove `null`.',
15
+ };
16
+
17
+ const isLooseEqual = node => node.type === 'BinaryExpression' && ['==', '!='].includes(node.operator);
18
+ const isStrictEqual = node => node.type === 'BinaryExpression' && ['===', '!=='].includes(node.operator);
19
+
20
+ /** @param {import('eslint').Rule.RuleContext} context */
21
+ const create = context => {
22
+ const {checkStrictEquality} = {
23
+ checkStrictEquality: false,
24
+ ...context.options[0],
25
+ };
26
+
27
+ return {
28
+ Literal(node) {
29
+ if (
30
+ // eslint-disable-next-line unicorn/no-null
31
+ !isLiteral(node, null)
32
+ || (!checkStrictEquality && isStrictEqual(node.parent))
33
+ // `Object.create(null)`, `Object.create(null, foo)`
34
+ || (
35
+ isMethodCall(node.parent, {
36
+ object: 'Object',
37
+ method: 'create',
38
+ minimumArguments: 1,
39
+ maximumArguments: 2,
40
+ optionalCall: false,
41
+ optionalMember: false,
42
+ })
43
+ && node.parent.arguments[0] === node
44
+ )
45
+ // `useRef(null)`
46
+ || (
47
+ isCallExpression(node.parent, {
48
+ name: 'useRef',
49
+ argumentsLength: 1,
50
+ optionalCall: false,
51
+ optionalMember: false,
52
+ })
53
+ && node.parent.arguments[0] === node
54
+ )
55
+ // `React.useRef(null)`
56
+ || (
57
+ isMethodCall(node.parent, {
58
+ object: 'React',
59
+ method: 'useRef',
60
+ argumentsLength: 1,
61
+ optionalCall: false,
62
+ optionalMember: false,
63
+ })
64
+ && node.parent.arguments[0] === node
65
+ )
66
+ // `foo.insertBefore(bar, null)`
67
+ || (
68
+ isMethodCall(node.parent, {
69
+ method: 'insertBefore',
70
+ argumentsLength: 2,
71
+ optionalCall: false,
72
+ optionalMember: false,
73
+ })
74
+ && node.parent.arguments[1] === node
75
+ )
76
+ ) {
77
+ return;
78
+ }
79
+
80
+ const {parent} = node;
81
+
82
+ const problem = {
83
+ node,
84
+ messageId: ERROR_MESSAGE_ID,
85
+ };
86
+
87
+ const useUndefinedFix = fixer => fixer.replaceText(node, 'undefined');
88
+
89
+ if (isLooseEqual(parent)) {
90
+ problem.fix = useUndefinedFix;
91
+ return problem;
92
+ }
93
+
94
+ const useUndefinedSuggestion = {
95
+ messageId: SUGGESTION_REPLACE_MESSAGE_ID,
96
+ fix: useUndefinedFix,
97
+ };
98
+
99
+ if (parent.type === 'ReturnStatement' && parent.argument === node) {
100
+ problem.suggest = [
101
+ {
102
+ messageId: SUGGESTION_REMOVE_MESSAGE_ID,
103
+ fix: fixer => fixer.remove(node),
104
+ },
105
+ useUndefinedSuggestion,
106
+ ];
107
+ return problem;
108
+ }
109
+
110
+ if (parent.type === 'VariableDeclarator' && parent.init === node && parent.parent.kind !== 'const') {
111
+ problem.suggest = [
112
+ {
113
+ messageId: SUGGESTION_REMOVE_MESSAGE_ID,
114
+ fix: fixer => fixer.removeRange([parent.id.range[1], node.range[1]]),
115
+ },
116
+ useUndefinedSuggestion,
117
+ ];
118
+ return problem;
119
+ }
120
+
121
+ problem.suggest = [useUndefinedSuggestion];
122
+ return problem;
123
+ },
124
+ };
125
+ };
126
+
127
+ const schema = [
128
+ {
129
+ type: 'object',
130
+ additionalProperties: false,
131
+ properties: {
132
+ checkStrictEquality: {
133
+ type: 'boolean',
134
+ default: false,
135
+ },
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 the use of the `null` literal.',
147
+ },
148
+ fixable: 'code',
149
+ hasSuggestions: true,
150
+ schema,
151
+ messages,
152
+ },
153
+ };
@@ -0,0 +1,50 @@
1
+ 'use strict';
2
+ const {isFunction} = require('./ast/index.js');
3
+
4
+ const MESSAGE_ID_IDENTIFIER = 'identifier';
5
+ const MESSAGE_ID_NON_IDENTIFIER = 'non-identifier';
6
+ const messages = {
7
+ [MESSAGE_ID_IDENTIFIER]: 'Do not use an object literal as default for parameter `{{parameter}}`.',
8
+ [MESSAGE_ID_NON_IDENTIFIER]: 'Do not use an object literal as default.',
9
+ };
10
+
11
+ /** @param {import('eslint').Rule.RuleContext} context */
12
+ const create = () => ({
13
+ AssignmentPattern(node) {
14
+ if (!(
15
+ node.right.type === 'ObjectExpression'
16
+ && node.right.properties.length > 0
17
+ && isFunction(node.parent)
18
+ && node.parent.params.includes(node)
19
+ )) {
20
+ return;
21
+ }
22
+
23
+ const {left, right} = node;
24
+
25
+ if (left.type === 'Identifier') {
26
+ return {
27
+ node: left,
28
+ messageId: MESSAGE_ID_IDENTIFIER,
29
+ data: {parameter: left.name},
30
+ };
31
+ }
32
+
33
+ return {
34
+ node: right,
35
+ messageId: MESSAGE_ID_NON_IDENTIFIER,
36
+ };
37
+ },
38
+ });
39
+
40
+ /** @type {import('eslint').Rule.RuleModule} */
41
+ module.exports = {
42
+ create,
43
+ meta: {
44
+ type: 'problem',
45
+ docs: {
46
+ description: 'Disallow the use of objects as default parameters.',
47
+ },
48
+ messages,
49
+ },
50
+ };
@@ -0,0 +1,104 @@
1
+ 'use strict';
2
+ const {isStaticRequire, isMethodCall, isLiteral} = require('./ast/index.js');
3
+
4
+ const MESSAGE_ID = 'no-process-exit';
5
+ const messages = {
6
+ [MESSAGE_ID]: 'Only use `process.exit()` in CLI apps. Throw an error instead.',
7
+ };
8
+
9
+ const isWorkerThreads = node =>
10
+ isLiteral(node, 'node:worker_threads')
11
+ || isLiteral(node, 'worker_threads');
12
+
13
+ /** @param {import('eslint').Rule.RuleContext} context */
14
+ const create = context => {
15
+ const startsWithHashBang = context.sourceCode.lines[0].indexOf('#!') === 0;
16
+
17
+ if (startsWithHashBang) {
18
+ return {};
19
+ }
20
+
21
+ let processEventHandler;
22
+
23
+ // Only report if it's outside an worker thread context. See #328.
24
+ let requiredWorkerThreadsModule = false;
25
+ const problemNodes = [];
26
+
27
+ // `require('worker_threads')`
28
+ context.on('CallExpression', callExpression => {
29
+ if (
30
+ isStaticRequire(callExpression)
31
+ && isWorkerThreads(callExpression.arguments[0])
32
+ ) {
33
+ requiredWorkerThreadsModule = true;
34
+ }
35
+ });
36
+
37
+ // `import workerThreads from 'worker_threads'`
38
+ context.on('ImportDeclaration', importDeclaration => {
39
+ if (
40
+ importDeclaration.source.type === 'Literal'
41
+ && isWorkerThreads(importDeclaration.source)
42
+ ) {
43
+ requiredWorkerThreadsModule = true;
44
+ }
45
+ });
46
+
47
+ // Check `process.on` / `process.once` call
48
+ context.on('CallExpression', node => {
49
+ if (isMethodCall(node, {
50
+ object: 'process',
51
+ methods: ['on', 'once'],
52
+ minimumArguments: 1,
53
+ optionalCall: false,
54
+ optionalMember: false,
55
+ })) {
56
+ processEventHandler = node;
57
+ }
58
+ });
59
+ context.onExit('CallExpression', node => {
60
+ if (node === processEventHandler) {
61
+ processEventHandler = undefined;
62
+ }
63
+ });
64
+
65
+ // Check `process.exit` call
66
+ context.on('CallExpression', node => {
67
+ if (
68
+ !processEventHandler
69
+ && isMethodCall(node, {
70
+ object: 'process',
71
+ method: 'exit',
72
+ optionalCall: false,
73
+ optionalMember: false,
74
+ })
75
+ ) {
76
+ problemNodes.push(node);
77
+ }
78
+ });
79
+
80
+ context.onExit('Program', function * () {
81
+ if (requiredWorkerThreadsModule) {
82
+ return;
83
+ }
84
+
85
+ for (const node of problemNodes) {
86
+ yield {
87
+ node,
88
+ messageId: MESSAGE_ID,
89
+ };
90
+ }
91
+ });
92
+ };
93
+
94
+ /** @type {import('eslint').Rule.RuleModule} */
95
+ module.exports = {
96
+ create,
97
+ meta: {
98
+ type: 'suggestion',
99
+ docs: {
100
+ description: 'Disallow `process.exit()`.',
101
+ },
102
+ messages,
103
+ },
104
+ };
@@ -0,0 +1,224 @@
1
+ 'use strict';
2
+ const {isSemicolonToken} = require('@eslint-community/eslint-utils');
3
+ const getClassHeadLocation = require('./utils/get-class-head-location.js');
4
+ const assertToken = require('./utils/assert-token.js');
5
+ const {removeSpacesAfter} = require('./fix/index.js');
6
+
7
+ const MESSAGE_ID = 'no-static-only-class';
8
+ const messages = {
9
+ [MESSAGE_ID]: 'Use an object instead of a class with only static members.',
10
+ };
11
+
12
+ const isEqualToken = ({type, value}) => type === 'Punctuator' && value === '=';
13
+ const isDeclarationOfExportDefaultDeclaration = node =>
14
+ node.type === 'ClassDeclaration'
15
+ && node.parent.type === 'ExportDefaultDeclaration'
16
+ && node.parent.declaration === node;
17
+
18
+ const isPropertyDefinition = node => node.type === 'PropertyDefinition';
19
+ const isMethodDefinition = node => node.type === 'MethodDefinition';
20
+
21
+ function isStaticMember(node) {
22
+ const {
23
+ private: isPrivate,
24
+ static: isStatic,
25
+ declare: isDeclare,
26
+ readonly: isReadonly,
27
+ accessibility,
28
+ decorators,
29
+ key,
30
+ } = node;
31
+
32
+ // Avoid matching unexpected node. For example: https://github.com/tc39/proposal-class-static-block
33
+ if (!isPropertyDefinition(node) && !isMethodDefinition(node)) {
34
+ return false;
35
+ }
36
+
37
+ if (!isStatic || isPrivate || key.type === 'PrivateIdentifier') {
38
+ return false;
39
+ }
40
+
41
+ // TypeScript class
42
+ if (
43
+ isDeclare
44
+ || isReadonly
45
+ || accessibility !== undefined
46
+ || (Array.isArray(decorators) && decorators.length > 0)
47
+ ) {
48
+ return false;
49
+ }
50
+
51
+ return true;
52
+ }
53
+
54
+ function * switchClassMemberToObjectProperty(node, sourceCode, fixer) {
55
+ const staticToken = sourceCode.getFirstToken(node);
56
+ assertToken(staticToken, {
57
+ expected: {type: 'Keyword', value: 'static'},
58
+ ruleId: 'no-static-only-class',
59
+ });
60
+
61
+ yield fixer.remove(staticToken);
62
+ yield removeSpacesAfter(staticToken, sourceCode, fixer);
63
+
64
+ const maybeSemicolonToken = isPropertyDefinition(node)
65
+ ? sourceCode.getLastToken(node)
66
+ : sourceCode.getTokenAfter(node);
67
+ const hasSemicolonToken = isSemicolonToken(maybeSemicolonToken);
68
+
69
+ if (isPropertyDefinition(node)) {
70
+ const {key, value} = node;
71
+
72
+ if (value) {
73
+ // Computed key may have `]` after `key`
74
+ const equalToken = sourceCode.getTokenAfter(key, isEqualToken);
75
+ yield fixer.replaceText(equalToken, ':');
76
+ } else if (hasSemicolonToken) {
77
+ yield fixer.insertTextBefore(maybeSemicolonToken, ': undefined');
78
+ } else {
79
+ yield fixer.insertTextAfter(node, ': undefined');
80
+ }
81
+ }
82
+
83
+ yield (
84
+ hasSemicolonToken
85
+ ? fixer.replaceText(maybeSemicolonToken, ',')
86
+ : fixer.insertTextAfter(node, ',')
87
+ );
88
+ }
89
+
90
+ function switchClassToObject(node, sourceCode) {
91
+ const {
92
+ type,
93
+ id,
94
+ body,
95
+ declare: isDeclare,
96
+ abstract: isAbstract,
97
+ implements: classImplements,
98
+ parent,
99
+ } = node;
100
+
101
+ if (
102
+ isDeclare
103
+ || isAbstract
104
+ || (Array.isArray(classImplements) && classImplements.length > 0)
105
+ ) {
106
+ return;
107
+ }
108
+
109
+ if (type === 'ClassExpression' && id) {
110
+ return;
111
+ }
112
+
113
+ const isExportDefault = isDeclarationOfExportDefaultDeclaration(node);
114
+
115
+ if (isExportDefault && id) {
116
+ return;
117
+ }
118
+
119
+ for (const node of body.body) {
120
+ if (
121
+ isPropertyDefinition(node)
122
+ && (
123
+ node.typeAnnotation
124
+ // This is a stupid way to check if `value` of `PropertyDefinition` uses `this`
125
+ || (node.value && sourceCode.getText(node.value).includes('this'))
126
+ )
127
+ ) {
128
+ return;
129
+ }
130
+ }
131
+
132
+ return function * (fixer) {
133
+ const classToken = sourceCode.getFirstToken(node);
134
+ /* c8 ignore next */
135
+ assertToken(classToken, {
136
+ expected: {type: 'Keyword', value: 'class'},
137
+ ruleId: 'no-static-only-class',
138
+ });
139
+
140
+ if (isExportDefault || type === 'ClassExpression') {
141
+ /*
142
+ There are comments after return, and `{` is not on same line
143
+
144
+ ```js
145
+ function a() {
146
+ return class // comment
147
+ {
148
+ static a() {}
149
+ }
150
+ }
151
+ ```
152
+ */
153
+ if (
154
+ type === 'ClassExpression'
155
+ && parent.type === 'ReturnStatement'
156
+ && body.loc.start.line !== parent.loc.start.line
157
+ && sourceCode.text.slice(classToken.range[1], body.range[0]).trim()
158
+ ) {
159
+ yield fixer.replaceText(classToken, '{');
160
+
161
+ const openingBraceToken = sourceCode.getFirstToken(body);
162
+ yield fixer.remove(openingBraceToken);
163
+ } else {
164
+ yield fixer.replaceText(classToken, '');
165
+
166
+ /*
167
+ Avoid breaking case like
168
+
169
+ ```js
170
+ return class
171
+ {};
172
+ ```
173
+ */
174
+ yield removeSpacesAfter(classToken, sourceCode, fixer);
175
+ }
176
+
177
+ // There should not be ASI problem
178
+ } else {
179
+ yield fixer.replaceText(classToken, 'const');
180
+ yield fixer.insertTextBefore(body, '= ');
181
+ yield fixer.insertTextAfter(body, ';');
182
+ }
183
+
184
+ for (const node of body.body) {
185
+ yield * switchClassMemberToObjectProperty(node, sourceCode, fixer);
186
+ }
187
+ };
188
+ }
189
+
190
+ function create(context) {
191
+ context.on(['ClassDeclaration', 'ClassExpression'], node => {
192
+ if (
193
+ node.superClass
194
+ || (node.decorators && node.decorators.length > 0)
195
+ || node.body.type !== 'ClassBody'
196
+ || node.body.body.length === 0
197
+ || node.body.body.some(node => !isStaticMember(node))
198
+ ) {
199
+ return;
200
+ }
201
+
202
+ const {sourceCode} = context;
203
+
204
+ return {
205
+ node,
206
+ loc: getClassHeadLocation(node, sourceCode),
207
+ messageId: MESSAGE_ID,
208
+ fix: switchClassToObject(node, sourceCode),
209
+ };
210
+ });
211
+ }
212
+
213
+ /** @type {import('eslint').Rule.RuleModule} */
214
+ module.exports = {
215
+ create,
216
+ meta: {
217
+ type: 'suggestion',
218
+ docs: {
219
+ description: 'Disallow classes that only have static members.',
220
+ },
221
+ fixable: 'code',
222
+ messages,
223
+ },
224
+ };