eslint-plugin-unicorn 61.0.2 → 62.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/index.js +1 -1
  2. package/package.json +28 -26
  3. package/readme.md +3 -0
  4. package/rules/ast/index.js +2 -0
  5. package/rules/ast/is-empty-array-expression.js +5 -0
  6. package/rules/ast/is-undefined.js +1 -1
  7. package/rules/ast/literal.js +1 -0
  8. package/rules/catch-error-name.js +1 -1
  9. package/rules/consistent-date-clone.js +1 -1
  10. package/rules/consistent-existence-index-check.js +1 -1
  11. package/rules/consistent-function-scoping.js +46 -1
  12. package/rules/escape-case.js +1 -1
  13. package/rules/explicit-length-check.js +1 -1
  14. package/rules/fix/add-parenthesizes-to-return-or-throw-expression.js +13 -1
  15. package/rules/fix/append-argument.js +14 -1
  16. package/rules/fix/fix-space-around-keywords.js +13 -2
  17. package/rules/fix/index.js +5 -4
  18. package/rules/fix/remove-argument.js +18 -8
  19. package/rules/fix/remove-expression-statement.js +34 -0
  20. package/rules/fix/remove-method-call.js +15 -4
  21. package/rules/fix/remove-parentheses.js +13 -2
  22. package/rules/fix/remove-spaces-after.js +15 -3
  23. package/rules/fix/remove-specifier.js +14 -1
  24. package/rules/fix/rename-variable.js +3 -2
  25. package/rules/fix/replace-argument.js +14 -2
  26. package/rules/fix/replace-member-expression-property.js +20 -4
  27. package/rules/fix/replace-node-or-token-and-spaces-before.js +16 -4
  28. package/rules/fix/replace-reference-identifier.js +5 -5
  29. package/rules/fix/replace-string-raw.js +3 -5
  30. package/rules/fix/replace-template-element.js +3 -3
  31. package/rules/fix/switch-call-expression-to-new-expression.js +15 -3
  32. package/rules/fix/switch-new-expression-to-call-expression.js +17 -6
  33. package/rules/index.js +3 -0
  34. package/rules/new-for-builtins.js +19 -28
  35. package/rules/no-anonymous-default-export.js +5 -4
  36. package/rules/no-array-callback-reference.js +3 -4
  37. package/rules/no-array-for-each.js +11 -9
  38. package/rules/no-array-method-this-argument.js +15 -17
  39. package/rules/no-await-expression-member.js +50 -54
  40. package/rules/no-await-in-promise-methods.js +1 -2
  41. package/rules/no-console-spaces.js +1 -1
  42. package/rules/no-document-cookie.js +1 -1
  43. package/rules/no-for-loop.js +1 -1
  44. package/rules/no-hex-escape.js +1 -1
  45. package/rules/no-immediate-mutation.js +778 -0
  46. package/rules/no-instanceof-builtins.js +7 -8
  47. package/rules/no-lonely-if.js +13 -11
  48. package/rules/no-magic-array-flat-depth.js +1 -1
  49. package/rules/no-named-default.js +6 -5
  50. package/rules/no-negated-condition.js +13 -11
  51. package/rules/no-negation-in-equality-check.js +4 -4
  52. package/rules/no-new-array.js +1 -1
  53. package/rules/no-new-buffer.js +4 -4
  54. package/rules/no-single-promise-in-promise-methods.js +9 -7
  55. package/rules/no-static-only-class.js +9 -9
  56. package/rules/no-typeof-undefined.js +4 -4
  57. package/rules/no-unnecessary-array-flat-depth.js +1 -1
  58. package/rules/no-unnecessary-await.js +4 -4
  59. package/rules/no-unreadable-array-destructuring.js +2 -2
  60. package/rules/no-unreadable-iife.js +1 -1
  61. package/rules/no-useless-collection-argument.js +101 -0
  62. package/rules/no-useless-fallback-in-spread.js +2 -2
  63. package/rules/no-useless-length-check.js +2 -2
  64. package/rules/no-useless-promise-resolve-reject.js +37 -41
  65. package/rules/no-useless-spread.js +20 -7
  66. package/rules/no-useless-switch-case.js +1 -1
  67. package/rules/no-useless-undefined.js +9 -46
  68. package/rules/no-zero-fractions.js +3 -3
  69. package/rules/prefer-array-find.js +11 -11
  70. package/rules/prefer-array-flat-map.js +1 -1
  71. package/rules/prefer-array-flat.js +8 -11
  72. package/rules/prefer-array-some.js +3 -3
  73. package/rules/prefer-at.js +12 -12
  74. package/rules/prefer-class-fields.js +1 -1
  75. package/rules/prefer-classlist-toggle.js +10 -9
  76. package/rules/prefer-code-point.js +25 -37
  77. package/rules/prefer-date-now.js +3 -3
  78. package/rules/prefer-dom-node-remove.js +3 -3
  79. package/rules/prefer-export-from.js +11 -10
  80. package/rules/prefer-global-this.js +1 -0
  81. package/rules/prefer-import-meta-properties.js +3 -3
  82. package/rules/prefer-json-parse-buffer.js +1 -1
  83. package/rules/prefer-logical-operator-over-ternary.js +8 -7
  84. package/rules/prefer-math-min-max.js +1 -1
  85. package/rules/prefer-math-trunc.js +2 -2
  86. package/rules/prefer-modern-math-apis.js +5 -5
  87. package/rules/prefer-module.js +27 -26
  88. package/rules/prefer-negative-index.js +1 -2
  89. package/rules/prefer-node-protocol.js +9 -0
  90. package/rules/prefer-number-properties.js +7 -9
  91. package/rules/prefer-object-from-entries.js +9 -8
  92. package/rules/prefer-prototype-methods.js +7 -7
  93. package/rules/prefer-query-selector.js +1 -1
  94. package/rules/prefer-regexp-test.js +5 -5
  95. package/rules/prefer-response-static-json.js +85 -0
  96. package/rules/prefer-set-size.js +4 -3
  97. package/rules/prefer-single-call.js +35 -26
  98. package/rules/prefer-spread.js +27 -24
  99. package/rules/prefer-string-raw.js +90 -24
  100. package/rules/prefer-string-replace-all.js +2 -2
  101. package/rules/prefer-string-slice.js +8 -9
  102. package/rules/prefer-string-starts-ends-with.js +3 -3
  103. package/rules/prefer-structured-clone.js +2 -2
  104. package/rules/prefer-switch.js +15 -12
  105. package/rules/prefer-ternary.js +7 -7
  106. package/rules/prevent-abbreviations.js +2 -2
  107. package/rules/require-array-join-separator.js +1 -1
  108. package/rules/require-module-attributes.js +1 -1
  109. package/rules/require-number-to-fixed-digits-argument.js +1 -1
  110. package/rules/require-post-message-target-origin.js +1 -1
  111. package/rules/rule/get-documentation-url.js +9 -0
  112. package/rules/rule/index.js +9 -0
  113. package/rules/rule/to-eslint-create.js +51 -0
  114. package/rules/rule/to-eslint-listener.js +40 -0
  115. package/rules/rule/to-eslint-problem.js +38 -0
  116. package/rules/rule/to-eslint-rule-fixer.js +49 -0
  117. package/rules/rule/to-eslint-rule.js +38 -0
  118. package/rules/rule/to-eslint-rules.js +13 -0
  119. package/rules/rule/unicorn-context.js +36 -0
  120. package/rules/rule/unicorn-listeners.js +65 -0
  121. package/rules/rule/utilities.js +27 -0
  122. package/rules/shared/negative-index.js +13 -3
  123. package/rules/shared/no-array-mutate-rule.js +1 -2
  124. package/rules/shared/no-unnecessary-length-or-infinity-rule.js +1 -1
  125. package/rules/string-content.js +2 -1
  126. package/rules/switch-case-braces.js +10 -8
  127. package/rules/template-indent.js +1 -1
  128. package/rules/text-encoding-identifier-case.js +44 -23
  129. package/rules/throw-new-error.js +1 -1
  130. package/rules/utils/get-call-expression-arguments-text.js +21 -8
  131. package/rules/utils/get-call-or-new-expression-tokens.js +67 -0
  132. package/rules/utils/get-class-head-location.js +9 -5
  133. package/rules/utils/get-indent-string.js +12 -1
  134. package/rules/utils/get-sibling-node.js +38 -0
  135. package/rules/utils/get-switch-case-head-location.js +9 -3
  136. package/rules/utils/global-reference-tracker.js +18 -17
  137. package/rules/utils/has-same-range.js +3 -5
  138. package/rules/utils/index.js +3 -2
  139. package/rules/utils/is-new-expression-with-parentheses.js +9 -3
  140. package/rules/utils/is-node-matches.js +19 -7
  141. package/rules/utils/is-on-same-line.js +6 -3
  142. package/rules/utils/is-shorthand-export-local.js +2 -2
  143. package/rules/utils/is-shorthand-import-local.js +2 -2
  144. package/rules/utils/needs-semicolon.js +10 -3
  145. package/rules/utils/parentheses.js +26 -21
  146. package/rules/utils/rule.js +5 -153
  147. package/rules/utils/should-add-parentheses-to-member-expression-object.js +8 -4
  148. package/rules/utils/to-location.js +10 -4
  149. package/rules/utils/get-call-expression-tokens.js +0 -30
  150. package/rules/utils/get-previous-node.js +0 -20
