eslint 9.22.0 → 9.24.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 (417) hide show
  1. package/README.md +48 -46
  2. package/bin/eslint.js +92 -90
  3. package/conf/default-cli-options.js +22 -22
  4. package/conf/ecma-version.js +1 -1
  5. package/conf/globals.js +97 -98
  6. package/conf/replacements.json +24 -20
  7. package/conf/rule-type-list.json +88 -92
  8. package/lib/api.js +12 -12
  9. package/lib/cli-engine/cli-engine.js +830 -810
  10. package/lib/cli-engine/file-enumerator.js +381 -387
  11. package/lib/cli-engine/formatters/formatters-meta.json +16 -16
  12. package/lib/cli-engine/formatters/html.js +107 -99
  13. package/lib/cli-engine/formatters/json-with-metadata.js +5 -5
  14. package/lib/cli-engine/formatters/json.js +2 -2
  15. package/lib/cli-engine/formatters/stylish.js +96 -75
  16. package/lib/cli-engine/hash.js +1 -1
  17. package/lib/cli-engine/index.js +1 -1
  18. package/lib/cli-engine/lint-result-cache.js +144 -145
  19. package/lib/cli-engine/load-rules.js +16 -16
  20. package/lib/cli.js +638 -457
  21. package/lib/config/config-loader.js +726 -622
  22. package/lib/config/config.js +247 -221
  23. package/lib/config/default-config.js +54 -45
  24. package/lib/config/flat-config-array.js +167 -172
  25. package/lib/config/flat-config-helpers.js +72 -72
  26. package/lib/config/flat-config-schema.js +375 -368
  27. package/lib/config/rule-validator.js +139 -144
  28. package/lib/config-api.js +2 -2
  29. package/lib/eslint/eslint-helpers.js +756 -681
  30. package/lib/eslint/eslint.js +934 -912
  31. package/lib/eslint/index.js +2 -2
  32. package/lib/eslint/legacy-eslint.js +577 -533
  33. package/lib/languages/js/index.js +263 -264
  34. package/lib/languages/js/source-code/index.js +1 -1
  35. package/lib/languages/js/source-code/source-code.js +1128 -1057
  36. package/lib/languages/js/source-code/token-store/backward-token-comment-cursor.js +39 -35
  37. package/lib/languages/js/source-code/token-store/backward-token-cursor.js +35 -36
  38. package/lib/languages/js/source-code/token-store/cursor.js +36 -36
  39. package/lib/languages/js/source-code/token-store/cursors.js +80 -52
  40. package/lib/languages/js/source-code/token-store/decorative-cursor.js +17 -18
  41. package/lib/languages/js/source-code/token-store/filter-cursor.js +19 -20
  42. package/lib/languages/js/source-code/token-store/forward-token-comment-cursor.js +40 -32
  43. package/lib/languages/js/source-code/token-store/forward-token-cursor.js +40 -41
  44. package/lib/languages/js/source-code/token-store/index.js +592 -498
  45. package/lib/languages/js/source-code/token-store/limit-cursor.js +17 -18
  46. package/lib/languages/js/source-code/token-store/padded-token-cursor.js +23 -16
  47. package/lib/languages/js/source-code/token-store/skip-cursor.js +19 -20
  48. package/lib/languages/js/source-code/token-store/utils.js +63 -60
  49. package/lib/languages/js/validate-language-options.js +104 -89
  50. package/lib/linter/apply-disable-directives.js +467 -383
  51. package/lib/linter/code-path-analysis/code-path-analyzer.js +650 -672
  52. package/lib/linter/code-path-analysis/code-path-segment.js +215 -216
  53. package/lib/linter/code-path-analysis/code-path-state.js +2118 -2096
  54. package/lib/linter/code-path-analysis/code-path.js +307 -319
  55. package/lib/linter/code-path-analysis/debug-helpers.js +183 -163
  56. package/lib/linter/code-path-analysis/fork-context.js +296 -271
  57. package/lib/linter/code-path-analysis/id-generator.js +22 -23
  58. package/lib/linter/file-context.js +119 -120
  59. package/lib/linter/index.js +3 -3
  60. package/lib/linter/interpolate.js +16 -16
  61. package/lib/linter/linter.js +2403 -2045
  62. package/lib/linter/node-event-generator.js +284 -225
  63. package/lib/linter/report-translator.js +256 -219
  64. package/lib/linter/rule-fixer.js +122 -124
  65. package/lib/linter/rules.js +36 -36
  66. package/lib/linter/safe-emitter.js +18 -18
  67. package/lib/linter/source-code-fixer.js +94 -92
  68. package/lib/linter/timing.js +104 -101
  69. package/lib/linter/vfile.js +70 -73
  70. package/lib/options.js +404 -361
  71. package/lib/rule-tester/index.js +1 -1
  72. package/lib/rule-tester/rule-tester.js +1308 -1046
  73. package/lib/rules/accessor-pairs.js +298 -263
  74. package/lib/rules/array-bracket-newline.js +250 -238
  75. package/lib/rules/array-bracket-spacing.js +263 -224
  76. package/lib/rules/array-callback-return.js +402 -356
  77. package/lib/rules/array-element-newline.js +358 -313
  78. package/lib/rules/arrow-body-style.js +400 -281
  79. package/lib/rules/arrow-parens.js +206 -173
  80. package/lib/rules/arrow-spacing.js +169 -163
  81. package/lib/rules/block-scoped-var.js +125 -123
  82. package/lib/rules/block-spacing.js +186 -176
  83. package/lib/rules/brace-style.js +262 -199
  84. package/lib/rules/callback-return.js +203 -190
  85. package/lib/rules/camelcase.js +403 -392
  86. package/lib/rules/capitalized-comments.js +253 -232
  87. package/lib/rules/class-methods-use-this.js +224 -172
  88. package/lib/rules/comma-dangle.js +379 -346
  89. package/lib/rules/comma-spacing.js +193 -195
  90. package/lib/rules/comma-style.js +375 -316
  91. package/lib/rules/complexity.js +173 -169
  92. package/lib/rules/computed-property-spacing.js +236 -211
  93. package/lib/rules/consistent-return.js +181 -170
  94. package/lib/rules/consistent-this.js +167 -147
  95. package/lib/rules/constructor-super.js +412 -404
  96. package/lib/rules/curly.js +407 -332
  97. package/lib/rules/default-case-last.js +38 -31
  98. package/lib/rules/default-case.js +89 -85
  99. package/lib/rules/default-param-last.js +69 -54
  100. package/lib/rules/dot-location.js +122 -110
  101. package/lib/rules/dot-notation.js +192 -156
  102. package/lib/rules/eol-last.js +122 -120
  103. package/lib/rules/eqeqeq.js +168 -155
  104. package/lib/rules/for-direction.js +146 -121
  105. package/lib/rules/func-call-spacing.js +261 -231
  106. package/lib/rules/func-name-matching.js +293 -209
  107. package/lib/rules/func-names.js +165 -164
  108. package/lib/rules/func-style.js +159 -127
  109. package/lib/rules/function-call-argument-newline.js +152 -129
  110. package/lib/rules/function-paren-newline.js +349 -291
  111. package/lib/rules/generator-star-spacing.js +229 -210
  112. package/lib/rules/getter-return.js +208 -172
  113. package/lib/rules/global-require.js +85 -74
  114. package/lib/rules/grouped-accessor-pairs.js +170 -150
  115. package/lib/rules/guard-for-in.js +72 -63
  116. package/lib/rules/handle-callback-err.js +108 -103
  117. package/lib/rules/id-blacklist.js +182 -199
  118. package/lib/rules/id-denylist.js +168 -187
  119. package/lib/rules/id-length.js +197 -171
  120. package/lib/rules/id-match.js +344 -289
  121. package/lib/rules/implicit-arrow-linebreak.js +102 -79
  122. package/lib/rules/indent-legacy.js +1344 -1118
  123. package/lib/rules/indent.js +2272 -1759
  124. package/lib/rules/index.js +317 -292
  125. package/lib/rules/init-declarations.js +137 -107
  126. package/lib/rules/jsx-quotes.js +94 -82
  127. package/lib/rules/key-spacing.js +750 -633
  128. package/lib/rules/keyword-spacing.js +648 -605
  129. package/lib/rules/line-comment-position.js +142 -128
  130. package/lib/rules/linebreak-style.js +107 -106
  131. package/lib/rules/lines-around-comment.js +540 -448
  132. package/lib/rules/lines-around-directive.js +233 -203
  133. package/lib/rules/lines-between-class-members.js +305 -234
  134. package/lib/rules/logical-assignment-operators.js +582 -399
  135. package/lib/rules/max-classes-per-file.js +69 -68
  136. package/lib/rules/max-depth.js +146 -143
  137. package/lib/rules/max-len.js +473 -434
  138. package/lib/rules/max-lines-per-function.js +201 -176
  139. package/lib/rules/max-lines.js +158 -162
  140. package/lib/rules/max-nested-callbacks.js +102 -104
  141. package/lib/rules/max-params.js +78 -76
  142. package/lib/rules/max-statements-per-line.js +205 -198
  143. package/lib/rules/max-statements.js +168 -164
  144. package/lib/rules/multiline-comment-style.js +637 -479
  145. package/lib/rules/multiline-ternary.js +241 -176
  146. package/lib/rules/new-cap.js +233 -213
  147. package/lib/rules/new-parens.js +88 -79
  148. package/lib/rules/newline-after-var.js +287 -250
  149. package/lib/rules/newline-before-return.js +229 -222
  150. package/lib/rules/newline-per-chained-call.js +142 -127
  151. package/lib/rules/no-alert.js +90 -79
  152. package/lib/rules/no-array-constructor.js +125 -113
  153. package/lib/rules/no-async-promise-executor.js +30 -24
  154. package/lib/rules/no-await-in-loop.js +69 -71
  155. package/lib/rules/no-bitwise.js +124 -100
  156. package/lib/rules/no-buffer-constructor.js +55 -47
  157. package/lib/rules/no-caller.js +39 -33
  158. package/lib/rules/no-case-declarations.js +61 -57
  159. package/lib/rules/no-catch-shadow.js +76 -73
  160. package/lib/rules/no-class-assign.js +51 -48
  161. package/lib/rules/no-compare-neg-zero.js +62 -48
  162. package/lib/rules/no-cond-assign.js +148 -132
  163. package/lib/rules/no-confusing-arrow.js +98 -81
  164. package/lib/rules/no-console.js +202 -199
  165. package/lib/rules/no-const-assign.js +47 -41
  166. package/lib/rules/no-constant-binary-expression.js +500 -405
  167. package/lib/rules/no-constant-condition.js +158 -143
  168. package/lib/rules/no-constructor-return.js +49 -49
  169. package/lib/rules/no-continue.js +25 -27
  170. package/lib/rules/no-control-regex.js +125 -121
  171. package/lib/rules/no-debugger.js +28 -30
  172. package/lib/rules/no-delete-var.js +29 -29
  173. package/lib/rules/no-div-regex.js +47 -41
  174. package/lib/rules/no-dupe-args.js +68 -69
  175. package/lib/rules/no-dupe-class-members.js +102 -89
  176. package/lib/rules/no-dupe-else-if.js +100 -77
  177. package/lib/rules/no-dupe-keys.js +133 -110
  178. package/lib/rules/no-duplicate-case.js +50 -43
  179. package/lib/rules/no-duplicate-imports.js +179 -176
  180. package/lib/rules/no-else-return.js +430 -385
  181. package/lib/rules/no-empty-character-class.js +57 -50
  182. package/lib/rules/no-empty-function.js +127 -128
  183. package/lib/rules/no-empty-pattern.js +63 -58
  184. package/lib/rules/no-empty-static-block.js +37 -35
  185. package/lib/rules/no-empty.js +98 -86
  186. package/lib/rules/no-eq-null.js +37 -32
  187. package/lib/rules/no-eval.js +256 -250
  188. package/lib/rules/no-ex-assign.js +42 -39
  189. package/lib/rules/no-extend-native.js +161 -159
  190. package/lib/rules/no-extra-bind.js +201 -190
  191. package/lib/rules/no-extra-boolean-cast.js +398 -348
  192. package/lib/rules/no-extra-label.js +150 -131
  193. package/lib/rules/no-extra-parens.js +1654 -1325
  194. package/lib/rules/no-extra-semi.js +146 -144
  195. package/lib/rules/no-fallthrough.js +199 -157
  196. package/lib/rules/no-floating-decimal.js +74 -66
  197. package/lib/rules/no-func-assign.js +54 -55
  198. package/lib/rules/no-global-assign.js +78 -73
  199. package/lib/rules/no-implicit-coercion.js +349 -293
  200. package/lib/rules/no-implicit-globals.js +158 -135
  201. package/lib/rules/no-implied-eval.js +140 -112
  202. package/lib/rules/no-import-assign.js +145 -159
  203. package/lib/rules/no-inline-comments.js +101 -95
  204. package/lib/rules/no-inner-declarations.js +115 -101
  205. package/lib/rules/no-invalid-regexp.js +222 -190
  206. package/lib/rules/no-invalid-this.js +123 -117
  207. package/lib/rules/no-irregular-whitespace.js +266 -252
  208. package/lib/rules/no-iterator.js +29 -33
  209. package/lib/rules/no-label-var.js +59 -62
  210. package/lib/rules/no-labels.js +138 -133
  211. package/lib/rules/no-lone-blocks.js +127 -123
  212. package/lib/rules/no-lonely-if.js +108 -77
  213. package/lib/rules/no-loop-func.js +238 -213
  214. package/lib/rules/no-loss-of-precision.js +218 -201
  215. package/lib/rules/no-magic-numbers.js +246 -218
  216. package/lib/rules/no-misleading-character-class.js +499 -446
  217. package/lib/rules/no-mixed-operators.js +188 -182
  218. package/lib/rules/no-mixed-requires.js +253 -240
  219. package/lib/rules/no-mixed-spaces-and-tabs.js +134 -121
  220. package/lib/rules/no-multi-assign.js +46 -44
  221. package/lib/rules/no-multi-spaces.js +163 -143
  222. package/lib/rules/no-multi-str.js +42 -41
  223. package/lib/rules/no-multiple-empty-lines.js +196 -158
  224. package/lib/rules/no-native-reassign.js +90 -85
  225. package/lib/rules/no-negated-condition.js +79 -75
  226. package/lib/rules/no-negated-in-lhs.js +45 -43
  227. package/lib/rules/no-nested-ternary.js +33 -32
  228. package/lib/rules/no-new-func.js +71 -62
  229. package/lib/rules/no-new-native-nonconstructor.js +43 -39
  230. package/lib/rules/no-new-object.js +48 -48
  231. package/lib/rules/no-new-require.js +48 -47
  232. package/lib/rules/no-new-symbol.js +52 -50
  233. package/lib/rules/no-new-wrappers.js +43 -41
  234. package/lib/rules/no-new.js +28 -29
  235. package/lib/rules/no-nonoctal-decimal-escape.js +141 -121
  236. package/lib/rules/no-obj-calls.js +66 -53
  237. package/lib/rules/no-object-constructor.js +104 -97
  238. package/lib/rules/no-octal-escape.js +40 -43
  239. package/lib/rules/no-octal.js +32 -32
  240. package/lib/rules/no-param-reassign.js +235 -217
  241. package/lib/rules/no-path-concat.js +66 -67
  242. package/lib/rules/no-plusplus.js +60 -61
  243. package/lib/rules/no-process-env.js +49 -48
  244. package/lib/rules/no-process-exit.js +54 -50
  245. package/lib/rules/no-promise-executor-return.js +214 -182
  246. package/lib/rules/no-proto.js +26 -29
  247. package/lib/rules/no-prototype-builtins.js +146 -124
  248. package/lib/rules/no-redeclare.js +154 -152
  249. package/lib/rules/no-regex-spaces.js +183 -161
  250. package/lib/rules/no-restricted-exports.js +208 -185
  251. package/lib/rules/no-restricted-globals.js +111 -112
  252. package/lib/rules/no-restricted-imports.js +657 -537
  253. package/lib/rules/no-restricted-modules.js +222 -202
  254. package/lib/rules/no-restricted-properties.js +181 -153
  255. package/lib/rules/no-restricted-syntax.js +56 -52
  256. package/lib/rules/no-return-assign.js +55 -50
  257. package/lib/rules/no-return-await.js +148 -124
  258. package/lib/rules/no-script-url.js +52 -45
  259. package/lib/rules/no-self-assign.js +148 -146
  260. package/lib/rules/no-self-compare.js +63 -46
  261. package/lib/rules/no-sequences.js +135 -116
  262. package/lib/rules/no-setter-return.js +185 -152
  263. package/lib/rules/no-shadow-restricted-names.js +61 -46
  264. package/lib/rules/no-shadow.js +342 -316
  265. package/lib/rules/no-spaced-func.js +82 -77
  266. package/lib/rules/no-sparse-arrays.js +54 -59
  267. package/lib/rules/no-sync.js +61 -60
  268. package/lib/rules/no-tabs.js +83 -72
  269. package/lib/rules/no-template-curly-in-string.js +33 -32
  270. package/lib/rules/no-ternary.js +25 -29
  271. package/lib/rules/no-this-before-super.js +321 -319
  272. package/lib/rules/no-throw-literal.js +31 -36
  273. package/lib/rules/no-trailing-spaces.js +199 -191
  274. package/lib/rules/no-undef-init.js +76 -61
  275. package/lib/rules/no-undef.js +51 -48
  276. package/lib/rules/no-undefined.js +73 -75
  277. package/lib/rules/no-underscore-dangle.js +370 -327
  278. package/lib/rules/no-unexpected-multiline.js +112 -102
  279. package/lib/rules/no-unmodified-loop-condition.js +254 -254
  280. package/lib/rules/no-unneeded-ternary.js +212 -147
  281. package/lib/rules/no-unreachable-loop.js +145 -142
  282. package/lib/rules/no-unreachable.js +255 -248
  283. package/lib/rules/no-unsafe-finally.js +93 -85
  284. package/lib/rules/no-unsafe-negation.js +105 -83
  285. package/lib/rules/no-unsafe-optional-chaining.js +192 -178
  286. package/lib/rules/no-unused-expressions.js +178 -162
  287. package/lib/rules/no-unused-labels.js +139 -124
  288. package/lib/rules/no-unused-private-class-members.js +206 -182
  289. package/lib/rules/no-unused-vars.js +1669 -1449
  290. package/lib/rules/no-use-before-define.js +229 -231
  291. package/lib/rules/no-useless-assignment.js +590 -511
  292. package/lib/rules/no-useless-backreference.js +212 -193
  293. package/lib/rules/no-useless-call.js +58 -53
  294. package/lib/rules/no-useless-catch.js +40 -40
  295. package/lib/rules/no-useless-computed-key.js +144 -115
  296. package/lib/rules/no-useless-concat.js +65 -60
  297. package/lib/rules/no-useless-constructor.js +158 -111
  298. package/lib/rules/no-useless-escape.js +342 -291
  299. package/lib/rules/no-useless-rename.js +183 -156
  300. package/lib/rules/no-useless-return.js +344 -312
  301. package/lib/rules/no-var.js +233 -212
  302. package/lib/rules/no-void.js +50 -48
  303. package/lib/rules/no-warning-comments.js +191 -186
  304. package/lib/rules/no-whitespace-before-property.js +131 -115
  305. package/lib/rules/no-with.js +24 -26
  306. package/lib/rules/nonblock-statement-body-position.js +149 -130
  307. package/lib/rules/object-curly-newline.js +306 -265
  308. package/lib/rules/object-curly-spacing.js +360 -314
  309. package/lib/rules/object-property-newline.js +137 -106
  310. package/lib/rules/object-shorthand.js +607 -502
  311. package/lib/rules/one-var-declaration-per-line.js +104 -100
  312. package/lib/rules/one-var.js +653 -537
  313. package/lib/rules/operator-assignment.js +219 -161
  314. package/lib/rules/operator-linebreak.js +295 -251
  315. package/lib/rules/padded-blocks.js +346 -308
  316. package/lib/rules/padding-line-between-statements.js +443 -439
  317. package/lib/rules/prefer-arrow-callback.js +362 -313
  318. package/lib/rules/prefer-const.js +418 -377
  319. package/lib/rules/prefer-destructuring.js +301 -279
  320. package/lib/rules/prefer-exponentiation-operator.js +176 -133
  321. package/lib/rules/prefer-named-capture-group.js +153 -140
  322. package/lib/rules/prefer-numeric-literals.js +121 -113
  323. package/lib/rules/prefer-object-has-own.js +116 -82
  324. package/lib/rules/prefer-object-spread.js +213 -193
  325. package/lib/rules/prefer-promise-reject-errors.js +140 -122
  326. package/lib/rules/prefer-reflect.js +127 -107
  327. package/lib/rules/prefer-regex-literals.js +578 -466
  328. package/lib/rules/prefer-rest-params.js +79 -80
  329. package/lib/rules/prefer-spread.js +47 -44
  330. package/lib/rules/prefer-template.js +266 -195
  331. package/lib/rules/quote-props.js +373 -307
  332. package/lib/rules/quotes.js +374 -326
  333. package/lib/rules/radix.js +152 -136
  334. package/lib/rules/require-atomic-updates.js +316 -285
  335. package/lib/rules/require-await.js +144 -116
  336. package/lib/rules/require-unicode-regexp.js +282 -177
  337. package/lib/rules/require-yield.js +53 -54
  338. package/lib/rules/rest-spread-spacing.js +128 -116
  339. package/lib/rules/semi-spacing.js +281 -250
  340. package/lib/rules/semi-style.js +176 -134
  341. package/lib/rules/semi.js +456 -436
  342. package/lib/rules/sort-imports.js +306 -233
  343. package/lib/rules/sort-keys.js +219 -188
  344. package/lib/rules/sort-vars.js +127 -93
  345. package/lib/rules/space-before-blocks.js +199 -189
  346. package/lib/rules/space-before-function-paren.js +186 -166
  347. package/lib/rules/space-in-parens.js +359 -288
  348. package/lib/rules/space-infix-ops.js +237 -201
  349. package/lib/rules/space-unary-ops.js +356 -298
  350. package/lib/rules/spaced-comment.js +363 -319
  351. package/lib/rules/strict.js +265 -230
  352. package/lib/rules/switch-colon-spacing.js +130 -122
  353. package/lib/rules/symbol-description.js +45 -48
  354. package/lib/rules/template-curly-spacing.js +148 -142
  355. package/lib/rules/template-tag-spacing.js +98 -88
  356. package/lib/rules/unicode-bom.js +54 -56
  357. package/lib/rules/use-isnan.js +237 -206
  358. package/lib/rules/utils/ast-utils.js +2039 -1860
  359. package/lib/rules/utils/char-source.js +162 -155
  360. package/lib/rules/utils/fix-tracker.js +83 -80
  361. package/lib/rules/utils/keywords.js +59 -59
  362. package/lib/rules/utils/lazy-loading-rule-map.js +79 -76
  363. package/lib/rules/utils/regular-expressions.js +32 -24
  364. package/lib/rules/utils/unicode/index.js +4 -4
  365. package/lib/rules/utils/unicode/is-combining-character.js +1 -1
  366. package/lib/rules/utils/unicode/is-emoji-modifier.js +1 -1
  367. package/lib/rules/utils/unicode/is-regional-indicator-symbol.js +1 -1
  368. package/lib/rules/utils/unicode/is-surrogate-pair.js +1 -1
  369. package/lib/rules/valid-typeof.js +153 -111
  370. package/lib/rules/vars-on-top.js +152 -145
  371. package/lib/rules/wrap-iife.js +204 -191
  372. package/lib/rules/wrap-regex.js +70 -58
  373. package/lib/rules/yield-star-spacing.js +145 -134
  374. package/lib/rules/yoda.js +283 -272
  375. package/lib/services/parser-service.js +35 -35
  376. package/lib/services/processor-service.js +66 -73
  377. package/lib/services/suppressions-service.js +289 -0
  378. package/lib/shared/ajv.js +14 -14
  379. package/lib/shared/assert.js +3 -4
  380. package/lib/shared/ast-utils.js +7 -6
  381. package/lib/shared/deep-merge-arrays.js +24 -22
  382. package/lib/shared/directives.js +3 -2
  383. package/lib/shared/flags.js +50 -17
  384. package/lib/shared/logging.js +24 -25
  385. package/lib/shared/option-utils.js +43 -36
  386. package/lib/shared/runtime-info.js +136 -127
  387. package/lib/shared/serialization.js +27 -27
  388. package/lib/shared/severity.js +22 -22
  389. package/lib/shared/stats.js +5 -5
  390. package/lib/shared/string-utils.js +16 -16
  391. package/lib/shared/text-table.js +28 -27
  392. package/lib/shared/traverser.js +153 -146
  393. package/lib/shared/types.js +4 -27
  394. package/lib/types/index.d.ts +2010 -1559
  395. package/lib/types/rules.d.ts +5253 -5140
  396. package/lib/types/use-at-your-own-risk.d.ts +32 -30
  397. package/lib/unsupported-api.js +5 -5
  398. package/messages/all-files-ignored.js +3 -3
  399. package/messages/all-matched-files-ignored.js +3 -3
  400. package/messages/config-file-missing.js +2 -2
  401. package/messages/config-plugin-missing.js +3 -3
  402. package/messages/config-serialize-function.js +9 -7
  403. package/messages/eslintrc-incompat.js +13 -15
  404. package/messages/eslintrc-plugins.js +3 -4
  405. package/messages/extend-config-missing.js +3 -3
  406. package/messages/failed-to-read-json.js +3 -3
  407. package/messages/file-not-found.js +3 -3
  408. package/messages/invalid-rule-options.js +2 -2
  409. package/messages/invalid-rule-severity.js +2 -2
  410. package/messages/no-config-found.js +3 -3
  411. package/messages/plugin-conflict.js +8 -8
  412. package/messages/plugin-invalid.js +3 -3
  413. package/messages/plugin-missing.js +3 -3
  414. package/messages/print-config-with-directory-path.js +2 -2
  415. package/messages/shared.js +6 -1
  416. package/messages/whitespace-found.js +3 -3
  417. package/package.json +14 -20
