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.
- package/README.md +165 -115
- package/bin/eslint.js +112 -89
- package/conf/default-cli-options.js +22 -22
- package/conf/ecma-version.js +16 -0
- package/conf/globals.js +109 -94
- package/conf/replacements.json +24 -20
- package/conf/rule-type-list.json +89 -26
- package/lib/api.js +16 -20
- package/lib/cli-engine/cli-engine.js +841 -810
- package/lib/cli-engine/file-enumerator.js +384 -390
- package/lib/cli-engine/formatters/formatters-meta.json +17 -45
- package/lib/cli-engine/formatters/html.js +110 -102
- package/lib/cli-engine/formatters/json-with-metadata.js +5 -5
- package/lib/cli-engine/formatters/json.js +2 -2
- package/lib/cli-engine/formatters/stylish.js +97 -76
- package/lib/cli-engine/hash.js +1 -1
- package/lib/cli-engine/index.js +1 -1
- package/lib/cli-engine/lint-result-cache.js +165 -148
- package/lib/cli-engine/load-rules.js +17 -17
- package/lib/cli.js +481 -399
- package/lib/config/config-loader.js +816 -0
- package/lib/config/config.js +674 -0
- package/lib/config/default-config.js +57 -46
- package/lib/config/flat-config-array.js +170 -333
- package/lib/config/flat-config-schema.js +389 -389
- package/lib/config-api.js +12 -0
- package/lib/eslint/eslint-helpers.js +1196 -663
- package/lib/eslint/eslint.js +1262 -607
- package/lib/eslint/index.js +3 -3
- package/lib/eslint/legacy-eslint.js +786 -0
- package/lib/eslint/worker.js +173 -0
- package/lib/languages/js/index.js +336 -0
- package/lib/languages/js/source-code/index.js +7 -0
- package/lib/languages/js/source-code/source-code.js +1364 -0
- package/lib/languages/js/source-code/token-store/backward-token-comment-cursor.js +61 -0
- package/lib/languages/js/source-code/token-store/backward-token-cursor.js +57 -0
- package/lib/{source-code → languages/js/source-code}/token-store/cursor.js +36 -36
- package/lib/languages/js/source-code/token-store/cursors.js +120 -0
- package/lib/{source-code → languages/js/source-code}/token-store/decorative-cursor.js +17 -18
- package/lib/{source-code → languages/js/source-code}/token-store/filter-cursor.js +19 -20
- package/lib/languages/js/source-code/token-store/forward-token-comment-cursor.js +65 -0
- package/lib/languages/js/source-code/token-store/forward-token-cursor.js +62 -0
- package/lib/languages/js/source-code/token-store/index.js +721 -0
- package/lib/{source-code → languages/js/source-code}/token-store/limit-cursor.js +17 -18
- package/lib/languages/js/source-code/token-store/padded-token-cursor.js +45 -0
- package/lib/{source-code → languages/js/source-code}/token-store/skip-cursor.js +19 -20
- package/lib/languages/js/source-code/token-store/utils.js +110 -0
- package/lib/languages/js/validate-language-options.js +196 -0
- package/lib/linter/apply-disable-directives.js +490 -371
- package/lib/linter/code-path-analysis/code-path-analyzer.js +650 -674
- package/lib/linter/code-path-analysis/code-path-segment.js +215 -216
- package/lib/linter/code-path-analysis/code-path-state.js +2118 -2096
- package/lib/linter/code-path-analysis/code-path.js +307 -317
- package/lib/linter/code-path-analysis/debug-helpers.js +183 -163
- package/lib/linter/code-path-analysis/fork-context.js +297 -272
- package/lib/linter/code-path-analysis/id-generator.js +22 -23
- package/lib/linter/esquery.js +332 -0
- package/lib/linter/file-context.js +144 -0
- package/lib/linter/file-report.js +608 -0
- package/lib/linter/index.js +3 -5
- package/lib/linter/interpolate.js +38 -16
- package/lib/linter/linter.js +2328 -1785
- package/lib/linter/rule-fixer.js +136 -107
- package/lib/linter/rules.js +37 -46
- package/lib/linter/source-code-fixer.js +96 -94
- package/lib/linter/source-code-traverser.js +333 -0
- package/lib/linter/source-code-visitor.js +81 -0
- package/lib/linter/timing.js +145 -97
- package/lib/linter/vfile.js +115 -0
- package/lib/options.js +464 -326
- package/lib/rule-tester/index.js +3 -1
- package/lib/rule-tester/rule-tester.js +1371 -998
- package/lib/rules/accessor-pairs.js +333 -259
- package/lib/rules/array-bracket-newline.js +250 -220
- package/lib/rules/array-bracket-spacing.js +286 -229
- package/lib/rules/array-callback-return.js +401 -354
- package/lib/rules/array-element-newline.js +358 -295
- package/lib/rules/arrow-body-style.js +400 -278
- package/lib/rules/arrow-parens.js +206 -155
- package/lib/rules/arrow-spacing.js +169 -145
- package/lib/rules/block-scoped-var.js +125 -123
- package/lib/rules/block-spacing.js +186 -158
- package/lib/rules/brace-style.js +262 -181
- package/lib/rules/callback-return.js +203 -174
- package/lib/rules/camelcase.js +403 -380
- package/lib/rules/capitalized-comments.js +253 -228
- package/lib/rules/class-methods-use-this.js +231 -168
- package/lib/rules/comma-dangle.js +379 -328
- package/lib/rules/comma-spacing.js +193 -177
- package/lib/rules/comma-style.js +375 -298
- package/lib/rules/complexity.js +180 -144
- package/lib/rules/computed-property-spacing.js +236 -193
- package/lib/rules/consistent-return.js +181 -170
- package/lib/rules/consistent-this.js +167 -141
- package/lib/rules/constructor-super.js +418 -411
- package/lib/rules/curly.js +407 -468
- package/lib/rules/default-case-last.js +39 -32
- package/lib/rules/default-case.js +89 -83
- package/lib/rules/default-param-last.js +69 -53
- package/lib/rules/dot-location.js +122 -92
- package/lib/rules/dot-notation.js +193 -153
- package/lib/rules/eol-last.js +122 -102
- package/lib/rules/eqeqeq.js +191 -155
- package/lib/rules/for-direction.js +150 -122
- package/lib/rules/func-call-spacing.js +261 -213
- package/lib/rules/func-name-matching.js +294 -209
- package/lib/rules/func-names.js +165 -164
- package/lib/rules/func-style.js +209 -86
- package/lib/rules/function-call-argument-newline.js +152 -111
- package/lib/rules/function-paren-newline.js +349 -273
- package/lib/rules/generator-star-spacing.js +229 -192
- package/lib/rules/getter-return.js +208 -170
- package/lib/rules/global-require.js +85 -58
- package/lib/rules/grouped-accessor-pairs.js +201 -148
- package/lib/rules/guard-for-in.js +72 -63
- package/lib/rules/handle-callback-err.js +108 -87
- package/lib/rules/id-blacklist.js +182 -187
- package/lib/rules/id-denylist.js +174 -179
- package/lib/rules/id-length.js +197 -157
- package/lib/rules/id-match.js +350 -286
- package/lib/rules/implicit-arrow-linebreak.js +102 -61
- package/lib/rules/indent-legacy.js +1345 -1102
- package/lib/rules/indent.js +2272 -1741
- package/lib/rules/index.js +320 -294
- package/lib/rules/init-declarations.js +139 -106
- package/lib/rules/jsx-quotes.js +94 -64
- package/lib/rules/key-spacing.js +750 -615
- package/lib/rules/keyword-spacing.js +648 -587
- package/lib/rules/line-comment-position.js +143 -108
- package/lib/rules/linebreak-style.js +115 -88
- package/lib/rules/lines-around-comment.js +540 -430
- package/lib/rules/lines-around-directive.js +233 -185
- package/lib/rules/lines-between-class-members.js +305 -216
- package/lib/rules/logical-assignment-operators.js +582 -398
- package/lib/rules/max-classes-per-file.js +69 -68
- package/lib/rules/max-depth.js +146 -143
- package/lib/rules/max-len.js +473 -416
- package/lib/rules/max-lines-per-function.js +201 -176
- package/lib/rules/max-lines.js +158 -162
- package/lib/rules/max-nested-callbacks.js +102 -104
- package/lib/rules/max-params.js +102 -75
- package/lib/rules/max-statements-per-line.js +205 -180
- package/lib/rules/max-statements.js +168 -164
- package/lib/rules/multiline-comment-style.js +638 -460
- package/lib/rules/multiline-ternary.js +241 -158
- package/lib/rules/new-cap.js +233 -232
- package/lib/rules/new-parens.js +88 -61
- package/lib/rules/newline-after-var.js +287 -233
- package/lib/rules/newline-before-return.js +229 -204
- package/lib/rules/newline-per-chained-call.js +142 -109
- package/lib/rules/no-alert.js +90 -79
- package/lib/rules/no-array-constructor.js +175 -113
- package/lib/rules/no-async-promise-executor.js +30 -24
- package/lib/rules/no-await-in-loop.js +79 -70
- package/lib/rules/no-bitwise.js +113 -87
- package/lib/rules/no-buffer-constructor.js +61 -37
- package/lib/rules/no-caller.js +39 -33
- package/lib/rules/no-case-declarations.js +61 -45
- package/lib/rules/no-catch-shadow.js +76 -62
- package/lib/rules/no-class-assign.js +51 -48
- package/lib/rules/no-compare-neg-zero.js +62 -48
- package/lib/rules/no-cond-assign.js +148 -132
- package/lib/rules/no-confusing-arrow.js +98 -63
- package/lib/rules/no-console.js +202 -188
- package/lib/rules/no-const-assign.js +58 -41
- package/lib/rules/no-constant-binary-expression.js +501 -407
- package/lib/rules/no-constant-condition.js +158 -131
- package/lib/rules/no-constructor-return.js +49 -49
- package/lib/rules/no-continue.js +25 -26
- package/lib/rules/no-control-regex.js +125 -121
- package/lib/rules/no-debugger.js +28 -30
- package/lib/rules/no-delete-var.js +29 -29
- package/lib/rules/no-div-regex.js +47 -40
- package/lib/rules/no-dupe-args.js +79 -69
- package/lib/rules/no-dupe-class-members.js +102 -89
- package/lib/rules/no-dupe-else-if.js +100 -77
- package/lib/rules/no-dupe-keys.js +133 -110
- package/lib/rules/no-duplicate-case.js +50 -43
- package/lib/rules/no-duplicate-imports.js +266 -188
- package/lib/rules/no-else-return.js +430 -385
- package/lib/rules/no-empty-character-class.js +57 -50
- package/lib/rules/no-empty-function.js +197 -128
- package/lib/rules/no-empty-pattern.js +63 -56
- package/lib/rules/no-empty-static-block.js +61 -35
- package/lib/rules/no-empty.js +135 -85
- package/lib/rules/no-eq-null.js +37 -32
- package/lib/rules/no-eval.js +258 -249
- package/lib/rules/no-ex-assign.js +42 -39
- package/lib/rules/no-extend-native.js +161 -160
- package/lib/rules/no-extra-bind.js +201 -190
- package/lib/rules/no-extra-boolean-cast.js +398 -295
- package/lib/rules/no-extra-label.js +150 -130
- package/lib/rules/no-extra-parens.js +1654 -1307
- package/lib/rules/no-extra-semi.js +146 -126
- package/lib/rules/no-fallthrough.js +200 -136
- package/lib/rules/no-floating-decimal.js +74 -48
- package/lib/rules/no-func-assign.js +54 -55
- package/lib/rules/no-global-assign.js +78 -72
- package/lib/rules/no-implicit-coercion.js +350 -262
- package/lib/rules/no-implicit-globals.js +174 -133
- package/lib/rules/no-implied-eval.js +150 -112
- package/lib/rules/no-import-assign.js +145 -159
- package/lib/rules/no-inline-comments.js +101 -96
- package/lib/rules/no-inner-declarations.js +115 -78
- package/lib/rules/no-invalid-regexp.js +223 -174
- package/lib/rules/no-invalid-this.js +145 -117
- package/lib/rules/no-irregular-whitespace.js +266 -250
- package/lib/rules/no-iterator.js +29 -33
- package/lib/rules/no-label-var.js +59 -61
- package/lib/rules/no-labels.js +138 -131
- package/lib/rules/no-lone-blocks.js +127 -123
- package/lib/rules/no-lonely-if.js +105 -67
- package/lib/rules/no-loop-func.js +245 -184
- package/lib/rules/no-loss-of-precision.js +236 -201
- package/lib/rules/no-magic-numbers.js +339 -217
- package/lib/rules/no-misleading-character-class.js +548 -253
- package/lib/rules/no-mixed-operators.js +188 -164
- package/lib/rules/no-mixed-requires.js +253 -224
- package/lib/rules/no-mixed-spaces-and-tabs.js +135 -103
- package/lib/rules/no-multi-assign.js +46 -47
- package/lib/rules/no-multi-spaces.js +163 -125
- package/lib/rules/no-multi-str.js +42 -40
- package/lib/rules/no-multiple-empty-lines.js +196 -140
- package/lib/rules/no-native-reassign.js +90 -74
- package/lib/rules/no-negated-condition.js +79 -74
- package/lib/rules/no-negated-in-lhs.js +45 -32
- package/lib/rules/no-nested-ternary.js +33 -31
- package/lib/rules/no-new-func.js +71 -62
- package/lib/rules/no-new-native-nonconstructor.js +43 -39
- package/lib/rules/no-new-object.js +48 -39
- package/lib/rules/no-new-require.js +48 -31
- package/lib/rules/no-new-symbol.js +61 -43
- package/lib/rules/no-new-wrappers.js +43 -41
- package/lib/rules/no-new.js +28 -29
- package/lib/rules/no-nonoctal-decimal-escape.js +149 -121
- package/lib/rules/no-obj-calls.js +66 -53
- package/lib/rules/no-object-constructor.js +104 -97
- package/lib/rules/no-octal-escape.js +40 -43
- package/lib/rules/no-octal.js +29 -32
- package/lib/rules/no-param-reassign.js +236 -218
- package/lib/rules/no-path-concat.js +66 -51
- package/lib/rules/no-plusplus.js +60 -63
- package/lib/rules/no-process-env.js +49 -32
- package/lib/rules/no-process-exit.js +48 -28
- package/lib/rules/no-promise-executor-return.js +205 -204
- package/lib/rules/no-proto.js +26 -29
- package/lib/rules/no-prototype-builtins.js +146 -124
- package/lib/rules/no-redeclare.js +154 -155
- package/lib/rules/no-regex-spaces.js +183 -161
- package/lib/rules/no-restricted-exports.js +208 -174
- package/lib/rules/no-restricted-globals.js +254 -112
- package/lib/rules/no-restricted-imports.js +824 -384
- package/lib/rules/no-restricted-modules.js +222 -186
- package/lib/rules/no-restricted-properties.js +218 -153
- package/lib/rules/no-restricted-syntax.js +56 -52
- package/lib/rules/no-return-assign.js +56 -49
- package/lib/rules/no-return-await.js +147 -120
- package/lib/rules/no-script-url.js +53 -46
- package/lib/rules/no-self-assign.js +148 -145
- package/lib/rules/no-self-compare.js +63 -46
- package/lib/rules/no-sequences.js +135 -115
- package/lib/rules/no-setter-return.js +176 -178
- package/lib/rules/no-shadow-restricted-names.js +84 -36
- package/lib/rules/no-shadow.js +598 -310
- package/lib/rules/no-spaced-func.js +82 -60
- package/lib/rules/no-sparse-arrays.js +46 -28
- package/lib/rules/no-sync.js +61 -44
- package/lib/rules/no-tabs.js +83 -54
- package/lib/rules/no-template-curly-in-string.js +33 -32
- package/lib/rules/no-ternary.js +25 -28
- package/lib/rules/no-this-before-super.js +332 -298
- package/lib/rules/no-throw-literal.js +31 -36
- package/lib/rules/no-trailing-spaces.js +208 -174
- package/lib/rules/no-unassigned-vars.js +80 -0
- package/lib/rules/no-undef-init.js +86 -60
- package/lib/rules/no-undef.js +52 -47
- package/lib/rules/no-undefined.js +73 -74
- package/lib/rules/no-underscore-dangle.js +370 -322
- package/lib/rules/no-unexpected-multiline.js +112 -102
- package/lib/rules/no-unmodified-loop-condition.js +254 -254
- package/lib/rules/no-unneeded-ternary.js +212 -146
- package/lib/rules/no-unreachable-loop.js +145 -140
- package/lib/rules/no-unreachable.js +255 -248
- package/lib/rules/no-unsafe-finally.js +93 -85
- package/lib/rules/no-unsafe-negation.js +105 -81
- package/lib/rules/no-unsafe-optional-chaining.js +193 -177
- package/lib/rules/no-unused-expressions.js +199 -158
- package/lib/rules/no-unused-labels.js +139 -124
- package/lib/rules/no-unused-private-class-members.js +206 -182
- package/lib/rules/no-unused-vars.js +1708 -687
- package/lib/rules/no-use-before-define.js +327 -229
- package/lib/rules/no-useless-assignment.js +654 -0
- package/lib/rules/no-useless-backreference.js +212 -143
- package/lib/rules/no-useless-call.js +58 -53
- package/lib/rules/no-useless-catch.js +40 -40
- package/lib/rules/no-useless-computed-key.js +144 -108
- package/lib/rules/no-useless-concat.js +65 -59
- package/lib/rules/no-useless-constructor.js +160 -97
- package/lib/rules/no-useless-escape.js +364 -291
- package/lib/rules/no-useless-rename.js +183 -153
- package/lib/rules/no-useless-return.js +344 -307
- package/lib/rules/no-var.js +245 -212
- package/lib/rules/no-void.js +51 -46
- package/lib/rules/no-warning-comments.js +191 -183
- package/lib/rules/no-whitespace-before-property.js +131 -97
- package/lib/rules/no-with.js +24 -26
- package/lib/rules/nonblock-statement-body-position.js +149 -112
- package/lib/rules/object-curly-newline.js +306 -247
- package/lib/rules/object-curly-spacing.js +360 -296
- package/lib/rules/object-property-newline.js +137 -88
- package/lib/rules/object-shorthand.js +632 -500
- package/lib/rules/one-var-declaration-per-line.js +104 -82
- package/lib/rules/one-var.js +686 -536
- package/lib/rules/operator-assignment.js +219 -158
- package/lib/rules/operator-linebreak.js +295 -233
- package/lib/rules/padded-blocks.js +346 -290
- package/lib/rules/padding-line-between-statements.js +443 -421
- package/lib/rules/prefer-arrow-callback.js +371 -315
- package/lib/rules/prefer-const.js +418 -373
- package/lib/rules/prefer-destructuring.js +309 -278
- package/lib/rules/prefer-exponentiation-operator.js +176 -132
- package/lib/rules/prefer-named-capture-group.js +160 -141
- package/lib/rules/prefer-numeric-literals.js +121 -112
- package/lib/rules/prefer-object-has-own.js +116 -82
- package/lib/rules/prefer-object-spread.js +214 -193
- package/lib/rules/prefer-promise-reject-errors.js +140 -118
- package/lib/rules/prefer-reflect.js +126 -103
- package/lib/rules/prefer-regex-literals.js +561 -463
- package/lib/rules/prefer-rest-params.js +79 -80
- package/lib/rules/prefer-spread.js +47 -43
- package/lib/rules/prefer-template.js +266 -194
- package/lib/rules/preserve-caught-error.js +535 -0
- package/lib/rules/quote-props.js +373 -289
- package/lib/rules/quotes.js +374 -308
- package/lib/rules/radix.js +152 -134
- package/lib/rules/require-atomic-updates.js +316 -282
- package/lib/rules/require-await.js +153 -82
- package/lib/rules/require-unicode-regexp.js +296 -108
- package/lib/rules/require-yield.js +53 -54
- package/lib/rules/rest-spread-spacing.js +128 -98
- package/lib/rules/semi-spacing.js +281 -232
- package/lib/rules/semi-style.js +176 -116
- package/lib/rules/semi.js +456 -418
- package/lib/rules/sort-imports.js +307 -229
- package/lib/rules/sort-keys.js +219 -181
- package/lib/rules/sort-vars.js +127 -91
- package/lib/rules/space-before-blocks.js +199 -171
- package/lib/rules/space-before-function-paren.js +186 -148
- package/lib/rules/space-in-parens.js +359 -270
- package/lib/rules/space-infix-ops.js +237 -183
- package/lib/rules/space-unary-ops.js +356 -280
- package/lib/rules/spaced-comment.js +363 -301
- package/lib/rules/strict.js +266 -229
- package/lib/rules/switch-colon-spacing.js +130 -104
- package/lib/rules/symbol-description.js +45 -48
- package/lib/rules/template-curly-spacing.js +148 -124
- package/lib/rules/template-tag-spacing.js +98 -70
- package/lib/rules/unicode-bom.js +54 -54
- package/lib/rules/use-isnan.js +237 -110
- package/lib/rules/utils/ast-utils.js +2139 -1688
- package/lib/rules/utils/char-source.js +247 -0
- package/lib/rules/utils/fix-tracker.js +99 -88
- package/lib/rules/utils/keywords.js +59 -59
- package/lib/rules/utils/lazy-loading-rule-map.js +81 -78
- package/lib/rules/utils/regular-expressions.js +35 -19
- package/lib/rules/utils/unicode/index.js +9 -4
- package/lib/rules/utils/unicode/is-combining-character.js +1 -1
- package/lib/rules/utils/unicode/is-emoji-modifier.js +1 -1
- package/lib/rules/utils/unicode/is-regional-indicator-symbol.js +1 -1
- package/lib/rules/utils/unicode/is-surrogate-pair.js +1 -1
- package/lib/rules/valid-typeof.js +153 -109
- package/lib/rules/vars-on-top.js +152 -144
- package/lib/rules/wrap-iife.js +204 -173
- package/lib/rules/wrap-regex.js +77 -47
- package/lib/rules/yield-star-spacing.js +145 -116
- package/lib/rules/yoda.js +283 -274
- package/lib/services/parser-service.js +65 -0
- package/lib/services/processor-service.js +101 -0
- package/lib/services/suppressions-service.js +302 -0
- package/lib/services/warning-service.js +98 -0
- package/lib/shared/ajv.js +14 -14
- package/lib/shared/assert.js +21 -0
- package/lib/shared/ast-utils.js +7 -6
- package/lib/shared/deep-merge-arrays.js +62 -0
- package/lib/shared/directives.js +3 -2
- package/lib/shared/flags.js +108 -0
- package/lib/shared/logging.js +24 -16
- package/lib/shared/naming.js +109 -0
- package/lib/shared/option-utils.js +63 -0
- package/lib/shared/relative-module-resolver.js +18 -40
- package/lib/shared/runtime-info.js +138 -128
- package/lib/shared/serialization.js +78 -0
- package/lib/shared/severity.js +22 -22
- package/lib/shared/stats.js +30 -0
- package/lib/shared/string-utils.js +19 -21
- package/lib/shared/text-table.js +68 -0
- package/lib/shared/translate-cli-options.js +281 -0
- package/lib/shared/traverser.js +153 -146
- package/lib/types/config-api.d.ts +12 -0
- package/lib/types/index.d.ts +1473 -0
- package/lib/types/rules.d.ts +5589 -0
- package/lib/types/universal.d.ts +6 -0
- package/lib/types/use-at-your-own-risk.d.ts +87 -0
- package/lib/universal.js +10 -0
- package/lib/unsupported-api.js +8 -9
- package/messages/all-files-ignored.js +3 -3
- package/messages/all-matched-files-ignored.js +21 -0
- package/messages/config-file-missing.js +16 -0
- package/messages/config-plugin-missing.js +14 -0
- package/messages/config-serialize-function.js +30 -0
- package/messages/eslintrc-incompat.js +35 -16
- package/messages/eslintrc-plugins.js +8 -5
- package/messages/extend-config-missing.js +3 -3
- package/messages/failed-to-read-json.js +3 -3
- package/messages/file-not-found.js +3 -3
- package/messages/invalid-rule-options.js +2 -2
- package/messages/invalid-rule-severity.js +2 -2
- package/messages/no-config-found.js +4 -4
- package/messages/plugin-conflict.js +9 -9
- package/messages/plugin-invalid.js +4 -4
- package/messages/plugin-missing.js +4 -4
- package/messages/print-config-with-directory-path.js +2 -2
- package/messages/shared.js +6 -1
- package/messages/whitespace-found.js +3 -3
- package/package.json +105 -60
- package/conf/config-schema.js +0 -93
- package/lib/cli-engine/formatters/checkstyle.js +0 -60
- package/lib/cli-engine/formatters/compact.js +0 -60
- package/lib/cli-engine/formatters/jslint-xml.js +0 -41
- package/lib/cli-engine/formatters/junit.js +0 -82
- package/lib/cli-engine/formatters/tap.js +0 -95
- package/lib/cli-engine/formatters/unix.js +0 -58
- package/lib/cli-engine/formatters/visualstudio.js +0 -63
- package/lib/cli-engine/xml-escape.js +0 -34
- package/lib/config/flat-config-helpers.js +0 -111
- package/lib/config/rule-validator.js +0 -158
- package/lib/eslint/flat-eslint.js +0 -1159
- package/lib/linter/config-comment-parser.js +0 -185
- package/lib/linter/node-event-generator.js +0 -354
- package/lib/linter/report-translator.js +0 -369
- package/lib/linter/safe-emitter.js +0 -52
- package/lib/rule-tester/flat-rule-tester.js +0 -1131
- package/lib/rules/require-jsdoc.js +0 -122
- package/lib/rules/utils/patterns/letters.js +0 -36
- package/lib/rules/valid-jsdoc.js +0 -516
- package/lib/shared/config-validator.js +0 -347
- package/lib/shared/deprecation-warnings.js +0 -58
- package/lib/shared/types.js +0 -216
- package/lib/source-code/index.js +0 -5
- package/lib/source-code/source-code.js +0 -1055
- package/lib/source-code/token-store/backward-token-comment-cursor.js +0 -57
- package/lib/source-code/token-store/backward-token-cursor.js +0 -58
- package/lib/source-code/token-store/cursors.js +0 -90
- package/lib/source-code/token-store/forward-token-comment-cursor.js +0 -57
- package/lib/source-code/token-store/forward-token-cursor.js +0 -63
- package/lib/source-code/token-store/index.js +0 -627
- package/lib/source-code/token-store/padded-token-cursor.js +0 -38
- 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
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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}
|
|
107
|
-
* @returns {boolean} `true` if `
|
|
136
|
+
* @param {any} value The value to check.
|
|
137
|
+
* @returns {boolean} `true` if `value` is a non-empty string.
|
|
108
138
|
*/
|
|
109
|
-
function isNonEmptyString(
|
|
110
|
-
|
|
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}
|
|
116
|
-
* @returns {boolean} `true` if `
|
|
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
|
|
119
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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 {
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
260
|
+
basePath,
|
|
261
|
+
patterns,
|
|
262
|
+
rawPatterns,
|
|
263
|
+
configLoader,
|
|
264
|
+
errorOnUnmatchedPattern,
|
|
233
265
|
}) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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 {
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
399
|
-
|
|
410
|
+
const patternHasMatch = await globMatch({
|
|
411
|
+
basePath,
|
|
412
|
+
pattern,
|
|
413
|
+
});
|
|
400
414
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
});
|
|
415
|
+
if (patternHasMatch) {
|
|
416
|
+
throw new AllFilesIgnoredError(rawPattern);
|
|
417
|
+
}
|
|
405
418
|
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
*
|
|
419
|
-
* @param {
|
|
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({
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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 {
|
|
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
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|
-
|
|
661
|
+
return message.severity === 2;
|
|
613
662
|
}
|
|
614
663
|
|
|
615
664
|
/**
|
|
616
665
|
* Returns result with warning by ignore settings
|
|
617
|
-
* @param {string} filePath
|
|
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
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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 {
|
|
799
|
+
* @param {ESLintOptions} options The options to process.
|
|
689
800
|
* @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
|
|
690
|
-
* @returns {
|
|
801
|
+
* @returns {ESLintOptions} The normalized options.
|
|
691
802
|
*/
|
|
692
803
|
function processOptions({
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
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
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
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
|
-
|
|
861
|
-
|
|
1138
|
+
return message =>
|
|
1139
|
+
shouldMessageBeFixed(message, config, fixTypesSet) &&
|
|
1140
|
+
originalFix(message);
|
|
1141
|
+
}
|
|
862
1142
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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
|
-
|
|
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
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
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
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
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
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
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
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1382
|
+
// Add plugins
|
|
1383
|
+
if (optionPlugins) {
|
|
1384
|
+
const plugins = {};
|
|
899
1385
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
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
|
-
|
|
907
|
-
|
|
908
|
-
return getCacheFileForDirectory();
|
|
909
|
-
}
|
|
1390
|
+
defaultConfigs.push({ plugins });
|
|
1391
|
+
}
|
|
910
1392
|
|
|
911
|
-
|
|
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
|
-
|
|
921
|
-
|
|
1441
|
+
createDebug,
|
|
1442
|
+
|
|
1443
|
+
findFiles,
|
|
1444
|
+
|
|
1445
|
+
isNonEmptyString,
|
|
1446
|
+
isArrayOfNonEmptyString,
|
|
922
1447
|
|
|
923
|
-
|
|
924
|
-
|
|
1448
|
+
createIgnoreResult,
|
|
1449
|
+
isErrorMessage,
|
|
1450
|
+
calculateStatsPerFile,
|
|
1451
|
+
getPlaceholderPath,
|
|
925
1452
|
|
|
926
|
-
|
|
927
|
-
|
|
1453
|
+
processOptions,
|
|
1454
|
+
loadOptionsFromModule,
|
|
928
1455
|
|
|
929
|
-
|
|
1456
|
+
getCacheFile,
|
|
1457
|
+
createLintResultCache,
|
|
930
1458
|
|
|
931
|
-
|
|
1459
|
+
getFixerForFixTypes,
|
|
1460
|
+
verifyText,
|
|
1461
|
+
lintFile,
|
|
1462
|
+
createLinter,
|
|
1463
|
+
createDefaultConfigs,
|
|
1464
|
+
createConfigLoader,
|
|
932
1465
|
};
|