package/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import createDeprecatedRules from './rules/utils/create-deprecated-rules.js';
2
2
  import flatConfigBase from './configs/flat-config-base.js';
3
3
  import * as rawRules from './rules/index.js';
4
- import {createRules} from './rules/utils/rule.js';
4
+ import {createRules} from './rules/rule/index.js';
5
5
  import packageJson from './package.json' with {type: 'json'};
6
6
 
7
7
  const rules = createRules(rawRules);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-unicorn",
3
- "version": "61.0.2",
3
+ "version": "62.0.0",
4
4
  "description": "More than 100 powerful ESLint rules",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/eslint-plugin-unicorn",
@@ -56,60 +56,62 @@
56
56
  "xo"
57
57
  ],
58
58
  "dependencies": {
59
- "@babel/helper-validator-identifier": "^7.27.1",
60
- "@eslint-community/eslint-utils": "^4.7.0",
61
- "@eslint/plugin-kit": "^0.3.3",
59
+ "@babel/helper-validator-identifier": "^7.28.5",
60
+ "@eslint-community/eslint-utils": "^4.9.0",
61
+ "@eslint/plugin-kit": "^0.4.0",
62
62
  "change-case": "^5.4.4",
63
- "ci-info": "^4.3.0",
63
+ "ci-info": "^4.3.1",
64
64
  "clean-regexp": "^1.0.0",
65
- "core-js-compat": "^3.44.0",
65
+ "core-js-compat": "^3.46.0",
66
66
  "esquery": "^1.6.0",
67
67
  "find-up-simple": "^1.0.1",
68
- "globals": "^16.3.0",
68
+ "globals": "^16.4.0",
69
69
  "indent-string": "^5.0.0",
70
70
  "is-builtin-module": "^5.0.0",
71
71
  "jsesc": "^3.1.0",
72
72
  "pluralize": "^8.0.0",
73
73
  "regexp-tree": "^0.1.27",
74
- "regjsparser": "^0.12.0",
75
- "semver": "^7.7.2",
76
- "strip-indent": "^4.0.0"
74
+ "regjsparser": "^0.13.0",
75
+ "semver": "^7.7.3",
76
+ "strip-indent": "^4.1.1"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@babel/code-frame": "^7.27.1",
80
- "@babel/core": "^7.28.0",
81
- "@babel/eslint-parser": "^7.28.0",
80
+ "@babel/core": "^7.28.5",
81
+ "@babel/eslint-parser": "^7.28.5",
82
82
  "@eslint/eslintrc": "^3.3.1",
83
83
  "@lubien/fixture-beta-package": "^1.0.0-beta.1",
84
- "@typescript-eslint/parser": "^8.37.0",
84
+ "@typescript-eslint/parser": "^8.46.2",
85
+ "@typescript-eslint/types": "^8.46.2",
85
86
  "ava": "^6.4.1",
86
87
  "c8": "^10.1.3",
87
88
  "enquirer": "^2.4.1",
88
- "eslint": "^9.29.0",
89
+ "eslint": "^9.38.0",
89
90
  "eslint-ava-rule-tester": "^5.0.1",
90
- "eslint-config-xo": "^0.47.0",
91
- "eslint-doc-generator": "^2.2.2",
92
- "eslint-plugin-eslint-plugin": "^6.5.0",
93
- "eslint-plugin-jsdoc": "^51.4.1",
94
- "eslint-remote-tester": "^4.0.2",
91
+ "eslint-config-xo": "^0.49.0",
92
+ "eslint-doc-generator": "^2.3.0",
93
+ "eslint-plugin-eslint-plugin": "^7.1.0",
94
+ "eslint-plugin-jsdoc": "^61.1.6",
95
+ "eslint-plugin-unicorn": "^61.0.2",
96
+ "eslint-remote-tester": "^4.0.3",
95
97
  "eslint-remote-tester-repositories": "^2.0.2",
96
98
  "espree": "^10.4.0",
97
- "listr2": "^9.0.1",
99
+ "listr2": "^9.0.5",
98
100
  "lodash-es": "^4.17.21",
99
101
  "markdownlint-cli": "^0.45.0",
100
- "nano-spawn": "^1.0.2",
101
- "node-style-text": "^1.0.0",
102
+ "nano-spawn": "^2.0.0",
103
+ "node-style-text": "^2.1.2",
102
104
  "npm-package-json-lint": "^9.0.0",
103
105
  "npm-run-all2": "^8.0.4",
104
106
  "open-editor": "^5.1.0",
105
107
  "outdent": "^0.8.0",
106
- "pretty-ms": "^9.2.0",
107
- "typescript": "^5.8.3",
108
+ "pretty-ms": "^9.3.0",
109
+ "typescript": "^5.9.3",
108
110
  "vue-eslint-parser": "^10.2.0",
109
- "yaml": "^2.8.0"
111
+ "yaml": "^2.8.1"
110
112
  },
