eslint-plugin-unicorn 62.0.0 → 64.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 (173) hide show
  1. package/index.d.ts +5 -5
  2. package/index.js +17 -27
  3. package/package.json +27 -31
  4. package/readme.md +6 -1
  5. package/rules/ast/call-or-new-expression.js +3 -5
  6. package/rules/ast/index.js +1 -0
  7. package/rules/ast/is-empty-object-expression.js +5 -0
  8. package/rules/ast/literal.js +1 -6
  9. package/rules/better-regex.js +98 -89
  10. package/rules/catch-error-name.js +57 -63
  11. package/rules/consistent-assert.js +6 -4
  12. package/rules/consistent-date-clone.js +4 -4
  13. package/rules/consistent-destructuring.js +211 -88
  14. package/rules/consistent-empty-array-spread.js +8 -12
  15. package/rules/consistent-existence-index-check.js +4 -5
  16. package/rules/consistent-function-scoping.js +16 -12
  17. package/rules/consistent-template-literal-escape.js +52 -0
  18. package/rules/custom-error-definition.js +105 -77
  19. package/rules/escape-case.js +1 -0
  20. package/rules/expiring-todo-comments.js +27 -34
  21. package/rules/explicit-length-check.js +64 -72
  22. package/rules/filename-case.js +49 -41
  23. package/rules/fix/append-argument.js +1 -1
  24. package/rules/fix/fix-space-around-keywords.js +1 -1
  25. package/rules/fix/remove-argument.js +1 -1
  26. package/rules/fix/remove-expression-statement.js +1 -0
  27. package/rules/fix/remove-method-call.js +1 -1
  28. package/rules/fix/remove-parentheses.js +1 -1
  29. package/rules/fix/replace-argument.js +1 -1
  30. package/rules/fix/replace-member-expression-property.js +1 -1
  31. package/rules/fix/replace-node-or-token-and-spaces-before.js +1 -1
  32. package/rules/fix/switch-call-expression-to-new-expression.js +5 -3
  33. package/rules/fix/switch-new-expression-to-call-expression.js +6 -4
  34. package/rules/import-style.js +14 -10
  35. package/rules/index.js +5 -0
  36. package/rules/isolated-functions.js +206 -0
  37. package/rules/no-abusive-eslint-disable.js +13 -27
  38. package/rules/no-accessor-recursion.js +18 -22
  39. package/rules/no-anonymous-default-export.js +12 -10
  40. package/rules/no-array-callback-reference.js +66 -6
  41. package/rules/no-array-for-each.js +14 -12
  42. package/rules/no-array-method-this-argument.js +8 -5
  43. package/rules/no-array-reduce.js +17 -18
  44. package/rules/no-await-in-promise-methods.js +4 -4
  45. package/rules/no-console-spaces.js +33 -35
  46. package/rules/no-document-cookie.js +3 -1
  47. package/rules/no-empty-file.js +17 -19
  48. package/rules/no-for-loop.js +182 -108
  49. package/rules/no-hex-escape.js +7 -6
  50. package/rules/no-immediate-mutation.js +3 -3
  51. package/rules/no-instanceof-builtins.js +52 -57
  52. package/rules/no-invalid-fetch-options.js +1 -1
  53. package/rules/no-invalid-remove-event-listener.js +4 -4
  54. package/rules/no-keyword-prefix.js +48 -47
  55. package/rules/no-lonely-if.js +8 -8
  56. package/rules/no-magic-array-flat-depth.js +4 -4
  57. package/rules/no-named-default.js +7 -6
  58. package/rules/no-negated-condition.js +9 -6
  59. package/rules/no-negation-in-equality-check.js +5 -5
  60. package/rules/no-nested-ternary.js +6 -6
  61. package/rules/no-new-array.js +10 -9
  62. package/rules/no-new-buffer.js +21 -23
  63. package/rules/no-null.js +92 -97
  64. package/rules/no-object-as-default-parameter.js +4 -4
  65. package/rules/no-process-exit.js +2 -2
  66. package/rules/no-single-promise-in-promise-methods.js +7 -9
  67. package/rules/no-typeof-undefined.js +77 -85
  68. package/rules/no-unnecessary-array-splice-count.js +3 -1
  69. package/rules/no-unnecessary-await.js +6 -8
  70. package/rules/no-unnecessary-polyfills.js +235 -48
  71. package/rules/no-unnecessary-slice-end.js +3 -1
  72. package/rules/no-unreadable-array-destructuring.js +45 -45
  73. package/rules/no-unreadable-iife.js +5 -7
  74. package/rules/no-unused-properties.js +22 -30
  75. package/rules/no-useless-collection-argument.js +45 -15
  76. package/rules/no-useless-fallback-in-spread.js +8 -7
  77. package/rules/no-useless-iterator-to-array.js +249 -0
  78. package/rules/no-useless-length-check.js +64 -69
  79. package/rules/no-useless-spread.js +11 -10
  80. package/rules/no-useless-switch-case.js +4 -4
  81. package/rules/no-useless-undefined.js +12 -7
  82. package/rules/no-zero-fractions.js +11 -9
  83. package/rules/number-literal-case.js +4 -4
  84. package/rules/numeric-separators-style.js +41 -60
  85. package/rules/prefer-add-event-listener.js +105 -104
  86. package/rules/prefer-array-find.js +2 -6
  87. package/rules/prefer-array-flat-map.js +4 -4
  88. package/rules/prefer-array-flat.js +108 -45
  89. package/rules/prefer-array-some.js +9 -4
  90. package/rules/prefer-at.js +5 -7
  91. package/rules/prefer-bigint-literals.js +59 -11
  92. package/rules/prefer-blob-reading-methods.js +4 -4
  93. package/rules/prefer-class-fields.js +85 -89
  94. package/rules/prefer-code-point.js +7 -6
  95. package/rules/prefer-date-now.js +13 -10
  96. package/rules/prefer-default-parameters.js +1 -2
  97. package/rules/prefer-dom-node-append.js +4 -4
  98. package/rules/prefer-dom-node-dataset.js +4 -4
  99. package/rules/prefer-dom-node-remove.js +67 -69
  100. package/rules/prefer-dom-node-text-content.js +7 -6
  101. package/rules/prefer-event-target.js +4 -4
  102. package/rules/prefer-export-from.js +73 -71
  103. package/rules/prefer-global-this.js +54 -11
  104. package/rules/prefer-json-parse-buffer.js +4 -4
  105. package/rules/prefer-keyboard-event-key.js +6 -6
  106. package/rules/prefer-logical-operator-over-ternary.js +37 -36
  107. package/rules/prefer-math-min-max.js +56 -31
  108. package/rules/prefer-modern-math-apis.js +62 -63
  109. package/rules/prefer-module.js +5 -7
  110. package/rules/prefer-native-coercion-functions.js +8 -1
  111. package/rules/prefer-negative-index.js +6 -9
  112. package/rules/prefer-node-protocol.js +6 -10
  113. package/rules/prefer-number-properties.js +3 -5
  114. package/rules/prefer-object-from-entries.js +50 -50
  115. package/rules/prefer-optional-catch-binding.js +4 -4
  116. package/rules/prefer-prototype-methods.js +8 -8
  117. package/rules/prefer-query-selector.js +4 -4
  118. package/rules/prefer-reflect-apply.js +4 -4
  119. package/rules/prefer-regexp-test.js +8 -8
  120. package/rules/prefer-response-static-json.js +7 -8
  121. package/rules/prefer-set-has.js +81 -29
  122. package/rules/prefer-set-size.js +55 -35
  123. package/rules/prefer-simple-condition-first.js +282 -0
  124. package/rules/prefer-single-call.js +72 -78
  125. package/rules/prefer-spread.js +149 -18
  126. package/rules/prefer-string-raw.js +5 -1
  127. package/rules/prefer-string-replace-all.js +4 -4
  128. package/rules/prefer-string-slice.js +12 -12
  129. package/rules/prefer-string-starts-ends-with.js +113 -111
  130. package/rules/prefer-string-trim-start-end.js +4 -4
  131. package/rules/prefer-structured-clone.js +3 -5
  132. package/rules/prefer-switch.js +41 -41
  133. package/rules/prefer-ternary.js +79 -80
  134. package/rules/prefer-top-level-await.js +104 -72
  135. package/rules/prefer-type-error.js +4 -4
  136. package/rules/prevent-abbreviations.js +76 -84
  137. package/rules/relative-url-style.js +6 -9
  138. package/rules/require-array-join-separator.js +4 -4
  139. package/rules/require-module-attributes.js +17 -19
  140. package/rules/require-number-to-fixed-digits-argument.js +4 -4
  141. package/rules/require-post-message-target-origin.js +33 -35
  142. package/rules/rule/index.js +1 -1
  143. package/rules/rule/to-eslint-create.js +5 -7
  144. package/rules/rule/to-eslint-rule.js +1 -1
  145. package/rules/rule/to-eslint-rules.js +4 -6
  146. package/rules/shared/abbreviations.js +1 -0
  147. package/rules/shared/is-jest-inline-snapshot.js +18 -0
  148. package/rules/shared/negative-index.js +4 -2
  149. package/rules/shared/no-array-mutate-rule.js +48 -49
  150. package/rules/shared/simple-array-search-rule.js +4 -4
  151. package/rules/string-content.js +3 -5
  152. package/rules/switch-case-braces.js +47 -50
  153. package/rules/switch-case-break-position.js +138 -0
  154. package/rules/template-indent.js +31 -50
  155. package/rules/text-encoding-identifier-case.js +23 -5
  156. package/rules/throw-new-error.js +4 -4
  157. package/rules/utils/array-or-object-prototype-property.js +7 -11
  158. package/rules/utils/assert-token.js +3 -5
  159. package/rules/utils/boolean.js +32 -23
  160. package/rules/utils/create-deprecated-rules.js +25 -20
  161. package/rules/utils/eslint-directive.js +21 -0
  162. package/rules/utils/get-call-expression-arguments-text.js +3 -5
  163. package/rules/utils/index.js +18 -4
  164. package/rules/utils/is-node-contains-lexical-this.js +65 -0
  165. package/rules/utils/is-number.js +2 -2
  166. package/rules/utils/is-same-reference.js +23 -17
  167. package/rules/utils/parentheses/get-parent-syntax-opening-parenthesis.js +80 -0
  168. package/rules/utils/parentheses/iterate-surrounding-parentheses.js +82 -0
  169. package/rules/utils/parentheses/parentheses.js +81 -0
  170. package/rules/utils/should-add-parentheses-to-conditional-expression-child.js +3 -1
  171. package/rules/utils/should-add-parentheses-to-expression-statement-expression.js +1 -1
  172. package/rules/utils/should-add-parentheses-to-member-expression-object.js +1 -1
  173. package/rules/utils/parentheses.js +0 -71