@@ -17,26 +17,25 @@ const { Linter } = require("../linter");
17
17
  const { getRuleFromConfig } = require("../config/flat-config-helpers");
18
18
  const { defaultConfig } = require("../config/default-config");
19
19
  const {
20
- Legacy: {
21
- ConfigOps: {
22
- getRuleSeverity
23
- },
24
- ModuleResolver,
25
- naming
26
- }
20
+ Legacy: {
21
+ ConfigOps: { getRuleSeverity },
22
+ ModuleResolver,
23
+ naming,
24
+ },
27
25
  } = require("@eslint/eslintrc");
28
26
 
29
27
  const {
30
- findFiles,
31
- getCacheFile,
28
+ findFiles,
29
+ getCacheFile,
32
30
 
33
- isNonEmptyString,
34
- isArrayOfNonEmptyString,
31
+ isNonEmptyString,
32
+ isArrayOfNonEmptyString,
35
33
 
36
- createIgnoreResult,
37
- isErrorMessage,
34
+ createIgnoreResult,
35
+ isErrorMessage,
36
+ calculateStatsPerFile,
38
37
 
39
- processOptions
38
+ processOptions,
40
39
  } = require("./eslint-helpers");
41
40
  const { pathToFileURL } = require("node:url");
42
41
  const LintResultCache = require("../cli-engine/lint-result-cache");
