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,9 @@
1
+ 'use strict';
2
+ const getVariableIdentifiers = require('../utils/get-variable-identifiers.js');
3
+ const replaceReferenceIdentifier = require('./replace-reference-identifier.js');
4
+
5
+ const renameVariable = (variable, name, fixer) =>
6
+ getVariableIdentifiers(variable)
7
+ .map(identifier => replaceReferenceIdentifier(identifier, name, fixer));
8
+
9
+ module.exports = renameVariable;
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+ const {getParenthesizedRange} = require('../utils/parentheses.js');
3
+
4
+ function replaceArgument(fixer, node, text, sourceCode) {
5
+ return fixer.replaceTextRange(getParenthesizedRange(node, sourceCode), text);
6
+ }
7
+
8
+ module.exports = replaceArgument;
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+ const {getParentheses} = require('../utils/parentheses.js');
3
+
4
+ function * replaceNodeOrTokenAndSpacesBefore(nodeOrToken, replacement, fixer, sourceCode, tokenStore = sourceCode) {
5
+ const tokens = getParentheses(nodeOrToken, tokenStore);
6
+
7
+ for (const token of tokens) {
8
+ yield * replaceNodeOrTokenAndSpacesBefore(token, '', fixer, sourceCode, tokenStore);
9
+ }
10
+
11
+ let [start, end] = nodeOrToken.range;
12
+
13
+ const textBefore = sourceCode.text.slice(0, start);
14
+ const [trailingSpaces] = textBefore.match(/\s*$/);
15
+ const [lineBreak] = trailingSpaces.match(/(?:\r?\n|\r){0,1}/);
16
+ start -= trailingSpaces.length;
17
+
18
+ yield fixer.replaceTextRange([start, end], `${lineBreak}${replacement}`);
19
+ }
20
+
21
+ module.exports = replaceNodeOrTokenAndSpacesBefore;
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ const isShorthandPropertyValue = require('../utils/is-shorthand-property-value.js');
4
+ const isShorthandPropertyAssignmentPatternLeft = require('../utils/is-shorthand-property-assignment-pattern-left.js');
5
+ const isShorthandImportLocal = require('../utils/is-shorthand-import-local.js');
6
+ const isShorthandExportLocal = require('../utils/is-shorthand-export-local.js');
7
+
8
+ function replaceReferenceIdentifier(identifier, replacement, fixer) {
9
+ if (
10
+ isShorthandPropertyValue(identifier)
11
+ || isShorthandPropertyAssignmentPatternLeft(identifier)
12
+ ) {
13
+ return fixer.replaceText(identifier, `${identifier.name}: ${replacement}`);
14
+ }
15
+
16
+ if (isShorthandImportLocal(identifier)) {
17
+ return fixer.replaceText(identifier, `${identifier.name} as ${replacement}`);
18
+ }
19
+
20
+ if (isShorthandExportLocal(identifier)) {
21
+ return fixer.replaceText(identifier, `${replacement} as ${identifier.name}`);
22
+ }
23
+
24
+ // `typeAnnotation`
25
+ if (identifier.typeAnnotation) {
26
+ return fixer.replaceTextRange(
27
+ [identifier.range[0], identifier.typeAnnotation.range[0]],
28
+ `${replacement}${identifier.optional ? '?' : ''}`,
29
+ );
30
+ }
31
+
32
+ return fixer.replaceText(identifier, replacement);
33
+ }
34
+
35
+ module.exports = replaceReferenceIdentifier;
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ function replaceStringLiteral(fixer, node, text, relativeRangeStart, relativeRangeEnd) {
4
+ const firstCharacterIndex = node.range[0] + 1;
5
+ const start = Number.isInteger(relativeRangeEnd) ? relativeRangeStart + firstCharacterIndex : firstCharacterIndex;
6
+ const end = Number.isInteger(relativeRangeEnd) ? relativeRangeEnd + firstCharacterIndex : node.range[1] - 1;
7
+
8
+ return fixer.replaceTextRange([start, end], text);
9
+ }
10
+
11
+ module.exports = replaceStringLiteral;
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ // Replace `StringLiteral` or `TemplateLiteral` node with raw text
4
+ const replaceStringRaw = (fixer, node, raw) =>
5
+ fixer.replaceTextRange(
6
+ // Ignore quotes and backticks
7
+ [
8
+ node.range[0] + 1,
9
+ node.range[1] - 1,
10
+ ],
11
+ raw,
12
+ );
13
+
14
+ module.exports = replaceStringRaw;
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ const replaceTemplateElement = (fixer, node, replacement) => {
4
+ const {range: [start, end], tail} = node;
5
+ return fixer.replaceTextRange(
6
+ [start + 1, end - (tail ? 1 : 2)],
7
+ replacement,
8
+ );
9
+ };
10
+
11
+ module.exports = replaceTemplateElement;
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+ const {isParenthesized} = require('../utils/parentheses.js');
3
+ const shouldAddParenthesesToNewExpressionCallee = require('../utils/should-add-parentheses-to-new-expression-callee.js');
4
+
5
+ function * switchCallExpressionToNewExpression(node, sourceCode, fixer) {
6
+ yield fixer.insertTextBefore(node, 'new ');
7
+
8
+ const {callee} = node;
9
+ if (
10
+ !isParenthesized(callee, sourceCode)
11
+ && shouldAddParenthesesToNewExpressionCallee(callee)
12
+ ) {
13
+ yield fixer.insertTextBefore(callee, '(');
14
+ yield fixer.insertTextAfter(callee, ')');
15
+ }
16
+ }
17
+
18
+ module.exports = switchCallExpressionToNewExpression;
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+ const isNewExpressionWithParentheses = require('../utils/is-new-expression-with-parentheses.js');
3
+ const {isParenthesized} = require('../utils/parentheses.js');
4
+ const isOnSameLine = require('../utils/is-on-same-line.js');
5
+ const addParenthesizesToReturnOrThrowExpression = require('./add-parenthesizes-to-return-or-throw-expression.js');
6
+ const removeSpaceAfter = require('./remove-spaces-after.js');
7
+
8
+ function * switchNewExpressionToCallExpression(newExpression, sourceCode, fixer) {
9
+ const newToken = sourceCode.getFirstToken(newExpression);
10
+ yield fixer.remove(newToken);
11
+ yield removeSpaceAfter(newToken, sourceCode, fixer);
12
+
13
+ if (!isNewExpressionWithParentheses(newExpression, sourceCode)) {
14
+ yield fixer.insertTextAfter(newExpression, '()');
15
+ }
16
+
17
+ /*
18
+ Remove `new` from this code will makes the function return `undefined`
19
+
20
+ ```js
21
+ () => {
22
+ return new // comment
23
+ Foo()
24
+ }
25
+ ```
26
+ */
27
+ if (!isOnSameLine(newToken, newExpression.callee) && !isParenthesized(newExpression, sourceCode)) {
28
+ // Ideally, we should use first parenthesis of the `callee`, and should check spaces after the `new` token
29
+ // But adding extra parentheses is harmless, no need to be too complicated
30
+ yield * addParenthesizesToReturnOrThrowExpression(fixer, newExpression.parent, sourceCode);
31
+ }
32
+ }
33
+
34
+ module.exports = switchNewExpressionToCallExpression;
@@ -0,0 +1,364 @@
1
+ 'use strict';
2
+ const {defaultsDeep} = require('./utils/lodash.js');
3
+ const {getStringIfConstant} = require('@eslint-community/eslint-utils');
4
+ const {isCallExpression} = require('./ast/index.js');
5
+
6
+ const MESSAGE_ID = 'importStyle';
7
+ const messages = {
8
+ [MESSAGE_ID]: 'Use {{allowedStyles}} import for module `{{moduleName}}`.',
9
+ };
10
+
11
+ const getActualImportDeclarationStyles = importDeclaration => {
12
+ const {specifiers} = importDeclaration;
13
+
14
+ if (specifiers.length === 0) {
15
+ return ['unassigned'];
16
+ }
17
+
18
+ const styles = new Set();
19
+
20
+ for (const specifier of specifiers) {
21
+ if (specifier.type === 'ImportDefaultSpecifier') {
22
+ styles.add('default');
23
+ continue;
24
+ }
25
+
26
+ if (specifier.type === 'ImportNamespaceSpecifier') {
27
+ styles.add('namespace');
28
+ continue;
29
+ }
30
+
31
+ if (specifier.type === 'ImportSpecifier') {
32
+ if (specifier.imported.type === 'Identifier' && specifier.imported.name === 'default') {
33
+ styles.add('default');
34
+ continue;
35
+ }
36
+
37
+ styles.add('named');
38
+ continue;
39
+ }
40
+ }
41
+
42
+ return [...styles];
43
+ };
44
+
45
+ const getActualExportDeclarationStyles = exportDeclaration => {
46
+ const {specifiers} = exportDeclaration;
47
+
48
+ if (specifiers.length === 0) {
49
+ return ['unassigned'];
50
+ }
51
+
52
+ const styles = new Set();
53
+
54
+ for (const specifier of specifiers) {
55
+ if (specifier.type === 'ExportSpecifier') {
56
+ if (specifier.exported.type === 'Identifier' && specifier.exported.name === 'default') {
57
+ styles.add('default');
58
+ continue;
59
+ }
60
+
61
+ styles.add('named');
62
+ continue;
63
+ }
64
+ }
65
+
66
+ return [...styles];
67
+ };
68
+
69
+ const getActualAssignmentTargetImportStyles = assignmentTarget => {
70
+ if (assignmentTarget.type === 'Identifier' || assignmentTarget.type === 'ArrayPattern') {
71
+ return ['namespace'];
72
+ }
73
+
74
+ if (assignmentTarget.type === 'ObjectPattern') {
75
+ if (assignmentTarget.properties.length === 0) {
76
+ return ['unassigned'];
77
+ }
78
+
79
+ const styles = new Set();
80
+
81
+ for (const property of assignmentTarget.properties) {
82
+ if (property.type === 'RestElement') {
83
+ styles.add('named');
84
+ continue;
85
+ }
86
+
87
+ if (property.key.type === 'Identifier') {
88
+ if (property.key.name === 'default') {
89
+ styles.add('default');
90
+ } else {
91
+ styles.add('named');
92
+ }
93
+ }
94
+ }
95
+
96
+ return [...styles];
97
+ }
98
+
99
+ // Next line is not test-coverable until unforceable changes to the language
100
+ // like an addition of new AST node types usable in `const __HERE__ = foo;`.
101
+ // An exotic custom parser or a bug in one could cover it too.
102
+ /* c8 ignore next */
103
+ return [];
104
+ };
105
+
106
+ const isAssignedDynamicImport = node =>
107
+ node.parent.type === 'AwaitExpression'
108
+ && node.parent.argument === node
109
+ && node.parent.parent.type === 'VariableDeclarator'
110
+ && node.parent.parent.init === node.parent;
111
+
112
+ // Keep this alphabetically sorted for easier maintenance
113
+ const defaultStyles = {
114
+ chalk: {
115
+ default: true,
116
+ },
117
+ path: {
118
+ default: true,
119
+ },
120
+ util: {
121
+ named: true,
122
+ },
123
+ };
124
+
125
+ /** @param {import('eslint').Rule.RuleContext} context */
126
+ const create = context => {
127
+ let [
128
+ {
129
+ styles = {},
130
+ extendDefaultStyles = true,
131
+ checkImport = true,
132
+ checkDynamicImport = true,
133
+ checkExportFrom = false,
134
+ checkRequire = true,
135
+ } = {},
136
+ ] = context.options;
137
+
138
+ styles = extendDefaultStyles
139
+ ? defaultsDeep({}, styles, defaultStyles)
140
+ : styles;
141
+
142
+ styles = new Map(
143
+ Object.entries(styles).map(
144
+ ([moduleName, styles]) =>
145
+ [moduleName, new Set(Object.entries(styles).filter(([, isAllowed]) => isAllowed).map(([style]) => style))],
146
+ ),
147
+ );
148
+
149
+ const {sourceCode} = context;
150
+
151
+ const report = (node, moduleName, actualImportStyles, allowedImportStyles, isRequire = false) => {
152
+ if (!allowedImportStyles || allowedImportStyles.size === 0) {
153
+ return;
154
+ }
155
+
156
+ let effectiveAllowedImportStyles = allowedImportStyles;
157
+
158
+ // For `require`, `'default'` style allows both `x = require('x')` (`'namespace'` style) and
159
+ // `{default: x} = require('x')` (`'default'` style) since we don't know in advance
160
+ // whether `'x'` is a compiled ES6 module (with `default` key) or a CommonJS module and `require`
161
+ // does not provide any automatic interop for this, so the user may have to use either of these.
162
+ if (isRequire && allowedImportStyles.has('default') && !allowedImportStyles.has('namespace')) {
163
+ effectiveAllowedImportStyles = new Set(allowedImportStyles);
164
+ effectiveAllowedImportStyles.add('namespace');
165
+ }
166
+
167
+ if (actualImportStyles.every(style => effectiveAllowedImportStyles.has(style))) {
168
+ return;
169
+ }
170
+
171
+ const data = {
172
+ allowedStyles: new Intl.ListFormat('en-US', {type: 'disjunction'}).format([...allowedImportStyles.keys()]),
173
+ moduleName,
174
+ };
175
+
176
+ context.report({
177
+ node,
178
+ messageId: MESSAGE_ID,
179
+ data,
180
+ });
181
+ };
182
+
183
+ if (checkImport) {
184
+ context.on('ImportDeclaration', node => {
185
+ const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
186
+
187
+ const allowedImportStyles = styles.get(moduleName);
188
+ const actualImportStyles = getActualImportDeclarationStyles(node);
189
+
190
+ report(node, moduleName, actualImportStyles, allowedImportStyles);
191
+ });
192
+ }
193
+
194
+ if (checkDynamicImport) {
195
+ context.on('ImportExpression', node => {
196
+ if (isAssignedDynamicImport(node)) {
197
+ return;
198
+ }
199
+
200
+ const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
201
+ const allowedImportStyles = styles.get(moduleName);
202
+ const actualImportStyles = ['unassigned'];
203
+
204
+ report(node, moduleName, actualImportStyles, allowedImportStyles);
205
+ });
206
+
207
+ context.on('VariableDeclarator', node => {
208
+ if (!(
209
+ node.init?.type === 'AwaitExpression'
210
+ && node.init.argument.type === 'ImportExpression'
211
+ )) {
212
+ return;
213
+ }
214
+
215
+ const assignmentTargetNode = node.id;
216
+ const moduleNameNode = node.init.argument.source;
217
+ const moduleName = getStringIfConstant(moduleNameNode, sourceCode.getScope(moduleNameNode));
218
+
219
+ if (!moduleName) {
220
+ return;
221
+ }
222
+
223
+ const allowedImportStyles = styles.get(moduleName);
224
+ const actualImportStyles = getActualAssignmentTargetImportStyles(assignmentTargetNode);
225
+
226
+ report(node, moduleName, actualImportStyles, allowedImportStyles);
227
+ });
228
+ }
229
+
230
+ if (checkExportFrom) {
231
+ context.on('ExportAllDeclaration', node => {
232
+ const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
233
+
234
+ const allowedImportStyles = styles.get(moduleName);
235
+ const actualImportStyles = ['namespace'];
236
+
237
+ report(node, moduleName, actualImportStyles, allowedImportStyles);
238
+ });
239
+
240
+ context.on('ExportNamedDeclaration', node => {
241
+ const moduleName = getStringIfConstant(node.source, sourceCode.getScope(node.source));
242
+
243
+ const allowedImportStyles = styles.get(moduleName);
244
+ const actualImportStyles = getActualExportDeclarationStyles(node);
245
+
246
+ report(node, moduleName, actualImportStyles, allowedImportStyles);
247
+ });
248
+ }
249
+
250
+ if (checkRequire) {
251
+ context.on('CallExpression', node => {
252
+ if (!(
253
+ isCallExpression(node, {
254
+ name: 'require',
255
+ argumentsLength: 1,
256
+ optionalCall: false,
257
+ optionalMember: false,
258
+ })
259
+ && (node.parent.type === 'ExpressionStatement' && node.parent.expression === node)
260
+ )) {
261
+ return;
262
+ }
263
+
264
+ const moduleName = getStringIfConstant(node.arguments[0], sourceCode.getScope(node.arguments[0]));
265
+ const allowedImportStyles = styles.get(moduleName);
266
+ const actualImportStyles = ['unassigned'];
267
+
268
+ report(node, moduleName, actualImportStyles, allowedImportStyles, true);
269
+ });
270
+
271
+ context.on('VariableDeclarator', node => {
272
+ if (!(
273
+ node.init?.type === 'CallExpression'
274
+ && node.init.callee.type === 'Identifier'
275
+ && node.init.callee.name === 'require'
276
+ )) {
277
+ return;
278
+ }
279
+
280
+ const assignmentTargetNode = node.id;
281
+ const moduleNameNode = node.init.arguments[0];
282
+ const moduleName = getStringIfConstant(moduleNameNode, sourceCode.getScope(moduleNameNode));
283
+
284
+ if (!moduleName) {
285
+ return;
286
+ }
287
+
288
+ const allowedImportStyles = styles.get(moduleName);
289
+ const actualImportStyles = getActualAssignmentTargetImportStyles(assignmentTargetNode);
290
+
291
+ report(node, moduleName, actualImportStyles, allowedImportStyles, true);
292
+ });
293
+ }
294
+ };
295
+
296
+ const schema = {
297
+ type: 'array',
298
+ additionalItems: false,
299
+ items: [
300
+ {
301
+ type: 'object',
302
+ additionalProperties: false,
303
+ properties: {
304
+ checkImport: {
305
+ type: 'boolean',
306
+ },
307
+ checkDynamicImport: {
308
+ type: 'boolean',
309
+ },
310
+ checkExportFrom: {
311
+ type: 'boolean',
312
+ },
313
+ checkRequire: {
314
+ type: 'boolean',
315
+ },
316
+ extendDefaultStyles: {
317
+ type: 'boolean',
318
+ },
319
+ styles: {
320
+ $ref: '#/definitions/moduleStyles',
321
+ },
322
+ },
323
+ },
324
+ ],
325
+ definitions: {
326
+ moduleStyles: {
327
+ type: 'object',
328
+ additionalProperties: {
329
+ $ref: '#/definitions/styles',
330
+ },
331
+ },
332
+ styles: {
333
+ anyOf: [
334
+ {
335
+ enum: [
336
+ false,
337
+ ],
338
+ },
339
+ {
340
+ $ref: '#/definitions/booleanObject',
341
+ },
342
+ ],
343
+ },
344
+ booleanObject: {
345
+ type: 'object',
346
+ additionalProperties: {
347
+ type: 'boolean',
348
+ },
349
+ },
350
+ },
351
+ };
352
+
353
+ /** @type {import('eslint').Rule.RuleModule} */
354
+ module.exports = {
355
+ create,
356
+ meta: {
357
+ type: 'problem',
358
+ docs: {
359
+ description: 'Enforce specific import styles per module.',
360
+ },
361
+ schema,
362
+ messages,
363
+ },
364
+ };
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+ const {GlobalReferenceTracker} = require('./utils/global-reference-tracker.js');
3
+ const builtins = require('./utils/builtins.js');
4
+ const {
5
+ switchCallExpressionToNewExpression,
6
+ switchNewExpressionToCallExpression,
7
+ } = require('./fix/index.js');
8
+
9
+ const messages = {
10
+ enforce: 'Use `new {{name}}()` instead of `{{name}}()`.',
11
+ disallow: 'Use `{{name}}()` instead of `new {{name}}()`.',
12
+ };
13
+
14
+ function enforceNewExpression({node, path: [name]}, sourceCode) {
15
+ if (name === 'Object') {
16
+ const {parent} = node;
17
+ if (
18
+ parent.type === 'BinaryExpression'
19
+ && (parent.operator === '===' || parent.operator === '!==')
20
+ && (parent.left === node || parent.right === node)
21
+ ) {
22
+ return;
23
+ }
24
+ }
25
+
26
+ return {
27
+ node,
28
+ messageId: 'enforce',
29
+ data: {name},
30
+ fix: fixer => switchCallExpressionToNewExpression(node, sourceCode, fixer),
31
+ };
32
+ }
33
+
34
+ function enforceCallExpression({node, path: [name]}, sourceCode) {
35
+ const problem = {
36
+ node,
37
+ messageId: 'disallow',
38
+ data: {name},
39
+ };
40
+
41
+ if (name !== 'String' && name !== 'Boolean' && name !== 'Number') {
42
+ problem.fix = function * (fixer) {
43
+ yield * switchNewExpressionToCallExpression(node, sourceCode, fixer);
44
+ };
45
+ }
46
+
47
+ return problem;
48
+ }
49
+
50
+ /** @param {import('eslint').Rule.RuleContext} context */
51
+ const create = context => {
52
+ const {sourceCode} = context;
53
+ const newExpressionTracker = new GlobalReferenceTracker({
54
+ objects: builtins.disallowNew,
55
+ type: GlobalReferenceTracker.CONSTRUCT,
56
+ handle: reference => enforceCallExpression(reference, sourceCode),
57
+ });
58
+ const callExpressionTracker = new GlobalReferenceTracker({
59
+ objects: builtins.enforceNew,
60
+ type: GlobalReferenceTracker.CALL,
61
+ handle: reference => enforceNewExpression(reference, sourceCode),
62
+ });
63
+
64
+ return {
65
+ * 'Program:exit'(program) {
66
+ const scope = sourceCode.getScope(program);
67
+
68
+ yield * newExpressionTracker.track(scope);
69
+ yield * callExpressionTracker.track(scope);
70
+ },
71
+ };
72
+ };
73
+
74
+ /** @type {import('eslint').Rule.RuleModule} */
75
+ module.exports = {
76
+ create,
77
+ meta: {
78
+ type: 'suggestion',
79
+ docs: {
80
+ description: 'Enforce the use of `new` for all builtins, except `String`, `Number`, `Boolean`, `Symbol` and `BigInt`.',
81
+ },
82
+ fixable: 'code',
83
+ messages,
84
+ },
85
+ };
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ const MESSAGE_ID = 'no-abusive-eslint-disable';
4
+ const messages = {
5
+ [MESSAGE_ID]: 'Specify the rules you want to disable.',
6
+ };
7
+
8
+ const disableRegex = /^eslint-disable(?:-next-line|-line)?(?<ruleId>$|(?:\s+(?:@(?:[\w-]+\/){1,2})?[\w-]+)?)/;
9
+
10
+ /** @param {import('eslint').Rule.RuleContext} context */
11
+ const create = () => ({
12
+ * Program(node) {
13
+ for (const comment of node.comments) {
14
+ const value = comment.value.trim();
15
+ const result = disableRegex.exec(value);
16
+
17
+ if (
18
+ result // It's a eslint-disable comment
19
+ && !result.groups.ruleId // But it did not specify any rules
20
+ ) {
21
+ yield {
22
+ // Can't set it at the given location as the warning
23
+ // will be ignored due to the disable comment
24
+ loc: {
25
+ start: {
26
+ ...comment.loc.start,
27
+ column: -1,
28
+ },
29
+ end: comment.loc.end,
30
+ },
31
+ messageId: MESSAGE_ID,
32
+ };
33
+ }
34
+ }
35
+ },
36
+ });
37
+
38
+ /** @type {import('eslint').Rule.RuleModule} */
39
+ module.exports = {
40
+ create,
41
+ meta: {
42
+ type: 'suggestion',
43
+ docs: {
44
+ description: 'Enforce specifying rules to disable in `eslint-disable` comments.',
45
+ },
46
+ messages,
47
+ },
48
+ };