package/index.d.ts CHANGED
@@ -2,15 +2,15 @@ import type {ESLint, Linter} from 'eslint';
2
2
 
3
3
  declare const eslintPluginUnicorn: ESLint.Plugin & {
4
4
  configs: {
5
- recommended: Linter.FlatConfig;
6
- unopinionated: Linter.FlatConfig;
7
- all: Linter.FlatConfig;
5
+ recommended: Linter.Config;
6
+ unopinionated: Linter.Config;
7
+ all: Linter.Config;
8
8
 
9
9
  /** @deprecated Use `all` instead. The `flat/` prefix is no longer needed. */
10
- 'flat/all': Linter.FlatConfig;
10
+ 'flat/all': Linter.Config;
11
11
 
12
12
  /** @deprecated Use `recommended` instead. The `flat/` prefix is no longer needed. */
13
- 'flat/recommended': Linter.FlatConfig;
13
+ 'flat/recommended': Linter.Config;
14
14
  };
15
15
  };
16
16
 
package/index.js CHANGED
@@ -1,13 +1,12 @@
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/rule/index.js';
4
+ import {toEslintRules} from './rules/rule/index.js';
5
5
  import packageJson from './package.json' with {type: 'json'};
6
6
 
7
- const rules = createRules(rawRules);
7
+ const rules = toEslintRules(rawRules);
8
8
 