@@ -53,19 +52,15 @@ const { ConfigLoader, LegacyConfigLoader } = require("../config/config-loader");
53
52
  //------------------------------------------------------------------------------
54
53
 
55
54
  // For VSCode IntelliSense
56
- /** @typedef {import("../cli-engine/cli-engine").ConfigArray} ConfigArray */
57
- /** @typedef {import("../shared/types").ConfigData} ConfigData */
58
- /** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */
59
- /** @typedef {import("../shared/types").LintMessage} LintMessage */
60
- /** @typedef {import("../shared/types").LintResult} LintResult */
61
- /** @typedef {import("../shared/types").ParserOptions} ParserOptions */
62
- /** @typedef {import("../shared/types").Plugin} Plugin */
63
- /** @typedef {import("../shared/types").ResultsMeta} ResultsMeta */
64
- /** @typedef {import("../shared/types").RuleConf} RuleConf */
65
- /** @typedef {import("../shared/types").Rule} Rule */
55
+ /**
56
+ * @import { ConfigArray } from "../cli-engine/cli-engine.js";
57
+ * @import { CLIEngineLintReport } from "./legacy-eslint.js";
58
+ * @import { FlatConfigArray } from "../config/flat-config-array.js";
59
+ * @import { RuleDefinition } from "@eslint/core";
60
+ * @import { ConfigData, DeprecatedRuleInfo, LintMessage, LintResult, Plugin, ResultsMeta } from "../shared/types.js";
61
+ */
62
+
66
63
  /** @typedef {ReturnType<ConfigArray.extractConfig>} ExtractedConfig */
67
- /** @typedef {import('../cli-engine/cli-engine').CLIEngine} CLIEngine */
68
- /** @typedef {import('./legacy-eslint').CLIEngineLintReport} CLIEngineLintReport */
69
64
 
70
65
  /**
71
66
  * The options with which to configure the ESLint instance.
@@ -101,63 +96,27 @@ const { ConfigLoader, LegacyConfigLoader } = require("../config/config-loader");
101
96
  const debug = require("debug")("eslint:eslint");
102
97
  const privateMembers = new WeakMap();
103
98
  const removedFormatters = new Set([
104
- "checkstyle",
105
- "codeframe",
106
- "compact",
107
- "jslint-xml",
108
- "junit",
109
- "table",
110
- "tap",
111
- "unix",
112
- "visualstudio"
99
+ "checkstyle",
100
+ "codeframe",
101
+ "compact",
102
+ "jslint-xml",
103
+ "junit",
104
+ "table",
105
+ "tap",
106
+ "unix",
107
+ "visualstudio",
113
108
  ]);
114
109
 
115
- /**
116
- * It will calculate the error and warning count for collection of messages per file
117
- * @param {LintMessage[]} messages Collection of messages
118
- * @returns {Object} Contains the stats
119
- * @private
120
- */
121
- function calculateStatsPerFile(messages) {
122
- const stat = {
123
- errorCount: 0,
124
- fatalErrorCount: 0,
125
- warningCount: 0,
126
- fixableErrorCount: 0,
127
- fixableWarningCount: 0
128
- };
129
-
130
- for (let i = 0; i < messages.length; i++) {
131
- const message = messages[i];
132
-
133
- if (message.fatal || message.severity === 2) {
134
- stat.errorCount++;
135
- if (message.fatal) {
136
- stat.fatalErrorCount++;
137
- }
138
- if (message.fix) {
139
- stat.fixableErrorCount++;
140
- }
141
- } else {
142
- stat.warningCount++;
143
- if (message.fix) {
144
- stat.fixableWarningCount++;
145
- }
146
- }
147
- }
148
- return stat;
149
- }
150
-
151
110
  /**
152
111
  * Create rulesMeta object.
153
- * @param {Map<string,Rule>} rules a map of rules from which to generate the object.
112
+ * @param {Map<string,RuleDefinition>} rules a map of rules from which to generate the object.
154
113
  * @returns {Object} metadata for all enabled rules.
155
114
  */
156
115
  function createRulesMeta(rules) {
157
- return Array.from(rules).reduce((retVal, [id, rule]) => {
158
- retVal[id] = rule.meta;
159
- return retVal;
160
- }, {});
116
+ return Array.from(rules).reduce((retVal, [id, rule]) => {
117
+ retVal[id] = rule.meta;
118
+ return retVal;
119
+ }, {});
161
120
  }
162
121
 
163
122
  /**
@@ -167,7 +126,7 @@ function createRulesMeta(rules) {
167
126
  * @returns {string} The absolute path of a file named `"__placeholder__.js"` in the given directory.
168
127
  */
169
128
  function getPlaceholderPath(cwd) {
170
- return path.join(cwd, "__placeholder__.js");
129
+ return path.join(cwd, "__placeholder__.js");
171
130
  }
172
131
 
173
132
  /** @type {WeakMap<ExtractedConfig, DeprecatedRuleInfo[]>} */
@@ -175,73 +134,77 @@ const usedDeprecatedRulesCache = new WeakMap();
175
134
 
176
135
  /**
177
136
  * Create used deprecated rule list.
178
- * @param {CLIEngine} eslint The CLIEngine instance.
137
+ * @param {ESLint} eslint The ESLint instance.
179
138
  * @param {string} maybeFilePath The absolute path to a lint target file or `"<text>"`.
180
139
  * @returns {DeprecatedRuleInfo[]} The used deprecated rule list.
181
140
  */
182
141
  function getOrFindUsedDeprecatedRules(eslint, maybeFilePath) {
183
- const {
184
- options: { cwd },
185
- configLoader
186
- } = privateMembers.get(eslint);
187
- const filePath = path.isAbsolute(maybeFilePath)
188
- ? maybeFilePath
189
- : getPlaceholderPath(cwd);
190
- const configs = configLoader.getCachedConfigArrayForFile(filePath);
191
- const config = configs.getConfig(filePath);
192
-
193
- // Most files use the same config, so cache it.
194
- if (config && !usedDeprecatedRulesCache.has(config)) {
195
- const retv = [];
196
-
197
- if (config.rules) {
198
- for (const [ruleId, ruleConf] of Object.entries(config.rules)) {
199
- if (getRuleSeverity(ruleConf) === 0) {
200
- continue;
201
- }
202
- const rule = getRuleFromConfig(ruleId, config);
203
- const meta = rule && rule.meta;
204
-
205
- if (meta && meta.deprecated) {
206
- const usesNewFormat = typeof meta.deprecated === "object";
207
-
208
- retv.push({
209
- ruleId,
210
- replacedBy: usesNewFormat ? meta.deprecated.replacedBy?.map(replacement => `${replacement.plugin?.name !== void 0 ? `${naming.getShorthandName(replacement.plugin.name, "eslint-plugin")}/` : ""}${replacement.rule?.name ?? ""}`) ?? [] : meta.replacedBy || [],
211
- info: usesNewFormat ? meta.deprecated : void 0
212
- });
213
- }
214
- }
215
- }
216
-
217
-
218
- usedDeprecatedRulesCache.set(config, Object.freeze(retv));
219
- }
220
-
221
- return config ? usedDeprecatedRulesCache.get(config) : Object.freeze([]);
142
+ const {
143
+ options: { cwd },
144
+ configLoader,
145
+ } = privateMembers.get(eslint);
146
+ const filePath = path.isAbsolute(maybeFilePath)
147
+ ? maybeFilePath
148
+ : getPlaceholderPath(cwd);
149
+ const configs = configLoader.getCachedConfigArrayForFile(filePath);
150
+ const config = configs.getConfig(filePath);
151
+
152
+ // Most files use the same config, so cache it.
153
+ if (config && !usedDeprecatedRulesCache.has(config)) {
154
+ const retv = [];
155
+
156
+ if (config.rules) {
157
+ for (const [ruleId, ruleConf] of Object.entries(config.rules)) {
158
+ if (getRuleSeverity(ruleConf) === 0) {
159
+ continue;
160
+ }
161
+ const rule = getRuleFromConfig(ruleId, config);
162
+ const meta = rule && rule.meta;
163
+
164
+ if (meta && meta.deprecated) {
165
+ const usesNewFormat = typeof meta.deprecated === "object";
166
+
167
+ retv.push({
168
+ ruleId,
169
+ replacedBy: usesNewFormat
170
+ ? (meta.deprecated.replacedBy?.map(
171
+ replacement =>
172
+ `${replacement.plugin?.name !== void 0 ? `${naming.getShorthandName(replacement.plugin.name, "eslint-plugin")}/` : ""}${replacement.rule?.name ?? ""}`,
173
+ ) ?? [])
174
+ : meta.replacedBy || [],
175
+ info: usesNewFormat ? meta.deprecated : void 0,
176
+ });
177
+ }
178
+ }
179
+ }
180
+
181
+ usedDeprecatedRulesCache.set(config, Object.freeze(retv));
182
+ }
183
+
184
+ return config ? usedDeprecatedRulesCache.get(config) : Object.freeze([]);
222
185
  }