111
113
  "peerDependencies": {
112
- "eslint": ">=9.29.0"
114
+ "eslint": ">=9.38.0"
113
115
  },
114
116
  "ava": {
115
117
  "files": [
package/readme.md CHANGED
@@ -90,6 +90,7 @@ export default [
90
90
  | [no-empty-file](docs/rules/no-empty-file.md) | Disallow empty files. | ✅ ☑️ | | |
91
91
  | [no-for-loop](docs/rules/no-for-loop.md) | Do not use a `for` loop that can be replaced with a `for-of` loop. | ✅ | 🔧 | 💡 |
92
92
  | [no-hex-escape](docs/rules/no-hex-escape.md) | Enforce the use of Unicode escapes instead of hexadecimal escapes. | ✅ ☑️ | 🔧 | |
93
+ | [no-immediate-mutation](docs/rules/no-immediate-mutation.md) | Disallow immediate mutation after variable assignment. | ✅ | 🔧 | 💡 |
93
94
  | [no-instanceof-builtins](docs/rules/no-instanceof-builtins.md) | Disallow `instanceof` with built-in objects | ✅ ☑️ | 🔧 | 💡 |
94
95
  | [no-invalid-fetch-options](docs/rules/no-invalid-fetch-options.md) | Disallow invalid options in `fetch()` and `new Request()`. | ✅ ☑️ | | |
95
96
  | [no-invalid-remove-event-listener](docs/rules/no-invalid-remove-event-listener.md) | Prevent calling `EventTarget#removeEventListener()` with the result of an expression. | ✅ ☑️ | | |
@@ -118,6 +119,7 @@ export default [
118
119
  | [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) | Disallow unreadable array destructuring. | ✅ ☑️ | 🔧 | |
119
120
  | [no-unreadable-iife](docs/rules/no-unreadable-iife.md) | Disallow unreadable IIFEs. | ✅ ☑️ | | |
120
121
  | [no-unused-properties](docs/rules/no-unused-properties.md) | Disallow unused object properties. | | | |
122
+ | [no-useless-collection-argument](docs/rules/no-useless-collection-argument.md) | Disallow useless values or fallbacks in `Set`, `Map`, `WeakSet`, or `WeakMap`. | ✅ ☑️ | 🔧 | |
121
123
  | [no-useless-error-capture-stack-trace](docs/rules/no-useless-error-capture-stack-trace.md) | Disallow unnecessary `Error.captureStackTrace(…)`. | ✅ ☑️ | 🔧 | |
122
124
  | [no-useless-fallback-in-spread](docs/rules/no-useless-fallback-in-spread.md) | Disallow useless fallback when spreading in object literals. | ✅ ☑️ | 🔧 | |
123
125
  | [no-useless-length-check](docs/rules/no-useless-length-check.md) | Disallow useless array length check. | ✅ ☑️ | 🔧 | |
@@ -169,6 +171,7 @@ export default [
169
171
  | [prefer-query-selector](docs/rules/prefer-query-selector.md) | Prefer `.querySelector()` over `.getElementById()`, `.querySelectorAll()` over `.getElementsByClassName()` and `.getElementsByTagName()` and `.getElementsByName()`. | ✅ | 🔧 | |
170
172
  | [prefer-reflect-apply](docs/rules/prefer-reflect-apply.md) | Prefer `Reflect.apply()` over `Function#apply()`. | ✅ ☑️ | 🔧 | |
171
173
  | [prefer-regexp-test](docs/rules/prefer-regexp-test.md) | Prefer `RegExp#test()` over `String#match()` and `RegExp#exec()`. | ✅ ☑️ | 🔧 | 💡 |
174
+ | [prefer-response-static-json](docs/rules/prefer-response-static-json.md) | Prefer `Response.json()` over `new Response(JSON.stringify())`. | ✅ ☑️ | 🔧 | |
172
175
  | [prefer-set-has](docs/rules/prefer-set-has.md) | Prefer `Set#has()` over `Array#includes()` when checking for existence or non-existence. | ✅ ☑️ | 🔧 | 💡 |
173
176
  | [prefer-set-size](docs/rules/prefer-set-size.md) | Prefer using `Set#size` instead of `Array#length`. | ✅ ☑️ | 🔧 | |
174
177
  | [prefer-single-call](docs/rules/prefer-single-call.md) | Enforce combining multiple `Array#push()`, `Element#classList.{add,remove}()`, and `importScripts()` into one call. | ✅ ☑️ | 🔧 | 💡 |
@@ -5,6 +5,7 @@ export {
5
5
  isBigIntLiteral,
6
6
  isNullLiteral,
7
7
  isRegexLiteral,
8
+ isEmptyStringLiteral,
8
9
  } from './literal.js';
9
10
 
10
11
  export {
@@ -16,6 +17,7 @@ export {
16
17
  export {default as isArrowFunctionBody} from './is-arrow-function-body.js';
17
18
  export {default as isDirective} from './is-directive.js';
18
19
  export {default as isEmptyNode} from './is-empty-node.js';
20
+ export {default as isEmptyArrayExpression} from './is-empty-array-expression.js';
19
21
  export {default as isExpressionStatement} from './is-expression-statement.js';
20
22
  export {default as isFunction} from './is-function.js';
21
23
  export {default as isMemberExpression} from './is-member-expression.js';
@@ -0,0 +1,5 @@
1
+ const isEmptyArrayExpression = node =>
2
+ node.type === 'ArrayExpression'
3
+ && node.elements.length === 0;
4
+
5
+ export default isEmptyArrayExpression;
@@ -1,3 +1,3 @@
1
1
  export default function isUndefined(node) {
2
- return node.type === 'Identifier' && node.name === 'undefined';
2
+ return node?.type === 'Identifier' && node.name === 'undefined';
3
3
  }
@@ -21,3 +21,4 @@ export const isNullLiteral = node => isLiteral(node, null);
21
21
 
22
22
  export const isBigIntLiteral = node => node.type === 'Literal' && Boolean(node.bigint);
23
23
 
24
+ export const isEmptyStringLiteral = node => isLiteral(node, '');
@@ -95,7 +95,7 @@ const create = context => {
95
95
  };
96
96
 
97
97
  if (fixedName) {
98
- problem.fix = fixer => renameVariable(variable, fixedName, fixer);
98
+ problem.fix = fixer => renameVariable(variable, fixedName, context, fixer);
99
99
  }
100
100
 
101
101
  return problem;
@@ -32,7 +32,7 @@ const create = context => ({
32
32
  end: sourceCode.getLoc(callExpression).end,
33
33
  },
34
34
  messageId: MESSAGE_ID_ERROR,
35
- fix: fixer => removeMethodCall(fixer, callExpression, sourceCode),
35
+ fix: fixer => removeMethodCall(fixer, callExpression, context),
36
36
  };
37
37
  },
38
38
  });
@@ -101,7 +101,7 @@ const create = context => ({
101
101
 
102
102
  yield {
103
103
  node: binaryExpression,
104
- loc: toLocation([start, end], sourceCode),
104
+ loc: toLocation([start, end], context),
105
105
  messageId: MESSAGE_ID,
106
106
  data: {
107
107
  ...replacement,
@@ -108,6 +108,43 @@ const isIife = node =>
108
108
  && node.parent.type === 'CallExpression'
109
109
  && node.parent.callee === node;
110
110
 
111
+ // Helper to walk up the chain to find the first non-arrow ancestor
112
+ function getNonArrowAncestor(node) {
113
+ let ancestor = node;
114
+ while (ancestor && ancestor.type === 'ArrowFunctionExpression') {
115
+ ancestor = ancestor.parent;
116
+ }
117
+
118
+ return ancestor;
119
+ }
120
+
121
+ // Helper to skip over a chain of ArrowFunctionExpression nodes
122
+ function skipArrowFunctionChain(node) {
123
+ let current = node;
124
+ while (current.type === 'ArrowFunctionExpression') {
125
+ current = current.parent;
126
+ }
127
+
128
+ return current;
129
+ }
130
+
131
+ function handleNestedArrowFunctions(parentNode, node) {
132
+ // Skip over arrow function expressions when they are parents and we came from a ReturnStatement
133
+ // This handles nested arrow functions: return next => action => { ... }
134
+ // But only when we're in a return statement context
135
+ if (parentNode.type === 'ArrowFunctionExpression' && node.type === 'ArrowFunctionExpression') {
136
+ const ancestor = getNonArrowAncestor(parentNode);
137
+ if (ancestor && ancestor.type === 'ReturnStatement') {
138
+ parentNode = skipArrowFunctionChain(parentNode);
139
+ if (parentNode.type === 'ReturnStatement') {
140
+ parentNode = parentNode.parent;
141
+ }
142
+ }
143
+ }
144
+
145
+ return parentNode;
146
+ }
147
+
111
148
  function checkNode(node, scopeManager) {
112
149
  const scope = scopeManager.acquire(node);
113
150
 
@@ -128,7 +165,15 @@ function checkNode(node, scopeManager) {
128
165
  parentNode = parentNode.parent;
129
166
  }
130
167
 
131
- if (parentNode.type === 'BlockStatement') {
168
+ // Only skip ReturnStatement for arrow functions
169
+ // Regular function expressions have different semantics and shouldn't be moved
170
+ if (parentNode?.type === 'ReturnStatement' && node.type === 'ArrowFunctionExpression') {
171
+ parentNode = parentNode.parent;
172
+ }
173
+
174
+ parentNode = handleNestedArrowFunctions(parentNode, node);
175
+
176
+ if (parentNode?.type === 'BlockStatement') {
132
177
  parentNode = parentNode.parent;
133
178
  }
134
179
 
@@ -56,7 +56,7 @@ const create = context => {
56
56
  node,
57
57
  original: node.value.raw,
58
58
  lowercase,
59
- fix: (fixer, fixed) => replaceTemplateElement(fixer, node, fixed),
59
+ fix: (fixer, fixed) => replaceTemplateElement(node, fixed, context, fixer),
60
60
  });
61
61
  });
62
62
  };
@@ -123,7 +123,7 @@ function create(context) {
123
123
 
124
124
  const fix = function * (fixer) {
125
125
  yield fixer.replaceText(node, fixed);
126
- yield * fixSpaceAroundKeyword(fixer, node, sourceCode);
126
+ yield fixSpaceAroundKeyword(fixer, node, context);
127
127
  };
128
128
 
129
129
  const problem = {
@@ -1,10 +1,22 @@
1
1
  import {isSemicolonToken} from '@eslint-community/eslint-utils';
2
2
 
3
- export default function * addParenthesizesToReturnOrThrowExpression(fixer, node, sourceCode) {
3
+ /**
4
+ @import {TSESTree as ESTree} from '@typescript-eslint/types';
5
+ @import * as ESLint from 'eslint';
6
+ */
7
+
8
+ /**
9
+ @param {ESLint.Rule.RuleFixer} fixer
10
+ @param {ESTree.ReturnStatement | ESTree.ThrowStatement} node
11
+ @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
12
+ @returns {ESLint.Rule.ReportFixer}
13
+ */
14
+ export default function * addParenthesizesToReturnOrThrowExpression(fixer, node, context) {
4
15
  if (node.type !== 'ReturnStatement' && node.type !== 'ThrowStatement') {
5
16
  return;
6
17
  }
7
18
 
19
+ const {sourceCode} = context;
8
20
  const returnOrThrowToken = sourceCode.getFirstToken(node);
9
21
  yield fixer.insertTextAfter(returnOrThrowToken, ' (');
10
22
  const lastToken = sourceCode.getLastToken(node);
@@ -1,6 +1,18 @@
1
1
  import {isCommaToken} from '@eslint-community/eslint-utils';
2
2
 
3
- export default function appendArgument(fixer, node, text, sourceCode) {
3
+ /**
4
+ @import {TSESTree as ESTree} from '@typescript-eslint/types';
5
+ @import * as ESLint from 'eslint';
6
+ */
7
+
8
+ /**
9
+ @param {ESLint.Rule.RuleFixer} fixer
10
+ @param {ESTree.CallExpression} node
11
+ @param {string} text
12
+ @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
13
+ @returns {ESLint.Rule.ReportFixer}
14
+ */
15
+ export default function appendArgument(fixer, node, text, context) {
4
16
  // This function should also work for `NewExpression`
5
17
  // But parentheses of `NewExpression` could be omitted, add this check to prevent accident use on it
6
18
  /* c8 ignore next 3 */
@@ -8,6 +20,7 @@ export default function appendArgument(fixer, node, text, sourceCode) {
8
20
  throw new Error(`Unexpected node "${node.type}".`);
9
21
  }
10
22
 
23
+ const {sourceCode} = context;
11
24
  const [penultimateToken, lastToken] = sourceCode.getLastTokens(node, 2);
12
25
  if (node.arguments.length > 0) {
13
26
  text = isCommaToken(penultimateToken) ? ` ${text},` : `, ${text}`;
@@ -1,5 +1,10 @@
1
1
  import {getParenthesizedRange} from '../utils/parentheses.js';
2
2
 
3
+ /**
4
+ @import {TSESTree as ESTree} from '@typescript-eslint/types';
5
+ @import * as ESLint from 'eslint';
6
+ */
7
+
3
8
  const isProblematicToken = ({type, value}) => (
4
9
  (type === 'Keyword' && /^[a-z]*$/.test(value))
5
10
  // ForOfStatement
@@ -8,8 +13,14 @@ const isProblematicToken = ({type, value}) => (
8
13
  || (type === 'Identifier' && value === 'await')
9
14
  );
10
15
 
11
- export default function * fixSpaceAroundKeyword(fixer, node, sourceCode) {
12
- const range = getParenthesizedRange(node, sourceCode);
16
+ /**
17
+ @param {ESLint.Rule.RuleFixer} fixer
18
+ @param {ESTree.Node} node
19
+ @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
20
+ */
21
+ export default function * fixSpaceAroundKeyword(fixer, node, context) {
22
+ const {sourceCode} = context;
23
+ const range = getParenthesizedRange(node, context);
13
24
  const tokenBefore = sourceCode.getTokenBefore({range}, {includeComments: true});
14
25
 
15
26
  if (
@@ -10,13 +10,14 @@ export {
10
10
  removeMemberExpressionProperty,
11
11
  } from './replace-member-expression-property.js';
12
12
  export {default as removeMethodCall} from './remove-method-call.js';
13
- export {default as replaceTemplateElement} from './replace-template-element.js';
14
- export {default as replaceReferenceIdentifier} from './replace-reference-identifier.js';
15
- export {default as renameVariable} from './rename-variable.js';
16
- export {default as replaceNodeOrTokenAndSpacesBefore} from './replace-node-or-token-and-spaces-before.js';
13
+ export {default as removeExpressionStatement} from './remove-expression-statement.js';
17
14
  export {default as removeSpacesAfter} from './remove-spaces-after.js';
18
15
  export {default as removeSpecifier} from './remove-specifier.js';
19
16
  export {default as removeObjectProperty} from './remove-object-property.js';
17
+ export {default as renameVariable} from './rename-variable.js';
18
+ export {default as replaceTemplateElement} from './replace-template-element.js';
19
+ export {default as replaceReferenceIdentifier} from './replace-reference-identifier.js';
20
+ export {default as replaceNodeOrTokenAndSpacesBefore} from './replace-node-or-token-and-spaces-before.js';
20
21
  export {default as fixSpaceAroundKeyword} from './fix-space-around-keywords.js';
21
22
  export {default as replaceStringRaw} from './replace-string-raw.js';
22
23
  export {default as addParenthesizesToReturnOrThrowExpression} from './add-parenthesizes-to-return-or-throw-expression.js';
@@ -1,12 +1,24 @@
1
1
  import {isCommaToken} from '@eslint-community/eslint-utils';
2
2
  import {getParentheses} from '../utils/parentheses.js';
3
3
 
4
- export default function removeArgument(fixer, node, sourceCode) {
5
- const callExpression = node.parent;
6
- const index = callExpression.arguments.indexOf(node);
7
- const parentheses = getParentheses(node, sourceCode);
4
+ /**
5
+ @import {TSESTree as ESTree} from '@typescript-eslint/types';
6
+ @import * as ESLint from 'eslint';
7
+ */
8
+
9
+ /**
10
+ @param {ESLint.Rule.RuleFixer} fixer
11
+ @param {ESTree.NewExpression | ESTree.CallExpression} node
12
+ @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
13
+ @returns {ESLint.Rule.ReportFixer}
14
+ */
15
+ export default function removeArgument(fixer, node, context) {
16
+ const callOrNewExpression = node.parent;
17
+ const index = callOrNewExpression.arguments.indexOf(node);
18
+ const parentheses = getParentheses(node, context);
8
19
  const firstToken = parentheses[0] || node;
9
20
  const lastToken = parentheses.at(-1) || node;
21
+ const {sourceCode} = context;
10
22
 
11
23
  let [start] = sourceCode.getRange(firstToken);
12
24
  let [, end] = sourceCode.getRange(lastToken);
@@ -17,14 +29,12 @@ export default function removeArgument(fixer, node, sourceCode) {
17
29
  }
18
30
 
19
31
  // If the removed argument is the only argument, the trailing comma must be removed too
20
- /* c8 ignore start */
21
- if (callExpression.arguments.length === 1) {
22
- const tokenAfter = sourceCode.getTokenBefore(lastToken);
32
+ if (callOrNewExpression.arguments.length === 1) {
33
+ const tokenAfter = sourceCode.getTokenAfter(lastToken);
23
34
  if (isCommaToken(tokenAfter)) {
24
35
  [, end] = sourceCode.getRange(tokenAfter);
25
36
  }
26
37
  }
27
- /* c8 ignore end */
28
38
 
29
39
  return fixer.removeRange([start, end]);
30
40
  }
@@ -0,0 +1,34 @@
1
+ import {isSemicolonToken} from '@eslint-community/eslint-utils';
2
+ const isWhitespaceOnly = text => /^\s*$/.test(text);
3
+
4
+ function removeExpressionStatement(expressionStatement, context, fixer, preserveSemiColon = false) {
5
+ const {sourceCode} = context;
6
+ const {lines} = sourceCode;
7
+ let endToken = expressionStatement;
8
+
9
+ if (preserveSemiColon) {
10
+ const [penultimateToken, lastToken] = sourceCode.getLastTokens(expressionStatement, 2);
11
+
12
+ if (isSemicolonToken(lastToken)) {
13
+ endToken = penultimateToken;
14
+ }
15
+ }
16
+
17
+ const startLocation = sourceCode.getLoc(expressionStatement).start;
18
+ const endLocation = sourceCode.getLoc(endToken).end;
19
+
20
+ const textBefore = lines[startLocation.line - 1].slice(0, startLocation.column);
21
+ const textAfter = lines[endLocation.line - 1].slice(endLocation.column);
22
+
23
+ let [start] = sourceCode.getRange(expressionStatement);
24
+ let [, end] = sourceCode.getRange(endToken);
25
+
26
+ if (isWhitespaceOnly(textBefore) && isWhitespaceOnly(textAfter)) {
27
+ start = Math.max(0, start - textBefore.length - 1);
28
+ end += textAfter.length;
29
+ }
30
+
31
+ return fixer.removeRange([start, end]);
32
+ }
33
+
34
+ export default removeExpressionStatement;
@@ -1,17 +1,28 @@
1
1
  import {getParenthesizedRange} from '../utils/parentheses.js';
2
2
  import {removeMemberExpressionProperty} from './replace-member-expression-property.js';
3
3
 
4
- export default function * removeMethodCall(fixer, callExpression, sourceCode) {
4
+ /**
5
+ @import {TSESTree as ESTree} from '@typescript-eslint/types';
6
+ @import * as ESLint from 'eslint';
7
+ */
8
+
9
+ /**
10
+ @param {ESLint.Rule.RuleFixer} fixer
11
+ @param {ESTree.CallExpression} callExpression
12
+ @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
13
+ @returns {ESLint.Rule.ReportFixer}
14
+ */
15
+ export default function * removeMethodCall(fixer, callExpression, context) {
5
16
  const memberExpression = callExpression.callee;
6
17
 
7
18
  // `(( (( foo )).bar ))()`
8
19
  // ^^^^
9
- yield removeMemberExpressionProperty(fixer, memberExpression, sourceCode);
20
+ yield removeMemberExpressionProperty(fixer, memberExpression, context);
10
21
 
11
22
  // `(( (( foo )).bar ))()`
12
23
  // ^^
13
- const [, start] = getParenthesizedRange(memberExpression, sourceCode);
14
- const [, end] = sourceCode.getRange(callExpression);
24
+ const [, start] = getParenthesizedRange(memberExpression, context);
25
+ const [, end] = context.sourceCode.getRange(callExpression);
15
26
 
16
27
  yield fixer.removeRange([start, end]);
17
28
  }
@@ -1,7 +1,18 @@
1
1
  import {getParentheses} from '../utils/parentheses.js';
2
2
 
3
- export default function * removeParentheses(node, fixer, sourceCode) {
4
- const parentheses = getParentheses(node, sourceCode);
3
+ /**
4
+ @import {TSESTree as ESTree} from '@typescript-eslint/types';
5
+ @import * as ESLint from 'eslint';
6
+ */
7
+
8
+ /**
9
+ @param {ESTree.Node} node
10
+ @param {ESLint.Rule.RuleFixer} fixer
11
+ @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
12
+ @returns {ESLint.Rule.ReportFixer}
13
+ */
14
+ export default function * removeParentheses(node, fixer, context) {
15
+ const parentheses = getParentheses(node, context);
5
16
  for (const token of parentheses) {
6
17
  yield fixer.remove(token);
7
18
  }
@@ -1,10 +1,22 @@
1
- export default function removeSpacesAfter(indexOrNodeOrToken, sourceCode, fixer) {
1
+ /**
2
+ @import {TSESTree as ESTree} from '@typescript-eslint/types';
3
+ @import * as ESLint from 'eslint';
4
+ */
5
+
6
+ /**
7
+ @param {ESTree.Node | ESTree.Token | number} indexOrNodeOrToken
8
+ @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
9
+ @param {ESLint.Rule.RuleFixer} fixer
10
+ @returns {ESLint.Rule.ReportFixer}
11
+ */
12
+
13
+ export default function removeSpacesAfter(indexOrNodeOrToken, context, fixer) {
2
14
  let index = indexOrNodeOrToken;
3
15
  if (typeof indexOrNodeOrToken === 'object') {
4
- index = sourceCode.getRange(indexOrNodeOrToken)[1];
16
+ index = context.sourceCode.getRange(indexOrNodeOrToken)[1];
5
17
  }
6
18
 
7
- const textAfter = sourceCode.text.slice(index);
19
+ const textAfter = context.sourceCode.text.slice(index);
8
20
  const [leadingSpaces] = textAfter.match(/^\s*/);
9
21
  return fixer.removeRange([index, index + leadingSpaces.length]);
10
22
  }
@@ -1,6 +1,18 @@
1
1
  import {isCommaToken, isOpeningBraceToken} from '@eslint-community/eslint-utils';
2
2
 
3
- export default function * removeSpecifier(specifier, fixer, sourceCode, keepDeclaration = false) {
3
+ /**
4
+ @import {TSESTree as ESTree} from '@typescript-eslint/types';
5
+ @import * as ESLint from 'eslint';
6
+ */
7
+
8
+ /**
9
+ @param {ESTree.ImportSpecifier | ESTree.ExportSpecifier | ESTree.ImportDefaultSpecifier | ESTree.ImportNamespaceSpecifier} specifier
10
+ @param {ESLint.Rule.RuleFixer} fixer
11
+ @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
12
+ @param {boolean} [keepDeclaration = false]
13
+ @returns {ESLint.Rule.ReportFixer}
14
+ */
15
+ export default function * removeSpecifier(specifier, fixer, context, keepDeclaration = false) {
4
16
  const declaration = specifier.parent;
5
17
  const {specifiers} = declaration;
6
18
 
@@ -9,6 +21,7 @@ export default function * removeSpecifier(specifier, fixer, sourceCode, keepDecl
9
21
  return;
10
22
  }
11
23
 
24
+ const {sourceCode} = context;
12
25
  switch (specifier.type) {
13
26
  case 'ImportSpecifier': {
14
27
  const isTheOnlyNamedImport = specifiers.every(node => specifier === node || specifier.type !== node.type);
@@ -1,7 +1,8 @@
1
1
  import getVariableIdentifiers from '../utils/get-variable-identifiers.js';
2
2
  import replaceReferenceIdentifier from './replace-reference-identifier.js';
3
3
 
4
- const renameVariable = (variable, name, fixer) => getVariableIdentifiers(variable)
5
- .map(identifier => replaceReferenceIdentifier(identifier, name, fixer));
4
+ const renameVariable = (variable, name, context, fixer) =>
5
+ getVariableIdentifiers(variable)
6
+ .map(identifier => replaceReferenceIdentifier(identifier, name, context, fixer));
6
7
 
7
8
  export default renameVariable;
@@ -1,5 +1,17 @@
1
1
  import {getParenthesizedRange} from '../utils/parentheses.js';
2
2
 
3
- export default function replaceArgument(fixer, node, text, sourceCode) {
4
- return fixer.replaceTextRange(getParenthesizedRange(node, sourceCode), text);
3
+ /**
4
+ @import {TSESTree as ESTree} from '@typescript-eslint/types';
5
+ @import * as ESLint from 'eslint';
6
+ */
7
+
8
+ /**
9
+ @param {ESLint.Rule.RuleFixer} fixer
10
+ @param {ESTree.CallExpression | ESTree.NewExpression} node
11
+ @param {string} text
12
+ @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
13
+ @returns {ESLint.Rule.ReportFixer}
14
+ */
15
+ export default function replaceArgument(fixer, node, text, context) {
16
+ return fixer.replaceTextRange(getParenthesizedRange(node, context), text);
5
17
  }
@@ -1,9 +1,25 @@
1
1
  import {getParenthesizedRange} from '../utils/parentheses.js';
2
2
 
3
- export function replaceMemberExpressionProperty(fixer, memberExpression, sourceCode, text) {
4
- const [, start] = getParenthesizedRange(memberExpression.object, sourceCode);
5
- const [, end] = sourceCode.getRange(memberExpression);
3
+ /**
4
+ @import {TSESTree as ESTree} from '@typescript-eslint/types';
5
+ @import * as ESLint from 'eslint';
6
+ */
7
+
8
+ /**
9
+ @param {ESTree.MemberExpression} memberExpression - The `MemberExpression` to fix.
10
+ @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
11
+ @param {string} text
12
+ @returns {ESLint.Rule.ReportFixer}
13
+ */
14
+ export function replaceMemberExpressionProperty(fixer, memberExpression, context, text) {
15
+ const [, start] = getParenthesizedRange(memberExpression.object, context);
16
+ const [, end] = context.sourceCode.getRange(memberExpression);
6
17
  return fixer.replaceTextRange([start, end], text);
7
18
  }
8
19
 
9
- export const removeMemberExpressionProperty = (fixer, memberExpression, sourceCode) => replaceMemberExpressionProperty(fixer, memberExpression, sourceCode, '');
20
+ /**
21
+ @param {ESTree.MemberExpression} memberExpression - The `MemberExpression` to fix.
22
+ @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
23
+ @returns {ESLint.Rule.ReportFixer}
24
+ */
25
+ export const removeMemberExpressionProperty = (fixer, memberExpression, context) => replaceMemberExpressionProperty(fixer, memberExpression, context, '');