9
9
  const deprecatedRules = createDeprecatedRules({
10
- // {ruleId: {message: string, replacedBy: string[]}}
11
10
  'no-instanceof-array': {
12
11
  message: 'Replaced by `unicorn/no-instanceof-builtins` which covers more cases.',
13
12
  replacedBy: ['unicorn/no-instanceof-builtins'],
@@ -29,26 +28,20 @@ const externalRules = {
29
28
  'no-nested-ternary': 'off',
30
29
  };
31
30
 
32
- const recommendedRules = Object.fromEntries(
33
- Object.entries(rules).map(([id, rule]) => [
34
- `unicorn/${id}`,
35
- rule.meta.docs.recommended ? 'error' : 'off',
36
- ]),
37
- );
31
+ const recommendedRules = Object.fromEntries(Object.entries(rules).map(([id, rule]) => [
32
+ `unicorn/${id}`,
33
+ rule.meta.docs.recommended ? 'error' : 'off',
34
+ ]));
38
35
 
39
- const unopinionatedRules = Object.fromEntries(
40
- Object.entries(rules).map(([id, rule]) => [
41
- `unicorn/${id}`,
42
- rule.meta.docs.recommended === 'unopinionated' ? 'error' : 'off',
43
- ]),
44
- );
36
+ const unopinionatedRules = Object.fromEntries(Object.entries(rules).map(([id, rule]) => [
37
+ `unicorn/${id}`,
38
+ rule.meta.docs.recommended === 'unopinionated' ? 'error' : 'off',
39
+ ]));
45
40
 
46
- const allRules = Object.fromEntries(
47
- Object.keys(rules).map(id => [
48
- `unicorn/${id}`,
49
- 'error',
50
- ]),
51
- );
41
+ const allRules = Object.fromEntries(Object.keys(rules).map(id => [
42
+ `unicorn/${id}`,
43
+ 'error',
44
+ ]));
52
45
 
53
46
  const createConfig = (rules, flatConfigName) => ({
54
47
  ...flatConfigBase,
@@ -68,7 +61,7 @@ const unicorn = {
68
61
  version: packageJson.version,
69
62
  },
70
63
  rules: {
71
- ...createRules(rules),
64
+ ...rules,
72
65
  ...deprecatedRules,
73
66
  },
74
67
  };
@@ -83,9 +76,6 @@ const configs = {
83
76
  'flat/all': createConfig(allRules, 'unicorn/flat/all'),
84
77
  };
85
78
 
86
- const allConfigs = {
87
- ...unicorn,
88
- configs,
89
- };
79
+ unicorn.configs = configs;
90
80
 
91
- export default allConfigs;
81
+ export default unicorn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-unicorn",
3
- "version": "62.0.0",
3
+ "version": "64.0.0",
4
4
  "description": "More than 100 powerful ESLint rules",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/eslint-plugin-unicorn",
@@ -57,58 +57,54 @@
57
57
  ],
58
58
  "dependencies": {
59
59
  "@babel/helper-validator-identifier": "^7.28.5",
60
- "@eslint-community/eslint-utils": "^4.9.0",
61
- "@eslint/plugin-kit": "^0.4.0",
60
+ "@eslint-community/eslint-utils": "^4.9.1",
62
61
  "change-case": "^5.4.4",
63
- "ci-info": "^4.3.1",
62
+ "ci-info": "^4.4.0",
64
63
  "clean-regexp": "^1.0.0",
65
- "core-js-compat": "^3.46.0",
66
- "esquery": "^1.6.0",
64
+ "core-js-compat": "^3.49.0",
67
65
  "find-up-simple": "^1.0.1",
68
- "globals": "^16.4.0",
66
+ "globals": "^17.4.0",
69
67
  "indent-string": "^5.0.0",
70
68
  "is-builtin-module": "^5.0.0",
71
69
  "jsesc": "^3.1.0",
72
70
  "pluralize": "^8.0.0",
73
71
  "regexp-tree": "^0.1.27",
74
72
  "regjsparser": "^0.13.0",
75
- "semver": "^7.7.3",
73
+ "semver": "^7.7.4",
76
74
  "strip-indent": "^4.1.1"
77
75
  },
78
76
  "devDependencies": {
79
- "@babel/code-frame": "^7.27.1",
80
- "@babel/core": "^7.28.5",
81
- "@babel/eslint-parser": "^7.28.5",
82
- "@eslint/eslintrc": "^3.3.1",
77
+ "@babel/code-frame": "^7.28.3",
78
+ "@eslint/eslintrc": "^3.3.5",
83
79
  "@lubien/fixture-beta-package": "^1.0.0-beta.1",
84
- "@typescript-eslint/parser": "^8.46.2",
85
- "@typescript-eslint/types": "^8.46.2",
86
- "ava": "^6.4.1",
87
- "c8": "^10.1.3",
80
+ "@typescript-eslint/parser": "^8.57.2",
81
+ "@typescript-eslint/types": "^8.57.2",
82
+ "ava": "^7.0.0",
83
+ "c8": "^11.0.0",
88
84
  "enquirer": "^2.4.1",
89
- "eslint": "^9.38.0",
85
+ "eslint": "^10.1.0",
90
86
  "eslint-ava-rule-tester": "^5.0.1",
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",
87
+ "eslint-config-xo": "^0.51.0",
88
+ "eslint-doc-generator": "^3.3.2",
89
+ "eslint-plugin-eslint-plugin": "^7.3.2",
90
+ "eslint-plugin-jsdoc": "^62.8.1",
91
+ "eslint-plugin-unicorn": "^63.0.0",
92
+ "eslint-remote-tester": "^4.0.4",
97
93
  "eslint-remote-tester-repositories": "^2.0.2",
98
- "espree": "^10.4.0",
99
- "listr2": "^9.0.5",
100
- "lodash-es": "^4.17.21",
101
- "markdownlint-cli": "^0.45.0",
94
+ "eslint-scope": "^9.1.2",
95
+ "espree": "^11.2.0",
96
+ "listr2": "^10.2.1",
97
+ "markdownlint-cli": "^0.48.0",
102
98
  "nano-spawn": "^2.0.0",
103
99
  "node-style-text": "^2.1.2",
104
- "npm-package-json-lint": "^9.0.0",
100
+ "npm-package-json-lint": "^9.1.0",
105
101
  "npm-run-all2": "^8.0.4",
106
- "open-editor": "^5.1.0",
102
+ "open-editor": "^6.0.0",
107
103
  "outdent": "^0.8.0",
108
104
  "pretty-ms": "^9.3.0",
109
105
  "typescript": "^5.9.3",
110
- "vue-eslint-parser": "^10.2.0",
111
- "yaml": "^2.8.1"
106
+ "vue-eslint-parser": "^10.4.0",
107
+ "yaml": "^2.8.3"
112
108
  },
113
109
  "peerDependencies": {
114
110
  "eslint": ">=9.38.0"
package/readme.md CHANGED
@@ -65,6 +65,7 @@ export default [
65
65
  | [consistent-empty-array-spread](docs/rules/consistent-empty-array-spread.md) | Prefer consistent types when spreading a ternary in an array literal. | ✅ | 🔧 | |
66
66
  | [consistent-existence-index-check](docs/rules/consistent-existence-index-check.md) | Enforce consistent style for element existence checks with `indexOf()`, `lastIndexOf()`, `findIndex()`, and `findLastIndex()`. | ✅ ☑️ | 🔧 | |
67
67
  | [consistent-function-scoping](docs/rules/consistent-function-scoping.md) | Move function definitions to the highest possible scope. | ✅ | | |
68
+ | [consistent-template-literal-escape](docs/rules/consistent-template-literal-escape.md) | Enforce consistent style for escaping `${` in template literals. | ✅ | 🔧 | |
68
69
  | [custom-error-definition](docs/rules/custom-error-definition.md) | Enforce correct `Error` subclassing. | | 🔧 | |
69
70
  | [empty-brace-spaces](docs/rules/empty-brace-spaces.md) | Enforce no spaces between braces. | ✅ | 🔧 | |
70
71
  | [error-message](docs/rules/error-message.md) | Enforce passing a `message` value when creating a built-in error. | ✅ ☑️ | | |
@@ -73,6 +74,7 @@ export default [
73
74
  | [explicit-length-check](docs/rules/explicit-length-check.md) | Enforce explicitly comparing the `length` or `size` property of a value. | ✅ | 🔧 | 💡 |
74
75
  | [filename-case](docs/rules/filename-case.md) | Enforce a case style for filenames. | ✅ | | |
75
76
  | [import-style](docs/rules/import-style.md) | Enforce specific import styles per module. | ✅ ☑️ | | |
77
+ | [isolated-functions](docs/rules/isolated-functions.md) | Prevent usage of variables from outside the scope of isolated functions. | ✅ | | |
76
78
  | [new-for-builtins](docs/rules/new-for-builtins.md) | Enforce the use of `new` for all builtins, except `String`, `Number`, `Boolean`, `Symbol` and `BigInt`. | ✅ ☑️ | 🔧 | 💡 |
77
79
  | [no-abusive-eslint-disable](docs/rules/no-abusive-eslint-disable.md) | Enforce specifying rules to disable in `eslint-disable` comments. | ✅ ☑️ | | |
78
80
  | [no-accessor-recursion](docs/rules/no-accessor-recursion.md) | Disallow recursive access to `this` within getters and setters. | ✅ ☑️ | | |
@@ -119,9 +121,10 @@ export default [
119
121
  | [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) | Disallow unreadable array destructuring. | ✅ ☑️ | 🔧 | |
120
122
  | [no-unreadable-iife](docs/rules/no-unreadable-iife.md) | Disallow unreadable IIFEs. | ✅ ☑️ | | |
121
123
  | [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`. | ✅ ☑️ | 🔧 | |
124
+ | [no-useless-collection-argument](docs/rules/no-useless-collection-argument.md) | Disallow useless values or fallbacks in `Set`, `Map`, `WeakSet`, or `WeakMap`. | ✅ ☑️ | 🔧 | 💡 |
123
125
  | [no-useless-error-capture-stack-trace](docs/rules/no-useless-error-capture-stack-trace.md) | Disallow unnecessary `Error.captureStackTrace(…)`. | ✅ ☑️ | 🔧 | |
124
126
  | [no-useless-fallback-in-spread](docs/rules/no-useless-fallback-in-spread.md) | Disallow useless fallback when spreading in object literals. | ✅ ☑️ | 🔧 | |
127
+ | [no-useless-iterator-to-array](docs/rules/no-useless-iterator-to-array.md) | Disallow unnecessary `.toArray()` on iterators. | ✅ ☑️ | 🔧 | 💡 |
125
128
  | [no-useless-length-check](docs/rules/no-useless-length-check.md) | Disallow useless array length check. | ✅ ☑️ | 🔧 | |
126
129
  | [no-useless-promise-resolve-reject](docs/rules/no-useless-promise-resolve-reject.md) | Disallow returning/yielding `Promise.resolve/reject()` in async functions or promise callbacks | ✅ ☑️ | 🔧 | |
127
130
  | [no-useless-spread](docs/rules/no-useless-spread.md) | Disallow unnecessary spread. | ✅ ☑️ | 🔧 | |
@@ -174,6 +177,7 @@ export default [
174
177
  | [prefer-response-static-json](docs/rules/prefer-response-static-json.md) | Prefer `Response.json()` over `new Response(JSON.stringify())`. | ✅ ☑️ | 🔧 | |
175
178
  | [prefer-set-has](docs/rules/prefer-set-has.md) | Prefer `Set#has()` over `Array#includes()` when checking for existence or non-existence. | ✅ ☑️ | 🔧 | 💡 |
176
179
  | [prefer-set-size](docs/rules/prefer-set-size.md) | Prefer using `Set#size` instead of `Array#length`. | ✅ ☑️ | 🔧 | |
180
+ | [prefer-simple-condition-first](docs/rules/prefer-simple-condition-first.md) | Prefer simple conditions first in logical expressions. | ✅ ☑️ | 🔧 | 💡 |
177
181
  | [prefer-single-call](docs/rules/prefer-single-call.md) | Enforce combining multiple `Array#push()`, `Element#classList.{add,remove}()`, and `importScripts()` into one call. | ✅ ☑️ | 🔧 | 💡 |
178
182
  | [prefer-spread](docs/rules/prefer-spread.md) | Prefer the spread operator over `Array.from(…)`, `Array#concat(…)`, `Array#{slice,toSpliced}()` and `String#split('')`. | ✅ | 🔧 | 💡 |
179
183
  | [prefer-string-raw](docs/rules/prefer-string-raw.md) | Prefer using the `String.raw` tag to avoid escaping `\`. | ✅ ☑️ | 🔧 | |
@@ -195,6 +199,7 @@ export default [
195
199
  | [require-post-message-target-origin](docs/rules/require-post-message-target-origin.md) | Enforce using the `targetOrigin` argument with `window.postMessage()`. | | | 💡 |
196
200
  | [string-content](docs/rules/string-content.md) | Enforce better string content. | | 🔧 | 💡 |
197
201
  | [switch-case-braces](docs/rules/switch-case-braces.md) | Enforce consistent brace style for `case` clauses. | ✅ | 🔧 | |
202
+ | [switch-case-break-position](docs/rules/switch-case-break-position.md) | Enforce consistent `break`/`return`/`continue`/`throw` position in `case` clauses. | ✅ | 🔧 | |
198
203
  | [template-indent](docs/rules/template-indent.md) | Fix whitespace-insensitive template indentation. | ✅ | 🔧 | |
199
204
  | [text-encoding-identifier-case](docs/rules/text-encoding-identifier-case.md) | Enforce consistent case for text encoding identifiers. | ✅ ☑️ | 🔧 | 💡 |
200
205
  | [throw-new-error](docs/rules/throw-new-error.md) | Require `new` when creating an error. | ✅ ☑️ | 🔧 | |
@@ -71,11 +71,9 @@ function create(node, options, types) {
71
71
  const maximumArgumentsLength = Number.isFinite(maximumArguments) ? maximumArguments : argumentsLength;
72
72
  if (
73
73
  typeof maximumArgumentsLength === 'number'
74
- && node.arguments.some(
75
- (node, index) =>
76
- node.type === 'SpreadElement'
77
- && index < maximumArgumentsLength,
78
- )
74
+ && node.arguments.some((node, index) =>
75
+ node.type === 'SpreadElement'
76
+ && index < maximumArgumentsLength)
79
77
  ) {
80
78
  return false;
81
79
  }
@@ -18,6 +18,7 @@ export {default as isArrowFunctionBody} from './is-arrow-function-body.js';
18
18
  export {default as isDirective} from './is-directive.js';
19
19
  export {default as isEmptyNode} from './is-empty-node.js';
20
20
  export {default as isEmptyArrayExpression} from './is-empty-array-expression.js';
21
+ export {default as isEmptyObjectExpression} from './is-empty-object-expression.js';
21
22
  export {default as isExpressionStatement} from './is-expression-statement.js';
22
23
  export {default as isFunction} from './is-function.js';
23
24
  export {default as isMemberExpression} from './is-member-expression.js';
@@ -0,0 +1,5 @@
1
+ const isEmptyArrayExpression = node =>
2
+ node.type === 'ObjectExpression'
3
+ && node.properties.length === 0;
4
+
5
+ export default isEmptyArrayExpression;
@@ -3,10 +3,6 @@ export function isLiteral(node, value) {
3
3
  return false;
4
4
  }
5
5
 
6
- if (value === null) {
7
- return node.raw === 'null';
8
- }
9
-
10
6
  return node.value === value;
11
7
  }
12
8
 
@@ -16,8 +12,7 @@ export const isNumericLiteral = node => node.type === 'Literal' && typeof node.v
16
12
 
17
13
  export const isRegexLiteral = node => node.type === 'Literal' && Boolean(node.regex);
18
14
 
19
- // eslint-disable-next-line unicorn/no-null
20
- export const isNullLiteral = node => isLiteral(node, null);
15
+ export const isNullLiteral = node => node?.type === 'Literal' && node.raw === 'null';
21
16
 
22
17
  export const isBigIntLiteral = node => node.type === 'Literal' && Boolean(node.bigint);
23
18
 
@@ -10,9 +10,17 @@ const messages = {
10
10
  [MESSAGE_ID_PARSE_ERROR]: 'Problem parsing {{original}}: {{error}}',
11
11
  };
12
12
 
13
+ // `regexp-tree` can optimize `/|/` into `//`, which is not valid JavaScript syntax.
14
+ // Normalize to an explicit empty alternative so the autofix always stays parseable.
15
+ const normalizeOptimizedRegexLiteral = optimizedRegexLiteral => (
16
+ optimizedRegexLiteral.startsWith('//')
17
+ ? `/(?:)/${optimizedRegexLiteral.slice(2)}`
18
+ : optimizedRegexLiteral
19
+ );
20
+
13
21
  /** @param {import('eslint').Rule.RuleContext} context */
14
22
  const create = context => {
15
- const {sortCharacterClasses} = context.options[0] || {};
23
+ const {sortCharacterClasses} = context.options[0];
16
24
 
17
25
  const ignoreList = [];
18
26
 
@@ -20,99 +28,99 @@ const create = context => {
20
28
  ignoreList.push('charClassClassrangesMerge');
21
29
  }
22
30
 
23
- return {
24
- Literal(node) {
25
- if (!isRegexLiteral(node)) {
26
- return;
27
- }
28
-
29
- const {raw: original, regex} = node;
30
- // Regular Expressions with `u` and `v` flag are not well handled by `regexp-tree`
31
- // https://github.com/DmitrySoshnikov/regexp-tree/issues/162
32
- if (regex.flags.includes('u') || regex.flags.includes('v')) {
33
- return;
34
- }
35
-
36
- let optimized = original;
37
-
38
- try {
39
- optimized = regexpTree.optimize(original, undefined, {blacklist: ignoreList}).toString();
40
- } catch (error) {
41
- return {
42
- node,
43
- messageId: MESSAGE_ID_PARSE_ERROR,
44
- data: {
45
- original,
46
- error: error.message,
47
- },
48
- };
49
- }
50
-
51
- if (original === optimized) {
52
- return;
53
- }
54
-
55
- const problem = {
31
+ context.on('Literal', node => {
32
+ if (!isRegexLiteral(node)) {
33
+ return;
34
+ }
35
+
36
+ const {raw: original, regex} = node;
37
+ // Regular Expressions with `u` and `v` flag are not well handled by `regexp-tree`
38
+ // https://github.com/DmitrySoshnikov/regexp-tree/issues/162
39
+ if (regex.flags.includes('u') || regex.flags.includes('v')) {
40
+ return;
41
+ }
42
+
43
+ let optimized = original;
44
+
45
+ try {
46
+ optimized = regexpTree.optimize(original, undefined, {blacklist: ignoreList}).toString();
47
+ optimized = normalizeOptimizedRegexLiteral(optimized);
48
+ } catch (error) {
49
+ return {
56
50
  node,
57
- messageId: MESSAGE_ID,
51
+ messageId: MESSAGE_ID_PARSE_ERROR,
58
52
  data: {
59
53
  original,
60
- optimized,
54
+ error: error.message,
61
55
  },
62
56
  };
63
-
64
- if (
65
- node.parent.type === 'MemberExpression'
66
- && node.parent.object === node
67
- && !node.parent.optional
68
- && !node.parent.computed
69
- && node.parent.property.type === 'Identifier'
70
- && (
71
- node.parent.property.name === 'toString'
72
- || node.parent.property.name === 'source'
73
- )
74
- ) {
75
- return problem;
76
- }
77
-
78
- return Object.assign(problem, {
79
- fix: fixer => fixer.replaceText(node, optimized),
80
- });
81
- },
82
- NewExpression(node) {
83
- if (!isNewExpression(node, {name: 'RegExp', minimumArguments: 1})) {
84
- return;
85
- }
86
-
87
- const [patternNode, flagsNode] = node.arguments;
88
-
89
- if (!isStringLiteral(patternNode)) {
90
- return;
91
- }
92
-
93
- const oldPattern = patternNode.value;
94
- const flags = isStringLiteral(flagsNode)
95
- ? flagsNode.value
96
- : '';
97
-
98
- const newPattern = cleanRegexp(oldPattern, flags);
99
-
100
- if (oldPattern !== newPattern) {
101
- return {
102
- node,
103
- messageId: MESSAGE_ID,
104
- data: {
105
- original: oldPattern,
106
- optimized: newPattern,
107
- },
108
- fix: fixer => fixer.replaceText(
109
- patternNode,
110
- escapeString(newPattern, patternNode.raw.charAt(0)),
111
- ),
112
- };
113
- }
114
- },
115
- };
57
+ }
58
+
59
+ if (original === optimized) {
60
+ return;
61
+ }
62
+
63
+ const problem = {
64
+ node,
65
+ messageId: MESSAGE_ID,
66
+ data: {
67
+ original,
68
+ optimized,
69
+ },
70
+ };
71
+
72
+ if (
73
+ node.parent.type === 'MemberExpression'
74
+ && node.parent.object === node
75
+ && !node.parent.optional
76
+ && !node.parent.computed
77
+ && node.parent.property.type === 'Identifier'
78
+ && (
79
+ node.parent.property.name === 'toString'
80
+ || node.parent.property.name === 'source'
81
+ )
82
+ ) {
83
+ return problem;
84
+ }
85
+
86
+ return Object.assign(problem, {
87
+ fix: fixer => fixer.replaceText(node, optimized),
88
+ });
89
+ });
90
+
91
+ context.on('NewExpression', node => {
92
+ if (!isNewExpression(node, {name: 'RegExp', minimumArguments: 1})) {
93
+ return;
94
+ }
95
+
96
+ const [patternNode, flagsNode] = node.arguments;
97
+
98
+ if (!isStringLiteral(patternNode)) {
99
+ return;
100
+ }
101
+
102
+ const oldPattern = patternNode.value;
103
+ const flags = isStringLiteral(flagsNode)
104
+ ? flagsNode.value
105
+ : '';
106
+
107
+ const newPattern = cleanRegexp(oldPattern, flags);
108
+
109
+ if (oldPattern !== newPattern) {
110
+ return {
111
+ node,
112
+ messageId: MESSAGE_ID,
113
+ data: {
114
+ original: oldPattern,
115
+ optimized: newPattern,
116
+ },
117
+ fix: fixer => fixer.replaceText(
118
+ patternNode,
119
+ escapeString(newPattern, patternNode.raw.charAt(0)),
120
+ ),
121
+ };
122
+ }
123
+ });
116
124
  };
117
125
 
118
126
  const schema = [
@@ -122,6 +130,7 @@ const schema = [
122
130
  properties: {
123
131
  sortCharacterClasses: {
124
132
  type: 'boolean',
133
+ description: 'Whether to sort character classes.',
125
134
  },
126
135
  },
127
136
  },