223
186
 
224
187
  /**
225
188
  * Processes the linting results generated by a CLIEngine linting report to
226
189
  * match the ESLint class's API.
227
- * @param {CLIEngine} eslint The CLIEngine instance.
190
+ * @param {ESLint} eslint The ESLint instance.
228
191
  * @param {CLIEngineLintReport} report The CLIEngine linting report to process.
229
192
  * @returns {LintResult[]} The processed linting results.
230
193
  */
231
194
  function processLintReport(eslint, { results }) {
232
- const descriptor = {
233
- configurable: true,
234
- enumerable: true,
235
- get() {
236
- return getOrFindUsedDeprecatedRules(eslint, this.filePath);
237
- }
238
- };
239
-
240
- for (const result of results) {
241
- Object.defineProperty(result, "usedDeprecatedRules", descriptor);
242
- }
243
-
244
- return results;
195
+ const descriptor = {
196
+ configurable: true,
197
+ enumerable: true,
198
+ get() {
199
+ return getOrFindUsedDeprecatedRules(eslint, this.filePath);
200
+ },
201
+ };
202
+
203
+ for (const result of results) {
204
+ Object.defineProperty(result, "usedDeprecatedRules", descriptor);
205
+ }
206
+
207
+ return results;
245
208
  }
246
209
 
247
210
  /**
@@ -251,18 +214,17 @@ function processLintReport(eslint, { results }) {
251
214
  * @returns {number} An integer representing the order in which the two results should occur.
252
215
  */
253
216
  function compareResultsByFilePath(a, b) {
254
- if (a.filePath < b.filePath) {
255
- return -1;
256
- }
217
+ if (a.filePath < b.filePath) {
218
+ return -1;
219
+ }
257
220
 
258
- if (a.filePath > b.filePath) {
259
- return 1;
260
- }
221
+ if (a.filePath > b.filePath) {
222
+ return 1;
223
+ }
261
224
 
262
- return 0;
225
+ return 0;
263
226
  }
264
227
 
265
-
266
228
  /**
267
229
  * Determines which config file to use. This is determined by seeing if an
268
230
  * override config file was passed, and if so, using it; otherwise, as long
@@ -276,22 +238,23 @@ function compareResultsByFilePath(a, b) {
276
238
  * the config file.
277
239
  */
278
240
  async function locateConfigFileToUse({ configFile, cwd }) {
279
-
280
- const configLoader = new ConfigLoader({
281
- cwd,
282
- configFile
283
- });
284
-
285
- const configFilePath = await configLoader.findConfigFileForPath(path.join(cwd, "__placeholder__.js"));
286
-
287
- if (!configFilePath) {
288
- throw new Error("No ESLint configuration file was found.");
289
- }
290
-
291
- return {
292
- configFilePath,
293
- basePath: configFile ? cwd : path.dirname(configFilePath)
294
- };
241
+ const configLoader = new ConfigLoader({
242
+ cwd,
243
+ configFile,
244
+ });
245
+
246
+ const configFilePath = await configLoader.findConfigFileForPath(
247
+ path.join(cwd, "__placeholder__.js"),
248
+ );
249
+
250
+ if (!configFilePath) {
251
+ throw new Error("No ESLint configuration file was found.");
252
+ }
253
+
254
+ return {
255
+ configFilePath,
256
+ basePath: configFile ? cwd : path.dirname(configFilePath),
257
+ };
295
258
  }
296
259
 
297
260
  /**
@@ -310,91 +273,88 @@ async function locateConfigFileToUse({ configFile, cwd }) {
310
273
  * @private
311
274
  */
312
275
  function verifyText({
313
- text,
314
- cwd,
315
- filePath: providedFilePath,
316
- configs,
317
- fix,
318
- allowInlineConfig,
319
- ruleFilter,
320
- stats,
321
- linter
276
+ text,
277
+ cwd,
278
+ filePath: providedFilePath,
279
+ configs,
280
+ fix,
281
+ allowInlineConfig,
282
+ ruleFilter,
283
+ stats,
284
+ linter,
322
285
  }) {
323
- const filePath = providedFilePath || "<text>";
324
-
325
- debug(`Lint ${filePath}`);
326
-
327
- /*
328
- * Verify.
329
- * `config.extractConfig(filePath)` requires an absolute path, but `linter`
330
- * doesn't know CWD, so it gives `linter` an absolute path always.
331
- */
332
- const filePathToVerify = filePath === "<text>" ? getPlaceholderPath(cwd) : filePath;
333
- const { fixed, messages, output } = linter.verifyAndFix(
334
- text,
335
- configs,
336
- {
337
- allowInlineConfig,
338
- filename: filePathToVerify,
339
- fix,
340
- ruleFilter,
341
- stats,
342
-
343
- /**
344
- * Check if the linter should adopt a given code block or not.
345
- * @param {string} blockFilename The virtual filename of a code block.
346
- * @returns {boolean} `true` if the linter should adopt the code block.
347
- */
348
- filterCodeBlock(blockFilename) {
349
- return configs.getConfig(blockFilename) !== void 0;
350
- }
351
- }
352
- );
353
-
354
- // Tweak and return.
355
- const result = {
356
- filePath: filePath === "<text>" ? filePath : path.resolve(filePath),
357
- messages,
358
- suppressedMessages: linter.getSuppressedMessages(),
359
- ...calculateStatsPerFile(messages)
360
- };
361
-
362
- if (fixed) {
363
- result.output = output;
364
- }
365
-
366
- if (
367
- result.errorCount + result.warningCount > 0 &&
368
- typeof result.output === "undefined"
369
- ) {
370
- result.source = text;
371
- }
372
-
373
- if (stats) {
374
- result.stats = {
375
- times: linter.getTimes(),
376
- fixPasses: linter.getFixPassCount()
377
- };
378
- }
379
-
380
- return result;
286
+ const filePath = providedFilePath || "<text>";
287
+
288
+ debug(`Lint ${filePath}`);
289
+
290
+ /*
291
+ * Verify.
292
+ * `config.extractConfig(filePath)` requires an absolute path, but `linter`
293
+ * doesn't know CWD, so it gives `linter` an absolute path always.
294
+ */
295
+ const filePathToVerify =
296
+ filePath === "<text>" ? getPlaceholderPath(cwd) : filePath;
297
+ const { fixed, messages, output } = linter.verifyAndFix(text, configs, {
298
+ allowInlineConfig,
299
+ filename: filePathToVerify,
300
+ fix,
301
+ ruleFilter,
302
+ stats,
303
+
304
+ /**
305
+ * Check if the linter should adopt a given code block or not.
306
+ * @param {string} blockFilename The virtual filename of a code block.
307
+ * @returns {boolean} `true` if the linter should adopt the code block.
308
+ */
309
+ filterCodeBlock(blockFilename) {
310
+ return configs.getConfig(blockFilename) !== void 0;
311
+ },
312
+ });
313
+
314
+ // Tweak and return.
315
+ const result = {
316
+ filePath: filePath === "<text>" ? filePath : path.resolve(filePath),
317
+ messages,
318
+ suppressedMessages: linter.getSuppressedMessages(),
319
+ ...calculateStatsPerFile(messages),
320
+ };
321
+
322
+ if (fixed) {
323
+ result.output = output;
324
+ }
325
+
326
+ if (
327
+ result.errorCount + result.warningCount > 0 &&
328
+ typeof result.output === "undefined"
329
+ ) {
330
+ result.source = text;
331
+ }
332
+
333
+ if (stats) {
334
+ result.stats = {
335
+ times: linter.getTimes(),
336
+ fixPasses: linter.getFixPassCount(),
337
+ };
338
+ }
339
+
340
+ return result;
381
341
  }
382
342
 
383
343
  /**
384
344
  * Checks whether a message's rule type should be fixed.
385
345
  * @param {LintMessage} message The message to check.
386
- * @param {FlatConfig} config The config for the file that generated the message.
346
+ * @param {FlatConfigArray} config The config for the file that generated the message.
387
347
  * @param {string[]} fixTypes An array of fix types to check.
388
348
  * @returns {boolean} Whether the message should be fixed.
389
349
  */
390
350
  function shouldMessageBeFixed(message, config, fixTypes) {
391
- if (!message.ruleId) {
392
- return fixTypes.has("directive");
393
- }
351
+ if (!message.ruleId) {
352
+ return fixTypes.has("directive");
353
+ }
394
354
 
395
- const rule = message.ruleId && getRuleFromConfig(message.ruleId, config);
355
+ const rule = message.ruleId && getRuleFromConfig(message.ruleId, config);
396
356
 
397
- return Boolean(rule && rule.meta && fixTypes.has(rule.meta.type));
357
+ return Boolean(rule && rule.meta && fixTypes.has(rule.meta.type));
398
358
  }
399
359
 
400
360
  /**
@@ -402,24 +362,28 @@ function shouldMessageBeFixed(message, config, fixTypes) {
402
362
  * @returns {TypeError} An error object.
403
363
  */
404
364
  function createExtraneousResultsError() {
405
- return new TypeError("Results object was not created from this ESLint instance.");
365
+ return new TypeError(
366
+ "Results object was not created from this ESLint instance.",
367
+ );
406
368
  }
407
369
 
408
370
  /**
409
371
  * Creates a fixer function based on the provided fix, fixTypesSet, and config.
410
372
  * @param {Function|boolean} fix The original fix option.
411
373
  * @param {Set<string>} fixTypesSet A set of fix types to filter messages for fixing.
412
- * @param {FlatConfig} config The config for the file that generated the message.
374
+ * @param {FlatConfigArray} config The config for the file that generated the message.
413
375
  * @returns {Function|boolean} The fixer function or the original fix value.
414
376
  */
415
377
  function getFixerForFixTypes(fix, fixTypesSet, config) {
416
- if (!fix || !fixTypesSet) {
417
- return fix;
418
- }
378
+ if (!fix || !fixTypesSet) {
379
+ return fix;
380
+ }
419
381
 
420
- const originalFix = (typeof fix === "function") ? fix : () => true;
382
+ const originalFix = typeof fix === "function" ? fix : () => true;
421
383
 
422
- return message => shouldMessageBeFixed(message, config, fixTypesSet) && originalFix(message);
384
+ return message =>
385
+ shouldMessageBeFixed(message, config, fixTypesSet) &&
386
+ originalFix(message);
423
387
  }
424
388
 
425
389
  //-----------------------------------------------------------------------------
