bahlint 28.58.6934-dev-001
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/LICENSE +19 -0
- package/README.md +354 -0
- package/bin/bahlint.js +267 -0
- package/bin/eslint.js +194 -0
- package/conf/ecma-version.js +16 -0
- package/conf/globals.js +169 -0
- package/conf/replacements.json +26 -0
- package/conf/rule-type-list.json +91 -0
- package/lib/api.js +39 -0
- package/lib/cli-engine/formatters/formatters-meta.json +18 -0
- package/lib/cli-engine/formatters/html.js +359 -0
- package/lib/cli-engine/formatters/json-with-metadata.js +16 -0
- package/lib/cli-engine/formatters/json.js +13 -0
- package/lib/cli-engine/formatters/stylish.js +153 -0
- package/lib/cli-engine/hash.js +35 -0
- package/lib/cli-engine/lint-result-cache.js +220 -0
- package/lib/cli.js +521 -0
- package/lib/config/config-loader.js +668 -0
- package/lib/config/config.js +674 -0
- package/lib/config/default-config.js +78 -0
- package/lib/config/flat-config-array.js +217 -0
- package/lib/config/flat-config-schema.js +598 -0
- package/lib/config-api.js +12 -0
- package/lib/eslint/eslint-helpers.js +1462 -0
- package/lib/eslint/eslint.js +1364 -0
- package/lib/eslint/index.js +7 -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 +1178 -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/languages/js/source-code/token-store/cursor.js +76 -0
- package/lib/languages/js/source-code/token-store/cursors.js +120 -0
- package/lib/languages/js/source-code/token-store/decorative-cursor.js +38 -0
- package/lib/languages/js/source-code/token-store/filter-cursor.js +42 -0
- 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 +695 -0
- package/lib/languages/js/source-code/token-store/limit-cursor.js +39 -0
- package/lib/languages/js/source-code/token-store/padded-token-cursor.js +45 -0
- package/lib/languages/js/source-code/token-store/skip-cursor.js +41 -0
- package/lib/languages/js/source-code/token-store/utils.js +131 -0
- package/lib/languages/js/validate-language-options.js +196 -0
- package/lib/linter/apply-disable-directives.js +583 -0
- package/lib/linter/code-path-analysis/code-path-analyzer.js +828 -0
- package/lib/linter/code-path-analysis/code-path-segment.js +262 -0
- package/lib/linter/code-path-analysis/code-path-state.js +2370 -0
- package/lib/linter/code-path-analysis/code-path.js +332 -0
- package/lib/linter/code-path-analysis/debug-helpers.js +223 -0
- package/lib/linter/code-path-analysis/fork-context.js +374 -0
- package/lib/linter/code-path-analysis/id-generator.js +44 -0
- package/lib/linter/esquery.js +332 -0
- package/lib/linter/file-context.js +88 -0
- package/lib/linter/file-report.js +604 -0
- package/lib/linter/index.js +11 -0
- package/lib/linter/interpolate.js +50 -0
- package/lib/linter/linter.js +1614 -0
- package/lib/linter/rule-fixer.js +199 -0
- package/lib/linter/source-code-fixer.js +154 -0
- package/lib/linter/source-code-traverser.js +333 -0
- package/lib/linter/source-code-visitor.js +81 -0
- package/lib/linter/timing.js +209 -0
- package/lib/linter/vfile.js +115 -0
- package/lib/options.js +416 -0
- package/lib/rule-tester/index.js +7 -0
- package/lib/rule-tester/rule-tester.js +1989 -0
- package/lib/rules/accessor-pairs.js +420 -0
- package/lib/rules/array-bracket-newline.js +291 -0
- package/lib/rules/array-bracket-spacing.js +301 -0
- package/lib/rules/array-callback-return.js +493 -0
- package/lib/rules/array-element-newline.js +374 -0
- package/lib/rules/arrow-body-style.js +418 -0
- package/lib/rules/arrow-parens.js +237 -0
- package/lib/rules/arrow-spacing.js +188 -0
- package/lib/rules/block-scoped-var.js +137 -0
- package/lib/rules/block-spacing.js +202 -0
- package/lib/rules/brace-style.js +278 -0
- package/lib/rules/callback-return.js +216 -0
- package/lib/rules/camelcase.js +422 -0
- package/lib/rules/capitalized-comments.js +325 -0
- package/lib/rules/class-methods-use-this.js +250 -0
- package/lib/rules/comma-dangle.js +424 -0
- package/lib/rules/comma-spacing.js +205 -0
- package/lib/rules/comma-style.js +391 -0
- package/lib/rules/complexity.js +201 -0
- package/lib/rules/computed-property-spacing.js +251 -0
- package/lib/rules/consistent-return.js +221 -0
- package/lib/rules/consistent-this.js +179 -0
- package/lib/rules/constructor-super.js +453 -0
- package/lib/rules/curly.js +425 -0
- package/lib/rules/default-case-last.js +51 -0
- package/lib/rules/default-case.js +103 -0
- package/lib/rules/default-param-last.js +78 -0
- package/lib/rules/dot-location.js +138 -0
- package/lib/rules/dot-notation.js +216 -0
- package/lib/rules/eol-last.js +135 -0
- package/lib/rules/eqeqeq.js +210 -0
- package/lib/rules/for-direction.js +168 -0
- package/lib/rules/func-call-spacing.js +281 -0
- package/lib/rules/func-name-matching.js +338 -0
- package/lib/rules/func-names.js +194 -0
- package/lib/rules/func-style.js +221 -0
- package/lib/rules/function-call-argument-newline.js +166 -0
- package/lib/rules/function-paren-newline.js +368 -0
- package/lib/rules/generator-star-spacing.js +246 -0
- package/lib/rules/getter-return.js +242 -0
- package/lib/rules/global-require.js +117 -0
- package/lib/rules/grouped-accessor-pairs.js +268 -0
- package/lib/rules/guard-for-in.js +85 -0
- package/lib/rules/handle-callback-err.js +122 -0
- package/lib/rules/id-blacklist.js +241 -0
- package/lib/rules/id-denylist.js +223 -0
- package/lib/rules/id-length.js +217 -0
- package/lib/rules/id-match.js +363 -0
- package/lib/rules/implicit-arrow-linebreak.js +125 -0
- package/lib/rules/indent-legacy.js +1369 -0
- package/lib/rules/indent.js +2334 -0
- package/lib/rules/index.js +332 -0
- package/lib/rules/init-declarations.js +172 -0
- package/lib/rules/jsx-quotes.js +128 -0
- package/lib/rules/key-spacing.js +822 -0
- package/lib/rules/keyword-spacing.js +701 -0
- package/lib/rules/line-comment-position.js +157 -0
- package/lib/rules/linebreak-style.js +135 -0
- package/lib/rules/lines-around-comment.js +581 -0
- package/lib/rules/lines-around-directive.js +249 -0
- package/lib/rules/lines-between-class-members.js +358 -0
- package/lib/rules/logical-assignment-operators.js +688 -0
- package/lib/rules/max-classes-per-file.js +90 -0
- package/lib/rules/max-depth.js +159 -0
- package/lib/rules/max-len.js +497 -0
- package/lib/rules/max-lines-per-function.js +238 -0
- package/lib/rules/max-lines.js +189 -0
- package/lib/rules/max-nested-callbacks.js +115 -0
- package/lib/rules/max-params.js +148 -0
- package/lib/rules/max-statements-per-line.js +224 -0
- package/lib/rules/max-statements.js +188 -0
- package/lib/rules/multiline-comment-style.js +652 -0
- package/lib/rules/multiline-ternary.js +257 -0
- package/lib/rules/new-cap.js +277 -0
- package/lib/rules/new-parens.js +120 -0
- package/lib/rules/newline-after-var.js +307 -0
- package/lib/rules/newline-before-return.js +242 -0
- package/lib/rules/newline-per-chained-call.js +159 -0
- package/lib/rules/no-alert.js +149 -0
- package/lib/rules/no-array-constructor.js +195 -0
- package/lib/rules/no-async-promise-executor.js +45 -0
- package/lib/rules/no-await-in-loop.js +115 -0
- package/lib/rules/no-bitwise.js +145 -0
- package/lib/rules/no-buffer-constructor.js +74 -0
- package/lib/rules/no-caller.js +52 -0
- package/lib/rules/no-case-declarations.js +80 -0
- package/lib/rules/no-catch-shadow.js +96 -0
- package/lib/rules/no-class-assign.js +66 -0
- package/lib/rules/no-compare-neg-zero.js +74 -0
- package/lib/rules/no-cond-assign.js +175 -0
- package/lib/rules/no-confusing-arrow.js +127 -0
- package/lib/rules/no-console.js +221 -0
- package/lib/rules/no-const-assign.js +73 -0
- package/lib/rules/no-constant-binary-expression.js +603 -0
- package/lib/rules/no-constant-condition.js +177 -0
- package/lib/rules/no-constructor-return.js +62 -0
- package/lib/rules/no-continue.js +38 -0
- package/lib/rules/no-control-regex.js +142 -0
- package/lib/rules/no-debugger.js +41 -0
- package/lib/rules/no-delete-var.js +42 -0
- package/lib/rules/no-div-regex.js +60 -0
- package/lib/rules/no-dupe-args.js +92 -0
- package/lib/rules/no-dupe-class-members.js +117 -0
- package/lib/rules/no-dupe-else-if.js +145 -0
- package/lib/rules/no-dupe-keys.js +165 -0
- package/lib/rules/no-duplicate-case.js +78 -0
- package/lib/rules/no-duplicate-imports.js +368 -0
- package/lib/rules/no-else-return.js +450 -0
- package/lib/rules/no-empty-character-class.js +83 -0
- package/lib/rules/no-empty-function.js +236 -0
- package/lib/rules/no-empty-pattern.js +85 -0
- package/lib/rules/no-empty-static-block.js +73 -0
- package/lib/rules/no-empty.js +153 -0
- package/lib/rules/no-eq-null.js +51 -0
- package/lib/rules/no-eval.js +295 -0
- package/lib/rules/no-ex-assign.js +57 -0
- package/lib/rules/no-extend-native.js +180 -0
- package/lib/rules/no-extra-bind.js +224 -0
- package/lib/rules/no-extra-boolean-cast.js +420 -0
- package/lib/rules/no-extra-label.js +169 -0
- package/lib/rules/no-extra-parens.js +1669 -0
- package/lib/rules/no-extra-semi.js +167 -0
- package/lib/rules/no-fallthrough.js +260 -0
- package/lib/rules/no-floating-decimal.js +99 -0
- package/lib/rules/no-func-assign.js +77 -0
- package/lib/rules/no-global-assign.js +101 -0
- package/lib/rules/no-implicit-coercion.js +468 -0
- package/lib/rules/no-implicit-globals.js +187 -0
- package/lib/rules/no-implied-eval.js +170 -0
- package/lib/rules/no-import-assign.js +227 -0
- package/lib/rules/no-inline-comments.js +115 -0
- package/lib/rules/no-inner-declarations.js +147 -0
- package/lib/rules/no-invalid-regexp.js +244 -0
- package/lib/rules/no-invalid-this.js +178 -0
- package/lib/rules/no-irregular-whitespace.js +292 -0
- package/lib/rules/no-iterator.js +48 -0
- package/lib/rules/no-label-var.js +78 -0
- package/lib/rules/no-labels.js +156 -0
- package/lib/rules/no-lone-blocks.js +140 -0
- package/lib/rules/no-lonely-if.js +126 -0
- package/lib/rules/no-loop-func.js +267 -0
- package/lib/rules/no-loss-of-precision.js +249 -0
- package/lib/rules/no-magic-numbers.js +365 -0
- package/lib/rules/no-misleading-character-class.js +595 -0
- package/lib/rules/no-mixed-operators.js +253 -0
- package/lib/rules/no-mixed-requires.js +267 -0
- package/lib/rules/no-mixed-spaces-and-tabs.js +148 -0
- package/lib/rules/no-multi-assign.js +66 -0
- package/lib/rules/no-multi-spaces.js +179 -0
- package/lib/rules/no-multi-str.js +67 -0
- package/lib/rules/no-multiple-empty-lines.js +210 -0
- package/lib/rules/no-native-reassign.js +114 -0
- package/lib/rules/no-negated-condition.js +100 -0
- package/lib/rules/no-negated-in-lhs.js +59 -0
- package/lib/rules/no-nested-ternary.js +46 -0
- package/lib/rules/no-new-func.js +96 -0
- package/lib/rules/no-new-native-nonconstructor.js +70 -0
- package/lib/rules/no-new-object.js +76 -0
- package/lib/rules/no-new-require.js +67 -0
- package/lib/rules/no-new-symbol.js +74 -0
- package/lib/rules/no-new-wrappers.js +62 -0
- package/lib/rules/no-new.js +42 -0
- package/lib/rules/no-nonoctal-decimal-escape.js +176 -0
- package/lib/rules/no-obj-calls.js +99 -0
- package/lib/rules/no-object-constructor.js +124 -0
- package/lib/rules/no-octal-escape.js +53 -0
- package/lib/rules/no-octal.js +42 -0
- package/lib/rules/no-param-reassign.js +248 -0
- package/lib/rules/no-path-concat.js +79 -0
- package/lib/rules/no-plusplus.js +102 -0
- package/lib/rules/no-process-env.js +68 -0
- package/lib/rules/no-process-exit.js +67 -0
- package/lib/rules/no-promise-executor-return.js +264 -0
- package/lib/rules/no-proto.js +45 -0
- package/lib/rules/no-prototype-builtins.js +181 -0
- package/lib/rules/no-redeclare.js +173 -0
- package/lib/rules/no-regex-spaces.js +219 -0
- package/lib/rules/no-restricted-exports.js +227 -0
- package/lib/rules/no-restricted-globals.js +266 -0
- package/lib/rules/no-restricted-imports.js +892 -0
- package/lib/rules/no-restricted-modules.js +249 -0
- package/lib/rules/no-restricted-properties.js +233 -0
- package/lib/rules/no-restricted-syntax.js +74 -0
- package/lib/rules/no-return-assign.js +87 -0
- package/lib/rules/no-return-await.js +162 -0
- package/lib/rules/no-script-url.js +68 -0
- package/lib/rules/no-self-assign.js +186 -0
- package/lib/rules/no-self-compare.js +77 -0
- package/lib/rules/no-sequences.js +158 -0
- package/lib/rules/no-setter-return.js +224 -0
- package/lib/rules/no-shadow-restricted-names.js +113 -0
- package/lib/rules/no-shadow.js +624 -0
- package/lib/rules/no-spaced-func.js +105 -0
- package/lib/rules/no-sparse-arrays.js +68 -0
- package/lib/rules/no-sync.js +81 -0
- package/lib/rules/no-tabs.js +110 -0
- package/lib/rules/no-template-curly-in-string.js +45 -0
- package/lib/rules/no-ternary.js +38 -0
- package/lib/rules/no-this-before-super.js +365 -0
- package/lib/rules/no-throw-literal.js +46 -0
- package/lib/rules/no-trailing-spaces.js +227 -0
- package/lib/rules/no-unassigned-vars.js +80 -0
- package/lib/rules/no-undef-init.js +101 -0
- package/lib/rules/no-undef.js +84 -0
- package/lib/rules/no-undefined.js +85 -0
- package/lib/rules/no-underscore-dangle.js +383 -0
- package/lib/rules/no-unexpected-multiline.js +130 -0
- package/lib/rules/no-unmodified-loop-condition.js +360 -0
- package/lib/rules/no-unneeded-ternary.js +232 -0
- package/lib/rules/no-unreachable-loop.js +190 -0
- package/lib/rules/no-unreachable.js +300 -0
- package/lib/rules/no-unsafe-finally.js +119 -0
- package/lib/rules/no-unsafe-negation.js +152 -0
- package/lib/rules/no-unsafe-optional-chaining.js +221 -0
- package/lib/rules/no-unused-expressions.js +227 -0
- package/lib/rules/no-unused-labels.js +158 -0
- package/lib/rules/no-unused-private-class-members.js +219 -0
- package/lib/rules/no-unused-vars.js +1739 -0
- package/lib/rules/no-use-before-define.js +446 -0
- package/lib/rules/no-useless-assignment.js +657 -0
- package/lib/rules/no-useless-backreference.js +263 -0
- package/lib/rules/no-useless-call.js +95 -0
- package/lib/rules/no-useless-catch.js +57 -0
- package/lib/rules/no-useless-computed-key.js +204 -0
- package/lib/rules/no-useless-concat.js +121 -0
- package/lib/rules/no-useless-constructor.js +262 -0
- package/lib/rules/no-useless-escape.js +406 -0
- package/lib/rules/no-useless-rename.js +202 -0
- package/lib/rules/no-useless-return.js +401 -0
- package/lib/rules/no-var.js +367 -0
- package/lib/rules/no-void.js +69 -0
- package/lib/rules/no-warning-comments.js +209 -0
- package/lib/rules/no-whitespace-before-property.js +150 -0
- package/lib/rules/no-with.js +37 -0
- package/lib/rules/nonblock-statement-body-position.js +164 -0
- package/lib/rules/object-curly-newline.js +383 -0
- package/lib/rules/object-curly-spacing.js +369 -0
- package/lib/rules/object-property-newline.js +151 -0
- package/lib/rules/object-shorthand.js +652 -0
- package/lib/rules/one-var-declaration-per-line.js +117 -0
- package/lib/rules/one-var.js +717 -0
- package/lib/rules/operator-assignment.js +270 -0
- package/lib/rules/operator-linebreak.js +315 -0
- package/lib/rules/padded-blocks.js +366 -0
- package/lib/rules/padding-line-between-statements.js +612 -0
- package/lib/rules/prefer-arrow-callback.js +437 -0
- package/lib/rules/prefer-const.js +546 -0
- package/lib/rules/prefer-destructuring.js +332 -0
- package/lib/rules/prefer-exponentiation-operator.js +235 -0
- package/lib/rules/prefer-named-capture-group.js +197 -0
- package/lib/rules/prefer-numeric-literals.js +157 -0
- package/lib/rules/prefer-object-has-own.js +148 -0
- package/lib/rules/prefer-object-spread.js +319 -0
- package/lib/rules/prefer-promise-reject-errors.js +154 -0
- package/lib/rules/prefer-reflect.js +150 -0
- package/lib/rules/prefer-regex-literals.js +605 -0
- package/lib/rules/prefer-rest-params.js +117 -0
- package/lib/rules/prefer-spread.js +91 -0
- package/lib/rules/prefer-template.js +347 -0
- package/lib/rules/preserve-caught-error.js +535 -0
- package/lib/rules/quote-props.js +394 -0
- package/lib/rules/quotes.js +416 -0
- package/lib/rules/radix.js +193 -0
- package/lib/rules/require-atomic-updates.js +365 -0
- package/lib/rules/require-await.js +184 -0
- package/lib/rules/require-unicode-regexp.js +317 -0
- package/lib/rules/require-yield.js +86 -0
- package/lib/rules/rest-spread-spacing.js +150 -0
- package/lib/rules/semi-spacing.js +297 -0
- package/lib/rules/semi-style.js +218 -0
- package/lib/rules/semi.js +476 -0
- package/lib/rules/sort-imports.js +319 -0
- package/lib/rules/sort-keys.js +268 -0
- package/lib/rules/sort-vars.js +140 -0
- package/lib/rules/space-before-blocks.js +232 -0
- package/lib/rules/space-before-function-paren.js +202 -0
- package/lib/rules/space-in-parens.js +374 -0
- package/lib/rules/space-infix-ops.js +249 -0
- package/lib/rules/space-unary-ops.js +400 -0
- package/lib/rules/spaced-comment.js +447 -0
- package/lib/rules/strict.js +314 -0
- package/lib/rules/switch-colon-spacing.js +158 -0
- package/lib/rules/symbol-description.js +70 -0
- package/lib/rules/template-curly-spacing.js +168 -0
- package/lib/rules/template-tag-spacing.js +121 -0
- package/lib/rules/unicode-bom.js +73 -0
- package/lib/rules/use-isnan.js +268 -0
- package/lib/rules/utils/ast-utils.js +2828 -0
- package/lib/rules/utils/char-source.js +247 -0
- package/lib/rules/utils/fix-tracker.js +125 -0
- package/lib/rules/utils/keywords.js +67 -0
- package/lib/rules/utils/lazy-loading-rule-map.js +118 -0
- package/lib/rules/utils/regular-expressions.js +58 -0
- package/lib/rules/utils/unicode/index.js +16 -0
- package/lib/rules/utils/unicode/is-combining-character.js +13 -0
- package/lib/rules/utils/unicode/is-emoji-modifier.js +13 -0
- package/lib/rules/utils/unicode/is-regional-indicator-symbol.js +13 -0
- package/lib/rules/utils/unicode/is-surrogate-pair.js +14 -0
- package/lib/rules/valid-typeof.js +171 -0
- package/lib/rules/vars-on-top.js +165 -0
- package/lib/rules/wrap-iife.js +238 -0
- package/lib/rules/wrap-regex.js +91 -0
- package/lib/rules/yield-star-spacing.js +158 -0
- package/lib/rules/yoda.js +362 -0
- package/lib/services/parser-service.js +64 -0
- package/lib/services/processor-service.js +100 -0
- package/lib/services/suppressions-service.js +302 -0
- package/lib/services/warning-service.js +87 -0
- package/lib/shared/ajv.js +34 -0
- package/lib/shared/assert.js +21 -0
- package/lib/shared/ast-utils.js +30 -0
- package/lib/shared/deep-merge-arrays.js +62 -0
- package/lib/shared/directives.js +16 -0
- package/lib/shared/flags.js +89 -0
- package/lib/shared/logging.js +38 -0
- package/lib/shared/naming.js +109 -0
- package/lib/shared/option-utils.js +63 -0
- package/lib/shared/relative-module-resolver.js +28 -0
- package/lib/shared/runtime-info.js +177 -0
- package/lib/shared/serialization.js +78 -0
- package/lib/shared/severity.js +49 -0
- package/lib/shared/stats.js +30 -0
- package/lib/shared/string-utils.js +58 -0
- package/lib/shared/text-table.js +68 -0
- package/lib/shared/translate-cli-options.js +223 -0
- package/lib/shared/traverser.js +202 -0
- package/lib/types/config-api.d.ts +12 -0
- package/lib/types/index.d.ts +1482 -0
- package/lib/types/rules.d.ts +5603 -0
- package/lib/types/universal.d.ts +6 -0
- package/lib/types/use-at-your-own-risk.d.ts +34 -0
- package/lib/universal.js +10 -0
- package/lib/unsupported-api.js +26 -0
- package/messages/all-files-ignored.js +16 -0
- 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 +117 -0
- package/messages/eslintrc-plugins.js +27 -0
- package/messages/extend-config-missing.js +13 -0
- package/messages/failed-to-read-json.js +11 -0
- package/messages/file-not-found.js +10 -0
- package/messages/invalid-rule-options.js +17 -0
- package/messages/invalid-rule-severity.js +13 -0
- package/messages/no-config-found.js +15 -0
- package/messages/plugin-conflict.js +22 -0
- package/messages/plugin-invalid.js +16 -0
- package/messages/plugin-missing.js +19 -0
- package/messages/print-config-with-directory-path.js +8 -0
- package/messages/shared.js +23 -0
- package/messages/whitespace-found.js +11 -0
- package/package.json +220 -0
|
@@ -0,0 +1,1614 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Main Linter Class
|
|
3
|
+
* @author Gyandeep Singh
|
|
4
|
+
* @author aladdin-add
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
// Requirements
|
|
11
|
+
//------------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
const path = require("node:path"),
|
|
14
|
+
eslintScope = require("eslint-scope"),
|
|
15
|
+
evk = require("eslint-visitor-keys"),
|
|
16
|
+
pkg = require("../../package.json"),
|
|
17
|
+
Traverser = require("../shared/traverser"),
|
|
18
|
+
{ SourceCode } = require("../languages/js/source-code"),
|
|
19
|
+
applyDisableDirectives = require("./apply-disable-directives"),
|
|
20
|
+
{ ConfigCommentParser } = require("@eslint/plugin-kit"),
|
|
21
|
+
SourceCodeFixer = require("./source-code-fixer"),
|
|
22
|
+
{ SourceCodeVisitor } = require("./source-code-visitor"),
|
|
23
|
+
timing = require("./timing");
|
|
24
|
+
const { FlatConfigArray } = require("../config/flat-config-array");
|
|
25
|
+
const { startTime, endTime } = require("../shared/stats");
|
|
26
|
+
const { assertIsRuleSeverity } = require("../config/flat-config-schema");
|
|
27
|
+
const {
|
|
28
|
+
normalizeSeverityToString,
|
|
29
|
+
normalizeSeverityToNumber,
|
|
30
|
+
} = require("../shared/severity");
|
|
31
|
+
const { deepMergeArrays } = require("../shared/deep-merge-arrays");
|
|
32
|
+
const {
|
|
33
|
+
activeFlags,
|
|
34
|
+
inactiveFlags,
|
|
35
|
+
getInactivityReasonMessage,
|
|
36
|
+
} = require("../shared/flags");
|
|
37
|
+
const debug = require("debug")("eslint:linter");
|
|
38
|
+
const MAX_AUTOFIX_PASSES = 10;
|
|
39
|
+
const DEFAULT_ECMA_VERSION = 5;
|
|
40
|
+
const commentParser = new ConfigCommentParser();
|
|
41
|
+
const { VFile } = require("./vfile");
|
|
42
|
+
const { ParserService } = require("../services/parser-service");
|
|
43
|
+
const { FileContext } = require("./file-context");
|
|
44
|
+
const { ProcessorService } = require("../services/processor-service");
|
|
45
|
+
const { containsDifferentProperty } = require("../shared/option-utils");
|
|
46
|
+
const { Config } = require("../config/config");
|
|
47
|
+
const { WarningService } = require("../services/warning-service");
|
|
48
|
+
const { SourceCodeTraverser } = require("./source-code-traverser");
|
|
49
|
+
const { FileReport, updateLocationInformation } = require("./file-report");
|
|
50
|
+
|
|
51
|
+
//------------------------------------------------------------------------------
|
|
52
|
+
// Typedefs
|
|
53
|
+
//------------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
/** @import { Language, LanguageOptions, RuleConfig, RuleDefinition } from "@eslint/core" */
|
|
56
|
+
|
|
57
|
+
/** @typedef {import("../types").Linter.Config} ConfigObject */
|
|
58
|
+
/** @typedef {import("../types").Linter.LanguageOptions} JSLanguageOptions */
|
|
59
|
+
/** @typedef {import("../types").Linter.LintMessage} LintMessage */
|
|
60
|
+
/** @typedef {import("../types").Linter.Parser} Parser */
|
|
61
|
+
/** @typedef {import("../types").Linter.ParserOptions} ParserOptions */
|
|
62
|
+
/** @typedef {import("../types").Linter.Processor} Processor */
|
|
63
|
+
/** @typedef {import("../types").Rule.RuleModule} Rule */
|
|
64
|
+
/** @typedef {import("../types").Linter.StringSeverity} StringSeverity */
|
|
65
|
+
/** @typedef {import("../types").Linter.SuppressedLintMessage} SuppressedLintMessage */
|
|
66
|
+
/** @typedef {import("../types").Linter.TimePass} TimePass */
|
|
67
|
+
|
|
68
|
+
/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
|
|
69
|
+
/**
|
|
70
|
+
* @template T
|
|
71
|
+
* @typedef {{ [P in keyof T]-?: T[P] }} Required
|
|
72
|
+
*/
|
|
73
|
+
/* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @typedef {Object} DisableDirective
|
|
77
|
+
* @property {("disable"|"enable"|"disable-line"|"disable-next-line")} type Type of directive
|
|
78
|
+
* @property {number} line The line number
|
|
79
|
+
* @property {number} column The column number
|
|
80
|
+
* @property {(string|null)} ruleId The rule ID
|
|
81
|
+
* @property {string} justification The justification of directive
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The private data for `Linter` instance.
|
|
86
|
+
* @typedef {Object} LinterInternalSlots
|
|
87
|
+
* @property {FlatConfigArray|null} lastConfigArray The `ConfigArray` instance that the last `verify()` call used.
|
|
88
|
+
* @property {SourceCode|null} lastSourceCode The `SourceCode` instance that the last `verify()` call used.
|
|
89
|
+
* @property {SuppressedLintMessage[]} lastSuppressedMessages The `SuppressedLintMessage[]` instance that the last `verify()` call produced.
|
|
90
|
+
* @property {{ passes: TimePass[]; }} times The times spent on applying a rule to a file (see `stats` option).
|
|
91
|
+
* @property {WarningService} warningService The warning service.
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @typedef {Object} VerifyOptions
|
|
96
|
+
* @property {boolean} [allowInlineConfig] Allow/disallow inline comments' ability
|
|
97
|
+
* to change config once it is set. Defaults to true if not supplied.
|
|
98
|
+
* Useful if you want to validate JS without comments overriding rules.
|
|
99
|
+
* @property {boolean} [disableFixes] if `true` then the linter doesn't make `fix`
|
|
100
|
+
* properties into the lint result.
|
|
101
|
+
* @property {string} [filename] the filename of the source code.
|
|
102
|
+
* @property {boolean | "off" | "warn" | "error"} [reportUnusedDisableDirectives] Adds reported errors for
|
|
103
|
+
* unused `eslint-disable` directives.
|
|
104
|
+
* @property {Function} [ruleFilter] A predicate function that determines whether a given rule should run.
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @typedef {Object} ProcessorOptions
|
|
109
|
+
* @property {(filename:string, text:string) => boolean} [filterCodeBlock] the
|
|
110
|
+
* predicate function that selects adopt code blocks.
|
|
111
|
+
* @property {Processor.postprocess} [postprocess] postprocessor for report
|
|
112
|
+
* messages. If provided, this should accept an array of the message lists
|
|
113
|
+
* for each code block returned from the preprocessor, apply a mapping to
|
|
114
|
+
* the messages as appropriate, and return a one-dimensional array of
|
|
115
|
+
* messages.
|
|
116
|
+
* @property {Processor.preprocess} [preprocess] preprocessor for source text.
|
|
117
|
+
* If provided, this should accept a string of source text, and return an
|
|
118
|
+
* array of code blocks to lint.
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @typedef {Object} FixOptions
|
|
123
|
+
* @property {boolean | ((message: LintMessage) => boolean)} [fix] Determines
|
|
124
|
+
* whether fixes should be applied.
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @typedef {Object} InternalOptions
|
|
129
|
+
* @property {string | null} warnInlineConfig The config name what `noInlineConfig` setting came from. If `noInlineConfig` setting didn't exist, this is null. If this is a config name, then the linter warns directive comments.
|
|
130
|
+
* @property {StringSeverity} reportUnusedDisableDirectives Severity to report unused disable directives, if not "off" (boolean values were normalized).
|
|
131
|
+
* @property {StringSeverity} reportUnusedInlineConfigs Severity to report unused inline configs, if not "off".
|
|
132
|
+
*/
|
|
133
|
+
|
|
134
|
+
//------------------------------------------------------------------------------
|
|
135
|
+
// Helpers
|
|
136
|
+
//------------------------------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Wraps the value in an Array if it isn't already one.
|
|
140
|
+
* @template T
|
|
141
|
+
* @param {T|T[]} value Value to be wrapped.
|
|
142
|
+
* @returns {Array} The value as an array.
|
|
143
|
+
*/
|
|
144
|
+
function asArray(value) {
|
|
145
|
+
return Array.isArray(value) ? value : [value];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Pushes a problem to inlineConfigProblems if ruleOptions are redundant.
|
|
150
|
+
* @param {Config} config Provided config.
|
|
151
|
+
* @param {Object} loc A line/column location
|
|
152
|
+
* @param {FileReport} report Report that may be added to.
|
|
153
|
+
* @param {string} ruleId The rule ID.
|
|
154
|
+
* @param {Array} ruleOptions The rule options, merged with the config's.
|
|
155
|
+
* @param {Array} ruleOptionsInline The rule options from the comment.
|
|
156
|
+
* @param {"error"|"warn"} severity The severity to report.
|
|
157
|
+
* @returns {void}
|
|
158
|
+
*/
|
|
159
|
+
function addProblemIfSameSeverityAndOptions(
|
|
160
|
+
config,
|
|
161
|
+
loc,
|
|
162
|
+
report,
|
|
163
|
+
ruleId,
|
|
164
|
+
ruleOptions,
|
|
165
|
+
ruleOptionsInline,
|
|
166
|
+
severity,
|
|
167
|
+
) {
|
|
168
|
+
const existingConfigRaw = config.rules?.[ruleId];
|
|
169
|
+
const existingConfig = existingConfigRaw
|
|
170
|
+
? asArray(existingConfigRaw)
|
|
171
|
+
: ["off"];
|
|
172
|
+
const existingSeverity = normalizeSeverityToString(existingConfig[0]);
|
|
173
|
+
const inlineSeverity = normalizeSeverityToString(ruleOptions[0]);
|
|
174
|
+
const sameSeverity = existingSeverity === inlineSeverity;
|
|
175
|
+
|
|
176
|
+
if (!sameSeverity) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const alreadyConfigured = existingConfigRaw
|
|
181
|
+
? `is already configured to '${existingSeverity}'`
|
|
182
|
+
: "is not enabled so can't be turned off";
|
|
183
|
+
let message;
|
|
184
|
+
|
|
185
|
+
if (
|
|
186
|
+
(existingConfig.length === 1 && ruleOptions.length === 1) ||
|
|
187
|
+
existingSeverity === "off"
|
|
188
|
+
) {
|
|
189
|
+
message = `Unused inline config ('${ruleId}' ${alreadyConfigured}).`;
|
|
190
|
+
} else if (
|
|
191
|
+
!containsDifferentProperty(
|
|
192
|
+
ruleOptions.slice(1),
|
|
193
|
+
existingConfig.slice(1),
|
|
194
|
+
)
|
|
195
|
+
) {
|
|
196
|
+
message =
|
|
197
|
+
ruleOptionsInline.length === 1
|
|
198
|
+
? `Unused inline config ('${ruleId}' ${alreadyConfigured}).`
|
|
199
|
+
: `Unused inline config ('${ruleId}' ${alreadyConfigured} with the same options).`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (message) {
|
|
203
|
+
const numericSeverity = normalizeSeverityToNumber(severity);
|
|
204
|
+
const descriptor = {
|
|
205
|
+
message,
|
|
206
|
+
loc,
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
if (numericSeverity === 1) {
|
|
210
|
+
report.addWarning(descriptor);
|
|
211
|
+
} else if (numericSeverity === 2) {
|
|
212
|
+
report.addError(descriptor);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Creates a collection of disable directives from a comment
|
|
219
|
+
* @param {Object} options to create disable directives
|
|
220
|
+
* @param {("disable"|"enable"|"disable-line"|"disable-next-line")} options.type The type of directive comment
|
|
221
|
+
* @param {string} options.value The value after the directive in the comment
|
|
222
|
+
* comment specified no specific rules, so it applies to all rules (e.g. `eslint-disable`)
|
|
223
|
+
* @param {string} options.justification The justification of the directive
|
|
224
|
+
* @param {ASTNode|token} options.node The Comment node/token.
|
|
225
|
+
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
|
|
226
|
+
* @param {Language} language The language to use to adjust the location information.
|
|
227
|
+
* @param {SourceCode} sourceCode The SourceCode object to get comments from.
|
|
228
|
+
* @param {FileReport} report The report to add problems to.
|
|
229
|
+
* @returns {Object[]} Directives from the comment
|
|
230
|
+
*/
|
|
231
|
+
function createDisableDirectives(
|
|
232
|
+
{ type, value, justification, node },
|
|
233
|
+
ruleMapper,
|
|
234
|
+
language,
|
|
235
|
+
sourceCode,
|
|
236
|
+
report,
|
|
237
|
+
) {
|
|
238
|
+
const ruleIds = Object.keys(commentParser.parseListConfig(value));
|
|
239
|
+
const directiveRules = ruleIds.length ? ruleIds : [null];
|
|
240
|
+
const directives = []; // valid disable directives
|
|
241
|
+
const parentDirective = { node, value, ruleIds };
|
|
242
|
+
|
|
243
|
+
for (const ruleId of directiveRules) {
|
|
244
|
+
const loc = sourceCode.getLoc(node);
|
|
245
|
+
|
|
246
|
+
// push to directives, if the rule is defined(including null, e.g. /*eslint enable*/)
|
|
247
|
+
if (ruleId === null || !!ruleMapper(ruleId)) {
|
|
248
|
+
if (type === "disable-next-line") {
|
|
249
|
+
const { line, column } = updateLocationInformation(
|
|
250
|
+
loc.end,
|
|
251
|
+
language,
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
directives.push({
|
|
255
|
+
parentDirective,
|
|
256
|
+
type,
|
|
257
|
+
line,
|
|
258
|
+
column,
|
|
259
|
+
ruleId,
|
|
260
|
+
justification,
|
|
261
|
+
});
|
|
262
|
+
} else {
|
|
263
|
+
const { line, column } = updateLocationInformation(
|
|
264
|
+
loc.start,
|
|
265
|
+
language,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
directives.push({
|
|
269
|
+
parentDirective,
|
|
270
|
+
type,
|
|
271
|
+
line,
|
|
272
|
+
column,
|
|
273
|
+
ruleId,
|
|
274
|
+
justification,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
report.addError({ ruleId, loc });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return directives;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Parses comments in file to extract disable directives.
|
|
287
|
+
* @param {SourceCode} sourceCode The SourceCode object to get comments from.
|
|
288
|
+
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
|
|
289
|
+
* @param {Language} language The language to use to adjust the location information
|
|
290
|
+
* @param {FileReport} report The report to add problems to.
|
|
291
|
+
* @returns {DisableDirective[]}
|
|
292
|
+
* A collection of the directive comments that were found, along with any problems that occurred when parsing
|
|
293
|
+
*/
|
|
294
|
+
function getDirectiveCommentsForFlatConfig(
|
|
295
|
+
sourceCode,
|
|
296
|
+
ruleMapper,
|
|
297
|
+
language,
|
|
298
|
+
report,
|
|
299
|
+
) {
|
|
300
|
+
const disableDirectives = [];
|
|
301
|
+
|
|
302
|
+
if (sourceCode.getDisableDirectives) {
|
|
303
|
+
const { directives: directivesSources, problems: directivesProblems } =
|
|
304
|
+
sourceCode.getDisableDirectives();
|
|
305
|
+
|
|
306
|
+
if (Array.isArray(directivesProblems)) {
|
|
307
|
+
directivesProblems.forEach(problem => report.addError(problem));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
directivesSources.forEach(directive => {
|
|
311
|
+
const directives = createDisableDirectives(
|
|
312
|
+
directive,
|
|
313
|
+
ruleMapper,
|
|
314
|
+
language,
|
|
315
|
+
sourceCode,
|
|
316
|
+
report,
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
disableDirectives.push(...directives);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return disableDirectives;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Convert "/path/to/<text>" to "<text>".
|
|
328
|
+
* `CLIEngine#executeOnText()` method gives "/path/to/<text>" if the filename
|
|
329
|
+
* was omitted because `configArray.extractConfig()` requires an absolute path.
|
|
330
|
+
* But the linter should pass `<text>` to `RuleContext#filename` in that
|
|
331
|
+
* case.
|
|
332
|
+
* Also, code blocks can have their virtual filename. If the parent filename was
|
|
333
|
+
* `<text>`, the virtual filename is `<text>/0_foo.js` or something like (i.e.,
|
|
334
|
+
* it's not an absolute path).
|
|
335
|
+
* @param {string} filename The filename to normalize.
|
|
336
|
+
* @returns {string} The normalized filename.
|
|
337
|
+
*/
|
|
338
|
+
function normalizeFilename(filename) {
|
|
339
|
+
const parts = filename.split(path.sep);
|
|
340
|
+
const index = parts.lastIndexOf("<text>");
|
|
341
|
+
|
|
342
|
+
return index === -1 ? filename : parts.slice(index).join(path.sep);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Normalizes the possible options for `linter.verify` and `linter.verifyAndFix` to a
|
|
347
|
+
* consistent shape.
|
|
348
|
+
* @param {VerifyOptions} providedOptions Options
|
|
349
|
+
* @param {Config} config Config.
|
|
350
|
+
* @returns {Required<VerifyOptions> & InternalOptions} Normalized options
|
|
351
|
+
*/
|
|
352
|
+
function normalizeVerifyOptions(providedOptions, config) {
|
|
353
|
+
const linterOptions = config.linterOptions || config;
|
|
354
|
+
const disableInlineConfig = linterOptions.noInlineConfig === true;
|
|
355
|
+
const ignoreInlineConfig = providedOptions.allowInlineConfig === false;
|
|
356
|
+
const configNameOfNoInlineConfig = config.configNameOfNoInlineConfig
|
|
357
|
+
? ` (${config.configNameOfNoInlineConfig})`
|
|
358
|
+
: "";
|
|
359
|
+
|
|
360
|
+
let reportUnusedDisableDirectives =
|
|
361
|
+
providedOptions.reportUnusedDisableDirectives;
|
|
362
|
+
|
|
363
|
+
if (typeof reportUnusedDisableDirectives === "boolean") {
|
|
364
|
+
reportUnusedDisableDirectives = reportUnusedDisableDirectives
|
|
365
|
+
? "error"
|
|
366
|
+
: "off";
|
|
367
|
+
}
|
|
368
|
+
if (typeof reportUnusedDisableDirectives !== "string") {
|
|
369
|
+
if (typeof linterOptions.reportUnusedDisableDirectives === "boolean") {
|
|
370
|
+
reportUnusedDisableDirectives =
|
|
371
|
+
linterOptions.reportUnusedDisableDirectives ? "warn" : "off";
|
|
372
|
+
} else {
|
|
373
|
+
reportUnusedDisableDirectives =
|
|
374
|
+
linterOptions.reportUnusedDisableDirectives === void 0
|
|
375
|
+
? "off"
|
|
376
|
+
: normalizeSeverityToString(
|
|
377
|
+
linterOptions.reportUnusedDisableDirectives,
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const reportUnusedInlineConfigs =
|
|
383
|
+
linterOptions.reportUnusedInlineConfigs === void 0
|
|
384
|
+
? "off"
|
|
385
|
+
: normalizeSeverityToString(
|
|
386
|
+
linterOptions.reportUnusedInlineConfigs,
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
let ruleFilter = providedOptions.ruleFilter;
|
|
390
|
+
|
|
391
|
+
if (typeof ruleFilter !== "function") {
|
|
392
|
+
ruleFilter = () => true;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
filename: normalizeFilename(providedOptions.filename || "<input>"),
|
|
397
|
+
allowInlineConfig: !ignoreInlineConfig,
|
|
398
|
+
warnInlineConfig:
|
|
399
|
+
disableInlineConfig && !ignoreInlineConfig
|
|
400
|
+
? `your config${configNameOfNoInlineConfig}`
|
|
401
|
+
: null,
|
|
402
|
+
reportUnusedDisableDirectives,
|
|
403
|
+
reportUnusedInlineConfigs,
|
|
404
|
+
disableFixes: Boolean(providedOptions.disableFixes),
|
|
405
|
+
stats: providedOptions.stats,
|
|
406
|
+
ruleFilter,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Store time measurements in map
|
|
412
|
+
* @param {number} time Time measurement
|
|
413
|
+
* @param {Object} timeOpts Options relating which time was measured
|
|
414
|
+
* @param {WeakMap<Linter, LinterInternalSlots>} slots Linter internal slots map
|
|
415
|
+
* @returns {void}
|
|
416
|
+
*/
|
|
417
|
+
function storeTime(time, timeOpts, slots) {
|
|
418
|
+
const { type, key } = timeOpts;
|
|
419
|
+
|
|
420
|
+
if (!slots.times) {
|
|
421
|
+
slots.times = { passes: [{}] };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const passIndex = slots.fixPasses;
|
|
425
|
+
|
|
426
|
+
if (passIndex > slots.times.passes.length - 1) {
|
|
427
|
+
slots.times.passes.push({});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (key) {
|
|
431
|
+
slots.times.passes[passIndex][type] ??= {};
|
|
432
|
+
slots.times.passes[passIndex][type][key] ??= { total: 0 };
|
|
433
|
+
slots.times.passes[passIndex][type][key].total += time;
|
|
434
|
+
} else {
|
|
435
|
+
slots.times.passes[passIndex][type] ??= { total: 0 };
|
|
436
|
+
slots.times.passes[passIndex][type].total += time;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Get the options for a rule (not including severity), if any
|
|
442
|
+
* @param {RuleConfig} ruleConfig rule configuration
|
|
443
|
+
* @param {Object|undefined} defaultOptions rule.meta.defaultOptions
|
|
444
|
+
* @returns {Array} of rule options, empty Array if none
|
|
445
|
+
*/
|
|
446
|
+
function getRuleOptions(ruleConfig, defaultOptions) {
|
|
447
|
+
if (Array.isArray(ruleConfig)) {
|
|
448
|
+
return deepMergeArrays(defaultOptions, ruleConfig.slice(1));
|
|
449
|
+
}
|
|
450
|
+
return defaultOptions ?? [];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Analyze scope of the given AST.
|
|
455
|
+
* @param {ASTNode} ast The `Program` node to analyze.
|
|
456
|
+
* @param {JSLanguageOptions} languageOptions The language options.
|
|
457
|
+
* @param {Record<string, string[]>} visitorKeys The visitor keys.
|
|
458
|
+
* @returns {ScopeManager} The analysis result.
|
|
459
|
+
*/
|
|
460
|
+
function analyzeScope(ast, languageOptions, visitorKeys) {
|
|
461
|
+
const parserOptions = languageOptions.parserOptions;
|
|
462
|
+
const ecmaFeatures = parserOptions.ecmaFeatures || {};
|
|
463
|
+
const ecmaVersion = languageOptions.ecmaVersion || DEFAULT_ECMA_VERSION;
|
|
464
|
+
|
|
465
|
+
return eslintScope.analyze(ast, {
|
|
466
|
+
ignoreEval: true,
|
|
467
|
+
nodejsScope: ecmaFeatures.globalReturn,
|
|
468
|
+
impliedStrict: ecmaFeatures.impliedStrict,
|
|
469
|
+
ecmaVersion: typeof ecmaVersion === "number" ? ecmaVersion : 6,
|
|
470
|
+
sourceType: languageOptions.sourceType || "script",
|
|
471
|
+
childVisitorKeys: visitorKeys || evk.KEYS,
|
|
472
|
+
fallback: Traverser.getKeys,
|
|
473
|
+
jsx: ecmaFeatures.jsx,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Runs a rule, and gets its listeners
|
|
479
|
+
* @param {RuleDefinition} rule A rule object
|
|
480
|
+
* @param {Context} ruleContext The context that should be passed to the rule
|
|
481
|
+
* @throws {TypeError} If `rule` is not an object with a `create` method
|
|
482
|
+
* @throws {any} Any error during the rule's `create`
|
|
483
|
+
* @returns {Object} A map of selector listeners provided by the rule
|
|
484
|
+
*/
|
|
485
|
+
function createRuleListeners(rule, ruleContext) {
|
|
486
|
+
if (
|
|
487
|
+
!rule ||
|
|
488
|
+
typeof rule !== "object" ||
|
|
489
|
+
typeof rule.create !== "function"
|
|
490
|
+
) {
|
|
491
|
+
throw new TypeError(
|
|
492
|
+
`Error while loading rule '${ruleContext.id}': Rule must be an object with a \`create\` method`,
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
try {
|
|
497
|
+
return rule.create(ruleContext);
|
|
498
|
+
} catch (ex) {
|
|
499
|
+
ex.message = `Error while loading rule '${ruleContext.id}': ${ex.message}`;
|
|
500
|
+
throw ex;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Runs the given rules on the given SourceCode object
|
|
506
|
+
* @param {SourceCode} sourceCode A SourceCode object for the given text
|
|
507
|
+
* @param {Object} configuredRules The rules configuration
|
|
508
|
+
* @param {function(string): RuleDefinition} ruleMapper A mapper function from rule names to rules
|
|
509
|
+
* @param {Language} language The language object used for parsing.
|
|
510
|
+
* @param {LanguageOptions} languageOptions The options for parsing the code.
|
|
511
|
+
* @param {Object} settings The settings that were enabled in the config
|
|
512
|
+
* @param {string} filename The reported filename of the code
|
|
513
|
+
* @param {boolean} applyDefaultOptions If true, apply rules' meta.defaultOptions in computing their config options.
|
|
514
|
+
* @param {string | undefined} cwd cwd of the cli
|
|
515
|
+
* @param {string} physicalFilename The full path of the file on disk without any code block information
|
|
516
|
+
* @param {Function} ruleFilter A predicate function to filter which rules should be executed.
|
|
517
|
+
* @param {boolean} stats If true, stats are collected appended to the result
|
|
518
|
+
* @param {WeakMap<Linter, LinterInternalSlots>} slots InternalSlotsMap of linter
|
|
519
|
+
* @param {FileReport} report The report to add problems to
|
|
520
|
+
* @returns {FileReport} report The report with added problems
|
|
521
|
+
* @throws {Error} If traversal into a node fails.
|
|
522
|
+
*/
|
|
523
|
+
function runRules(
|
|
524
|
+
sourceCode,
|
|
525
|
+
configuredRules,
|
|
526
|
+
ruleMapper,
|
|
527
|
+
language,
|
|
528
|
+
languageOptions,
|
|
529
|
+
settings,
|
|
530
|
+
filename,
|
|
531
|
+
applyDefaultOptions,
|
|
532
|
+
cwd,
|
|
533
|
+
physicalFilename,
|
|
534
|
+
ruleFilter,
|
|
535
|
+
stats,
|
|
536
|
+
slots,
|
|
537
|
+
report,
|
|
538
|
+
) {
|
|
539
|
+
const visitor = new SourceCodeVisitor();
|
|
540
|
+
|
|
541
|
+
/*
|
|
542
|
+
* Create a frozen object with the ruleContext properties and methods that are shared by all rules.
|
|
543
|
+
* All rule contexts will inherit from this object. This avoids the performance penalty of copying all the
|
|
544
|
+
* properties once for each rule.
|
|
545
|
+
*/
|
|
546
|
+
const fileContext = new FileContext({
|
|
547
|
+
cwd,
|
|
548
|
+
filename,
|
|
549
|
+
physicalFilename: physicalFilename || filename,
|
|
550
|
+
sourceCode,
|
|
551
|
+
languageOptions,
|
|
552
|
+
settings,
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
const steps = sourceCode.traverse();
|
|
556
|
+
|
|
557
|
+
Object.keys(configuredRules).forEach(ruleId => {
|
|
558
|
+
const severity = Config.getRuleNumericSeverity(configuredRules[ruleId]);
|
|
559
|
+
|
|
560
|
+
// not load disabled rules
|
|
561
|
+
if (severity === 0) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (ruleFilter && !ruleFilter({ ruleId, severity })) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const rule = ruleMapper(ruleId);
|
|
570
|
+
|
|
571
|
+
if (!rule) {
|
|
572
|
+
report.addError({ ruleId });
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const ruleContext = fileContext.extend({
|
|
577
|
+
id: ruleId,
|
|
578
|
+
options: getRuleOptions(
|
|
579
|
+
configuredRules[ruleId],
|
|
580
|
+
applyDefaultOptions ? rule.meta?.defaultOptions : void 0,
|
|
581
|
+
),
|
|
582
|
+
report(...args) {
|
|
583
|
+
const problem = report.addRuleMessage(
|
|
584
|
+
ruleId,
|
|
585
|
+
severity,
|
|
586
|
+
...args,
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
if (problem.fix && !(rule.meta && rule.meta.fixable)) {
|
|
590
|
+
throw new Error(
|
|
591
|
+
'Fixable rules must set the `meta.fixable` property to "code" or "whitespace".',
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (
|
|
596
|
+
problem.suggestions &&
|
|
597
|
+
!(rule.meta && rule.meta.hasSuggestions === true)
|
|
598
|
+
) {
|
|
599
|
+
if (
|
|
600
|
+
rule.meta &&
|
|
601
|
+
rule.meta.docs &&
|
|
602
|
+
typeof rule.meta.docs.suggestion !== "undefined"
|
|
603
|
+
) {
|
|
604
|
+
// Encourage migration from the former property name.
|
|
605
|
+
throw new Error(
|
|
606
|
+
"Rules with suggestions must set the `meta.hasSuggestions` property to `true`. `meta.docs.suggestion` is ignored by ESLint.",
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
throw new Error(
|
|
610
|
+
"Rules with suggestions must set the `meta.hasSuggestions` property to `true`.",
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
const ruleListenersReturn =
|
|
617
|
+
timing.enabled || stats
|
|
618
|
+
? timing.time(
|
|
619
|
+
ruleId,
|
|
620
|
+
createRuleListeners,
|
|
621
|
+
stats,
|
|
622
|
+
)(rule, ruleContext)
|
|
623
|
+
: createRuleListeners(rule, ruleContext);
|
|
624
|
+
|
|
625
|
+
const ruleListeners = stats
|
|
626
|
+
? ruleListenersReturn.result
|
|
627
|
+
: ruleListenersReturn;
|
|
628
|
+
|
|
629
|
+
if (stats) {
|
|
630
|
+
storeTime(
|
|
631
|
+
ruleListenersReturn.tdiff,
|
|
632
|
+
{ type: "rules", key: ruleId },
|
|
633
|
+
slots,
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Include `ruleId` in error logs
|
|
639
|
+
* @param {Function} ruleListener A rule method that listens for a node.
|
|
640
|
+
* @returns {Function} ruleListener wrapped in error handler
|
|
641
|
+
*/
|
|
642
|
+
function addRuleErrorHandler(ruleListener) {
|
|
643
|
+
return function ruleErrorHandler(...listenerArgs) {
|
|
644
|
+
try {
|
|
645
|
+
const ruleListenerReturn = ruleListener(...listenerArgs);
|
|
646
|
+
|
|
647
|
+
const ruleListenerResult = stats
|
|
648
|
+
? ruleListenerReturn.result
|
|
649
|
+
: ruleListenerReturn;
|
|
650
|
+
|
|
651
|
+
if (stats) {
|
|
652
|
+
storeTime(
|
|
653
|
+
ruleListenerReturn.tdiff,
|
|
654
|
+
{ type: "rules", key: ruleId },
|
|
655
|
+
slots,
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return ruleListenerResult;
|
|
660
|
+
} catch (e) {
|
|
661
|
+
e.ruleId = ruleId;
|
|
662
|
+
throw e;
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (typeof ruleListeners === "undefined" || ruleListeners === null) {
|
|
668
|
+
throw new Error(
|
|
669
|
+
`The create() function for rule '${ruleId}' did not return an object.`,
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// add all the selectors from the rule as listeners
|
|
674
|
+
Object.keys(ruleListeners).forEach(selector => {
|
|
675
|
+
const ruleListener =
|
|
676
|
+
timing.enabled || stats
|
|
677
|
+
? timing.time(ruleId, ruleListeners[selector], stats)
|
|
678
|
+
: ruleListeners[selector];
|
|
679
|
+
|
|
680
|
+
visitor.add(selector, addRuleErrorHandler(ruleListener));
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
const traverser = SourceCodeTraverser.getInstance(language);
|
|
685
|
+
|
|
686
|
+
traverser.traverseSync(sourceCode, visitor, { steps });
|
|
687
|
+
|
|
688
|
+
return report;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Ensure the source code to be a string.
|
|
693
|
+
* @param {string|SourceCode} textOrSourceCode The text or source code object.
|
|
694
|
+
* @returns {string} The source code text.
|
|
695
|
+
*/
|
|
696
|
+
function ensureText(textOrSourceCode) {
|
|
697
|
+
if (typeof textOrSourceCode === "object") {
|
|
698
|
+
const { hasBOM, text } = textOrSourceCode;
|
|
699
|
+
const bom = hasBOM ? "\uFEFF" : "";
|
|
700
|
+
|
|
701
|
+
return bom + text;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return String(textOrSourceCode);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Normalize the value of the cwd
|
|
709
|
+
* @param {string | undefined} cwd raw value of the cwd, path to a directory that should be considered as the current working directory, can be undefined.
|
|
710
|
+
* @returns {string | undefined} normalized cwd
|
|
711
|
+
*/
|
|
712
|
+
function normalizeCwd(cwd) {
|
|
713
|
+
if (cwd) {
|
|
714
|
+
return cwd;
|
|
715
|
+
}
|
|
716
|
+
if (typeof process === "object") {
|
|
717
|
+
return process.cwd();
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// It's more explicit to assign the undefined
|
|
721
|
+
|
|
722
|
+
return undefined;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* The map to store private data.
|
|
727
|
+
* @type {WeakMap<Linter, LinterInternalSlots>}
|
|
728
|
+
*/
|
|
729
|
+
const internalSlotsMap = new WeakMap();
|
|
730
|
+
|
|
731
|
+
//------------------------------------------------------------------------------
|
|
732
|
+
// Public Interface
|
|
733
|
+
//------------------------------------------------------------------------------
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Object that is responsible for verifying JavaScript text
|
|
737
|
+
* @name Linter
|
|
738
|
+
*/
|
|
739
|
+
class Linter {
|
|
740
|
+
/**
|
|
741
|
+
* Initialize the Linter.
|
|
742
|
+
* @param {Object} [config] the config object
|
|
743
|
+
* @param {string} [config.cwd] path to a directory that should be considered as the current working directory, can be undefined.
|
|
744
|
+
* @param {Array<string>} [config.flags] the feature flags to enable.
|
|
745
|
+
* @param {"flat"} [config.configType="flat"] the type of config used. Retrained for backwards compatibility, will be removed in future.
|
|
746
|
+
* @param {WarningService} [config.warningService] The warning service to use.
|
|
747
|
+
*/
|
|
748
|
+
constructor({
|
|
749
|
+
cwd,
|
|
750
|
+
configType = "flat",
|
|
751
|
+
flags = [],
|
|
752
|
+
warningService = new WarningService(),
|
|
753
|
+
} = {}) {
|
|
754
|
+
const processedFlags = [];
|
|
755
|
+
|
|
756
|
+
if (configType !== "flat") {
|
|
757
|
+
throw new TypeError(
|
|
758
|
+
`The 'configType' option value must be 'flat'. The value '${configType}' is not supported.`,
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
flags.forEach(flag => {
|
|
763
|
+
if (inactiveFlags.has(flag)) {
|
|
764
|
+
const inactiveFlagData = inactiveFlags.get(flag);
|
|
765
|
+
const inactivityReason =
|
|
766
|
+
getInactivityReasonMessage(inactiveFlagData);
|
|
767
|
+
const message = `The flag '${flag}' is inactive: ${inactivityReason}`;
|
|
768
|
+
|
|
769
|
+
if (typeof inactiveFlagData.replacedBy === "undefined") {
|
|
770
|
+
throw new Error(message);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// if there's a replacement, enable it instead of original
|
|
774
|
+
if (typeof inactiveFlagData.replacedBy === "string") {
|
|
775
|
+
processedFlags.push(inactiveFlagData.replacedBy);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
warningService.emitInactiveFlagWarning(flag, message);
|
|
779
|
+
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
if (!activeFlags.has(flag)) {
|
|
784
|
+
throw new Error(`Unknown flag '${flag}'.`);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
processedFlags.push(flag);
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
internalSlotsMap.set(this, {
|
|
791
|
+
cwd: normalizeCwd(cwd),
|
|
792
|
+
flags: processedFlags,
|
|
793
|
+
lastConfigArray: null,
|
|
794
|
+
lastSourceCode: null,
|
|
795
|
+
lastSuppressedMessages: [],
|
|
796
|
+
warningService,
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
this.version = pkg.version;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Getter for package version.
|
|
804
|
+
* @static
|
|
805
|
+
* @returns {string} The version from package.json.
|
|
806
|
+
*/
|
|
807
|
+
static get version() {
|
|
808
|
+
return pkg.version;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Indicates if the given feature flag is enabled for this instance.
|
|
813
|
+
* @param {string} flag The feature flag to check.
|
|
814
|
+
* @returns {boolean} `true` if the feature flag is enabled, `false` if not.
|
|
815
|
+
*/
|
|
816
|
+
hasFlag(flag) {
|
|
817
|
+
return internalSlotsMap.get(this).flags.includes(flag);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Verifies the text against the rules specified by the second argument.
|
|
822
|
+
* @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
|
|
823
|
+
* @param {ConfigObject|ConfigObject[]} config The ESLint config object or array to use.
|
|
824
|
+
* @param {(string|(VerifyOptions&ProcessorOptions))} [filenameOrOptions] The optional filename of the file being checked.
|
|
825
|
+
* If this is not set, the filename will default to '<input>' in the rule context. If
|
|
826
|
+
* an object, then it has "filename", "allowInlineConfig", and some properties.
|
|
827
|
+
* @returns {LintMessage[]} The results as an array of messages or an empty array if no messages.
|
|
828
|
+
*/
|
|
829
|
+
verify(textOrSourceCode, config, filenameOrOptions) {
|
|
830
|
+
debug("Verify");
|
|
831
|
+
|
|
832
|
+
const { cwd } = internalSlotsMap.get(this);
|
|
833
|
+
|
|
834
|
+
const options =
|
|
835
|
+
typeof filenameOrOptions === "string"
|
|
836
|
+
? { filename: filenameOrOptions }
|
|
837
|
+
: filenameOrOptions || {};
|
|
838
|
+
|
|
839
|
+
const configToUse = config ?? {};
|
|
840
|
+
|
|
841
|
+
/*
|
|
842
|
+
* Because of how Webpack packages up the files, we can't
|
|
843
|
+
* compare directly to `FlatConfigArray` using `instanceof`
|
|
844
|
+
* because it's not the same `FlatConfigArray` as in the tests.
|
|
845
|
+
* So, we work around it by assuming an array is, in fact, a
|
|
846
|
+
* `FlatConfigArray` if it has a `getConfig()` method.
|
|
847
|
+
*/
|
|
848
|
+
let configArray = configToUse;
|
|
849
|
+
|
|
850
|
+
if (
|
|
851
|
+
!Array.isArray(configToUse) ||
|
|
852
|
+
typeof configToUse.getConfig !== "function"
|
|
853
|
+
) {
|
|
854
|
+
configArray = new FlatConfigArray(configToUse, {
|
|
855
|
+
basePath: cwd,
|
|
856
|
+
});
|
|
857
|
+
configArray.normalizeSync();
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
return this._distinguishSuppressedMessages(
|
|
861
|
+
this._verifyWithFlatConfigArray(
|
|
862
|
+
textOrSourceCode,
|
|
863
|
+
configArray,
|
|
864
|
+
options,
|
|
865
|
+
true,
|
|
866
|
+
),
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* Verify with a processor.
|
|
872
|
+
* @param {string|SourceCode} textOrSourceCode The source code.
|
|
873
|
+
* @param {Config} config The config array.
|
|
874
|
+
* @param {VerifyOptions&ProcessorOptions} options The options.
|
|
875
|
+
* @param {FlatConfigArray} [configForRecursive] The `ConfigArray` object to apply multiple processors recursively.
|
|
876
|
+
* @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
|
|
877
|
+
*/
|
|
878
|
+
_verifyWithFlatConfigArrayAndProcessor(
|
|
879
|
+
textOrSourceCode,
|
|
880
|
+
config,
|
|
881
|
+
options,
|
|
882
|
+
configForRecursive,
|
|
883
|
+
) {
|
|
884
|
+
const slots = internalSlotsMap.get(this);
|
|
885
|
+
const filename = options.filename || "<input>";
|
|
886
|
+
const filenameToExpose = normalizeFilename(filename);
|
|
887
|
+
const physicalFilename = options.physicalFilename || filenameToExpose;
|
|
888
|
+
const text = ensureText(textOrSourceCode);
|
|
889
|
+
const file = new VFile(filenameToExpose, text, {
|
|
890
|
+
physicalPath: physicalFilename,
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
const preprocess = options.preprocess || (rawText => [rawText]);
|
|
894
|
+
const postprocess =
|
|
895
|
+
options.postprocess || (messagesList => messagesList.flat());
|
|
896
|
+
|
|
897
|
+
const processorService = new ProcessorService();
|
|
898
|
+
const preprocessResult = processorService.preprocessSync(file, {
|
|
899
|
+
processor: {
|
|
900
|
+
preprocess,
|
|
901
|
+
postprocess,
|
|
902
|
+
},
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
if (!preprocessResult.ok) {
|
|
906
|
+
return preprocessResult.errors;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
const filterCodeBlock =
|
|
910
|
+
options.filterCodeBlock ||
|
|
911
|
+
(blockFilename => blockFilename.endsWith(".js"));
|
|
912
|
+
const originalExtname = path.extname(filename);
|
|
913
|
+
const { files } = preprocessResult;
|
|
914
|
+
|
|
915
|
+
const messageLists = files.map(block => {
|
|
916
|
+
debug("A code block was found: %o", block.path || "(unnamed)");
|
|
917
|
+
|
|
918
|
+
// Keep the legacy behavior.
|
|
919
|
+
if (typeof block === "string") {
|
|
920
|
+
return this._verifyWithFlatConfigArrayAndWithoutProcessors(
|
|
921
|
+
block,
|
|
922
|
+
config,
|
|
923
|
+
options,
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// Skip this block if filtered.
|
|
928
|
+
if (!filterCodeBlock(block.path, block.body)) {
|
|
929
|
+
debug("This code block was skipped.");
|
|
930
|
+
return [];
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// Resolve configuration again if the file content or extension was changed.
|
|
934
|
+
if (
|
|
935
|
+
configForRecursive &&
|
|
936
|
+
(text !== block.rawBody ||
|
|
937
|
+
path.extname(block.path) !== originalExtname)
|
|
938
|
+
) {
|
|
939
|
+
debug(
|
|
940
|
+
"Resolving configuration again because the file content or extension was changed.",
|
|
941
|
+
);
|
|
942
|
+
return this._verifyWithFlatConfigArray(
|
|
943
|
+
block.rawBody,
|
|
944
|
+
configForRecursive,
|
|
945
|
+
{
|
|
946
|
+
...options,
|
|
947
|
+
filename: block.path,
|
|
948
|
+
physicalFilename: block.physicalPath,
|
|
949
|
+
},
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
slots.lastSourceCode = null;
|
|
954
|
+
|
|
955
|
+
// Does lint.
|
|
956
|
+
return this.#flatVerifyWithoutProcessors(block, config, {
|
|
957
|
+
...options,
|
|
958
|
+
filename: block.path,
|
|
959
|
+
physicalFilename: block.physicalPath,
|
|
960
|
+
});
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
return processorService.postprocessSync(file, messageLists, {
|
|
964
|
+
processor: {
|
|
965
|
+
preprocess,
|
|
966
|
+
postprocess,
|
|
967
|
+
},
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* Verify using flat config and without any processors.
|
|
973
|
+
* @param {VFile} file The file to lint.
|
|
974
|
+
* @param {Config} providedConfig An ESLintConfig instance to configure everything.
|
|
975
|
+
* @param {VerifyOptions} [providedOptions] The optional filename of the file being checked.
|
|
976
|
+
* @throws {Error} If during rule execution.
|
|
977
|
+
* @returns {(LintMessage|SuppressedLintMessage)[]} The results as an array of messages or an empty array if no messages.
|
|
978
|
+
*/
|
|
979
|
+
#flatVerifyWithoutProcessors(file, providedConfig, providedOptions) {
|
|
980
|
+
const slots = internalSlotsMap.get(this);
|
|
981
|
+
const config = providedConfig || {};
|
|
982
|
+
const { settings = {}, languageOptions } = config;
|
|
983
|
+
const options = normalizeVerifyOptions(providedOptions, config);
|
|
984
|
+
|
|
985
|
+
if (!slots.lastSourceCode) {
|
|
986
|
+
let t;
|
|
987
|
+
|
|
988
|
+
if (options.stats) {
|
|
989
|
+
t = startTime();
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
const parserService = new ParserService();
|
|
993
|
+
const parseResult = parserService.parseSync(file, config);
|
|
994
|
+
|
|
995
|
+
if (options.stats) {
|
|
996
|
+
const time = endTime(t);
|
|
997
|
+
|
|
998
|
+
storeTime(time, { type: "parse" }, slots);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
if (!parseResult.ok) {
|
|
1002
|
+
return parseResult.errors;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
slots.lastSourceCode = parseResult.sourceCode;
|
|
1006
|
+
} else {
|
|
1007
|
+
/*
|
|
1008
|
+
* If the given source code object as the first argument does not have scopeManager, analyze the scope.
|
|
1009
|
+
* This is for backward compatibility (SourceCode is frozen so it cannot rebind).
|
|
1010
|
+
*
|
|
1011
|
+
* We check explicitly for `null` to ensure that this is a JS-flavored language.
|
|
1012
|
+
* For non-JS languages we don't want to do this.
|
|
1013
|
+
*
|
|
1014
|
+
* TODO: Remove this check when we stop exporting the `SourceCode` object.
|
|
1015
|
+
*/
|
|
1016
|
+
if (slots.lastSourceCode.scopeManager === null) {
|
|
1017
|
+
slots.lastSourceCode = new SourceCode({
|
|
1018
|
+
text: slots.lastSourceCode.text,
|
|
1019
|
+
ast: slots.lastSourceCode.ast,
|
|
1020
|
+
hasBOM: slots.lastSourceCode.hasBOM,
|
|
1021
|
+
parserServices: slots.lastSourceCode.parserServices,
|
|
1022
|
+
visitorKeys: slots.lastSourceCode.visitorKeys,
|
|
1023
|
+
scopeManager: analyzeScope(
|
|
1024
|
+
slots.lastSourceCode.ast,
|
|
1025
|
+
languageOptions,
|
|
1026
|
+
),
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
const sourceCode = slots.lastSourceCode;
|
|
1032
|
+
const report = new FileReport({
|
|
1033
|
+
ruleMapper: ruleId => config.getRuleDefinition(ruleId),
|
|
1034
|
+
language: config.language,
|
|
1035
|
+
sourceCode,
|
|
1036
|
+
disableFixes: options.disableFixes,
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
/*
|
|
1040
|
+
* Make adjustments based on the language options. For JavaScript,
|
|
1041
|
+
* this is primarily about adding variables into the global scope
|
|
1042
|
+
* to account for ecmaVersion and configured globals.
|
|
1043
|
+
*/
|
|
1044
|
+
sourceCode.applyLanguageOptions?.(languageOptions);
|
|
1045
|
+
|
|
1046
|
+
const mergedInlineConfig = {
|
|
1047
|
+
rules: {},
|
|
1048
|
+
};
|
|
1049
|
+
|
|
1050
|
+
/*
|
|
1051
|
+
* Inline config can be either enabled or disabled. If disabled, it's possible
|
|
1052
|
+
* to detect the inline config and emit a warning (though this is not required).
|
|
1053
|
+
* So we first check to see if inline config is allowed at all, and if so, we
|
|
1054
|
+
* need to check if it's a warning or not.
|
|
1055
|
+
*/
|
|
1056
|
+
if (options.allowInlineConfig) {
|
|
1057
|
+
// if inline config should warn then add the warnings
|
|
1058
|
+
if (options.warnInlineConfig) {
|
|
1059
|
+
if (sourceCode.getInlineConfigNodes) {
|
|
1060
|
+
sourceCode.getInlineConfigNodes().forEach(node => {
|
|
1061
|
+
const loc = sourceCode.getLoc(node);
|
|
1062
|
+
const range = sourceCode.getRange(node);
|
|
1063
|
+
|
|
1064
|
+
report.addWarning({
|
|
1065
|
+
message: `'${sourceCode.text.slice(range[0], range[1])}' has no effect because you have 'noInlineConfig' setting in ${options.warnInlineConfig}.`,
|
|
1066
|
+
loc,
|
|
1067
|
+
});
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
} else {
|
|
1071
|
+
const inlineConfigResult = sourceCode.applyInlineConfig?.();
|
|
1072
|
+
|
|
1073
|
+
if (inlineConfigResult) {
|
|
1074
|
+
inlineConfigResult.problems.forEach(problem => {
|
|
1075
|
+
report.addFatal(problem);
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
for (const {
|
|
1079
|
+
config: inlineConfig,
|
|
1080
|
+
loc,
|
|
1081
|
+
} of inlineConfigResult.configs) {
|
|
1082
|
+
Object.keys(inlineConfig.rules).forEach(ruleId => {
|
|
1083
|
+
const rule = config.getRuleDefinition(ruleId);
|
|
1084
|
+
const ruleValue = inlineConfig.rules[ruleId];
|
|
1085
|
+
|
|
1086
|
+
if (!rule) {
|
|
1087
|
+
report.addError({
|
|
1088
|
+
ruleId,
|
|
1089
|
+
loc,
|
|
1090
|
+
});
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if (
|
|
1095
|
+
Object.hasOwn(mergedInlineConfig.rules, ruleId)
|
|
1096
|
+
) {
|
|
1097
|
+
report.addError({
|
|
1098
|
+
message: `Rule "${ruleId}" is already configured by another configuration comment in the preceding code. This configuration is ignored.`,
|
|
1099
|
+
loc,
|
|
1100
|
+
});
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
try {
|
|
1105
|
+
const ruleOptionsInline = asArray(ruleValue);
|
|
1106
|
+
let ruleOptions = ruleOptionsInline;
|
|
1107
|
+
|
|
1108
|
+
assertIsRuleSeverity(ruleId, ruleOptions[0]);
|
|
1109
|
+
|
|
1110
|
+
/*
|
|
1111
|
+
* If the rule was already configured, inline rule configuration that
|
|
1112
|
+
* only has severity should retain options from the config and just override the severity.
|
|
1113
|
+
*
|
|
1114
|
+
* Example:
|
|
1115
|
+
*
|
|
1116
|
+
* {
|
|
1117
|
+
* rules: {
|
|
1118
|
+
* curly: ["error", "multi"]
|
|
1119
|
+
* }
|
|
1120
|
+
* }
|
|
1121
|
+
*
|
|
1122
|
+
* /* eslint curly: ["warn"] * /
|
|
1123
|
+
*
|
|
1124
|
+
* Results in:
|
|
1125
|
+
*
|
|
1126
|
+
* curly: ["warn", "multi"]
|
|
1127
|
+
*/
|
|
1128
|
+
|
|
1129
|
+
let shouldValidateOptions = true;
|
|
1130
|
+
|
|
1131
|
+
if (
|
|
1132
|
+
/*
|
|
1133
|
+
* If inline config for the rule has only severity
|
|
1134
|
+
*/
|
|
1135
|
+
ruleOptions.length === 1 &&
|
|
1136
|
+
/*
|
|
1137
|
+
* And the rule was already configured
|
|
1138
|
+
*/
|
|
1139
|
+
config.rules &&
|
|
1140
|
+
Object.hasOwn(config.rules, ruleId)
|
|
1141
|
+
) {
|
|
1142
|
+
/*
|
|
1143
|
+
* Then use severity from the inline config and options from the provided config
|
|
1144
|
+
*/
|
|
1145
|
+
ruleOptions = [
|
|
1146
|
+
ruleOptions[0], // severity from the inline config
|
|
1147
|
+
...config.rules[ruleId].slice(1), // options from the provided config
|
|
1148
|
+
];
|
|
1149
|
+
|
|
1150
|
+
// if the rule was enabled, the options have already been validated
|
|
1151
|
+
if (config.rules[ruleId][0] > 0) {
|
|
1152
|
+
shouldValidateOptions = false;
|
|
1153
|
+
}
|
|
1154
|
+
} else {
|
|
1155
|
+
/**
|
|
1156
|
+
* Since we know the user provided options, apply defaults on top of them
|
|
1157
|
+
*/
|
|
1158
|
+
const slicedOptions = ruleOptions.slice(1);
|
|
1159
|
+
const mergedOptions = deepMergeArrays(
|
|
1160
|
+
rule.meta?.defaultOptions,
|
|
1161
|
+
slicedOptions,
|
|
1162
|
+
);
|
|
1163
|
+
|
|
1164
|
+
if (mergedOptions.length) {
|
|
1165
|
+
ruleOptions = [
|
|
1166
|
+
ruleOptions[0],
|
|
1167
|
+
...mergedOptions,
|
|
1168
|
+
];
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
if (
|
|
1173
|
+
options.reportUnusedInlineConfigs !== "off"
|
|
1174
|
+
) {
|
|
1175
|
+
addProblemIfSameSeverityAndOptions(
|
|
1176
|
+
config,
|
|
1177
|
+
loc,
|
|
1178
|
+
report,
|
|
1179
|
+
ruleId,
|
|
1180
|
+
ruleOptions,
|
|
1181
|
+
ruleOptionsInline,
|
|
1182
|
+
options.reportUnusedInlineConfigs,
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
if (shouldValidateOptions) {
|
|
1187
|
+
config.validateRulesConfig({
|
|
1188
|
+
[ruleId]: ruleOptions,
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
mergedInlineConfig.rules[ruleId] = ruleOptions;
|
|
1193
|
+
} catch (err) {
|
|
1194
|
+
/*
|
|
1195
|
+
* If the rule has invalid `meta.schema`, throw the error because
|
|
1196
|
+
* this is not an invalid inline configuration but an invalid rule.
|
|
1197
|
+
*/
|
|
1198
|
+
if (
|
|
1199
|
+
err.code ===
|
|
1200
|
+
"ESLINT_INVALID_RULE_OPTIONS_SCHEMA"
|
|
1201
|
+
) {
|
|
1202
|
+
throw err;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
let baseMessage = err.message
|
|
1206
|
+
.slice(
|
|
1207
|
+
err.message.startsWith('Key "rules":')
|
|
1208
|
+
? err.message.indexOf(":", 12) + 1
|
|
1209
|
+
: err.message.indexOf(":") + 1,
|
|
1210
|
+
)
|
|
1211
|
+
.trim();
|
|
1212
|
+
|
|
1213
|
+
if (err.messageTemplate) {
|
|
1214
|
+
baseMessage += ` You passed "${ruleValue}".`;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
report.addError({
|
|
1218
|
+
ruleId,
|
|
1219
|
+
message: `Inline configuration for rule "${ruleId}" is invalid:\n\t${baseMessage}\n`,
|
|
1220
|
+
loc,
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
const commentDirectives =
|
|
1230
|
+
options.allowInlineConfig && !options.warnInlineConfig
|
|
1231
|
+
? getDirectiveCommentsForFlatConfig(
|
|
1232
|
+
sourceCode,
|
|
1233
|
+
ruleId => config.getRuleDefinition(ruleId),
|
|
1234
|
+
config.language,
|
|
1235
|
+
report,
|
|
1236
|
+
)
|
|
1237
|
+
: [];
|
|
1238
|
+
|
|
1239
|
+
const configuredRules = Object.assign(
|
|
1240
|
+
{},
|
|
1241
|
+
config.rules,
|
|
1242
|
+
mergedInlineConfig.rules,
|
|
1243
|
+
);
|
|
1244
|
+
|
|
1245
|
+
sourceCode.finalize?.();
|
|
1246
|
+
|
|
1247
|
+
try {
|
|
1248
|
+
runRules(
|
|
1249
|
+
sourceCode,
|
|
1250
|
+
configuredRules,
|
|
1251
|
+
ruleId => config.getRuleDefinition(ruleId),
|
|
1252
|
+
config.language,
|
|
1253
|
+
languageOptions,
|
|
1254
|
+
settings,
|
|
1255
|
+
options.filename,
|
|
1256
|
+
false,
|
|
1257
|
+
slots.cwd,
|
|
1258
|
+
providedOptions.physicalFilename,
|
|
1259
|
+
options.ruleFilter,
|
|
1260
|
+
options.stats,
|
|
1261
|
+
slots,
|
|
1262
|
+
report,
|
|
1263
|
+
);
|
|
1264
|
+
} catch (err) {
|
|
1265
|
+
err.message += `\nOccurred while linting ${options.filename}`;
|
|
1266
|
+
debug("An error occurred while traversing");
|
|
1267
|
+
debug("Filename:", options.filename);
|
|
1268
|
+
if (err.currentNode) {
|
|
1269
|
+
const { line } = sourceCode.getLoc(err.currentNode).start;
|
|
1270
|
+
|
|
1271
|
+
debug("Line:", line);
|
|
1272
|
+
err.message += `:${line}`;
|
|
1273
|
+
}
|
|
1274
|
+
debug("Parser Options:", languageOptions.parserOptions);
|
|
1275
|
+
debug("Settings:", settings);
|
|
1276
|
+
|
|
1277
|
+
if (err.ruleId) {
|
|
1278
|
+
err.message += `\nRule: "${err.ruleId}"`;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
throw err;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
return applyDisableDirectives({
|
|
1285
|
+
language: config.language,
|
|
1286
|
+
sourceCode,
|
|
1287
|
+
directives: commentDirectives,
|
|
1288
|
+
disableFixes: options.disableFixes,
|
|
1289
|
+
problems: report.messages.sort(
|
|
1290
|
+
(problemA, problemB) =>
|
|
1291
|
+
problemA.line - problemB.line ||
|
|
1292
|
+
problemA.column - problemB.column,
|
|
1293
|
+
),
|
|
1294
|
+
reportUnusedDisableDirectives:
|
|
1295
|
+
options.reportUnusedDisableDirectives,
|
|
1296
|
+
ruleFilter: options.ruleFilter,
|
|
1297
|
+
configuredRules,
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
/**
|
|
1302
|
+
* Same as linter.verify, except without support for processors.
|
|
1303
|
+
* @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
|
|
1304
|
+
* @param {Config} providedConfig An ESLintConfig instance to configure everything.
|
|
1305
|
+
* @param {VerifyOptions} [providedOptions] The optional filename of the file being checked.
|
|
1306
|
+
* @throws {Error} If during rule execution.
|
|
1307
|
+
* @returns {(LintMessage|SuppressedLintMessage)[]} The results as an array of messages or an empty array if no messages.
|
|
1308
|
+
*/
|
|
1309
|
+
_verifyWithFlatConfigArrayAndWithoutProcessors(
|
|
1310
|
+
textOrSourceCode,
|
|
1311
|
+
providedConfig,
|
|
1312
|
+
providedOptions,
|
|
1313
|
+
) {
|
|
1314
|
+
const slots = internalSlotsMap.get(this);
|
|
1315
|
+
const filename = normalizeFilename(
|
|
1316
|
+
providedOptions.filename || "<input>",
|
|
1317
|
+
);
|
|
1318
|
+
let text;
|
|
1319
|
+
|
|
1320
|
+
// evaluate arguments
|
|
1321
|
+
if (typeof textOrSourceCode === "string") {
|
|
1322
|
+
slots.lastSourceCode = null;
|
|
1323
|
+
text = textOrSourceCode;
|
|
1324
|
+
} else {
|
|
1325
|
+
slots.lastSourceCode = textOrSourceCode;
|
|
1326
|
+
text = textOrSourceCode.text;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
const file = new VFile(filename, text, {
|
|
1330
|
+
physicalPath: providedOptions.physicalFilename,
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
return this.#flatVerifyWithoutProcessors(
|
|
1334
|
+
file,
|
|
1335
|
+
providedConfig,
|
|
1336
|
+
providedOptions,
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
/**
|
|
1341
|
+
* Verify a given code with a flat config.
|
|
1342
|
+
* @param {string|SourceCode} textOrSourceCode The source code.
|
|
1343
|
+
* @param {FlatConfigArray} configArray The config array.
|
|
1344
|
+
* @param {VerifyOptions&ProcessorOptions} options The options.
|
|
1345
|
+
* @param {boolean} firstCall Indicates if this is the first call in `verify()`
|
|
1346
|
+
* to determine processor behavior.
|
|
1347
|
+
* @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
|
|
1348
|
+
*/
|
|
1349
|
+
_verifyWithFlatConfigArray(
|
|
1350
|
+
textOrSourceCode,
|
|
1351
|
+
configArray,
|
|
1352
|
+
options,
|
|
1353
|
+
firstCall = false,
|
|
1354
|
+
) {
|
|
1355
|
+
debug("With flat config: %s", options.filename);
|
|
1356
|
+
|
|
1357
|
+
// we need a filename to match configs against
|
|
1358
|
+
const filename = options.filename || "__placeholder__.js";
|
|
1359
|
+
|
|
1360
|
+
// Store the config array in order to get plugin envs and rules later.
|
|
1361
|
+
internalSlotsMap.get(this).lastConfigArray = configArray;
|
|
1362
|
+
const config = configArray.getConfig(filename);
|
|
1363
|
+
|
|
1364
|
+
if (!config) {
|
|
1365
|
+
return [
|
|
1366
|
+
{
|
|
1367
|
+
ruleId: null,
|
|
1368
|
+
severity: 1,
|
|
1369
|
+
message: `No matching configuration found for ${filename}.`,
|
|
1370
|
+
line: 0,
|
|
1371
|
+
column: 0,
|
|
1372
|
+
},
|
|
1373
|
+
];
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
// Verify.
|
|
1377
|
+
if (config.processor) {
|
|
1378
|
+
debug("Apply the processor: %o", config.processor);
|
|
1379
|
+
const { preprocess, postprocess, supportsAutofix } =
|
|
1380
|
+
config.processor;
|
|
1381
|
+
const disableFixes = options.disableFixes || !supportsAutofix;
|
|
1382
|
+
|
|
1383
|
+
return this._verifyWithFlatConfigArrayAndProcessor(
|
|
1384
|
+
textOrSourceCode,
|
|
1385
|
+
config,
|
|
1386
|
+
{ ...options, filename, disableFixes, postprocess, preprocess },
|
|
1387
|
+
configArray,
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// check for options-based processing
|
|
1392
|
+
if (firstCall && (options.preprocess || options.postprocess)) {
|
|
1393
|
+
return this._verifyWithFlatConfigArrayAndProcessor(
|
|
1394
|
+
textOrSourceCode,
|
|
1395
|
+
config,
|
|
1396
|
+
options,
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
return this._verifyWithFlatConfigArrayAndWithoutProcessors(
|
|
1401
|
+
textOrSourceCode,
|
|
1402
|
+
config,
|
|
1403
|
+
options,
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
/**
|
|
1408
|
+
* Given a list of reported problems, distinguish problems between normal messages and suppressed messages.
|
|
1409
|
+
* The normal messages will be returned and the suppressed messages will be stored as lastSuppressedMessages.
|
|
1410
|
+
* @param {Array<LintMessage|SuppressedLintMessage>} problems A list of reported problems.
|
|
1411
|
+
* @returns {LintMessage[]} A list of LintMessage.
|
|
1412
|
+
*/
|
|
1413
|
+
_distinguishSuppressedMessages(problems) {
|
|
1414
|
+
const messages = [];
|
|
1415
|
+
const suppressedMessages = [];
|
|
1416
|
+
const slots = internalSlotsMap.get(this);
|
|
1417
|
+
|
|
1418
|
+
for (const problem of problems) {
|
|
1419
|
+
if (problem.suppressions) {
|
|
1420
|
+
suppressedMessages.push(problem);
|
|
1421
|
+
} else {
|
|
1422
|
+
messages.push(problem);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
slots.lastSuppressedMessages = suppressedMessages;
|
|
1427
|
+
|
|
1428
|
+
return messages;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
/**
|
|
1432
|
+
* Gets the SourceCode object representing the parsed source.
|
|
1433
|
+
* @returns {SourceCode} The SourceCode object.
|
|
1434
|
+
*/
|
|
1435
|
+
getSourceCode() {
|
|
1436
|
+
return internalSlotsMap.get(this).lastSourceCode;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* Gets the times spent on (parsing, fixing, linting) a file.
|
|
1441
|
+
* @returns {{ passes: TimePass[]; }} The times.
|
|
1442
|
+
*/
|
|
1443
|
+
getTimes() {
|
|
1444
|
+
return internalSlotsMap.get(this).times ?? { passes: [] };
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/**
|
|
1448
|
+
* Gets the number of autofix passes that were made in the last run.
|
|
1449
|
+
* @returns {number} The number of autofix passes.
|
|
1450
|
+
*/
|
|
1451
|
+
getFixPassCount() {
|
|
1452
|
+
return internalSlotsMap.get(this).fixPasses ?? 0;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
/**
|
|
1456
|
+
* Gets the list of SuppressedLintMessage produced in the last running.
|
|
1457
|
+
* @returns {SuppressedLintMessage[]} The list of SuppressedLintMessage
|
|
1458
|
+
*/
|
|
1459
|
+
getSuppressedMessages() {
|
|
1460
|
+
return internalSlotsMap.get(this).lastSuppressedMessages;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* Performs multiple autofix passes over the text until as many fixes as possible
|
|
1465
|
+
* have been applied.
|
|
1466
|
+
* @param {string} text The source text to apply fixes to.
|
|
1467
|
+
* @param {ConfigObject|ConfigObject[]} config The ESLint config object or array to use.
|
|
1468
|
+
* @param {string|(VerifyOptions&ProcessorOptions&FixOptions)} [filenameOrOptions] The filename or ESLint options object to use.
|
|
1469
|
+
* @returns {{fixed:boolean,messages:LintMessage[],output:string}} The result of the fix operation as returned from the
|
|
1470
|
+
* SourceCodeFixer.
|
|
1471
|
+
*/
|
|
1472
|
+
verifyAndFix(text, config, filenameOrOptions) {
|
|
1473
|
+
let messages,
|
|
1474
|
+
fixedResult,
|
|
1475
|
+
fixed = false,
|
|
1476
|
+
passNumber = 0,
|
|
1477
|
+
currentText = text,
|
|
1478
|
+
secondPreviousText,
|
|
1479
|
+
previousText;
|
|
1480
|
+
const options =
|
|
1481
|
+
typeof filenameOrOptions === "string"
|
|
1482
|
+
? { filename: filenameOrOptions }
|
|
1483
|
+
: filenameOrOptions || {};
|
|
1484
|
+
const debugTextDescription =
|
|
1485
|
+
options.filename || `${text.slice(0, 10)}...`;
|
|
1486
|
+
const shouldFix =
|
|
1487
|
+
typeof options.fix !== "undefined" ? options.fix : true;
|
|
1488
|
+
const stats = options?.stats;
|
|
1489
|
+
|
|
1490
|
+
const slots = internalSlotsMap.get(this);
|
|
1491
|
+
|
|
1492
|
+
// Remove lint times from the last run.
|
|
1493
|
+
if (stats) {
|
|
1494
|
+
delete slots.times;
|
|
1495
|
+
slots.fixPasses = 0;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
/**
|
|
1499
|
+
* This loop continues until one of the following is true:
|
|
1500
|
+
*
|
|
1501
|
+
* 1. No more fixes have been applied.
|
|
1502
|
+
* 2. Ten passes have been made.
|
|
1503
|
+
*
|
|
1504
|
+
* That means anytime a fix is successfully applied, there will be another pass.
|
|
1505
|
+
* Essentially, guaranteeing a minimum of two passes.
|
|
1506
|
+
*/
|
|
1507
|
+
do {
|
|
1508
|
+
passNumber++;
|
|
1509
|
+
let tTotal;
|
|
1510
|
+
|
|
1511
|
+
if (stats) {
|
|
1512
|
+
tTotal = startTime();
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
debug(
|
|
1516
|
+
`Linting code for ${debugTextDescription} (pass ${passNumber})`,
|
|
1517
|
+
);
|
|
1518
|
+
messages = this.verify(currentText, config, options);
|
|
1519
|
+
|
|
1520
|
+
debug(
|
|
1521
|
+
`Generating fixed text for ${debugTextDescription} (pass ${passNumber})`,
|
|
1522
|
+
);
|
|
1523
|
+
let t;
|
|
1524
|
+
|
|
1525
|
+
if (stats) {
|
|
1526
|
+
t = startTime();
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
fixedResult = SourceCodeFixer.applyFixes(
|
|
1530
|
+
currentText,
|
|
1531
|
+
messages,
|
|
1532
|
+
shouldFix,
|
|
1533
|
+
);
|
|
1534
|
+
|
|
1535
|
+
if (stats) {
|
|
1536
|
+
if (fixedResult.fixed) {
|
|
1537
|
+
const time = endTime(t);
|
|
1538
|
+
|
|
1539
|
+
storeTime(time, { type: "fix" }, slots);
|
|
1540
|
+
slots.fixPasses++;
|
|
1541
|
+
} else {
|
|
1542
|
+
storeTime(0, { type: "fix" }, slots);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
/*
|
|
1547
|
+
* stop if there are any syntax errors.
|
|
1548
|
+
* 'fixedResult.output' is a empty string.
|
|
1549
|
+
*/
|
|
1550
|
+
if (messages.length === 1 && messages[0].fatal) {
|
|
1551
|
+
break;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
// keep track if any fixes were ever applied - important for return value
|
|
1555
|
+
fixed = fixed || fixedResult.fixed;
|
|
1556
|
+
|
|
1557
|
+
// update to use the fixed output instead of the original text
|
|
1558
|
+
secondPreviousText = previousText;
|
|
1559
|
+
previousText = currentText;
|
|
1560
|
+
currentText = fixedResult.output;
|
|
1561
|
+
|
|
1562
|
+
if (stats) {
|
|
1563
|
+
tTotal = endTime(tTotal);
|
|
1564
|
+
const passIndex = slots.times.passes.length - 1;
|
|
1565
|
+
|
|
1566
|
+
slots.times.passes[passIndex].total = tTotal;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
// Stop if we've made a circular fix
|
|
1570
|
+
if (
|
|
1571
|
+
passNumber > 1 &&
|
|
1572
|
+
currentText.length === secondPreviousText.length &&
|
|
1573
|
+
currentText === secondPreviousText
|
|
1574
|
+
) {
|
|
1575
|
+
debug(
|
|
1576
|
+
`Circular fixes detected after pass ${passNumber}. Exiting fix loop.`,
|
|
1577
|
+
);
|
|
1578
|
+
slots.warningService.emitCircularFixesWarning(
|
|
1579
|
+
options.filename ?? "text",
|
|
1580
|
+
);
|
|
1581
|
+
break;
|
|
1582
|
+
}
|
|
1583
|
+
} while (fixedResult.fixed && passNumber < MAX_AUTOFIX_PASSES);
|
|
1584
|
+
|
|
1585
|
+
/*
|
|
1586
|
+
* If the last result had fixes, we need to lint again to be sure we have
|
|
1587
|
+
* the most up-to-date information.
|
|
1588
|
+
*/
|
|
1589
|
+
if (fixedResult.fixed) {
|
|
1590
|
+
let tTotal;
|
|
1591
|
+
|
|
1592
|
+
if (stats) {
|
|
1593
|
+
tTotal = startTime();
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
fixedResult.messages = this.verify(currentText, config, options);
|
|
1597
|
+
|
|
1598
|
+
if (stats) {
|
|
1599
|
+
storeTime(0, { type: "fix" }, slots);
|
|
1600
|
+
slots.times.passes.at(-1).total = endTime(tTotal);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// ensure the last result properly reflects if fixes were done
|
|
1605
|
+
fixedResult.fixed = fixed;
|
|
1606
|
+
fixedResult.output = currentText;
|
|
1607
|
+
|
|
1608
|
+
return fixedResult;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
module.exports = {
|
|
1613
|
+
Linter,
|
|
1614
|
+
};
|