eslint 8.57.1 → 9.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (458) hide show
  1. package/README.md +165 -115
  2. package/bin/eslint.js +112 -89
  3. package/conf/default-cli-options.js +22 -22
  4. package/conf/ecma-version.js +16 -0
  5. package/conf/globals.js +109 -94
  6. package/conf/replacements.json +24 -20
  7. package/conf/rule-type-list.json +89 -26
  8. package/lib/api.js +16 -20
  9. package/lib/cli-engine/cli-engine.js +841 -810
  10. package/lib/cli-engine/file-enumerator.js +384 -390
  11. package/lib/cli-engine/formatters/formatters-meta.json +17 -45
  12. package/lib/cli-engine/formatters/html.js +110 -102
  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 +97 -76
  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 +165 -148
  19. package/lib/cli-engine/load-rules.js +17 -17
  20. package/lib/cli.js +481 -399
  21. package/lib/config/config-loader.js +816 -0
  22. package/lib/config/config.js +674 -0
  23. package/lib/config/default-config.js +57 -46
  24. package/lib/config/flat-config-array.js +170 -333
  25. package/lib/config/flat-config-schema.js +389 -389
  26. package/lib/config-api.js +12 -0
  27. package/lib/eslint/eslint-helpers.js +1196 -663
  28. package/lib/eslint/eslint.js +1262 -607
  29. package/lib/eslint/index.js +3 -3
  30. package/lib/eslint/legacy-eslint.js +786 -0
  31. package/lib/eslint/worker.js +173 -0
  32. package/lib/languages/js/index.js +336 -0
  33. package/lib/languages/js/source-code/index.js +7 -0
  34. package/lib/languages/js/source-code/source-code.js +1364 -0
  35. package/lib/languages/js/source-code/token-store/backward-token-comment-cursor.js +61 -0
  36. package/lib/languages/js/source-code/token-store/backward-token-cursor.js +57 -0
  37. package/lib/{source-code → languages/js/source-code}/token-store/cursor.js +36 -36
  38. package/lib/languages/js/source-code/token-store/cursors.js +120 -0
  39. package/lib/{source-code → languages/js/source-code}/token-store/decorative-cursor.js +17 -18
  40. package/lib/{source-code → languages/js/source-code}/token-store/filter-cursor.js +19 -20
  41. package/lib/languages/js/source-code/token-store/forward-token-comment-cursor.js +65 -0
  42. package/lib/languages/js/source-code/token-store/forward-token-cursor.js +62 -0
  43. package/lib/languages/js/source-code/token-store/index.js +721 -0
  44. package/lib/{source-code → languages/js/source-code}/token-store/limit-cursor.js +17 -18
  45. package/lib/languages/js/source-code/token-store/padded-token-cursor.js +45 -0
  46. package/lib/{source-code → languages/js/source-code}/token-store/skip-cursor.js +19 -20
  47. package/lib/languages/js/source-code/token-store/utils.js +110 -0
  48. package/lib/languages/js/validate-language-options.js +196 -0
  49. package/lib/linter/apply-disable-directives.js +490 -371
  50. package/lib/linter/code-path-analysis/code-path-analyzer.js +650 -674
  51. package/lib/linter/code-path-analysis/code-path-segment.js +215 -216
  52. package/lib/linter/code-path-analysis/code-path-state.js +2118 -2096
  53. package/lib/linter/code-path-analysis/code-path.js +307 -317
  54. package/lib/linter/code-path-analysis/debug-helpers.js +183 -163
  55. package/lib/linter/code-path-analysis/fork-context.js +297 -272
  56. package/lib/linter/code-path-analysis/id-generator.js +22 -23
  57. package/lib/linter/esquery.js +332 -0
  58. package/lib/linter/file-context.js +144 -0
  59. package/lib/linter/file-report.js +608 -0
  60. package/lib/linter/index.js +3 -5
  61. package/lib/linter/interpolate.js +38 -16
  62. package/lib/linter/linter.js +2328 -1785
  63. package/lib/linter/rule-fixer.js +136 -107
  64. package/lib/linter/rules.js +37 -46
  65. package/lib/linter/source-code-fixer.js +96 -94
  66. package/lib/linter/source-code-traverser.js +333 -0
  67. package/lib/linter/source-code-visitor.js +81 -0
  68. package/lib/linter/timing.js +145 -97
  69. package/lib/linter/vfile.js +115 -0
  70. package/lib/options.js +464 -326
  71. package/lib/rule-tester/index.js +3 -1
  72. package/lib/rule-tester/rule-tester.js +1371 -998
  73. package/lib/rules/accessor-pairs.js +333 -259
  74. package/lib/rules/array-bracket-newline.js +250 -220
  75. package/lib/rules/array-bracket-spacing.js +286 -229
  76. package/lib/rules/array-callback-return.js +401 -354
  77. package/lib/rules/array-element-newline.js +358 -295
  78. package/lib/rules/arrow-body-style.js +400 -278
  79. package/lib/rules/arrow-parens.js +206 -155
  80. package/lib/rules/arrow-spacing.js +169 -145
  81. package/lib/rules/block-scoped-var.js +125 -123
  82. package/lib/rules/block-spacing.js +186 -158
  83. package/lib/rules/brace-style.js +262 -181
  84. package/lib/rules/callback-return.js +203 -174
  85. package/lib/rules/camelcase.js +403 -380
  86. package/lib/rules/capitalized-comments.js +253 -228
  87. package/lib/rules/class-methods-use-this.js +231 -168
  88. package/lib/rules/comma-dangle.js +379 -328
  89. package/lib/rules/comma-spacing.js +193 -177
  90. package/lib/rules/comma-style.js +375 -298
  91. package/lib/rules/complexity.js +180 -144
  92. package/lib/rules/computed-property-spacing.js +236 -193
  93. package/lib/rules/consistent-return.js +181 -170
  94. package/lib/rules/consistent-this.js +167 -141
  95. package/lib/rules/constructor-super.js +418 -411
  96. package/lib/rules/curly.js +407 -468
  97. package/lib/rules/default-case-last.js +39 -32
  98. package/lib/rules/default-case.js +89 -83
  99. package/lib/rules/default-param-last.js +69 -53
  100. package/lib/rules/dot-location.js +122 -92
  101. package/lib/rules/dot-notation.js +193 -153
  102. package/lib/rules/eol-last.js +122 -102
  103. package/lib/rules/eqeqeq.js +191 -155
  104. package/lib/rules/for-direction.js +150 -122
  105. package/lib/rules/func-call-spacing.js +261 -213
  106. package/lib/rules/func-name-matching.js +294 -209
  107. package/lib/rules/func-names.js +165 -164
  108. package/lib/rules/func-style.js +209 -86
  109. package/lib/rules/function-call-argument-newline.js +152 -111
  110. package/lib/rules/function-paren-newline.js +349 -273
  111. package/lib/rules/generator-star-spacing.js +229 -192
  112. package/lib/rules/getter-return.js +208 -170
  113. package/lib/rules/global-require.js +85 -58
  114. package/lib/rules/grouped-accessor-pairs.js +201 -148
  115. package/lib/rules/guard-for-in.js +72 -63
  116. package/lib/rules/handle-callback-err.js +108 -87
  117. package/lib/rules/id-blacklist.js +182 -187
  118. package/lib/rules/id-denylist.js +174 -179
  119. package/lib/rules/id-length.js +197 -157
  120. package/lib/rules/id-match.js +350 -286
  121. package/lib/rules/implicit-arrow-linebreak.js +102 -61
  122. package/lib/rules/indent-legacy.js +1345 -1102
  123. package/lib/rules/indent.js +2272 -1741
  124. package/lib/rules/index.js +320 -294
  125. package/lib/rules/init-declarations.js +139 -106
  126. package/lib/rules/jsx-quotes.js +94 -64
  127. package/lib/rules/key-spacing.js +750 -615
  128. package/lib/rules/keyword-spacing.js +648 -587
  129. package/lib/rules/line-comment-position.js +143 -108
  130. package/lib/rules/linebreak-style.js +115 -88
  131. package/lib/rules/lines-around-comment.js +540 -430
  132. package/lib/rules/lines-around-directive.js +233 -185
  133. package/lib/rules/lines-between-class-members.js +305 -216
  134. package/lib/rules/logical-assignment-operators.js +582 -398
  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 -416
  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 +102 -75
  142. package/lib/rules/max-statements-per-line.js +205 -180
  143. package/lib/rules/max-statements.js +168 -164
  144. package/lib/rules/multiline-comment-style.js +638 -460
  145. package/lib/rules/multiline-ternary.js +241 -158
  146. package/lib/rules/new-cap.js +233 -232
  147. package/lib/rules/new-parens.js +88 -61
  148. package/lib/rules/newline-after-var.js +287 -233
  149. package/lib/rules/newline-before-return.js +229 -204
  150. package/lib/rules/newline-per-chained-call.js +142 -109
  151. package/lib/rules/no-alert.js +90 -79
  152. package/lib/rules/no-array-constructor.js +175 -113
  153. package/lib/rules/no-async-promise-executor.js +30 -24
  154. package/lib/rules/no-await-in-loop.js +79 -70
  155. package/lib/rules/no-bitwise.js +113 -87
  156. package/lib/rules/no-buffer-constructor.js +61 -37
  157. package/lib/rules/no-caller.js +39 -33
  158. package/lib/rules/no-case-declarations.js +61 -45
  159. package/lib/rules/no-catch-shadow.js +76 -62
  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 -63
  164. package/lib/rules/no-console.js +202 -188
  165. package/lib/rules/no-const-assign.js +58 -41
  166. package/lib/rules/no-constant-binary-expression.js +501 -407
  167. package/lib/rules/no-constant-condition.js +158 -131
  168. package/lib/rules/no-constructor-return.js +49 -49
  169. package/lib/rules/no-continue.js +25 -26
  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 -40
  174. package/lib/rules/no-dupe-args.js +79 -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 +266 -188
  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 +197 -128
  183. package/lib/rules/no-empty-pattern.js +63 -56
  184. package/lib/rules/no-empty-static-block.js +61 -35
  185. package/lib/rules/no-empty.js +135 -85
  186. package/lib/rules/no-eq-null.js +37 -32
  187. package/lib/rules/no-eval.js +258 -249
  188. package/lib/rules/no-ex-assign.js +42 -39
  189. package/lib/rules/no-extend-native.js +161 -160
  190. package/lib/rules/no-extra-bind.js +201 -190
  191. package/lib/rules/no-extra-boolean-cast.js +398 -295
  192. package/lib/rules/no-extra-label.js +150 -130
  193. package/lib/rules/no-extra-parens.js +1654 -1307
  194. package/lib/rules/no-extra-semi.js +146 -126
  195. package/lib/rules/no-fallthrough.js +200 -136
  196. package/lib/rules/no-floating-decimal.js +74 -48
  197. package/lib/rules/no-func-assign.js +54 -55
  198. package/lib/rules/no-global-assign.js +78 -72
  199. package/lib/rules/no-implicit-coercion.js +350 -262
  200. package/lib/rules/no-implicit-globals.js +174 -133
  201. package/lib/rules/no-implied-eval.js +150 -112
  202. package/lib/rules/no-import-assign.js +145 -159
  203. package/lib/rules/no-inline-comments.js +101 -96
  204. package/lib/rules/no-inner-declarations.js +115 -78
  205. package/lib/rules/no-invalid-regexp.js +223 -174
  206. package/lib/rules/no-invalid-this.js +145 -117
  207. package/lib/rules/no-irregular-whitespace.js +266 -250
  208. package/lib/rules/no-iterator.js +29 -33
  209. package/lib/rules/no-label-var.js +59 -61
  210. package/lib/rules/no-labels.js +138 -131
  211. package/lib/rules/no-lone-blocks.js +127 -123
  212. package/lib/rules/no-lonely-if.js +105 -67
  213. package/lib/rules/no-loop-func.js +245 -184
  214. package/lib/rules/no-loss-of-precision.js +236 -201
  215. package/lib/rules/no-magic-numbers.js +339 -217
  216. package/lib/rules/no-misleading-character-class.js +548 -253
  217. package/lib/rules/no-mixed-operators.js +188 -164
  218. package/lib/rules/no-mixed-requires.js +253 -224
  219. package/lib/rules/no-mixed-spaces-and-tabs.js +135 -103
  220. package/lib/rules/no-multi-assign.js +46 -47
  221. package/lib/rules/no-multi-spaces.js +163 -125
  222. package/lib/rules/no-multi-str.js +42 -40
  223. package/lib/rules/no-multiple-empty-lines.js +196 -140
  224. package/lib/rules/no-native-reassign.js +90 -74
  225. package/lib/rules/no-negated-condition.js +79 -74
  226. package/lib/rules/no-negated-in-lhs.js +45 -32
  227. package/lib/rules/no-nested-ternary.js +33 -31
  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 -39
  231. package/lib/rules/no-new-require.js +48 -31
  232. package/lib/rules/no-new-symbol.js +61 -43
  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 +149 -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 +29 -32
  240. package/lib/rules/no-param-reassign.js +236 -218
  241. package/lib/rules/no-path-concat.js +66 -51
  242. package/lib/rules/no-plusplus.js +60 -63
  243. package/lib/rules/no-process-env.js +49 -32
  244. package/lib/rules/no-process-exit.js +48 -28
  245. package/lib/rules/no-promise-executor-return.js +205 -204
  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 -155
  249. package/lib/rules/no-regex-spaces.js +183 -161
  250. package/lib/rules/no-restricted-exports.js +208 -174
  251. package/lib/rules/no-restricted-globals.js +254 -112
  252. package/lib/rules/no-restricted-imports.js +824 -384
  253. package/lib/rules/no-restricted-modules.js +222 -186
  254. package/lib/rules/no-restricted-properties.js +218 -153
  255. package/lib/rules/no-restricted-syntax.js +56 -52
  256. package/lib/rules/no-return-assign.js +56 -49
  257. package/lib/rules/no-return-await.js +147 -120
  258. package/lib/rules/no-script-url.js +53 -46
  259. package/lib/rules/no-self-assign.js +148 -145
  260. package/lib/rules/no-self-compare.js +63 -46
  261. package/lib/rules/no-sequences.js +135 -115
  262. package/lib/rules/no-setter-return.js +176 -178
  263. package/lib/rules/no-shadow-restricted-names.js +84 -36
  264. package/lib/rules/no-shadow.js +598 -310
  265. package/lib/rules/no-spaced-func.js +82 -60
  266. package/lib/rules/no-sparse-arrays.js +46 -28
  267. package/lib/rules/no-sync.js +61 -44
  268. package/lib/rules/no-tabs.js +83 -54
  269. package/lib/rules/no-template-curly-in-string.js +33 -32
  270. package/lib/rules/no-ternary.js +25 -28
  271. package/lib/rules/no-this-before-super.js +332 -298
  272. package/lib/rules/no-throw-literal.js +31 -36
  273. package/lib/rules/no-trailing-spaces.js +208 -174
  274. package/lib/rules/no-unassigned-vars.js +80 -0
  275. package/lib/rules/no-undef-init.js +86 -60
  276. package/lib/rules/no-undef.js +52 -47
  277. package/lib/rules/no-undefined.js +73 -74
  278. package/lib/rules/no-underscore-dangle.js +370 -322
  279. package/lib/rules/no-unexpected-multiline.js +112 -102
  280. package/lib/rules/no-unmodified-loop-condition.js +254 -254
  281. package/lib/rules/no-unneeded-ternary.js +212 -146
  282. package/lib/rules/no-unreachable-loop.js +145 -140
  283. package/lib/rules/no-unreachable.js +255 -248
  284. package/lib/rules/no-unsafe-finally.js +93 -85
  285. package/lib/rules/no-unsafe-negation.js +105 -81
  286. package/lib/rules/no-unsafe-optional-chaining.js +193 -177
  287. package/lib/rules/no-unused-expressions.js +199 -158
  288. package/lib/rules/no-unused-labels.js +139 -124
  289. package/lib/rules/no-unused-private-class-members.js +206 -182
  290. package/lib/rules/no-unused-vars.js +1708 -687
  291. package/lib/rules/no-use-before-define.js +327 -229
  292. package/lib/rules/no-useless-assignment.js +654 -0
  293. package/lib/rules/no-useless-backreference.js +212 -143
  294. package/lib/rules/no-useless-call.js +58 -53
  295. package/lib/rules/no-useless-catch.js +40 -40
  296. package/lib/rules/no-useless-computed-key.js +144 -108
  297. package/lib/rules/no-useless-concat.js +65 -59
  298. package/lib/rules/no-useless-constructor.js +160 -97
  299. package/lib/rules/no-useless-escape.js +364 -291
  300. package/lib/rules/no-useless-rename.js +183 -153
  301. package/lib/rules/no-useless-return.js +344 -307
  302. package/lib/rules/no-var.js +245 -212
  303. package/lib/rules/no-void.js +51 -46
  304. package/lib/rules/no-warning-comments.js +191 -183
  305. package/lib/rules/no-whitespace-before-property.js +131 -97
  306. package/lib/rules/no-with.js +24 -26
  307. package/lib/rules/nonblock-statement-body-position.js +149 -112
  308. package/lib/rules/object-curly-newline.js +306 -247
  309. package/lib/rules/object-curly-spacing.js +360 -296
  310. package/lib/rules/object-property-newline.js +137 -88
  311. package/lib/rules/object-shorthand.js +632 -500
  312. package/lib/rules/one-var-declaration-per-line.js +104 -82
  313. package/lib/rules/one-var.js +686 -536
  314. package/lib/rules/operator-assignment.js +219 -158
  315. package/lib/rules/operator-linebreak.js +295 -233
  316. package/lib/rules/padded-blocks.js +346 -290
  317. package/lib/rules/padding-line-between-statements.js +443 -421
  318. package/lib/rules/prefer-arrow-callback.js +371 -315
  319. package/lib/rules/prefer-const.js +418 -373
  320. package/lib/rules/prefer-destructuring.js +309 -278
  321. package/lib/rules/prefer-exponentiation-operator.js +176 -132
  322. package/lib/rules/prefer-named-capture-group.js +160 -141
  323. package/lib/rules/prefer-numeric-literals.js +121 -112
  324. package/lib/rules/prefer-object-has-own.js +116 -82
  325. package/lib/rules/prefer-object-spread.js +214 -193
  326. package/lib/rules/prefer-promise-reject-errors.js +140 -118
  327. package/lib/rules/prefer-reflect.js +126 -103
  328. package/lib/rules/prefer-regex-literals.js +561 -463
  329. package/lib/rules/prefer-rest-params.js +79 -80
  330. package/lib/rules/prefer-spread.js +47 -43
  331. package/lib/rules/prefer-template.js +266 -194
  332. package/lib/rules/preserve-caught-error.js +535 -0
  333. package/lib/rules/quote-props.js +373 -289
  334. package/lib/rules/quotes.js +374 -308
  335. package/lib/rules/radix.js +152 -134
  336. package/lib/rules/require-atomic-updates.js +316 -282
  337. package/lib/rules/require-await.js +153 -82
  338. package/lib/rules/require-unicode-regexp.js +296 -108
  339. package/lib/rules/require-yield.js +53 -54
  340. package/lib/rules/rest-spread-spacing.js +128 -98
  341. package/lib/rules/semi-spacing.js +281 -232
  342. package/lib/rules/semi-style.js +176 -116
  343. package/lib/rules/semi.js +456 -418
  344. package/lib/rules/sort-imports.js +307 -229
  345. package/lib/rules/sort-keys.js +219 -181
  346. package/lib/rules/sort-vars.js +127 -91
  347. package/lib/rules/space-before-blocks.js +199 -171
  348. package/lib/rules/space-before-function-paren.js +186 -148
  349. package/lib/rules/space-in-parens.js +359 -270
  350. package/lib/rules/space-infix-ops.js +237 -183
  351. package/lib/rules/space-unary-ops.js +356 -280
  352. package/lib/rules/spaced-comment.js +363 -301
  353. package/lib/rules/strict.js +266 -229
  354. package/lib/rules/switch-colon-spacing.js +130 -104
  355. package/lib/rules/symbol-description.js +45 -48
  356. package/lib/rules/template-curly-spacing.js +148 -124
  357. package/lib/rules/template-tag-spacing.js +98 -70
  358. package/lib/rules/unicode-bom.js +54 -54
  359. package/lib/rules/use-isnan.js +237 -110
  360. package/lib/rules/utils/ast-utils.js +2139 -1688
  361. package/lib/rules/utils/char-source.js +247 -0
  362. package/lib/rules/utils/fix-tracker.js +99 -88
  363. package/lib/rules/utils/keywords.js +59 -59
  364. package/lib/rules/utils/lazy-loading-rule-map.js +81 -78
  365. package/lib/rules/utils/regular-expressions.js +35 -19
  366. package/lib/rules/utils/unicode/index.js +9 -4
  367. package/lib/rules/utils/unicode/is-combining-character.js +1 -1
  368. package/lib/rules/utils/unicode/is-emoji-modifier.js +1 -1
  369. package/lib/rules/utils/unicode/is-regional-indicator-symbol.js +1 -1
  370. package/lib/rules/utils/unicode/is-surrogate-pair.js +1 -1
  371. package/lib/rules/valid-typeof.js +153 -109
  372. package/lib/rules/vars-on-top.js +152 -144
  373. package/lib/rules/wrap-iife.js +204 -173
  374. package/lib/rules/wrap-regex.js +77 -47
  375. package/lib/rules/yield-star-spacing.js +145 -116
  376. package/lib/rules/yoda.js +283 -274
  377. package/lib/services/parser-service.js +65 -0
  378. package/lib/services/processor-service.js +101 -0
  379. package/lib/services/suppressions-service.js +302 -0
  380. package/lib/services/warning-service.js +98 -0
  381. package/lib/shared/ajv.js +14 -14
  382. package/lib/shared/assert.js +21 -0
  383. package/lib/shared/ast-utils.js +7 -6
  384. package/lib/shared/deep-merge-arrays.js +62 -0
  385. package/lib/shared/directives.js +3 -2
  386. package/lib/shared/flags.js +108 -0
  387. package/lib/shared/logging.js +24 -16
  388. package/lib/shared/naming.js +109 -0
  389. package/lib/shared/option-utils.js +63 -0
  390. package/lib/shared/relative-module-resolver.js +18 -40
  391. package/lib/shared/runtime-info.js +138 -128
  392. package/lib/shared/serialization.js +78 -0
  393. package/lib/shared/severity.js +22 -22
  394. package/lib/shared/stats.js +30 -0
  395. package/lib/shared/string-utils.js +19 -21
  396. package/lib/shared/text-table.js +68 -0
  397. package/lib/shared/translate-cli-options.js +281 -0
  398. package/lib/shared/traverser.js +153 -146
  399. package/lib/types/config-api.d.ts +12 -0
  400. package/lib/types/index.d.ts +1473 -0
  401. package/lib/types/rules.d.ts +5589 -0
  402. package/lib/types/universal.d.ts +6 -0
  403. package/lib/types/use-at-your-own-risk.d.ts +87 -0
  404. package/lib/universal.js +10 -0
  405. package/lib/unsupported-api.js +8 -9
  406. package/messages/all-files-ignored.js +3 -3
  407. package/messages/all-matched-files-ignored.js +21 -0
  408. package/messages/config-file-missing.js +16 -0
  409. package/messages/config-plugin-missing.js +14 -0
  410. package/messages/config-serialize-function.js +30 -0
  411. package/messages/eslintrc-incompat.js +35 -16
  412. package/messages/eslintrc-plugins.js +8 -5
  413. package/messages/extend-config-missing.js +3 -3
  414. package/messages/failed-to-read-json.js +3 -3
  415. package/messages/file-not-found.js +3 -3
  416. package/messages/invalid-rule-options.js +2 -2
  417. package/messages/invalid-rule-severity.js +2 -2
  418. package/messages/no-config-found.js +4 -4
  419. package/messages/plugin-conflict.js +9 -9
  420. package/messages/plugin-invalid.js +4 -4
  421. package/messages/plugin-missing.js +4 -4
  422. package/messages/print-config-with-directory-path.js +2 -2
  423. package/messages/shared.js +6 -1
  424. package/messages/whitespace-found.js +3 -3
  425. package/package.json +105 -60
  426. package/conf/config-schema.js +0 -93
  427. package/lib/cli-engine/formatters/checkstyle.js +0 -60
  428. package/lib/cli-engine/formatters/compact.js +0 -60
  429. package/lib/cli-engine/formatters/jslint-xml.js +0 -41
  430. package/lib/cli-engine/formatters/junit.js +0 -82
  431. package/lib/cli-engine/formatters/tap.js +0 -95
  432. package/lib/cli-engine/formatters/unix.js +0 -58
  433. package/lib/cli-engine/formatters/visualstudio.js +0 -63
  434. package/lib/cli-engine/xml-escape.js +0 -34
  435. package/lib/config/flat-config-helpers.js +0 -111
  436. package/lib/config/rule-validator.js +0 -158
  437. package/lib/eslint/flat-eslint.js +0 -1159
  438. package/lib/linter/config-comment-parser.js +0 -185
  439. package/lib/linter/node-event-generator.js +0 -354
  440. package/lib/linter/report-translator.js +0 -369
  441. package/lib/linter/safe-emitter.js +0 -52
  442. package/lib/rule-tester/flat-rule-tester.js +0 -1131
  443. package/lib/rules/require-jsdoc.js +0 -122
  444. package/lib/rules/utils/patterns/letters.js +0 -36
  445. package/lib/rules/valid-jsdoc.js +0 -516
  446. package/lib/shared/config-validator.js +0 -347
  447. package/lib/shared/deprecation-warnings.js +0 -58
  448. package/lib/shared/types.js +0 -216
  449. package/lib/source-code/index.js +0 -5
  450. package/lib/source-code/source-code.js +0 -1055
  451. package/lib/source-code/token-store/backward-token-comment-cursor.js +0 -57
  452. package/lib/source-code/token-store/backward-token-cursor.js +0 -58
  453. package/lib/source-code/token-store/cursors.js +0 -90
  454. package/lib/source-code/token-store/forward-token-comment-cursor.js +0 -57
  455. package/lib/source-code/token-store/forward-token-cursor.js +0 -63
  456. package/lib/source-code/token-store/index.js +0 -627
  457. package/lib/source-code/token-store/padded-token-cursor.js +0 -38
  458. package/lib/source-code/token-store/utils.js +0 -107