@@ -430,679 +394,737 @@ function getFixerForFixTypes(fix, fixTypesSet, config) {
430
394
  * Primary Node.js API for ESLint.
431
395
  */
432
396
  class ESLint {
433
-
434
- /**
435
- * The type of configuration used by this class.
436
- * @type {string}
437
- */
438
- static configType = "flat";
439
-
440
- /**
441
- * The loader to use for finding config files.
442
- * @type {ConfigLoader|LegacyConfigLoader}
443
- */
444
- #configLoader;
445
-
446
- /**
447
- * Creates a new instance of the main ESLint API.
448
- * @param {ESLintOptions} options The options for this instance.
449
- */
450
- constructor(options = {}) {
451
-
452
- const defaultConfigs = [];
453
- const processedOptions = processOptions(options);
454
- const linter = new Linter({
455
- cwd: processedOptions.cwd,
456
- configType: "flat",
457
- flags: processedOptions.flags
458
- });
459
-
460
- const cacheFilePath = getCacheFile(
461
- processedOptions.cacheLocation,
462
- processedOptions.cwd
463
- );
464
-
465
- const lintResultCache = processedOptions.cache
466
- ? new LintResultCache(cacheFilePath, processedOptions.cacheStrategy)
467
- : null;
468
-
469
- const configLoaderOptions = {
470
- cwd: processedOptions.cwd,
471
- baseConfig: processedOptions.baseConfig,
472
- overrideConfig: processedOptions.overrideConfig,
473
- configFile: processedOptions.configFile,
474
- ignoreEnabled: processedOptions.ignore,
475
- ignorePatterns: processedOptions.ignorePatterns,
476
- defaultConfigs
477
- };
478
-
479
- this.#configLoader = linter.hasFlag("unstable_config_lookup_from_file")
480
- ? new ConfigLoader(configLoaderOptions)
481
- : new LegacyConfigLoader(configLoaderOptions);
482
-
483
- debug(`Using config loader ${this.#configLoader.constructor.name}`);
484
-
485
- privateMembers.set(this, {
486
- options: processedOptions,
487
- linter,
488
- cacheFilePath,
489
- lintResultCache,
490
- defaultConfigs,
491
- configs: null,
492
- configLoader: this.#configLoader
493
- });
494
-
495
-
496
- /**
497
- * If additional plugins are passed in, add that to the default
498
- * configs for this instance.
499
- */
500
- if (options.plugins) {
501
-
502
- const plugins = {};
503
-
504
- for (const [pluginName, plugin] of Object.entries(options.plugins)) {
505
- plugins[naming.getShorthandName(pluginName, "eslint-plugin")] = plugin;
506
- }
507
-
508
- defaultConfigs.push({
509
- plugins
510
- });
511
- }
512
-
513
- // Check for the .eslintignore file, and warn if it's present.
514
- if (existsSync(path.resolve(processedOptions.cwd, ".eslintignore"))) {
515
- process.emitWarning(
516
- "The \".eslintignore\" file is no longer supported. Switch to using the \"ignores\" property in \"eslint.config.js\": https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files",
517
- "ESLintIgnoreWarning"
518
- );
519
- }
520
- }
521
-
522
- /**
523
- * The version text.
524
- * @type {string}
525
- */
526
- static get version() {
527
- return version;
528
- }
529
-
530
- /**
531
- * The default configuration that ESLint uses internally. This is provided for tooling that wants to calculate configurations using the same defaults as ESLint.
532
- * Keep in mind that the default configuration may change from version to version, so you shouldn't rely on any particular keys or values to be present.
533
- * @type {ConfigArray}
534
- */
535
- static get defaultConfig() {
536
- return defaultConfig;
537
- }
538
-
539
- /**
540
- * Outputs fixes from the given results to files.
541
- * @param {LintResult[]} results The lint results.
542
- * @returns {Promise<void>} Returns a promise that is used to track side effects.
543
- */
544
- static async outputFixes(results) {
545
- if (!Array.isArray(results)) {
546
- throw new Error("'results' must be an array");
547
- }
548
-
549
- await Promise.all(
550
- results
551
- .filter(result => {
552
- if (typeof result !== "object" || result === null) {
553
- throw new Error("'results' must include only objects");
554
- }
555
- return (
556
- typeof result.output === "string" &&
557
- path.isAbsolute(result.filePath)
558
- );
559
- })
560
- .map(r => fs.writeFile(r.filePath, r.output))
561
- );
562
- }
563
-
564
- /**
565
- * Returns results that only contains errors.
566
- * @param {LintResult[]} results The results to filter.
567
- * @returns {LintResult[]} The filtered results.
568
- */
569
- static getErrorResults(results) {
570
- const filtered = [];
571
-
572
- results.forEach(result => {
573
- const filteredMessages = result.messages.filter(isErrorMessage);
574
- const filteredSuppressedMessages = result.suppressedMessages.filter(isErrorMessage);
575
-
576
- if (filteredMessages.length > 0) {
577
- filtered.push({
578
- ...result,
579
- messages: filteredMessages,
580
- suppressedMessages: filteredSuppressedMessages,
581
- errorCount: filteredMessages.length,
582
- warningCount: 0,
583
- fixableErrorCount: result.fixableErrorCount,
584
- fixableWarningCount: 0
585
- });
586
- }
587
- });
588
-
589
- return filtered;
590
- }
591
-
592
- /**
593
- * Returns meta objects for each rule represented in the lint results.
594
- * @param {LintResult[]} results The results to fetch rules meta for.
595
- * @returns {Object} A mapping of ruleIds to rule meta objects.
596
- * @throws {TypeError} When the results object wasn't created from this ESLint instance.
597
- * @throws {TypeError} When a plugin or rule is missing.
598
- */
599
- getRulesMetaForResults(results) {
600
-
601
- // short-circuit simple case
602
- if (results.length === 0) {
603
- return {};
604
- }
605
-
606
- const resultRules = new Map();
607
- const {
608
- configLoader,
609
- options: { cwd }
610
- } = privateMembers.get(this);
611
-
612
- for (const result of results) {
613
-
614
- /*
615
- * Normalize filename for <text>.
616
- */
617
- const filePath = result.filePath === "<text>"
618
- ? getPlaceholderPath(cwd) : result.filePath;
619
- const allMessages = result.messages.concat(result.suppressedMessages);
620
-
621
- for (const { ruleId } of allMessages) {
622
- if (!ruleId) {
623
- continue;
624
- }
625
-
626
- /*
627
- * All of the plugin and rule information is contained within the
628
- * calculated config for the given file.
629
- */
630
- let configs;
631
-
632
- try {
633
- configs = configLoader.getCachedConfigArrayForFile(filePath);
634
- } catch {
635
- throw createExtraneousResultsError();
636
- }
637
-
638
- const config = configs.getConfig(filePath);
639
-
640
- if (!config) {
641
- throw createExtraneousResultsError();
642
- }
643
- const rule = getRuleFromConfig(ruleId, config);
644
-
645
- // ignore unknown rules
646
- if (rule) {
647
- resultRules.set(ruleId, rule);
648
- }
649
- }
650
- }
651
-
652
- return createRulesMeta(resultRules);
653
- }
654
-
655
- /**
656
- * Indicates if the given feature flag is enabled for this instance.
657
- * @param {string} flag The feature flag to check.
658
- * @returns {boolean} `true` if the feature flag is enabled, `false` if not.
659
- */
660
- hasFlag(flag) {
661
-
662
- // note: Linter does validation of the flags
663
- return privateMembers.get(this).linter.hasFlag(flag);
664
- }
665
-
666
- /**
667
- * Executes the current configuration on an array of file and directory names.
668
- * @param {string|string[]} patterns An array of file and directory names.
669
- * @returns {Promise<LintResult[]>} The results of linting the file patterns given.
670
- */
671
- async lintFiles(patterns) {
672
-
673
- let normalizedPatterns = patterns;
674
- const {
675
- cacheFilePath,
676
- lintResultCache,
677
- linter,
678
- options: eslintOptions
679
- } = privateMembers.get(this);
680
-
681
- /*
682
- * Special cases:
683
- * 1. `patterns` is an empty string
684
- * 2. `patterns` is an empty array
685
- *
686
- * In both cases, we use the cwd as the directory to lint.
687
- */
688
- if (patterns === "" || Array.isArray(patterns) && patterns.length === 0) {
689
-
690
- /*
691
- * Special case: If `passOnNoPatterns` is true, then we just exit
692
- * without doing any work.
693
- */
694
- if (eslintOptions.passOnNoPatterns) {
695
- return [];
696
- }
697
-
698
- normalizedPatterns = ["."];
699
- } else {
700
-
701
- if (!isNonEmptyString(patterns) && !isArrayOfNonEmptyString(patterns)) {
702
- throw new Error("'patterns' must be a non-empty string or an array of non-empty strings");
703
- }
704
-
705
- if (typeof patterns === "string") {
706
- normalizedPatterns = [patterns];
707
- }
708
- }
709
-
710
- debug(`Using file patterns: ${normalizedPatterns}`);
711
-
712
- const {
713
- allowInlineConfig,
714
- cache,
715
- cwd,
716
- fix,
717
- fixTypes,
718
- ruleFilter,
719
- stats,
720
- globInputPaths,
721
- errorOnUnmatchedPattern,
722
- warnIgnored
723
- } = eslintOptions;
724
- const startTime = Date.now();
725
- const fixTypesSet = fixTypes ? new Set(fixTypes) : null;
726
-
727
- // Delete cache file; should this be done here?
728
- if (!cache && cacheFilePath) {
729
- debug(`Deleting cache file at ${cacheFilePath}`);
730
-
731
- try {
732
- await fs.unlink(cacheFilePath);
733
- } catch (error) {
734
- const errorCode = error && error.code;
735
-
736
- // Ignore errors when no such file exists or file system is read only (and cache file does not exist)
737
- if (errorCode !== "ENOENT" && !(errorCode === "EROFS" && !existsSync(cacheFilePath))) {
738
- throw error;
739
- }
740
- }
741
- }
742
-
743
- const filePaths = await findFiles({
744
- patterns: normalizedPatterns,
745
- cwd,
746
- globInputPaths,
747
- configLoader: this.#configLoader,
748
- errorOnUnmatchedPattern
749
- });
750
- const controller = new AbortController();
751
- const retryCodes = new Set(["ENFILE", "EMFILE"]);
752
- const retrier = new Retrier(error => retryCodes.has(error.code), { concurrency: 100 });
753
-
754
- debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`);
755
-
756
- /*
757
- * Because we need to process multiple files, including reading from disk,
758
- * it is most efficient to start by reading each file via promises so that
759
- * they can be done in parallel. Then, we can lint the returned text. This
760
- * ensures we are waiting the minimum amount of time in between lints.
761
- */
762
- const results = await Promise.all(
763
-
764
- filePaths.map(async filePath => {
765
-
766
- const configs = await this.#configLoader.loadConfigArrayForFile(filePath);
767
- const config = configs.getConfig(filePath);
768
-
769
- /*
770
- * If a filename was entered that cannot be matched
771
- * to a config, then notify the user.
772
- */
773
- if (!config) {
774
- if (warnIgnored) {
775
- const configStatus = configs.getConfigStatus(filePath);
776
-
777
- return createIgnoreResult(filePath, cwd, configStatus);
778
- }
779
-
780
- return void 0;
781
- }
782
-
783
- // Skip if there is cached result.
784
- if (lintResultCache) {
785
- const cachedResult =
786
- lintResultCache.getCachedLintResults(filePath, config);
787
-
788
- if (cachedResult) {
789
- const hadMessages =
790
- cachedResult.messages &&
791
- cachedResult.messages.length > 0;
792
-
793
- if (hadMessages && fix) {
794
- debug(`Reprocessing cached file to allow autofix: ${filePath}`);
795
- } else {
796
- debug(`Skipping file since it hasn't changed: ${filePath}`);
797
- return cachedResult;
798
- }
799
- }
800
- }
801
-
802
-
803
- // set up fixer for fixTypes if necessary
804
- const fixer = getFixerForFixTypes(fix, fixTypesSet, config);
805
-
806
- return retrier.retry(() => fs.readFile(filePath, { encoding: "utf8", signal: controller.signal })
807
- .then(text => {
808
-
809
- // fail immediately if an error occurred in another file
810
- controller.signal.throwIfAborted();
811
-
812
- // do the linting
813
- const result = verifyText({
814
- text,
815
- filePath,
816
- configs,
817
- cwd,
818
- fix: fixer,
819
- allowInlineConfig,
820
- ruleFilter,
821
- stats,
822
- linter
823
- });
824
-
825
- /*
826
- * Store the lint result in the LintResultCache.
827
- * NOTE: The LintResultCache will remove the file source and any
828
- * other properties that are difficult to serialize, and will
829
- * hydrate those properties back in on future lint runs.
830
- */
831
- if (lintResultCache) {
832
- lintResultCache.setCachedLintResults(filePath, config, result);
833
- }
834
-
835
- return result;
836
- }), { signal: controller.signal })
837
- .catch(error => {
838
- controller.abort(error);
839
- throw error;
840
- });
841
- })
842
- );
843
-
844
- // Persist the cache to disk.
845
- if (lintResultCache) {
846
- lintResultCache.reconcile();
847
- }
848
-
849
- const finalResults = results.filter(result => !!result);
850
-
851
- return processLintReport(this, {
852
- results: finalResults
853
- });
854
- }
855
-
856
- /**
857
- * Executes the current configuration on text.
858
- * @param {string} code A string of JavaScript code to lint.
859
- * @param {Object} [options] The options.
860
- * @param {string} [options.filePath] The path to the file of the source code.
861
- * @param {boolean} [options.warnIgnored] When set to true, warn if given filePath is an ignored path.
862
- * @returns {Promise<LintResult[]>} The results of linting the string of code given.
863
- */
864
- async lintText(code, options = {}) {
865
-
866
- // Parameter validation
867
-
868
- if (typeof code !== "string") {
869
- throw new Error("'code' must be a string");
870
- }
871
-
872
- if (typeof options !== "object") {
873
- throw new Error("'options' must be an object, null, or undefined");
874
- }
875
-
876
- // Options validation
877
-
878
- const {
879
- filePath,
880
- warnIgnored,
881
- ...unknownOptions
882
- } = options || {};
883
-
884
- const unknownOptionKeys = Object.keys(unknownOptions);
885
-
886
- if (unknownOptionKeys.length > 0) {
887
- throw new Error(`'options' must not include the unknown option(s): ${unknownOptionKeys.join(", ")}`);
888
- }
889
-
890
- if (filePath !== void 0 && !isNonEmptyString(filePath)) {
891
- throw new Error("'options.filePath' must be a non-empty string or undefined");
892
- }
893
-
894
- if (typeof warnIgnored !== "boolean" && typeof warnIgnored !== "undefined") {
895
- throw new Error("'options.warnIgnored' must be a boolean or undefined");
896
- }
897
-
898
- // Now we can get down to linting
899
-
900
- const {
901
- linter,
902
- options: eslintOptions
903
- } = privateMembers.get(this);
904
- const {
905
- allowInlineConfig,
906
- cwd,
907
- fix,
908
- fixTypes,
909
- warnIgnored: constructorWarnIgnored,
910
- ruleFilter,
911
- stats
912
- } = eslintOptions;
913
- const results = [];
914
- const startTime = Date.now();
915
- const fixTypesSet = fixTypes ? new Set(fixTypes) : null;
916
- const resolvedFilename = path.resolve(cwd, filePath || "__placeholder__.js");
917
- const configs = await this.#configLoader.loadConfigArrayForFile(resolvedFilename);
918
- const configStatus = configs?.getConfigStatus(resolvedFilename) ?? "unconfigured";
919
-
920
- // Clear the last used config arrays.
921
- if (resolvedFilename && configStatus !== "matched") {
922
- const shouldWarnIgnored = typeof warnIgnored === "boolean" ? warnIgnored : constructorWarnIgnored;
923
-
924
- if (shouldWarnIgnored) {
925
- results.push(createIgnoreResult(resolvedFilename, cwd, configStatus));
926
- }
927
- } else {
928
-
929
- const config = configs.getConfig(resolvedFilename);
930
- const fixer = getFixerForFixTypes(fix, fixTypesSet, config);
931
-
932
- // Do lint.
933
- results.push(verifyText({
934
- text: code,
935
- filePath: resolvedFilename.endsWith("__placeholder__.js") ? "<text>" : resolvedFilename,
936
- configs,
937
- cwd,
938
- fix: fixer,
939
- allowInlineConfig,
940
- ruleFilter,
941
- stats,
942
- linter
943
- }));
944
- }
945
-
946
- debug(`Linting complete in: ${Date.now() - startTime}ms`);
947
-
948
- return processLintReport(this, {
949
- results
950
- });
951
-
952
- }
953
-
954
- /**
955
- * Returns the formatter representing the given formatter name.
956
- * @param {string} [name] The name of the formatter to load.
957
- * The following values are allowed:
958
- * - `undefined` ... Load `stylish` builtin formatter.
959
- * - A builtin formatter name ... Load the builtin formatter.
960
- * - A third-party formatter name:
961
- * - `foo` → `eslint-formatter-foo`
962
- * - `@foo` `@foo/eslint-formatter`
963
- * - `@foo/bar` → `@foo/eslint-formatter-bar`
964
- * - A file path ... Load the file.
965
- * @returns {Promise<Formatter>} A promise resolving to the formatter object.
966
- * This promise will be rejected if the given formatter was not found or not
967
- * a function.
968
- */
969
- async loadFormatter(name = "stylish") {
970
- if (typeof name !== "string") {
971
- throw new Error("'name' must be a string");
972
- }
973
-
974
- // replace \ with / for Windows compatibility
975
- const normalizedFormatName = name.replace(/\\/gu, "/");
976
- const namespace = naming.getNamespaceFromTerm(normalizedFormatName);
977
-
978
- // grab our options
979
- const { cwd } = privateMembers.get(this).options;
980
-
981
-
982
- let formatterPath;
983
-
984
- // if there's a slash, then it's a file (TODO: this check seems dubious for scoped npm packages)
985
- if (!namespace && normalizedFormatName.includes("/")) {
986
- formatterPath = path.resolve(cwd, normalizedFormatName);
987
- } else {
988
- try {
989
- const npmFormat = naming.normalizePackageName(normalizedFormatName, "eslint-formatter");
990
-
991
- // TODO: This is pretty dirty...would be nice to clean up at some point.
992
- formatterPath = ModuleResolver.resolve(npmFormat, getPlaceholderPath(cwd));
993
- } catch {
994
- formatterPath = path.resolve(__dirname, "../", "cli-engine", "formatters", `${normalizedFormatName}.js`);
995
- }
996
- }
997
-
998
- let formatter;
999
-
1000
- try {
1001
- formatter = (await import(pathToFileURL(formatterPath))).default;
1002
- } catch (ex) {
1003
-
1004
- // check for formatters that have been removed
1005
- if (removedFormatters.has(name)) {
1006
- ex.message = `The ${name} formatter is no longer part of core ESLint. Install it manually with \`npm install -D eslint-formatter-${name}\``;
1007
- } else {
1008
- ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
1009
- }
1010
-
1011
- throw ex;
1012
- }
1013
-
1014
-
1015
- if (typeof formatter !== "function") {
1016
- throw new TypeError(`Formatter must be a function, but got a ${typeof formatter}.`);
1017
- }
1018
-
1019
- const eslint = this;
1020
-
1021
- return {
1022
-
1023
- /**
1024
- * The main formatter method.
1025
- * @param {LintResult[]} results The lint results to format.
1026
- * @param {ResultsMeta} resultsMeta Warning count and max threshold.
1027
- * @returns {string} The formatted lint results.
1028
- */
1029
- format(results, resultsMeta) {
1030
- let rulesMeta = null;
1031
-
1032
- results.sort(compareResultsByFilePath);
1033
-
1034
- return formatter(results, {
1035
- ...resultsMeta,
1036
- cwd,
1037
- get rulesMeta() {
1038
- if (!rulesMeta) {
1039
- rulesMeta = eslint.getRulesMetaForResults(results);
1040
- }
1041
-
1042
- return rulesMeta;
1043
- }
1044
- });
1045
- }
1046
- };
1047
- }
1048
-
1049
- /**
1050
- * Returns a configuration object for the given file based on the CLI options.
1051
- * This is the same logic used by the ESLint CLI executable to determine
1052
- * configuration for each file it processes.
1053
- * @param {string} filePath The path of the file to retrieve a config object for.
1054
- * @returns {Promise<ConfigData|undefined>} A configuration object for the file
1055
- * or `undefined` if there is no configuration data for the object.
1056
- */
1057
- async calculateConfigForFile(filePath) {
1058
- if (!isNonEmptyString(filePath)) {
1059
- throw new Error("'filePath' must be a non-empty string");
1060
- }
1061
- const options = privateMembers.get(this).options;
1062
- const absolutePath = path.resolve(options.cwd, filePath);
1063
- const configs = await this.#configLoader.loadConfigArrayForFile(absolutePath);
1064
-
1065
- if (!configs) {
1066
- const error = new Error("Could not find config file.");
1067
-
1068
- error.messageTemplate = "config-file-missing";
1069
- throw error;
1070
- }
1071
-
1072
- return configs.getConfig(absolutePath);
1073
- }
1074
-
1075
- /**
1076
- * Finds the config file being used by this instance based on the options
1077
- * passed to the constructor.
1078
- * @param {string} [filePath] The path of the file to find the config file for.
1079
- * @returns {Promise<string|undefined>} The path to the config file being used or
1080
- * `undefined` if no config file is being used.
1081
- */
1082
- findConfigFile(filePath) {
1083
- const options = privateMembers.get(this).options;
1084
-
1085
- /*
1086
- * Because the new config lookup scheme skips the current directory
1087
- * and looks into the parent directories, we need to use a placeholder
1088
- * directory to ensure the file in cwd is checked.
1089
- */
1090
- const fakeCwd = path.join(options.cwd, "__placeholder__");
1091
-
1092
- return this.#configLoader.findConfigFileForPath(filePath ?? fakeCwd)
1093
- .catch(() => void 0);
1094
- }
1095
-
1096
- /**
1097
- * Checks if a given path is ignored by ESLint.
1098
- * @param {string} filePath The path of the file to check.
1099
- * @returns {Promise<boolean>} Whether or not the given path is ignored.
1100
- */
1101
- async isPathIgnored(filePath) {
1102
- const config = await this.calculateConfigForFile(filePath);
1103
-
1104
- return config === void 0;
1105
- }
397
+ /**
398
+ * The type of configuration used by this class.
399
+ * @type {string}
400
+ */
401
+ static configType = "flat";
402
+
403
+ /**
404
+ * The loader to use for finding config files.
405
+ * @type {ConfigLoader|LegacyConfigLoader}
406
+ */
407
+ #configLoader;
408
+
409
+ /**
410
+ * Creates a new instance of the main ESLint API.
411
+ * @param {ESLintOptions} options The options for this instance.
412
+ */
413
+ constructor(options = {}) {
414
+ const defaultConfigs = [];
415
+ const processedOptions = processOptions(options);
416
+ const linter = new Linter({
417
+ cwd: processedOptions.cwd,
418
+ configType: "flat",
419
+ flags: processedOptions.flags,
420
+ });
421
+
422
+ const cacheFilePath = getCacheFile(
423
+ processedOptions.cacheLocation,
424
+ processedOptions.cwd,
425
+ );
426
+
427
+ const lintResultCache = processedOptions.cache
428
+ ? new LintResultCache(cacheFilePath, processedOptions.cacheStrategy)
429
+ : null;
430
+
431
+ const configLoaderOptions = {
432
+ cwd: processedOptions.cwd,
433
+ baseConfig: processedOptions.baseConfig,
434
+ overrideConfig: processedOptions.overrideConfig,
435
+ configFile: processedOptions.configFile,
436
+ ignoreEnabled: processedOptions.ignore,
437
+ ignorePatterns: processedOptions.ignorePatterns,
438
+ defaultConfigs,
439
+ hasUnstableNativeNodeJsTSConfigFlag: linter.hasFlag(
440
+ "unstable_native_nodejs_ts_config",
441
+ ),
442
+ };
443
+
444
+ this.#configLoader = linter.hasFlag("unstable_config_lookup_from_file")
445
+ ? new ConfigLoader(configLoaderOptions)
446
+ : new LegacyConfigLoader(configLoaderOptions);
447
+
448
+ debug(`Using config loader ${this.#configLoader.constructor.name}`);
449
+
450
+ privateMembers.set(this, {
451
+ options: processedOptions,
452
+ linter,
453
+ cacheFilePath,
454
+ lintResultCache,
455
+ defaultConfigs,
456
+ configs: null,
457
+ configLoader: this.#configLoader,
458
+ });
459
+
460
+ /**
461
+ * If additional plugins are passed in, add that to the default
462
+ * configs for this instance.
463
+ */
464
+ if (options.plugins) {
465
+ const plugins = {};
466
+
467
+ for (const [pluginName, plugin] of Object.entries(
468
+ options.plugins,
469
+ )) {
470
+ plugins[naming.getShorthandName(pluginName, "eslint-plugin")] =
471
+ plugin;
472
+ }
473
+
474
+ defaultConfigs.push({
475
+ plugins,
476
+ });
477
+ }
478
+
479
+ // Check for the .eslintignore file, and warn if it's present.
480
+ if (existsSync(path.resolve(processedOptions.cwd, ".eslintignore"))) {
481
+ process.emitWarning(
482
+ 'The ".eslintignore" file is no longer supported. Switch to using the "ignores" property in "eslint.config.js": https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files',
483
+ "ESLintIgnoreWarning",
484
+ );
485
+ }
486
+ }
487
+
488
+ /**
489
+ * The version text.
490
+ * @type {string}
491
+ */
492
+ static get version() {
493
+ return version;
494
+ }
495
+
496
+ /**
497
+ * The default configuration that ESLint uses internally. This is provided for tooling that wants to calculate configurations using the same defaults as ESLint.
498
+ * Keep in mind that the default configuration may change from version to version, so you shouldn't rely on any particular keys or values to be present.
499
+ * @type {ConfigArray}
500
+ */
501
+ static get defaultConfig() {
502
+ return defaultConfig;
503
+ }
504
+
505
+ /**
506
+ * Outputs fixes from the given results to files.
507
+ * @param {LintResult[]} results The lint results.
508
+ * @returns {Promise<void>} Returns a promise that is used to track side effects.
509
+ */
510
+ static async outputFixes(results) {
511
+ if (!Array.isArray(results)) {
512
+ throw new Error("'results' must be an array");
513
+ }
514
+
515
+ await Promise.all(
516
+ results
517
+ .filter(result => {
518
+ if (typeof result !== "object" || result === null) {
519
+ throw new Error("'results' must include only objects");
520
+ }
521
+ return (
522
+ typeof result.output === "string" &&
523
+ path.isAbsolute(result.filePath)
524
+ );
525
+ })
526
+ .map(r => fs.writeFile(r.filePath, r.output)),
527
+ );
528
+ }
529
+
530
+ /**
531
+ * Returns results that only contains errors.
532
+ * @param {LintResult[]} results The results to filter.
533
+ * @returns {LintResult[]} The filtered results.
534
+ */
535
+ static getErrorResults(results) {
536
+ const filtered = [];
537
+
538
+ results.forEach(result => {
539
+ const filteredMessages = result.messages.filter(isErrorMessage);
540
+ const filteredSuppressedMessages =
541
+ result.suppressedMessages.filter(isErrorMessage);
542
+
543
+ if (filteredMessages.length > 0) {
544
+ filtered.push({
545
+ ...result,
546
+ messages: filteredMessages,
547
+ suppressedMessages: filteredSuppressedMessages,
548
+ errorCount: filteredMessages.length,
549
+ warningCount: 0,
550
+ fixableErrorCount: result.fixableErrorCount,
551
+ fixableWarningCount: 0,
552
+ });
553
+ }
554
+ });
555
+
556
+ return filtered;
557
+ }
558
+
559
+ /**
560
+ * Returns meta objects for each rule represented in the lint results.
561
+ * @param {LintResult[]} results The results to fetch rules meta for.
562
+ * @returns {Object} A mapping of ruleIds to rule meta objects.
563
+ * @throws {TypeError} When the results object wasn't created from this ESLint instance.
564
+ * @throws {TypeError} When a plugin or rule is missing.
565
+ */
566
+ getRulesMetaForResults(results) {
567
+ // short-circuit simple case
568
+ if (results.length === 0) {
569
+ return {};
570
+ }
571
+
572
+ const resultRules = new Map();
573
+ const {
574
+ configLoader,
575
+ options: { cwd },
576
+ } = privateMembers.get(this);
577
+
578
+ for (const result of results) {
579
+ /*
580
+ * Normalize filename for <text>.
581
+ */
582
+ const filePath =
583
+ result.filePath === "<text>"
584
+ ? getPlaceholderPath(cwd)
585
+ : result.filePath;
586
+ const allMessages = result.messages.concat(
587
+ result.suppressedMessages,
588
+ );
589
+
590
+ for (const { ruleId } of allMessages) {
591
+ if (!ruleId) {
592
+ continue;
593
+ }
594
+
595
+ /*
596
+ * All of the plugin and rule information is contained within the
597
+ * calculated config for the given file.
598
+ */
599
+ let configs;
600
+
601
+ try {
602
+ configs =
603
+ configLoader.getCachedConfigArrayForFile(filePath);
604
+ } catch {
605
+ throw createExtraneousResultsError();
606
+ }
607
+
608
+ const config = configs.getConfig(filePath);
609
+
610
+ if (!config) {
611
+ throw createExtraneousResultsError();
612
+ }
613
+ const rule = getRuleFromConfig(ruleId, config);
614
+
615
+ // ignore unknown rules
616
+ if (rule) {
617
+ resultRules.set(ruleId, rule);
618
+ }
619
+ }
620
+ }
621
+
622
+ return createRulesMeta(resultRules);
623
+ }
624
+
625
+ /**
626
+ * Indicates if the given feature flag is enabled for this instance.
627
+ * @param {string} flag The feature flag to check.
628
+ * @returns {boolean} `true` if the feature flag is enabled, `false` if not.
629
+ */
630
+ hasFlag(flag) {
631
+ // note: Linter does validation of the flags
632
+ return privateMembers.get(this).linter.hasFlag(flag);
633
+ }
634
+
635
+ /**
636
+ * Executes the current configuration on an array of file and directory names.
637
+ * @param {string|string[]} patterns An array of file and directory names.
638
+ * @returns {Promise<LintResult[]>} The results of linting the file patterns given.
639
+ */
640
+ async lintFiles(patterns) {
641
+ let normalizedPatterns = patterns;
642
+ const {
643
+ cacheFilePath,
644
+ lintResultCache,
645
+ linter,
646
+ options: eslintOptions,
647
+ } = privateMembers.get(this);
648
+
649
+ /*
650
+ * Special cases:
651
+ * 1. `patterns` is an empty string
652
+ * 2. `patterns` is an empty array
653
+ *
654
+ * In both cases, we use the cwd as the directory to lint.
655
+ */
656
+ if (
657
+ patterns === "" ||
658
+ (Array.isArray(patterns) && patterns.length === 0)
659
+ ) {
660
+ /*
661
+ * Special case: If `passOnNoPatterns` is true, then we just exit
662
+ * without doing any work.
663
+ */
664
+ if (eslintOptions.passOnNoPatterns) {
665
+ return [];
666
+ }
667
+
668
+ normalizedPatterns = ["."];
669
+ } else {
670
+ if (
671
+ !isNonEmptyString(patterns) &&
672
+ !isArrayOfNonEmptyString(patterns)
673
+ ) {
674
+ throw new Error(
675
+ "'patterns' must be a non-empty string or an array of non-empty strings",
676
+ );
677
+ }
678
+
679
+ if (typeof patterns === "string") {
680
+ normalizedPatterns = [patterns];
681
+ }
682
+ }
683
+
684
+ debug(`Using file patterns: ${normalizedPatterns}`);
685
+
686
+ const {
687
+ allowInlineConfig,
688
+ cache,
689
+ cwd,
690
+ fix,
691
+ fixTypes,
692
+ ruleFilter,
693
+ stats,
694
+ globInputPaths,
695
+ errorOnUnmatchedPattern,
696
+ warnIgnored,
697
+ } = eslintOptions;
698
+ const startTime = Date.now();
699
+ const fixTypesSet = fixTypes ? new Set(fixTypes) : null;
700
+
701
+ // Delete cache file; should this be done here?
702
+ if (!cache && cacheFilePath) {
703
+ debug(`Deleting cache file at ${cacheFilePath}`);
704
+
705
+ try {
706
+ await fs.unlink(cacheFilePath);
707
+ } catch (error) {
708
+ const errorCode = error && error.code;
709
+
710
+ // Ignore errors when no such file exists or file system is read only (and cache file does not exist)
711
+ if (
712
+ errorCode !== "ENOENT" &&
713
+ !(errorCode === "EROFS" && !existsSync(cacheFilePath))
714
+ ) {
715
+ throw error;
716
+ }
717
+ }
718
+ }
719
+
720
+ const filePaths = await findFiles({
721
+ patterns: normalizedPatterns,
722
+ cwd,
723
+ globInputPaths,
724
+ configLoader: this.#configLoader,
725
+ errorOnUnmatchedPattern,
726
+ });
727
+ const controller = new AbortController();
728
+ const retryCodes = new Set(["ENFILE", "EMFILE"]);
729
+ const retrier = new Retrier(error => retryCodes.has(error.code), {
730
+ concurrency: 100,
731
+ });
732
+
733
+ debug(
734
+ `${filePaths.length} files found in: ${Date.now() - startTime}ms`,
735
+ );
736
+
737
+ /*
738
+ * Because we need to process multiple files, including reading from disk,
739
+ * it is most efficient to start by reading each file via promises so that
740
+ * they can be done in parallel. Then, we can lint the returned text. This
741
+ * ensures we are waiting the minimum amount of time in between lints.
742
+ */
743
+ const results = await Promise.all(
744
+ filePaths.map(async filePath => {
745
+ const configs =
746
+ await this.#configLoader.loadConfigArrayForFile(filePath);
747
+ const config = configs.getConfig(filePath);
748
+
749
+ /*
750
+ * If a filename was entered that cannot be matched
751
+ * to a config, then notify the user.
752
+ */
753
+ if (!config) {
754
+ if (warnIgnored) {
755
+ const configStatus = configs.getConfigStatus(filePath);
756
+
757
+ return createIgnoreResult(filePath, cwd, configStatus);
758
+ }
759
+
760
+ return void 0;
761
+ }
762
+
763
+ // Skip if there is cached result.
764
+ if (lintResultCache) {
765
+ const cachedResult = lintResultCache.getCachedLintResults(
766
+ filePath,
767
+ config,
768
+ );
769
+
770
+ if (cachedResult) {
771
+ const hadMessages =
772
+ cachedResult.messages &&
773
+ cachedResult.messages.length > 0;
774
+
775
+ if (hadMessages && fix) {
776
+ debug(
777
+ `Reprocessing cached file to allow autofix: ${filePath}`,
778
+ );
779
+ } else {
780
+ debug(
781
+ `Skipping file since it hasn't changed: ${filePath}`,
782
+ );
783
+ return cachedResult;
784
+ }
785
+ }
786
+ }
787
+
788
+ // set up fixer for fixTypes if necessary
789
+ const fixer = getFixerForFixTypes(fix, fixTypesSet, config);
790
+
791
+ return retrier
792
+ .retry(
793
+ () =>
794
+ fs
795
+ .readFile(filePath, {
796
+ encoding: "utf8",
797
+ signal: controller.signal,
798
+ })
799
+ .then(text => {
800
+ // fail immediately if an error occurred in another file
801
+ controller.signal.throwIfAborted();
802
+
803
+ // do the linting
804
+ const result = verifyText({
805
+ text,
806
+ filePath,
807
+ configs,
808
+ cwd,
809
+ fix: fixer,
810
+ allowInlineConfig,
811
+ ruleFilter,
812
+ stats,
813
+ linter,
814
+ });
815
+
816
+ /*
817
+ * Store the lint result in the LintResultCache.
818
+ * NOTE: The LintResultCache will remove the file source and any
819
+ * other properties that are difficult to serialize, and will
820
+ * hydrate those properties back in on future lint runs.
821
+ */
822
+ if (lintResultCache) {
823
+ lintResultCache.setCachedLintResults(
824
+ filePath,
825
+ config,
826
+ result,
827
+ );
828
+ }
829
+
830
+ return result;
831
+ }),
832
+ { signal: controller.signal },
833
+ )
834
+ .catch(error => {
835
+ controller.abort(error);
836
+ throw error;
837
+ });
838
+ }),
839
+ );
840
+
841
+ // Persist the cache to disk.
842
+ if (lintResultCache) {
843
+ lintResultCache.reconcile();
844
+ }
845
+
846
+ const finalResults = results.filter(result => !!result);
847
+
848
+ return processLintReport(this, {
849
+ results: finalResults,
850
+ });
851
+ }
852
+
853
+ /**
854
+ * Executes the current configuration on text.
855
+ * @param {string} code A string of JavaScript code to lint.
856
+ * @param {Object} [options] The options.
857
+ * @param {string} [options.filePath] The path to the file of the source code.
858
+ * @param {boolean} [options.warnIgnored] When set to true, warn if given filePath is an ignored path.
859
+ * @returns {Promise<LintResult[]>} The results of linting the string of code given.
860
+ */
861
+ async lintText(code, options = {}) {
862
+ // Parameter validation
863
+
864
+ if (typeof code !== "string") {
865
+ throw new Error("'code' must be a string");
866
+ }
867
+
868
+ if (typeof options !== "object") {
869
+ throw new Error("'options' must be an object, null, or undefined");
870
+ }
871
+
872
+ // Options validation
873
+
874
+ const { filePath, warnIgnored, ...unknownOptions } = options || {};
875
+
876
+ const unknownOptionKeys = Object.keys(unknownOptions);
877
+
878
+ if (unknownOptionKeys.length > 0) {
879
+ throw new Error(
880
+ `'options' must not include the unknown option(s): ${unknownOptionKeys.join(", ")}`,
881
+ );
882
+ }
883
+
884
+ if (filePath !== void 0 && !isNonEmptyString(filePath)) {
885
+ throw new Error(
886
+ "'options.filePath' must be a non-empty string or undefined",
887
+ );
888
+ }
889
+
890
+ if (
891
+ typeof warnIgnored !== "boolean" &&
892
+ typeof warnIgnored !== "undefined"
893
+ ) {
894
+ throw new Error(
895
+ "'options.warnIgnored' must be a boolean or undefined",
896
+ );
897
+ }
898
+
899
+ // Now we can get down to linting
900
+
901
+ const { linter, options: eslintOptions } = privateMembers.get(this);
902
+ const {
903
+ allowInlineConfig,
904
+ cwd,
905
+ fix,
906
+ fixTypes,
907
+ warnIgnored: constructorWarnIgnored,
908
+ ruleFilter,
909
+ stats,
910
+ } = eslintOptions;
911
+ const results = [];
912
+ const startTime = Date.now();
913
+ const fixTypesSet = fixTypes ? new Set(fixTypes) : null;
914
+ const resolvedFilename = path.resolve(
915
+ cwd,
916
+ filePath || "__placeholder__.js",
917
+ );
918
+ const configs =
919
+ await this.#configLoader.loadConfigArrayForFile(resolvedFilename);
920
+ const configStatus =
921
+ configs?.getConfigStatus(resolvedFilename) ?? "unconfigured";
922
+
923
+ // Clear the last used config arrays.
924
+ if (resolvedFilename && configStatus !== "matched") {
925
+ const shouldWarnIgnored =
926
+ typeof warnIgnored === "boolean"
927
+ ? warnIgnored
928
+ : constructorWarnIgnored;
929
+
930
+ if (shouldWarnIgnored) {
931
+ results.push(
932
+ createIgnoreResult(resolvedFilename, cwd, configStatus),
933
+ );
934
+ }
935
+ } else {
936
+ const config = configs.getConfig(resolvedFilename);
937
+ const fixer = getFixerForFixTypes(fix, fixTypesSet, config);
938
+
939
+ // Do lint.
940
+ results.push(
941
+ verifyText({
942
+ text: code,
943
+ filePath: resolvedFilename.endsWith("__placeholder__.js")
944
+ ? "<text>"
945
+ : resolvedFilename,
946
+ configs,
947
+ cwd,
948
+ fix: fixer,
949
+ allowInlineConfig,
950
+ ruleFilter,
951
+ stats,
952
+ linter,
953
+ }),
954
+ );
955
+ }
956
+
957
+ debug(`Linting complete in: ${Date.now() - startTime}ms`);
958
+
959
+ return processLintReport(this, {
960
+ results,
961
+ });
962
+ }
963
+
964
+ /**
965
+ * Returns the formatter representing the given formatter name.
966
+ * @param {string} [name] The name of the formatter to load.
967
+ * The following values are allowed:
968
+ * - `undefined` ... Load `stylish` builtin formatter.
969
+ * - A builtin formatter name ... Load the builtin formatter.
970
+ * - A third-party formatter name:
971
+ * - `foo` → `eslint-formatter-foo`
972
+ * - `@foo` `@foo/eslint-formatter`
973
+ * - `@foo/bar` → `@foo/eslint-formatter-bar`
974
+ * - A file path ... Load the file.
975
+ * @returns {Promise<Formatter>} A promise resolving to the formatter object.
976
+ * This promise will be rejected if the given formatter was not found or not
977
+ * a function.
978
+ */
979
+ async loadFormatter(name = "stylish") {
980
+ if (typeof name !== "string") {
981
+ throw new Error("'name' must be a string");
982
+ }
983
+
984
+ // replace \ with / for Windows compatibility
985
+ const normalizedFormatName = name.replace(/\\/gu, "/");
986
+ const namespace = naming.getNamespaceFromTerm(normalizedFormatName);
987
+
988
+ // grab our options
989
+ const { cwd } = privateMembers.get(this).options;
990
+
991
+ let formatterPath;
992
+
993
+ // if there's a slash, then it's a file (TODO: this check seems dubious for scoped npm packages)
994
+ if (!namespace && normalizedFormatName.includes("/")) {
995
+ formatterPath = path.resolve(cwd, normalizedFormatName);
996
+ } else {
997
+ try {
998
+ const npmFormat = naming.normalizePackageName(
999
+ normalizedFormatName,
1000
+ "eslint-formatter",
1001
+ );
1002
+
1003
+ // TODO: This is pretty dirty...would be nice to clean up at some point.
1004
+ formatterPath = ModuleResolver.resolve(
1005
+ npmFormat,
1006
+ getPlaceholderPath(cwd),
1007
+ );
1008
+ } catch {
1009
+ formatterPath = path.resolve(
1010
+ __dirname,
1011
+ "../",
1012
+ "cli-engine",
1013
+ "formatters",
1014
+ `${normalizedFormatName}.js`,
1015
+ );
1016
+ }
1017
+ }
1018
+
1019
+ let formatter;
1020
+
1021
+ try {
1022
+ formatter = (await import(pathToFileURL(formatterPath))).default;
1023
+ } catch (ex) {
1024
+ // check for formatters that have been removed
1025
+ if (removedFormatters.has(name)) {
1026
+ ex.message = `The ${name} formatter is no longer part of core ESLint. Install it manually with \`npm install -D eslint-formatter-${name}\``;
1027
+ } else {
1028
+ ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
1029
+ }
1030
+
1031
+ throw ex;
1032
+ }
1033
+
1034
+ if (typeof formatter !== "function") {
1035
+ throw new TypeError(
1036
+ `Formatter must be a function, but got a ${typeof formatter}.`,
1037
+ );
1038
+ }
1039
+
1040
+ const eslint = this;
1041
+
1042
+ return {
1043
+ /**
1044
+ * The main formatter method.
1045
+ * @param {LintResult[]} results The lint results to format.
1046
+ * @param {ResultsMeta} resultsMeta Warning count and max threshold.
1047
+ * @returns {string} The formatted lint results.
1048
+ */
1049
+ format(results, resultsMeta) {
1050
+ let rulesMeta = null;
1051
+
1052
+ results.sort(compareResultsByFilePath);
1053
+
1054
+ return formatter(results, {
1055
+ ...resultsMeta,
1056
+ cwd,
1057
+ get rulesMeta() {
1058
+ if (!rulesMeta) {
1059
+ rulesMeta = eslint.getRulesMetaForResults(results);
1060
+ }
1061
+
1062
+ return rulesMeta;
1063
+ },
1064
+ });
1065
+ },
1066
+ };
1067
+ }
1068
+
1069
+ /**
1070
+ * Returns a configuration object for the given file based on the CLI options.
1071
+ * This is the same logic used by the ESLint CLI executable to determine
1072
+ * configuration for each file it processes.
1073
+ * @param {string} filePath The path of the file to retrieve a config object for.
1074
+ * @returns {Promise<ConfigData|undefined>} A configuration object for the file
1075
+ * or `undefined` if there is no configuration data for the object.
1076
+ */
1077
+ async calculateConfigForFile(filePath) {
1078
+ if (!isNonEmptyString(filePath)) {
1079
+ throw new Error("'filePath' must be a non-empty string");
1080
+ }
1081
+ const options = privateMembers.get(this).options;
1082
+ const absolutePath = path.resolve(options.cwd, filePath);
1083
+ const configs =
1084
+ await this.#configLoader.loadConfigArrayForFile(absolutePath);
1085
+
1086
+ if (!configs) {
1087
+ const error = new Error("Could not find config file.");
1088
+
1089
+ error.messageTemplate = "config-file-missing";
1090
+ throw error;
1091
+ }
1092
+
1093
+ return configs.getConfig(absolutePath);
1094
+ }
1095
+
1096
+ /**
1097
+ * Finds the config file being used by this instance based on the options
1098
+ * passed to the constructor.
1099
+ * @param {string} [filePath] The path of the file to find the config file for.
1100
+ * @returns {Promise<string|undefined>} The path to the config file being used or
1101
+ * `undefined` if no config file is being used.
1102
+ */
1103
+ findConfigFile(filePath) {
1104
+ const options = privateMembers.get(this).options;
1105
+
1106
+ /*
1107
+ * Because the new config lookup scheme skips the current directory
1108
+ * and looks into the parent directories, we need to use a placeholder
1109
+ * directory to ensure the file in cwd is checked.
1110
+ */
1111
+ const fakeCwd = path.join(options.cwd, "__placeholder__");
1112
+
1113
+ return this.#configLoader
1114
+ .findConfigFileForPath(filePath ?? fakeCwd)
1115
+ .catch(() => void 0);
1116
+ }
1117
+
1118
+ /**
1119
+ * Checks if a given path is ignored by ESLint.
1120
+ * @param {string} filePath The path of the file to check.
1121
+ * @returns {Promise<boolean>} Whether or not the given path is ignored.
1122
+ */
1123
+ async isPathIgnored(filePath) {
1124
+ const config = await this.calculateConfigForFile(filePath);
1125
+
1126
+ return config === void 0;
1127
+ }
1106
1128
  }
1107
1129
 
1108
1130
  /**
@@ -1110,7 +1132,7 @@ class ESLint {
1110
1132
  * @returns {Promise<boolean>} Whether flat config should be used.
1111
1133
  */
1112
1134
  async function shouldUseFlatConfig() {
1113
- return (process.env.ESLINT_USE_FLAT_CONFIG !== "false");
1135
+ return process.env.ESLINT_USE_FLAT_CONFIG !== "false";
1114
1136
  }
1115
1137
 
1116
1138
  //------------------------------------------------------------------------------
@@ -1118,7 +1140,7 @@ async function shouldUseFlatConfig() {
1118
1140
  //------------------------------------------------------------------------------
1119
1141
 
1120
1142
  module.exports = {
1121
- ESLint,
1122
- shouldUseFlatConfig,
1123
- locateConfigFileToUse
1143
+ ESLint,
1144
+ shouldUseFlatConfig,
1145
+ locateConfigFileToUse,
1124
1146
  };