eslint-plugin-unicorn 52.0.0 → 54.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 (135) hide show
  1. package/index.js +33 -11
  2. package/package.json +29 -38
  3. package/readme.md +12 -6
  4. package/rules/ast/index.js +2 -0
  5. package/rules/ast/is-directive.js +7 -0
  6. package/rules/ast/is-reference-identifier.js +7 -0
  7. package/rules/ast/is-tagged-template-literal.js +28 -0
  8. package/rules/better-regex.js +1 -0
  9. package/rules/catch-error-name.js +1 -0
  10. package/rules/consistent-destructuring.js +1 -0
  11. package/rules/consistent-empty-array-spread.js +126 -0
  12. package/rules/consistent-function-scoping.js +1 -0
  13. package/rules/custom-error-definition.js +1 -0
  14. package/rules/empty-brace-spaces.js +1 -0
  15. package/rules/error-message.js +1 -0
  16. package/rules/escape-case.js +17 -6
  17. package/rules/expiring-todo-comments.js +11 -2
  18. package/rules/explicit-length-check.js +7 -1
  19. package/rules/filename-case.js +1 -0
  20. package/rules/fix/switch-call-expression-to-new-expression.js +2 -0
  21. package/rules/import-style.js +1 -0
  22. package/rules/new-for-builtins.js +1 -0
  23. package/rules/no-abusive-eslint-disable.js +1 -0
  24. package/rules/no-anonymous-default-export.js +1 -0
  25. package/rules/no-array-callback-reference.js +1 -0
  26. package/rules/no-array-for-each.js +13 -2
  27. package/rules/no-array-method-this-argument.js +3 -2
  28. package/rules/no-array-push-push.js +15 -7
  29. package/rules/no-array-reduce.js +1 -0
  30. package/rules/no-await-expression-member.js +1 -0
  31. package/rules/no-await-in-promise-methods.js +1 -0
  32. package/rules/no-console-spaces.js +1 -0
  33. package/rules/no-document-cookie.js +1 -0
  34. package/rules/no-empty-file.js +2 -2
  35. package/rules/no-for-loop.js +1 -0
  36. package/rules/no-hex-escape.js +13 -2
  37. package/rules/no-instanceof-array.js +1 -0
  38. package/rules/no-invalid-fetch-options.js +111 -0
  39. package/rules/no-invalid-remove-event-listener.js +1 -0
  40. package/rules/no-keyword-prefix.js +1 -0
  41. package/rules/no-lonely-if.js +1 -0
  42. package/rules/no-magic-array-flat-depth.js +54 -0
  43. package/rules/no-negated-condition.js +1 -0
  44. package/rules/no-negation-in-equality-check.js +104 -0
  45. package/rules/no-nested-ternary.js +1 -0
  46. package/rules/no-new-array.js +1 -0
  47. package/rules/no-new-buffer.js +1 -0
  48. package/rules/no-null.js +1 -0
  49. package/rules/no-object-as-default-parameter.js +1 -0
  50. package/rules/no-process-exit.js +1 -0
  51. package/rules/no-single-promise-in-promise-methods.js +1 -0
  52. package/rules/no-static-only-class.js +1 -0
  53. package/rules/no-thenable.js +1 -0
  54. package/rules/no-this-assignment.js +1 -0
  55. package/rules/no-typeof-undefined.js +1 -0
  56. package/rules/no-unnecessary-await.js +1 -0
  57. package/rules/no-unnecessary-polyfills.js +3 -2
  58. package/rules/no-unreadable-array-destructuring.js +1 -0
  59. package/rules/no-unreadable-iife.js +1 -0
  60. package/rules/no-unused-properties.js +1 -0
  61. package/rules/no-useless-fallback-in-spread.js +1 -0
  62. package/rules/no-useless-length-check.js +1 -0
  63. package/rules/no-useless-promise-resolve-reject.js +3 -2
  64. package/rules/no-useless-spread.js +1 -0
  65. package/rules/no-useless-switch-case.js +3 -16
  66. package/rules/no-useless-undefined.js +1 -0
  67. package/rules/no-zero-fractions.js +1 -0
  68. package/rules/number-literal-case.js +1 -0
  69. package/rules/numeric-separators-style.js +1 -0
  70. package/rules/prefer-add-event-listener.js +2 -1
  71. package/rules/prefer-array-find.js +4 -3
  72. package/rules/prefer-array-flat-map.js +1 -0
  73. package/rules/prefer-array-flat.js +1 -0
  74. package/rules/prefer-array-index-of.js +1 -0
  75. package/rules/prefer-array-some.js +65 -7
  76. package/rules/prefer-at.js +1 -0
  77. package/rules/prefer-blob-reading-methods.js +1 -0
  78. package/rules/prefer-code-point.js +1 -0
  79. package/rules/prefer-date-now.js +1 -0
  80. package/rules/prefer-default-parameters.js +1 -0
  81. package/rules/prefer-dom-node-append.js +1 -0
  82. package/rules/prefer-dom-node-dataset.js +12 -0
  83. package/rules/prefer-dom-node-remove.js +1 -0
  84. package/rules/prefer-dom-node-text-content.js +1 -0
  85. package/rules/prefer-event-target.js +1 -0
  86. package/rules/prefer-export-from.js +2 -3
  87. package/rules/prefer-includes.js +8 -4
  88. package/rules/prefer-json-parse-buffer.js +1 -0
  89. package/rules/prefer-keyboard-event-key.js +1 -0
  90. package/rules/prefer-logical-operator-over-ternary.js +1 -0
  91. package/rules/prefer-math-trunc.js +1 -0
  92. package/rules/prefer-modern-dom-apis.js +1 -0
  93. package/rules/prefer-modern-math-apis.js +1 -0
  94. package/rules/prefer-module.js +13 -12
  95. package/rules/prefer-native-coercion-functions.js +1 -0
  96. package/rules/prefer-negative-index.js +1 -0
  97. package/rules/prefer-node-protocol.js +1 -0
  98. package/rules/prefer-number-properties.js +19 -5
  99. package/rules/prefer-object-from-entries.js +1 -0
  100. package/rules/prefer-optional-catch-binding.js +1 -0
  101. package/rules/prefer-prototype-methods.js +1 -0
  102. package/rules/prefer-query-selector.js +1 -0
  103. package/rules/prefer-reflect-apply.js +1 -0
  104. package/rules/prefer-regexp-test.js +1 -0
  105. package/rules/prefer-set-has.js +1 -0
  106. package/rules/prefer-set-size.js +1 -0
  107. package/rules/prefer-spread.js +1 -0
  108. package/rules/prefer-string-raw.js +94 -0
  109. package/rules/prefer-string-replace-all.js +1 -0
  110. package/rules/prefer-string-slice.js +7 -1
  111. package/rules/prefer-string-starts-ends-with.js +1 -0
  112. package/rules/prefer-string-trim-start-end.js +1 -0
  113. package/rules/prefer-structured-clone.js +151 -0
  114. package/rules/prefer-switch.js +1 -0
  115. package/rules/prefer-ternary.js +1 -0
  116. package/rules/prefer-top-level-await.js +1 -0
  117. package/rules/prefer-type-error.js +1 -0
  118. package/rules/prevent-abbreviations.js +2 -1
  119. package/rules/relative-url-style.js +1 -0
  120. package/rules/require-array-join-separator.js +1 -0
  121. package/rules/require-number-to-fixed-digits-argument.js +1 -0
  122. package/rules/require-post-message-target-origin.js +3 -0
  123. package/rules/string-content.js +3 -1
  124. package/rules/switch-case-braces.js +1 -0
  125. package/rules/template-indent.js +9 -7
  126. package/rules/text-encoding-identifier-case.js +1 -0
  127. package/rules/throw-new-error.js +3 -9
  128. package/rules/utils/escape-template-element-raw.js +1 -1
  129. package/rules/utils/get-call-expression-arguments-text.js +11 -6
  130. package/rules/utils/get-call-expression-tokens.js +37 -0
  131. package/rules/utils/index.js +2 -0
  132. package/rules/utils/numeric.js +6 -1
  133. package/rules/utils/rule.js +2 -6
  134. package/configs/all.js +0 -6
  135. package/configs/recommended.js +0 -120