@@ -9,15 +9,19 @@
9
9
  // Requirements
10
10
  //-----------------------------------------------------------------------------
11
11
 
12
- const path = require("path");
13
- const fs = require("fs");
12
+ const path = require("node:path");
13
+ const fs = require("node:fs");
14
+ const { isMainThread, threadId } = require("node:worker_threads");
14
15
  const fsp = fs.promises;
15
16
  const isGlob = require("is-glob");
16
17
  const hash = require("../cli-engine/hash");
17
18
  const minimatch = require("minimatch");
18
- const fswalk = require("@nodelib/fs.walk");
19
19
  const globParent = require("glob-parent");
20
- const isPathInside = require("is-path-inside");
20
+ const { Linter } = require("../linter");
21
+ const { getShorthandName } = require("../shared/naming");
22
+ const LintResultCache = require("../cli-engine/lint-result-cache");
23
+ const { ConfigLoader, LegacyConfigLoader } = require("../config/config-loader");
24
+ const createDebug = require("debug");
21
25
 
22
26
  //-----------------------------------------------------------------------------
23
27
  // Fixup references
@@ -25,11 +29,25 @@ const isPathInside = require("is-path-inside");
25
29
 
26
30
  const Minimatch = minimatch.Minimatch;
27
31
  const MINIMATCH_OPTIONS = { dot: true };
