bahlint 28.58.6934
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 +370 -0
- package/bin/eslint.js +195 -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 +22 -0
- package/lib/cli-engine/formatters/gasoline.js +168 -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 +607 -0
- package/lib/config/config-loader.js +683 -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 +1817 -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,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview An object that creates fix commands for rules.
|
|
3
|
+
* @author Nicholas C. Zakas
|
|
4
|
+
*/
|
|
5
|
+
"use strict";
|
|
6
|
+
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Typedefs
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @import { SourceRange } from "@eslint/core";
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/* eslint class-methods-use-this: off -- Methods desired on instance */
|
|
16
|
+
|
|
17
|
+
//------------------------------------------------------------------------------
|
|
18
|
+
// Requirements
|
|
19
|
+
//------------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
// none!
|
|
22
|
+
|
|
23
|
+
//------------------------------------------------------------------------------
|
|
24
|
+
// Helpers
|
|
25
|
+
//------------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Creates a fix command that inserts text at the specified index in the source text.
|
|
29
|
+
* @param {number} index The 0-based index at which to insert the new text.
|
|
30
|
+
* @param {string} text The text to insert.
|
|
31
|
+
* @returns {Object} The fix command.
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
function insertTextAt(index, text) {
|
|
35
|
+
return {
|
|
36
|
+
range: [index, index],
|
|
37
|
+
text,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Asserts that the provided text is a string.
|
|
43
|
+
* @param {unknown} text The text to validate.
|
|
44
|
+
* @returns {void}
|
|
45
|
+
* @throws {TypeError} If `text` is not a string.
|
|
46
|
+
*/
|
|
47
|
+
function assertIsString(text) {
|
|
48
|
+
if (typeof text !== "string") {
|
|
49
|
+
throw new TypeError("'text' must be a string");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//------------------------------------------------------------------------------
|
|
54
|
+
// Public Interface
|
|
55
|
+
//------------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Creates code fixing commands for rules.
|
|
59
|
+
*/
|
|
60
|
+
class RuleFixer {
|
|
61
|
+
/**
|
|
62
|
+
* The source code object representing the text to be fixed.
|
|
63
|
+
* @type {SourceCode}
|
|
64
|
+
*/
|
|
65
|
+
#sourceCode;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Creates a new instance.
|
|
69
|
+
* @param {Object} options The options for the fixer.
|
|
70
|
+
* @param {SourceCode} options.sourceCode The source code object representing the text to be fixed.
|
|
71
|
+
*/
|
|
72
|
+
constructor({ sourceCode }) {
|
|
73
|
+
this.#sourceCode = sourceCode;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Creates a fix command that inserts text after the given node or token.
|
|
78
|
+
* The fix is not applied until applyFixes() is called.
|
|
79
|
+
* @param {ASTNode|Token} nodeOrToken The node or token to insert after.
|
|
80
|
+
* @param {string} text The text to insert.
|
|
81
|
+
* @returns {Object} The fix command.
|
|
82
|
+
* @throws {TypeError} If `text` is not a string.
|
|
83
|
+
*/
|
|
84
|
+
insertTextAfter(nodeOrToken, text) {
|
|
85
|
+
assertIsString(text);
|
|
86
|
+
|
|
87
|
+
const range = this.#sourceCode.getRange(nodeOrToken);
|
|
88
|
+
|
|
89
|
+
return this.insertTextAfterRange(range, text);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates a fix command that inserts text after the specified range in the source text.
|
|
94
|
+
* The fix is not applied until applyFixes() is called.
|
|
95
|
+
* @param {SourceRange} range The range to replace, first item is start of range, second
|
|
96
|
+
* is end of range.
|
|
97
|
+
* @param {string} text The text to insert.
|
|
98
|
+
* @returns {Object} The fix command.
|
|
99
|
+
* @throws {TypeError} If `text` is not a string.
|
|
100
|
+
*/
|
|
101
|
+
insertTextAfterRange(range, text) {
|
|
102
|
+
assertIsString(text);
|
|
103
|
+
|
|
104
|
+
return insertTextAt(range[1], text);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Creates a fix command that inserts text before the given node or token.
|
|
109
|
+
* The fix is not applied until applyFixes() is called.
|
|
110
|
+
* @param {ASTNode|Token} nodeOrToken The node or token to insert before.
|
|
111
|
+
* @param {string} text The text to insert.
|
|
112
|
+
* @returns {Object} The fix command.
|
|
113
|
+
* @throws {TypeError} If `text` is not a string.
|
|
114
|
+
*/
|
|
115
|
+
insertTextBefore(nodeOrToken, text) {
|
|
116
|
+
assertIsString(text);
|
|
117
|
+
|
|
118
|
+
const range = this.#sourceCode.getRange(nodeOrToken);
|
|
119
|
+
|
|
120
|
+
return this.insertTextBeforeRange(range, text);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Creates a fix command that inserts text before the specified range in the source text.
|
|
125
|
+
* The fix is not applied until applyFixes() is called.
|
|
126
|
+
* @param {SourceRange} range The range to replace, first item is start of range, second
|
|
127
|
+
* is end of range.
|
|
128
|
+
* @param {string} text The text to insert.
|
|
129
|
+
* @returns {Object} The fix command.
|
|
130
|
+
* @throws {TypeError} If `text` is not a string.
|
|
131
|
+
*/
|
|
132
|
+
insertTextBeforeRange(range, text) {
|
|
133
|
+
assertIsString(text);
|
|
134
|
+
|
|
135
|
+
return insertTextAt(range[0], text);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Creates a fix command that replaces text at the node or token.
|
|
140
|
+
* The fix is not applied until applyFixes() is called.
|
|
141
|
+
* @param {ASTNode|Token} nodeOrToken The node or token to remove.
|
|
142
|
+
* @param {string} text The text to insert.
|
|
143
|
+
* @returns {Object} The fix command.
|
|
144
|
+
* @throws {TypeError} If `text` is not a string.
|
|
145
|
+
*/
|
|
146
|
+
replaceText(nodeOrToken, text) {
|
|
147
|
+
assertIsString(text);
|
|
148
|
+
|
|
149
|
+
const range = this.#sourceCode.getRange(nodeOrToken);
|
|
150
|
+
|
|
151
|
+
return this.replaceTextRange(range, text);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Creates a fix command that replaces text at the specified range in the source text.
|
|
156
|
+
* The fix is not applied until applyFixes() is called.
|
|
157
|
+
* @param {SourceRange} range The range to replace, first item is start of range, second
|
|
158
|
+
* is end of range.
|
|
159
|
+
* @param {string} text The text to insert.
|
|
160
|
+
* @returns {Object} The fix command.
|
|
161
|
+
* @throws {TypeError} If `text` is not a string.
|
|
162
|
+
*/
|
|
163
|
+
replaceTextRange(range, text) {
|
|
164
|
+
assertIsString(text);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
range,
|
|
168
|
+
text,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Creates a fix command that removes the node or token from the source.
|
|
174
|
+
* The fix is not applied until applyFixes() is called.
|
|
175
|
+
* @param {ASTNode|Token} nodeOrToken The node or token to remove.
|
|
176
|
+
* @returns {Object} The fix command.
|
|
177
|
+
*/
|
|
178
|
+
remove(nodeOrToken) {
|
|
179
|
+
const range = this.#sourceCode.getRange(nodeOrToken);
|
|
180
|
+
|
|
181
|
+
return this.removeRange(range);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Creates a fix command that removes the specified range of text from the source.
|
|
186
|
+
* The fix is not applied until applyFixes() is called.
|
|
187
|
+
* @param {SourceRange} range The range to remove, first item is start of range, second
|
|
188
|
+
* is end of range.
|
|
189
|
+
* @returns {Object} The fix command.
|
|
190
|
+
*/
|
|
191
|
+
removeRange(range) {
|
|
192
|
+
return {
|
|
193
|
+
range,
|
|
194
|
+
text: "",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
module.exports = { RuleFixer };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview An object that caches and applies source code fixes.
|
|
3
|
+
* @author Nicholas C. Zakas
|
|
4
|
+
*/
|
|
5
|
+
"use strict";
|
|
6
|
+
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Requirements
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const debug = require("debug")("eslint:source-code-fixer");
|
|
12
|
+
|
|
13
|
+
//------------------------------------------------------------------------------
|
|
14
|
+
// Helpers
|
|
15
|
+
//------------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
const BOM = "\uFEFF";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Compares items in a messages array by range.
|
|
21
|
+
* @param {Message} a The first message.
|
|
22
|
+
* @param {Message} b The second message.
|
|
23
|
+
* @returns {number} -1 if a comes before b, 1 if a comes after b, 0 if equal.
|
|
24
|
+
* @private
|
|
25
|
+
*/
|
|
26
|
+
function compareMessagesByFixRange(a, b) {
|
|
27
|
+
return a.fix.range[0] - b.fix.range[0] || a.fix.range[1] - b.fix.range[1];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Compares items in a messages array by line and column.
|
|
32
|
+
* @param {Message} a The first message.
|
|
33
|
+
* @param {Message} b The second message.
|
|
34
|
+
* @returns {number} -1 if a comes before b, 1 if a comes after b, 0 if equal.
|
|
35
|
+
* @private
|
|
36
|
+
*/
|
|
37
|
+
function compareMessagesByLocation(a, b) {
|
|
38
|
+
return a.line - b.line || a.column - b.column;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//------------------------------------------------------------------------------
|
|
42
|
+
// Public Interface
|
|
43
|
+
//------------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Utility for apply fixes to source code.
|
|
47
|
+
* @constructor
|
|
48
|
+
*/
|
|
49
|
+
function SourceCodeFixer() {
|
|
50
|
+
Object.freeze(this);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Applies the fixes specified by the messages to the given text. Tries to be
|
|
55
|
+
* smart about the fixes and won't apply fixes over the same area in the text.
|
|
56
|
+
* @param {string} sourceText The text to apply the changes to.
|
|
57
|
+
* @param {Message[]} messages The array of messages reported by ESLint.
|
|
58
|
+
* @param {boolean|Function} [shouldFix=true] Determines whether each message should be fixed
|
|
59
|
+
* @returns {Object} An object containing the fixed text and any unfixed messages.
|
|
60
|
+
*/
|
|
61
|
+
SourceCodeFixer.applyFixes = function (sourceText, messages, shouldFix) {
|
|
62
|
+
debug("Applying fixes");
|
|
63
|
+
|
|
64
|
+
if (shouldFix === false) {
|
|
65
|
+
debug("shouldFix parameter was false, not attempting fixes");
|
|
66
|
+
return {
|
|
67
|
+
fixed: false,
|
|
68
|
+
messages,
|
|
69
|
+
output: sourceText,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// clone the array
|
|
74
|
+
const remainingMessages = [],
|
|
75
|
+
fixes = [],
|
|
76
|
+
bom = sourceText.startsWith(BOM) ? BOM : "",
|
|
77
|
+
text = bom ? sourceText.slice(1) : sourceText;
|
|
78
|
+
let lastPos = Number.NEGATIVE_INFINITY,
|
|
79
|
+
output = bom;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Try to use the 'fix' from a problem.
|
|
83
|
+
* @param {Message} problem The message object to apply fixes from
|
|
84
|
+
* @returns {boolean} Whether fix was successfully applied
|
|
85
|
+
*/
|
|
86
|
+
function attemptFix(problem) {
|
|
87
|
+
const fix = problem.fix;
|
|
88
|
+
const start = fix.range[0];
|
|
89
|
+
const end = fix.range[1];
|
|
90
|
+
|
|
91
|
+
// Remain it as a problem if it's overlapped or it's a negative range
|
|
92
|
+
if (lastPos >= start || start > end) {
|
|
93
|
+
remainingMessages.push(problem);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Remove BOM.
|
|
98
|
+
if (
|
|
99
|
+
(start < 0 && end >= 0) ||
|
|
100
|
+
(start === 0 && fix.text.startsWith(BOM))
|
|
101
|
+
) {
|
|
102
|
+
output = "";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Make output to this fix.
|
|
106
|
+
output += text.slice(Math.max(0, lastPos), Math.max(0, start));
|
|
107
|
+
output += fix.text;
|
|
108
|
+
lastPos = end;
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
messages.forEach(problem => {
|
|
113
|
+
if (Object.hasOwn(problem, "fix") && problem.fix) {
|
|
114
|
+
fixes.push(problem);
|
|
115
|
+
} else {
|
|
116
|
+
remainingMessages.push(problem);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (fixes.length) {
|
|
121
|
+
debug("Found fixes to apply");
|
|
122
|
+
let fixesWereApplied = false;
|
|
123
|
+
|
|
124
|
+
for (const problem of fixes.sort(compareMessagesByFixRange)) {
|
|
125
|
+
if (typeof shouldFix !== "function" || shouldFix(problem)) {
|
|
126
|
+
attemptFix(problem);
|
|
127
|
+
|
|
128
|
+
/*
|
|
129
|
+
* The only time attemptFix will fail is if a previous fix was
|
|
130
|
+
* applied which conflicts with it. So we can mark this as true.
|
|
131
|
+
*/
|
|
132
|
+
fixesWereApplied = true;
|
|
133
|
+
} else {
|
|
134
|
+
remainingMessages.push(problem);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
output += text.slice(Math.max(0, lastPos));
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
fixed: fixesWereApplied,
|
|
141
|
+
messages: remainingMessages.sort(compareMessagesByLocation),
|
|
142
|
+
output,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
debug("No fixes to apply");
|
|
147
|
+
return {
|
|
148
|
+
fixed: false,
|
|
149
|
+
messages,
|
|
150
|
+
output: bom + text,
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
module.exports = SourceCodeFixer;
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Traverser for SourceCode objects.
|
|
3
|
+
* @author Nicholas C. Zakas
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
//------------------------------------------------------------------------------
|
|
9
|
+
// Requirements
|
|
10
|
+
//------------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
const { parse, matches } = require("./esquery");
|
|
13
|
+
const vk = require("eslint-visitor-keys");
|
|
14
|
+
|
|
15
|
+
//-----------------------------------------------------------------------------
|
|
16
|
+
// Typedefs
|
|
17
|
+
//-----------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @import { Language, SourceCode } from "@eslint/core";
|
|
21
|
+
* @import { ESQueryOptions } from "esquery";
|
|
22
|
+
* @import { ESQueryParsedSelector } from "./esquery.js";
|
|
23
|
+
* @import { SourceCodeVisitor } from "./source-code-visitor.js";
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
//-----------------------------------------------------------------------------
|
|
27
|
+
// Helpers
|
|
28
|
+
//-----------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
const STEP_KIND_VISIT = 1;
|
|
31
|
+
const STEP_KIND_CALL = 2;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Compares two ESQuery selectors by specificity.
|
|
35
|
+
* @param {ESQueryParsedSelector} a The first selector to compare.
|
|
36
|
+
* @param {ESQueryParsedSelector} b The second selector to compare.
|
|
37
|
+
* @returns {number} A negative number if `a` is less specific than `b` or they are equally specific and `a` <= `b` alphabetically, a positive number if `a` is more specific than `b`.
|
|
38
|
+
*/
|
|
39
|
+
function compareSpecificity(a, b) {
|
|
40
|
+
return a.compare(b);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Helper to wrap ESQuery operations.
|
|
45
|
+
*/
|
|
46
|
+
class ESQueryHelper {
|
|
47
|
+
/**
|
|
48
|
+
* Creates a new instance.
|
|
49
|
+
* @param {SourceCodeVisitor} visitor The visitor containing the functions to call.
|
|
50
|
+
* @param {ESQueryOptions} esqueryOptions `esquery` options for traversing custom nodes.
|
|
51
|
+
*/
|
|
52
|
+
constructor(visitor, esqueryOptions) {
|
|
53
|
+
/**
|
|
54
|
+
* The visitor to use during traversal.
|
|
55
|
+
* @type {SourceCodeVisitor}
|
|
56
|
+
*/
|
|
57
|
+
this.visitor = visitor;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The options for `esquery` to use during matching.
|
|
61
|
+
* @type {ESQueryOptions}
|
|
62
|
+
*/
|
|
63
|
+
this.esqueryOptions = esqueryOptions;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A map of node type to selectors targeting that node type on the
|
|
67
|
+
* enter phase of traversal.
|
|
68
|
+
* @type {Map<string, ESQueryParsedSelector[]>}
|
|
69
|
+
*/
|
|
70
|
+
this.enterSelectorsByNodeType = new Map();
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A map of node type to selectors targeting that node type on the
|
|
74
|
+
* exit phase of traversal.
|
|
75
|
+
* @type {Map<string, ESQueryParsedSelector[]>}
|
|
76
|
+
*/
|
|
77
|
+
this.exitSelectorsByNodeType = new Map();
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* An array of selectors that match any node type on the
|
|
81
|
+
* enter phase of traversal.
|
|
82
|
+
* @type {ESQueryParsedSelector[]}
|
|
83
|
+
*/
|
|
84
|
+
this.anyTypeEnterSelectors = [];
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* An array of selectors that match any node type on the
|
|
88
|
+
* exit phase of traversal.
|
|
89
|
+
* @type {ESQueryParsedSelector[]}
|
|
90
|
+
*/
|
|
91
|
+
this.anyTypeExitSelectors = [];
|
|
92
|
+
|
|
93
|
+
visitor.forEachName(rawSelector => {
|
|
94
|
+
const selector = parse(rawSelector);
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
* If this selector has identified specific node types,
|
|
98
|
+
* add it to the map for these node types for faster lookup.
|
|
99
|
+
*/
|
|
100
|
+
if (selector.nodeTypes) {
|
|
101
|
+
const typeMap = selector.isExit
|
|
102
|
+
? this.exitSelectorsByNodeType
|
|
103
|
+
: this.enterSelectorsByNodeType;
|
|
104
|
+
|
|
105
|
+
selector.nodeTypes.forEach(nodeType => {
|
|
106
|
+
if (!typeMap.has(nodeType)) {
|
|
107
|
+
typeMap.set(nodeType, []);
|
|
108
|
+
}
|
|
109
|
+
typeMap.get(nodeType).push(selector);
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/*
|
|
115
|
+
* Remaining selectors are added to the "any type" selectors
|
|
116
|
+
* list for the appropriate phase of traversal. This ensures
|
|
117
|
+
* that all selectors will still be applied even if no
|
|
118
|
+
* specific node type is matched.
|
|
119
|
+
*/
|
|
120
|
+
const selectors = selector.isExit
|
|
121
|
+
? this.anyTypeExitSelectors
|
|
122
|
+
: this.anyTypeEnterSelectors;
|
|
123
|
+
|
|
124
|
+
selectors.push(selector);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// sort all selectors by specificity for prioritizing call order
|
|
128
|
+
this.anyTypeEnterSelectors.sort(compareSpecificity);
|
|
129
|
+
this.anyTypeExitSelectors.sort(compareSpecificity);
|
|
130
|
+
this.enterSelectorsByNodeType.forEach(selectorList =>
|
|
131
|
+
selectorList.sort(compareSpecificity),
|
|
132
|
+
);
|
|
133
|
+
this.exitSelectorsByNodeType.forEach(selectorList =>
|
|
134
|
+
selectorList.sort(compareSpecificity),
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Checks if a node matches a given selector.
|
|
140
|
+
* @param {ASTNode} node The node to check
|
|
141
|
+
* @param {ASTNode[]} ancestry The ancestry of the node being checked.
|
|
142
|
+
* @param {ESQueryParsedSelector} selector An AST selector descriptor
|
|
143
|
+
* @returns {boolean} `true` if the selector matches the node, `false` otherwise
|
|
144
|
+
*/
|
|
145
|
+
matches(node, ancestry, selector) {
|
|
146
|
+
return matches(node, selector.root, ancestry, this.esqueryOptions);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Calculates all appropriate selectors to a node, in specificity order
|
|
151
|
+
* @param {ASTNode} node The node to check
|
|
152
|
+
* @param {ASTNode[]} ancestry The ancestry of the node being checked.
|
|
153
|
+
* @param {boolean} isExit `false` if the node is currently being entered, `true` if it's currently being exited
|
|
154
|
+
* @returns {string[]} An array of selectors that match the node.
|
|
155
|
+
*/
|
|
156
|
+
calculateSelectors(node, ancestry, isExit) {
|
|
157
|
+
const nodeTypeKey = this.esqueryOptions?.nodeTypeKey || "type";
|
|
158
|
+
const selectors = [];
|
|
159
|
+
|
|
160
|
+
/*
|
|
161
|
+
* Get the selectors that may match this node. First, check
|
|
162
|
+
* to see if the node type has specific selectors,
|
|
163
|
+
* then gather the "any type" selectors.
|
|
164
|
+
*/
|
|
165
|
+
const selectorsByNodeType =
|
|
166
|
+
(isExit
|
|
167
|
+
? this.exitSelectorsByNodeType
|
|
168
|
+
: this.enterSelectorsByNodeType
|
|
169
|
+
).get(node[nodeTypeKey]) || [];
|
|
170
|
+
const anyTypeSelectors = isExit
|
|
171
|
+
? this.anyTypeExitSelectors
|
|
172
|
+
: this.anyTypeEnterSelectors;
|
|
173
|
+
|
|
174
|
+
/*
|
|
175
|
+
* selectorsByNodeType and anyTypeSelectors were already sorted by specificity in the constructor.
|
|
176
|
+
* Iterate through each of them, applying selectors in the right order.
|
|
177
|
+
*/
|
|
178
|
+
let selectorsByNodeTypeIndex = 0;
|
|
179
|
+
let anyTypeSelectorsIndex = 0;
|
|
180
|
+
|
|
181
|
+
while (
|
|
182
|
+
selectorsByNodeTypeIndex < selectorsByNodeType.length ||
|
|
183
|
+
anyTypeSelectorsIndex < anyTypeSelectors.length
|
|
184
|
+
) {
|
|
185
|
+
/*
|
|
186
|
+
* If we've already exhausted the selectors for this node type,
|
|
187
|
+
* or if the next any type selector is more specific than the
|
|
188
|
+
* next selector for this node type, apply the any type selector.
|
|
189
|
+
*/
|
|
190
|
+
const hasMoreNodeTypeSelectors =
|
|
191
|
+
selectorsByNodeTypeIndex < selectorsByNodeType.length;
|
|
192
|
+
const hasMoreAnyTypeSelectors =
|
|
193
|
+
anyTypeSelectorsIndex < anyTypeSelectors.length;
|
|
194
|
+
const anyTypeSelector = anyTypeSelectors[anyTypeSelectorsIndex];
|
|
195
|
+
const nodeTypeSelector =
|
|
196
|
+
selectorsByNodeType[selectorsByNodeTypeIndex];
|
|
197
|
+
|
|
198
|
+
// Only compare specificity if both selectors exist
|
|
199
|
+
const isAnyTypeSelectorLessSpecific =
|
|
200
|
+
hasMoreAnyTypeSelectors &&
|
|
201
|
+
hasMoreNodeTypeSelectors &&
|
|
202
|
+
anyTypeSelector.compare(nodeTypeSelector) < 0;
|
|
203
|
+
|
|
204
|
+
if (!hasMoreNodeTypeSelectors || isAnyTypeSelectorLessSpecific) {
|
|
205
|
+
anyTypeSelectorsIndex++;
|
|
206
|
+
|
|
207
|
+
if (this.matches(node, ancestry, anyTypeSelector)) {
|
|
208
|
+
selectors.push(anyTypeSelector.source);
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
selectorsByNodeTypeIndex++;
|
|
212
|
+
|
|
213
|
+
if (this.matches(node, ancestry, nodeTypeSelector)) {
|
|
214
|
+
selectors.push(nodeTypeSelector.source);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return selectors;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
//------------------------------------------------------------------------------
|
|
224
|
+
// Public Interface
|
|
225
|
+
//------------------------------------------------------------------------------
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Traverses source code and ensures that visitor methods are called when
|
|
229
|
+
* entering and leaving each node.
|
|
230
|
+
*/
|
|
231
|
+
class SourceCodeTraverser {
|
|
232
|
+
/**
|
|
233
|
+
* The language of the source code being traversed.
|
|
234
|
+
* @type {Language}
|
|
235
|
+
*/
|
|
236
|
+
#language;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Map of languages to instances of this class.
|
|
240
|
+
* @type {WeakMap<Language, SourceCodeTraverser>}
|
|
241
|
+
*/
|
|
242
|
+
static instances = new WeakMap();
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Creates a new instance.
|
|
246
|
+
* @param {Language} language The language of the source code being traversed.
|
|
247
|
+
*/
|
|
248
|
+
constructor(language) {
|
|
249
|
+
this.#language = language;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
static getInstance(language) {
|
|
253
|
+
if (!this.instances.has(language)) {
|
|
254
|
+
this.instances.set(language, new this(language));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return this.instances.get(language);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Traverses the given source code synchronously.
|
|
262
|
+
* @param {SourceCode} sourceCode The source code to traverse.
|
|
263
|
+
* @param {SourceCodeVisitor} visitor The emitter to use for events.
|
|
264
|
+
* @param {Object} options Options for traversal.
|
|
265
|
+
* @param {ReturnType<SourceCode["traverse"]>} options.steps The steps to take during traversal.
|
|
266
|
+
* @returns {void}
|
|
267
|
+
* @throws {Error} If an error occurs during traversal.
|
|
268
|
+
*/
|
|
269
|
+
traverseSync(sourceCode, visitor, { steps } = {}) {
|
|
270
|
+
const esquery = new ESQueryHelper(visitor, {
|
|
271
|
+
visitorKeys: sourceCode.visitorKeys ?? this.#language.visitorKeys,
|
|
272
|
+
fallback: vk.getKeys,
|
|
273
|
+
matchClass: this.#language.matchesSelectorClass ?? (() => false),
|
|
274
|
+
nodeTypeKey: this.#language.nodeTypeKey,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const currentAncestry = [];
|
|
278
|
+
|
|
279
|
+
for (const step of steps ?? sourceCode.traverse()) {
|
|
280
|
+
switch (step.kind) {
|
|
281
|
+
case STEP_KIND_VISIT: {
|
|
282
|
+
try {
|
|
283
|
+
if (step.phase === 1) {
|
|
284
|
+
esquery
|
|
285
|
+
.calculateSelectors(
|
|
286
|
+
step.target,
|
|
287
|
+
currentAncestry,
|
|
288
|
+
false,
|
|
289
|
+
)
|
|
290
|
+
.forEach(selector => {
|
|
291
|
+
visitor.callSync(
|
|
292
|
+
selector,
|
|
293
|
+
...(step.args ?? [step.target]),
|
|
294
|
+
);
|
|
295
|
+
});
|
|
296
|
+
currentAncestry.unshift(step.target);
|
|
297
|
+
} else {
|
|
298
|
+
currentAncestry.shift();
|
|
299
|
+
esquery
|
|
300
|
+
.calculateSelectors(
|
|
301
|
+
step.target,
|
|
302
|
+
currentAncestry,
|
|
303
|
+
true,
|
|
304
|
+
)
|
|
305
|
+
.forEach(selector => {
|
|
306
|
+
visitor.callSync(
|
|
307
|
+
selector,
|
|
308
|
+
...(step.args ?? [step.target]),
|
|
309
|
+
);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
} catch (err) {
|
|
313
|
+
err.currentNode = step.target;
|
|
314
|
+
throw err;
|
|
315
|
+
}
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
case STEP_KIND_CALL: {
|
|
320
|
+
visitor.callSync(step.target, ...step.args);
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
default:
|
|
325
|
+
throw new Error(
|
|
326
|
+
`Invalid traversal step found: "${step.kind}".`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
module.exports = { SourceCodeTraverser };
|