package/index.js CHANGED
@@ -3,8 +3,6 @@ const createDeprecatedRules = require('./rules/utils/create-deprecated-rules.js'
3
3
  const {loadRules} = require('./rules/utils/rule.js');
4
4
  const legacyConfigBase = require('./configs/legacy-config-base.js');
5
5
  const flatConfigBase = require('./configs/flat-config-base.js');
6
- const recommendedRules = require('./configs/recommended.js');
7
- const allRules = require('./configs/all.js');
8
6
  const {name, version} = require('./package.json');
9
7
 
10
8
  const deprecatedRules = createDeprecatedRules({
@@ -28,10 +26,34 @@ const deprecatedRules = createDeprecatedRules({
28
26
  'regex-shorthand': 'unicorn/better-regex',
29
27
  });
30
28
 
31
- const createConfig = (rules, isLegacyConfig = false) => ({
32
- ...(isLegacyConfig ? legacyConfigBase : flatConfigBase),
33
- plugins: isLegacyConfig ? ['unicorn'] : {unicorn},
34
- rules,
29
+ const externalRules = {
30
+ // Covered by `unicorn/no-negated-condition`
31
+ 'no-negated-condition': 'off',
32
+ // Covered by `unicorn/no-nested-ternary`
33
+ 'no-nested-ternary': 'off',
34
+ };
35
+
36
+ const rules = loadRules();
37
+ const recommendedRules = Object.fromEntries(
38
+ Object.entries(rules).map(([id, rule]) => [
39
+ `unicorn/${id}`,
40
+ rule.meta.docs.recommended ? 'error' : 'off',
41
+ ]),
42
+ );
43
+ const allRules = Object.fromEntries(
44
+ Object.keys(rules).map(id => [
45
+ `unicorn/${id}`,
46
+ 'error',
47
+ ]),
48
+ );
49
+
50
+ const createConfig = (rules, flatConfigName = false) => ({
51
+ ...(
52
+ flatConfigName
53
+ ? {...flatConfigBase, name: flatConfigName, plugins: {unicorn}}
54
+ : {...legacyConfigBase, plugins: ['unicorn']}
55
+ ),
56
+ rules: {...externalRules, ...rules},
35
57
  });
36
58
 
37
59
  const unicorn = {
@@ -40,16 +62,16 @@ const unicorn = {
40
62
  version,
41
63
  },
42
64
  rules: {
43
- ...loadRules(),
65
+ ...rules,
44
66
  ...deprecatedRules,
45
67
  },
46
68
  };
47
69
 
48
70
  const configs = {
49
- recommended: createConfig(recommendedRules, /* isLegacyConfig */ true),
50
- all: createConfig(allRules, /* isLegacyConfig */ true),
51
- 'flat/recommended': createConfig(recommendedRules),
52
- 'flat/all': createConfig(allRules),
71
+ recommended: createConfig(recommendedRules),
72
+ all: createConfig(allRules),
73
+ 'flat/recommended': createConfig(recommendedRules, 'unicorn/flat/recommended'),
74
+ 'flat/all': createConfig(allRules, 'unicorn/flat/all'),
53
75
  };
54
76
 
55
77
  module.exports = {...unicorn, configs};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-unicorn",
3
- "version": "52.0.0",
3
+ "version": "54.0.0",
4
4
  "description": "More than 100 powerful ESLint rules",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/eslint-plugin-unicorn",
@@ -13,9 +13,10 @@
13
13
  "main": "index.js",
14
14
  "sideEffects": false,
15
15
  "engines": {
16
- "node": ">=16"
16
+ "node": ">=18.18"
17
17
  },
18
18
  "scripts": {
19
+ "bundle-lodash": "echo \"export {defaultsDeep, camelCase, kebabCase, snakeCase, upperFirst, lowerFirst} from 'lodash-es';\" | npx esbuild --bundle --outfile=rules/utils/lodash.js --format=cjs",
19
20
  "create-rule": "node ./scripts/create-rule.mjs && npm run fix:eslint-docs",
20
21
  "fix": "run-p --continue-on-error fix:*",
21
22
  "fix:eslint-docs": "eslint-doc-generator",
@@ -28,8 +29,7 @@
28
29
  "lint:markdown": "markdownlint \"**/*.md\"",
29
30
  "lint:package-json": "npmPkgJsonLint .",
30
31
  "run-rules-on-codebase": "node ./test/run-rules-on-codebase/lint.mjs",
31
- "bundle-lodash": "echo \"export {defaultsDeep, camelCase, kebabCase, snakeCase, upperFirst, lowerFirst} from 'lodash-es';\" | npx esbuild --bundle --outfile=rules/utils/lodash.js --format=cjs",
32
- "smoke": "eslint-remote-tester --config ./test/smoke/eslint-remote-tester.config.js",
32
+ "smoke": "eslint-remote-tester --config ./test/smoke/eslint-remote-tester.config.mjs",
33
33
  "test": "npm-run-all --continue-on-error lint test:*",
34
34
  "test:js": "c8 ava"
35
35
  },
@@ -49,12 +49,12 @@
49
49
  "xo"
50
50
  ],
51
51
  "dependencies": {
52
- "@babel/helper-validator-identifier": "^7.22.20",
52
+ "@babel/helper-validator-identifier": "^7.24.5",
53
53
  "@eslint-community/eslint-utils": "^4.4.0",
54
- "@eslint/eslintrc": "^2.1.4",
54
+ "@eslint/eslintrc": "^3.0.2",
55
55
  "ci-info": "^4.0.0",
56
56
  "clean-regexp": "^1.0.0",
57
- "core-js-compat": "^3.34.0",
57
+ "core-js-compat": "^3.37.0",
58
58
  "esquery": "^1.5.0",
59
59
  "indent-string": "^4.0.0",
60
60
  "is-builtin-module": "^3.2.1",
@@ -63,39 +63,40 @@
63
63
  "read-pkg-up": "^7.0.1",
64
64
  "regexp-tree": "^0.1.27",
65
65
  "regjsparser": "^0.10.0",
66
- "semver": "^7.5.4",
66
+ "semver": "^7.6.1",
67
67
  "strip-indent": "^3.0.0"
68
68
  },
69
69
  "devDependencies": {
70
- "@babel/code-frame": "^7.23.5",
71
- "@babel/core": "^7.23.6",
72
- "@babel/eslint-parser": "^7.23.3",
70
+ "@babel/code-frame": "^7.24.2",
71
+ "@babel/core": "^7.24.5",
72
+ "@babel/eslint-parser": "^7.24.5",
73
73
  "@lubien/fixture-beta-package": "^1.0.0-beta.1",
74
- "@typescript-eslint/parser": "^6.15.0",
75
- "ava": "^6.0.1",
76
- "c8": "^8.0.1",
74
+ "@typescript-eslint/parser": "^8.0.0-alpha.12",
75
+ "ava": "^6.1.3",
76
+ "c8": "^9.1.0",
77
77
  "chalk": "^5.3.0",
78
78
  "enquirer": "^2.4.1",
79
- "eslint": "^8.56.0",
79
+ "eslint": "^9.2.0",
80
80
  "eslint-ava-rule-tester": "^5.0.1",
81
- "eslint-doc-generator": "^1.7.0",
82
- "eslint-plugin-eslint-plugin": "^5.2.1",
81
+ "eslint-doc-generator": "1.7.0",
82
+ "eslint-plugin-eslint-plugin": "^6.1.0",
83
83
  "eslint-plugin-internal-rules": "file:./scripts/internal-rules/",
84
- "eslint-remote-tester": "^3.0.1",
85
- "eslint-remote-tester-repositories": "^1.0.1",
84
+ "eslint-remote-tester": "^4.0.0",
85
+ "eslint-remote-tester-repositories": "^2.0.0",
86
+ "espree": "^10.0.1",
86
87
  "execa": "^8.0.1",
87
88
  "listr": "^0.14.3",
88
89
  "lodash-es": "^4.17.21",
89
- "markdownlint-cli": "^0.38.0",
90
+ "markdownlint-cli": "^0.40.0",
90
91
  "memoize": "^10.0.0",
91
92
  "npm-package-json-lint": "^7.1.0",
92
- "npm-run-all2": "^6.1.1",
93
+ "npm-run-all2": "^6.1.2",
93
94
  "outdent": "^0.8.0",
94
- "pretty-ms": "^8.0.0",
95
- "typescript": "^5.3.3",
96
- "vue-eslint-parser": "^9.3.2",
97
- "xo": "^0.56.0",
98
- "yaml": "^2.3.4"
95
+ "pretty-ms": "^9.0.0",
96
+ "typescript": "^5.4.5",
97
+ "vue-eslint-parser": "^9.4.2",
98
+ "xo": "^0.58.0",
99
+ "yaml": "^2.4.2"
99
100
  },
100
101
  "peerDependencies": {
101
102
  "eslint": ">=8.56.0"
@@ -123,7 +124,9 @@
123
124
  "test/integration/{fixtures,fixtures-local}/**"
124
125
  ],
125
126
  "rules": {
127
+ "unicorn/escape-case": "off",
126
128
  "unicorn/expiring-todo-comments": "off",
129
+ "unicorn/no-hex-escape": "off",
127
130
  "unicorn/no-null": "error",
128
131
  "unicorn/prefer-array-flat": [
129
132
  "error",
@@ -173,17 +176,5 @@
173
176
  }
174
177
  }
175
178
  ]
176
- },
177
- "npmpackagejsonlint": {
178
- "rules": {
179
- "prefer-caret-version-devDependencies": [
180
- "error",
181
- {
182
- "exceptions": [
183
- "eslint-plugin-internal-rules"
184
- ]
185
- }
186
- ]
187
- }
188
179
  }
189
180
  }
package/readme.md CHANGED
@@ -17,7 +17,7 @@ npm install --save-dev eslint eslint-plugin-unicorn
17
17
 
18
18
  ## Usage (`eslint.config.js`)
19
19
 
20
- **Requires ESLint `>=8.23.0`.**
20
+ **Requires ESLint `>=8.56.0`.**
21
21
 
22
22
  Use a [preset config](#preset-configs-eslintconfigjs) or configure each rule in `eslint.config.js`.
23
23
 
@@ -103,8 +103,8 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
103
103
  <!-- Do not manually modify this list. Run: `npm run fix:eslint-docs` -->
104
104
  <!-- begin auto-generated rules list -->
105
105
 
106
- 💼 [Configurations](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs) enabled in.\
107
- ✅ Set in the `recommended` [configuration](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs).\
106
+ 💼 [Configurations](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs-eslintconfigjs) enabled in.\
107
+ ✅ Set in the `recommended` [configuration](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs-eslintconfigjs).\
108
108
  🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
109
109
  💡 Manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
110
110
 
@@ -113,6 +113,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
113
113
  | [better-regex](docs/rules/better-regex.md) | Improve regexes by making them shorter, consistent, and safer. | ✅ | 🔧 | |
114
114
  | [catch-error-name](docs/rules/catch-error-name.md) | Enforce a specific parameter name in catch clauses. | ✅ | 🔧 | |
115
115
  | [consistent-destructuring](docs/rules/consistent-destructuring.md) | Use destructured variables over properties. | | 🔧 | 💡 |
116
+ | [consistent-empty-array-spread](docs/rules/consistent-empty-array-spread.md) | Prefer consistent types when spreading a ternary in an array literal. | ✅ | 🔧 | |
116
117
  | [consistent-function-scoping](docs/rules/consistent-function-scoping.md) | Move function definitions to the highest possible scope. | ✅ | | |
117
118
  | [custom-error-definition](docs/rules/custom-error-definition.md) | Enforce correct `Error` subclassing. | | 🔧 | |
118
119
  | [empty-brace-spaces](docs/rules/empty-brace-spaces.md) | Enforce no spaces between braces. | ✅ | 🔧 | |
@@ -138,10 +139,13 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
138
139
  | [no-for-loop](docs/rules/no-for-loop.md) | Do not use a `for` loop that can be replaced with a `for-of` loop. | ✅ | 🔧 | 💡 |
139
140
  | [no-hex-escape](docs/rules/no-hex-escape.md) | Enforce the use of Unicode escapes instead of hexadecimal escapes. | ✅ | 🔧 | |
140
141
  | [no-instanceof-array](docs/rules/no-instanceof-array.md) | Require `Array.isArray()` instead of `instanceof Array`. | ✅ | 🔧 | |
142
+ | [no-invalid-fetch-options](docs/rules/no-invalid-fetch-options.md) | Disallow invalid options in `fetch()` and `new Request()`. | ✅ | | |
141
143
  | [no-invalid-remove-event-listener](docs/rules/no-invalid-remove-event-listener.md) | Prevent calling `EventTarget#removeEventListener()` with the result of an expression. | ✅ | | |
142
144
  | [no-keyword-prefix](docs/rules/no-keyword-prefix.md) | Disallow identifiers starting with `new` or `class`. | | | |
143
145
  | [no-lonely-if](docs/rules/no-lonely-if.md) | Disallow `if` statements as the only statement in `if` blocks without `else`. | ✅ | 🔧 | |
146
+ | [no-magic-array-flat-depth](docs/rules/no-magic-array-flat-depth.md) | Disallow a magic number as the `depth` argument in `Array#flat(…).` | ✅ | | |
144
147
  | [no-negated-condition](docs/rules/no-negated-condition.md) | Disallow negated conditions. | ✅ | 🔧 | |
148
+ | [no-negation-in-equality-check](docs/rules/no-negation-in-equality-check.md) | Disallow negated expression in equality check. | ✅ | | 💡 |
145
149
  | [no-nested-ternary](docs/rules/no-nested-ternary.md) | Disallow nested ternary expressions. | ✅ | 🔧 | |
146
150
  | [no-new-array](docs/rules/no-new-array.md) | Disallow `new Array()`. | ✅ | 🔧 | 💡 |
147
151
  | [no-new-buffer](docs/rules/no-new-buffer.md) | Enforce the use of `Buffer.from()` and `Buffer.alloc()` instead of the deprecated `new Buffer()`. | ✅ | 🔧 | 💡 |
@@ -172,7 +176,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
172
176
  | [prefer-array-flat](docs/rules/prefer-array-flat.md) | Prefer `Array#flat()` over legacy techniques to flatten arrays. | ✅ | 🔧 | |
173
177
  | [prefer-array-flat-map](docs/rules/prefer-array-flat-map.md) | Prefer `.flatMap(…)` over `.map(…).flat()`. | ✅ | 🔧 | |
174
178
  | [prefer-array-index-of](docs/rules/prefer-array-index-of.md) | Prefer `Array#{indexOf,lastIndexOf}()` over `Array#{findIndex,findLastIndex}()` when looking for the index of an item. | ✅ | 🔧 | 💡 |
175
- | [prefer-array-some](docs/rules/prefer-array-some.md) | Prefer `.some(…)` over `.filter(…).length` check and `.{find,findLast}(…)`. | ✅ | 🔧 | 💡 |
179
+ | [prefer-array-some](docs/rules/prefer-array-some.md) | Prefer `.some(…)` over `.filter(…).length` check and `.{find,findLast,findIndex,findLastIndex}(…)`. | ✅ | 🔧 | 💡 |
176
180
  | [prefer-at](docs/rules/prefer-at.md) | Prefer `.at()` method for index access and `String#charAt()`. | ✅ | 🔧 | 💡 |
177
181
  | [prefer-blob-reading-methods](docs/rules/prefer-blob-reading-methods.md) | Prefer `Blob#arrayBuffer()` over `FileReader#readAsArrayBuffer(…)` and `Blob#text()` over `FileReader#readAsText(…)`. | ✅ | | |
178
182
  | [prefer-code-point](docs/rules/prefer-code-point.md) | Prefer `String#codePointAt(…)` over `String#charCodeAt(…)` and `String.fromCodePoint(…)` over `String.fromCharCode(…)`. | ✅ | | 💡 |
@@ -184,7 +188,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
184
188
  | [prefer-dom-node-text-content](docs/rules/prefer-dom-node-text-content.md) | Prefer `.textContent` over `.innerText`. | ✅ | | 💡 |
185
189
  | [prefer-event-target](docs/rules/prefer-event-target.md) | Prefer `EventTarget` over `EventEmitter`. | ✅ | | |
186
190
  | [prefer-export-from](docs/rules/prefer-export-from.md) | Prefer `export…from` when re-exporting. | ✅ | 🔧 | 💡 |
187
- | [prefer-includes](docs/rules/prefer-includes.md) | Prefer `.includes()` over `.indexOf()` and `Array#some()` when checking for existence or non-existence. | ✅ | 🔧 | 💡 |
191
+ | [prefer-includes](docs/rules/prefer-includes.md) | Prefer `.includes()` over `.indexOf()`, `.lastIndexOf()`, and `Array#some()` when checking for existence or non-existence. | ✅ | 🔧 | 💡 |
188
192
  | [prefer-json-parse-buffer](docs/rules/prefer-json-parse-buffer.md) | Prefer reading a JSON file as a buffer. | | 🔧 | |
189
193
  | [prefer-keyboard-event-key](docs/rules/prefer-keyboard-event-key.md) | Prefer `KeyboardEvent#key` over `KeyboardEvent#keyCode`. | ✅ | 🔧 | |
190
194
  | [prefer-logical-operator-over-ternary](docs/rules/prefer-logical-operator-over-ternary.md) | Prefer using a logical operator over a ternary. | ✅ | | 💡 |
@@ -205,10 +209,12 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
205
209
  | [prefer-set-has](docs/rules/prefer-set-has.md) | Prefer `Set#has()` over `Array#includes()` when checking for existence or non-existence. | ✅ | 🔧 | 💡 |
206
210
  | [prefer-set-size](docs/rules/prefer-set-size.md) | Prefer using `Set#size` instead of `Array#length`. | ✅ | 🔧 | |
207
211
  | [prefer-spread](docs/rules/prefer-spread.md) | Prefer the spread operator over `Array.from(…)`, `Array#concat(…)`, `Array#{slice,toSpliced}()` and `String#split('')`. | ✅ | 🔧 | 💡 |
212
+ | [prefer-string-raw](docs/rules/prefer-string-raw.md) | Prefer using the `String.raw` tag to avoid escaping `\`. | ✅ | 🔧 | |
208
213
  | [prefer-string-replace-all](docs/rules/prefer-string-replace-all.md) | Prefer `String#replaceAll()` over regex searches with the global flag. | ✅ | 🔧 | |
209
214
  | [prefer-string-slice](docs/rules/prefer-string-slice.md) | Prefer `String#slice()` over `String#substr()` and `String#substring()`. | ✅ | 🔧 | |
210
215
  | [prefer-string-starts-ends-with](docs/rules/prefer-string-starts-ends-with.md) | Prefer `String#startsWith()` & `String#endsWith()` over `RegExp#test()`. | ✅ | 🔧 | 💡 |
211
216
  | [prefer-string-trim-start-end](docs/rules/prefer-string-trim-start-end.md) | Prefer `String#trimStart()` / `String#trimEnd()` over `String#trimLeft()` / `String#trimRight()`. | ✅ | 🔧 | |
217
+ | [prefer-structured-clone](docs/rules/prefer-structured-clone.md) | Prefer using `structuredClone` to create a deep clone. | ✅ | | 💡 |
212
218
  | [prefer-switch](docs/rules/prefer-switch.md) | Prefer `switch` over multiple `else-if`. | ✅ | 🔧 | |
213
219
  | [prefer-ternary](docs/rules/prefer-ternary.md) | Prefer ternary expressions over simple `if-else` statements. | ✅ | 🔧 | |
214
220
  | [prefer-top-level-await](docs/rules/prefer-top-level-await.md) | Prefer top-level await over top-level promises and async function calls. | ✅ | | 💡 |
@@ -222,7 +228,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
222
228
  | [switch-case-braces](docs/rules/switch-case-braces.md) | Enforce consistent brace style for `case` clauses. | ✅ | 🔧 | |
223
229
  | [template-indent](docs/rules/template-indent.md) | Fix whitespace-insensitive template indentation. | ✅ | 🔧 | |
224
230
  | [text-encoding-identifier-case](docs/rules/text-encoding-identifier-case.md) | Enforce consistent case for text encoding identifiers. | ✅ | 🔧 | 💡 |
225
- | [throw-new-error](docs/rules/throw-new-error.md) | Require `new` when throwing an error. | ✅ | 🔧 | |
231
+ | [throw-new-error](docs/rules/throw-new-error.md) | Require `new` when creating an error. | ✅ | 🔧 | |
226
232
 
227
233
  <!-- end auto-generated rules list -->
228
234
 
@@ -25,6 +25,7 @@ module.exports = {
25
25
  isArrowFunctionBody: require('./is-arrow-function-body.js'),
26
26
  isCallExpression,
27
27
  isCallOrNewExpression,
28
+ isDirective: require('./is-directive.js'),
28
29
  isEmptyNode: require('./is-empty-node.js'),
29
30
  isExpressionStatement: require('./is-expression-statement.js'),
30
31
  isFunction: require('./is-function.js'),
@@ -33,6 +34,7 @@ module.exports = {
33
34
  isNewExpression,
34
35
  isReferenceIdentifier: require('./is-reference-identifier.js'),
35
36
  isStaticRequire: require('./is-static-require.js'),
37
+ isTaggedTemplateLiteral: require('./is-tagged-template-literal.js'),
36
38
  isUndefined: require('./is-undefined.js'),
37
39
 
38
40
  functionTypes: require('./function-types.js'),
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const isDirective = node =>
4
+ node.type === 'ExpressionStatement'
5
+ && typeof node.directive === 'string';
6
+
7
+ module.exports = isDirective;
@@ -120,11 +120,18 @@ function isNotReference(node) {
120
120
  return parent.parameters.includes(node);
121
121
  }
122
122
 
123
+ // `@typescript-eslint/parse` v7
123
124
  // `type Foo = { [Identifier in keyof string]: number; };`
124
125
  case 'TSTypeParameter': {
125
126
  return parent.name === node;
126
127
  }
127
128
 
129
+ // `@typescript-eslint/parse` v8
130
+ // `type Foo = { [Identifier in keyof string]: number; };`
131
+ case 'TSMappedType': {
132
+ return parent.key === node;
133
+ }
134
+
128
135
  // `type Identifier = Foo`
129
136
  case 'TSTypeAliasDeclaration': {
130
137
  return parent.id === node;
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const {isNodeMatches} = require('../utils/is-node-matches.js');
4
+
5
+ /**
6
+ Check if the given node is a tagged template literal.
7
+
8
+ @param {Node} node - The AST node to check.
9
+ @param {string[]} tags - The object name or key paths.
10
+ @returns {boolean}
11
+ */
12
+ function isTaggedTemplateLiteral(node, tags) {
13
+ if (
14
+ node.type !== 'TemplateLiteral'
15
+ || node.parent.type !== 'TaggedTemplateExpression'
16
+ || node.parent.quasi !== node
17
+ ) {
18
+ return false;
19
+ }
20
+
21
+ if (tags) {
22
+ return isNodeMatches(node.parent.tag, tags);
23
+ }
24
+
25
+ return true;
26
+ }
27
+
28
+ module.exports = isTaggedTemplateLiteral;
@@ -136,6 +136,7 @@ module.exports = {
136
136
  type: 'suggestion',
137
137
  docs: {
138
138
  description: 'Improve regexes by making them shorter, consistent, and safer.',
139
+ recommended: true,
139
140
  },
140
141
  fixable: 'code',
141
142
  schema,
@@ -128,6 +128,7 @@ module.exports = {
128
128
  type: 'suggestion',
129
129
  docs: {
130
130
  description: 'Enforce a specific parameter name in catch clauses.',
131
+ recommended: true,
131
132
  },
132
133
  fixable: 'code',
133
134
  schema,
@@ -157,6 +157,7 @@ module.exports = {
157
157
  type: 'suggestion',
158
158
  docs: {
159
159
  description: 'Use destructured variables over properties.',
160
+ recommended: false,
160
161
  },
161
162
  fixable: 'code',
162
163
  hasSuggestions: true,
@@ -0,0 +1,126 @@
1
+ 'use strict';
2
+ const {getStaticValue} = require('@eslint-community/eslint-utils');
3
+
4
+ const MESSAGE_ID = 'consistent-empty-array-spread';
5
+ const messages = {
6
+ [MESSAGE_ID]: 'Prefer using empty {{replacementDescription}} since the {{anotherNodePosition}} is {{anotherNodeDescription}}.',
7
+ };
8
+
9
+ const isEmptyArrayExpression = node =>
10
+ node.type === 'ArrayExpression'
11
+ && node.elements.length === 0;
12
+
13
+ const isEmptyStringLiteral = node =>
14
+ node.type === 'Literal'
15
+ && node.value === '';
16
+
17
+ const isString = (node, context) => {
18
+ const staticValueResult = getStaticValue(node, context.sourceCode.getScope(node));
19
+ return typeof staticValueResult?.value === 'string';
20
+ };
21
+
22
+ const isArray = (node, context) => {
23
+ if (node.type === 'ArrayExpression') {
24
+ return true;
25
+ }
26
+
27
+ const staticValueResult = getStaticValue(node, context.sourceCode.getScope(node));
28
+ return Array.isArray(staticValueResult?.value);
29
+ };
30
+
31
+ const cases = [
32
+ {
33
+ oneSidePredicate: isEmptyStringLiteral,
34
+ anotherSidePredicate: isArray,
35
+ anotherNodeDescription: 'an array',
36
+ replacementDescription: 'array',
37
+ replacementCode: '[]',
38
+ },
39
+ {
40
+ oneSidePredicate: isEmptyArrayExpression,
41
+ anotherSidePredicate: isString,
42
+ anotherNodeDescription: 'a string',
43
+ replacementDescription: 'string',
44
+ replacementCode: '\'\'',
45
+ },
46
+ ];
47
+
48
+ function createProblem({
49
+ problemNode,
50
+ anotherNodePosition,
51
+ anotherNodeDescription,
52
+ replacementDescription,
53
+ replacementCode,
54
+ }) {
55
+ return {
56
+ node: problemNode,
57
+ messageId: MESSAGE_ID,
58
+ data: {
59
+ replacementDescription,
60
+ anotherNodePosition,
61
+ anotherNodeDescription,
62
+ },
63
+ fix: fixer => fixer.replaceText(problemNode, replacementCode),
64
+ };
65
+ }
66
+
67
+ function getProblem(conditionalExpression, context) {
68
+ const {
69
+ consequent,
70
+ alternate,
71
+ } = conditionalExpression;
72
+
73
+ for (const problemCase of cases) {
74
+ const {
75
+ oneSidePredicate,
76
+ anotherSidePredicate,
77
+ } = problemCase;
78
+
79
+ if (oneSidePredicate(consequent, context) && anotherSidePredicate(alternate, context)) {
80
+ return createProblem({
81
+ ...problemCase,
82
+ problemNode: consequent,
83
+ anotherNodePosition: 'alternate',
84
+ });
85
+ }
86
+
87
+ if (oneSidePredicate(alternate, context) && anotherSidePredicate(consequent, context)) {
88
+ return createProblem({
89
+ ...problemCase,
90
+ problemNode: alternate,
91
+ anotherNodePosition: 'consequent',
92
+ });
93
+ }
94
+ }
95
+ }
96
+
97
+ /** @param {import('eslint').Rule.RuleContext} context */
98
+ const create = context => ({
99
+ * ArrayExpression(arrayExpression) {
100
+ for (const element of arrayExpression.elements) {
101
+ if (
102
+ element?.type !== 'SpreadElement'
103
+ || element.argument.type !== 'ConditionalExpression'
104
+ ) {
105
+ continue;
106
+ }
107
+
108
+ yield getProblem(element.argument, context);
109
+ }
110
+ },
111
+ });
112
+
113
+ /** @type {import('eslint').Rule.RuleModule} */
114
+ module.exports = {
115
+ create,
116
+ meta: {
117
+ type: 'suggestion',
118
+ docs: {
119
+ description: 'Prefer consistent types when spreading a ternary in an array literal.',
120
+ recommended: true,
121
+ },
122
+ fixable: 'code',
123
+
124
+ messages,
125
+ },
126
+ };
@@ -216,6 +216,7 @@ module.exports = {
216
216
  type: 'suggestion',
217
217
  docs: {
218
218
  description: 'Move function definitions to the highest possible scope.',
219
+ recommended: true,
219
220
  },
220
221
  schema,
221
222
  messages,
@@ -208,6 +208,7 @@ module.exports = {
208
208
  type: 'problem',
209
209
  docs: {
210
210
  description: 'Enforce correct `Error` subclassing.',
211
+ recommended: false,
211
212
  },
212
213
  fixable: 'code',
213
214
  messages,
@@ -65,6 +65,7 @@ module.exports = {
65
65
  type: 'layout',
66
66
  docs: {
67
67
  description: 'Enforce no spaces between braces.',
68
+ recommended: true,
68
69
  },
69
70
  fixable: 'whitespace',
70
71
  messages,
@@ -98,6 +98,7 @@ module.exports = {
98
98
  type: 'problem',
99
99
  docs: {
100
100
  description: 'Enforce passing a `message` value when creating a built-in error.',
101
+ recommended: true,
101
102
  },
102
103
  messages,
103
104
  },
@@ -1,6 +1,10 @@
1
1
  'use strict';
2
2
  const {replaceTemplateElement} = require('./fix/index.js');
3
- const {isRegexLiteral, isStringLiteral} = require('./ast/index.js');
3
+ const {
4
+ isRegexLiteral,
5
+ isStringLiteral,
6
+ isTaggedTemplateLiteral,
7
+ } = require('./ast/index.js');
4
8
 
5
9
  const MESSAGE_ID = 'escape-case';
6
10
  const messages = {
@@ -42,11 +46,17 @@ const create = context => {
42
46
  }
43
47
  });
44
48
 
45
- context.on('TemplateElement', node => getProblem({
46
- node,
47
- original: node.value.raw,
48
- fix: (fixer, fixed) => replaceTemplateElement(fixer, node, fixed),
49
- }));
49
+ context.on('TemplateElement', node => {
50
+ if (isTaggedTemplateLiteral(node.parent, ['String.raw'])) {
51
+ return;
52
+ }
53
+
54
+ return getProblem({
55
+ node,
56
+ original: node.value.raw,
57
+ fix: (fixer, fixed) => replaceTemplateElement(fixer, node, fixed),
58
+ });
59
+ });
50
60
  };
51
61
 
52
62
  /** @type {import('eslint').Rule.RuleModule} */
@@ -56,6 +66,7 @@ module.exports = {
56
66
  type: 'suggestion',
57
67
  docs: {
58
68
  description: 'Require escape sequences to use uppercase values.',
69
+ recommended: true,
59
70
  },
60
71
  fixable: 'code',
61
72
  messages,
@@ -186,7 +186,15 @@ function getPackageHelpers(dirname) {
186
186
  return afterArguments;
187
187
  }
188
188
 
189
- return {packageResult, hasPackage, packageJson, packageDependencies, parseArgument, parseTodoMessage, parseTodoWithArguments};
189
+ return {
190
+ packageResult,
191
+ hasPackage,
192
+ packageJson,
193
+ packageDependencies,
194
+ parseArgument,
195
+ parseTodoMessage,
196
+ parseTodoWithArguments,
197
+ };
190
198
  }
191
199
 
192
200
  const DEPENDENCY_INCLUSION_RE = /^[+-]\s*@?\S+\/?\S+/;
@@ -197,7 +205,7 @@ const ISO8601_DATE = /\d{4}-\d{2}-\d{2}/;
197
205
  function createArgumentGroup(arguments_) {
198
206
  const groups = {};
199
207
  for (const {value, type} of arguments_) {
200
- groups[type] = groups[type] || [];
208
+ groups[type] ??= [];
201
209
  groups[type].push(value);
202
210
  }
203
211
 
@@ -573,6 +581,7 @@ module.exports = {
573
581
  type: 'suggestion',
574
582
  docs: {
575
583
  description: 'Add expiration conditions to TODO comments.',
584
+ recommended: true,
576
585
  },
577
586
  schema,
578
587
  messages,