32
+ const hrtimeBigint = process.hrtime.bigint;
28
33
 
29
34
  //-----------------------------------------------------------------------------
30
35
  // Types
31
36
  //-----------------------------------------------------------------------------
32
37
 
38
+ /**
39
+ * @import { ESLintOptions } from "./eslint.js";
40
+ * @import { Config as CalculatedConfig } from "../config/config.js";
41
+ * @import { FlatConfigArray } from "../config/flat-config-array.js";
42
+ * @import { WarningService } from "../services/warning-service.js";
43
+ * @import { Retrier } from "@humanwhocodes/retry";
44
+ */
45
+
46
+ /** @typedef {import("../types").Linter.Config} Config */
47
+ /** @typedef {import("../types").Linter.LintMessage} LintMessage */
48
+ /** @typedef {import("../types").ESLint.LintResult} LintResult */
49
+ /** @typedef {import("../types").ESLint.Plugin} Plugin */
50
+
33
51
  /**
34
52
  * @typedef {Object} GlobSearch
35
53
  * @property {Array<string>} patterns The normalized patterns to use for a search.
@@ -37,6 +55,18 @@ const MINIMATCH_OPTIONS = { dot: true };
37
55
  * before doing any normalization.
38
56
  */
39
57
 
58
+ //------------------------------------------------------------------------------
59
+ // Debug Helpers
60
+ //------------------------------------------------------------------------------
61
+
62
+ // Add %t formatter to print bigint nanosecond times in milliseconds.
63
+ createDebug.formatters.t = timeDiff =>
64
+ `${(timeDiff + 500_000n) / 1_000_000n} ms`;
65
+
66
+ const debug = createDebug(
67
+ `eslint:eslint-helpers${isMainThread ? "" : `:thread-${threadId}`}`,
68
+ );
69
+
40
70
  //-----------------------------------------------------------------------------
41
71
  // Errors
42
72
  //-----------------------------------------------------------------------------
@@ -45,78 +75,99 @@ const MINIMATCH_OPTIONS = { dot: true };
45
75
  * The error type when no files match a glob.
46
76
  */
47
77
  class NoFilesFoundError extends Error {
48
-
49
- /**
50
- * @param {string} pattern The glob pattern which was not found.
51
- * @param {boolean} globEnabled If `false` then the pattern was a glob pattern, but glob was disabled.
52
- */
53
- constructor(pattern, globEnabled) {
54
- super(`No files matching '${pattern}' were found${!globEnabled ? " (glob was disabled)" : ""}.`);
55
- this.messageTemplate = "file-not-found";
56
- this.messageData = { pattern, globDisabled: !globEnabled };
57
- }
78
+ /**
79
+ * @param {string} pattern The glob pattern which was not found.
80
+ * @param {boolean} globEnabled If `false` then the pattern was a glob pattern, but glob was disabled.
81
+ */
82
+ constructor(pattern, globEnabled) {
83
+ super(
84
+ `No files matching '${pattern}' were found${!globEnabled ? " (glob was disabled)" : ""}.`,
85
+ );
86
+ this.messageTemplate = "file-not-found";
87
+ this.messageData = { pattern, globDisabled: !globEnabled };
88
+ }
58
89
  }
59
90
 
60
91
  /**
61
92
  * The error type when a search fails to match multiple patterns.
62
93
  */
63
94
  class UnmatchedSearchPatternsError extends Error {
64
-
65
- /**
66
- * @param {Object} options The options for the error.
67
- * @param {string} options.basePath The directory that was searched.
68
- * @param {Array<string>} options.unmatchedPatterns The glob patterns
69
- * which were not found.
70
- * @param {Array<string>} options.patterns The glob patterns that were
71
- * searched.
72
- * @param {Array<string>} options.rawPatterns The raw glob patterns that
73
- * were searched.
74
- */
75
- constructor({ basePath, unmatchedPatterns, patterns, rawPatterns }) {
76
- super(`No files matching '${rawPatterns}' in '${basePath}' were found.`);
77
- this.basePath = basePath;
78
- this.unmatchedPatterns = unmatchedPatterns;
79
- this.patterns = patterns;
80
- this.rawPatterns = rawPatterns;
81
- }
95
+ /**
96
+ * @param {Object} options The options for the error.
97
+ * @param {string} options.basePath The directory that was searched.
98
+ * @param {Array<string>} options.unmatchedPatterns The glob patterns
99
+ * which were not found.
100
+ * @param {Array<string>} options.patterns The glob patterns that were
101
+ * searched.
102
+ * @param {Array<string>} options.rawPatterns The raw glob patterns that
103
+ * were searched.
104
+ */
105
+ constructor({ basePath, unmatchedPatterns, patterns, rawPatterns }) {
106
+ super(
107
+ `No files matching '${rawPatterns}' in '${basePath}' were found.`,
108
+ );
109
+ this.basePath = basePath;
110
+ this.unmatchedPatterns = unmatchedPatterns;
111
+ this.patterns = patterns;
112
+ this.rawPatterns = rawPatterns;
113
+ }
82
114
  }
83
115
 
84
116
  /**
85
117
  * The error type when there are files matched by a glob, but all of them have been ignored.
86
118
  */
87
119
  class AllFilesIgnoredError extends Error {
88
-
89
- /**
90
- * @param {string} pattern The glob pattern which was not found.
91
- */
92
- constructor(pattern) {
93
- super(`All files matched by '${pattern}' are ignored.`);
94
- this.messageTemplate = "all-files-ignored";
95
- this.messageData = { pattern };
96
- }
120
+ /**
121
+ * @param {string} pattern The glob pattern which was not found.
122
+ */
123
+ constructor(pattern) {
124
+ super(`All files matched by '${pattern}' are ignored.`);
125
+ this.messageTemplate = "all-matched-files-ignored";
126
+ this.messageData = { pattern };
127
+ }
97
128
  }
98
129
 
99
-
100
130
  //-----------------------------------------------------------------------------
101
131
  // General Helpers
102
132
  //-----------------------------------------------------------------------------
103
133
 
104
134
  /**
105
135
  * Check if a given value is a non-empty string or not.
106
- * @param {any} x The value to check.
107
- * @returns {boolean} `true` if `x` is a non-empty string.
136
+ * @param {any} value The value to check.
137
+ * @returns {boolean} `true` if `value` is a non-empty string.
108
138
  */
109
- function isNonEmptyString(x) {
110
- return typeof x === "string" && x.trim() !== "";
139
+ function isNonEmptyString(value) {
140
+ return typeof value === "string" && value.trim() !== "";
111
141
  }
112
142
 
113
143
  /**
114
144
  * Check if a given value is an array of non-empty strings or not.
115
- * @param {any} x The value to check.
116
- * @returns {boolean} `true` if `x` is an array of non-empty strings.
145
+ * @param {any} value The value to check.
146
+ * @returns {boolean} `true` if `value` is an array of non-empty strings.
147
+ */
148
+ function isArrayOfNonEmptyString(value) {
149
+ return (
150
+ Array.isArray(value) && !!value.length && value.every(isNonEmptyString)
151
+ );
152
+ }
153
+
154
+ /**
155
+ * Check if a given value is an empty array or an array of non-empty strings.
156
+ * @param {any} value The value to check.
157
+ * @returns {boolean} `true` if `value` is an empty array or an array of non-empty
158
+ * strings.
159
+ */
160
+ function isEmptyArrayOrArrayOfNonEmptyString(value) {
161
+ return Array.isArray(value) && value.every(isNonEmptyString);
162
+ }
163
+
164
+ /**
165
+ * Check if a given value is a positive integer.
166
+ * @param {unknown} value The value to check.
167
+ * @returns {boolean} `true` if `value` is a positive integer.
117
168
  */
118
- function isArrayOfNonEmptyString(x) {
119
- return Array.isArray(x) && x.every(isNonEmptyString);
169
+ function isPositiveInteger(value) {
170
+ return Number.isInteger(value) && value > 0;
120
171
  }
121
172
 
122
173
  //-----------------------------------------------------------------------------
@@ -129,7 +180,7 @@ function isArrayOfNonEmptyString(x) {
129
180
  * @returns {string} The pattern with slashes normalized.
130
181
  */
131
182
  function normalizeToPosix(pattern) {
132
- return pattern.replace(/\\/gu, "/");
183
+ return pattern.replace(/\\/gu, "/");
133
184
  }
134
185
 
135
186
  /**
@@ -138,70 +189,51 @@ function normalizeToPosix(pattern) {
138
189
  * @returns {boolean} `true` if the string is a glob pattern.
139
190
  */
140
191
  function isGlobPattern(pattern) {
141
- return isGlob(path.sep === "\\" ? normalizeToPosix(pattern) : pattern);
192
+ return isGlob(path.sep === "\\" ? normalizeToPosix(pattern) : pattern);
142
193
  }
143
194
 
144
-
145
195
  /**
146
196
  * Determines if a given glob pattern will return any results.
147
197
  * Used primarily to help with useful error messages.
148
198
  * @param {Object} options The options for the function.
149
199
  * @param {string} options.basePath The directory to search.
150
- * @param {string} options.pattern A glob pattern to match.
200
+ * @param {string} options.pattern An absolute path glob pattern to match.
151
201
  * @returns {Promise<boolean>} True if there is a glob match, false if not.
152
202
  */
153
- function globMatch({ basePath, pattern }) {
154
-
155
- let found = false;
156
- const patternToUse = path.isAbsolute(pattern)
157
- ? normalizeToPosix(path.relative(basePath, pattern))
158
- : pattern;
159
-
160
- const matcher = new Minimatch(patternToUse, MINIMATCH_OPTIONS);
161
-
162
- const fsWalkSettings = {
163
-
164
- deepFilter(entry) {
165
- const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
166
-
167
- return !found && matcher.match(relativePath, true);
168
- },
169
-
170
- entryFilter(entry) {
171
- if (found || entry.dirent.isDirectory()) {
172
- return false;
173
- }
174
-
175
- const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
176
-
177
- if (matcher.match(relativePath)) {
178
- found = true;
179
- return true;
180
- }
181
-
182
- return false;
183
- }
184
- };
185
-
186
- return new Promise(resolve => {
187
-
188
- // using a stream so we can exit early because we just need one match
189
- const globStream = fswalk.walkStream(basePath, fsWalkSettings);
190
-
191
- globStream.on("data", () => {
192
- globStream.destroy();
193
- resolve(true);
194
- });
195
-
196
- // swallow errors as they're not important here
197
- globStream.on("error", () => { });
198
-
199
- globStream.on("end", () => {
200
- resolve(false);
201
- });
202
- globStream.read();
203
- });
204
-
203
+ async function globMatch({ basePath, pattern }) {
204
+ let found = false;
205
+ const { hfs } = await import("@humanfs/node");
206
+ const patternToUse = normalizeToPosix(path.relative(basePath, pattern));
207
+
208
+ const matcher = new Minimatch(patternToUse, MINIMATCH_OPTIONS);
209
+
210
+ const walkSettings = {
211
+ directoryFilter(entry) {
212
+ return !found && matcher.match(entry.path, true);
213
+ },
214
+
215
+ entryFilter(entry) {
216
+ if (found || entry.isDirectory) {
217
+ return false;
218
+ }
219
+
220
+ if (matcher.match(entry.path)) {
221
+ found = true;
222
+ return true;
223
+ }
224
+
225
+ return false;
226
+ },
227
+ };
228
+
229
+ if (await hfs.isDirectory(basePath)) {
230
+ return hfs
231
+ .walk(basePath, walkSettings)
232
+ .next()
233
+ .then(() => found);
234
+ }
235
+
236
+ return found;
205
237
  }
206
238
 
207
239
  /**
@@ -211,11 +243,11 @@ function globMatch({ basePath, pattern }) {
211
243
  * ESLint.
212
244
  * @param {Object} options The options for this function.
213
245
  * @param {string} options.basePath The directory to search.
214
- * @param {Array<string>} options.patterns An array of glob patterns
246
+ * @param {Array<string>} options.patterns An array of absolute path glob patterns
215
247
  * to match.
216
248
  * @param {Array<string>} options.rawPatterns An array of glob patterns
217
249
  * as the user inputted them. Used for errors.
218
- * @param {FlatConfigArray} options.configs The config array to use for
250
+ * @param {ConfigLoader|LegacyConfigLoader} options.configLoader The config array to use for
219
251
  * determining what to ignore.
220
252
  * @param {boolean} options.errorOnUnmatchedPattern Determines if an error
221
253
  * should be thrown when a pattern is unmatched.
@@ -225,150 +257,128 @@ function globMatch({ basePath, pattern }) {
225
257
  * match any files.
226
258
  */
227
259
  async function globSearch({
228
- basePath,
229
- patterns,
230
- rawPatterns,
231
- configs,
232
- errorOnUnmatchedPattern
260
+ basePath,
261
+ patterns,
262
+ rawPatterns,
263
+ configLoader,
264
+ errorOnUnmatchedPattern,
233
265
  }) {
234
-
235
- if (patterns.length === 0) {
236
- return [];
237
- }
238
-
239
- /*
240
- * In this section we are converting the patterns into Minimatch
241
- * instances for performance reasons. Because we are doing the same
242
- * matches repeatedly, it's best to compile those patterns once and
243
- * reuse them multiple times.
244
- *
245
- * To do that, we convert any patterns with an absolute path into a
246
- * relative path and normalize it to Posix-style slashes. We also keep
247
- * track of the relative patterns to map them back to the original
248
- * patterns, which we need in order to throw an error if there are any
249
- * unmatched patterns.
250
- */
251
- const relativeToPatterns = new Map();
252
- const matchers = patterns.map((pattern, i) => {
253
- const patternToUse = path.isAbsolute(pattern)
254
- ? normalizeToPosix(path.relative(basePath, pattern))
255
- : pattern;
256
-
257
- relativeToPatterns.set(patternToUse, patterns[i]);
258
-
259
- return new Minimatch(patternToUse, MINIMATCH_OPTIONS);
260
- });
261
-
262
- /*
263
- * We track unmatched patterns because we may want to throw an error when
264
- * they occur. To start, this set is initialized with all of the patterns.
265
- * Every time a match occurs, the pattern is removed from the set, making
266
- * it easy to tell if we have any unmatched patterns left at the end of
267
- * search.
268
- */
269
- const unmatchedPatterns = new Set([...relativeToPatterns.keys()]);
270
-
271
- const filePaths = (await new Promise((resolve, reject) => {
272
-
273
- let promiseRejected = false;
274
-
275
- /**
276
- * Wraps a boolean-returning filter function. The wrapped function will reject the promise if an error occurs.
277
- * @param {Function} filter A filter function to wrap.
278
- * @returns {Function} A function similar to the wrapped filter that rejects the promise if an error occurs.
279
- */
280
- function wrapFilter(filter) {
281
- return (...args) => {
282
-
283
- // No need to run the filter if an error has been thrown.
284
- if (!promiseRejected) {
285
- try {
286
- return filter(...args);
287
- } catch (error) {
288
- promiseRejected = true;
289
- reject(error);
290
- }
291
- }
292
- return false;
293
- };
294
- }
295
-
296
- fswalk.walk(
297
- basePath,
298
- {
299
- deepFilter: wrapFilter(entry => {
300
- const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
301
- const matchesPattern = matchers.some(matcher => matcher.match(relativePath, true));
302
-
303
- return matchesPattern && !configs.isDirectoryIgnored(entry.path);
304
- }),
305
- entryFilter: wrapFilter(entry => {
306
- const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
307
-
308
- // entries may be directories or files so filter out directories
309
- if (entry.dirent.isDirectory()) {
310
- return false;
311
- }
312
-
313
- /*
314
- * Optimization: We need to track when patterns are left unmatched
315
- * and so we use `unmatchedPatterns` to do that. There is a bit of
316
- * complexity here because the same file can be matched by more than
317
- * one pattern. So, when we start, we actually need to test every
318
- * pattern against every file. Once we know there are no remaining
319
- * unmatched patterns, then we can switch to just looking for the
320
- * first matching pattern for improved speed.
321
- */
322
- const matchesPattern = unmatchedPatterns.size > 0
323
- ? matchers.reduce((previousValue, matcher) => {
324
- const pathMatches = matcher.match(relativePath);
325
-
326
- /*
327
- * We updated the unmatched patterns set only if the path
328
- * matches and the file isn't ignored. If the file is
329
- * ignored, that means there wasn't a match for the
330
- * pattern so it should not be removed.
331
- *
332
- * Performance note: isFileIgnored() aggressively caches
333
- * results so there is no performance penalty for calling
334
- * it twice with the same argument.
335
- */
336
- if (pathMatches && !configs.isFileIgnored(entry.path)) {
337
- unmatchedPatterns.delete(matcher.pattern);
338
- }
339
-
340
- return pathMatches || previousValue;
341
- }, false)
342
- : matchers.some(matcher => matcher.match(relativePath));
343
-
344
- return matchesPattern && !configs.isFileIgnored(entry.path);
345
- })
346
- },
347
- (error, entries) => {
348
-
349
- // If the promise is already rejected, calling `resolve` or `reject` will do nothing.
350
- if (error) {
351
- reject(error);
352
- } else {
353
- resolve(entries);
354
- }
355
- }
356
- );
357
- })).map(entry => entry.path);
358
-
359
- // now check to see if we have any unmatched patterns
360
- if (errorOnUnmatchedPattern && unmatchedPatterns.size > 0) {
361
- throw new UnmatchedSearchPatternsError({
362
- basePath,
363
- unmatchedPatterns: [...unmatchedPatterns].map(
364
- pattern => relativeToPatterns.get(pattern)
365
- ),
366
- patterns,
367
- rawPatterns
368
- });
369
- }
370
-
371
- return filePaths;
266
+ if (patterns.length === 0) {
267
+ return [];
268
+ }
269
+
270
+ /*
271
+ * In this section we are converting the patterns into Minimatch
272
+ * instances for performance reasons. Because we are doing the same
273
+ * matches repeatedly, it's best to compile those patterns once and
274
+ * reuse them multiple times.
275
+ *
276
+ * To do that, we convert any patterns with an absolute path into a
277
+ * relative path and normalize it to Posix-style slashes. We also keep
278
+ * track of the relative patterns to map them back to the original
279
+ * patterns, which we need in order to throw an error if there are any
280
+ * unmatched patterns.
281
+ */
282
+ const relativeToPatterns = new Map();
283
+ const matchers = patterns.map((pattern, i) => {
284
+ const patternToUse = normalizeToPosix(path.relative(basePath, pattern));
285
+
286
+ relativeToPatterns.set(patternToUse, patterns[i]);
287
+
288
+ return new Minimatch(patternToUse, MINIMATCH_OPTIONS);
289
+ });
290
+
291
+ /*
292
+ * We track unmatched patterns because we may want to throw an error when
293
+ * they occur. To start, this set is initialized with all of the patterns.
294
+ * Every time a match occurs, the pattern is removed from the set, making
295
+ * it easy to tell if we have any unmatched patterns left at the end of
296
+ * search.
297
+ */
298
+ const unmatchedPatterns = new Set([...relativeToPatterns.keys()]);
299
+ const { hfs } = await import("@humanfs/node");
300
+
301
+ const walk = hfs.walk(basePath, {
302
+ async directoryFilter(entry) {
303
+ if (!matchers.some(matcher => matcher.match(entry.path, true))) {
304
+ return false;
305
+ }
306
+
307
+ const absolutePath = path.resolve(basePath, entry.path);
308
+ const configs =
309
+ await configLoader.loadConfigArrayForDirectory(absolutePath);
310
+
311
+ return !configs.isDirectoryIgnored(absolutePath);
312
+ },
313
+ async entryFilter(entry) {
314
+ const absolutePath = path.resolve(basePath, entry.path);
315
+
316
+ // entries may be directories or files so filter out directories
317
+ if (entry.isDirectory) {
318
+ return false;
319
+ }
320
+
321
+ const configs =
322
+ await configLoader.loadConfigArrayForFile(absolutePath);
323
+ const config = configs.getConfig(absolutePath);
324
+
325
+ /*
326
+ * Optimization: We need to track when patterns are left unmatched
327
+ * and so we use `unmatchedPatterns` to do that. There is a bit of
328
+ * complexity here because the same file can be matched by more than
329
+ * one pattern. So, when we start, we actually need to test every
330
+ * pattern against every file. Once we know there are no remaining
331
+ * unmatched patterns, then we can switch to just looking for the
332
+ * first matching pattern for improved speed.
333
+ */
334
+ const matchesPattern =
335
+ unmatchedPatterns.size > 0
336
+ ? matchers.reduce((previousValue, matcher) => {
337
+ const pathMatches = matcher.match(entry.path);
338
+
339
+ /*
340
+ * We updated the unmatched patterns set only if the path
341
+ * matches and the file has a config. If the file has no
342
+ * config, that means there wasn't a match for the
343
+ * pattern so it should not be removed.
344
+ *
345
+ * Performance note: `getConfig()` aggressively caches
346
+ * results so there is no performance penalty for calling
347
+ * it multiple times with the same argument.
348
+ */
349
+ if (pathMatches && config) {
350
+ unmatchedPatterns.delete(matcher.pattern);
351
+ }
352
+
353
+ return pathMatches || previousValue;
354
+ }, false)
355
+ : matchers.some(matcher => matcher.match(entry.path));
356
+
357
+ return matchesPattern && config !== void 0;
358
+ },
359
+ });
360
+
361
+ const filePaths = [];
362
+
363
+ if (await hfs.isDirectory(basePath)) {
364
+ for await (const entry of walk) {
365
+ filePaths.push(path.resolve(basePath, entry.path));
366
+ }
367
+ }
368
+
369
+ // now check to see if we have any unmatched patterns
370
+ if (errorOnUnmatchedPattern && unmatchedPatterns.size > 0) {
371
+ throw new UnmatchedSearchPatternsError({
372
+ basePath,
373
+ unmatchedPatterns: [...unmatchedPatterns].map(pattern =>
374
+ relativeToPatterns.get(pattern),
375
+ ),
376
+ patterns,
377
+ rawPatterns,
378
+ });
379
+ }
380
+
381
+ return filePaths;
372
382
  }
373
383
 
374
384
  /**
@@ -380,112 +390,113 @@ async function globSearch({
380
390
  * that were used in the original search.
381
391
  * @param {Array<string>} options.rawPatterns An array of glob patterns
382
392
  * as the user inputted them. Used for errors.
383
- * @param {Array<string>} options.unmatchedPatterns A non-empty array of glob patterns
393
+ * @param {Array<string>} options.unmatchedPatterns A non-empty array of absolute path glob patterns
384
394
  * that were unmatched in the original search.
385
- * @returns {void} Always throws an error.
395
+ * @returns {Promise<never>} Always throws an error.
386
396
  * @throws {NoFilesFoundError} If the first unmatched pattern
387
397
  * doesn't match any files even when there are no ignores.
388
398
  * @throws {AllFilesIgnoredError} If the first unmatched pattern
389
399
  * matches some files when there are no ignores.
390
400
  */
391
401
  async function throwErrorForUnmatchedPatterns({
392
- basePath,
393
- patterns,
394
- rawPatterns,
395
- unmatchedPatterns
402
+ basePath,
403
+ patterns,
404
+ rawPatterns,
405
+ unmatchedPatterns,
396
406
  }) {
407
+ const pattern = unmatchedPatterns[0];
408
+ const rawPattern = rawPatterns[patterns.indexOf(pattern)];
397
409
 
398
- const pattern = unmatchedPatterns[0];
399
- const rawPattern = rawPatterns[patterns.indexOf(pattern)];
410
+ const patternHasMatch = await globMatch({
411
+ basePath,
412
+ pattern,
413
+ });
400
414
 
401
- const patternHasMatch = await globMatch({
402
- basePath,
403
- pattern
404
- });
415
+ if (patternHasMatch) {
416
+ throw new AllFilesIgnoredError(rawPattern);
417
+ }
405
418
 
406
- if (patternHasMatch) {
407
- throw new AllFilesIgnoredError(rawPattern);
408
- }
409
-
410
- // if we get here there are truly no matches
411
- throw new NoFilesFoundError(rawPattern, true);
419
+ // if we get here there are truly no matches
420
+ throw new NoFilesFoundError(rawPattern, true);
412
421
  }
413
422
 
414
423
  /**
415
424
  * Performs multiple glob searches in parallel.
416
425
  * @param {Object} options The options for this function.
417
426
  * @param {Map<string,GlobSearch>} options.searches
418
- * An array of glob patterns to match.
419
- * @param {FlatConfigArray} options.configs The config array to use for
427
+ * A map of absolute path glob patterns to match.
428
+ * @param {ConfigLoader|LegacyConfigLoader} options.configLoader The config loader to use for
420
429
  * determining what to ignore.
421
430
  * @param {boolean} options.errorOnUnmatchedPattern Determines if an
422
431
  * unmatched glob pattern should throw an error.
423
432
  * @returns {Promise<Array<string>>} An array of matching file paths
424
433
  * or an empty array if there are no matches.
425
434
  */
426
- async function globMultiSearch({ searches, configs, errorOnUnmatchedPattern }) {
427
-
428
- /*
429
- * For convenience, we normalized the search map into an array of objects.
430
- * Next, we filter out all searches that have no patterns. This happens
431
- * primarily for the cwd, which is prepopulated in the searches map as an
432
- * optimization. However, if it has no patterns, it means all patterns
433
- * occur outside of the cwd and we can safely filter out that search.
434
- */
435
- const normalizedSearches = [...searches].map(
436
- ([basePath, { patterns, rawPatterns }]) => ({ basePath, patterns, rawPatterns })
437
- ).filter(({ patterns }) => patterns.length > 0);
438
-
439
- const results = await Promise.allSettled(
440
- normalizedSearches.map(
441
- ({ basePath, patterns, rawPatterns }) => globSearch({
442
- basePath,
443
- patterns,
444
- rawPatterns,
445
- configs,
446
- errorOnUnmatchedPattern
447
- })
448
- )
449
- );
450
-
451
- const filePaths = [];
452
-
453
- for (let i = 0; i < results.length; i++) {
454
-
455
- const result = results[i];
456
- const currentSearch = normalizedSearches[i];
457
-
458
- if (result.status === "fulfilled") {
459
-
460
- // if the search was successful just add the results
461
- if (result.value.length > 0) {
462
- filePaths.push(...result.value);
463
- }
464
-
465
- continue;
466
- }
467
-
468
- // if we make it here then there was an error
469
- const error = result.reason;
470
-
471
- // unexpected errors should be re-thrown
472
- if (!error.basePath) {
473
- throw error;
474
- }
475
-
476
- if (errorOnUnmatchedPattern) {
477
-
478
- await throwErrorForUnmatchedPatterns({
479
- ...currentSearch,
480
- unmatchedPatterns: error.unmatchedPatterns
481
- });
482
-
483
- }
484
-
485
- }
486
-
487
- return filePaths;
488
-
435
+ async function globMultiSearch({
436
+ searches,
437
+ configLoader,
438
+ errorOnUnmatchedPattern,
439
+ }) {
440
+ /*
441
+ * For convenience, we normalized the search map into an array of objects.
442
+ * Next, we filter out all searches that have no patterns. This happens
443
+ * primarily for the cwd, which is prepopulated in the searches map as an
444
+ * optimization. However, if it has no patterns, it means all patterns
445
+ * occur outside of the cwd and we can safely filter out that search.
446
+ */
447
+ const normalizedSearches = [...searches]
448
+ .map(([basePath, { patterns, rawPatterns }]) => ({
449
+ basePath,
450
+ patterns,
451
+ rawPatterns,
452
+ }))
453
+ .filter(({ patterns }) => patterns.length > 0);
454
+
455
+ const results = await Promise.allSettled(
456
+ normalizedSearches.map(({ basePath, patterns, rawPatterns }) =>
457
+ globSearch({
458
+ basePath,
459
+ patterns,
460
+ rawPatterns,
461
+ configLoader,
462
+ errorOnUnmatchedPattern,
463
+ }),
464
+ ),
465
+ );
466
+
467
+ /*
468
+ * The first loop handles errors from the glob searches. Since we can't
469
+ * use `await` inside `flatMap`, we process errors separately in this loop.
470
+ * This results in two iterations over `results`, but since the length is
471
+ * less than or equal to the number of globs and directories passed on the
472
+ * command line, the performance impact should be minimal.
473
+ */
474
+ for (let i = 0; i < results.length; i++) {
475
+ const result = results[i];
476
+ const currentSearch = normalizedSearches[i];
477
+
478
+ if (result.status === "fulfilled") {
479
+ continue;
480
+ }
481
+
482
+ // if we make it here then there was an error
483
+ const error = result.reason;
484
+
485
+ // unexpected errors should be re-thrown
486
+ if (!error.basePath) {
487
+ throw error;
488
+ }
489
+
490
+ if (errorOnUnmatchedPattern) {
491
+ await throwErrorForUnmatchedPatterns({
492
+ ...currentSearch,
493
+ unmatchedPatterns: error.unmatchedPatterns,
494
+ });
495
+ }
496
+ }
497
+
498
+ // second loop for `fulfilled` results
499
+ return results.flatMap(result => result.value);
489
500
  }
490
501
 
491
502
  /**
@@ -495,7 +506,7 @@ async function globMultiSearch({ searches, configs, errorOnUnmatchedPattern }) {
495
506
  * @param {boolean} args.globInputPaths true to interpret glob patterns,
496
507
  * false to not interpret glob patterns.
497
508
  * @param {string} args.cwd The current working directory to find from.
498
- * @param {FlatConfigArray} args.configs The configs for the current run.
509
+ * @param {ConfigLoader|LegacyConfigLoader} args.configLoader The config loader for the current run.
499
510
  * @param {boolean} args.errorOnUnmatchedPattern Determines if an unmatched pattern
500
511
  * should throw an error.
501
512
  * @returns {Promise<Array<string>>} The fully resolved file paths.
@@ -503,99 +514,137 @@ async function globMultiSearch({ searches, configs, errorOnUnmatchedPattern }) {
503
514
  * @throws {NoFilesFoundError} If no files matched the given patterns.
504
515
  */
505
516
  async function findFiles({
506
- patterns,
507
- globInputPaths,
508
- cwd,
509
- configs,
510
- errorOnUnmatchedPattern
517
+ patterns,
518
+ globInputPaths,
519
+ cwd,
520
+ configLoader,
521
+ errorOnUnmatchedPattern,
511
522
  }) {
523
+ const results = [];
524
+ const missingPatterns = [];
525
+ let globbyPatterns = [];
526
+ let rawPatterns = [];
527
+ const searches = new Map([
528
+ [cwd, { patterns: globbyPatterns, rawPatterns: [] }],
529
+ ]);
530
+
531
+ /*
532
+ * This part is a bit involved because we need to account for
533
+ * the different ways that the patterns can match directories.
534
+ * For each different way, we need to decide if we should look
535
+ * for a config file or just use the default config. (Directories
536
+ * without a config file always use the default config.)
537
+ *
538
+ * Here are the cases:
539
+ *
540
+ * 1. A directory is passed directly (e.g., "subdir"). In this case, we
541
+ * can assume that the user intends to lint this directory and we should
542
+ * not look for a config file in the parent directory, because the only
543
+ * reason to do that would be to ignore this directory (which we already
544
+ * know we don't want to do). Instead, we use the default config until we
545
+ * get to the directory that was passed, at which point we start looking
546
+ * for config files again.
547
+ *
548
+ * 2. A dot (".") or star ("*"). In this case, we want to read
549
+ * the config file in the current directory because the user is
550
+ * explicitly asking to lint the current directory. Note that "."
551
+ * will traverse into subdirectories while "*" will not.
552
+ *
553
+ * 3. A directory is passed in the form of "subdir/subsubdir".
554
+ * In this case, we don't want to look for a config file in the
555
+ * parent directory ("subdir"). We can skip looking for a config
556
+ * file until `entry.depth` is greater than 1 because there's no
557
+ * way that the pattern can match `entry.path` yet.
558
+ *
559
+ * 4. A directory glob pattern is passed (e.g., "subd*"). We want
560
+ * this case to act like case 2 because it's unclear whether or not
561
+ * any particular directory is meant to be traversed.
562
+ *
563
+ * 5. A recursive glob pattern is passed (e.g., "**"). We want this
564
+ * case to act like case 2.
565
+ */
566
+
567
+ // check to see if we have explicit files and directories
568
+ const filePaths = patterns.map(filePath => path.resolve(cwd, filePath));
569
+ const stats = await Promise.all(
570
+ filePaths.map(filePath => fsp.stat(filePath).catch(() => {})),
571
+ );
572
+
573
+ const promises = [];
574
+ stats.forEach((stat, index) => {
575
+ const filePath = filePaths[index];
576
+ const pattern = normalizeToPosix(patterns[index]);
577
+
578
+ if (stat) {
579
+ // files are added directly to the list
580
+ if (stat.isFile()) {
581
+ results.push(filePath);
582
+ promises.push(configLoader.loadConfigArrayForFile(filePath));
583
+ }
584
+
585
+ // directories need extensions attached
586
+ if (stat.isDirectory()) {
587
+ if (!searches.has(filePath)) {
588
+ searches.set(filePath, { patterns: [], rawPatterns: [] });
589
+ }
590
+ ({ patterns: globbyPatterns, rawPatterns } =
591
+ searches.get(filePath));
592
+
593
+ globbyPatterns.push(`${normalizeToPosix(filePath)}/**`);
594
+ rawPatterns.push(pattern);
595
+ }
596
+
597
+ return;
598
+ }
599
+
600
+ // save patterns for later use based on whether globs are enabled
601
+ if (globInputPaths && isGlobPattern(pattern)) {
602
+ /*
603
+ * We are grouping patterns by their glob parent. This is done to
604
+ * make it easier to determine when a config file should be loaded.
605
+ */
606
+
607
+ const basePath = path.resolve(cwd, globParent(pattern));
608
+
609
+ if (!searches.has(basePath)) {
610
+ searches.set(basePath, { patterns: [], rawPatterns: [] });
611
+ }
612
+ ({ patterns: globbyPatterns, rawPatterns } =
613
+ searches.get(basePath));
614
+
615
+ globbyPatterns.push(filePath);
616
+ rawPatterns.push(pattern);
617
+ } else {
618
+ missingPatterns.push(pattern);
619
+ }
620
+ });
621
+
622
+ // there were patterns that didn't match anything, tell the user
623
+ if (errorOnUnmatchedPattern && missingPatterns.length) {
624
+ throw new NoFilesFoundError(missingPatterns[0], globInputPaths);
625
+ }
626
+
627
+ // now we are safe to do the search
628
+ promises.push(
629
+ globMultiSearch({
630
+ searches,
631
+ configLoader,
632
+ errorOnUnmatchedPattern,
633
+ }),
634
+ );
635
+ const globbyResults = (await Promise.all(promises)).at(-1);
636
+
637
+ return [...new Set([...results, ...globbyResults])];
638
+ }
512
639
 
513
- const results = [];
514
- const missingPatterns = [];
515
- let globbyPatterns = [];
516
- let rawPatterns = [];
517
- const searches = new Map([[cwd, { patterns: globbyPatterns, rawPatterns: [] }]]);
518
-
519
- // check to see if we have explicit files and directories
520
- const filePaths = patterns.map(filePath => path.resolve(cwd, filePath));
521
- const stats = await Promise.all(
522
- filePaths.map(
523
- filePath => fsp.stat(filePath).catch(() => { })
524
- )
525
- );
526
-
527
- stats.forEach((stat, index) => {
528
-
529
- const filePath = filePaths[index];
530
- const pattern = normalizeToPosix(patterns[index]);
531
-
532
- if (stat) {
533
-
534
- // files are added directly to the list
535
- if (stat.isFile()) {
536
- results.push(filePath);
537
- }
538
-
539
- // directories need extensions attached
540
- if (stat.isDirectory()) {
541
-
542
- // group everything in cwd together and split out others
543
- if (isPathInside(filePath, cwd)) {
544
- ({ patterns: globbyPatterns, rawPatterns } = searches.get(cwd));
545
- } else {
546
- if (!searches.has(filePath)) {
547
- searches.set(filePath, { patterns: [], rawPatterns: [] });
548
- }
549
- ({ patterns: globbyPatterns, rawPatterns } = searches.get(filePath));
550
- }
551
-
552
- globbyPatterns.push(`${normalizeToPosix(filePath)}/**`);
553
- rawPatterns.push(pattern);
554
- }
555
-
556
- return;
557
- }
558
-
559
- // save patterns for later use based on whether globs are enabled
560
- if (globInputPaths && isGlobPattern(pattern)) {
561
-
562
- const basePath = path.resolve(cwd, globParent(pattern));
563
-
564
- // group in cwd if possible and split out others
565
- if (isPathInside(basePath, cwd)) {
566
- ({ patterns: globbyPatterns, rawPatterns } = searches.get(cwd));
567
- } else {
568
- if (!searches.has(basePath)) {
569
- searches.set(basePath, { patterns: [], rawPatterns: [] });
570
- }
571
- ({ patterns: globbyPatterns, rawPatterns } = searches.get(basePath));
572
- }
573
-
574
- globbyPatterns.push(filePath);
575
- rawPatterns.push(pattern);
576
- } else {
577
- missingPatterns.push(pattern);
578
- }
579
- });
580
-
581
- // there were patterns that didn't match anything, tell the user
582
- if (errorOnUnmatchedPattern && missingPatterns.length) {
583
- throw new NoFilesFoundError(missingPatterns[0], globInputPaths);
584
- }
585
-
586
- // now we are safe to do the search
587
- const globbyResults = await globMultiSearch({
588
- searches,
589
- configs,
590
- errorOnUnmatchedPattern
591
- });
592
-
593
- return [
594
- ...new Set([
595
- ...results,
596
- ...globbyResults.map(filePath => path.resolve(filePath))
597
- ])
598
- ];
640
+ /**
641
+ * Return the absolute path of a file named `"__placeholder__.js"` in a given directory.
642
+ * This is used as a replacement for a missing file path.
643
+ * @param {string} cwd An absolute directory path.
644
+ * @returns {string} The absolute path of a file named `"__placeholder__.js"` in the given directory.
645
+ */
646
+ function getPlaceholderPath(cwd) {
647
+ return path.join(cwd, "__placeholder__.js");
599
648
  }
600
649
 
601
650
  //-----------------------------------------------------------------------------
@@ -609,58 +658,120 @@ async function findFiles({
609
658
  * @private
610
659
  */
611
660
  function isErrorMessage(message) {
612
- return message.severity === 2;
661
+ return message.severity === 2;
613
662
  }
614
663
 
615
664
  /**
616
665
  * Returns result with warning by ignore settings
617
- * @param {string} filePath File path of checked code
666
+ * @param {string} filePath Absolute file path of checked code
618
667
  * @param {string} baseDir Absolute path of base directory
668
+ * @param {"ignored"|"external"|"unconfigured"} configStatus A status that determines why the file is ignored
619
669
  * @returns {LintResult} Result with single warning
620
670
  * @private
621
671
  */
622
- function createIgnoreResult(filePath, baseDir) {
623
- let message;
624
- const isInNodeModules = baseDir && path.dirname(path.relative(baseDir, filePath)).split(path.sep).includes("node_modules");
625
-
626
- if (isInNodeModules) {
627
- message = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
628
- } else {
629
- message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
630
- }
631
-
632
- return {
633
- filePath: path.resolve(filePath),
634
- messages: [
635
- {
636
- ruleId: null,
637
- fatal: false,
638
- severity: 1,
639
- message,
640
- nodeType: null
641
- }
642
- ],
643
- suppressedMessages: [],
644
- errorCount: 0,
645
- warningCount: 1,
646
- fatalErrorCount: 0,
647
- fixableErrorCount: 0,
648
- fixableWarningCount: 0
649
- };
672
+ function createIgnoreResult(filePath, baseDir, configStatus) {
673
+ let message;
674
+
675
+ switch (configStatus) {
676
+ case "external":
677
+ message = "File ignored because outside of base path.";
678
+ break;
679
+ case "unconfigured":
680
+ message =
681
+ "File ignored because no matching configuration was supplied.";
682
+ break;
683
+ default:
684
+ {
685
+ const isInNodeModules =
686
+ baseDir &&
687
+ path
688
+ .dirname(path.relative(baseDir, filePath))
689
+ .split(path.sep)
690
+ .includes("node_modules");
691
+
692
+ if (isInNodeModules) {
693
+ message =
694
+ 'File ignored by default because it is located under the node_modules directory. Use ignore pattern "!**/node_modules/" to disable file ignore settings or use "--no-warn-ignored" to suppress this warning.';
695
+ } else {
696
+ message =
697
+ 'File ignored because of a matching ignore pattern. Use "--no-ignore" to disable file ignore settings or use "--no-warn-ignored" to suppress this warning.';
698
+ }
699
+ }
700
+ break;
701
+ }
702
+
703
+ return {
704
+ filePath,
705
+ messages: [
706
+ {
707
+ ruleId: null,
708
+ fatal: false,
709
+ severity: 1,
710
+ message,
711
+ nodeType: null,
712
+ },
713
+ ],
714
+ suppressedMessages: [],
715
+ errorCount: 0,
716
+ warningCount: 1,
717
+ fatalErrorCount: 0,
718
+ fixableErrorCount: 0,
719
+ fixableWarningCount: 0,
720
+ };
721
+ }
722
+
723
+ /**
724
+ * It will calculate the error and warning count for collection of messages per file
725
+ * @param {LintMessage[]} messages Collection of messages
726
+ * @returns {Object} Contains the stats
727
+ * @private
728
+ */
729
+ function calculateStatsPerFile(messages) {
730
+ const stat = {
731
+ errorCount: 0,
732
+ fatalErrorCount: 0,
733
+ warningCount: 0,
734
+ fixableErrorCount: 0,
735
+ fixableWarningCount: 0,
736
+ };
737
+
738
+ for (let i = 0; i < messages.length; i++) {
739
+ const message = messages[i];
740
+
741
+ if (message.fatal || message.severity === 2) {
742
+ stat.errorCount++;
743
+ if (message.fatal) {
744
+ stat.fatalErrorCount++;
745
+ }
746
+ if (message.fix) {
747
+ stat.fixableErrorCount++;
748
+ }
749
+ } else {
750
+ stat.warningCount++;
751
+ if (message.fix) {
752
+ stat.fixableWarningCount++;
753
+ }
754
+ }
755
+ }
756
+ return stat;
650
757
  }
651
758
 
652
759
  //-----------------------------------------------------------------------------
653
760
  // Options-related Helpers
654
761
  //-----------------------------------------------------------------------------
655
762
 
656
-
657
763
  /**
658
764
  * Check if a given value is a valid fix type or not.
659
765
  * @param {any} x The value to check.
660
766
  * @returns {boolean} `true` if `x` is valid fix type.
661
767
  */
662
768
  function isFixType(x) {
663
- return x === "directive" || x === "problem" || x === "suggestion" || x === "layout";
769
+ return (
770
+ x === "directive" ||
771
+ x === "problem" ||
772
+ x === "suggestion" ||
773
+ x === "layout"
774
+ );
664
775
  }
665
776
 
666
777
  /**
@@ -669,171 +780,240 @@ function isFixType(x) {
669
780
  * @returns {boolean} `true` if `x` is an array of fix types.
670
781
  */
671
782
  function isFixTypeArray(x) {
672
- return Array.isArray(x) && x.every(isFixType);
783
+ return Array.isArray(x) && x.every(isFixType);
673
784
  }
674
785
 
675
786
  /**
676
787
  * The error for invalid options.
677
788
  */
678
789
  class ESLintInvalidOptionsError extends Error {
679
- constructor(messages) {
680
- super(`Invalid Options:\n- ${messages.join("\n- ")}`);
681
- this.code = "ESLINT_INVALID_OPTIONS";
682
- Error.captureStackTrace(this, ESLintInvalidOptionsError);
683
- }
790
+ constructor(messages) {
791
+ super(`Invalid Options:\n- ${messages.join("\n- ")}`);
792
+ this.code = "ESLINT_INVALID_OPTIONS";
793
+ Error.captureStackTrace(this, ESLintInvalidOptionsError);
794
+ }
684
795
  }
685
796
 
686
797
  /**
687
798
  * Validates and normalizes options for the wrapped CLIEngine instance.
688
- * @param {FlatESLintOptions} options The options to process.
799
+ * @param {ESLintOptions} options The options to process.
689
800
  * @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
690
- * @returns {FlatESLintOptions} The normalized options.
801
+ * @returns {ESLintOptions} The normalized options.
691
802
  */
692
803
  function processOptions({
693
- allowInlineConfig = true, // ← we cannot use `overrideConfig.noInlineConfig` instead because `allowInlineConfig` has side-effect that suppress warnings that show inline configs are ignored.
694
- baseConfig = null,
695
- cache = false,
696
- cacheLocation = ".eslintcache",
697
- cacheStrategy = "metadata",
698
- cwd = process.cwd(),
699
- errorOnUnmatchedPattern = true,
700
- fix = false,
701
- fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
702
- globInputPaths = true,
703
- ignore = true,
704
- ignorePatterns = null,
705
- overrideConfig = null,
706
- overrideConfigFile = null,
707
- plugins = {},
708
- warnIgnored = true,
709
- ...unknownOptions
804
+ allowInlineConfig = true, // ← we cannot use `overrideConfig.noInlineConfig` instead because `allowInlineConfig` has side-effect that suppress warnings that show inline configs are ignored.
805
+ baseConfig = null,
806
+ cache = false,
807
+ cacheLocation = ".eslintcache",
808
+ cacheStrategy = "metadata",
809
+ concurrency = "off",
810
+ cwd = process.cwd(),
811
+ errorOnUnmatchedPattern = true,
812
+ fix = false,
813
+ fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
814
+ flags = [],
815
+ globInputPaths = true,
816
+ ignore = true,
817
+ ignorePatterns = null,
818
+ overrideConfig = null,
819
+ overrideConfigFile = null,
820
+ plugins = {},
821
+ stats = false,
822
+ warnIgnored = true,
823
+ passOnNoPatterns = false,
824
+ ruleFilter = () => true,
825
+ ...unknownOptions
710
826
  }) {
711
- const errors = [];
712
- const unknownOptionKeys = Object.keys(unknownOptions);
713
-
714
- if (unknownOptionKeys.length >= 1) {
715
- errors.push(`Unknown options: ${unknownOptionKeys.join(", ")}`);
716
- if (unknownOptionKeys.includes("cacheFile")) {
717
- errors.push("'cacheFile' has been removed. Please use the 'cacheLocation' option instead.");
718
- }
719
- if (unknownOptionKeys.includes("configFile")) {
720
- errors.push("'configFile' has been removed. Please use the 'overrideConfigFile' option instead.");
721
- }
722
- if (unknownOptionKeys.includes("envs")) {
723
- errors.push("'envs' has been removed.");
724
- }
725
- if (unknownOptionKeys.includes("extensions")) {
726
- errors.push("'extensions' has been removed.");
727
- }
728
- if (unknownOptionKeys.includes("resolvePluginsRelativeTo")) {
729
- errors.push("'resolvePluginsRelativeTo' has been removed.");
730
- }
731
- if (unknownOptionKeys.includes("globals")) {
732
- errors.push("'globals' has been removed. Please use the 'overrideConfig.languageOptions.globals' option instead.");
733
- }
734
- if (unknownOptionKeys.includes("ignorePath")) {
735
- errors.push("'ignorePath' has been removed.");
736
- }
737
- if (unknownOptionKeys.includes("ignorePattern")) {
738
- errors.push("'ignorePattern' has been removed. Please use the 'overrideConfig.ignorePatterns' option instead.");
739
- }
740
- if (unknownOptionKeys.includes("parser")) {
741
- errors.push("'parser' has been removed. Please use the 'overrideConfig.languageOptions.parser' option instead.");
742
- }
743
- if (unknownOptionKeys.includes("parserOptions")) {
744
- errors.push("'parserOptions' has been removed. Please use the 'overrideConfig.languageOptions.parserOptions' option instead.");
745
- }
746
- if (unknownOptionKeys.includes("rules")) {
747
- errors.push("'rules' has been removed. Please use the 'overrideConfig.rules' option instead.");
748
- }
749
- if (unknownOptionKeys.includes("rulePaths")) {
750
- errors.push("'rulePaths' has been removed. Please define your rules using plugins.");
751
- }
752
- if (unknownOptionKeys.includes("reportUnusedDisableDirectives")) {
753
- errors.push("'reportUnusedDisableDirectives' has been removed. Please use the 'overrideConfig.linterOptions.reportUnusedDisableDirectives' option instead.");
754
- }
755
- }
756
- if (typeof allowInlineConfig !== "boolean") {
757
- errors.push("'allowInlineConfig' must be a boolean.");
758
- }
759
- if (typeof baseConfig !== "object") {
760
- errors.push("'baseConfig' must be an object or null.");
761
- }
762
- if (typeof cache !== "boolean") {
763
- errors.push("'cache' must be a boolean.");
764
- }
765
- if (!isNonEmptyString(cacheLocation)) {
766
- errors.push("'cacheLocation' must be a non-empty string.");
767
- }
768
- if (
769
- cacheStrategy !== "metadata" &&
770
- cacheStrategy !== "content"
771
- ) {
772
- errors.push("'cacheStrategy' must be any of \"metadata\", \"content\".");
773
- }
774
- if (!isNonEmptyString(cwd) || !path.isAbsolute(cwd)) {
775
- errors.push("'cwd' must be an absolute path.");
776
- }
777
- if (typeof errorOnUnmatchedPattern !== "boolean") {
778
- errors.push("'errorOnUnmatchedPattern' must be a boolean.");
779
- }
780
- if (typeof fix !== "boolean" && typeof fix !== "function") {
781
- errors.push("'fix' must be a boolean or a function.");
782
- }
783
- if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
784
- errors.push("'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".");
785
- }
786
- if (typeof globInputPaths !== "boolean") {
787
- errors.push("'globInputPaths' must be a boolean.");
788
- }
789
- if (typeof ignore !== "boolean") {
790
- errors.push("'ignore' must be a boolean.");
791
- }
792
- if (!isArrayOfNonEmptyString(ignorePatterns) && ignorePatterns !== null) {
793
- errors.push("'ignorePatterns' must be an array of non-empty strings or null.");
794
- }
795
- if (typeof overrideConfig !== "object") {
796
- errors.push("'overrideConfig' must be an object or null.");
797
- }
798
- if (!isNonEmptyString(overrideConfigFile) && overrideConfigFile !== null && overrideConfigFile !== true) {
799
- errors.push("'overrideConfigFile' must be a non-empty string, null, or true.");
800
- }
801
- if (typeof plugins !== "object") {
802
- errors.push("'plugins' must be an object or null.");
803
- } else if (plugins !== null && Object.keys(plugins).includes("")) {
804
- errors.push("'plugins' must not include an empty string.");
805
- }
806
- if (Array.isArray(plugins)) {
807
- errors.push("'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead.");
808
- }
809
- if (typeof warnIgnored !== "boolean") {
810
- errors.push("'warnIgnored' must be a boolean.");
811
- }
812
- if (errors.length > 0) {
813
- throw new ESLintInvalidOptionsError(errors);
814
- }
815
-
816
- return {
817
- allowInlineConfig,
818
- baseConfig,
819
- cache,
820
- cacheLocation,
821
- cacheStrategy,
822
-
823
- // when overrideConfigFile is true that means don't do config file lookup
824
- configFile: overrideConfigFile === true ? false : overrideConfigFile,
825
- overrideConfig,
826
- cwd: path.normalize(cwd),
827
- errorOnUnmatchedPattern,
828
- fix,
829
- fixTypes,
830
- globInputPaths,
831
- ignore,
832
- ignorePatterns,
833
- warnIgnored
834
- };
827
+ const errors = [];
828
+ const unknownOptionKeys = Object.keys(unknownOptions);
829
+
830
+ if (unknownOptionKeys.length >= 1) {
831
+ errors.push(`Unknown options: ${unknownOptionKeys.join(", ")}`);
832
+ if (unknownOptionKeys.includes("cacheFile")) {
833
+ errors.push(
834
+ "'cacheFile' has been removed. Please use the 'cacheLocation' option instead.",
835
+ );
836
+ }
837
+ if (unknownOptionKeys.includes("configFile")) {
838
+ errors.push(
839
+ "'configFile' has been removed. Please use the 'overrideConfigFile' option instead.",
840
+ );
841
+ }
842
+ if (unknownOptionKeys.includes("envs")) {
843
+ errors.push("'envs' has been removed.");
844
+ }
845
+ if (unknownOptionKeys.includes("extensions")) {
846
+ errors.push("'extensions' has been removed.");
847
+ }
848
+ if (unknownOptionKeys.includes("resolvePluginsRelativeTo")) {
849
+ errors.push("'resolvePluginsRelativeTo' has been removed.");
850
+ }
851
+ if (unknownOptionKeys.includes("globals")) {
852
+ errors.push(
853
+ "'globals' has been removed. Please use the 'overrideConfig.languageOptions.globals' option instead.",
854
+ );
855
+ }
856
+ if (unknownOptionKeys.includes("ignorePath")) {
857
+ errors.push("'ignorePath' has been removed.");
858
+ }
859
+ if (unknownOptionKeys.includes("ignorePattern")) {
860
+ errors.push(
861
+ "'ignorePattern' has been removed. Please use the 'overrideConfig.ignorePatterns' option instead.",
862
+ );
863
+ }
864
+ if (unknownOptionKeys.includes("parser")) {
865
+ errors.push(
866
+ "'parser' has been removed. Please use the 'overrideConfig.languageOptions.parser' option instead.",
867
+ );
868
+ }
869
+ if (unknownOptionKeys.includes("parserOptions")) {
870
+ errors.push(
871
+ "'parserOptions' has been removed. Please use the 'overrideConfig.languageOptions.parserOptions' option instead.",
872
+ );
873
+ }
874
+ if (unknownOptionKeys.includes("rules")) {
875
+ errors.push(
876
+ "'rules' has been removed. Please use the 'overrideConfig.rules' option instead.",
877
+ );
878
+ }
879
+ if (unknownOptionKeys.includes("rulePaths")) {
880
+ errors.push(
881
+ "'rulePaths' has been removed. Please define your rules using plugins.",
882
+ );
883
+ }
884
+ if (unknownOptionKeys.includes("reportUnusedDisableDirectives")) {
885
+ errors.push(
886
+ "'reportUnusedDisableDirectives' has been removed. Please use the 'overrideConfig.linterOptions.reportUnusedDisableDirectives' option instead.",
887
+ );
888
+ }
889
+ }
890
+ if (typeof allowInlineConfig !== "boolean") {
891
+ errors.push("'allowInlineConfig' must be a boolean.");
892
+ }
893
+ if (typeof baseConfig !== "object") {
894
+ errors.push("'baseConfig' must be an object or null.");
895
+ }
896
+ if (typeof cache !== "boolean") {
897
+ errors.push("'cache' must be a boolean.");
898
+ }
899
+ if (!isNonEmptyString(cacheLocation)) {
900
+ errors.push("'cacheLocation' must be a non-empty string.");
901
+ }
902
+ if (cacheStrategy !== "metadata" && cacheStrategy !== "content") {
903
+ errors.push('\'cacheStrategy\' must be any of "metadata", "content".');
904
+ }
905
+ if (
906
+ concurrency !== "off" &&
907
+ concurrency !== "auto" &&
908
+ !isPositiveInteger(concurrency)
909
+ ) {
910
+ errors.push(
911
+ '\'concurrency\' must be a positive integer, "auto", or "off".',
912
+ );
913
+ }
914
+ if (!isNonEmptyString(cwd) || !path.isAbsolute(cwd)) {
915
+ errors.push("'cwd' must be an absolute path.");
916
+ }
917
+ if (typeof errorOnUnmatchedPattern !== "boolean") {
918
+ errors.push("'errorOnUnmatchedPattern' must be a boolean.");
919
+ }
920
+ if (typeof fix !== "boolean" && typeof fix !== "function") {
921
+ errors.push("'fix' must be a boolean or a function.");
922
+ }
923
+ if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
924
+ errors.push(
925
+ '\'fixTypes\' must be an array of any of "directive", "problem", "suggestion", and "layout".',
926
+ );
927
+ }
928
+ if (!isEmptyArrayOrArrayOfNonEmptyString(flags)) {
929
+ errors.push("'flags' must be an array of non-empty strings.");
930
+ }
931
+ if (typeof globInputPaths !== "boolean") {
932
+ errors.push("'globInputPaths' must be a boolean.");
933
+ }
934
+ if (typeof ignore !== "boolean") {
935
+ errors.push("'ignore' must be a boolean.");
936
+ }
937
+ if (
938
+ !isEmptyArrayOrArrayOfNonEmptyString(ignorePatterns) &&
939
+ ignorePatterns !== null
940
+ ) {
941
+ errors.push(
942
+ "'ignorePatterns' must be an array of non-empty strings or null.",
943
+ );
944
+ }
945
+ if (typeof overrideConfig !== "object") {
946
+ errors.push("'overrideConfig' must be an object or null.");
947
+ }
948
+ if (
949
+ !isNonEmptyString(overrideConfigFile) &&
950
+ overrideConfigFile !== null &&
951
+ overrideConfigFile !== true
952
+ ) {
953
+ errors.push(
954
+ "'overrideConfigFile' must be a non-empty string, null, or true.",
955
+ );
956
+ }
957
+ if (typeof passOnNoPatterns !== "boolean") {
958
+ errors.push("'passOnNoPatterns' must be a boolean.");
959
+ }
960
+ if (typeof plugins !== "object") {
961
+ errors.push("'plugins' must be an object or null.");
962
+ } else if (plugins !== null && Object.keys(plugins).includes("")) {
963
+ errors.push("'plugins' must not include an empty string.");
964
+ }
965
+ if (Array.isArray(plugins)) {
966
+ errors.push(
967
+ "'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead.",
968
+ );
969
+ }
970
+ if (typeof stats !== "boolean") {
971
+ errors.push("'stats' must be a boolean.");
972
+ }
973
+ if (typeof warnIgnored !== "boolean") {
974
+ errors.push("'warnIgnored' must be a boolean.");
975
+ }
976
+ if (typeof ruleFilter !== "function") {
977
+ errors.push("'ruleFilter' must be a function.");
978
+ }
979
+ if (errors.length > 0) {
980
+ throw new ESLintInvalidOptionsError(errors);
981
+ }
982
+
983
+ return {
984
+ allowInlineConfig,
985
+ baseConfig,
986
+ cache,
987
+ cacheLocation,
988
+ cacheStrategy,
989
+ concurrency,
990
+
991
+ // when overrideConfigFile is true that means don't do config file lookup
992
+ configFile: overrideConfigFile === true ? false : overrideConfigFile,
993
+ overrideConfig,
994
+ cwd: path.normalize(cwd),
995
+ errorOnUnmatchedPattern,
996
+ fix,
997
+ fixTypes,
998
+ flags: [...flags],
999
+ globInputPaths,
1000
+ ignore,
1001
+ ignorePatterns,
1002
+ stats,
1003
+ passOnNoPatterns,
1004
+ warnIgnored,
1005
+ ruleFilter,
1006
+ };
835
1007
  }
836
1008
 
1009
+ /**
1010
+ * Loads ESLint constructor options from an options module.
1011
+ * @param {string} optionsURL The URL string of the options module to load.
1012
+ * @returns {Promise<ESLintOptions>} ESLint constructor options.
1013
+ */
1014
+ async function loadOptionsFromModule(optionsURL) {
1015
+ return (await import(optionsURL)).default;
1016
+ }
837
1017
 
838
1018
  //-----------------------------------------------------------------------------
839
1019
  // Cache-related helpers
@@ -847,86 +1027,439 @@ function processOptions({
847
1027
  * if cacheFile points to a file or looks like a file then in will just use that file
848
1028
  * @param {string} cacheFile The name of file to be used to store the cache
849
1029
  * @param {string} cwd Current working directory
1030
+ * @param {Object} options The options
1031
+ * @param {string} [options.prefix] The prefix to use for the cache file
850
1032
  * @returns {string} the resolved path to the cache file
851
1033
  */
852
- function getCacheFile(cacheFile, cwd) {
1034
+ function getCacheFile(cacheFile, cwd, { prefix = ".cache_" } = {}) {
1035
+ /*
1036
+ * make sure the path separators are normalized for the environment/os
1037
+ * keeping the trailing path separator if present
1038
+ */
1039
+ const normalizedCacheFile = path.normalize(cacheFile);
1040
+
1041
+ const resolvedCacheFile = path.resolve(cwd, normalizedCacheFile);
1042
+ const looksLikeADirectory = normalizedCacheFile.slice(-1) === path.sep;
1043
+
1044
+ /**
1045
+ * return the name for the cache file in case the provided parameter is a directory
1046
+ * @returns {string} the resolved path to the cacheFile
1047
+ */
1048
+ function getCacheFileForDirectory() {
1049
+ return path.join(resolvedCacheFile, `${prefix}${hash(cwd)}`);
1050
+ }
1051
+
1052
+ let fileStats;
1053
+
1054
+ try {
1055
+ fileStats = fs.lstatSync(resolvedCacheFile);
1056
+ } catch {
1057
+ fileStats = null;
1058
+ }
1059
+
1060
+ /*
1061
+ * in case the file exists we need to verify if the provided path
1062
+ * is a directory or a file. If it is a directory we want to create a file
1063
+ * inside that directory
1064
+ */
1065
+ if (fileStats) {
1066
+ /*
1067
+ * is a directory or is a file, but the original file the user provided
1068
+ * looks like a directory but `path.resolve` removed the `last path.sep`
1069
+ * so we need to still treat this like a directory
1070
+ */
1071
+ if (fileStats.isDirectory() || looksLikeADirectory) {
1072
+ return getCacheFileForDirectory();
1073
+ }
1074
+
1075
+ // is file so just use that file
1076
+ return resolvedCacheFile;
1077
+ }
1078
+
1079
+ /*
1080
+ * here we known the file or directory doesn't exist,
1081
+ * so we will try to infer if its a directory if it looks like a directory
1082
+ * for the current operating system.
1083
+ */
1084
+
1085
+ // if the last character passed is a path separator we assume is a directory
1086
+ if (looksLikeADirectory) {
1087
+ return getCacheFileForDirectory();
1088
+ }
1089
+
1090
+ return resolvedCacheFile;
1091
+ }
1092
+
1093
+ /**
1094
+ * Creates a new lint result cache.
1095
+ * @param {ESLintOptions} eslintOptions The processed ESLint options.
1096
+ * @param {string} cacheFilePath The path to the cache file.
1097
+ * @returns {?LintResultCache} A new lint result cache or `null`.
1098
+ */
1099
+ function createLintResultCache({ cache, cacheStrategy }, cacheFilePath) {
1100
+ return cache ? new LintResultCache(cacheFilePath, cacheStrategy) : null;
1101
+ }
1102
+
1103
+ //-----------------------------------------------------------------------------
1104
+ // Lint helpers
1105
+ //-----------------------------------------------------------------------------
1106
+
1107
+ /**
1108
+ * Checks whether a message's rule type should be fixed.
1109
+ * @param {LintMessage} message The message to check.
1110
+ * @param {CalculatedConfig} config The config for the file that generated the message.
1111
+ * @param {string[]} fixTypes An array of fix types to check.
1112
+ * @returns {boolean} Whether the message should be fixed.
1113
+ */
1114
+ function shouldMessageBeFixed(message, config, fixTypes) {
1115
+ if (!message.ruleId) {
1116
+ return fixTypes.has("directive");
1117
+ }
1118
+
1119
+ const rule = message.ruleId && config.getRuleDefinition(message.ruleId);
1120
+
1121
+ return Boolean(rule && rule.meta && fixTypes.has(rule.meta.type));
1122
+ }
1123
+
1124
+ /**
1125
+ * Creates a fixer function based on the provided fix, fixTypesSet, and config.
1126
+ * @param {Function|boolean} fix The original fix option.
1127
+ * @param {Set<string>} fixTypesSet A set of fix types to filter messages for fixing.
1128
+ * @param {CalculatedConfig} config The config for the file that generated the message.
1129
+ * @returns {Function|boolean} The fixer function or the original fix value.
1130
+ */
1131
+ function getFixerForFixTypes(fix, fixTypesSet, config) {
1132
+ if (!fix || !fixTypesSet) {
1133
+ return fix;
1134
+ }
853
1135
 
854
- /*
855
- * make sure the path separators are normalized for the environment/os
856
- * keeping the trailing path separator if present
857
- */
858
- const normalizedCacheFile = path.normalize(cacheFile);
1136
+ const originalFix = typeof fix === "function" ? fix : () => true;
859
1137
 
860
- const resolvedCacheFile = path.resolve(cwd, normalizedCacheFile);
861
- const looksLikeADirectory = normalizedCacheFile.slice(-1) === path.sep;
1138
+ return message =>
1139
+ shouldMessageBeFixed(message, config, fixTypesSet) &&
1140
+ originalFix(message);
1141
+ }
862
1142
 
863
- /**
864
- * return the name for the cache file in case the provided parameter is a directory
865
- * @returns {string} the resolved path to the cacheFile
866
- */
867
- function getCacheFileForDirectory() {
868
- return path.join(resolvedCacheFile, `.cache_${hash(cwd)}`);
869
- }
1143
+ /**
1144
+ * Processes a source code using ESLint.
1145
+ * @param {Object} config The config object.
1146
+ * @param {string} config.text The source code to verify.
1147
+ * @param {string} config.cwd The path to the current working directory.
1148
+ * @param {string|undefined} config.filePath The path to the file of `text`. If this is undefined, it uses `<text>`.
1149
+ * @param {FlatConfigArray} config.configs The config.
1150
+ * @param {boolean} config.fix If `true` then it does fix.
1151
+ * @param {boolean} config.allowInlineConfig If `true` then it uses directive comments.
1152
+ * @param {Function} config.ruleFilter A predicate function to filter which rules should be run.
1153
+ * @param {boolean} config.stats If `true`, then if reports extra statistics with the lint results.
1154
+ * @param {Linter} config.linter The linter instance to verify.
1155
+ * @returns {LintResult} The result of linting.
1156
+ * @private
1157
+ */
1158
+ function verifyText({
1159
+ text,
1160
+ cwd,
1161
+ filePath: providedFilePath,
1162
+ configs,
1163
+ fix,
1164
+ allowInlineConfig,
1165
+ ruleFilter,
1166
+ stats,
1167
+ linter,
1168
+ }) {
1169
+ const startTime = hrtimeBigint();
1170
+
1171
+ const filePath = providedFilePath || "<text>";
1172
+
1173
+ /*
1174
+ * Verify.
1175
+ * `config.extractConfig(filePath)` requires an absolute path, but `linter`
1176
+ * doesn't know CWD, so it gives `linter` an absolute path always.
1177
+ */
1178
+ const filePathToVerify =
1179
+ filePath === "<text>" ? getPlaceholderPath(cwd) : filePath;
1180
+ const { fixed, messages, output } = linter.verifyAndFix(text, configs, {
1181
+ allowInlineConfig,
1182
+ filename: filePathToVerify,
1183
+ fix,
1184
+ ruleFilter,
1185
+ stats,
1186
+
1187
+ /**
1188
+ * Check if the linter should adopt a given code block or not.
1189
+ * @param {string} blockFilename The virtual filename of a code block.
1190
+ * @returns {boolean} `true` if the linter should adopt the code block.
1191
+ */
1192
+ filterCodeBlock(blockFilename) {
1193
+ return configs.getConfig(blockFilename) !== void 0;
1194
+ },
1195
+ });
1196
+
1197
+ // Tweak and return.
1198
+ const result = {
1199
+ filePath: filePath === "<text>" ? filePath : path.resolve(filePath),
1200
+ messages,
1201
+ suppressedMessages: linter.getSuppressedMessages(),
1202
+ ...calculateStatsPerFile(messages),
1203
+ };
1204
+
1205
+ if (fixed) {
1206
+ result.output = output;
1207
+ }
1208
+
1209
+ if (
1210
+ result.errorCount + result.warningCount > 0 &&
1211
+ typeof result.output === "undefined"
1212
+ ) {
1213
+ result.source = text;
1214
+ }
1215
+
1216
+ if (stats) {
1217
+ result.stats = {
1218
+ times: linter.getTimes(),
1219
+ fixPasses: linter.getFixPassCount(),
1220
+ };
1221
+ }
1222
+
1223
+ const endTime = hrtimeBigint();
1224
+ debug('File "%s" linted in %t', filePath, endTime - startTime);
1225
+
1226
+ return result;
1227
+ }
870
1228
 
871
- let fileStats;
1229
+ /**
1230
+ * Lints a single file.
1231
+ * @param {string} filePath File path to lint.
1232
+ * @param {FlatConfigArray} configs The config array for the file.
1233
+ * @param {ESLintOptions} eslintOptions The processed ESLint options.
1234
+ * @param {Linter} linter The linter instance to use.
1235
+ * @param {?LintResultCache} lintResultCache The result cache or `null`.
1236
+ * @param {?{ duration: bigint; }} readFileCounter Used to keep track of the time spent reading files.
1237
+ * @param {Retrier} [retrier] Used to retry linting on certain errors.
1238
+ * @param {AbortController} [controller] Used to stop linting when an error occurs.
1239
+ * @returns {Promise<LintResult>} The lint result.
1240
+ */
1241
+ async function lintFile(
1242
+ filePath,
1243
+ configs,
1244
+ eslintOptions,
1245
+ linter,
1246
+ lintResultCache,
1247
+ readFileCounter,
1248
+ retrier,
1249
+ controller,
1250
+ ) {
1251
+ const config = configs.getConfig(filePath);
1252
+ const {
1253
+ allowInlineConfig,
1254
+ cwd,
1255
+ fix,
1256
+ fixTypes,
1257
+ ruleFilter,
1258
+ stats,
1259
+ warnIgnored,
1260
+ } = eslintOptions;
1261
+ const fixTypesSet = fixTypes ? new Set(fixTypes) : null;
1262
+
1263
+ /*
1264
+ * If a filename was entered that cannot be matched
1265
+ * to a config, then notify the user.
1266
+ */
1267
+ if (!config) {
1268
+ if (warnIgnored) {
1269
+ const configStatus = configs.getConfigStatus(filePath);
1270
+
1271
+ return createIgnoreResult(filePath, cwd, configStatus);
1272
+ }
1273
+
1274
+ return void 0;
1275
+ }
1276
+
1277
+ // Skip if there is cached result.
1278
+ if (lintResultCache) {
1279
+ const cachedResult = lintResultCache.getCachedLintResults(
1280
+ filePath,
1281
+ config,
1282
+ );
1283
+
1284
+ if (cachedResult) {
1285
+ const hadMessages =
1286
+ cachedResult.messages && cachedResult.messages.length > 0;
1287
+
1288
+ if (hadMessages && fix) {
1289
+ debug(`Reprocessing cached file to allow autofix: ${filePath}`);
1290
+ } else {
1291
+ debug(`Skipping file since it hasn't changed: ${filePath}`);
1292
+ return cachedResult;
1293
+ }
1294
+ }
1295
+ }
1296
+
1297
+ // set up fixer for fixTypes if necessary
1298
+ const fixer = getFixerForFixTypes(fix, fixTypesSet, config);
1299
+
1300
+ /**
1301
+ * Reads the file and lints its content.
1302
+ * @returns {Promise<LintResult>} A lint result.
1303
+ */
1304
+ async function readAndVerifyFile() {
1305
+ const readFileEnterTime = hrtimeBigint();
1306
+ const text = await fsp.readFile(filePath, {
1307
+ encoding: "utf8",
1308
+ signal: controller?.signal,
1309
+ });
1310
+ const readFileExitTime = hrtimeBigint();
1311
+ const readFileDuration = readFileExitTime - readFileEnterTime;
1312
+ debug('File "%s" read in %t', filePath, readFileDuration);
1313
+ if (readFileCounter) {
1314
+ readFileCounter.duration += readFileDuration;
1315
+ }
1316
+
1317
+ // fail immediately if an error occurred in another file
1318
+ controller?.signal.throwIfAborted();
1319
+
1320
+ // do the linting
1321
+ return verifyText({
1322
+ text,
1323
+ filePath,
1324
+ configs,
1325
+ cwd,
1326
+ fix: fixer,
1327
+ allowInlineConfig,
1328
+ ruleFilter,
1329
+ stats,
1330
+ linter,
1331
+ });
1332
+ }
1333
+
1334
+ // Use the retrier if provided, otherwise just call the function.
1335
+ const readAndVerifyFilePromise = retrier
1336
+ ? retrier.retry(readAndVerifyFile, { signal: controller?.signal })
1337
+ : readAndVerifyFile();
1338
+
1339
+ return readAndVerifyFilePromise.catch(error => {
1340
+ controller?.abort(error);
1341
+ throw error;
1342
+ });
1343
+ }
872
1344
 
873
- try {
874
- fileStats = fs.lstatSync(resolvedCacheFile);
875
- } catch {
876
- fileStats = null;
877
- }
1345
+ /**
1346
+ * Retrieves flags from the environment variable ESLINT_FLAGS.
1347
+ * @param {string[]} flags The flags defined via the API.
1348
+ * @returns {string[]} The merged flags to use.
1349
+ */
1350
+ function mergeEnvironmentFlags(flags) {
1351
+ if (!process.env.ESLINT_FLAGS) {
1352
+ return flags;
1353
+ }
878
1354
 
1355
+ const envFlags = process.env.ESLINT_FLAGS.trim().split(/\s*,\s*/gu);
1356
+ return Array.from(new Set([...envFlags, ...flags]));
1357
+ }
879
1358
 
880
- /*
881
- * in case the file exists we need to verify if the provided path
882
- * is a directory or a file. If it is a directory we want to create a file
883
- * inside that directory
884
- */
885
- if (fileStats) {
1359
+ /**
1360
+ * Creates a new linter instance.
1361
+ * @param {ESLintOptions} eslintOptions The processed ESLint options.
1362
+ * @param {WarningService} warningService The warning service to use.
1363
+ * @returns {Linter} The linter instance.
1364
+ */
1365
+ function createLinter({ cwd, flags }, warningService) {
1366
+ return new Linter({
1367
+ configType: "flat",
1368
+ cwd,
1369
+ flags: mergeEnvironmentFlags(flags),
1370
+ warningService,
1371
+ });
1372
+ }
886
1373
 
887
- /*
888
- * is a directory or is a file, but the original file the user provided
889
- * looks like a directory but `path.resolve` removed the `last path.sep`
890
- * so we need to still treat this like a directory
891
- */
892
- if (fileStats.isDirectory() || looksLikeADirectory) {
893
- return getCacheFileForDirectory();
894
- }
1374
+ /**
1375
+ * Creates default configs with the specified plugins.
1376
+ * @param {Record<string, Plugin> | undefined} optionPlugins The plugins specified in the ESLint options.
1377
+ * @returns {Config[]} The default configs.
1378
+ */
1379
+ function createDefaultConfigs(optionPlugins) {
1380
+ const defaultConfigs = [];
895
1381
 
896
- // is file so just use that file
897
- return resolvedCacheFile;
898
- }
1382
+ // Add plugins
1383
+ if (optionPlugins) {
1384
+ const plugins = {};
899
1385
 
900
- /*
901
- * here we known the file or directory doesn't exist,
902
- * so we will try to infer if its a directory if it looks like a directory
903
- * for the current operating system.
904
- */
1386
+ for (const [pluginName, plugin] of Object.entries(optionPlugins)) {
1387
+ plugins[getShorthandName(pluginName, "eslint-plugin")] = plugin;
1388
+ }
905
1389
 
906
- // if the last character passed is a path separator we assume is a directory
907
- if (looksLikeADirectory) {
908
- return getCacheFileForDirectory();
909
- }
1390
+ defaultConfigs.push({ plugins });
1391
+ }
910
1392
 
911
- return resolvedCacheFile;
1393
+ return defaultConfigs;
912
1394
  }
913
1395
 
1396
+ /**
1397
+ * Creates a config loader.
1398
+ * @param {ESLintOptions} eslintOptions The processed ESLint options.
1399
+ * @param {Config[]} defaultConfigs The default configs.
1400
+ * @param {Linter} linter The linter instance.
1401
+ * @param {WarningService} warningService The warning service to use.
1402
+ * @returns {ConfigLoader} The config loader.
1403
+ */
1404
+ function createConfigLoader(
1405
+ {
1406
+ cwd,
1407
+ baseConfig,
1408
+ overrideConfig,
1409
+ configFile,
1410
+ ignore: ignoreEnabled,
1411
+ ignorePatterns,
1412
+ },
1413
+ defaultConfigs,
1414
+ linter,
1415
+ warningService,
1416
+ ) {
1417
+ const configLoaderOptions = {
1418
+ cwd,
1419
+ baseConfig,
1420
+ overrideConfig,
1421
+ configFile,
1422
+ ignoreEnabled,
1423
+ ignorePatterns,
1424
+ defaultConfigs,
1425
+ hasUnstableNativeNodeJsTSConfigFlag: linter.hasFlag(
1426
+ "unstable_native_nodejs_ts_config",
1427
+ ),
1428
+ warningService,
1429
+ };
1430
+
1431
+ return linter.hasFlag("v10_config_lookup_from_file")
1432
+ ? new ConfigLoader(configLoaderOptions)
1433
+ : new LegacyConfigLoader(configLoaderOptions);
1434
+ }
914
1435
 
915
1436
  //-----------------------------------------------------------------------------
916
1437
  // Exports
917
1438
  //-----------------------------------------------------------------------------
918
1439
 
919
1440
  module.exports = {
920
- isGlobPattern,
921
- findFiles,
1441
+ createDebug,
1442
+
1443
+ findFiles,
1444
+
1445
+ isNonEmptyString,
1446
+ isArrayOfNonEmptyString,
922
1447
 
923
- isNonEmptyString,
924
- isArrayOfNonEmptyString,
1448
+ createIgnoreResult,
1449
+ isErrorMessage,
1450
+ calculateStatsPerFile,
1451
+ getPlaceholderPath,
925
1452
 
926
- createIgnoreResult,
927
- isErrorMessage,
1453
+ processOptions,
1454
+ loadOptionsFromModule,
928
1455
 
929
- processOptions,
1456
+ getCacheFile,
1457
+ createLintResultCache,
930
1458
 
931
- getCacheFile
1459
+ getFixerForFixTypes,
1460
+ verifyText,
1461
+ lintFile,
1462
+ createLinter,
1463
+ createDefaultConfigs,
1464
+ createConfigLoader,
932
1465
  };