]> git.proxmox.com Git - pve-eslint.git/commitdiff
import 8.3.0 source
authorThomas Lamprecht <t.lamprecht@proxmox.com>
Wed, 1 Dec 2021 12:39:06 +0000 (13:39 +0100)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Wed, 1 Dec 2021 12:39:06 +0000 (13:39 +0100)
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
637 files changed:
eslint/.eslintrc.js
eslint/.github/ISSUE_TEMPLATE/BUG_REPORT.md [deleted file]
eslint/.github/ISSUE_TEMPLATE/CHANGE.md [deleted file]
eslint/.github/ISSUE_TEMPLATE/NEW_RULE.md [deleted file]
eslint/.github/ISSUE_TEMPLATE/NEW_SYNTAX.md [new file with mode: 0644]
eslint/.github/ISSUE_TEMPLATE/RULE_CHANGE.md [deleted file]
eslint/.github/ISSUE_TEMPLATE/bug-report.yml [new file with mode: 0644]
eslint/.github/ISSUE_TEMPLATE/change.yml [new file with mode: 0644]
eslint/.github/ISSUE_TEMPLATE/new-rule.yml [new file with mode: 0644]
eslint/.github/ISSUE_TEMPLATE/rule-change.yml [new file with mode: 0644]
eslint/.github/PULL_REQUEST_TEMPLATE.md
eslint/.github/workflows/ci.yml
eslint/.github/workflows/codeql-analysis.yml [new file with mode: 0644]
eslint/.github/workflows/stale.yml [new file with mode: 0644]
eslint/.markdownlintignore [new file with mode: 0644]
eslint/CHANGELOG.md
eslint/LICENSE
eslint/Makefile.js
eslint/README.md
eslint/bin/eslint.js
eslint/conf/category-list.json [deleted file]
eslint/conf/rule-type-list.json [new file with mode: 0644]
eslint/docs/developer-guide/README.md
eslint/docs/developer-guide/architecture.md
eslint/docs/developer-guide/code-conventions.md
eslint/docs/developer-guide/code-path-analysis.md
eslint/docs/developer-guide/code-path-analysis/README.md
eslint/docs/developer-guide/contributing/README.md
eslint/docs/developer-guide/contributing/pull-requests.md
eslint/docs/developer-guide/development-environment.md
eslint/docs/developer-guide/nodejs-api.md
eslint/docs/developer-guide/scope-manager-interface.md
eslint/docs/developer-guide/selectors.md
eslint/docs/developer-guide/unit-tests.md
eslint/docs/developer-guide/working-with-plugins.md
eslint/docs/developer-guide/working-with-rules-deprecated.md
eslint/docs/developer-guide/working-with-rules.md
eslint/docs/rules/block-scoped-var.md
eslint/docs/rules/block-spacing.md
eslint/docs/rules/brace-style.md
eslint/docs/rules/class-methods-use-this.md
eslint/docs/rules/complexity.md
eslint/docs/rules/dot-notation.md
eslint/docs/rules/func-name-matching.md
eslint/docs/rules/func-names.md
eslint/docs/rules/id-denylist.md
eslint/docs/rules/id-length.md
eslint/docs/rules/id-match.md
eslint/docs/rules/indent-legacy.md
eslint/docs/rules/indent.md
eslint/docs/rules/keyword-spacing.md
eslint/docs/rules/lines-around-comment.md
eslint/docs/rules/lines-between-class-members.md
eslint/docs/rules/max-classes-per-file.md
eslint/docs/rules/max-depth.md
eslint/docs/rules/max-statements.md
eslint/docs/rules/new-cap.md
eslint/docs/rules/no-console.md
eslint/docs/rules/no-dupe-class-members.md
eslint/docs/rules/no-eq-null.md
eslint/docs/rules/no-eval.md
eslint/docs/rules/no-extra-parens.md
eslint/docs/rules/no-extra-semi.md
eslint/docs/rules/no-fallthrough.md
eslint/docs/rules/no-implicit-globals.md
eslint/docs/rules/no-import-assign.md
eslint/docs/rules/no-inner-declarations.md
eslint/docs/rules/no-invalid-this.md
eslint/docs/rules/no-lone-blocks.md
eslint/docs/rules/no-multi-assign.md
eslint/docs/rules/no-new-func.md
eslint/docs/rules/no-redeclare.md
eslint/docs/rules/no-undef-init.md
eslint/docs/rules/no-unreachable.md
eslint/docs/rules/no-unused-expressions.md
eslint/docs/rules/no-unused-private-class-members.md [new file with mode: 0644]
eslint/docs/rules/no-use-before-define.md
eslint/docs/rules/no-useless-computed-key.md
eslint/docs/rules/one-var.md
eslint/docs/rules/operator-linebreak.md
eslint/docs/rules/padded-blocks.md
eslint/docs/rules/padding-line-between-statements.md
eslint/docs/rules/prefer-const.md
eslint/docs/rules/prefer-destructuring.md
eslint/docs/rules/prefer-named-capture-group.md
eslint/docs/rules/require-atomic-updates.md
eslint/docs/rules/semi-style.md
eslint/docs/rules/semi.md
eslint/docs/rules/space-after-keywords.md
eslint/docs/rules/space-before-blocks.md
eslint/docs/rules/space-before-keywords.md
eslint/docs/rules/space-return-throw-case.md
eslint/docs/rules/strict.md
eslint/docs/rules/use-isnan.md
eslint/docs/rules/vars-on-top.md
eslint/docs/user-guide/command-line-interface.md
eslint/docs/user-guide/configuring/configuration-files.md
eslint/docs/user-guide/configuring/language-options.md
eslint/docs/user-guide/configuring/rules.md
eslint/docs/user-guide/getting-started.md
eslint/docs/user-guide/integrations.md
eslint/docs/user-guide/migrating-to-8.0.0.md [new file with mode: 0644]
eslint/lib/api.js
eslint/lib/cli-engine/cli-engine.js
eslint/lib/cli-engine/file-enumerator.js
eslint/lib/cli-engine/formatters/codeframe.js [deleted file]
eslint/lib/cli-engine/formatters/html.js
eslint/lib/cli-engine/formatters/table.js [deleted file]
eslint/lib/cli-engine/formatters/tap.js
eslint/lib/cli-engine/hash.js
eslint/lib/cli-engine/xml-escape.js
eslint/lib/cli.js
eslint/lib/config/default-config.js [new file with mode: 0644]
eslint/lib/config/flat-config-array.js [new file with mode: 0644]
eslint/lib/config/flat-config-schema.js [new file with mode: 0644]
eslint/lib/config/rule-validator.js [new file with mode: 0644]
eslint/lib/eslint/eslint.js
eslint/lib/init/autoconfig.js
eslint/lib/init/config-file.js
eslint/lib/init/config-initializer.js
eslint/lib/init/config-rule.js
eslint/lib/init/npm-utils.js
eslint/lib/init/source-code-utils.js
eslint/lib/linter/apply-disable-directives.js
eslint/lib/linter/code-path-analysis/code-path-analyzer.js
eslint/lib/linter/code-path-analysis/code-path-segment.js
eslint/lib/linter/code-path-analysis/code-path-state.js
eslint/lib/linter/code-path-analysis/code-path.js
eslint/lib/linter/code-path-analysis/debug-helpers.js
eslint/lib/linter/code-path-analysis/fork-context.js
eslint/lib/linter/code-path-analysis/id-generator.js
eslint/lib/linter/config-comment-parser.js
eslint/lib/linter/linter.js
eslint/lib/linter/node-event-generator.js
eslint/lib/linter/report-translator.js
eslint/lib/linter/rules.js
eslint/lib/linter/safe-emitter.js
eslint/lib/linter/source-code-fixer.js
eslint/lib/linter/timing.js
eslint/lib/options.js
eslint/lib/rule-tester/rule-tester.js
eslint/lib/rules/accessor-pairs.js
eslint/lib/rules/array-bracket-newline.js
eslint/lib/rules/array-bracket-spacing.js
eslint/lib/rules/array-callback-return.js
eslint/lib/rules/array-element-newline.js
eslint/lib/rules/arrow-body-style.js
eslint/lib/rules/arrow-parens.js
eslint/lib/rules/arrow-spacing.js
eslint/lib/rules/block-scoped-var.js
eslint/lib/rules/block-spacing.js
eslint/lib/rules/brace-style.js
eslint/lib/rules/callback-return.js
eslint/lib/rules/camelcase.js
eslint/lib/rules/capitalized-comments.js
eslint/lib/rules/class-methods-use-this.js
eslint/lib/rules/comma-dangle.js
eslint/lib/rules/comma-spacing.js
eslint/lib/rules/comma-style.js
eslint/lib/rules/complexity.js
eslint/lib/rules/computed-property-spacing.js
eslint/lib/rules/consistent-return.js
eslint/lib/rules/consistent-this.js
eslint/lib/rules/constructor-super.js
eslint/lib/rules/curly.js
eslint/lib/rules/default-case-last.js
eslint/lib/rules/default-case.js
eslint/lib/rules/default-param-last.js
eslint/lib/rules/dot-location.js
eslint/lib/rules/dot-notation.js
eslint/lib/rules/eol-last.js
eslint/lib/rules/eqeqeq.js
eslint/lib/rules/for-direction.js
eslint/lib/rules/func-call-spacing.js
eslint/lib/rules/func-name-matching.js
eslint/lib/rules/func-names.js
eslint/lib/rules/func-style.js
eslint/lib/rules/function-call-argument-newline.js
eslint/lib/rules/function-paren-newline.js
eslint/lib/rules/generator-star-spacing.js
eslint/lib/rules/getter-return.js
eslint/lib/rules/global-require.js
eslint/lib/rules/grouped-accessor-pairs.js
eslint/lib/rules/guard-for-in.js
eslint/lib/rules/handle-callback-err.js
eslint/lib/rules/id-blacklist.js
eslint/lib/rules/id-denylist.js
eslint/lib/rules/id-length.js
eslint/lib/rules/id-match.js
eslint/lib/rules/implicit-arrow-linebreak.js
eslint/lib/rules/indent-legacy.js
eslint/lib/rules/indent.js
eslint/lib/rules/index.js
eslint/lib/rules/init-declarations.js
eslint/lib/rules/jsx-quotes.js
eslint/lib/rules/key-spacing.js
eslint/lib/rules/keyword-spacing.js
eslint/lib/rules/line-comment-position.js
eslint/lib/rules/linebreak-style.js
eslint/lib/rules/lines-around-comment.js
eslint/lib/rules/lines-around-directive.js
eslint/lib/rules/lines-between-class-members.js
eslint/lib/rules/max-classes-per-file.js
eslint/lib/rules/max-depth.js
eslint/lib/rules/max-len.js
eslint/lib/rules/max-lines-per-function.js
eslint/lib/rules/max-lines.js
eslint/lib/rules/max-nested-callbacks.js
eslint/lib/rules/max-params.js
eslint/lib/rules/max-statements-per-line.js
eslint/lib/rules/max-statements.js
eslint/lib/rules/multiline-comment-style.js
eslint/lib/rules/multiline-ternary.js
eslint/lib/rules/new-cap.js
eslint/lib/rules/new-parens.js
eslint/lib/rules/newline-after-var.js
eslint/lib/rules/newline-before-return.js
eslint/lib/rules/newline-per-chained-call.js
eslint/lib/rules/no-alert.js
eslint/lib/rules/no-array-constructor.js
eslint/lib/rules/no-async-promise-executor.js
eslint/lib/rules/no-await-in-loop.js
eslint/lib/rules/no-bitwise.js
eslint/lib/rules/no-buffer-constructor.js
eslint/lib/rules/no-caller.js
eslint/lib/rules/no-case-declarations.js
eslint/lib/rules/no-catch-shadow.js
eslint/lib/rules/no-class-assign.js
eslint/lib/rules/no-compare-neg-zero.js
eslint/lib/rules/no-cond-assign.js
eslint/lib/rules/no-confusing-arrow.js
eslint/lib/rules/no-console.js
eslint/lib/rules/no-const-assign.js
eslint/lib/rules/no-constant-condition.js
eslint/lib/rules/no-constructor-return.js
eslint/lib/rules/no-continue.js
eslint/lib/rules/no-control-regex.js
eslint/lib/rules/no-debugger.js
eslint/lib/rules/no-delete-var.js
eslint/lib/rules/no-div-regex.js
eslint/lib/rules/no-dupe-args.js
eslint/lib/rules/no-dupe-class-members.js
eslint/lib/rules/no-dupe-else-if.js
eslint/lib/rules/no-dupe-keys.js
eslint/lib/rules/no-duplicate-case.js
eslint/lib/rules/no-duplicate-imports.js
eslint/lib/rules/no-else-return.js
eslint/lib/rules/no-empty-character-class.js
eslint/lib/rules/no-empty-function.js
eslint/lib/rules/no-empty-pattern.js
eslint/lib/rules/no-empty.js
eslint/lib/rules/no-eq-null.js
eslint/lib/rules/no-eval.js
eslint/lib/rules/no-ex-assign.js
eslint/lib/rules/no-extend-native.js
eslint/lib/rules/no-extra-bind.js
eslint/lib/rules/no-extra-boolean-cast.js
eslint/lib/rules/no-extra-label.js
eslint/lib/rules/no-extra-parens.js
eslint/lib/rules/no-extra-semi.js
eslint/lib/rules/no-fallthrough.js
eslint/lib/rules/no-floating-decimal.js
eslint/lib/rules/no-func-assign.js
eslint/lib/rules/no-global-assign.js
eslint/lib/rules/no-implicit-coercion.js
eslint/lib/rules/no-implicit-globals.js
eslint/lib/rules/no-implied-eval.js
eslint/lib/rules/no-import-assign.js
eslint/lib/rules/no-inline-comments.js
eslint/lib/rules/no-inner-declarations.js
eslint/lib/rules/no-invalid-regexp.js
eslint/lib/rules/no-invalid-this.js
eslint/lib/rules/no-irregular-whitespace.js
eslint/lib/rules/no-iterator.js
eslint/lib/rules/no-label-var.js
eslint/lib/rules/no-labels.js
eslint/lib/rules/no-lone-blocks.js
eslint/lib/rules/no-lonely-if.js
eslint/lib/rules/no-loop-func.js
eslint/lib/rules/no-loss-of-precision.js
eslint/lib/rules/no-magic-numbers.js
eslint/lib/rules/no-misleading-character-class.js
eslint/lib/rules/no-mixed-operators.js
eslint/lib/rules/no-mixed-requires.js
eslint/lib/rules/no-mixed-spaces-and-tabs.js
eslint/lib/rules/no-multi-assign.js
eslint/lib/rules/no-multi-spaces.js
eslint/lib/rules/no-multi-str.js
eslint/lib/rules/no-multiple-empty-lines.js
eslint/lib/rules/no-native-reassign.js
eslint/lib/rules/no-negated-condition.js
eslint/lib/rules/no-negated-in-lhs.js
eslint/lib/rules/no-nested-ternary.js
eslint/lib/rules/no-new-func.js
eslint/lib/rules/no-new-object.js
eslint/lib/rules/no-new-require.js
eslint/lib/rules/no-new-symbol.js
eslint/lib/rules/no-new-wrappers.js
eslint/lib/rules/no-new.js
eslint/lib/rules/no-nonoctal-decimal-escape.js
eslint/lib/rules/no-obj-calls.js
eslint/lib/rules/no-octal-escape.js
eslint/lib/rules/no-octal.js
eslint/lib/rules/no-param-reassign.js
eslint/lib/rules/no-path-concat.js
eslint/lib/rules/no-plusplus.js
eslint/lib/rules/no-process-env.js
eslint/lib/rules/no-process-exit.js
eslint/lib/rules/no-promise-executor-return.js
eslint/lib/rules/no-proto.js
eslint/lib/rules/no-prototype-builtins.js
eslint/lib/rules/no-redeclare.js
eslint/lib/rules/no-regex-spaces.js
eslint/lib/rules/no-restricted-exports.js
eslint/lib/rules/no-restricted-globals.js
eslint/lib/rules/no-restricted-imports.js
eslint/lib/rules/no-restricted-modules.js
eslint/lib/rules/no-restricted-properties.js
eslint/lib/rules/no-restricted-syntax.js
eslint/lib/rules/no-return-assign.js
eslint/lib/rules/no-return-await.js
eslint/lib/rules/no-script-url.js
eslint/lib/rules/no-self-assign.js
eslint/lib/rules/no-self-compare.js
eslint/lib/rules/no-sequences.js
eslint/lib/rules/no-setter-return.js
eslint/lib/rules/no-shadow-restricted-names.js
eslint/lib/rules/no-shadow.js
eslint/lib/rules/no-spaced-func.js
eslint/lib/rules/no-sparse-arrays.js
eslint/lib/rules/no-sync.js
eslint/lib/rules/no-tabs.js
eslint/lib/rules/no-template-curly-in-string.js
eslint/lib/rules/no-ternary.js
eslint/lib/rules/no-this-before-super.js
eslint/lib/rules/no-throw-literal.js
eslint/lib/rules/no-trailing-spaces.js
eslint/lib/rules/no-undef-init.js
eslint/lib/rules/no-undef.js
eslint/lib/rules/no-undefined.js
eslint/lib/rules/no-underscore-dangle.js
eslint/lib/rules/no-unexpected-multiline.js
eslint/lib/rules/no-unmodified-loop-condition.js
eslint/lib/rules/no-unneeded-ternary.js
eslint/lib/rules/no-unreachable-loop.js
eslint/lib/rules/no-unreachable.js
eslint/lib/rules/no-unsafe-finally.js
eslint/lib/rules/no-unsafe-negation.js
eslint/lib/rules/no-unsafe-optional-chaining.js
eslint/lib/rules/no-unused-expressions.js
eslint/lib/rules/no-unused-labels.js
eslint/lib/rules/no-unused-private-class-members.js [new file with mode: 0644]
eslint/lib/rules/no-unused-vars.js
eslint/lib/rules/no-use-before-define.js
eslint/lib/rules/no-useless-backreference.js
eslint/lib/rules/no-useless-call.js
eslint/lib/rules/no-useless-catch.js
eslint/lib/rules/no-useless-computed-key.js
eslint/lib/rules/no-useless-concat.js
eslint/lib/rules/no-useless-constructor.js
eslint/lib/rules/no-useless-escape.js
eslint/lib/rules/no-useless-rename.js
eslint/lib/rules/no-useless-return.js
eslint/lib/rules/no-var.js
eslint/lib/rules/no-void.js
eslint/lib/rules/no-warning-comments.js
eslint/lib/rules/no-whitespace-before-property.js
eslint/lib/rules/no-with.js
eslint/lib/rules/nonblock-statement-body-position.js
eslint/lib/rules/object-curly-newline.js
eslint/lib/rules/object-curly-spacing.js
eslint/lib/rules/object-property-newline.js
eslint/lib/rules/object-shorthand.js
eslint/lib/rules/one-var-declaration-per-line.js
eslint/lib/rules/one-var.js
eslint/lib/rules/operator-assignment.js
eslint/lib/rules/operator-linebreak.js
eslint/lib/rules/padded-blocks.js
eslint/lib/rules/padding-line-between-statements.js
eslint/lib/rules/prefer-arrow-callback.js
eslint/lib/rules/prefer-const.js
eslint/lib/rules/prefer-destructuring.js
eslint/lib/rules/prefer-exponentiation-operator.js
eslint/lib/rules/prefer-named-capture-group.js
eslint/lib/rules/prefer-numeric-literals.js
eslint/lib/rules/prefer-object-spread.js
eslint/lib/rules/prefer-promise-reject-errors.js
eslint/lib/rules/prefer-reflect.js
eslint/lib/rules/prefer-regex-literals.js
eslint/lib/rules/prefer-rest-params.js
eslint/lib/rules/prefer-spread.js
eslint/lib/rules/prefer-template.js
eslint/lib/rules/quote-props.js
eslint/lib/rules/quotes.js
eslint/lib/rules/radix.js
eslint/lib/rules/require-atomic-updates.js
eslint/lib/rules/require-await.js
eslint/lib/rules/require-jsdoc.js
eslint/lib/rules/require-unicode-regexp.js
eslint/lib/rules/require-yield.js
eslint/lib/rules/rest-spread-spacing.js
eslint/lib/rules/semi-spacing.js
eslint/lib/rules/semi-style.js
eslint/lib/rules/semi.js
eslint/lib/rules/sort-imports.js
eslint/lib/rules/sort-keys.js
eslint/lib/rules/sort-vars.js
eslint/lib/rules/space-before-blocks.js
eslint/lib/rules/space-before-function-paren.js
eslint/lib/rules/space-in-parens.js
eslint/lib/rules/space-infix-ops.js
eslint/lib/rules/space-unary-ops.js
eslint/lib/rules/spaced-comment.js
eslint/lib/rules/strict.js
eslint/lib/rules/switch-colon-spacing.js
eslint/lib/rules/symbol-description.js
eslint/lib/rules/template-curly-spacing.js
eslint/lib/rules/template-tag-spacing.js
eslint/lib/rules/unicode-bom.js
eslint/lib/rules/use-isnan.js
eslint/lib/rules/utils/ast-utils.js
eslint/lib/rules/utils/lazy-loading-rule-map.js
eslint/lib/rules/valid-jsdoc.js
eslint/lib/rules/valid-typeof.js
eslint/lib/rules/vars-on-top.js
eslint/lib/rules/wrap-iife.js
eslint/lib/rules/wrap-regex.js
eslint/lib/rules/yield-star-spacing.js
eslint/lib/rules/yoda.js
eslint/lib/shared/ajv.js
eslint/lib/shared/config-validator.js
eslint/lib/shared/logging.js
eslint/lib/shared/relative-module-resolver.js
eslint/lib/shared/runtime-info.js
eslint/lib/shared/traverser.js
eslint/lib/shared/types.js
eslint/lib/source-code/source-code.js
eslint/lib/source-code/token-store/cursor.js
eslint/lib/unsupported-api.js [new file with mode: 0644]
eslint/package.json
eslint/templates/blogpost.md.ejs
eslint/templates/formatter-examples.md.ejs
eslint/tests/_utils/in-memory-fs.js
eslint/tests/_utils/index.js
eslint/tests/bin/eslint.js
eslint/tests/fixtures/code-path-analysis/class-fields-init--arrow-function.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-fields-init--call-expression.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-fields-init--conditional.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-fields-init--simple.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-static-blocks--between-fields.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-static-blocks--between-methods.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-static-blocks--conditional.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-static-blocks--empty.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-static-blocks--function-inside.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-static-blocks--if-else.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-static-blocks--multiple-1.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-static-blocks--multiple-2.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/class-static-blocks--simple.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/function--new.js [new file with mode: 0644]
eslint/tests/fixtures/code-path-analysis/object-literal--conditional.js [new file with mode: 0644]
eslint/tests/fixtures/exit-on-fatal-error/fatal-error.js [new file with mode: 0644]
eslint/tests/fixtures/exit-on-fatal-error/no-fatal-error-rule-violation.js [new file with mode: 0644]
eslint/tests/fixtures/exit-on-fatal-error/no-fatal-error.js [new file with mode: 0644]
eslint/tests/fixtures/parsers/empty-program-parser.js [new file with mode: 0644]
eslint/tests/fixtures/parsers/enhanced-parser3.js
eslint/tests/fixtures/parsers/keyword-spacing/prefix-cast-operator-no-space.js [new file with mode: 0644]
eslint/tests/fixtures/parsers/keyword-spacing/prefix-cast-operator-space.js [new file with mode: 0644]
eslint/tests/fixtures/rules/make-syntax-error-rule.js
eslint/tests/fixtures/rules/wrong/custom-rule.js
eslint/tests/fixtures/testers/rule-tester/fixes-one-problem.js
eslint/tests/fixtures/testers/rule-tester/suggestions.js
eslint/tests/lib/api.js
eslint/tests/lib/cli-engine/cli-engine.js
eslint/tests/lib/cli-engine/file-enumerator.js
eslint/tests/lib/cli-engine/formatters/codeframe.js [deleted file]
eslint/tests/lib/cli-engine/formatters/junit.js
eslint/tests/lib/cli-engine/formatters/stylish.js
eslint/tests/lib/cli-engine/formatters/table.js [deleted file]
eslint/tests/lib/cli-engine/load-rules.js
eslint/tests/lib/cli.js
eslint/tests/lib/config/flat-config-array.js [new file with mode: 0644]
eslint/tests/lib/eslint/eslint.js
eslint/tests/lib/init/config-file.js
eslint/tests/lib/linter/apply-disable-directives.js
eslint/tests/lib/linter/code-path-analysis/code-path-analyzer.js
eslint/tests/lib/linter/code-path-analysis/code-path.js
eslint/tests/lib/linter/interpolate.js
eslint/tests/lib/linter/linter.js
eslint/tests/lib/linter/node-event-generator.js
eslint/tests/lib/linter/report-translator.js
eslint/tests/lib/linter/rule-fixer.js
eslint/tests/lib/linter/safe-emitter.js
eslint/tests/lib/linter/timing.js
eslint/tests/lib/rule-tester/no-test-runners.js
eslint/tests/lib/rule-tester/rule-tester.js
eslint/tests/lib/rules/accessor-pairs.js
eslint/tests/lib/rules/array-callback-return.js
eslint/tests/lib/rules/arrow-parens.js
eslint/tests/lib/rules/block-scoped-var.js
eslint/tests/lib/rules/block-spacing.js
eslint/tests/lib/rules/brace-style.js
eslint/tests/lib/rules/camelcase.js
eslint/tests/lib/rules/class-methods-use-this.js
eslint/tests/lib/rules/comma-style.js
eslint/tests/lib/rules/complexity.js
eslint/tests/lib/rules/computed-property-spacing.js
eslint/tests/lib/rules/consistent-return.js
eslint/tests/lib/rules/curly.js
eslint/tests/lib/rules/default-param-last.js
eslint/tests/lib/rules/dot-location.js
eslint/tests/lib/rules/dot-notation.js
eslint/tests/lib/rules/eol-last.js
eslint/tests/lib/rules/for-direction.js
eslint/tests/lib/rules/func-name-matching.js
eslint/tests/lib/rules/func-names.js
eslint/tests/lib/rules/getter-return.js
eslint/tests/lib/rules/global-require.js
eslint/tests/lib/rules/grouped-accessor-pairs.js
eslint/tests/lib/rules/id-denylist.js
eslint/tests/lib/rules/id-length.js
eslint/tests/lib/rules/id-match.js
eslint/tests/lib/rules/indent.js
eslint/tests/lib/rules/init-declarations.js
eslint/tests/lib/rules/jsx-quotes.js
eslint/tests/lib/rules/key-spacing.js
eslint/tests/lib/rules/keyword-spacing.js
eslint/tests/lib/rules/lines-around-comment.js
eslint/tests/lib/rules/lines-between-class-members.js
eslint/tests/lib/rules/max-classes-per-file.js
eslint/tests/lib/rules/max-depth.js
eslint/tests/lib/rules/max-lines-per-function.js
eslint/tests/lib/rules/max-nested-callbacks.js
eslint/tests/lib/rules/max-statements.js
eslint/tests/lib/rules/multiline-comment-style.js
eslint/tests/lib/rules/new-parens.js
eslint/tests/lib/rules/newline-per-chained-call.js
eslint/tests/lib/rules/no-await-in-loop.js
eslint/tests/lib/rules/no-buffer-constructor.js
eslint/tests/lib/rules/no-compare-neg-zero.js
eslint/tests/lib/rules/no-dupe-class-members.js
eslint/tests/lib/rules/no-empty-character-class.js
eslint/tests/lib/rules/no-eval.js
eslint/tests/lib/rules/no-extra-boolean-cast.js
eslint/tests/lib/rules/no-extra-parens.js
eslint/tests/lib/rules/no-extra-semi.js
eslint/tests/lib/rules/no-fallthrough.js
eslint/tests/lib/rules/no-inner-declarations.js
eslint/tests/lib/rules/no-invalid-regexp.js
eslint/tests/lib/rules/no-invalid-this.js
eslint/tests/lib/rules/no-lone-blocks.js
eslint/tests/lib/rules/no-loss-of-precision.js
eslint/tests/lib/rules/no-magic-numbers.js
eslint/tests/lib/rules/no-misleading-character-class.js
eslint/tests/lib/rules/no-multi-assign.js
eslint/tests/lib/rules/no-multiple-empty-lines.js
eslint/tests/lib/rules/no-new-func.js
eslint/tests/lib/rules/no-proto.js
eslint/tests/lib/rules/no-prototype-builtins.js
eslint/tests/lib/rules/no-redeclare.js
eslint/tests/lib/rules/no-regex-spaces.js
eslint/tests/lib/rules/no-restricted-imports.js
eslint/tests/lib/rules/no-restricted-modules.js
eslint/tests/lib/rules/no-restricted-properties.js
eslint/tests/lib/rules/no-self-assign.js
eslint/tests/lib/rules/no-self-compare.js
eslint/tests/lib/rules/no-setter-return.js
eslint/tests/lib/rules/no-shadow-restricted-names.js
eslint/tests/lib/rules/no-shadow.js
eslint/tests/lib/rules/no-this-before-super.js
eslint/tests/lib/rules/no-throw-literal.js
eslint/tests/lib/rules/no-undef-init.js
eslint/tests/lib/rules/no-undef.js
eslint/tests/lib/rules/no-underscore-dangle.js
eslint/tests/lib/rules/no-unexpected-multiline.js
eslint/tests/lib/rules/no-unmodified-loop-condition.js
eslint/tests/lib/rules/no-unreachable.js
eslint/tests/lib/rules/no-unsafe-optional-chaining.js
eslint/tests/lib/rules/no-unused-expressions.js
eslint/tests/lib/rules/no-unused-private-class-members.js [new file with mode: 0644]
eslint/tests/lib/rules/no-unused-vars.js
eslint/tests/lib/rules/no-use-before-define.js
eslint/tests/lib/rules/no-useless-call.js
eslint/tests/lib/rules/no-useless-computed-key.js
eslint/tests/lib/rules/no-useless-concat.js
eslint/tests/lib/rules/no-useless-escape.js
eslint/tests/lib/rules/no-useless-rename.js
eslint/tests/lib/rules/one-var.js
eslint/tests/lib/rules/operator-assignment.js
eslint/tests/lib/rules/operator-linebreak.js
eslint/tests/lib/rules/padded-blocks.js
eslint/tests/lib/rules/padding-line-between-statements.js
eslint/tests/lib/rules/prefer-const.js
eslint/tests/lib/rules/prefer-destructuring.js
eslint/tests/lib/rules/prefer-exponentiation-operator.js
eslint/tests/lib/rules/prefer-numeric-literals.js
eslint/tests/lib/rules/prefer-object-spread.js
eslint/tests/lib/rules/prefer-promise-reject-errors.js
eslint/tests/lib/rules/prefer-regex-literals.js
eslint/tests/lib/rules/prefer-rest-params.js
eslint/tests/lib/rules/prefer-spread.js
eslint/tests/lib/rules/quote-props.js
eslint/tests/lib/rules/quotes.js
eslint/tests/lib/rules/radix.js
eslint/tests/lib/rules/require-atomic-updates.js
eslint/tests/lib/rules/require-jsdoc.js
eslint/tests/lib/rules/require-unicode-regexp.js
eslint/tests/lib/rules/rest-spread-spacing.js
eslint/tests/lib/rules/semi-spacing.js
eslint/tests/lib/rules/semi-style.js
eslint/tests/lib/rules/semi.js
eslint/tests/lib/rules/space-before-blocks.js
eslint/tests/lib/rules/space-infix-ops.js
eslint/tests/lib/rules/space-unary-ops.js
eslint/tests/lib/rules/spaced-comment.js
eslint/tests/lib/rules/strict.js
eslint/tests/lib/rules/symbol-description.js
eslint/tests/lib/rules/unicode-bom.js
eslint/tests/lib/rules/use-isnan.js
eslint/tests/lib/rules/utils/ast-utils.js
eslint/tests/lib/rules/vars-on-top.js
eslint/tests/lib/rules/wrap-regex.js
eslint/tests/lib/shared/config-validator.js
eslint/tests/lib/shared/traverser.js
eslint/tests/lib/unsupported-api.js [new file with mode: 0644]
eslint/tests/tools/code-sample-minimizer.js
eslint/tests/tools/internal-rules/consistent-docs-url.js [deleted file]
eslint/tests/tools/internal-rules/consistent-meta-messages.js [deleted file]
eslint/tests/tools/internal-rules/multiline-comment-style.js
eslint/tests/tools/internal-rules/no-invalid-meta.js
eslint/tools/internal-rules/consistent-docs-url.js [deleted file]
eslint/tools/internal-rules/consistent-meta-messages.js [deleted file]
eslint/tools/internal-rules/multiline-comment-style.js
eslint/tools/internal-rules/no-invalid-meta.js
eslint/tools/internal-testers/event-generator-tester.js
eslint/tools/rule-types.json
eslint/tools/update-readme.js
eslint/tools/update-rule-types.js

index 668bc2747c54478cfe920192dd26f80650db2377..f97d93a1bf5ea3c42aa6f382b2e4344b7b45b7bd 100644 (file)
@@ -51,7 +51,7 @@ module.exports = {
         "plugin:eslint-plugin/recommended"
     ],
     parserOptions: {
-        ecmaVersion: 2020
+        ecmaVersion: 2021
     },
 
     /*
@@ -64,13 +64,12 @@ module.exports = {
         }
     },
     rules: {
-        "eslint-plugin/consistent-output": "error",
-        "eslint-plugin/no-deprecated-context-methods": "error",
+        "eslint-plugin/prefer-message-ids": "error",
         "eslint-plugin/prefer-output-null": "error",
         "eslint-plugin/prefer-placeholders": "error",
+        "eslint-plugin/prefer-replace-text": "error",
         "eslint-plugin/report-message-format": ["error", "[^a-z].*\\.$"],
         "eslint-plugin/require-meta-docs-description": "error",
-        "eslint-plugin/require-meta-type": "error",
         "eslint-plugin/test-case-property-ordering": "error",
         "eslint-plugin/test-case-shorthand-strings": "error",
         "internal-rules/multiline-comment-style": "error"
@@ -80,15 +79,14 @@ module.exports = {
             files: ["lib/rules/*", "tools/internal-rules/*"],
             excludedFiles: ["index.js"],
             rules: {
-                "internal-rules/no-invalid-meta": "error",
-                "internal-rules/consistent-meta-messages": "error"
+                "internal-rules/no-invalid-meta": "error"
             }
         },
         {
             files: ["lib/rules/*"],
             excludedFiles: ["index.js"],
             rules: {
-                "internal-rules/consistent-docs-url": "error"
+                "eslint-plugin/require-meta-docs-url": ["error", { pattern: "https://eslint.org/docs/rules/{{name}}" }]
             }
         },
         {
@@ -105,6 +103,7 @@ module.exports = {
         // Restrict relative path imports
         {
             files: ["lib/*"],
+            excludedFiles: ["lib/unsupported-api.js"],
             rules: {
                 "node/no-restricted-require": ["error", [
                     ...createInternalFilesPatterns()
diff --git a/eslint/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/eslint/.github/ISSUE_TEMPLATE/BUG_REPORT.md
deleted file mode 100644 (file)
index 9b46d29..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
----
-name: "\U0001F41E  Bug report"
-about: Report an issue with ESLint or rules bundled with ESLint
-title: ''
-labels: bug, repro:needed
-assignees: ''
-
----
-
-<!--
-    ESLint adheres to the Open JS Foundation Code of Conduct:
-    https://eslint.org/conduct
-
-    This template is for bug reports. If you are here for another reason, please see below:
-
-    1. To propose a new rule: https://eslint.org/docs/developer-guide/contributing/new-rules
-    2. To request a rule change: https://eslint.org/docs/developer-guide/contributing/rule-changes
-    3. To request a change that is not a bug fix, rule change, or new rule: https://eslint.org/docs/developer-guide/contributing/changes
-    4. If you have any questions, please stop by our chatroom: https://eslint.org/chat/help
-
-    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
--->
-
-
-**Tell us about your environment**
-
-<!--
-    If you are using ESLint v6.5.0 or later, you can run ESLint with the `--env-info` flag and paste the output here.
--->
-
-* **ESLint Version:**
-* **Node Version:**
-* **npm Version:**
-* **Operating System:** 
-
-**What parser (default, `@babel/eslint-parser`, `@typescript-eslint/parser`, etc.) are you using?**
-
-<!-- if you are using a parser other than ESLint's default, please try using the default parser to verify if the issue is parser-specific -->
-
-**Please show your full configuration:**
-
-<details>
-<summary>Configuration</summary>
-
-<!-- Paste your configuration below: -->
-```js
-
-```
-
-</details>
-
-**What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.**
-
-<!-- Paste the source code below: -->
-```js
-
-```
-
-<!-- Paste the command you used to run ESLint: -->
-```bash
-
-```
-
-**What did you expect to happen?**
-
-
-**What actually happened? Please copy-paste the actual, raw output from ESLint.**
-
-
-**Steps to reproduce this issue:**
-
-<!-- Please tell us exactly how to see the issue you're describing -->
-
-1. 
-1. 
-1. 
-
-**Are you willing to submit a pull request to fix this bug?**
diff --git a/eslint/.github/ISSUE_TEMPLATE/CHANGE.md b/eslint/.github/ISSUE_TEMPLATE/CHANGE.md
deleted file mode 100644 (file)
index ef8437a..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
----
-name: "\U0001F4DD Non-rule change request"
-about: Request a change that is not a bug fix, rule change, or new rule
-title: ''
-labels: enhancement, triage, core
-assignees: ''
-
----
-
-<!--
-    ESLint adheres to the Open JS Foundation Code of Conduct:
-    https://eslint.org/conduct
-
-    This template is for requesting a change that is not a bug fix, rule change, or new rule. If you are here for another reason, please see below:
-
-    1. To report a bug: https://eslint.org/docs/developer-guide/contributing/reporting-bugs
-    2. To request a rule change: https://eslint.org/docs/developer-guide/contributing/rule-changes
-    3. To propose a new rule: https://eslint.org/docs/developer-guide/contributing/new-rules
-    4. If you have any questions, please stop by our chatroom: https://eslint.org/chat
-
-    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
--->
-
-
-
-**The version of ESLint you are using.**
-
-
-**The problem you want to solve.**
-
-
-**Your take on the correct solution to problem.**
-
-
-**Are you willing to submit a pull request to implement this change?**
diff --git a/eslint/.github/ISSUE_TEMPLATE/NEW_RULE.md b/eslint/.github/ISSUE_TEMPLATE/NEW_RULE.md
deleted file mode 100644 (file)
index 31dfc8c..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
----
-name: "\U0001F680 New rule proposal"
-about: Propose a new rule to be added to ESLint
-title: ''
-labels: triage, rule, feature
-assignees: ''
-
----
-
-<!--
-    ESLint adheres to the Open JS Foundation Code of Conduct:
-    https://eslint.org/conduct
-
-    This template is for new rule proposals. If you are proposing a new rule, please continue on. If you are here for another reason, please see below:
-
-    1. To report a bug: https://eslint.org/docs/developer-guide/contributing/reporting-bugs
-    2. To request a rule change: https://eslint.org/docs/developer-guide/contributing/rule-changes
-    3. To request a change that is not a bug fix, rule change, or new rule: https://eslint.org/docs/developer-guide/contributing/changes
-    4. If you have any questions, please stop by our chatroom: https://eslint.org/chat
-
-    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
--->
-
-
-**Please describe what the rule should do:**
-
-
-**What new ECMAScript feature does this rule relate to?**
-
-<!-- New rules must be related to ECMAScript features added within the last 12 months -->
-
-**What category of rule is this? (place an "X" next to just one item)**
-
-[ ] Warns about a potential error (problem)
-[ ] Suggests an alternate way of doing something (suggestion)
-[ ] Other (please specify:)
-
-**Provide 2-3 code examples that this rule will warn about:**
-
-<!-- Put your code examples here -->
-```js
-
-```
-
-**Why should this rule be included in ESLint (instead of a plugin)?**
-
-
-**Are you willing to submit a pull request to implement this rule?**
diff --git a/eslint/.github/ISSUE_TEMPLATE/NEW_SYNTAX.md b/eslint/.github/ISSUE_TEMPLATE/NEW_SYNTAX.md
new file mode 100644 (file)
index 0000000..e03cb6e
--- /dev/null
@@ -0,0 +1,43 @@
+---
+name: "\U0001F4DD Request new syntax support"
+about: Request new stage 4 syntax be supported.
+title: ''
+labels:
+    - core
+    - new syntax
+assignees: ''
+
+---
+
+<!--
+    ESLint adheres to the Open JS Foundation Code of Conduct:
+    https://eslint.org/conduct
+-->
+
+**What is the name of the syntax to implement?**
+
+<!-- for example, "class fields" -->
+
+**Please provide the TC39 URL for the syntax proposal:**
+
+
+
+**Please provide some example code for the new syntax:**
+
+```js
+// example code here
+```
+
+## Implementation Checklist
+
+Please check off all items that have already been completed. Be sure to paste the pull request URLs next to each item so we can verify the work as done.
+
+- [ ] Ecma262 update: <!-- paste PR URL for this syntax here -->
+- [ ] ESTree update: <!-- paste PR URL for this ESTree update here -->
+- [ ] Acorn update: <!-- paste PR URL for this syntax here -->
+- [ ] `eslint-visitor-keys` update: <!-- paste PR URL for this syntax here -->
+- [ ] `espree` update: <!-- paste PR URL for this syntax here -->
+- [ ] `eslint-scope` update: <!-- paste PR URL for this syntax here -->
+- [ ] `eslint` update: <!-- paste PR URL for this syntax here -->
+
+**Are you willing to submit a pull request to implement this syntax?**
diff --git a/eslint/.github/ISSUE_TEMPLATE/RULE_CHANGE.md b/eslint/.github/ISSUE_TEMPLATE/RULE_CHANGE.md
deleted file mode 100644 (file)
index ced29fa..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
----
-name: "\U0001F4DD Rule change request"
-about: Request a change to an existing rule
-title: ''
-labels: enhancement, triage, rule
-assignees: ''
-
----
-
-<!--
-    ESLint adheres to the Open JS Foundation Code of Conduct:
-    https://eslint.org/conduct
-
-    This template is for requesting a rule change. If you are here for another reason, please see below:
-
-    1. To report a bug: https://eslint.org/docs/developer-guide/contributing/reporting-bugs
-    2. To propose a new rule: https://eslint.org/docs/developer-guide/contributing/new-rules
-    3. To request a change that is not a bug fix, rule change, or new rule: https://eslint.org/docs/developer-guide/contributing/changes
-    4. If you have any questions, please stop by our chatroom: https://eslint.org/chat
-
-    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
--->
-
-
-**What rule do you want to change?**
-
-**Does this change cause the rule to produce more or fewer warnings?**
-
-**How will the change be implemented? (New option, new default behavior, etc.)?**
-
-**Please provide some example code that this change will affect:**
-
-<!-- Put your code examples here -->
-```js
-
-```
-
-**What does the rule currently do for this code?**
-
-**What will the rule do after it's changed?**
-
-**Are you willing to submit a pull request to implement this change?**
diff --git a/eslint/.github/ISSUE_TEMPLATE/bug-report.yml b/eslint/.github/ISSUE_TEMPLATE/bug-report.yml
new file mode 100644 (file)
index 0000000..8c477f4
--- /dev/null
@@ -0,0 +1,80 @@
+name: "\U0001F41E Report a problem"
+description: "Report an issue with ESLint or rules bundled with ESLint"
+title: "Bug: (fill in)"
+labels:
+  - bug
+  - "repro:needed"
+body:
+- type: markdown
+  attributes:
+    value: By opening an issue, you agree to abide by the [Open JS Foundation Code of Conduct](https://eslint.org/conduct).
+- type: textarea
+  attributes:
+    label: Environment
+    description: |
+      Please tell us about how you're running ESLint (Run `npx eslint --env-info`.)
+    value: |
+        Node version: 
+        npm version: 
+        Local ESLint version: 
+        Global ESLint version: 
+        Operating System: 
+  validations:
+    required: true
+- type: dropdown
+  attributes:
+    label: What parser are you using?
+    description: |
+      Please keep in mind that some problems are parser-specific.
+    options:
+      - "Default (Espree)"
+      - "@typescript-eslint/parser"
+      - "@babel/eslint-parser"
+      - "vue-eslint-parser"
+      - "@angular-eslint/template-parser"
+      - Other
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: What did you do?
+    description: |
+      Please include a *minimal* reproduction case with source code, configuration file, any other information about how you're using ESLint. You can use Markdown in this field.
+    value: |
+      <details>
+      <summary>Configuration</summary>
+
+      ```
+      <!-- Paste your configuration here -->
+      ```
+      </details>
+
+      ```js
+      <!-- Paste your code here -->
+      ```
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: What did you expect to happen?
+    description: |
+      You can use Markdown in this field.
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: What actually happened?
+    description: |
+      Please copy-paste the actual ESLint output. You can use Markdown in this field.
+  validations:
+    required: true
+- type: checkboxes
+  attributes:
+    label: Participation
+    options:
+      - label: I am willing to submit a pull request for this issue.
+        required: false
+- type: textarea
+  attributes:
+    label: Additional comments
+    description: Is there anything else that's important for the team to know?
diff --git a/eslint/.github/ISSUE_TEMPLATE/change.yml b/eslint/.github/ISSUE_TEMPLATE/change.yml
new file mode 100644 (file)
index 0000000..cc2066d
--- /dev/null
@@ -0,0 +1,48 @@
+name: "\U0001F4DD Request a change (not rule-related)"
+description: "Request a change that is not a bug fix, rule change, or new rule"
+title: "Change Request: (fill in)"
+labels:
+  - enhancement
+  - triage
+  - core
+body:
+- type: markdown
+  attributes:
+    value: By opening an issue, you agree to abide by the [Open JS Foundation Code of Conduct](https://eslint.org/conduct).
+- type: input
+  attributes:
+    label: ESLint version
+    description: |
+      What version of ESLint are you currently using? (Run `npx eslint --version`.)
+    placeholder: |
+      e.g. v8.0.0
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: What problem do you want to solve?
+    description: |
+      Please explain your use case in as much detail as possible.
+    placeholder: |
+      ESLint currently...
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: What do you think is the correct solution?
+    description: |
+      Please explain how you'd like to change ESLint to address the problem.
+    placeholder: |
+      I'd like ESLint to...
+  validations:
+    required: true
+- type: checkboxes
+  attributes:
+    label: Participation
+    options:
+      - label: I am willing to submit a pull request for this change.
+        required: false
+- type: textarea
+  attributes:
+    label: Additional comments
+    description: Is there anything else that's important for the team to know?
diff --git a/eslint/.github/ISSUE_TEMPLATE/new-rule.yml b/eslint/.github/ISSUE_TEMPLATE/new-rule.yml
new file mode 100644 (file)
index 0000000..9ef139f
--- /dev/null
@@ -0,0 +1,54 @@
+name: "\U0001F680 Propose a new core rule"
+description: "Propose a new rule to be added to the ESLint core"
+title: "New Rule: (fill in)"
+labels:
+  - rule
+  - feature
+body:
+- type: markdown
+  attributes:
+    value: By opening an issue, you agree to abide by the [Open JS Foundation Code of Conduct](https://eslint.org/conduct).
+- type: input
+  attributes:
+    label: Rule details
+    description: What should the new rule do?
+  validations:
+    required: true
+- type: input
+  attributes:
+    label: Related ECMAScript feature
+    description: What new ECMAScript feature does this rule relate to? Note that we only accept new core rules related to new ECMAScript features.
+  validations:
+    required: true
+- type: dropdown
+  attributes:
+    label: What type of rule is this?
+    options:
+      - Warns about a potential problem
+      - Suggests an alternate way of doing something
+      - Enforces a formatting/stylistic preference
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: Example code
+    description: Please provide some example JavaScript code that this rule will warn about. This field will render as JavaScript.
+    render: js
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: Why should this rule be in the core instead of a plugin?
+    description: In general, we prefer that rules be implemented in plugins where they can be tailored to your specific use case.
+  validations:
+    required: true
+- type: checkboxes
+  attributes:
+    label: Participation
+    options:
+      - label: I am willing to submit a pull request to implement this rule.
+        required: false
+- type: textarea
+  attributes:
+    label: Additional comments
+    description: Is there anything else that's important for the team to know?
diff --git a/eslint/.github/ISSUE_TEMPLATE/rule-change.yml b/eslint/.github/ISSUE_TEMPLATE/rule-change.yml
new file mode 100644 (file)
index 0000000..86758cb
--- /dev/null
@@ -0,0 +1,61 @@
+name: "\U0001F4DD Request a rule change"
+description: "Request a change to an existing core rule"
+title: "Rule Change: (fill in)"
+labels:
+  - enhancement
+  - rule
+body:
+- type: markdown
+  attributes:
+    value: By opening an issue, you agree to abide by the [Open JS Foundation Code of Conduct](https://eslint.org/conduct).
+- type: input
+  attributes:
+    label: What rule do you want to change?
+  validations:
+    required: true
+- type: dropdown
+  attributes:
+    label: What change to do you want to make?
+    options:
+      - Generate more warnings
+      - Generate fewer warnings
+      - Implement autofix
+      - Implement suggestions
+  validations:
+    required: true
+- type: dropdown
+  attributes:
+    label: How do you think the change should be implemented?
+    options:
+      - A new option
+      - A new default behavior
+      - Other
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: Example code
+    description: Please provide some example code that this change will affect. This field will render as JavaScript.
+    render: js
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: What does the rule currently do for this code?
+  validations:
+    required: true
+- type: textarea
+  attributes:
+    label: What will the rule do after it's changed?
+  validations:
+    required: true
+- type: checkboxes
+  attributes:
+    label: Participation
+    options:
+      - label: I am willing to submit a pull request to implement this change.
+        required: false
+- type: textarea
+  attributes:
+    label: Additional comments
+    description: Is there anything else that's important for the team to know?
index f0d6c3e2ed91f4562805ea4331007c799ecfa687..e15677eada1047dc68ff50c6ddd4a57b552c0ab1 100644 (file)
@@ -6,7 +6,7 @@
 
 #### Prerequisites checklist
 
-- [ ] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/master/CONTRIBUTING.md).
+- [ ] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/HEAD/CONTRIBUTING.md).
 
 #### What is the purpose of this pull request? (put an "X" next to an item)
 
@@ -16,9 +16,9 @@
 -->
 
 [ ] Documentation update
-[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
-[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
-[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
+[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/bug-report.md))
+[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-proposal.md))
+[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-change-proposal.md))
 [ ] Add autofixing to a rule
 [ ] Add a CLI option
 [ ] Add something to the core
index 6ba0bdc384be3e8634460e11d1cbfc358a737679..d6baaa97036dcaad34f90eabc1fa53effaf0408d 100644 (file)
@@ -1,9 +1,9 @@
 name: CI
 on:
   push:
-    branches: [master]
+    branches: [master, main]
   pull_request:
-    branches: [master]
+    branches: [master, main]
 
 jobs:
   verify_files:
@@ -11,7 +11,9 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v2
-    - uses: actions/setup-node@v1
+    - uses: actions/setup-node@v2
+      with:
+        node-version: '14.x'
     - name: Install Packages
       run: npm install
     - name: Lint Files
@@ -26,7 +28,7 @@ jobs:
     strategy:
       matrix:
         os: [ubuntu-latest]
-        node: [16.x, 15.x, 14.x, 13.x, 12.x, 10.x, "10.12.0"]
+        node: [17.x, 16.x, 14.x, 12.x, "12.22.0"]
         include:
         - os: windows-latest
           node: "12.x"
@@ -35,7 +37,7 @@ jobs:
     runs-on: ${{ matrix.os }}
     steps:
     - uses: actions/checkout@v2
-    - uses: actions/setup-node@v1
+    - uses: actions/setup-node@v2
       with:
         node-version: ${{ matrix.node }}
     - name: Install Packages
@@ -50,7 +52,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v2
-    - uses: actions/setup-node@v1
+    - uses: actions/setup-node@v2
       with:
         node-version: '12'
     - name: Install Packages
diff --git a/eslint/.github/workflows/codeql-analysis.yml b/eslint/.github/workflows/codeql-analysis.yml
new file mode 100644 (file)
index 0000000..ef362a4
--- /dev/null
@@ -0,0 +1,71 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+  push:
+    branches: [master, main]
+  pull_request:
+    # The branches below must be a subset of the branches above
+    branches: [master, main]
+  schedule:
+    - cron: '28 17 * * 5'
+
+jobs:
+  analyze:
+    name: Analyze
+    runs-on: ubuntu-latest
+    permissions:
+      actions: read
+      contents: read
+      security-events: write
+
+    strategy:
+      fail-fast: false
+      matrix:
+        language: [ 'javascript' ]
+        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
+        # Learn more:
+        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v2
+
+    # Initializes the CodeQL tools for scanning.
+    - name: Initialize CodeQL
+      uses: github/codeql-action/init@v1
+      with:
+        languages: ${{ matrix.language }}
+        # If you wish to specify custom queries, you can do so here or in a config file.
+        # By default, queries listed here will override any specified in a config file.
+        # Prefix the list here with "+" to use these queries and those in the config file.
+        # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
+    # If this step fails, then you should remove it and run the build manually (see below)
+    - name: Autobuild
+      uses: github/codeql-action/autobuild@v1
+
+    # ℹ️ Command-line programs to run using the OS shell.
+    # 📚 https://git.io/JvXDl
+
+    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+    #    and modify them (or add more) to build your code if your project
+    #    uses a compiled language
+
+    #- run: |
+    #   make bootstrap
+    #   make release
+
+    - name: Perform CodeQL Analysis
+      uses: github/codeql-action/analyze@v1
diff --git a/eslint/.github/workflows/stale.yml b/eslint/.github/workflows/stale.yml
new file mode 100644 (file)
index 0000000..4574022
--- /dev/null
@@ -0,0 +1,30 @@
+# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
+#
+# You can adjust the behavior by modifying this file.
+# For more information, see:
+# https://github.com/actions/stale
+name: Mark stale issues and pull requests
+
+on:
+  schedule:
+  - cron: '31 22 * * *'
+
+jobs:
+  stale:
+
+    runs-on: ubuntu-latest
+    permissions:
+      issues: write
+      pull-requests: write
+
+    steps:
+    - uses: actions/stale@v3
+      with:
+        repo-token: ${{ secrets.GITHUB_TOKEN }}
+        days-before-stale: 60
+        days-before-close: 7
+        stale-issue-message: 'Oops! It looks like we lost track of this issue. What do we want to do here? This issue will auto-close in 7 days without an update.'
+        stale-pr-message: 'Oops! It looks like we lost track of this pull request. What do we want to do here? This pull request will auto-close in 7 days without an update.'
+        exempt-all-assignees: true
+        exempt-issue-labels: accepted
+        exempt-pr-labels: accepted
diff --git a/eslint/.markdownlintignore b/eslint/.markdownlintignore
new file mode 100644 (file)
index 0000000..1b763b1
--- /dev/null
@@ -0,0 +1 @@
+CHANGELOG.md
index 6eaeb9a9a560a6afd9245fc6bce5cbf2edee911e..abcfbb41ca4150582c0771f0bcf8e0c03aa7acd9 100644 (file)
@@ -1,3 +1,367 @@
+v8.3.0 - November 21, 2021
+
+* [`60b0a29`](https://github.com/eslint/eslint/commit/60b0a292efd1b9cdc318b1e88a0cb7bbf14860b1) feat: add `allowProperties` option to require-atomic-updates (#15238) (Milos Djermanovic)
+* [`79278a1`](https://github.com/eslint/eslint/commit/79278a14f1c8747bff8f5cb2100d8776f9d517f2) feat: update no-use-before-define for class static blocks (#15312) (Milos Djermanovic)
+* [`8aa7645`](https://github.com/eslint/eslint/commit/8aa764524cf74f0b70d184c7957dbbb5f36a5ac7) fix: update vars-on-top for class static blocks (#15306) (Milos Djermanovic)
+* [`479a4cb`](https://github.com/eslint/eslint/commit/479a4cbc70f4032d4accd48e4471629e8635d677) fix: update semi-style for class static blocks (#15309) (Milos Djermanovic)
+* [`ddd01dc`](https://github.com/eslint/eslint/commit/ddd01dcd5f14c6ddea5decca46db2f379ec35aeb) feat: update no-redeclare for class static blocks (#15313) (Milos Djermanovic)
+* [`de69cec`](https://github.com/eslint/eslint/commit/de69cec834411aeb276a525c11dc10f628df2f51) feat: update no-inner-declarations for class static blocks (#15290) (Milos Djermanovic)
+* [`e2fe7ef`](https://github.com/eslint/eslint/commit/e2fe7ef7ea0458de56bed4e4c3d5f71aaebd3f28) feat: support for private-in syntax (fixes #14811) (#15060) (Yosuke Ota)
+* [`34bc8d7`](https://github.com/eslint/eslint/commit/34bc8d7cb42d696ec56e0a3c780aa5b042285d6b) feat: Update espree and eslint-scope (#15338) (Brandon Mills)
+* [`b171cd7`](https://github.com/eslint/eslint/commit/b171cd7ec839a0481a74a613b0d48a193f16bb6b) feat: update max-depth for class static blocks (#15316) (Milos Djermanovic)
+* [`6487df3`](https://github.com/eslint/eslint/commit/6487df371496dd15272e2097e4d2c932532c8727) feat: update padded-blocks for class static blocks (#15333) (Milos Djermanovic)
+* [`194f36d`](https://github.com/eslint/eslint/commit/194f36d9c009a72ec72fa9592ea9e31f9f168a52) feat: update the complexity rule for class static blocks (#15328) (Milos Djermanovic)
+* [`3530337`](https://github.com/eslint/eslint/commit/3530337e71327d8325d0de01e8e73952010b1a08) feat: update the indent rule for class static blocks (#15324) (Milos Djermanovic)
+* [`f03cd14`](https://github.com/eslint/eslint/commit/f03cd146a97ed312d635ac7b53ba0f8d01aa8b47) feat: update lines-around-comment for class static blocks (#15323) (Milos Djermanovic)
+* [`5c64747`](https://github.com/eslint/eslint/commit/5c64747a8d7a4f896f0cbce67c7f5e7690837a9b) feat: update brace-style for class static blocks (#15322) (Milos Djermanovic)
+* [`df2f1cc`](https://github.com/eslint/eslint/commit/df2f1cc81a559bbc9eee78a3a97315e2927af764) feat: update max-statements for class static blocks (#15315) (Milos Djermanovic)
+* [`fd5a0b8`](https://github.com/eslint/eslint/commit/fd5a0b8506e4b6acd740ab966cc2c0e4ff6a4d15) feat: update prefer-const for class static blocks (#15325) (Milos Djermanovic)
+* [`b3669fd`](https://github.com/eslint/eslint/commit/b3669fde2316f136af3a16b58b0c44e8ec196cee) feat: code path analysis for class static blocks (#15282) (Milos Djermanovic)
+* [`15c1397`](https://github.com/eslint/eslint/commit/15c1397f0063931f50f31af8d110a23c6d660000) feat: update eslint-scope for class static blocks (#15321) (Milos Djermanovic)
+* [`1a1bb4b`](https://github.com/eslint/eslint/commit/1a1bb4b1ee87c1b33f2d86ef70b3d81e83377547) feat: update one-var for class static blocks (#15317) (Milos Djermanovic)
+* [`9b666e0`](https://github.com/eslint/eslint/commit/9b666e0682bacf44d2a5afa0023874b8b131b5f5) feat: update padding-line-between-statements for class static blocks (#15318) (Milos Djermanovic)
+* [`6b85426`](https://github.com/eslint/eslint/commit/6b85426c33ba7ac0206cccef39ccc875b773aeae) docs: Expand  `--debug` option description in the CLI documentation (#15308) (darkred)
+* [`3ae5258`](https://github.com/eslint/eslint/commit/3ae52584296887e5fc5b0267346294bb920a00e6) docs: the strict rule does not apply to class static blocks (#15314) (Milos Djermanovic)
+* [`6d1c666`](https://github.com/eslint/eslint/commit/6d1c666d318cc9e1860e1e2c72fbfa4bdd4a2c4b) fix: update no-invalid-this and no-eval for class static blocks (#15300) (Milos Djermanovic)
+* [`d3a267f`](https://github.com/eslint/eslint/commit/d3a267f5f39167e3ee8248ae6b9cae5034d0486f) feat: update class-methods-use-this for class static blocks (#15298) (Milos Djermanovic)
+* [`cdaa541`](https://github.com/eslint/eslint/commit/cdaa54130aca7a9c8dfd76c613d0718b048401b2) feat: update no-lone-blocks for class static blocks (#15295) (Milos Djermanovic)
+* [`8611538`](https://github.com/eslint/eslint/commit/8611538b47e325c6d6b115bf3d901a26e9ac29f8) feat: update block-spacing for class static blocks (#15297) (Milos Djermanovic)
+* [`7b56844`](https://github.com/eslint/eslint/commit/7b56844ece544e501f0173f6427038c9c5e0534f) feat: update keyword-spacing for class static blocks (#15289) (Milos Djermanovic)
+* [`ea18711`](https://github.com/eslint/eslint/commit/ea1871146402a77234393613fe56a416382c7f0f) feat: update no-extra-semi for class static blocks (#15287) (Milos Djermanovic)
+* [`0f0971f`](https://github.com/eslint/eslint/commit/0f0971ffc2ca6f4513eeffdf5cfa36826c8f4543) feat: update semi rule for class static blocks (#15286) (Milos Djermanovic)
+* [`abe740c`](https://github.com/eslint/eslint/commit/abe740ce68dcc9e5413df93b3d80a2e3260f1c18) feat: add examples for block-scoped-var with class static blocks (#15302) (Milos Djermanovic)
+* [`9309841`](https://github.com/eslint/eslint/commit/9309841a6cfa85005e0bf79e20415bb9220ba46e) docs: Remove inconsistent colon in pull request docs (#15303) (Jordan Eldredge)
+* [`da238cc`](https://github.com/eslint/eslint/commit/da238cc731a9b5ecd48280e0ea4ebd8a48ebeedc) docs: remove deprecation note from lines-around-comment (#15293) (Milos Djermanovic)
+* [`1055f16`](https://github.com/eslint/eslint/commit/1055f16fc6f78cc553f0b1462e8af44244c1f84b) docs: no-unused-expressions - class static blocks don't have directives (#15283) (Milos Djermanovic)
+* [`edd8d24`](https://github.com/eslint/eslint/commit/edd8d240db8878763dbb147fb6124412c0783a42) chore: upgrade eslint-visitor-keys for class static blocks (#15277) (Milos Djermanovic)
+* [`4c55216`](https://github.com/eslint/eslint/commit/4c55216ba958fcc8c3dd29fcaa80298216a48303) docs: Add variables option to no-use-before-define (#15276) (Mathias Rasmussen)
+* [`0338fd2`](https://github.com/eslint/eslint/commit/0338fd201614247eeb21e68a26e4b4c8a74f71b0) feat: Normalize ecmaVersion to eslint-scope when using custom parser (#15268) (Yosuke Ota)
+
+v8.2.0 - November 5, 2021
+
+* [`cf5b6be`](https://github.com/eslint/eslint/commit/cf5b6be6f8144f5932cdf062d380f7c0f51e64bd) chore: update @eslint/eslintrc to avoid different versions of `js-yaml` (#15265) (Milos Djermanovic)
+* [`c9fefd2`](https://github.com/eslint/eslint/commit/c9fefd2e40348b3e02b855597707a557dc4991d5) feat: report class evaluation TDZ errors in no-use-before-define (#15134) (Milos Djermanovic)
+* [`4fd7a6c`](https://github.com/eslint/eslint/commit/4fd7a6ca7339bcbbfa6feda266dcca96684b81c6) perf: don't prepare a fix for valid code in key-spacing (#15239) (Milos Djermanovic)
+* [`c415c04`](https://github.com/eslint/eslint/commit/c415c041912a3abbf106cc5713bdcf4ef42590ac) docs: Use string rule severity in CLI examples (#15253) (Kevin Partington)
+* [`796587a`](https://github.com/eslint/eslint/commit/796587ad950f6804d60473c2b5998ed3ec71c59e) build: upgrade eslint-release to v3.2.0 to support conventional commits (#15246) (Milos Djermanovic)
+* [`12b627d`](https://github.com/eslint/eslint/commit/12b627da401c68a5081822a49068421f1bb2465c) docs: fix typo in `working-with-rules.md` (#15233) (Nitin Kumar)
+* [`a86ffc0`](https://github.com/eslint/eslint/commit/a86ffc076014d1de7eefc7456a8ccfb3a2318155) docs: fix broken anchor in configuration files (#15223) (Pierre Berger)
+* [`fda533c`](https://github.com/eslint/eslint/commit/fda533cda4b70278acfce4e21b5b1ebe52ff7a3d) chore: update `strip-ansi` dependency (#15221) (Nitin Kumar)
+* [`ee8af5f`](https://github.com/eslint/eslint/commit/ee8af5fb864b510ba6b50dcfb706b8b28fdfb74e) docs: Link to unit tests from rule documentation (#15207) (Brandon Mills)
+* [`1c0ca3c`](https://github.com/eslint/eslint/commit/1c0ca3c744dd5761d424d19c9cdcccc569dfe34c) docs: add `ci` and `perf` tags for commit (#15215) (Nitin Kumar)
+* [`67949bd`](https://github.com/eslint/eslint/commit/67949bd9f3cbda08442d2e5946feb9a4f8b22d85) ci: Remove Node 16 CI prerelease workaround (#14935) (Brandon Mills)
+
+v8.1.0 - October 22, 2021
+
+* [`446b4b3`](https://github.com/eslint/eslint/commit/446b4b3583f90dba7e0ac347b57db013aecc101d) Docs: Update commit message format docs (#15200) (Nicholas C. Zakas)
+* [`d9d84a0`](https://github.com/eslint/eslint/commit/d9d84a060362efbaac727f18e3a790098bf0bc4b) Fix: keyword-spacing conflict with space-infix-ops on `>` (fixes #14712) (#15172) (Milos Djermanovic)
+* [`a1f7ad7`](https://github.com/eslint/eslint/commit/a1f7ad77e2da00ac7d6daade547fe6bef4ef6003) Fix: allow `baseConfig` to extend preloaded plugin config (fixes #15079) (#15187) (Milos Djermanovic)
+* [`3d370fb`](https://github.com/eslint/eslint/commit/3d370fb3596ccd3463c29f1a7a1e3f321dd8083a) New: Add no-unused-private-class-members rule (fixes #14859) (#14895) (Tim van der Lippe)
+* [`e926b17`](https://github.com/eslint/eslint/commit/e926b1735c77bf55abc1150b060a535a6c4e2778) New: Add name to RuleTester (#15179) (Gareth Jones)
+* [`90a5b6b`](https://github.com/eslint/eslint/commit/90a5b6b4aeff7343783f85418c683f2c9901ab07) Chore: improve performance of `:function` selector (#15181) (Milos Djermanovic)
+* [`31af1c8`](https://github.com/eslint/eslint/commit/31af1c8770c7dac9e9686a0549af329abe5a795b) Chore: fix counting of files in performance test (#15190) (Milos Djermanovic)
+* [`1b87fa8`](https://github.com/eslint/eslint/commit/1b87fa835892d9da3b945db763196715d8088090) Build: add node v17 (#15193) (唯然)
+* [`0fb3bb2`](https://github.com/eslint/eslint/commit/0fb3bb2af3301c92ccd46ece739644a17df89bab) Docs: remove `instanceof` from keyword-spacing docs (#15180) (Milos Djermanovic)
+* [`249a040`](https://github.com/eslint/eslint/commit/249a04070f88d2c895af3b78d60d2eff2730730e) Upgrade: `eslint-plugin-eslint-plugin` to v4 (#15169) (Bryan Mishkin)
+* [`35f3254`](https://github.com/eslint/eslint/commit/35f3254d5f8027f75a6cb35b58bea10037003be8) Docs: Describe range in rule docs (fixes #14162) (#15174) (Nicholas C. Zakas)
+* [`b5049c8`](https://github.com/eslint/eslint/commit/b5049c89a00f1a0da59ecaee74b9b024ef3c3621) Chore: Update stale bot settings (#15173) (Nicholas C. Zakas)
+* [`2b32f50`](https://github.com/eslint/eslint/commit/2b32f50460d6858367b25df20b7a717528891e0d) Docs: Fix typo in README.md (#15168) (Dmitriy Fishman)
+* [`dd58cd4`](https://github.com/eslint/eslint/commit/dd58cd4afa6ced9016c091fc99a702c97a3e44f0) Chore: migrate master to main (#15062) (Nitesh Seram)
+* [`ec0f8e0`](https://github.com/eslint/eslint/commit/ec0f8e0bb7d7ce502ca68fcd13ac323eb6307455) Chore: Add stale issue/PR checker (#15151) (Nicholas C. Zakas)
+* [`2cfbd4b`](https://github.com/eslint/eslint/commit/2cfbd4bfd90b31cd728d6595bd1e36667715c84d) Docs: Update README team and sponsors (ESLint Jenkins)
+
+v8.0.1 - October 13, 2021
+
+* [`f9217e5`](https://github.com/eslint/eslint/commit/f9217e527e1c49c6244400c4a58b6d1c14de51db) Upgrade: @eslint/eslintrc@1.0.3 for Jest workaround (#15164) (Brandon Mills)
+* [`c584a63`](https://github.com/eslint/eslint/commit/c584a63e2d6d9c0a66e5c5a5d43bc8148c054f5d) Chore: add ecmaVersion 13 to types.js (#15163) (Milos Djermanovic)
+* [`ff5fcd4`](https://github.com/eslint/eslint/commit/ff5fcd4d9bf43354a1b85d1f7ec1c4e1c0e5cbd9) Docs: add 13 as allowed ecma version (fixes #15159) (#15162) (唯然)
+
+v8.0.0 - October 9, 2021
+
+* [`7d3f7f0`](https://github.com/eslint/eslint/commit/7d3f7f01281671c4761f8da0d3ae9882a38eca8a) Upgrade: unfrozen @eslint/eslintrc (fixes #15036) (#15146) (Brandon Mills)
+* [`2174a6f`](https://github.com/eslint/eslint/commit/2174a6f0e5d18b673604d31e3ca7b790cdc9429b) Fix: require-atomic-updates property assignment message (fixes #15076) (#15109) (Milos Djermanovic)
+* [`f885fe0`](https://github.com/eslint/eslint/commit/f885fe06a0a79d91fc72a132fd31edf9ef0502cd) Docs: add note and example for extending the range of fix (refs #13706) (#13748) (Milos Djermanovic)
+* [`3da1509`](https://github.com/eslint/eslint/commit/3da1509106f508f0eb8ba48cdfc666225fda7edc) Docs: Add jsdoc `type` annotation to sample rule (#15085) (Bryan Mishkin)
+* [`68a49a9`](https://github.com/eslint/eslint/commit/68a49a9446c3286bb9ff24b90713c794b7e1f6f5) Docs: Update Rollup Integrations (#15142) (xiaohai)
+* [`d867f81`](https://github.com/eslint/eslint/commit/d867f8100737bb82742debee2b5dc853c5f07c91) Docs: Remove a dot from curly link (#15128) (Mauro Murru)
+* [`9f8b919`](https://github.com/eslint/eslint/commit/9f8b91922839b9d438df6cc1d542eea0509ef122) Sponsors: Sync README with website (ESLint Jenkins)
+* [`4b08f29`](https://github.com/eslint/eslint/commit/4b08f299a172d3eef09e97e85d19a1612e83ac45) Sponsors: Sync README with website (ESLint Jenkins)
+* [`ebc1ba1`](https://github.com/eslint/eslint/commit/ebc1ba1416834b7a52d1e16909ba05c731e97ed4) Sponsors: Sync README with website (ESLint Jenkins)
+* [`2d654f1`](https://github.com/eslint/eslint/commit/2d654f115f6e05b59c85434e75cf68204b976f22) Docs: add example .eslintrc.json (#15087) (Nicolas Mattia)
+* [`16034f0`](https://github.com/eslint/eslint/commit/16034f09ae6c7a78b8268b4c859928f18de7b9d6) Docs: fix fixable example (#15107) (QiChang Li)
+* [`07175b8`](https://github.com/eslint/eslint/commit/07175b8e9532d79e55c499aa27f79f023abda3c3) 8.0.0-rc.0 (ESLint Jenkins)
+* [`71faa38`](https://github.com/eslint/eslint/commit/71faa38adada4bd2f1ec0da7e45e6c7c84d1671d) Build: changelog update for 8.0.0-rc.0 (ESLint Jenkins)
+* [`67c0074`](https://github.com/eslint/eslint/commit/67c0074fa843fab629f464ff875007a8ee33cc7f) Update: Suggest missing rule in flat config (fixes #14027) (#15074) (Nicholas C. Zakas)
+* [`cf34e5c`](https://github.com/eslint/eslint/commit/cf34e5cf5ed5d09eb53c16cca06821c4e34b7b70) Update: space-before-blocks ignore after switch colons (fixes #15082) (#15093) (Milos Djermanovic)
+* [`c9efb5f`](https://github.com/eslint/eslint/commit/c9efb5f91937dcb6c8f3d7cb2f59940046d77901) Fix: preserve formatting when rules are removed from disable directives (#15081) (Milos Djermanovic)
+* [`14a4739`](https://github.com/eslint/eslint/commit/14a4739ab2233acef995a6dde233de05d067a0f3) Update: `no-new-func` rule catching eval case of `MemberExpression` (#14860) (Mojtaba Samimi)
+* [`7f2346b`](https://github.com/eslint/eslint/commit/7f2346b40ffd0d470092e52b995d7ab2648089db) Docs: Update release blog post template (#15094) (Nicholas C. Zakas)
+* [`fabdf8a`](https://github.com/eslint/eslint/commit/fabdf8a4e2f82b5fe2f903f015c3e60747a0b143) Chore: Remove `target.all` from `Makefile.js` (#15088) (Hirotaka Tagawa / wafuwafu13)
+* [`e3cd141`](https://github.com/eslint/eslint/commit/e3cd1414489ceda460d593ac7e7b14f8ad45d4fc) Sponsors: Sync README with website (ESLint Jenkins)
+* [`05d7140`](https://github.com/eslint/eslint/commit/05d7140d46e2b5300d4dc9a60450eed956c95420) Chore: document target global in Makefile.js (#15084) (Hirotaka Tagawa / wafuwafu13)
+* [`0a1a850`](https://github.com/eslint/eslint/commit/0a1a850575ca75db017051abe5e931f0f9c8012b) Update: include `ruleId` in error logs (fixes #15037) (#15053) (Ari Perkkiö)
+* [`47be800`](https://github.com/eslint/eslint/commit/47be8003d700bc0606495ae42610eaba94e639c5) Chore: test Property > .key with { a = 1 } pattern (fixes #14799) (#15072) (Milos Djermanovic)
+* [`a744dfa`](https://github.com/eslint/eslint/commit/a744dfa1f077afe406014f84135f8d26e9a12a94) Docs: Update CLA info (#15058) (Brian Warner)
+* [`9fb0f70`](https://github.com/eslint/eslint/commit/9fb0f7040759ea23538997648f2d2d53e7c9db8a) Chore: fix bug report template (#15061) (Milos Djermanovic)
+* [`f87e199`](https://github.com/eslint/eslint/commit/f87e199e988f42fc490890eee0642d86c48c85ff) Chore: Cleanup issue templates (#15039) (Nicholas C. Zakas)
+* [`660f075`](https://github.com/eslint/eslint/commit/660f075386d0b700faf1a1a94cde9d51899738a3) 8.0.0-beta.2 (ESLint Jenkins)
+* [`d148ffd`](https://github.com/eslint/eslint/commit/d148ffdec385e832956c748e36941e598b57b031) Build: changelog update for 8.0.0-beta.2 (ESLint Jenkins)
+* [`9e5c2e8`](https://github.com/eslint/eslint/commit/9e5c2e853ace560876c2f2119e134639be8659d0) Upgrade: @eslint/eslintrc@1.0.1 (#15047) (Milos Djermanovic)
+* [`7cf96cf`](https://github.com/eslint/eslint/commit/7cf96cf185f849d379b660072d660ec35ac5b46d) Breaking: Disallow reserved words in ES3 (fixes #15017) (#15046) (Milos Djermanovic)
+* [`88a3952`](https://github.com/eslint/eslint/commit/88a39520716bdd11f8647e47c57bd8bf91bc7148) Update: support class fields in the `complexity` rule (refs #14857) (#14957) (Milos Djermanovic)
+* [`9bd3d87`](https://github.com/eslint/eslint/commit/9bd3d87c8d7369e85f2b7d9b784fed8143191d30) Fix: semicolon-less style in lines-between-class-members (refs #14857) (#15045) (Milos Djermanovic)
+* [`6d1ccb6`](https://github.com/eslint/eslint/commit/6d1ccb676fedd1ceb4b1e44abf8133f116a5aecb) Update: enforceForClassFields in class-methods-use-this (refs #14857) (#15018) (YeonJuan)
+* [`91e82f5`](https://github.com/eslint/eslint/commit/91e82f5c4cfeab5ac6d01865ce0eb9ea0649df39) Docs: LintMessage.line and column are possibly undefined (#15032) (Brandon Mills)
+* [`921ba1e`](https://github.com/eslint/eslint/commit/921ba1ee53e5f2219f09050565b8d69fab517d72) Chore: fix failing cli test (#15041) (Milos Djermanovic)
+* [`dd56631`](https://github.com/eslint/eslint/commit/dd5663166a8235512e797522731af1e9651f9392) Docs: remove duplicate code path analysis document (#15033) (Milos Djermanovic)
+* [`143a598`](https://github.com/eslint/eslint/commit/143a5987f18f063a47a0646fa1e10e0f88602f6f) Chore: Switch issues to use forms (#15024) (Nicholas C. Zakas)
+* [`f966fe6`](https://github.com/eslint/eslint/commit/f966fe6286b6f668812f5155b79d4ee2a8b584b3) Fix: Update semi for class-fields (refs #14857) (#14945) (Nicholas C. Zakas)
+* [`8c61f5a`](https://github.com/eslint/eslint/commit/8c61f5ac67682fcfec7fc6faafcf72e4b1a339ff) Docs: add info about non-capturing groups to prefer-named-capture-group (#15009) (Andrzej Wódkiewicz)
+* [`dd10937`](https://github.com/eslint/eslint/commit/dd109379f730a988a9e6c0102bcfe443ad0b4b94) Update: added ignoreExpressions option to max-classes-per-file (#15000) (Josh Goldberg)
+* [`e9764f3`](https://github.com/eslint/eslint/commit/e9764f3e2fe3f7b6341c9a4381f0dcd23548338e) Fix: no-undef-init should not apply to class fields (refs #14857) (#14994) (Milos Djermanovic)
+* [`4338b74`](https://github.com/eslint/eslint/commit/4338b74767fa71e4e8d171f8503aa33d970e509f) Docs: add no-dupe-class-members examples with class fields (refs #14857) (#15005) (Milos Djermanovic)
+* [`b4232d4`](https://github.com/eslint/eslint/commit/b4232d47f88611c68a6c0f915b092b68845ecbaf) Chore: Add test that deprecated rules display a deprecated notice (#14989) (TagawaHirotaka)
+* [`88b4e3d`](https://github.com/eslint/eslint/commit/88b4e3d191c2577e2e1a283cc5f825feea6271cc) Docs: Make clear how rule options are overridden (fixes #14962) (#14976) (Jake Ob)
+* [`4165c7f`](https://github.com/eslint/eslint/commit/4165c7f937f5fc46d4209ae8f763238d73f37238) Docs: Clarify Linter vs ESLint in node.js api docs (fixes #14953) (#14995) (Brian Bartels)
+* [`80cfb8f`](https://github.com/eslint/eslint/commit/80cfb8f858888bddfefd7de6b4ecbf5aabe267bc) Docs: fix typo in migration guide (#14985) (Nitin Kumar)
+* [`1ddc955`](https://github.com/eslint/eslint/commit/1ddc9559dff437c605e33c156b4380246a231a6e) 8.0.0-beta.1 (ESLint Jenkins)
+* [`95cc61e`](https://github.com/eslint/eslint/commit/95cc61e40a89aa2278ae93ae2f35c38737280abb) Build: changelog update for 8.0.0-beta.1 (ESLint Jenkins)
+* [`05ca24c`](https://github.com/eslint/eslint/commit/05ca24c57f90f91421b682dca3d7a45b7957fb77) Update: Code path analysis for class fields (fixes #14343) (#14886) (Nicholas C. Zakas)
+* [`db15183`](https://github.com/eslint/eslint/commit/db1518374a5e88efedf1ed4609d879f3091af74f) Chore: Refactor comments of tests (#14956) (TagawaHirotaka)
+* [`396a0e3`](https://github.com/eslint/eslint/commit/396a0e3c7c82e5d2680d07250008094f336856db) Docs: update ScopeManager with class fields (#14974) (Milos Djermanovic)
+* [`6663e7a`](https://github.com/eslint/eslint/commit/6663e7aed498a73108b5e6371f218d9411b87796) Docs: remove `docs` script (fixes #14288) (#14971) (Nitin Kumar)
+* [`44c6fc8`](https://github.com/eslint/eslint/commit/44c6fc879de61e9513835d1d4d6ae978d9a43c51) Update: support class fields in func-name-matching (refs #14857) (#14964) (Milos Djermanovic)
+* [`44f7de5`](https://github.com/eslint/eslint/commit/44f7de5ee4d934dee540d3d55305126c670f6bfc) Docs: Update deprecated information (#14961) (TagawaHirotaka)
+* [`305e14a`](https://github.com/eslint/eslint/commit/305e14af8bd12afc01487abee5c9b0f3eaca989e) Breaking: remove meta.docs.category in core rules (fixes #13398) (#14594) (薛定谔的猫)
+* [`a79c9f3`](https://github.com/eslint/eslint/commit/a79c9f35d665c2bcc63267bdf359a8176e0a84ce) Chore: Enforce jsdoc check-line-alignment never (#14955) (Brett Zamir)
+* [`a8bcef7`](https://github.com/eslint/eslint/commit/a8bcef70a4a6b1fbb2007075bed754635f27ff01) Docs: Add 2021 and 2022 to supported ECMAScript versions (#14952) (coderaiser)
+* [`3409785`](https://github.com/eslint/eslint/commit/3409785a41a5bd2b128ed11b8baf7a59f9e412ee) Fix: camelcase ignoreGlobals shouldn't apply to undef vars (refs #14857) (#14966) (Milos Djermanovic)
+* [`b301069`](https://github.com/eslint/eslint/commit/b301069981dc1dcca51df2813dcebdca8c150502) Docs: fix 'When Not To Use' in prefer-named-capture-group (refs #14959) (#14969) (Milos Djermanovic)
+* [`2d18db6`](https://github.com/eslint/eslint/commit/2d18db6278320fb97bc8e0bff3518c790566a6a6) Chore: add test for merging `parserOptions` in Linter (#14948) (Milos Djermanovic)
+* [`3d7d5fb`](https://github.com/eslint/eslint/commit/3d7d5fb32425e8c04d3eaa0107a2ab03a2e285df) Update: reporting loc for `never` option in `eol-last` (refs #12334) (#14840) (Nitin Kumar)
+* [`f110926`](https://github.com/eslint/eslint/commit/f110926a7abcc875a86dd13116f794e4f950e2ba) Update: fix no-unused-vars false negative with comma operator (#14928) (Sachin)
+* [`e98f14d`](https://github.com/eslint/eslint/commit/e98f14d356b5ff934dd2a0a1fb226f1b15317ab3) Docs: Fix typo in no-implicit-globals.md (#14954) (jwbth)
+* [`9a4ae3b`](https://github.com/eslint/eslint/commit/9a4ae3b68a1afd9483d331997635727fb19a1a99) Chore: Apply comment require-description and check ClassDeclaration (#14949) (Brett Zamir)
+* [`8344675`](https://github.com/eslint/eslint/commit/8344675c309a359dd2af5afddba6122f5dc803d0) Chore: fix small typo (#14951) (Sosuke Suzuki)
+* [`26b0cd9`](https://github.com/eslint/eslint/commit/26b0cd924e79a0ab2374c0cd813e92055f9fff7b) Update: fix no-unreachable logic for class fields (refs #14857) (#14920) (Milos Djermanovic)
+* [`ee1b54f`](https://github.com/eslint/eslint/commit/ee1b54f31fa840e6ec72a313aa4090fdd3e985cd) Fix: keyword-spacing private name compat (refs #14857) (#14946) (Nicholas C. Zakas)
+* [`58840ac`](https://github.com/eslint/eslint/commit/58840ac844a61c72eabb603ecfb761812b82a7ed) Chore: Update jsdoc plugin and tweak rules in effect (#14814) (Brett Zamir)
+* [`81c60f4`](https://github.com/eslint/eslint/commit/81c60f4a8725738f191580646562d1dca7eee933) Docs: document ESLint api (#14934) (Sam Chen)
+* [`c74fe08`](https://github.com/eslint/eslint/commit/c74fe08642c30e1a4cd4e0866251a2d29466add8) Build: Force prerelease peer dep for Node 16 in CI (#14933) (Brandon Mills)
+* [`c9947d2`](https://github.com/eslint/eslint/commit/c9947d2a3e0250928d4d80f3b287f10e68fc8db2) 8.0.0-beta.0 (ESLint Jenkins)
+* [`027165c`](https://github.com/eslint/eslint/commit/027165cacf62ab1662f4c343ff30b235fd9d46b8) Build: changelog update for 8.0.0-beta.0 (ESLint Jenkins)
+* [`be334f9`](https://github.com/eslint/eslint/commit/be334f9d8633e9d193dcb8b36f484547e9d3ab97) Chore: Fix Makefile call to linter.getRules() (#14932) (Brandon Mills)
+* [`0c86b68`](https://github.com/eslint/eslint/commit/0c86b68a6e2435eb03b681b51b099b552b521adc) Chore: Replace old syntax for Array flat/flatMap (#14614) (Stephen Wade)
+* [`6a89f3f`](https://github.com/eslint/eslint/commit/6a89f3f7b6a3edb3465952521bdf06a220515b95) Chore: ignore `yarn-error.log` and `.pnpm-debug.log` (#14925) (Nitin Kumar)
+* [`28fe19c`](https://github.com/eslint/eslint/commit/28fe19c4a9108111932966aa7c9f361c26601d70) Docs: Add v8.0.0 migration guide (fixes #14856) (#14884) (Nicholas C. Zakas)
+* [`ec9db63`](https://github.com/eslint/eslint/commit/ec9db63e53a6605a558dcd82947d2425f89887c3) Upgrade: @eslint/eslintrc@1.0.0 (#14865) (Milos Djermanovic)
+* [`1f5d088`](https://github.com/eslint/eslint/commit/1f5d0889264c60dddb6fb07a3b1e43f840e84d57) Docs: add an example `Object.assign()` for rule no-import-assign (#14916) (薛定谔的猫)
+* [`af96584`](https://github.com/eslint/eslint/commit/af965848c010612c3e136c367cc9b9e2e822f580) Fix: handle computed class fields in operator-linebreak (refs #14857) (#14915) (Milos Djermanovic)
+* [`3b6cd89`](https://github.com/eslint/eslint/commit/3b6cd8934b3640ffb6fa49b471babf07f0ad769a) Chore: Add rel/abs path tests in `no-restricted-{imports/modules}` rules (#14910) (Bryan Mishkin)
+* [`62c6fe7`](https://github.com/eslint/eslint/commit/62c6fe7d10ff4eeebd196e143f96cfd88818393d) Upgrade: Debug 4.0.1 > 4.3.2 (#14892) (sandesh bafna)
+* [`f984515`](https://github.com/eslint/eslint/commit/f98451584a82e41f82ceacd484ea0fe90aa9ce63) Chore: add assertions on reporting location in `semi` (#14899) (Nitin Kumar)
+* [`a773b99`](https://github.com/eslint/eslint/commit/a773b99873965652a86bec489193dc42a8923f5f) Fix: no-useless-computed-key edge cases with class fields (refs #14857) (#14903) (Milos Djermanovic)
+* [`88db3f5`](https://github.com/eslint/eslint/commit/88db3f54988dddfbda35764ecf1ea16354c4213a) Upgrade: `js-yaml` to v4 (#14890) (Bryan Mishkin)
+* [`cbc43da`](https://github.com/eslint/eslint/commit/cbc43daad2ea229fb15a9198efd2bc2721dfb75f) Fix: prefer-destructuring PrivateIdentifier false positive (refs #14857) (#14897) (Milos Djermanovic)
+* [`ccb9a91`](https://github.com/eslint/eslint/commit/ccb9a9138acd63457e004630475495954c1be6f4) Fix: dot-notation false positive with private identifier (refs #14857) (#14898) (Milos Djermanovic)
+* [`8c35066`](https://github.com/eslint/eslint/commit/8c350660e61284c41a5cc1a5955c858db53c516b) Sponsors: Sync README with website (ESLint Jenkins)
+* [`a3dd825`](https://github.com/eslint/eslint/commit/a3dd8257252f392de5cf793c36ecab2acd955659) Sponsors: Sync README with website (ESLint Jenkins)
+* [`c4e5802`](https://github.com/eslint/eslint/commit/c4e58023f22381508babfc52087853b5e3965b9c) Docs: improve rule details for `no-console` (fixes #14793) (#14901) (Nitin Kumar)
+* [`9052eee`](https://github.com/eslint/eslint/commit/9052eee07a459dc059cd92f657a3ae73acc95bb5) Update: check class fields in no-extra-parens (refs #14857) (#14906) (Milos Djermanovic)
+* [`5c3a470`](https://github.com/eslint/eslint/commit/5c3a47072aeb5cfda40a1eb20b43a10c5ca7aab3) Docs: add class fields in no-multi-assign documentation (refs #14857) (#14907) (Milos Djermanovic)
+* [`d234d89`](https://github.com/eslint/eslint/commit/d234d890b383837f8e4bda0f6ce1e2a348f9835e) Docs: add class fields in func-names documentation (refs #14857) (#14908) (Milos Djermanovic)
+* [`ae6072b`](https://github.com/eslint/eslint/commit/ae6072b1de5c8b30ce6c58290852082439c40b30) Upgrade: `eslint-visitor-keys` to v3 (#14902) (Bryan Mishkin)
+* [`e53d8cf`](https://github.com/eslint/eslint/commit/e53d8cf9d73bd105cf6ba4f6b5477ccc4b980939) Upgrade: `markdownlint` dev dependencies (#14883) (Bryan Mishkin)
+* [`d66e941`](https://github.com/eslint/eslint/commit/d66e9414be60e05badb96bc3e1a55ca34636d7f8) Upgrade: @humanwhocodes/config-array to 0.6 (#14891) (Bryan Mishkin)
+* [`149230c`](https://github.com/eslint/eslint/commit/149230ce7e296c029a0b6c085216fc0360ed4c65) Chore: Specify Node 14.x for Verify Files CI job (#14896) (Milos Djermanovic)
+* [`537cf6a`](https://github.com/eslint/eslint/commit/537cf6a0e78ee9b7167e7f8c56f4053d3fb5b2d7) Chore: update `glob-parent` (fixes #14879)(#14887) (Nitin Kumar)
+* [`f7b4a3f`](https://github.com/eslint/eslint/commit/f7b4a3f6a44e167c71985d373f73eebd3a4d9556) Chore: update dev deps to latest (#14624) (薛定谔的猫)
+* [`24c9f2a`](https://github.com/eslint/eslint/commit/24c9f2ac57efcd699ca69695c82e51ce5742df7b) Breaking: Strict package exports (refs #13654) (#14706) (Nicholas C. Zakas)
+* [`86d31a4`](https://github.com/eslint/eslint/commit/86d31a4951e3a39e359e284f5fe336ac477369fe) Breaking: disallow SourceCode#getComments() in RuleTester (refs #14744) (#14769) (Milos Djermanovic)
+* [`1d2213d`](https://github.com/eslint/eslint/commit/1d2213deb69c5901c1950bbe648aa819e7e742ed) Breaking: Fixable disable directives (fixes #11815) (#14617) (Josh Goldberg)
+* [`4a7aab7`](https://github.com/eslint/eslint/commit/4a7aab7d4323ff7027eebca709d4e95a9aaa80bc) Breaking: require `meta` for fixable rules (fixes #13349) (#14634) (Milos Djermanovic)
+* [`d6a761f`](https://github.com/eslint/eslint/commit/d6a761f9b6582e9f71705161be827ca303ef183f) Breaking: Require `meta.hasSuggestions` for rules with suggestions (#14573) (Bryan Mishkin)
+* [`6bd747b`](https://github.com/eslint/eslint/commit/6bd747b5b7731195224875b952a9ea61445a9938) Breaking: support new regex d flag (fixes #14640) (#14653) (Yosuke Ota)
+* [`8b4f3ab`](https://github.com/eslint/eslint/commit/8b4f3abdb794feb3be31959bb44bfb0ef6318e8e) Breaking: fix comma-dangle schema (fixes #13739) (#14030) (Joakim Nilsson)
+* [`b953a4e`](https://github.com/eslint/eslint/commit/b953a4ee12f120658a9ec27d1f8ca88dd3dfb599) Breaking: upgrade espree and support new class features (refs #14343) (#14591) (Toru Nagashima)
+* [`8cce06c`](https://github.com/eslint/eslint/commit/8cce06cb39886902ce0d2e6882f46c3bf52fb955) Breaking: add some rules to eslint:recommended (refs #14673) (#14691) (薛定谔的猫)
+* [`86bb63b`](https://github.com/eslint/eslint/commit/86bb63b370e0ff350e988a5fa228a8234abe800c) Breaking: Drop `codeframe` and `table` formatters (#14316) (Federico Brigante)
+* [`f3cb320`](https://github.com/eslint/eslint/commit/f3cb3208c8952a6218d54658cfda85942b9fda42) Breaking: drop node v10/v13/v15 (fixes #14023) (#14592) (薛定谔的猫)
+* [`b8b2d55`](https://github.com/eslint/eslint/commit/b8b2d5553b0de23e8b72ee45949650cd5f9a10d2) Build: add codeql (#14729) (薛定谔的猫)
+* [`e037d61`](https://github.com/eslint/eslint/commit/e037d61a12ad17a36e05dcf65aa63fad303c79b9) Docs: Mention workaround for escaping the slash character in selectors (#14675) (Aria)
+* [`81f03b6`](https://github.com/eslint/eslint/commit/81f03b6ad69c7f67ad6ba72e02e73266aa8f7696) Docs: Update license copyright (#14877) (Nicholas C. Zakas)
+* [`fa1c07c`](https://github.com/eslint/eslint/commit/fa1c07c0d65ce21a30f5bb4a9f2ac511f8df6446) Sponsors: Sync README with website (ESLint Jenkins)
+* [`e31f492`](https://github.com/eslint/eslint/commit/e31f49206f94e2b3977ec37892d4b87ab1e46872) Sponsors: Sync README with website (ESLint Jenkins)
+* [`8307256`](https://github.com/eslint/eslint/commit/83072561b006a558d026c5a507f92945b821a0cd) Sponsors: Sync README with website (ESLint Jenkins)
+
+v8.0.0-rc.0 - September 24, 2021
+
+* [`67c0074`](https://github.com/eslint/eslint/commit/67c0074fa843fab629f464ff875007a8ee33cc7f) Update: Suggest missing rule in flat config (fixes #14027) (#15074) (Nicholas C. Zakas)
+* [`cf34e5c`](https://github.com/eslint/eslint/commit/cf34e5cf5ed5d09eb53c16cca06821c4e34b7b70) Update: space-before-blocks ignore after switch colons (fixes #15082) (#15093) (Milos Djermanovic)
+* [`c9efb5f`](https://github.com/eslint/eslint/commit/c9efb5f91937dcb6c8f3d7cb2f59940046d77901) Fix: preserve formatting when rules are removed from disable directives (#15081) (Milos Djermanovic)
+* [`14a4739`](https://github.com/eslint/eslint/commit/14a4739ab2233acef995a6dde233de05d067a0f3) Update: `no-new-func` rule catching eval case of `MemberExpression` (#14860) (Mojtaba Samimi)
+* [`7f2346b`](https://github.com/eslint/eslint/commit/7f2346b40ffd0d470092e52b995d7ab2648089db) Docs: Update release blog post template (#15094) (Nicholas C. Zakas)
+* [`fabdf8a`](https://github.com/eslint/eslint/commit/fabdf8a4e2f82b5fe2f903f015c3e60747a0b143) Chore: Remove `target.all` from `Makefile.js` (#15088) (Hirotaka Tagawa / wafuwafu13)
+* [`e3cd141`](https://github.com/eslint/eslint/commit/e3cd1414489ceda460d593ac7e7b14f8ad45d4fc) Sponsors: Sync README with website (ESLint Jenkins)
+* [`05d7140`](https://github.com/eslint/eslint/commit/05d7140d46e2b5300d4dc9a60450eed956c95420) Chore: document target global in Makefile.js (#15084) (Hirotaka Tagawa / wafuwafu13)
+* [`0a1a850`](https://github.com/eslint/eslint/commit/0a1a850575ca75db017051abe5e931f0f9c8012b) Update: include `ruleId` in error logs (fixes #15037) (#15053) (Ari Perkkiö)
+* [`47be800`](https://github.com/eslint/eslint/commit/47be8003d700bc0606495ae42610eaba94e639c5) Chore: test Property > .key with { a = 1 } pattern (fixes #14799) (#15072) (Milos Djermanovic)
+* [`a744dfa`](https://github.com/eslint/eslint/commit/a744dfa1f077afe406014f84135f8d26e9a12a94) Docs: Update CLA info (#15058) (Brian Warner)
+* [`9fb0f70`](https://github.com/eslint/eslint/commit/9fb0f7040759ea23538997648f2d2d53e7c9db8a) Chore: fix bug report template (#15061) (Milos Djermanovic)
+* [`f87e199`](https://github.com/eslint/eslint/commit/f87e199e988f42fc490890eee0642d86c48c85ff) Chore: Cleanup issue templates (#15039) (Nicholas C. Zakas)
+
+v8.0.0-beta.2 - September 10, 2021
+
+* [`9e5c2e8`](https://github.com/eslint/eslint/commit/9e5c2e853ace560876c2f2119e134639be8659d0) Upgrade: @eslint/eslintrc@1.0.1 (#15047) (Milos Djermanovic)
+* [`7cf96cf`](https://github.com/eslint/eslint/commit/7cf96cf185f849d379b660072d660ec35ac5b46d) Breaking: Disallow reserved words in ES3 (fixes #15017) (#15046) (Milos Djermanovic)
+* [`88a3952`](https://github.com/eslint/eslint/commit/88a39520716bdd11f8647e47c57bd8bf91bc7148) Update: support class fields in the `complexity` rule (refs #14857) (#14957) (Milos Djermanovic)
+* [`9bd3d87`](https://github.com/eslint/eslint/commit/9bd3d87c8d7369e85f2b7d9b784fed8143191d30) Fix: semicolon-less style in lines-between-class-members (refs #14857) (#15045) (Milos Djermanovic)
+* [`6d1ccb6`](https://github.com/eslint/eslint/commit/6d1ccb676fedd1ceb4b1e44abf8133f116a5aecb) Update: enforceForClassFields in class-methods-use-this (refs #14857) (#15018) (YeonJuan)
+* [`91e82f5`](https://github.com/eslint/eslint/commit/91e82f5c4cfeab5ac6d01865ce0eb9ea0649df39) Docs: LintMessage.line and column are possibly undefined (#15032) (Brandon Mills)
+* [`921ba1e`](https://github.com/eslint/eslint/commit/921ba1ee53e5f2219f09050565b8d69fab517d72) Chore: fix failing cli test (#15041) (Milos Djermanovic)
+* [`dd56631`](https://github.com/eslint/eslint/commit/dd5663166a8235512e797522731af1e9651f9392) Docs: remove duplicate code path analysis document (#15033) (Milos Djermanovic)
+* [`143a598`](https://github.com/eslint/eslint/commit/143a5987f18f063a47a0646fa1e10e0f88602f6f) Chore: Switch issues to use forms (#15024) (Nicholas C. Zakas)
+* [`f966fe6`](https://github.com/eslint/eslint/commit/f966fe6286b6f668812f5155b79d4ee2a8b584b3) Fix: Update semi for class-fields (refs #14857) (#14945) (Nicholas C. Zakas)
+* [`8c61f5a`](https://github.com/eslint/eslint/commit/8c61f5ac67682fcfec7fc6faafcf72e4b1a339ff) Docs: add info about non-capturing groups to prefer-named-capture-group (#15009) (Andrzej Wódkiewicz)
+* [`dd10937`](https://github.com/eslint/eslint/commit/dd109379f730a988a9e6c0102bcfe443ad0b4b94) Update: added ignoreExpressions option to max-classes-per-file (#15000) (Josh Goldberg)
+* [`e9764f3`](https://github.com/eslint/eslint/commit/e9764f3e2fe3f7b6341c9a4381f0dcd23548338e) Fix: no-undef-init should not apply to class fields (refs #14857) (#14994) (Milos Djermanovic)
+* [`4338b74`](https://github.com/eslint/eslint/commit/4338b74767fa71e4e8d171f8503aa33d970e509f) Docs: add no-dupe-class-members examples with class fields (refs #14857) (#15005) (Milos Djermanovic)
+* [`b4232d4`](https://github.com/eslint/eslint/commit/b4232d47f88611c68a6c0f915b092b68845ecbaf) Chore: Add test that deprecated rules display a deprecated notice (#14989) (TagawaHirotaka)
+* [`88b4e3d`](https://github.com/eslint/eslint/commit/88b4e3d191c2577e2e1a283cc5f825feea6271cc) Docs: Make clear how rule options are overridden (fixes #14962) (#14976) (Jake Ob)
+* [`4165c7f`](https://github.com/eslint/eslint/commit/4165c7f937f5fc46d4209ae8f763238d73f37238) Docs: Clarify Linter vs ESLint in node.js api docs (fixes #14953) (#14995) (Brian Bartels)
+* [`80cfb8f`](https://github.com/eslint/eslint/commit/80cfb8f858888bddfefd7de6b4ecbf5aabe267bc) Docs: fix typo in migration guide (#14985) (Nitin Kumar)
+
+v8.0.0-beta.1 - August 27, 2021
+
+* [`41617ec`](https://github.com/eslint/eslint/commit/41617ec3c4bc8bd1ba5f66521185be1566e6f5f4) Revert "allow all directives in line comments" (fixes #14960) (#14973) (薛定谔的猫)
+* [`05ca24c`](https://github.com/eslint/eslint/commit/05ca24c57f90f91421b682dca3d7a45b7957fb77) Update: Code path analysis for class fields (fixes #14343) (#14886) (Nicholas C. Zakas)
+* [`db15183`](https://github.com/eslint/eslint/commit/db1518374a5e88efedf1ed4609d879f3091af74f) Chore: Refactor comments of tests (#14956) (TagawaHirotaka)
+* [`396a0e3`](https://github.com/eslint/eslint/commit/396a0e3c7c82e5d2680d07250008094f336856db) Docs: update ScopeManager with class fields (#14974) (Milos Djermanovic)
+* [`6663e7a`](https://github.com/eslint/eslint/commit/6663e7aed498a73108b5e6371f218d9411b87796) Docs: remove `docs` script (fixes #14288) (#14971) (Nitin Kumar)
+* [`44c6fc8`](https://github.com/eslint/eslint/commit/44c6fc879de61e9513835d1d4d6ae978d9a43c51) Update: support class fields in func-name-matching (refs #14857) (#14964) (Milos Djermanovic)
+* [`44f7de5`](https://github.com/eslint/eslint/commit/44f7de5ee4d934dee540d3d55305126c670f6bfc) Docs: Update deprecated information (#14961) (TagawaHirotaka)
+* [`305e14a`](https://github.com/eslint/eslint/commit/305e14af8bd12afc01487abee5c9b0f3eaca989e) Breaking: remove meta.docs.category in core rules (fixes #13398) (#14594) (薛定谔的猫)
+* [`a79c9f3`](https://github.com/eslint/eslint/commit/a79c9f35d665c2bcc63267bdf359a8176e0a84ce) Chore: Enforce jsdoc check-line-alignment never (#14955) (Brett Zamir)
+* [`a8bcef7`](https://github.com/eslint/eslint/commit/a8bcef70a4a6b1fbb2007075bed754635f27ff01) Docs: Add 2021 and 2022 to supported ECMAScript versions (#14952) (coderaiser)
+* [`3409785`](https://github.com/eslint/eslint/commit/3409785a41a5bd2b128ed11b8baf7a59f9e412ee) Fix: camelcase ignoreGlobals shouldn't apply to undef vars (refs #14857) (#14966) (Milos Djermanovic)
+* [`b301069`](https://github.com/eslint/eslint/commit/b301069981dc1dcca51df2813dcebdca8c150502) Docs: fix 'When Not To Use' in prefer-named-capture-group (refs #14959) (#14969) (Milos Djermanovic)
+* [`2d18db6`](https://github.com/eslint/eslint/commit/2d18db6278320fb97bc8e0bff3518c790566a6a6) Chore: add test for merging `parserOptions` in Linter (#14948) (Milos Djermanovic)
+* [`3d7d5fb`](https://github.com/eslint/eslint/commit/3d7d5fb32425e8c04d3eaa0107a2ab03a2e285df) Update: reporting loc for `never` option in `eol-last` (refs #12334) (#14840) (Nitin Kumar)
+* [`f110926`](https://github.com/eslint/eslint/commit/f110926a7abcc875a86dd13116f794e4f950e2ba) Update: fix no-unused-vars false negative with comma operator (#14928) (Sachin)
+* [`e98f14d`](https://github.com/eslint/eslint/commit/e98f14d356b5ff934dd2a0a1fb226f1b15317ab3) Docs: Fix typo in no-implicit-globals.md (#14954) (jwbth)
+* [`9a4ae3b`](https://github.com/eslint/eslint/commit/9a4ae3b68a1afd9483d331997635727fb19a1a99) Chore: Apply comment require-description and check ClassDeclaration (#14949) (Brett Zamir)
+* [`8344675`](https://github.com/eslint/eslint/commit/8344675c309a359dd2af5afddba6122f5dc803d0) Chore: fix small typo (#14951) (Sosuke Suzuki)
+* [`26b0cd9`](https://github.com/eslint/eslint/commit/26b0cd924e79a0ab2374c0cd813e92055f9fff7b) Update: fix no-unreachable logic for class fields (refs #14857) (#14920) (Milos Djermanovic)
+* [`ee1b54f`](https://github.com/eslint/eslint/commit/ee1b54f31fa840e6ec72a313aa4090fdd3e985cd) Fix: keyword-spacing private name compat (refs #14857) (#14946) (Nicholas C. Zakas)
+* [`58840ac`](https://github.com/eslint/eslint/commit/58840ac844a61c72eabb603ecfb761812b82a7ed) Chore: Update jsdoc plugin and tweak rules in effect (#14814) (Brett Zamir)
+* [`81c60f4`](https://github.com/eslint/eslint/commit/81c60f4a8725738f191580646562d1dca7eee933) Docs: document ESLint api (#14934) (Sam Chen)
+* [`c74fe08`](https://github.com/eslint/eslint/commit/c74fe08642c30e1a4cd4e0866251a2d29466add8) Build: Force prerelease peer dep for Node 16 in CI (#14933) (Brandon Mills)
+
+v8.0.0-beta.0 - August 14, 2021
+
+* [`be334f9`](https://github.com/eslint/eslint/commit/be334f9d8633e9d193dcb8b36f484547e9d3ab97) Chore: Fix Makefile call to linter.getRules() (#14932) (Brandon Mills)
+* [`0c86b68`](https://github.com/eslint/eslint/commit/0c86b68a6e2435eb03b681b51b099b552b521adc) Chore: Replace old syntax for Array flat/flatMap (#14614) (Stephen Wade)
+* [`6a89f3f`](https://github.com/eslint/eslint/commit/6a89f3f7b6a3edb3465952521bdf06a220515b95) Chore: ignore `yarn-error.log` and `.pnpm-debug.log` (#14925) (Nitin Kumar)
+* [`28fe19c`](https://github.com/eslint/eslint/commit/28fe19c4a9108111932966aa7c9f361c26601d70) Docs: Add v8.0.0 migration guide (fixes #14856) (#14884) (Nicholas C. Zakas)
+* [`ec9db63`](https://github.com/eslint/eslint/commit/ec9db63e53a6605a558dcd82947d2425f89887c3) Upgrade: @eslint/eslintrc@1.0.0 (#14865) (Milos Djermanovic)
+* [`1f5d088`](https://github.com/eslint/eslint/commit/1f5d0889264c60dddb6fb07a3b1e43f840e84d57) Docs: add an example `Object.assign()` for rule no-import-assign (#14916) (薛定谔的猫)
+* [`af96584`](https://github.com/eslint/eslint/commit/af965848c010612c3e136c367cc9b9e2e822f580) Fix: handle computed class fields in operator-linebreak (refs #14857) (#14915) (Milos Djermanovic)
+* [`3b6cd89`](https://github.com/eslint/eslint/commit/3b6cd8934b3640ffb6fa49b471babf07f0ad769a) Chore: Add rel/abs path tests in `no-restricted-{imports/modules}` rules (#14910) (Bryan Mishkin)
+* [`62c6fe7`](https://github.com/eslint/eslint/commit/62c6fe7d10ff4eeebd196e143f96cfd88818393d) Upgrade: Debug 4.0.1 > 4.3.2 (#14892) (sandesh bafna)
+* [`f984515`](https://github.com/eslint/eslint/commit/f98451584a82e41f82ceacd484ea0fe90aa9ce63) Chore: add assertions on reporting location in `semi` (#14899) (Nitin Kumar)
+* [`a773b99`](https://github.com/eslint/eslint/commit/a773b99873965652a86bec489193dc42a8923f5f) Fix: no-useless-computed-key edge cases with class fields (refs #14857) (#14903) (Milos Djermanovic)
+* [`88db3f5`](https://github.com/eslint/eslint/commit/88db3f54988dddfbda35764ecf1ea16354c4213a) Upgrade: `js-yaml` to v4 (#14890) (Bryan Mishkin)
+* [`cbc43da`](https://github.com/eslint/eslint/commit/cbc43daad2ea229fb15a9198efd2bc2721dfb75f) Fix: prefer-destructuring PrivateIdentifier false positive (refs #14857) (#14897) (Milos Djermanovic)
+* [`ccb9a91`](https://github.com/eslint/eslint/commit/ccb9a9138acd63457e004630475495954c1be6f4) Fix: dot-notation false positive with private identifier (refs #14857) (#14898) (Milos Djermanovic)
+* [`8c35066`](https://github.com/eslint/eslint/commit/8c350660e61284c41a5cc1a5955c858db53c516b) Sponsors: Sync README with website (ESLint Jenkins)
+* [`a3dd825`](https://github.com/eslint/eslint/commit/a3dd8257252f392de5cf793c36ecab2acd955659) Sponsors: Sync README with website (ESLint Jenkins)
+* [`c4e5802`](https://github.com/eslint/eslint/commit/c4e58023f22381508babfc52087853b5e3965b9c) Docs: improve rule details for `no-console` (fixes #14793) (#14901) (Nitin Kumar)
+* [`9052eee`](https://github.com/eslint/eslint/commit/9052eee07a459dc059cd92f657a3ae73acc95bb5) Update: check class fields in no-extra-parens (refs #14857) (#14906) (Milos Djermanovic)
+* [`5c3a470`](https://github.com/eslint/eslint/commit/5c3a47072aeb5cfda40a1eb20b43a10c5ca7aab3) Docs: add class fields in no-multi-assign documentation (refs #14857) (#14907) (Milos Djermanovic)
+* [`d234d89`](https://github.com/eslint/eslint/commit/d234d890b383837f8e4bda0f6ce1e2a348f9835e) Docs: add class fields in func-names documentation (refs #14857) (#14908) (Milos Djermanovic)
+* [`ae6072b`](https://github.com/eslint/eslint/commit/ae6072b1de5c8b30ce6c58290852082439c40b30) Upgrade: `eslint-visitor-keys` to v3 (#14902) (Bryan Mishkin)
+* [`e53d8cf`](https://github.com/eslint/eslint/commit/e53d8cf9d73bd105cf6ba4f6b5477ccc4b980939) Upgrade: `markdownlint` dev dependencies (#14883) (Bryan Mishkin)
+* [`d66e941`](https://github.com/eslint/eslint/commit/d66e9414be60e05badb96bc3e1a55ca34636d7f8) Upgrade: @humanwhocodes/config-array to 0.6 (#14891) (Bryan Mishkin)
+* [`149230c`](https://github.com/eslint/eslint/commit/149230ce7e296c029a0b6c085216fc0360ed4c65) Chore: Specify Node 14.x for Verify Files CI job (#14896) (Milos Djermanovic)
+* [`537cf6a`](https://github.com/eslint/eslint/commit/537cf6a0e78ee9b7167e7f8c56f4053d3fb5b2d7) Chore: update `glob-parent` (fixes #14879)(#14887) (Nitin Kumar)
+* [`f7b4a3f`](https://github.com/eslint/eslint/commit/f7b4a3f6a44e167c71985d373f73eebd3a4d9556) Chore: update dev deps to latest (#14624) (薛定谔的猫)
+* [`24c9f2a`](https://github.com/eslint/eslint/commit/24c9f2ac57efcd699ca69695c82e51ce5742df7b) Breaking: Strict package exports (refs #13654) (#14706) (Nicholas C. Zakas)
+* [`86d31a4`](https://github.com/eslint/eslint/commit/86d31a4951e3a39e359e284f5fe336ac477369fe) Breaking: disallow SourceCode#getComments() in RuleTester (refs #14744) (#14769) (Milos Djermanovic)
+* [`1d2213d`](https://github.com/eslint/eslint/commit/1d2213deb69c5901c1950bbe648aa819e7e742ed) Breaking: Fixable disable directives (fixes #11815) (#14617) (Josh Goldberg)
+* [`4a7aab7`](https://github.com/eslint/eslint/commit/4a7aab7d4323ff7027eebca709d4e95a9aaa80bc) Breaking: require `meta` for fixable rules (fixes #13349) (#14634) (Milos Djermanovic)
+* [`d6a761f`](https://github.com/eslint/eslint/commit/d6a761f9b6582e9f71705161be827ca303ef183f) Breaking: Require `meta.hasSuggestions` for rules with suggestions (#14573) (Bryan Mishkin)
+* [`4c841b8`](https://github.com/eslint/eslint/commit/4c841b880b5649392a55c98ecc9af757bd213ff0) Breaking: allow all directives in line comments (fixes #14575) (#14656) (薛定谔的猫)
+* [`6bd747b`](https://github.com/eslint/eslint/commit/6bd747b5b7731195224875b952a9ea61445a9938) Breaking: support new regex d flag (fixes #14640) (#14653) (Yosuke Ota)
+* [`8b4f3ab`](https://github.com/eslint/eslint/commit/8b4f3abdb794feb3be31959bb44bfb0ef6318e8e) Breaking: fix comma-dangle schema (fixes #13739) (#14030) (Joakim Nilsson)
+* [`b953a4e`](https://github.com/eslint/eslint/commit/b953a4ee12f120658a9ec27d1f8ca88dd3dfb599) Breaking: upgrade espree and support new class features (refs #14343) (#14591) (Toru Nagashima)
+* [`8cce06c`](https://github.com/eslint/eslint/commit/8cce06cb39886902ce0d2e6882f46c3bf52fb955) Breaking: add some rules to eslint:recommended (refs #14673) (#14691) (薛定谔的猫)
+* [`86bb63b`](https://github.com/eslint/eslint/commit/86bb63b370e0ff350e988a5fa228a8234abe800c) Breaking: Drop `codeframe` and `table` formatters (#14316) (Federico Brigante)
+* [`f3cb320`](https://github.com/eslint/eslint/commit/f3cb3208c8952a6218d54658cfda85942b9fda42) Breaking: drop node v10/v13/v15 (fixes #14023) (#14592) (薛定谔的猫)
+* [`b8b2d55`](https://github.com/eslint/eslint/commit/b8b2d5553b0de23e8b72ee45949650cd5f9a10d2) Build: add codeql (#14729) (薛定谔的猫)
+* [`e037d61`](https://github.com/eslint/eslint/commit/e037d61a12ad17a36e05dcf65aa63fad303c79b9) Docs: Mention workaround for escaping the slash character in selectors (#14675) (Aria)
+* [`81f03b6`](https://github.com/eslint/eslint/commit/81f03b6ad69c7f67ad6ba72e02e73266aa8f7696) Docs: Update license copyright (#14877) (Nicholas C. Zakas)
+* [`fa1c07c`](https://github.com/eslint/eslint/commit/fa1c07c0d65ce21a30f5bb4a9f2ac511f8df6446) Sponsors: Sync README with website (ESLint Jenkins)
+* [`e31f492`](https://github.com/eslint/eslint/commit/e31f49206f94e2b3977ec37892d4b87ab1e46872) Sponsors: Sync README with website (ESLint Jenkins)
+* [`8307256`](https://github.com/eslint/eslint/commit/83072561b006a558d026c5a507f92945b821a0cd) Sponsors: Sync README with website (ESLint Jenkins)
+
+v7.32.0 - July 30, 2021
+
+* [`3c78a7b`](https://github.com/eslint/eslint/commit/3c78a7bff6044fd196ae3b737983e6744c6eb7c8) Chore: Adopt `eslint-plugin/prefer-message-ids` rule internally (#14841) (Bryan Mishkin)
+* [`faecf56`](https://github.com/eslint/eslint/commit/faecf56cdb4146b28bfa4f1980adb41b4d3614b1) Update: change reporting location for `curly` rule (refs #12334) (#14766) (Nitin Kumar)
+* [`d7dc07a`](https://github.com/eslint/eslint/commit/d7dc07a15e256cee9232183165e2f6102f2c0873) Fix: ignore lines with empty elements (fixes #12756) (#14837) (Soufiane Boutahlil)
+* [`1bfbefd`](https://github.com/eslint/eslint/commit/1bfbefdaaf19ef32df42b89a3f5d32cff1e5b831) New: Exit on fatal error (fixes #13711) (#14730) (Antonios Katopodis)
+* [`ed007c8`](https://github.com/eslint/eslint/commit/ed007c82ee9d2170c87500d98303554b5f90b915) Chore: Simplify internal `no-invalid-meta` rule (#14842) (Bryan Mishkin)
+* [`d53d906`](https://github.com/eslint/eslint/commit/d53d9064b9dd0dd6a8ea39e07b16310c8364db69) Docs: Prepare data for website to indicate rules with suggestions (#14830) (Bryan Mishkin)
+* [`d28f2ff`](https://github.com/eslint/eslint/commit/d28f2ffb986e49d6da5c1d91215580591f4cfd35) Docs: Reference eslint-config-eslint to avoid potential for staleness (#14805) (Brett Zamir)
+* [`8be8a36`](https://github.com/eslint/eslint/commit/8be8a36010145dfcd31cbdd4f781a91989e3b1bd) Chore: Adopt `eslint-plugin/require-meta-docs-url` rule internally (#14823) (Bryan Mishkin)
+* [`f9c164f`](https://github.com/eslint/eslint/commit/f9c164f7b74ca73384c8c80eed5bdbe359b44f6c) Docs: New syntax issue template (#14826) (Nicholas C. Zakas)
+* [`eba0c45`](https://github.com/eslint/eslint/commit/eba0c4595c126a91f700d5f2e8723ec3f820a830) Chore: assertions on reporting loc in `unicode-bom` (refs #12334) (#14809) (Nitin Kumar)
+* [`ed945bd`](https://github.com/eslint/eslint/commit/ed945bd662714b1917e9de71d5b322a28be9161b) Docs: fix multiple broken links (#14833) (Sam Chen)
+* [`60df44c`](https://github.com/eslint/eslint/commit/60df44c79b0f74406119c0c040a360ca84e721fc) Chore: use `actions/setup-node@v2` (#14816) (Nitin Kumar)
+* [`6641d88`](https://github.com/eslint/eslint/commit/6641d88e17d952a8e51df5e0d3882a842d4c3f35) Docs: Update README team and sponsors (ESLint Jenkins)
+
+v7.31.0 - July 17, 2021
+
+* [`efdbb12`](https://github.com/eslint/eslint/commit/efdbb1227019427ec2d968a8d6e9151dd8a77c35) Upgrade: @eslint/eslintrc to v0.4.3 (#14808) (Brandon Mills)
+* [`a96b05f`](https://github.com/eslint/eslint/commit/a96b05f6c5649cfee112d605c91d95aa191e2f78) Update: add end location to report in `consistent-return` (refs #12334) (#14798) (Nitin Kumar)
+* [`e0e8e30`](https://github.com/eslint/eslint/commit/e0e8e308929c9c66612505f2da89043f8592eea7) Docs: update BUG_REPORT template (#14787) (Nitin Kumar)
+* [`39115c8`](https://github.com/eslint/eslint/commit/39115c8b71d2629161359f6456f47fdbd552fddd) Docs: provide more context to no-eq-null (#14801) (gfyoung)
+* [`9a3c73c`](https://github.com/eslint/eslint/commit/9a3c73c130d437a65f4edba0dcb63390e68cac41) Docs: fix a broken link (#14790) (Sam Chen)
+* [`ddffa8a`](https://github.com/eslint/eslint/commit/ddffa8ad58b4b124b08061e9045fdb5370cbdbe3) Update: Indicating the operator in question (#14764) (Paul Smith)
+* [`bba714c`](https://github.com/eslint/eslint/commit/bba714c2ed813821ed288fbc07722cdde6e534fe) Update: Clarifying what changes need to be made in no-mixed-operators (#14765) (Paul Smith)
+* [`b0d22e3`](https://github.com/eslint/eslint/commit/b0d22e3eff18ea7f08189134c07cddceaec69a09) Docs: Mention benefit of providing `meta.docs.url` (#14774) (Bryan Mishkin)
+* [`000cc79`](https://github.com/eslint/eslint/commit/000cc796fd487e7b9ba8bcc5857dd691044479cc) Sponsors: Sync README with website (ESLint Jenkins)
+* [`a6a7438`](https://github.com/eslint/eslint/commit/a6a7438502abc6a1e29ec35cfbe2058ffc0803b1) Chore: pin fs-teardown@0.1.1 (#14771) (Milos Djermanovic)
+
+v7.30.0 - July 2, 2021
+
+* [`5f74642`](https://github.com/eslint/eslint/commit/5f746420700d457b92dd86659de588d272937b79) Chore: don't check Program.start in SourceCode#getComments (refs #14744) (#14748) (Milos Djermanovic)
+* [`19a871a`](https://github.com/eslint/eslint/commit/19a871a35ae9997ce352624b1081c96c54b73a9f) Docs: Suggest linting plugins for ESLint plugin developers (#14754) (Bryan Mishkin)
+* [`aa87329`](https://github.com/eslint/eslint/commit/aa87329d919f569404ca573b439934552006572f) Docs: fix broken links (#14756) (Sam Chen)
+* [`278813a`](https://github.com/eslint/eslint/commit/278813a6e759f6b5512ac64c7530c9c51732e692) Docs: fix and add more examples for new-cap rule (fixes #12874) (#14725) (Nitin Kumar)
+* [`ed1da5d`](https://github.com/eslint/eslint/commit/ed1da5d96af2587b7211854e45cf8657ef808710) Update: ecmaVersion allows "latest" (#14720) (薛定谔的猫)
+* [`104c0b5`](https://github.com/eslint/eslint/commit/104c0b592f203d315a108d311c58375357e40b24) Update: improve use-isnan rule to detect `Number.NaN` (fixes #14715) (#14718) (Nitin Kumar)
+* [`b08170b`](https://github.com/eslint/eslint/commit/b08170b92beb22db6ec612ebdfff930f9e0582ab) Update: Implement FlatConfigArray (refs #13481) (#14321) (Nicholas C. Zakas)
+* [`f113cdd`](https://github.com/eslint/eslint/commit/f113cdd872257d72bbd66d95e4eaf13623323b24) Chore: upgrade eslint-plugin-eslint-plugin (#14738) (薛定谔的猫)
+* [`1b8997a`](https://github.com/eslint/eslint/commit/1b8997ab63781f4ebf87e3269400b2ef4c7d2973) Docs: Fix getRulesMetaForResults link syntax (#14723) (Brandon Mills)
+* [`aada733`](https://github.com/eslint/eslint/commit/aada733d2aee830aa32cccb9828cd72db4ccd6bd) Docs: fix two broken links (#14726) (Sam Chen)
+* [`8972529`](https://github.com/eslint/eslint/commit/8972529f82d13bd04059ee8852b4ebb9b5350962) Docs: Update README team and sponsors (ESLint Jenkins)
+
+v7.29.0 - June 18, 2021
+
+* [`bfbfe5c`](https://github.com/eslint/eslint/commit/bfbfe5c1fd4c39a06d5e159dbe48479ca4305fc0) New: Add only to RuleTester (refs eslint/rfcs#73) (#14677) (Brandon Mills)
+* [`c2cd7b4`](https://github.com/eslint/eslint/commit/c2cd7b4a18057ca6067bdfc16de771dc5d90c0ea) New: Add ESLint#getRulesMetaForResults() (refs #13654) (#14716) (Nicholas C. Zakas)
+* [`eea7e0d`](https://github.com/eslint/eslint/commit/eea7e0d09d6ef43d6663cbe424e7974764a5f7fe) Chore: remove duplicate code (#14719) (Nitin Kumar)
+* [`6a1c7a0`](https://github.com/eslint/eslint/commit/6a1c7a0dac050ea5876972c50563a7eb867b38d3) Fix: allow fallthrough comment inside block (fixes #14701) (#14702) (Kevin Gibbons)
+* [`a47e5e3`](https://github.com/eslint/eslint/commit/a47e5e30b0da364593b6881f6826c595da8696f5) Docs: Add Mega-Linter to the list of integrations (#14707) (Nicolas Vuillamy)
+* [`353ddf9`](https://github.com/eslint/eslint/commit/353ddf965078030794419b089994373e27ffc86e) Chore: enable reportUnusedDisableDirectives in eslint-config-eslint (#14699) (薛定谔的猫)
+* [`757c495`](https://github.com/eslint/eslint/commit/757c49584a5852c468c1b4a0b74ad3aa39d954e5) Chore: add some rules to eslint-config-eslint (#14692) (薛定谔的猫)
+* [`c93a222`](https://github.com/eslint/eslint/commit/c93a222563177a9b5bc7a59aa106bc0a6d31e063) Docs: fix a broken link (#14697) (Sam Chen)
+* [`655c118`](https://github.com/eslint/eslint/commit/655c1187fc845bac61ae8d06c556f1a59ee2071b) Sponsors: Sync README with website (ESLint Jenkins)
+* [`e2bed2e`](https://github.com/eslint/eslint/commit/e2bed2ead22b575d55ccaeed94eecd3a979dd871) Sponsors: Sync README with website (ESLint Jenkins)
+* [`8490fb4`](https://github.com/eslint/eslint/commit/8490fb42e559ef0b3c34ac60be4e05e0d879a9cb) Sponsors: Sync README with website (ESLint Jenkins)
+* [`ddbe877`](https://github.com/eslint/eslint/commit/ddbe877c95224e127215d35562a175c6f2b7ba22) Sponsors: Sync README with website (ESLint Jenkins)
+
 v7.28.0 - June 4, 2021
 
 * [`1237705`](https://github.com/eslint/eslint/commit/1237705dd08c209c5e3136045ec51a4ba87a3abe) Upgrade: @eslint/eslintrc to 0.4.2 (#14672) (Milos Djermanovic)
index 7fe552a86615053f376bbc3f0aa87d5fd172edc4..b607bb36e96c33fd8ef50897db0336c585b6137a 100644 (file)
@@ -1,4 +1,4 @@
-Copyright JS Foundation and other contributors, https://js.foundation
+Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
index 6cffb770c640c3abe9bcaf275f749bb31950f43c..0a0846b9270dbe75877b6a6420d21e94926f6b9b 100644 (file)
@@ -3,16 +3,13 @@
  * @author nzakas
  */
 
-/* global target */
-/* eslint no-use-before-define: "off", no-console: "off" */
+/* eslint no-use-before-define: "off", no-console: "off" -- CLI */
 "use strict";
 
 //------------------------------------------------------------------------------
 // Requirements
 //------------------------------------------------------------------------------
 
-require("shelljs/make");
-
 const checker = require("npm-license"),
     ReleaseOps = require("eslint-release"),
     dateformat = require("dateformat"),
@@ -28,6 +25,13 @@ const checker = require("npm-license"),
     { CLIEngine } = require("./lib/cli-engine"),
     builtinRules = require("./lib/rules/index");
 
+require("shelljs/make");
+/* global target -- global.target is declared in `shelljs/make.js` */
+/**
+ * global.target = {};
+ * @see https://github.com/shelljs/shelljs/blob/124d3349af42cb794ae8f78fc9b0b538109f7ca7/make.js#L4
+ * @see https://github.com/DefinitelyTyped/DefinitelyTyped/blob/3aa2d09b6408380598cfb802743b07e1edb725f3/types/shelljs/make.d.ts#L8-L11
+ */
 const { cat, cd, cp, echo, exec, exit, find, ls, mkdir, pwd, rm, test } = require("shelljs");
 
 //------------------------------------------------------------------------------
@@ -43,7 +47,7 @@ const { cat, cd, cp, echo, exec, exit, find, ls, mkdir, pwd, rm, test } = requir
 const PERF_MULTIPLIER = 13e6;
 
 const OPEN_SOURCE_LICENSES = [
-    /MIT/u, /BSD/u, /Apache/u, /ISC/u, /WTF/u, /Public Domain/u, /LGPL/u
+    /MIT/u, /BSD/u, /Apache/u, /ISC/u, /WTF/u, /Public Domain/u, /LGPL/u, /Python/u
 ];
 
 //------------------------------------------------------------------------------
@@ -66,7 +70,8 @@ const NODE = "node ", // intentional extra space
     // Files
     RULE_FILES = glob.sync("lib/rules/*.js").filter(filePath => path.basename(filePath) !== "index.js"),
     JSON_FILES = find("conf/").filter(fileType("json")),
-    MARKDOWN_FILES_ARRAY = find("docs/").concat(ls(".")).filter(fileType("md")),
+    MARKDOWNLINT_IGNORED_FILES = fs.readFileSync(path.join(__dirname, ".markdownlintignore"), "utf-8").split("\n"),
+    MARKDOWN_FILES_ARRAY = find("docs/").concat(ls(".")).filter(fileType("md")).filter(file => !MARKDOWNLINT_IGNORED_FILES.includes(file)),
     TEST_FILES = "\"tests/{bin,lib,tools}/**/*.js\"",
     PERF_ESLINTRC = path.join(PERF_TMP_DIR, "eslintrc.yml"),
     PERF_MULTIFILES_TARGET_DIR = path.join(PERF_TMP_DIR, "eslint"),
@@ -148,8 +153,8 @@ function generateBlogPost(releaseInfo, prereleaseMajorVersion) {
 
 /**
  * Generates a doc page with formatter result examples
- * @param  {Object} formatterInfo Linting results from each formatter
- * @param  {string} [prereleaseVersion] The version used for a prerelease. This
+ * @param {Object} formatterInfo Linting results from each formatter
+ * @param {string} [prereleaseVersion] The version used for a prerelease. This
  *      changes where the output is stored.
  * @returns {void}
  */
@@ -176,8 +181,8 @@ function generateFormatterExamples(formatterInfo, prereleaseVersion) {
  */
 function generateRuleIndexPage() {
     const outputFile = "../website/_data/rules.yml",
-        categoryList = "conf/category-list.json",
-        categoriesData = JSON.parse(cat(path.resolve(categoryList)));
+        ruleTypes = "conf/rule-type-list.json",
+        ruleTypesData = JSON.parse(cat(path.resolve(ruleTypes)));
 
     RULE_FILES
         .map(filename => [filename, path.basename(filename, ".js")])
@@ -188,7 +193,7 @@ function generateRuleIndexPage() {
             const rule = require(path.resolve(filename));
 
             if (rule.meta.deprecated) {
-                categoriesData.deprecated.rules.push({
+                ruleTypesData.deprecated.rules.push({
                     name: basename,
                     replacedBy: rule.meta.replacedBy || []
                 });
@@ -197,22 +202,23 @@ function generateRuleIndexPage() {
                         name: basename,
                         description: rule.meta.docs.description,
                         recommended: rule.meta.docs.recommended || false,
-                        fixable: !!rule.meta.fixable
+                        fixable: !!rule.meta.fixable,
+                        hasSuggestions: !!rule.meta.hasSuggestions
                     },
-                    category = categoriesData.categories.find(c => c.name === rule.meta.docs.category);
+                    ruleType = ruleTypesData.types.find(c => c.name === rule.meta.type);
 
-                if (!category.rules) {
-                    category.rules = [];
+                if (!ruleType.rules) {
+                    ruleType.rules = [];
                 }
 
-                category.rules.push(output);
+                ruleType.rules.push(output);
             }
         });
 
     // `.rules` will be `undefined` if all rules in category are deprecated.
-    categoriesData.categories = categoriesData.categories.filter(category => !!category.rules);
+    ruleTypesData.types = ruleTypesData.types.filter(ruleType => !!ruleType.rules);
 
-    const output = yaml.safeDump(categoriesData, { sortKeys: true });
+    const output = yaml.dump(ruleTypesData, { sortKeys: true });
 
     output.to(outputFile);
 }
@@ -388,7 +394,7 @@ function getFirstVersionOfDeletion(filePath) {
  * @private
  */
 function lintMarkdown(files) {
-    const config = yaml.safeLoad(fs.readFileSync(path.join(__dirname, "./.markdownlint.yml"), "utf8")),
+    const config = yaml.load(fs.readFileSync(path.join(__dirname, "./.markdownlint.yml"), "utf8")),
         result = markdownlint.sync({
             files,
             config,
@@ -471,10 +477,6 @@ function getBinFile(command) {
 // Tasks
 //------------------------------------------------------------------------------
 
-target.all = function() {
-    target.test();
-};
-
 target.lint = function([fix = false] = []) {
     let errors = 0,
         lastReturn;
@@ -544,7 +546,7 @@ target.mocha = () => {
 
     echo("Running unit tests");
 
-    lastReturn = exec(`${getBinFile("nyc")} -- ${MOCHA} -R progress -t ${MOCHA_TIMEOUT} -c ${TEST_FILES}`);
+    lastReturn = exec(`${getBinFile("nyc")} -- ${MOCHA} --forbid-only -R progress -t ${MOCHA_TIMEOUT} -c ${TEST_FILES}`);
     if (lastReturn.code !== 0) {
         errors++;
     }
@@ -580,12 +582,6 @@ target.test = function() {
     target.checkLicenses();
 };
 
-target.docs = function() {
-    echo("Generating documentation");
-    exec(`${getBinFile("jsdoc")} -d jsdoc lib`);
-    echo("Documentation has been output to /jsdoc");
-};
-
 target.gensite = function(prereleaseVersion) {
     echo("Generating eslint.org");
 
@@ -636,10 +632,12 @@ target.gensite = function(prereleaseVersion) {
         };
     }
 
-    const rules = require(".").linter.getRules();
+    const { Linter } = require(".");
+    const rules = new Linter().getRules();
 
     const RECOMMENDED_TEXT = "\n\n(recommended) The `\"extends\": \"eslint:recommended\"` property in a configuration file enables this rule.";
     const FIXABLE_TEXT = "\n\n(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.";
+    const HAS_SUGGESTIONS_TEXT = "\n\n(hasSuggestions) Some problems reported by this rule are manually fixable by editor [suggestions](../developer-guide/working-with-rules#providing-suggestions).";
 
     // 4. Loop through all files in temporary directory
     process.stdout.write("> Updating files (Steps 4-9): 0/... - ...\r");
@@ -649,8 +647,9 @@ target.gensite = function(prereleaseVersion) {
     tempFiles.forEach((filename, i) => {
         if (test("-f", filename) && path.extname(filename) === ".md") {
 
-            const rulesUrl = "https://github.com/eslint/eslint/tree/master/lib/rules/",
-                docsUrl = "https://github.com/eslint/eslint/tree/master/docs/rules/",
+            const rulesUrl = "https://github.com/eslint/eslint/tree/HEAD/lib/rules/",
+                testsUrl = "https://github.com/eslint/eslint/tree/HEAD/tests/lib/rules/",
+                docsUrl = "https://github.com/eslint/eslint/tree/HEAD/docs/rules/",
                 baseName = path.basename(filename),
                 sourceBaseName = `${path.basename(filename, ".md")}.js`,
                 sourcePath = path.join("lib/rules", sourceBaseName),
@@ -669,13 +668,14 @@ target.gensite = function(prereleaseVersion) {
                 const rule = rules.get(ruleName);
                 const isRecommended = rule && rule.meta.docs.recommended;
                 const isFixable = rule && rule.meta.fixable;
+                const hasSuggestions = rule && rule.meta.hasSuggestions;
 
                 // Incorporate the special portion into the documentation content
                 const textSplit = text.split("\n");
                 const ruleHeading = textSplit[0];
                 const ruleDocsContent = textSplit.slice(1).join("\n");
 
-                text = `${ruleHeading}${isRecommended ? RECOMMENDED_TEXT : ""}${isFixable ? FIXABLE_TEXT : ""}\n${ruleDocsContent}`;
+                text = `${ruleHeading}${isRecommended ? RECOMMENDED_TEXT : ""}${isFixable ? FIXABLE_TEXT : ""}${hasSuggestions ? HAS_SUGGESTIONS_TEXT : ""}\n${ruleDocsContent}`;
                 title = `${ruleName} - Rules`;
 
                 if (rule && rule.meta) {
@@ -696,7 +696,7 @@ target.gensite = function(prereleaseVersion) {
                 "---",
                 `title: ${title}`,
                 "layout: doc",
-                `edit_link: https://github.com/eslint/eslint/edit/master/${filePath}`,
+                `edit_link: https://github.com/eslint/eslint/edit/main/${filePath}`,
                 ruleType,
                 "---",
                 "<!-- Note: No pull requests accepted for this file. See README.md in the root directory for details. -->",
@@ -732,6 +732,7 @@ target.gensite = function(prereleaseVersion) {
                 text += "\n## Resources\n\n";
                 if (!removed) {
                     text += `* [Rule source](${rulesUrl}${sourceBaseName})\n`;
+                    text += `* [Test source](${testsUrl}${sourceBaseName})\n`;
                 }
                 text += `* [Documentation source](${docsUrl}${baseName})\n`;
             }
@@ -817,6 +818,20 @@ target.checkRuleFiles = function() {
             return idNewAtBeginningOfTitleRegExp.test(docText) || idOldAtEndOfTitleRegExp.test(docText);
         }
 
+        /**
+         * Check if deprecated information is in rule code and READNE.md.
+         * @returns {boolean} true if present
+         * @private
+         */
+        function hasDeprecatedInfo() {
+            const ruleCode = cat(filename);
+            const deprecatedTagRegExp = /@deprecated in ESLint/u;
+            const docText = cat(docFilename);
+            const deprecatedInfoRegExp = /This rule was .+deprecated.+in ESLint/u;
+
+            return deprecatedTagRegExp.test(ruleCode) && deprecatedInfoRegExp.test(docText);
+        }
+
         // check for docs
         if (!test("-f", docFilename)) {
             console.error("Missing documentation for rule %s", basename);
@@ -843,12 +858,17 @@ target.checkRuleFiles = function() {
         if (!ruleDef) {
             console.error(`Missing rule from index (./lib/rules/index.js): ${basename}. If you just added a new rule then add an entry for it in this file.`);
             errors++;
-        }
+        } else {
 
-        // check eslint:recommended
-        const recommended = require("./conf/eslint-recommended");
+            // check deprecated
+            if (ruleDef.meta.deprecated && !hasDeprecatedInfo()) {
+                console.error(`Missing deprecated information in ${basename} rule code or README.md. Please write @deprecated tag in code or 「This rule was deprecated in ESLint ...」 in README.md.`);
+                errors++;
+            }
+
+            // check eslint:recommended
+            const recommended = require("./conf/eslint-recommended");
 
-        if (ruleDef) {
             if (ruleDef.meta.docs.recommended) {
                 if (recommended.rules[basename] !== "error") {
                     console.error(`Missing rule from eslint:recommended (./conf/eslint-recommended.js): ${basename}. If you just made a rule recommended then add an entry for it in this file.`);
@@ -957,14 +977,20 @@ function createConfigForPerformanceTest() {
     content.join("\n").to(PERF_ESLINTRC);
 }
 
+/**
+ * @callback TimeCallback
+ * @param {?int[]} results
+ * @returns {void}
+ */
+
 /**
  * Calculates the time for each run for performance
  * @param {string} cmd cmd
  * @param {int} runs Total number of runs to do
  * @param {int} runNumber Current run number
  * @param {int[]} results Collection results from each run
- * @param {Function} cb Function to call when everything is done
- * @returns {int[]} calls the cb with all the results
+ * @param {TimeCallback} cb Function to call when everything is done
+ * @returns {void} calls the cb with all the results
  * @private
  */
 function time(cmd, runs, runNumber, results, cb) {
@@ -1076,9 +1102,12 @@ target.perf = function() {
 
                 // Count test target files.
                 const count = glob.sync(
-                    process.platform === "win32"
-                        ? PERF_MULTIFILES_TARGETS.slice(2).replace(/\\/gu, "/")
-                        : PERF_MULTIFILES_TARGETS
+                    (
+                        process.platform === "win32"
+                            ? PERF_MULTIFILES_TARGETS.replace(/\\/gu, "/")
+                            : PERF_MULTIFILES_TARGETS
+                    )
+                        .slice(1, -1) // strip quotes
                 ).length;
 
                 runPerformanceTest(
index 438a087ff234326811277bc7771b031a76ab84dc..bbc74b7ce4d25e1770b5d240f1ded0cf626cbd15 100644 (file)
@@ -43,7 +43,7 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
 
 ## <a name="installation-and-usage"></a>Installation and Usage
 
-Prerequisites: [Node.js](https://nodejs.org/) (`^10.12.0`, or `>=12.0.0`) built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)
+Prerequisites: [Node.js](https://nodejs.org/) (`^12.22.0`, `^14.17.0`, or `>=16.0.0`) built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)
 
 You can install ESLint using npm:
 
@@ -123,7 +123,7 @@ Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [confi
 
 ### What ECMAScript versions does ESLint support?
 
-ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, and 2020. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/user-guide/configuring).
+ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, 2020, 2021 and 2022. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/user-guide/configuring).
 
 ### What about experimental features?
 
@@ -206,6 +206,9 @@ This means:
 These folks keep the project moving and are resources for help.
 
 <!-- NOTE: This section is autogenerated. Do not manually edit.-->
+
+
+
 <!--teamstart-->
 
 ### Technical Steering Committee (TSC)
@@ -242,7 +245,7 @@ Toru Nagashima
 </td><td align="center" valign="top" width="11%">
 <a href="https://github.com/aladdin-add">
 <img src="https://github.com/aladdin-add.png?s=75" width="75" height="75"><br />
-薛定谔的猫
+唯然
 </a>
 </td></tr></tbody></table>
 
@@ -254,6 +257,16 @@ Toru Nagashima
 The people who review and fix bugs and help triage issues.
 
 <table><tbody><tr><td align="center" valign="top" width="11%">
+<a href="https://github.com/brettz9">
+<img src="https://github.com/brettz9.png?s=75" width="75" height="75"><br />
+Brett Zamir
+</a>
+</td><td align="center" valign="top" width="11%">
+<a href="https://github.com/bmish">
+<img src="https://github.com/bmish.png?s=75" width="75" height="75"><br />
+Bryan Mishkin
+</a>
+</td><td align="center" valign="top" width="11%">
 <a href="https://github.com/g-plane">
 <img src="https://github.com/g-plane.png?s=75" width="75" height="75"><br />
 Pig Fang
@@ -268,11 +281,19 @@ Anix
 <img src="https://github.com/yeonjuan.png?s=75" width="75" height="75"><br />
 YeonJuan
 </a>
+</td><td align="center" valign="top" width="11%">
+<a href="https://github.com/snitin315">
+<img src="https://github.com/snitin315.png?s=75" width="75" height="75"><br />
+Nitin Kumar
+</a>
 </td></tr></tbody></table>
 
 
 <!--teamend-->
 
+
+
+
 ## <a name="sponsors"></a>Sponsors
 
 The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://opencollective.com/eslint) to get your logo on our README and website.
@@ -281,9 +302,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <!--sponsorsstart-->
 <h3>Platinum Sponsors</h3>
 <p><a href="https://automattic.com"><img src="https://images.opencollective.com/photomatt/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
-<p><a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
-<p><a href="https://retool.com/"><img src="https://images.opencollective.com/retool/98ea68e/logo.png" alt="Retool" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
-<p><a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a></p>
+<p><a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://coinbase.com"><img src="https://avatars.githubusercontent.com/u/1885080?v=4" alt="Coinbase" height="96"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
+<p><a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
+<p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" height="32"></a> <a href="https://troypoint.com"><img src="https://images.opencollective.com/troypoint/080f96f/avatar.png" alt="TROYPOINT" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://www.practiceignition.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Practice Ignition" height="32"></a></p>
 <!--sponsorsend-->
 
 ## <a name="technology-sponsors"></a>Technology Sponsors
index 5fa5766828edc3f234f805af6dd2b0fe5989445c..6b05356b9dbfafd166b046f0e1641b697939d285 100755 (executable)
@@ -5,7 +5,7 @@
  * @author Nicholas C. Zakas
  */
 
-/* eslint no-console:off */
+/* eslint no-console:off -- CLI */
 
 "use strict";
 
diff --git a/eslint/conf/category-list.json b/eslint/conf/category-list.json
deleted file mode 100644 (file)
index cd3b816..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-{
-    "categories": [
-        { "name": "Possible Errors", "description": "These rules relate to possible syntax or logic errors in JavaScript code:" },
-        { "name": "Best Practices", "description": "These rules relate to better ways of doing things to help you avoid problems:" },
-        { "name": "Strict Mode", "description": "These rules relate to strict mode directives:" },
-        { "name": "Variables", "description": "These rules relate to variable declarations:" },
-        { "name": "Stylistic Issues", "description": "These rules relate to style guidelines, and are therefore quite subjective:" },
-        { "name": "ECMAScript 6", "description": "These rules relate to ES6, also known as ES2015:" }
-    ],
-    "deprecated": {
-        "name": "Deprecated",
-        "description": "These rules have been deprecated in accordance with the <a href=\"/docs/user-guide/rule-deprecation\">deprecation policy</a>, and replaced by newer rules:",
-        "rules": []
-    },
-    "removed": {
-        "name": "Removed",
-        "description": "These rules from older versions of ESLint (before the <a href=\"/docs/user-guide/rule-deprecation\">deprecation policy</a> existed) have been replaced by newer rules:",
-        "rules": [
-            { "removed": "generator-star", "replacedBy": ["generator-star-spacing"] },
-            { "removed": "global-strict", "replacedBy": ["strict"] },
-            { "removed": "no-arrow-condition", "replacedBy": ["no-confusing-arrow", "no-constant-condition"] },
-            { "removed": "no-comma-dangle", "replacedBy": ["comma-dangle"] },
-            { "removed": "no-empty-class", "replacedBy": ["no-empty-character-class"] },
-            { "removed": "no-empty-label", "replacedBy": ["no-labels"] },
-            { "removed": "no-extra-strict", "replacedBy": ["strict"] },
-            { "removed": "no-reserved-keys", "replacedBy": ["quote-props"] },
-            { "removed": "no-space-before-semi", "replacedBy": ["semi-spacing"] },
-            { "removed": "no-wrap-func", "replacedBy": ["no-extra-parens"] },
-            { "removed": "space-after-function-name", "replacedBy": ["space-before-function-paren"] },
-            { "removed": "space-after-keywords", "replacedBy": ["keyword-spacing"] },
-            { "removed": "space-before-function-parentheses", "replacedBy": ["space-before-function-paren"] },
-            { "removed": "space-before-keywords", "replacedBy": ["keyword-spacing"] },
-            { "removed": "space-in-brackets", "replacedBy": ["object-curly-spacing", "array-bracket-spacing"] },
-            { "removed": "space-return-throw-case", "replacedBy": ["keyword-spacing"] },
-            { "removed": "space-unary-word-ops", "replacedBy": ["space-unary-ops"] },
-            { "removed": "spaced-line-comment", "replacedBy": ["spaced-comment"] }
-        ]
-    }
-}
diff --git a/eslint/conf/rule-type-list.json b/eslint/conf/rule-type-list.json
new file mode 100644 (file)
index 0000000..f362aa4
--- /dev/null
@@ -0,0 +1,36 @@
+{
+    "types": [
+        { "name": "problem", "displayName": "Possible Problems", "description": "These rules relate to possible logic errors in code:" },
+        { "name": "suggestion", "displayName": "Suggestions", "description": "These rules suggest alternate ways of doing things:" },
+        { "name": "layout", "displayName": "Layout & Formatting", "description": "These rules care about how the code looks rather than how it executes:" }
+    ],
+    "deprecated": {
+        "name": "Deprecated",
+        "description": "These rules have been deprecated in accordance with the <a href=\"/docs/user-guide/rule-deprecation\">deprecation policy</a>, and replaced by newer rules:",
+        "rules": []
+    },
+    "removed": {
+        "name": "Removed",
+        "description": "These rules from older versions of ESLint (before the <a href=\"/docs/user-guide/rule-deprecation\">deprecation policy</a> existed) have been replaced by newer rules:",
+        "rules": [
+            { "removed": "generator-star", "replacedBy": ["generator-star-spacing"] },
+            { "removed": "global-strict", "replacedBy": ["strict"] },
+            { "removed": "no-arrow-condition", "replacedBy": ["no-confusing-arrow", "no-constant-condition"] },
+            { "removed": "no-comma-dangle", "replacedBy": ["comma-dangle"] },
+            { "removed": "no-empty-class", "replacedBy": ["no-empty-character-class"] },
+            { "removed": "no-empty-label", "replacedBy": ["no-labels"] },
+            { "removed": "no-extra-strict", "replacedBy": ["strict"] },
+            { "removed": "no-reserved-keys", "replacedBy": ["quote-props"] },
+            { "removed": "no-space-before-semi", "replacedBy": ["semi-spacing"] },
+            { "removed": "no-wrap-func", "replacedBy": ["no-extra-parens"] },
+            { "removed": "space-after-function-name", "replacedBy": ["space-before-function-paren"] },
+            { "removed": "space-after-keywords", "replacedBy": ["keyword-spacing"] },
+            { "removed": "space-before-function-parentheses", "replacedBy": ["space-before-function-paren"] },
+            { "removed": "space-before-keywords", "replacedBy": ["keyword-spacing"] },
+            { "removed": "space-in-brackets", "replacedBy": ["object-curly-spacing", "array-bracket-spacing"] },
+            { "removed": "space-return-throw-case", "replacedBy": ["keyword-spacing"] },
+            { "removed": "space-unary-word-ops", "replacedBy": ["space-unary-ops"] },
+            { "removed": "spaced-line-comment", "replacedBy": ["spaced-comment"] }
+        ]
+    }
+}
index ea1943099778b24055b2b63e889f59de349c0ed8..a5e6592786911e66c50dce66a4f32e51a512ca80 100644 (file)
@@ -32,7 +32,7 @@ You're finally ready to start working with rules. You may want to fix an existin
 
 ## Section 5: [Working with Plugins](working-with-plugins.md)
 
-You've developed library-specific rules for ESLint and you want to share it with the community. You can publish an ESLint plugin on npm.
+You've developed library-specific rules for ESLint and you want to share them with the community. You can publish an ESLint plugin on npm.
 
 ## Section 6: [Working with Custom Parsers](working-with-custom-parsers.md)
 
index 1ea74bb7966094cdef0d35c8d65f149258cb4790..6531c28391f8940ae374cb7647290c1dadc2da56 100644 (file)
@@ -5,7 +5,7 @@
 At a high level, there are a few key parts to ESLint:
 
 * `bin/eslint.js` - this is the file that actually gets executed with the command line utility. It's a dumb wrapper that does nothing more than bootstrap ESLint, passing the command line arguments to `cli`. This is intentionally small so as not to require heavy testing.
-* `lib/api.js` - this is the entry point of `require("eslint")`. This file exposes an object that contains public classes `Linter`, `CLIEngine`, `RuleTester`, and `SourceCode`.
+* `lib/api.js` - this is the entry point of `require("eslint")`. This file exposes an object that contains public classes `Linter`, `ESLint`, `RuleTester`, and `SourceCode`.
 * `lib/cli.js` - this is the heart of the ESLint CLI. It takes an array of arguments and then uses `eslint` to execute the commands. By keeping this as a separate utility, it allows others to effectively call ESLint from within another Node.js program as if it were done on the command line. The main call is `cli.execute()`. This is also the part that does all the file reading, directory traversing, input, and output.
 * `lib/init/` - this module contains `--init` functionality that set up a configuration file for end users.
 * `lib/cli-engine/` - this module is `CLIEngine` class that finds source code files and configuration files then does code verifying with the `Linter` class. This includes the loading logic of configuration files, parsers, plugins, and formatters.
index 3ee02286c9eea8c734e210578a3238f0015b59ef..0c1cbe6245d60bb01c6a050e3279a638deb6bce5 100644 (file)
@@ -1,925 +1,9 @@
 # Code Conventions
 
-Programming language style guides are important for the long-term maintainability of software. This guide is based on the [Code Conventions for the Java Programming Language](https://java.sun.com/docs/codeconv/) and [Douglas Crockford's Code Conventions for the JavaScript Programming Language](http://javascript.crockford.com/code.html). Modifications have been made due to my personal experience and preferences.
+Code conventions for ESLint are determined by
+[eslint-config-eslint](https://www.npmjs.com/package/eslint-config-eslint).
 
-## File Format
-
-Each file has this same basic format:
-
-```js
-/**
- * @fileoverview Description of the file
- * @author Your Name
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-// require() statements
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-// private methods/data
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-// exported objects/methods
-module.exports = {
-
-};
-```
-
-The `@author` field gives you credit for having created the file.
-
-## Indentation
-
-Each indentation level is made up of four spaces. Do not use tabs.
-
-    // Good
-    if (true) {
-        doSomething();
-    }
-
-## Primitive Literals
-
-Strings should always use double quotes (never single quotes) and should always appear on a single line. Never use a slash to create a new line in a string.
-
-    // Good
-    var name = "Nicholas";
-
-    // Bad: Single quotes
-    var name = 'Nicholas';
-
-    // Bad: Wrapping to second line
-    var longString = "Here's the story, of a man \
-    named Brady.";
-
-Numbers should be written as decimal integers, e-notation integers, hexadecimal integers or floating-point decimals with at least one digit before and one digit after the decimal point. Never use octal literals.
-
-    // Good
-    var count = 10;
-
-    // Good
-    var price = 10.0;
-    var price = 10.00;
-
-    // Good
-    var num = 0xA2;
-
-    // Good
-    var num = 1e23;
-
-    // Bad: Hanging decimal point
-    var price = 10.;
-
-    // Bad: Leading decimal point
-    var price = .1;
-
-    // Bad: Octal (base 8) is deprecated
-    var num = 010;
-
-The special value `null` should be used only in the following situations:
-
-1. To initialize a variable that may later be assign an object value.
-1. To compare against an initialized variable that may or may not have an object value.
-1. To pass into a function where an object is expected.
-1. To return from a function where an object is expected.
-
-Examples:
-
-    // Good
-    var person = null;
-
-    // Good
-    function getPerson() {
-        if (condition) {
-            return new Person("Nicholas");
-        } else {
-            return null;
-        }
-    }
-
-    // Good
-    var person = getPerson();
-    if (person !== null){
-        doSomething();
-    }
-
-    // Bad: Testing against uninitialized variable
-    var person;
-    if (person != null){
-        doSomething();
-    }
-
-    // Bad: Testing to see if an argument was passed
-    function doSomething(arg1, arg2, arg3, arg4){
-        if (arg4 != null){
-            doSomethingElse();
-        }
-    }
-
-Never use the special value `undefined`. To see if a variable has been defined, use the `typeof` operator:
-
-    // Good
-    if (typeof variable == "undefined") {
-        // do something
-    }
-
-    // Bad: Using undefined literal
-    if (variable == undefined) {
-        // do something
-    }
-
-## Operator Spacing
-
-Operators with two operands must be preceded and followed by a single space to make the expression clear. Operators include assignments and logical operators.
-
-    // Good
-    var found = (values[i] === item);
-
-    // Good
-    if (found && (count > 10)) {
-        doSomething();
-    }
-
-    // Good
-    for (i = 0; i < count; i++) {
-        process(i);
-    }
-
-    // Bad: Missing spaces
-    var found = (values[i]===item);
-
-    // Bad: Missing spaces
-    if (found&&(count>10)) {
-        doSomething();
-    }
-
-    // Bad: Missing spaces
-    for (i=0; i<count; i++) {
-        process(i);
-    }
-
-## Parentheses Spacing
-
-When parentheses are used, there should be no whitespace immediately after the opening paren or immediately before the closing paren.
-
-    // Good
-    var found = (values[i] === item);
-
-    // Good
-    if (found && (count > 10)) {
-        doSomething();
-    }
-
-    // Good
-    for (i = 0; i < count; i++) {
-        process(i);
-    }
-
-    // Bad: Extra space after opening paren
-    var found = ( values[i] === item);
-
-    // Bad: Extra space before closing paren
-    if (found && (count > 10) ) {
-        doSomething();
-    }
-
-    // Bad: Extra space around argument
-    for (i = 0; i < count; i++) {
-        process( i );
-    }
-
-## Object Literals
-
-Object literals should have the following format:
-
-* The opening brace should be on the same line as the containing statement.
-* Each property-value pair should be indented one level with the first property appearing on the next line after the opening brace.
-* Each property-value pair should have an unquoted property name, followed by a colon (no space preceding it), followed by the value.
-* If the value is a function, it should wrap under the property name and should have a blank line both before and after the function.
-* Additional empty lines may be inserted to group related properties or otherwise improve readability.
-* The closing brace should be on a separate line.
-
-Examples:
-
-    // Good
-    var object = {
-
-        key1: value1,
-        key2: value2,
-
-        func: function() {
-            // do something
-        },
-
-        key3: value3
-    };
-
-    // Bad: Improper indentation
-    var object = {
-                    key1: value1,
-                    key2: value2
-                };
-
-    // Bad: Missing blank lines around function
-    var object = {
-
-        key1: value1,
-        key2: value2,
-        func: function() {
-            // do something
-        },
-        key3: value3
-    };
-
-When an object literal is passed to a function, the opening brace should be on the same line as if the value is a variable. All other formatting rules from above still apply.
-
-    // Good
-    doSomething({
-        key1: value1,
-        key2: value2
-    });
-
-    // Bad: All on one line
-    doSomething({ key1: value1, key2: value2 });
-
-## Comments
-
-Make frequent use of comments to aid others in understanding your code. Use comments when:
-
-* Code is difficult to understand.
-* The code might be mistaken for an error.
-* Browser-specific code is necessary but not obvious.
-* Documentation generation is necessary for an object, method, or property (use appropriate documentation comments).
-
-### Single-Line Comments
-
-Single-line comments should be used to document one line of code or a group of related lines of code. A single-line comment may be used in three ways:
-
-1. On a separate line, describing the code beneath it.
-1. At the end of a line, describing the code before it.
-1. On multiple lines, to comment out sections of code.
-
-When on a separate line, a single-line comment should be at the same indentation level as the code it describes and be preceded by a single line. Never use multiple single-line comments on consecutive lines, use a multi-line comment instead.
-
-    // Good
-    if (condition){
-
-        // if you made it here, then all security checks passed
-        allowed();
-    }
-
-    // Bad: No empty line preceding comment
-    if (condition){
-        // if you made it here, then all security checks passed
-        allowed();
-    }
-
-    // Bad: Wrong indentation
-    if (condition){
-
-    // if you made it here, then all security checks passed
-        allowed();
-    }
-
-    // Bad: This should be a multi-line comment
-    // This next piece of code is quite difficult, so let me explain.
-    // What you want to do is determine if the condition is true
-    // and only then allow the user in. The condition is calculated
-    // from several different functions and may change during the
-    // lifetime of the session.
-    if (condition){
-        // if you made it here, then all security checks passed
-        allowed();
-    }
-
-For single-line comments at the end of a line, ensure there is at least one indentation level between the end of the code and the beginning of the comment:
-
-    // Good
-    var result = something + somethingElse;    // somethingElse will never be null
-
-    // Bad: Not enough space between code and comment
-    var result = something + somethingElse;// somethingElse will never be null
-
-The only acceptable time to have multiple single-line comments on successive lines is to comment out large sections of code. Multi-line comments should not be used for this purpose.
-
-    // Good
-    // if (condition){
-    //    doSomething();
-    //    thenDoSomethingElse();
-    // }
-
-### Multi-Line Comments
-
-Multi-line comments should be used to document code that requires more explanation. Each multi-line comment should have at least three lines:
-
-1. The first line contains only the `/*` comment opening. No further text is allowed on this line.
-1. The next line(s) have a `*` aligned with the `*` in the first line. Text is allowed on these lines.
-1. The last line has the `*/` comment opening aligned with the preceding lines. No other text is allowed on this line.
-
-The first line of multi-comments should be indented to the same level as the code it describes. Each subsequent line should have the same indentation plus one space (for proper alignment of the `*` characters). Each multi-line comment should be preceded by one empty line.
-
-    // Good
-    if (condition){
-
-        /*
-         * if you made it here,
-         * then all security checks passed
-         */
-        allowed();
-    }
-
-    // Bad: No empty line preceding comment
-    if (condition){
-        /*
-         * if you made it here,
-         * then all security checks passed
-         */
-        allowed();
-    }
-
-    // Bad: Missing a space after asterisk
-    if (condition){
-
-        /*
-         *if you made it here,
-         *then all security checks passed
-         */
-        allowed();
-    }
-
-    // Bad: Wrong indentation
-    if (condition){
-
-    /*
-     * if you made it here,
-     * then all security checks passed
-     */
-        allowed();
-    }
-
-    // Bad: Don't use multi-line comments for trailing comments
-    var result = something + somethingElse;    /*somethingElse will never be null*/
-
-### Comment Annotations
-
-Comments may be used to annotate pieces of code with additional information. These annotations take the form of a single word followed by a colon. The acceptable annotations are:
-
-* `TODO` - indicates that the code is not yet complete. Information about the next steps should be included.
-* `HACK` - indicates that the code is using a shortcut. Information about why the hack is being used should be included. This may also indicate that it would be nice to come up with a better way to solve the problem.
-* `XXX` - indicates that the code is problematic and should be fixed as soon as possible.
-* `FIXME` - indicates that the code is problematic and should be fixed soon. Less important than `XXX`.
-* `REVIEW` - indicates that the code needs to be reviewed for potential changes.
-
-These annotations may be used with either single-line or multi-line comments and should follow the same formatting rules as the general comment type. Examples:
-
-    // Good
-    // TODO: I'd like to find a way to make this faster
-    doSomething();
-
-    // Good
-    /*
-     * HACK: Have to do this for IE. I plan on revisiting in
-     * the future when I have more time. This probably should
-     * get replaced before v1.2.
-     */
-    if (document.all) {
-        doSomething();
-    }
-
-    // Good
-    // REVIEW: Is there a better way to do this?
-    if (document.all) {
-        doSomething();
-    }
-
-    // Bad: Annotation spacing is incorrect
-    // TODO : I'd like to find a way to make this faster
-    doSomething();
-
-    // Bad: Comment should be at the same indentation as code
-        // REVIEW: Is there a better way to do this?
-    if (document.all) {
-        doSomething();
-    }
-
-
-## Variable Declarations
-
-All variables should be declared before they are used. Variable declarations should take place at the beginning of a function using a single `var` statement with one variable per line. All lines after the first should be indented one level so the variable names line up. Variables should be initialized when declared if applicable and the equals operator should be at a consistent indentation level. Initialized variables should come first followed by uninitialized variables.
-
-    // Good
-    var count   = 10,
-        name    = "Nicholas",
-        found   = false,
-        empty;
-
-    // Bad: Improper initialization alignment
-    var count = 10,
-        name = "Nicholas",
-        found= false,
-        empty;
-
-    // Bad: Incorrect indentation
-    var count   = 10,
-    name    = "Nicholas",
-    found   = false,
-    empty;
-
-    // Bad: Multiple declarations on one line
-    var count   = 10, name = "Nicholas",
-        found   = false, empty;
-
-    // Bad: Uninitialized variables first
-    var empty,
-        count   = 10,
-        name    = "Nicholas",
-        found   = false;
-
-    // Bad: Multiple var statements
-    var count   = 10,
-        name    = "Nicholas";
-
-    var found   = false,
-        empty;
-
-Always declare variables. Implied globals should not be used.
-
-## Function Declarations
-
-Functions should be declared before they are used. When a function is not a method (not attached to an object) it should be defined using function declaration format (not function expression format nor using the `Function` constructor). There should be no space between the function name and the opening parentheses. There should be one space between the closing parentheses and the right brace. The right brace should be on the same line as the `function` keyword. There should be no space after the opening parentheses or before the closing parentheses. Named arguments should have a space after the comma but not before it. The function body should be indented one level.
-
-    // Good
-    function doSomething(arg1, arg2) {
-        return arg1 + arg2;
-    }
-
-    // Bad: Improper spacing of first line
-    function doSomething (arg1, arg2){
-        return arg1 + arg2;
-    }
-
-    // Bad: Function expression
-    var doSomething = function(arg1, arg2) {
-        return arg1 + arg2;
-    };
-
-    // Bad: Left brace on wrong line
-    function doSomething(arg1, arg2)
-    {
-        return arg1 + arg2;
-    }
-
-    // Bad: Using Function constructor
-    var doSomething = new Function("arg1", "arg2", "return arg1 + arg2");
-
-Functions declared inside of other functions should be declared immediately after the `var` statement.
-
-    // Good
-    function outer() {
-
-        var count   = 10,
-            name    = "Nicholas",
-            found   = false,
-            empty;
-
-        function inner() {
-            // code
-        }
-
-        // code that uses inner()
-    }
-
-    // Bad: Inner function declared before variables
-    function outer() {
-
-        function inner() {
-            // code
-        }
-
-        var count   = 10,
-            name    = "Nicholas",
-            found   = false,
-            empty;
-
-        // code that uses inner()
-    }
-
-Anonymous functions may be used for assignment of object methods or as arguments to other functions. There should be no space between the `function` keyword and the opening parentheses.
-
-    // Good
-    object.method = function() {
-        // code
-    };
-
-    // Bad: Incorrect spacing
-    object.method = function () {
-        // code
-    };
-
-Immediately-invoked functions should surround the entire function call with parentheses.
-
-    // Good
-    var value = (function() {
-
-        // function body
-
-        return {
-            message: "Hi"
-        }
-    }());
-
-    // Bad: No parentheses around function call
-    var value = function() {
-
-        // function body
-
-        return {
-            message: "Hi"
-        }
-    }();
-
-    // Bad: Improper parentheses placement
-    var value = (function() {
-
-        // function body
-
-        return {
-            message: "Hi"
-        }
-    })();
-
-## Naming
-
-Care should be taken to name variables and functions properly. Names should be limited to alphanumeric characters and, in some cases, the underscore character. Do not use the dollar sign (`$`) or back slash (`\`) characters in any names.
-
-Variable names should be formatted in camel case with the first letter being lowercase and the first letter of each subsequent word being uppercase. The first word of a variable name should be a noun (not a verb) to avoid confusion with functions. Do not use underscore for variable names.
-
-    // Good
-    var accountNumber = "8401-1";
-
-    // Bad: Begins with uppercase letter
-    var AccountNumber = "8401-1";
-
-    // Bad: Begins with verb
-    var getAccountNumber = "8401-1";
-
-    // Bad: Uses underscore
-    var account_number = "8401-1";
-
-Function names should also be formatted using camel case. The first word of a function name should be a verb (not a noun) to avoid confusion with variables. Do not use underscore for function names.
-
-    // Good
-    function doSomething() {
-        // code
-    }
-
-    // Bad: Begins with uppercase letter
-    function DoSomething() {
-        // code
-    }
-
-    // Bad: Begins with noun
-    function car() {
-        // code
-    }
-
-    // Bad: Uses underscores
-    function do_something() {
-        // code
-    }
-
-Constructor functions, those functions used with the `new` operator to create new objects, should be formatted in camel case but must begin with an uppercase letter. Constructor function names should begin with a non-verb because `new` is the action of creating an object instance.
-
-    // Good
-    function MyObject() {
-        // code
-    }
-
-    // Bad: Begins with lowercase letter
-    function myObject() {
-        // code
-    }
-
-    // Bad: Uses underscores
-    function My_Object() {
-        // code
-    }
-
-    // Bad: Begins with verb
-    function getMyObject() {
-        // code
-    }
-
-Variables that act as constants (values that won't be changed) should be formatted using all uppercase letters with words separated by a single underscore.
-
-    // Good
-    var TOTAL_COUNT = 10;
-
-    // Bad: Camel case
-    var totalCount = 10;
-
-    // Bad: Mixed case
-    var total_COUNT = 10;
-
-Object properties follow the same naming conventions as variables. Object methods follow the same naming conventions as functions. If a property or method is meant to be private, then it should be prefixed with an underscore character.
-
-    // Good
-    var object = {
-        _count: 10,
-
-        _getCount: function () {
-            return this._count;
-        }
-    };
-
-## Strict Mode
-
-Strict mode should be used in all modules, specified below the file overview comment and above everything else:
-
-    // Bad: Strict mode in functions
-    function doSomething() {
-        "use strict";
-
-        // code
-    }
-
-    // Bad: Strict mode in global scope and redundant strict mode directive in function
-    "use strict";       // This one is good
-
-    function doSomething() {
-        "use strict";   // This one is bad
-
-        // code
-    }
-
-    // Good: Global strict mode
-    "use strict";
-
-    function doSomething() {
-        // no "use strict" here
-
-        // code
-    }
-
-## Assignments
-
-When assigning a value to a variable, use parentheses around a right-side expression that contains a comparison.
-
-    // Good
-    var flag = (i < count);
-
-    // Bad: Missing parentheses
-    var flag = i < count;
-
-## Equality Operators
-
-Use `===` and `!==` instead of `==` and `!=`. This avoids type coercion errors.
-
-    // Good
-    var same = (a === b);
-
-    // Bad: Using ==
-    var same = (a == b);
-
-## Ternary Operator
-
-The ternary operator should be used only for assigning values conditionally and never as a shortcut for an `if` statement.
-
-    // Good
-    var value = condition ? value1 : value2;
-
-    // Bad: no assignment, should be an if statement
-    condition ? doSomething() : doSomethingElse();
-
-## Statements
-
-### Simple Statements
-
-Each line should contain at most one statement. All simple statements should end with a semicolon (`;`).
-
-    // Good
-    count++;
-    a = b;
-
-    // Bad: Multiple statements on one line
-    count++; a = b;
-
-### return Statement
-
-A return statement with a value should not use parentheses unless they make the return value more obvious in some way. Example:
-
-    return;
-
-    return collection.size();
-
-    return (size > 0 ? size : defaultSize);
-
-### Compound Statements
-
-Compound statements are lists of statements enclosed inside of braces.
-
-* The enclosed statements should be indented one more level than the compound statement.
-* The opening brace should be at the end of the line that begins the compound statement; the closing brace should begin a line and be indented to the beginning of the compound statement.
-* Braces are used around all statements, even single statements, when they are part of a control structure, such as a `if` or `for` statement. This makes it easier to add statements without accidentally introducing bugs due to forgetting to add braces.
-* The statement beginning keyword, such as `if`, should be followed by one space and the opening brace should be preceded by a space.
-
-### if Statement
-
-The `if` class of statements should have the following form:
-
-    if (condition) {
-        statements
-    }
-
-    if (condition) {
-        statements
-    } else {
-        statements
-    }
-
-    if (condition) {
-        statements
-    } else if (condition) {
-        statements
-    } else {
-        statements
-    }
-
-It is never permissible to omit the braces in any part of an `if` statement.
-
-    // Good
-    if (condition) {
-        doSomething();
-    }
-
-    // Bad: Improper spacing
-    if(condition){
-        doSomething();
-    }
-
-    // Bad: Missing braces
-    if (condition)
-        doSomething();
-
-    // Bad: All on one line
-    if (condition) { doSomething(); }
-
-    // Bad: All on one line without braces
-    if (condition) doSomething();
-
-### for Statement
-
-The `for` class of statements should have the following form:
-
-    for (initialization; condition; update) {
-        statements
-    }
-
-    for (variable in object) {
-        statements
-    }
-
-Variables should not be declared in the initialization section of a `for` statement.
-
-    // Good
-    var i,
-        len;
-
-    for (i=0, len=10; i < len; i++) {
-        // code
-    }
-
-    // Bad: Variables declared during initialization
-    for (var i=0, len=10; i < len; i++) {
-        // code
-    }
-
-    // Bad: Variables declared during initialization
-    for (var prop in object) {
-        // code
-    }
-
-When using a `for-in` statement, double-check whether or not you need to use `hasOwnProperty()` to filter out object members.
-
-### while Statement
-
-The `while` class of statements should have the following form:
-
-    while (condition) {
-        statements
-    }
-
-### do Statement
-
-The `do` class of statements should have the following form:
-
-    do {
-        statements
-    } while (condition);
-
-Note the use of a semicolon as the final part of this statement. There should be a space before and after the `while` keyword.
-
-### switch Statement
-
-The `switch` class of statements should have the following form:
-
-    switch (expression) {
-        case expression:
-            statements
-
-        default:
-            statements
-    }
-
-Each `case` is indented one level under the `switch`. Each `case` after the first, including `default`, should be preceded by a single empty line.
-
-Each group of statements (except the default) should end with `break`, `return`, `throw`, or a comment indicating fall through.
-
-    // Good
-    switch (value) {
-        case 1:
-            /* falls through */
-
-        case 2:
-            doSomething();
-            break;
-
-        case 3:
-            return true;
-
-        default:
-            throw new Error("This shouldn't happen.);
-    }
-
-If a `switch` doesn't have a `default` case, then it should be indicated with a comment.
-
-    // Good
-    switch (value) {
-        case 1:
-            /*falls through*/
-
-        case 2:
-            doSomething();
-            break;
-
-        case 3:
-            return true;
-
-        // no default
-    }
-
-### try Statement
-
-The `try` class of statements should have the following form:
-
-    try {
-        statements
-    } catch (variable) {
-        statements
-    }
-
-    try {
-        statements
-    } catch (variable) {
-        statements
-    } finally {
-        statements
-    }
-
-## Whitespace
-
-Blank lines improve readability by setting off sections of code that are logically related.
-
-Two blank lines should always be used in the following circumstances:
-
-* Between sections of a source file
-* Between class and interface definitions
-
-One blank line should always be used in the following circumstances:
-
-* Between methods
-* Between the local variables in a method and its first statement
-* Before a multi-line or single-line comment
-* Between logical sections inside a method to improve readability
-
-Blank spaces should be used in the following circumstances:
-
-* A keyword followed by a parenthesis should be separated by a space.
-* A blank space should appear after commas in argument lists.
-* All binary operators except dot (`.`) should be separated from their operands by spaces. Blank spaces should never separate unary operators such as unary minus, increment (`++`), and decrement (`--`) from their operands.
-* The expressions in a `for` statement should be separated by blank spaces. Blank spaces should only be used after semicolons, not before.
-
-## Things to Avoid
-
-* Never use the primitive wrapper types, such as `String`, to create new objects.
-* Never use `eval()`.
-* Never use the `with` statement. This statement isn't available in strict mode and likely won't be available in future ECMAScript editions.
+The rationales for the specific rules in use can be found by looking to the
+project documentation for any given rule. If the rule is one of our own, see
+our own [rule documentation](https://eslint.org/docs/rules/) and otherwise, see
+the documentation of the plugin in which the rule can be found.
index cd3fc29365adc18ce7030b3b8a47e036cad9ff5a..f22eb8f3497499b404c7f5da520de9e85130356c 100644 (file)
@@ -27,6 +27,7 @@ This has references of both the initial segment and the final segments of a code
 `CodePath` has the following properties:
 
 * `id` (`string`) - A unique string. Respective rules can use `id` to save additional information for each code path.
+* `origin` (`string`) - The reason that the code path was started. May be `"program"`, `"function"`, `"class-field-initializer"`, or `"class-static-block"`.
 * `initialSegment` (`CodePathSegment`) - The initial segment of this code path.
 * `finalSegments` (`CodePathSegment[]`) - The final segments which includes both returned and thrown.
 * `returnedSegments` (`CodePathSegment[]`) - The final segments which includes only returned.
@@ -225,9 +226,9 @@ module.exports = function(context) {
 ```
 
 See Also:
-[no-unreachable](https://github.com/eslint/eslint/blob/master/lib/rules/no-unreachable.js),
-[no-fallthrough](https://github.com/eslint/eslint/blob/master/lib/rules/no-fallthrough.js),
-[consistent-return](https://github.com/eslint/eslint/blob/master/lib/rules/consistent-return.js)
+[no-unreachable](https://github.com/eslint/eslint/blob/HEAD/lib/rules/no-unreachable.js),
+[no-fallthrough](https://github.com/eslint/eslint/blob/HEAD/lib/rules/no-fallthrough.js),
+[consistent-return](https://github.com/eslint/eslint/blob/HEAD/lib/rules/consistent-return.js)
 
 ### To check state of a code path
 
@@ -323,8 +324,8 @@ module.exports = function(context) {
 ```
 
 See Also:
-[constructor-super](https://github.com/eslint/eslint/blob/master/lib/rules/constructor-super.js),
-[no-this-before-super](https://github.com/eslint/eslint/blob/master/lib/rules/no-this-before-super.js)
+[constructor-super](https://github.com/eslint/eslint/blob/HEAD/lib/rules/constructor-super.js),
+[no-this-before-super](https://github.com/eslint/eslint/blob/HEAD/lib/rules/no-this-before-super.js)
 
 ## Code Path Examples
 
index 1c84b2e1f73b969e02b74a30d2b3dbc9ea45ebca..2b6fd7486f6858264372c343f28408d573c86813 100644 (file)
@@ -1,549 +1 @@
-# Code Path Analysis Details
-
-ESLint's rules can use code paths.
-The code path is execution routes of programs.
-It forks/joins at such as `if` statements.
-
-```js
-if (a && b) {
-    foo();
-}
-bar();
-```
-
-![Code Path Example](./helo.svg)
-
-## Objects
-
-Program is expressed with several code paths.
-A code path is expressed with objects of two kinds: `CodePath` and `CodePathSegment`.
-
-### `CodePath`
-
-`CodePath` expresses whole of one code path.
-This object exists for each function and the global.
-This has references of both the initial segment and the final segments of a code path.
-
-`CodePath` has the following properties:
-
-* `id` (`string`) - A unique string. Respective rules can use `id` to save additional information for each code path.
-* `initialSegment` (`CodePathSegment`) - The initial segment of this code path.
-* `finalSegments` (`CodePathSegment[]`) - The final segments which includes both returned and thrown.
-* `returnedSegments` (`CodePathSegment[]`) - The final segments which includes only returned.
-* `thrownSegments` (`CodePathSegment[]`) - The final segments which includes only thrown.
-* `currentSegments` (`CodePathSegment[]`) - Segments of the current position.
-* `upper` (`CodePath|null`) - The code path of the upper function/global scope.
-* `childCodePaths` (`CodePath[]`) - Code paths of functions this code path contains.
-
-### `CodePathSegment`
-
-`CodePathSegment` is a part of a code path.
-A code path is expressed with plural `CodePathSegment` objects, it's similar to doubly linked list.
-Difference from doubly linked list is what there are forking and merging (the next/prev are plural).
-
-`CodePathSegment` has the following properties:
-
-* `id` (`string`) - A unique string. Respective rules can use `id` to save additional information for each segment.
-* `nextSegments` (`CodePathSegment[]`) - The next segments. If forking, there are two or more. If final, there is nothing.
-* `prevSegments` (`CodePathSegment[]`) - The previous segments. If merging, there are two or more. If initial, there is nothing.
-* `reachable` (`boolean`) - A flag which shows whether or not it's reachable. This becomes `false` when preceded by `return`, `throw`, `break`, or `continue`.
-
-## Events
-
-There are five events related to code paths, and you can define event handlers in rules.
-
-```js
-module.exports = function(context) {
-    return {
-        /**
-         * This is called at the start of analyzing a code path.
-         * In this time, the code path object has only the initial segment.
-         *
-         * @param {CodePath} codePath - The new code path.
-         * @param {ASTNode} node - The current node.
-         * @returns {void}
-         */
-        "onCodePathStart": function(codePath, node) {
-            // do something with codePath
-        },
-
-        /**
-         * This is called at the end of analyzing a code path.
-         * In this time, the code path object is complete.
-         *
-         * @param {CodePath} codePath - The completed code path.
-         * @param {ASTNode} node - The current node.
-         * @returns {void}
-         */
-        "onCodePathEnd": function(codePath, node) {
-            // do something with codePath
-        },
-
-        /**
-         * This is called when a code path segment was created.
-         * It meant the code path is forked or merged.
-         * In this time, the segment has the previous segments and has been
-         * judged reachable or not.
-         *
-         * @param {CodePathSegment} segment - The new code path segment.
-         * @param {ASTNode} node - The current node.
-         * @returns {void}
-         */
-        "onCodePathSegmentStart": function(segment, node) {
-            // do something with segment
-        },
-
-        /**
-         * This is called when a code path segment was leaved.
-         * In this time, the segment does not have the next segments yet.
-         *
-         * @param {CodePathSegment} segment - The leaved code path segment.
-         * @param {ASTNode} node - The current node.
-         * @returns {void}
-         */
-        "onCodePathSegmentEnd": function(segment, node) {
-            // do something with segment
-        },
-
-        /**
-         * This is called when a code path segment was looped.
-         * Usually segments have each previous segments when created,
-         * but when looped, a segment is added as a new previous segment into a
-         * existing segment.
-         *
-         * @param {CodePathSegment} fromSegment - A code path segment of source.
-         * @param {CodePathSegment} toSegment - A code path segment of destination.
-         * @param {ASTNode} node - The current node.
-         * @returns {void}
-         */
-        "onCodePathSegmentLoop": function(fromSegment, toSegment, node) {
-            // do something with segment
-        }
-    };
-};
-```
-
-### About `onCodePathSegmentLoop`
-
-This event is always fired when the next segment has existed already.
-That timing is the end of loops mainly.
-
-For Example 1:
-
-```js
-while (a) {
-    a = foo();
-}
-bar();
-```
-
-1. First, the analysis advances to the end of loop.
-
-   ![Loop Event's Example 1](./loop-event-example-while-1.svg)
-
-2. Second, it creates the looping path.
-   At this time, the next segment has existed already, so the `onCodePathSegmentStart` event is not fired.
-   It fires `onCodePathSegmentLoop` instead.
-
-   ![Loop Event's Example 2](./loop-event-example-while-2.svg)
-
-3. Last, it advances to the end.
-
-   ![Loop Event's Example 3](./loop-event-example-while-3.svg)
-
-For example 2:
-
-```js
-for (let i = 0; i < 10; ++i) {
-    foo(i);
-}
-bar();
-```
-
-1. `for` statements are more complex.
-   First, the analysis advances to `ForStatement.update`.
-   The `update` segment is hovered at first.
-
-   ![Loop Event's Example 1](./loop-event-example-for-1.svg)
-
-2. Second, it advances to `ForStatement.body`.
-   Of course the `body` segment is preceded by the `test` segment.
-   It keeps the `update` segment hovering.
-
-   ![Loop Event's Example 2](./loop-event-example-for-2.svg)
-
-3. Third, it creates the looping path from `body` segment to `update` segment.
-   At this time, the next segment has existed already, so the `onCodePathSegmentStart` event is not fired.
-   It fires `onCodePathSegmentLoop` instead.
-
-   ![Loop Event's Example 3](./loop-event-example-for-3.svg)
-
-4. Fourth, also it creates the looping path from `update` segment to `test` segment.
-   At this time, the next segment has existed already, so the `onCodePathSegmentStart` event is not fired.
-   It fires `onCodePathSegmentLoop` instead.
-
-   ![Loop Event's Example 4](./loop-event-example-for-4.svg)
-
-5. Last, it advances to the end.
-
-   ![Loop Event's Example 5](./loop-event-example-for-5.svg)
-
-
-
-## Usage Examples
-
-### To check whether or not this is reachable
-
-```js
-function isReachable(segment) {
-    return segment.reachable;
-}
-
-module.exports = function(context) {
-    var codePathStack = [];
-
-    return {
-        // Stores CodePath objects.
-        "onCodePathStart": function(codePath) {
-            codePathStack.push(codePath);
-        },
-        "onCodePathEnd": function(codePath) {
-            codePathStack.pop();
-        },
-
-        // Checks reachable or not.
-        "ExpressionStatement": function(node) {
-            var codePath = codePathStack[codePathStack.length - 1];
-
-            // Checks the current code path segments.
-            if (!codePath.currentSegments.some(isReachable)) {
-                context.report({message: "Unreachable!", node: node});
-            }
-        }
-    };
-};
-```
-
-See Also:
-[no-unreachable](https://github.com/eslint/eslint/blob/master/lib/rules/no-unreachable.js),
-[no-fallthrough](https://github.com/eslint/eslint/blob/master/lib/rules/no-fallthrough.js),
-[consistent-return](https://github.com/eslint/eslint/blob/master/lib/rules/consistent-return.js)
-
-### To check state of a code path
-
-This example is checking whether or not the parameter `cb` is called in every path.
-Instances of `CodePath` and `CodePathSegment` are shared to every rule.
-So a rule must not modify those instances.
-Please use a map of information instead.
-
-```js
-function hasCb(node, context) {
-    if (node.type.indexOf("Function") !== -1) {
-        return context.getDeclaredVariables(node).some(function(v) {
-            return v.type === "Parameter" && v.name === "cb";
-        });
-    }
-    return false;
-}
-
-function isCbCalled(info) {
-    return info.cbCalled;
-}
-
-module.exports = function(context) {
-    var funcInfoStack = [];
-    var segmentInfoMap = Object.create(null);
-
-    return {
-        // Checks `cb`.
-        "onCodePathStart": function(codePath, node) {
-            funcInfoStack.push({
-                codePath: codePath,
-                hasCb: hasCb(node, context)
-            });
-        },
-        "onCodePathEnd": function(codePath, node) {
-            funcInfoStack.pop();
-
-            // Checks `cb` was called in every paths.
-            var cbCalled = codePath.finalSegments.every(function(segment) {
-                var info = segmentInfoMap[segment.id];
-                return info.cbCalled;
-            });
-
-            if (!cbCalled) {
-                context.report({
-                    message: "`cb` should be called in every path.",
-                    node: node
-                });
-            }
-        },
-
-        // Manages state of code paths.
-        "onCodePathSegmentStart": function(segment) {
-            var funcInfo = funcInfoStack[funcInfoStack - 1];
-
-            // Ignores if `cb` doesn't exist.
-            if (!funcInfo.hasCb) {
-                return;
-            }
-
-            // Initialize state of this path.
-            var info = segmentInfoMap[segment.id] = {
-                cbCalled: false
-            };
-
-            // If there are the previous paths, merges state.
-            // Checks `cb` was called in every previous path.
-            if (segment.prevSegments.length > 0) {
-                info.cbCalled = segment.prevSegments.every(isCbCalled);
-            }
-        },
-
-        // Checks reachable or not.
-        "CallExpression": function(node) {
-            var funcInfo = funcInfoStack[funcInfoStack - 1];
-
-            // Ignores if `cb` doesn't exist.
-            if (!funcInfo.hasCb) {
-                return;
-            }
-
-            // Sets marks that `cb` was called.
-            var callee = node.callee;
-            if (callee.type === "Identifier" && callee.name === "cb") {
-                funcInfo.codePath.currentSegments.forEach(function(segment) {
-                    var info = segmentInfoMap[segment.id];
-                    info.cbCalled = true;
-                });
-            }
-        }
-    };
-};
-```
-
-See Also:
-[constructor-super](https://github.com/eslint/eslint/blob/master/lib/rules/constructor-super.js),
-[no-this-before-super](https://github.com/eslint/eslint/blob/master/lib/rules/no-this-before-super.js)
-
-## Code Path Examples
-
-### Hello World
-
-```js
-console.log("Hello world!");
-```
-
-![Hello World](./example-hello-world.svg)
-
-### `IfStatement`
-
-```js
-if (a) {
-    foo();
-} else {
-    bar();
-}
-```
-
-![`IfStatement`](./example-ifstatement.svg)
-
-### `IfStatement` (chain)
-
-```js
-if (a) {
-    foo();
-} else if (b) {
-    bar();
-} else if (c) {
-    hoge();
-}
-```
-
-![`IfStatement` (chain)](./example-ifstatement-chain.svg)
-
-### `SwitchStatement`
-
-```js
-switch (a) {
-    case 0:
-        foo();
-        break;
-
-    case 1:
-    case 2:
-        bar();
-        // fallthrough
-
-    case 3:
-        hoge();
-        break;
-}
-```
-
-![`SwitchStatement`](./example-switchstatement.svg)
-
-### `SwitchStatement` (has `default`)
-
-```js
-switch (a) {
-    case 0:
-        foo();
-        break;
-
-    case 1:
-    case 2:
-        bar();
-        // fallthrough
-
-    case 3:
-        hoge();
-        break;
-
-    default:
-        fuga();
-        break;
-}
-```
-
-![`SwitchStatement` (has `default`)](./example-switchstatement-has-default.svg)
-
-### `TryStatement` (try-catch)
-
-```js
-try {
-    foo();
-    if (a) {
-        throw new Error();
-    }
-    bar();
-} catch (err) {
-    hoge(err);
-}
-last();
-```
-
-It creates the paths from `try` block to `catch` block at:
-
-* `throw` statements.
-* The first throwable node (e.g. a function call) in the `try` block.
-* The end of the `try` block.
-
-![`TryStatement` (try-catch)](./example-trystatement-try-catch.svg)
-
-### `TryStatement` (try-finally)
-
-```js
-try {
-    foo();
-    bar();
-} finally {
-    fuga();
-}
-last();
-```
-
-If there is not `catch` block, `finally` block has two current segments.
-At this time, `CodePath.currentSegments.length` is `2`.
-One is the normal path, and another is the leaving path (`throw` or `return`).
-
-![`TryStatement` (try-finally)](./example-trystatement-try-finally.svg)
-
-### `TryStatement` (try-catch-finally)
-
-```js
-try {
-    foo();
-    bar();
-} catch (err) {
-    hoge(err);
-} finally {
-    fuga();
-}
-last();
-```
-
-![`TryStatement` (try-catch-finally)](./example-trystatement-try-catch-finally.svg)
-
-### `WhileStatement`
-
-```js
-while (a) {
-    foo();
-    if (b) {
-        continue;
-    }
-    bar();
-}
-```
-
-![`WhileStatement`](./example-whilestatement.svg)
-
-### `DoWhileStatement`
-
-```js
-do {
-    foo();
-    bar();
-} while (a);
-```
-
-![`DoWhileStatement`](./example-dowhilestatement.svg)
-
-### `ForStatement`
-
-```js
-for (let i = 0; i < 10; ++i) {
-    foo();
-    if (b) {
-        break;
-    }
-    bar();
-}
-```
-
-![`ForStatement`](./example-forstatement.svg)
-
-### `ForStatement` (for ever)
-
-```js
-for (;;) {
-    foo();
-}
-bar();
-```
-
-![`ForStatement` (for ever)](./example-forstatement-for-ever.svg)
-
-### `ForInStatement`
-
-```js
-for (let key in obj) {
-    foo(key);
-}
-```
-
-![`ForInStatement`](./example-forinstatement.svg)
-
-### When there is a function
-
-```js
-function foo(a) {
-    if (a) {
-        return;
-    }
-    bar();
-}
-
-foo(false);
-```
-
-It creates two code paths.
-
-* The global's
-
-  ![When there is a function](./example-when-there-is-a-function-g.svg)
-
-* The function's
-
-  ![When there is a function](./example-when-there-is-a-function-f.svg)
+[Code Path Analysis Details](../code-path-analysis.md)
index e341083febced0a3db9e54ab05210e2a5140b97d..25a889466e5bf88e7e703d80fdcc75b97f514625 100644 (file)
@@ -10,7 +10,7 @@ ESLint welcomes contributions from everyone and adheres to the [OpenJS Foundatio
 
 ## [Signing the CLA](https://openjsf.org/about/the-openjs-foundation-cla/)
 
-In order to submit code or documentation to an ESLint project, you will need to electronically sign our [Contributor License Agreement](https://cla.js.foundation/eslint/eslint). The CLA is you giving us permission to use your contribution.
+In order to submit code or documentation to an ESLint project, you will need to electronically sign our [Contributor License Agreement](https://github.com/openjs-foundation/easycla). The CLA is the commonly used Apache-style template, and is you giving us permission to use your contribution. You only need to sign the CLA once for any OpenJS Foundation projects that use EasyCLA.
 
 ## [Bug Reporting](reporting-bugs)
 
index ef4169e23d0702ca58b0c48e13ad0a7ca872e58b..dde1c31446f193204f8320f13ce0e775b2703aa0 100644 (file)
@@ -47,37 +47,42 @@ $ git add -A
 $ git commit
 ```
 
-Our commit message format is as follows:
+All ESLint projects follow [Conventional Commits](https://www.conventionalcommits.org/) for our commit messages. Here's an example commit message:
 
 ```
-Tag: Short description (fixes #1234)
+tag: Short description of what you did
 
 Longer description here if necessary
+
+Fixes #1234
 ```
 
 The first line of the commit message (the summary) must have a specific format. This format is checked by our build tools.
 
-The `Tag` is one of the following:
+The `tag` is one of the following:
 
-* `Fix` - for a bug fix.
-* `Update` - either for a backwards-compatible enhancement or for a rule change that adds reported problems.
-* `New` - implemented a new feature.
-* `Breaking` - for a backwards-incompatible enhancement or feature.
-* `Docs` - changes to documentation only.
-* `Build` - changes to build process only.
-* `Upgrade` - for a dependency upgrade.
-* `Chore` - for refactoring, adding tests, etc. (anything that isn't user-facing).
+* `fix` - for a bug fix.
+* `feat` - either for a backwards-compatible enhancement or for a rule change that adds reported problems.
+* `fix!` - for a backwards-incompatible bug fix.
+* `feat!` - for a backwards-incompatible enhancement or feature.
+* `docs` - changes to documentation only.
+* `chore` - for changes that aren't user-facing.
+* `build` - changes to build process only.
+* `refactor` - a change that doesn't affect APIs or user experience.
+* `test` - just changes to test files.
+* `ci` - changes to our CI configuration files and scripts.
+* `perf` - a code change that improves performance.
 
 Use the [labels of the issue you are working on](working-on-issues.md#issue-labels) to determine the best tag.
 
-The message summary should be a one-sentence description of the change, and it must be 72 characters in length or shorter. If the pull request addresses an issue, then the issue number should be mentioned at the end. If the commit doesn't completely fix the issue, then use `(refs #1234)` instead of `(fixes #1234)`.
+The message summary should be a one-sentence description of the change, and it must be 72 characters in length or shorter. If the pull request addresses an issue, then the issue number should be mentioned in the body of the commit message in the format `Fixes #1234`. If the commit doesn't completely fix the issue, then use `Refs #1234` instead of `Fixes #1234`.
 
 Here are some good commit message summary examples:
 
 ```
-Build: Update Travis to only test Node 0.10 (refs #734)
-Fix: Semi rule incorrectly flagging extra semicolon (fixes #840)
-Upgrade: Esprima to 1.2, switch to using comment attachment (fixes #730)
+build: Update Travis to only test Node 0.10
+fix: Semi rule incorrectly flagging extra semicolon
+chore: Upgrade Esprima to 1.2, switch to using comment attachment
 ```
 
 The commit message format is important because these messages are used to create a changelog for each release. The tag and issue number help to create more consistent and useful changelogs.
@@ -88,7 +93,7 @@ Before you send the pull request, be sure to rebase onto the upstream source. Th
 
 ```
 git fetch upstream
-git rebase upstream/master
+git rebase upstream/main
 ```
 
 ### Step 4: Run the tests<a name="step4"></a>
@@ -164,7 +169,9 @@ $ git commit
 $ git push origin issue1234
 ```
 
-When updating the code, it's usually better to add additional commits to your branch rather than amending the original commit, because reviewers can easily tell which changes were made in response to a particular review. When we merge pull requests, we will squash all the commits from your branch into a single commit on the `master` branch.
+When updating the code, it's usually better to add additional commits to your branch rather than amending the original commit, because reviewers can easily tell which changes were made in response to a particular review. When we merge pull requests, we will squash all the commits from your branch into a single commit on the `main` branch.
+
+The commit messages in subsequent commits do not need to be in any specific format because these commits do not show up in the changelog.
 
 ### Rebasing
 
@@ -172,7 +179,7 @@ If your code is out-of-date, we might ask you to rebase. That means we want you
 
 ```
 $ git fetch upstream
-$ git rebase upstream/master
+$ git rebase upstream/main
 ```
 
 You might find that there are merge conflicts when you attempt to rebase. Please [resolve the conflicts](https://help.github.com/articles/resolving-merge-conflicts-after-a-git-rebase/) and then do a forced push to your branch:
index 8433bd04967c0377f87e28c6f48ab8fa268d43da..7981c66e685e1bc8db16a209f45fa73ddecd1539 100644 (file)
@@ -81,12 +81,8 @@ Be sure to run this after making changes and before sending a pull request with
 
 #### npm run lint
 
-Runs just the JavaScript and JSON linting on the repository
+Runs just the JavaScript and JSON linting on the repository.
 
 #### npm run webpack
 
-Generates `build/eslint.js`, a version of ESLint for use in the browser
-
-#### npm run docs
-
-Generates JSDoc documentation and places it into `/jsdoc`.
+Generates `build/eslint.js`, a version of ESLint for use in the browser.
index f1e21584e326c3b9f444db7f5d12a90a9c9959b6..378b9c16b8da0be410d3695385c94f562489a37c 100644 (file)
@@ -10,6 +10,7 @@ While ESLint is designed to be run on the command line, it's possible to use ESL
     * [constructor()][eslint-constructor]
     * [lintFiles()][eslint-lintfiles]
     * [lintText()][eslint-linttext]
+    * [getRulesMetaForResults()][eslint-getrulesmetaforresults]
     * [calculateConfigForFile()][eslint-calculateconfigforfile]
     * [isPathIgnored()][eslint-ispathignored]
     * [loadFormatter()][eslint-loadformatter]
@@ -30,11 +31,8 @@ While ESLint is designed to be run on the command line, it's possible to use ESL
     * [getRules()](#lintergetrules)
     * [defineParser()](#linterdefineparser)
     * [version](#linterversionlinterversion)
-* [linter (deprecated)](#linter-1)
-* [CLIEngine (deprecated)](#cliengine)
 * [RuleTester](#ruletester)
     * [Customizing RuleTester](#customizing-ruletester)
-* [Deprecated APIs](#deprecated-apis)
 
 ---
 
@@ -147,7 +145,7 @@ The `ESLint` constructor takes an `options` object. If you omit the `options` ob
 
 * `options.fix` (`boolean | (message: LintMessage) => boolean`)<br>
   Default is `false`. If `true` is present, the [`eslint.lintFiles()`][eslint-lintfiles] and [`eslint.lintText()`][eslint-linttext] methods work in autofix mode. If a predicate function is present, the methods pass each lint message to the function, then use only the lint messages for which the function returned `true`.
-* `options.fixTypes` (`("problem" | "suggestion" | "layout")[] | null`)<br>
+* `options.fixTypes` (`("directive" | "problem" | "suggestion" | "layout")[] | null`)<br>
   Default is `null`. The types of the rules that the [`eslint.lintFiles()`][eslint-lintfiles] and [`eslint.lintText()`][eslint-linttext] methods use for autofix.
 
 ##### Cache-related
@@ -205,6 +203,25 @@ The second parameter `options` is omittable.
 * (`Promise<LintResult[]>`)<br>
   The promise that will be fulfilled with an array of [LintResult] objects. This is an array (despite there being only one lint result) in order to keep the interfaces between this and the [`eslint.lintFiles()`][eslint-lintfiles] method similar.
 
+### ◆ eslint.getRulesMetaForResults(results)
+
+```js
+const results = await eslint.lintFiles(patterns);
+const rulesMeta = eslint.getRulesMetaForResults(results);
+```
+
+This method returns an object containing meta information for each rule that triggered a lint error in the given `results`.
+
+#### Parameters
+
+* `results` (`LintResult[]`)<br>
+  An array of [LintResult] objects returned from a call to `ESLint#lintFiles()` or `ESLint#lintText()`.
+
+#### Return Value
+
+* (`Object`)<br>
+  An object whose property names are the rule IDs from the `results` and whose property values are the rule's meta information (if available).
+
 ### ◆ eslint.calculateConfigForFile(filePath)
 
 ```js
@@ -335,7 +352,9 @@ The `LintResult` value is the information of the linting result of each file. Th
 * `fixableWarningCount` (`number`)<br>
   The number of warnings that can be fixed automatically by the `fix` constructor option.
 * `errorCount` (`number`)<br>
-  The number of errors. This includes fixable errors.
+  The number of errors. This includes fixable errors and fatal errors.
+* `fatalErrorCount` (`number`)<br>
+  The number of fatal errors.
 * `warningCount` (`number`)<br>
   The number of warnings. This includes fixable warnings.
 * `output` (`string | undefined`)<br>
@@ -357,9 +376,9 @@ The `LintMessage` value is the information of each linting error. The `messages`
   `true` if this is a fatal error unrelated to a rule, like a parsing error.
 * `message` (`string`)<br>
   The error message.
-* `line` (`number`)<br>
+* `line` (`number | undefined`)<br>
   The 1-based line number of the begin point of this message.
-* `column` (`number`)<br>
+* `column` (`number | undefined`)<br>
   The 1-based column number of the begin point of this message.
 * `endLine` (`number | undefined`)<br>
   The 1-based line number of the end point of this message. This property is undefined if this message is not a range.
@@ -439,10 +458,11 @@ const codeLines = SourceCode.splitLines(code);
 
 ## Linter
 
-The `Linter` object does the actual evaluation of the JavaScript code. It doesn't do any filesystem operations, it simply parses and reports on the code. In particular, the `Linter` object does not process configuration objects or files.
+The `Linter` object does the actual evaluation of the JavaScript code. It doesn't do any filesystem operations, it simply parses and reports on the code. In particular, the `Linter` object does not process configuration objects or files. Unless you are working in the browser, you probably want to use the [ESLint class](#eslint-class) class instead.
+
 The `Linter` is a constructor, and you can create a new instance by passing in the options you want to use. The available options are:
 
-* `cwd` - Path to a directory that should be considered as the current working directory. It is accessible to rules by calling `context.getCwd()` (see [The Context Object](./working-with-rules.md#The-Context-Object)). If `cwd` is `undefined`, it will be normalized to `process.cwd()` if the global `process` object is defined (for example, in the Node.js runtime) , or `undefined` otherwise.
+* `cwd` - Path to a directory that should be considered as the current working directory. It is accessible to rules by calling `context.getCwd()` (see [The Context Object](./working-with-rules.md#the-context-object)). If `cwd` is `undefined`, it will be normalized to `process.cwd()` if the global `process` object is defined (for example, in the Node.js runtime) , or `undefined` otherwise.
 
 For example:
 
@@ -460,8 +480,8 @@ Those run on `linter2` will get `process.cwd()` if the global `process` object i
 The most important method on `Linter` is `verify()`, which initiates linting of the given text. This method accepts three arguments:
 
 * `code` - the source code to lint (a string or instance of `SourceCode`).
-* `config` - a configuration object that has been processed and normalized by CLIEngine using eslintrc files and/or other configuration arguments.
-    * **Note**: If you want to lint text and have your configuration be read and processed, use CLIEngine's [`executeOnFiles`](#cliengineexecuteonfiles) or [`executeOnText`](#cliengineexecuteontext) instead.
+* `config` - a configuration object that has been processed and normalized by `ESLint` using eslintrc files and/or other configuration arguments.
+    * **Note**: If you want to lint text and have your configuration be read and processed, use [`ESLint#lintFiles()`][eslint-lintfiles] or [`ESLint#lintText()`][eslint-linttext] instead.
 * `options` - (optional) Additional options for this run.
     * `filename` - (optional) the filename to associate with the source code.
     * `preprocess` - (optional) A function that [Processors in Plugins](/docs/developer-guide/working-with-plugins.md#processors-in-plugins) documentation describes as the `preprocess` method.
@@ -679,509 +699,6 @@ const Linter = require("eslint").Linter;
 Linter.version; // => '4.5.0'
 ```
 
-## linter
-
-The `eslint.linter` object (deprecated) is an instance of the `Linter` class as defined [above](#linter). `eslint.linter` exists for backwards compatibility, but we do not recommend using it because any mutations to it are shared among every module that uses `eslint`. Instead, please create your own instance of `eslint.Linter`.
-
-```js
-const linter = require("eslint").linter;
-
-const messages = linter.verify("var foo;", {
-    rules: {
-        semi: 2
-    }
-}, { filename: "foo.js" });
-```
-
-Note: This API is deprecated as of 4.0.0.
-
----
-
-## CLIEngine
-
-⚠️ The `CLIEngine` class has been deprecated in favor of the `ESLint` class as of v7.0.0.
-
-The primary Node.js API is `CLIEngine`, which is the underlying utility that runs the ESLint command line interface. This object will read the filesystem for configuration and file information but will not output any results. Instead, it allows you direct access to the important information so you can deal with the output yourself.
-
-You can get a reference to the `CLIEngine` by doing the following:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-```
-
-The `CLIEngine` is a constructor, and you can create a new instance by passing in the options you want to use. The available options are:
-
-* `allowInlineConfig` - Set to `false` to disable the use of configuration comments (such as `/*eslint-disable*/`). Corresponds to `--no-inline-config`.
-* `baseConfig` - Can optionally be set to a config object that has the same schema as `.eslintrc.*`. This will used as a default config, and will be merged with any configuration defined in `.eslintrc.*` files, with the `.eslintrc.*` files having precedence.
-* `cache` - Operate only on changed files (default: `false`). Corresponds to `--cache`.
-* `cacheFile` - Name of the file where the cache will be stored (default: `.eslintcache`). Corresponds to `--cache-file`. Deprecated: use `cacheLocation` instead.
-* `cacheLocation` - Name of the file or directory where the cache will be stored (default: `.eslintcache`). Corresponds to `--cache-location`.
-* `configFile` - The configuration file to use (default: null). If `useEslintrc` is true or not specified, this configuration will be merged with any configuration defined in `.eslintrc.*` files, with options in this configuration having precedence. Corresponds to `-c`.
-* `cwd` - Path to a directory that should be considered as the current working directory.
-* `envs` - An array of environments to load (default: empty array). Corresponds to `--env`. Note: This differs from `.eslintrc.*` / `baseConfig`, where instead the option is called `env` and is an object.
-* `errorOnUnmatchedPattern` - Set to `false` to prevent errors when pattern is unmatched. Corresponds to `--no-error-on-unmatched-pattern`.
-* `extensions` - An array of filename extensions that should be checked for code. The default is an array containing just `".js"`. Corresponds to `--ext`. It is only used in conjunction with directories, not with filenames, glob patterns or when using `executeOnText()`.
-* `fix` - A boolean or a function (default: `false`). If a function, it will be passed each linting message and should return a boolean indicating whether the fix should be included with the output report (errors and warnings will not be listed if fixed). Files on disk are never changed regardless of the value of `fix`. To persist changes to disk, call [`outputFixes()`](#cliengineoutputfixes).
-* `fixTypes` - An array of rule types for which fixes should be applied (default: `null`). This array acts like a filter, only allowing rules of the given types to apply fixes. Possible array values are `"problem"`, `"suggestion"`, and `"layout"`.
-* `globals` - An array of global variables to declare (default: empty array). Corresponds to `--global`, and similarly supports passing `'name:true'` to denote a writeable global. Note: This differs from `.eslintrc.*` / `baseConfig`, where `globals` is an object.
-* `ignore` - False disables use of `.eslintignore`, `ignorePath` and `ignorePattern` (default: true). Corresponds to `--no-ignore`.
-* `ignorePath` - The ignore file to use instead of `.eslintignore` (default: null). Corresponds to `--ignore-path`.
-* `ignorePattern` - Glob patterns for paths to ignore. String or array of strings.
-* `parser` - Specify the parser to be used (default: `espree`). Corresponds to `--parser`.
-* `parserOptions` - An object containing parser options (default: empty object). Corresponds to `--parser-options`.
-* `plugins` - An array of plugins to load (default: empty array). Corresponds to `--plugin`.
-* `reportUnusedDisableDirectives` - When set to `true`, adds reported errors for unused `eslint-disable` directives when no problems would be reported in the disabled area anyway (default: false). Corresponds to `--report-unused-disable-directives`.
-* `resolvePluginsRelativeTo` - Determines the folder where plugins should be resolved from. Should be used when an integration installs plugins and uses those plugins to lint code on behalf of the end user. Corresponds to `--resolve-plugins-relative-to`.
-* `rulePaths` - An array of directories to load custom rules from (default: empty array). Corresponds to `--rulesdir`.
-* `rules` - An object of rules to use (default: null). Corresponds to `--rule`.
-* `useEslintrc` - Set to false to disable use of `.eslintrc` files (default: true). Corresponds to `--no-eslintrc`.
-* `globInputPaths` - Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
-
-To programmatically set `.eslintrc.*` options not supported above (such as `extends`,
-`overrides` and `settings`), define them in a config object passed to `baseConfig` instead.
-
-For example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    baseConfig: {
-        extends: ["eslint-config-shared"],
-        settings: {
-            sharedData: "Hello"
-        }
-    },
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-```
-
-In this example, a new `CLIEngine` instance is created that extends a configuration called
-`"eslint-config-shared"`, a setting named `"sharedData"` and two environments (`"browser"`
-and `"mocha"`) are defined, loading of `.eslintrc` and `package.json` files are disabled,
-and the `semi` rule enabled as an error. You can then call methods on `cli` and these options
-will be used to perform the correct action.
-
-Note: Currently `CLIEngine` does not validate options passed to it, but may start doing so in the future.
-
-### CLIEngine#executeOnFiles()
-
-If you want to lint one or more files, use the `executeOnFiles()` method. This method accepts a single argument, which is an array of files and/or directories to traverse for files. You can pass the same values as you would using the ESLint command line interface, such as `"."` to search all JavaScript files in the current directory. Here's an example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-// lint myfile.js and all files in lib/
-const report = cli.executeOnFiles(["myfile.js", "lib/"]);
-```
-
-The return value is an object containing the results of the linting operation. Here's an example of a report object:
-
-```js
-{
-    results: [
-        {
-            filePath: "/Users/eslint/project/myfile.js",
-            messages: [{
-                ruleId: "semi",
-                severity: 2,
-                message: "Missing semicolon.",
-                line: 1,
-                column: 13,
-                nodeType: "ExpressionStatement",
-                fix: { range: [12, 12], text: ";" }
-            }, {
-                ruleId: "no-useless-escape",
-                severity: 1,
-                message: "disallow unnecessary escape characters",
-                line: 1,
-                column: 10,
-                nodeType: "ExpressionStatement",
-                suggestions: [{
-                    desc: "Remove unnecessary escape. This maintains the current functionality.",
-                    fix: { range: [9, 10], text: "" }
-                }, {
-                    desc: "Escape backslash to include it in the RegExp.",
-                    fix: { range: [9, 9], text: "\\" }
-                }]
-            }],
-            errorCount: 1,
-            warningCount: 1,
-            fixableErrorCount: 1,
-            fixableWarningCount: 0,
-            source: "\"use strict\"\n"
-        }
-    ],
-    errorCount: 1,
-    warningCount: 0,
-    fixableErrorCount: 1,
-    fixableWarningCount: 0,
-    usedDeprecatedRules: []
-}
-```
-
-You can also pass `fix: true` when instantiating the `CLIEngine` in order to have it figure out what fixes can be applied.
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    fix: true, // difference from last example
-    useEslintrc: false,
-    rules: {
-        semi: 2,
-        quotes: [2, "double"]
-    }
-});
-
-// lint myfile.js and all files in lib/
-const report = cli.executeOnFiles(["myfile.js", "lib/"]);
-```
-
-```js
-{
-    results: [
-        {
-            filePath: "/Users/eslint/project/myfile.js",
-            messages: [
-                {
-                    ruleId: "semi",
-                    severity: 2,
-                    message: "Missing semicolon.",
-                    line: 1,
-                    column: 13,
-                    nodeType: "ExpressionStatement",
-                    fix: { range: [12, 12], text: ";" }
-                },
-                {
-                    ruleId: "func-name-matching",
-                    severity: 2,
-                    message: "Function name `bar` should match variable name `foo`",
-                    line: 2,
-                    column: 5,
-                    nodeType: "VariableDeclarator"
-                }
-            ],
-            errorCount: 2,
-            warningCount: 0,
-            fixableErrorCount: 1,
-            fixableWarningCount: 0,
-            output: "\"use strict\";\nvar foo = function bar() {};\nfoo();\n"
-        }
-    ],
-    errorCount: 2,
-    warningCount: 0,
-    fixableErrorCount: 1,
-    fixableWarningCount: 0,
-    usedDeprecatedRules: []
-}
-```
-
-If the operation ends with a parsing error, you will get a single message for this file, with `fatal: true` added as an extra property.
-
-```js
-{
-    results: [
-        {
-            filePath: "./myfile.js",
-            messages: [
-                {
-                    ruleId: null,
-                    fatal: true,
-                    severity: 2,
-                    message: "Parsing error: Unexpected token foo",
-                    line: 1,
-                    column: 10
-                }
-            ],
-            errorCount: 1,
-            warningCount: 0,
-            fixableErrorCount: 0,
-            fixableWarningCount: 0,
-            source: "function foo() {}"
-        }
-    ],
-    errorCount: 1,
-    warningCount: 0,
-    fixableErrorCount: 0,
-    fixableWarningCount: 0,
-    usedDeprecatedRules: []
-}
-```
-
-The top-level report object has a `results` array containing all linting results for files that had warnings or errors (any files that did not produce a warning or error are omitted). Each file result includes:
-
-* `filePath` - Path to the given file.
-* `messages` - Array containing the result of calling `linter.verify()` on the given file.
-* `errorCount` and `warningCount` - The exact number of errors and warnings respectively on the given file.
-* `source` - The source code for the given file. This property is omitted if this file has no errors/warnings or if the `output` property is present.
-* `output` - The source code for the given file with as many fixes applied as possible, so you can use that to rewrite the files if necessary. This property is omitted if no fix is available.
-
-The top-level report object also has `errorCount` and `warningCount` which give the exact number of errors and warnings respectively on all the files. Additionally, `usedDeprecatedRules` signals any deprecated rules used and their replacement (if available). Specifically, it is an array of objects with properties like so:
-
-* `ruleId` - The name of the rule (e.g. `indent-legacy`).
-* `replacedBy` - An array of rules that replace the deprecated rule (e.g. `["indent"]`).
-
-Once you get a report object, it's up to you to determine how to output the results. Fixes will not be automatically applied to the files, even if you set `fix: true` when constructing the `CLIEngine` instance. To apply fixes to the files, call [`outputFixes`](#cliengineoutputfixes).
-
-### CLIEngine#resolveFileGlobPatterns()
-
-You can pass filesystem-style or glob patterns to ESLint and have it function properly. In order to achieve this, ESLint must resolve non-glob patterns into glob patterns before determining which files to execute on. The `resolveFileGlobPatterns()` methods uses the current settings from `CLIEngine` to resolve non-glob patterns into glob patterns. Pass an array of patterns that might be passed to the ESLint CLI and it will return an array of glob patterns that mean the same thing. Here's an example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-});
-
-// pass an array of patterns
-const globPatterns = cli.resolveFileGlobPatterns(["."]);
-console.log(globPatterns[i]);       // ["**/*.js"]
-```
-
-### CLIEngine#getConfigForFile()
-
-If you want to retrieve a configuration object for a given file, use the `getConfigForFile()` method. This method accepts one argument, a file path, and returns an object represented the calculated configuration of the file. Here's an example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-const config = cli.getConfigForFile("myfile.js");
-```
-
-Once you have the configuration information, you can pass it into the `linter` object:
-
-```js
-const CLIEngine = require("eslint").CLIEngine,
-    Linter = require("eslint").Linter;
-
-const linter = new Linter();
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-const config = cli.getConfigForFile("myfile.js");
-
-const messages = linter.verify('var foo;', config);
-```
-
-### CLIEngine#executeOnText()
-
-If you already have some text to lint, then you can use the `executeOnText()` method to lint that text. The linter will assume that the text is a file in the current working directory, and so will still obey any `.eslintrc` and `.eslintignore` files that may be present. Here's an example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-// Lint the supplied text and optionally set a filename that is displayed in the report
-const report = cli.executeOnText("var foo = 'bar';", "foo.js");
-
-// In addition to the above, warn if the resolved file name is ignored.
-const reportAndWarnOnIgnoredFile = cli.executeOnText("var foo = 'bar';", "foo.js", true);
-```
-
-The `report` returned from `executeOnText()` is in the same format as from `executeOnFiles()`, but there is only ever one result in `report.results`.
-
-If a filename in the optional second parameter matches a file that is configured to be ignored, then this function returns no errors or warnings. The method includes an additional optional boolean third parameter. When `true`, a resolved file name that is ignored will return a warning.
-
-### CLIEngine#addPlugin()
-
-Loads a plugin from configuration object with specified name. Name can include plugin prefix ("eslint-plugin-")
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-const cli = new CLIEngine({
-    ignore: true
-});
-cli.addPlugin("eslint-plugin-processor", {
-    processors: {
-        ".txt": {
-            preprocess: function(text) {
-                return [text];
-            },
-            postprocess: function(messages) {
-                return messages[0];
-            }
-        }
-    }
-});
-```
-
-### CLIEngine#isPathIgnored()
-
-Checks if a given path is ignored by ESLint.
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    ignore: true,
-    ignorePath: ".customIgnoreFile"
-});
-
-const isIgnored = cli.isPathIgnored("foo/bar.js");
-```
-
-### CLIEngine#getFormatter()
-
-Retrieves a formatter, which you can then use to format a report object. The argument is either the name of a built-in formatter:
-
-* "[checkstyle](../user-guide/formatters#checkstyle)"
-* "[codeframe](../user-guide/formatters#codeframe)"
-* "[compact](../user-guide/formatters#compact)"
-* "[html](../user-guide/formatters#html)"
-* "[jslint-xml](../user-guide/formatters#jslint-xml)"
-* "[json](../user-guide/formatters#json)"
-* "[junit](../user-guide/formatters#junit)"
-* "[stylish](../user-guide/formatters#stylish)" (the default)
-* "[table](../user-guide/formatters#table)"
-* "[tap](../user-guide/formatters#tap)"
-* "[unix](../user-guide/formatters#unix)"
-* "[visualstudio](../user-guide/formatters#visualstudio)"
-
-or the full path to a JavaScript file containing a custom formatter. You can also omit the argument to retrieve the default formatter.
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-// lint myfile.js and all files in lib/
-const report = cli.executeOnFiles(["myfile.js", "lib/"]);
-
-// get the default formatter
-const formatter = cli.getFormatter();
-
-// Also could do...
-// const formatter = cli.getFormatter("compact");
-// const formatter = cli.getFormatter("./my/formatter.js");
-
-// output to console
-console.log(formatter(report.results));
-```
-
-**Note:** Also available as a static function on `CLIEngine`.
-
-```js
-// get the default formatter by calling the static function
-const formatter = CLIEngine.getFormatter();
-```
-
-**Important:** You must pass in the `results` property of the report. Passing in `report` directly will result in an error.
-
-### CLIEngine#getErrorResults()
-
-This is a static function on `CLIEngine`. It can be used to filter out all the non error messages from the report object.
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-// lint myfile.js and all files in lib/
-const report = cli.executeOnFiles(["myfile.js", "lib/"]);
-
-// only get the error messages
-const errorReport = CLIEngine.getErrorResults(report.results)
-```
-
-**Important:** You must pass in the `results` property of the report. Passing in `report` directly will result in an error.
-
-### CLIEngine#outputFixes()
-
-This is a static function on `CLIEngine` that is used to output fixes from `report` to disk. It does by looking for files that have an `output` property in their results. Here's an example:
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-
-const cli = new CLIEngine({
-    envs: ["browser", "mocha"],
-    fix: true,
-    useEslintrc: false,
-    rules: {
-        semi: 2
-    }
-});
-
-// lint myfile.js and all files in lib/
-const report = cli.executeOnFiles(["myfile.js", "lib/"]);
-
-// output fixes to disk
-CLIEngine.outputFixes(report);
-```
-
-### CLIEngine#getRules()
-
-This method returns a map of all loaded rules. Under the hood, it calls [Linter#getRules](#lintergetrules).
-
-```js
-const CLIEngine = require("eslint").CLIEngine;
-const cli = new CLIEngine();
-
-cli.getRules();
-
-/*
-Map {
-  'accessor-pairs' => { meta: { docs: [Object], schema: [Array] }, create: [Function: create] },
-  'array-bracket-newline' => { meta: { docs: [Object], schema: [Array] }, create: [Function: create] },
-  ...
-}
-*/
-```
-
-
-### CLIEngine.version
-
-`CLIEngine` has a static `version` property containing the semantic version number of ESLint that it comes from.
-
-```js
-require("eslint").CLIEngine.version; // '4.5.0'
-```
-
 ---
 
 ## RuleTester
@@ -1233,9 +750,11 @@ The `RuleTester#run()` method is used to run the tests. It should be passed the
 
 A test case is an object with the following properties:
 
+* `name` (string, optional): The name to use for the test case, to make it easier to find
 * `code` (string, required): The source code that the rule should be run on
 * `options` (array, optional): The options passed to the rule. The rule severity should not be included in this list.
 * `filename` (string, optional): The filename for the given case (useful for rules that make assertions about filenames).
+* `only` (boolean, optional): Run this case exclusively for debugging in supported test frameworks.
 
 In addition to the properties above, invalid test cases can also have the following properties:
 
@@ -1335,10 +854,13 @@ ruleTester.run("my-rule-for-no-foo", rule, {
 `RuleTester` depends on two functions to run tests: `describe` and `it`. These functions can come from various places:
 
 1. If `RuleTester.describe` and `RuleTester.it` have been set to function values, `RuleTester` will use `RuleTester.describe` and `RuleTester.it` to run tests. You can use this to customize the behavior of `RuleTester` to match a test framework that you're using.
-1. Otherwise, if `describe` and `it` are present as globals, `RuleTester` will use `global.describe` and `global.it` to run tests. This allows `RuleTester` to work when using frameworks like [Mocha](https://mochajs.org/) without any additional configuration.
-1. Otherwise, `RuleTester#run` will simply execute all of the tests in sequence, and will throw an error if one of them fails. This means you can simply execute a test file that calls `RuleTester.run` using `node`, without needing a testing framework.
 
-`RuleTester#run` calls the `describe` function with two arguments: a string describing the rule, and a callback function. The callback calls the `it` function with a string describing the test case, and a test function. The test function will return successfully if the test passes, and throw an error if the test fails. (Note that this is the standard behavior for test suites when using frameworks like [Mocha](https://mochajs.org/); this information is only relevant if you plan to customize `RuleTester.it` and `RuleTester.describe`.)
+    If `RuleTester.itOnly` has been set to a function value, `RuleTester` will call `RuleTester.itOnly` instead of `RuleTester.it` to run cases with `only: true`. If `RuleTester.itOnly` is not set but `RuleTester.it` has an `only` function property, `RuleTester` will fall back to `RuleTester.it.only`.
+
+2. Otherwise, if `describe` and `it` are present as globals, `RuleTester` will use `global.describe` and `global.it` to run tests and `global.it.only` to run cases with `only: true`. This allows `RuleTester` to work when using frameworks like [Mocha](https://mochajs.org/) without any additional configuration.
+3. Otherwise, `RuleTester#run` will simply execute all of the tests in sequence, and will throw an error if one of them fails. This means you can simply execute a test file that calls `RuleTester.run` using `Node.js`, without needing a testing framework.
+
+`RuleTester#run` calls the `describe` function with two arguments: a string describing the rule, and a callback function. The callback calls the `it` function with a string describing the test case, and a test function. The test function will return successfully if the test passes, and throw an error if the test fails. The signature for `only` is the same as `it`. `RuleTester` calls either `it` or `only` for every case even when some cases have `only: true`, and the test framework is responsible for implementing test case exclusivity. (Note that this is the standard behavior for test suites when using frameworks like [Mocha](https://mochajs.org/); this information is only relevant if you plan to customize `RuleTester.describe`, `RuleTester.it`, or `RuleTester.itOnly`.)
 
 Example of customizing `RuleTester`:
 
@@ -1374,14 +896,6 @@ ruleTester.run("my-rule", myRule, {
 
 ---
 
-## Deprecated APIs
-
-* `cli` - the `cli` object has been deprecated in favor of `CLIEngine`. As of v1.0.0, `cli` is no longer exported and should not be used by external tools.
-* `linter` - the `linter` object has been deprecated in favor of `Linter` as of v4.0.0.
-* `CLIEngine` - the `CLIEngine` class has been deprecated in favor of the `ESLint` class as of v7.0.0.
-
----
-
 [configuration object]: ../user-guide/configuring
 [builtin-formatters]: https://eslint.org/docs/user-guide/formatters/
 [thirdparty-formatters]: https://www.npmjs.com/search?q=eslintformatter
@@ -1389,6 +903,7 @@ ruleTester.run("my-rule", myRule, {
 [eslint-constructor]: #-new-eslintoptions
 [eslint-lintfiles]: #-eslintlintfilespatterns
 [eslint-linttext]: #-eslintlinttextcode-options
+[eslint-getrulesmetaforresults]: #-eslintgetrulesmetaforresultsresults
 [eslint-calculateconfigforfile]: #-eslintcalculateconfigforfilefilepath
 [eslint-ispathignored]: #-eslintispathignoredfilepath
 [eslint-loadformatter]: #-eslintloadformatternameorpath
index 3ee5b23d3cb71c2c07b31b8916421a4bd9ebfe85..2762f74eace711d3d236adcd9af413fadec923b2 100644 (file)
@@ -77,7 +77,7 @@ Those members are defined but not used in ESLint.
 #### type
 
 * **Type:** `string`
-* **Description:** The type of this scope. This is one of `"block"`, `"catch"`, `"class"`, `"for"`, `"function"`, `"function-expression-name"`, `"global"`, `"module"`, `"switch"`, `"with"`
+* **Description:** The type of this scope. This is one of `"block"`, `"catch"`, `"class"`, `"class-field-initializer"`, `"class-static-block"`, `"for"`, `"function"`, `"function-expression-name"`, `"global"`, `"module"`, `"switch"`, `"with"`.
 
 #### isStrict
 
@@ -97,7 +97,9 @@ Those members are defined but not used in ESLint.
 #### variableScope
 
 * **Type:** `Scope`
-* **Description:** The scope which hosts variables which are defined by `var` declarations.
+* **Description:** The nearest ancestor whose `type` is one of `"class-field-initializer"`, `"class-static-block"`, `"function"`, `"global"`, or `"module"`. For the aforementioned scopes this is a self-reference.
+
+> This represents the lowest enclosing function or top-level scope. Class field initializers and class static blocks are implicit functions. Historically, this was the scope which hosts variables that are defined by `var` declarations, and thus the name `variableScope`.
 
 #### block
 
index afe6600dc8c2b54104102f83e7ed3cbc8b2c403c..78bb260842662bb3517b69bb1b4312f33881e8be 100644 (file)
@@ -31,7 +31,7 @@ The following selectors are supported:
 * wildcard (matches all nodes): `*`
 * attribute existence: `[attr]`
 * attribute value: `[attr="foo"]` or `[attr=123]`
-* attribute regex: `[attr=/foo.*/]`
+* attribute regex: `[attr=/foo.*/]` <sub>(with some [known issues](#known-issues))</sub>
 * attribute conditions: `[attr!="foo"]`, `[attr>2]`, `[attr<3]`, `[attr>=2]`, or `[attr<=3]`
 * nested attribute: `[attr.level2="foo"]`
 * field: `FunctionDeclaration > Identifier.id`
@@ -131,3 +131,7 @@ Or you can enforce that calls to `setTimeout` always have two arguments:
 ```
 
 Using selectors in the `no-restricted-syntax` rule can give you a lot of control over problematic patterns in your codebase, without needing to write custom rules to detect each pattern.
+
+### Known issues
+
+Due to a [bug](https://github.com/estools/esquery/issues/68) in [esquery](https://github.com/estools/esquery), regular expressions that contain a forward-slash character `/` aren't properly parsed, so `[value=/some\/path/]` will be a syntax error. As a [workaround](https://github.com/estools/esquery/issues/68), you can replace the `/` character with its unicode counterpart, like so: `[value=/some\\u002Fpath/]`.
index 281c39674540e5eb2f815f81f706934b7bf338da..46d77b59076cf03ec25665e942c0c20a702393a0 100644 (file)
@@ -10,11 +10,29 @@ This automatically starts Mocha and runs all tests in the `tests` directory. You
 
 ## Running Individual Tests
 
-If you want to quickly run just one test, you can do so by running Mocha directly and passing in the filename. For example:
+If you want to quickly run just one test file, you can do so by running Mocha directly and passing in the filename. For example:
 
     npm run test:cli tests/lib/rules/no-wrap-func.js
 
-Running individual tests is useful when you're working on a specific bug and iterating on the solution. You should be sure to run `npm test` before submitting a pull request.
+If you want to run just one or a subset of `RuleTester` test cases, add `only: true` to each test case or wrap the test case in `RuleTester.only(...)` to add it automatically:
+
+```js
+ruleTester.run("my-rule", myRule, {
+    valid: [
+        RuleTester.only("const valid = 42;"),
+        // Other valid cases
+    ],
+    invalid: [
+        {
+            code: "const invalid = 42;",
+            only: true,
+        },
+        // Other invalid cases
+    ]
+})
+```
+
+Running individual tests is useful when you're working on a specific bug and iterating on the solution. You should be sure to run `npm test` before submitting a pull request. `npm test` uses Mocha's `--forbid-only` option to prevent `only` tests from passing full test runs.
 
 ## More Control on Unit Testing
 
index 3809e3700d079fe3c01e26bc9b2f79eabe587142..bea47021932fde57109f6b0207a279adceef06c6 100644 (file)
@@ -216,6 +216,14 @@ The plugin support was introduced in ESLint version `0.8.0`. Ensure the `peerDep
 
 ESLint provides the [`RuleTester`](/docs/developer-guide/nodejs-api.md#ruletester) utility to make it easy to test the rules of your plugin.
 
+### Linting
+
+ESLint plugins should be linted too! It's suggested to lint your plugin with the `recommended` configurations of:
+
+* [eslint](https://www.npmjs.com/package/eslint)
+* [eslint-plugin-eslint-plugin](https://www.npmjs.com/package/eslint-plugin-eslint-plugin)
+* [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node)
+
 ## Share Plugins
 
 In order to make your plugin available to the community you have to publish it on npm.
index 5265cb50e79297f97b9ade9eedafc319e83e78c0..63d7bb2fc69181db6c8c54f2aaf4ef6e277f2a85 100644 (file)
@@ -493,8 +493,8 @@ To keep the linting process efficient and unobtrusive, it is useful to verify th
 The `npm run perf` command gives a high-level overview of ESLint running time with default rules (`eslint:recommended`) enabled.
 
 ```bash
-$ git checkout master
-Switched to branch 'master'
+$ git checkout main
+Switched to branch 'main'
 
 $ npm run perf
 CPU Speed is 2200 with multiplier 7500000
index 9dadae00e5097439e08660ab06c1e1142db094f6..ee5a5606fac4e6d36e483999c4d7b8dc7e1eba99 100644 (file)
@@ -24,6 +24,9 @@ Here is the basic format of the source file for a rule:
 // Rule Definition
 //------------------------------------------------------------------------------
 
+/**
+ * @type {import('eslint').Rule.RuleModule}
+ */
 module.exports = {
     meta: {
         type: "suggestion",
@@ -61,15 +64,18 @@ The source file for a rule exports an object with the following properties.
     * `description` (string) provides the short description of the rule in the [rules index](../rules/)
     * `category` (string) specifies the heading under which the rule is listed in the [rules index](../rules/)
     * `recommended` (boolean) is whether the `"extends": "eslint:recommended"` property in a [configuration file](../user-guide/configuring/configuration-files.md#extending-configuration-files) enables the rule
-    * `url` (string) specifies the URL at which the full documentation can be accessed
-    * `suggestion` (boolean) specifies whether rules can return suggestions (defaults to false if omitted)
+    * `url` (string) specifies the URL at which the full documentation can be accessed (enabling code editors to provide a helpful link on highlighted rule violations)
 
     In a custom rule or plugin, you can omit `docs` or include any properties that you need in it.
 
-* `fixable` (string) is either `"code"` or `"whitespace"` if the `--fix` option on the [command line](../user-guide/command-line-interface.md#fix) automatically fixes problems reported by the rule
+* `fixable` (string) is either `"code"` or `"whitespace"` if the `--fix` option on the [command line](../user-guide/command-line-interface.md#--fix) automatically fixes problems reported by the rule
 
     **Important:** the `fixable` property is mandatory for fixable rules. If this property isn't specified, ESLint will throw an error whenever the rule attempts to produce a fix. Omit the `fixable` property if the rule is not fixable.
 
+* `hasSuggestions` (boolean) specifies whether rules can return suggestions (defaults to `false` if omitted)
+
+     **Important:** the `hasSuggestions` property is mandatory for rules that provide suggestions. If this property isn't set to `true`, ESLint will throw an error whenever the rule attempts to produce a suggestion. Omit the `hasSuggestions` property if the rule does not provide suggestions.
+
 * `schema` (array) specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../user-guide/configuring/rules.md#configuring-rules)
 
 * `deprecated` (boolean) indicates whether the rule has been deprecated.  You may omit the `deprecated` property if the rule has not been deprecated.
@@ -127,7 +133,7 @@ The `context` object contains additional functionality that is helpful for rules
 Additionally, the `context` object has the following methods:
 
 * `getAncestors()` - returns an array of the ancestors of the currently-traversed node, starting at the root of the AST and continuing through the direct parent of the current node. This array does not include the currently-traversed node itself.
-* `getCwd()` - returns the `cwd` passed to [Linter](./nodejs-api.md#Linter). It is a path to a directory that should be considered as the current working directory.
+* `getCwd()` - returns the `cwd` passed to [Linter](./nodejs-api.md#linter). It is a path to a directory that should be considered as the current working directory.
 * `getDeclaredVariables(node)` - returns a list of [variables](./scope-manager-interface.md#variable-interface) declared by the given node. This information can be used to track references to variables.
     * If the node is a `VariableDeclaration`, all variables declared in the declaration are returned.
     * If the node is a `VariableDeclarator`, all variables declared in the declarator are returned.
@@ -315,6 +321,8 @@ The `fixer` object has the following methods:
 * `replaceText(nodeOrToken, text)` - replaces the text in the given node or token
 * `replaceTextRange(range, text)` - replaces the text in the given range
 
+A range is a two-item array containing character indices inside of the source code. The first item is the start of the range (inclusive) and the second item is the end of the range (exclusive). Every node and token has a `range` property to identify the source code range they represent.
+
 The above methods return a `fixing` object.
 The `fix()` function can return the following values:
 
@@ -346,6 +354,24 @@ Best practices for fixes:
 
     * This fixer can just select a quote type arbitrarily. If it guesses wrong, the resulting code will be automatically reported and fixed by the [`quotes`](/docs/rules/quotes.md) rule.
 
+Note: Making fixes as small as possible is a best practice, but in some cases it may be correct to extend the range of the fix in order to intentionally prevent other rules from making fixes in a surrounding range in the same pass. For instance, if replacement text declares a new variable, it can be useful to prevent other changes in the scope of the variable as they might cause name collisions.
+
+The following example replaces `node` and also ensures that no other fixes will be applied in the range of `node.parent` in the same pass:
+
+```js
+context.report({
+    node,
+    message,
+    *fix(fixer) {
+        yield fixer.replaceText(node, replacementText);
+
+        // extend range of the fix to the range of `node.parent`
+        yield fixer.insertTextBefore(node.parent, "");
+        yield fixer.insertTextAfter(node.parent, "");
+    }
+});
+```
+
 ### Providing Suggestions
 
 In some cases fixes aren't appropriate to be automatically applied, for example, if a fix potentially changes functionality or if there are multiple valid ways to fix a rule depending on the implementation intent (see the best practices for [applying fixes](#applying-fixes) listed above). In these cases, there is an alternative `suggest` option on `context.report()` that allows other tools, such as editors, to expose helpers for users to manually apply a suggestion.
@@ -376,6 +402,8 @@ context.report({
 {% endraw %}
 ```
 
+**Important:** The `meta.hasSuggestions` property is mandatory for rules that provide suggestions. ESLint will throw an error if a rule attempts to produce a suggestion but does not [export](#rule-basics) this property.
+
 Note: Suggestions will be applied as a stand-alone change, without triggering multipass fixes. Each suggestion should focus on a singular change in the code and should not try to conform to user defined styles. For example, if a suggestion is adding a new statement into the codebase, it should not try to match correct indentation, or conform to user preferences on presence/absence of semicolons. All of those things can be corrected by multipass autofix when the user triggers it.
 
 Best practices for suggestions:
@@ -387,7 +415,7 @@ Suggestions are intended to provide fixes. ESLint will automatically remove the
 
 #### Suggestion `messageId`s
 
-Instead of using a `desc` key for suggestions a `messageId` can be used instead. This works the same way as `messageId`s for the overall error (see [messageIds](#messageIds)). Here is an example of how to use it in a rule:
+Instead of using a `desc` key for suggestions a `messageId` can be used instead. This works the same way as `messageId`s for the overall error (see [messageIds](#messageids)). Here is an example of how to use it in a rule:
 
 ```js
 {% raw %}
@@ -397,7 +425,8 @@ module.exports = {
             unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
             removeEscape: "Remove the `\\`. This maintains the current functionality.",
             escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
-        }
+        },
+        hasSuggestions: true
     },
     create: function(context) {
         // ...
@@ -438,7 +467,8 @@ module.exports = {
         messages: {
             unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
             removeEscape: "Remove `\\` before {{character}}.",
-        }
+        },
+        hasSuggestions: true
     },
     create: function(context) {
         // ...
@@ -501,7 +531,7 @@ module.exports = {
 };
 ```
 
-Once you have an instance of `SourceCode`, you can use the methods on it to work with the code:
+Once you have an instance of `SourceCode`, you can use the following methods on it to work with the code:
 
 * `getText(node)` - returns the source code for the given node. Omit `node` to get the whole source.
 * `getAllComments()` - returns an array of all comments in the source.
@@ -667,8 +697,8 @@ To keep the linting process efficient and unobtrusive, it is useful to verify th
 When developing in the ESLint core repository, the `npm run perf` command gives a high-level overview of ESLint running time with all core rules enabled.
 
 ```bash
-$ git checkout master
-Switched to branch 'master'
+$ git checkout main
+Switched to branch 'main'
 
 $ npm run perf
 CPU Speed is 2200 with multiplier 7500000
index 75c7db025807c6d00d7140d98d2504cb592c2a17..9c308feb2b08146adf13237f7e57d5226df8d63c 100644 (file)
@@ -41,6 +41,15 @@ function doFor() {
     }
     console.log(y);
 }
+
+class C {
+    static {
+        if (something) {
+            var build = true;
+        }
+        build = false;
+    }
+}
 ```
 
 Examples of **correct** code for this rule:
@@ -85,6 +94,15 @@ function doFor() {
         console.log(y);
     }
 }
+
+class C {
+    static {
+        var build = false;
+        if (something) {
+            build = true;
+        }
+    }
+}
 ```
 
 ## Further Reading
index aef7c333cd31d90e130dba21d270d12f26dd15fa..04c7aeb6176646ec9d15739663e083512d6ef611 100644 (file)
@@ -23,6 +23,10 @@ if (foo) { bar = 0;}
 function baz() {let i = 0;
     return i;
 }
+
+class C {
+    static {this.bar = 0;}
+}
 ```
 
 Examples of **correct** code for this rule with the default `"always"` option:
@@ -32,6 +36,10 @@ Examples of **correct** code for this rule with the default `"always"` option:
 
 function foo() { return true; }
 if (foo) { bar = 0; }
+
+class C {
+    static { this.bar = 0; }
+}
 ```
 
 ### never
@@ -43,6 +51,10 @@ Examples of **incorrect** code for this rule with the `"never"` option:
 
 function foo() { return true; }
 if (foo) { bar = 0;}
+
+class C {
+    static { this.bar = 0; }
+}
 ```
 
 Examples of **correct** code for this rule with the `"never"` option:
@@ -52,6 +64,10 @@ Examples of **correct** code for this rule with the `"never"` option:
 
 function foo() {return true;}
 if (foo) {bar = 0;}
+
+class C {
+    static {this.bar = 0;}
+}
 ```
 
 ## When Not To Use It
index 1a5bf00aaefdf65169882e0b563a40ded9ceb543..c22c790ec2732c9d043ca55841a276d903c6236a 100644 (file)
@@ -85,6 +85,14 @@ if (foo) {
 else {
   baz();
 }
+
+class C
+{
+    static
+    {
+        foo();
+    }
+}
 ```
 
 Examples of **correct** code for this rule with the default `"1tbs"` option:
@@ -112,6 +120,12 @@ try {
   handleError();
 }
 
+class C {
+    static {
+        foo();
+    }
+}
+
 // when there are no braces, there are no problems
 if (foo) bar();
 else if (baz) boom();
@@ -150,6 +164,12 @@ if (foo) { baz(); } else if (bar) {
 try { somethingRisky(); } catch(e) {
   handleError();
 }
+
+class C {
+    static { foo(); }
+}
+
+class D { static { foo(); } }
 ```
 
 ### stroustrup
@@ -177,6 +197,14 @@ try
   handleError();
 }
 
+class C
+{
+    static
+    {
+        foo();
+    }
+}
+
 if (foo) {
   bar();
 } else {
@@ -211,6 +239,12 @@ catch(e) {
   handleError();
 }
 
+class C {
+    static {
+        foo();
+    }
+}
+
 // when there are no braces, there are no problems
 if (foo) bar();
 else if (baz) boom();
@@ -230,6 +264,12 @@ else { baz(); }
 
 try { somethingRisky(); }
 catch(e) { handleError(); }
+
+class C {
+    static { foo(); }
+}
+
+class D { static { foo(); } }
 ```
 
 ### allman
@@ -255,6 +295,12 @@ try
   handleError();
 }
 
+class C {
+    static {
+        foo();
+    }
+}
+
 if (foo) {
   bar();
 } else {
@@ -295,6 +341,14 @@ catch(e)
   handleError();
 }
 
+class C
+{
+    static
+    {
+        foo();
+    }
+}
+
 // when there are no braces, there are no problems
 if (foo) bar();
 else if (baz) boom();
@@ -314,6 +368,16 @@ else { baz(); }
 
 try { somethingRisky(); }
 catch(e) { handleError(); }
+
+class C
+{
+    static { foo(); }
+
+    static
+    { foo(); }
+}
+
+class D { static { foo(); } }
 ```
 
 ## When Not To Use It
index 89f6762b2221cdcd797d6ee52056ac7555f1879b..73efc9dba5482b4840e38bba402e4f36ddf70b66 100644 (file)
@@ -83,12 +83,21 @@ class A {
     static foo() {
         // OK. static methods aren't expected to use this.
     }
+
+    static {
+        // OK. static blocks are exempt.
+    }
 }
 ```
 
 ## Options
 
-### Exceptions
+This rule has two options:
+
+* `"exceptMethods"` allows specified method names to be ignored with this rule.
+* `"enforceForClassFields"` enforces that functions used as instance field initializers utilize `this`. (default: `true`)
+
+### exceptMethods
 
 ```
 "class-methods-use-this": [<enabled>, { "exceptMethods": [<...exceptions>] }]
@@ -110,11 +119,51 @@ class A {
 Examples of **correct** code for this rule when used with exceptMethods:
 
 ```js
-/*eslint class-methods-use-this: ["error", { "exceptMethods": ["foo"] }] */
+/*eslint class-methods-use-this: ["error", { "exceptMethods": ["foo", "#bar"] }] */
 
 class A {
     foo() {
     }
+    #bar() {
+    }
+}
+```
+
+## enforceForClassFields
+
+```
+"class-methods-use-this": [<enabled>, { "enforceForClassFields": true | false }]
+```
+
+The `enforceForClassFields` option enforces that arrow functions and function expressions used as instance field initializers utilize `this`. (default: `true`)
+
+Examples of **incorrect** code for this rule with the `{ "enforceForClassFields": true }` option (default):
+
+```js
+/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
+
+class A {
+    foo = () => {}
+}
+```
+
+Examples of **correct** code for this rule with the `{ "enforceForClassFields": true }` option (default):
+
+```js
+/*eslint class-methods-use-this: ["error", { "enforceForClassFields": true }] */
+
+class A {
+    foo = () => {this;}
+}
+```
+
+Examples of **correct** code for this rule with the `{ "enforceForClassFields": false }` option:
+
+```js
+/*eslint class-methods-use-this: ["error", { "enforceForClassFields": false }] */
+
+class A {
+    foo = () => {}
 }
 ```
 
index a57b4366c1cc269c9272728da4a97042a38a0907..7955d1a285415a73e273deefa235affc9565f819 100644 (file)
@@ -57,6 +57,53 @@ function b() {
 }
 ```
 
+Class field initializers and class static blocks are implicit functions. Therefore, their complexity is calculated separately for each initializer and each static block, and it doesn't contribute to the complexity of the enclosing code.
+
+Examples of additional **incorrect** code for a maximum of 2:
+
+```js
+/*eslint complexity: ["error", 2]*/
+
+class C {
+    x = a || b || c; // this initializer has complexity = 3
+}
+
+class D { // this static block has complexity = 3
+    static {
+        if (foo) {
+            bar = baz || qux;
+        }
+    }
+}
+```
+
+Examples of additional **correct** code for a maximum of 2:
+
+```js
+/*eslint complexity: ["error", 2]*/
+
+function foo() { // this function has complexity = 1
+    class C {
+        x = a + b; // this initializer has complexity = 1
+        y = c || d; // this initializer has complexity = 2
+        z = e && f; // this initializer has complexity = 2
+
+        static p = g || h; // this initializer has complexity = 2
+        static q = i ? j : k; // this initializer has complexity = 2
+
+        static { // this static block has complexity = 2
+            if (foo) {
+                baz = bar;
+            }
+        }
+
+        static { // this static block has complexity = 2
+            qux = baz || quux;
+        }
+    }
+}
+```
+
 ## Options
 
 Optionally, you may specify a `max` object property:
index a13049a3f3f56bba8fc228b5980fdbe758ba9d3f..26c77756c4b40642790d83c0d64fd47ebea9769e 100644 (file)
@@ -46,6 +46,19 @@ var foo = { "class": "CS 101" }
 var x = foo["class"]; // Property name is a reserved word, square-bracket notation required
 ```
 
+Examples of additional **correct** code for the `{ "allowKeywords": false }` option:
+
+```js
+/*eslint dot-notation: ["error", { "allowKeywords": false }]*/
+
+class C {
+    #in;
+    foo() {
+        this.#in; // Dot notation is required for private identifiers
+    }
+}
+```
+
 ### allowPattern
 
 For example, when preparing data to be sent to an external API, it is often required to use property names that include underscores.  If the `camelcase` rule is in effect, these [snake case](https://en.wikipedia.org/wiki/Snake_case) properties would not be allowed.  By providing an `allowPattern` to the `dot-notation` rule, these snake case properties can be accessed with bracket notation.
index 53be2a09987d03572f15ed338e66b34407333cf8..39ee411f035373f14e8a7383a0821f57f603a02c 100644 (file)
@@ -15,6 +15,10 @@ obj.foo = function bar() {};
 obj['foo'] = function bar() {};
 var obj = {foo: function bar() {}};
 ({['foo']: function bar() {}});
+
+class C {
+    foo = function bar() {};
+}
 ```
 
 ```js
@@ -26,6 +30,10 @@ obj.foo = function foo() {};
 obj['foo'] = function foo() {};
 var obj = {foo: function foo() {}};
 ({['foo']: function foo() {}});
+
+class C {
+    foo = function foo() {};
+}
 ```
 
 Examples of **correct** code for this rule:
@@ -54,6 +62,21 @@ obj['x' + 2] = function bar(){};
 var [ bar ] = [ function bar(){} ];
 ({[foo]: function bar() {}})
 
+class C {
+    foo = function foo() {};
+    baz = function() {};
+}
+
+// private names are ignored
+class D {
+    #foo = function foo() {};
+    #bar = function foo() {};
+    baz() {
+        this.#foo = function foo() {};
+        this.#foo = function bar() {};
+    }
+}
+
 module.exports = function foo(name) {};
 module['exports'] = function foo(name) {};
 ```
@@ -81,6 +104,21 @@ obj['x' + 2] = function bar(){};
 var [ bar ] = [ function bar(){} ];
 ({[foo]: function bar() {}})
 
+class C {
+    foo = function bar() {};
+    baz = function() {};
+}
+
+// private names are ignored
+class D {
+    #foo = function foo() {};
+    #bar = function foo() {};
+    baz() {
+        this.#foo = function foo() {};
+        this.#foo = function bar() {};
+    }
+}
+
 module.exports = function foo(name) {};
 module['exports'] = function foo(name) {};
 ```
index 27b9cc20fcffe78d5597e9b8985704201e2b2492..ea1ba617e540c8783f0c76597ae537500863b306 100644 (file)
@@ -17,14 +17,14 @@ This rule can enforce or disallow the use of named function expressions.
 This rule has a string option:
 
 * `"always"` (default) requires function expressions to have a name
-* `"as-needed"` requires function expressions to have a name, if the name cannot be assigned automatically in an ES6 environment
+* `"as-needed"` requires function expressions to have a name, if the name isn't assigned automatically per the ECMAScript specification.
 * `"never"` disallows named function expressions, except in recursive functions, where a name is needed
 
 This rule has an object option:
 
 * `"generators": "always" | "as-needed" | "never"`
     * `"always"` require named generators
-    * `"as-needed"` require named generators if the name cannot be assigned automatically in an ES6 environment.
+    * `"as-needed"` require named generators if the name isn't assigned automatically per the ECMAScript specification.
     * `"never"` disallow named generators where possible.
 
 When a value for `generators` is not provided the behavior for generator functions falls back to the base option.
@@ -98,6 +98,13 @@ const cat = {
   meow: function() {}
 }
 
+class C {
+    #bar = function() {};
+    baz = function() {};
+}
+
+quux ??= function() {};
+
 (function bar() {
     // ...
 }())
index 040f26e894c099f633ca57cbbd40de5fd06c3426..071a34f7940f8dfe5e2423d37e6655bbe1f97fc7 100644 (file)
@@ -13,6 +13,8 @@ This rule will catch disallowed identifiers that are:
 - variable declarations
 - function declarations
 - object properties assigned to during object creation
+- class fields
+- class methods
 
 It will not catch disallowed identifiers that are:
 
@@ -49,6 +51,22 @@ element.callback = function() {
 var itemSet = {
     data: [...]
 };
+
+class Foo {
+    data = [];
+}
+
+class Foo {
+    #data = [];
+}
+
+class Foo {
+    callback( {);
+}
+
+class Foo {
+    #callback( {);
+}
 ```
 
 Examples of **correct** code for this rule with sample `"data", "callback"` restricted identifiers:
@@ -75,6 +93,22 @@ callback(); // all function calls are ignored
 foo.callback(); // all function calls are ignored
 
 foo.data; // all property names that are not assignments are ignored
+
+class Foo {
+    items = [];
+}
+
+class Foo {
+    #items = [];
+}
+
+class Foo {
+    method( {);
+}
+
+class Foo {
+    #method( {);
+}
 ```
 
 ## When Not To Use It
index e9f8d9e1eb8dfff32851f3572d9f873180890427..d8b1b45d212adb0335477519973377fb47c573dc 100644 (file)
@@ -30,6 +30,9 @@ var myObj = { a: 1 };
 (a) => { a * a };
 class x { }
 class Foo { x() {} }
+class Foo { #x() {} }
+class Foo { x = 1 }
+class Foo { #x = 1 }
 function foo(...x) { }
 function foo([x]) { }
 var [x] = arr;
@@ -61,6 +64,9 @@ var myObj = { apple: 1 };
 function foo(num = 0) { }
 class MyClass { }
 class Foo { method() {} }
+class Foo { #method() {} }
+class Foo { field = 1 }
+class Foo { #field = 1 }
 function foo(...args) { }
 function foo([longName]) { }
 var { prop } = {};
index 0d9deebd76d12ccdcd65ce0d2762853cabf4321a..5bb2a1d94c84181c0f5f9169256aee9dd406c57a 100644 (file)
@@ -35,9 +35,20 @@ var MY_FAVORITE_COLOR = "#112C85";
 function do_something() {
     // ...
 }
+
 obj.do_something = function() {
     // ...
 };
+
+class My_Class {}
+
+class myClass {
+    do_something() {}
+}
+
+class myClass {
+    #do_something() {}
+}
 ```
 
 Examples of **correct** code for this rule with the `"^[a-z]+([A-Z][a-z]+)*$"` option:
@@ -52,13 +63,26 @@ do_something();
 var obj = {
     my_pref: 1
 };
+
+class myClass {}
+
+class myClass {
+    doSomething() {}
+}
+
+class myClass {
+    #doSomething() {}
+}
 ```
 
 This rule has an object option:
 
-* `"properties": true` requires object properties to match the specified regular expression
+* `"properties": false` (default) does not check object properties
+* `"properties": true` requires object literal properties and member expression assignment properties to match the specified regular expression
+* `"classFields": false` (default) does not class field names
+* `"classFields": true` requires class field names to match the specified regular expression
+* `"onlyDeclarations": false` (default) requires all variable names to match the specified regular expression
 * `"onlyDeclarations": true` requires only `var`, `function`, and `class` declarations to match the specified regular expression
-* `"onlyDeclarations": false` requires all variable names to match the specified regular expression
 * `"ignoreDestructuring": false` (default) enforces `id-match` for destructured identifiers
 * `"ignoreDestructuring": true` does not check destructured identifiers
 
@@ -74,6 +98,22 @@ var obj = {
 };
 ```
 
+### classFields
+
+Examples of **incorrect** code for this rule with the `"^[a-z]+([A-Z][a-z]+)*$", { "classFields": true }` options:
+
+```js
+/*eslint id-match: ["error", "^[a-z]+([A-Z][a-z]+)*$", { "properties": true }]*/
+
+class myClass {
+    my_pref = 1;
+}
+
+class myClass {
+    #my_pref = 1;
+}
+```
+
 ### onlyDeclarations
 
 Examples of **correct** code for this rule with the `"^[a-z]+([A-Z][a-z]+)*$", { "onlyDeclarations": true }` options:
index 45ef1a62f7e8b9f9af6deac84b05effde8d8d3a9..2711ccc69871753a5dfb6c2008d4a95c5236ba63 100644 (file)
@@ -1,5 +1,7 @@
 # enforce consistent indentation (indent-legacy)
 
+This rule was **deprecated** in ESLint v4.0.0.
+
 ESLint 4.0.0 introduced a rewrite of the [`indent`](/docs/rules/indent) rule, which now reports more errors than it did in previous versions. To ease the process of migrating to 4.0.0, the `indent-legacy` rule was introduced as a snapshot of the `indent` rule from ESLint 3.x. If your build is failing after the upgrade to 4.0.0, you can disable `indent` and enable `indent-legacy` as a quick fix. Eventually, you should switch back to the `indent` rule to get bugfixes and improvements in future versions.
 
 ---
index 2a626604e01c264168cb4359a0d487bc26ec5c0f..74fb0e7e2acd839065ca21bf178974476ee0d354 100644 (file)
@@ -79,6 +79,8 @@ This rule has an object option:
 * `"FunctionExpression"` takes an object to define rules for function expressions.
     * `parameters` (default: 1) enforces indentation level for parameters in a function expression. This can either be a number indicating indentation level, or the string `"first"` indicating that all parameters of the expression must be aligned with the first parameter. This can also be set to `"off"` to disable checking for FunctionExpression parameters.
     * `body` (default: 1) enforces indentation level for the body of a function expression.
+* `"StaticBlock"` takes an object to define rules for class static blocks.
+    * `body` (default: 1) enforces indentation level for the body of a class static block.
 * `"CallExpression"` takes an object to define rules for function call expressions.
     * `arguments` (default: 1) enforces indentation level for arguments in a call expression. This can either be a number indicating indentation level, or the string `"first"` indicating that all arguments of the expression must be aligned with the first argument. This can also be set to `"off"` to disable checking for CallExpression arguments.
 * `"ArrayExpression"` (default: 1) enforces indentation level for elements in arrays. It can also be set to the string `"first"`, indicating that all the elements in the array should be aligned with the first element. This can also be set to `"off"` to disable checking for array elements.
@@ -484,6 +486,56 @@ var foo = function(bar, baz,
 }
 ```
 
+### StaticBlock
+
+Examples of **incorrect** code for this rule with the `2, { "StaticBlock": {"body": 1} }` option:
+
+```js
+/*eslint indent: ["error", 2, { "StaticBlock": {"body": 1} }]*/
+
+class C {
+  static {
+      foo();
+  }
+}
+```
+
+Examples of **correct** code for this rule with the `2, { "StaticBlock": {"body": 1} }` option:
+
+```js
+/*eslint indent: ["error", 2, { "StaticBlock": {"body": 1} }]*/
+
+class C {
+  static {
+    foo();
+  }
+}
+```
+
+Examples of **incorrect** code for this rule with the `2, { "StaticBlock": {"body": 2} }` option:
+
+```js
+/*eslint indent: ["error", 2, { "StaticBlock": {"body": 2} }]*/
+
+class C {
+  static {
+    foo();
+  }
+}
+```
+
+Examples of **correct** code for this rule with the `2, { "StaticBlock": {"body": 2} }` option:
+
+```js
+/*eslint indent: ["error", 2, { "StaticBlock": {"body": 2} }]*/
+
+class C {
+  static {
+      foo();
+  }
+}
+```
+
 ### CallExpression
 
 Examples of **incorrect** code for this rule with the `2, { "CallExpression": {"arguments": 1} }` option:
index ecc6bb0a49a5cb82f36418dbe5ef06fe6b0d0ec4..8ca6739371cee494eba6c41dfa2f8036e869ac06 100644 (file)
@@ -19,7 +19,7 @@ However, if you want to enforce the style of spacing between the `function` keyw
 
 ## Rule Details
 
-This rule enforces consistent spacing around keywords and keyword-like tokens: `as` (in module declarations), `async` (of async functions), `await` (of await expressions), `break`, `case`, `catch`, `class`, `const`, `continue`, `debugger`, `default`, `delete`, `do`, `else`, `export`, `extends`, `finally`, `for`, `from` (in module declarations), `function`, `get` (of getters), `if`, `import`, `in`, `instanceof`, `let`, `new`, `of` (in for-of statements), `return`, `set` (of setters), `static`, `super`, `switch`, `this`, `throw`, `try`, `typeof`, `var`, `void`, `while`, `with`, and `yield`. This rule is designed carefully not to conflict with other spacing rules: it does not apply to spacing where other rules report problems.
+This rule enforces consistent spacing around keywords and keyword-like tokens: `as` (in module declarations), `async` (of async functions), `await` (of await expressions), `break`, `case`, `catch`, `class`, `const`, `continue`, `debugger`, `default`, `delete`, `do`, `else`, `export`, `extends`, `finally`, `for`, `from` (in module declarations), `function`, `get` (of getters), `if`, `import`, `in` (in for-in statements), `let`, `new`, `of` (in for-of statements), `return`, `set` (of setters), `static`, `super`, `switch`, `this`, `throw`, `try`, `typeof`, `var`, `void`, `while`, `with`, and `yield`. This rule is designed carefully not to conflict with other spacing rules: it does not apply to spacing where other rules report problems.
 
 ## Options
 
@@ -243,13 +243,14 @@ if(foo) {
 
 ### overrides
 
-Examples of **correct** code for this rule with the `{ "overrides": { "if": { "after": false }, "for": { "after": false }, "while": { "after": false } } }` option:
+Examples of **correct** code for this rule with the `{ "overrides": { "if": { "after": false }, "for": { "after": false }, "while": { "after": false }, "static": { "after": false } } }` option:
 
 ```js
 /*eslint keyword-spacing: ["error", { "overrides": {
   "if": { "after": false },
   "for": { "after": false },
-  "while": { "after": false }
+  "while": { "after": false },
+  "static": { "after": false }
 } }]*/
 
 if(foo) {
@@ -263,7 +264,13 @@ if(foo) {
 for(;;);
 
 while(true) {
-  //...
+    //...
+}
+
+class C {
+    static{
+        //...
+    }
 }
 ```
 
index ae543bd837b8aff760ee2e3ffdcbcbeb0383bd42..fb0161102727c1c1cdfc3cc67333067ffcd899b0 100644 (file)
@@ -15,8 +15,8 @@ This rule has an object option:
 * `"afterBlockComment": true` requires an empty line after block comments
 * `"beforeLineComment": true` requires an empty line before line comments
 * `"afterLineComment": true` requires an empty line after line comments
-* `"allowBlockStart": true` allows comments to appear at the start of block statements
-* `"allowBlockEnd": true` allows comments to appear at the end of block statements
+* `"allowBlockStart": true` allows comments to appear at the start of block statements, function bodies, classes, and class static blocks
+* `"allowBlockEnd": true` allows comments to appear at the end of block statements, function bodies, classes, and class static blocks
 * `"allowObjectStart": true` allows comments to appear at the start of object literals
 * `"allowObjectEnd": true` allows comments to appear at the end of object literals
 * `"allowArrayStart": true` allows comments to appear at the start of array literals
@@ -133,6 +133,25 @@ function foo(){
     var day = "great"
     return day;
 }
+
+if (bar) {
+    // what a great and wonderful day
+    foo();
+}
+
+class C {
+    // what a great and wonderful day
+
+    method() {
+        // what a great and wonderful day
+        foo();
+    }
+
+    static {
+        // what a great and wonderful day
+        foo();
+    }
+}
 ```
 
 Examples of **correct** code for this rule with the `{ "beforeBlockComment": true, "allowBlockStart": true }` options:
@@ -145,6 +164,25 @@ function foo(){
     var day = "great"
     return day;
 }
+
+if (bar) {
+    /* what a great and wonderful day */
+    foo();
+}
+
+class C {
+    /* what a great and wonderful day */
+
+    method() {
+        /* what a great and wonderful day */
+        foo();
+    }
+
+    static {
+        /* what a great and wonderful day */
+        foo();
+    }
+}
 ```
 
 ### allowBlockEnd
@@ -159,6 +197,26 @@ function foo(){
     return day;
     // what a great and wonderful day
 }
+
+if (bar) {
+    foo();
+    // what a great and wonderful day
+}
+
+class C {
+
+    method() {
+        foo();
+        // what a great and wonderful day
+    }
+
+    static {
+        foo();
+        // what a great and wonderful day
+    }
+
+    // what a great and wonderful day
+}
 ```
 
 Examples of **correct** code for this rule with the `{ "afterBlockComment": true, "allowBlockEnd": true }` option:
@@ -172,6 +230,29 @@ function foo(){
 
     /* what a great and wonderful day */
 }
+
+if (bar) {
+    foo();
+
+    /* what a great and wonderful day */
+}
+
+class C {
+
+    method() {
+        foo();
+
+        /* what a great and wonderful day */
+    }
+
+    static {
+        foo();
+
+        /* what a great and wonderful day */
+    }
+
+    /* what a great and wonderful day */
+}
 ```
 
 ### allowClassStart
index f950a6b0105c7a89e1385142d6dcb3d4b915b362..27084c28346f431bd3a496f26ffb72184637c342 100644 (file)
@@ -9,6 +9,7 @@ Examples of **incorrect** code for this rule:
 ```js
 /* eslint lines-between-class-members: ["error", "always"]*/
 class MyClass {
+  x;
   foo() {
     //...
   }
@@ -23,6 +24,8 @@ Examples of **correct** code for this rule:
 ```js
 /* eslint lines-between-class-members: ["error", "always"]*/
 class MyClass {
+  x;
+
   foo() {
     //...
   }
@@ -33,6 +36,17 @@ class MyClass {
 }
 ```
 
+Examples of additional **correct** code for this rule:
+
+```js
+/* eslint lines-between-class-members: ["error", "always"]*/
+class MyClass {
+  x = 1
+
+  ;in = 2
+}
+```
+
 ### Options
 
 This rule has a string option and an object option.
@@ -52,12 +66,15 @@ Examples of **incorrect** code for this rule with the string option:
 ```js
 /* eslint lines-between-class-members: ["error", "always"]*/
 class Foo{
+  x;
   bar(){}
   baz(){}
 }
 
 /* eslint lines-between-class-members: ["error", "never"]*/
 class Foo{
+  x;
+
   bar(){}
 
   baz(){}
@@ -69,6 +86,8 @@ Examples of **correct** code for this rule with the string option:
 ```js
 /* eslint lines-between-class-members: ["error", "always"]*/
 class Foo{
+  x;
+
   bar(){}
 
   baz(){}
@@ -76,6 +95,7 @@ class Foo{
 
 /* eslint lines-between-class-members: ["error", "never"]*/
 class Foo{
+  x;
   bar(){}
   baz(){}
 }
@@ -86,6 +106,7 @@ Examples of **correct** code for this rule with the object option:
 ```js
 /* eslint lines-between-class-members: ["error", "always", { "exceptAfterSingleLine": true }]*/
 class Foo{
+  x; // single line class member
   bar(){} // single line class member
   baz(){
     // multi line class member
index 98ca1d1003dab157bb4755bbc608cc8c4d81a9fd..70f3348a0fa8cc26ead4578c454455773d0d017c 100644 (file)
@@ -28,8 +28,12 @@ class Foo {}
 
 ## Options
 
-This rule has a numeric option (defaulted to 1) to specify the
-maximum number of classes.
+This rule may be configured with either an object or a number.
+
+If the option is an object, it may contain one or both of:
+
+-   `ignoreExpressions`: a boolean option (defaulted to `false`) to ignore class expressions.
+-   `max`: a numeric option (defaulted to 1) to specify the maximum number of classes.
 
 For example:
 
@@ -39,7 +43,16 @@ For example:
 }
 ```
 
-Examples of **correct** code for this rule with the numeric option set to `2`:
+```json
+{
+    "max-classes-per-file": [
+        "error",
+        { "ignoreExpressions": true, "max": 2 }
+    ]
+}
+```
+
+Examples of **correct** code for this rule with the `max` option set to `2`:
 
 ```js
 /* eslint max-classes-per-file: ["error", 2] */
@@ -47,3 +60,19 @@ Examples of **correct** code for this rule with the numeric option set to `2`:
 class Foo {}
 class Bar {}
 ```
+
+Examples of **correct** code for this rule with the `ignoreExpressions` option set to `true`:
+
+```js
+/* eslint max-classes-per-file: ["error", { ignoreExpressions: true }] */
+
+class VisitorFactory {
+    forDescriptor(descriptor) {
+        return class {
+            visit(node) {
+                return `Visiting ${descriptor}.`;
+            }
+        };
+    }
+}
+```
index b853d02671366f751baea286e4040abf49646311..ae8121e69305d119144f8923370a742a6ddc4901 100644 (file)
@@ -20,7 +20,6 @@ Examples of **incorrect** code for this rule with the default `{ "max": 4 }` opt
 
 ```js
 /*eslint max-depth: ["error", 4]*/
-/*eslint-env es6*/
 
 function foo() {
     for (;;) { // Nested 1 deep
@@ -40,7 +39,6 @@ Examples of **correct** code for this rule with the default `{ "max": 4 }` optio
 
 ```js
 /*eslint max-depth: ["error", 4]*/
-/*eslint-env es6*/
 
 function foo() {
     for (;;) { // Nested 1 deep
@@ -54,6 +52,48 @@ function foo() {
 }
 ```
 
+Note that class static blocks do not count as nested blocks, and that the depth in them is calculated separately from the enclosing context.
+
+Examples of **incorrect** code for this rule with `{ "max": 2 }` option:
+
+```js
+/*eslint max-depth: ["error", 2]*/
+
+function foo() {
+    if (true) { // Nested 1 deep
+        class C {
+            static {
+                if (true) { // Nested 1 deep
+                    if (true) { // Nested 2 deep
+                        if (true) { // Nested 3 deep
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+```
+
+Examples of **correct** code for this rule with `{ "max": 2 }` option:
+
+```js
+/*eslint max-depth: ["error", 2]*/
+
+function foo() {
+    if (true) { // Nested 1 deep
+        class C {
+            static {
+                if (true) { // Nested 1 deep
+                    if (true) { // Nested 2 deep
+                    }
+                }
+            }
+        }
+    }
+}
+```
+
 ## Related Rules
 
 * [complexity](complexity.md)
index 6ef87c2f82fb71ec8b368fefe0807c7a5c9f2889..c2a673310630be87394eb80d9fd21733ed2a7de1 100644 (file)
@@ -112,6 +112,30 @@ let foo = () => {
 }
 ```
 
+Note that this rule does not apply to class static blocks, and that statements in class static blocks do not count as statements in the enclosing function.
+
+Examples of **correct** code for this rule with `{ "max": 2 }` option:
+
+```js
+/*eslint max-statements: ["error", 2]*/
+
+function foo() {
+    let one;
+    let two = class {
+        static {
+            let three;
+            let four;
+            let five;
+            if (six) {
+                let seven;
+                let eight;
+                let nine;
+            }
+        }
+    };
+}
+```
+
 ### ignoreTopLevelFunctions
 
 Examples of additional **correct** code for this rule with the `{ "max": 10 }, { "ignoreTopLevelFunctions": true }` options:
index 22ac57729dc90841efa16e34b29bd461d957d475..ef8c61508c6c9927738c13591ab27ea23377b9f7 100644 (file)
@@ -112,15 +112,24 @@ var emitter = new events();
 
 ### newIsCapExceptionPattern
 
-Examples of additional **correct** code for this rule with the `{ "newIsCapExceptionPattern": "^person\.." }` option:
+Examples of additional **correct** code for this rule with the `{ "newIsCapExceptionPattern": "^person\\.." }` option:
 
 ```js
-/*eslint new-cap: ["error", { "newIsCapExceptionPattern": "^person\.." }]*/
+/*eslint new-cap: ["error", { "newIsCapExceptionPattern": "^person\\.." }]*/
 
 var friend = new person.acquaintance();
+
 var bestFriend = new person.friend();
 ```
 
+Examples of additional **correct** code for this rule with the `{ "newIsCapExceptionPattern": "\\.bar$" }` option:
+
+```js
+/*eslint new-cap: ["error", { "newIsCapExceptionPattern": "\\.bar$" }]*/
+
+var friend = new person.bar();
+```
+
 ### capIsNewExceptions
 
 Examples of additional **correct** code for this rule with the `{ "capIsNewExceptions": ["Person"] }` option:
@@ -135,15 +144,35 @@ function foo(arg) {
 
 ### capIsNewExceptionPattern
 
-Examples of additional **correct** code for this rule with the `{ "capIsNewExceptionPattern": "^Person\.." }` option:
+Examples of additional **correct** code for this rule with the `{ "capIsNewExceptionPattern": "^person\\.." }` option:
 
 ```js
-/*eslint new-cap: ["error", { "capIsNewExceptionPattern": "^Person\.." }]*/
+/*eslint new-cap: ["error", { "capIsNewExceptionPattern": "^person\\.." }]*/
 
 var friend = person.Acquaintance();
 var bestFriend = person.Friend();
 ```
 
+Examples of additional **correct** code for this rule with the `{ "capIsNewExceptionPattern": "\\.Bar$" }` option:
+
+```js
+/*eslint new-cap: ["error", { "capIsNewExceptionPattern": "\\.Bar$" }]*/
+
+foo.Bar();
+```
+
+Examples of additional **correct** code for this rule with the `{ "capIsNewExceptionPattern": "^Foo" }` option:
+
+```js
+/*eslint new-cap: ["error", { "capIsNewExceptionPattern": "^Foo" }]*/
+
+var x = Foo(42);
+
+var y = Foobar(42);
+
+var z = Foo.Bar(42);
+```
+
 ### properties
 
 Examples of **incorrect** code for this rule with the default `{ "properties": true }` option:
index f2fb79ec42159f6a3746b5299543c91da67bb7af..b1e4e601c0fbcdc21b4598d70e1a346610d686bb 100644 (file)
@@ -9,7 +9,7 @@ console.error("That shouldn't have happened.");
 
 ## Rule Details
 
-This rule disallows calls to methods of the `console` object.
+This rule disallows calls or assignments to methods of the `console` object.
 
 Examples of **incorrect** code for this rule:
 
@@ -19,6 +19,7 @@ Examples of **incorrect** code for this rule:
 console.log("Log a debug level message.");
 console.warn("Log a warn level message.");
 console.error("Log an error level message.");
+console.log = foo();
 ```
 
 Examples of **correct** code for this rule:
index 9c1cc4faa30dde2c2296b85c7e288b78f05dcda8..48b22e88598eb9497fb207674fc9602cf0a19b80 100644 (file)
@@ -25,7 +25,6 @@ Examples of **incorrect** code for this rule:
 
 ```js
 /*eslint no-dupe-class-members: "error"*/
-/*eslint-env es6*/
 
 class Foo {
   bar() { }
@@ -37,6 +36,16 @@ class Foo {
   get bar() { }
 }
 
+class Foo {
+  bar;
+  bar;
+}
+
+class Foo {
+  bar;
+  bar() { }
+}
+
 class Foo {
   static bar() { }
   static bar() { }
@@ -47,7 +56,6 @@ Examples of **correct** code for this rule:
 
 ```js
 /*eslint no-dupe-class-members: "error"*/
-/*eslint-env es6*/
 
 class Foo {
   bar() { }
@@ -59,6 +67,16 @@ class Foo {
   set bar(value) { }
 }
 
+class Foo {
+  bar;
+  qux;
+}
+
+class Foo {
+  bar;
+  qux() { }
+}
+
 class Foo {
   static bar() { }
   bar() { }
index df8f97a5ebadebc3c84845ca023239452e542326..7b0f5df142a721158417c3476d57a3559e33d643 100644 (file)
@@ -39,3 +39,11 @@ while (qux !== null) {
   baz();
 }
 ```
+
+## Compatibility
+
+* **JSHint**: This rule corresponds to `eqnull` rule of JSHint.
+
+## When Not To Use It
+
+If you want to enforce type-checking operations in general, use the more powerful [eqeqeq](./eqeqeq) instead.
index 9f7b71ad620adb5450f9abf6ff77930c28e2f408..4a2a3b797335bfb87ed9cf55dddfc0450e613a0d 100644 (file)
@@ -66,6 +66,14 @@ class A {
 
     eval() {
     }
+
+    static {
+        // This is a user-defined static method.
+        this.eval("var a = 0");
+    }
+
+    static eval() {
+    }
 }
 ```
 
index 68b986eff6c64f8b50cb26ae6e22ef885b0c8a69..b521dcbe2682797f0b62718d15726fc68e27b713 100644 (file)
@@ -48,6 +48,14 @@ for (a of (b));
 typeof (a);
 
 (function(){} ? a() : b());
+
+class A {
+    [(x)] = 1;
+}
+
+class B {
+    x = (y + z);
+}
 ```
 
 Examples of **correct** code for this rule with the default `"all"` option:
@@ -72,6 +80,14 @@ for (a of b);
 for (a in b, c);
 
 for (a in b);
+
+class A {
+    [x] = 1;
+}
+
+class B {
+    x = y + z;
+}
 ```
 
 ### conditionalAssign
index f4dd8e09c302232e706f006e1323216f5cddb947..a33a493c817dc59903af0e471610a7531c597638 100644 (file)
@@ -17,6 +17,17 @@ function foo() {
     // code
 };
 
+class C {
+    field;;
+
+    method() {
+        // code
+    };
+
+    static {
+        // code
+    };
+};
 ```
 
 Examples of **correct** code for this rule:
@@ -26,10 +37,25 @@ Examples of **correct** code for this rule:
 
 var x = 5;
 
-var foo = function() {
+function foo() {
+    // code
+}
+
+var bar = function() {
     // code
 };
 
+class C {
+    field;
+
+    method() {
+        // code
+    }
+
+    static {
+        // code
+    }
+}
 ```
 
 ## When Not To Use It
index 15ca77f661b94df4bc6a24fde8defc1a7a73d873..81b428cf070f35fe9502a5dfe8356f48ccba450c 100644 (file)
@@ -54,6 +54,17 @@ switch(foo) {
     case 2:
         doSomethingElse();
 }
+
+switch(foo) {
+    case 1: {
+        doSomething();
+        // falls through
+    }
+
+    case 2: {
+        doSomethingElse();
+    }
+}
 ```
 
 In this example, there is no confusion as to the expected behavior. It is clear that the first case is meant to fall through to the second case.
@@ -124,6 +135,17 @@ switch(foo) {
     case 2:
         doSomething();
 }
+
+switch(foo) {
+    case 1: {
+        doSomething();
+        // falls through
+    }
+
+    case 2: {
+        doSomethingElse();
+    }
+}
 ```
 
 Note that the last `case` statement in these examples does not cause a warning because there is nothing to fall through into.
index 04a3cd3fdbbff2f75118e325699f7e81c3d8a183..479d958d62c59b82560ff7a71c43adbcf6bbc0ca 100644 (file)
@@ -70,7 +70,7 @@ function bar() {}
 ### Global variable leaks
 
 When the code is not in `strict` mode, an assignment to an undeclared variable creates
-a new global variable. This will happen even is the code is in a function.
+a new global variable. This will happen even if the code is in a function.
 
 This does not apply to ES modules since the module code is implicitly in `strict` mode.
 
index 78b228dd828b12234d2cd8c93001f179694d7332..49595cda2f8b774c8b8be82f3702464f17bf8847 100644 (file)
@@ -16,8 +16,10 @@ import * as mod_ns from "./mod.mjs"
 
 mod = 1          // ERROR: 'mod' is readonly.
 named = 2        // ERROR: 'named' is readonly.
-mod_ns.named = 3 // ERROR: the members of 'mod_ns' is readonly.
+mod_ns.named = 3 // ERROR: The members of 'mod_ns' are readonly.
 mod_ns = {}      // ERROR: 'mod_ns' is readonly.
+// Can't extend 'mod_ns'
+Object.assign(mod_ns, { foo: "foo" }) // ERROR: The members of 'mod_ns' are readonly.
 ```
 
 Examples of **correct** code for this rule:
index 707fc6e781fa698060b8f48944fbea952c046c83..7c64e9dc19e2f5ebaf72cdc8616a9bcbefeacd15 100644 (file)
@@ -1,4 +1,4 @@
-# disallow variable or `function` declarations in nested blocks  (no-inner-declarations)
+# disallow variable or `function` declarations in nested blocks (no-inner-declarations)
 
 In JavaScript, prior to ES6, a function declaration is only allowed in the first level of a program or the body of another function, though parsers sometimes [erroneously accept them elsewhere](https://code.google.com/p/esprima/issues/detail?id=422). This only applies to function declarations; named or anonymous function expressions can occur anywhere an expression is permitted.
 
@@ -56,7 +56,7 @@ function doSomething() {
 
 ## Rule Details
 
-This rule requires that function declarations and, optionally, variable declarations be in the root of a program or the body of a function.
+This rule requires that function declarations and, optionally, variable declarations be in the root of a program, or in the root of the body of a function, or in the root of the body of a class static block.
 
 ## Options
 
@@ -83,6 +83,14 @@ function doSomethingElse() {
 }
 
 if (foo) function f(){}
+
+class C {
+    static {
+        if (test) {
+            function doSomething() { }
+        }
+    }
+}
 ```
 
 Examples of **correct** code for this rule with the default `"functions"` option:
@@ -96,6 +104,12 @@ function doSomethingElse() {
     function doAnotherThing() { }
 }
 
+class C {
+    static {
+        function doSomething() { }
+    }
+}
+
 if (test) {
     asyncCall(id, function (err, data) { });
 }
@@ -125,17 +139,23 @@ function doAnotherThing() {
     }
 }
 
-
 if (foo) var a;
 
 if (foo) function f(){}
+
+class C {
+    static {
+        if (test) {
+            var something;
+        }
+    }
+}
 ```
 
 Examples of **correct** code for this rule with the `"both"` option:
 
 ```js
 /*eslint no-inner-declarations: ["error", "both"]*/
-/*eslint-env es6*/
 
 var bar = 42;
 
@@ -146,6 +166,12 @@ if (test) {
 function doAnotherThing() {
     var baz = 81;
 }
+
+class C {
+    static {
+        var something;
+    }
+}
 ```
 
 ## When Not To Use It
index 7614b59a87bf91b40fbeecc9ce6d3c6f1794557d..f2f17d41a953543876778649e043ec4c555d3676 100644 (file)
@@ -18,7 +18,7 @@ This rule judges from following conditions whether or not the function is a meth
 
 * The function is on an object literal.
 * The function is assigned to a property.
-* The function is a method/getter/setter of ES2015 Classes. (excepts static methods)
+* The function is a method/getter/setter of ES2015 Classes.
 
 And this rule allows `this` keywords in functions below:
 
@@ -26,6 +26,11 @@ And this rule allows `this` keywords in functions below:
 * The function is a callback of array methods (such as `.forEach()`) if `thisArg` is given.
 * The function has `@this` tag in its JSDoc comment.
 
+And this rule always allows `this` keywords in the following contexts:
+
+* In class field initializers.
+* In class static blocks.
+
 Otherwise are considered problems.
 
 This rule applies **only** in strict mode.
@@ -166,6 +171,13 @@ Foo.prototype.foo = function foo() {
 };
 
 class Foo {
+
+    // OK, this is in a class field initializer.
+    a = this.b;
+
+    // OK, static initializers also have valid this.
+    static a = this.b;
+
     foo() {
         // OK, this is in a method.
         this.a = 0;
@@ -177,6 +189,12 @@ class Foo {
         this.a = 0;
         baz(() => this);
     }
+
+    static {
+        // OK, static blocks also have valid this.
+        this.a = 0;
+        baz(() => this);
+    }
 }
 
 var foo = (function foo() {
index 8cb45d6359fb143d73341a9f04fbf51c5fb63311..49fdda6f8acd27bf97e42cb9882994990b691bdd 100644 (file)
@@ -42,6 +42,14 @@ function bar() {
     aLabel: {
     }
 }
+
+class C {
+    static {
+        {
+            foo();
+        }
+    }
+}
 ```
 
 Examples of **correct** code for this rule with ES6 environment:
@@ -78,6 +86,18 @@ function bar() {
 
 aLabel: {
 }
+
+class C {
+    static {
+        lbl: {
+            if (something) {
+                break lbl;
+            }
+
+            foo();
+        }
+    }
+}
 ```
 
 Examples of **correct** code for this rule with ES6 environment and strict mode via `"parserOptions": { "sourceType": "module" }` in the ESLint configuration or `"use strict"` directive in the code:
index 7ef70567593763696ed2daa4640f4b28f6a2f769..14721dab0d87358bf38ad77a5c6901ec92989af8 100644 (file)
@@ -26,12 +26,19 @@ const foo = bar = "baz";
 let a =
     b =
     c;
+
+class Foo {
+    a = b = 10;
+}
+
+a = b = "quux";
 ```
 
 Examples of **correct** code for this rule:
 
 ```js
 /*eslint no-multi-assign: "error"*/
+
 var a = 5;
 var b = 5;
 var c = 5;
@@ -41,13 +48,21 @@ const bar = "baz";
 
 let a = c;
 let b = c;
+
+class Foo {
+    a = 10;
+    b = 10;
+}
+
+a = "quux";
+b = "quux";
 ```
 
 ## Options
 
 This rule has an object option:
 
-* `"ignoreNonDeclaration"`: When set to `true`, the rule allows chains that don't include initializing a variable in a declaration. Default is `false`.
+* `"ignoreNonDeclaration"`: When set to `true`, the rule allows chains that don't include initializing a variable in a declaration or initializing a class field. Default is `false`.
 
 ### ignoreNonDeclaration
 
@@ -73,6 +88,10 @@ Examples of **incorrect** code for the `{ "ignoreNonDeclaration": true }` option
 let a = b = "baz";
 
 const foo = bar = 1;
+
+class Foo {
+    a = b = 10;
+}
 ```
 
 ## Related Rules
index 16ac00c6acb41704bd0146aaf341e452c04815e1..7bdfc9ec7329f6f32f383d480f7c1bc42226fe55 100644 (file)
@@ -1,12 +1,16 @@
 # Disallow Function Constructor (no-new-func)
 
-It's possible to create functions in JavaScript using the `Function` constructor, such as:
+It's possible to create functions in JavaScript from strings at runtime using the `Function` constructor, such as:
 
 ```js
 var x = new Function("a", "b", "return a + b");
+var x = Function("a", "b", "return a + b");
+var x = Function.call(null, "a", "b", "return a + b");
+var x = Function.apply(null, ["a", "b", "return a + b"]);
+var x = Function.bind(null, "a", "b", "return a + b")();
 ```
 
-This is considered by many to be a bad practice due to the difficulty in debugging and reading these types of functions.
+This is considered by many to be a bad practice due to the difficulty in debugging and reading these types of functions. In addition, Content-Security-Policy (CSP) directives may disallow the use of eval() and similar methods for creating code from strings.
 
 ## Rule Details
 
@@ -19,6 +23,10 @@ Examples of **incorrect** code for this rule:
 
 var x = new Function("a", "b", "return a + b");
 var x = Function("a", "b", "return a + b");
+var x = Function.call(null, "a", "b", "return a + b");
+var x = Function.apply(null, ["a", "b", "return a + b"]);
+var x = Function.bind(null, "a", "b", "return a + b")();
+var f = Function.bind(null, "a", "b", "return a + b"); // assuming that the result of Function.bind(...) will be eventually called.
 ```
 
 Examples of **correct** code for this rule:
index c3b8422cf3f68ae0e71786d421b4095c8f68d730..bd029ebc1d4a50a19129d287c5de7c424c7eea00 100644 (file)
@@ -13,6 +13,18 @@ Examples of **incorrect** code for this rule:
 
 var a = 3;
 var a = 10;
+
+class C {
+    foo() {
+        var b = 3;
+        var b = 10;
+    }
+
+    static {
+        var c = 3;
+        var c = 10;
+    }
+}
 ```
 
 Examples of **correct** code for this rule:
@@ -21,8 +33,20 @@ Examples of **correct** code for this rule:
 /*eslint no-redeclare: "error"*/
 
 var a = 3;
-// ...
 a = 10;
+
+class C {
+    foo() {
+        var b = 3;
+        b = 10;
+    }
+
+    static {
+        var c = 3;
+        c = 10;
+    }
+}
+
 ```
 
 ## Options
index 10db58c17b0a684a6ee5f0b03449ac2d60d73e1e..0cf99c192961e4972c671e4098fa719bf43a3acd 100644 (file)
@@ -19,13 +19,12 @@ It's considered a best practice to avoid initializing variables to `undefined`.
 
 ## Rule Details
 
-This rule aims to eliminate variable declarations that initialize to `undefined`.
+This rule aims to eliminate `var` and `let` variable declarations that initialize to `undefined`.
 
 Examples of **incorrect** code for this rule:
 
 ```js
 /*eslint no-undef-init: "error"*/
-/*eslint-env es6*/
 
 var foo = undefined;
 let bar = undefined;
@@ -35,11 +34,29 @@ Examples of **correct** code for this rule:
 
 ```js
 /*eslint no-undef-init: "error"*/
-/*eslint-env es6*/
 
 var foo;
 let bar;
-const baz = undefined;
+```
+
+Please note that this rule does not check `const` declarations, destructuring patterns, function parameters, and class fields.
+
+Examples of additional **correct** code for this rule:
+
+```js
+/*eslint no-undef-init: "error"*/
+
+const foo = undefined;
+
+let { bar = undefined } = baz;
+
+[quux = undefined] = quuux;
+
+(foo = undefined) => {};
+
+class Foo {
+    bar = undefined;
+}
 ```
 
 ## When Not To Use It
index 7bd16d383c905a6c13663c2241dc94f8d7a9e86a..2fe8a6916e325a6741b62160c9d37b3fa3b6bf46 100644 (file)
@@ -10,9 +10,21 @@ function fn() {
 }
 ```
 
+Another kind of mistake is defining instance fields in a subclass whose constructor doesn't call `super()`. Instance fields of a subclass are only added to the instance after `super()`. If there are no `super()` calls, their definitions are never applied and therefore are unreachable code.
+
+```js
+class C extends B {
+    #x; // this will never be added to instances
+
+    constructor() {
+        return {};
+    }
+}
+```
+
 ## Rule Details
 
-This rule disallows unreachable code after `return`, `throw`, `continue`, and `break` statements.
+This rule disallows unreachable code after `return`, `throw`, `continue`, and `break` statements. This rule also flags definitions of instance fields in subclasses whose constructors don't have `super()` calls.
 
 Examples of **incorrect** code for this rule:
 
@@ -73,3 +85,57 @@ switch (foo) {
         var x;
 }
 ```
+
+Examples of additional **incorrect** code for this rule:
+
+```js
+/*eslint no-unreachable: "error"*/
+
+class C extends B {
+    #x; // unreachable
+    #y = 1; // unreachable
+    a; // unreachable
+    b = 1; // unreachable
+
+    constructor() {
+        return {};
+    }
+}
+```
+
+Examples of additional **correct** code for this rule:
+
+```js
+/*eslint no-unreachable: "error"*/
+
+class D extends B {
+    #x;
+    #y = 1;
+    a;
+    b = 1;
+
+    constructor() {
+        super();
+    }
+}
+
+class E extends B {
+    #x;
+    #y = 1;
+    a;
+    b = 1;
+
+    // implicit constructor always calls `super()`
+}
+
+class F extends B {
+    static #x;
+    static #y = 1;
+    static a;
+    static b = 1;
+
+    constructor() {
+        return {};
+    }
+}
+```
index 1557725a19301089eab77f8c33f39bde084fdbfe..89cfb334e802cec17d9c7758288aca6d5e9e91e5 100644 (file)
@@ -62,16 +62,6 @@ injectGlobal`body{ color: red; }`
 
 ```
 
-Note that one or more string expression statements (with or without semi-colons) will only be considered as unused if they are not in the beginning of a script, module, or function (alone and uninterrupted by other statements). Otherwise, they will be treated as part of a "directive prologue", a section potentially usable by JavaScript engines. This includes "strict mode" directives.
-
-```js
-"use strict";
-"use asm"
-"use stricter";
-"use babel"
-"any other strings like this in the prologue";
-```
-
 Examples of **correct** code for the default `{ "allowShortCircuit": false, "allowTernary": false }` options:
 
 ```js
@@ -96,6 +86,50 @@ delete a.b
 void a
 ```
 
+Note that one or more string expression statements (with or without semi-colons) will only be considered as unused if they are not in the beginning of a script, module, or function (alone and uninterrupted by other statements). Otherwise, they will be treated as part of a "directive prologue", a section potentially usable by JavaScript engines. This includes "strict mode" directives.
+
+Examples of **correct** code for this rule in regard to directives:
+
+```js
+/*eslint no-unused-expressions: "error"*/
+
+"use strict";
+"use asm"
+"use stricter";
+"use babel"
+"any other strings like this in the directive prologue";
+"this is still the directive prologue";
+
+function foo() {
+    "bar";
+}
+
+class Foo {
+    someMethod() {
+        "use strict";
+    }
+}
+```
+
+Examples of **incorrect** code for this rule in regard to directives:
+
+```js
+/*eslint no-unused-expressions: "error"*/
+
+doSomething();
+"use strict"; // this isn't in a directive prologue, because there is a non-directive statement before it
+
+function foo() {
+    "bar" + 1;
+}
+
+class Foo {
+    static {
+        "use strict"; // class static blocks do not have directive prologues
+    }
+}
+```
+
 ### allowShortCircuit
 
 Examples of **incorrect** code for the `{ "allowShortCircuit": true }` option:
diff --git a/eslint/docs/rules/no-unused-private-class-members.md b/eslint/docs/rules/no-unused-private-class-members.md
new file mode 100644 (file)
index 0000000..2eb9f4b
--- /dev/null
@@ -0,0 +1,78 @@
+# Disallow Unused Private Class Members (no-unused-private-class-members)
+
+Private class members that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such class members take up space in the code and can lead to confusion by readers.
+
+## Rule Details
+
+This rule reports unused private class members.
+
+* A private field or method is considered to be unused if its value is never read.
+* A private accessor is considered to be unused if it is never accessed (read or write).
+
+Examples of **incorrect** code for this rule:
+
+```js
+/*eslint no-unused-private-class-members: "error"*/
+
+class Foo {
+    #unusedMember = 5;
+}
+
+class Foo {
+    #usedOnlyInWrite = 5;
+    method() {
+        this.#usedOnlyInWrite = 42;
+    }
+}
+
+class Foo {
+    #usedOnlyToUpdateItself = 5;
+    method() {
+        this.#usedOnlyToUpdateItself++;
+    }
+}
+
+class Foo {
+    #unusedMethod() {}
+}
+
+class Foo {
+    get #unusedAccessor() {}
+    set #unusedAccessor(value) {}
+}
+```
+
+Examples of **correct** code for this rule:
+
+```js
+/*eslint no-unused-private-class-members: "error"*/
+
+class Foo {
+    #usedMember = 42;
+    method() {
+        return this.#usedMember;
+    }
+}
+
+class Foo {
+    #usedMethod() {
+        return 42;
+    }
+    anotherMethod() {
+        return this.#usedMethod();
+    }
+}
+
+class Foo {
+    get #usedAccessor() {}
+    set #usedAccessor(value) {}
+    
+    method() {
+        this.#usedAccessor = 42;
+    }
+}
+```
+
+## When Not To Use It
+
+If you don't want to be notified about unused private class members, you can safely turn this rule off.
index 391c323aed06e9cbba07c9a1f56172cded8b5b90..8af260ea0ad452d78390d2f5f07cbac9834fde55 100644 (file)
@@ -12,7 +12,6 @@ Examples of **incorrect** code for this rule:
 
 ```js
 /*eslint no-use-before-define: "error"*/
-/*eslint-env es6*/
 
 alert(a);
 var a = 10;
@@ -29,13 +28,37 @@ var b = 1;
     alert(c);
     let c = 1;
 }
+
+{
+    class C extends C {}
+}
+
+{
+    class C {
+        static x = "foo";
+        [C.x]() {}
+    }
+}
+
+{
+    const C = class {
+        static x = C;
+    }
+}
+
+{
+    const C = class {
+        static {
+            C.x = "foo";
+        }
+    }
+}
 ```
 
 Examples of **correct** code for this rule:
 
 ```js
 /*eslint no-use-before-define: "error"*/
-/*eslint-env es6*/
 
 var a;
 a = 10;
@@ -53,13 +76,39 @@ function g() {
     let c;
     c++;
 }
+
+{
+    class C {
+        static x = C;
+    }
+}
+
+{
+    const C = class C {
+        static x = C;
+    }
+}
+
+{
+    const C = class {
+        x = C;
+    }
+}
+
+{
+    const C = class C {
+        static {
+            C.x = "foo";
+        }
+    }
+}
 ```
 
 ## Options
 
 ```json
 {
-    "no-use-before-define": ["error", { "functions": true, "classes": true }]
+    "no-use-before-define": ["error", { "functions": true, "classes": true, "variables": true }]
 }
 ```
 
@@ -103,18 +152,41 @@ Examples of **incorrect** code for the `{ "classes": false }` option:
 
 ```js
 /*eslint no-use-before-define: ["error", { "classes": false }]*/
-/*eslint-env es6*/
 
 new A();
 class A {
 }
+
+{
+    class C extends C {}
+}
+
+{
+    class C extends D {}
+    class D {}
+}
+
+{
+    class C {
+        static x = "foo";
+        [C.x]() {}
+    }
+}
+
+{
+    class C {
+        static {
+            new D();
+        }
+    }
+    class D {}
+}
 ```
 
 Examples of **correct** code for the `{ "classes": false }` option:
 
 ```js
 /*eslint no-use-before-define: ["error", { "classes": false }]*/
-/*eslint-env es6*/
 
 function foo() {
     return new A();
@@ -139,6 +211,28 @@ const f = () => {};
 
 g();
 const g = function() {};
+
+{
+    const C = class {
+        static x = C;
+    }
+}
+
+{
+    const C = class {
+        static x = foo;
+    }
+    const foo = 1;
+}
+
+{
+    class C {
+        static {
+            this.x = foo;
+        }
+    }
+    const foo = 1;
+}
 ```
 
 Examples of **correct** code for the `{ "variables": false }` option:
@@ -158,4 +252,11 @@ const f = () => {};
 
 const e = function() { return g(); }
 const g = function() {}
+
+{
+    const C = class {
+        x = foo;
+    }
+    const foo = 1;
+}
 ```
index 9b3c592849b3be31b94aa03ad0d3607586e700ef..2d3ddb3d50b71ed93b29a5bf2680e9a49c1ac3c4 100644 (file)
@@ -20,7 +20,6 @@ Examples of **incorrect** code for this rule:
 
 ```js
 /*eslint no-useless-computed-key: "error"*/
-/*eslint-env es6*/
 
 var a = { ['0']: 0 };
 var a = { ['0+1,234']: 0 };
@@ -41,6 +40,18 @@ var c = { a: 0 };
 var c = { '0+1,234': 0 };
 ```
 
+Examples of additional **correct** code for this rule:
+
+```js
+/*eslint no-useless-computed-key: "error"*/
+
+var c = {
+    "__proto__": foo, // defines object's prototype
+
+    ["__proto__"]: bar // defines a property named "__proto__"
+};
+```
+
 ## Options
 
 This rule has an object option:
@@ -52,24 +63,64 @@ This rule has an object option:
 By default, this rule does not check class declarations and class expressions,
 as the default value for `enforceForClassMembers` is `false`.
 
-When `enforceForClassMembers` is set to `true`, the rule will also disallow unnecessary computed
-keys inside of class methods, getters and setters.
+When `enforceForClassMembers` is set to `true`, the rule will also disallow unnecessary computed keys inside of class fields, class methods, class getters, and class setters.
 
-Examples of **incorrect** code for `{ "enforceForClassMembers": true }`:
+Examples of **incorrect** code for this rule with the `{ "enforceForClassMembers": true }` option:
 
 ```js
 /*eslint no-useless-computed-key: ["error", { "enforceForClassMembers": true }]*/
 
 class Foo {
+    ["foo"] = "bar";
+
     [0]() {}
     ['a']() {}
     get ['b']() {}
     set ['c'](value) {}
 
+    static ["foo"] = "bar";
+
     static ['a']() {}
 }
 ```
 
+Examples of **correct** code for this rule with the `{ "enforceForClassMembers": true }` option:
+
+```js
+/*eslint no-useless-computed-key: ["error", { "enforceForClassMembers": true }]*/
+
+class Foo {
+    "foo" = "bar";
+
+    0() {}
+    'a'() {}
+    get 'b'() {}
+    set 'c'(value) {}
+
+    static "foo" = "bar";
+
+    static 'a'() {}
+}
+```
+
+Examples of additional **correct** code for this rule with the `{ "enforceForClassMembers": true }` option:
+
+```js
+/*eslint no-useless-computed-key: ["error", { "enforceForClassMembers": true }]*/
+
+class Foo {
+    ["constructor"]; // instance field named "constructor"
+
+    "constructor"() {} // the constructor of this class
+
+    ["constructor"]() {} // method named "constructor"
+
+    static ["constructor"]; // static field named "constructor"
+
+    static ["prototype"]; // runtime error, it would be a parsing error without `[]`
+}
+```
+
 ## When Not To Use It
 
 If you don't want to be notified about unnecessary computed property keys, you can safely disable this rule.
index 3c964c4a4fddb889f1a9f759499a03020d81109b..df7ee1f60d565c590245e9c7c438ba91448320b3 100644 (file)
@@ -66,7 +66,6 @@ Examples of **incorrect** code for this rule with the default `"always"` option:
 
 ```js
 /*eslint one-var: ["error", "always"]*/
-/*eslint-env es6*/
 
 function foo() {
     var bar;
@@ -89,13 +88,31 @@ function foo() {
         var qux = true;
     }
 }
+
+class C {
+    static {
+        var foo;
+        var bar;
+    }
+
+    static {
+        var foo;
+        if (bar) {
+            var baz = true;
+        }
+    }
+
+    static {
+        let foo;
+        let bar;
+    }
+}
 ```
 
 Examples of **correct** code for this rule with the default `"always"` option:
 
 ```js
 /*eslint one-var: ["error", "always"]*/
-/*eslint-env es6*/
 
 function foo() {
     var bar,
@@ -127,6 +144,30 @@ function foo(){
         let qux;
     }
 }
+
+class C {
+    static {
+        var foo, bar;
+    }
+
+    static {
+        var foo, baz;
+        if (bar) {
+            baz = true;
+        }
+    }
+
+    static {
+        let foo, bar;
+    }
+
+    static {
+        let foo;
+        if (bar) {
+            let baz;
+        }
+    }
+}
 ```
 
 ### never
@@ -135,7 +176,6 @@ Examples of **incorrect** code for this rule with the `"never"` option:
 
 ```js
 /*eslint one-var: ["error", "never"]*/
-/*eslint-env es6*/
 
 function foo() {
     var bar,
@@ -157,13 +197,19 @@ function foo(){
     let bar = true,
         baz = false;
 }
+
+class C {
+    static {
+        var foo, bar;
+        let baz, qux;
+    }
+}
 ```
 
 Examples of **correct** code for this rule with the `"never"` option:
 
 ```js
 /*eslint one-var: ["error", "never"]*/
-/*eslint-env es6*/
 
 function foo() {
     var bar;
@@ -185,6 +231,15 @@ function foo() {
         let qux = true;
     }
 }
+
+class C {
+    static {
+        var foo;
+        var bar;
+        let baz;
+        let qux;
+    }
+}
 ```
 
 ### consecutive
@@ -193,7 +248,6 @@ Examples of **incorrect** code for this rule with the `"consecutive"` option:
 
 ```js
 /*eslint one-var: ["error", "consecutive"]*/
-/*eslint-env es6*/
 
 function foo() {
     var bar;
@@ -209,14 +263,21 @@ function foo(){
     var qux = 3;
     var quux;
 }
+
+class C {
+    static {
+        var foo;
+        var bar;
+        let baz;
+        let qux;
+    }
+}
 ```
 
 Examples of **correct** code for this rule with the `"consecutive"` option:
 
 ```js
 /*eslint one-var: ["error", "consecutive"]*/
-/*eslint-env es6*/
-
 
 function foo() {
     var bar,
@@ -232,6 +293,16 @@ function foo(){
     var qux = 3,
         quux;
 }
+
+class C {
+    static {
+        var foo, bar;
+        let baz, qux;
+        doSomething();
+        let quux;
+        var quuux;
+    }
+}
 ```
 
 ### var, let, and const
index 159bd528ad597201771b98f58e0c1b5630df36ce..6ae484ea5cdb54f02864b8657db13a5f1b27c360 100644 (file)
@@ -60,6 +60,16 @@ if (someCondition
 answer = everything
   ? 42
   : foo;
+
+class Foo {
+    a
+        = 1;
+    [b]
+        = 2;
+    [c
+    ]
+        = 3;
+}
 ```
 
 Examples of **correct** code for this rule with the `"after"` option:
@@ -82,6 +92,17 @@ if (someCondition ||
 answer = everything ?
   42 :
   foo;
+
+class Foo {
+    a =
+        1;
+    [b] =
+        2;
+    [c
+    ] =
+        3;
+    d = 4;
+}
 ```
 
 ### before
@@ -104,6 +125,16 @@ if (someCondition ||
 answer = everything ?
   42 :
   foo;
+
+class Foo {
+    a =
+        1;
+    [b] =
+        2;
+    [c
+    ] =
+        3;
+}
 ```
 
 Examples of **correct** code for this rule with the `"before"` option:
@@ -126,6 +157,17 @@ if (someCondition
 answer = everything
   ? 42
   : foo;
+
+class Foo {
+    a
+        = 1;
+    [b]
+        = 2;
+    [c
+    ]
+        = 3;
+    d = 4;
+}
 ```
 
 ### none
@@ -156,6 +198,23 @@ answer = everything
 answer = everything ?
   42 :
   foo;
+
+class Foo {
+    a =
+        1;
+    [b] =
+        2;
+    [c
+    ] =
+        3;
+    d
+        = 4;
+    [e]
+        = 5;
+    [f
+    ]
+        = 6;
+}
 ```
 
 Examples of **correct** code for this rule with the `"none"` option:
@@ -171,6 +230,17 @@ if (someCondition || otherCondition) {
 }
 
 answer = everything ? 42 : foo;
+
+class Foo {
+    a = 1;
+    [b] = 2;
+    [c
+    ] = 3;
+    d = 4;
+    [e] = 5;
+    [f
+    ] = 6;
+}
 ```
 
 ### overrides
index d8b855ed37fd6966a7d64335b4d567daf9b1a690..85c0a8b9ced50b239f8db64de50aa2833e8b42ff 100644 (file)
@@ -27,12 +27,12 @@ The second one is an object option, it can allow exceptions.
 
 String option:
 
-* `"always"` (default) requires empty lines at the beginning and ending of block statements and classes
-* `"never"` disallows empty lines at the beginning and ending of block statements and classes
+* `"always"` (default) requires empty lines at the beginning and ending of block statements, function bodies, class static blocks, classes, and `switch` statements.
+* `"never"` disallows empty lines at the beginning and ending of block statements, function bodies, class static blocks, classes, and `switch` statements.
 
 Object option:
 
-* `"blocks"` require or disallow padding within block statements
+* `"blocks"` require or disallow padding within block statements, function bodies, and class static blocks
 * `"classes"` require or disallow padding within classes
 * `"switches"` require or disallow padding within `switch` statements
 
@@ -68,6 +68,12 @@ if (a) {
     b();
 
 }
+
+class C {
+    static {
+        a();
+    }
+}
 ```
 
 Examples of **correct** code for this rule with the default `"always"` option:
@@ -93,6 +99,16 @@ if (a) {
     // comment
     b();
 
+}
+
+class C {
+
+    static {
+
+        a();
+
+    }
+
 }
 ```
 
@@ -124,6 +140,16 @@ if (a) {
 if (a) {
     b();
 
+}
+
+class C {
+
+    static {
+
+        a();
+
+    }
+
 }
 ```
 
@@ -140,6 +166,12 @@ if (a)
 {
     b();
 }
+
+class C {
+    static {
+        a();
+    }
+}
 ```
 
 ### blocks
@@ -174,6 +206,14 @@ if (a) {
     // comment
     b();
 
+}
+
+class C {
+
+    static {
+        a();
+    }
+
 }
 ```
 
@@ -200,6 +240,25 @@ if (a) {
     // comment
     b();
 
+}
+
+class C {
+
+    static {
+
+        a();
+
+    }
+
+}
+
+class D {
+    static {
+
+        a();
+
+    }
+
 }
 ```
 
@@ -230,6 +289,14 @@ if (a) {
     b();
 
 }
+
+class C {
+    static {
+
+        a();
+
+    }
+}
 ```
 
 Examples of **correct** code for this rule with the `{ "blocks": "never" }` option:
@@ -245,6 +312,20 @@ if (a)
 {
     b();
 }
+
+class C {
+    static {
+        a();
+    }
+}
+
+class D {
+
+    static {
+        a();
+    }
+
+}
 ```
 
 ### classes
index 2f2f67ee07cece632f315204626fbe2aee083010..065c7d937c901aa77d62b27b09fd913d928bcb14 100644 (file)
@@ -147,6 +147,13 @@ function foo() {
     const a = 0;
     bar();
 }
+
+class C {
+    static {
+        let a = 0;
+        bar();
+    }
+}
 ```
 
 Examples of **correct** code for the `[{ blankLine: "always", prev: ["const", "let", "var"], next: "*"}, { blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"]}]` configuration:
@@ -178,6 +185,15 @@ function foo() {
 
     bar();
 }
+
+class C {
+    static {
+        let a = 0;
+        let b = 0;
+
+        bar();
+    }
+}
 ```
 
 ----
index 3e5a5d0c3feb3395cd35d301437dcd2c035579b5..3cdba9e4da00c42c707542eb84c0d1e8cbfb00f0 100644 (file)
@@ -12,7 +12,6 @@ Examples of **incorrect** code for this rule:
 
 ```js
 /*eslint prefer-const: "error"*/
-/*eslint-env es6*/
 
 // it's initialized and never reassigned.
 let a = 3;
@@ -22,6 +21,14 @@ let a;
 a = 0;
 console.log(a);
 
+class C {
+    static {
+        let a;
+        a = 0;
+        console.log(a);
+    }
+}
+
 // `i` is redefined (not reassigned) on each loop step.
 for (let i in [1, 2, 3]) {
     console.log(i);
@@ -37,7 +44,6 @@ Examples of **correct** code for this rule:
 
 ```js
 /*eslint prefer-const: "error"*/
-/*eslint-env es6*/
 
 // using const.
 const a = 0;
@@ -59,6 +65,15 @@ if (true) {
 }
 console.log(a);
 
+// it's initialized in a different scope.
+let a;
+class C {
+    #x;
+    static {
+        a = obj => obj.#x;
+    }
+}
+
 // it's initialized at a place that we cannot write a variable declaration.
 let a;
 if (true) a = 0;
index 7f18559ed501ce8e61baa50ecb4f0df284fd8867..64e914fcee4931f694e375c5f592a580177a3180 100644 (file)
@@ -62,6 +62,17 @@ Examples of **correct** code when `enforceForRenamedProperties` is enabled:
 var { bar: foo } = object;
 ```
 
+Examples of additional **correct** code when `enforceForRenamedProperties` is enabled:
+
+```javascript
+class C {
+    #x;
+    foo() {
+        const bar = this.#x; // private identifiers are not allowed in destructuring
+    }
+}
+```
+
 An example configuration, with the defaults `array` and `object` filled in, looks like this:
 
 ```json
index ff8790c836d62ed103fa1267c6e0bf4c0afc7b96..2caf9518eabd65718fe85b0f1aa29f5fa757171d 100644 (file)
@@ -1,14 +1,20 @@
 # Suggest using named capture group in regular expression (prefer-named-capture-group)
 
+
+## Rule Details
+
 With the landing of ECMAScript 2018, named capture groups can be used in regular expressions, which can improve their readability.
+This rule is aimed at using named capture groups instead of numbered capture groups in regular expressions:
 
 ```js
 const regex = /(?<year>[0-9]{4})/;
 ```
 
-## Rule Details
+Alternatively, if your intention is not to _capture_ the results, but only express the alternative, use a non-capturing group:
 
-This rule is aimed at using named capture groups instead of numbered capture groups in regular expressions.
+```js
+const regex = /(?:cauli|sun)flower/;
+```
 
 Examples of **incorrect** code for this rule:
 
@@ -30,13 +36,14 @@ Examples of **correct** code for this rule:
 const foo = /(?<id>ba[rz])/;
 const bar = new RegExp('(?<id>ba[rz])');
 const baz = RegExp('(?<id>ba[rz])');
+const xyz = /xyz(?:zy|abc)/;
 
 foo.exec('bar').groups.id; // Retrieve the group result.
 ```
 
 ## When Not To Use It
 
-If you are targeting ECMAScript 2017 and/or older environments, you can disable this rule, because this ECMAScript feature is only supported in ECMAScript 2018 and/or newer environments.
+If you are targeting ECMAScript 2017 and/or older environments, you should not use this rule, because this ECMAScript feature is only supported in ECMAScript 2018 and/or newer environments.
 
 ## Related Rules
 
index 8dd313815c805b95c5b3387f9f8d4b13e3589fda..64fae21d61990af0f3cf4fa9a91f7822c734f476 100644 (file)
@@ -38,11 +38,22 @@ Promise.all([getPageLength(1), getPageLength(2)]).then(pageLengths => {
 
 ## Rule Details
 
-This rule aims to report assignments to variables or properties where all of the following are true:
+This rule aims to report assignments to variables or properties in cases where the assignments may be based on outdated values.
 
-* A variable or property is reassigned to a new value which is based on its old value.
-* A `yield` or `await` expression interrupts the assignment after the old value is read, and before the new value is set.
-* The rule cannot easily verify that the assignment is safe (e.g. if an assigned variable is local and would not be readable from anywhere else while the function is paused).
+### Variables
+
+This rule reports an assignment to a variable when it detects the following execution flow in a generator or async function:
+
+1. The variable is read.
+2. A `yield` or `await` pauses the function.
+3. After the function is resumed, a value is assigned to the variable from step 1.
+
+The assignment in step 3 is reported because it may be incorrectly resolved because the value of the variable from step 1 may have changed between steps 2 and 3. In particular, if the variable can be accessed from other execution contexts (for example, if it is not a local variable and therefore other functions can change it), the value of the variable may have changed elsewhere while the function was paused in step 2.
+
+Note that the rule does not report the assignment in step 3 in any of the following cases:
+
+* If the variable is read again between steps 2 and 3.
+* If the variable cannot be accessed while the function is paused (for example, if it's a local variable).
 
 Examples of **incorrect** code for this rule:
 
@@ -50,20 +61,27 @@ Examples of **incorrect** code for this rule:
 /* eslint require-atomic-updates: error */
 
 let result;
-async function foo() {
-  result += await somethingElse;
 
-  result = result + await somethingElse;
+async function foo() {
+    result += await something;
+}
 
-  result = result + doSomething(await somethingElse);
+async function bar() {
+    result = result + await something;
 }
 
-function* bar() {
-  result += yield;
+async function baz() {
+    result = result + doSomething(await somethingElse);
+}
 
-  result = result + (yield somethingElse);
+async function qux() {
+    if (!result) {
+        result = await initialize();
+    }
+}
 
-  result = result + doSomething(yield somethingElse);
+function* generator() {
+    result += yield;
 }
 ```
 
@@ -73,22 +91,89 @@ Examples of **correct** code for this rule:
 /* eslint require-atomic-updates: error */
 
 let result;
-async function foo() {
-  result = await somethingElse + result;
 
-  let tmp = await somethingElse;
-  result += tmp;
+async function foobar() {
+    result = await something + result;
+}
+
+async function baz() {
+    const tmp = doSomething(await somethingElse);
+    result += tmp;
+}
+
+async function qux() {
+    if (!result) {
+        const tmp = await initialize();
+        if (!result) {
+            result = tmp;
+        }
+    }
+}
+
+async function quux() {
+    let localVariable = 0;
+    localVariable += await something;
+}
+
+function* generator() {
+    result = (yield) + result;
+}
+```
+
+### Properties
+
+This rule reports an assignment to a property through a variable when it detects the following execution flow in a generator or async function:
+
+1. The variable or object property is read.
+2. A `yield` or `await` pauses the function.
+3. After the function is resumed, a value is assigned to a property.
 
-  let localVariable = 0;
-  localVariable += await somethingElse;
+This logic is similar to the logic for variables, but stricter because the property in step 3 doesn't have to be the same as the property in step 1. It is assumed that the flow depends on the state of the object as a whole.
+
+Example of **incorrect** code for this rule:
+
+```js
+/* eslint require-atomic-updates: error */
+
+async function foo(obj) {
+    if (!obj.done) {
+        obj.something = await getSomething();
+    }
 }
+```
 
-function* bar() {
-  result = (yield) + result;
+Example of **correct** code for this rule:
 
-  result = (yield somethingElse) + result;
+```js
+/* eslint require-atomic-updates: error */
+
+async function foo(obj) {
+    if (!obj.done) {
+        const tmp = await getSomething();
+        if (!obj.done) {
+            obj.something = tmp;
+        }
+    }
+}
+```
+
+## Options
+
+This rule has an object option:
+
+* `"allowProperties"`: When set to `true`, the rule does not report assignments to properties. Default is `false`.
+
+### allowProperties
+
+Example of **correct** code for this rule with the `{ "allowProperties": true }` option:
+
+```js
+/* eslint require-atomic-updates: ["error", { "allowProperties": true }] */
 
-  result = doSomething(yield somethingElse, result);
+async function foo(obj) {
+    if (!obj.done) {
+        obj.something = await getSomething();
+    }
 }
 ```
 
index 7bd916f9d68c97e600b36630a6a91819fcd29092..368bb532a2d7fe192729615cc228d3567e860bfc 100644 (file)
@@ -32,6 +32,13 @@ for (
 ) {
     foo()
 }
+
+class C {
+    static {
+        foo()
+        ;bar()
+    }
+}
 ```
 
 Examples of **correct** code for this rule with `"last"` option:
@@ -49,6 +56,13 @@ for (
 ) {
     foo()
 }
+
+class C {
+    static {
+        foo();
+        bar()
+    }
+}
 ```
 
 Examples of **incorrect** code for this rule with `"first"` option:
@@ -66,6 +80,13 @@ for (
 ) {
     foo()
 }
+
+class C {
+    static {
+        foo();
+        bar()
+    }
+}
 ```
 
 Examples of **correct** code for this rule with `"first"` option:
@@ -83,6 +104,13 @@ for (
 ) {
     foo()
 }
+
+class C {
+    static {
+        foo()
+        ;bar()
+    }
+}
 ```
 
 ## When Not To Use It
index 049ae41cf16e1ada7093fa2a092012667e8c324c..cb8e6dd0dabd4dd54efb383ad9073d3aa260e3cd 100644 (file)
@@ -76,6 +76,8 @@ Object option (when `"never"`):
 * `"beforeStatementContinuationChars": "always"` requires semicolons at the end of statements if the next line starts with `[`, `(`, `/`, `+`, or `-`.
 * `"beforeStatementContinuationChars": "never"` disallows semicolons as the end of statements if it doesn't make ASI hazard even if the next line starts with `[`, `(`, `/`, `+`, or `-`.
 
+**Note:** `beforeStatementContinuationChars` does not apply to class fields because class fields are not statements.
+
 ### always
 
 Examples of **incorrect** code for this rule with the default `"always"` option:
@@ -88,6 +90,10 @@ var name = "ESLint"
 object.method = function() {
     // ...
 }
+
+class Foo {
+    bar = 1
+}
 ```
 
 Examples of **correct** code for this rule with the default `"always"` option:
@@ -100,6 +106,10 @@ var name = "ESLint";
 object.method = function() {
     // ...
 };
+
+class Foo {
+    bar = 1;
+}
 ```
 
 ### never
@@ -114,6 +124,10 @@ var name = "ESLint";
 object.method = function() {
     // ...
 };
+
+class Foo {
+    bar = 1;
+}
 ```
 
 Examples of **correct** code for this rule with the `"never"` option:
@@ -142,6 +156,10 @@ import b from "b"
 ;(function() {
     // ...
 })()
+
+class Foo {
+    bar = 1
+}
 ```
 
 #### omitLastInOneLineBlock
@@ -154,6 +172,14 @@ Examples of additional **correct** code for this rule with the `"always", { "omi
 if (foo) { bar() }
 
 if (foo) { bar(); baz() }
+
+function f() { bar(); baz() }
+
+class C {
+    foo() { bar(); baz() }
+
+    static { bar(); baz() }
+}
 ```
 
 #### beforeStatementContinuationChars
index af1b739fe0e8d5c6db095eec70dfa6e6b56661fd..7f6182b227a70ade7145524ac98280207ee51415 100644 (file)
@@ -2,7 +2,7 @@
 
 (removed) This rule was **removed** in ESLint v2.0 and replaced by the [keyword-spacing](keyword-spacing.md) rule.
 
-(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fix) automatically fixed problems reported by this rule.
+(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#--fix) automatically fixed problems reported by this rule.
 
 Some style guides will require or disallow spaces following the certain keywords.
 
index 25b81cae222bab962d05b9ba6c46dc9dfee9d397..35a4c16c3ce0625b98f46bf6b1daf9c89a8721d7 100644 (file)
@@ -11,6 +11,7 @@ This rule will enforce consistency of spacing before blocks. It is only applied
 
 * This rule ignores spacing which is between `=>` and a block. The spacing is handled by the `arrow-spacing` rule.
 * This rule ignores spacing which is between a keyword and a block. The spacing is handled by the `keyword-spacing` rule.
+* This rule ignores spacing which is between `:` of a switch case and a block. The spacing is handled by the `switch-colon-spacing` rule.
 
 ## Options
 
@@ -62,6 +63,9 @@ if (a) {
     c();
 }
 
+class C {
+    static{} /*no error. this is checked by `keyword-spacing` rule.*/
+}
 
 function a() {}
 
@@ -210,4 +214,5 @@ You can turn this rule off if you are not concerned with the consistency of spac
 
 * [keyword-spacing](keyword-spacing.md)
 * [arrow-spacing](arrow-spacing.md)
+* [switch-colon-spacing](switch-colon-spacing.md)
 * [brace-style](brace-style.md)
index 8adf593a6c7e26385190a71d920f00ce2abd2756..077c9a322fafedd56e0d00a95abbcc424aef66dc 100644 (file)
@@ -2,7 +2,7 @@
 
 (removed) This rule was **removed** in ESLint v2.0 and **replaced** by the [keyword-spacing](keyword-spacing.md) rule.
 
-(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fix) automatically fixed problems reported by this rule.
+(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#--fix) automatically fixed problems reported by this rule.
 
 Keywords are syntax elements of JavaScript, such as `function` and `if`. These identifiers have special meaning to the language and so often appear in a different color in code editors. As an important part of the language, style guides often refer to the spacing that should be used around keywords. For example, you might have a style guide that says keywords should be always be preceded by spaces, which would mean `if-else` statements must look like this:
 
index 024eb3c9586a568a765b350e11ad379c62b2016c..ff55f7f5effbf266c6eaaf7deeb4257d24124f04 100644 (file)
@@ -2,7 +2,7 @@
 
 (removed) This rule was **removed** in ESLint v2.0 and **replaced** by the [keyword-spacing](keyword-spacing.md) rule.
 
-(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fix) automatically fixed problems reported by this rule.
+(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#--fix) automatically fixed problems reported by this rule.
 
 Require spaces following `return`, `throw`, and `case`.
 
index a6d87c20ac1e41c7860483d58667a8bc911d3690..97285afc0254d0f6b80dd671d7991b49c0c20e0f 100644 (file)
@@ -49,6 +49,8 @@ This rule disallows strict mode directives, no matter which option is specified,
 
 This rule disallows strict mode directives, no matter which option is specified, in functions with non-simple parameter lists (for example, parameter lists with default parameter values) because that is a syntax error in **ECMAScript 2016** and later. See the examples of the [function](#function) option.
 
+This rule does not apply to class static blocks, no matter which option is specified, because class static blocks do not have directives. Therefore, a `"use strict"` statement in a class static block is not a directive, and will be reported by the [no-unused-expressions](no-unused-expressions.md) rule.
+
 The `--fix` option on the command line does not insert new `"use strict"` statements, but only removes unneeded statements.
 
 ## Options
index ae9fefe36afe7dba34cb53543dea1bbdaf7ff6fa..67c83c96a9072fa28d02bf890b82976355f39609 100644 (file)
@@ -25,6 +25,14 @@ if (foo == NaN) {
 if (foo != NaN) {
     // ...
 }
+
+if (foo == Number.NaN) {
+    // ...
+}
+
+if (foo != Number.NaN) {
+    // ...
+}
 ```
 
 Examples of **correct** code for this rule:
@@ -77,6 +85,26 @@ switch (NaN) {
         break;
     // ...
 }
+
+switch (foo) {
+    case Number.NaN:
+        bar();
+        break;
+    case 1:
+        baz();
+        break;
+    // ...
+}
+
+switch (Number.NaN) {
+    case a:
+        bar();
+        break;
+    case b:
+        baz();
+        break;
+    // ...
+}
 ```
 
 Examples of **correct** code for this rule with `"enforceForSwitchCase"` option set to `true` (default):
@@ -126,6 +154,26 @@ switch (NaN) {
         break;
     // ...
 }
+
+switch (foo) {
+    case Number.NaN:
+        bar();
+        break;
+    case 1:
+        baz();
+        break;
+    // ...
+}
+
+switch (Number.NaN) {
+    case a:
+        bar();
+        break;
+    case b:
+        baz();
+        break;
+    // ...
+}
 ```
 
 ### enforceForIndexOf
index 78a8bee86b6cc20d6ee9502f965aac000d61736f..c8ce81e8191888c9a2b645087c4f68133bf0ce1c 100644 (file)
@@ -14,11 +14,10 @@ Examples of **incorrect** code for this rule:
 ```js
 /*eslint vars-on-top: "error"*/
 
-// Variable declarations in a block:
+// Variable declaration in a nested block, and a variable declaration after other statements:
 function doSomething() {
-    var first;
     if (true) {
-        first = true;
+        var first = true;
     }
     var second;
 }
@@ -32,11 +31,34 @@ function doSomething() {
 ```js
 /*eslint vars-on-top: "error"*/
 
-// Variables after other statements:
+// Variable declaration after other statements:
 f();
 var a;
 ```
 
+```js
+/*eslint vars-on-top: "error"*/
+
+// Variables in class static blocks should be at the top of the static blocks.
+
+class C {
+
+    // Variable declaration in a nested block:
+    static {
+        if (something) {
+            var a = true;
+        }
+    }
+
+    // Variable declaration after other statements:
+    static {
+        f();
+        var a;
+    }
+
+}
+```
+
 Examples of **correct** code for this rule:
 
 ```js
@@ -66,6 +88,26 @@ f();
 ```js
 /*eslint vars-on-top: "error"*/
 
+class C {
+
+    static {
+        var a;
+        if (something) {
+            a = true;
+        }
+    }
+
+    static {
+        var a;
+        f();
+    }
+
+}
+```
+
+```js
+/*eslint vars-on-top: "error"*/
+
 // Directives may precede variable declarations.
 "use strict";
 var a;
index 99fd61eb950b9d49fe3e7742484a29271fa0b4d9..ad84c37b6be770978c644402598768498a0b2822 100644 (file)
@@ -47,7 +47,7 @@ Specifying rules and plugins:
 Fixing problems:
   --fix                          Automatically fix problems
   --fix-dry-run                  Automatically fix problems without saving the changes to the file system
-  --fix-type Array               Specify the types of fixes to apply (problem, suggestion, layout)
+  --fix-type Array               Specify the types of fixes to apply (directive, problem, suggestion, layout)
 
 Ignoring files:
   --ignore-path path::String     Specify path of ignore file
@@ -81,6 +81,7 @@ Miscellaneous:
   --init                         Run config initialization wizard - default: false
   --env-info                     Output execution environment information - default: false
   --no-error-on-unmatched-pattern  Prevent errors when pattern is unmatched - default: false
+  --exit-on-fatal-error          Exit with exit code 2 in case of fatal error - default: false
   --debug                        Output debugging information
   -h, --help                     Show help
   -v, --version                  Output the version number
@@ -210,9 +211,9 @@ If the rule is defined within a plugin, you have to prefix the rule ID with the
 
 Examples:
 
-    eslint --rule 'quotes: [2, double]'
-    eslint --rule 'guard-for-in: 2' --rule 'brace-style: [2, 1tbs]'
-    eslint --rule 'jquery/dollar-sign: 2'
+    eslint --rule 'quotes: [error, double]'
+    eslint --rule 'guard-for-in: error' --rule 'brace-style: [error, 1tbs]'
+    eslint --rule 'jquery/dollar-sign: error'
 
 ### Fixing problems
 
@@ -239,11 +240,12 @@ This flag can be useful for integrations (e.g. editor plugins) which need to aut
 
 #### `--fix-type`
 
-This option allows you to specify the type of fixes to apply when using either `--fix` or `--fix-dry-run`. The three types of fixes are:
+This option allows you to specify the type of fixes to apply when using either `--fix` or `--fix-dry-run`. The four types of fixes are:
 
 1. `problem` - fix potential errors in the code
 1. `suggestion` - apply fixes to the code that improve it
 1. `layout` - apply fixes that do not change the program structure (AST)
+1. `directive` - apply fixes to inline directives such as `// eslint-disable`
 
 You can specify one or more fix type on the command line. Here are some examples:
 
@@ -337,14 +339,12 @@ When specified, the given format is output into the provided file name.
 This option specifies the output format for the console. Possible formats are:
 
 * [checkstyle](formatters.md/#checkstyle)
-* [codeframe](formatters.md/#codeframe)
 * [compact](formatters.md/#compact)
 * [html](formatters.md/#html)
 * [jslint-xml](formatters.md/#jslint-xml)
 * [json](formatters.md/#json)
 * [junit](formatters.md/#junit)
 * [stylish](formatters.md/#stylish) (the default)
-* [table](formatters.md/#table)
 * [tap](formatters.md/#tap)
 * [unix](formatters.md/#unix)
 * [visualstudio](formatters.md/#visualstudio)
@@ -467,9 +467,14 @@ This option outputs information about the execution environment, including the v
 
 This option prevents errors when a quoted glob pattern or `--ext` is unmatched. This will not prevent errors when your shell can't match a glob.
 
+#### `--exit-on-fatal-error`
+
+This option causes ESLint to exit with exit code 2 if one or more fatal parsing errors occur. Without this option, fatal parsing errors are reported as rule violations.
+
 #### `--debug`
 
-This option outputs debugging information to the console. This information is useful when you're seeing a problem and having a hard time pinpointing it. The ESLint team may ask for this debugging information to help solve bugs.
+This option outputs debugging information to the console. This information is useful when you're seeing a problem and having a hard time pinpointing it. The ESLint team may ask for this debugging information to help solve bugs.  
+Add this flag to an ESLint command line invocation in order to get extra debug information as the command is run (e.g. `eslint --debug test.js` and `eslint test.js --debug` are equivalent)
 
 #### `-h`, `--help`
 
index e8aa6bf0e779182cb31de03a0d2e1f96db38e723..091d043b9e40477892fbd58abb715a29b1a10cd3 100644 (file)
@@ -6,7 +6,7 @@
 * [Cascading and Hierarchy](#cascading-and-hierarchy)
 * [Extending Configuration Files](#extending-configuration-files)
 * [Configuration Based on Glob Patterns](#configuration-based-on-glob-patterns)
-* [Personal Configuration Files](#personal-configuration-files)
+* [Personal Configuration Files](#personal-configuration-files-deprecated)
 
 ## Configuration File Formats
 
@@ -37,7 +37,34 @@ The second way to use configuration files is to save the file wherever you would
 
     eslint -c myconfig.json myfiletotest.js
 
-If you are using one configuration file and want ESLint to ignore any `.eslintrc.*` files, make sure to use [`--no-eslintrc`](https://eslint.org/docs/user-guide/command-line-interface#-no-eslintrc) along with the [`-c`](https://eslint.org/docs/user-guide/command-line-interface#-c-config) flag.
+If you are using one configuration file and want ESLint to ignore any `.eslintrc.*` files, make sure to use [`--no-eslintrc`](https://eslint.org/docs/user-guide/command-line-interface#--no-eslintrc) along with the [`-c`](https://eslint.org/docs/user-guide/command-line-interface#-c---config) flag.
+
+Here's an example JSON configuration file that uses the `typescript-eslint` parser to support TypeScript syntax:
+
+```json
+{
+    "root": true,
+    "extends": [
+        "eslint:recommended",
+        "plugin:@typescript-eslint/recommended"
+    ],
+    "parser": "@typescript-eslint/parser",
+    "parserOptions": { "project": ["./tsconfig.json"] },
+    "plugins": [
+        "@typescript-eslint"
+    ],
+    "rules": {
+        "@typescript-eslint/strict-boolean-expressions": [
+            2,
+            {
+                "allowString" : false,
+                "allowNumber" : false
+            }
+        ]
+    },
+    "ignorePatterns": ["src/**/*.test.ts", "src/frontend/generated/*"]
+}
+```
 
 ### Comments in configuration files
 
@@ -192,6 +219,10 @@ The `rules` property can do any of the following to extend (or override) the set
     * Base config: `"quotes": ["error", "single", "avoid-escape"]`
     * Derived config: `"quotes": ["error", "single"]`
     * Resulting actual config: `"quotes": ["error", "single"]`
+* override options for rules given as object from base configurations:
+    * Base config: `"max-lines": ["error", { "max": 200, "skipBlankLines": true, "skipComments": true }]`
+    * Derived config: `"max-lines": ["error", { "max": 100 }]`
+    * Resulting actual config: `"max-lines": ["error", { "max": 100 }]` where `skipBlankLines` and `skipComments` default to `false`
 
 ### Using a shareable configuration package
 
@@ -297,7 +328,7 @@ The `extends` property value can be `"eslint:all"` to enable all core rules in t
 
 You might enable all core rules as a shortcut to explore rules and options while you decide on the configuration for a project, especially if you rarely override options or disable rules. The default options for rules are not endorsements by ESLint (for example, the default option for the [`quotes`](https://eslint.org/docs/rules/quotes) rule does not mean double quotes are better than single quotes).
 
-If your configuration extends `eslint:all`, after you upgrade to a newer major or minor version of ESLint, review the reported problems before you use the `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fix), so you know if a new fixable rule will make changes to the code.
+If your configuration extends `eslint:all`, after you upgrade to a newer major or minor version of ESLint, review the reported problems before you use the `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#--fix), so you know if a new fixable rule will make changes to the code.
 
 Example of a configuration file in JavaScript format:
 
index eb3fe8a0afe6dadef9ebbffe71fde75918ad638e..1ee3f2d19bee5a6a5f3c3f0b5cf266cd61cb59da 100644 (file)
@@ -187,7 +187,7 @@ For ES6 syntax, use `{ "parserOptions": { "ecmaVersion": 6 } }`; for new ES6 glo
 
 Parser options are set in your `.eslintrc.*` file by using the `parserOptions` property. The available options are:
 
-* `ecmaVersion` - set to 3, 5 (default), 6, 7, 8, 9, 10, 11, or 12 to specify the version of ECMAScript syntax you want to use. You can also set to 2015 (same as 6), 2016 (same as 7), 2017 (same as 8), 2018 (same as 9), 2019 (same as 10), 2020 (same as 11), or 2021 (same as 12) to use the year-based naming.
+* `ecmaVersion` - set to 3, 5 (default), 6, 7, 8, 9, 10, 11, 12, or 13 to specify the version of ECMAScript syntax you want to use. You can also set to 2015 (same as 6), 2016 (same as 7), 2017 (same as 8), 2018 (same as 9), 2019 (same as 10), 2020 (same as 11), 2021 (same as 12), or 2022 (same as 13) to use the year-based naming. You can also set "latest" to use the most recently supported version.
 * `sourceType` - set to `"script"` (default) or `"module"` if your code is in ECMAScript modules.
 * `ecmaFeatures` - an object indicating which additional language features you'd like to use:
     * `globalReturn` - allow `return` statements in the global scope
@@ -199,7 +199,7 @@ Here's an example `.eslintrc.json` file:
 ```json
 {
     "parserOptions": {
-        "ecmaVersion": 6,
+        "ecmaVersion": "latest",
         "sourceType": "module",
         "ecmaFeatures": {
             "jsx": true
index 2274b98ed022f2d66caaaadadd432418f046812f..0191f6348862f340152999566a6bc9bbe42ec6e2 100644 (file)
@@ -20,7 +20,7 @@ To configure rules inside of a file using configuration comments, use a comment
 /* eslint eqeqeq: "off", curly: "error" */
 ```
 
-In this example, [`eqeqeq`](https://eslint.org/docs/rules/eqeqeq) is turned off and [`curly`](.https://eslint.org/docs/rules/curly) is turned on as an error. You can also use the numeric equivalent for the rule severity:
+In this example, [`eqeqeq`](https://eslint.org/docs/rules/eqeqeq) is turned off and [`curly`](https://eslint.org/docs/rules/curly) is turned on as an error. You can also use the numeric equivalent for the rule severity:
 
 ```js
 /* eslint eqeqeq: 0, curly: 2 */
index 3b28a91a2f6e5af31123851e4773bfe781fbc20a..3c9633bb31553ecf8e0d923f9d886d16fb950377 100644 (file)
@@ -8,7 +8,7 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
 
 ## Installation and Usage
 
-Prerequisites: [Node.js](https://nodejs.org/en/) (`^10.12.0`, or `>=12.0.0`) built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)
+Prerequisites: [Node.js](https://nodejs.org/en/) (`^12.22.0`, `^14.17.0`, or `>=16.0.0`) built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)
 
 You can install ESLint using npm or yarn:
 
index c7130bb9f8074be428add5139d6a7589ffe5e756..c919c2d0d42eec53533f4070e5d961e91998259d 100644 (file)
@@ -27,7 +27,7 @@
 * Broccoli: [broccoli-eslint](https://www.npmjs.com/package/broccoli-eslint)
 * Browserify: [eslintify](https://www.npmjs.com/package/eslintify)
 * Webpack: [eslint-webpack-plugin](https://www.npmjs.com/package/eslint-webpack-plugin)
-* Rollup: [rollup-plugin-eslint](https://www.npmjs.com/package/rollup-plugin-eslint)
+* Rollup: [@rollup/plugin-eslint](https://www.npmjs.com/package/@rollup/plugin-eslint)
 * Ember-cli: [ember-cli-eslint](https://www.npmjs.com/package/ember-cli-eslint)
 * Sails.js: [sails-hook-lint](https://www.npmjs.com/package/sails-hook-lint), [sails-eslint](https://www.npmjs.com/package/sails-eslint)
 * Start: [@start/plugin-lib-eslint](https://www.npmjs.com/package/@start/plugin-lib-eslint)
@@ -44,6 +44,7 @@
 * [Git Precommit Hook](https://coderwall.com/p/zq8jlq/eslint-pre-commit-hook)
 * [Git pre-commit hook that only lints staged changes](https://gist.github.com/dahjelle/8ddedf0aebd488208a9a7c829f19b9e8)
 * [overcommit Git hook manager](https://github.com/brigade/overcommit)
+* [Mega-Linter](https://nvuillam.github.io/mega-linter): Linters aggregator for CI, [embedding eslint](https://nvuillam.github.io/mega-linter/descriptors/javascript_eslint/)
 
 ## Testing
 
diff --git a/eslint/docs/user-guide/migrating-to-8.0.0.md b/eslint/docs/user-guide/migrating-to-8.0.0.md
new file mode 100644 (file)
index 0000000..351309c
--- /dev/null
@@ -0,0 +1,297 @@
+# Migrating to v8.0.0
+
+ESLint v8.0.0 is a major release of ESLint. We have made a few breaking changes in this release. This guide is intended to walk you through the breaking changes.
+
+The lists below are ordered roughly by the number of users each change is expected to affect, where the first items are expected to affect the most users.
+
+## Table of Contents
+
+### Breaking changes for users
+
+- [Node.js 10, 13, and 15 are no longer supported](#drop-old-node)
+- [Removed `codeframe` and `table` formatters](#removed-formatters)
+- [`comma-dangle` rule schema is stricter](#comma-dangle)
+- [Unused disable directives are now fixable](#directives)
+- [`eslint:recommended` has been updated](#eslint-recommended)
+
+### Breaking changes for plugin developers
+
+- [Node.js 10, 13, and 15 are no longer supported](#drop-old-node)
+- [Rules require `meta.hasSuggestions` to provide suggestions](#suggestions)
+- [Rules require `meta.fixable` to provide fixes](#fixes)
+- [`SourceCode#getComments()` fails in `RuleTester`](#get-comments)
+- [Changes to shorthand property AST format](#ast-format)
+
+### Breaking changes for integration developers
+
+- [Node.js 10, 13, and 15 are no longer supported](#drop-old-node)
+- [The `CLIEngine` class has been removed](#remove-cliengine)
+- [The `linter` object has been removed](#remove-linter)
+- [The `/lib` entrypoint has been removed](#remove-lib)
+
+---
+
+## <a name="drop-old-node"></a> Node.js 10, 13, and 15 are no longer supported
+
+Node.js 10, 13, 15 all reached end of life either in 2020 or early 2021. ESLint is officially dropping support for these versions of Node.js starting with ESLint v8.0.0. ESLint now supports the following versions of Node.js:
+
+- Node.js 12.22 and above
+- Node.js 14 and above
+- Node.js 16 and above
+
+**To address:** Make sure you upgrade to at least Node.js `12.22.0` when using ESLint v8.0.0. One important thing to double check is the Node.js version supported by your editor when using ESLint via editor integrations. If you are unable to upgrade, we recommend continuing to use ESLint 7 until you are able to upgrade Node.js.
+
+**Related issue(s):** [#14023](https://github.com/eslint/eslint/issues/14023)
+
+## <a name="removed-formatters"></a> Removed `codeframe` and `table` formatters
+
+ESLint v8.0.0 has removed the `codeframe` and `table` formatters from the core. These formatters required dependencies that weren't used anywhere else in ESLint, and removing them allows us to reduce the size of ESLint, allowing for faster installation.
+
+**To address:** If you are using the `codeframe` or `table` formatters, you'll need to install the standalone [`eslint-formatter-codeframe`](https://github.com/fregante/eslint-formatter-codeframe) or [`eslint-formatter-table`](https://github.com/fregante/eslint-formatter-table) packages, respectively, to be able to use them in ESLint v8.0.0.
+
+**Related issue(s):** [#14277](https://github.com/eslint/eslint/issues/14277), [#14316](https://github.com/eslint/eslint/pull/14316)
+
+
+## <a name="comma-dangle"></a> `comma-dangle` rule schema is stricter
+
+In ESLint v7.0.0, the `comma-dangle` rule could be configured like this without error:
+
+```json
+{
+    "rules": {
+        "comma-dangle": ["error", "never", { "arrays": "always" }]
+    }
+}
+```
+
+With this configuration, the rule would ignore the third element in the array because only the second element is read. In ESLint v8.0.0, this configuration will cause ESLint to throw an error.
+
+**To address:** Change your rule configuration so that there are only two elements in the array, and the second element is either a string or an object, such as:
+
+```jsonc
+{
+    "comma-dangle": ["error", "never"],
+    // or
+    "comma-dangle": ["error", {
+        "arrays": "never",
+        "objects": "never",
+        "imports": "never",
+        "exports": "never",
+        "functions": "never"
+    }]
+}
+```
+
+**Related issue(s):** [#13739](https://github.com/eslint/eslint/issues/13739)
+
+## <a name="directives"></a> Unused disable directives are now fixable
+
+In ESLint v7.0.0, using both `--report-unused-disable-directives` and `--fix` on the command line would fix only rules but leave unused disable directives in place. In ESLint v8.0.0, this combination of command-line options will result in the unused disable directives being removed.
+
+**To address:** If you are using `--report-unused-disable-directives` and `--fix` together on the command line, and you don't want unused disable directives to be removed, add `--fix-type problem,suggestion,layout` as a command line option.
+
+**Related issue(s):** [#11815](https://github.com/eslint/eslint/issues/11815)
+
+## <a name="eslint-recommended"></a> `eslint:recommended` has been updated
+
+Four new rules have been enabled in the `eslint:recommended` preset.
+
+- [`no-loss-of-precision`](https://eslint.org/docs/rules/no-loss-of-precision)
+- [`no-nonoctal-decimal-escape`](https://eslint.org/docs/rules/no-nonoctal-decimal-escape)
+- [`no-unsafe-optional-chaining`](https://eslint.org/docs/rules/no-unsafe-optional-chaining)
+- [`no-useless-backreference`](https://eslint.org/docs/rules/no-useless-backreference)
+
+**To address:** Fix errors or disable these rules.
+
+**Related issue(s):** [#14673](https://github.com/eslint/eslint/issues/14673)
+
+
+## <a name="suggestions"></a> Rules require `meta.hasSuggestions` to provide suggestions
+
+In ESLint v7.0.0, rules that [provided suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions) did not need to let ESLint know. In v8.0.0, rules providing suggestions need to set their `meta.hasSuggestions` to `true`. This informs ESLint that the rule intends to provide suggestions. Without this property, any attempt to provide a suggestion will result in an error.
+
+**To address:** If your rule provides suggestions, add `meta.hasSuggestions` to the object, such as:
+
+```js
+module.exports = {
+    meta: {
+        hasSuggestions: true
+    },
+    create(context) {
+        // your rule
+    }
+};
+```
+
+The [eslint-plugin/require-meta-has-suggestions](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-has-suggestions.md) rule can automatically fix and enforce that your rules are properly specifying `meta.hasSuggestions`.
+
+**Related issue(s):** [#14312](https://github.com/eslint/eslint/issues/14312)
+
+## <a name="fixes"></a> Rules require `meta.fixable` to provide fixes
+
+In ESLint v7.0.0, rules that were written as a function (rather than object) were able to provide fixes. In ESLint v8.0.0, only rules written as an object are allowed to provide fixes and must have a `meta.fixable` property set to either `"code"` or `"whitespace"`.
+
+**To address:** If your rule makes fixes and is written as a function, such as:
+
+```js
+module.exports = function(context) {
+    // your rule
+};
+```
+
+Then rewrite your rule in this format:
+
+```js
+module.exports = {
+    meta: {
+        fixable: "code" // or "whitespace"
+    },
+    create(context) {
+        // your rule
+    }
+};
+```
+
+The [eslint-plugin/require-meta-fixable](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-fixable.md) rule can automatically fix and enforce that your rules are properly specifying `meta.fixable`.
+
+The [eslint-plugin/prefer-object-rule](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-object-rule.md) rule can automatically fix and enforce that your rules are written with the object format instead of the deprecated function format.
+
+See the [rule documentation](https://eslint.org/docs/developer-guide/working-with-rules) for more information on writing rules.
+
+**Related issue(s):** [#13349](https://github.com/eslint/eslint/issues/13349)
+
+## <a name="get-comments"></a> `SourceCode#getComments()` fails in `RuleTester`
+
+Back in ESLint v4.0.0, we deprecated `SourceCode#getComments()`, but we neglected to remove it. Rather than removing it completely in v8.0.0, we are taking the intermediate step of updating `RuleTester` to fail when `SourceCode#getComments()` is used inside of a rule. As such, all existing rules will continue to work, but when the developer runs tests for the rule there will be a failure.
+
+The `SourceCode#getComments()` method will be removed in v9.0.0.
+
+**To address:** If your rule uses `SourceCode#getComments()`, please use [`SourceCode#getCommentsBefore()`, `SourceCode#getCommentsAfter()`, or `SourceCode#getCommentsInside()`](https://eslint.org/docs/developer-guide/working-with-rules#sourcecodegetcommentsbefore-sourcecodegetcommentsafter-and-sourcecodegetcommentsinside).
+
+**Related issue(s):** [#14744](https://github.com/eslint/eslint/issues/14744)
+
+## <a name="ast-format"></a> Changes to shorthand property AST format
+
+ESLint v8.0.0 includes an upgrade to Espree v8.0.0 to support new syntax. This Espree upgrade, in turn, contains an upgrade to Acorn v8.0.0, which changed how shorthand properties were represented in the AST. Here's an example:
+
+```js
+const version = 8;
+const x = {
+    version
+};
+```
+
+This code creates a property node that looks like this:
+
+```json
+{
+    "type": "Property",
+    "method": false,
+    "shorthand": true,
+    "computed": false,
+    "key": {
+        "type": "Identifier",
+        "name": "version"
+    },
+    "kind": "init",
+    "value": {
+        "type": "Identifier",
+        "name": "version"
+    }
+}
+```
+
+Note that both the `key` and the `value` properties contain the same information. Prior to Acorn v8.0.0 (and therefore prior to ESLint v8.0.0), these two nodes were represented by the same object, so you could use `===` to determine if they represented the same node, such as:
+
+```js
+// true in ESLint v7.x, false in ESLint v8.0.0
+if (propertyNode.key === propertyNode.value) {
+    // do something
+}
+```
+
+In ESLint v8.0.0 (via Acorn v8.0.0), the key and value are now separate objects and therefore no longer equivalent.
+
+**To address:** If your rule makes a comparison between the key and value of a shorthand object literal property to determine if they are the same node, you'll need to change your code in one of two ways:
+
+1. Use `propertyNode.shorthand` to determine if the property is a shorthand property node.
+1. Use the `range` property of each node to determine if the key and value occupy the same location.
+
+**Related issue(s):** [#14591](https://github.com/eslint/eslint/pull/14591#issuecomment-887733070)
+
+
+## <a name="remove-cliengine"></a> The `CLIEngine` class has been removed
+
+The `CLIEngine` class has been removed and replaced by the [`ESLint` class](https://eslint.org/docs/developer-guide/nodejs-api#eslint-class).
+
+**To address:** Update your code to use the new `ESLint` class if you are currently using `CLIEngine`. The following table maps the existing `CLIEngine` methods to their `ESLint` counterparts:
+
+| `CLIEngine`                                  | `ESLint`                           |
+| :------------------------------------------- | :--------------------------------- |
+| `executeOnFiles(patterns)`                   | `lintFiles(patterns)`              |
+| `executeOnText(text, filePath, warnIgnored)` | `lintText(text, options)`          |
+| `getFormatter(name)`                         | `loadFormatter(name)`              |
+| `getConfigForFile(filePath)`                 | `calculateConfigForFile(filePath)` |
+| `isPathIgnored(filePath)`                    | `isPathIgnored(filePath)`          |
+| `static outputFixes(results)`                | `static outputFixes(results)`      |
+| `static getErrorResults(results)`            | `static getErrorResults(results)`  |
+| `static getFormatter(name)`                  | (removed ※1)                      |
+| `addPlugin(pluginId, definition)`            | the `plugins` constructor option   |
+| `getRules()`                                 | (removed ※2)                      |
+| `resolveFileGlobPatterns()`                  | (removed ※3)                      |
+
+- ※1 The `engine.getFormatter()` method currently returns the object of loaded packages as-is, which made it difficult to add new features to formatters for backward compatibility reasons. The new `eslint.loadFormatter()` method returns an adapter object that wraps the object of loaded packages, to ease the process of adding new features. Additionally, the adapter object has access to the `ESLint` instance to calculate default data (using loaded plugin rules to make `rulesMeta`, for example). As a result, the `ESLint` class only implements an instance version of the `loadFormatter()` method.
+- ※2 The `CLIEngine#getRules()` method had side effects and so was removed. If you were using `CLIEngine#getRules()` to retrieve meta information about rules based on linting results, use `ESLint#getRulesMetaForResults()` instead. If you were using `CLIEngine#getRules()` to retrieve all built-in rules, import `builtinRules` from `eslint/use-at-your-own-risk` for an unsupported API that allows access to internal rules.
+- ※3 Since ESLint v6.0.0, ESLint uses different logic from the `resolveFileGlobPatterns()` method to iterate files, making this method obsolete.
+
+**Related issue(s):** [RFC80](https://github.com/eslint/rfcs/tree/main/designs/2021-package-exports), [#14716](https://github.com/eslint/eslint/pull/14716), [#13654](https://github.com/eslint/eslint/issues/13654)
+
+## <a name="remove-linter"></a> The `linter` object has been removed
+
+The deprecated `linter` object has been removed from the ESLint package in v8.0.0.
+
+**To address:** If you are using the `linter` object, such as:
+
+```js
+const { linter } = require("eslint");
+```
+
+Change your code to this:
+
+```js
+const { Linter } = require("eslint");
+const linter = new Linter();
+```
+
+**Related issue(s):** [RFC80](https://github.com/eslint/rfcs/tree/main/designs/2021-package-exports), [#14716](https://github.com/eslint/eslint/pull/14716), [#13654](https://github.com/eslint/eslint/issues/13654)
+
+## <a name="remove-lib"></a> The `/lib` entrypoint has been removed
+
+Beginning in v8.0.0, ESLint is strictly defining its public API. Previously, you could reach into individual files such as `require("eslint/lib/rules/semi")` and this is no longer allowed. There are a limited number of existing APIs that are now available through the `/use-at-your-own-risk` entrypoint for backwards compatibility, but these APIs are not formally supported and may break or disappear at any point in time.
+
+**To address:** If you are accessing rules directly through the `/lib` entrypoint, such as:
+
+```js
+const rule = require("eslint/lib/rules/semi");
+```
+
+Change your code to this:
+
+```js
+const { builtinRules } = require("eslint/use-at-your-own-risk");
+const rule = builtinRules.get("semi");
+```
+
+If you are accessing `FileEnumerator` directly through the `/lib` entrypoint, such as:
+
+```js
+const { FileEnumerator } = require("eslint/lib/cli-engine/file-enumerator");
+```
+
+Change your code to this:
+
+```js
+const { FileEnumerator } = require("eslint/use-at-your-own-risk");
+```
+
+**Related issue(s):** [RFC80](https://github.com/eslint/rfcs/tree/main/designs/2021-package-exports), [#14716](https://github.com/eslint/eslint/pull/14716), [#13654](https://github.com/eslint/eslint/issues/13654)
index e4b6643b44780a176bd6f0c2ef58de78748fe920..3dde0985505a07ed8b64250bb96f470a0b17ace1 100644 (file)
@@ -5,30 +5,22 @@
 
 "use strict";
 
-const { CLIEngine } = require("./cli-engine");
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
 const { ESLint } = require("./eslint");
 const { Linter } = require("./linter");
 const { RuleTester } = require("./rule-tester");
 const { SourceCode } = require("./source-code");
 
+//-----------------------------------------------------------------------------
+// Exports
+//-----------------------------------------------------------------------------
+
 module.exports = {
     Linter,
-    CLIEngine,
     ESLint,
     RuleTester,
     SourceCode
 };
-
-// DOTO: remove deprecated API.
-let deprecatedLinterInstance = null;
-
-Object.defineProperty(module.exports, "linter", {
-    enumerable: false,
-    get() {
-        if (!deprecatedLinterInstance) {
-            deprecatedLinterInstance = new Linter();
-        }
-
-        return deprecatedLinterInstance;
-    }
-});
index ca298f9c356c3cb84af2498222faeaca13d7fd35..e3647018d26fa3f829022515cf17ade3465d4ece 100644 (file)
@@ -41,7 +41,7 @@ const hash = require("./hash");
 const LintResultCache = require("./lint-result-cache");
 
 const debug = require("debug")("eslint:cli-engine");
-const validFixTypes = new Set(["problem", "suggestion", "layout"]);
+const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);
 
 //------------------------------------------------------------------------------
 // Typedefs
@@ -55,8 +55,8 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]);
 /** @typedef {import("../shared/types").Plugin} Plugin */
 /** @typedef {import("../shared/types").RuleConf} RuleConf */
 /** @typedef {import("../shared/types").Rule} Rule */
-/** @typedef {ReturnType<CascadingConfigArrayFactory["getConfigArrayForFile"]>} ConfigArray */
-/** @typedef {ReturnType<ConfigArray["extractConfig"]>} ExtractedConfig */
+/** @typedef {ReturnType<CascadingConfigArrayFactory.getConfigArrayForFile>} ConfigArray */
+/** @typedef {ReturnType<ConfigArray.extractConfig>} ExtractedConfig */
 
 /**
  * The options to configure a CLI engine with.
@@ -156,6 +156,9 @@ function calculateStatsPerFile(messages) {
     return messages.reduce((stat, message) => {
         if (message.fatal || message.severity === 2) {
             stat.errorCount++;
+            if (message.fatal) {
+                stat.fatalErrorCount++;
+            }
             if (message.fix) {
                 stat.fixableErrorCount++;
             }
@@ -168,6 +171,7 @@ function calculateStatsPerFile(messages) {
         return stat;
     }, {
         errorCount: 0,
+        fatalErrorCount: 0,
         warningCount: 0,
         fixableErrorCount: 0,
         fixableWarningCount: 0
@@ -183,12 +187,14 @@ function calculateStatsPerFile(messages) {
 function calculateStatsPerRun(results) {
     return results.reduce((stat, result) => {
         stat.errorCount += result.errorCount;
+        stat.fatalErrorCount += result.fatalErrorCount;
         stat.warningCount += result.warningCount;
         stat.fixableErrorCount += result.fixableErrorCount;
         stat.fixableWarningCount += result.fixableWarningCount;
         return stat;
     }, {
         errorCount: 0,
+        fatalErrorCount: 0,
         warningCount: 0,
         fixableErrorCount: 0,
         fixableWarningCount: 0
@@ -274,7 +280,7 @@ function verifyText({
 /**
  * Returns result with warning by ignore settings
  * @param {string} filePath File path of checked code
- * @param {string} baseDir  Absolute path of base directory
+ * @param {string} baseDir Absolute path of base directory
  * @returns {LintResult} Result with single warning
  * @private
  */
@@ -325,6 +331,23 @@ function getRule(ruleId, configArrays) {
     return builtInRules.get(ruleId) || null;
 }
 
+/**
+ * Checks whether a message's rule type should be fixed.
+ * @param {LintMessage} message The message to check.
+ * @param {ConfigArray[]} lastConfigArrays The list of config arrays that the last `executeOnFiles` or `executeOnText` used.
+ * @param {string[]} fixTypes An array of fix types to check.
+ * @returns {boolean} Whether the message should be fixed.
+ */
+function shouldMessageBeFixed(message, lastConfigArrays, fixTypes) {
+    if (!message.ruleId) {
+        return fixTypes.has("directive");
+    }
+
+    const rule = message.ruleId && getRule(message.ruleId, lastConfigArrays);
+
+    return Boolean(rule && rule.meta && fixTypes.has(rule.meta.type));
+}
+
 /**
  * Collect used deprecated rules.
  * @param {ConfigArray[]} usedConfigArrays The config arrays which were used.
@@ -457,6 +480,7 @@ function getCacheFile(cacheFile, cwd) {
  * @param {string[]|null} keys The keys to assign true.
  * @param {boolean} defaultValue The default value for each property.
  * @param {string} displayName The property name which is used in error message.
+ * @throws {Error} Requires array.
  * @returns {Record<string,boolean>} The boolean map.
  */
 function toBooleanMap(keys, defaultValue, displayName) {
@@ -520,6 +544,7 @@ function createConfigDataFromOptions(options) {
 /**
  * Checks whether a directory exists at the given location
  * @param {string} resolvedPath A path from the CWD
+ * @throws {Error} As thrown by `fs.statSync` or `fs.isDirectory`.
  * @returns {boolean} `true` if a directory exists
  */
 function directoryExists(resolvedPath) {
@@ -537,13 +562,18 @@ function directoryExists(resolvedPath) {
 // Public Interface
 //------------------------------------------------------------------------------
 
+/**
+ * Core CLI.
+ */
 class CLIEngine {
 
     /**
      * Creates a new instance of the core CLI engine.
      * @param {CLIEngineOptions} providedOptions The options for this instance.
+     * @param {Object} [additionalData] Additional settings that are not CLIEngineOptions.
+     * @param {Record<string,Plugin>|null} [additionalData.preloadedPlugins] Preloaded plugins.
      */
-    constructor(providedOptions) {
+    constructor(providedOptions, { preloadedPlugins } = {}) {
         const options = Object.assign(
             Object.create(null),
             defaultOptions,
@@ -556,6 +586,13 @@ class CLIEngine {
         }
 
         const additionalPluginPool = new Map();
+
+        if (preloadedPlugins) {
+            for (const [id, plugin] of Object.entries(preloadedPlugins)) {
+                additionalPluginPool.set(id, plugin);
+            }
+        }
+
         const cacheFilePath = getCacheFile(
             options.cacheLocation || options.cacheFile,
             options.cwd
@@ -617,12 +654,7 @@ class CLIEngine {
             const originalFix = (typeof options.fix === "function")
                 ? options.fix : () => true;
 
-            options.fix = message => {
-                const rule = message.ruleId && getRule(message.ruleId, lastConfigArrays);
-                const matches = rule && rule.meta && fixTypes.has(rule.meta.type);
-
-                return matches && originalFix(message);
-            };
+            options.fix = message => shouldMessageBeFixed(message, lastConfigArrays, fixTypes) && originalFix(message);
         }
     }
 
@@ -675,26 +707,6 @@ class CLIEngine {
         });
     }
 
-
-    /**
-     * Add a plugin by passing its configuration
-     * @param {string} name Name of the plugin.
-     * @param {Plugin} pluginObject Plugin configuration object.
-     * @returns {void}
-     */
-    addPlugin(name, pluginObject) {
-        const {
-            additionalPluginPool,
-            configArrayFactory,
-            lastConfigArrays
-        } = internalSlotsMap.get(this);
-
-        additionalPluginPool.set(name, pluginObject);
-        configArrayFactory.clearCache();
-        lastConfigArrays.length = 1;
-        lastConfigArrays[0] = configArrayFactory.getConfigArrayForFile();
-    }
-
     /**
      * Resolves the patterns passed into executeOnFiles() into glob-based patterns
      * for easier handling.
@@ -724,6 +736,7 @@ class CLIEngine {
     /**
      * Executes the current configuration on an array of file and directory names.
      * @param {string[]} patterns An array of file and directory names.
+     * @throws {Error} As may be thrown by `fs.unlinkSync`.
      * @returns {LintReport} The results for all files that were linted.
      */
     executeOnFiles(patterns) {
@@ -930,6 +943,7 @@ class CLIEngine {
      * This is the same logic used by the ESLint CLI executable to determine
      * configuration for each file it processes.
      * @param {string} filePath The path of the file to retrieve a config object for.
+     * @throws {Error} If filepath a directory path.
      * @returns {ConfigData} A configuration object for the file.
      */
     getConfigForFile(filePath) {
@@ -978,6 +992,7 @@ class CLIEngine {
      * Returns the formatter representing the given format or null if the `format` is not a string.
      * @param {string} [format] The name of the format to load or the path to a
      *      custom formatter.
+     * @throws {any} As may be thrown by requiring of formatter
      * @returns {(Function|null)} The formatter function or null if the `format` is not a string.
      */
     getFormatter(format) {
@@ -1013,7 +1028,11 @@ class CLIEngine {
             try {
                 return require(formatterPath);
             } catch (ex) {
-                ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
+                if (format === "table" || format === "codeframe") {
+                    ex.message = `The ${format} formatter is no longer part of core ESLint. Install it manually with \`npm install -D eslint-formatter-${format}\``;
+                } else {
+                    ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
+                }
                 throw ex;
             }
 
index ade28517b425ee6a0d23ce9455a34040b0f424e5..f1442d150b844dda52a56a12ed390ee60e280dd7 100644 (file)
@@ -60,7 +60,7 @@ const IGNORED_SILENTLY = 1;
 const IGNORED = 2;
 
 // For VSCode intellisense
-/** @typedef {ReturnType<CascadingConfigArrayFactory["getConfigArrayForFile"]>} ConfigArray */
+/** @typedef {ReturnType<CascadingConfigArrayFactory.getConfigArrayForFile>} ConfigArray */
 
 /**
  * @typedef {Object} FileEnumeratorOptions
@@ -114,6 +114,7 @@ function isGlobPattern(pattern) {
 /**
  * Get stats of a given path.
  * @param {string} filePath The path to target file.
+ * @throws {Error} As may be thrown by `fs.statSync`.
  * @returns {fs.Stats|null} The stats.
  * @private
  */
@@ -132,6 +133,7 @@ function statSafeSync(filePath) {
 /**
  * Get filenames in a given path to a directory.
  * @param {string} directoryPath The path to target directory.
+ * @throws {Error} As may be thrown by `fs.readdirSync`.
  * @returns {import("fs").Dirent[]} The filenames.
  * @private
  */
@@ -173,7 +175,6 @@ function createExtensionRegExp(extensions) {
  */
 class NoFilesFoundError extends Error {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {string} pattern The glob pattern which was not found.
      * @param {boolean} globDisabled If `true` then the pattern was a glob pattern, but glob was disabled.
@@ -190,7 +191,6 @@ class NoFilesFoundError extends Error {
  */
 class AllFilesIgnoredError extends Error {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {string} pattern The glob pattern which was not found.
      */
@@ -270,6 +270,7 @@ class FileEnumerator {
     /**
      * Iterate files which are matched by given glob patterns.
      * @param {string|string[]} patternOrPatterns The glob patterns to iterate files.
+     * @throws {NoFilesFoundError|AllFilesIgnoredError} On an unmatched pattern.
      * @returns {IterableIterator<FileAndConfig>} The found files.
      */
     *iterateFiles(patternOrPatterns) {
diff --git a/eslint/lib/cli-engine/formatters/codeframe.js b/eslint/lib/cli-engine/formatters/codeframe.js
deleted file mode 100644 (file)
index 41e3ab7..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/**
- * @fileoverview Codeframe reporter
- * @author Vitor Balocco
- */
-"use strict";
-
-const chalk = require("chalk");
-const { codeFrameColumns } = require("@babel/code-frame");
-const path = require("path");
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Given a word and a count, append an s if count is not one.
- * @param   {string} word  A word in its singular form.
- * @param   {number} count A number controlling whether word should be pluralized.
- * @returns {string}       The original word with an s on the end if count is not one.
- */
-function pluralize(word, count) {
-    return (count === 1 ? word : `${word}s`);
-}
-
-/**
- * Gets a formatted relative file path from an absolute path and a line/column in the file.
- * @param   {string} filePath The absolute file path to format.
- * @param   {number} line     The line from the file to use for formatting.
- * @param   {number} column   The column from the file to use for formatting.
- * @returns {string}          The formatted file path.
- */
-function formatFilePath(filePath, line, column) {
-    let relPath = path.relative(process.cwd(), filePath);
-
-    if (line && column) {
-        relPath += `:${line}:${column}`;
-    }
-
-    return chalk.green(relPath);
-}
-
-/**
- * Gets the formatted output for a given message.
- * @param   {Object} message      The object that represents this message.
- * @param   {Object} parentResult The result object that this message belongs to.
- * @returns {string}              The formatted output.
- */
-function formatMessage(message, parentResult) {
-    const type = (message.fatal || message.severity === 2) ? chalk.red("error") : chalk.yellow("warning");
-    const msg = `${chalk.bold(message.message.replace(/([^ ])\.$/u, "$1"))}`;
-    const ruleId = message.fatal ? "" : chalk.dim(`(${message.ruleId})`);
-    const filePath = formatFilePath(parentResult.filePath, message.line, message.column);
-    const sourceCode = parentResult.output ? parentResult.output : parentResult.source;
-
-    const firstLine = [
-        `${type}:`,
-        `${msg}`,
-        ruleId ? `${ruleId}` : "",
-        sourceCode ? `at ${filePath}:` : `at ${filePath}`
-    ].filter(String).join(" ");
-
-    const result = [firstLine];
-
-    if (sourceCode) {
-        result.push(
-            codeFrameColumns(sourceCode, { start: { line: message.line, column: message.column } }, { highlightCode: false })
-        );
-    }
-
-    return result.join("\n");
-}
-
-/**
- * Gets the formatted output summary for a given number of errors and warnings.
- * @param   {number} errors   The number of errors.
- * @param   {number} warnings The number of warnings.
- * @param   {number} fixableErrors The number of fixable errors.
- * @param   {number} fixableWarnings The number of fixable warnings.
- * @returns {string}          The formatted output summary.
- */
-function formatSummary(errors, warnings, fixableErrors, fixableWarnings) {
-    const summaryColor = errors > 0 ? "red" : "yellow";
-    const summary = [];
-    const fixablesSummary = [];
-
-    if (errors > 0) {
-        summary.push(`${errors} ${pluralize("error", errors)}`);
-    }
-
-    if (warnings > 0) {
-        summary.push(`${warnings} ${pluralize("warning", warnings)}`);
-    }
-
-    if (fixableErrors > 0) {
-        fixablesSummary.push(`${fixableErrors} ${pluralize("error", fixableErrors)}`);
-    }
-
-    if (fixableWarnings > 0) {
-        fixablesSummary.push(`${fixableWarnings} ${pluralize("warning", fixableWarnings)}`);
-    }
-
-    let output = chalk[summaryColor].bold(`${summary.join(" and ")} found.`);
-
-    if (fixableErrors || fixableWarnings) {
-        output += chalk[summaryColor].bold(`\n${fixablesSummary.join(" and ")} potentially fixable with the \`--fix\` option.`);
-    }
-
-    return output;
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-module.exports = function(results) {
-    let errors = 0;
-    let warnings = 0;
-    let fixableErrors = 0;
-    let fixableWarnings = 0;
-
-    const resultsWithMessages = results.filter(result => result.messages.length > 0);
-
-    let output = resultsWithMessages.reduce((resultsOutput, result) => {
-        const messages = result.messages.map(message => `${formatMessage(message, result)}\n\n`);
-
-        errors += result.errorCount;
-        warnings += result.warningCount;
-        fixableErrors += result.fixableErrorCount;
-        fixableWarnings += result.fixableWarningCount;
-
-        return resultsOutput.concat(messages);
-    }, []).join("\n");
-
-    output += "\n";
-    output += formatSummary(errors, warnings, fixableErrors, fixableWarnings);
-
-    return (errors + warnings) > 0 ? output : "";
-};
index baddb63079de4503e6745b466fe8a80e0852eaa4..e28996f6cd2f427658509661c4391b465f9aa761 100644 (file)
@@ -281,8 +281,8 @@ function resultTemplate(it) {
 `.trimLeft();
 }
 
-// eslint-disable-next-line jsdoc/require-description
 /**
+ * Render the results.
  * @param {Array} results Test results.
  * @param {Object} rulesMeta Dictionary containing metadata for each rule executed by the analysis.
  * @returns {string} HTML string describing the results.
diff --git a/eslint/lib/cli-engine/formatters/table.js b/eslint/lib/cli-engine/formatters/table.js
deleted file mode 100644 (file)
index a74cce0..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * @fileoverview "table reporter.
- * @author Gajus Kuizinas <gajus@gajus.com>
- */
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const chalk = require("chalk"),
-    table = require("table").table;
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Given a word and a count, append an "s" if count is not one.
- * @param {string} word A word.
- * @param {number} count Quantity.
- * @returns {string} The original word with an s on the end if count is not one.
- */
-function pluralize(word, count) {
-    return (count === 1 ? word : `${word}s`);
-}
-
-/**
- * Draws text table.
- * @param {Array<Object>} messages Error messages relating to a specific file.
- * @returns {string} A text table.
- */
-function drawTable(messages) {
-    const rows = [];
-
-    if (messages.length === 0) {
-        return "";
-    }
-
-    rows.push([
-        chalk.bold("Line"),
-        chalk.bold("Column"),
-        chalk.bold("Type"),
-        chalk.bold("Message"),
-        chalk.bold("Rule ID")
-    ]);
-
-    messages.forEach(message => {
-        let messageType;
-
-        if (message.fatal || message.severity === 2) {
-            messageType = chalk.red("error");
-        } else {
-            messageType = chalk.yellow("warning");
-        }
-
-        rows.push([
-            message.line || 0,
-            message.column || 0,
-            messageType,
-            message.message,
-            message.ruleId || ""
-        ]);
-    });
-
-    return table(rows, {
-        columns: {
-            0: {
-                width: 8,
-                wrapWord: true
-            },
-            1: {
-                width: 8,
-                wrapWord: true
-            },
-            2: {
-                width: 8,
-                wrapWord: true
-            },
-            3: {
-                paddingRight: 5,
-                width: 50,
-                wrapWord: true
-            },
-            4: {
-                width: 20,
-                wrapWord: true
-            }
-        },
-        drawHorizontalLine(index) {
-            return index === 1;
-        }
-    });
-}
-
-/**
- * Draws a report (multiple tables).
- * @param {Array} results Report results for every file.
- * @returns {string} A column of text tables.
- */
-function drawReport(results) {
-    let files;
-
-    files = results.map(result => {
-        if (!result.messages.length) {
-            return "";
-        }
-
-        return `\n${result.filePath}\n\n${drawTable(result.messages)}`;
-    });
-
-    files = files.filter(content => content.trim());
-
-    return files.join("");
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-module.exports = function(report) {
-    let result,
-        errorCount,
-        warningCount;
-
-    result = "";
-    errorCount = 0;
-    warningCount = 0;
-
-    report.forEach(fileReport => {
-        errorCount += fileReport.errorCount;
-        warningCount += fileReport.warningCount;
-    });
-
-    if (errorCount || warningCount) {
-        result = drawReport(report);
-    }
-
-    result += `\n${table([
-        [
-            chalk.red(pluralize(`${errorCount} Error`, errorCount))
-        ],
-        [
-            chalk.yellow(pluralize(`${warningCount} Warning`, warningCount))
-        ]
-    ], {
-        columns: {
-            0: {
-                width: 110,
-                wrapWord: true
-            }
-        },
-        drawHorizontalLine() {
-            return true;
-        }
-    })}`;
-
-    return result;
-};
index 354872a0c92af3e142aef5eb335fe63c0636b0ab..e4148a3b3929a0f618ac9630ee6afc1a0bc0107c 100644 (file)
@@ -31,7 +31,7 @@ function outputDiagnostics(diagnostic) {
     const prefix = "  ";
     let output = `${prefix}---\n`;
 
-    output += prefix + yaml.safeDump(diagnostic).split("\n").join(`\n${prefix}`);
+    output += prefix + yaml.dump(diagnostic).split("\n").join(`\n${prefix}`);
     output += "...\n";
     return output;
 }
index 6d7ef8bf1b2c64bed4499f2eab5486a0f6c07672..8e467734a023571097446b31fe084eb60d5b348b 100644 (file)
@@ -21,8 +21,8 @@ const murmur = require("imurmurhash");
 
 /**
  * hash the given string
- * @param  {string} str the string to hash
- * @returns {string}    the hash
+ * @param {string} str the string to hash
+ * @returns {string} the hash
  */
 function hash(str) {
     return murmur(str).result().toString(36);
index 175c2c0c2ddd7dec3c894d5baba5d14679b9b7e3..2e52dbaac02235258e83363dd646a69b01369e7f 100644 (file)
@@ -15,7 +15,7 @@
  * @private
  */
 module.exports = function(s) {
-    return (`${s}`).replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/gu, c => { // eslint-disable-line no-control-regex
+    return (`${s}`).replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/gu, c => { // eslint-disable-line no-control-regex -- Converting controls to entities
         switch (c) {
             case "<":
                 return "&lt;";
index f766764546cc5a002c4d13fddc2616b649e35494..477310da5850e7c488adcc998c1e3d7985fd215e 100644 (file)
@@ -131,14 +131,16 @@ function translateOptions({
  */
 function countErrors(results) {
     let errorCount = 0;
+    let fatalErrorCount = 0;
     let warningCount = 0;
 
     for (const result of results) {
         errorCount += result.errorCount;
+        fatalErrorCount += result.fatalErrorCount;
         warningCount += result.warningCount;
     }
 
-    return { errorCount, warningCount };
+    return { errorCount, fatalErrorCount, warningCount };
 }
 
 /**
@@ -314,9 +316,12 @@ const cli = {
         if (await printResults(engine, resultsToPrint, options.format, options.outputFile)) {
 
             // Errors and warnings from the original unfiltered results should determine the exit code
-            const { errorCount, warningCount } = countErrors(results);
+            const { errorCount, fatalErrorCount, warningCount } = countErrors(results);
+
             const tooManyWarnings =
                 options.maxWarnings >= 0 && warningCount > options.maxWarnings;
+            const shouldExitForFatalErrors =
+                options.exitOnFatalError && fatalErrorCount > 0;
 
             if (!errorCount && tooManyWarnings) {
                 log.error(
@@ -325,6 +330,10 @@ const cli = {
                 );
             }
 
+            if (shouldExitForFatalErrors) {
+                return 2;
+            }
+
             return (errorCount || tooManyWarnings) ? 1 : 0;
         }
 
diff --git a/eslint/lib/config/default-config.js b/eslint/lib/config/default-config.js
new file mode 100644 (file)
index 0000000..cb6f403
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * @fileoverview Default configuration
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
+const Rules = require("../rules");
+
+//-----------------------------------------------------------------------------
+// Helpers
+//-----------------------------------------------------------------------------
+
+
+exports.defaultConfig = [
+    {
+        plugins: {
+            "@": {
+                parsers: {
+                    espree: require("espree")
+                },
+
+                /*
+                 * Because we try to delay loading rules until absolutely
+                 * necessary, a proxy  allows us to hook into the lazy-loading
+                 * aspect of the rules map while still keeping all of the
+                 * relevant configuration inside of the config array.
+                 */
+                rules: new Proxy({}, {
+                    get(target, property) {
+                        return Rules.get(property);
+                    },
+
+                    has(target, property) {
+                        return Rules.has(property);
+                    }
+                })
+            }
+        },
+        ignores: [
+            "**/node_modules/**",
+            ".git/**"
+        ],
+        languageOptions: {
+            parser: "@/espree"
+        }
+    }
+];
diff --git a/eslint/lib/config/flat-config-array.js b/eslint/lib/config/flat-config-array.js
new file mode 100644 (file)
index 0000000..ef9cb33
--- /dev/null
@@ -0,0 +1,125 @@
+/**
+ * @fileoverview Flat Config Array
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
+const { ConfigArray, ConfigArraySymbol } = require("@humanwhocodes/config-array");
+const { flatConfigSchema } = require("./flat-config-schema");
+const { RuleValidator } = require("./rule-validator");
+const { defaultConfig } = require("./default-config");
+const recommendedConfig = require("../../conf/eslint-recommended");
+const allConfig = require("../../conf/eslint-all");
+
+//-----------------------------------------------------------------------------
+// Helpers
+//-----------------------------------------------------------------------------
+
+const ruleValidator = new RuleValidator();
+
+/**
+ * Splits a plugin identifier in the form a/b/c into two parts: a/b and c.
+ * @param {string} identifier The identifier to parse.
+ * @returns {{objectName: string, pluginName: string}} The parts of the plugin
+ *      name.
+ */
+function splitPluginIdentifier(identifier) {
+    const parts = identifier.split("/");
+
+    return {
+        objectName: parts.pop(),
+        pluginName: parts.join("/")
+    };
+}
+
+//-----------------------------------------------------------------------------
+// Exports
+//-----------------------------------------------------------------------------
+
+/**
+ * Represents an array containing configuration information for ESLint.
+ */
+class FlatConfigArray extends ConfigArray {
+
+    /**
+     * Creates a new instance.
+     * @param {*[]} configs An array of configuration information.
+     * @param {{basePath: string, baseConfig: FlatConfig}} options The options
+     *      to use for the config array instance.
+     */
+    constructor(configs, { basePath, baseConfig = defaultConfig }) {
+        super(configs, {
+            basePath,
+            schema: flatConfigSchema
+        });
+
+        this.unshift(baseConfig);
+    }
+
+    /* eslint-disable class-methods-use-this -- Desired as instance method */
+    /**
+     * Replaces a config with another config to allow us to put strings
+     * in the config array that will be replaced by objects before
+     * normalization.
+     * @param {Object} config The config to preprocess.
+     * @returns {Object} The preprocessed config.
+     */
+    [ConfigArraySymbol.preprocessConfig](config) {
+        if (config === "eslint:recommended") {
+            return recommendedConfig;
+        }
+
+        if (config === "eslint:all") {
+            return allConfig;
+        }
+
+        return config;
+    }
+
+    /**
+     * Finalizes the config by replacing plugin references with their objects
+     * and validating rule option schemas.
+     * @param {Object} config The config to finalize.
+     * @returns {Object} The finalized config.
+     * @throws {TypeError} If the config is invalid.
+     */
+    [ConfigArraySymbol.finalizeConfig](config) {
+
+        const { plugins, languageOptions, processor } = config;
+
+        // Check parser value
+        if (languageOptions && languageOptions.parser && typeof languageOptions.parser === "string") {
+            const { pluginName, objectName: parserName } = splitPluginIdentifier(languageOptions.parser);
+
+            if (!plugins || !plugins[pluginName] || !plugins[pluginName].parsers || !plugins[pluginName].parsers[parserName]) {
+                throw new TypeError(`Key "parser": Could not find "${parserName}" in plugin "${pluginName}".`);
+            }
+
+            languageOptions.parser = plugins[pluginName].parsers[parserName];
+        }
+
+        // Check processor value
+        if (processor && typeof processor === "string") {
+            const { pluginName, objectName: processorName } = splitPluginIdentifier(processor);
+
+            if (!plugins || !plugins[pluginName] || !plugins[pluginName].processors || !plugins[pluginName].processors[processorName]) {
+                throw new TypeError(`Key "processor": Could not find "${processorName}" in plugin "${pluginName}".`);
+            }
+
+            config.processor = plugins[pluginName].processors[processorName];
+        }
+
+        ruleValidator.validate(config);
+
+        return config;
+    }
+    /* eslint-enable class-methods-use-this -- Desired as instance method */
+
+}
+
+exports.FlatConfigArray = FlatConfigArray;
diff --git a/eslint/lib/config/flat-config-schema.js b/eslint/lib/config/flat-config-schema.js
new file mode 100644 (file)
index 0000000..c8cc711
--- /dev/null
@@ -0,0 +1,452 @@
+/**
+ * @fileoverview Flat config schema
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//-----------------------------------------------------------------------------
+// Type Definitions
+//-----------------------------------------------------------------------------
+
+/**
+ * @typedef ObjectPropertySchema
+ * @property {Function|string} merge The function or name of the function to call
+ *      to merge multiple objects with this property.
+ * @property {Function|string} validate The function or name of the function to call
+ *      to validate the value of this property.
+ */
+
+//-----------------------------------------------------------------------------
+// Helpers
+//-----------------------------------------------------------------------------
+
+const ruleSeverities = new Map([
+    [0, 0], ["off", 0],
+    [1, 1], ["warn", 1],
+    [2, 2], ["error", 2]
+]);
+
+const globalVariablesValues = new Set([
+    true, "true", "writable", "writeable",
+    false, "false", "readonly", "readable", null,
+    "off"
+]);
+
+/**
+ * Check if a value is a non-null object.
+ * @param {any} value The value to check.
+ * @returns {boolean} `true` if the value is a non-null object.
+ */
+function isNonNullObject(value) {
+    return typeof value === "object" && value !== null;
+}
+
+/**
+ * Check if a value is undefined.
+ * @param {any} value The value to check.
+ * @returns {boolean} `true` if the value is undefined.
+ */
+function isUndefined(value) {
+    return typeof value === "undefined";
+}
+
+/**
+ * Deeply merges two objects.
+ * @param {Object} first The base object.
+ * @param {Object} second The overrides object.
+ * @returns {Object} An object with properties from both first and second.
+ */
+function deepMerge(first = {}, second = {}) {
+
+    /*
+     * If the second value is an array, just return it. We don't merge
+     * arrays because order matters and we can't know the correct order.
+     */
+    if (Array.isArray(second)) {
+        return second;
+    }
+
+    /*
+     * First create a result object where properties from the second object
+     * overwrite properties from the first. This sets up a baseline to use
+     * later rather than needing to inspect and change every property
+     * individually.
+     */
+    const result = {
+        ...first,
+        ...second
+    };
+
+    for (const key of Object.keys(second)) {
+
+        // avoid hairy edge case
+        if (key === "__proto__") {
+            continue;
+        }
+
+        const firstValue = first[key];
+        const secondValue = second[key];
+
+        if (isNonNullObject(firstValue)) {
+            result[key] = deepMerge(firstValue, secondValue);
+        } else if (isUndefined(firstValue)) {
+            if (isNonNullObject(secondValue)) {
+                result[key] = deepMerge(
+                    Array.isArray(secondValue) ? [] : {},
+                    secondValue
+                );
+            } else if (!isUndefined(secondValue)) {
+                result[key] = secondValue;
+            }
+        }
+    }
+
+    return result;
+
+}
+
+/**
+ * Normalizes the rule options config for a given rule by ensuring that
+ * it is an array and that the first item is 0, 1, or 2.
+ * @param {Array|string|number} ruleOptions The rule options config.
+ * @returns {Array} An array of rule options.
+ */
+function normalizeRuleOptions(ruleOptions) {
+
+    const finalOptions = Array.isArray(ruleOptions)
+        ? ruleOptions.slice(0)
+        : [ruleOptions];
+
+    finalOptions[0] = ruleSeverities.get(finalOptions[0]);
+    return finalOptions;
+}
+
+//-----------------------------------------------------------------------------
+// Assertions
+//-----------------------------------------------------------------------------
+
+/**
+ * Validates that a value is a valid rule options entry.
+ * @param {any} value The value to check.
+ * @returns {void}
+ * @throws {TypeError} If the value isn't a valid rule options.
+ */
+function assertIsRuleOptions(value) {
+
+    if (typeof value !== "string" && typeof value !== "number" && !Array.isArray(value)) {
+        throw new TypeError("Expected a string, number, or array.");
+    }
+}
+
+/**
+ * Validates that a value is valid rule severity.
+ * @param {any} value The value to check.
+ * @returns {void}
+ * @throws {TypeError} If the value isn't a valid rule severity.
+ */
+function assertIsRuleSeverity(value) {
+    const severity = typeof value === "string"
+        ? ruleSeverities.get(value.toLowerCase())
+        : ruleSeverities.get(value);
+
+    if (typeof severity === "undefined") {
+        throw new TypeError("Expected severity of \"off\", 0, \"warn\", 1, \"error\", or 2.");
+    }
+}
+
+/**
+ * Validates that a given string is the form pluginName/objectName.
+ * @param {string} value The string to check.
+ * @returns {void}
+ * @throws {TypeError} If the string isn't in the correct format.
+ */
+function assertIsPluginMemberName(value) {
+    if (!/[@a-z0-9-_$]+(?:\/(?:[a-z0-9-_$]+))+$/iu.test(value)) {
+        throw new TypeError(`Expected string in the form "pluginName/objectName" but found "${value}".`);
+    }
+}
+
+/**
+ * Validates that a value is an object.
+ * @param {any} value The value to check.
+ * @returns {void}
+ * @throws {TypeError} If the value isn't an object.
+ */
+function assertIsObject(value) {
+    if (!isNonNullObject(value)) {
+        throw new TypeError("Expected an object.");
+    }
+}
+
+/**
+ * Validates that a value is an object or a string.
+ * @param {any} value The value to check.
+ * @returns {void}
+ * @throws {TypeError} If the value isn't an object or a string.
+ */
+function assertIsObjectOrString(value) {
+    if ((!value || typeof value !== "object") && typeof value !== "string") {
+        throw new TypeError("Expected an object or string.");
+    }
+}
+
+//-----------------------------------------------------------------------------
+// Low-Level Schemas
+//-----------------------------------------------------------------------------
+
+
+/** @type {ObjectPropertySchema} */
+const numberSchema = {
+    merge: "replace",
+    validate: "number"
+};
+
+/** @type {ObjectPropertySchema} */
+const booleanSchema = {
+    merge: "replace",
+    validate: "boolean"
+};
+
+/** @type {ObjectPropertySchema} */
+const deepObjectAssignSchema = {
+    merge(first = {}, second = {}) {
+        return deepMerge(first, second);
+    },
+    validate: "object"
+};
+
+//-----------------------------------------------------------------------------
+// High-Level Schemas
+//-----------------------------------------------------------------------------
+
+/** @type {ObjectPropertySchema} */
+const globalsSchema = {
+    merge: "assign",
+    validate(value) {
+
+        assertIsObject(value);
+
+        for (const key of Object.keys(value)) {
+
+            // avoid hairy edge case
+            if (key === "__proto__") {
+                continue;
+            }
+
+            if (key !== key.trim()) {
+                throw new TypeError(`Global "${key}" has leading or trailing whitespace.`);
+            }
+
+            if (!globalVariablesValues.has(value[key])) {
+                throw new TypeError(`Key "${key}": Expected "readonly", "writable", or "off".`);
+            }
+        }
+    }
+};
+
+/** @type {ObjectPropertySchema} */
+const parserSchema = {
+    merge: "replace",
+    validate(value) {
+        assertIsObjectOrString(value);
+
+        if (typeof value === "object" && typeof value.parse !== "function" && typeof value.parseForESLint !== "function") {
+            throw new TypeError("Expected object to have a parse() or parseForESLint() method.");
+        }
+
+        if (typeof value === "string") {
+            assertIsPluginMemberName(value);
+        }
+    }
+};
+
+/** @type {ObjectPropertySchema} */
+const pluginsSchema = {
+    merge(first = {}, second = {}) {
+        const keys = new Set([...Object.keys(first), ...Object.keys(second)]);
+        const result = {};
+
+        // manually validate that plugins are not redefined
+        for (const key of keys) {
+
+            // avoid hairy edge case
+            if (key === "__proto__") {
+                continue;
+            }
+
+            if (key in first && key in second && first[key] !== second[key]) {
+                throw new TypeError(`Cannot redefine plugin "${key}".`);
+            }
+
+            result[key] = second[key] || first[key];
+        }
+
+        return result;
+    },
+    validate(value) {
+
+        // first check the value to be sure it's an object
+        if (value === null || typeof value !== "object") {
+            throw new TypeError("Expected an object.");
+        }
+
+        // second check the keys to make sure they are objects
+        for (const key of Object.keys(value)) {
+
+            // avoid hairy edge case
+            if (key === "__proto__") {
+                continue;
+            }
+
+            if (value[key] === null || typeof value[key] !== "object") {
+                throw new TypeError(`Key "${key}": Expected an object.`);
+            }
+        }
+    }
+};
+
+/** @type {ObjectPropertySchema} */
+const processorSchema = {
+    merge: "replace",
+    validate(value) {
+        if (typeof value === "string") {
+            assertIsPluginMemberName(value);
+        } else if (value && typeof value === "object") {
+            if (typeof value.preprocess !== "function" || typeof value.postprocess !== "function") {
+                throw new TypeError("Object must have a preprocess() and a postprocess() method.");
+            }
+        } else {
+            throw new TypeError("Expected an object or a string.");
+        }
+    }
+};
+
+/** @type {ObjectPropertySchema} */
+const rulesSchema = {
+    merge(first = {}, second = {}) {
+
+        const result = {
+            ...first,
+            ...second
+        };
+
+        for (const ruleId of Object.keys(result)) {
+
+            // avoid hairy edge case
+            if (ruleId === "__proto__") {
+
+                /* eslint-disable-next-line no-proto -- Though deprecated, may still be present */
+                delete result.__proto__;
+                continue;
+            }
+
+            result[ruleId] = normalizeRuleOptions(result[ruleId]);
+
+            /*
+             * If either rule config is missing, then the correct
+             * config is already present and we just need to normalize
+             * the severity.
+             */
+            if (!(ruleId in first) || !(ruleId in second)) {
+                continue;
+            }
+
+            const firstRuleOptions = normalizeRuleOptions(first[ruleId]);
+            const secondRuleOptions = normalizeRuleOptions(second[ruleId]);
+
+            /*
+             * If the second rule config only has a severity (length of 1),
+             * then use that severity and keep the rest of the options from
+             * the first rule config.
+             */
+            if (secondRuleOptions.length === 1) {
+                result[ruleId] = [secondRuleOptions[0], ...firstRuleOptions.slice(1)];
+                continue;
+            }
+
+            /*
+             * In any other situation, then the second rule config takes
+             * precedence. That means the value at `result[ruleId]` is
+             * already correct and no further work is necessary.
+             */
+        }
+
+        return result;
+    },
+
+    validate(value) {
+        assertIsObject(value);
+
+        let lastRuleId;
+
+        // Performance: One try-catch has less overhead than one per loop iteration
+        try {
+
+            /*
+             * We are not checking the rule schema here because there is no
+             * guarantee that the rule definition is present at this point. Instead
+             * we wait and check the rule schema during the finalization step
+             * of calculating a config.
+             */
+            for (const ruleId of Object.keys(value)) {
+
+                // avoid hairy edge case
+                if (ruleId === "__proto__") {
+                    continue;
+                }
+
+                lastRuleId = ruleId;
+
+                const ruleOptions = value[ruleId];
+
+                assertIsRuleOptions(ruleOptions);
+
+                if (Array.isArray(ruleOptions)) {
+                    assertIsRuleSeverity(ruleOptions[0]);
+                } else {
+                    assertIsRuleSeverity(ruleOptions);
+                }
+            }
+        } catch (error) {
+            error.message = `Key "${lastRuleId}": ${error.message}`;
+            throw error;
+        }
+    }
+};
+
+/** @type {ObjectPropertySchema} */
+const sourceTypeSchema = {
+    merge: "replace",
+    validate(value) {
+        if (typeof value !== "string" || !/^(?:script|module|commonjs)$/u.test(value)) {
+            throw new TypeError("Expected \"script\", \"module\", or \"commonjs\".");
+        }
+    }
+};
+
+//-----------------------------------------------------------------------------
+// Full schema
+//-----------------------------------------------------------------------------
+
+exports.flatConfigSchema = {
+    settings: deepObjectAssignSchema,
+    linterOptions: {
+        schema: {
+            noInlineConfig: booleanSchema,
+            reportUnusedDisableDirectives: booleanSchema
+        }
+    },
+    languageOptions: {
+        schema: {
+            ecmaVersion: numberSchema,
+            sourceType: sourceTypeSchema,
+            globals: globalsSchema,
+            parser: parserSchema,
+            parserOptions: deepObjectAssignSchema
+        }
+    },
+    processor: processorSchema,
+    plugins: pluginsSchema,
+    rules: rulesSchema
+};
diff --git a/eslint/lib/config/rule-validator.js b/eslint/lib/config/rule-validator.js
new file mode 100644 (file)
index 0000000..527a56e
--- /dev/null
@@ -0,0 +1,186 @@
+/**
+ * @fileoverview Rule Validator
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
+const ajv = require("../shared/ajv")();
+
+//-----------------------------------------------------------------------------
+// Helpers
+//-----------------------------------------------------------------------------
+
+/**
+ * Finds a rule with the given ID in the given config.
+ * @param {string} ruleId The ID of the rule to find.
+ * @param {Object} config The config to search in.
+ * @throws {TypeError} For missing plugin or rule.
+ * @returns {{create: Function, schema: (Array|null)}} THe rule object.
+ */
+function findRuleDefinition(ruleId, config) {
+    const ruleIdParts = ruleId.split("/");
+    let pluginName, ruleName;
+
+    // built-in rule
+    if (ruleIdParts.length === 1) {
+        pluginName = "@";
+        ruleName = ruleIdParts[0];
+    } else {
+        ruleName = ruleIdParts.pop();
+        pluginName = ruleIdParts.join("/");
+    }
+
+    const errorMessageHeader = `Key "rules": Key "${ruleId}"`;
+    let errorMessage = `${errorMessageHeader}: Could not find plugin "${pluginName}".`;
+
+    // if the plugin exists then we need to check if the rule exists
+    if (config.plugins && config.plugins[pluginName]) {
+
+        const plugin = config.plugins[pluginName];
+
+        // first check for exact rule match
+        if (plugin.rules && plugin.rules[ruleName]) {
+            return config.plugins[pluginName].rules[ruleName];
+        }
+
+        errorMessage = `${errorMessageHeader}: Could not find "${ruleName}" in plugin "${pluginName}".`;
+
+        // otherwise, let's see if we can find the rule name elsewhere
+        for (const [otherPluginName, otherPlugin] of Object.entries(config.plugins)) {
+            if (otherPlugin.rules && otherPlugin.rules[ruleName]) {
+                errorMessage += ` Did you mean "${otherPluginName}/${ruleName}"?`;
+                break;
+            }
+        }
+
+        // falls through to throw error
+    }
+
+    throw new TypeError(errorMessage);
+}
+
+/**
+ * Gets a complete options schema for a rule.
+ * @param {{create: Function, schema: (Array|null)}} rule A new-style rule object
+ * @returns {Object} JSON Schema for the rule's options.
+ */
+function getRuleOptionsSchema(rule) {
+
+    if (!rule) {
+        return null;
+    }
+
+    const schema = rule.schema || rule.meta && rule.meta.schema;
+
+    if (Array.isArray(schema)) {
+        if (schema.length) {
+            return {
+                type: "array",
+                items: schema,
+                minItems: 0,
+                maxItems: schema.length
+            };
+        }
+        return {
+            type: "array",
+            minItems: 0,
+            maxItems: 0
+        };
+
+    }
+
+    // Given a full schema, leave it alone
+    return schema || null;
+}
+
+//-----------------------------------------------------------------------------
+// Exports
+//-----------------------------------------------------------------------------
+
+/**
+ * Implements validation functionality for the rules portion of a config.
+ */
+class RuleValidator {
+
+    /**
+     * Creates a new instance.
+     */
+    constructor() {
+
+        /**
+         * A collection of compiled validators for rules that have already
+         * been validated.
+         * @type {WeakMap}
+         */
+        this.validators = new WeakMap();
+    }
+
+    /**
+     * Validates all of the rule configurations in a config against each
+     * rule's schema.
+     * @param {Object} config The full config to validate. This object must
+     *      contain both the rules section and the plugins section.
+     * @returns {void}
+     * @throws {Error} If a rule's configuration does not match its schema.
+     */
+    validate(config) {
+
+        if (!config.rules) {
+            return;
+        }
+
+        for (const [ruleId, ruleOptions] of Object.entries(config.rules)) {
+
+            // check for edge case
+            if (ruleId === "__proto__") {
+                continue;
+            }
+
+            /*
+             * If a rule is disabled, we don't do any validation. This allows
+             * users to safely set any value to 0 or "off" without worrying
+             * that it will cause a validation error.
+             *
+             * Note: ruleOptions is always an array at this point because
+             * this validation occurs after FlatConfigArray has merged and
+             * normalized values.
+             */
+            if (ruleOptions[0] === 0) {
+                continue;
+            }
+
+            const rule = findRuleDefinition(ruleId, config);
+
+            // Precompile and cache validator the first time
+            if (!this.validators.has(rule)) {
+                const schema = getRuleOptionsSchema(rule);
+
+                if (schema) {
+                    this.validators.set(rule, ajv.compile(schema));
+                }
+            }
+
+            const validateRule = this.validators.get(rule);
+
+            if (validateRule) {
+
+                validateRule(ruleOptions.slice(1));
+
+                if (validateRule.errors) {
+                    throw new Error(`Key "rules": Key "${ruleId}": ${
+                        validateRule.errors.map(
+                            error => `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`
+                        ).join("")
+                    }`);
+                }
+            }
+        }
+    }
+}
+
+exports.RuleValidator = RuleValidator;
index ae2d2100861b4c9f8d5d495670a4db7056cabec7..8886d45ab43480d4bbec8480aa0b1eda3730cced 100644 (file)
@@ -54,7 +54,7 @@ const { version } = require("../../package.json");
  * @property {string} [ignorePath] The ignore file to use instead of .eslintignore.
  * @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance
  * @property {string} [overrideConfigFile] The configuration file to use.
- * @property {Record<string,Plugin>} [plugins] An array of plugin implementations.
+ * @property {Record<string,Plugin>|null} [plugins] Preloaded plugins. This is a map-like object, keys are plugin IDs and each value is implementation.
  * @property {"error" | "warn" | "off"} [reportUnusedDisableDirectives] the severity to report unused eslint-disable directives.
  * @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD.
  * @property {string[]} [rulePaths] An array of directories to load custom rules from.
@@ -125,7 +125,7 @@ function isArrayOfNonEmptyString(x) {
  * @returns {boolean} `true` if `x` is valid fix type.
  */
 function isFixType(x) {
-    return x === "problem" || x === "suggestion" || x === "layout";
+    return x === "directive" || x === "problem" || x === "suggestion" || x === "layout";
 }
 
 /**
@@ -151,6 +151,7 @@ class ESLintInvalidOptionsError extends Error {
 /**
  * Validates and normalizes options for the wrapped CLIEngine instance.
  * @param {ESLintOptions} options The options to process.
+ * @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
  * @returns {ESLintOptions} The normalized options.
  */
 function processOptions({
@@ -237,7 +238,7 @@ function processOptions({
         errors.push("'fix' must be a boolean or a function.");
     }
     if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
-        errors.push("'fixTypes' must be an array of any of \"problem\", \"suggestion\", and \"layout\".");
+        errors.push("'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".");
     }
     if (typeof globInputPaths !== "boolean") {
         errors.push("'globInputPaths' must be a boolean.");
@@ -421,6 +422,9 @@ function compareResultsByFilePath(a, b) {
     return 0;
 }
 
+/**
+ * Main API.
+ */
 class ESLint {
 
     /**
@@ -429,26 +433,13 @@ class ESLint {
      */
     constructor(options = {}) {
         const processedOptions = processOptions(options);
-        const cliEngine = new CLIEngine(processedOptions);
+        const cliEngine = new CLIEngine(processedOptions, { preloadedPlugins: options.plugins });
         const {
-            additionalPluginPool,
             configArrayFactory,
             lastConfigArrays
         } = getCLIEngineInternalSlots(cliEngine);
         let updated = false;
 
-        /*
-         * Address `plugins` to add plugin implementations.
-         * Operate the `additionalPluginPool` internal slot directly to avoid
-         * using `addPlugin(id, plugin)` method that resets cache everytime.
-         */
-        if (options.plugins) {
-            for (const [id, plugin] of Object.entries(options.plugins)) {
-                additionalPluginPool.set(id, plugin);
-                updated = true;
-            }
-        }
-
         /*
          * Address `overrideConfig` to set override config.
          * Operate the `configArrayFactory` internal slot directly because this
@@ -514,6 +505,39 @@ class ESLint {
         return CLIEngine.getErrorResults(results);
     }
 
+    /**
+     * Returns meta objects for each rule represented in the lint results.
+     * @param {LintResult[]} results The results to fetch rules meta for.
+     * @returns {Object} A mapping of ruleIds to rule meta objects.
+     */
+    getRulesMetaForResults(results) {
+
+        const resultRuleIds = new Set();
+
+        // first gather all ruleIds from all results
+
+        for (const result of results) {
+            for (const { ruleId } of result.messages) {
+                resultRuleIds.add(ruleId);
+            }
+        }
+
+        // create a map of all rules in the results
+
+        const { cliEngine } = privateMembersMap.get(this);
+        const rules = cliEngine.getRules();
+        const resultRules = new Map();
+
+        for (const [ruleId, rule] of rules) {
+            if (resultRuleIds.has(ruleId)) {
+                resultRules.set(ruleId, rule);
+            }
+        }
+
+        return createRulesMeta(resultRules);
+
+    }
+
     /**
      * Executes the current configuration on an array of file and directory names.
      * @param {string[]} patterns An array of file and directory names.
@@ -552,9 +576,12 @@ class ESLint {
             ...unknownOptions
         } = options || {};
 
-        for (const key of Object.keys(unknownOptions)) {
-            throw new Error(`'options' must not include the unknown option '${key}'`);
+        const unknownOptionKeys = Object.keys(unknownOptions);
+
+        if (unknownOptionKeys.length > 0) {
+            throw new Error(`'options' must not include the unknown option(s): ${unknownOptionKeys.join(", ")}`);
         }
+
         if (filePath !== void 0 && !isNonEmptyString(filePath)) {
             throw new Error("'options.filePath' must be a non-empty string or undefined");
         }
index 054c538496f88614345fb7d49fb3b70fe10d1eab..ea2523421c2ea44962d81323db8330f251ba5dd9 100644 (file)
 
 const equal = require("fast-deep-equal"),
     recConfig = require("../../conf/eslint-recommended"),
-    ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
+    {
+        Legacy: {
+            ConfigOps
+        }
+    } = require("@eslint/eslintrc"),
     { Linter } = require("../linter"),
     configRule = require("./config-rule");
 
@@ -32,9 +36,9 @@ const MAX_CONFIG_COMBINATIONS = 17, // 16 combinations + 1 for severity only
 /**
  * Information about a rule configuration, in the context of a Registry.
  * @typedef {Object}     registryItem
- * @param   {ruleConfig} config        A valid configuration for the rule
- * @param   {number}     specificity   The number of elements in the ruleConfig array
- * @param   {number}     errorCount    The number of errors encountered when linting with the config
+ * @property {ruleConfig} config A valid configuration for the rule
+ * @property {number} specificity The number of elements in the ruleConfig array
+ * @property {number} errorCount The number of errors encountered when linting with the config
  */
 
 /**
@@ -45,8 +49,8 @@ const MAX_CONFIG_COMBINATIONS = 17, // 16 combinations + 1 for severity only
 
 /**
  * Create registryItems for rules
- * @param   {rulesConfig} rulesConfig Hash of rule names and arrays of ruleConfig items
- * @returns {Object}                  registryItems for each rule in provided rulesConfig
+ * @param {rulesConfig} rulesConfig Hash of rule names and arrays of ruleConfig items
+ * @returns {Object} registryItems for each rule in provided rulesConfig
  */
 function makeRegistryItems(rulesConfig) {
     return Object.keys(rulesConfig).reduce((accumulator, ruleId) => {
@@ -69,7 +73,6 @@ function makeRegistryItems(rulesConfig) {
  */
 class Registry {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {rulesConfig} [rulesConfig] Hash of rule names and arrays of possible configurations
      */
@@ -100,7 +103,7 @@ class Registry {
      * configurations.
      *
      * The length of the returned array will be <= MAX_CONFIG_COMBINATIONS.
-     * @returns {Object[]}          "rules" configurations to use for linting
+     * @returns {Object[]} "rules" configurations to use for linting
      */
     buildRuleSets() {
         let idx = 0;
@@ -112,7 +115,7 @@ class Registry {
          *
          * This is broken out into its own function so that it doesn't need to be
          * created inside of the while loop.
-         * @param   {string} rule The ruleId to add.
+         * @param {string} rule The ruleId to add.
          * @returns {void}
          */
         const addRuleToRuleSet = function(rule) {
@@ -199,7 +202,7 @@ class Registry {
      * Creates a registry of rules which had no error-free configs.
      * The new registry is intended to be analyzed to determine whether its rules
      * should be disabled or set to warning.
-     * @returns {Registry}  A registry of failing rules.
+     * @returns {Registry} A registry of failing rules.
      */
     getFailingRulesRegistry() {
         const ruleIds = Object.keys(this.rules),
@@ -236,8 +239,8 @@ class Registry {
 
     /**
      * Return a cloned registry containing only configs with a desired specificity
-     * @param   {number} specificity Only keep configs with this specificity
-     * @returns {Registry}           A registry of rules
+     * @param {number} specificity Only keep configs with this specificity
+     * @returns {Registry} A registry of rules
      */
     filterBySpecificity(specificity) {
         const ruleIds = Object.keys(this.rules),
@@ -253,10 +256,10 @@ class Registry {
 
     /**
      * Lint SourceCodes against all configurations in the registry, and record results
-     * @param   {Object[]} sourceCodes  SourceCode objects for each filename
-     * @param   {Object}   config       ESLint config object
-     * @param   {progressCallback} [cb] Optional callback for reporting execution status
-     * @returns {Registry}              New registry with errorCount populated
+     * @param {Object[]} sourceCodes SourceCode objects for each filename
+     * @param {Object} config ESLint config object
+     * @param {progressCallback} [cb] Optional callback for reporting execution status
+     * @returns {Registry} New registry with errorCount populated
      */
     lintSourceCode(sourceCodes, config, cb) {
         let lintedRegistry = new Registry();
@@ -301,7 +304,7 @@ class Registry {
                 ruleSetIdx += 1;
 
                 if (cb) {
-                    cb(totalFilesLinting); // eslint-disable-line node/callback-return
+                    cb(totalFilesLinting); // eslint-disable-line node/callback-return -- End of function
                 }
             });
 
@@ -318,8 +321,8 @@ class Registry {
  *
  * This will return a new config with `["extends": [ ..., "eslint:recommended"]` and
  * only the rules which have configurations different from the recommended config.
- * @param   {Object} config config object
- * @returns {Object}        config object using `"extends": ["eslint:recommended"]`
+ * @param {Object} config config object
+ * @returns {Object} config object using `"extends": ["eslint:recommended"]`
  */
 function extendFromRecommended(config) {
     const newConfig = Object.assign({}, config);
index 4c648ac05517dfdd9e0c7ad25dafe619d6e7ac4d..9eb10fab3a403376c09ece81ee2a59d073b224a0 100644 (file)
@@ -23,9 +23,9 @@ const debug = require("debug")("eslint:config-file");
  * Determines sort order for object keys for json-stable-stringify
  *
  * see: https://github.com/samn/json-stable-stringify#cmp
- * @param   {Object} a The first comparison object ({key: akey, value: avalue})
- * @param   {Object} b The second comparison object ({key: bkey, value: bvalue})
- * @returns {number}   1 or -1, used in stringify cmp method
+ * @param {Object} a The first comparison object ({key: akey, value: avalue})
+ * @param {Object} b The second comparison object ({key: bkey, value: bvalue})
+ * @returns {number} 1 or -1, used in stringify cmp method
  */
 function sortByKey(a, b) {
     return a.key > b.key ? 1 : -1;
@@ -63,7 +63,7 @@ function writeYAMLConfigFile(config, filePath) {
     // lazy load YAML to improve performance when not used
     const yaml = require("js-yaml");
 
-    const content = yaml.safeDump(config, { sortKeys: true });
+    const content = yaml.dump(config, { sortKeys: true });
 
     fs.writeFileSync(filePath, content, "utf8");
 }
index 3c7f2ba0944f292131970a4327ece0e79799c03b..3c244b7bcc06f01a534b7e6b985c9027737e834f 100644 (file)
@@ -18,9 +18,13 @@ const util = require("util"),
     semver = require("semver"),
     espree = require("espree"),
     recConfig = require("../../conf/eslint-recommended"),
-    ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
+    {
+        Legacy: {
+            ConfigOps,
+            naming
+        }
+    } = require("@eslint/eslintrc"),
     log = require("../shared/logging"),
-    naming = require("@eslint/eslintrc/lib/shared/naming"),
     ModuleResolver = require("../shared/relative-module-resolver"),
     autoconfig = require("./autoconfig.js"),
     ConfigFile = require("./config-file"),
@@ -98,8 +102,8 @@ getPeerDependencies.cache = new Map();
 
 /**
  * Return necessary plugins, configs, parsers, etc. based on the config
- * @param   {Object} config  config object
- * @param   {boolean} [installESLint=true]  If `false` is given, it does not install eslint.
+ * @param {Object} config config object
+ * @param {boolean} [installESLint=true] If `false` is given, it does not install eslint.
  * @returns {string[]} An array of modules to be installed.
  */
 function getModulesList(config, installESLint) {
@@ -157,9 +161,10 @@ function getModulesList(config, installESLint) {
  *
  * Note: This clones the config object and returns a new config to avoid mutating
  * the original config parameter.
- * @param   {Object} answers  answers received from enquirer
- * @param   {Object} config   config object
- * @returns {Object}          config object with configured rules
+ * @param {Object} answers answers received from enquirer
+ * @param {Object} config config object
+ * @throws {Error} If source code retrieval fails or source code file count is 0.
+ * @returns {Object} config object with configured rules
  */
 function configureRules(answers, config) {
     const BAR_TOTAL = 20,
@@ -411,7 +416,7 @@ function hasESLintVersionConflict(answers) {
 
 /**
  * Install modules.
- * @param   {string[]} modules Modules to be installed.
+ * @param {string[]} modules Modules to be installed.
  * @returns {void}
  */
 function installModules(modules) {
@@ -422,9 +427,9 @@ function installModules(modules) {
 /* istanbul ignore next: no need to test enquirer */
 /**
  * Ask user to install modules.
- * @param   {string[]} modules Array of modules to be installed.
- * @param   {boolean} packageJsonExists Indicates if package.json is existed.
- * @returns {Promise} Answer that indicates if user wants to install.
+ * @param {string[]} modules Array of modules to be installed.
+ * @param {boolean} packageJsonExists Indicates if package.json is existed.
+ * @returns {Promise<void>} Answer that indicates if user wants to install.
  */
 function askInstallModules(modules, packageJsonExists) {
 
@@ -460,7 +465,7 @@ function askInstallModules(modules, packageJsonExists) {
 /* istanbul ignore next: no need to test enquirer */
 /**
  * Ask use a few questions on command prompt
- * @returns {Promise} The promise with the result of the prompt
+ * @returns {Promise<void>} The promise with the result of the prompt
  */
 function promptUser() {
 
index 7aec89c3df004f52d06ff5cdb734ea1cd6426994..131e84a60c5c3dc9daaac8086611195b9bc963b0 100644 (file)
@@ -17,8 +17,8 @@ const builtInRules = require("../rules");
 
 /**
  * Wrap all of the elements of an array into arrays.
- * @param   {*[]}     xs Any array.
- * @returns {Array[]}    An array of arrays.
+ * @param {*[]} xs Any array.
+ * @returns {Array[]} An array of arrays.
  */
 function explodeArray(xs) {
     return xs.reduce((accumulator, x) => {
@@ -33,9 +33,9 @@ function explodeArray(xs) {
  *
  * For example:
  * combineArrays([a, [b, c]], [x, y]); // -> [[a, x], [a, y], [b, c, x], [b, c, y]]
- * @param   {Array} arr1 The first array to combine.
- * @param   {Array} arr2 The second array to combine.
- * @returns {Array}      A mixture of the elements of the first and second arrays.
+ * @param {Array} arr1 The first array to combine.
+ * @param {Array} arr2 The second array to combine.
+ * @returns {Array} A mixture of the elements of the first and second arrays.
  */
 function combineArrays(arr1, arr2) {
     const res = [];
@@ -70,8 +70,8 @@ function combineArrays(arr1, arr2) {
  *     [{before: true}, {before: false}],
  *     [{after: true}, {after: false}]
  * ]
- * @param   {Object[]} objects Array of objects, each with one property/value pair
- * @returns {Array[]}          Array of arrays of objects grouped by property
+ * @param {Object[]} objects Array of objects, each with one property/value pair
+ * @returns {Array[]} Array of arrays of objects grouped by property
  */
 function groupByProperty(objects) {
     const groupedObj = objects.reduce((accumulator, obj) => {
@@ -97,7 +97,7 @@ function groupByProperty(objects) {
  * Configs may also have one or more additional elements to specify rule
  * configuration or options.
  * @typedef {Array|number} ruleConfig
- * @param {number}  0  The rule's severity (0, 1, 2).
+ * @param {number} 0 The rule's severity (0, 1, 2).
  */
 
 /**
@@ -131,9 +131,9 @@ function groupByProperty(objects) {
  *     {before: false, after: true},
  *     {before: false, after: false}
  * ]
- * @param   {Object[]} objArr1 Single key/value objects, all with the same key
- * @param   {Object[]} objArr2 Single key/value objects, all with another key
- * @returns {Object[]}         Combined objects for each combination of input properties and values
+ * @param {Object[]} objArr1 Single key/value objects, all with the same key
+ * @param {Object[]} objArr2 Single key/value objects, all with another key
+ * @returns {Object[]} Combined objects for each combination of input properties and values
  */
 function combinePropertyObjects(objArr1, objArr2) {
     const res = [];
@@ -174,7 +174,6 @@ function combinePropertyObjects(objArr1, objArr2) {
  */
 class RuleConfigSet {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {ruleConfig[]} configs Valid rule configurations
      */
@@ -206,7 +205,7 @@ class RuleConfigSet {
 
     /**
      * Add rule configs from an array of strings (schema enums)
-     * @param  {string[]} enums Array of valid rule options (e.g. ["always", "never"])
+     * @param {string[]} enums Array of valid rule options (e.g. ["always", "never"])
      * @returns {void}
      */
     addEnums(enums) {
@@ -215,7 +214,7 @@ class RuleConfigSet {
 
     /**
      * Add rule configurations from a schema object
-     * @param  {Object} obj Schema item with type === "object"
+     * @param {Object} obj Schema item with type === "object"
      * @returns {boolean} true if at least one schema for the object could be generated, false otherwise
      */
     addObject(obj) {
@@ -260,8 +259,8 @@ class RuleConfigSet {
 
 /**
  * Generate valid rule configurations based on a schema object
- * @param   {Object} schema  A rule's schema object
- * @returns {Array[]}        Valid rule configurations
+ * @param {Object} schema A rule's schema object
+ * @returns {Array[]} Valid rule configurations
  */
 function generateConfigsFromSchema(schema) {
     const configSet = new RuleConfigSet();
index 35191cc08764c015fcbd5c3f681abf643cbef08f..4a8efe964f330c4716f72f6cdddbf49ec5eb6369 100644 (file)
@@ -21,8 +21,8 @@ const fs = require("fs"),
 /**
  * Find the closest package.json file, starting at process.cwd (by default),
  * and working up to root.
- * @param   {string} [startDir=process.cwd()] Starting directory
- * @returns {string}                          Absolute path to closest package.json file
+ * @param {string} [startDir=process.cwd()] Starting directory
+ * @returns {string} Absolute path to closest package.json file
  */
 function findPackageJson(startDir) {
     let dir = path.resolve(startDir || process.cwd());
@@ -45,13 +45,12 @@ function findPackageJson(startDir) {
 
 /**
  * Install node modules synchronously and save to devDependencies in package.json
- * @param   {string|string[]} packages Node module or modules to install
+ * @param {string|string[]} packages Node module or modules to install
  * @returns {void}
  */
 function installSyncSaveDev(packages) {
     const packageList = Array.isArray(packages) ? packages : [packages];
-    const npmProcess = spawn.sync("npm", ["i", "--save-dev"].concat(packageList),
-        { stdio: "inherit" });
+    const npmProcess = spawn.sync("npm", ["i", "--save-dev"].concat(packageList), { stdio: "inherit" });
     const error = npmProcess.error;
 
     if (error && error.code === "ENOENT") {
@@ -87,12 +86,13 @@ function fetchPeerDependencies(packageName) {
 
 /**
  * Check whether node modules are include in a project's package.json.
- * @param   {string[]} packages           Array of node module names
- * @param   {Object}  opt                 Options Object
- * @param   {boolean} opt.dependencies    Set to true to check for direct dependencies
- * @param   {boolean} opt.devDependencies Set to true to check for development dependencies
- * @param   {boolean} opt.startdir        Directory to begin searching from
- * @returns {Object}                      An object whose keys are the module names
+ * @param {string[]} packages Array of node module names
+ * @param {Object} opt Options Object
+ * @param {boolean} opt.dependencies Set to true to check for direct dependencies
+ * @param {boolean} opt.devDependencies Set to true to check for development dependencies
+ * @param {boolean} opt.startdir Directory to begin searching from
+ * @throws {Error} If cannot find valid `package.json` file.
+ * @returns {Object} An object whose keys are the module names
  *                                        and values are booleans indicating installation.
  */
 function check(packages, opt) {
@@ -134,9 +134,9 @@ function check(packages, opt) {
  * package.json.
  *
  * Convenience wrapper around check().
- * @param   {string[]} packages  Array of node modules to check.
- * @param   {string}   rootDir   The directory containing a package.json
- * @returns {Object}             An object whose keys are the module names
+ * @param {string[]} packages Array of node modules to check.
+ * @param {string} rootDir The directory containing a package.json
+ * @returns {Object} An object whose keys are the module names
  *                               and values are booleans indicating installation.
  */
 function checkDeps(packages, rootDir) {
@@ -148,8 +148,8 @@ function checkDeps(packages, rootDir) {
  * package.json.
  *
  * Convenience wrapper around check().
- * @param   {string[]} packages  Array of node modules to check.
- * @returns {Object}             An object whose keys are the module names
+ * @param {string[]} packages Array of node modules to check.
+ * @returns {Object} An object whose keys are the module names
  *                               and values are booleans indicating installation.
  */
 function checkDevDeps(packages) {
@@ -158,7 +158,7 @@ function checkDevDeps(packages) {
 
 /**
  * Check whether package.json is found in current path.
- * @param   {string} [startDir] Starting directory
+ * @param {string} [startDir] Starting directory
  * @returns {boolean} Whether a package.json is found in current path.
  */
 function checkPackageJson(startDir) {
index dca6541d1ed32831791d172d1fdaa7769f6c1f76..08c20e5d56e2149d98d474b70650dd45639397f5 100644 (file)
@@ -23,7 +23,7 @@ const { CLIEngine } = require("../cli-engine");
  * TODO1: Expose the API that enumerates target files.
  * TODO2: Extract the creation logic of `SourceCode` from `Linter` class.
  */
-const { getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); // eslint-disable-line node/no-restricted-require
+const { getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); // eslint-disable-line node/no-restricted-require -- Todo
 
 const debug = require("debug")("eslint:source-code-utils");
 
@@ -33,9 +33,10 @@ const debug = require("debug")("eslint:source-code-utils");
 
 /**
  * Get the SourceCode object for a single file
- * @param   {string}     filename The fully resolved filename to get SourceCode from.
- * @param   {Object}     engine  A CLIEngine.
- * @returns {Array}               Array of the SourceCode object representing the file
+ * @param {string} filename The fully resolved filename to get SourceCode from.
+ * @param {Object} engine A CLIEngine.
+ * @throws {Error} Upon fatal errors from execution.
+ * @returns {Array} Array of the SourceCode object representing the file
  *                                and fatal error message.
  */
 function getSourceCodeOfFile(filename, engine) {
@@ -97,7 +98,7 @@ function getSourceCodeOfFiles(patterns, options, callback) {
             sourceCodes[filename] = sourceCode;
         }
         if (callback) {
-            callback(filenames.length); // eslint-disable-line node/callback-return
+            callback(filenames.length); // eslint-disable-line node/callback-return -- End of function
         }
     });
 
index 0ba69ca9cc44c73bfd8556e3298a93092404fbd9..e5f2e528ef86ea9f77d8c1292cb30510c4d76994 100644 (file)
@@ -5,6 +5,8 @@
 
 "use strict";
 
+const escapeRegExp = require("escape-string-regexp");
+
 /**
  * Compares the locations of two objects in a source file
  * @param {{line: number, column: number}} itemA The first object
@@ -16,6 +18,177 @@ function compareLocations(itemA, itemB) {
     return itemA.line - itemB.line || itemA.column - itemB.column;
 }
 
+/**
+ * Groups a set of directives into sub-arrays by their parent comment.
+ * @param {Directive[]} directives Unused directives to be removed.
+ * @returns {Directive[][]} Directives grouped by their parent comment.
+ */
+function groupByParentComment(directives) {
+    const groups = new Map();
+
+    for (const directive of directives) {
+        const { unprocessedDirective: { parentComment } } = directive;
+
+        if (groups.has(parentComment)) {
+            groups.get(parentComment).push(directive);
+        } else {
+            groups.set(parentComment, [directive]);
+        }
+    }
+
+    return [...groups.values()];
+}
+
+/**
+ * Creates removal details for a set of directives within the same comment.
+ * @param {Directive[]} directives Unused directives to be removed.
+ * @param {Token} commentToken The backing Comment token.
+ * @returns {{ description, fix, position }[]} Details for later creation of output Problems.
+ */
+function createIndividualDirectivesRemoval(directives, commentToken) {
+
+    /*
+     * `commentToken.value` starts right after `//` or `/*`.
+     * All calculated offsets will be relative to this index.
+     */
+    const commentValueStart = commentToken.range[0] + "//".length;
+
+    // Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`)
+    const listStartOffset = /^\s*\S+\s+/u.exec(commentToken.value)[0].length;
+
+    /*
+     * Get the list text without any surrounding whitespace. In order to preserve the original
+     * formatting, we don't want to change that whitespace.
+     *
+     *     // eslint-disable-line rule-one , rule-two , rule-three -- comment
+     *                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+     */
+    const listText = commentToken.value
+        .slice(listStartOffset) // remove directive name and all whitespace before the list
+        .split(/\s-{2,}\s/u)[0] // remove `-- comment`, if it exists
+        .trimRight(); // remove all whitespace after the list
+
+    /*
+     * We can assume that `listText` contains multiple elements.
+     * Otherwise, this function wouldn't be called - if there is
+     * only one rule in the list, then the whole comment must be removed.
+     */
+
+    return directives.map(directive => {
+        const { ruleId } = directive;
+
+        const regex = new RegExp(String.raw`(?:^|\s*,\s*)${escapeRegExp(ruleId)}(?:\s*,\s*|$)`, "u");
+        const match = regex.exec(listText);
+        const matchedText = match[0];
+        const matchStartOffset = listStartOffset + match.index;
+        const matchEndOffset = matchStartOffset + matchedText.length;
+
+        const firstIndexOfComma = matchedText.indexOf(",");
+        const lastIndexOfComma = matchedText.lastIndexOf(",");
+
+        let removalStartOffset, removalEndOffset;
+
+        if (firstIndexOfComma !== lastIndexOfComma) {
+
+            /*
+             * Since there are two commas, this must one of the elements in the middle of the list.
+             * Matched range starts where the previous rule name ends, and ends where the next rule name starts.
+             *
+             *     // eslint-disable-line rule-one , rule-two , rule-three -- comment
+             *                                    ^^^^^^^^^^^^^^
+             *
+             * We want to remove only the content between the two commas, and also one of the commas.
+             *
+             *     // eslint-disable-line rule-one , rule-two , rule-three -- comment
+             *                                     ^^^^^^^^^^^
+             */
+            removalStartOffset = matchStartOffset + firstIndexOfComma;
+            removalEndOffset = matchStartOffset + lastIndexOfComma;
+
+        } else {
+
+            /*
+             * This is either the first element or the last element.
+             *
+             * If this is the first element, matched range starts where the first rule name starts
+             * and ends where the second rule name starts. This is exactly the range we want
+             * to remove so that the second rule name will start where the first one was starting
+             * and thus preserve the original formatting.
+             *
+             *     // eslint-disable-line rule-one , rule-two , rule-three -- comment
+             *                            ^^^^^^^^^^^
+             *
+             * Similarly, if this is the last element, we've already matched the range we want to
+             * remove. The previous rule name will end where the last one was ending, relative
+             * to the content on the right side.
+             *
+             *     // eslint-disable-line rule-one , rule-two , rule-three -- comment
+             *                                               ^^^^^^^^^^^^^
+             */
+            removalStartOffset = matchStartOffset;
+            removalEndOffset = matchEndOffset;
+        }
+
+        return {
+            description: `'${ruleId}'`,
+            fix: {
+                range: [
+                    commentValueStart + removalStartOffset,
+                    commentValueStart + removalEndOffset
+                ],
+                text: ""
+            },
+            position: directive.unprocessedDirective
+        };
+    });
+}
+
+/**
+ * Creates a description of deleting an entire unused disable comment.
+ * @param {Directive[]} directives Unused directives to be removed.
+ * @param {Token} commentToken The backing Comment token.
+ * @returns {{ description, fix, position }} Details for later creation of an output Problem.
+ */
+function createCommentRemoval(directives, commentToken) {
+    const { range } = commentToken;
+    const ruleIds = directives.filter(directive => directive.ruleId).map(directive => `'${directive.ruleId}'`);
+
+    return {
+        description: ruleIds.length <= 2
+            ? ruleIds.join(" or ")
+            : `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds[ruleIds.length - 1]}`,
+        fix: {
+            range,
+            text: " "
+        },
+        position: directives[0].unprocessedDirective
+    };
+}
+
+/**
+ * Parses details from directives to create output Problems.
+ * @param {Directive[]} allDirectives Unused directives to be removed.
+ * @returns {{ description, fix, position }[]} Details for later creation of output Problems.
+ */
+function processUnusedDisableDirectives(allDirectives) {
+    const directiveGroups = groupByParentComment(allDirectives);
+
+    return directiveGroups.flatMap(
+        directives => {
+            const { parentComment } = directives[0].unprocessedDirective;
+            const remainingRuleIds = new Set(parentComment.ruleIds);
+
+            for (const directive of directives) {
+                remainingRuleIds.delete(directive.ruleId);
+            }
+
+            return remainingRuleIds.size
+                ? createIndividualDirectivesRemoval(directives, parentComment.commentToken)
+                : [createCommentRemoval(directives, parentComment.commentToken)];
+        }
+    );
+}
+
 /**
  * This is the same as the exported function, except that it
  * doesn't handle disable-line and disable-next-line directives, and it always reports unused
@@ -82,17 +255,22 @@ function applyDirectives(options) {
         }
     }
 
-    const unusedDisableDirectives = options.directives
-        .filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive))
-        .map(directive => ({
+    const unusedDisableDirectivesToReport = options.directives
+        .filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive));
+
+    const processed = processUnusedDisableDirectives(unusedDisableDirectivesToReport);
+
+    const unusedDisableDirectives = processed
+        .map(({ description, fix, position }) => ({
             ruleId: null,
-            message: directive.ruleId
-                ? `Unused eslint-disable directive (no problems were reported from '${directive.ruleId}').`
+            message: description
+                ? `Unused eslint-disable directive (no problems were reported from ${description}).`
                 : "Unused eslint-disable directive (no problems were reported).",
-            line: directive.unprocessedDirective.line,
-            column: directive.unprocessedDirective.column,
+            line: position.line,
+            column: position.column,
             severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
-            nodeType: null
+            nodeType: null,
+            ...options.disableFixes ? {} : { fix }
         }));
 
     return { problems, unusedDisableDirectives };
@@ -113,30 +291,17 @@ function applyDirectives(options) {
  * @param {{ruleId: (string|null), line: number, column: number}[]} options.problems
  * A list of problems reported by rules, sorted by increasing location in the file, with one-based columns.
  * @param {"off" | "warn" | "error"} options.reportUnusedDisableDirectives If `"warn"` or `"error"`, adds additional problems for unused directives
+ * @param {boolean} options.disableFixes If true, it doesn't make `fix` properties.
  * @returns {{ruleId: (string|null), line: number, column: number}[]}
  * A list of reported problems that were not disabled by the directive comments.
  */
-module.exports = ({ directives, problems, reportUnusedDisableDirectives = "off" }) => {
+module.exports = ({ directives, disableFixes, problems, reportUnusedDisableDirectives = "off" }) => {
     const blockDirectives = directives
         .filter(directive => directive.type === "disable" || directive.type === "enable")
         .map(directive => Object.assign({}, directive, { unprocessedDirective: directive }))
         .sort(compareLocations);
 
-    /**
-     * Returns a new array formed by applying a given callback function to each element of the array, and then flattening the result by one level.
-     * TODO(stephenwade): Replace this with array.flatMap when we drop support for Node v10
-     * @param {any[]} array The array to process
-     * @param {Function} fn The function to use
-     * @returns {any[]} The result array
-     */
-    function flatMap(array, fn) {
-        const mapped = array.map(fn);
-        const flattened = [].concat(...mapped);
-
-        return flattened;
-    }
-
-    const lineDirectives = flatMap(directives, directive => {
+    const lineDirectives = directives.flatMap(directive => {
         switch (directive.type) {
             case "disable":
             case "enable":
@@ -162,11 +327,13 @@ module.exports = ({ directives, problems, reportUnusedDisableDirectives = "off"
     const blockDirectivesResult = applyDirectives({
         problems,
         directives: blockDirectives,
+        disableFixes,
         reportUnusedDisableDirectives
     });
     const lineDirectivesResult = applyDirectives({
         problems: blockDirectivesResult.problems,
         directives: lineDirectives,
+        disableFixes,
         reportUnusedDisableDirectives
     });
 
index 47427c11a3d2500d458ddeaf0830a8d9c62b64b8..2dcc2734884459538b99d7ce32aa07851a2891fa 100644 (file)
@@ -29,6 +29,18 @@ function isCaseNode(node) {
     return Boolean(node.test);
 }
 
+/**
+ * Checks if a given node appears as the value of a PropertyDefinition node.
+ * @param {ASTNode} node THe node to check.
+ * @returns {boolean} `true` if the node is a PropertyDefinition value,
+ *      false if not.
+ */
+function isPropertyDefinitionValue(node) {
+    const parent = node.parent;
+
+    return parent && parent.type === "PropertyDefinition" && parent.value === node;
+}
+
 /**
  * Checks whether the given logical operator is taken into account for the code
  * path analysis.
@@ -138,6 +150,7 @@ function isIdentifierReference(node) {
             return parent.id !== node;
 
         case "Property":
+        case "PropertyDefinition":
         case "MethodDefinition":
             return (
                 parent.key !== node ||
@@ -388,29 +401,68 @@ function processCodePathToEnter(analyzer, node) {
     let state = codePath && CodePath.getState(codePath);
     const parent = node.parent;
 
+    /**
+     * Creates a new code path and trigger the onCodePathStart event
+     * based on the currently selected node.
+     * @param {string} origin The reason the code path was started.
+     * @returns {void}
+     */
+    function startCodePath(origin) {
+        if (codePath) {
+
+            // Emits onCodePathSegmentStart events if updated.
+            forwardCurrentToHead(analyzer, node);
+            debug.dumpState(node, state, false);
+        }
+
+        // Create the code path of this scope.
+        codePath = analyzer.codePath = new CodePath({
+            id: analyzer.idGenerator.next(),
+            origin,
+            upper: codePath,
+            onLooped: analyzer.onLooped
+        });
+        state = CodePath.getState(codePath);
+
+        // Emits onCodePathStart events.
+        debug.dump(`onCodePathStart ${codePath.id}`);
+        analyzer.emitter.emit("onCodePathStart", codePath, node);
+    }
+
+    /*
+     * Special case: The right side of class field initializer is considered
+     * to be its own function, so we need to start a new code path in this
+     * case.
+     */
+    if (isPropertyDefinitionValue(node)) {
+        startCodePath("class-field-initializer");
+
+        /*
+         * Intentional fall through because `node` needs to also be
+         * processed by the code below. For example, if we have:
+         *
+         * class Foo {
+         *     a = () => {}
+         * }
+         *
+         * In this case, we also need start a second code path.
+         */
+
+    }
+
     switch (node.type) {
         case "Program":
+            startCodePath("program");
+            break;
+
         case "FunctionDeclaration":
         case "FunctionExpression":
         case "ArrowFunctionExpression":
-            if (codePath) {
-
-                // Emits onCodePathSegmentStart events if updated.
-                forwardCurrentToHead(analyzer, node);
-                debug.dumpState(node, state, false);
-            }
-
-            // Create the code path of this scope.
-            codePath = analyzer.codePath = new CodePath(
-                analyzer.idGenerator.next(),
-                codePath,
-                analyzer.onLooped
-            );
-            state = CodePath.getState(codePath);
+            startCodePath("function");
+            break;
 
-            // Emits onCodePathStart events.
-            debug.dump(`onCodePathStart ${codePath.id}`);
-            analyzer.emitter.emit("onCodePathStart", codePath, node);
+        case "StaticBlock":
+            startCodePath("class-static-block");
             break;
 
         case "ChainExpression":
@@ -503,6 +555,7 @@ function processCodePathToEnter(analyzer, node) {
  * @returns {void}
  */
 function processCodePathToExit(analyzer, node) {
+
     const codePath = analyzer.codePath;
     const state = CodePath.getState(codePath);
     let dontForward = false;
@@ -627,28 +680,39 @@ function processCodePathToExit(analyzer, node) {
  * @returns {void}
  */
 function postprocess(analyzer, node) {
-    switch (node.type) {
-        case "Program":
-        case "FunctionDeclaration":
-        case "FunctionExpression":
-        case "ArrowFunctionExpression": {
-            let codePath = analyzer.codePath;
 
-            // Mark the current path as the final node.
-            CodePath.getState(codePath).makeFinal();
+    /**
+     * Ends the code path for the current node.
+     * @returns {void}
+     */
+    function endCodePath() {
+        let codePath = analyzer.codePath;
+
+        // Mark the current path as the final node.
+        CodePath.getState(codePath).makeFinal();
 
-            // Emits onCodePathSegmentEnd event of the current segments.
-            leaveFromCurrentSegment(analyzer, node);
+        // Emits onCodePathSegmentEnd event of the current segments.
+        leaveFromCurrentSegment(analyzer, node);
 
-            // Emits onCodePathEnd event of this code path.
-            debug.dump(`onCodePathEnd ${codePath.id}`);
-            analyzer.emitter.emit("onCodePathEnd", codePath, node);
-            debug.dumpDot(codePath);
+        // Emits onCodePathEnd event of this code path.
+        debug.dump(`onCodePathEnd ${codePath.id}`);
+        analyzer.emitter.emit("onCodePathEnd", codePath, node);
+        debug.dumpDot(codePath);
 
-            codePath = analyzer.codePath = analyzer.codePath.upper;
-            if (codePath) {
-                debug.dumpState(node, CodePath.getState(codePath), true);
-            }
+        codePath = analyzer.codePath = analyzer.codePath.upper;
+        if (codePath) {
+            debug.dumpState(node, CodePath.getState(codePath), true);
+        }
+
+    }
+
+    switch (node.type) {
+        case "Program":
+        case "FunctionDeclaration":
+        case "FunctionExpression":
+        case "ArrowFunctionExpression":
+        case "StaticBlock": {
+            endCodePath();
             break;
         }
 
@@ -662,6 +726,27 @@ function postprocess(analyzer, node) {
         default:
             break;
     }
+
+    /*
+     * Special case: The right side of class field initializer is considered
+     * to be its own function, so we need to end a code path in this
+     * case.
+     *
+     * We need to check after the other checks in order to close the
+     * code paths in the correct order for code like this:
+     *
+     *
+     * class Foo {
+     *     a = () => {}
+     * }
+     *
+     * In this case, The ArrowFunctionExpression code path is closed first
+     * and then we need to close the code path for the PropertyDefinition
+     * value.
+     */
+    if (isPropertyDefinitionValue(node)) {
+        endCodePath();
+    }
 }
 
 //------------------------------------------------------------------------------
@@ -674,7 +759,6 @@ function postprocess(analyzer, node) {
  */
 class CodePathAnalyzer {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {EventGenerator} eventGenerator An event generator to wrap.
      */
index ca96ad34189acc042504e90b46189f67efda4faf..fad559a2ad23e1fcc52775cd8124c995051ac807 100644 (file)
@@ -33,7 +33,6 @@ function isReachable(segment) {
  */
 class CodePathSegment {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {string} id An identifier.
      * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
index f75e60e28a807e40ad1e0cc31ed85c1bd1f85d59..118f70a170756bf42cd881b7bb1f957e346dd4a9 100644 (file)
@@ -219,7 +219,6 @@ function finalizeTestSegmentsOfFor(context, choiceContext, head) {
  */
 class CodePathState {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {IdGenerator} idGenerator An id generator to generate id for code
      *   path segments.
@@ -360,6 +359,7 @@ class CodePathState {
 
     /**
      * Pops the last choice context and finalizes it.
+     * @throws {Error} (Unreachable.)
      * @returns {ChoiceContext} The popped context.
      */
     popChoiceContext() {
@@ -450,6 +450,7 @@ class CodePathState {
     /**
      * Makes a code path segment of the right-hand operand of a logical
      * expression.
+     * @throws {Error} (Unreachable.)
      * @returns {void}
      */
     makeLogicalRight() {
@@ -965,6 +966,7 @@ class CodePathState {
      *   `WhileStatement`, `DoWhileStatement`, `ForStatement`, `ForInStatement`,
      *   and `ForStatement`.
      * @param {string|null} label A label of the node which was triggered.
+     * @throws {Error} (Unreachable - unknown type.)
      * @returns {void}
      */
     pushLoopContext(type, label) {
@@ -1036,6 +1038,7 @@ class CodePathState {
 
     /**
      * Pops the last context of a loop statement and finalizes it.
+     * @throws {Error} (Unreachable - unknown type.)
      * @returns {void}
      */
     popLoopContext() {
index 49b37c6b2272db59f2ab9c6f05639fc63858cc10..0e666277094355801feda17e4f6cfe5b15d048fc 100644 (file)
@@ -21,13 +21,15 @@ const IdGenerator = require("./id-generator");
  */
 class CodePath {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
-     * @param {string} id An identifier.
-     * @param {CodePath|null} upper The code path of the upper function scope.
-     * @param {Function} onLooped A callback function to notify looping.
+     * Creates a new instance.
+     * @param {Object} options Options for the function (see below).
+     * @param {string} options.id An identifier.
+     * @param {string} options.origin The type of code path origin.
+     * @param {CodePath|null} options.upper The code path of the upper function scope.
+     * @param {Function} options.onLooped A callback function to notify looping.
      */
-    constructor(id, upper, onLooped) {
+    constructor({ id, origin, upper, onLooped }) {
 
         /**
          * The identifier of this code path.
@@ -36,6 +38,13 @@ class CodePath {
          */
         this.id = id;
 
+        /**
+         * The reason that this code path was started. May be "program",
+         * "function", "class-field-initializer", or "class-static-block".
+         * @type {string}
+         */
+        this.origin = origin;
+
         /**
          * The code path of the upper function scope.
          * @type {CodePath|null}
index a4cb99a22e0126d0153008029674fbb02bc1c48f..ca64862db32cea6db0358d0db18f418900ab4763 100644 (file)
@@ -21,7 +21,7 @@ const debug = require("debug")("eslint:code-path");
  * @returns {string} Id of the segment.
  */
 /* istanbul ignore next */
-function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc
+function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc -- Ignoring
     return segment.id + (segment.reachable ? "" : "!");
 }
 
@@ -115,7 +115,7 @@ module.exports = {
         const traceMap = Object.create(null);
         const arrows = this.makeDotArrows(codePath, traceMap);
 
-        for (const id in traceMap) { // eslint-disable-line guard-for-in
+        for (const id in traceMap) { // eslint-disable-line guard-for-in -- Want ability to traverse prototype
             const segment = traceMap[id];
 
             text += `${id}[`;
index 2e872b5c0dbf5a9a779554ed858a9fb4f9484bab..04c59b5e4172539e727a878a0c07059417e5003f 100644 (file)
@@ -97,7 +97,6 @@ function mergeExtraSegments(context, segments) {
  */
 class ForkContext {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {IdGenerator} idGenerator An identifier generator for segments.
      * @param {ForkContext|null} upper An upper fork context.
index 4cb2e0e3d87cc1767d04541849a6a5474bbbf17c..83787a4ea5a85f86b385f7ebdf27420dea861e92 100644 (file)
@@ -18,7 +18,6 @@
  */
 class IdGenerator {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {string} prefix Optional. A prefix of generated ids.
      */
index 07bbead281cbf6cabb1712bd3f541967a2752fa2..b88c5e6c850929873e64b3b64a87c480647ca501 100644 (file)
@@ -3,7 +3,7 @@
  * @author Nicholas C. Zakas
  */
 
-/* eslint-disable class-methods-use-this*/
+/* eslint class-methods-use-this: off -- Methods desired on instance */
 "use strict";
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 const levn = require("levn"),
-    ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops");
+    {
+        Legacy: {
+            ConfigOps
+        }
+    } = require("@eslint/eslintrc/universal");
 
 const debug = require("debug")("eslint:config-comment-parser");
 
index e94b507b5dd30ab522fa6630cc435b29c55ef453..4e07a25751e114102711d94e30d5ac1367ac8a56 100644 (file)
@@ -16,11 +16,15 @@ const
     evk = require("eslint-visitor-keys"),
     espree = require("espree"),
     merge = require("lodash.merge"),
-    BuiltInEnvironments = require("@eslint/eslintrc/conf/environments"),
     pkg = require("../../package.json"),
     astUtils = require("../shared/ast-utils"),
-    ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
-    ConfigValidator = require("@eslint/eslintrc/lib/shared/config-validator"),
+    {
+        Legacy: {
+            ConfigOps,
+            ConfigValidator,
+            environments: BuiltInEnvironments
+        }
+    } = require("@eslint/eslintrc/universal"),
     Traverser = require("../shared/traverser"),
     { SourceCode } = require("../source-code"),
     CodePathAnalyzer = require("./code-path-analysis/code-path-analyzer"),
@@ -37,15 +41,17 @@ const
 const debug = require("debug")("eslint:linter");
 const MAX_AUTOFIX_PASSES = 10;
 const DEFAULT_PARSER_NAME = "espree";
+const DEFAULT_ECMA_VERSION = 5;
 const commentParser = new ConfigCommentParser();
 const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } };
+const parserSymbol = Symbol.for("eslint.RuleTester.parser");
 
 //------------------------------------------------------------------------------
 // Typedefs
 //------------------------------------------------------------------------------
 
-/** @typedef {InstanceType<import("../cli-engine/config-array")["ConfigArray"]>} ConfigArray */
-/** @typedef {InstanceType<import("../cli-engine/config-array")["ExtractedConfig"]>} ExtractedConfig */
+/** @typedef {InstanceType<import("../cli-engine/config-array").ConfigArray>} ConfigArray */
+/** @typedef {InstanceType<import("../cli-engine/config-array").ExtractedConfig>} ExtractedConfig */
 /** @typedef {import("../shared/types").ConfigData} ConfigData */
 /** @typedef {import("../shared/types").Environment} Environment */
 /** @typedef {import("../shared/types").GlobalConf} GlobalConf */
@@ -54,17 +60,19 @@ const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, colum
 /** @typedef {import("../shared/types").Processor} Processor */
 /** @typedef {import("../shared/types").Rule} Rule */
 
+/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
 /**
  * @template T
  * @typedef {{ [P in keyof T]-?: T[P] }} Required
  */
+/* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
 
 /**
  * @typedef {Object} DisableDirective
- * @property {("disable"|"enable"|"disable-line"|"disable-next-line")} type
- * @property {number} line
- * @property {number} column
- * @property {(string|null)} ruleId
+ * @property {("disable"|"enable"|"disable-line"|"disable-next-line")} type Type of directive
+ * @property {number} line The line number
+ * @property {number} column The column number
+ * @property {(string|null)} ruleId The rule ID
  */
 
 /**
@@ -92,12 +100,12 @@ const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, colum
  * @typedef {Object} ProcessorOptions
  * @property {(filename:string, text:string) => boolean} [filterCodeBlock] the
  *      predicate function that selects adopt code blocks.
- * @property {Processor["postprocess"]} [postprocess] postprocessor for report
+ * @property {Processor.postprocess} [postprocess] postprocessor for report
  *      messages. If provided, this should accept an array of the message lists
  *      for each code block returned from the preprocessor, apply a mapping to
  *      the messages as appropriate, and return a one-dimensional array of
  *      messages.
- * @property {Processor["preprocess"]} [preprocess] preprocessor for source text.
+ * @property {Processor.preprocess} [preprocess] preprocessor for source text.
  *      If provided, this should accept a string of source text, and return an
  *      array of code blocks to lint.
  */
@@ -240,14 +248,14 @@ function createLintingProblem(options) {
  * Creates a collection of disable directives from a comment
  * @param {Object} options to create disable directives
  * @param {("disable"|"enable"|"disable-line"|"disable-next-line")} options.type The type of directive comment
- * @param {{line: number, column: number}} options.loc The 0-based location of the comment token
+ * @param {token} options.commentToken The Comment token
  * @param {string} options.value The value after the directive in the comment
  * comment specified no specific rules, so it applies to all rules (e.g. `eslint-disable`)
  * @param {function(string): {create: Function}} options.ruleMapper A map from rule IDs to defined rules
  * @returns {Object} Directives and problems from the comment
  */
 function createDisableDirectives(options) {
-    const { type, loc, value, ruleMapper } = options;
+    const { commentToken, type, value, ruleMapper } = options;
     const ruleIds = Object.keys(commentParser.parseListConfig(value));
     const directiveRules = ruleIds.length ? ruleIds : [null];
     const result = {
@@ -255,13 +263,15 @@ function createDisableDirectives(options) {
         directiveProblems: [] // problems in directives
     };
 
+    const parentComment = { commentToken, ruleIds };
+
     for (const ruleId of directiveRules) {
 
         // push to directives, if the rule is defined(including null, e.g. /*eslint enable*/)
         if (ruleId === null || ruleMapper(ruleId) !== null) {
-            result.directives.push({ type, line: loc.start.line, column: loc.start.column + 1, ruleId });
+            result.directives.push({ parentComment, type, line: commentToken.loc.start.line, column: commentToken.loc.start.column + 1, ruleId });
         } else {
-            result.directiveProblems.push(createLintingProblem({ ruleId, loc }));
+            result.directiveProblems.push(createLintingProblem({ ruleId, loc: commentToken.loc }));
         }
     }
     return result;
@@ -342,7 +352,7 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
             case "eslint-disable-next-line":
             case "eslint-disable-line": {
                 const directiveType = directiveText.slice("eslint-".length);
-                const options = { type: directiveType, loc: comment.loc, value: directiveValue, ruleMapper };
+                const options = { commentToken: comment, type: directiveType, value: directiveValue, ruleMapper };
                 const { directives, directiveProblems } = createDisableDirectives(options);
 
                 disableDirectives.push(...directives);
@@ -432,10 +442,16 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
 
 /**
  * Normalize ECMAScript version from the initial config
- * @param  {number} ecmaVersion ECMAScript version from the initial config
+ * @param {Parser} parser The parser which uses this options.
+ * @param {number} ecmaVersion ECMAScript version from the initial config
  * @returns {number} normalized ECMAScript version
  */
-function normalizeEcmaVersion(ecmaVersion) {
+function normalizeEcmaVersion(parser, ecmaVersion) {
+    if ((parser[parserSymbol] || parser) === espree) {
+        if (ecmaVersion === "latest") {
+            return espree.latestEcmaVersion;
+        }
+    }
 
     /*
      * Calculate ECMAScript edition number from official year version starting with
@@ -444,7 +460,7 @@ function normalizeEcmaVersion(ecmaVersion) {
     return ecmaVersion >= 2015 ? ecmaVersion - 2009 : ecmaVersion;
 }
 
-const eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)\*\//gsu;
+const eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)(?:\*\/|$)/gsu;
 
 /**
  * Checks whether or not there is a comment which has "eslint-env *" in a given text.
@@ -457,10 +473,12 @@ function findEslintEnv(text) {
     eslintEnvPattern.lastIndex = 0;
 
     while ((match = eslintEnvPattern.exec(text)) !== null) {
-        retv = Object.assign(
-            retv || {},
-            commentParser.parseListConfig(stripDirectiveComment(match[1]))
-        );
+        if (match[0].endsWith("*/")) {
+            retv = Object.assign(
+                retv || {},
+                commentParser.parseListConfig(stripDirectiveComment(match[1]))
+            );
+        }
     }
 
     return retv;
@@ -521,12 +539,13 @@ function normalizeVerifyOptions(providedOptions, config) {
 
 /**
  * Combines the provided parserOptions with the options from environments
- * @param {string} parserName The parser name which uses this options.
+ * @param {Parser} parser The parser which uses this options.
  * @param {ParserOptions} providedOptions The provided 'parserOptions' key in a config
  * @param {Environment[]} enabledEnvironments The environments enabled in configuration and with inline comments
  * @returns {ParserOptions} Resulting parser options after merge
  */
-function resolveParserOptions(parserName, providedOptions, enabledEnvironments) {
+function resolveParserOptions(parser, providedOptions, enabledEnvironments) {
+
     const parserOptionsFromEnv = enabledEnvironments
         .filter(env => env.parserOptions)
         .reduce((parserOptions, env) => merge(parserOptions, env.parserOptions), {});
@@ -542,12 +561,7 @@ function resolveParserOptions(parserName, providedOptions, enabledEnvironments)
         mergedParserOptions.ecmaFeatures = Object.assign({}, mergedParserOptions.ecmaFeatures, { globalReturn: false });
     }
 
-    /*
-     * TODO: @aladdin-add
-     * 1. for a 3rd-party parser, do not normalize parserOptions
-     * 2. for espree, no need to do this (espree will do it)
-     */
-    mergedParserOptions.ecmaVersion = normalizeEcmaVersion(mergedParserOptions.ecmaVersion);
+    mergedParserOptions.ecmaVersion = normalizeEcmaVersion(parser, mergedParserOptions.ecmaVersion);
 
     return mergedParserOptions;
 }
@@ -606,13 +620,13 @@ function getRuleOptions(ruleConfig) {
  */
 function analyzeScope(ast, parserOptions, visitorKeys) {
     const ecmaFeatures = parserOptions.ecmaFeatures || {};
-    const ecmaVersion = parserOptions.ecmaVersion || 5;
+    const ecmaVersion = parserOptions.ecmaVersion || DEFAULT_ECMA_VERSION;
 
     return eslintScope.analyze(ast, {
         ignoreEval: true,
         nodejsScope: ecmaFeatures.globalReturn,
         impliedStrict: ecmaFeatures.impliedStrict,
-        ecmaVersion,
+        ecmaVersion: typeof ecmaVersion === "number" ? ecmaVersion : 6,
         sourceType: parserOptions.sourceType || "script",
         childVisitorKeys: visitorKeys || evk.KEYS,
         fallback: Traverser.getKeys
@@ -754,6 +768,7 @@ function markVariableAsUsed(scopeManager, currentNode, parserOptions, name) {
  * Runs a rule, and gets its listeners
  * @param {Rule} rule A normalized rule with a `create` method
  * @param {Context} ruleContext The context that should be passed to the rule
+ * @throws {any} Any error during the rule's `create`
  * @returns {Object} A map of selector listeners provided by the rule
  */
 function createRuleListeners(rule, ruleContext) {
@@ -921,8 +936,16 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser
                         }
                         const problem = reportTranslator(...args);
 
-                        if (problem.fix && rule.meta && !rule.meta.fixable) {
-                            throw new Error("Fixable rules should export a `meta.fixable` property.");
+                        if (problem.fix && !(rule.meta && rule.meta.fixable)) {
+                            throw new Error("Fixable rules must set the `meta.fixable` property to \"code\" or \"whitespace\".");
+                        }
+                        if (problem.suggestions && !(rule.meta && rule.meta.hasSuggestions === true)) {
+                            if (rule.meta && rule.meta.docs && typeof rule.meta.docs.suggestion !== "undefined") {
+
+                                // Encourage migration from the former property name.
+                                throw new Error("Rules with suggestions must set the `meta.hasSuggestions` property to `true`. `meta.docs.suggestion` is ignored by ESLint.");
+                            }
+                            throw new Error("Rules with suggestions must set the `meta.hasSuggestions` property to `true`.");
                         }
                         lintingProblems.push(problem);
                     }
@@ -932,13 +955,31 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser
 
         const ruleListeners = createRuleListeners(rule, ruleContext);
 
+        /**
+         * Include `ruleId` in error logs
+         * @param {Function} ruleListener A rule method that listens for a node.
+         * @returns {Function} ruleListener wrapped in error handler
+         */
+        function addRuleErrorHandler(ruleListener) {
+            return function ruleErrorHandler(...listenerArgs) {
+                try {
+                    return ruleListener(...listenerArgs);
+                } catch (e) {
+                    e.ruleId = ruleId;
+                    throw e;
+                }
+            };
+        }
+
         // add all the selectors from the rule as listeners
         Object.keys(ruleListeners).forEach(selector => {
+            const ruleListener = timing.enabled
+                ? timing.time(ruleId, ruleListeners[selector])
+                : ruleListeners[selector];
+
             emitter.on(
                 selector,
-                timing.enabled
-                    ? timing.time(ruleId, ruleListeners[selector])
-                    : ruleListeners[selector]
+                addRuleErrorHandler(ruleListener)
             );
         });
     });
@@ -1023,7 +1064,7 @@ function normalizeCwd(cwd) {
     }
 
     // It's more explicit to assign the undefined
-    // eslint-disable-next-line no-undefined
+    // eslint-disable-next-line no-undefined -- Consistently returning a value
     return undefined;
 }
 
@@ -1046,7 +1087,7 @@ class Linter {
     /**
      * Initialize the Linter.
      * @param {Object} [config] the config object
-     * @param {string} [config.cwd]  path to a directory that should be considered as the current working directory, can be undefined.
+     * @param {string} [config.cwd] path to a directory that should be considered as the current working directory, can be undefined.
      */
     constructor({ cwd } = {}) {
         internalSlotsMap.set(this, {
@@ -1074,6 +1115,7 @@ class Linter {
      * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
      * @param {ConfigData} providedConfig An ESLintConfig instance to configure everything.
      * @param {VerifyOptions} [providedOptions] The optional filename of the file being checked.
+     * @throws {Error} If during rule execution.
      * @returns {LintMessage[]} The results as an array of messages or an empty array if no messages.
      */
     _verifyWithoutProcessors(textOrSourceCode, providedConfig, providedOptions) {
@@ -1123,7 +1165,7 @@ class Linter {
             .map(envName => getEnv(slots, envName))
             .filter(env => env);
 
-        const parserOptions = resolveParserOptions(parserName, config.parserOptions || {}, enabledEnvs);
+        const parserOptions = resolveParserOptions(parser, config.parserOptions || {}, enabledEnvs);
         const configuredGlobals = resolveGlobals(config.globals || {}, enabledEnvs);
         const settings = config.settings || {};
 
@@ -1199,11 +1241,17 @@ class Linter {
             debug("Parser Options:", parserOptions);
             debug("Parser Path:", parserName);
             debug("Settings:", settings);
+
+            if (err.ruleId) {
+                err.message += `\nRule: "${err.ruleId}"`;
+            }
+
             throw err;
         }
 
         return applyDisableDirectives({
             directives: commentDirectives.disableDirectives,
+            disableFixes: options.disableFixes,
             problems: lintingProblems
                 .concat(commentDirectives.problems)
                 .sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column),
@@ -1291,8 +1339,7 @@ class Linter {
         const text = ensureText(textOrSourceCode);
         const preprocess = options.preprocess || (rawText => [rawText]);
 
-        // TODO(stephenwade): Replace this with array.flat() when we drop support for Node v10
-        const postprocess = options.postprocess || (array => [].concat(...array));
+        const postprocess = options.postprocess || (messagesList => messagesList.flat());
         const filterCodeBlock =
             options.filterCodeBlock ||
             (blockFilename => blockFilename.endsWith(".js"));
index 8b619fdff83e11fadd43857808062d0b2675e80a..d56bef2fa9defde5473775643accc90ced99a8a6 100644 (file)
@@ -37,9 +37,7 @@ const esquery = require("esquery");
  * @returns {any[]} The union of the input arrays
  */
 function union(...arrays) {
-
-    // TODO(stephenwade): Replace this with arrays.flat() when we drop support for Node v10
-    return [...new Set([].concat(...arrays))];
+    return [...new Set(arrays.flat())];
 }
 
 /**
@@ -100,6 +98,13 @@ function getPossibleTypes(parsedSelector) {
         case "adjacent":
             return getPossibleTypes(parsedSelector.right);
 
+        case "class":
+            if (parsedSelector.name === "function") {
+                return ["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"];
+            }
+
+            return null;
+
         default:
             return null;
 
@@ -239,7 +244,6 @@ function parseSelector(rawSelector) {
  */
 class NodeEventGenerator {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {SafeEmitter} emitter
      * An SafeEmitter which is the destination of events. This emitter must already
index 75005c16e5808992506da1a1bad6c9dbcc4f3992..781b3136ec5639c2426a9c6f1946993f7cfb2392 100644 (file)
@@ -32,18 +32,18 @@ const interpolate = require("./interpolate");
 /**
  * Information about the report
  * @typedef {Object} ReportInfo
- * @property {string} ruleId
- * @property {(0|1|2)} severity
- * @property {(string|undefined)} message
- * @property {(string|undefined)} [messageId]
- * @property {number} line
- * @property {number} column
- * @property {(number|undefined)} [endLine]
- * @property {(number|undefined)} [endColumn]
- * @property {(string|null)} nodeType
- * @property {string} source
- * @property {({text: string, range: (number[]|null)}|null)} [fix]
- * @property {Array<{text: string, range: (number[]|null)}|null>} [suggestions]
+ * @property {string} ruleId The rule ID
+ * @property {(0|1|2)} severity Severity of the error
+ * @property {(string|undefined)} message The message
+ * @property {(string|undefined)} [messageId] The message ID
+ * @property {number} line The line number
+ * @property {number} column The column number
+ * @property {(number|undefined)} [endLine] The ending line number
+ * @property {(number|undefined)} [endColumn] The ending column number
+ * @property {(string|null)} nodeType Type of node
+ * @property {string} source Source text
+ * @property {({text: string, range: (number[]|null)}|null)} [fix] The fix object
+ * @property {Array<{text: string, range: (number[]|null)}|null>} [suggestions] Suggestion info
  */
 
 //------------------------------------------------------------------------------
index a153266efb3f4da3e571cd7f55be03b3feb279a9..647bab68784388c6100ad9f53c2ea0a508b285dd 100644 (file)
@@ -30,6 +30,9 @@ function normalizeRule(rule) {
 // Public Interface
 //------------------------------------------------------------------------------
 
+/**
+ * A storage for rules.
+ */
 class Rules {
     constructor() {
         this._rules = Object.create(null);
index ab212230d398e4b9a9e3923d803d5260f5a701ee..f4837c1ddcf51d6baeba8c7f4d9e7a6bf5a07a69 100644 (file)
@@ -12,8 +12,8 @@
 /**
  * An event emitter
  * @typedef {Object} SafeEmitter
- * @property {function(eventName: string, listenerFunc: Function): void} on Adds a listener for a given event name
- * @property {function(eventName: string, arg1?: any, arg2?: any, arg3?: any)} emit Emits an event with a given name.
+ * @property {(eventName: string, listenerFunc: Function) => void} on Adds a listener for a given event name
+ * @property {(eventName: string, arg1?: any, arg2?: any, arg3?: any) => void} emit Emits an event with a given name.
  * This calls all the listeners that were listening for that name, with `arg1`, `arg2`, and `arg3` as arguments.
  * @property {function(): string[]} eventNames Gets the list of event names that have registered listeners.
  */
index 53dc1dc6be73c486ec95c2782f30cc1102956a39..15386c926c25ab3030557f5aa0a9d60b2832c0a2 100644 (file)
@@ -80,8 +80,8 @@ SourceCodeFixer.applyFixes = function(sourceText, messages, shouldFix) {
 
     /**
      * Try to use the 'fix' from a problem.
-     * @param   {Message} problem The message object to apply fixes from
-     * @returns {boolean}         Whether fix was successfully applied
+     * @param {Message} problem The message object to apply fixes from
+     * @returns {boolean} Whether fix was successfully applied
      */
     function attemptFix(problem) {
         const fix = problem.fix;
index 58230306855abe44229a7f650260b7f1f3db0422..c9ab01ec64906721a36e6904f99b041ff3a312e5 100644 (file)
@@ -116,7 +116,7 @@ function display(data) {
         return ALIGN[index](":", width + extraAlignment, "-");
     }).join("|"));
 
-    console.log(table.join("\n")); // eslint-disable-line no-console
+    console.log(table.join("\n")); // eslint-disable-line no-console -- Debugging function
 }
 
 /* istanbul ignore next */
@@ -126,7 +126,7 @@ module.exports = (function() {
 
     /**
      * Time the run
-     * @param {*} key key from the data object
+     * @param {any} key key from the data object
      * @param {Function} fn function to be called
      * @returns {Function} function to be executed
      * @private
index 92c140bd3c8b2eb6d4ecd6dccda49ddce7b43709..2dd186de3e82a432ff99c977a02d298825dc7265 100644 (file)
@@ -32,7 +32,7 @@ const optionator = require("optionator");
  * @property {string[]} [ext] Specify JavaScript file extensions
  * @property {boolean} fix Automatically fix problems
  * @property {boolean} fixDryRun Automatically fix problems without saving the changes to the file system
- * @property {("problem" | "suggestion" | "layout")[]} [fixType] Specify the types of fixes to apply (problem, suggestion, layout)
+ * @property {("directive" | "problem" | "suggestion" | "layout")[]} [fixType] Specify the types of fixes to apply (directive, problem, suggestion, layout)
  * @property {string} format Use a specific output format
  * @property {string[]} [global] Define global variables
  * @property {boolean} [help] Show help
@@ -151,7 +151,7 @@ module.exports = optionator({
         {
             option: "fix-type",
             type: "Array",
-            description: "Specify the types of fixes to apply (problem, suggestion, layout)"
+            description: "Specify the types of fixes to apply (directive, problem, suggestion, layout)"
         },
         {
             heading: "Ignoring files"
@@ -290,6 +290,12 @@ module.exports = optionator({
             default: "true",
             description: "Prevent errors when pattern is unmatched"
         },
+        {
+            option: "exit-on-fatal-error",
+            type: "Boolean",
+            default: "false",
+            description: "Exit with exit code 2 in case of fatal error"
+        },
         {
             option: "debug",
             type: "Boolean",
index b08303c62b2626778541bc32be3be53a149263c7..7f590a5ea70995e81d19b123e08ac043032bdc17 100644 (file)
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-/* global describe, it */
+/* eslint-env mocha -- Mocha wrapper */
 
 /*
  * This is a wrapper around mocha to allow for DRY unittests for eslint
@@ -53,6 +53,9 @@ const
 const ajv = require("../shared/ajv")({ strictDefaults: true });
 
 const espreePath = require.resolve("espree");
+const parserSymbol = Symbol.for("eslint.RuleTester.parser");
+
+const { SourceCode } = require("../source-code");
 
 //------------------------------------------------------------------------------
 // Typedefs
@@ -60,9 +63,11 @@ const espreePath = require.resolve("espree");
 
 /** @typedef {import("../shared/types").Parser} Parser */
 
+/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
 /**
  * A test case that is expected to pass lint.
  * @typedef {Object} ValidTestCase
+ * @property {string} [name] Name for the test case.
  * @property {string} code Code for the test case.
  * @property {any[]} [options] Options for the test case.
  * @property {{ [name: string]: any }} [settings] Settings for the test case.
@@ -71,11 +76,13 @@ const espreePath = require.resolve("espree");
  * @property {{ [name: string]: any }} [parserOptions] Options for the parser.
  * @property {{ [name: string]: "readonly" | "writable" | "off" }} [globals] The additional global variables.
  * @property {{ [name: string]: boolean }} [env] Environments for the test case.
+ * @property {boolean} [only] Run only this test case or the subset of test cases with this property.
  */
 
 /**
  * A test case that is expected to fail lint.
  * @typedef {Object} InvalidTestCase
+ * @property {string} [name] Name for the test case.
  * @property {string} code Code for the test case.
  * @property {number | Array<TestCaseError | string | RegExp>} errors Expected errors.
  * @property {string | null} [output] The expected code after autofixes are applied. If set to `null`, the test runner will assert that no autofix is suggested.
@@ -86,6 +93,7 @@ const espreePath = require.resolve("espree");
  * @property {{ [name: string]: any }} [parserOptions] Options for the parser.
  * @property {{ [name: string]: "readonly" | "writable" | "off" }} [globals] The additional global variables.
  * @property {{ [name: string]: boolean }} [env] Environments for the test case.
+ * @property {boolean} [only] Run only this test case or the subset of test cases with this property.
  */
 
 /**
@@ -100,6 +108,7 @@ const espreePath = require.resolve("espree");
  * @property {number} [endLine] The 1-based line number of the reported end location.
  * @property {number} [endColumn] The 1-based column number of the reported end location.
  */
+/* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
 
 //------------------------------------------------------------------------------
 // Private Members
@@ -117,11 +126,13 @@ let defaultConfig = { rules: {} };
  * configuration
  */
 const RuleTesterParameters = [
+    "name",
     "code",
     "filename",
     "options",
     "errors",
-    "output"
+    "output",
+    "only"
 ];
 
 /*
@@ -206,7 +217,7 @@ function freezeDeeply(x) {
  */
 function sanitize(text) {
     return text.replace(
-        /[\u0000-\u0009\u000b-\u001a]/gu, // eslint-disable-line no-control-regex
+        /[\u0000-\u0009\u000b-\u001a]/gu, // eslint-disable-line no-control-regex -- Escaping controls
         c => `\\u${c.codePointAt(0).toString(16).padStart(4, "0")}`
     );
 }
@@ -236,6 +247,7 @@ function defineStartEndAsError(objName, node) {
     });
 }
 
+
 /**
  * Define `start`/`end` properties of all nodes of the given AST as throwing error.
  * @param {ASTNode} ast The root node to errorize `start`/`end` properties.
@@ -255,8 +267,10 @@ function defineStartEndAsErrorInTree(ast, visitorKeys) {
  * @returns {Parser} Wrapped parser object.
  */
 function wrapParser(parser) {
+
     if (typeof parser.parseForESLint === "function") {
         return {
+            [parserSymbol]: parser,
             parseForESLint(...args) {
                 const ret = parser.parseForESLint(...args);
 
@@ -265,7 +279,9 @@ function wrapParser(parser) {
             }
         };
     }
+
     return {
+        [parserSymbol]: parser,
         parse(...args) {
             const ast = parser.parse(...args);
 
@@ -275,6 +291,17 @@ function wrapParser(parser) {
     };
 }
 
+/**
+ * Function to replace `SourceCode.prototype.getComments`.
+ * @returns {void}
+ * @throws {Error} Deprecation message.
+ */
+function getCommentsDeprecation() {
+    throw new Error(
+        "`SourceCode#getComments()` is deprecated and will be removed in a future major version. Use `getCommentsBefore()`, `getCommentsAfter()`, and `getCommentsInside()` instead."
+    );
+}
+
 //------------------------------------------------------------------------------
 // Public Interface
 //------------------------------------------------------------------------------
@@ -282,12 +309,14 @@ function wrapParser(parser) {
 // default separators for testing
 const DESCRIBE = Symbol("describe");
 const IT = Symbol("it");
+const IT_ONLY = Symbol("itOnly");
 
 /**
  * This is `it` default handler if `it` don't exist.
  * @this {Mocha}
  * @param {string} text The description of the test case.
  * @param {Function} method The logic of the test case.
+ * @throws {Error} Any error upon execution of `method`.
  * @returns {any} Returned value of `method`.
  */
 function itDefaultHandler(text, method) {
@@ -312,6 +341,9 @@ function describeDefaultHandler(text, method) {
     return method.call(this);
 }
 
+/**
+ * Mocha test wrapper.
+ */
 class RuleTester {
 
     /**
@@ -343,6 +375,7 @@ class RuleTester {
     /**
      * Set the configuration to use for all future tests
      * @param {Object} config the configuration to use.
+     * @throws {TypeError} If non-object config.
      * @returns {void}
      */
     static setDefaultConfig(config) {
@@ -400,6 +433,46 @@ class RuleTester {
         this[IT] = value;
     }
 
+    /**
+     * Adds the `only` property to a test to run it in isolation.
+     * @param {string | ValidTestCase | InvalidTestCase} item A single test to run by itself.
+     * @returns {ValidTestCase | InvalidTestCase} The test with `only` set.
+     */
+    static only(item) {
+        if (typeof item === "string") {
+            return { code: item, only: true };
+        }
+
+        return { ...item, only: true };
+    }
+
+    static get itOnly() {
+        if (typeof this[IT_ONLY] === "function") {
+            return this[IT_ONLY];
+        }
+        if (typeof this[IT] === "function" && typeof this[IT].only === "function") {
+            return Function.bind.call(this[IT].only, this[IT]);
+        }
+        if (typeof it === "function" && typeof it.only === "function") {
+            return Function.bind.call(it.only, it);
+        }
+
+        if (typeof this[DESCRIBE] === "function" || typeof this[IT] === "function") {
+            throw new Error(
+                "Set `RuleTester.itOnly` to use `only` with a custom test framework.\n" +
+                "See https://eslint.org/docs/developer-guide/nodejs-api#customizing-ruletester for more."
+            );
+        }
+        if (typeof it === "function") {
+            throw new Error("The current test framework does not support exclusive tests with `only`.");
+        }
+        throw new Error("To use `only`, use RuleTester with a test framework that provides `it.only()` like Mocha.");
+    }
+
+    static set itOnly(value) {
+        this[IT_ONLY] = value;
+    }
+
     /**
      * Define a rule for one particular run of tests.
      * @param {string} name The name of the rule to define.
@@ -418,6 +491,8 @@ class RuleTester {
      *   valid: (ValidTestCase | string)[],
      *   invalid: InvalidTestCase[]
      * }} test The collection of tests to run.
+     * @throws {TypeError|Error} If non-object `test`, or if a required
+     * scenario of the given type is missing.
      * @returns {void}
      */
     run(ruleName, rule, test) {
@@ -461,6 +536,7 @@ class RuleTester {
         /**
          * Run the rule for the given item
          * @param {string|Object} item Item to run the rule against
+         * @throws {Error} If an invalid schema.
          * @returns {Object} Eslint run result
          * @private
          */
@@ -557,7 +633,16 @@ class RuleTester {
             validate(config, "rule-tester", id => (id === ruleName ? rule : null));
 
             // Verify the code.
-            const messages = linter.verify(code, config, filename);
+            const { getComments } = SourceCode.prototype;
+            let messages;
+
+            try {
+                SourceCode.prototype.getComments = getCommentsDeprecation;
+                messages = linter.verify(code, config, filename);
+            } finally {
+                SourceCode.prototype.getComments = getComments;
+            }
+
             const fatalErrorMessage = messages.find(m => m.fatal);
 
             assert(!fatalErrorMessage, `A fatal parsing error occurred: ${fatalErrorMessage && fatalErrorMessage.message}`);
@@ -610,7 +695,8 @@ class RuleTester {
             const messages = result.messages;
 
             assert.strictEqual(messages.length, 0, util.format("Should have no errors but had %d: %s",
-                messages.length, util.inspect(messages)));
+                messages.length,
+                util.inspect(messages)));
 
             assertASTDidntChange(result.beforeAST, result.afterAST);
         }
@@ -665,13 +751,18 @@ class RuleTester {
                 }
 
                 assert.strictEqual(messages.length, item.errors, util.format("Should have %d error%s but had %d: %s",
-                    item.errors, item.errors === 1 ? "" : "s", messages.length, util.inspect(messages)));
+                    item.errors,
+                    item.errors === 1 ? "" : "s",
+                    messages.length,
+                    util.inspect(messages)));
             } else {
                 assert.strictEqual(
-                    messages.length, item.errors.length,
-                    util.format(
+                    messages.length, item.errors.length, util.format(
                         "Should have %d error%s but had %d: %s",
-                        item.errors.length, item.errors.length === 1 ? "" : "s", messages.length, util.inspect(messages)
+                        item.errors.length,
+                        item.errors.length === 1 ? "" : "s",
+                        messages.length,
+                        util.inspect(messages)
                     )
                 );
 
@@ -865,16 +956,6 @@ class RuleTester {
                 );
             }
 
-            // Rules that produce fixes must have `meta.fixable` property.
-            if (result.output !== item.code) {
-                assert.ok(
-                    hasOwnProperty(rule, "meta"),
-                    "Fixable rules should export a `meta.fixable` property."
-                );
-
-                // Linter throws if a rule that produced a fix has `meta` but doesn't have `meta.fixable`.
-            }
-
             assertASTDidntChange(result.beforeAST, result.afterAST);
         }
 
@@ -885,23 +966,29 @@ class RuleTester {
         RuleTester.describe(ruleName, () => {
             RuleTester.describe("valid", () => {
                 test.valid.forEach(valid => {
-                    RuleTester.it(sanitize(typeof valid === "object" ? valid.code : valid), () => {
-                        testValidTemplate(valid);
-                    });
+                    RuleTester[valid.only ? "itOnly" : "it"](
+                        sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
+                        () => {
+                            testValidTemplate(valid);
+                        }
+                    );
                 });
             });
 
             RuleTester.describe("invalid", () => {
                 test.invalid.forEach(invalid => {
-                    RuleTester.it(sanitize(invalid.code), () => {
-                        testInvalidTemplate(invalid);
-                    });
+                    RuleTester[invalid.only ? "itOnly" : "it"](
+                        sanitize(invalid.name || invalid.code),
+                        () => {
+                            testInvalidTemplate(invalid);
+                        }
+                    );
                 });
             });
         });
     }
 }
 
-RuleTester[DESCRIBE] = RuleTester[IT] = null;
+RuleTester[DESCRIBE] = RuleTester[IT] = RuleTester[IT_ONLY] = null;
 
 module.exports = RuleTester;
index 0e0d07a00c971da3dc9e4373fcb18d75633ae8a3..f047252668000e2ad6c29ef34cb19614e8d665c1 100644 (file)
@@ -140,7 +140,6 @@ module.exports = {
 
         docs: {
             description: "enforce getter and setter pairs in objects and classes",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/accessor-pairs"
         },
index b4b4dd430f65644a5d1b03f69c1bee189f9c6df4..28a05b3504308809154b3d238ba1e3fa14a49cfc 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "enforce linebreaks after opening and before closing array brackets",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/array-bracket-newline"
         },
index c2b77a641f7294288cfb358580559ad2b77f914f..1eea99c14ca1fba3d20ba6854fe369ec7335cedd 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing inside array brackets",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/array-bracket-spacing"
         },
index 7267347149da9aea2175cf837ae7e5a5762e2fda..d13ecd717906f0171b17369e977355c8d875c980 100644 (file)
@@ -139,7 +139,6 @@ module.exports = {
 
         docs: {
             description: "enforce `return` statements in callbacks of array methods",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/array-callback-return"
         },
index b7a967865b92a16c8f128ec4eb812bc39d543a65..535fa2155a204266db50572502a37a43940a1e7a 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "enforce line breaks after each array element",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/array-element-newline"
         },
index 5b8a5f0116717aa456ecb64b7f4f1c20882553e5..3a3f544444e99c69ced5af94a9e368f28d470c5e 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "require braces around arrow function bodies",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/arrow-body-style"
         },
index eaa1aab023889ebb1a40abdce393b9ca988b9fd8..4f4dea0e23735f402b145cc06b7da7a731850ad1 100644 (file)
@@ -33,7 +33,6 @@ module.exports = {
 
         docs: {
             description: "require parentheses around arrow function arguments",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/arrow-parens"
         },
index e5110c6c87d43f72b6a50bfbbefe8fb38fc23d08..9e1ed71d4bd22376e195601fd5b5d18b2f035072 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing before and after the arrow in arrow functions",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/arrow-spacing"
         },
index 481057ba6c7c6bdc7adc1f2be5e2ddf90f2720b9..d98250b062bcb22a2ef9aeec78e62eed7e6255a5 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "enforce the use of variables within the scope they are defined",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/block-scoped-var"
         },
@@ -113,6 +112,8 @@ module.exports = {
             "SwitchStatement:exit": exitScope,
             CatchClause: enterScope,
             "CatchClause:exit": exitScope,
+            StaticBlock: enterScope,
+            "StaticBlock:exit": exitScope,
 
             // Finds and reports references which are outside of valid scope.
             VariableDeclaration: checkForVariables
index c4b30b0b70b9839bd145fdd36c5e947d0d1d1a72..990b441e34dd8a7636b9d32c1bb245c8cd35e9bc 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "disallow or enforce spaces inside of blocks after opening block and before closing block",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/block-spacing"
         },
@@ -41,7 +40,7 @@ module.exports = {
 
         /**
          * Gets the open brace token from a given node.
-         * @param {ASTNode} node A BlockStatement/SwitchStatement node to get.
+         * @param {ASTNode} node A BlockStatement/StaticBlock/SwitchStatement node to get.
          * @returns {Token} The token of the open brace.
          */
         function getOpenBrace(node) {
@@ -51,6 +50,12 @@ module.exports = {
                 }
                 return sourceCode.getLastToken(node, 1);
             }
+
+            if (node.type === "StaticBlock") {
+                return sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
+            }
+
+            // "BlockStatement"
             return sourceCode.getFirstToken(node);
         }
 
@@ -73,8 +78,8 @@ module.exports = {
         }
 
         /**
-         * Reports invalid spacing style inside braces.
-         * @param {ASTNode} node A BlockStatement/SwitchStatement node to get.
+         * Checks and reports invalid spacing style inside braces.
+         * @param {ASTNode} node A BlockStatement/StaticBlock/SwitchStatement node to check.
          * @returns {void}
          */
         function checkSpacingInsideBraces(node) {
@@ -158,6 +163,7 @@ module.exports = {
 
         return {
             BlockStatement: checkSpacingInsideBraces,
+            StaticBlock: checkSpacingInsideBraces,
             SwitchStatement: checkSpacingInsideBraces
         };
     }
index 07223d10d4a3b45dfea0ea6dfa56de66a3e1d406..89f9ba559538d83ae6333dc9653a217964e218ac 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent brace style for blocks",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/brace-style"
         },
@@ -156,6 +155,12 @@ module.exports = {
                     validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
                 }
             },
+            StaticBlock(node) {
+                validateCurlyPair(
+                    sourceCode.getFirstToken(node, { skip: 1 }), // skip the `static` token
+                    sourceCode.getLastToken(node)
+                );
+            },
             ClassBody(node) {
                 validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
             },
index fa66e6383b7cc781599b0216a178611cfa60bff2..449b9a96227363a7ef9574f1f257e13a1509f8a3 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Enforce return after a callback.
  * @author Jamund Ferguson
+ * @deprecated in ESLint v7.0.0
  */
 "use strict";
 
@@ -18,7 +19,6 @@ module.exports = {
 
         docs: {
             description: "require `return` statements after callbacks",
-            category: "Node.js and CommonJS",
             recommended: false,
             url: "https://eslint.org/docs/rules/callback-return"
         },
index d34656cfabe731b7516475be66531f3fa904c954..7e8fc68da6319e04ea89922e0c7a4e4094387021 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "enforce camelcase naming convention",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/camelcase"
         },
@@ -55,32 +54,25 @@ module.exports = {
         ],
 
         messages: {
-            notCamelCase: "Identifier '{{name}}' is not in camel case."
+            notCamelCase: "Identifier '{{name}}' is not in camel case.",
+            notCamelCasePrivate: "#{{name}} is not in camel case."
         }
     },
 
     create(context) {
-
         const options = context.options[0] || {};
-        let properties = options.properties || "";
+        const properties = options.properties === "never" ? "never" : "always";
         const ignoreDestructuring = options.ignoreDestructuring;
         const ignoreImports = options.ignoreImports;
         const ignoreGlobals = options.ignoreGlobals;
         const allow = options.allow || [];
 
-        let globalScope;
-
-        if (properties !== "always" && properties !== "never") {
-            properties = "always";
-        }
-
         //--------------------------------------------------------------------------
         // Helpers
         //--------------------------------------------------------------------------
 
         // contains reported nodes to avoid reporting twice on destructuring with shorthand notation
-        const reported = [];
-        const ALLOWED_PARENT_TYPES = new Set(["CallExpression", "NewExpression"]);
+        const reported = new Set();
 
         /**
          * Checks if a string contains an underscore and isn't all upper-case
@@ -89,9 +81,10 @@ module.exports = {
          * @private
          */
         function isUnderscored(name) {
+            const nameBody = name.replace(/^_+|_+$/gu, "");
 
             // if there's an underscore, it might be A_CONSTANT, which is okay
-            return name.includes("_") && name !== name.toUpperCase();
+            return nameBody.includes("_") && nameBody !== nameBody.toUpperCase();
         }
 
         /**
@@ -107,219 +100,292 @@ module.exports = {
         }
 
         /**
-         * Checks if a parent of a node is an ObjectPattern.
-         * @param {ASTNode} node The node to check.
-         * @returns {boolean} if the node is inside an ObjectPattern
+         * Checks if a given name is good or not.
+         * @param {string} name The name to check.
+         * @returns {boolean} `true` if the name is good.
          * @private
          */
-        function isInsideObjectPattern(node) {
-            let current = node;
-
-            while (current) {
-                const parent = current.parent;
+        function isGoodName(name) {
+            return !isUnderscored(name) || isAllowed(name);
+        }
 
-                if (parent && parent.type === "Property" && parent.computed && parent.key === current) {
-                    return false;
-                }
+        /**
+         * Checks if a given identifier reference or member expression is an assignment
+         * target.
+         * @param {ASTNode} node The node to check.
+         * @returns {boolean} `true` if the node is an assignment target.
+         */
+        function isAssignmentTarget(node) {
+            const parent = node.parent;
 
-                if (current.type === "ObjectPattern") {
+            switch (parent.type) {
+                case "AssignmentExpression":
+                case "AssignmentPattern":
+                    return parent.left === node;
+
+                case "Property":
+                    return (
+                        parent.parent.type === "ObjectPattern" &&
+                        parent.value === node
+                    );
+                case "ArrayPattern":
+                case "RestElement":
                     return true;
-                }
 
-                current = parent;
+                default:
+                    return false;
             }
-
-            return false;
         }
 
         /**
-         * Checks whether the given node represents assignment target property in destructuring.
-         *
-         * For examples:
-         *    ({a: b.foo} = c);  // => true for `foo`
-         *    ([a.foo] = b);     // => true for `foo`
-         *    ([a.foo = 1] = b); // => true for `foo`
-         *    ({...a.foo} = b);  // => true for `foo`
-         * @param {ASTNode} node An Identifier node to check
-         * @returns {boolean} True if the node is an assignment target property in destructuring.
+         * Checks if a given binding identifier uses the original name as-is.
+         * - If it's in object destructuring, the original name is its property name.
+         * - If it's in import declaration, the original name is its exported name.
+         * @param {ASTNode} node The `Identifier` node to check.
+         * @returns {boolean} `true` if the identifier uses the original name as-is.
          */
-        function isAssignmentTargetPropertyInDestructuring(node) {
-            if (
-                node.parent.type === "MemberExpression" &&
-                node.parent.property === node &&
-                !node.parent.computed
-            ) {
-                const effectiveParent = node.parent.parent;
-
-                return (
-                    effectiveParent.type === "Property" &&
-                    effectiveParent.value === node.parent &&
-                    effectiveParent.parent.type === "ObjectPattern" ||
-                    effectiveParent.type === "ArrayPattern" ||
-                    effectiveParent.type === "RestElement" ||
-                    (
-                        effectiveParent.type === "AssignmentPattern" &&
-                        effectiveParent.left === node.parent
-                    )
-                );
+        function equalsToOriginalName(node) {
+            const localName = node.name;
+            const valueNode = node.parent.type === "AssignmentPattern"
+                ? node.parent
+                : node;
+            const parent = valueNode.parent;
+
+            switch (parent.type) {
+                case "Property":
+                    return (
+                        parent.parent.type === "ObjectPattern" &&
+                        parent.value === valueNode &&
+                        !parent.computed &&
+                        parent.key.type === "Identifier" &&
+                        parent.key.name === localName
+                    );
+
+                case "ImportSpecifier":
+                    return (
+                        parent.local === node &&
+                        parent.imported.name === localName
+                    );
+
+                default:
+                    return false;
             }
-            return false;
         }
 
         /**
-         * Checks whether the given node represents a reference to a global variable that is not declared in the source code.
-         * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
-         * @param {ASTNode} node `Identifier` node to check.
-         * @returns {boolean} `true` if the node is a reference to a global variable.
+         * Reports an AST node as a rule violation.
+         * @param {ASTNode} node The node to report.
+         * @returns {void}
+         * @private
          */
-        function isReferenceToGlobalVariable(node) {
-            const variable = globalScope.set.get(node.name);
-
-            return variable && variable.defs.length === 0 &&
-                variable.references.some(ref => ref.identifier === node);
+        function report(node) {
+            if (reported.has(node.range[0])) {
+                return;
+            }
+            reported.add(node.range[0]);
+
+            // Report it.
+            context.report({
+                node,
+                messageId: node.type === "PrivateIdentifier"
+                    ? "notCamelCasePrivate"
+                    : "notCamelCase",
+                data: { name: node.name }
+            });
         }
 
         /**
-         * Checks whether the given node represents a reference to a property of an object in an object literal expression.
-         * This allows to differentiate between a global variable that is allowed to be used as a reference, and the key
-         * of the expressed object (which shouldn't be allowed).
-         * @param {ASTNode} node `Identifier` node to check.
-         * @returns {boolean} `true` if the node is a property name of an object literal expression
+         * Reports an identifier reference or a binding identifier.
+         * @param {ASTNode} node The `Identifier` node to report.
+         * @returns {void}
          */
-        function isPropertyNameInObjectLiteral(node) {
-            const parent = node.parent;
+        function reportReferenceId(node) {
 
-            return (
-                parent.type === "Property" &&
-                parent.parent.type === "ObjectExpression" &&
-                !parent.computed &&
-                parent.key === node
-            );
-        }
+            /*
+             * For backward compatibility, if it's in callings then ignore it.
+             * Not sure why it is.
+             */
+            if (
+                node.parent.type === "CallExpression" ||
+                node.parent.type === "NewExpression"
+            ) {
+                return;
+            }
 
-        /**
-         * Reports an AST node as a rule violation.
-         * @param {ASTNode} node The node to report.
-         * @returns {void}
-         * @private
-         */
-        function report(node) {
-            if (!reported.includes(node)) {
-                reported.push(node);
-                context.report({ node, messageId: "notCamelCase", data: { name: node.name } });
+            /*
+             * For backward compatibility, if it's a default value of
+             * destructuring/parameters then ignore it.
+             * Not sure why it is.
+             */
+            if (
+                node.parent.type === "AssignmentPattern" &&
+                node.parent.right === node
+            ) {
+                return;
             }
+
+            /*
+             * The `ignoreDestructuring` flag skips the identifiers that uses
+             * the property name as-is.
+             */
+            if (ignoreDestructuring && equalsToOriginalName(node)) {
+                return;
+            }
+
+            report(node);
         }
 
         return {
 
+            // Report camelcase of global variable references ------------------
             Program() {
-                globalScope = context.getScope();
-            },
-
-            Identifier(node) {
+                const scope = context.getScope();
 
-                /*
-                 * Leading and trailing underscores are commonly used to flag
-                 * private/protected identifiers, strip them before checking if underscored
-                 */
-                const name = node.name,
-                    nameIsUnderscored = isUnderscored(name.replace(/^_+|_+$/gu, "")),
-                    effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
+                if (!ignoreGlobals) {
 
-                // First, we ignore the node if it match the ignore list
-                if (isAllowed(name)) {
-                    return;
-                }
+                    // Defined globals in config files or directive comments.
+                    for (const variable of scope.variables) {
+                        if (
+                            variable.identifiers.length > 0 ||
+                            isGoodName(variable.name)
+                        ) {
+                            continue;
+                        }
+                        for (const reference of variable.references) {
 
-                // Check if it's a global variable
-                if (ignoreGlobals && isReferenceToGlobalVariable(node) && !isPropertyNameInObjectLiteral(node)) {
-                    return;
+                            /*
+                             * For backward compatibility, this rule reports read-only
+                             * references as well.
+                             */
+                            reportReferenceId(reference.identifier);
+                        }
+                    }
                 }
 
-                // MemberExpressions get special rules
-                if (node.parent.type === "MemberExpression") {
+                // Undefined globals.
+                for (const reference of scope.through) {
+                    const id = reference.identifier;
 
-                    // "never" check properties
-                    if (properties === "never") {
-                        return;
+                    if (isGoodName(id.name)) {
+                        continue;
                     }
 
-                    // Always report underscored object names
-                    if (node.parent.object.type === "Identifier" && node.parent.object.name === node.name && nameIsUnderscored) {
-                        report(node);
-
-                    // Report AssignmentExpressions only if they are the left side of the assignment
-                    } else if (effectiveParent.type === "AssignmentExpression" && nameIsUnderscored && (effectiveParent.right.type !== "MemberExpression" || effectiveParent.left.type === "MemberExpression" && effectiveParent.left.property.name === node.name)) {
-                        report(node);
+                    /*
+                     * For backward compatibility, this rule reports read-only
+                     * references as well.
+                     */
+                    reportReferenceId(id);
+                }
+            },
 
-                    } else if (isAssignmentTargetPropertyInDestructuring(node) && nameIsUnderscored) {
-                        report(node);
+            // Report camelcase of declared variables --------------------------
+            [[
+                "VariableDeclaration",
+                "FunctionDeclaration",
+                "FunctionExpression",
+                "ArrowFunctionExpression",
+                "ClassDeclaration",
+                "ClassExpression",
+                "CatchClause"
+            ]](node) {
+                for (const variable of context.getDeclaredVariables(node)) {
+                    if (isGoodName(variable.name)) {
+                        continue;
                     }
+                    const id = variable.identifiers[0];
 
-                /*
-                 * Properties have their own rules, and
-                 * AssignmentPattern nodes can be treated like Properties:
-                 * e.g.: const { no_camelcased = false } = bar;
-                 */
-                } else if (node.parent.type === "Property" || node.parent.type === "AssignmentPattern") {
-
-                    if (node.parent.parent && node.parent.parent.type === "ObjectPattern") {
-                        if (node.parent.shorthand && node.parent.value.left && nameIsUnderscored) {
-                            report(node);
-                        }
-
-                        const assignmentKeyEqualsValue = node.parent.key.name === node.parent.value.name;
-
-                        if (nameIsUnderscored && node.parent.computed) {
-                            report(node);
-                        }
+                    // Report declaration.
+                    if (!(ignoreDestructuring && equalsToOriginalName(id))) {
+                        report(id);
+                    }
 
-                        // prevent checking righthand side of destructured object
-                        if (node.parent.key === node && node.parent.value !== node) {
-                            return;
+                    /*
+                     * For backward compatibility, report references as well.
+                     * It looks unnecessary because declarations are reported.
+                     */
+                    for (const reference of variable.references) {
+                        if (reference.init) {
+                            continue; // Skip the write references of initializers.
                         }
+                        reportReferenceId(reference.identifier);
+                    }
+                }
+            },
 
-                        const valueIsUnderscored = node.parent.value.name && nameIsUnderscored;
+            // Report camelcase in properties ----------------------------------
+            [[
+                "ObjectExpression > Property[computed!=true] > Identifier.key",
+                "MethodDefinition[computed!=true] > Identifier.key",
+                "PropertyDefinition[computed!=true] > Identifier.key",
+                "MethodDefinition > PrivateIdentifier.key",
+                "PropertyDefinition > PrivateIdentifier.key"
+            ]](node) {
+                if (properties === "never" || isGoodName(node.name)) {
+                    return;
+                }
+                report(node);
+            },
+            "MemberExpression[computed!=true] > Identifier.property"(node) {
+                if (
+                    properties === "never" ||
+                    !isAssignmentTarget(node.parent) || // ← ignore read-only references.
+                    isGoodName(node.name)
+                ) {
+                    return;
+                }
+                report(node);
+            },
 
-                        // ignore destructuring if the option is set, unless a new identifier is created
-                        if (valueIsUnderscored && !(assignmentKeyEqualsValue && ignoreDestructuring)) {
-                            report(node);
-                        }
+            // Report camelcase in import --------------------------------------
+            ImportDeclaration(node) {
+                for (const variable of context.getDeclaredVariables(node)) {
+                    if (isGoodName(variable.name)) {
+                        continue;
                     }
+                    const id = variable.identifiers[0];
 
-                    // "never" check properties or always ignore destructuring
-                    if (properties === "never" || (ignoreDestructuring && isInsideObjectPattern(node))) {
-                        return;
+                    // Report declaration.
+                    if (!(ignoreImports && equalsToOriginalName(id))) {
+                        report(id);
                     }
 
-                    // don't check right hand side of AssignmentExpression to prevent duplicate warnings
-                    if (nameIsUnderscored && !ALLOWED_PARENT_TYPES.has(effectiveParent.type) && !(node.parent.right === node)) {
-                        report(node);
+                    /*
+                     * For backward compatibility, report references as well.
+                     * It looks unnecessary because declarations are reported.
+                     */
+                    for (const reference of variable.references) {
+                        reportReferenceId(reference.identifier);
                     }
+                }
+            },
 
-                // Check if it's an import specifier
-                } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].includes(node.parent.type)) {
-
-                    if (node.parent.type === "ImportSpecifier" && ignoreImports) {
-                        return;
-                    }
+            // Report camelcase in re-export -----------------------------------
+            [[
+                "ExportAllDeclaration > Identifier.exported",
+                "ExportSpecifier > Identifier.exported"
+            ]](node) {
+                if (isGoodName(node.name)) {
+                    return;
+                }
+                report(node);
+            },
 
-                    // Report only if the local imported identifier is underscored
-                    if (
-                        node.parent.local &&
-                        node.parent.local.name === node.name &&
-                        nameIsUnderscored
-                    ) {
-                        report(node);
-                    }
+            // Report camelcase in labels --------------------------------------
+            [[
+                "LabeledStatement > Identifier.label",
 
-                // Report anything that is underscored that isn't a CallExpression
-                } else if (nameIsUnderscored && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)) {
-                    report(node);
+                /*
+                 * For backward compatibility, report references as well.
+                 * It looks unnecessary because declarations are reported.
+                 */
+                "BreakStatement > Identifier.label",
+                "ContinueStatement > Identifier.label"
+            ]](node) {
+                if (isGoodName(node.name)) {
+                    return;
                 }
+                report(node);
             }
-
         };
-
     }
 };
index d7524b878d22756aac1e3bd90137301f2627d488..e5f429356b2951ad6b07e8cd929e5f8cc04798aa 100644 (file)
@@ -105,7 +105,6 @@ module.exports = {
 
         docs: {
             description: "enforce or disallow capitalization of the first letter of a comment",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/capitalized-comments"
         },
index 2cc5cc41842c0c1a71f3d3aa03790b1d049231c0..beb742d93a793824b88ef23f3299c06adaff680b 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "enforce that class methods utilize `this`",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/class-methods-use-this"
         },
@@ -34,6 +33,10 @@ module.exports = {
                     items: {
                         type: "string"
                     }
+                },
+                enforceForClassFields: {
+                    type: "boolean",
+                    default: true
                 }
             },
             additionalProperties: false
@@ -45,10 +48,27 @@ module.exports = {
     },
     create(context) {
         const config = Object.assign({}, context.options[0]);
+        const enforceForClassFields = config.enforceForClassFields !== false;
         const exceptMethods = new Set(config.exceptMethods || []);
 
         const stack = [];
 
+        /**
+         * Push `this` used flag initialized with `false` onto the stack.
+         * @returns {void}
+         */
+        function pushContext() {
+            stack.push(false);
+        }
+
+        /**
+         * Pop `this` used flag from the stack.
+         * @returns {boolean | undefined} `this` used flag
+         */
+        function popContext() {
+            return stack.pop();
+        }
+
         /**
          * Initializes the current context to false and pushes it onto the stack.
          * These booleans represent whether 'this' has been used in the context.
@@ -56,7 +76,7 @@ module.exports = {
          * @private
          */
         function enterFunction() {
-            stack.push(false);
+            pushContext();
         }
 
         /**
@@ -66,7 +86,14 @@ module.exports = {
          * @private
          */
         function isInstanceMethod(node) {
-            return !node.static && node.kind !== "constructor" && node.type === "MethodDefinition";
+            switch (node.type) {
+                case "MethodDefinition":
+                    return !node.static && node.kind !== "constructor";
+                case "PropertyDefinition":
+                    return !node.static && enforceForClassFields;
+                default:
+                    return false;
+            }
         }
 
         /**
@@ -76,8 +103,19 @@ module.exports = {
          * @private
          */
         function isIncludedInstanceMethod(node) {
-            return isInstanceMethod(node) &&
-                (node.computed || !exceptMethods.has(node.key.name));
+            if (isInstanceMethod(node)) {
+                if (node.computed) {
+                    return true;
+                }
+
+                const hashIfNeeded = node.key.type === "PrivateIdentifier" ? "#" : "";
+                const name = node.key.type === "Literal"
+                    ? astUtils.getStaticStringValue(node.key)
+                    : (node.key.name || "");
+
+                return !exceptMethods.has(hashIfNeeded + name);
+            }
+            return false;
         }
 
         /**
@@ -89,11 +127,12 @@ module.exports = {
          * @private
          */
         function exitFunction(node) {
-            const methodUsesThis = stack.pop();
+            const methodUsesThis = popContext();
 
             if (isIncludedInstanceMethod(node.parent) && !methodUsesThis) {
                 context.report({
                     node,
+                    loc: astUtils.getFunctionHeadLoc(node, context.getSourceCode()),
                     messageId: "missingThis",
                     data: {
                         name: astUtils.getFunctionNameWithKind(node)
@@ -118,8 +157,30 @@ module.exports = {
             "FunctionDeclaration:exit": exitFunction,
             FunctionExpression: enterFunction,
             "FunctionExpression:exit": exitFunction,
+
+            /*
+             * Class field value are implicit functions.
+             */
+            "PropertyDefinition > *.key:exit": pushContext,
+            "PropertyDefinition:exit": popContext,
+
+            /*
+             * Class static blocks are implicit functions. They aren't required to use `this`,
+             * but we have to push context so that it captures any use of `this` in the static block
+             * separately from enclosing contexts, because static blocks have their own `this` and it
+             * shouldn't count as used `this` in enclosing contexts.
+             */
+            StaticBlock: pushContext,
+            "StaticBlock:exit": popContext,
+
             ThisExpression: markThisUsed,
-            Super: markThisUsed
+            Super: markThisUsed,
+            ...(
+                enforceForClassFields && {
+                    "PropertyDefinition > ArrowFunctionExpression.value": enterFunction,
+                    "PropertyDefinition > ArrowFunctionExpression.value:exit": exitFunction
+                }
+            )
         };
     }
 };
index 798c111ec3b6b61555d0546d22315ce4800d1fc2..e97a59886e3ac92edd1f891619f9000f44d508e4 100644 (file)
@@ -76,7 +76,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow trailing commas",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/comma-dangle"
         },
@@ -123,7 +122,8 @@ module.exports = {
                         }
                     ]
                 }
-            ]
+            ],
+            additionalItems: false
         },
 
         messages: {
index 2bf41a00bb66d8999fcc7add510a8d1e1a437bcd..d30a5ef320dea55736000f0bc8466713de414ad2 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing before and after commas",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/comma-spacing"
         },
index f1a23d63b786a0bd9877836c3bf9e1e1b0e0b82d..1d62fcf1c4d70317feeedf4ed9218e1b9e8184fa 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent comma style",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/comma-style"
         },
@@ -207,8 +206,7 @@ module.exports = {
                      * they are always valid regardless of an undefined item.
                      */
                     if (astUtils.isCommaToken(commaToken)) {
-                        validateCommaItemSpacing(previousItemToken, commaToken,
-                            currentItemToken, reportItem);
+                        validateCommaItemSpacing(previousItemToken, commaToken, currentItemToken, reportItem);
                     }
 
                     if (item) {
@@ -217,6 +215,8 @@ module.exports = {
                         previousItemToken = tokenAfterItem
                             ? sourceCode.getTokenBefore(tokenAfterItem)
                             : sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1];
+                    } else {
+                        previousItemToken = currentItemToken;
                     }
                 });
 
index 116c8ad0a63ee9b10039080b38e3abbaba0983e4..a247039760e9a056de2155e2ad8f45e15362045f 100644 (file)
@@ -23,7 +23,6 @@ module.exports = {
 
         docs: {
             description: "enforce a maximum cyclomatic complexity allowed in a program",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/complexity"
         },
@@ -75,60 +74,16 @@ module.exports = {
         // Helpers
         //--------------------------------------------------------------------------
 
-        // Using a stack to store complexity (handling nested functions)
-        const fns = [];
+        // Using a stack to store complexity per code path
+        const complexities = [];
 
         /**
-         * When parsing a new function, store it in our function stack
-         * @returns {void}
-         * @private
-         */
-        function startFunction() {
-            fns.push(1);
-        }
-
-        /**
-         * Evaluate the node at the end of function
-         * @param {ASTNode} node node to evaluate
-         * @returns {void}
-         * @private
-         */
-        function endFunction(node) {
-            const name = upperCaseFirst(astUtils.getFunctionNameWithKind(node));
-            const complexity = fns.pop();
-
-            if (complexity > THRESHOLD) {
-                context.report({
-                    node,
-                    messageId: "complex",
-                    data: { name, complexity, max: THRESHOLD }
-                });
-            }
-        }
-
-        /**
-         * Increase the complexity of the function in context
+         * Increase the complexity of the code path in context
          * @returns {void}
          * @private
          */
         function increaseComplexity() {
-            if (fns.length) {
-                fns[fns.length - 1]++;
-            }
-        }
-
-        /**
-         * Increase the switch complexity in context
-         * @param {ASTNode} node node to evaluate
-         * @returns {void}
-         * @private
-         */
-        function increaseSwitchComplexity(node) {
-
-            // Avoiding `default`
-            if (node.test) {
-                increaseComplexity();
-            }
+            complexities[complexities.length - 1]++;
         }
 
         //--------------------------------------------------------------------------
@@ -136,13 +91,14 @@ module.exports = {
         //--------------------------------------------------------------------------
 
         return {
-            FunctionDeclaration: startFunction,
-            FunctionExpression: startFunction,
-            ArrowFunctionExpression: startFunction,
-            "FunctionDeclaration:exit": endFunction,
-            "FunctionExpression:exit": endFunction,
-            "ArrowFunctionExpression:exit": endFunction,
 
+            onCodePathStart() {
+
+                // The initial complexity is 1, representing one execution path in the CodePath
+                complexities.push(1);
+            },
+
+            // Each branching in the code adds 1 to the complexity
             CatchClause: increaseComplexity,
             ConditionalExpression: increaseComplexity,
             LogicalExpression: increaseComplexity,
@@ -150,14 +106,57 @@ module.exports = {
             ForInStatement: increaseComplexity,
             ForOfStatement: increaseComplexity,
             IfStatement: increaseComplexity,
-            SwitchCase: increaseSwitchComplexity,
             WhileStatement: increaseComplexity,
             DoWhileStatement: increaseComplexity,
 
+            // Avoid `default`
+            "SwitchCase[test]": increaseComplexity,
+
+            // Logical assignment operators have short-circuiting behavior
             AssignmentExpression(node) {
                 if (astUtils.isLogicalAssignmentOperator(node.operator)) {
                     increaseComplexity();
                 }
+            },
+
+            onCodePathEnd(codePath, node) {
+                const complexity = complexities.pop();
+
+                /*
+                 * This rule only evaluates complexity of functions, so "program" is excluded.
+                 * Class field initializers and class static blocks are implicit functions. Therefore,
+                 * they shouldn't contribute to the enclosing function's complexity, but their
+                 * own complexity should be evaluated.
+                 */
+                if (
+                    codePath.origin !== "function" &&
+                    codePath.origin !== "class-field-initializer" &&
+                    codePath.origin !== "class-static-block"
+                ) {
+                    return;
+                }
+
+                if (complexity > THRESHOLD) {
+                    let name;
+
+                    if (codePath.origin === "class-field-initializer") {
+                        name = "class field initializer";
+                    } else if (codePath.origin === "class-static-block") {
+                        name = "class static block";
+                    } else {
+                        name = astUtils.getFunctionNameWithKind(node);
+                    }
+
+                    context.report({
+                        node,
+                        messageId: "complex",
+                        data: {
+                            name: upperCaseFirst(name),
+                            complexity,
+                            max: THRESHOLD
+                        }
+                    });
+                }
             }
         };
 
index 53fdb8f4e41f956a102cf479c71bab40b9013025..c8d8834ea9233d5ea6df1ebaa6056a4f21e92f61 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing inside computed property brackets",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/computed-property-spacing"
         },
@@ -195,7 +194,8 @@ module.exports = {
         };
 
         if (enforceForClassMembers) {
-            listeners.MethodDefinition = checkSpacing("key");
+            listeners.MethodDefinition =
+                listeners.PropertyDefinition = listeners.Property;
         }
 
         return listeners;
index a250430cb766b5677022626cfe6a4239e82aadc7..b509c36564fb03948251579a34b58b5ca735a25a 100644 (file)
@@ -46,7 +46,6 @@ module.exports = {
 
         docs: {
             description: "require `return` statements to either always or never specify values",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/consistent-return"
         },
@@ -104,18 +103,18 @@ module.exports = {
             } else if (node.type === "ArrowFunctionExpression") {
 
                 // `=>` token
-                loc = context.getSourceCode().getTokenBefore(node.body, astUtils.isArrowToken).loc.start;
+                loc = context.getSourceCode().getTokenBefore(node.body, astUtils.isArrowToken).loc;
             } else if (
                 node.parent.type === "MethodDefinition" ||
                 (node.parent.type === "Property" && node.parent.method)
             ) {
 
                 // Method name.
-                loc = node.parent.key.loc.start;
+                loc = node.parent.key.loc;
             } else {
 
                 // Function name or `function` keyword.
-                loc = (node.id || node).loc.start;
+                loc = (node.id || context.getSourceCode().getFirstToken(node)).loc;
             }
 
             if (!name) {
index e5bc9678daef3c4f48fd77768312432e02044e3e..025f3d0a3402911803c7c6adfaf01a95d0232192 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent naming when capturing the current execution context",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/consistent-this"
         },
@@ -47,7 +46,7 @@ module.exports = {
          * Reports that a variable declarator or assignment expression is assigning
          * a non-'this' value to the specified alias.
          * @param {ASTNode} node The assigning node.
-         * @param {string}  name the name of the alias that was incorrectly used.
+         * @param {string} name the name of the alias that was incorrectly used.
          * @returns {void}
          */
         function reportBadAssignment(node, name) {
index dfec18fb65a4b13090605b50f9b0643c900bd01a..38eb489327d597634ffdc3b05c5d4156afb28e97 100644 (file)
@@ -122,7 +122,6 @@ module.exports = {
 
         docs: {
             description: "require `super()` calls in constructors",
-            category: "ECMAScript 6",
             recommended: true,
             url: "https://eslint.org/docs/rules/constructor-super"
         },
index 92d31a6476e4359ed4fda9b0e3622d33eb1b0b77..57c2e72e4e4d9e886618c32bdf8b6f3cd7a5fd13 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent brace style for all control statements",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/curly"
         },
@@ -131,15 +130,6 @@ module.exports = {
             return token.value === "else" && token.type === "Keyword";
         }
 
-        /**
-         * Gets the `else` keyword token of a given `IfStatement` node.
-         * @param {ASTNode} node A `IfStatement` node to get.
-         * @returns {Token} The `else` keyword token.
-         */
-        function getElseKeyword(node) {
-            return node.alternate && sourceCode.getFirstTokenBetween(node.consequent, node.alternate, isElseKeywordToken);
-        }
-
         /**
          * Determines whether the given node has an `else` keyword token as the first token after.
          * @param {ASTNode} node The node to check.
@@ -361,7 +351,7 @@ module.exports = {
                         if (this.expected) {
                             context.report({
                                 node,
-                                loc: (name !== "else" ? node : getElseKeyword(node)).loc.start,
+                                loc: body.loc,
                                 messageId: opts && opts.condition ? "missingCurlyAfterCondition" : "missingCurlyAfter",
                                 data: {
                                     name
@@ -371,7 +361,7 @@ module.exports = {
                         } else {
                             context.report({
                                 node,
-                                loc: (name !== "else" ? node : getElseKeyword(node)).loc.start,
+                                loc: body.loc,
                                 messageId: opts && opts.condition ? "unexpectedCurlyAfterCondition" : "unexpectedCurlyAfter",
                                 data: {
                                     name
index 80c5d6bda73958fa7a032fc5f9530c0f89bf8100..1eeadd1e71637e9b72d479ba0a4fa125c9341c1a 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "enforce default clauses in switch statements to be last",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/default-case-last"
         },
index 821e0d72bd1cba5657799868e2881d6168e15a71..b839aa2013313938b1e8f0f0f93e1b11a20b9344 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "require `default` cases in `switch` statements",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/default-case"
         },
@@ -50,8 +49,8 @@ module.exports = {
 
         /**
          * Shortcut to get last element of array
-         * @param  {*[]} collection Array
-         * @returns {*} Last element
+         * @param {*[]} collection Array
+         * @returns {any} Last element
          */
         function last(collection) {
             return collection[collection.length - 1];
index 12e0b5950dace6c89ca781a20f3a8e6a488dba61..8382d46e760fe2fecd496369e588378f2a70706f 100644 (file)
@@ -11,7 +11,6 @@ module.exports = {
 
         docs: {
             description: "enforce default parameters to be last",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/default-param-last"
         },
@@ -25,8 +24,8 @@ module.exports = {
 
     create(context) {
 
-        // eslint-disable-next-line jsdoc/require-description
         /**
+         * Handler for function contexts.
          * @param {ASTNode} node function node
          * @returns {void}
          */
index a8d5a760562d3e1e468bebae6d892e3938ebc2d2..d80f87090a73f3ee497d4bea1b06853c0fe7d6dc 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent newlines before and after dots",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/dot-location"
         },
index 751b4628edc4d5bef1eca0f333cb9c28e96c1c58..1cd908f7a2c25ef1ebedb29bfa07a6b9b53c6957 100644 (file)
@@ -26,7 +26,6 @@ module.exports = {
 
         docs: {
             description: "enforce dot notation whenever possible",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/dot-notation"
         },
@@ -94,7 +93,7 @@ module.exports = {
 
                         // Don't perform any fixes if there are comments inside the brackets.
                         if (sourceCode.commentsExistBetween(leftBracket, rightBracket)) {
-                            return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
+                            return;
                         }
 
                         // Replace the brackets by an identifier.
@@ -141,6 +140,7 @@ module.exports = {
                 if (
                     !allowKeywords &&
                     !node.computed &&
+                    node.property.type === "Identifier" &&
                     keywords.indexOf(String(node.property.name)) !== -1
                 ) {
                     context.report({
@@ -154,12 +154,12 @@ module.exports = {
 
                             // A statement that starts with `let[` is parsed as a destructuring variable declaration, not a MemberExpression.
                             if (node.object.type === "Identifier" && node.object.name === "let" && !node.optional) {
-                                return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
+                                return;
                             }
 
                             // Don't perform any fixes if there are comments between the dot and the property name.
                             if (sourceCode.commentsExistBetween(dotToken, node.property)) {
-                                return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
+                                return;
                             }
 
                             // Replace the identifier to brackets.
index 24b0c9279c71ca74d56c58e0b70954a65fa72a58..f8b922c25037614a596ee88ca80ed421e7cc2f4a 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow newline at the end of files",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/eol-last"
         },
@@ -86,10 +85,15 @@ module.exports = {
                     });
                 } else if (mode === "never" && endsWithNewline) {
 
+                    const secondLastLine = sourceCode.lines[sourceCode.lines.length - 2];
+
                     // File is newline-terminated, but shouldn't be
                     context.report({
                         node,
-                        loc: location,
+                        loc: {
+                            start: { line: sourceCode.lines.length - 1, column: secondLastLine.length },
+                            end: { line: sourceCode.lines.length, column: 0 }
+                        },
                         messageId: "unexpected",
                         fix(fixer) {
                             const finalEOLs = /(?:\r?\n)+$/u,
index 57926dbed0e0529c1a95bdb1adafbf5089288609..d3e6b5af0b6cd69e632dbae360decda716c3240c 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "require the use of `===` and `!==`",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/eqeqeq"
         },
@@ -78,7 +77,7 @@ module.exports = {
 
         /**
          * Checks if an expression is a typeof expression
-         * @param  {ASTNode} node The node to check
+         * @param {ASTNode} node The node to check
          * @returns {boolean} if the node is a typeof expression
          */
         function isTypeOf(node) {
index c15d10e5f842a43853aa052e6acbc9ee2c950f4f..abe4ad3c6b8c2bc3891b7523d6fb49924739c332 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "enforce \"for\" loop update clause moving the counter in the right direction.",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/for-direction"
         },
index 132a5833143844259f8301576c2698af5e385df2..a6ebde4ac0bdd4027b5319b114486c0fdba0ef32 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow spacing between function identifiers and their invocations",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/func-call-spacing"
         },
index 755c2ee5075c6b845b0cf550e09c33b35753b4aa..122cfd8d33c62d47cc339a7a6fc2f80efcf94a1f 100644 (file)
@@ -74,7 +74,6 @@ module.exports = {
 
         docs: {
             description: "require function names to match the name of the variable or property to which they are assigned",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/func-name-matching"
         },
@@ -196,21 +195,25 @@ module.exports = {
                 const isProp = node.left.type === "MemberExpression";
                 const name = isProp ? astUtils.getStaticPropertyName(node.left) : node.left.name;
 
-                if (node.right.id && isIdentifier(name) && shouldWarn(name, node.right.id.name)) {
+                if (node.right.id && name && isIdentifier(name) && shouldWarn(name, node.right.id.name)) {
                     report(node, name, node.right.id.name, isProp);
                 }
             },
 
-            Property(node) {
-                if (node.value.type !== "FunctionExpression" || !node.value.id || node.computed && !isStringLiteral(node.key)) {
+            "Property, PropertyDefinition[value]"(node) {
+                if (!(node.value.type === "FunctionExpression" && node.value.id)) {
                     return;
                 }
 
-                if (node.key.type === "Identifier") {
+                if (node.key.type === "Identifier" && !node.computed) {
                     const functionName = node.value.id.name;
                     let propertyName = node.key.name;
 
-                    if (considerPropertyDescriptor && propertyName === "value") {
+                    if (
+                        considerPropertyDescriptor &&
+                        propertyName === "value" &&
+                        node.parent.type === "ObjectExpression"
+                    ) {
                         if (isPropertyCall("Object", "defineProperty", node.parent.parent) || isPropertyCall("Reflect", "defineProperty", node.parent.parent)) {
                             const property = node.parent.parent.arguments[1];
 
index ecfedb9e0e99c71d431d94759bf09288d003697c..589903c96a50d6b415af4983358c80ef22c0d428 100644 (file)
@@ -30,7 +30,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow named `function` expressions",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/func-names"
         },
@@ -118,6 +117,7 @@ module.exports = {
             return isObjectOrClassMethod(node) ||
                 (parent.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.init === node) ||
                 (parent.type === "Property" && parent.value === node) ||
+                (parent.type === "PropertyDefinition" && parent.value === node) ||
                 (parent.type === "AssignmentExpression" && parent.left.type === "Identifier" && parent.right === node) ||
                 (parent.type === "AssignmentPattern" && parent.left.type === "Identifier" && parent.right === node);
         }
index e150b1a76f26a64079c629591445197283a0cb50..0921ff54cd4d462ee1fdd108c07e277a02adabed 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "enforce the consistent use of either `function` declarations or expressions",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/func-style"
         },
index b6abbe95fa98c3d75f4fe2a36ab98b50bc5eb158..ed4e296fd6e90f03fdca8968fe0cc02b511a09d4 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "enforce line breaks between arguments of a function call",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/function-call-argument-newline"
         },
index 9d8d67ba14148d4f62dbe38abc1e7bd54bfebe2f..18435b7897823c59c6ba2ae50a330e9ffab0a9ad 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent line breaks inside function parentheses",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/function-paren-newline"
         },
@@ -183,6 +182,7 @@ module.exports = {
         /**
          * Gets the left paren and right paren tokens of a node.
          * @param {ASTNode} node The node with parens
+         * @throws {TypeError} Unexecpted node type.
          * @returns {Object} An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token.
          * Can also return `null` if an expression has no parens (e.g. a NewExpression with no arguments, or an ArrowFunctionExpression
          * with a single parameter)
index 65534f727fad0ef34260772197d49d4d3332cf88..c50445c9c9bf9e1d77cbe7b773dc130dd6c053ee 100644 (file)
@@ -31,7 +31,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing around `*` operators in generator functions",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/generator-star-spacing"
         },
index c54ebfb4ffb8cd509ebf8e5f489a0cb6e940b4c5..8bb42536e8c06752ae8202fdaf44c893546a0374 100644 (file)
@@ -35,7 +35,6 @@ module.exports = {
 
         docs: {
             description: "enforce `return` statements in getters",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/getter-return"
         },
index 09d0332007e1745bd7b1f22fe2f9512ced2713b3..f2d29d1df5aa5d6378bfabb377cde007ba611b56 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Rule for disallowing require() outside of the top-level module context
  * @author Jamund Ferguson
+ * @deprecated in ESLint v7.0.0
  */
 
 "use strict";
@@ -57,7 +58,6 @@ module.exports = {
 
         docs: {
             description: "require `require()` calls to be placed at top-level module scope",
-            category: "Node.js and CommonJS",
             recommended: false,
             url: "https://eslint.org/docs/rules/global-require"
         },
index a790f83750b2eb87a4e937e75cb9ebd10833ec25..cc4a4b522a14e072b6d5ab4fcef149ba14c5bc35 100644 (file)
@@ -96,7 +96,6 @@ module.exports = {
 
         docs: {
             description: "require grouped accessor pairs in object literals and classes",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/grouped-accessor-pairs"
         },
index 2c0976d997b60117dc01fc41c60ecddd080be4e9..6f877bab9588b020ffefe9604faaa7d615177869 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "require `for-in` loops to include an `if` statement",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/guard-for-in"
         },
index b92490ad16bdafda8592f0ce7282d640773dad7e..cdb3a7642ea3c90f3537b883c08ce2578c2695e3 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Ensure handling of errors when we know they exist.
  * @author Jamund Ferguson
+ * @deprecated in ESLint v7.0.0
  */
 
 "use strict";
@@ -19,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "require error handling in callbacks",
-            category: "Node.js and CommonJS",
             recommended: false,
             url: "https://eslint.org/docs/rules/handle-callback-err"
         },
index 4fbba909fde453c5d792aad25985b4b05ef48e95..77deac707085ca6875e6d95c396a53f00e344d24 100644 (file)
@@ -2,6 +2,7 @@
  * @fileoverview Rule that warns when identifier names that are
  * specified in the configuration are used.
  * @author Keith Cirkel (http://keithcirkel.co.uk)
+ * @deprecated in ESLint v7.5.0
  */
 
 "use strict";
@@ -118,7 +119,6 @@ module.exports = {
 
         docs: {
             description: "disallow specified identifiers",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/id-blacklist"
         },
@@ -205,7 +205,17 @@ module.exports = {
          * @private
          */
         function report(node) {
-            if (!reportedNodes.has(node)) {
+
+            /*
+             * We used the range instead of the node because it's possible
+             * for the same identifier to be represented by two different
+             * nodes, with the most clear example being shorthand properties:
+             * { foo }
+             * In this case, "foo" is represented by one node for the name
+             * and one for the value. The only way to know they are the same
+             * is to look at the range.
+             */
+            if (!reportedNodes.has(node.range.toString())) {
                 context.report({
                     node,
                     messageId: "restricted",
@@ -213,8 +223,9 @@ module.exports = {
                         name: node.name
                     }
                 });
-                reportedNodes.add(node);
+                reportedNodes.add(node.range.toString());
             }
+
         }
 
         return {
index 112fd8a9d5515eed6f1a2a3ce76139a0af09c5c0..2b3463554234540a4eb53fc55eacdab2bbef5e35 100644 (file)
@@ -69,14 +69,14 @@ function isRenamedImport(node) {
 }
 
 /**
- * Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring.
+ * Checks whether the given node is an ObjectPattern destructuring.
  *
  * Examples:
- * const { a : b } = foo; // node `a` is renamed node.
+ * const { a : b } = foo;
  * @param {ASTNode} node `Identifier` node to check.
- * @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring.
+ * @returns {boolean} `true` if the node is in an ObjectPattern destructuring.
  */
-function isRenamedInDestructuring(node) {
+function isPropertyNameInDestructuring(node) {
     const parent = node.parent;
 
     return (
@@ -84,27 +84,11 @@ function isRenamedInDestructuring(node) {
             !parent.computed &&
             parent.type === "Property" &&
             parent.parent.type === "ObjectPattern" &&
-            parent.value !== node &&
             parent.key === node
         )
     );
 }
 
-/**
- * Checks whether the given node represents shorthand definition of a property in an object literal.
- * @param {ASTNode} node `Identifier` node to check.
- * @returns {boolean} `true` if the node is a shorthand property definition.
- */
-function isShorthandPropertyDefinition(node) {
-    const parent = node.parent;
-
-    return (
-        parent.type === "Property" &&
-        parent.parent.type === "ObjectExpression" &&
-        parent.shorthand
-    );
-}
-
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
@@ -115,7 +99,6 @@ module.exports = {
 
         docs: {
             description: "disallow specified identifiers",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/id-denylist"
         },
@@ -128,7 +111,8 @@ module.exports = {
             uniqueItems: true
         },
         messages: {
-            restricted: "Identifier '{{name}}' is restricted."
+            restricted: "Identifier '{{name}}' is restricted.",
+            restrictedPrivate: "Identifier '#{{name}}' is restricted."
         }
     },
 
@@ -187,11 +171,8 @@ module.exports = {
                 parent.type !== "CallExpression" &&
                 parent.type !== "NewExpression" &&
                 !isRenamedImport(node) &&
-                !isRenamedInDestructuring(node) &&
-                !(
-                    isReferenceToGlobalVariable(node) &&
-                    !isShorthandPropertyDefinition(node)
-                )
+                !isPropertyNameInDestructuring(node) &&
+                !isReferenceToGlobalVariable(node)
             );
         }
 
@@ -202,15 +183,27 @@ module.exports = {
          * @private
          */
         function report(node) {
-            if (!reportedNodes.has(node)) {
+
+            /*
+             * We used the range instead of the node because it's possible
+             * for the same identifier to be represented by two different
+             * nodes, with the most clear example being shorthand properties:
+             * { foo }
+             * In this case, "foo" is represented by one node for the name
+             * and one for the value. The only way to know they are the same
+             * is to look at the range.
+             */
+            if (!reportedNodes.has(node.range.toString())) {
+                const isPrivate = node.type === "PrivateIdentifier";
+
                 context.report({
                     node,
-                    messageId: "restricted",
+                    messageId: isPrivate ? "restrictedPrivate" : "restricted",
                     data: {
                         name: node.name
                     }
                 });
-                reportedNodes.add(node);
+                reportedNodes.add(node.range.toString());
             }
         }
 
@@ -220,7 +213,10 @@ module.exports = {
                 globalScope = context.getScope();
             },
 
-            Identifier(node) {
+            [[
+                "Identifier",
+                "PrivateIdentifier"
+            ]](node) {
                 if (isRestricted(node.name) && shouldCheck(node)) {
                     report(node);
                 }
index 4df081ff9fe4f85011848c324784ce8090246d67..ac6385f59c045222b148fc63ba15b78afff1847d 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "enforce minimum and maximum identifier lengths",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/id-length"
         },
@@ -55,7 +54,9 @@ module.exports = {
         ],
         messages: {
             tooShort: "Identifier name '{{name}}' is too short (< {{min}}).",
-            tooLong: "Identifier name '{{name}}' is too long (> {{max}})."
+            tooShortPrivate: "Identifier name '#{{name}}' is too short (< {{min}}).",
+            tooLong: "Identifier name '{{name}}' is too long (> {{max}}).",
+            tooLongPrivate: "Identifier name #'{{name}}' is too long (> {{max}})."
         }
     },
 
@@ -66,7 +67,7 @@ module.exports = {
         const properties = options.properties !== "never";
         const exceptions = new Set(options.exceptions);
         const exceptionPatterns = (options.exceptionPatterns || []).map(pattern => new RegExp(pattern, "u"));
-        const reportedNode = new Set();
+        const reportedNodes = new Set();
 
         /**
          * Checks if a string matches the provided exception patterns
@@ -99,12 +100,14 @@ module.exports = {
             Property(parent, node) {
 
                 if (parent.parent.type === "ObjectPattern") {
+                    const isKeyAndValueSame = parent.value.name === parent.key.name;
+
                     return (
-                        parent.value !== parent.key && parent.value === node ||
-                        parent.value === parent.key && parent.key === node && properties
+                        !isKeyAndValueSame && parent.value === node ||
+                        isKeyAndValueSame && parent.key === node && properties
                     );
                 }
-                return properties && !parent.computed && parent.key === node;
+                return properties && !parent.computed && parent.key.name === node.name;
             },
             ImportDefaultSpecifier: true,
             RestElement: true,
@@ -113,12 +116,16 @@ module.exports = {
             ClassDeclaration: true,
             FunctionDeclaration: true,
             MethodDefinition: true,
+            PropertyDefinition: true,
             CatchClause: true,
             ArrayPattern: true
         };
 
         return {
-            Identifier(node) {
+            [[
+                "Identifier",
+                "PrivateIdentifier"
+            ]](node) {
                 const name = node.name;
                 const parent = node.parent;
 
@@ -131,11 +138,27 @@ module.exports = {
 
                 const isValidExpression = SUPPORTED_EXPRESSIONS[parent.type];
 
-                if (isValidExpression && !reportedNode.has(node) && (isValidExpression === true || isValidExpression(parent, node))) {
-                    reportedNode.add(node);
+                /*
+                 * We used the range instead of the node because it's possible
+                 * for the same identifier to be represented by two different
+                 * nodes, with the most clear example being shorthand properties:
+                 * { foo }
+                 * In this case, "foo" is represented by one node for the name
+                 * and one for the value. The only way to know they are the same
+                 * is to look at the range.
+                 */
+                if (isValidExpression && !reportedNodes.has(node.range.toString()) && (isValidExpression === true || isValidExpression(parent, node))) {
+                    reportedNodes.add(node.range.toString());
+
+                    let messageId = isShort ? "tooShort" : "tooLong";
+
+                    if (node.type === "PrivateIdentifier") {
+                        messageId += "Private";
+                    }
+
                     context.report({
                         node,
-                        messageId: isShort ? "tooShort" : "tooLong",
+                        messageId,
                         data: { name, min: minLength, max: maxLength }
                     });
                 }
index 7e400d037a07ab39ee0cb970f0706db3de1298a4..7a6cd058f2ec41948e5b2fb833e156bbefa89f47 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "require identifiers to match a specified regular expression",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/id-match"
         },
@@ -31,6 +30,10 @@ module.exports = {
                         type: "boolean",
                         default: false
                     },
+                    classFields: {
+                        type: "boolean",
+                        default: false
+                    },
                     onlyDeclarations: {
                         type: "boolean",
                         default: false
@@ -44,7 +47,8 @@ module.exports = {
             }
         ],
         messages: {
-            notMatch: "Identifier '{{name}}' does not match the pattern '{{pattern}}'."
+            notMatch: "Identifier '{{name}}' does not match the pattern '{{pattern}}'.",
+            notMatchPrivate: "Identifier '#{{name}}' does not match the pattern '{{pattern}}'."
         }
     },
 
@@ -57,7 +61,8 @@ module.exports = {
             regexp = new RegExp(pattern, "u");
 
         const options = context.options[1] || {},
-            properties = !!options.properties,
+            checkProperties = !!options.properties,
+            checkClassFields = !!options.classFields,
             onlyDeclarations = !!options.onlyDeclarations,
             ignoreDestructuring = !!options.ignoreDestructuring;
 
@@ -66,7 +71,7 @@ module.exports = {
         //--------------------------------------------------------------------------
 
         // contains reported nodes to avoid reporting twice on destructuring with shorthand notation
-        const reported = new Map();
+        const reportedNodes = new Set();
         const ALLOWED_PARENT_TYPES = new Set(["CallExpression", "NewExpression"]);
         const DECLARATION_TYPES = new Set(["FunctionDeclaration", "VariableDeclarator"]);
         const IMPORT_TYPES = new Set(["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"]);
@@ -120,16 +125,30 @@ module.exports = {
          * @private
          */
         function report(node) {
-            if (!reported.has(node)) {
+
+            /*
+             * We used the range instead of the node because it's possible
+             * for the same identifier to be represented by two different
+             * nodes, with the most clear example being shorthand properties:
+             * { foo }
+             * In this case, "foo" is represented by one node for the name
+             * and one for the value. The only way to know they are the same
+             * is to look at the range.
+             */
+            if (!reportedNodes.has(node.range.toString())) {
+
+                const messageId = (node.type === "PrivateIdentifier")
+                    ? "notMatchPrivate" : "notMatch";
+
                 context.report({
                     node,
-                    messageId: "notMatch",
+                    messageId,
                     data: {
                         name: node.name,
                         pattern
                     }
                 });
-                reported.set(node, true);
+                reportedNodes.add(node.range.toString());
             }
         }
 
@@ -142,7 +161,7 @@ module.exports = {
 
                 if (parent.type === "MemberExpression") {
 
-                    if (!properties) {
+                    if (!checkProperties) {
                         return;
                     }
 
@@ -176,8 +195,7 @@ module.exports = {
                 } else if (parent.type === "Property" || parent.type === "AssignmentPattern") {
 
                     if (parent.parent && parent.parent.type === "ObjectPattern") {
-                        if (parent.shorthand && parent.value.left && isInvalid(name)) {
-
+                        if (!ignoreDestructuring && parent.shorthand && parent.value.left && isInvalid(name)) {
                             report(node);
                         }
 
@@ -197,7 +215,7 @@ module.exports = {
                     }
 
                     // never check properties or always ignore destructuring
-                    if (!properties || (ignoreDestructuring && isInsideObjectPattern(node))) {
+                    if (!checkProperties || (ignoreDestructuring && isInsideObjectPattern(node))) {
                         return;
                     }
 
@@ -214,10 +232,29 @@ module.exports = {
                         report(node);
                     }
 
+                } else if (parent.type === "PropertyDefinition") {
+
+                    if (checkClassFields && isInvalid(name)) {
+                        report(node);
+                    }
+
                 // Report anything that is invalid that isn't a CallExpression
                 } else if (shouldReport(effectiveParent, name)) {
                     report(node);
                 }
+            },
+
+            "PrivateIdentifier"(node) {
+
+                const isClassField = node.parent.type === "PropertyDefinition";
+
+                if (isClassField && !checkClassFields) {
+                    return;
+                }
+
+                if (isInvalid(node.name)) {
+                    report(node);
+                }
             }
 
         };
index 409145e7dc1329c4f4e6bd1ee86c335fce54c412..2d09552440b2007bffc6de43d59caab4cc38d37a 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "enforce the location of arrow function bodies",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/implicit-arrow-linebreak"
         },
index a26ee87b1305ed8e79362f86b26a9ccd760b1ca5..54ca9ddd138e98a57cea8a516ca3d34fac4d8947 100644 (file)
@@ -4,6 +4,7 @@
  * This rule has been ported and modified from nodeca.
  * @author Vitaly Puzrin
  * @author Gyandeep Singh
+ * @deprecated in ESLint v4.0.0
  */
 
 "use strict";
@@ -25,7 +26,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent indentation",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/indent-legacy"
         },
index b1af2a73b33be262230525b0a1f5e18ce9b48d5c..f4dbfff27b41cb40c89381149b906253868d4614 100644 (file)
@@ -60,12 +60,15 @@ const KNOWN_NODES = new Set([
     "NewExpression",
     "ObjectExpression",
     "ObjectPattern",
+    "PrivateIdentifier",
     "Program",
     "Property",
+    "PropertyDefinition",
     "RestElement",
     "ReturnStatement",
     "SequenceExpression",
     "SpreadElement",
+    "StaticBlock",
     "Super",
     "SwitchCase",
     "SwitchStatement",
@@ -138,7 +141,7 @@ class BinarySearchTree {
     /**
      * Inserts an entry into the tree.
      * @param {number} key The entry's key
-     * @param {*} value The entry's value
+     * @param {any} value The entry's value
      * @returns {void}
      */
     insert(key, value) {
@@ -188,7 +191,6 @@ class BinarySearchTree {
  */
 class TokenInfo {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {SourceCode} sourceCode A SourceCode object
      */
@@ -238,7 +240,6 @@ class TokenInfo {
  */
 class OffsetStorage {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {TokenInfo} tokenInfo a TokenInfo instance
      * @param {number} indentSize The desired size of each indentation level
@@ -263,7 +264,7 @@ class OffsetStorage {
 
     /**
      * Sets the offset column of token B to match the offset column of token A.
-     * **WARNING**: This matches a *column*, even if baseToken is not the first token on its line. In
+     * **WARNING**: This matches a *column*, even if baseToken is not the first token on its line. In
      * most cases, `setDesiredOffset` should be used instead.
      * @param {Token} baseToken The first token
      * @param {Token} offsetToken The second token, whose offset should be matched to the first token
@@ -352,11 +353,11 @@ class OffsetStorage {
      * Instead, the offset tree is represented as a collection of contiguous offset ranges in a file. For example, the following
      * list could represent the state of the offset tree at a given point:
      *
-     * * Tokens starting in the interval [0, 15) are aligned with the beginning of the file
-     * * Tokens starting in the interval [15, 30) are offset by 1 indent level from the `bar` token
-     * * Tokens starting in the interval [30, 43) are offset by 1 indent level from the `foo` token
-     * * Tokens starting in the interval [43, 820) are offset by 2 indent levels from the `bar` token
-     * * Tokens starting in the interval [820, ∞) are offset by 1 indent level from the `baz` token
+     * - Tokens starting in the interval [0, 15) are aligned with the beginning of the file
+     * - Tokens starting in the interval [15, 30) are offset by 1 indent level from the `bar` token
+     * - Tokens starting in the interval [30, 43) are offset by 1 indent level from the `foo` token
+     * - Tokens starting in the interval [43, 820) are offset by 2 indent levels from the `bar` token
+     * - Tokens starting in the interval [820, ∞) are offset by 1 indent level from the `baz` token
      *
      * The `setDesiredOffsets` methods inserts ranges like the ones above. The third line above would be inserted by using:
      * `setDesiredOffsets([30, 43], fooToken, 1);`
@@ -499,7 +500,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent indentation",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/indent"
         },
@@ -584,6 +584,16 @@ module.exports = {
                         },
                         additionalProperties: false
                     },
+                    StaticBlock: {
+                        type: "object",
+                        properties: {
+                            body: {
+                                type: "integer",
+                                minimum: 0
+                            }
+                        },
+                        additionalProperties: false
+                    },
                     CallExpression: {
                         type: "object",
                         properties: {
@@ -647,6 +657,9 @@ module.exports = {
                 parameters: DEFAULT_PARAMETER_INDENT,
                 body: DEFAULT_FUNCTION_BODY_INDENT
             },
+            StaticBlock: {
+                body: DEFAULT_FUNCTION_BODY_INDENT
+            },
             CallExpression: {
                 arguments: DEFAULT_PARAMETER_INDENT
             },
@@ -1177,8 +1190,7 @@ module.exports = {
                     offsets.setDesiredOffset(questionMarkToken, firstToken, 1);
                     offsets.setDesiredOffset(colonToken, firstToken, 1);
 
-                    offsets.setDesiredOffset(firstConsequentToken, firstToken,
-                        firstConsequentToken.type === "Punctuator" &&
+                    offsets.setDesiredOffset(firstConsequentToken, firstToken, firstConsequentToken.type === "Punctuator" &&
                         options.offsetTernaryExpressions ? 2 : 1);
 
                     /*
@@ -1204,8 +1216,7 @@ module.exports = {
                          * If `baz` were aligned with `bar` rather than being offset by 1 from `foo`, `baz` would end up
                          * having no expected indentation.
                          */
-                        offsets.setDesiredOffset(firstAlternateToken, firstToken,
-                            firstAlternateToken.type === "Punctuator" &&
+                        offsets.setDesiredOffset(firstAlternateToken, firstToken, firstAlternateToken.type === "Punctuator" &&
                             options.offsetTernaryExpressions ? 2 : 1);
                     }
                 }
@@ -1361,6 +1372,52 @@ module.exports = {
                 }
             },
 
+            PropertyDefinition(node) {
+                const firstToken = sourceCode.getFirstToken(node);
+                const maybeSemicolonToken = sourceCode.getLastToken(node);
+                let keyLastToken = null;
+
+                // Indent key.
+                if (node.computed) {
+                    const bracketTokenL = sourceCode.getTokenBefore(node.key, astUtils.isOpeningBracketToken);
+                    const bracketTokenR = keyLastToken = sourceCode.getTokenAfter(node.key, astUtils.isClosingBracketToken);
+                    const keyRange = [bracketTokenL.range[1], bracketTokenR.range[0]];
+
+                    if (bracketTokenL !== firstToken) {
+                        offsets.setDesiredOffset(bracketTokenL, firstToken, 0);
+                    }
+                    offsets.setDesiredOffsets(keyRange, bracketTokenL, 1);
+                    offsets.setDesiredOffset(bracketTokenR, bracketTokenL, 0);
+                } else {
+                    const idToken = keyLastToken = sourceCode.getFirstToken(node.key);
+
+                    if (idToken !== firstToken) {
+                        offsets.setDesiredOffset(idToken, firstToken, 1);
+                    }
+                }
+
+                // Indent initializer.
+                if (node.value) {
+                    const eqToken = sourceCode.getTokenBefore(node.value, astUtils.isEqToken);
+                    const valueToken = sourceCode.getTokenAfter(eqToken);
+
+                    offsets.setDesiredOffset(eqToken, keyLastToken, 1);
+                    offsets.setDesiredOffset(valueToken, eqToken, 1);
+                    if (astUtils.isSemicolonToken(maybeSemicolonToken)) {
+                        offsets.setDesiredOffset(maybeSemicolonToken, eqToken, 1);
+                    }
+                } else if (astUtils.isSemicolonToken(maybeSemicolonToken)) {
+                    offsets.setDesiredOffset(maybeSemicolonToken, keyLastToken, 1);
+                }
+            },
+
+            StaticBlock(node) {
+                const openingCurly = sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
+                const closingCurly = sourceCode.getLastToken(node);
+
+                addElementListIndent(node.body, openingCurly, closingCurly, options.StaticBlock.body);
+            },
+
             SwitchStatement(node) {
                 const openingCurly = sourceCode.getTokenAfter(node.discriminant, astUtils.isOpeningBraceToken);
                 const closingCurly = sourceCode.getLastToken(node);
index 35af38fd108ff50a164966f6223898bd52d3646a..ed322a4120af134a85831bdfd04e5624aaa82d76 100644 (file)
@@ -6,7 +6,7 @@
 
 "use strict";
 
-/* eslint sort-keys: ["error", "asc"] */
+/* eslint sort-keys: ["error", "asc"] -- More readable for long list */
 
 const { LazyLoadingRuleMap } = require("./utils/lazy-loading-rule-map");
 
@@ -221,6 +221,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
     "no-unsafe-optional-chaining": () => require("./no-unsafe-optional-chaining"),
     "no-unused-expressions": () => require("./no-unused-expressions"),
     "no-unused-labels": () => require("./no-unused-labels"),
+    "no-unused-private-class-members": () => require("./no-unused-private-class-members"),
     "no-unused-vars": () => require("./no-unused-vars"),
     "no-use-before-define": () => require("./no-use-before-define"),
     "no-useless-backreference": () => require("./no-useless-backreference"),
index 6cfdf92c909155c7982fd1c1cf09234fe0090a79..d994bbc1ea0590ea54d780dc14e8d84425c262c2 100644 (file)
@@ -48,7 +48,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow initialization in variable declarations",
-            category: "Variables",
             recommended: false,
             url: "https://eslint.org/docs/rules/init-declarations"
         },
index 3b282df2f0760160a237bc33254bc9f0c3e2bbea..cbadc19d5ed5632567538b41d40f1b61b72aead5 100644 (file)
@@ -42,7 +42,6 @@ module.exports = {
 
         docs: {
             description: "enforce the consistent use of either double or single quotes in JSX attributes",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/jsx-quotes"
         },
index fc885a117a1ea95c881ab733bc41bfb918cadf4d..c09cebb513ae3dd141a4e73699d8515c0a923214 100644 (file)
@@ -139,7 +139,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing between keys and values in object literal properties",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/key-spacing"
         },
@@ -428,19 +427,7 @@ module.exports = {
          * @returns {void}
          */
         function report(property, side, whitespace, expected, mode) {
-            const diff = whitespace.length - expected,
-                nextColon = getNextColon(property.key),
-                tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }),
-                tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }),
-                isKeySide = side === "key",
-                isExtra = diff > 0,
-                diffAbs = Math.abs(diff),
-                spaces = Array(diffAbs + 1).join(" ");
-
-            const locStart = isKeySide ? tokenBeforeColon.loc.end : nextColon.loc.start;
-            const locEnd = isKeySide ? nextColon.loc.start : tokenAfterColon.loc.start;
-            const missingLoc = isKeySide ? tokenBeforeColon.loc : tokenAfterColon.loc;
-            const loc = isExtra ? { start: locStart, end: locEnd } : missingLoc;
+            const diff = whitespace.length - expected;
 
             if ((
                 diff && mode === "strict" ||
@@ -448,6 +435,19 @@ module.exports = {
                 diff > 0 && !expected && mode === "minimum") &&
                 !(expected && containsLineTerminator(whitespace))
             ) {
+                const nextColon = getNextColon(property.key),
+                    tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }),
+                    tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }),
+                    isKeySide = side === "key",
+                    isExtra = diff > 0,
+                    diffAbs = Math.abs(diff),
+                    spaces = Array(diffAbs + 1).join(" ");
+
+                const locStart = isKeySide ? tokenBeforeColon.loc.end : nextColon.loc.start;
+                const locEnd = isKeySide ? nextColon.loc.start : tokenAfterColon.loc.start;
+                const missingLoc = isKeySide ? tokenBeforeColon.loc : tokenAfterColon.loc;
+                const loc = isExtra ? { start: locStart, end: locEnd } : missingLoc;
+
                 let fix;
 
                 if (isExtra) {
@@ -531,8 +531,8 @@ module.exports = {
 
         /**
          * Creates groups of properties.
-         * @param  {ASTNode} node ObjectExpression node being evaluated.
-         * @returns {Array.<ASTNode[]>} Groups of property AST node lists.
+         * @param {ASTNode} node ObjectExpression node being evaluated.
+         * @returns {Array<ASTNode[]>} Groups of property AST node lists.
          */
         function createGroups(node) {
             if (node.properties.length === 1) {
@@ -600,7 +600,7 @@ module.exports = {
 
         /**
          * Verifies spacing of property conforms to specified options.
-         * @param  {ASTNode} node Property node being evaluated.
+         * @param {ASTNode} node Property node being evaluated.
          * @param {Object} lineOptions Configured singleLine or multiLine options
          * @returns {void}
          */
@@ -629,7 +629,7 @@ module.exports = {
 
         /**
          * Verifies vertical alignment, taking into account groups of properties.
-         * @param  {ASTNode} node ObjectExpression node being evaluated.
+         * @param {ASTNode} node ObjectExpression node being evaluated.
          * @returns {void}
          */
         function verifyAlignment(node) {
index 913cf4682f903acde73b0f1075a96cbd395cdbce..44222e1a493f68ce3bae6570cbcde386e6c23380 100644 (file)
@@ -22,7 +22,7 @@ const PREV_TOKEN_M = /^[)\]}>*]$/u;
 const NEXT_TOKEN_M = /^[{*]$/u;
 const TEMPLATE_OPEN_PAREN = /\$\{$/u;
 const TEMPLATE_CLOSE_PAREN = /^\}/u;
-const CHECK_TYPE = /^(?:JSXElement|RegularExpression|String|Template)$/u;
+const CHECK_TYPE = /^(?:JSXElement|RegularExpression|String|Template|PrivateIdentifier)$/u;
 const KEYS = keywords.concat(["as", "async", "await", "from", "get", "let", "of", "set", "yield"]);
 
 // check duplications.
@@ -67,7 +67,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing before and after keywords",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/keyword-spacing"
         },
@@ -110,6 +109,8 @@ module.exports = {
     create(context) {
         const sourceCode = context.getSourceCode();
 
+        const tokensToIgnore = new WeakSet();
+
         /**
          * Reports a given token if there are not space(s) before the token.
          * @param {Token} token A token to report.
@@ -122,6 +123,7 @@ module.exports = {
             if (prevToken &&
                 (CHECK_TYPE.test(prevToken.type) || pattern.test(prevToken.value)) &&
                 !isOpenParenOfTemplate(prevToken) &&
+                !tokensToIgnore.has(prevToken) &&
                 astUtils.isTokenOnSameLine(prevToken, token) &&
                 !sourceCode.isSpaceBetweenTokens(prevToken, token)
             ) {
@@ -148,6 +150,7 @@ module.exports = {
             if (prevToken &&
                 (CHECK_TYPE.test(prevToken.type) || pattern.test(prevToken.value)) &&
                 !isOpenParenOfTemplate(prevToken) &&
+                !tokensToIgnore.has(prevToken) &&
                 astUtils.isTokenOnSameLine(prevToken, token) &&
                 sourceCode.isSpaceBetweenTokens(prevToken, token)
             ) {
@@ -174,6 +177,7 @@ module.exports = {
             if (nextToken &&
                 (CHECK_TYPE.test(nextToken.type) || pattern.test(nextToken.value)) &&
                 !isCloseParenOfTemplate(nextToken) &&
+                !tokensToIgnore.has(nextToken) &&
                 astUtils.isTokenOnSameLine(token, nextToken) &&
                 !sourceCode.isSpaceBetweenTokens(token, nextToken)
             ) {
@@ -200,6 +204,7 @@ module.exports = {
             if (nextToken &&
                 (CHECK_TYPE.test(nextToken.type) || pattern.test(nextToken.value)) &&
                 !isCloseParenOfTemplate(nextToken) &&
+                !tokensToIgnore.has(nextToken) &&
                 astUtils.isTokenOnSameLine(token, nextToken) &&
                 sourceCode.isSpaceBetweenTokens(token, nextToken)
             ) {
@@ -403,7 +408,15 @@ module.exports = {
          */
         function checkSpacingForForInStatement(node) {
             checkSpacingAroundFirstToken(node);
-            checkSpacingAroundTokenBefore(node.right);
+
+            const inToken = sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken);
+            const previousToken = sourceCode.getTokenBefore(inToken);
+
+            if (previousToken.type !== "PrivateIdentifier") {
+                checkSpacingBefore(inToken);
+            }
+
+            checkSpacingAfter(inToken);
         }
 
         /**
@@ -419,7 +432,15 @@ module.exports = {
             } else {
                 checkSpacingAroundFirstToken(node);
             }
-            checkSpacingAround(sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken));
+
+            const ofToken = sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken);
+            const previousToken = sourceCode.getTokenBefore(ofToken);
+
+            if (previousToken.type !== "PrivateIdentifier") {
+                checkSpacingBefore(ofToken);
+            }
+
+            checkSpacingAfter(ofToken);
         }
 
         /**
@@ -473,6 +494,7 @@ module.exports = {
          * Reports `static`, `get`, and `set` keywords of a given node if usage of
          * spacing around those keywords is invalid.
          * @param {ASTNode} node A node to report.
+         * @throws {Error} If unable to find token get, set, or async beside method name.
          * @returns {void}
          */
         function checkSpacingForProperty(node) {
@@ -567,7 +589,16 @@ module.exports = {
             // Others
             ImportNamespaceSpecifier: checkSpacingForImportNamespaceSpecifier,
             MethodDefinition: checkSpacingForProperty,
-            Property: checkSpacingForProperty
+            PropertyDefinition: checkSpacingForProperty,
+            StaticBlock: checkSpacingAroundFirstToken,
+            Property: checkSpacingForProperty,
+
+            // To avoid conflicts with `space-infix-ops`, e.g. `a > this.b`
+            "BinaryExpression[operator='>']"(node) {
+                const operatorToken = sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken);
+
+                tokensToIgnore.add(operatorToken);
+            }
         };
     }
 };
index 77ee147cbec9888befe9e7e5e76f2280deadef65..ad109a4f795a5a481cb9ee17f4f3207646528eea 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "enforce position of line comments",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/line-comment-position"
         },
index b3b393ead77de84984d2bdf7044ed4c0547da61a..92996ebd33189d2f122805e8a696af9c39d1ba21 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent linebreak style",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/linebreak-style"
         },
index 6806e793cd193b41dc1fa63162534d9ca9e94b8a..513d196224e10a4a4e0e9fa50969b14e3e5679ff 100644 (file)
@@ -55,7 +55,6 @@ module.exports = {
 
         docs: {
             description: "require empty lines around comments",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/lines-around-comment"
         },
@@ -186,10 +185,39 @@ module.exports = {
         /**
          * Returns the parent node that contains the given token.
          * @param {token} token The token to check.
-         * @returns {ASTNode} The parent node that contains the given token.
+         * @returns {ASTNode|null} The parent node that contains the given token.
          */
         function getParentNodeOfToken(token) {
-            return sourceCode.getNodeByRangeIndex(token.range[0]);
+            const node = sourceCode.getNodeByRangeIndex(token.range[0]);
+
+            /*
+             * For the purpose of this rule, the comment token is in a `StaticBlock` node only
+             * if it's inside the braces of that `StaticBlock` node.
+             *
+             * Example where this function returns `null`:
+             *
+             *   static
+             *   // comment
+             *   {
+             *   }
+             *
+             * Example where this function returns `StaticBlock` node:
+             *
+             *   static
+             *   {
+             *   // comment
+             *   }
+             *
+             */
+            if (node && node.type === "StaticBlock") {
+                const openingBrace = sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
+
+                return token.range[0] >= openingBrace.range[0]
+                    ? node
+                    : null;
+            }
+
+            return node;
         }
 
         /**
@@ -201,8 +229,15 @@ module.exports = {
         function isCommentAtParentStart(token, nodeType) {
             const parent = getParentNodeOfToken(token);
 
-            return parent && isParentNodeType(parent, nodeType) &&
-                    token.loc.start.line - parent.loc.start.line === 1;
+            if (parent && isParentNodeType(parent, nodeType)) {
+                const parentStartNodeOrToken = parent.type === "StaticBlock"
+                    ? sourceCode.getFirstToken(parent, { skip: 1 }) // opening brace of the static block
+                    : parent;
+
+                return token.loc.start.line - parentStartNodeOrToken.loc.start.line === 1;
+            }
+
+            return false;
         }
 
         /**
@@ -214,7 +249,7 @@ module.exports = {
         function isCommentAtParentEnd(token, nodeType) {
             const parent = getParentNodeOfToken(token);
 
-            return parent && isParentNodeType(parent, nodeType) &&
+            return !!parent && isParentNodeType(parent, nodeType) &&
                     parent.loc.end.line - token.loc.end.line === 1;
         }
 
@@ -224,7 +259,12 @@ module.exports = {
          * @returns {boolean} True if the comment is at block start.
          */
         function isCommentAtBlockStart(token) {
-            return isCommentAtParentStart(token, "ClassBody") || isCommentAtParentStart(token, "BlockStatement") || isCommentAtParentStart(token, "SwitchCase");
+            return (
+                isCommentAtParentStart(token, "ClassBody") ||
+                isCommentAtParentStart(token, "BlockStatement") ||
+                isCommentAtParentStart(token, "StaticBlock") ||
+                isCommentAtParentStart(token, "SwitchCase")
+            );
         }
 
         /**
@@ -233,7 +273,13 @@ module.exports = {
          * @returns {boolean} True if the comment is at block end.
          */
         function isCommentAtBlockEnd(token) {
-            return isCommentAtParentEnd(token, "ClassBody") || isCommentAtParentEnd(token, "BlockStatement") || isCommentAtParentEnd(token, "SwitchCase") || isCommentAtParentEnd(token, "SwitchStatement");
+            return (
+                isCommentAtParentEnd(token, "ClassBody") ||
+                isCommentAtParentEnd(token, "BlockStatement") ||
+                isCommentAtParentEnd(token, "StaticBlock") ||
+                isCommentAtParentEnd(token, "SwitchCase") ||
+                isCommentAtParentEnd(token, "SwitchStatement")
+            );
         }
 
         /**
index fb439dad6a3c7582b8f09ec717fe67fa765b37f5..c0c70e1a61ff39e7210eeda782d0b8eb9680e8bf 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * @fileoverview Require or disallow newlines around directives.
  * @author Kai Cataldo
- * @deprecated
+ * @deprecated in ESLint v4.0.0
  */
 
 "use strict";
@@ -18,7 +18,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow newlines around directives",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/lines-around-directive"
         },
index 97235303699089e20fa842051a897d33769e7bbc..e4c05f3050c0379bfc3fe67ed0a0eec8416ff751 100644 (file)
@@ -4,6 +4,10 @@
  */
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const astUtils = require("./utils/ast-utils");
 
 //------------------------------------------------------------------------------
@@ -16,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow an empty line between class members",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/lines-between-class-members"
         },
@@ -53,6 +56,51 @@ module.exports = {
 
         const sourceCode = context.getSourceCode();
 
+        /**
+         * Gets a pair of tokens that should be used to check lines between two class member nodes.
+         *
+         * In most cases, this returns the very last token of the current node and
+         * the very first token of the next node.
+         * For example:
+         *
+         *     class C {
+         *         x = 1;   // curLast: `;` nextFirst: `in`
+         *         in = 2
+         *     }
+         *
+         * There is only one exception. If the given node ends with a semicolon, and it looks like
+         * a semicolon-less style's semicolon - one that is not on the same line as the preceding
+         * token, but is on the line where the next class member starts - this returns the preceding
+         * token and the semicolon as boundary tokens.
+         * For example:
+         *
+         *     class C {
+         *         x = 1    // curLast: `1` nextFirst: `;`
+         *         ;in = 2
+         *     }
+         * When determining the desired layout of the code, we should treat this semicolon as
+         * a part of the next class member node instead of the one it technically belongs to.
+         * @param {ASTNode} curNode Current class member node.
+         * @param {ASTNode} nextNode Next class member node.
+         * @returns {Token} The actual last token of `node`.
+         * @private
+         */
+        function getBoundaryTokens(curNode, nextNode) {
+            const lastToken = sourceCode.getLastToken(curNode);
+            const prevToken = sourceCode.getTokenBefore(lastToken);
+            const nextToken = sourceCode.getFirstToken(nextNode); // skip possible lone `;` between nodes
+
+            const isSemicolonLessStyle = (
+                astUtils.isSemicolonToken(lastToken) &&
+                !astUtils.isTokenOnSameLine(prevToken, lastToken) &&
+                astUtils.isTokenOnSameLine(lastToken, nextToken)
+            );
+
+            return isSemicolonLessStyle
+                ? { curLast: prevToken, nextFirst: lastToken }
+                : { curLast: lastToken, nextFirst: nextToken };
+        }
+
         /**
          * Return the last token among the consecutive tokens that have no exceed max line difference in between, before the first token in the next member.
          * @param {Token} prevLastToken The last token in the previous member node.
@@ -101,8 +149,7 @@ module.exports = {
 
                 for (let i = 0; i < body.length - 1; i++) {
                     const curFirst = sourceCode.getFirstToken(body[i]);
-                    const curLast = sourceCode.getLastToken(body[i]);
-                    const nextFirst = sourceCode.getFirstToken(body[i + 1]);
+                    const { curLast, nextFirst } = getBoundaryTokens(body[i], body[i + 1]);
                     const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);
                     const skip = !isMulti && options[1].exceptAfterSingleLine;
                     const beforePadding = findLastConsecutiveTokenAfter(curLast, nextFirst, 1);
index bb48a546e95c6055ec2f6b0a95a5fbb68bad7504..3d26108a71504ace6b7ff9e1a76a05cec95a6d11 100644 (file)
@@ -19,15 +19,31 @@ module.exports = {
 
         docs: {
             description: "enforce a maximum number of classes per file",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/max-classes-per-file"
         },
 
         schema: [
             {
-                type: "integer",
-                minimum: 1
+                oneOf: [
+                    {
+                        type: "integer",
+                        minimum: 1
+                    },
+                    {
+                        type: "object",
+                        properties: {
+                            ignoreExpressions: {
+                                type: "boolean"
+                            },
+                            max: {
+                                type: "integer",
+                                minimum: 1
+                            }
+                        },
+                        additionalProperties: false
+                    }
+                ]
             }
         ],
 
@@ -36,8 +52,10 @@ module.exports = {
         }
     },
     create(context) {
-
-        const maxClasses = context.options[0] || 1;
+        const [option = {}] = context.options;
+        const [ignoreExpressions, max] = typeof option === "number"
+            ? [false, option || 1]
+            : [option.ignoreExpressions, option.max || 1];
 
         let classCount = 0;
 
@@ -46,19 +64,24 @@ module.exports = {
                 classCount = 0;
             },
             "Program:exit"(node) {
-                if (classCount > maxClasses) {
+                if (classCount > max) {
                     context.report({
                         node,
                         messageId: "maximumExceeded",
                         data: {
                             classCount,
-                            max: maxClasses
+                            max
                         }
                     });
                 }
             },
-            "ClassDeclaration, ClassExpression"() {
+            "ClassDeclaration"() {
                 classCount++;
+            },
+            "ClassExpression"() {
+                if (!ignoreExpressions) {
+                    classCount++;
+                }
             }
         };
     }
index 5c5296bec00b1955f2ce22679eefbf92e08d4671..bdacc66afb13052e6babb789fdd80bce82371fcb 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "enforce a maximum depth that blocks can be nested",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/max-depth"
         },
@@ -119,6 +118,7 @@ module.exports = {
             FunctionDeclaration: startFunction,
             FunctionExpression: startFunction,
             ArrowFunctionExpression: startFunction,
+            StaticBlock: startFunction,
 
             IfStatement(node) {
                 if (node.parent.type !== "IfStatement") {
@@ -147,6 +147,7 @@ module.exports = {
             "FunctionDeclaration:exit": endFunction,
             "FunctionExpression:exit": endFunction,
             "ArrowFunctionExpression:exit": endFunction,
+            "StaticBlock:exit": endFunction,
             "Program:exit": endFunction
         };
 
index dd76760c50504eb4dc1fd13bc8ce74bfa88a40f8..8c7985d3e16b452263a0c232f1e2df249943d982 100644 (file)
@@ -69,7 +69,6 @@ module.exports = {
 
         docs: {
             description: "enforce a maximum line length",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/max-len"
         },
@@ -215,7 +214,7 @@ module.exports = {
          * Ensure that an array exists at [key] on `object`, and add `value` to it.
          * @param {Object} object the object to mutate
          * @param {string} key the object's key
-         * @param {*} value the value to add
+         * @param {any} value the value to add
          * @returns {void}
          * @private
          */
index 60e2e879f54df053b0ad4d9bf28ee5f56539c11a..b2130ca260b4dcd31ccebbfbf90b834ddc0889d8 100644 (file)
@@ -48,7 +48,7 @@ const OPTIONS_OR_INTEGER_SCHEMA = {
 /**
  * Given a list of comment nodes, return a map with numeric keys (source code line numbers) and comment token values.
  * @param {Array} comments An array of comment nodes.
- * @returns {Map.<string,Node>} A map with numeric keys (source code line numbers) and comment token values.
+ * @returns {Map<string, Node>} A map with numeric keys (source code line numbers) and comment token values.
  */
 function getCommentLineNumbers(comments) {
     const map = new Map();
@@ -71,7 +71,6 @@ module.exports = {
 
         docs: {
             description: "enforce a maximum number of lines of code in a function",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/max-lines-per-function"
         },
index 8bd5a1c95f4043a46995eda3ca043e62f07b8fb0..291d7d9c1a89cbb9e9df294bdd0b408ae41543b3 100644 (file)
@@ -34,7 +34,6 @@ module.exports = {
 
         docs: {
             description: "enforce a maximum number of lines per file",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/max-lines"
         },
@@ -137,20 +136,6 @@ module.exports = {
             return [];
         }
 
-        /**
-         * Returns a new array formed by applying a given callback function to each element of the array, and then flattening the result by one level.
-         * TODO(stephenwade): Replace this with array.flatMap when we drop support for Node v10
-         * @param {any[]} array The array to process
-         * @param {Function} fn The function to use
-         * @returns {any[]} The result array
-         */
-        function flatMap(array, fn) {
-            const mapped = array.map(fn);
-            const flattened = [].concat(...mapped);
-
-            return flattened;
-        }
-
         return {
             "Program:exit"() {
                 let lines = sourceCode.lines.map((text, i) => ({
@@ -173,7 +158,7 @@ module.exports = {
                 if (skipComments) {
                     const comments = sourceCode.getAllComments();
 
-                    const commentLines = flatMap(comments, comment => getLinesWithoutCode(comment));
+                    const commentLines = comments.flatMap(getLinesWithoutCode);
 
                     lines = lines.filter(
                         l => !commentLines.includes(l.lineNumber)
index df1baceeb412f503a81e8cd705703f057a74cf46..df24a96da583954eafe907366a328ab347096cb2 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "enforce a maximum depth that callbacks can be nested",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/max-nested-callbacks"
         },
index 8fb798401cb8d4fb9ba353c866b744a6f0a469d2..c8be60e57745fd19d1e2abecb4db2acbc1051642 100644 (file)
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "enforce a maximum number of parameters in function definitions",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/max-params"
         },
index 5407cff3c548749991cbb9c2a0e377f61cdf6525..7c743292bd662f461701bd308ddd1e5f8e0f18d4 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "enforce a maximum number of statements allowed per line",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/max-statements-per-line"
         },
index 65d5539550d37a7a48e6cc90c76320338db2563d..969e40bbd855c55ce1bf29f364f858c659026f94 100644 (file)
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "enforce a maximum number of statements allowed in function blocks",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/max-statements"
         },
@@ -124,6 +123,14 @@ module.exports = {
         function endFunction(node) {
             const count = functionStack.pop();
 
+            /*
+             * This rule does not apply to class static blocks, but we have to track them so
+             * that stataments in them do not count as statements in the enclosing function.
+             */
+            if (node.type === "StaticBlock") {
+                return;
+            }
+
             if (ignoreTopLevelFunctions && functionStack.length === 0) {
                 topLevelFunctions.push({ node, count });
             } else {
@@ -149,12 +156,14 @@ module.exports = {
             FunctionDeclaration: startFunction,
             FunctionExpression: startFunction,
             ArrowFunctionExpression: startFunction,
+            StaticBlock: startFunction,
 
             BlockStatement: countStatements,
 
             "FunctionDeclaration:exit": endFunction,
             "FunctionExpression:exit": endFunction,
             "ArrowFunctionExpression:exit": endFunction,
+            "StaticBlock:exit": endFunction,
 
             "Program:exit"() {
                 if (topLevelFunctions.length === 1) {
index 9524818b8bd74dac0090253898ae26dbd67283b8..da5ee50df4a6e539058c98a896e73922fc97f0af 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "enforce a particular style for multiline comments",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/multiline-comment-style"
         },
index 98360b9cad467618ded439a8b0a7f5b25c8e2a58..6f468c828c04a57c5e67e5e27dadea94b56ec18e 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "enforce newlines between operands of ternary expressions",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/multiline-ternary"
         },
index 4249a542802de14dbc3f613ac328983542943687..9abf3379b8a7d9be465a42536ffbad04db265265 100644 (file)
@@ -33,7 +33,8 @@ const CAPS_ALLOWED = [
  * Ensure that if the key is provided, it must be an array.
  * @param {Object} obj Object to check with `key`.
  * @param {string} key Object key to check on `obj`.
- * @param {*} fallback If obj[key] is not present, this will be returned.
+ * @param {any} fallback If obj[key] is not present, this will be returned.
+ * @throws {TypeError} If key is not an own array type property of `obj`.
  * @returns {string[]} Returns obj[key] if it's an Array, otherwise `fallback`
  */
 function checkArray(obj, key, fallback) {
@@ -81,7 +82,6 @@ module.exports = {
 
         docs: {
             description: "require constructor names to begin with a capital letter",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/new-cap"
         },
index 405ec1b515a4d610a7806b63a36804d36049f2e9..786300dba7d61002dd069f307e203b3fc6fb2102 100644 (file)
@@ -25,7 +25,6 @@ module.exports = {
 
         docs: {
             description: "enforce or disallow parentheses when invoking a constructor with no arguments",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/new-parens"
         },
index 4809d9bfc5a2daf8ae1b00c1c822df3b8316a72b..3eea1b1f7328e21b14434478c15e8d1e9f0719a8 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * @fileoverview Rule to check empty newline after "var" statement
  * @author Gopal Venkatesan
- * @deprecated
+ * @deprecated in ESLint v4.0.0
  */
 
 "use strict";
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow an empty line after variable declarations",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/newline-after-var"
         },
@@ -145,9 +144,9 @@ module.exports = {
 
         /**
          * Determine if a token starts more than one line after a comment ends
-         * @param  {token}   token            The token being checked
-         * @param {integer}  commentStartLine The line number on which the comment starts
-         * @returns {boolean}                 True if `token` does not start immediately after a comment
+         * @param {token} token The token being checked
+         * @param {integer} commentStartLine The line number on which the comment starts
+         * @returns {boolean} True if `token` does not start immediately after a comment
          */
         function hasBlankLineAfterComment(token, commentStartLine) {
             return token.loc.start.line > getLastCommentLineOfBlock(commentStartLine) + 1;
index 65ca32321cc823a73e484bb960c89acc71bba18d..fd6341e67c0d667ba91ecaa125b309f61029c440 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * @fileoverview Rule to require newlines before `return` statement
  * @author Kai Cataldo
- * @deprecated
+ * @deprecated in ESLint v4.0.0
  */
 "use strict";
 
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "require an empty line before `return` statements",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/newline-before-return"
         },
index 46c9d6c10f802d2b225d6285ea23764417da2576..8de9a6a239801a1c5866e7dc2ecaafe118f0fb31 100644 (file)
@@ -18,7 +18,6 @@ module.exports = {
 
         docs: {
             description: "require a newline after each call in a method chain",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/newline-per-chained-call"
         },
@@ -53,7 +52,7 @@ module.exports = {
          * Get the prefix of a given MemberExpression node.
          * If the MemberExpression node is a computed value it returns a
          * left bracket. If not it returns a period.
-         * @param  {ASTNode} node A MemberExpression node to get
+         * @param {ASTNode} node A MemberExpression node to get
          * @returns {string} The prefix of the node.
          */
         function getPrefix(node) {
index 702b4d2ba7cccf1ddea02bc10b4168534f0bc7e0..918b98489a7e72a57df0c1560aad8ebb888fa9f7 100644 (file)
@@ -88,7 +88,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of `alert`, `confirm`, and `prompt`",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-alert"
         },
index 90c6d6bbd59281a5de550ccaa3b05971ceb0ba4f..0904fa6d8f0cd9a79beb62046d64c92fb50c9053 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow `Array` constructors",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-array-constructor"
         },
index 553311e580450820d30fbf81d64764cb3be403eb..27116f1da788e78ab6705c11759a59022cc1c221 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow using an async function as a Promise executor",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-async-promise-executor"
         },
index 9ca89866a6e114aa7c8238530653195c9e018b4e..38af8b56c1a8a118277af598c1242fcf0af56728 100644 (file)
@@ -59,7 +59,6 @@ module.exports = {
 
         docs: {
             description: "disallow `await` inside of loops",
-            category: "Possible Errors",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-await-in-loop"
         },
index a9c3360a7c5b42904b84cb9bea5719788e5c33b5..10bf24a29977a3debeb5bc7bd0f8e88702b31fca 100644 (file)
@@ -26,7 +26,6 @@ module.exports = {
 
         docs: {
             description: "disallow bitwise operators",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-bitwise"
         },
@@ -63,7 +62,7 @@ module.exports = {
 
         /**
          * Reports an unexpected use of a bitwise operator.
-         * @param   {ASTNode} node Node which contains the bitwise operator.
+         * @param {ASTNode} node Node which contains the bitwise operator.
          * @returns {void}
          */
         function report(node) {
@@ -72,7 +71,7 @@ module.exports = {
 
         /**
          * Checks if the given node has a bitwise operator.
-         * @param   {ASTNode} node The node to check.
+         * @param {ASTNode} node The node to check.
          * @returns {boolean} Whether or not the node has a bitwise operator.
          */
         function hasBitwiseOperator(node) {
@@ -81,7 +80,7 @@ module.exports = {
 
         /**
          * Checks if exceptions were provided, e.g. `{ allow: ['~', '|'] }`.
-         * @param   {ASTNode} node The node to check.
+         * @param {ASTNode} node The node to check.
          * @returns {boolean} Whether or not the node has a bitwise operator.
          */
         function allowedOperator(node) {
@@ -90,7 +89,7 @@ module.exports = {
 
         /**
          * Checks if the given bitwise operator is used for integer typecasting, i.e. "|0"
-         * @param   {ASTNode} node The node to check.
+         * @param {ASTNode} node The node to check.
          * @returns {boolean} whether the node is used in integer typecasting.
          */
         function isInt32Hint(node) {
@@ -100,7 +99,7 @@ module.exports = {
 
         /**
          * Report if the given node contains a bitwise operator.
-         * @param   {ASTNode} node The node to check.
+         * @param {ASTNode} node The node to check.
          * @returns {void}
          */
         function checkNodeForBitwiseOperator(node) {
index 152dda0ceae2cec2998371c925e0f4a74adb7479..cc5906e78a353f6198e83d58b3761c55c313c319 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview disallow use of the Buffer() constructor
  * @author Teddy Katz
+ * @deprecated in ESLint v7.0.0
  */
 "use strict";
 
@@ -18,7 +19,6 @@ module.exports = {
 
         docs: {
             description: "disallow use of the `Buffer()` constructor",
-            category: "Node.js and CommonJS",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-buffer-constructor"
         },
index 5fe1bd449857c3acf82bcf7b2605759d46e6f03e..dbb527906f257077cad540262eedb09c6732a5e1 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of `arguments.caller` or `arguments.callee`",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-caller"
         },
index 1d54e221625e70b2dbd305e7a2a3a70a06410f35..a132f0370fa60068a415c2eafcb1297e3076dee9 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow lexical declarations in case clauses",
-            category: "Best Practices",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-case-declarations"
         },
index 4917af84e432114c5f380c5d1b12a7e3b8ed68fb..0cbeedf90b6ed2dc2b87b2620276537abe91adcc 100644 (file)
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "disallow `catch` clause parameters from shadowing variables in the outer scope",
-            category: "Variables",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-catch-shadow"
         },
index 887058ba0f967d502c8edb99cbebe9c222c06b18..839ad03e292d1be273a0fd06ca52ff2cfd023c25 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "disallow reassigning class members",
-            category: "ECMAScript 6",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-class-assign"
         },
index 0c6865ad59e8fe5d2cebf27d10d2aae4da9f1931..e8fdaa0cc6949335c55419d5e3a96efb50b6edf8 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow comparing against -0",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-compare-neg-zero"
         },
index 3843a7ac2e3e91c319185fec72e3b6875b661d56..42f75af7d0c7d2857cddfb5e45e331f4694326ff 100644 (file)
@@ -34,7 +34,6 @@ module.exports = {
 
         docs: {
             description: "disallow assignment operators in conditional expressions",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-cond-assign"
         },
index 9009b64fa1559a9825ea8faaeb72da0476138aae..fa87f4012e8561ba7bc465c2893cccddaf9ff8a2 100644 (file)
@@ -31,7 +31,6 @@ module.exports = {
 
         docs: {
             description: "disallow arrow functions where they could be confused with comparisons",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-confusing-arrow"
         },
index 56dbbc3a9fd5953243703aef31c88604f0dd4faf..a5937cbddb20eaf11148affcc8abbfc2f4250d2b 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of `console`",
-            category: "Possible Errors",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-console"
         },
index e4ae891705f89932e206fee395537c56f40d5f98..6ca1b6107a86bd4edab1851cab6e99e01d5b2f65 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "disallow reassigning `const` variables",
-            category: "ECMAScript 6",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-const-assign"
         },
index 3c2d68cbf6caf114c0d81126dc5c30b409ebcfb9..7a7030a9a46cb0cccb5873aee2ce1c02370f1d64 100644 (file)
@@ -19,7 +19,6 @@ module.exports = {
 
         docs: {
             description: "disallow constant expressions in conditions",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-constant-condition"
         },
index 4757770b7cc8e604020d21d7d0f8476cdfd7579d..b4b5baf743c28c130139fa2218228c2d7f90fe66 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow returning value from constructor",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-constructor-return"
         },
index 96718d17a3db4be287c3f6985ece1f63dc16fab4..e72e862df596f6554058d2d8338b50479bda86ad 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow `continue` statements",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-continue"
         },
index 6feeb6419d566d1e52f5a754a0c77b9521584cb8..908d61ae449ba1f3cc1f8aad82c7b8dfa7557fc5 100644 (file)
@@ -52,7 +52,6 @@ module.exports = {
 
         docs: {
             description: "disallow control characters in regular expressions",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-control-regex"
         },
index 95a28a862151be145972f1d81ac831461ce20654..46dd57639cf41599d217d7c9e18e74f74a1342b1 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of `debugger`",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-debugger"
         },
index aeab951d75ff82f525829d0c3035f0622da396d7..1438ebc33bd51f091f0d3717ca0335cae6053e0a 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow deleting variables",
-            category: "Variables",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-delete-var"
         },
index 0ccabdcc6988b419f5d8670c56648433c326d79d..40388c366e266f976d73aec5794c22b2208da1fe 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow division operators explicitly at the beginning of regular expressions",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-div-regex"
         },
index 817277f522e7323de0c9fcc45acb8d88c86d3644..0880b9c812476bb353cb056d4d965def7682872a 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow duplicate arguments in `function` definitions",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-dupe-args"
         },
index b12939d57bc884b1a461c020f32842caaddb380d..f74865b82a8862f1e1cfec42802c2e1c99690b75 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "disallow duplicate class members",
-            category: "ECMAScript 6",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-dupe-class-members"
         },
@@ -73,20 +72,21 @@ module.exports = {
             },
 
             // Reports the node if its name has been declared already.
-            MethodDefinition(node) {
+            "MethodDefinition, PropertyDefinition"(node) {
                 const name = astUtils.getStaticPropertyName(node);
+                const kind = node.type === "MethodDefinition" ? node.kind : "field";
 
-                if (name === null || node.kind === "constructor") {
+                if (name === null || kind === "constructor") {
                     return;
                 }
 
                 const state = getState(name, node.static);
                 let isDuplicate = false;
 
-                if (node.kind === "get") {
+                if (kind === "get") {
                     isDuplicate = (state.init || state.get);
                     state.get = true;
-                } else if (node.kind === "set") {
+                } else if (kind === "set") {
                     isDuplicate = (state.init || state.set);
                     state.set = true;
                 } else {
index cbeb437da1e7ec16acbf492d42cf58d8ca70c608..0d8b17cc0ab4c9ca17bedba70de42dad93f5726c 100644 (file)
@@ -52,7 +52,6 @@ module.exports = {
 
         docs: {
             description: "disallow duplicate conditions in if-else-if chains",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-dupe-else-if"
         },
index 89e1f2de642285d77b953c282c34898121b94fef..ecec022185fd686858afd5e76cfa4f9aada7e76d 100644 (file)
@@ -23,7 +23,6 @@ const SET_KIND = /^(?:init|set)$/u;
  */
 class ObjectInfo {
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
      * @param {ObjectInfo|null} upper The information of the outer object.
      * @param {ASTNode} node The ObjectExpression node of this information.
@@ -89,7 +88,6 @@ module.exports = {
 
         docs: {
             description: "disallow duplicate keys in object literals",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-dupe-keys"
         },
index e2d9665e7f564d8fced4fbba68fe7def57756f67..4669dcee1baa50be4e52e8ac98b3d7e6969a0709 100644 (file)
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "disallow duplicate case labels",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-duplicate-case"
         },
index cc3da1d5a68080adf9408cd72701cf95f5110522..2663698dc96c4292ffd78883230032615828d86d 100644 (file)
@@ -233,7 +233,6 @@ module.exports = {
 
         docs: {
             description: "disallow duplicate module imports",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-duplicate-imports"
         },
index 84409fac87cff8b4568ad6f435ecd1f3f1f4cbaf..4c981ae159293ddad3dbf74aa8b20ca622241650 100644 (file)
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "disallow `else` blocks after `return` statements in `if` statements",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-else-return"
         },
index 7dc219fe1a76c5de9cb3b13330c4f8b256b5a141..85e8ef767912ef5282523a5ade0be8f9956840c3 100644 (file)
 /*
  * plain-English description of the following regexp:
  * 0. `^` fix the match at the beginning of the string
- * 1. `\/`: the `/` that begins the regexp
- * 2. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following
- * 2.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes)
- * 2.1. `\\.`: an escape sequence
- * 2.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty
- * 3. `\/` the `/` that ends the regexp
- * 4. `[gimuy]*`: optional regexp flags
- * 5. `$`: fix the match at the end of the string
+ * 1. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following
+ * 1.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes)
+ * 1.1. `\\.`: an escape sequence
+ * 1.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty
+ * 2. `$`: fix the match at the end of the string
  */
-const regex = /^\/([^\\[]|\\.|\[([^\\\]]|\\.)+\])*\/[gimuys]*$/u;
+const regex = /^([^\\[]|\\.|\[([^\\\]]|\\.)+\])*$/u;
 
 //------------------------------------------------------------------------------
 // Rule Definition
@@ -33,7 +30,6 @@ module.exports = {
 
         docs: {
             description: "disallow empty character classes in regular expressions",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-empty-character-class"
         },
@@ -46,18 +42,12 @@ module.exports = {
     },
 
     create(context) {
-        const sourceCode = context.getSourceCode();
-
         return {
-
-            Literal(node) {
-                const token = sourceCode.getFirstToken(node);
-
-                if (token.type === "RegularExpression" && !regex.test(token.value)) {
+            "Literal[regex]"(node) {
+                if (!regex.test(node.regex.pattern)) {
                     context.report({ node, messageId: "unexpected" });
                 }
             }
-
         };
 
     }
index c512f8cd5f45004b00c0d927ad279a8c38dcc5f8..8b1073a59d86444d89bf1cb16b0a274507a3fb6f 100644 (file)
@@ -95,7 +95,6 @@ module.exports = {
 
         docs: {
             description: "disallow empty functions",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-empty-function"
         },
index 9f34bfde92e2e9dc225ed2ed2480a9fef96abfea..99ea3a7905b6a8cfda72f8f999be2719c702999a 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow empty destructuring patterns",
-            category: "Best Practices",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-empty-pattern"
         },
index 45bf03c13aee5f39661ce8056ebe37b558e791b4..4ed3c5c5458b1da059ebbbf82feb400f65ff5da5 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "disallow empty block statements",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-empty"
         },
index b8dead96d2514c58821036667d57b7763a668d01..dae922840b825eb1ccf7ccb8064f996d16e67ee8 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "disallow `null` comparisons without type-checking operators",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-eq-null"
         },
index a020fdee014a201c0ba77adf0a908b40e620892e..96b85a07120ef5fcd325eefbf54da56d500e5535 100644 (file)
@@ -43,7 +43,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of `eval()`",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-eval"
         },
@@ -247,6 +246,10 @@ module.exports = {
             "FunctionExpression:exit": exitVarScope,
             ArrowFunctionExpression: enterVarScope,
             "ArrowFunctionExpression:exit": exitVarScope,
+            "PropertyDefinition > *.value": enterVarScope,
+            "PropertyDefinition > *.value:exit": exitVarScope,
+            StaticBlock: enterVarScope,
+            "StaticBlock:exit": exitVarScope,
 
             ThisExpression(node) {
                 if (!isMember(node.parent, "eval")) {
index 1163920361d2d4bc999144217c8450586a7c476d..cd56c94af7587cb39d1e95a9558e239832ffb07a 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "disallow reassigning exceptions in `catch` clauses",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-ex-assign"
         },
index 2a804b563980bebc393208a3f2b9e3d8a5ce0ecd..4d5accbae632978e53ea89585aaa98bad9deea7e 100644 (file)
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "disallow extending native types",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-extend-native"
         },
index 2db440dc1ea9b7beba32f9a05c89c5700e770b0d..6fd3be1d601818f7fe0b299f537df6e1a3229909 100644 (file)
@@ -26,7 +26,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary calls to `.bind()`",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-extra-bind"
         },
index 6ae3ea62ca77d67b064f113d197e03f137893f1a..cb061dac5d543c08eafad776ab8fd5b471d7664c 100644 (file)
@@ -24,7 +24,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary boolean casts",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-extra-boolean-cast"
         },
@@ -150,6 +149,7 @@ module.exports = {
          * For example, if the parent is `ConditionalExpression`, `previousNode` must be its `test` child.
          * @param {ASTNode} previousNode Previous node.
          * @param {ASTNode} node The node to check.
+         * @throws {Error} (Unreachable.)
          * @returns {boolean} `true` if the node needs to be parenthesized.
          */
         function needsParens(previousNode, node) {
index 81406e7609573322747299c8a61350fc0fa169b7..bbb2413b2c76b329058c29462db935e2bf8419af 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary labels",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-extra-label"
         },
index 307e340c958d880d060d5c19648618fcc2492ad5..0756d2fb2c7bfcd79798ccd0b314990a05909355 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary parentheses",
-            category: "Possible Errors",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-extra-parens"
         },
@@ -808,13 +807,6 @@ module.exports = {
 
             CallExpression: checkCallNew,
 
-            ClassBody(node) {
-                node.body
-                    .filter(member => member.type === "MethodDefinition" && member.computed && member.key)
-                    .filter(member => hasExcessParensWithPrecedence(member.key, PRECEDENCE_OF_ASSIGNMENT_EXPR))
-                    .forEach(member => report(member.key));
-            },
-
             ConditionalExpression(node) {
                 if (isReturnAssignException(node)) {
                     return;
@@ -1063,6 +1055,12 @@ module.exports = {
                 }
             },
 
+            "MethodDefinition[computed=true]"(node) {
+                if (hasExcessParensWithPrecedence(node.key, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
+                    report(node.key);
+                }
+            },
+
             NewExpression: checkCallNew,
 
             ObjectExpression(node) {
@@ -1090,6 +1088,16 @@ module.exports = {
                 }
             },
 
+            PropertyDefinition(node) {
+                if (node.computed && hasExcessParensWithPrecedence(node.key, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
+                    report(node.key);
+                }
+
+                if (node.value && hasExcessParensWithPrecedence(node.value, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
+                    report(node.value);
+                }
+            },
+
             RestElement(node) {
                 const argument = node.argument;
 
index e0a8df0565af98359b3cacd67005ce8a46fdcccd..0e2bcaf077891b54f47774d7048d0a92c597ae4a 100644 (file)
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary semicolons",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-extra-semi"
         },
@@ -117,7 +116,7 @@ module.exports = {
              * @param {Node} node A MethodDefinition node of the start point.
              * @returns {void}
              */
-            MethodDefinition(node) {
+            "MethodDefinition, PropertyDefinition, StaticBlock"(node) {
                 checkForPartOfClassBody(sourceCode.getTokenAfter(node));
             }
         };
index e8016e93e59bc908aa13b761f45557b4704ab07c..bf2c82514bb2d1b376a41f58cf8008416eb819e1 100644 (file)
 const DEFAULT_FALLTHROUGH_COMMENT = /falls?\s?through/iu;
 
 /**
- * Checks whether or not a given node has a fallthrough comment.
- * @param {ASTNode} node A SwitchCase node to get comments.
+ * Checks whether or not a given case has a fallthrough comment.
+ * @param {ASTNode} caseWhichFallsThrough SwitchCase node which falls through.
+ * @param {ASTNode} subsequentCase The case after caseWhichFallsThrough.
  * @param {RuleContext} context A rule context which stores comments.
  * @param {RegExp} fallthroughCommentPattern A pattern to match comment to.
- * @returns {boolean} `true` if the node has a valid fallthrough comment.
+ * @returns {boolean} `true` if the case has a valid fallthrough comment.
  */
-function hasFallthroughComment(node, context, fallthroughCommentPattern) {
+function hasFallthroughComment(caseWhichFallsThrough, subsequentCase, context, fallthroughCommentPattern) {
     const sourceCode = context.getSourceCode();
-    const comment = sourceCode.getCommentsBefore(node).pop();
+
+    if (caseWhichFallsThrough.consequent.length === 1 && caseWhichFallsThrough.consequent[0].type === "BlockStatement") {
+        const trailingCloseBrace = sourceCode.getLastToken(caseWhichFallsThrough.consequent[0]);
+        const commentInBlock = sourceCode.getCommentsBefore(trailingCloseBrace).pop();
+
+        if (commentInBlock && fallthroughCommentPattern.test(commentInBlock.value)) {
+            return true;
+        }
+    }
+
+    const comment = sourceCode.getCommentsBefore(subsequentCase).pop();
 
     return Boolean(comment && fallthroughCommentPattern.test(comment.value));
 }
@@ -53,7 +64,6 @@ module.exports = {
 
         docs: {
             description: "disallow fallthrough of `case` statements",
-            category: "Best Practices",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-fallthrough"
         },
@@ -108,7 +118,7 @@ module.exports = {
                  * Checks whether or not there is a fallthrough comment.
                  * And reports the previous fallthrough node if that does not exist.
                  */
-                if (fallthroughCase && !hasFallthroughComment(node, context, fallthroughCommentPattern)) {
+                if (fallthroughCase && !hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern)) {
                     context.report({
                         messageId: node.test ? "case" : "default",
                         node
index b1d883212e20761209ace9626df3d6b5193b78ac..92ac2326b9fd7be12bc4e7740a6ada8aa462df2b 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow leading or trailing decimal points in numeric literals",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-floating-decimal"
         },
index 33d0ad9ecd14335370d46039de934569f2726e65..aa04f337ae0c43f79f064ee66e724f315656ab62 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "disallow reassigning `function` declarations",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-func-assign"
         },
index ea854c4aa8cd9bf8fe4b6544a21d6ec40821f2c5..85aac7cdc0f280c55979c9a052cb62405bd2870a 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow assignments to native objects or read-only global variables",
-            category: "Best Practices",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-global-assign"
         },
index 993b8d1f1c8d4457dacddcda450a6b251cca3a21..1d11e10d597ced793874da1c33096af31afd3fde 100644 (file)
@@ -173,7 +173,6 @@ module.exports = {
 
         docs: {
             description: "disallow shorthand type conversions",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-implicit-coercion"
         },
index d4bfa3af82fc3bc9e221fc9a135a822083c85c74..8740cd8053194a61978800dcf693055e7ecb5b45 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow declarations in the global scope",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-implicit-globals"
         },
index b8120a64887c5a38c4b30896ae986c20775ca182..2432e68b6120d382465b50a5c699a2c9fd7729ef 100644 (file)
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of `eval()`-like methods",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-implied-eval"
         },
index 41060d8ac9e0b252e9c763aa6ee0774ec780b8b8..fbe63d0539f4941398ecdaf541c06d3650752fca 100644 (file)
@@ -180,7 +180,6 @@ module.exports = {
 
         docs: {
             description: "disallow assigning to imported bindings",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-import-assign"
         },
index dec278615e2e120344b366884e820b01592d22d1..8a955a6130e298b6dfc2115bae0ff444dbc508d4 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "disallow inline comments after code",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-inline-comments"
         },
index 0768bc61149cecc2040e39ed7b0219cf04912156..49b5114a2099bcc2c94c7fe717a97a2774702a51 100644 (file)
@@ -15,16 +15,39 @@ const astUtils = require("./utils/ast-utils");
 // Rule Definition
 //------------------------------------------------------------------------------
 
-const validParent = new Set(["Program", "ExportNamedDeclaration", "ExportDefaultDeclaration"]);
+const validParent = new Set(["Program", "StaticBlock", "ExportNamedDeclaration", "ExportDefaultDeclaration"]);
 const validBlockStatementParent = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
 
+/**
+ * Finds the nearest enclosing context where this rule allows declarations and returns its description.
+ * @param {ASTNode} node Node to search from.
+ * @returns {string} Description. One of "program", "function body", "class static block body".
+ */
+function getAllowedBodyDescription(node) {
+    let { parent } = node;
+
+    while (parent) {
+
+        if (parent.type === "StaticBlock") {
+            return "class static block body";
+        }
+
+        if (astUtils.isFunction(parent)) {
+            return "function body";
+        }
+
+        ({ parent } = parent);
+    }
+
+    return "program";
+}
+
 module.exports = {
     meta: {
         type: "problem",
 
         docs: {
             description: "disallow variable or `function` declarations in nested blocks",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-inner-declarations"
         },
@@ -60,14 +83,12 @@ module.exports = {
                 return;
             }
 
-            const upperFunction = astUtils.getUpperFunction(parent);
-
             context.report({
                 node,
                 messageId: "moveDeclToRoot",
                 data: {
                     type: (node.type === "FunctionDeclaration" ? "function" : "variable"),
-                    body: (upperFunction === null ? "program" : "function body")
+                    body: getAllowedBodyDescription(node)
                 }
             });
         }
index 94ad5ba6d5c23db14c2564ff059db94e797dfbb7..ee19932896609934ccae25fe9a697889c860f53e 100644 (file)
@@ -10,7 +10,7 @@
 
 const RegExpValidator = require("regexpp").RegExpValidator;
 const validator = new RegExpValidator();
-const validFlags = /[gimuys]/gu;
+const validFlags = /[dgimsuy]/gu;
 const undefined1 = void 0;
 
 //------------------------------------------------------------------------------
@@ -23,7 +23,6 @@ module.exports = {
 
         docs: {
             description: "disallow invalid regular expression strings in `RegExp` constructors",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-invalid-regexp"
         },
index a79c586d71985296150978b2c2caf8b57e8c3cff..e1d7cbcf553860307412256e47aa17c936a40c0f 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow `this` keywords outside of classes or class-like objects",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-invalid-this"
         },
@@ -129,6 +128,14 @@ module.exports = {
             FunctionExpression: enterFunction,
             "FunctionExpression:exit": exitFunction,
 
+            // Field initializers are implicit functions.
+            "PropertyDefinition > *.value": enterFunction,
+            "PropertyDefinition > *.value:exit": exitFunction,
+
+            // Class static blocks are implicit functions.
+            StaticBlock: enterFunction,
+            "StaticBlock:exit": exitFunction,
+
             // Reports if `this` of the current context is invalid.
             ThisExpression(node) {
                 const current = stack.getCurrent();
index 15711c6157a9a49215dfcb838f322efd038ea461..c160971539471bb635d6f6cb0d3d96a25c650f29 100644 (file)
@@ -31,7 +31,6 @@ module.exports = {
 
         docs: {
             description: "disallow irregular whitespace",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-irregular-whitespace"
         },
index 9ba1e7aefdbc3b94d57d0708eec2f689e1d06167..4117f6211c7614d2162152eed4852e4fc021bd87 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of the `__iterator__` property",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-iterator"
         },
index 570db03285cf973459dddef277137a585ed95f52..4532527c6e84b65f607f305821b6d3663ac9f13a 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow labels that share a name with a variable",
-            category: "Variables",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-label-var"
         },
index 85760d80dbedd7c2f5672cd9af79c7b688ad5e06..5dd15be092e32a8b34548633d191d09185100e96 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "disallow labeled statements",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-labels"
         },
index 290784b82ea2fb7eb416b50dca6ffa8f85ca86fe..33d4706a946646fac6f8264dc8f68b9007f2b6e2 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary nested blocks",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-lone-blocks"
         },
@@ -40,7 +39,9 @@ module.exports = {
          * @returns {void}
          */
         function report(node) {
-            const messageId = node.parent.type === "BlockStatement" ? "redundantNestedBlock" : "redundantBlock";
+            const messageId = node.parent.type === "BlockStatement" || node.parent.type === "StaticBlock"
+                ? "redundantNestedBlock"
+                : "redundantBlock";
 
             context.report({
                 node,
@@ -55,6 +56,7 @@ module.exports = {
          */
         function isLoneBlock(node) {
             return node.parent.type === "BlockStatement" ||
+                node.parent.type === "StaticBlock" ||
                 node.parent.type === "Program" ||
 
                 // Don't report blocks in switch cases if the block is the only statement of the case.
@@ -100,7 +102,10 @@ module.exports = {
                         loneBlocks.pop();
                         report(node);
                     } else if (
-                        node.parent.type === "BlockStatement" &&
+                        (
+                            node.parent.type === "BlockStatement" ||
+                            node.parent.type === "StaticBlock"
+                        ) &&
                         node.parent.body.length === 1
                     ) {
                         report(node);
index 6552adc5752852fde000c3831dbdd656e2be4b06..e44f000141bf25f7099af306d213f7820a6b52d1 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow `if` statements as the only statement in `else` blocks",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-lonely-if"
         },
index 13ebd3ee22b8d721fee98392ebe6437bc7154a2d..d1a7868072a48019f3e6e3f624fe97936c8e31ca 100644 (file)
@@ -154,7 +154,6 @@ module.exports = {
 
         docs: {
             description: "disallow function declarations that contain unsafe references inside loop statements",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-loop-func"
         },
@@ -174,7 +173,7 @@ module.exports = {
          * - has a loop node in ancestors.
          * - has any references which refers to an unsafe variable.
          * @param {ASTNode} node The AST node to check.
-         * @returns {boolean} Whether or not the node is within a loop.
+         * @returns {void}
          */
         function checkForLoops(node) {
             const loopNode = getContainingLoopNode(node);
index 2d0c61842209ae2041c09ef65941624d0c9cafe5..417616dd231c93d73cfbb69141b818ed3de02e5d 100644 (file)
@@ -15,8 +15,7 @@ module.exports = {
 
         docs: {
             description: "disallow literal numbers that lose precision",
-            category: "Possible Errors",
-            recommended: false,
+            recommended: true,
             url: "https://eslint.org/docs/rules/no-loss-of-precision"
         },
         schema: [],
index 510b3f9b26172ec7617d4a0312bc32bc2251707e..a2c678e710080f72e9110817b60c9842a05a279b 100644 (file)
@@ -32,7 +32,6 @@ module.exports = {
 
         docs: {
             description: "disallow magic numbers",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-magic-numbers"
         },
index 3d004615c3f0707b28935d89d00b40e2ee9f0b81..70e31e604f4abd556a53f96e7d5018fa0ffcb838 100644 (file)
@@ -104,7 +104,6 @@ module.exports = {
 
         docs: {
             description: "disallow characters which are made with multiple code points in character class syntax",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-misleading-character-class"
         },
index 15eb20bed2a49ecd826313169b0b203d04b0fcea..ed37a90b1c6a40840ce6505e59bc1d4a45f93a39 100644 (file)
@@ -58,7 +58,7 @@ function normalizeOptions(options = {}) {
 
 /**
  * Checks whether any group which includes both given operator exists or not.
- * @param {Array.<string[]>} groups A list of groups to check.
+ * @param {Array<string[]>} groups A list of groups to check.
  * @param {string} left An operator.
  * @param {string} right Another operator.
  * @returns {boolean} `true` if such group existed.
@@ -88,7 +88,6 @@ module.exports = {
 
         docs: {
             description: "disallow mixed binary operators",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-mixed-operators"
         },
@@ -117,7 +116,7 @@ module.exports = {
         ],
 
         messages: {
-            unexpectedMixedOperator: "Unexpected mix of '{{leftOperator}}' and '{{rightOperator}}'."
+            unexpectedMixedOperator: "Unexpected mix of '{{leftOperator}}' and '{{rightOperator}}'. Use parentheses to clarify the intended order of operations."
         }
     },
 
index a02de9104bdd88670fa6091097b36e0c1e132537..f7c2d11ee4f74ed9722442bf0a82ccff08741832 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Rule to enforce grouped require statements for Node.JS
  * @author Raphael Pigulla
+ * @deprecated in ESLint v7.0.0
  */
 
 "use strict";
@@ -19,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "disallow `require` calls to be mixed with regular variable declarations",
-            category: "Node.js and CommonJS",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-mixed-requires"
         },
index 287cbda03daf5a6795aedc74c74e883512765ce8..ac73cddda3b4c32bbeefe8c2cad1b2d72a747122 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow mixed spaces and tabs for indentation",
-            category: "Stylistic Issues",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-mixed-spaces-and-tabs"
         },
index d2606a1502a2499bc085a6379fc85afd00c6bfab..8d7bd32c7dd99310e2e7ef085c062f403bee1d3b 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "disallow use of chained assignment expressions",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-multi-assign"
         },
@@ -45,16 +44,21 @@ module.exports = {
         const options = context.options[0] || {
             ignoreNonDeclaration: false
         };
-        const targetParent = options.ignoreNonDeclaration ? ["VariableDeclarator"] : ["AssignmentExpression", "VariableDeclarator"];
+        const selectors = [
+            "VariableDeclarator > AssignmentExpression.init",
+            "PropertyDefinition > AssignmentExpression.value"
+        ];
+
+        if (!options.ignoreNonDeclaration) {
+            selectors.push("AssignmentExpression > AssignmentExpression.right");
+        }
 
         return {
-            AssignmentExpression(node) {
-                if (targetParent.indexOf(node.parent.type) !== -1) {
-                    context.report({
-                        node,
-                        messageId: "unexpectedChain"
-                    });
-                }
+            [selectors](node) {
+                context.report({
+                    node,
+                    messageId: "unexpectedChain"
+                });
             }
         };
 
index d43ed7363379453975657091add4b5dca54eab0a..0134dd279b587c6ea4142554fa8e2cb98922f577 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "disallow multiple spaces",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-multi-spaces"
         },
index 7cf1ae367942fe89ffc21be29abca842825fb915..848f8d405a869b9a4c5999344cfcdd6e6837dd89 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow multiline strings",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-multi-str"
         },
index 9cccef3088a56e1a2430913a684c11ddd49d3cda..33ac76f60378941e2d0da22fd9fd929d232322c9 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow multiple empty lines",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-multiple-empty-lines"
         },
index 833e3b7ce409b3ffe00005d03079a002648f9cec..80ba0948cbce650a110bc6f57f9f9af7e2383a76 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "disallow assignments to native objects or read-only global variables",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-native-reassign"
         },
index 8a9eba881df04c667ffde3f4c80486f2dbbbf8b0..b5cbadca50f908dcd0541ae71c13b9d557e55071 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow negated conditions",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-negated-condition"
         },
index 1229cedd1190dca070a478df1b585c3ea2dd94c0..0f9c84be6c918da38de5c1d0402386f99c6d13f8 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "disallow negating the left operand in `in` expressions",
-            category: "Possible Errors",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-negated-in-lhs"
         },
index 383bb238887d2aeef9c961db29ca1c49d884e768..2d3359d38f144955199712d079bece9c319efdf1 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow nested ternary expressions",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-nested-ternary"
         },
index 9af4e31cabf6d44fe55905bcaa1a7ac129270095..ddf61024dac54b529c05b0fcbfeedda49667e50f 100644 (file)
@@ -5,6 +5,18 @@
 
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("./utils/ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const callMethods = new Set(["apply", "bind", "call"]);
+
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
@@ -15,7 +27,6 @@ module.exports = {
 
         docs: {
             description: "disallow `new` operators with the `Function` object",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-new-func"
         },
@@ -38,14 +49,30 @@ module.exports = {
                     variable.references.forEach(ref => {
                         const node = ref.identifier;
                         const { parent } = node;
+                        let evalNode;
+
+                        if (parent) {
+                            if (node === parent.callee && (
+                                parent.type === "NewExpression" ||
+                                parent.type === "CallExpression"
+                            )) {
+                                evalNode = parent;
+                            } else if (
+                                parent.type === "MemberExpression" &&
+                                node === parent.object &&
+                                callMethods.has(astUtils.getStaticPropertyName(parent))
+                            ) {
+                                const maybeCallee = parent.parent.type === "ChainExpression" ? parent.parent : parent;
+
+                                if (maybeCallee.parent.type === "CallExpression" && maybeCallee.parent.callee === maybeCallee) {
+                                    evalNode = maybeCallee.parent;
+                                }
+                            }
+                        }
 
-                        if (
-                            parent &&
-                            (parent.type === "NewExpression" || parent.type === "CallExpression") &&
-                            node === parent.callee
-                        ) {
+                        if (evalNode) {
                             context.report({
-                                node: parent,
+                                node: evalNode,
                                 messageId: "noFunctionConstructor"
                             });
                         }
index e9f915db5eaa9173dfe8a094da8dbdcd7b519252..17dfd344476fe9aedab9cd65981096d17c02f3b1 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow `Object` constructors",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-new-object"
         },
index 063f783e70cc3c7d0107f5d5ab2a952b777cd691..7973f8f2a27beb7d72441c05890a78fbce58e94c 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Rule to disallow use of new operator with the `require` function
  * @author Wil Moore III
+ * @deprecated in ESLint v7.0.0
  */
 
 "use strict";
@@ -19,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "disallow `new` operators with calls to `require`",
-            category: "Node.js and CommonJS",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-new-require"
         },
index aeb509c0d9c1424396f4a5a684fd62fb14cb75da..391527df90cd694f381950d9079255fa9192e5bd 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow `new` operators with the `Symbol` object",
-            category: "ECMAScript 6",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-new-symbol"
         },
index d276c48d203fabada46a66e6faa30759de371952..b697d8d7951f7cb16e4e3593886812688b04865d 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow `new` operators with the `String`, `Number`, and `Boolean` objects",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-new-wrappers"
         },
index aa8a4e2687660979eb907c13351655260cd62200..1b37f077d5c4b697393a61ac55524ed799ee5121 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "disallow `new` operators outside of assignments or comparisons",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-new"
         },
index a4b46d9591f5ed5ffb1cef67df228dad4d167ef5..da61f61d02f2a69116eae48f525bbf3c424887ae 100644 (file)
@@ -30,12 +30,12 @@ module.exports = {
 
         docs: {
             description: "disallow `\\8` and `\\9` escape sequences in string literals",
-            category: "Best Practices",
-            recommended: false,
-            url: "https://eslint.org/docs/rules/no-nonoctal-decimal-escape",
-            suggestion: true
+            recommended: true,
+            url: "https://eslint.org/docs/rules/no-nonoctal-decimal-escape"
         },
 
+        hasSuggestions: true,
+
         schema: [],
 
         messages: {
index 6eb200c9b879e626fda1d3d9edf14d1b62dea660..d62c1f0b4be852ccbeba27d4c8cc0533bc67fe4d 100644 (file)
@@ -43,7 +43,6 @@ module.exports = {
 
         docs: {
             description: "disallow calling global object properties as functions",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-obj-calls"
         },
index 5b4c7b2ebb45c883e77b19362aa62ed4c1c2866a..4513a83861d259491d96d7ff0c079cb678c21b69 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow octal escape sequences in string literals",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-octal-escape"
         },
index e9940befafa889a4bd9c8bc40e4beeff0278b748..5ee6895f623cbccd0c11165874ccd3e4af5546a8 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow octal literals",
-            category: "Best Practices",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-octal"
         },
index 6874af44f389b731f5086a25ac67303d4b79e1b4..b758b9d97fccbec91dfc2e0fa59399f08af8c2e8 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "disallow reassigning `function` parameters",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-param-reassign"
         },
index fc1f894f878988f0f995aac46034e3d071ac2e07..184c9182b4d536f82d39093ed84f0e51306e5881 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Disallow string concatenation when using __dirname and __filename
  * @author Nicholas C. Zakas
+ * @deprecated in ESLint v7.0.0
  */
 "use strict";
 
@@ -18,7 +19,6 @@ module.exports = {
 
         docs: {
             description: "disallow string concatenation with `__dirname` and `__filename`",
-            category: "Node.js and CommonJS",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-path-concat"
         },
index 84d6c3e1f91d519ef13599b197edcc4a9aa5bd1f..d7b6c730562670deae186004537f2e0f9e86cc54 100644 (file)
@@ -51,7 +51,6 @@ module.exports = {
 
         docs: {
             description: "disallow the unary operators `++` and `--`",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-plusplus"
         },
index 49d1734906e81f567c8edb98e99da9d6af22b637..c61b5572314a42866d6ebd7be38d91e95b4b6a62 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Disallow the use of process.env()
  * @author Vignesh Anand
+ * @deprecated in ESLint v7.0.0
  */
 "use strict";
 
@@ -18,7 +19,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of `process.env`",
-            category: "Node.js and CommonJS",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-process-env"
         },
index 77c9cfd7cbd3429c6b0171fc983d380967be06b1..73310a9e4e6364379cd65b186a3fb3c20be74495 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Disallow the use of process.exit()
  * @author Nicholas C. Zakas
+ * @deprecated in ESLint v7.0.0
  */
 "use strict";
 
@@ -18,7 +19,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of `process.exit()`",
-            category: "Node.js and CommonJS",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-process-exit"
         },
index 32ee6e15124b8ec666740a2b9f05dd2c9c25898d..42652416f8fac461f61efb0a5115ed470d645efd 100644 (file)
@@ -69,7 +69,6 @@ module.exports = {
 
         docs: {
             description: "disallow returning values from Promise executor functions",
-            category: "Possible Errors",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-promise-executor-return"
         },
index 82ce02fa4e3b9eebe3f400d2f85f132ea01ed569..0c2490f7b421e0a4d8b21e238c060eaff8b38125 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of the `__proto__` property",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-proto"
         },
index c5e4d49259b84adbc41ecd9412d9f2eb7016714b..1f837b960403d8800a1485edbc33b102f4dbc619 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "disallow calling some `Object.prototype` methods directly on objects",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-prototype-builtins"
         },
index 6ddb21c9e15d9d2e30fdc51ff5d92eeac054f7e1..3de4397b1b0e15fdbd4290340d92f62fe8795dc0 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow variable redeclaration",
-            category: "Best Practices",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-redeclare"
         },
@@ -162,6 +161,8 @@ module.exports = {
             FunctionExpression: checkForBlock,
             ArrowFunctionExpression: checkForBlock,
 
+            StaticBlock: checkForBlock,
+
             BlockStatement: checkForBlock,
             ForStatement: checkForBlock,
             ForInStatement: checkForBlock,
index e6d4c9efba75febf354d4e4dc498f5418b5bf76e..1d6b121ba80e759565e2aa6bdd36dd1982db260b 100644 (file)
@@ -39,7 +39,6 @@ module.exports = {
 
         docs: {
             description: "disallow multiple spaces in regular expressions",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-regex-spaces"
         },
index f0df0ffaedb64988c0c516483fad73638ee312b7..f568fdc6850245e108b67b28918ec97dc35e1632 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow specified names in exports",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-restricted-exports"
         },
index 2c932a7307c0b251a96e22c17cff781fca0ac319..efbcd755b1f607aa7a234a8f3271aea3c00bf6f4 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow specified global variables",
-            category: "Variables",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-restricted-globals"
         },
@@ -43,7 +42,7 @@ module.exports = {
 
         messages: {
             defaultMessage: "Unexpected use of '{{name}}'.",
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             customMessage: "Unexpected use of '{{name}}'. {{customMessage}}"
         }
     },
index 414164d29f79cc178b5744a377d0f40cfc379687..eda63407ff2d48626ec812d955a0329d4ebc1ab3 100644 (file)
@@ -79,26 +79,25 @@ module.exports = {
 
         docs: {
             description: "disallow specified modules when loaded by `import`",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-restricted-imports"
         },
 
         messages: {
             path: "'{{importSource}}' import is restricted from being used.",
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             pathWithCustomMessage: "'{{importSource}}' import is restricted from being used. {{customMessage}}",
 
             patterns: "'{{importSource}}' import is restricted from being used by a pattern.",
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             patternWithCustomMessage: "'{{importSource}}' import is restricted from being used by a pattern. {{customMessage}}",
 
             everything: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted.",
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             everythingWithCustomMessage: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted. {{customMessage}}",
 
             importName: "'{{importName}}' import from '{{importSource}}' is restricted.",
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             importNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted. {{customMessage}}"
         },
 
@@ -147,7 +146,7 @@ module.exports = {
             ? [{ matcher: ignore().add(restrictedPatterns) }]
             : restrictedPatterns.map(({ group, message }) => ({ matcher: ignore().add(group), customMessage: message }));
 
-        // if no imports are restricted we don"t need to check
+        // if no imports are restricted we don't need to check
         if (Object.keys(restrictedPaths).length === 0 && restrictedPatternGroups.length === 0) {
             return {};
         }
index d0b8a78a507fa80930e83dcf6832db9d461c84a5..66e6fe49e3183e63245a4c7dd02b9a620585e901 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Restrict usage of specified node modules.
  * @author Christian Schulz
+ * @deprecated in ESLint v7.0.0
  */
 "use strict";
 
@@ -48,7 +49,6 @@ module.exports = {
 
         docs: {
             description: "disallow specified modules when loaded by `require`",
-            category: "Node.js and CommonJS",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-restricted-modules"
         },
@@ -73,7 +73,7 @@ module.exports = {
 
         messages: {
             defaultMessage: "'{{name}}' module is restricted from being used.",
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             customMessage: "'{{name}}' module is restricted from being used. {{customMessage}}",
             patternMessage: "'{{name}}' module is restricted from being used by a pattern."
         }
@@ -97,7 +97,7 @@ module.exports = {
             return memo;
         }, {});
 
-        // if no imports are restricted we don"t need to check
+        // if no imports are restricted we don't need to check
         if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
             return {};
         }
index 7ab83995a3ebbfc0bcb26fbc1cdbb877d7bf75ba..3671d88eb600b9d7e0bed9d29847c0d0638caa29 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "disallow certain properties on certain objects",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-restricted-properties"
         },
@@ -64,9 +63,9 @@ module.exports = {
         },
 
         messages: {
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             restrictedObjectProperty: "'{{objectName}}.{{propertyName}}' is restricted from being used.{{message}}",
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             restrictedProperty: "'{{propertyName}}' is restricted from being used.{{message}}"
         }
     },
index 9572603a82485aa90e99267adbf20a3342667096..0ff6b91bc69448612e83a7feeec39990d8193730 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow specified syntax",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-restricted-syntax"
         },
@@ -42,7 +41,7 @@ module.exports = {
         },
 
         messages: {
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             restrictedSyntax: "{{message}}"
         }
     },
index 4b57d42eb9952bb828fcaaa364df7c12a262decc..ecb789ea269926d4f75529646c75fa492026996d 100644 (file)
@@ -26,7 +26,6 @@ module.exports = {
 
         docs: {
             description: "disallow assignment operators in `return` statements",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-return-assign"
         },
index d1d89826856dc48d93a6beed10ae5ef40df8d1b6..7ec808f50a3280ab17d87bc0a9ce900ae5af8466 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary `return await`",
-            category: "Best Practices",
 
             recommended: false,
 
index 0c82052440372f7c8890dd3ae891929ff1a2df08..12451ad9a9eb3f4f39ac4e8306424d6114f91696 100644 (file)
@@ -2,8 +2,7 @@
  * @fileoverview Rule to flag when using javascript: urls
  * @author Ilya Volodin
  */
-/* jshint scripturl: true */
-/* eslint no-script-url: 0 */
+/* eslint no-script-url: 0 -- Code is checking to report such URLs */
 
 "use strict";
 
@@ -19,7 +18,6 @@ module.exports = {
 
         docs: {
             description: "disallow `javascript:` urls",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-script-url"
         },
index 705be324cf0944967eea6d0b16f72b3468a65d57..813771e700a2435fcb3515607f824093fd4ef23f 100644 (file)
@@ -130,7 +130,6 @@ module.exports = {
 
         docs: {
             description: "disallow assignments where both sides are exactly the same",
-            category: "Best Practices",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-self-assign"
         },
index 79b6ac7ea0f28d3176b77ba7a0f5799c79951dc5..ee77ff08b202e84239da6c5d49eba62f794687d4 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "disallow comparisons where both sides are exactly the same",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-self-compare"
         },
index fe516975fbc942c37bbb51620bfb1ebf377d232b..b8941256e6f1a5a7c3f95b6a937e47af220f9046 100644 (file)
@@ -29,7 +29,6 @@ module.exports = {
 
         docs: {
             description: "disallow comma operators",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-sequences"
         },
index 9c79240dda1fa8f2cf962f79be6737cbf2103a44..67114ade8215311970d50b907c29139e959fda4b 100644 (file)
@@ -93,6 +93,7 @@ function isSetter(node, scope) {
     const parent = node.parent;
 
     if (
+        (parent.type === "Property" || parent.type === "MethodDefinition") &&
         parent.kind === "set" &&
         parent.value === node
     ) {
@@ -141,7 +142,6 @@ module.exports = {
 
         docs: {
             description: "disallow returning values from setters",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-setter-return"
         },
index 9647e9a1bcdd9eca5908c8c6d0cce75546143c99..7d4174a104e3a99be2bcedc78ab9e068ccbf5b16 100644 (file)
@@ -27,7 +27,6 @@ module.exports = {
 
         docs: {
             description: "disallow identifiers from shadowing restricted names",
-            category: "Variables",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-shadow-restricted-names"
         },
index a0b1db50c0b5f24c3279a476ba6295aaf7053c8f..4ec357620b6436f46533cdee788e3ecc54ed97af 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow variable declarations from shadowing variables declared in the outer scope",
-            category: "Variables",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-shadow"
         },
@@ -59,7 +58,7 @@ module.exports = {
 
         /**
          * Check if variable name is allowed.
-         * @param  {ASTNode} variable The variable to check.
+         * @param {ASTNode} variable The variable to check.
          * @returns {boolean} Whether or not the variable name is allowed.
          */
         function isAllowed(variable) {
index 961bc681f7eac50bfe816d7d9295982f74a26555..8f51d5446d844b987bbe1739f17d9b52f8b7d826 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "disallow spacing between function identifiers and their applications (deprecated)",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-spaced-func"
         },
index e8407c3faede2d5a1983ea9ff011e3752701d5b7..56ce5dcc871b1ef99f704718ca18a6c28f6b52b9 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow sparse arrays",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-sparse-arrays"
         },
index 06424e0b38f33fb39e993d253b405f0d53707355..ea40df12933b5c994953e484b8db63b4887981ba 100644 (file)
@@ -1,10 +1,9 @@
 /**
  * @fileoverview Rule to check for properties whose identifier ends with the string Sync
  * @author Matt DuVall<http://mattduvall.com/>
+ * @deprecated in ESLint v7.0.0
  */
 
-/* jshint node:true */
-
 "use strict";
 
 //------------------------------------------------------------------------------
@@ -21,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "disallow synchronous methods",
-            category: "Node.js and CommonJS",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-sync"
         },
index ca7be261653c028eafdf62e0b9e3ec397b28e7cc..1f3921a9a70fd620e614c409335b0071ec244301 100644 (file)
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "disallow all tabs",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-tabs"
         },
index 539cd5be5ff9c537470c0946c09d676c6e63a51f..e71480fc0131ab7d51f3254c3d09b8bab8e071ed 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow template literal placeholder syntax in regular strings",
-            category: "Possible Errors",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-template-curly-in-string"
         },
index b3ced86056614c6be7a8f0e85971dc8a69bea752..8b2e10a34a4e5c40b1c538d0eefd90091c636bb5 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow ternary operators",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-ternary"
         },
index 5bfba66fc65c32ced16069fadda06224dfd1f862..9cc85ebbe21064c4fc349b34a193ca04f8a0404b 100644 (file)
@@ -40,7 +40,6 @@ module.exports = {
 
         docs: {
             description: "disallow `this`/`super` before calling `super()` in constructors",
-            category: "ECMAScript 6",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-this-before-super"
         },
index 29fb3718b4896bc436baf08476b277cae0eefca0..311e6d4f079daec7d29d6da6c5afbbc56e114f09 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "disallow throwing literals as exceptions",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-throw-literal"
         },
index 98ae62c896359ce28fcc1b7ac4e015365fe09b98..1930098bea00404a393a14b8936ed3ef2f9864fa 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "disallow trailing whitespace at the end of lines",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-trailing-spaces"
         },
index 5c240fef742b8375eae92be09edff4bf7f67d119..7298d3449417cd7ef8ffe184750ce37aa42d79f4 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "disallow initializing variables to `undefined`",
-            category: "Variables",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-undef-init"
         },
index 6b5140819bbd3010a86b9e2c1548a55ce3c1a71a..ee611f9c866df1085e155511e4d8687b98518b97 100644 (file)
@@ -29,7 +29,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of undeclared variables unless mentioned in `/*global */` comments",
-            category: "Variables",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-undef"
         },
index a075d903e21e98dde9e02bfcfdcad4d8982c936e..ad302255420acbb5124332197087b1826f2dd47f 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of `undefined` as an identifier",
-            category: "Variables",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-undefined"
         },
index 87d2336fa4a40f38f96bd9ea4775cf5a809ccaf6..916b8c01baa1bd60b19028f347bcdee33dedb384 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow dangling underscores in identifiers",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-underscore-dangle"
         },
@@ -253,7 +252,9 @@ module.exports = {
                     node,
                     messageId: "unexpectedUnderscore",
                     data: {
-                        identifier
+                        identifier: node.key.type === "PrivateIdentifier"
+                            ? `#${identifier}`
+                            : identifier
                     }
                 });
             }
@@ -268,6 +269,7 @@ module.exports = {
             VariableDeclarator: checkForDanglingUnderscoreInVariableExpression,
             MemberExpression: checkForDanglingUnderscoreInMemberExpression,
             MethodDefinition: checkForDanglingUnderscoreInMethod,
+            PropertyDefinition: checkForDanglingUnderscoreInMethod,
             Property: checkForDanglingUnderscoreInMethod,
             FunctionExpression: checkForDanglingUnderscoreInFunction,
             ArrowFunctionExpression: checkForDanglingUnderscoreInFunction
index 7af3fe67090dfa23e0136e1597cb8c225cfac35c..4447959ed9a17636d08668fb40df6a107fcfe79c 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "disallow confusing multiline expressions",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-unexpected-multiline"
         },
index 7031a4dd8b83fc290f86a0f944e3d0fdf0aa67ed..ba321d2c6ed33198e1dff8bfbcbb17a7515f77c5 100644 (file)
@@ -162,7 +162,6 @@ module.exports = {
 
         docs: {
             description: "disallow unmodified loop conditions",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-unmodified-loop-condition"
         },
index 06c615f38240502304f9d821ee3ab795a88431c0..e12240d03e0d767c7ebf7b2547ac1f541770928a 100644 (file)
@@ -29,7 +29,6 @@ module.exports = {
 
         docs: {
             description: "disallow ternary operators when simpler alternatives exist",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-unneeded-ternary"
         },
index 868a6ff98f81146b9ec56fdfe5bfa5604cdd8f06..5cbfac46d318f18d9016036be1d2b57d63a328ec 100644 (file)
@@ -59,7 +59,6 @@ module.exports = {
 
         docs: {
             description: "disallow loops with a body that allows only one iteration",
-            category: "Possible Errors",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-unreachable-loop"
         },
index 415631a6f7d726e8b9eaebce05359bcfe66e8e6d..ce17a5966a7eccae48cb3ac0976fadebf3c20936 100644 (file)
@@ -8,6 +8,12 @@
 // Helpers
 //------------------------------------------------------------------------------
 
+/**
+ * @typedef {Object} ConstructorInfo
+ * @property {ConstructorInfo | null} upper Info about the constructor that encloses this constructor.
+ * @property {boolean} hasSuperCall The flag about having `super()` expressions.
+ */
+
 /**
  * Checks whether or not a given variable declarator has the initializer.
  * @param {ASTNode} node A VariableDeclarator node to check.
@@ -105,7 +111,6 @@ module.exports = {
 
         docs: {
             description: "disallow unreachable code after `return`, `throw`, `continue`, and `break` statements",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-unreachable"
         },
@@ -120,6 +125,10 @@ module.exports = {
     create(context) {
         let currentCodePath = null;
 
+        /** @type {ConstructorInfo | null} */
+        let constructorInfo = null;
+
+        /** @type {ConsecutiveRange} */
         const range = new ConsecutiveRange(context.getSourceCode());
 
         /**
@@ -130,7 +139,7 @@ module.exports = {
         function reportIfUnreachable(node) {
             let nextNode = null;
 
-            if (node && currentCodePath.currentSegments.every(isUnreachable)) {
+            if (node && (node.type === "PropertyDefinition" || currentCodePath.currentSegments.every(isUnreachable))) {
 
                 // Store this statement to distinguish consecutive statements.
                 if (range.isEmpty) {
@@ -212,6 +221,42 @@ module.exports = {
 
             "Program:exit"() {
                 reportIfUnreachable();
+            },
+
+            /*
+             * Instance fields defined in a subclass are never created if the constructor of the subclass
+             * doesn't call `super()`, so their definitions are unreachable code.
+             */
+            "MethodDefinition[kind='constructor']"() {
+                constructorInfo = {
+                    upper: constructorInfo,
+                    hasSuperCall: false
+                };
+            },
+            "MethodDefinition[kind='constructor']:exit"(node) {
+                const { hasSuperCall } = constructorInfo;
+
+                constructorInfo = constructorInfo.upper;
+
+                // skip typescript constructors without the body
+                if (!node.value.body) {
+                    return;
+                }
+
+                const classDefinition = node.parent.parent;
+
+                if (classDefinition.superClass && !hasSuperCall) {
+                    for (const element of classDefinition.body.body) {
+                        if (element.type === "PropertyDefinition" && !element.static) {
+                            reportIfUnreachable(element);
+                        }
+                    }
+                }
+            },
+            "CallExpression > Super.callee"() {
+                if (constructorInfo) {
+                    constructorInfo.hasSuperCall = true;
+                }
             }
         };
     }
index 11bf06e872a813be0166e9fb0df52c30298313fb..4bb7f7fec4f2c3de1cd4b1f7cfce04446fcc2911 100644 (file)
@@ -24,7 +24,6 @@ module.exports = {
 
         docs: {
             description: "disallow control flow statements in `finally` blocks",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-unsafe-finally"
         },
index a9c2ee74f2c8281bb479e0316f5ea771af31b646..c681986941ae0e16567f34f89a62122aeed1704e 100644 (file)
@@ -52,12 +52,12 @@ module.exports = {
 
         docs: {
             description: "disallow negating the left operand of relational operators",
-            category: "Possible Errors",
             recommended: true,
-            url: "https://eslint.org/docs/rules/no-unsafe-negation",
-            suggestion: true
+            url: "https://eslint.org/docs/rules/no-unsafe-negation"
         },
 
+        hasSuggestions: true,
+
         schema: [
             {
                 type: "object",
index 2eafc1ad8f108b5ebae2f0f8fb2b1294447f058b..cc15c9988138f66c8084bc94320954c38c7034de 100644 (file)
@@ -24,8 +24,7 @@ module.exports = {
 
         docs: {
             description: "disallow use of optional chaining in contexts where the `undefined` value is not allowed",
-            category: "Possible Errors",
-            recommended: false,
+            recommended: true,
             url: "https://eslint.org/docs/rules/no-unsafe-optional-chaining"
         },
         schema: [{
index 58c9b33418794b286b9c150f944942b21264a40c..2081a518b30ce06603e268731cb8adae1b416604 100644 (file)
@@ -30,7 +30,6 @@ module.exports = {
 
         docs: {
             description: "disallow unused expressions",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-unused-expressions"
         },
@@ -72,8 +71,8 @@ module.exports = {
             allowTaggedTemplates = config.allowTaggedTemplates || false,
             enforceForJSX = config.enforceForJSX || false;
 
-        // eslint-disable-next-line jsdoc/require-description
         /**
+         * Has AST suggesting a directive.
          * @param {ASTNode} node any node
          * @returns {boolean} whether the given node structurally represents a directive
          */
@@ -82,8 +81,8 @@ module.exports = {
                 node.expression.type === "Literal" && typeof node.expression.value === "string";
         }
 
-        // eslint-disable-next-line jsdoc/require-description
         /**
+         * Gets the leading sequence of members in a list that pass the predicate.
          * @param {Function} predicate ([a] -> Boolean) the function used to make the determination
          * @param {a[]} list the input list
          * @returns {a[]} the leading sequence of members in the given list that pass the given predicate
@@ -97,8 +96,8 @@ module.exports = {
             return list.slice();
         }
 
-        // eslint-disable-next-line jsdoc/require-description
         /**
+         * Gets leading directives nodes in a Node body.
          * @param {ASTNode} node a Program or BlockStatement node
          * @returns {ASTNode[]} the leading sequence of directive nodes in the given node's body
          */
@@ -106,8 +105,8 @@ module.exports = {
             return takeWhile(looksLikeDirective, node.body);
         }
 
-        // eslint-disable-next-line jsdoc/require-description
         /**
+         * Detect if a Node is a directive.
          * @param {ASTNode} node any node
          * @param {ASTNode[]} ancestors the given node's ancestors
          * @returns {boolean} whether the given node is considered a directive in its current position
@@ -116,6 +115,12 @@ module.exports = {
             const parent = ancestors[ancestors.length - 1],
                 grandparent = ancestors[ancestors.length - 2];
 
+            /**
+             * https://tc39.es/ecma262/#directive-prologue
+             *
+             * Only `FunctionBody`, `ScriptBody` and `ModuleBody` can have directive prologue.
+             * Class static blocks do not have directive prologue.
+             */
             return (parent.type === "Program" || parent.type === "BlockStatement" &&
                     (/Function/u.test(grandparent.type))) &&
                     directives(parent).indexOf(node) >= 0;
index b33fcb786790c44f5fb9c8e017f9c06c8baafdf3..f0b09614e09ce40863607e2178143a231f7748e0 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow unused labels",
-            category: "Best Practices",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-unused-labels"
         },
diff --git a/eslint/lib/rules/no-unused-private-class-members.js b/eslint/lib/rules/no-unused-private-class-members.js
new file mode 100644 (file)
index 0000000..74cf6ab
--- /dev/null
@@ -0,0 +1,194 @@
+/**
+ * @fileoverview Rule to flag declared but unused private class members
+ * @author Tim van der Lippe
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        type: "problem",
+
+        docs: {
+            description: "disallow unused private class members",
+            recommended: false,
+            url: "https://eslint.org/docs/rules/no-unused-private-class-members"
+        },
+
+        schema: [],
+
+        messages: {
+            unusedPrivateClassMember: "'{{classMemberName}}' is defined but never used."
+        }
+    },
+
+    create(context) {
+        const trackedClasses = [];
+
+        /**
+         * Check whether the current node is in a write only assignment.
+         * @param {ASTNode} privateIdentifierNode Node referring to a private identifier
+         * @returns {boolean} Whether the node is in a write only assignment
+         * @private
+         */
+        function isWriteOnlyAssignment(privateIdentifierNode) {
+            const parentStatement = privateIdentifierNode.parent.parent;
+            const isAssignmentExpression = parentStatement.type === "AssignmentExpression";
+
+            if (!isAssignmentExpression &&
+                parentStatement.type !== "ForInStatement" &&
+                parentStatement.type !== "ForOfStatement" &&
+                parentStatement.type !== "AssignmentPattern") {
+                return false;
+            }
+
+            // It is a write-only usage, since we still allow usages on the right for reads
+            if (parentStatement.left !== privateIdentifierNode.parent) {
+                return false;
+            }
+
+            // For any other operator (such as '+=') we still consider it a read operation
+            if (isAssignmentExpression && parentStatement.operator !== "=") {
+
+                /*
+                 * However, if the read operation is "discarded" in an empty statement, then
+                 * we consider it write only.
+                 */
+                return parentStatement.parent.type === "ExpressionStatement";
+            }
+
+            return true;
+        }
+
+        //--------------------------------------------------------------------------
+        // Public
+        //--------------------------------------------------------------------------
+
+        return {
+
+            // Collect all declared members up front and assume they are all unused
+            ClassBody(classBodyNode) {
+                const privateMembers = new Map();
+
+                trackedClasses.unshift(privateMembers);
+                for (const bodyMember of classBodyNode.body) {
+                    if (bodyMember.type === "PropertyDefinition" || bodyMember.type === "MethodDefinition") {
+                        if (bodyMember.key.type === "PrivateIdentifier") {
+                            privateMembers.set(bodyMember.key.name, {
+                                declaredNode: bodyMember,
+                                isAccessor: bodyMember.type === "MethodDefinition" &&
+                                    (bodyMember.kind === "set" || bodyMember.kind === "get")
+                            });
+                        }
+                    }
+                }
+            },
+
+            /*
+             * Process all usages of the private identifier and remove a member from
+             * `declaredAndUnusedPrivateMembers` if we deem it used.
+             */
+            PrivateIdentifier(privateIdentifierNode) {
+                const classBody = trackedClasses.find(classProperties => classProperties.has(privateIdentifierNode.name));
+
+                // Can't happen, as it is a parser to have a missing class body, but let's code defensively here.
+                if (!classBody) {
+                    return;
+                }
+
+                // In case any other usage was already detected, we can short circuit the logic here.
+                const memberDefinition = classBody.get(privateIdentifierNode.name);
+
+                if (memberDefinition.isUsed) {
+                    return;
+                }
+
+                // The definition of the class member itself
+                if (privateIdentifierNode.parent.type === "PropertyDefinition" ||
+                    privateIdentifierNode.parent.type === "MethodDefinition") {
+                    return;
+                }
+
+                /*
+                 * Any usage of an accessor is considered a read, as the getter/setter can have
+                 * side-effects in its definition.
+                 */
+                if (memberDefinition.isAccessor) {
+                    memberDefinition.isUsed = true;
+                    return;
+                }
+
+                // Any assignments to this member, except for assignments that also read
+                if (isWriteOnlyAssignment(privateIdentifierNode)) {
+                    return;
+                }
+
+                const wrappingExpressionType = privateIdentifierNode.parent.parent.type;
+                const parentOfWrappingExpressionType = privateIdentifierNode.parent.parent.parent.type;
+
+                // A statement which only increments (`this.#x++;`)
+                if (wrappingExpressionType === "UpdateExpression" &&
+                    parentOfWrappingExpressionType === "ExpressionStatement") {
+                    return;
+                }
+
+                /*
+                 * ({ x: this.#usedInDestructuring } = bar);
+                 *
+                 * But should treat the following as a read:
+                 * ({ [this.#x]: a } = foo);
+                 */
+                if (wrappingExpressionType === "Property" &&
+                    parentOfWrappingExpressionType === "ObjectPattern" &&
+                    privateIdentifierNode.parent.parent.value === privateIdentifierNode.parent) {
+                    return;
+                }
+
+                // [...this.#unusedInRestPattern] = bar;
+                if (wrappingExpressionType === "RestElement") {
+                    return;
+                }
+
+                // [this.#unusedInAssignmentPattern] = bar;
+                if (wrappingExpressionType === "ArrayPattern") {
+                    return;
+                }
+
+                /*
+                 * We can't delete the memberDefinition, as we need to keep track of which member we are marking as used.
+                 * In the case of nested classes, we only mark the first member we encounter as used. If you were to delete
+                 * the member, then any subsequent usage could incorrectly mark the member of an encapsulating parent class
+                 * as used, which is incorrect.
+                 */
+                memberDefinition.isUsed = true;
+            },
+
+            /*
+             * Post-process the class members and report any remaining members.
+             * Since private members can only be accessed in the current class context,
+             * we can safely assume that all usages are within the current class body.
+             */
+            "ClassBody:exit"() {
+                const unusedPrivateMembers = trackedClasses.shift();
+
+                for (const [classMemberName, { declaredNode, isUsed }] of unusedPrivateMembers.entries()) {
+                    if (isUsed) {
+                        continue;
+                    }
+                    context.report({
+                        node: declaredNode,
+                        loc: declaredNode.key.loc,
+                        messageId: "unusedPrivateClassMember",
+                        data: {
+                            classMemberName: `#${classMemberName}`
+                        }
+                    });
+                }
+            }
+        };
+    }
+};
index f04818f8e9d5979f2879af8954dfd6207df254ab..847e21baf5f46201a2d4b5643a5e105a4f6e3537 100644 (file)
@@ -33,7 +33,6 @@ module.exports = {
 
         docs: {
             description: "disallow unused variables",
-            category: "Variables",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-unused-vars"
         },
@@ -295,6 +294,31 @@ module.exports = {
             );
         }
 
+        /**
+         * Checks whether a given node is unused expression or not.
+         * @param {ASTNode} node The node itself
+         * @returns {boolean} The node is an unused expression.
+         * @private
+         */
+        function isUnusedExpression(node) {
+            const parent = node.parent;
+
+            if (parent.type === "ExpressionStatement") {
+                return true;
+            }
+
+            if (parent.type === "SequenceExpression") {
+                const isLastExpression = parent.expressions[parent.expressions.length - 1] === node;
+
+                if (!isLastExpression) {
+                    return true;
+                }
+                return isUnusedExpression(parent);
+            }
+
+            return false;
+        }
+
         /**
          * If a given reference is left-hand side of an assignment, this gets
          * the right-hand side node of the assignment.
@@ -313,7 +337,6 @@ module.exports = {
         function getRhsNode(ref, prevRhsNode) {
             const id = ref.identifier;
             const parent = id.parent;
-            const grandparent = parent.parent;
             const refScope = ref.from.variableScope;
             const varScope = ref.resolved.scope.variableScope;
             const canBeUsedLater = refScope !== varScope || astUtils.isInLoop(id);
@@ -327,7 +350,7 @@ module.exports = {
             }
 
             if (parent.type === "AssignmentExpression" &&
-                grandparent.type === "ExpressionStatement" &&
+                isUnusedExpression(parent) &&
                 id === parent.left &&
                 !canBeUsedLater
             ) {
@@ -410,31 +433,6 @@ module.exports = {
             );
         }
 
-        /**
-         * Checks whether a given node is unused expression or not.
-         * @param {ASTNode} node The node itself
-         * @returns {boolean} The node is an unused expression.
-         * @private
-         */
-        function isUnusedExpression(node) {
-            const parent = node.parent;
-
-            if (parent.type === "ExpressionStatement") {
-                return true;
-            }
-
-            if (parent.type === "SequenceExpression") {
-                const isLastExpression = parent.expressions[parent.expressions.length - 1] === node;
-
-                if (!isLastExpression) {
-                    return true;
-                }
-                return isUnusedExpression(parent);
-            }
-
-            return false;
-        }
-
         /**
          * Checks whether a given reference is a read to update itself or not.
          * @param {eslint-scope.Reference} ref A reference to check.
index c7300567ede0b7856495d83068b40deec22ad3c2..7f904f46bd540466f14880263b2dbf17c92cc0be 100644 (file)
@@ -34,52 +34,113 @@ function parseOptions(options) {
 }
 
 /**
- * Checks whether or not a given variable is a function declaration.
- * @param {eslint-scope.Variable} variable A variable to check.
- * @returns {boolean} `true` if the variable is a function declaration.
+ * Checks whether or not a given location is inside of the range of a given node.
+ * @param {ASTNode} node An node to check.
+ * @param {number} location A location to check.
+ * @returns {boolean} `true` if the location is inside of the range of the node.
  */
-function isFunction(variable) {
-    return variable.defs[0].type === "FunctionName";
+function isInRange(node, location) {
+    return node && node.range[0] <= location && location <= node.range[1];
 }
 
 /**
- * Checks whether or not a given variable is a class declaration in an upper function scope.
- * @param {eslint-scope.Variable} variable A variable to check.
- * @param {eslint-scope.Reference} reference A reference to check.
- * @returns {boolean} `true` if the variable is a class declaration.
+ * Checks whether or not a given location is inside of the range of a class static initializer.
+ * Static initializers are static blocks and initializers of static fields.
+ * @param {ASTNode} node `ClassBody` node to check static initializers.
+ * @param {number} location A location to check.
+ * @returns {boolean} `true` if the location is inside of a class static initializer.
  */
-function isOuterClass(variable, reference) {
-    return (
-        variable.defs[0].type === "ClassName" &&
-        variable.scope.variableScope !== reference.from.variableScope
-    );
+function isInClassStaticInitializerRange(node, location) {
+    return node.body.some(classMember => (
+        (
+            classMember.type === "StaticBlock" &&
+            isInRange(classMember, location)
+        ) ||
+        (
+            classMember.type === "PropertyDefinition" &&
+            classMember.static &&
+            classMember.value &&
+            isInRange(classMember.value, location)
+        )
+    ));
 }
 
 /**
- * Checks whether or not a given variable is a variable declaration in an upper function scope.
- * @param {eslint-scope.Variable} variable A variable to check.
- * @param {eslint-scope.Reference} reference A reference to check.
- * @returns {boolean} `true` if the variable is a variable declaration.
+ * Checks whether a given scope is the scope of a a class static initializer.
+ * Static initializers are static blocks and initializers of static fields.
+ * @param {eslint-scope.Scope} scope A scope to check.
+ * @returns {boolean} `true` if the scope is a class static initializer scope.
  */
-function isOuterVariable(variable, reference) {
-    return (
-        variable.defs[0].type === "Variable" &&
-        variable.scope.variableScope !== reference.from.variableScope
-    );
+function isClassStaticInitializerScope(scope) {
+    if (scope.type === "class-static-block") {
+        return true;
+    }
+
+    if (scope.type === "class-field-initializer") {
+
+        // `scope.block` is PropertyDefinition#value node
+        const propertyDefinition = scope.block.parent;
+
+        return propertyDefinition.static;
+    }
+
+    return false;
 }
 
 /**
- * Checks whether or not a given location is inside of the range of a given node.
- * @param {ASTNode} node An node to check.
- * @param {number} location A location to check.
- * @returns {boolean} `true` if the location is inside of the range of the node.
+ * Checks whether a given reference is evaluated in an execution context
+ * that isn't the one where the variable it refers to is defined.
+ * Execution contexts are:
+ * - top-level
+ * - functions
+ * - class field initializers (implicit functions)
+ * - class static blocks (implicit functions)
+ * Static class field initializers and class static blocks are automatically run during the class definition evaluation,
+ * and therefore we'll consider them as a part of the parent execution context.
+ * Example:
+ *
+ *   const x = 1;
+ *
+ *   x; // returns `false`
+ *   () => x; // returns `true`
+ *
+ *   class C {
+ *       field = x; // returns `true`
+ *       static field = x; // returns `false`
+ *
+ *       method() {
+ *           x; // returns `true`
+ *       }
+ *
+ *       static method() {
+ *           x; // returns `true`
+ *       }
+ *
+ *       static {
+ *           x; // returns `false`
+ *       }
+ *   }
+ * @param {eslint-scope.Reference} reference A reference to check.
+ * @returns {boolean} `true` if the reference is from a separate execution context.
  */
-function isInRange(node, location) {
-    return node && node.range[0] <= location && location <= node.range[1];
+function isFromSeparateExecutionContext(reference) {
+    const variable = reference.resolved;
+    let scope = reference.from;
+
+    // Scope#variableScope represents execution context
+    while (variable.scope.variableScope !== scope.variableScope) {
+        if (isClassStaticInitializerScope(scope.variableScope)) {
+            scope = scope.variableScope.upper;
+        } else {
+            return true;
+        }
+    }
+
+    return false;
 }
 
 /**
- * Checks whether or not a given reference is inside of the initializers of a given variable.
+ * Checks whether or not a given reference is evaluated during the initialization of its variable.
  *
  * This returns `true` in the following cases:
  *
@@ -88,17 +149,45 @@ function isInRange(node, location) {
  *     var {a = a} = obj
  *     for (var a in a) {}
  *     for (var a of a) {}
- * @param {Variable} variable A variable to check.
+ *     var C = class { [C]; };
+ *     var C = class { static foo = C; };
+ *     var C = class { static { foo = C; } };
+ *     class C extends C {}
+ *     class C extends (class { static foo = C; }) {}
+ *     class C { [C]; }
  * @param {Reference} reference A reference to check.
- * @returns {boolean} `true` if the reference is inside of the initializers.
+ * @returns {boolean} `true` if the reference is evaluated during the initialization.
  */
-function isInInitializer(variable, reference) {
-    if (variable.scope !== reference.from) {
+function isEvaluatedDuringInitialization(reference) {
+    if (isFromSeparateExecutionContext(reference)) {
+
+        /*
+         * Even if the reference appears in the initializer, it isn't evaluated during the initialization.
+         * For example, `const x = () => x;` is valid.
+         */
         return false;
     }
 
-    let node = variable.identifiers[0].parent;
     const location = reference.identifier.range[1];
+    const definition = reference.resolved.defs[0];
+
+    if (definition.type === "ClassName") {
+
+        // `ClassDeclaration` or `ClassExpression`
+        const classDefinition = definition.node;
+
+        return (
+            isInRange(classDefinition, location) &&
+
+            /*
+             * Class binding is initialized before running static initializers.
+             * For example, `class C { static foo = C; static { bar = C; } }` is valid.
+             */
+            !isInClassStaticInitializerRange(classDefinition.body, location)
+        );
+    }
+
+    let node = definition.name.parent;
 
     while (node) {
         if (node.type === "VariableDeclarator") {
@@ -135,7 +224,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of variables before they are defined",
-            category: "Variables",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-use-before-define"
         },
@@ -168,65 +256,77 @@ module.exports = {
         const options = parseOptions(context.options[0]);
 
         /**
-         * Determines whether a given use-before-define case should be reported according to the options.
-         * @param {eslint-scope.Variable} variable The variable that gets used before being defined
-         * @param {eslint-scope.Reference} reference The reference to the variable
-         * @returns {boolean} `true` if the usage should be reported
+         * Determines whether a given reference should be checked.
+         *
+         * Returns `false` if the reference is:
+         * - initialization's (e.g., `let a = 1`).
+         * - referring to an undefined variable (i.e., if it's an unresolved reference).
+         * - referring to a variable that is defined, but not in the given source code
+         *   (e.g., global environment variable or `arguments` in functions).
+         * - allowed by options.
+         * @param {eslint-scope.Reference} reference The reference
+         * @returns {boolean} `true` if the reference should be checked
          */
-        function isForbidden(variable, reference) {
-            if (isFunction(variable)) {
-                return options.functions;
+        function shouldCheck(reference) {
+            if (reference.init) {
+                return false;
             }
-            if (isOuterClass(variable, reference)) {
-                return options.classes;
+
+            const variable = reference.resolved;
+
+            if (!variable || variable.defs.length === 0) {
+                return false;
             }
-            if (isOuterVariable(variable, reference)) {
-                return options.variables;
+
+            const definitionType = variable.defs[0].type;
+
+            if (!options.functions && definitionType === "FunctionName") {
+                return false;
             }
+
+            if (
+                (
+                    !options.variables && definitionType === "Variable" ||
+                    !options.classes && definitionType === "ClassName"
+                ) &&
+
+                // don't skip checking the reference if it's in the same execution context, because of TDZ
+                isFromSeparateExecutionContext(reference)
+            ) {
+                return false;
+            }
+
             return true;
         }
 
         /**
-         * Finds and validates all variables in a given scope.
-         * @param {Scope} scope The scope object.
+         * Finds and validates all references in a given scope and its child scopes.
+         * @param {eslint-scope.Scope} scope The scope object.
          * @returns {void}
-         * @private
          */
-        function findVariablesInScope(scope) {
-            scope.references.forEach(reference => {
+        function checkReferencesInScope(scope) {
+            scope.references.filter(shouldCheck).forEach(reference => {
                 const variable = reference.resolved;
+                const definitionIdentifier = variable.defs[0].name;
 
-                /*
-                 * Skips when the reference is:
-                 * - initialization's.
-                 * - referring to an undefined variable.
-                 * - referring to a global environment variable (there're no identifiers).
-                 * - located preceded by the variable (except in initializers).
-                 * - allowed by options.
-                 */
-                if (reference.init ||
-                    !variable ||
-                    variable.identifiers.length === 0 ||
-                    (variable.identifiers[0].range[1] < reference.identifier.range[1] && !isInInitializer(variable, reference)) ||
-                    !isForbidden(variable, reference)
+                if (
+                    reference.identifier.range[1] < definitionIdentifier.range[1] ||
+                    isEvaluatedDuringInitialization(reference)
                 ) {
-                    return;
+                    context.report({
+                        node: reference.identifier,
+                        messageId: "usedBeforeDefined",
+                        data: reference.identifier
+                    });
                 }
-
-                // Reports.
-                context.report({
-                    node: reference.identifier,
-                    messageId: "usedBeforeDefined",
-                    data: reference.identifier
-                });
             });
 
-            scope.childScopes.forEach(findVariablesInScope);
+            scope.childScopes.forEach(checkReferencesInScope);
         }
 
         return {
             Program() {
-                findVariablesInScope(context.getScope());
+                checkReferencesInScope(context.getScope());
             }
         };
     }
index 529c16439e3806966e5210da4af92feee3d70bdc..ae491471282458793e8b7396c822f439d4d16388 100644 (file)
@@ -64,8 +64,7 @@ module.exports = {
 
         docs: {
             description: "disallow useless backreferences in regular expressions",
-            category: "Possible Errors",
-            recommended: false,
+            recommended: true,
             url: "https://eslint.org/docs/rules/no-useless-backreference"
         },
 
index b1382a2fa28034588e48b77b136b81a617275ef7..89350665fe37723d8e745beae1f65714984c5d78 100644 (file)
@@ -55,7 +55,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary calls to `.call()` and `.apply()`",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-useless-call"
         },
index f303c272948489943108aaded11e19fa07918e1a..280ba55336727962ccf51b176fe597f0fc50e5e0 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary `catch` clauses",
-            category: "Best Practices",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-useless-catch"
         },
index a1cacc296129feae1fb38fe6ddc0d33835e61056..a8769214f5478a126a76f8529550ac55cf52cbcd 100644 (file)
 
 const astUtils = require("./utils/ast-utils");
 
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Determines whether the computed key syntax is unnecessarily used for the given node.
+ * In particular, it determines whether removing the square brackets and using the content between them
+ * directly as the key (e.g. ['foo'] -> 'foo') would produce valid syntax and preserve the same behavior.
+ * Valid non-computed keys are only: identifiers, number literals and string literals.
+ * Only literals can preserve the same behavior, with a few exceptions for specific node types:
+ * Property
+ *   - { ["__proto__"]: foo } defines a property named "__proto__"
+ *     { "__proto__": foo } defines object's prototype
+ * PropertyDefinition
+ *   - class C { ["constructor"]; } defines an instance field named "constructor"
+ *     class C { "constructor"; } produces a parsing error
+ *   - class C { static ["constructor"]; } defines a static field named "constructor"
+ *     class C { static "constructor"; } produces a parsing error
+ *   - class C { static ["prototype"]; } produces a runtime error (doesn't break the whole script)
+ *     class C { static "prototype"; } produces a parsing error (breaks the whole script)
+ * MethodDefinition
+ *   - class C { ["constructor"]() {} } defines a prototype method named "constructor"
+ *     class C { "constructor"() {} } defines the constructor
+ *   - class C { static ["prototype"]() {} } produces a runtime error (doesn't break the whole script)
+ *     class C { static "prototype"() {} } produces a parsing error (breaks the whole script)
+ * @param {ASTNode} node The node to check. It can be `Property`, `PropertyDefinition` or `MethodDefinition`.
+ * @throws {Error} (Unreachable.)
+ * @returns {void} `true` if the node has useless computed key.
+ */
+function hasUselessComputedKey(node) {
+    if (!node.computed) {
+        return false;
+    }
+
+    const { key } = node;
+
+    if (key.type !== "Literal") {
+        return false;
+    }
+
+    const { value } = key;
+
+    if (typeof value !== "number" && typeof value !== "string") {
+        return false;
+    }
+
+    switch (node.type) {
+        case "Property":
+            return value !== "__proto__";
+
+        case "PropertyDefinition":
+            if (node.static) {
+                return value !== "constructor" && value !== "prototype";
+            }
+
+            return value !== "constructor";
+
+        case "MethodDefinition":
+            if (node.static) {
+                return value !== "prototype";
+            }
+
+            return value !== "constructor";
+
+        /* istanbul ignore next */
+        default:
+            throw new Error(`Unexpected node type: ${node.type}`);
+    }
+
+}
+
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
@@ -20,7 +91,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary computed property keys in objects and classes",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-useless-computed-key"
         },
@@ -51,22 +121,9 @@ module.exports = {
          * @returns {void}
          */
         function check(node) {
-            if (!node.computed) {
-                return;
-            }
-
-            const key = node.key,
-                nodeType = typeof key.value;
-
-            let allowedKey;
-
-            if (node.type === "MethodDefinition") {
-                allowedKey = node.static ? "prototype" : "constructor";
-            } else {
-                allowedKey = "__proto__";
-            }
+            if (hasUselessComputedKey(node)) {
+                const { key } = node;
 
-            if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== allowedKey) {
                 context.report({
                     node,
                     messageId: "unnecessarilyComputedProperty",
@@ -103,7 +160,8 @@ module.exports = {
 
         return {
             Property: check,
-            MethodDefinition: enforceForClassMembers ? check : noop
+            MethodDefinition: enforceForClassMembers ? check : noop,
+            PropertyDefinition: enforceForClassMembers ? check : noop
         };
     }
 };
index cfc60c8fb51a240b3a9a8984ed71256ecced2deb..a0176a7e9db47314f5d3d0c17cd721e3fd48f526 100644 (file)
@@ -70,7 +70,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary concatenation of literals or template literals",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-useless-concat"
         },
index baabe7ec80f3a73531008f64affddbb39aef0427..13ec6755015adc59bf774090920ac99cf67c0edb 100644 (file)
@@ -138,7 +138,6 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary constructors",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-useless-constructor"
         },
index 512c93a8bc0820ca7b4ab1e87d171be47880b24c..a780a7a84a670623da9d3743a07cffefe53019e4 100644 (file)
@@ -34,16 +34,17 @@ const REGEX_NON_CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("^/.$*+
  * @returns {Object[]} A list of characters, each with info on escaping and whether they're in a character class.
  * @example
  *
- * parseRegExp('a\\b[cd-]')
+ * parseRegExp("a\\b[cd-]");
  *
- * returns:
+ * // returns:
  * [
- *   {text: 'a', index: 0, escaped: false, inCharClass: false, startsCharClass: false, endsCharClass: false},
- *   {text: 'b', index: 2, escaped: true, inCharClass: false, startsCharClass: false, endsCharClass: false},
- *   {text: 'c', index: 4, escaped: false, inCharClass: true, startsCharClass: true, endsCharClass: false},
- *   {text: 'd', index: 5, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false},
- *   {text: '-', index: 6, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false}
- * ]
+ *     { text: "a", index: 0, escaped: false, inCharClass: false, startsCharClass: false, endsCharClass: false },
+ *     { text: "b", index: 2, escaped: true, inCharClass: false, startsCharClass: false, endsCharClass: false },
+ *     { text: "c", index: 4, escaped: false, inCharClass: true, startsCharClass: true, endsCharClass: false },
+ *     { text: "d", index: 5, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false },
+ *     { text: "-", index: 6, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false }
+ * ];
+ *
  */
 function parseRegExp(regExpText) {
     const charList = [];
@@ -83,12 +84,12 @@ module.exports = {
 
         docs: {
             description: "disallow unnecessary escape characters",
-            category: "Best Practices",
             recommended: true,
-            url: "https://eslint.org/docs/rules/no-useless-escape",
-            suggestion: true
+            url: "https://eslint.org/docs/rules/no-useless-escape"
         },
 
+        hasSuggestions: true,
+
         messages: {
             unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
             removeEscape: "Remove the `\\`. This maintains the current functionality.",
index a7cec025da7e701378eff29e05f1f7ec91b4f067..c0d27e600a8aab80c0d8d9f2372080f39193a270 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "disallow renaming import, export, and destructured assignments to the same name",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-useless-rename"
         },
index 111cb21015fb8100dbfd65537524bb816a665b4a..87f05892a943e22a080879a0f2157388c8389505 100644 (file)
@@ -67,7 +67,6 @@ module.exports = {
 
         docs: {
             description: "disallow redundant return statements",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-useless-return"
         },
index f2cb96b1f708e9ed00fc01bc572f9fe14ed725f5..a821c38a36e0edd2dd412771ec41af9c7d7c9973 100644 (file)
@@ -185,7 +185,6 @@ module.exports = {
 
         docs: {
             description: "require `let` or `const` instead of `var`",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-var"
         },
index 99c83785552ab826e81bc6b2845965fc1172a6ae..dba4932385d720d46265cba691cf09125b0c4013 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "disallow `void` operators",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-void"
         },
index e5f702bc7d710e652ab3e1da91bcb545e562622b..23e3da35e146726b4bbd2249aacc7f442ea4cc4c 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "disallow specified warning terms in comments",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-warning-comments"
         },
index 226f873c5f6bd7d50b40fff1286ff794f6b07a2a..9a492997d765806bc55433b005132ddbc18404be 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "disallow whitespace before properties",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-whitespace-before-property"
         },
index d3e52e02f3d15ae7f9645fe8f3422ad549605f0b..219a68094cb5bf78db440130a7f0d2d82746df8d 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "disallow `with` statements",
-            category: "Best Practices",
             recommended: true,
             url: "https://eslint.org/docs/rules/no-with"
         },
index 34e6eeac39de50440bcf43ee67a413dd8d040eb1..7ed541b3802750a4f9f093569dbaeb3945fddc4e 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "enforce the location of single-line statements",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/nonblock-statement-body-position"
         },
index 1fbea00c5d7e403db78c1f25b1c43dc93ec3052f..e16099135826b98c4be1fb14489af3d0d78d4b37 100644 (file)
@@ -150,7 +150,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent line breaks after opening and before closing braces",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/object-curly-newline"
         },
index c0044f5033c00571259d37add4c72ff52d2ea0f9..b18ef57404599fa563fcbf46de97208c72166c31 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing inside braces",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/object-curly-spacing"
         },
index 0c7f800282e57ae3b3d405dd32a09e3696b81442..7cca23ff31ffa98687b559dbde43d441638dc641 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "enforce placing object properties on separate lines",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/object-property-newline"
         },
index 3999ff8b99fb29e35a1278cb55a2099eeb332a12..10bb07bbb0cb0159ab4546df75b339a3ae0b8182 100644 (file)
@@ -28,7 +28,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow method and property shorthand syntax for object literals",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/object-shorthand"
         },
@@ -149,7 +148,6 @@ module.exports = {
          * @param {ASTNode} property Property AST node
          * @returns {boolean} True if the property can have a shorthand form
          * @private
-         *
          */
         function canHaveShorthand(property) {
             return (property.kind !== "set" && property.kind !== "get" && property.type !== "SpreadElement" && property.type !== "SpreadProperty" && property.type !== "ExperimentalSpreadProperty");
@@ -157,7 +155,7 @@ module.exports = {
 
         /**
          * Checks whether a node is a string literal.
-         * @param   {ASTNode} node Any AST node.
+         * @param {ASTNode} node Any AST node.
          * @returns {boolean} `true` if it is a string literal.
          */
         function isStringLiteral(node) {
@@ -169,7 +167,6 @@ module.exports = {
          * @param {ASTNode} property Property AST node
          * @returns {boolean} True if the property is considered shorthand, false if not.
          * @private
-         *
          */
         function isShorthand(property) {
 
@@ -182,7 +179,6 @@ module.exports = {
          * @param {ASTNode} property Property AST node
          * @returns {boolean} True if the key and value are named equally, false if not.
          * @private
-         *
          */
         function isRedundant(property) {
             const value = property.value;
@@ -199,10 +195,9 @@ module.exports = {
 
         /**
          * Ensures that an object's properties are consistently shorthand, or not shorthand at all.
-         * @param   {ASTNode} node Property AST node
-         * @param   {boolean} checkRedundancy Whether to check longform redundancy
+         * @param {ASTNode} node Property AST node
+         * @param {boolean} checkRedundancy Whether to check longform redundancy
          * @returns {void}
-         *
          */
         function checkConsistency(node, checkRedundancy) {
 
index db4674760a0f273a5d622fe6c96c924a75bb966f..c0ad70004e6716fbc4f8f3fc747b7c747e8e5d08 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow newlines around variable declarations",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/one-var-declaration-per-line"
         },
index e3df8320f8b13002ba88582a332f5ed2c64f258f..daff2d22b15254870362e940b7a58116842047c0 100644 (file)
@@ -34,7 +34,6 @@ module.exports = {
 
         docs: {
             description: "enforce variables to be declared either together or separately in functions",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/one-var"
         },
@@ -209,7 +208,7 @@ module.exports = {
 
         /**
          * Determines the current scope (function or block)
-         * @param  {string} statementType node.kind, one of: "var", "let", or "const"
+         * @param {string} statementType node.kind, one of: "var", "let", or "const"
          * @returns {Object} The scope associated with statementType
          */
         function getCurrentScope(statementType) {
@@ -542,6 +541,8 @@ module.exports = {
             FunctionDeclaration: startFunction,
             FunctionExpression: startFunction,
             ArrowFunctionExpression: startFunction,
+            StaticBlock: startFunction, // StaticBlock creates a new scope for `var` variables
+
             BlockStatement: startBlock,
             ForStatement: startBlock,
             ForInStatement: startBlock,
@@ -553,10 +554,12 @@ module.exports = {
             "ForInStatement:exit": endBlock,
             "SwitchStatement:exit": endBlock,
             "BlockStatement:exit": endBlock,
+
             "Program:exit": endFunction,
             "FunctionDeclaration:exit": endFunction,
             "FunctionExpression:exit": endFunction,
-            "ArrowFunctionExpression:exit": endFunction
+            "ArrowFunctionExpression:exit": endFunction,
+            "StaticBlock:exit": endFunction
         };
 
     }
index fdb0884922b6548d58ab8c11c6d3465ab0d8fd7c..34bdb861f46bee416a4917c2c3de77bcca84c731 100644 (file)
@@ -17,8 +17,8 @@ const astUtils = require("./utils/ast-utils");
 /**
  * Checks whether an operator is commutative and has an operator assignment
  * shorthand form.
- * @param   {string}  operator Operator to check.
- * @returns {boolean}          True if the operator is commutative and has a
+ * @param {string} operator Operator to check.
+ * @returns {boolean} True if the operator is commutative and has a
  *     shorthand form.
  */
 function isCommutativeOperatorWithShorthand(operator) {
@@ -28,8 +28,8 @@ function isCommutativeOperatorWithShorthand(operator) {
 /**
  * Checks whether an operator is not commutative and has an operator assignment
  * shorthand form.
- * @param   {string}  operator Operator to check.
- * @returns {boolean}          True if the operator is not commutative and has
+ * @param {string} operator Operator to check.
+ * @returns {boolean} True if the operator is not commutative and has
  *     a shorthand form.
  */
 function isNonCommutativeOperatorWithShorthand(operator) {
@@ -63,7 +63,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow assignment operator shorthand where possible",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/operator-assignment"
         },
@@ -76,8 +75,8 @@ module.exports = {
 
         fixable: "code",
         messages: {
-            replaced: "Assignment can be replaced with operator assignment.",
-            unexpected: "Unexpected operator assignment shorthand."
+            replaced: "Assignment (=) can be replaced with operator assignment ({{operator}}=).",
+            unexpected: "Unexpected operator assignment ({{operator}}=) shorthand."
         }
     },
 
@@ -96,7 +95,7 @@ module.exports = {
 
         /**
          * Ensures that an assignment uses the shorthand form where possible.
-         * @param   {ASTNode} node An AssignmentExpression node.
+         * @param {ASTNode} node An AssignmentExpression node.
          * @returns {void}
          */
         function verify(node) {
@@ -113,6 +112,7 @@ module.exports = {
                     context.report({
                         node,
                         messageId: "replaced",
+                        data: { operator },
                         fix(fixer) {
                             if (canBeFixed(left) && canBeFixed(expr.left)) {
                                 const equalsToken = getOperatorToken(node);
@@ -139,7 +139,8 @@ module.exports = {
                      */
                     context.report({
                         node,
-                        messageId: "replaced"
+                        messageId: "replaced",
+                        data: { operator }
                     });
                 }
             }
@@ -147,7 +148,7 @@ module.exports = {
 
         /**
          * Warns if an assignment expression uses operator assignment shorthand.
-         * @param   {ASTNode} node An AssignmentExpression node.
+         * @param {ASTNode} node An AssignmentExpression node.
          * @returns {void}
          */
         function prohibit(node) {
@@ -155,6 +156,7 @@ module.exports = {
                 context.report({
                     node,
                     messageId: "unexpected",
+                    data: { operator: node.operator },
                     fix(fixer) {
                         if (canBeFixed(node.left)) {
                             const firstToken = sourceCode.getFirstToken(node);
index 18da5c55b4a4fe346dfdb2250854f2691d75bb2f..6eab0cccbb4e1ed0f61e45b08c5ab2bdf3c267e5 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent linebreak style for operators",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/operator-linebreak"
         },
@@ -136,23 +135,21 @@ module.exports = {
         /**
          * Checks the operator placement
          * @param {ASTNode} node The node to check
-         * @param {ASTNode} leftSide The node that comes before the operator in `node`
+         * @param {ASTNode} rightSide The node that comes after the operator in `node`
+         * @param {string} operator The operator
          * @private
          * @returns {void}
          */
-        function validateNode(node, leftSide) {
+        function validateNode(node, rightSide, operator) {
 
             /*
-             * When the left part of a binary expression is a single expression wrapped in
-             * parentheses (ex: `(a) + b`), leftToken will be the last token of the expression
-             * and operatorToken will be the closing parenthesis.
-             * The leftToken should be the last closing parenthesis, and the operatorToken
-             * should be the token right after that.
+             * Find the operator token by searching from the right side, because between the left side and the operator
+             * there could be additional tokens from type annotations. Search specifically for the token which
+             * value equals the operator, in order to skip possible opening parentheses before the right side node.
              */
-            const operatorToken = sourceCode.getTokenAfter(leftSide, astUtils.isNotClosingParenToken);
+            const operatorToken = sourceCode.getTokenBefore(rightSide, token => token.value === operator);
             const leftToken = sourceCode.getTokenBefore(operatorToken);
             const rightToken = sourceCode.getTokenAfter(operatorToken);
-            const operator = operatorToken.value;
             const operatorStyleOverride = styleOverrides[operator];
             const style = operatorStyleOverride || globalStyle;
             const fix = getFixer(operatorToken, style);
@@ -222,7 +219,7 @@ module.exports = {
          * @returns {void}
          */
         function validateBinaryExpression(node) {
-            validateNode(node, node.left);
+            validateNode(node, node.right, node.operator);
         }
 
         //--------------------------------------------------------------------------
@@ -235,12 +232,17 @@ module.exports = {
             AssignmentExpression: validateBinaryExpression,
             VariableDeclarator(node) {
                 if (node.init) {
-                    validateNode(node, node.id);
+                    validateNode(node, node.init, "=");
+                }
+            },
+            PropertyDefinition(node) {
+                if (node.value) {
+                    validateNode(node, node.value, "=");
                 }
             },
             ConditionalExpression(node) {
-                validateNode(node, node.test);
-                validateNode(node, node.consequent);
+                validateNode(node, node.consequent, "?");
+                validateNode(node, node.alternate, ":");
             }
         };
     }
index f8b5bd977778f344c24a93df7e9f28c4544574a4..de75e8d2d51b06d88a8985bb281a6d6e1547b862 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow padding within blocks",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/padded-blocks"
         },
@@ -107,6 +106,12 @@ module.exports = {
             if (node.type === "SwitchStatement") {
                 return sourceCode.getTokenBefore(node.cases[0]);
             }
+
+            if (node.type === "StaticBlock") {
+                return sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
+            }
+
+            // `BlockStatement` or `ClassBody`
             return sourceCode.getFirstToken(node);
         }
 
@@ -167,11 +172,13 @@ module.exports = {
         /**
          * Checks if a node should be padded, according to the rule config.
          * @param {ASTNode} node The AST node to check.
+         * @throws {Error} (Unreachable)
          * @returns {boolean} True if the node should be padded, false otherwise.
          */
         function requirePaddingFor(node) {
             switch (node.type) {
                 case "BlockStatement":
+                case "StaticBlock":
                     return options.blocks;
                 case "SwitchStatement":
                     return options.switches;
@@ -282,6 +289,7 @@ module.exports = {
                 }
                 checkPadding(node);
             };
+            rule.StaticBlock = rule.BlockStatement;
         }
 
         if (Object.prototype.hasOwnProperty.call(options, "classes")) {
index c97b9956b7136e9c5997de8526157fb70333878b..42859dd38b0b8fa12307c664d77f321c4cf728eb 100644 (file)
@@ -431,7 +431,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow padding lines between statements",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/padding-line-between-statements"
         },
@@ -619,9 +618,11 @@ module.exports = {
             Program: enterScope,
             BlockStatement: enterScope,
             SwitchStatement: enterScope,
+            StaticBlock: enterScope,
             "Program:exit": exitScope,
             "BlockStatement:exit": exitScope,
             "SwitchStatement:exit": exitScope,
+            "StaticBlock:exit": exitScope,
 
             ":statement": verify,
 
index ee5cfe3c8c7fd9b68d0b1e84a89c61dd1ffc7186..518bf4b2da3bda40438c3ce9ab09b247846a8686 100644 (file)
@@ -60,6 +60,7 @@ function getVariableOfArguments(scope) {
 /**
  * Checks whether or not a given node is a callback.
  * @param {ASTNode} node A node to check.
+ * @throws {Error} (Unreachable.)
  * @returns {Object}
  *   {boolean} retv.isCallback - `true` if the node is a callback.
  *   {boolean} retv.isLexicalThis - `true` if the node is with `.bind(this)`.
@@ -150,7 +151,6 @@ module.exports = {
 
         docs: {
             description: "require using arrow functions for callbacks",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-arrow-callback"
         },
@@ -295,7 +295,7 @@ module.exports = {
                                  * If the callback function has duplicates in its list of parameters (possible in sloppy mode),
                                  * don't replace it with an arrow function, because this is a SyntaxError with arrow functions.
                                  */
-                                return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
+                                return;
                             }
 
                             // Remove `.bind(this)` if exists.
@@ -307,7 +307,7 @@ module.exports = {
                                  * E.g. `(foo || function(){}).bind(this)`
                                  */
                                 if (memberNode.type !== "MemberExpression") {
-                                    return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
+                                    return;
                                 }
 
                                 const callNode = memberNode.parent;
@@ -320,12 +320,12 @@ module.exports = {
                                  *                    ^^^^^^^^^^^^
                                  */
                                 if (astUtils.isParenthesised(sourceCode, memberNode)) {
-                                    return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
+                                    return;
                                 }
 
                                 // If comments exist in the `.bind(this)`, don't remove those.
                                 if (sourceCode.commentsExistBetween(firstTokenToRemove, lastTokenToRemove)) {
-                                    return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
+                                    return;
                                 }
 
                                 yield fixer.removeRange([firstTokenToRemove.range[0], lastTokenToRemove.range[1]]);
index f6e79e71c3e9758ba292b64c1d57543df6e1fa8e..38ec973fcdb592920098b80823da74ecce5d3924 100644 (file)
@@ -17,7 +17,7 @@ const astUtils = require("./utils/ast-utils");
 //------------------------------------------------------------------------------
 
 const PATTERN_TYPE = /^(?:.+?Pattern|RestElement|SpreadProperty|ExperimentalRestProperty|Property)$/u;
-const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|SwitchCase)$/u;
+const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|StaticBlock|SwitchCase)$/u;
 const DESTRUCTURING_HOST_TYPE = /^(?:VariableDeclarator|AssignmentExpression)$/u;
 
 /**
@@ -332,7 +332,6 @@ module.exports = {
 
         docs: {
             description: "require `const` declarations for variables that are never reassigned after declared",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-const"
         },
index f82bb75c1225d3c771d0095696177b35d535569f..46986d237df3dc05f35f9fb0ca60a65ed7c99528 100644 (file)
@@ -26,7 +26,6 @@ module.exports = {
 
         docs: {
             description: "require destructuring from arrays and/or objects",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-destructuring"
         },
@@ -119,8 +118,8 @@ module.exports = {
         // Helpers
         //--------------------------------------------------------------------------
 
-        // eslint-disable-next-line jsdoc/require-description
         /**
+         * Checks if destructuring type should be checked.
          * @param {string} nodeType "AssignmentExpression" or "VariableDeclarator"
          * @param {string} destructuringType "array" or "object"
          * @returns {boolean} `true` if the destructuring type should be checked for the given node
@@ -221,7 +220,11 @@ module.exports = {
          * @returns {void}
          */
         function performCheck(leftNode, rightNode, reportNode) {
-            if (rightNode.type !== "MemberExpression" || rightNode.object.type === "Super") {
+            if (
+                rightNode.type !== "MemberExpression" ||
+                rightNode.object.type === "Super" ||
+                rightNode.property.type === "PrivateIdentifier"
+            ) {
                 return;
             }
 
index 6121af889710b58e0dc34430298ee9500088b7eb..de802ce1e11cb89df8e2c7e4c92bc3f1207e50cc 100644 (file)
@@ -90,7 +90,6 @@ module.exports = {
 
         docs: {
             description: "disallow the use of `Math.pow` in favor of the `**` operator",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-exponentiation-operator"
         },
index 7d0aa3f9dc094d6ad02a39def39f34305cbce404..41aa549327d96923f9b6176ead7b21225df0774b 100644 (file)
@@ -33,7 +33,6 @@ module.exports = {
 
         docs: {
             description: "enforce using named capture group in regular expression",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-named-capture-group"
         },
index cc82e6653c03b3b0d2594d984f2b5128db8853d6..91bb26724eb13c4fc49849a86ef1f7655aa19430 100644 (file)
@@ -45,7 +45,6 @@ module.exports = {
 
         docs: {
             description: "disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-numeric-literals"
         },
index ab252c73ae3aafcd805b3e6cad6c1faa99a2f269..3958a51b30e4601c1edcb258f736707150ab538d 100644 (file)
@@ -247,7 +247,6 @@ module.exports = {
         docs: {
             description:
                 "disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead.",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-object-spread"
         },
index ec16e445555dbfb3861d3315cdbf6ef31901d92d..bdc1fef4d7c18a79778d8a84ad89b0797d9a7ebb 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "require using Error objects as Promise rejection reasons",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-promise-reject-errors"
         },
index 156d61251c48775a5ab403ee3b5dbd2065531609..fea88c67827afbd4638383744bf75dac1b8d29d8 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "require `Reflect` methods where applicable",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-reflect"
         },
index 9e8ce02354701fc15f3fd6f45d23757a1aec6b21..fbfeb5636d754a2b1138219f9a0b087751a606f5 100644 (file)
@@ -54,7 +54,6 @@ module.exports = {
 
         docs: {
             description: "disallow use of the `RegExp` constructor in favor of regular expression literals",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-regex-literals"
         },
index 3ecea732af8b94c734be50889e75d35a99dd1740..157f0bbd61a84e65e4fc76cac169073d9fd1a05b 100644 (file)
@@ -65,7 +65,6 @@ module.exports = {
 
         docs: {
             description: "require rest parameters instead of `arguments`",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-rest-params"
         },
index d3c3c4d22972a12e85c0466162541c4392c2894b..3944fedb126cacd34ca4a6b83655ca0cd6499d03 100644 (file)
@@ -49,7 +49,6 @@ module.exports = {
 
         docs: {
             description: "require spread operators instead of `.apply()`",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-spread"
         },
index cb967660a62b8659873daac5c2a6b94ef0baffb9..564dd555f8e9b843d639eeddad349660fefa67e1 100644 (file)
@@ -128,7 +128,6 @@ module.exports = {
 
         docs: {
             description: "require template literals instead of string concatenation",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/prefer-template"
         },
index fab7bdc9c1555b38ca454f67fbcab0ed86985da6..ce277cdcc9d897c9c1853f1567ac77f01c0affa9 100644 (file)
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "require quotes around object literal property names",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/quote-props"
         },
@@ -91,7 +90,7 @@ module.exports = {
 
         /**
          * Checks whether a certain string constitutes an ES3 token
-         * @param   {string} tokenStr The string to be checked.
+         * @param {string} tokenStr The string to be checked.
          * @returns {boolean} `true` if it is an ES3 token.
          */
         function isKeyword(tokenStr) {
@@ -100,9 +99,9 @@ module.exports = {
 
         /**
          * Checks if an espree-tokenized key has redundant quotes (i.e. whether quotes are unnecessary)
-         * @param   {string} rawKey The raw key value from the source
-         * @param   {espreeTokens} tokens The espree-tokenized node key
-         * @param   {boolean} [skipNumberLiterals=false] Indicates whether number literals should be checked
+         * @param {string} rawKey The raw key value from the source
+         * @param {espreeTokens} tokens The espree-tokenized node key
+         * @param {boolean} [skipNumberLiterals=false] Indicates whether number literals should be checked
          * @returns {boolean} Whether or not a key has redundant quotes.
          * @private
          */
@@ -139,7 +138,7 @@ module.exports = {
 
         /**
          * Ensures that a property's key is quoted only when necessary
-         * @param   {ASTNode} node Property AST node
+         * @param {ASTNode} node Property AST node
          * @returns {void}
          */
         function checkUnnecessaryQuotes(node) {
@@ -195,7 +194,7 @@ module.exports = {
 
         /**
          * Ensures that a property's key is quoted
-         * @param   {ASTNode} node Property AST node
+         * @param {ASTNode} node Property AST node
          * @returns {void}
          */
         function checkOmittedQuotes(node) {
@@ -213,8 +212,8 @@ module.exports = {
 
         /**
          * Ensures that an object's keys are consistently quoted, optionally checks for redundancy of quotes
-         * @param   {ASTNode} node Property AST node
-         * @param   {boolean} checkQuotesRedundancy Whether to check quotes' redundancy
+         * @param {ASTNode} node Property AST node
+         * @param {boolean} checkQuotesRedundancy Whether to check quotes' redundancy
          * @returns {void}
          */
         function checkConsistency(node, checkQuotesRedundancy) {
index da7e127493eaa1f89be7490a4d602572a0f3838f..d7959c0d1f02653112c18b20a6dba6cf6e2dfb54 100644 (file)
@@ -80,7 +80,6 @@ module.exports = {
 
         docs: {
             description: "enforce the consistent use of either backticks, double, or single quotes",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/quotes"
         },
@@ -216,6 +215,7 @@ module.exports = {
 
                 // LiteralPropertyName.
                 case "Property":
+                case "PropertyDefinition":
                 case "MethodDefinition":
                     return parent.key === node && !parent.computed;
 
index d1b67e5e280b93193e3d23e78c834f0620a38e71..0c6c6ffb1a60bb4f12ab0b51ed2257bca53e2312 100644 (file)
@@ -80,12 +80,12 @@ module.exports = {
 
         docs: {
             description: "enforce the consistent use of the radix argument when using `parseInt()`",
-            category: "Best Practices",
             recommended: false,
-            url: "https://eslint.org/docs/rules/radix",
-            suggestion: true
+            url: "https://eslint.org/docs/rules/radix"
         },
 
+        hasSuggestions: true,
+
         schema: [
             {
                 enum: ["always", "as-needed"]
index b3df907420c0409c3b8752d15e3e94b09ad369ff..248b0eb11d00d59a1a49438e477a9a8bf2408721 100644 (file)
@@ -79,6 +79,9 @@ function isLocalVariableWithoutEscape(variable, isMemberAccess) {
         reference.from.variableScope === functionScope);
 }
 
+/**
+ * Represents segment information.
+ */
 class SegmentInfo {
     constructor() {
         this.info = new WeakMap();
@@ -168,20 +171,32 @@ module.exports = {
 
         docs: {
             description: "disallow assignments that can lead to race conditions due to usage of `await` or `yield`",
-            category: "Possible Errors",
             recommended: false,
             url: "https://eslint.org/docs/rules/require-atomic-updates"
         },
 
         fixable: null,
-        schema: [],
+
+        schema: [{
+            type: "object",
+            properties: {
+                allowProperties: {
+                    type: "boolean",
+                    default: false
+                }
+            },
+            additionalProperties: false
+        }],
 
         messages: {
-            nonAtomicUpdate: "Possible race condition: `{{value}}` might be reassigned based on an outdated value of `{{value}}`."
+            nonAtomicUpdate: "Possible race condition: `{{value}}` might be reassigned based on an outdated value of `{{value}}`.",
+            nonAtomicObjectUpdate: "Possible race condition: `{{value}}` might be assigned based on an outdated state of `{{object}}`."
         }
     },
 
     create(context) {
+        const allowProperties = !!context.options[0] && context.options[0].allowProperties;
+
         const sourceCode = context.getSourceCode();
         const assignmentReferences = new Map();
         const segmentInfo = new SegmentInfo();
@@ -273,13 +288,25 @@ module.exports = {
                         const variable = reference.resolved;
 
                         if (segmentInfo.isOutdated(codePath.currentSegments, variable)) {
-                            context.report({
-                                node: node.parent,
-                                messageId: "nonAtomicUpdate",
-                                data: {
-                                    value: sourceCode.getText(node.parent.left)
-                                }
-                            });
+                            if (node.parent.left === reference.identifier) {
+                                context.report({
+                                    node: node.parent,
+                                    messageId: "nonAtomicUpdate",
+                                    data: {
+                                        value: variable.name
+                                    }
+                                });
+                            } else if (!allowProperties) {
+                                context.report({
+                                    node: node.parent,
+                                    messageId: "nonAtomicObjectUpdate",
+                                    data: {
+                                        value: sourceCode.getText(node.parent.left),
+                                        object: variable.name
+                                    }
+                                });
+                            }
+
                         }
                     }
                 }
index 9b5acc78c814ea02077c8784d65b8097fac2dc5a..8ec6f547ae9403db1b4979106902f62a6d4a635a 100644 (file)
@@ -34,7 +34,6 @@ module.exports = {
 
         docs: {
             description: "disallow async functions which have no `await` expression",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/require-await"
         },
index e581b2bee4646a43863abab8e83072a7f7e1d958..1d76e3d222b0e7bf3f11f8f08f08780e3493de84 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Rule to check for jsdoc presence.
  * @author Gyandeep Singh
+ * @deprecated in ESLint v5.10.0
  */
 "use strict";
 
@@ -10,7 +11,6 @@ module.exports = {
 
         docs: {
             description: "require JSDoc comments",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/require-jsdoc"
         },
index 880405e9a25dbfe5c90f85f454f615f2cee9bb77..a332b48da669aebe74002ef7ac4c78a48a8f5af0 100644 (file)
@@ -26,7 +26,6 @@ module.exports = {
 
         docs: {
             description: "enforce the use of `u` flag on RegExp",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/require-unicode-regexp"
         },
index af2344dfa6b8ff7e0d2eba7e4dfc813e6fec314f..f5b5d530396a0041c55c965e01023e58811f9e0f 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "require generator functions to contain `yield`",
-            category: "ECMAScript 6",
             recommended: true,
             url: "https://eslint.org/docs/rules/require-yield"
         },
index 8cb9814f0c9129e1328eb4933f115bd1559e4a8e..a636defdcd9046e17e2da37ce6b97ac2326aba18 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "enforce spacing between rest and spread operators and their expressions",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/rest-spread-spacing"
         },
index 5c546f2902870ef6dddf5d7d9651ff6d717203ef..e5e2ae25cc7365f125d3850fdc3cf7ca0438da0b 100644 (file)
@@ -17,7 +17,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing before and after semicolons",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/semi-spacing"
         },
@@ -238,7 +237,8 @@ module.exports = {
                 if (node.test) {
                     checkSemicolonSpacing(sourceCode.getTokenAfter(node.test), node);
                 }
-            }
+            },
+            PropertyDefinition: checkNode
         };
     }
 };
index 0c9bec4c85ede81e29c4493f42234263df2fe5fc..2d17d028c2fb06b6cc2d824b0f338bba2e89e686 100644 (file)
@@ -15,19 +15,18 @@ const astUtils = require("./utils/ast-utils");
 // Rule Definition
 //------------------------------------------------------------------------------
 
-const SELECTOR = `:matches(${
-    [
-        "BreakStatement", "ContinueStatement", "DebuggerStatement",
-        "DoWhileStatement", "ExportAllDeclaration",
-        "ExportDefaultDeclaration", "ExportNamedDeclaration",
-        "ExpressionStatement", "ImportDeclaration", "ReturnStatement",
-        "ThrowStatement", "VariableDeclaration"
-    ].join(",")
-})`;
+const SELECTOR = [
+    "BreakStatement", "ContinueStatement", "DebuggerStatement",
+    "DoWhileStatement", "ExportAllDeclaration",
+    "ExportDefaultDeclaration", "ExportNamedDeclaration",
+    "ExpressionStatement", "ImportDeclaration", "ReturnStatement",
+    "ThrowStatement", "VariableDeclaration", "PropertyDefinition"
+].join(",");
 
 /**
  * Get the child node list of a given node.
- * This returns `Program#body`, `BlockStatement#body`, or `SwitchCase#consequent`.
+ * This returns `BlockStatement#body`, `StaticBlock#body`, `Program#body`,
+ * `ClassBody#body`, or `SwitchCase#consequent`.
  * This is used to check whether a node is the first/last child.
  * @param {Node} node A node to get child node list.
  * @returns {Node[]|null} The child node list.
@@ -35,7 +34,12 @@ const SELECTOR = `:matches(${
 function getChildren(node) {
     const t = node.type;
 
-    if (t === "BlockStatement" || t === "Program") {
+    if (
+        t === "BlockStatement" ||
+        t === "StaticBlock" ||
+        t === "Program" ||
+        t === "ClassBody"
+    ) {
         return node.body;
     }
     if (t === "SwitchCase") {
@@ -69,7 +73,6 @@ module.exports = {
 
         docs: {
             description: "enforce location of semicolons",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/semi-style"
         },
index d2f0670427b3ab66b3aebf8e488b1c1d419f2438..c29029cedfe153750ff49fffc6118934e5f8d8e6 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow semicolons instead of ASI",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/semi"
         },
@@ -78,6 +77,8 @@ module.exports = {
     create(context) {
 
         const OPT_OUT_PATTERN = /^[-[(/+`]/u; // One of [(/+-`
+        const unsafeClassFieldNames = new Set(["get", "set", "static"]);
+        const unsafeClassFieldFollowers = new Set(["*", "in", "instanceof"]);
         const options = context.options[1];
         const never = context.options[0] === "never";
         const exceptOneLine = Boolean(options && options.omitLastInOneLineBlock);
@@ -166,6 +167,55 @@ module.exports = {
             );
         }
 
+        /**
+         * Checks if a given PropertyDefinition node followed by a semicolon
+         * can safely remove that semicolon. It is not to safe to remove if
+         * the class field name is "get", "set", or "static", or if
+         * followed by a generator method.
+         * @param {ASTNode} node The node to check.
+         * @returns {boolean} `true` if the node cannot have the semicolon
+         *      removed.
+         */
+        function maybeClassFieldAsiHazard(node) {
+
+            if (node.type !== "PropertyDefinition") {
+                return false;
+            }
+
+            /*
+             * Computed property names and non-identifiers are always safe
+             * as they can be distinguished from keywords easily.
+             */
+            const needsNameCheck = !node.computed && node.key.type === "Identifier";
+
+            /*
+             * Certain names are problematic unless they also have a
+             * a way to distinguish between keywords and property
+             * names.
+             */
+            if (needsNameCheck && unsafeClassFieldNames.has(node.key.name)) {
+
+                /*
+                 * Special case: If the field name is `static`,
+                 * it is only valid if the field is marked as static,
+                 * so "static static" is okay but "static" is not.
+                 */
+                const isStaticStatic = node.static && node.key.name === "static";
+
+                /*
+                 * For other unsafe names, we only care if there is no
+                 * initializer. No initializer = hazard.
+                 */
+                if (!isStaticStatic && !node.value) {
+                    return true;
+                }
+            }
+
+            const followingToken = sourceCode.getTokenAfter(node);
+
+            return unsafeClassFieldFollowers.has(followingToken.value);
+        }
+
         /**
          * Check whether a given node is on the same line with the next token.
          * @param {Node} node A statement node to check.
@@ -233,10 +283,19 @@ module.exports = {
             if (isRedundantSemi(sourceCode.getLastToken(node))) {
                 return true; // `;;` or `;}`
             }
+            if (maybeClassFieldAsiHazard(node)) {
+                return false;
+            }
             if (isOnSameLineWithNextToken(node)) {
                 return false; // One liner.
             }
-            if (beforeStatementContinuationChars === "never" && !maybeAsiHazardAfter(node)) {
+
+            // continuation characters should not apply to class fields
+            if (
+                node.type !== "PropertyDefinition" &&
+                beforeStatementContinuationChars === "never" &&
+                !maybeAsiHazardAfter(node)
+            ) {
                 return true; // ASI works. This statement doesn't connect to the next.
             }
             if (!maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) {
@@ -247,22 +306,31 @@ module.exports = {
         }
 
         /**
-         * Checks a node to see if it's in a one-liner block statement.
+         * Checks a node to see if it's the last item in a one-liner block.
+         * Block is any `BlockStatement` or `StaticBlock` node. Block is a one-liner if its
+         * braces (and consequently everything between them) are on the same line.
          * @param {ASTNode} node The node to check.
-         * @returns {boolean} whether the node is in a one-liner block statement.
+         * @returns {boolean} whether the node is the last item in a one-liner block.
          */
-        function isOneLinerBlock(node) {
+        function isLastInOneLinerBlock(node) {
             const parent = node.parent;
             const nextToken = sourceCode.getTokenAfter(node);
 
             if (!nextToken || nextToken.value !== "}") {
                 return false;
             }
-            return (
-                !!parent &&
-                parent.type === "BlockStatement" &&
-                parent.loc.start.line === parent.loc.end.line
-            );
+
+            if (parent.type === "BlockStatement") {
+                return parent.loc.start.line === parent.loc.end.line;
+            }
+
+            if (parent.type === "StaticBlock") {
+                const openingBrace = sourceCode.getFirstToken(parent, { skip: 1 }); // skip the `static` token
+
+                return openingBrace.loc.start.line === parent.loc.end.line;
+            }
+
+            return false;
         }
 
         /**
@@ -276,11 +344,15 @@ module.exports = {
             if (never) {
                 if (isSemi && canRemoveSemicolon(node)) {
                     report(node, true);
-                } else if (!isSemi && beforeStatementContinuationChars === "always" && maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) {
+                } else if (
+                    !isSemi && beforeStatementContinuationChars === "always" &&
+                    node.type !== "PropertyDefinition" &&
+                    maybeAsiHazardBefore(sourceCode.getTokenAfter(node))
+                ) {
                     report(node);
                 }
             } else {
-                const oneLinerBlock = (exceptOneLine && isOneLinerBlock(node));
+                const oneLinerBlock = (exceptOneLine && isLastInOneLinerBlock(node));
 
                 if (isSemi && oneLinerBlock) {
                     report(node, true);
@@ -329,7 +401,8 @@ module.exports = {
                 if (!/(?:Class|Function)Declaration/u.test(node.declaration.type)) {
                     checkForSemicolon(node);
                 }
-            }
+            },
+            PropertyDefinition: checkForSemicolon
         };
 
     }
index 4c3ddec766960a1a395d00b5ff6bc5d605953429..dd43daddf05b6d97ede27cdb9f995f4ef45c5c19 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "enforce sorted import declarations within modules",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/sort-imports"
         },
index 8a95ee25d6194824d752897bb6b9c014364c2e3b..65a99142279c8d181c527aab07d14a5394e67a57 100644 (file)
@@ -81,7 +81,6 @@ module.exports = {
 
         docs: {
             description: "require object keys to be sorted",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/sort-keys"
         },
index 7add2cf74b29400e4a238518fa1866e5addf0329..0616c44ac621bb2f2fdf77c3b15c35f723928c0f 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "require variables within the same declaration block to be sorted",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/sort-vars"
         },
index 87ef9bfd88013cbcc85f21ea598b173bd108a2c4..87d5b27aace7baab25c0251c5526a3d0067f0470 100644 (file)
@@ -40,7 +40,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing before blocks",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/space-before-blocks"
         },
@@ -108,13 +107,25 @@ module.exports = {
          * Checks whether the spacing before the given block is already controlled by another rule:
          * - `arrow-spacing` checks spaces after `=>`.
          * - `keyword-spacing` checks spaces after keywords in certain contexts.
+         * - `switch-colon-spacing` checks spaces after `:` of switch cases.
          * @param {Token} precedingToken first token before the block.
          * @param {ASTNode|Token} node `BlockStatement` node or `{` token of a `SwitchStatement` node.
          * @returns {boolean} `true` if requiring or disallowing spaces before the given block could produce conflicts with other rules.
          */
         function isConflicted(precedingToken, node) {
-            return astUtils.isArrowToken(precedingToken) ||
-                astUtils.isKeywordToken(precedingToken) && !isFunctionBody(node);
+            return (
+                astUtils.isArrowToken(precedingToken) ||
+                (
+                    astUtils.isKeywordToken(precedingToken) &&
+                    !isFunctionBody(node)
+                ) ||
+                (
+                    astUtils.isColonToken(precedingToken) &&
+                    node.parent &&
+                    node.parent.type === "SwitchCase" &&
+                    precedingToken === astUtils.getSwitchCaseColonToken(node.parent, sourceCode)
+                )
+            );
         }
 
         /**
index 1021a110cfd3f0488a46a597a368d6bb2b787f9f..b60bee0409720a91411233e54e3b7c6e609b806c 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing before `function` definition opening parenthesis",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/space-before-function-paren"
         },
index b0a604d955d788ff8c81a07907d8cc4015f4e44c..24378b89f046614cc2c321cfdf3eaa01b82fda63 100644 (file)
@@ -16,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing inside parentheses",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/space-in-parens"
         },
index 3c550984fc625295559095c25ebd86b5d08646b8..8065b5240b9ffac0e43c5e882de0bc86e4bb9db2 100644 (file)
@@ -4,6 +4,8 @@
  */
 "use strict";
 
+const { isEqToken } = require("./utils/ast-utils");
+
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
@@ -14,7 +16,6 @@ module.exports = {
 
         docs: {
             description: "require spacing around infix operators",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/space-infix-ops"
         },
@@ -164,7 +165,29 @@ module.exports = {
             BinaryExpression: checkBinary,
             LogicalExpression: checkBinary,
             ConditionalExpression: checkConditional,
-            VariableDeclarator: checkVar
+            VariableDeclarator: checkVar,
+
+            PropertyDefinition(node) {
+                if (!node.value) {
+                    return;
+                }
+
+                /*
+                 * Because of computed properties and type annotations, some
+                 * tokens may exist between `node.key` and `=`.
+                 * Therefore, find the `=` from the right.
+                 */
+                const operatorToken = sourceCode.getTokenBefore(node.value, isEqToken);
+                const leftToken = sourceCode.getTokenBefore(operatorToken);
+                const rightToken = sourceCode.getTokenAfter(operatorToken);
+
+                if (
+                    !sourceCode.isSpaceBetweenTokens(leftToken, operatorToken) ||
+                    !sourceCode.isSpaceBetweenTokens(operatorToken, rightToken)
+                ) {
+                    report(node, operatorToken);
+                }
+            }
         };
 
     }
index 57f6e784501d2d980c14fe9adc4b5e48d3949c56..de9018f9b69e0de7840c18ae8a4891a07ad7c72c 100644 (file)
@@ -20,7 +20,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing before or after unary operators",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/space-unary-ops"
         },
index 226a2d44798a192b87920a26ed6e8c76186140e2..6f0b432b765ac5812d1103ac99741439fc11f43d 100644 (file)
@@ -152,7 +152,6 @@ module.exports = {
 
         docs: {
             description: "enforce consistent spacing after the `//` or `/*` in a comment",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/spaced-comment"
         },
index b0d6cf9172a74bdbb3aefd0877216d99b6e05a2b..24af39dabee3889504a3e7d4856ad81b1d2c35ca 100644 (file)
@@ -69,7 +69,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow strict mode directives",
-            category: "Strict Mode",
             recommended: false,
             url: "https://eslint.org/docs/rules/strict"
         },
index c90641573c6010e06bf3941555aa9481c480ade3..a4f3ba213eb52141d6af65476e8d2597f3ee9bd5 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "enforce spacing around colons of switch statements",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/switch-colon-spacing"
         },
@@ -51,18 +50,6 @@ module.exports = {
         const beforeSpacing = options.before === true; // false by default
         const afterSpacing = options.after !== false; // true by default
 
-        /**
-         * Get the colon token of the given SwitchCase node.
-         * @param {ASTNode} node The SwitchCase node to get.
-         * @returns {Token} The colon token of the node.
-         */
-        function getColonToken(node) {
-            if (node.test) {
-                return sourceCode.getTokenAfter(node.test, astUtils.isColonToken);
-            }
-            return sourceCode.getFirstToken(node, 1);
-        }
-
         /**
          * Check whether the spacing between the given 2 tokens is valid or not.
          * @param {Token} left The left token to check.
@@ -115,7 +102,7 @@ module.exports = {
 
         return {
             SwitchCase(node) {
-                const colonToken = getColonToken(node);
+                const colonToken = astUtils.getSwitchCaseColonToken(node, sourceCode);
                 const beforeToken = sourceCode.getTokenBefore(colonToken);
                 const afterToken = sourceCode.getTokenAfter(colonToken);
 
index 155cea4dc0bceb8d7012203068f760b6d2731e3f..9f5d9358f00253a40fe2db201ef093b61f700637 100644 (file)
@@ -22,7 +22,6 @@ module.exports = {
 
         docs: {
             description: "require symbol descriptions",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/symbol-description"
         },
index 26043bc912210a6930045fe77b448105b63bd5d5..5133a54539cb32e5dded906757d5c3565a328d99 100644 (file)
@@ -21,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow spacing around embedded expressions of template strings",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/template-curly-spacing"
         },
index 16f586255af567b0bab8cc0dedd7a46207108cf4..45b66068a83f3d3d79090070c663d15e78a22fef 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow spacing between template tags and their literals",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/template-tag-spacing"
         },
index 39642f85193e90ab525ae4c5f66365e13eca2980..e80497d19ca029bf302a8d479d00cb94c221cc87 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow Unicode byte order mark (BOM)",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/unicode-bom"
         },
index 0c7e888c976d8d19729aff7bb8e49568dfb729f9..cd8331f932e3edd75fa47708db5d34aa0d15127c 100644 (file)
@@ -21,7 +21,10 @@ const astUtils = require("./utils/ast-utils");
  * @returns {boolean} `true` if the node is 'NaN' identifier.
  */
 function isNaNIdentifier(node) {
-    return Boolean(node) && node.type === "Identifier" && node.name === "NaN";
+    return Boolean(node) && (
+        astUtils.isSpecificId(node, "NaN") ||
+        astUtils.isSpecificMemberAccess(node, "Number", "NaN")
+    );
 }
 
 //------------------------------------------------------------------------------
@@ -34,7 +37,6 @@ module.exports = {
 
         docs: {
             description: "require calls to `isNaN()` when checking for `NaN`",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/use-isnan"
         },
index 6b853001132ed29c9a2b779d165b219e2a2442c3..16d7b81157148349b277868918ed57e4f31ee6ab 100644 (file)
@@ -35,7 +35,7 @@ const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|g
 const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
 
 // A set of node types that can contain a list of statements
-const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
+const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "StaticBlock", "SwitchCase"]);
 
 const DECIMAL_INTEGER_PATTERN = /^(?:0|0[0-7]*[89]\d*|[1-9](?:_?\d)*)$/u;
 
@@ -266,6 +266,7 @@ function getStaticPropertyName(node) {
             return getStaticPropertyName(node.expression);
 
         case "Property":
+        case "PropertyDefinition":
         case "MethodDefinition":
             prop = node.key;
             break;
@@ -407,6 +408,7 @@ function isSameReference(left, right, disableStaticComputedKey = false) {
             return true;
 
         case "Identifier":
+        case "PrivateIdentifier":
             return left.name === right.name;
         case "Literal":
             return equalLiteralValue(left, right);
@@ -516,6 +518,15 @@ function isParenthesised(sourceCode, node) {
         nextToken.value === ")" && nextToken.range[0] >= node.range[1];
 }
 
+/**
+ * Checks if the given token is a `=` token or not.
+ * @param {Token} token The token to check.
+ * @returns {boolean} `true` if the token is a `=` token.
+ */
+function isEqToken(token) {
+    return token.value === "=" && token.type === "Punctuator";
+}
+
 /**
  * Checks if the given token is an arrow token or not.
  * @param {Token} token The token to check.
@@ -649,6 +660,16 @@ function isKeywordToken(token) {
  * @returns {Token} `(` token.
  */
 function getOpeningParenOfParams(node, sourceCode) {
+
+    // If the node is an arrow function and doesn't have parens, this returns the identifier of the first param.
+    if (node.type === "ArrowFunctionExpression" && node.params.length === 1) {
+        const argToken = sourceCode.getFirstToken(node.params[0]);
+        const maybeParenToken = sourceCode.getTokenBefore(argToken);
+
+        return isOpeningParenToken(maybeParenToken) ? maybeParenToken : argToken;
+    }
+
+    // Otherwise, returns paren.
     return node.id
         ? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
         : sourceCode.getFirstToken(node, isOpeningParenToken);
@@ -735,6 +756,19 @@ function isLogicalAssignmentOperator(operator) {
     return LOGICAL_ASSIGNMENT_OPERATORS.has(operator);
 }
 
+/**
+ * Get the colon token of the given SwitchCase node.
+ * @param {ASTNode} node The SwitchCase node to get.
+ * @param {SourceCode} sourceCode The source code object to get tokens.
+ * @returns {Token} The colon token of the node.
+ */
+function getSwitchCaseColonToken(node, sourceCode) {
+    if (node.test) {
+        return sourceCode.getTokenAfter(node.test, isColonToken);
+    }
+    return sourceCode.getFirstToken(node, 1);
+}
+
 //------------------------------------------------------------------------------
 // Public Interface
 //------------------------------------------------------------------------------
@@ -794,6 +828,7 @@ module.exports = {
     isOpeningBracketToken,
     isOpeningParenToken,
     isSemicolonToken,
+    isEqToken,
 
     /**
      * Checks whether or not a given node is a string literal.
@@ -836,8 +871,8 @@ module.exports = {
 
     /**
      * Validate that a string passed in is surrounded by the specified character
-     * @param  {string} val The text to check.
-     * @param  {string} character The character to see if it's surrounded by.
+     * @param {string} val The text to check.
+     * @param {string} character The character to see if it's surrounded by.
      * @returns {boolean} True if the text is surrounded by the character, false if not.
      * @private
      */
@@ -902,6 +937,8 @@ module.exports = {
      *
      * First, this checks the node:
      *
+     * - The given node is not in `PropertyDefinition#value` position.
+     * - The given node is not `StaticBlock`.
      * - The function name does not start with uppercase. It's a convention to capitalize the names
      *   of constructor functions. This check is not performed if `capIsConstructor` is set to `false`.
      * - The function does not have a JSDoc comment that has a @this tag.
@@ -916,13 +953,29 @@ module.exports = {
      * - The location is not on an ES2015 class.
      * - Its `bind`/`call`/`apply` method is not called directly.
      * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given.
-     * @param {ASTNode} node A function node to check.
+     * @param {ASTNode} node A function node to check. It also can be an implicit function, like `StaticBlock`
+     * or any expression that is `PropertyDefinition#value` node.
      * @param {SourceCode} sourceCode A SourceCode instance to get comments.
      * @param {boolean} [capIsConstructor = true] `false` disables the assumption that functions which name starts
      * with an uppercase or are assigned to a variable which name starts with an uppercase are constructors.
      * @returns {boolean} The function node is the default `this` binding.
      */
     isDefaultThisBinding(node, sourceCode, { capIsConstructor = true } = {}) {
+
+        /*
+         * Class field initializers are implicit functions, but ESTree doesn't have the AST node of field initializers.
+         * Therefore, A expression node at `PropertyDefinition#value` is a function.
+         * In this case, `this` is always not default binding.
+         */
+        if (node.parent.type === "PropertyDefinition" && node.parent.value === node) {
+            return false;
+        }
+
+        // Class static blocks are implicit functions. In this case, `this` is always not default binding.
+        if (node.type === "StaticBlock") {
+            return false;
+        }
+
         if (
             (capIsConstructor && isES5Constructor(node)) ||
             hasJSDocThisTag(node, sourceCode)
@@ -983,8 +1036,10 @@ module.exports = {
                  *   class A { get foo() { ... } }
                  *   class A { set foo() { ... } }
                  *   class A { static foo() { ... } }
+                 *   class A { foo = function() { ... } }
                  */
                 case "Property":
+                case "PropertyDefinition":
                 case "MethodDefinition":
                     return parent.value !== currentNode;
 
@@ -1261,7 +1316,8 @@ module.exports = {
      * 5e1_000   // false
      * 5n        // false
      * 1_000n    // false
-     * '5'       // false
+     * "5"       // false
+     *
      */
     isDecimalInteger(node) {
         return node.type === "Literal" && typeof node.value === "number" &&
@@ -1323,6 +1379,16 @@ module.exports = {
      * - `class A { static async foo() {} }`  .... `static async method 'foo'`
      * - `class A { static get foo() {} }`  ...... `static getter 'foo'`
      * - `class A { static set foo(a) {} }`  ..... `static setter 'foo'`
+     * - `class A { foo = () => {}; }`  .......... `method 'foo'`
+     * - `class A { foo = function() {}; }`  ..... `method 'foo'`
+     * - `class A { foo = function bar() {}; }`  . `method 'foo'`
+     * - `class A { static foo = () => {}; }`  ... `static method 'foo'`
+     * - `class A { '#foo' = () => {}; }`  ....... `method '#foo'`
+     * - `class A { #foo = () => {}; }`  ......... `private method #foo`
+     * - `class A { static #foo = () => {}; }`  .. `static private method #foo`
+     * - `class A { '#foo'() {} }`  .............. `method '#foo'`
+     * - `class A { #foo() {} }`  ................ `private method #foo`
+     * - `class A { static #foo() {} }`  ......... `static private method #foo`
      * @param {ASTNode} node The function node to get.
      * @returns {string} The name and kind of the function node.
      */
@@ -1330,8 +1396,15 @@ module.exports = {
         const parent = node.parent;
         const tokens = [];
 
-        if (parent.type === "MethodDefinition" && parent.static) {
-            tokens.push("static");
+        if (parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") {
+
+            // The proposal uses `static` word consistently before visibility words: https://github.com/tc39/proposal-static-class-features
+            if (parent.static) {
+                tokens.push("static");
+            }
+            if (!parent.computed && parent.key.type === "PrivateIdentifier") {
+                tokens.push("private");
+            }
         }
         if (node.async) {
             tokens.push("async");
@@ -1340,9 +1413,7 @@ module.exports = {
             tokens.push("generator");
         }
 
-        if (node.type === "ArrowFunctionExpression") {
-            tokens.push("arrow", "function");
-        } else if (parent.type === "Property" || parent.type === "MethodDefinition") {
+        if (parent.type === "Property" || parent.type === "MethodDefinition") {
             if (parent.kind === "constructor") {
                 return "constructor";
             }
@@ -1353,18 +1424,29 @@ module.exports = {
             } else {
                 tokens.push("method");
             }
+        } else if (parent.type === "PropertyDefinition") {
+            tokens.push("method");
         } else {
+            if (node.type === "ArrowFunctionExpression") {
+                tokens.push("arrow");
+            }
             tokens.push("function");
         }
 
-        if (node.id) {
-            tokens.push(`'${node.id.name}'`);
-        } else {
-            const name = getStaticPropertyName(parent);
+        if (parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") {
+            if (!parent.computed && parent.key.type === "PrivateIdentifier") {
+                tokens.push(`#${parent.key.name}`);
+            } else {
+                const name = getStaticPropertyName(parent);
 
-            if (name !== null) {
-                tokens.push(`'${name}'`);
+                if (name !== null) {
+                    tokens.push(`'${name}'`);
+                } else if (node.id) {
+                    tokens.push(`'${node.id.name}'`);
+                }
             }
+        } else if (node.id) {
+            tokens.push(`'${node.id.name}'`);
         }
 
         return tokens.join(" ");
@@ -1457,6 +1539,12 @@ module.exports = {
      *              ^^^^^^^^^^^^^^
      * - `class A { static set foo(a) {} }`
      *              ^^^^^^^^^^^^^^
+     * - `class A { foo = function() {} }`
+     *              ^^^^^^^^^^^^^^
+     * - `class A { static foo = function() {} }`
+     *              ^^^^^^^^^^^^^^^^^^^^^
+     * - `class A { foo = (a, b) => {} }`
+     *              ^^^^^^
      * @param {ASTNode} node The function node to get.
      * @param {SourceCode} sourceCode The source code object to get tokens.
      * @returns {string} The location of the function node for reporting.
@@ -1466,14 +1554,14 @@ module.exports = {
         let start = null;
         let end = null;
 
-        if (node.type === "ArrowFunctionExpression") {
+        if (parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") {
+            start = parent.loc.start;
+            end = getOpeningParenOfParams(node, sourceCode).loc.start;
+        } else if (node.type === "ArrowFunctionExpression") {
             const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken);
 
             start = arrowToken.loc.start;
             end = arrowToken.loc.end;
-        } else if (parent.type === "Property" || parent.type === "MethodDefinition") {
-            start = parent.loc.start;
-            end = getOpeningParenOfParams(node, sourceCode).loc.start;
         } else {
             start = node.loc.start;
             end = getOpeningParenOfParams(node, sourceCode).loc.start;
@@ -1573,9 +1661,9 @@ module.exports = {
         return sourceCode.getText().slice(leftToken.range[0], rightToken.range[1]);
     },
 
-    /*
+    /**
      * Determine if a node has a possibility to be an Error object
-     * @param  {ASTNode} node  ASTNode to check
+     * @param {ASTNode} node ASTNode to check
      * @returns {boolean} True if there is a chance it contains an Error obj
      */
     couldBeError(node) {
@@ -1745,6 +1833,10 @@ module.exports = {
             return true;
         }
 
+        if (rightToken.type === "PrivateIdentifier") {
+            return true;
+        }
+
         return false;
     },
 
@@ -1805,5 +1897,6 @@ module.exports = {
     isSpecificMemberAccess,
     equalLiteralValue,
     isSameReference,
-    isLogicalAssignmentOperator
+    isLogicalAssignmentOperator,
+    getSwitchCaseColonToken
 };
index d426d85c59a7b385c0b7163d2e5c38c57fb0bc48..7f116a2684fef77d354404734fb5527ef0a4e385 100644 (file)
@@ -14,10 +14,10 @@ const debug = require("debug")("eslint:rules");
  * const rules = new LazyLoadingRuleMap([
  *     ["eqeqeq", () => require("eqeqeq")],
  *     ["semi", () => require("semi")],
- *     ["no-unused-vars", () => require("no-unused-vars")],
- * ])
+ *     ["no-unused-vars", () => require("no-unused-vars")]
+ * ]);
  *
- * rules.get("semi") // call `() => require("semi")` here.
+ * rules.get("semi"); // call `() => require("semi")` here.
  *
  * @extends {Map<string, () => Rule>}
  */
index 9ec6938e35af7d3a85b1429f1bd4738f2d86fa5b..824410b33704d892efbf6fde88a579353e401b30 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * @fileoverview Validates JSDoc comments are syntactically correct
  * @author Nicholas C. Zakas
+ * @deprecated in ESLint v5.10.0
  */
 "use strict";
 
@@ -20,7 +21,6 @@ module.exports = {
 
         docs: {
             description: "enforce valid JSDoc comments",
-            category: "Possible Errors",
             recommended: false,
             url: "https://eslint.org/docs/rules/valid-jsdoc"
         },
index a0f20f74d0ec6149815214d9a6f3d69a7e036172..33b64f56cc3857c16b2b815acd91dcd5994032f8 100644 (file)
@@ -14,7 +14,6 @@ module.exports = {
 
         docs: {
             description: "enforce comparing `typeof` expressions against valid strings",
-            category: "Possible Errors",
             recommended: true,
             url: "https://eslint.org/docs/rules/valid-typeof"
         },
index 28ddae442b517a43f66a709dfa9d8e104c6db70f..0f95d58dbc670e55a07f5caf2b230945fdd652c2 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "require `var` declarations be placed at the top of their containing scope",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/vars-on-top"
         },
@@ -32,8 +31,8 @@ module.exports = {
         // Helpers
         //--------------------------------------------------------------------------
 
-        // eslint-disable-next-line jsdoc/require-description
         /**
+         * Has AST suggesting a directive.
          * @param {ASTNode} node any node
          * @returns {boolean} whether the given node structurally represents a directive
          */
@@ -78,10 +77,12 @@ module.exports = {
             const l = statements.length;
             let i = 0;
 
-            // skip over directives
-            for (; i < l; ++i) {
-                if (!looksLikeDirective(statements[i]) && !looksLikeImport(statements[i])) {
-                    break;
+            // Skip over directives and imports. Static blocks don't have either.
+            if (node.parent.type !== "StaticBlock") {
+                for (; i < l; ++i) {
+                    if (!looksLikeDirective(statements[i]) && !looksLikeImport(statements[i])) {
+                        break;
+                    }
                 }
             }
 
@@ -112,16 +113,27 @@ module.exports = {
         /**
          * Checks whether variable is on top at functional block scope level
          * @param {ASTNode} node The node to check
-         * @param {ASTNode} parent Parent of the node
-         * @param {ASTNode} grandParent Parent of the node's parent
          * @returns {void}
          */
-        function blockScopeVarCheck(node, parent, grandParent) {
-            if (!(/Function/u.test(grandParent.type) &&
-                    parent.type === "BlockStatement" &&
-                    isVarOnTop(node, parent.body))) {
-                context.report({ node, messageId: "top" });
+        function blockScopeVarCheck(node) {
+            const { parent } = node;
+
+            if (
+                parent.type === "BlockStatement" &&
+                /Function/u.test(parent.parent.type) &&
+                isVarOnTop(node, parent.body)
+            ) {
+                return;
             }
+
+            if (
+                parent.type === "StaticBlock" &&
+                isVarOnTop(node, parent.body)
+            ) {
+                return;
+            }
+
+            context.report({ node, messageId: "top" });
         }
 
         //--------------------------------------------------------------------------
@@ -135,7 +147,7 @@ module.exports = {
                 } else if (node.parent.type === "Program") {
                     globalVarCheck(node, node.parent);
                 } else {
-                    blockScopeVarCheck(node, node.parent, node.parent.parent);
+                    blockScopeVarCheck(node);
                 }
             }
         };
index 07e5b84a4a5ae45873152b4b34fe5ccae31dd9d0..498d7bd2842e2928594886db40e80e2ae353410a 100644 (file)
@@ -43,7 +43,6 @@ module.exports = {
 
         docs: {
             description: "require parentheses around immediate `function` invocations",
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/wrap-iife"
         },
index 4ecbcecbbeb0b394b787a5772de935702dc5f3c8..945eb5ecea1ff076f12cf59d4865fe0dabc82b9e 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "require parenthesis around regex literals",
-            category: "Stylistic Issues",
             recommended: false,
             url: "https://eslint.org/docs/rules/wrap-regex"
         },
index 20b8e9ea91eb2ec27f9ae2a60cf5c9303eed3387..8c3eefa4acd71030796f115f0fbde85a5c32b5d9 100644 (file)
@@ -15,7 +15,6 @@ module.exports = {
 
         docs: {
             description: "require or disallow spacing around the `*` in `yield*` expressions",
-            category: "ECMAScript 6",
             recommended: false,
             url: "https://eslint.org/docs/rules/yield-star-spacing"
         },
index 2fca7571113ecf24fea9385bdda21ef6d83f39f8..2d018dc3df1253f1c6351e90e028dd0a3d1bddb3 100644 (file)
@@ -121,7 +121,6 @@ module.exports = {
 
         docs: {
             description: 'require or disallow "Yoda" conditions',
-            category: "Best Practices",
             recommended: false,
             url: "https://eslint.org/docs/rules/yoda"
         },
index 3fb0fbdd33c146f1f279ca3c173c7cc5a3abc7d6..f4f6a62183d49514df73913969ebb0e5fcc9e297 100644 (file)
@@ -27,7 +27,7 @@ module.exports = (additionalOptions = {}) => {
     });
 
     ajv.addMetaSchema(metaSchema);
-    // eslint-disable-next-line no-underscore-dangle
+    // eslint-disable-next-line no-underscore-dangle -- Ajv's API
     ajv._opts.defaultMeta = metaSchema.id;
 
     return ajv;
index 03b32f1c9183ed495a1e5428106a990f9e951b58..47353ac4814b72cc77cdbf5c3dc94b05dda034d0 100644 (file)
 const
     util = require("util"),
     configSchema = require("../../conf/config-schema"),
-    BuiltInEnvironments = require("@eslint/eslintrc/conf/environments"),
     BuiltInRules = require("../rules"),
-    ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
+    {
+        Legacy: {
+            ConfigOps,
+            environments: BuiltInEnvironments
+        }
+    } = require("@eslint/eslintrc"),
     { emitDeprecationWarning } = require("./deprecation-warnings");
 
 const ajv = require("./ajv")();
@@ -80,6 +84,7 @@ function getRuleOptionsSchema(rule) {
 /**
  * Validates a rule's severity and returns the severity value. Throws an error if the severity is invalid.
  * @param {options} options The given options for the rule.
+ * @throws {Error} Wrong severity value.
  * @returns {number|string} The rule's severity value
  */
 function validateRuleSeverity(options) {
@@ -98,6 +103,7 @@ function validateRuleSeverity(options) {
  * Validates the non-severity options passed to a rule, based on its schema.
  * @param {{create: Function}} rule The rule to validate
  * @param {Array} localOptions The options for the rule, excluding severity
+ * @throws {Error} Any rule validation errors.
  * @returns {void}
  */
 function validateRuleSchema(rule, localOptions) {
@@ -128,6 +134,7 @@ function validateRuleSchema(rule, localOptions) {
  * @param {Array|number} options The given options for the rule.
  * @param {string|null} source The name of the configuration source to report in any errors. If null or undefined,
  * no source is prepended to the message.
+ * @throws {Error} Upon any bad rule configuration.
  * @returns {void}
  */
 function validateRuleOptions(rule, ruleId, options, source = null) {
@@ -152,7 +159,7 @@ function validateRuleOptions(rule, ruleId, options, source = null) {
  * Validates an environment object
  * @param {Object} environment The environment config object to validate.
  * @param {string} source The name of the configuration source to report in any errors.
- * @param {function(envId:string): Object} [getAdditionalEnv] A map from strings to loaded environments.
+ * @param {(envId:string) => Object} [getAdditionalEnv] A map from strings to loaded environments.
  * @returns {void}
  */
 function validateEnvironment(
@@ -181,7 +188,7 @@ function validateEnvironment(
  * Validates a rules config object
  * @param {Object} rulesConfig The rules config object to validate.
  * @param {string} source The name of the configuration source to report in any errors.
- * @param {function(ruleId:string): Object} getAdditionalRule A map from strings to loaded rules
+ * @param {(ruleId:string) => Object} getAdditionalRule A map from strings to loaded rules
  * @returns {void}
  */
 function validateRules(
@@ -225,7 +232,8 @@ function validateGlobals(globalsConfig, source = null) {
  * Validate `processor` configuration.
  * @param {string|undefined} processorName The processor name.
  * @param {string} source The name of config file.
- * @param {function(id:string): Processor} getProcessor The getter of defined processors.
+ * @param {(id:string) => Processor} getProcessor The getter of defined processors.
+ * @throws {Error} For invalid processor configuration.
  * @returns {void}
  */
 function validateProcessor(processorName, source, getProcessor) {
@@ -264,6 +272,7 @@ function formatErrors(errors) {
  * Validates the top level properties of the config object.
  * @param {Object} config The config object to validate.
  * @param {string} source The name of the configuration source to report in any errors.
+ * @throws {Error} For any config invalid per the schema.
  * @returns {void}
  */
 function validateConfigSchema(config, source = null) {
@@ -282,8 +291,8 @@ function validateConfigSchema(config, source = null) {
  * Validates an entire config object.
  * @param {Object} config The config object to validate.
  * @param {string} source The name of the configuration source to report in any errors.
- * @param {function(ruleId:string): Object} [getAdditionalRule] A map from strings to loaded rules.
- * @param {function(envId:string): Object} [getAdditionalEnv] A map from strings to loaded envs.
+ * @param {(ruleId:string) => Object} [getAdditionalRule] A map from strings to loaded rules.
+ * @param {(envId:string) => Object} [getAdditionalEnv] A map from strings to loaded envs.
  * @returns {void}
  */
 function validate(config, source, getAdditionalRule, getAdditionalEnv) {
index 6aa1f5a1e59c77b630f0f17ef74440a3cc1e158d..7f06a74e1423ca2bcec07bee5d6ee411109ed530 100644 (file)
@@ -5,7 +5,7 @@
 
 "use strict";
 
-/* eslint no-console: "off" */
+/* eslint no-console: "off" -- Logging util */
 
 /* istanbul ignore next */
 module.exports = {
index cd743f3795b2daad89a2983c8d6382b882df18d3..18a694983c19f52f9562db6e932a7b71f070dfc6 100644 (file)
 
 "use strict";
 
-const Module = require("module");
-
-/*
- * `Module.createRequire` is added in v12.2.0. It supports URL as well.
- * We only support the case where the argument is a filepath, not a URL.
- */
-// eslint-disable-next-line node/no-unsupported-features/node-builtins, node/no-deprecated-api
-const createRequire = Module.createRequire || Module.createRequireFromPath;
+const { createRequire } = require("module");
 
 module.exports = {
 
@@ -33,6 +26,7 @@ module.exports = {
      * @param {string} moduleName The name of a Node module, or a path to a Node module.
      * @param {string} relativeToPath An absolute path indicating the module that `moduleName` should be resolved relative to. This must be
      * a file rather than a directory, but the file need not actually exist.
+     * @throws {Error} Any error from `module.createRequire` or its `resolve`.
      * @returns {string} The absolute path that would result from calling `require.resolve(moduleName)` in a file located at `relativeToPath`
      */
     resolve(moduleName, relativeToPath) {
index aa5eff756a8cd02188bf481f6ca022c017858a51..56c0898be5475c00459810437a66cc3cc95699b2 100644 (file)
@@ -40,6 +40,7 @@ function environment() {
      * Synchronously executes a shell command and formats the result.
      * @param {string} cmd The command to execute.
      * @param {Array} args The arguments to be executed with the command.
+     * @throws {Error} As may be collected by `cross-spawn.sync`.
      * @returns {string} The version returned by the command.
      */
     function execCommand(cmd, args) {
@@ -73,6 +74,7 @@ function environment() {
     /**
      * Gets bin version.
      * @param {string} bin The bin to check.
+     * @throws {Error} As may be collected by `cross-spawn.sync`.
      * @returns {string} The normalized version returned by the command.
      */
     function getBinVersion(bin) {
@@ -90,6 +92,7 @@ function environment() {
      * Gets installed npm package version.
      * @param {string} pkg The package to check.
      * @param {boolean} global Whether to check globally or not.
+     * @throws {Error} As may be collected by `cross-spawn.sync`.
      * @returns {string} The normalized version returned by the command.
      */
     function getNpmPackageVersion(pkg, { global = false } = {}) {
index 32f76779507c5a361d2df73e52151453104ea36d..be0ed59a4f9cf49ff1459f295d2cd57101d6ec06 100644 (file)
@@ -65,16 +65,16 @@ class Traverser {
         this._leave = null;
     }
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
+     * Gives current node.
      * @returns {ASTNode} The current node.
      */
     current() {
         return this._current;
     }
 
-    // eslint-disable-next-line jsdoc/require-description
     /**
+     * Gives a a copy of the ancestor nodes.
      * @returns {ASTNode[]} The ancestor nodes.
      */
     parents() {
index c3b76e42d5f075a21b30de5d2dfb770546849ece..c497f783be5c464ecc5c9e5d904843d1215f9f39 100644 (file)
@@ -21,7 +21,7 @@ module.exports = {};
 /**
  * @typedef {Object} ParserOptions
  * @property {EcmaFeatures} [ecmaFeatures] The optional features.
- * @property {3|5|6|7|8|9|10|11|12|2015|2016|2017|2018|2019|2020|2021} [ecmaVersion] The ECMAScript version (or revision number).
+ * @property {3|5|6|7|8|9|10|11|12|13|2015|2016|2017|2018|2019|2020|2021|2022} [ecmaVersion] The ECMAScript version (or revision number).
  * @property {"script"|"module"} [sourceType] The source code type.
  */
 
@@ -83,12 +83,12 @@ module.exports = {};
 
 /**
  * @typedef {Object} LintMessage
- * @property {number} column The 1-based column number.
+ * @property {number|undefined} column The 1-based column number.
  * @property {number} [endColumn] The 1-based column number of the end location.
  * @property {number} [endLine] The 1-based line number of the end location.
  * @property {boolean} fatal If `true` then this is a fatal error.
  * @property {{range:[number,number], text:string}} [fix] Information for autofix.
- * @property {number} line The 1-based line number.
+ * @property {number|undefined} line The 1-based line number.
  * @property {string} message The error message.
  * @property {string|null} ruleId The ID of the rule which makes this message.
  * @property {0|1|2} severity The severity of this message.
index c13ce29b877a4aa0f83647c5d799408946aaf2c4..49b2820a846a0f4c1b7f65328e87ed4f35c4c1f7 100644 (file)
@@ -143,10 +143,12 @@ function isSpaceBetween(sourceCode, first, second, checkInsideOfJSXText) {
 // Public Interface
 //------------------------------------------------------------------------------
 
+/**
+ * Represents parsed source code.
+ */
 class SourceCode extends TokenStore {
 
     /**
-     * Represents parsed source code.
      * @param {string|Object} textOrConfig The source code text or config object.
      * @param {string} textOrConfig.text The source code text.
      * @param {ASTNode} textOrConfig.ast The Program node of the AST representing the code. This AST should be created from the text that BOM was stripped.
@@ -175,20 +177,20 @@ class SourceCode extends TokenStore {
 
         /**
          * The flag to indicate that the source code has Unicode BOM.
-         * @type boolean
+         * @type {boolean}
          */
         this.hasBOM = (text.charCodeAt(0) === 0xFEFF);
 
         /**
          * The original text source code.
          * BOM was stripped from this text.
-         * @type string
+         * @type {string}
          */
         this.text = (this.hasBOM ? text.slice(1) : text);
 
         /**
          * The parsed AST for the source code.
-         * @type ASTNode
+         * @type {ASTNode}
          */
         this.ast = ast;
 
@@ -223,7 +225,7 @@ class SourceCode extends TokenStore {
         /**
          * The source code split into lines according to ECMA-262 specification.
          * This is done to avoid each rule needing to do so separately.
-         * @type string[]
+         * @type {string[]}
          */
         this.lines = [];
         this.lineStartIndices = [0];
@@ -349,7 +351,7 @@ class SourceCode extends TokenStore {
             let currentToken = this.getTokenBefore(node, { includeComments: true });
 
             while (currentToken && isCommentToken(currentToken)) {
-                if (node.parent && (currentToken.start < node.parent.start)) {
+                if (node.parent && node.parent.type !== "Program" && (currentToken.start < node.parent.start)) {
                     break;
                 }
                 comments.leading.push(currentToken);
@@ -361,7 +363,7 @@ class SourceCode extends TokenStore {
             currentToken = this.getTokenAfter(node, { includeComments: true });
 
             while (currentToken && isCommentToken(currentToken)) {
-                if (node.parent && (currentToken.end > node.parent.end)) {
+                if (node.parent && node.parent.type !== "Program" && (currentToken.end > node.parent.end)) {
                     break;
                 }
                 comments.trailing.push(currentToken);
@@ -506,6 +508,7 @@ class SourceCode extends TokenStore {
     /**
      * Converts a source text index into a (line, column) pair.
      * @param {number} index The index of a character in a file
+     * @throws {TypeError} If non-numeric index or index out of range.
      * @returns {Object} A {line, column} location object with a 0-indexed column
      * @public
      */
@@ -545,6 +548,9 @@ class SourceCode extends TokenStore {
      * @param {Object} loc A line/column location
      * @param {number} loc.line The line number of the location (1-indexed)
      * @param {number} loc.column The column number of the location (0-indexed)
+     * @throws {TypeError|RangeError} If `loc` is not an object with a numeric
+     *   `line` and `column`, if the `line` is less than or equal to zero or
+     *   the line or column is out of the expected range.
      * @returns {number} The range index of the location in the file.
      * @public
      */
index 4e1595c6dcb8a35ffab970d9a519352f0283f71e..494762acdfae334791280ef03c18a5861c0224a7 100644 (file)
@@ -70,7 +70,7 @@ module.exports = class Cursor {
      * @abstract
      */
     /* istanbul ignore next */
-    moveNext() { // eslint-disable-line class-methods-use-this
+    moveNext() { // eslint-disable-line class-methods-use-this -- Unused
         throw new Error("Not implemented.");
     }
 };
diff --git a/eslint/lib/unsupported-api.js b/eslint/lib/unsupported-api.js
new file mode 100644 (file)
index 0000000..110b35a
--- /dev/null
@@ -0,0 +1,23 @@
+/**
+ * @fileoverview APIs that are not officially supported by ESLint.
+ *      These APIs may change or be removed at any time. Use at your
+ *      own risk.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
+const { FileEnumerator } = require("./cli-engine/file-enumerator");
+
+//-----------------------------------------------------------------------------
+// Exports
+//-----------------------------------------------------------------------------
+
+module.exports = {
+    builtinRules: require("./rules"),
+    FileEnumerator
+};
index 601033eb44e5da3f448f4db4ad2cdac1162ecfab..c3dfa1bb3703e4822482219ae28792d97ec63c17 100644 (file)
@@ -1,12 +1,17 @@
 {
   "name": "eslint",
-  "version": "7.28.0",
+  "version": "8.3.0",
   "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
   "description": "An AST-based pattern checker for JavaScript.",
   "bin": {
     "eslint": "./bin/eslint.js"
   },
   "main": "./lib/api.js",
+  "exports": {
+    "./package.json": "./package.json",
+    ".": "./lib/api.js",
+    "./use-at-your-own-risk": "./lib/unsupported-api.js"
+  },
   "scripts": {
     "test": "node Makefile.js test",
     "test:cli": "mocha",
@@ -18,7 +23,6 @@
     "generate-betarelease": "node Makefile.js generatePrerelease -- beta",
     "generate-rcrelease": "node Makefile.js generatePrerelease -- rc",
     "publish-release": "node Makefile.js publishRelease",
-    "docs": "node Makefile.js docs",
     "gensite": "node Makefile.js gensite",
     "webpack": "node Makefile.js webpack",
     "perf": "node Makefile.js perf"
   "homepage": "https://eslint.org",
   "bugs": "https://github.com/eslint/eslint/issues/",
   "dependencies": {
-    "@babel/code-frame": "7.12.11",
-    "@eslint/eslintrc": "^0.4.2",
+    "@eslint/eslintrc": "^1.0.4",
+    "@humanwhocodes/config-array": "^0.6.0",
     "ajv": "^6.10.0",
     "chalk": "^4.0.0",
     "cross-spawn": "^7.0.2",
-    "debug": "^4.0.1",
+    "debug": "^4.3.2",
     "doctrine": "^3.0.0",
     "enquirer": "^2.3.5",
     "escape-string-regexp": "^4.0.0",
-    "eslint-scope": "^5.1.1",
-    "eslint-utils": "^2.1.0",
-    "eslint-visitor-keys": "^2.0.0",
-    "espree": "^7.3.1",
+    "eslint-scope": "^7.1.0",
+    "eslint-utils": "^3.0.0",
+    "eslint-visitor-keys": "^3.1.0",
+    "espree": "^9.1.0",
     "esquery": "^1.4.0",
     "esutils": "^2.0.2",
     "fast-deep-equal": "^3.1.3",
     "file-entry-cache": "^6.0.1",
     "functional-red-black-tree": "^1.0.1",
-    "glob-parent": "^5.1.2",
+    "glob-parent": "^6.0.1",
     "globals": "^13.6.0",
     "ignore": "^4.0.6",
     "import-fresh": "^3.0.0",
     "imurmurhash": "^0.1.4",
     "is-glob": "^4.0.0",
-    "js-yaml": "^3.13.1",
+    "js-yaml": "^4.1.0",
     "json-stable-stringify-without-jsonify": "^1.0.1",
     "levn": "^0.4.1",
     "lodash.merge": "^4.6.2",
     "natural-compare": "^1.4.0",
     "optionator": "^0.9.1",
     "progress": "^2.0.0",
-    "regexpp": "^3.1.0",
+    "regexpp": "^3.2.0",
     "semver": "^7.2.1",
-    "strip-ansi": "^6.0.0",
+    "strip-ansi": "^6.0.1",
     "strip-json-comments": "^3.1.0",
-    "table": "^6.0.9",
     "text-table": "^0.2.0",
     "v8-compile-cache": "^2.0.3"
   },
     "cheerio": "^0.22.0",
     "common-tags": "^1.8.0",
     "core-js": "^3.1.3",
-    "dateformat": "^3.0.3",
+    "dateformat": "^4.5.1",
     "ejs": "^3.0.2",
     "eslint": "file:.",
     "eslint-config-eslint": "file:packages/eslint-config-eslint",
-    "eslint-plugin-eslint-plugin": "^3.0.3",
+    "eslint-plugin-eslint-comments": "^3.2.0",
+    "eslint-plugin-eslint-plugin": "^4.0.1",
     "eslint-plugin-internal-rules": "file:tools/internal-rules",
-    "eslint-plugin-jsdoc": "^25.4.3",
+    "eslint-plugin-jsdoc": "^37.0.0",
     "eslint-plugin-node": "^11.1.0",
-    "eslint-release": "^2.0.0",
+    "eslint-release": "^3.2.0",
     "eslump": "^3.0.0",
     "esprima": "^4.0.1",
-    "fs-teardown": "^0.1.0",
+    "fs-teardown": "^0.1.3",
     "glob": "^7.1.6",
     "jsdoc": "^3.5.5",
     "karma": "^6.1.1",
     "karma-mocha": "^2.0.1",
     "karma-mocha-reporter": "^2.2.5",
     "karma-webpack": "^5.0.0",
-    "lint-staged": "^10.1.2",
+    "lint-staged": "^11.0.0",
     "load-perf": "^0.2.0",
-    "markdownlint": "^0.19.0",
-    "markdownlint-cli": "^0.22.0",
+    "markdownlint": "^0.23.1",
+    "markdownlint-cli": "^0.28.1",
     "memfs": "^3.0.1",
     "mocha": "^8.3.2",
     "mocha-junit-reporter": "^2.0.0",
     "npm-license": "^0.3.3",
     "nyc": "^15.0.1",
     "proxyquire": "^2.0.1",
-    "puppeteer": "^7.1.0",
-    "recast": "^0.19.0",
+    "puppeteer": "^9.1.1",
+    "recast": "^0.20.4",
     "regenerator-runtime": "^0.13.2",
     "shelljs": "^0.8.2",
-    "sinon": "^9.0.1",
+    "sinon": "^11.0.0",
     "temp": "^0.9.0",
     "webpack": "^5.23.0",
     "webpack-cli": "^4.5.0",
   ],
   "license": "MIT",
   "engines": {
-    "node": "^10.12.0 || >=12.0.0"
+    "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
   }
 }
index a812a79d94d87839aa87beb81e24ee63182bc68c..71e1973b2e7d16b0d7c330760929a428e22db1dd 100644 (file)
@@ -5,7 +5,6 @@ tags:
   - release
   - <%- type %>
 ---
-# ESLint v<%= version %> released
 
 We just pushed ESLint v<%- version %>, which is a <%- type %> release upgrade of ESLint. This release <% if (type !== "patch") { %>adds some new features and <% } %>fixes several bugs found in the previous release.<% if (type === "major") { %> This release also has some breaking changes, so please read the following closely.<% } %>
 
index 3231ea44d74a83e806b6b42c84f3e434bf7287a1..b5583957f670822a4ccb68083f97abb44f3fbd9d 100644 (file)
@@ -6,7 +6,7 @@ layout: doc
 
 ESLint comes with several built-in formatters to control the appearance of the linting results, and supports third-party formatters as well.
 
-You can specify a formatter using the `--format` or `-f` flag on the command line. For example, `--format codeframe` uses the `codeframe` formatter.
+You can specify a formatter using the `--format` or `-f` flag on the command line. For example, `--format json` uses the `json` formatter.
 
 The built-in formatter options are:
 
index ddacfc7bec6f1cffb2c21362f6696e36416a25dc..8242096dc90389631fa3d4e2b60fd305715aea9e 100644 (file)
 /**
- * @fileoverview Define classes what use the in-memory file system.
- *
- * This provides utilities to test `ConfigArrayFactory`,
- * `CascadingConfigArrayFactory`, `FileEnumerator`, `CLIEngine`, and `ESLint`.
- *
- * - `defineConfigArrayFactoryWithInMemoryFileSystem({ cwd, files })`
- * - `defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ cwd, files })`
- * - `defineFileEnumeratorWithInMemoryFileSystem({ cwd, files })`
- * - `defineCLIEngineWithInMemoryFileSystem({ cwd, files })`
- * - `defineESLintWithInMemoryFileSystem({ cwd, files })`
- *
- * Those functions define correspond classes with the in-memory file system.
- * Those search config files, parsers, and plugins in the `files` option via the
- * in-memory file system.
- *
- * For each test case, it makes more readable if we define minimal files the
- * test case requires.
- *
- * For example:
- *
- * ```js
- * const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({
- *     files: {
- *         "node_modules/eslint-config-foo/index.js": `
- *             module.exports = {
- *                 parser: "./parser",
- *                 rules: {
- *                     "no-undef": "error"
- *                 }
- *             }
- *         `,
- *         "node_modules/eslint-config-foo/parser.js": `
- *             module.exports = {
- *                 parse() {}
- *             }
- *         `,
- *         ".eslintrc.json": JSON.stringify({ root: true, extends: "foo" })
- *     }
- * });
- * const factory = new ConfigArrayFactory();
- * const config = factory.loadFile(".eslintrc.json");
- *
- * assert(config[0].name === ".eslintrc.json » eslint-config-foo");
- * assert(config[0].filePath === path.resolve("node_modules/eslint-config-foo/index.js"));
- * assert(config[0].parser.filePath === path.resolve("node_modules/eslint-config-foo/parser.js"));
- *
- * assert(config[1].name === ".eslintrc.json");
- * assert(config[1].filePath === path.resolve(".eslintrc.json"));
- * assert(config[1].root === true);
- * ```
- *
+ * @fileoverview in-memory file system.
  * @author Toru Nagashima <https://github.com/mysticatea>
  */
 "use strict";
 
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
 const path = require("path");
-const vm = require("vm");
 const { Volume, createFsFromVolume } = require("memfs");
-const Proxyquire = require("proxyquire/lib/proxyquire");
-
-const CascadingConfigArrayFactoryPath =
-    require.resolve("@eslint/eslintrc/lib/cascading-config-array-factory");
-const CLIEnginePath =
-    require.resolve("../../lib/cli-engine/cli-engine");
-const ConfigArrayFactoryPath =
-    require.resolve("@eslint/eslintrc/lib/config-array-factory");
-const FileEnumeratorPath =
-    require.resolve("../../lib/cli-engine/file-enumerator");
-const LoadRulesPath =
-    require.resolve("../../lib/cli-engine/load-rules");
-const ESLintPath =
-    require.resolve("../../lib/eslint/eslint");
-const ESLintAllPath =
-    require.resolve("../../conf/eslint-all");
-const ESLintRecommendedPath =
-    require.resolve("../../conf/eslint-recommended");
-
-// Ensure the needed files has been loaded and cached.
-require(CascadingConfigArrayFactoryPath);
-require(CLIEnginePath);
-require(ConfigArrayFactoryPath);
-require(FileEnumeratorPath);
-require(LoadRulesPath);
-require(ESLintPath);
-require("js-yaml");
-require("espree");
-
-// Override `_require` in order to throw runtime errors in stubs.
-const ERRORED = Symbol("errored");
-const proxyquire = new class extends Proxyquire {
-    _require(...args) {
-        const retv = super._require(...args); // eslint-disable-line no-underscore-dangle
-
-        if (retv[ERRORED]) {
-            throw retv[ERRORED];
-        }
-        return retv;
-    }
-}(module).noCallThru().noPreserveCache();
-
-// Separated (sandbox) context to compile fixture files.
-const context = vm.createContext();
-
-/**
- * Check if a given path is an existing file.
- * @param {import("fs")} fs The file system.
- * @param {string} filePath Tha path to a file to check.
- * @returns {boolean} `true` if the file existed.
- */
-function isExistingFile(fs, filePath) {
-    try {
-        return fs.statSync(filePath).isFile();
-    } catch {
-        return false;
-    }
-}
-
-/**
- * Get some paths to test.
- * @param {string} prefix The prefix to try.
- * @returns {string[]} The paths to test.
- */
-function getTestPaths(prefix) {
-    return [
-        path.join(prefix),
-        path.join(`${prefix}.js`),
-        path.join(prefix, "index.js")
-    ];
-}
-
-/**
- * Iterate the candidate paths of a given request to resolve.
- * @param {string} request Tha package name or file path to resolve.
- * @param {string} relativeTo Tha path to the file what called this resolving.
- * @returns {IterableIterator<string>} The candidate paths.
- */
-function *iterateCandidatePaths(request, relativeTo) {
-    if (path.isAbsolute(request)) {
-        yield* getTestPaths(request);
-        return;
-    }
-    if (/^\.{1,2}[/\\]/u.test(request)) {
-        yield* getTestPaths(path.resolve(path.dirname(relativeTo), request));
-        return;
-    }
-
-    let prevPath = path.resolve(relativeTo);
-    let dirPath = path.dirname(prevPath);
-
-    while (dirPath && dirPath !== prevPath) {
-        yield* getTestPaths(path.join(dirPath, "node_modules", request));
-        prevPath = dirPath;
-        dirPath = path.dirname(dirPath);
-    }
-}
-
-/**
- * Resolve a given module name or file path relatively in the given file system.
- * @param {import("fs")} fs The file system.
- * @param {string} request Tha package name or file path to resolve.
- * @param {string} relativeTo Tha path to the file what called this resolving.
- * @returns {void}
- */
-function fsResolve(fs, request, relativeTo) {
-    for (const filePath of iterateCandidatePaths(request, relativeTo)) {
-        if (isExistingFile(fs, filePath)) {
-            return filePath;
-        }
-    }
 
-    throw Object.assign(
-        new Error(`Cannot find module '${request}'`),
-        { code: "MODULE_NOT_FOUND" }
-    );
-}
-
-/**
- * Compile a JavaScript file.
- * This is used to compile only fixture files, so this is minimam.
- * @param {import("fs")} fs The file system.
- * @param {Object} stubs The stubs.
- * @param {string} filePath The path to a JavaScript file to compile.
- * @param {string} content The source code to compile.
- * @returns {any} The exported value.
- */
-function compile(fs, stubs, filePath, content) {
-    const code = `(function(exports, require, module, __filename, __dirname) { ${content} })`;
-    const f = vm.runInContext(code, context);
-    const exports = {};
-    const module = { exports };
-
-    f.call(
-        exports,
-        exports,
-        request => {
-            const modulePath = fsResolve(fs, request, filePath);
-            const stub = stubs[modulePath];
-
-            if (stub[ERRORED]) {
-                throw stub[ERRORED];
-            }
-            return stub;
-        },
-        module,
-        filePath,
-        path.dirname(filePath)
-    );
-
-    return module.exports;
-}
-
-/**
- * Import a given file path in the given file system.
- * @param {import("fs")} fs The file system.
- * @param {Object} stubs The stubs.
- * @param {string} absolutePath Tha file path to import.
- * @returns {void}
- */
-function fsImportFresh(fs, stubs, absolutePath) {
-    if (absolutePath === ESLintAllPath) {
-        return require(ESLintAllPath);
-    }
-    if (absolutePath === ESLintRecommendedPath) {
-        return require(ESLintRecommendedPath);
-    }
-
-    if (fs.existsSync(absolutePath)) {
-        return compile(
-            fs,
-            stubs,
-            absolutePath,
-            fs.readFileSync(absolutePath, "utf8")
-        );
-    }
-
-    throw Object.assign(
-        new Error(`Cannot find module '${absolutePath}'`),
-        { code: "MODULE_NOT_FOUND" }
-    );
-}
+//-----------------------------------------------------------------------------
+// Helpers
+//-----------------------------------------------------------------------------
 
 /**
  * Define in-memory file system.
@@ -285,257 +60,10 @@ function defineInMemoryFs({
     return fs;
 }
 
-/**
- * Define stubbed `ConfigArrayFactory` class what uses the in-memory file system.
- * @param {Object} options The options.
- * @param {() => string} [options.cwd] The current working directory.
- * @param {Object} [options.files] The initial files definition in the in-memory file system.
- * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"] }} The stubbed `ConfigArrayFactory` class.
- */
-function defineConfigArrayFactoryWithInMemoryFileSystem({
-    cwd = process.cwd,
-    files = {}
-} = {}) {
-    const fs = defineInMemoryFs({ cwd, files });
-    const RelativeModuleResolver = { resolve: fsResolve.bind(null, fs) };
-
-    /*
-     * Stubs for proxyquire.
-     * This contains the JavaScript files in `options.files`.
-     */
-    const stubs = {};
-
-    stubs.fs = fs;
-    stubs["import-fresh"] = fsImportFresh.bind(null, fs, stubs);
-    stubs["../shared/relative-module-resolver"] = RelativeModuleResolver;
-
-    /*
-     * Write all files to the in-memory file system and compile all JavaScript
-     * files then set to `stubs`.
-     */
-    (function initFiles(directoryPath, definition) {
-        for (const [filename, content] of Object.entries(definition)) {
-            const filePath = path.resolve(directoryPath, filename);
-
-            if (typeof content === "object") {
-                initFiles(filePath, content);
-                continue;
-            }
-
-            /*
-             * Compile then stub if this file is a JavaScript file.
-             * For parsers and plugins that `require()` will import.
-             */
-            if (path.extname(filePath) === ".js") {
-                Object.defineProperty(stubs, filePath, {
-                    configurable: true,
-                    enumerable: true,
-                    get() {
-                        let stub;
-
-                        try {
-                            stub = compile(fs, stubs, filePath, content);
-                        } catch (error) {
-                            stub = { [ERRORED]: error };
-                        }
-                        Object.defineProperty(stubs, filePath, {
-                            configurable: true,
-                            enumerable: true,
-                            value: stub
-                        });
-
-                        return stub;
-                    }
-                });
-            }
-        }
-    }(cwd(), files));
-
-    // Load the stubbed one.
-    const { ConfigArrayFactory } = proxyquire(ConfigArrayFactoryPath, stubs);
-
-    // Override the default cwd.
-    return {
-        fs,
-        stubs,
-        RelativeModuleResolver,
-        ConfigArrayFactory: cwd === process.cwd
-            ? ConfigArrayFactory
-            : class extends ConfigArrayFactory {
-                constructor(options) {
-                    super({ cwd: cwd(), ...options });
-                }
-            }
-    };
-}
-
-/**
- * Define stubbed `CascadingConfigArrayFactory` class what uses the in-memory file system.
- * @param {Object} options The options.
- * @param {() => string} [options.cwd] The current working directory.
- * @param {Object} [options.files] The initial files definition in the in-memory file system.
- * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"], CascadingConfigArrayFactory: import("../../../lib/cli-engine/cascading-config-array-factory")["CascadingConfigArrayFactory"] }} The stubbed `CascadingConfigArrayFactory` class.
- */
-function defineCascadingConfigArrayFactoryWithInMemoryFileSystem({
-    cwd = process.cwd,
-    files = {}
-} = {}) {
-    const { fs, stubs, RelativeModuleResolver, ConfigArrayFactory } =
-           defineConfigArrayFactoryWithInMemoryFileSystem({ cwd, files });
-    const loadRules = proxyquire(LoadRulesPath, stubs);
-    const { CascadingConfigArrayFactory } =
-        proxyquire(CascadingConfigArrayFactoryPath, {
-            "./config-array-factory": { ConfigArrayFactory },
-            "./load-rules": loadRules
-        });
-
-    // Override the default cwd.
-    return {
-        fs,
-        RelativeModuleResolver,
-        ConfigArrayFactory,
-        CascadingConfigArrayFactory: cwd === process.cwd
-            ? CascadingConfigArrayFactory
-            : class extends CascadingConfigArrayFactory {
-                constructor(options) {
-                    super({ cwd: cwd(), ...options });
-                }
-            }
-    };
-}
-
-/**
- * Define stubbed `FileEnumerator` class what uses the in-memory file system.
- * @param {Object} options The options.
- * @param {() => string} [options.cwd] The current working directory.
- * @param {Object} [options.files] The initial files definition in the in-memory file system.
- * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"], CascadingConfigArrayFactory: import("../../../lib/cli-engine/cascading-config-array-factory")["CascadingConfigArrayFactory"], FileEnumerator: import("../../../lib/cli-engine/file-enumerator")["FileEnumerator"] }} The stubbed `FileEnumerator` class.
- */
-function defineFileEnumeratorWithInMemoryFileSystem({
-    cwd = process.cwd,
-    files = {}
-} = {}) {
-    const {
-        fs,
-        RelativeModuleResolver,
-        ConfigArrayFactory,
-        CascadingConfigArrayFactory
-    } =
-        defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ cwd, files });
-    const { FileEnumerator } = proxyquire(FileEnumeratorPath, {
-        fs,
-        "./cascading-config-array-factory": { CascadingConfigArrayFactory }
-    });
-
-    // Override the default cwd.
-    return {
-        fs,
-        RelativeModuleResolver,
-        ConfigArrayFactory,
-        CascadingConfigArrayFactory,
-        FileEnumerator: cwd === process.cwd
-            ? FileEnumerator
-            : class extends FileEnumerator {
-                constructor(options) {
-                    super({ cwd: cwd(), ...options });
-                }
-            }
-    };
-}
-
-/**
- * Define stubbed `CLIEngine` class what uses the in-memory file system.
- * @param {Object} options The options.
- * @param {() => string} [options.cwd] The current working directory.
- * @param {Object} [options.files] The initial files definition in the in-memory file system.
- * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"], CascadingConfigArrayFactory: import("../../../lib/cli-engine/cascading-config-array-factory")["CascadingConfigArrayFactory"], FileEnumerator: import("../../../lib/cli-engine/file-enumerator")["FileEnumerator"], CLIEngine: import("../../../lib/cli-engine/cli-engine")["CLIEngine"], getCLIEngineInternalSlots: import("../../../lib/cli-engine/cli-engine")["getCLIEngineInternalSlots"] }} The stubbed `CLIEngine` class.
- */
-function defineCLIEngineWithInMemoryFileSystem({
-    cwd = process.cwd,
-    files = {}
-} = {}) {
-    const {
-        fs,
-        RelativeModuleResolver,
-        ConfigArrayFactory,
-        CascadingConfigArrayFactory,
-        FileEnumerator
-    } =
-        defineFileEnumeratorWithInMemoryFileSystem({ cwd, files });
-    const { CLIEngine, getCLIEngineInternalSlots } = proxyquire(CLIEnginePath, {
-        fs,
-        "./cascading-config-array-factory": { CascadingConfigArrayFactory },
-        "./file-enumerator": { FileEnumerator },
-        "../shared/relative-module-resolver": RelativeModuleResolver
-    });
-
-    // Override the default cwd.
-    return {
-        fs,
-        RelativeModuleResolver,
-        ConfigArrayFactory,
-        CascadingConfigArrayFactory,
-        FileEnumerator,
-        CLIEngine: cwd === process.cwd
-            ? CLIEngine
-            : class extends CLIEngine {
-                constructor(options) {
-                    super({ cwd: cwd(), ...options });
-                }
-            },
-        getCLIEngineInternalSlots
-    };
-}
-
-/**
- * Define stubbed `ESLint` class that uses the in-memory file system.
- * @param {Object} options The options.
- * @param {() => string} [options.cwd] The current working directory.
- * @param {Object} [options.files] The initial files definition in the in-memory file system.
- * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"], CascadingConfigArrayFactory: import("../../lib/cli-engine/cascading-config-array-factory")["CascadingConfigArrayFactory"], FileEnumerator: import("../../lib/cli-engine/file-enumerator")["FileEnumerator"], ESLint: import("../../lib/eslint/eslint")["ESLint"], getCLIEngineInternalSlots: import("../../lib//eslint/eslint")["getESLintInternalSlots"] }} The stubbed `ESLint` class.
- */
-function defineESLintWithInMemoryFileSystem({
-    cwd = process.cwd,
-    files = {}
-} = {}) {
-    const {
-        fs,
-        RelativeModuleResolver,
-        ConfigArrayFactory,
-        CascadingConfigArrayFactory,
-        FileEnumerator,
-        CLIEngine,
-        getCLIEngineInternalSlots
-    } = defineCLIEngineWithInMemoryFileSystem({ cwd, files });
-    const { ESLint, getESLintPrivateMembers } = proxyquire(ESLintPath, {
-        "../cli-engine/cli-engine": { CLIEngine, getCLIEngineInternalSlots }
-    });
-
-    // Override the default cwd.
-    return {
-        fs,
-        RelativeModuleResolver,
-        ConfigArrayFactory,
-        CascadingConfigArrayFactory,
-        FileEnumerator,
-        CLIEngine,
-        getCLIEngineInternalSlots,
-        ESLint: cwd === process.cwd
-            ? ESLint
-            : class extends ESLint {
-                constructor(options) {
-                    super({ cwd: cwd(), ...options });
-                }
-            },
-        getESLintPrivateMembers
-    };
-}
+//-----------------------------------------------------------------------------
+// Exports
+//-----------------------------------------------------------------------------
 
 module.exports = {
-    defineInMemoryFs,
-    defineConfigArrayFactoryWithInMemoryFileSystem,
-    defineCascadingConfigArrayFactoryWithInMemoryFileSystem,
-    defineFileEnumeratorWithInMemoryFileSystem,
-    defineCLIEngineWithInMemoryFileSystem,
-    defineESLintWithInMemoryFileSystem
+    defineInMemoryFs
 };
index 7615613d25c21a2bb297521e494070d9d25ab515..08ef08ae6900c180512c61d1f02d2b3c616ff429 100644 (file)
@@ -9,12 +9,7 @@
 //-----------------------------------------------------------------------------
 
 const {
-    defineInMemoryFs,
-    defineConfigArrayFactoryWithInMemoryFileSystem,
-    defineCascadingConfigArrayFactoryWithInMemoryFileSystem,
-    defineFileEnumeratorWithInMemoryFileSystem,
-    defineCLIEngineWithInMemoryFileSystem,
-    defineESLintWithInMemoryFileSystem
+    defineInMemoryFs
 } = require("./in-memory-fs");
 
 const { createTeardown, addFile } = require("fs-teardown");
@@ -64,10 +59,5 @@ function createCustomTeardown({ cwd, files }) {
 module.exports = {
     unIndent,
     defineInMemoryFs,
-    defineConfigArrayFactoryWithInMemoryFileSystem,
-    defineCascadingConfigArrayFactoryWithInMemoryFileSystem,
-    defineFileEnumeratorWithInMemoryFileSystem,
-    defineCLIEngineWithInMemoryFileSystem,
-    defineESLintWithInMemoryFileSystem,
     createCustomTeardown
 };
index 7500d6611cee76f3fbe04284d242a92cb9c62cfa..38f132b583964b1895713942a8cf8241ac732efd 100644 (file)
@@ -5,13 +5,25 @@
 
 "use strict";
 
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
 const childProcess = require("child_process");
 const fs = require("fs");
 const assert = require("chai").assert;
 const path = require("path");
 
+//------------------------------------------------------------------------------
+// Data
+//------------------------------------------------------------------------------
+
 const EXECUTABLE_PATH = path.resolve(path.join(__dirname, "../../bin/eslint.js"));
 
+//-----------------------------------------------------------------------------
+// Helpers
+//-----------------------------------------------------------------------------
+
 /**
  * Returns a Promise for when a child process exits
  * @param {ChildProcess} exitingProcess The child process
@@ -25,7 +37,7 @@ function awaitExit(exitingProcess) {
  * Asserts that the exit code of a given child process will equal the given value.
  * @param {ChildProcess} exitingProcess The child process
  * @param {number} expectedExitCode The expected exit code of the child process
- * @returns {Promise} A Promise that fulfills if the exit code ends up matching, and rejects otherwise.
+ * @returns {Promise<void>} A Promise that fulfills if the exit code ends up matching, and rejects otherwise.
  */
 function assertExitCode(exitingProcess, expectedExitCode) {
     return awaitExit(exitingProcess).then(exitCode => {
@@ -48,6 +60,10 @@ function getOutput(runningProcess) {
     return awaitExit(runningProcess).then(() => ({ stdout, stderr }));
 }
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 describe("bin/eslint.js", () => {
     const forkedProcesses = new Set();
 
@@ -89,6 +105,7 @@ describe("bin/eslint.js", () => {
                     filePath: "<text>",
                     messages: [],
                     errorCount: 0,
+                    fatalErrorCount: 0,
                     warningCount: 0,
                     fixableErrorCount: 0,
                     fixableWarningCount: 0,
@@ -117,6 +134,14 @@ describe("bin/eslint.js", () => {
             return assertExitCode(child, 1);
         });
 
+        it("has exit code 2 if a syntax error is thrown when exit-on-fatal-error is true", () => {
+            const child = runESLint(["--stdin", "--no-eslintrc", "--exit-on-fatal-error"]);
+
+            child.stdin.write("This is not valid JS syntax.\n");
+            child.stdin.end();
+            return assertExitCode(child, 2);
+        });
+
         it("has exit code 1 if a linting error occurs", () => {
             const child = runESLint(["--stdin", "--no-eslintrc", "--rule", "semi:2"]);
 
@@ -366,10 +391,11 @@ describe("bin/eslint.js", () => {
             const child = runESLint(["--no-ignore", invalidConfig]);
             const exitCodeAssertion = assertExitCode(child, 2);
             const outputAssertion = getOutput(child).then(output => {
-                const expectedSubstring = ": bad indentation of a mapping entry at line";
-
                 assert.strictEqual(output.stdout, "");
-                assert.include(output.stderr, expectedSubstring);
+                assert.match(
+                    output.stderr,
+                    /: bad indentation of a mapping entry \(\d+:\d+\)/u // a part of the error message from `js-yaml` dependency
+                );
             });
 
             return Promise.all([exitCodeAssertion, outputAssertion]);
diff --git a/eslint/tests/fixtures/code-path-analysis/class-fields-init--arrow-function.js b/eslint/tests/fixtures/code-path-analysis/class-fields-init--arrow-function.js
new file mode 100644 (file)
index 0000000..9005f13
--- /dev/null
@@ -0,0 +1,37 @@
+/*expected
+initial->s3_1->final;
+*/
+/*expected
+initial->s2_1->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+
+class Foo { a = () => b }
+
+/*DOT
+digraph {
+node[shape=box,style="rounded,filled",fillcolor=white];
+initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+s3_1[label="ArrowFunctionExpression:enter\nIdentifier (b)\nArrowFunctionExpression:exit"];
+initial->s3_1->final;
+}
+
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="ArrowFunctionExpression"];
+    initial->s2_1->final;
+}
+
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nPropertyDefinition:enter\nIdentifier (a)\nArrowFunctionExpression\nPropertyDefinition:exit\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-fields-init--call-expression.js b/eslint/tests/fixtures/code-path-analysis/class-fields-init--call-expression.js
new file mode 100644 (file)
index 0000000..ec43f0a
--- /dev/null
@@ -0,0 +1,26 @@
+/*expected
+initial->s2_1->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+
+class Foo { a = b() }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="CallExpression:enter\nIdentifier (b)\nCallExpression:exit"];
+    initial->s2_1->final;
+}
+
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nPropertyDefinition:enter\nIdentifier (a)\nCallExpression\nPropertyDefinition:exit\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-fields-init--conditional.js b/eslint/tests/fixtures/code-path-analysis/class-fields-init--conditional.js
new file mode 100644 (file)
index 0000000..e763ca9
--- /dev/null
@@ -0,0 +1,32 @@
+/*expected
+initial->s2_1->s2_2->s2_4;
+s2_1->s2_3->s2_4->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+
+
+class Foo { a = b ? c : d }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="ConditionalExpression:enter\nIdentifier (b)"];
+    s2_2[label="Identifier (c)"];
+    s2_4[label="ConditionalExpression:exit"];
+    s2_3[label="Identifier (d)"];
+    initial->s2_1->s2_2->s2_4;
+    s2_1->s2_3->s2_4->final;
+}
+
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nPropertyDefinition:enter\nIdentifier (a)\nConditionalExpression\nPropertyDefinition:exit\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-fields-init--simple.js b/eslint/tests/fixtures/code-path-analysis/class-fields-init--simple.js
new file mode 100644 (file)
index 0000000..fd249c5
--- /dev/null
@@ -0,0 +1,28 @@
+/*expected
+initial->s2_1->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+
+class Foo {
+    a = b;
+}
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="Identifier (b)"];
+    initial->s2_1->final;
+}
+
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nPropertyDefinition:enter\nIdentifier (a)\nIdentifier (b)\nPropertyDefinition:exit\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-static-blocks--between-fields.js b/eslint/tests/fixtures/code-path-analysis/class-static-blocks--between-fields.js
new file mode 100644 (file)
index 0000000..9bf8edf
--- /dev/null
@@ -0,0 +1,65 @@
+/*expected
+initial->s2_1->s2_2->s2_3;
+s2_1->s2_3->final;
+*/
+/*expected
+initial->s3_1->s3_2->s3_3->s3_4;
+s3_1->s3_4;
+s3_2->s3_4->final;
+*/
+/*expected
+initial->s4_1->s4_2->s4_3->s4_4->s4_5;
+s4_1->s4_5;
+s4_2->s4_5;
+s4_3->s4_5->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+class Foo { bar = a || b; static { x || y || z } baz = p || q || r || s; }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="LogicalExpression:enter\nIdentifier (a)"];
+    s2_2[label="Identifier (b)"];
+    s2_3[label="LogicalExpression:exit"];
+    initial->s2_1->s2_2->s2_3;
+    s2_1->s2_3->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s3_1[label="StaticBlock:enter\nExpressionStatement:enter\nLogicalExpression:enter\nLogicalExpression:enter\nIdentifier (x)"];
+    s3_2[label="Identifier (y)\nLogicalExpression:exit"];
+    s3_3[label="Identifier (z)"];
+    s3_4[label="LogicalExpression:exit\nExpressionStatement:exit\nStaticBlock:exit"];
+    initial->s3_1->s3_2->s3_3->s3_4;
+    s3_1->s3_4;
+    s3_2->s3_4->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s4_1[label="LogicalExpression:enter\nLogicalExpression:enter\nLogicalExpression:enter\nIdentifier (p)"];
+    s4_2[label="Identifier (q)\nLogicalExpression:exit"];
+    s4_3[label="Identifier (r)\nLogicalExpression:exit"];
+    s4_4[label="Identifier (s)"];
+    s4_5[label="LogicalExpression:exit"];
+    initial->s4_1->s4_2->s4_3->s4_4->s4_5;
+    s4_1->s4_5;
+    s4_2->s4_5;
+    s4_3->s4_5->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nPropertyDefinition:enter\nIdentifier (bar)\nLogicalExpression\nPropertyDefinition:exit\nStaticBlock\nPropertyDefinition:enter\nIdentifier (baz)\nLogicalExpression\nPropertyDefinition:exit\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-static-blocks--between-methods.js b/eslint/tests/fixtures/code-path-analysis/class-static-blocks--between-methods.js
new file mode 100644 (file)
index 0000000..a6de690
--- /dev/null
@@ -0,0 +1,65 @@
+/*expected
+initial->s2_1->s2_2->s2_3;
+s2_1->s2_3->final;
+*/
+/*expected
+initial->s3_1->s3_2->s3_3->s3_4;
+s3_1->s3_4;
+s3_2->s3_4->final;
+*/
+/*expected
+initial->s4_1->s4_2->s4_3->s4_4->s4_5;
+s4_1->s4_5;
+s4_2->s4_5;
+s4_3->s4_5->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+class Foo { bar () { a || b } static { x || y || z } baz() { p || q || r || s } }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="FunctionExpression:enter\nBlockStatement:enter\nExpressionStatement:enter\nLogicalExpression:enter\nIdentifier (a)"];
+    s2_2[label="Identifier (b)"];
+    s2_3[label="LogicalExpression:exit\nExpressionStatement:exit\nBlockStatement:exit\nFunctionExpression:exit"];
+    initial->s2_1->s2_2->s2_3;
+    s2_1->s2_3->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s3_1[label="StaticBlock:enter\nExpressionStatement:enter\nLogicalExpression:enter\nLogicalExpression:enter\nIdentifier (x)"];
+    s3_2[label="Identifier (y)\nLogicalExpression:exit"];
+    s3_3[label="Identifier (z)"];
+    s3_4[label="LogicalExpression:exit\nExpressionStatement:exit\nStaticBlock:exit"];
+    initial->s3_1->s3_2->s3_3->s3_4;
+    s3_1->s3_4;
+    s3_2->s3_4->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s4_1[label="FunctionExpression:enter\nBlockStatement:enter\nExpressionStatement:enter\nLogicalExpression:enter\nLogicalExpression:enter\nLogicalExpression:enter\nIdentifier (p)"];
+    s4_2[label="Identifier (q)\nLogicalExpression:exit"];
+    s4_3[label="Identifier (r)\nLogicalExpression:exit"];
+    s4_4[label="Identifier (s)"];
+    s4_5[label="LogicalExpression:exit\nExpressionStatement:exit\nBlockStatement:exit\nFunctionExpression:exit"];
+    initial->s4_1->s4_2->s4_3->s4_4->s4_5;
+    s4_1->s4_5;
+    s4_2->s4_5;
+    s4_3->s4_5->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nMethodDefinition:enter\nIdentifier (bar)\nFunctionExpression\nMethodDefinition:exit\nStaticBlock\nMethodDefinition:enter\nIdentifier (baz)\nFunctionExpression\nMethodDefinition:exit\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-static-blocks--conditional.js b/eslint/tests/fixtures/code-path-analysis/class-static-blocks--conditional.js
new file mode 100644 (file)
index 0000000..bcdb4a7
--- /dev/null
@@ -0,0 +1,29 @@
+/*expected
+initial->s2_1->s2_2->s2_4;
+s2_1->s2_3->s2_4->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+class Foo { static { this.bar = a ? b : c; } }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="StaticBlock:enter\nExpressionStatement:enter\nAssignmentExpression:enter\nMemberExpression:enter\nThisExpression\nIdentifier (bar)\nMemberExpression:exit\nConditionalExpression:enter\nIdentifier (a)"];
+    s2_2[label="Identifier (b)"];
+    s2_4[label="ConditionalExpression:exit\nAssignmentExpression:exit\nExpressionStatement:exit\nStaticBlock:exit"];
+    s2_3[label="Identifier (c)"];
+    initial->s2_1->s2_2->s2_4;
+    s2_1->s2_3->s2_4->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nStaticBlock\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-static-blocks--empty.js b/eslint/tests/fixtures/code-path-analysis/class-static-blocks--empty.js
new file mode 100644 (file)
index 0000000..40a1e71
--- /dev/null
@@ -0,0 +1,24 @@
+/*expected
+initial->s2_1->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+class Foo { static {} }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="StaticBlock"];
+    initial->s2_1->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nStaticBlock\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-static-blocks--function-inside.js b/eslint/tests/fixtures/code-path-analysis/class-static-blocks--function-inside.js
new file mode 100644 (file)
index 0000000..49df347
--- /dev/null
@@ -0,0 +1,34 @@
+/*expected
+initial->s3_1->final;
+*/
+/*expected
+initial->s2_1->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+class Foo { static { (p) => {} } }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s3_1[label="ArrowFunctionExpression:enter\nIdentifier (p)\nBlockStatement\nArrowFunctionExpression:exit"];
+    initial->s3_1->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="StaticBlock:enter\nExpressionStatement:enter\nArrowFunctionExpression\nExpressionStatement:exit\nStaticBlock:exit"];
+    initial->s2_1->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nStaticBlock\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-static-blocks--if-else.js b/eslint/tests/fixtures/code-path-analysis/class-static-blocks--if-else.js
new file mode 100644 (file)
index 0000000..7b4a542
--- /dev/null
@@ -0,0 +1,29 @@
+/*expected
+initial->s2_1->s2_2->s2_4;
+s2_1->s2_3->s2_4->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+class Foo { static { if (bar) { this.baz = 1; } else { this.qux = 2; } } }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="StaticBlock:enter\nIfStatement:enter\nIdentifier (bar)"];
+    s2_2[label="BlockStatement:enter\nExpressionStatement:enter\nAssignmentExpression:enter\nMemberExpression:enter\nThisExpression\nIdentifier (baz)\nMemberExpression:exit\nLiteral (1)\nAssignmentExpression:exit\nExpressionStatement:exit\nBlockStatement:exit"];
+    s2_4[label="IfStatement:exit\nStaticBlock:exit"];
+    s2_3[label="BlockStatement:enter\nExpressionStatement:enter\nAssignmentExpression:enter\nMemberExpression:enter\nThisExpression\nIdentifier (qux)\nMemberExpression:exit\nLiteral (2)\nAssignmentExpression:exit\nExpressionStatement:exit\nBlockStatement:exit"];
+    initial->s2_1->s2_2->s2_4;
+    s2_1->s2_3->s2_4->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nStaticBlock\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-static-blocks--multiple-1.js b/eslint/tests/fixtures/code-path-analysis/class-static-blocks--multiple-1.js
new file mode 100644 (file)
index 0000000..67365fb
--- /dev/null
@@ -0,0 +1,34 @@
+/*expected
+initial->s2_1->final;
+*/
+/*expected
+initial->s3_1->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+class Foo { static { this.bar = 1; } static { this.baz = 2; } }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="StaticBlock:enter\nExpressionStatement:enter\nAssignmentExpression:enter\nMemberExpression:enter\nThisExpression\nIdentifier (bar)\nMemberExpression:exit\nLiteral (1)\nAssignmentExpression:exit\nExpressionStatement:exit\nStaticBlock:exit"];
+    initial->s2_1->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s3_1[label="StaticBlock:enter\nExpressionStatement:enter\nAssignmentExpression:enter\nMemberExpression:enter\nThisExpression\nIdentifier (baz)\nMemberExpression:exit\nLiteral (2)\nAssignmentExpression:exit\nExpressionStatement:exit\nStaticBlock:exit"];
+    initial->s3_1->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nStaticBlock\nStaticBlock\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-static-blocks--multiple-2.js b/eslint/tests/fixtures/code-path-analysis/class-static-blocks--multiple-2.js
new file mode 100644 (file)
index 0000000..218d18a
--- /dev/null
@@ -0,0 +1,45 @@
+/*expected
+initial->s2_1->s2_2->s2_3->s2_4;
+s2_1->s2_4;
+s2_2->s2_4->final;
+*/
+/*expected
+initial->s3_1->s3_2->s3_3;
+s3_1->s3_3->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+class Foo { static { x || y || z } static { p || q } }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="StaticBlock:enter\nExpressionStatement:enter\nLogicalExpression:enter\nLogicalExpression:enter\nIdentifier (x)"];
+    s2_2[label="Identifier (y)\nLogicalExpression:exit"];
+    s2_3[label="Identifier (z)"];
+    s2_4[label="LogicalExpression:exit\nExpressionStatement:exit\nStaticBlock:exit"];
+    initial->s2_1->s2_2->s2_3->s2_4;
+    s2_1->s2_4;
+    s2_2->s2_4->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s3_1[label="StaticBlock:enter\nExpressionStatement:enter\nLogicalExpression:enter\nIdentifier (p)"];
+    s3_2[label="Identifier (q)"];
+    s3_3[label="LogicalExpression:exit\nExpressionStatement:exit\nStaticBlock:exit"];
+    initial->s3_1->s3_2->s3_3;
+    s3_1->s3_3->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nStaticBlock\nStaticBlock\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/class-static-blocks--simple.js b/eslint/tests/fixtures/code-path-analysis/class-static-blocks--simple.js
new file mode 100644 (file)
index 0000000..06bb737
--- /dev/null
@@ -0,0 +1,24 @@
+/*expected
+initial->s2_1->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+class Foo { static { this.bar = baz; } }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="StaticBlock:enter\nExpressionStatement:enter\nAssignmentExpression:enter\nMemberExpression:enter\nThisExpression\nIdentifier (bar)\nMemberExpression:exit\nIdentifier (baz)\nAssignmentExpression:exit\nExpressionStatement:exit\nStaticBlock:exit"];
+    initial->s2_1->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nClassDeclaration:enter\nIdentifier (Foo)\nClassBody:enter\nStaticBlock\nClassBody:exit\nClassDeclaration:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/function--new.js b/eslint/tests/fixtures/code-path-analysis/function--new.js
new file mode 100644 (file)
index 0000000..1aa471e
--- /dev/null
@@ -0,0 +1,30 @@
+/*expected
+initial->s2_1->s2_2->s2_4;
+s2_1->s2_3->s2_4->final;
+*/
+/*expected
+initial->s1_1->final;
+*/
+function Foo() { this.a = b ? c : d }; new Foo()
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s2_1[label="FunctionDeclaration:enter\nIdentifier (Foo)\nBlockStatement:enter\nExpressionStatement:enter\nAssignmentExpression:enter\nMemberExpression:enter\nThisExpression\nIdentifier (a)\nMemberExpression:exit\nConditionalExpression:enter\nIdentifier (b)"];
+    s2_2[label="Identifier (c)"];
+    s2_4[label="ConditionalExpression:exit\nAssignmentExpression:exit\nExpressionStatement:exit\nBlockStatement:exit\nFunctionDeclaration:exit"];
+    s2_3[label="Identifier (d)"];
+    initial->s2_1->s2_2->s2_4;
+    s2_1->s2_3->s2_4->final;
+}
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nFunctionDeclaration\nEmptyStatement\nExpressionStatement:enter\nNewExpression:enter\nIdentifier (Foo)\nNewExpression:exit\nExpressionStatement:exit\nProgram:exit"];
+    initial->s1_1->final;
+}
+*/
diff --git a/eslint/tests/fixtures/code-path-analysis/object-literal--conditional.js b/eslint/tests/fixtures/code-path-analysis/object-literal--conditional.js
new file mode 100644 (file)
index 0000000..c5a4145
--- /dev/null
@@ -0,0 +1,20 @@
+/*expected
+initial->s1_1->s1_2->s1_4;
+s1_1->s1_3->s1_4->final;
+*/
+
+x = { a: b ? c : d }
+
+/*DOT
+digraph {
+    node[shape=box,style="rounded,filled",fillcolor=white];
+    initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];
+    s1_1[label="Program:enter\nExpressionStatement:enter\nAssignmentExpression:enter\nIdentifier (x)\nObjectExpression:enter\nProperty:enter\nIdentifier (a)\nConditionalExpression:enter\nIdentifier
+    (b)"];
+    s1_2[label="Identifier (c)"];
+    s1_4[label="ConditionalExpression:exit\nProperty:exit\nObjectExpression:exit\nAssignmentExpression:exit\nExpressionStatement:exit\nProgram:exit"];
+    s1_3[label="Identifier (d)"];
+    initial->s1_1->s1_2->s1_4;
+    s1_1->s1_3->s1_4->final;
+}*/
diff --git a/eslint/tests/fixtures/exit-on-fatal-error/fatal-error.js b/eslint/tests/fixtures/exit-on-fatal-error/fatal-error.js
new file mode 100644 (file)
index 0000000..53a47a5
--- /dev/null
@@ -0,0 +1 @@
+var foo  1
\ No newline at end of file
diff --git a/eslint/tests/fixtures/exit-on-fatal-error/no-fatal-error-rule-violation.js b/eslint/tests/fixtures/exit-on-fatal-error/no-fatal-error-rule-violation.js
new file mode 100644 (file)
index 0000000..a23ad4a
--- /dev/null
@@ -0,0 +1,3 @@
+/*eslint no-unused-vars: "error"*/
+
+var foo = 1;
\ No newline at end of file
diff --git a/eslint/tests/fixtures/exit-on-fatal-error/no-fatal-error.js b/eslint/tests/fixtures/exit-on-fatal-error/no-fatal-error.js
new file mode 100644 (file)
index 0000000..d5814b8
--- /dev/null
@@ -0,0 +1 @@
+var foo = 1;
\ No newline at end of file
diff --git a/eslint/tests/fixtures/parsers/empty-program-parser.js b/eslint/tests/fixtures/parsers/empty-program-parser.js
new file mode 100644 (file)
index 0000000..7f336cd
--- /dev/null
@@ -0,0 +1,27 @@
+"use strict";
+
+exports.parse = function (text, parserOptions) {
+    return {
+        "type": "Program",
+        "start": 0,
+        "end": 0,
+        "loc": {
+          "start": {
+            "line": 1,
+            "column": 0
+          },
+          "end": {
+            "line": 1,
+            "column": 0
+          }
+        },
+        "range": [
+          0,
+          0
+        ],
+        "body": [],
+        "sourceType": "script",
+        "comments": [],
+        "tokens": []
+      };
+};
index bc08e3da47e30f6dfea87fcebbe24f9ed266edc8..fb57e80473874242f2cc24575841d9e92df10d5c 100644 (file)
@@ -1,8 +1,7 @@
 "use strict";
 
 const assert = require("assert");
-const ScopeManager = require("eslint-scope/lib/scope-manager");
-const Referencer = require("eslint-scope/lib/referencer");
+const { ScopeManager, Referencer } = require("eslint-scope");
 const vk = require("eslint-visitor-keys");
 
 class EnhancedReferencer extends Referencer {
diff --git a/eslint/tests/fixtures/parsers/keyword-spacing/prefix-cast-operator-no-space.js b/eslint/tests/fixtures/parsers/keyword-spacing/prefix-cast-operator-no-space.js
new file mode 100644 (file)
index 0000000..9021044
--- /dev/null
@@ -0,0 +1,125 @@
+"use strict";
+
+/**
+ * Parser: @typescript-eslint/parser@5.0.0
+ *
+ * Source code:
+ * <Thing>this.blah
+ */
+
+exports.parse = () => ({
+    type: "Program",
+    body: [
+        {
+            type: "ExpressionStatement",
+            expression: {
+                type: "TSTypeAssertion",
+                typeAnnotation: {
+                    type: "TSTypeReference",
+                    typeName: {
+                        type: "Identifier",
+                        name: "Thing",
+                        range: [1, 6],
+                        loc: {
+                            start: { line: 1, column: 1 },
+                            end: { line: 1, column: 6 },
+                        },
+                    },
+                    range: [1, 6],
+                    loc: {
+                        start: { line: 1, column: 1 },
+                        end: { line: 1, column: 6 },
+                    },
+                },
+                expression: {
+                    type: "MemberExpression",
+                    object: {
+                        type: "ThisExpression",
+                        range: [7, 11],
+                        loc: {
+                            start: { line: 1, column: 7 },
+                            end: { line: 1, column: 11 },
+                        },
+                    },
+                    property: {
+                        type: "Identifier",
+                        name: "blah",
+                        range: [12, 16],
+                        loc: {
+                            start: { line: 1, column: 12 },
+                            end: { line: 1, column: 16 },
+                        },
+                    },
+                    computed: false,
+                    optional: false,
+                    range: [7, 16],
+                    loc: {
+                        start: { line: 1, column: 7 },
+                        end: { line: 1, column: 16 },
+                    },
+                },
+                range: [0, 16],
+                loc: {
+                    start: { line: 1, column: 0 },
+                    end: { line: 1, column: 16 },
+                },
+            },
+            range: [0, 16],
+            loc: {
+                start: { line: 1, column: 0 },
+                end: { line: 1, column: 16 },
+            },
+        },
+    ],
+    sourceType: "script",
+    range: [0, 16],
+    loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 16 } },
+    tokens: [
+        {
+            type: "Punctuator",
+            value: "<",
+            range: [0, 1],
+            loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } },
+        },
+        {
+            type: "Identifier",
+            value: "Thing",
+            range: [1, 6],
+            loc: { start: { line: 1, column: 1 }, end: { line: 1, column: 6 } },
+        },
+        {
+            type: "Punctuator",
+            value: ">",
+            range: [6, 7],
+            loc: { start: { line: 1, column: 6 }, end: { line: 1, column: 7 } },
+        },
+        {
+            type: "Keyword",
+            value: "this",
+            range: [7, 11],
+            loc: {
+                start: { line: 1, column: 7 },
+                end: { line: 1, column: 11 },
+            },
+        },
+        {
+            type: "Punctuator",
+            value: ".",
+            range: [11, 12],
+            loc: {
+                start: { line: 1, column: 11 },
+                end: { line: 1, column: 12 },
+            },
+        },
+        {
+            type: "Identifier",
+            value: "blah",
+            range: [12, 16],
+            loc: {
+                start: { line: 1, column: 12 },
+                end: { line: 1, column: 16 },
+            },
+        },
+    ],
+    comments: [],
+});
diff --git a/eslint/tests/fixtures/parsers/keyword-spacing/prefix-cast-operator-space.js b/eslint/tests/fixtures/parsers/keyword-spacing/prefix-cast-operator-space.js
new file mode 100644 (file)
index 0000000..00ae0f6
--- /dev/null
@@ -0,0 +1,125 @@
+"use strict";
+
+/**
+ * Parser: @typescript-eslint/parser@5.0.0
+ *
+ * Source code:
+ * <Thing> this.blah
+ */
+
+exports.parse = () => ({
+    type: "Program",
+    body: [
+        {
+            type: "ExpressionStatement",
+            expression: {
+                type: "TSTypeAssertion",
+                typeAnnotation: {
+                    type: "TSTypeReference",
+                    typeName: {
+                        type: "Identifier",
+                        name: "Thing",
+                        range: [1, 6],
+                        loc: {
+                            start: { line: 1, column: 1 },
+                            end: { line: 1, column: 6 },
+                        },
+                    },
+                    range: [1, 6],
+                    loc: {
+                        start: { line: 1, column: 1 },
+                        end: { line: 1, column: 6 },
+                    },
+                },
+                expression: {
+                    type: "MemberExpression",
+                    object: {
+                        type: "ThisExpression",
+                        range: [8, 12],
+                        loc: {
+                            start: { line: 1, column: 8 },
+                            end: { line: 1, column: 12 },
+                        },
+                    },
+                    property: {
+                        type: "Identifier",
+                        name: "blah",
+                        range: [13, 17],
+                        loc: {
+                            start: { line: 1, column: 13 },
+                            end: { line: 1, column: 17 },
+                        },
+                    },
+                    computed: false,
+                    optional: false,
+                    range: [8, 17],
+                    loc: {
+                        start: { line: 1, column: 8 },
+                        end: { line: 1, column: 17 },
+                    },
+                },
+                range: [0, 17],
+                loc: {
+                    start: { line: 1, column: 0 },
+                    end: { line: 1, column: 17 },
+                },
+            },
+            range: [0, 17],
+            loc: {
+                start: { line: 1, column: 0 },
+                end: { line: 1, column: 17 },
+            },
+        },
+    ],
+    sourceType: "script",
+    range: [0, 17],
+    loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 17 } },
+    tokens: [
+        {
+            type: "Punctuator",
+            value: "<",
+            range: [0, 1],
+            loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } },
+        },
+        {
+            type: "Identifier",
+            value: "Thing",
+            range: [1, 6],
+            loc: { start: { line: 1, column: 1 }, end: { line: 1, column: 6 } },
+        },
+        {
+            type: "Punctuator",
+            value: ">",
+            range: [6, 7],
+            loc: { start: { line: 1, column: 6 }, end: { line: 1, column: 7 } },
+        },
+        {
+            type: "Keyword",
+            value: "this",
+            range: [8, 12],
+            loc: {
+                start: { line: 1, column: 8 },
+                end: { line: 1, column: 12 },
+            },
+        },
+        {
+            type: "Punctuator",
+            value: ".",
+            range: [12, 13],
+            loc: {
+                start: { line: 1, column: 12 },
+                end: { line: 1, column: 13 },
+            },
+        },
+        {
+            type: "Identifier",
+            value: "blah",
+            range: [13, 17],
+            loc: {
+                start: { line: 1, column: 13 },
+                end: { line: 1, column: 17 },
+            },
+        },
+    ],
+    comments: [],
+});
index 528b4b0f4de90faee3c4bb60915c7a7cac90b0e6..fce60a56c4c206dd64d8ce96f0c8c37145a2def0 100644 (file)
@@ -1,14 +1,19 @@
-module.exports = function(context) {
-    return {
-        Program: function(node) {
-            context.report({
-                node: node,
-                message: "ERROR",
-                fix: function(fixer) {
-                    return fixer.insertTextAfter(node, "this is a syntax error.");
-                }
-            });
-        }
-    };
+module.exports = {
+    meta: {
+        schema: [],
+        fixable: "code"
+    },
+    create(context) {
+        return {
+            Program: function(node) {
+                context.report({
+                    node: node,
+                    message: "ERROR",
+                    fix: function(fixer) {
+                        return fixer.insertTextAfter(node, "this is a syntax error.");
+                    }
+                });
+            }
+        };
+    }
 };
-module.exports.schema = [];
index 9a64230cf7e6430be5692bcdb2cf3bebed137814..9cc26c9ecf26366f8eb1ed73c3786eb602b13e79 100644 (file)
@@ -1,5 +1,3 @@
 module.exports = function() {
-
-    "use strict";
-    return (null).something;
+    throw new Error("Boom!");
 };
index 9ed9ca71cbdc7d679e1bf2f4d3cd90f64d866c06..e692cae338640b56d80ae39ab8740a800a54d91b 100644 (file)
@@ -5,19 +5,24 @@
 
 "use strict";
 
-module.exports = context => {
-    return {
-        Program(node) {
-            context.report({
-                node,
-                message: "No programs allowed."
-            });
+module.exports = {
+    meta: {
+        fixable: "code"
+    },
+    create(context) {
+        return {
+            Program(node) {
+                context.report({
+                    node,
+                    message: "No programs allowed."
+                });
 
-            context.report({
-                node,
-                message: "Seriously, no programs allowed.",
-                fix: fixer => fixer.remove(node)
-            });
+                context.report({
+                    node,
+                    message: "Seriously, no programs allowed.",
+                    fix: fixer => fixer.remove(node)
+                });
+            }
         }
     }
 };
index df97e6dd1382818cc29a35792f4faef12257a828..ecedde04637b15c87ae1fb8774e78f12a60df63f 100644 (file)
@@ -1,6 +1,7 @@
 "use strict";
 
 module.exports.basic = {
+    meta: { hasSuggestions: true },
     create(context) {
         return {
             Identifier(node) {
@@ -25,7 +26,8 @@ module.exports.withMessageIds = {
             avoidFoo: "Avoid using identifiers named '{{ name }}'.",
             unused: "An unused key",
             renameFoo: "Rename identifier 'foo' to '{{ newName }}'"
-        }
+        },
+        hasSuggestions: true
     },
     create(context) {
         return {
@@ -57,4 +59,16 @@ module.exports.withMessageIds = {
     }
 };
 
-
+module.exports.withoutHasSuggestionsProperty = {
+    create(context) {
+        return {
+            Identifier(node) {
+                context.report({
+                    node,
+                    message: "some message",
+                    suggest: [{ desc: "some suggestion", fix: fixer => fixer.replaceText(node, 'bar') }]
+                });
+            }
+        };
+    }
+};
index f943f526c73099b87e7608be7d9ff8e8b211586f..074d206e52ebea6f447b2f025e708e5e0ad79909 100644 (file)
@@ -5,21 +5,33 @@
 
 "use strict";
 
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
 const assert = require("chai").assert,
     api = require("../../lib/api");
 
+//-----------------------------------------------------------------------------
+// Tests
+//-----------------------------------------------------------------------------
+
 describe("api", () => {
 
     it("should have RuleTester exposed", () => {
         assert.isFunction(api.RuleTester);
     });
 
-    it("should have CLIEngine exposed", () => {
-        assert.isFunction(api.CLIEngine);
+    it("should not have CLIEngine exposed", () => {
+        assert.isUndefined(api.CLIEngine);
+    });
+
+    it("should not have linter exposed", () => {
+        assert.isUndefined(api.linter);
     });
 
-    it("should have linter exposed", () => {
-        assert.isObject(api.linter);
+    it("should have Linter exposed", () => {
+        assert.isFunction(api.Linter);
     });
 
     it("should have SourceCode exposed", () => {
index 59243b0b7dcf3273eb2bba6a4551dd15573c53e8..31f59bf51b4b78213d934b1821e855da735d9bb7 100644 (file)
@@ -16,7 +16,11 @@ const assert = require("chai").assert,
     fs = require("fs"),
     os = require("os"),
     hash = require("../../../lib/cli-engine/hash"),
-    { CascadingConfigArrayFactory } = require("@eslint/eslintrc/lib/cascading-config-array-factory"),
+    {
+        Legacy: {
+            CascadingConfigArrayFactory
+        }
+    } = require("@eslint/eslintrc"),
     { unIndent, createCustomTeardown } = require("../../_utils");
 
 const proxyquire = require("proxyquire").noCallThru().noPreserveCache();
@@ -40,10 +44,10 @@ describe("CLIEngine", () => {
         originalDir = process.cwd(),
         fixtureDir = path.resolve(fs.realpathSync(os.tmpdir()), "eslint/fixtures");
 
-    /** @type {import("../../../lib/cli-engine")["CLIEngine"]} */
+    /** @type {import("../../../lib/cli-engine").CLIEngine} */
     let CLIEngine;
 
-    /** @type {import("../../../lib/cli-engine/cli-engine")["getCLIEngineInternalSlots"]} */
+    /** @type {import("../../../lib/cli-engine/cli-engine").getCLIEngineInternalSlots} */
     let getCLIEngineInternalSlots;
 
     /**
@@ -69,14 +73,13 @@ describe("CLIEngine", () => {
      * @private
      */
     function cliEngineWithPlugins(options) {
-        const engine = new CLIEngine(options);
-
-        // load the mocked plugins
-        engine.addPlugin(examplePluginName, examplePlugin);
-        engine.addPlugin(examplePluginNameWithNamespace, examplePlugin);
-        engine.addPlugin(examplePreprocessorName, require("../../fixtures/processors/custom-processor"));
-
-        return engine;
+        return new CLIEngine(options, {
+            preloadedPlugins: {
+                [examplePluginName]: examplePlugin,
+                [examplePluginNameWithNamespace]: examplePlugin,
+                [examplePreprocessorName]: require("../../fixtures/processors/custom-processor")
+            }
+        });
     }
 
     // copy into clean area so as not to get "infected" by this project's .eslintrc files
@@ -88,7 +91,7 @@ describe("CLIEngine", () => {
          * exceeds the default test timeout, so raise it just for this hook.
          * Mocha uses `this` to set timeouts on an individual hook level.
          */
-        this.timeout(60 * 1000); // eslint-disable-line no-invalid-this
+        this.timeout(60 * 1000); // eslint-disable-line no-invalid-this -- Mocha API
         shell.mkdir("-p", fixtureDir);
         shell.cp("-r", "./tests/fixtures/.", fixtureDir);
     });
@@ -116,7 +119,7 @@ describe("CLIEngine", () => {
 
         it("should report one fatal message when given a path by --ignore-path that is not a file when ignore is true.", () => {
             assert.throws(() => {
-                // eslint-disable-next-line no-new
+                // eslint-disable-next-line no-new -- Testing synchronous throwing
                 new CLIEngine({ ignorePath: fixtureDir });
             }, `Cannot read .eslintignore file: ${fixtureDir}\nError: EISDIR: illegal operation on a directory, read`);
         });
@@ -125,7 +128,7 @@ describe("CLIEngine", () => {
         it("should not modify baseConfig when format is specified", () => {
             const customBaseConfig = { root: true };
 
-            new CLIEngine({ baseConfig: customBaseConfig, format: "foo" }); // eslint-disable-line no-new
+            new CLIEngine({ baseConfig: customBaseConfig, format: "foo" }); // eslint-disable-line no-new -- Test side effects
 
             assert.deepStrictEqual(customBaseConfig, { root: true });
         });
@@ -144,6 +147,7 @@ describe("CLIEngine", () => {
             assert.strictEqual(report.results.length, 1);
             assert.strictEqual(report.errorCount, 5);
             assert.strictEqual(report.warningCount, 0);
+            assert.strictEqual(report.fatalErrorCount, 0);
             assert.strictEqual(report.fixableErrorCount, 3);
             assert.strictEqual(report.fixableWarningCount, 0);
             assert.strictEqual(report.results[0].messages.length, 5);
@@ -310,6 +314,7 @@ describe("CLIEngine", () => {
                         messages: [],
                         errorCount: 0,
                         warningCount: 0,
+                        fatalErrorCount: 0,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         output: "var bar = foo;"
@@ -317,6 +322,7 @@ describe("CLIEngine", () => {
                 ],
                 errorCount: 0,
                 warningCount: 0,
+                fatalErrorCount: 0,
                 fixableErrorCount: 0,
                 fixableWarningCount: 0,
                 usedDeprecatedRules: []
@@ -519,6 +525,7 @@ describe("CLIEngine", () => {
                         ],
                         errorCount: 1,
                         warningCount: 0,
+                        fatalErrorCount: 0,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         source: "var bar = foo"
@@ -526,6 +533,7 @@ describe("CLIEngine", () => {
                 ],
                 errorCount: 1,
                 warningCount: 0,
+                fatalErrorCount: 0,
                 fixableErrorCount: 0,
                 fixableWarningCount: 0,
                 usedDeprecatedRules: []
@@ -562,6 +570,7 @@ describe("CLIEngine", () => {
                         ],
                         errorCount: 1,
                         warningCount: 0,
+                        fatalErrorCount: 1,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         output: "var bar = foothis is a syntax error."
@@ -569,6 +578,7 @@ describe("CLIEngine", () => {
                 ],
                 errorCount: 1,
                 warningCount: 0,
+                fatalErrorCount: 1,
                 fixableErrorCount: 0,
                 fixableWarningCount: 0,
                 usedDeprecatedRules: []
@@ -604,6 +614,7 @@ describe("CLIEngine", () => {
                         ],
                         errorCount: 1,
                         warningCount: 0,
+                        fatalErrorCount: 1,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         source: "var bar ="
@@ -611,6 +622,7 @@ describe("CLIEngine", () => {
                 ],
                 errorCount: 1,
                 warningCount: 0,
+                fatalErrorCount: 1,
                 fixableErrorCount: 0,
                 fixableWarningCount: 0,
                 usedDeprecatedRules: []
@@ -692,6 +704,7 @@ describe("CLIEngine", () => {
                         ],
                         errorCount: 1,
                         warningCount: 0,
+                        fatalErrorCount: 1,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         source: "var bar = foothis is a syntax error.\n return bar;"
@@ -699,6 +712,7 @@ describe("CLIEngine", () => {
                 ],
                 errorCount: 1,
                 warningCount: 0,
+                fatalErrorCount: 1,
                 fixableErrorCount: 0,
                 fixableWarningCount: 0,
                 usedDeprecatedRules: []
@@ -726,7 +740,7 @@ describe("CLIEngine", () => {
             const Module = require("module");
             let originalFindPath = null;
 
-            /* eslint-disable no-underscore-dangle */
+            /* eslint-disable no-underscore-dangle -- Private Node API overriding */
             before(() => {
                 originalFindPath = Module._findPath;
                 Module._findPath = function(id, ...otherArgs) {
@@ -739,7 +753,7 @@ describe("CLIEngine", () => {
             after(() => {
                 Module._findPath = originalFindPath;
             });
-            /* eslint-enable no-underscore-dangle */
+            /* eslint-enable no-underscore-dangle -- Private Node API overriding */
 
             it("should resolve 'plugins:[\"@scope\"]' to 'node_modules/@scope/eslint-plugin'.", () => {
                 engine = new CLIEngine({ cwd: getFixturePath("plugin-shorthand/basic") });
@@ -777,7 +791,7 @@ describe("CLIEngine", () => {
 
     describe("executeOnFiles()", () => {
 
-        /** @type {InstanceType<import("../../../lib/cli-engine")["CLIEngine"]>} */
+        /** @type {InstanceType<import("../../../lib/cli-engine").CLIEngine>} */
         let engine;
 
         it("should use correct parser when custom parser is specified", () => {
@@ -1684,6 +1698,7 @@ describe("CLIEngine", () => {
                         messages: [],
                         errorCount: 0,
                         warningCount: 0,
+                        fatalErrorCount: 0,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         output: "true ? \"yes\" : \"no\";\n"
@@ -1693,6 +1708,7 @@ describe("CLIEngine", () => {
                         messages: [],
                         errorCount: 0,
                         warningCount: 0,
+                        fatalErrorCount: 0,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0
                     },
@@ -1713,6 +1729,7 @@ describe("CLIEngine", () => {
                         ],
                         errorCount: 1,
                         warningCount: 0,
+                        fatalErrorCount: 0,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         output: "var msg = \"hi\";\nif (msg == \"hi\") {\n\n}\n"
@@ -1734,6 +1751,7 @@ describe("CLIEngine", () => {
                         ],
                         errorCount: 1,
                         warningCount: 0,
+                        fatalErrorCount: 0,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         output: "var msg = \"hi\" + foo;\n"
@@ -2132,10 +2150,16 @@ describe("CLIEngine", () => {
                     useEslintrc: false,
                     plugins: ["test"],
                     rules: { "test/example-rule": 1 }
+                }, {
+                    preloadedPlugins: {
+                        "eslint-plugin-test": {
+                            rules: {
+                                "example-rule": require("../../fixtures/rules/custom-rule")
+                            }
+                        }
+                    }
                 });
 
-                engine.addPlugin("eslint-plugin-test", { rules: { "example-rule": require("../../fixtures/rules/custom-rule") } });
-
                 const report = engine.executeOnFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]);
 
                 assert.strictEqual(report.results.length, 1);
@@ -2849,16 +2873,18 @@ describe("CLIEngine", () => {
                     },
                     extensions: ["js", "txt"],
                     cwd: path.join(fixtureDir, "..")
-                });
-
-                engine.addPlugin("test-processor", {
-                    processors: {
-                        ".txt": {
-                            preprocess(text) {
-                                return [text];
-                            },
-                            postprocess(messages) {
-                                return messages[0];
+                }, {
+                    preloadedPlugins: {
+                        "test-processor": {
+                            processors: {
+                                ".txt": {
+                                    preprocess(text) {
+                                        return [text];
+                                    },
+                                    postprocess(messages) {
+                                        return messages[0];
+                                    }
+                                }
                             }
                         }
                     }
@@ -2892,17 +2918,19 @@ describe("CLIEngine", () => {
                     },
                     extensions: ["js", "txt"],
                     cwd: path.join(fixtureDir, "..")
-                });
-
-                engine.addPlugin("test-processor", {
-                    processors: {
-                        ".txt": {
-                            preprocess(text) {
-                                return [text.replace("a()", "b()")];
-                            },
-                            postprocess(messages) {
-                                messages[0][0].ruleId = "post-processed";
-                                return messages[0];
+                }, {
+                    preloadedPlugins: {
+                        "test-processor": {
+                            processors: {
+                                ".txt": {
+                                    preprocess(text) {
+                                        return [text.replace("a()", "b()")];
+                                    },
+                                    postprocess(messages) {
+                                        messages[0][0].ruleId = "post-processed";
+                                        return messages[0];
+                                    }
+                                }
                             }
                         }
                     }
@@ -2936,17 +2964,19 @@ describe("CLIEngine", () => {
                     },
                     extensions: ["js", "txt"],
                     ignore: false
-                });
-
-                engine.addPlugin("test-processor", {
-                    processors: {
-                        ".txt": {
-                            preprocess(text) {
-                                return [text.replace("a()", "b()")];
-                            },
-                            postprocess(messages) {
-                                messages[0][0].ruleId = "post-processed";
-                                return messages[0];
+                }, {
+                    preloadedPlugins: {
+                        "test-processor": {
+                            processors: {
+                                ".txt": {
+                                    preprocess(text) {
+                                        return [text.replace("a()", "b()")];
+                                    },
+                                    postprocess(messages) {
+                                        messages[0][0].ruleId = "post-processed";
+                                        return messages[0];
+                                    }
+                                }
                             }
                         }
                     }
@@ -2988,11 +3018,13 @@ describe("CLIEngine", () => {
                         extensions: ["js", "txt"],
                         ignore: false,
                         fix: true
-                    });
-
-                    engine.addPlugin("test-processor", {
-                        processors: {
-                            ".html": Object.assign({ supportsAutofix: true }, HTML_PROCESSOR)
+                    }, {
+                        preloadedPlugins: {
+                            "test-processor": {
+                                processors: {
+                                    ".html": Object.assign({ supportsAutofix: true }, HTML_PROCESSOR)
+                                }
+                            }
                         }
                     });
 
@@ -3012,10 +3044,16 @@ describe("CLIEngine", () => {
                         extensions: ["js", "txt"],
                         ignore: false,
                         fix: true
+                    }, {
+                        preloadedPlugins: {
+                            "test-processor": {
+                                processors: {
+                                    ".html": HTML_PROCESSOR
+                                }
+                            }
+                        }
                     });
 
-                    engine.addPlugin("test-processor", { processors: { ".html": HTML_PROCESSOR } });
-
                     const report = engine.executeOnText("<script>foo</script>", "foo.html");
 
                     assert.strictEqual(report.results[0].messages.length, 1);
@@ -3031,11 +3069,13 @@ describe("CLIEngine", () => {
                         },
                         extensions: ["js", "txt"],
                         ignore: false
-                    });
-
-                    engine.addPlugin("test-processor", {
-                        processors: {
-                            ".html": Object.assign({ supportsAutofix: true }, HTML_PROCESSOR)
+                    }, {
+                        preloadedPlugins: {
+                            "test-processor": {
+                                processors: {
+                                    ".html": Object.assign({ supportsAutofix: true }, HTML_PROCESSOR)
+                                }
+                            }
                         }
                     });
 
@@ -4284,7 +4324,7 @@ describe("CLIEngine", () => {
 
                 assert.throw(() => {
                     try {
-                        // eslint-disable-next-line no-new
+                        // eslint-disable-next-line no-new -- Check for throwing
                         new CLIEngine({ cwd });
                     } catch (error) {
                         assert.strictEqual(error.messageTemplate, "failed-to-read-json");
@@ -4310,7 +4350,7 @@ describe("CLIEngine", () => {
                 const cwd = getFixturePath("ignored-paths", "bad-package-json-ignore");
 
                 assert.throws(() => {
-                    // eslint-disable-next-line no-new
+                    // eslint-disable-next-line no-new -- Check for throwing
                     new CLIEngine({ cwd });
                 }, "Package.json eslintIgnore property requires an array of paths");
             });
@@ -4442,7 +4482,7 @@ describe("CLIEngine", () => {
                 const ignorePath = getFixturePath("ignored-paths", "not-a-directory", ".foobaz");
 
                 assert.throws(() => {
-                    // eslint-disable-next-line no-new
+                    // eslint-disable-next-line no-new -- Check for throwing
                     new CLIEngine({ ignorePath, cwd });
                 }, "Cannot read .eslintignore file");
             });
@@ -4628,7 +4668,7 @@ describe("CLIEngine", () => {
             assert.isFunction(formatter);
         });
 
-        it("should return null when a customer formatter doesn't exist", () => {
+        it("should return null when a custom formatter doesn't exist", () => {
             const engine = new CLIEngine(),
                 formatterPath = getFixturePath("formatters", "doesntexist.js"),
                 fullFormatterPath = path.resolve(formatterPath);
@@ -4647,6 +4687,18 @@ describe("CLIEngine", () => {
             }, `There was a problem loading formatter: ${fullFormatterPath}\nError: Cannot find module '${fullFormatterPath}'`);
         });
 
+        it("should throw when a built-in formatter no longer exists", () => {
+            const engine = new CLIEngine();
+
+            assert.throws(() => {
+                engine.getFormatter("table");
+            }, "The table formatter is no longer part of core ESLint. Install it manually with `npm install -D eslint-formatter-table`");
+
+            assert.throws(() => {
+                engine.getFormatter("codeframe");
+            }, "The codeframe formatter is no longer part of core ESLint. Install it manually with `npm install -D eslint-formatter-codeframe`");
+        });
+
         it("should throw if the required formatter exists but has an error", () => {
             const engine = new CLIEngine(),
                 formatterPath = getFixturePath("formatters", "broken.js");
@@ -4866,10 +4918,14 @@ describe("CLIEngine", () => {
             assert(engine.getRules().has("node/no-deprecated-api"), "node/no-deprecated-api is present");
         });
 
-        it("should expose the rules of the plugin that is added by 'addPlugin'.", () => {
-            const engine = new CLIEngine({ plugins: ["foo"] });
-
-            engine.addPlugin("foo", require("eslint-plugin-node"));
+        it("should expose the list of rules from a preloaded plugin", () => {
+            const engine = new CLIEngine({
+                plugins: ["foo"]
+            }, {
+                preloadedPlugins: {
+                    foo: require("eslint-plugin-node")
+                }
+            });
 
             assert(engine.getRules().has("foo/no-deprecated-api"), "foo/no-deprecated-api is present");
         });
@@ -5008,6 +5064,7 @@ describe("CLIEngine", () => {
             const config = {
                 envs: ["browser"],
                 ignore: true,
+                useEslintrc: false,
                 allowInlineConfig: false,
                 rules: {
                     "eol-last": 0,
@@ -5034,6 +5091,7 @@ describe("CLIEngine", () => {
             const config = {
                 envs: ["browser"],
                 ignore: true,
+                useEslintrc: false,
 
                 // allowInlineConfig: true is the default
                 rules: {
@@ -5071,20 +5129,26 @@ describe("CLIEngine", () => {
                                     message: "Unused eslint-disable directive (no problems were reported).",
                                     line: 1,
                                     column: 1,
+                                    fix: {
+                                        range: [0, 20],
+                                        text: " "
+                                    },
                                     severity: 2,
                                     nodeType: null
                                 }
                             ],
                             errorCount: 1,
                             warningCount: 0,
-                            fixableErrorCount: 0,
+                            fatalErrorCount: 0,
+                            fixableErrorCount: 1,
                             fixableWarningCount: 0,
                             source: "/* eslint-disable */"
                         }
                     ],
                     errorCount: 1,
                     warningCount: 0,
-                    fixableErrorCount: 0,
+                    fatalErrorCount: 0,
+                    fixableErrorCount: 1,
                     fixableWarningCount: 0,
                     usedDeprecatedRules: []
                 }
@@ -6155,7 +6219,8 @@ describe("CLIEngine", () => {
                             }
                         ],
                         source: "a == b",
-                        warningCount: 0
+                        warningCount: 0,
+                        fatalErrorCount: 0
                     }
                 ]);
             });
@@ -6177,7 +6242,8 @@ describe("CLIEngine", () => {
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         messages: [],
-                        warningCount: 0
+                        warningCount: 0,
+                        fatalErrorCount: 0
                     }
                 ]);
             });
@@ -6223,7 +6289,8 @@ describe("CLIEngine", () => {
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         messages: [],
-                        warningCount: 0
+                        warningCount: 0,
+                        fatalErrorCount: 0
                     }
                 ]);
             });
@@ -6258,7 +6325,8 @@ describe("CLIEngine", () => {
                             }
                         ],
                         source: "a == b",
-                        warningCount: 0
+                        warningCount: 0,
+                        fatalErrorCount: 0
                     }
                 ]);
             });
index 1ea7a3bd2c4fa9014f4a08b9c186277cd97a0da6..a1de4cd2161f3bc734d1b75feb3e4ef265c60c33 100644 (file)
@@ -4,16 +4,27 @@
  */
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const fs = require("fs");
 const path = require("path");
 const os = require("os");
 const { assert } = require("chai");
 const sh = require("shelljs");
-const { CascadingConfigArrayFactory } =
-    require("@eslint/eslintrc/lib/cascading-config-array-factory");
+const {
+    Legacy: {
+        CascadingConfigArrayFactory
+    }
+} = require("@eslint/eslintrc");
 const { createCustomTeardown } = require("../../_utils");
 const { FileEnumerator } = require("../../../lib/cli-engine/file-enumerator");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 describe("FileEnumerator", () => {
     describe("'iterateFiles(patterns)' method should iterate files and configs.", () => {
         describe("with three directories ('lib', 'lib/nested', 'test') that contains 'one.js' and 'two.js'", () => {
@@ -54,7 +65,7 @@ describe("FileEnumerator", () => {
 
             describe("if 'lib/*.js' was given,", () => {
 
-                /** @type {Array<{config:(typeof import('../../../lib/cli-engine'))["ConfigArray"], filePath:string, ignored:boolean}>} */
+                /** @type {Array<{config:(typeof import('../../../lib/cli-engine')).ConfigArray, filePath:string, ignored:boolean}>} */
                 let list;
 
                 beforeEach(() => {
@@ -86,7 +97,7 @@ describe("FileEnumerator", () => {
 
             describe("if 'lib/**/*.js' was given,", () => {
 
-                /** @type {Array<{config:(typeof import('../../../lib/cli-engine'))["ConfigArray"], filePath:string, ignored:boolean}>} */
+                /** @type {Array<{config:(typeof import('../../../lib/cli-engine')).ConfigArray, filePath:string, ignored:boolean}>} */
                 let list;
 
                 beforeEach(() => {
@@ -129,7 +140,7 @@ describe("FileEnumerator", () => {
 
             describe("if 'lib/*.js' and 'test/*.js' were given,", () => {
 
-                /** @type {Array<{config:(typeof import('../../../lib/cli-engine'))["ConfigArray"], filePath:string, ignored:boolean}>} */
+                /** @type {Array<{config:(typeof import('../../../lib/cli-engine')).ConfigArray, filePath:string, ignored:boolean}>} */
                 let list;
 
                 beforeEach(() => {
@@ -215,7 +226,7 @@ describe("FileEnumerator", () => {
                  * it just for this hook. Mocha uses `this` to set timeouts on
                  * an individual hook level.
                  */
-                this.timeout(60 * 1000); // eslint-disable-line no-invalid-this
+                this.timeout(60 * 1000); // eslint-disable-line no-invalid-this -- Mocha API
                 fixtureDir = `${os.tmpdir()}/eslint/tests/fixtures/`;
                 sh.mkdir("-p", fixtureDir);
                 sh.cp("-r", "./tests/fixtures/*", fixtureDir);
diff --git a/eslint/tests/lib/cli-engine/formatters/codeframe.js b/eslint/tests/lib/cli-engine/formatters/codeframe.js
deleted file mode 100644 (file)
index 398d6b8..0000000
+++ /dev/null
@@ -1,483 +0,0 @@
-/**
- * @fileoverview Tests for codeframe reporter.
- * @author Vitor Balocco
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const assert = require("chai").assert;
-const sinon = require("sinon");
-const proxyquire = require("proxyquire");
-const chalk = require("chalk");
-const path = require("path");
-const stripAnsi = require("strip-ansi");
-
-// Chalk protects its methods so we need to inherit from it for Sinon to work.
-const chalkStub = Object.create(chalk, {
-    yellow: {
-        value(str) {
-            return chalk.yellow(str);
-        },
-        writable: true
-    },
-    red: {
-        value(str) {
-            return chalk.red(str);
-        },
-        writable: true
-    }
-});
-
-chalkStub.yellow.bold = chalk.yellow.bold;
-chalkStub.red.bold = chalk.red.bold;
-
-const formatter = proxyquire("../../../../lib/cli-engine/formatters/codeframe", { chalk: chalkStub });
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-describe("formatter:codeframe", () => {
-    afterEach(() => {
-        sinon.verifyAndRestore();
-    });
-
-    describe("when passed no messages", () => {
-        const code = [{
-            filePath: "foo.js",
-            messages: [],
-            errorCount: 0,
-            warningCount: 0
-        }];
-
-        it("should return nothing", () => {
-            const result = formatter(code);
-
-            assert.strictEqual(result, "");
-        });
-    });
-
-    describe("when passed a single warning message", () => {
-        const code = [{
-            filePath: path.join(process.cwd(), "lib", "foo.js"),
-            source: "var foo = 1;\n var bar = 2;\n",
-            messages: [{
-                message: "Unexpected foo.",
-                severity: 1,
-                line: 1,
-                column: 5,
-                ruleId: "foo"
-            }],
-            errorCount: 0,
-            warningCount: 1,
-            fixableErrorCount: 0,
-            fixableWarningCount: 0
-        }];
-
-        it("should return a string in the correct format for warnings", () => {
-            const result = formatter(code);
-
-            assert.strictEqual(stripAnsi(result), [
-                `warning: Unexpected foo (foo) at ${path.join("lib", "foo.js")}:1:5:`,
-                "> 1 | var foo = 1;",
-                "    |     ^",
-                "  2 |  var bar = 2;",
-                "  3 | ",
-                "\n",
-                "1 warning found."
-            ].join("\n"));
-        });
-
-        it("should return bold yellow summary when there are only warnings", () => {
-            sinon.spy(chalkStub.yellow, "bold");
-            sinon.spy(chalkStub.red, "bold");
-
-            formatter(code);
-
-            assert.strictEqual(chalkStub.yellow.bold.callCount, 1);
-            assert.strictEqual(chalkStub.red.bold.callCount, 0);
-        });
-
-        describe("when the warning is fixable", () => {
-            beforeEach(() => {
-                code[0].fixableWarningCount = 1;
-            });
-
-            it("should return a string in the correct format", () => {
-                const result = formatter(code);
-
-                assert.strictEqual(stripAnsi(result), [
-                    `warning: Unexpected foo (foo) at ${path.join("lib", "foo.js")}:1:5:`,
-                    "> 1 | var foo = 1;",
-                    "    |     ^",
-                    "  2 |  var bar = 2;",
-                    "  3 | ",
-                    "\n",
-                    "1 warning found.",
-                    "1 warning potentially fixable with the `--fix` option."
-                ].join("\n"));
-            });
-        });
-    });
-
-    describe("when passed a single error message", () => {
-        const code = [{
-            filePath: path.join(process.cwd(), "lib", "foo.js"),
-            source: "var foo = 1;\n var bar = 2;\n",
-            messages: [{
-                message: "Unexpected foo.",
-                severity: 2,
-                line: 1,
-                column: 5,
-                ruleId: "foo"
-            }],
-            errorCount: 1,
-            warningCount: 0
-        }];
-
-        it("should return a string in the correct format for errors", () => {
-            const result = formatter(code);
-
-            assert.strictEqual(stripAnsi(result), [
-                `error: Unexpected foo (foo) at ${path.join("lib", "foo.js")}:1:5:`,
-                "> 1 | var foo = 1;",
-                "    |     ^",
-                "  2 |  var bar = 2;",
-                "  3 | ",
-                "\n",
-                "1 error found."
-            ].join("\n"));
-        });
-
-        it("should return bold red summary when there are errors", () => {
-            sinon.spy(chalkStub.yellow, "bold");
-            sinon.spy(chalkStub.red, "bold");
-
-            formatter(code);
-
-            assert.strictEqual(chalkStub.yellow.bold.callCount, 0);
-            assert.strictEqual(chalkStub.red.bold.callCount, 1);
-        });
-    });
-
-    describe("when passed a message that ends with ' .'", () => {
-        const code = [{
-            filePath: "foo.js",
-            messages: [{
-                ruleId: "foo",
-                message: "Unexpected .",
-                severity: 2,
-                source: "foo"
-            }],
-            errorCount: 1,
-            warningCount: 0
-        }];
-
-        it("should return a string in the correct format (retaining the ' .')", () => {
-            const result = formatter(code);
-
-            assert.strictEqual(stripAnsi(result), "error: Unexpected . (foo) at foo.js\n\n\n1 error found.");
-        });
-    });
-
-    describe("when passed multiple messages", () => {
-        const code = [{
-            filePath: "foo.js",
-            source: "const foo = 1\n",
-            messages: [{
-                message: "Missing semicolon.",
-                severity: 2,
-                line: 1,
-                column: 14,
-                ruleId: "semi"
-            }, {
-                message: "'foo' is assigned a value but never used.",
-                severity: 2,
-                line: 1,
-                column: 7,
-                ruleId: "no-unused-vars"
-            }],
-            errorCount: 2,
-            warningCount: 0
-        }];
-
-        it("should return a string with multiple entries", () => {
-            const result = formatter(code);
-
-            assert.strictEqual(stripAnsi(result), [
-                "error: Missing semicolon (semi) at foo.js:1:14:",
-                "> 1 | const foo = 1",
-                "    |              ^",
-                "  2 | ",
-                "\n",
-                "error: 'foo' is assigned a value but never used (no-unused-vars) at foo.js:1:7:",
-                "> 1 | const foo = 1",
-                "    |       ^",
-                "  2 | ",
-                "\n",
-                "2 errors found."
-            ].join("\n"));
-        });
-
-        it("should return bold red summary when at least 1 of the messages is an error", () => {
-            sinon.spy(chalkStub.yellow, "bold");
-            sinon.spy(chalkStub.red, "bold");
-            code[0].messages[0].severity = 1;
-            code[0].warningCount = 1;
-            code[0].errorCount = 1;
-
-            formatter(code);
-
-            assert.strictEqual(chalkStub.yellow.bold.callCount, 0);
-            assert.strictEqual(chalkStub.red.bold.callCount, 1);
-        });
-    });
-
-    describe("when passed one file with 1 message and fixes applied", () => {
-        const code = [{
-            filePath: "foo.js",
-            messages: [{
-                ruleId: "no-unused-vars",
-                severity: 2,
-                message: "'foo' is assigned a value but never used.",
-                line: 4,
-                column: 11,
-                source: "    const foo = 1;"
-            }],
-            errorCount: 1,
-            warningCount: 0,
-            output: "function foo() {\n\n    // a comment\n    const foo = 1;\n}\n\n"
-        }];
-
-        it("should return a string with code preview pointing to the correct location after fixes", () => {
-            const result = formatter(code);
-
-            assert.strictEqual(stripAnsi(result), [
-                "error: 'foo' is assigned a value but never used (no-unused-vars) at foo.js:4:11:",
-                "  2 | ",
-                "  3 |     // a comment",
-                "> 4 |     const foo = 1;",
-                "    |           ^",
-                "  5 | }",
-                "  6 | ",
-                "  7 | ",
-                "\n",
-                "1 error found."
-            ].join("\n"));
-        });
-    });
-
-    describe("when passed multiple files with 1 message each", () => {
-        const code = [{
-            filePath: "foo.js",
-            source: "const foo = 1\n",
-            messages: [{
-                message: "Missing semicolon.",
-                severity: 2,
-                line: 1,
-                column: 14,
-                ruleId: "semi"
-            }],
-            errorCount: 1,
-            warningCount: 0
-        }, {
-            filePath: "bar.js",
-            source: "const bar = 2\n",
-            messages: [{
-                message: "Missing semicolon.",
-                severity: 2,
-                line: 1,
-                column: 14,
-                ruleId: "semi"
-            }],
-            errorCount: 1,
-            warningCount: 0
-        }];
-
-        it("should return a string with multiple entries", () => {
-            const result = formatter(code);
-
-            assert.strictEqual(stripAnsi(result), [
-                "error: Missing semicolon (semi) at foo.js:1:14:",
-                "> 1 | const foo = 1",
-                "    |              ^",
-                "  2 | ",
-                "\n",
-                "error: Missing semicolon (semi) at bar.js:1:14:",
-                "> 1 | const bar = 2",
-                "    |              ^",
-                "  2 | ",
-                "\n",
-                "2 errors found."
-            ].join("\n"));
-        });
-    });
-
-    describe("when passed a fatal error message", () => {
-        const code = [{
-            filePath: "foo.js",
-            source: "e{}\n",
-            messages: [{
-                ruleId: null,
-                fatal: true,
-                severity: 2,
-                source: "e{}",
-                message: "Parsing error: Unexpected token {",
-                line: 1,
-                column: 2
-            }],
-            errorCount: 1,
-            warningCount: 0
-        }];
-
-        it("should return a string in the correct format", () => {
-            const result = formatter(code);
-
-            assert.strictEqual(stripAnsi(result), [
-                "error: Parsing error: Unexpected token { at foo.js:1:2:",
-                "> 1 | e{}",
-                "    |  ^",
-                "  2 | ",
-                "\n",
-                "1 error found."
-            ].join("\n"));
-        });
-    });
-
-    describe("when passed one file not found message", () => {
-        const code = [{
-            filePath: "foo.js",
-            messages: [{
-                fatal: true,
-                message: "Couldn't find foo.js."
-            }],
-            errorCount: 1,
-            warningCount: 0
-        }];
-
-        it("should return a string without code preview (codeframe)", () => {
-            const result = formatter(code);
-
-            assert.strictEqual(stripAnsi(result), "error: Couldn't find foo.js at foo.js\n\n\n1 error found.");
-        });
-    });
-
-    describe("when passed a single message with no line or column", () => {
-        const code = [{
-            filePath: "foo.js",
-            messages: [{
-                ruleId: "foo",
-                message: "Unexpected foo.",
-                severity: 2,
-                source: "foo"
-            }],
-            errorCount: 1,
-            warningCount: 0
-        }];
-
-        it("should return a string without code preview (codeframe)", () => {
-            const result = formatter(code);
-
-            assert.strictEqual(stripAnsi(result), "error: Unexpected foo (foo) at foo.js\n\n\n1 error found.");
-        });
-
-        it("should output filepath but without 'line:column' appended", () => {
-            const result = formatter(code);
-
-            assert.strictEqual(stripAnsi(result), "error: Unexpected foo (foo) at foo.js\n\n\n1 error found.");
-        });
-    });
-
-
-    describe("fixable problems", () => {
-        it("should not output fixable problems message when no errors or warnings are fixable", () => {
-            const code = [{
-                filePath: "foo.js",
-                errorCount: 1,
-                warningCount: 0,
-                fixableErrorCount: 0,
-                fixableWarningCount: 0,
-                messages: [{
-                    message: "Unexpected foo.",
-                    severity: 2,
-                    line: 5,
-                    column: 10,
-                    ruleId: "foo"
-                }]
-            }];
-
-            const result = formatter(code);
-
-            assert.notInclude(result, "potentially fixable");
-        });
-
-        it("should output the fixable problems message when errors are fixable", () => {
-            const code = [{
-                filePath: "foo.js",
-                errorCount: 1,
-                warningCount: 0,
-                fixableErrorCount: 1,
-                fixableWarningCount: 0,
-                messages: [{
-                    message: "Unexpected foo.",
-                    severity: 2,
-                    line: 5,
-                    column: 10,
-                    ruleId: "foo"
-                }]
-            }];
-
-            const result = formatter(code);
-
-            assert.include(result, "1 error potentially fixable with the `--fix` option.");
-        });
-
-        it("should output fixable problems message when warnings are fixable", () => {
-            const code = [{
-                filePath: "foo.js",
-                errorCount: 0,
-                warningCount: 3,
-                fixableErrorCount: 0,
-                fixableWarningCount: 2,
-                messages: [{
-                    message: "Unexpected foo."
-                }]
-            }];
-
-            const result = formatter(code);
-
-            assert.include(result, "2 warnings potentially fixable with the `--fix` option.");
-        });
-
-        it("should output the total number of fixable errors and warnings", () => {
-            const code = [{
-                filePath: "foo.js",
-                errorCount: 5,
-                warningCount: 3,
-                fixableErrorCount: 5,
-                fixableWarningCount: 2,
-                messages: [{
-                    message: "Unexpected foo."
-                }]
-            }, {
-                filePath: "bar.js",
-                errorCount: 4,
-                warningCount: 2,
-                fixableErrorCount: 4,
-                fixableWarningCount: 1,
-                messages: [{
-                    message: "Unexpected bar."
-                }]
-            }];
-
-            const result = formatter(code);
-
-            assert.include(result, "9 errors and 3 warnings potentially fixable with the `--fix` option.");
-        });
-    });
-
-});
index b77c177885aebfd42c45b83d7942f1ac9cac9664..26a2445b3b39f2079639ea5a4518dd96d9373f9e 100644 (file)
@@ -3,8 +3,6 @@
  * @author Jamund Ferguson
  */
 
-/* jshint node:true */
-
 "use strict";
 
 //------------------------------------------------------------------------------
index 66f6abe464aca0daa2c465aa82f25ca83c2a3b17..24f4be9f32b41a29fbbf3c804064ee64a2449119 100644 (file)
@@ -14,6 +14,10 @@ const assert = require("chai").assert,
     proxyquire = require("proxyquire"),
     sinon = require("sinon");
 
+//-----------------------------------------------------------------------------
+// Helpers
+//-----------------------------------------------------------------------------
+
 /*
  * Chalk protects its methods so we need to inherit from it
  * for Sinon to work.
diff --git a/eslint/tests/lib/cli-engine/formatters/table.js b/eslint/tests/lib/cli-engine/formatters/table.js
deleted file mode 100644 (file)
index 3e29caf..0000000
+++ /dev/null
@@ -1,324 +0,0 @@
-/**
- * @fileoverview Tests for "table" reporter.
- * @author Gajus Kuizinas <gajus@gajus.com>
- */
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const assert = require("chai").assert;
-const chalk = require("chalk");
-const formatter = require("../../../../lib/cli-engine/formatters/table");
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-describe("formatter:table", () => {
-    const originalColorLevel = chalk.level;
-
-    before(() => {
-        chalk.level = 0;
-    });
-
-    after(() => {
-        chalk.level = originalColorLevel;
-    });
-
-    describe("when passed no messages", () => {
-        const code = [
-            {
-                filePath: "foo.js",
-                messages: [],
-                errorCount: 0,
-                warningCount: 0
-            }
-        ];
-
-        it("should return a table of error and warning count with no messages", () => {
-            const expectedOutput = [
-                "",
-                "╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗",
-                "║ 0 Errors                                                                                                       ║",
-                "╟────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢",
-                "║ 0 Warnings                                                                                                     ║",
-                "╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝",
-                ""
-            ].join("\n");
-
-            const result = formatter(code);
-
-            assert.strictEqual(result, expectedOutput);
-        });
-    });
-
-    describe("when passed a single message", () => {
-        it("should return a string in the correct format for errors", () => {
-            const code = [
-                {
-                    filePath: "foo.js",
-                    messages: [
-                        {
-                            message: "Unexpected foo.",
-                            severity: 2,
-                            line: 5,
-                            column: 10,
-                            ruleId: "foo"
-                        }
-                    ],
-                    errorCount: 1,
-                    warningCount: 0
-                }
-            ];
-
-            const expectedOutput = [
-                "",
-                "foo.js",
-                "",
-                "║ Line     │ Column   │ Type     │ Message                                                │ Rule ID              ║",
-                "╟──────────┼──────────┼──────────┼────────────────────────────────────────────────────────┼──────────────────────╢",
-                "║ 5        │ 10       │ error    │ Unexpected foo.                                        │ foo                  ║",
-                "",
-                "╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗",
-                "║ 1 Error                                                                                                        ║",
-                "╟────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢",
-                "║ 0 Warnings                                                                                                     ║",
-                "╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝",
-                ""
-            ].join("\n");
-
-            const result = formatter(code);
-
-            assert.strictEqual(result, expectedOutput);
-        });
-
-        it("should return a string in the correct format for warnings", () => {
-            const code = [
-                {
-                    filePath: "foo.js",
-                    messages: [
-                        {
-                            message: "Unexpected foo.",
-                            severity: 1,
-                            line: 5,
-                            column: 10,
-                            ruleId: "foo"
-                        }
-                    ],
-                    errorCount: 0,
-                    warningCount: 1
-                }
-            ];
-
-            const expectedOutput = [
-                "",
-                "foo.js",
-                "",
-                "║ Line     │ Column   │ Type     │ Message                                                │ Rule ID              ║",
-                "╟──────────┼──────────┼──────────┼────────────────────────────────────────────────────────┼──────────────────────╢",
-                "║ 5        │ 10       │ warning  │ Unexpected foo.                                        │ foo                  ║",
-                "",
-                "╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗",
-                "║ 0 Errors                                                                                                       ║",
-                "╟────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢",
-                "║ 1 Warning                                                                                                      ║",
-                "╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝",
-                ""
-            ].join("\n");
-
-            const result = formatter(code);
-
-            assert.strictEqual(result, expectedOutput);
-        });
-    });
-
-    describe("when passed a fatal error message", () => {
-        it("should return a string in the correct format", () => {
-            const code = [
-                {
-                    filePath: "foo.js",
-                    messages: [
-                        {
-                            fatal: true,
-                            message: "Unexpected foo.",
-                            line: 5,
-                            column: 10,
-                            ruleId: "foo"
-                        }
-                    ],
-                    errorCount: 1,
-                    warningCount: 0
-                }
-            ];
-
-            const expectedOutput = [
-                "",
-                "foo.js",
-                "",
-                "║ Line     │ Column   │ Type     │ Message                                                │ Rule ID              ║",
-                "╟──────────┼──────────┼──────────┼────────────────────────────────────────────────────────┼──────────────────────╢",
-                "║ 5        │ 10       │ error    │ Unexpected foo.                                        │ foo                  ║",
-                "",
-                "╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗",
-                "║ 1 Error                                                                                                        ║",
-                "╟────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢",
-                "║ 0 Warnings                                                                                                     ║",
-                "╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝",
-                ""
-            ].join("\n");
-
-            const result = formatter(code);
-
-            assert.strictEqual(result, expectedOutput);
-        });
-    });
-
-    describe("when passed multiple messages", () => {
-        it("should return a string with multiple entries", () => {
-            const code = [
-                {
-                    filePath: "foo.js",
-                    messages: [
-                        {
-                            message: "Unexpected foo.",
-                            severity: 2,
-                            line: 5,
-                            column: 10,
-                            ruleId: "foo"
-                        },
-                        {
-                            message: "Unexpected bar.",
-                            severity: 1,
-                            line: 6,
-                            column: 11,
-                            ruleId: "bar"
-                        }
-                    ],
-                    errorCount: 1,
-                    warningCount: 1
-                }
-            ];
-
-            const expectedOutput = [
-                "",
-                "foo.js",
-                "",
-                "║ Line     │ Column   │ Type     │ Message                                                │ Rule ID              ║",
-                "╟──────────┼──────────┼──────────┼────────────────────────────────────────────────────────┼──────────────────────╢",
-                "║ 5        │ 10       │ error    │ Unexpected foo.                                        │ foo                  ║",
-                "║ 6        │ 11       │ warning  │ Unexpected bar.                                        │ bar                  ║",
-                "",
-                "╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗",
-                "║ 1 Error                                                                                                        ║",
-                "╟────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢",
-                "║ 1 Warning                                                                                                      ║",
-                "╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝",
-                ""
-            ].join("\n");
-
-            const result = formatter(code);
-
-            assert.strictEqual(result, expectedOutput);
-        });
-    });
-
-    describe("when passed multiple files with 1 message each", () => {
-        it("should return a string with multiple entries", () => {
-            const code = [
-                {
-                    filePath: "foo.js",
-                    messages: [
-                        {
-                            message: "Unexpected foo.",
-                            severity: 2,
-                            line: 5,
-                            column: 10,
-                            ruleId: "foo"
-                        }
-                    ],
-                    errorCount: 1,
-                    warningCount: 0
-                }, {
-                    filePath: "bar.js",
-                    messages: [
-                        {
-                            message: "Unexpected bar.",
-                            severity: 1,
-                            line: 6,
-                            column: 11,
-                            ruleId: "bar"
-                        }
-                    ],
-                    errorCount: 0,
-                    warningCount: 1
-                }
-            ];
-
-            const expectedOutput = [
-                "",
-                "foo.js",
-                "",
-                "║ Line     │ Column   │ Type     │ Message                                                │ Rule ID              ║",
-                "╟──────────┼──────────┼──────────┼────────────────────────────────────────────────────────┼──────────────────────╢",
-                "║ 5        │ 10       │ error    │ Unexpected foo.                                        │ foo                  ║",
-                "",
-                "bar.js",
-                "",
-                "║ Line     │ Column   │ Type     │ Message                                                │ Rule ID              ║",
-                "╟──────────┼──────────┼──────────┼────────────────────────────────────────────────────────┼──────────────────────╢",
-                "║ 6        │ 11       │ warning  │ Unexpected bar.                                        │ bar                  ║",
-                "",
-                "╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗",
-                "║ 1 Error                                                                                                        ║",
-                "╟────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢",
-                "║ 1 Warning                                                                                                      ║",
-                "╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝",
-                ""
-            ].join("\n");
-
-            const result = formatter(code);
-
-            assert.strictEqual(result, expectedOutput);
-        });
-    });
-
-    describe("when passed one file not found message", () => {
-        it("should return a string without line and column (0, 0)", () => {
-            const code = [
-                {
-                    filePath: "foo.js",
-                    messages: [
-                        {
-                            fatal: true,
-                            message: "Couldn't find foo.js."
-                        }
-                    ],
-                    errorCount: 1,
-                    warningCount: 0
-                }
-            ];
-
-            const expectedOutput = [
-                "",
-                "foo.js",
-                "",
-                "║ Line     │ Column   │ Type     │ Message                                                │ Rule ID              ║",
-                "╟──────────┼──────────┼──────────┼────────────────────────────────────────────────────────┼──────────────────────╢",
-                "║ 0        │ 0        │ error    │ Couldn't find foo.js.                                  │                      ║",
-                "",
-                "╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗",
-                "║ 1 Error                                                                                                        ║",
-                "╟────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢",
-                "║ 0 Warnings                                                                                                     ║",
-                "╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝",
-                ""
-            ].join("\n");
-
-            const result = formatter(code);
-
-            assert.strictEqual(result, expectedOutput);
-        });
-    });
-});
index 1ff1470cd14cbf2d2bb20e356f6d3e82f1bdd218..beebecfb3863ed753e9475d1630ec08264ff5d96 100644 (file)
@@ -5,9 +5,17 @@
 
 "use strict";
 
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
 const assert = require("chai").assert;
 const loadRules = require("../../../lib/cli-engine/load-rules");
 
+//-----------------------------------------------------------------------------
+// Tests
+//-----------------------------------------------------------------------------
+
 describe("when given an invalid rules directory", () => {
     it("should throw an error", () => {
         assert.throws(() => {
index d1fea182db1472cb95202cc50ab21391980120df..1b3828b4090215be50f05ce69f9edc030618fdf8 100644 (file)
@@ -90,7 +90,7 @@ describe("cli", () => {
          * exceeds the default test timeout, so raise it just for this hook.
          * Mocha uses `this` to set timeouts on an individual hook level.
          */
-        this.timeout(60 * 1000); // eslint-disable-line no-invalid-this
+        this.timeout(60 * 1000); // eslint-disable-line no-invalid-this -- Mocha API
         fixtureDir = `${os.tmpdir()}/eslint/fixtures`;
         sh.mkdir("-p", fixtureDir);
         sh.cp("-r", "./tests/fixtures/.", fixtureDir);
@@ -512,7 +512,7 @@ describe("cli", () => {
                 const exit = await cli.execute(code);
 
                 assert.strictEqual(exit, 2);
-            }, /Error while loading rule 'custom-rule': Cannot read property/u);
+            }, /Error while loading rule 'custom-rule': Boom!/u);
         });
 
         it("should return a warning when rule is matched", async () => {
@@ -803,6 +803,38 @@ describe("cli", () => {
         });
     });
 
+    describe("when given the exit-on-fatal-error flag", () => {
+        it("should not change exit code if no fatal errors are reported", async () => {
+            const filePath = getFixturePath("exit-on-fatal-error", "no-fatal-error.js");
+            const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`);
+
+            assert.strictEqual(exitCode, 0);
+        });
+
+        it("should exit with exit code 1 if no fatal errors are found, but rule violations are found", async () => {
+            const filePath = getFixturePath("exit-on-fatal-error", "no-fatal-error-rule-violation.js");
+            const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`);
+
+            assert.strictEqual(exitCode, 1);
+        });
+
+        it("should exit with exit code 2 if fatal error is found", async () => {
+            const filePath = getFixturePath("exit-on-fatal-error", "fatal-error.js");
+            const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`);
+
+            assert.strictEqual(exitCode, 2);
+        });
+
+        it("should exit with exit code 2 if fatal error is found in any file", async () => {
+            const filePath = getFixturePath("exit-on-fatal-error");
+            const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`);
+
+            assert.strictEqual(exitCode, 2);
+        });
+
+
+    });
+
     describe("when passed --no-inline-config", () => {
         let localCLI;
 
diff --git a/eslint/tests/lib/config/flat-config-array.js b/eslint/tests/lib/config/flat-config-array.js
new file mode 100644 (file)
index 0000000..f6b0990
--- /dev/null
@@ -0,0 +1,1493 @@
+/**
+ * @fileoverview Tests for FlatConfigArray
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
+const { FlatConfigArray } = require("../../../lib/config/flat-config-array");
+const assert = require("chai").assert;
+const allConfig = require("../../../conf/eslint-all");
+const recommendedConfig = require("../../../conf/eslint-recommended");
+
+//-----------------------------------------------------------------------------
+// Helpers
+//-----------------------------------------------------------------------------
+
+const baseConfig = {
+    plugins: {
+        "@": {
+            rules: {
+                foo: {
+                    schema: {
+                        type: "array",
+                        items: [
+                            {
+                                enum: ["always", "never"]
+                            }
+                        ],
+                        minItems: 0,
+                        maxItems: 1
+                    }
+
+                },
+                bar: {
+
+                },
+                baz: {
+
+                },
+
+                // old-style
+                boom() {},
+
+                foo2: {
+                    schema: {
+                        type: "array",
+                        items: {
+                            type: "string"
+                        },
+                        uniqueItems: true,
+                        minItems: 1
+                    }
+                }
+            }
+        },
+        test1: {
+            rules: {
+                match: {}
+            }
+        },
+        test2: {
+            rules: {
+                nomatch: {}
+            }
+        }
+    }
+};
+
+/**
+ * Creates a config array with the correct default options.
+ * @param {*[]} configs An array of configs to use in the config array.
+ * @returns {FlatConfigArray} The config array;
+ */
+function createFlatConfigArray(configs) {
+    return new FlatConfigArray(configs, {
+        basePath: __dirname,
+        baseConfig
+    });
+}
+
+/**
+ * Asserts that a given set of configs will be merged into the given
+ * result config.
+ * @param {*[]} values An array of configs to use in the config array.
+ * @param {Object} result The expected merged result of the configs.
+ * @returns {void}
+ * @throws {AssertionError} If the actual result doesn't match the
+ *      expected result.
+ */
+async function assertMergedResult(values, result) {
+    const configs = createFlatConfigArray(values);
+
+    await configs.normalize();
+
+    const config = configs.getConfig("foo.js");
+
+    assert.deepStrictEqual(config, result);
+}
+
+/**
+ * Asserts that a given set of configs results in an invalid config.
+ * @param {*[]} values An array of configs to use in the config array.
+ * @param {string|RegExp} message The expected error message.
+ * @returns {void}
+ * @throws {AssertionError} If the config is valid or if the error
+ *      has an unexpected message.
+ */
+async function assertInvalidConfig(values, message) {
+    const configs = createFlatConfigArray(values);
+
+    await configs.normalize();
+
+    assert.throws(() => {
+        configs.getConfig("foo.js");
+    }, message);
+}
+
+/**
+ * Normalizes the rule configs to an array with severity to match
+ * how Flat Config merges rule options.
+ * @param {Object} rulesConfig The rules config portion of a config.
+ * @returns {Array} The rules config object.
+ */
+function normalizeRuleConfig(rulesConfig) {
+    const rulesConfigCopy = {
+        ...rulesConfig
+    };
+
+    for (const ruleId of Object.keys(rulesConfigCopy)) {
+        rulesConfigCopy[ruleId] = [2];
+    }
+
+    return rulesConfigCopy;
+}
+
+//-----------------------------------------------------------------------------
+// Tests
+//-----------------------------------------------------------------------------
+
+describe("FlatConfigArray", () => {
+
+    describe("Special configs", () => {
+        it("eslint:recommended is replaced with an actual config", async () => {
+            const configs = new FlatConfigArray(["eslint:recommended"], { basePath: __dirname });
+
+            await configs.normalize();
+            const config = configs.getConfig("foo.js");
+
+            assert.deepStrictEqual(config.rules, normalizeRuleConfig(recommendedConfig.rules));
+        });
+
+        it("eslint:all is replaced with an actual config", async () => {
+            const configs = new FlatConfigArray(["eslint:all"], { basePath: __dirname });
+
+            await configs.normalize();
+            const config = configs.getConfig("foo.js");
+
+            assert.deepStrictEqual(config.rules, normalizeRuleConfig(allConfig.rules));
+        });
+    });
+
+    describe("Config Properties", () => {
+
+        describe("settings", () => {
+
+            it("should merge two objects", () => assertMergedResult([
+                {
+                    settings: {
+                        a: true,
+                        b: false
+                    }
+                },
+                {
+                    settings: {
+                        c: true,
+                        d: false
+                    }
+                }
+            ], {
+                plugins: baseConfig.plugins,
+
+                settings: {
+                    a: true,
+                    b: false,
+                    c: true,
+                    d: false
+                }
+            }));
+
+            it("should merge two objects when second object has overrides", () => assertMergedResult([
+                {
+                    settings: {
+                        a: true,
+                        b: false,
+                        d: [1, 2],
+                        e: [5, 6]
+                    }
+                },
+                {
+                    settings: {
+                        c: true,
+                        a: false,
+                        d: [3, 4]
+                    }
+                }
+            ], {
+                plugins: baseConfig.plugins,
+
+                settings: {
+                    a: false,
+                    b: false,
+                    c: true,
+                    d: [3, 4],
+                    e: [5, 6]
+                }
+            }));
+
+            it("should deeply merge two objects when second object has overrides", () => assertMergedResult([
+                {
+                    settings: {
+                        object: {
+                            a: true,
+                            b: false
+                        }
+                    }
+                },
+                {
+                    settings: {
+                        object: {
+                            c: true,
+                            a: false
+                        }
+                    }
+                }
+            ], {
+                plugins: baseConfig.plugins,
+
+                settings: {
+                    object: {
+                        a: false,
+                        b: false,
+                        c: true
+                    }
+                }
+            }));
+
+            it("should merge an object and undefined into one object", () => assertMergedResult([
+                {
+                    settings: {
+                        a: true,
+                        b: false
+                    }
+                },
+                {
+                }
+            ], {
+                plugins: baseConfig.plugins,
+
+                settings: {
+                    a: true,
+                    b: false
+                }
+            }));
+
+            it("should merge undefined and an object into one object", () => assertMergedResult([
+                {
+                },
+                {
+                    settings: {
+                        a: true,
+                        b: false
+                    }
+                }
+            ], {
+                plugins: baseConfig.plugins,
+
+                settings: {
+                    a: true,
+                    b: false
+                }
+            }));
+
+        });
+
+        describe("plugins", () => {
+
+            const pluginA = {};
+            const pluginB = {};
+            const pluginC = {};
+
+            it("should merge two objects", () => assertMergedResult([
+                {
+                    plugins: {
+                        a: pluginA,
+                        b: pluginB
+                    }
+                },
+                {
+                    plugins: {
+                        c: pluginC
+                    }
+                }
+            ], {
+                plugins: {
+                    a: pluginA,
+                    b: pluginB,
+                    c: pluginC,
+                    ...baseConfig.plugins
+                }
+            }));
+
+            it("should merge an object and undefined into one object", () => assertMergedResult([
+                {
+                    plugins: {
+                        a: pluginA,
+                        b: pluginB
+                    }
+                },
+                {
+                }
+            ], {
+                plugins: {
+                    a: pluginA,
+                    b: pluginB,
+                    ...baseConfig.plugins
+                }
+            }));
+
+            it("should error when attempting to redefine a plugin", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        plugins: {
+                            a: pluginA,
+                            b: pluginB
+                        }
+                    },
+                    {
+                        plugins: {
+                            a: pluginC
+                        }
+                    }
+                ], "Cannot redefine plugin \"a\".");
+            });
+
+            it("should error when plugin is not an object", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        plugins: {
+                            a: true
+                        }
+                    }
+                ], "Key \"a\": Expected an object.");
+            });
+
+
+        });
+
+        describe("processor", () => {
+
+            it("should merge two values when second is a string", () => {
+
+                const stubProcessor = {
+                    preprocess() {},
+                    postprocess() {}
+                };
+
+                return assertMergedResult([
+                    {
+                        processor: {
+                            preprocess() {},
+                            postprocess() {}
+                        }
+                    },
+                    {
+                        plugins: {
+                            markdown: {
+                                processors: {
+                                    markdown: stubProcessor
+                                }
+                            }
+                        },
+                        processor: "markdown/markdown"
+                    }
+                ], {
+                    plugins: {
+                        markdown: {
+                            processors: {
+                                markdown: stubProcessor
+                            }
+                        },
+                        ...baseConfig.plugins
+                    },
+                    processor: stubProcessor
+                });
+            });
+
+            it("should merge two values when second is an object", () => {
+
+                const processor = {
+                    preprocess() { },
+                    postprocess() { }
+                };
+
+                return assertMergedResult([
+                    {
+                        processor: "markdown/markdown"
+                    },
+                    {
+                        processor
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    processor
+                });
+            });
+
+            it("should error when an invalid string is used", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        processor: "foo"
+                    }
+                ], "pluginName/objectName");
+            });
+
+            it("should error when an empty string is used", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        processor: ""
+                    }
+                ], "pluginName/objectName");
+            });
+
+            it("should error when an invalid processor is used", async () => {
+                await assertInvalidConfig([
+                    {
+                        processor: {}
+                    }
+                ], "Object must have a preprocess() and a postprocess() method.");
+
+            });
+
+            it("should error when a processor cannot be found in a plugin", async () => {
+                await assertInvalidConfig([
+                    {
+                        plugins: {
+                            foo: {}
+                        },
+                        processor: "foo/bar"
+                    }
+                ], /Could not find "bar" in plugin "foo"/u);
+
+            });
+
+        });
+
+        describe("linterOptions", () => {
+
+            it("should error when an unexpected key is found", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        linterOptions: {
+                            foo: true
+                        }
+                    }
+                ], "Unexpected key \"foo\" found.");
+
+            });
+
+            describe("noInlineConfig", () => {
+
+                it("should error when an unexpected value is found", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            linterOptions: {
+                                noInlineConfig: "true"
+                            }
+                        }
+                    ], "Expected a Boolean.");
+                });
+
+                it("should merge two objects when second object has overrides", () => assertMergedResult([
+                    {
+                        linterOptions: {
+                            noInlineConfig: true
+                        }
+                    },
+                    {
+                        linterOptions: {
+                            noInlineConfig: false
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    linterOptions: {
+                        noInlineConfig: false
+                    }
+                }));
+
+                it("should merge an object and undefined into one object", () => assertMergedResult([
+                    {
+                        linterOptions: {
+                            noInlineConfig: false
+                        }
+                    },
+                    {
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    linterOptions: {
+                        noInlineConfig: false
+                    }
+                }));
+
+                it("should merge undefined and an object into one object", () => assertMergedResult([
+                    {
+                    },
+                    {
+                        linterOptions: {
+                            noInlineConfig: false
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    linterOptions: {
+                        noInlineConfig: false
+                    }
+                }));
+
+
+            });
+            describe("reportUnusedDisableDirectives", () => {
+
+                it("should error when an unexpected value is found", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            linterOptions: {
+                                reportUnusedDisableDirectives: "true"
+                            }
+                        }
+                    ], /Expected a Boolean/u);
+                });
+
+                it("should merge two objects when second object has overrides", () => assertMergedResult([
+                    {
+                        linterOptions: {
+                            reportUnusedDisableDirectives: false
+                        }
+                    },
+                    {
+                        linterOptions: {
+                            reportUnusedDisableDirectives: true
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    linterOptions: {
+                        reportUnusedDisableDirectives: true
+                    }
+                }));
+
+                it("should merge an object and undefined into one object", () => assertMergedResult([
+                    {},
+                    {
+                        linterOptions: {
+                            reportUnusedDisableDirectives: true
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    linterOptions: {
+                        reportUnusedDisableDirectives: true
+                    }
+                }));
+
+
+            });
+
+        });
+
+        describe("languageOptions", () => {
+
+            it("should error when an unexpected key is found", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        languageOptions: {
+                            foo: true
+                        }
+                    }
+                ], "Unexpected key \"foo\" found.");
+
+            });
+
+            it("should merge two languageOptions objects with different properties", () => assertMergedResult([
+                {
+                    languageOptions: {
+                        ecmaVersion: 2019
+                    }
+                },
+                {
+                    languageOptions: {
+                        sourceType: "commonjs"
+                    }
+                }
+            ], {
+                plugins: baseConfig.plugins,
+
+                languageOptions: {
+                    ecmaVersion: 2019,
+                    sourceType: "commonjs"
+                }
+            }));
+
+            describe("ecmaVersion", () => {
+
+                it("should error when an unexpected value is found", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            languageOptions: {
+                                ecmaVersion: "true"
+                            }
+                        }
+                    ], "Expected a number.");
+                });
+
+                it("should merge two objects when second object has overrides", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            ecmaVersion: 2019
+                        }
+                    },
+                    {
+                        languageOptions: {
+                            ecmaVersion: 2021
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        ecmaVersion: 2021
+                    }
+                }));
+
+                it("should merge an object and undefined into one object", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            ecmaVersion: 2021
+                        }
+                    },
+                    {
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        ecmaVersion: 2021
+                    }
+                }));
+
+
+                it("should merge undefined and an object into one object", () => assertMergedResult([
+                    {
+                    },
+                    {
+                        languageOptions: {
+                            ecmaVersion: 2021
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        ecmaVersion: 2021
+                    }
+                }));
+
+
+            });
+
+            describe("sourceType", () => {
+
+                it("should error when an unexpected value is found", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            languageOptions: {
+                                sourceType: "true"
+                            }
+                        }
+                    ], "Expected \"script\", \"module\", or \"commonjs\".");
+                });
+
+                it("should merge two objects when second object has overrides", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            sourceType: "module"
+                        }
+                    },
+                    {
+                        languageOptions: {
+                            sourceType: "script"
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        sourceType: "script"
+                    }
+                }));
+
+                it("should merge an object and undefined into one object", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            sourceType: "script"
+                        }
+                    },
+                    {
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        sourceType: "script"
+                    }
+                }));
+
+
+                it("should merge undefined and an object into one object", () => assertMergedResult([
+                    {
+                    },
+                    {
+                        languageOptions: {
+                            sourceType: "module"
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        sourceType: "module"
+                    }
+                }));
+
+
+            });
+
+            describe("globals", () => {
+
+                it("should error when an unexpected value is found", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            languageOptions: {
+                                globals: "true"
+                            }
+                        }
+                    ], "Expected an object.");
+                });
+
+                it("should error when an unexpected key value is found", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            languageOptions: {
+                                globals: {
+                                    foo: "truex"
+                                }
+                            }
+                        }
+                    ], "Key \"foo\": Expected \"readonly\", \"writable\", or \"off\".");
+                });
+
+                it("should error when a global has leading whitespace", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            languageOptions: {
+                                globals: {
+                                    " foo": "readonly"
+                                }
+                            }
+                        }
+                    ], /Global " foo" has leading or trailing whitespace/u);
+                });
+
+                it("should error when a global has trailing whitespace", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            languageOptions: {
+                                globals: {
+                                    "foo ": "readonly"
+                                }
+                            }
+                        }
+                    ], /Global "foo " has leading or trailing whitespace/u);
+                });
+
+                it("should merge two objects when second object has different keys", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            globals: {
+                                foo: "readonly"
+                            }
+                        }
+                    },
+                    {
+                        languageOptions: {
+                            globals: {
+                                bar: "writable"
+                            }
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        globals: {
+                            foo: "readonly",
+                            bar: "writable"
+                        }
+                    }
+                }));
+
+                it("should merge two objects when second object has overrides", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            globals: {
+                                foo: null
+                            }
+                        }
+                    },
+                    {
+                        languageOptions: {
+                            globals: {
+                                foo: "writeable"
+                            }
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        globals: {
+                            foo: "writeable"
+                        }
+                    }
+                }));
+
+                it("should merge an object and undefined into one object", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            globals: {
+                                foo: "readable"
+                            }
+                        }
+                    },
+                    {
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        globals: {
+                            foo: "readable"
+                        }
+                    }
+                }));
+
+
+                it("should merge undefined and an object into one object", () => assertMergedResult([
+                    {
+                    },
+                    {
+                        languageOptions: {
+                            globals: {
+                                foo: "false"
+                            }
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        globals: {
+                            foo: "false"
+                        }
+                    }
+                }));
+
+
+            });
+
+            describe("parser", () => {
+
+                it("should error when an unexpected value is found", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            languageOptions: {
+                                parser: true
+                            }
+                        }
+                    ], "Expected an object or string.");
+                });
+
+                it("should error when an unexpected value is found", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            languageOptions: {
+                                parser: "true"
+                            }
+                        }
+                    ], /Expected string in the form "pluginName\/objectName"/u);
+                });
+
+                it("should error when a plugin parser can't be found", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            languageOptions: {
+                                parser: "foo/bar"
+                            }
+                        }
+                    ], "Key \"parser\": Could not find \"bar\" in plugin \"foo\".");
+                });
+
+                it("should error when a value doesn't have a parse() method", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            languageOptions: {
+                                parser: {}
+                            }
+                        }
+                    ], "Expected object to have a parse() or parseForESLint() method.");
+                });
+
+                it("should merge two objects when second object has overrides", () => {
+
+                    const parser = { parse() {} };
+                    const stubParser = { parse() { } };
+
+                    return assertMergedResult([
+                        {
+                            languageOptions: {
+                                parser
+                            }
+                        },
+                        {
+                            plugins: {
+                                "@foo/baz": {
+                                    parsers: {
+                                        bar: stubParser
+                                    }
+                                }
+                            },
+                            languageOptions: {
+                                parser: "@foo/baz/bar"
+                            }
+                        }
+                    ], {
+                        plugins: {
+                            "@foo/baz": {
+                                parsers: {
+                                    bar: stubParser
+                                }
+                            },
+                            ...baseConfig.plugins
+                        },
+                        languageOptions: {
+                            parser: stubParser
+                        }
+                    });
+                });
+
+                it("should merge an object and undefined into one object", () => {
+
+                    const stubParser = { parse() { } };
+
+                    return assertMergedResult([
+                        {
+                            plugins: {
+                                foo: {
+                                    parsers: {
+                                        bar: stubParser
+                                    }
+                                }
+                            },
+
+                            languageOptions: {
+                                parser: "foo/bar"
+                            }
+                        },
+                        {
+                        }
+                    ], {
+                        plugins: {
+                            foo: {
+                                parsers: {
+                                    bar: stubParser
+                                }
+                            },
+                            ...baseConfig.plugins
+                        },
+
+                        languageOptions: {
+                            parser: stubParser
+                        }
+                    });
+
+                });
+
+
+                it("should merge undefined and an object into one object", () => {
+
+                    const stubParser = { parse() {} };
+
+                    return assertMergedResult([
+                        {
+                        },
+                        {
+                            plugins: {
+                                foo: {
+                                    parsers: {
+                                        bar: stubParser
+                                    }
+                                }
+                            },
+
+                            languageOptions: {
+                                parser: "foo/bar"
+                            }
+                        }
+                    ], {
+                        plugins: {
+                            foo: {
+                                parsers: {
+                                    bar: stubParser
+                                }
+                            },
+                            ...baseConfig.plugins
+                        },
+
+                        languageOptions: {
+                            parser: stubParser
+                        }
+                    });
+
+                });
+
+            });
+
+
+            describe("parserOptions", () => {
+
+                it("should error when an unexpected value is found", async () => {
+
+                    await assertInvalidConfig([
+                        {
+                            languageOptions: {
+                                parserOptions: "true"
+                            }
+                        }
+                    ], "Expected an object.");
+                });
+
+                it("should merge two objects when second object has different keys", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            parserOptions: {
+                                foo: "whatever"
+                            }
+                        }
+                    },
+                    {
+                        languageOptions: {
+                            parserOptions: {
+                                bar: "baz"
+                            }
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        parserOptions: {
+                            foo: "whatever",
+                            bar: "baz"
+                        }
+                    }
+                }));
+
+                it("should deeply merge two objects when second object has different keys", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            parserOptions: {
+                                ecmaFeatures: {
+                                    jsx: true
+                                }
+                            }
+                        }
+                    },
+                    {
+                        languageOptions: {
+                            parserOptions: {
+                                ecmaFeatures: {
+                                    globalReturn: true
+                                }
+                            }
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        parserOptions: {
+                            ecmaFeatures: {
+                                jsx: true,
+                                globalReturn: true
+                            }
+                        }
+                    }
+                }));
+
+                it("should deeply merge two objects when second object has missing key", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            parserOptions: {
+                                ecmaFeatures: {
+                                    jsx: true
+                                }
+                            }
+                        }
+                    },
+                    {
+                        languageOptions: {
+                            ecmaVersion: 2021
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        ecmaVersion: 2021,
+                        parserOptions: {
+                            ecmaFeatures: {
+                                jsx: true
+                            }
+                        }
+                    }
+                }));
+
+                it("should merge two objects when second object has overrides", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            parserOptions: {
+                                foo: "whatever"
+                            }
+                        }
+                    },
+                    {
+                        languageOptions: {
+                            parserOptions: {
+                                foo: "bar"
+                            }
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        parserOptions: {
+                            foo: "bar"
+                        }
+                    }
+                }));
+
+                it("should merge an object and undefined into one object", () => assertMergedResult([
+                    {
+                        languageOptions: {
+                            parserOptions: {
+                                foo: "whatever"
+                            }
+                        }
+                    },
+                    {
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        parserOptions: {
+                            foo: "whatever"
+                        }
+                    }
+                }));
+
+
+                it("should merge undefined and an object into one object", () => assertMergedResult([
+                    {
+                    },
+                    {
+                        languageOptions: {
+                            parserOptions: {
+                                foo: "bar"
+                            }
+                        }
+                    }
+                ], {
+                    plugins: baseConfig.plugins,
+
+                    languageOptions: {
+                        parserOptions: {
+                            foo: "bar"
+                        }
+                    }
+                }));
+
+
+            });
+
+
+        });
+
+        describe("rules", () => {
+
+            it("should error when an unexpected value is found", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        rules: true
+                    }
+                ], "Expected an object.");
+            });
+
+            it("should error when an invalid rule severity is set", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        rules: {
+                            foo: true
+                        }
+                    }
+                ], "Key \"rules\": Key \"foo\": Expected a string, number, or array.");
+            });
+
+            it("should error when an invalid rule severity of the right type is set", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        rules: {
+                            foo: 3
+                        }
+                    }
+                ], "Key \"rules\": Key \"foo\": Expected severity of \"off\", 0, \"warn\", 1, \"error\", or 2.");
+            });
+
+            it("should error when an invalid rule severity is set in an array", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        rules: {
+                            foo: [true]
+                        }
+                    }
+                ], "Key \"rules\": Key \"foo\": Expected severity of \"off\", 0, \"warn\", 1, \"error\", or 2.");
+            });
+
+            it("should error when rule doesn't exist", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        rules: {
+                            foox: [1, "bar"]
+                        }
+                    }
+                ], /Key "rules": Key "foox": Could not find "foox" in plugin "@"./u);
+            });
+
+            it("should error and suggest alternative when rule doesn't exist", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        rules: {
+                            "test2/match": "error"
+                        }
+                    }
+                ], /Key "rules": Key "test2\/match": Could not find "match" in plugin "test2"\. Did you mean "test1\/match"\?/u);
+            });
+
+            it("should error when plugin for rule doesn't exist", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        rules: {
+                            "doesnt-exist/match": "error"
+                        }
+                    }
+                ], /Key "rules": Key "doesnt-exist\/match": Could not find plugin "doesnt-exist"\./u);
+            });
+
+            it("should error when rule options don't match schema", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        rules: {
+                            foo: [1, "bar"]
+                        }
+                    }
+                ], /Value "bar" should be equal to one of the allowed values/u);
+            });
+
+            it("should error when rule options don't match schema requiring at least one item", async () => {
+
+                await assertInvalidConfig([
+                    {
+                        rules: {
+                            foo2: 1
+                        }
+                    }
+                ], /Value \[\] should NOT have fewer than 1 items/u);
+            });
+
+            it("should merge two objects", () => assertMergedResult([
+                {
+                    rules: {
+                        foo: 1,
+                        bar: "error"
+                    }
+                },
+                {
+                    rules: {
+                        baz: "warn",
+                        boom: 0
+                    }
+                }
+            ], {
+                plugins: baseConfig.plugins,
+
+                rules: {
+                    foo: [1],
+                    bar: [2],
+                    baz: [1],
+                    boom: [0]
+                }
+            }));
+
+            it("should merge two objects when second object has simple overrides", () => assertMergedResult([
+                {
+                    rules: {
+                        foo: [1, "always"],
+                        bar: "error"
+                    }
+                },
+                {
+                    rules: {
+                        foo: "error",
+                        bar: 0
+                    }
+                }
+            ], {
+                plugins: baseConfig.plugins,
+
+                rules: {
+                    foo: [2, "always"],
+                    bar: [0]
+                }
+            }));
+
+            it("should merge two objects when second object has array overrides", () => assertMergedResult([
+                {
+                    rules: {
+                        foo: 1,
+                        bar: "error"
+                    }
+                },
+                {
+                    rules: {
+                        foo: ["error", "never"],
+                        bar: ["warn", "foo"]
+                    }
+                }
+            ], {
+                plugins: baseConfig.plugins,
+                rules: {
+                    foo: [2, "never"],
+                    bar: [1, "foo"]
+                }
+            }));
+
+            it("should merge two objects and options when second object overrides without options", () => assertMergedResult([
+                {
+                    rules: {
+                        foo: [1, "always"],
+                        bar: "error"
+                    }
+                },
+                {
+                    plugins: {
+                        "foo/baz/boom": {
+                            rules: {
+                                bang: {}
+                            }
+                        }
+                    },
+                    rules: {
+                        foo: ["error"],
+                        bar: 0,
+                        "foo/baz/boom/bang": "error"
+                    }
+                }
+            ], {
+                plugins: {
+                    ...baseConfig.plugins,
+                    "foo/baz/boom": {
+                        rules: {
+                            bang: {}
+                        }
+                    }
+                },
+                rules: {
+                    foo: [2, "always"],
+                    bar: [0],
+                    "foo/baz/boom/bang": [2]
+                }
+            }));
+
+            it("should merge an object and undefined into one object", () => assertMergedResult([
+                {
+                    rules: {
+                        foo: 0,
+                        bar: 1
+                    }
+                },
+                {
+                }
+            ], {
+                plugins: baseConfig.plugins,
+                rules: {
+                    foo: [0],
+                    bar: [1]
+                }
+            }));
+
+            it("should merge a rule that doesn't exist without error when the rule is off", () => assertMergedResult([
+                {
+                    rules: {
+                        foo: 0,
+                        bar: 1
+                    }
+                },
+                {
+                    rules: {
+                        nonExistentRule: 0,
+                        nonExistentRule2: ["off", "bar"]
+                    }
+                }
+            ], {
+                plugins: baseConfig.plugins,
+                rules: {
+                    foo: [0],
+                    bar: [1],
+                    nonExistentRule: [0],
+                    nonExistentRule2: [0, "bar"]
+                }
+            }));
+
+        });
+
+    });
+});
index c7783a6c059e09f3e23e2ef5a7c7a9d8359b46d1..eaf4aaaaf05971245b36ff81838d7b882172862f 100644 (file)
@@ -19,9 +19,14 @@ const fCache = require("file-entry-cache");
 const sinon = require("sinon");
 const proxyquire = require("proxyquire").noCallThru().noPreserveCache();
 const shell = require("shelljs");
-const { CascadingConfigArrayFactory } = require("@eslint/eslintrc/lib/cascading-config-array-factory");
+const {
+    Legacy: {
+        CascadingConfigArrayFactory
+    }
+} = require("@eslint/eslintrc");
 const hash = require("../../../lib/cli-engine/hash");
 const { unIndent, createCustomTeardown } = require("../../_utils");
+const coreRules = require("../../../lib/rules");
 
 //------------------------------------------------------------------------------
 // Tests
@@ -40,7 +45,7 @@ describe("ESLint", () => {
     const originalDir = process.cwd();
     const fixtureDir = path.resolve(fs.realpathSync(os.tmpdir()), "eslint/fixtures");
 
-    /** @type {import("../../../lib/eslint")["ESLint"]} */
+    /** @type {import("../../../lib/eslint").ESLint} */
     let ESLint;
 
     /**
@@ -94,7 +99,7 @@ describe("ESLint", () => {
          * exceeds the default test timeout, so raise it just for this hook.
          * Mocha uses `this` to set timeouts on an individual hook level.
          */
-        this.timeout(60 * 1000); // eslint-disable-line no-invalid-this
+        this.timeout(60 * 1000); // eslint-disable-line no-invalid-this -- Mocha API
         shell.mkdir("-p", fixtureDir);
         shell.cp("-r", "./tests/fixtures/.", fixtureDir);
     });
@@ -122,7 +127,7 @@ describe("ESLint", () => {
 
         it("should report one fatal message when given a path by --ignore-path that is not a file when ignore is true.", () => {
             assert.throws(() => {
-                // eslint-disable-next-line no-new
+                // eslint-disable-next-line no-new -- Check for throwing
                 new ESLint({ ignorePath: fixtureDir });
             }, new RegExp(escapeStringRegExp(`Cannot read .eslintignore file: ${fixtureDir}\nError: EISDIR: illegal operation on a directory, read`), "u"));
         });
@@ -131,7 +136,7 @@ describe("ESLint", () => {
         it("should not modify baseConfig when format is specified", () => {
             const customBaseConfig = { root: true };
 
-            new ESLint({ baseConfig: customBaseConfig }); // eslint-disable-line no-new
+            new ESLint({ baseConfig: customBaseConfig }); // eslint-disable-line no-new -- Check for argument side effects
 
             assert.deepStrictEqual(customBaseConfig, { root: true });
         });
@@ -198,7 +203,7 @@ describe("ESLint", () => {
                     "- 'errorOnUnmatchedPattern' must be a boolean.",
                     "- 'extensions' must be an array of non-empty strings or null.",
                     "- 'fix' must be a boolean or a function.",
-                    "- 'fixTypes' must be an array of any of \"problem\", \"suggestion\", and \"layout\".",
+                    "- 'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".",
                     "- 'globInputPaths' must be a boolean.",
                     "- 'ignore' must be a boolean.",
                     "- 'ignorePath' must be a non-empty string or null.",
@@ -396,6 +401,7 @@ describe("ESLint", () => {
                     messages: [],
                     errorCount: 0,
                     warningCount: 0,
+                    fatalErrorCount: 0,
                     fixableErrorCount: 0,
                     fixableWarningCount: 0,
                     output: "var bar = foo;",
@@ -441,7 +447,7 @@ describe("ESLint", () => {
                         fix: true,
                         fixTypes: ["layou"]
                     });
-                }, /'fixTypes' must be an array of any of "problem", "suggestion", and "layout"\./iu);
+                }, /'fixTypes' must be an array of any of "directive", "problem", "suggestion", and "layout"\./iu);
             });
 
             it("should not fix any rules when fixTypes is used without fix", async () => {
@@ -596,6 +602,7 @@ describe("ESLint", () => {
                     ],
                     errorCount: 1,
                     warningCount: 0,
+                    fatalErrorCount: 0,
                     fixableErrorCount: 0,
                     fixableWarningCount: 0,
                     source: "var bar = foo",
@@ -635,6 +642,7 @@ describe("ESLint", () => {
                     ],
                     errorCount: 1,
                     warningCount: 0,
+                    fatalErrorCount: 1,
                     fixableErrorCount: 0,
                     fixableWarningCount: 0,
                     output: "var bar = foothis is a syntax error.",
@@ -673,6 +681,7 @@ describe("ESLint", () => {
                     ],
                     errorCount: 1,
                     warningCount: 0,
+                    fatalErrorCount: 1,
                     fixableErrorCount: 0,
                     fixableWarningCount: 0,
                     source: "var bar =",
@@ -760,6 +769,7 @@ describe("ESLint", () => {
                     ],
                     errorCount: 1,
                     warningCount: 0,
+                    fatalErrorCount: 1,
                     fixableErrorCount: 0,
                     fixableWarningCount: 0,
                     source: "var bar = foothis is a syntax error.\n return bar;",
@@ -786,7 +796,7 @@ describe("ESLint", () => {
             const Module = require("module");
             let originalFindPath = null;
 
-            /* eslint-disable no-underscore-dangle */
+            /* eslint-disable no-underscore-dangle -- Override Node API */
             before(() => {
                 originalFindPath = Module._findPath;
                 Module._findPath = function(id, ...otherArgs) {
@@ -799,7 +809,7 @@ describe("ESLint", () => {
             after(() => {
                 Module._findPath = originalFindPath;
             });
-            /* eslint-enable no-underscore-dangle */
+            /* eslint-enable no-underscore-dangle -- Override Node API */
 
             it("should resolve 'plugins:[\"@scope\"]' to 'node_modules/@scope/eslint-plugin'.", async () => {
                 eslint = new ESLint({ cwd: getFixturePath("plugin-shorthand/basic") });
@@ -846,7 +856,7 @@ describe("ESLint", () => {
 
         it("should throw if 'options' argument contains unknown key", async () => {
             eslint = new ESLint();
-            await assert.rejects(() => eslint.lintText("var a = 0", { filename: "foo.js" }), /'options' must not include the unknown option 'filename'/u);
+            await assert.rejects(() => eslint.lintText("var a = 0", { filename: "foo.js" }), /'options' must not include the unknown option\(s\): filename/u);
         });
 
         it("should throw if non-string value is given to 'options.filePath' option", async () => {
@@ -862,7 +872,7 @@ describe("ESLint", () => {
 
     describe("lintFiles()", () => {
 
-        /** @type {InstanceType<import("../../../lib/eslint")["ESLint"]>} */
+        /** @type {InstanceType<import("../../../lib/eslint").ESLint>} */
         let eslint;
 
         it("should use correct parser when custom parser is specified", async () => {
@@ -1657,6 +1667,7 @@ describe("ESLint", () => {
                         messages: [],
                         errorCount: 0,
                         warningCount: 0,
+                        fatalErrorCount: 0,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         output: "true ? \"yes\" : \"no\";\n",
@@ -1667,6 +1678,7 @@ describe("ESLint", () => {
                         messages: [],
                         errorCount: 0,
                         warningCount: 0,
+                        fatalErrorCount: 0,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         usedDeprecatedRules: []
@@ -1688,6 +1700,7 @@ describe("ESLint", () => {
                         ],
                         errorCount: 1,
                         warningCount: 0,
+                        fatalErrorCount: 0,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         output: "var msg = \"hi\";\nif (msg == \"hi\") {\n\n}\n",
@@ -1710,6 +1723,7 @@ describe("ESLint", () => {
                         ],
                         errorCount: 1,
                         warningCount: 0,
+                        fatalErrorCount: 0,
                         fixableErrorCount: 0,
                         fixableWarningCount: 0,
                         output: "var msg = \"hi\" + foo;\n",
@@ -2081,6 +2095,36 @@ describe("ESLint", () => {
                 assert.strictEqual(results[0].messages[0].ruleId, "test/example-rule");
             });
 
+            it("should return two messages when executing with `baseConfig` that extends preloaded plugin config", async () => {
+                eslint = new ESLint({
+                    cwd: path.join(fixtureDir, ".."),
+                    useEslintrc: false,
+                    baseConfig: {
+                        extends: ["plugin:test/preset"]
+                    },
+                    plugins: {
+                        test: {
+                            rules: {
+                                "example-rule": require("../../fixtures/rules/custom-rule")
+                            },
+                            configs: {
+                                preset: {
+                                    rules: {
+                                        "test/example-rule": 1
+                                    },
+                                    plugins: ["test"]
+                                }
+                            }
+                        }
+                    }
+                });
+                const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]);
+
+                assert.strictEqual(results.length, 1);
+                assert.strictEqual(results[0].messages.length, 2);
+                assert.strictEqual(results[0].messages[0].ruleId, "test/example-rule");
+            });
+
             it("should load plugins from the `loadPluginsRelativeTo` directory, if specified", async () => {
                 eslint = new ESLint({
                     resolvePluginsRelativeTo: getFixturePath("plugins"),
@@ -4297,7 +4341,7 @@ describe("ESLint", () => {
 
                 assert.throws(() => {
                     try {
-                        // eslint-disable-next-line no-new
+                        // eslint-disable-next-line no-new -- Check for error
                         new ESLint({ cwd });
                     } catch (error) {
                         assert.strictEqual(error.messageTemplate, "failed-to-read-json");
@@ -4323,7 +4367,7 @@ describe("ESLint", () => {
                 const cwd = getFixturePath("ignored-paths", "bad-package-json-ignore");
 
                 assert.throws(() => {
-                    // eslint-disable-next-line no-new
+                    // eslint-disable-next-line no-new -- Check for throwing
                     new ESLint({ cwd });
                 }, /Package\.json eslintIgnore property requires an array of paths/u);
             });
@@ -4452,7 +4496,7 @@ describe("ESLint", () => {
                 const ignorePath = getFixturePath("ignored-paths", "not-a-directory", ".foobaz");
 
                 assert.throws(() => {
-                    // eslint-disable-next-line no-new
+                    // eslint-disable-next-line no-new -- Check for throwing
                     new ESLint({ ignorePath, cwd });
                 }, /Cannot read \.eslintignore file/u);
             });
@@ -4652,7 +4696,7 @@ describe("ESLint", () => {
             assert.strictEqual(typeof formatter.format, "function");
         });
 
-        it("should throw if a customer formatter doesn't exist", async () => {
+        it("should throw if a custom formatter doesn't exist", async () => {
             const engine = new ESLint();
             const formatterPath = getFixturePath("formatters", "doesntexist.js");
             const fullFormatterPath = path.resolve(formatterPath);
@@ -4790,6 +4834,80 @@ describe("ESLint", () => {
         });
     });
 
+    describe("getRulesMetaForResults()", () => {
+        it("should return empty object when there are no linting errors", async () => {
+            const engine = new ESLint({
+                useEslintrc: false
+            });
+
+            const rulesMeta = engine.getRulesMetaForResults([]);
+
+            assert.strictEqual(Object.keys(rulesMeta).length, 0);
+        });
+
+        it("should return one rule meta when there is a linting error", async () => {
+            const engine = new ESLint({
+                useEslintrc: false,
+                overrideConfig: {
+                    rules: {
+                        semi: 2
+                    }
+                }
+            });
+
+            const results = await engine.lintText("a");
+            const rulesMeta = engine.getRulesMetaForResults(results);
+
+            assert.strictEqual(rulesMeta.semi, coreRules.get("semi").meta);
+        });
+
+        it("should return multiple rule meta when there are multiple linting errors", async () => {
+            const engine = new ESLint({
+                useEslintrc: false,
+                overrideConfig: {
+                    rules: {
+                        semi: 2,
+                        quotes: [2, "double"]
+                    }
+                }
+            });
+
+            const results = await engine.lintText("'a'");
+            const rulesMeta = engine.getRulesMetaForResults(results);
+
+            assert.strictEqual(rulesMeta.semi, coreRules.get("semi").meta);
+            assert.strictEqual(rulesMeta.quotes, coreRules.get("quotes").meta);
+        });
+
+        it("should return multiple rule meta when there are multiple linting errors from a plugin", async () => {
+            const nodePlugin = require("eslint-plugin-node");
+            const engine = new ESLint({
+                useEslintrc: false,
+                plugins: {
+                    node: nodePlugin
+                },
+                overrideConfig: {
+                    plugins: ["node"],
+                    rules: {
+                        "node/no-new-require": 2,
+                        semi: 2,
+                        quotes: [2, "double"]
+                    }
+                }
+            });
+
+            const results = await engine.lintText("new require('hi')");
+            const rulesMeta = engine.getRulesMetaForResults(results);
+
+            assert.strictEqual(rulesMeta.semi, coreRules.get("semi").meta);
+            assert.strictEqual(rulesMeta.quotes, coreRules.get("quotes").meta);
+            assert.strictEqual(
+                rulesMeta["node/no-new-require"],
+                nodePlugin.rules["no-new-require"].meta
+            );
+        });
+    });
+
     describe("outputFixes()", () => {
         afterEach(() => {
             sinon.verifyAndRestore();
@@ -4864,6 +4982,7 @@ describe("ESLint", () => {
             ].join("\n");
             const config = {
                 ignore: true,
+                useEslintrc: false,
                 allowInlineConfig: false,
                 overrideConfig: {
                     env: { browser: true },
@@ -4890,6 +5009,7 @@ describe("ESLint", () => {
             ].join("\n");
             const config = {
                 ignore: true,
+                useEslintrc: false,
                 allowInlineConfig: true,
                 overrideConfig: {
                     env: { browser: true },
@@ -4925,13 +5045,18 @@ describe("ESLint", () => {
                                 message: "Unused eslint-disable directive (no problems were reported).",
                                 line: 1,
                                 column: 1,
+                                fix: {
+                                    range: [0, 20],
+                                    text: " "
+                                },
                                 severity: 2,
                                 nodeType: null
                             }
                         ],
                         errorCount: 1,
                         warningCount: 0,
-                        fixableErrorCount: 0,
+                        fatalErrorCount: 0,
+                        fixableErrorCount: 1,
                         fixableWarningCount: 0,
                         source: "/* eslint-disable */",
                         usedDeprecatedRules: []
@@ -5985,7 +6110,8 @@ describe("ESLint", () => {
                         ],
                         source: "a == b",
                         usedDeprecatedRules: [],
-                        warningCount: 0
+                        warningCount: 0,
+                        fatalErrorCount: 0
                     }
                 ]);
             });
@@ -6008,7 +6134,8 @@ describe("ESLint", () => {
                         fixableWarningCount: 0,
                         messages: [],
                         usedDeprecatedRules: [],
-                        warningCount: 0
+                        warningCount: 0,
+                        fatalErrorCount: 0
                     }
                 ]);
             });
@@ -6055,7 +6182,8 @@ describe("ESLint", () => {
                         fixableWarningCount: 0,
                         messages: [],
                         usedDeprecatedRules: [],
-                        warningCount: 0
+                        warningCount: 0,
+                        fatalErrorCount: 0
                     }
                 ]);
             });
@@ -6091,7 +6219,8 @@ describe("ESLint", () => {
                         ],
                         source: "a == b",
                         usedDeprecatedRules: [],
-                        warningCount: 0
+                        warningCount: 0,
+                        fatalErrorCount: 0
                     }
                 ]);
             });
index e9fe62e2c305bcb8d439907c13fcbe1cf38587d2..50a9af1d5079771c33791f4c380e4ac5458e8ff8 100644 (file)
@@ -60,8 +60,8 @@ describe("ConfigFile", () => {
         [
             ["JavaScript", "foo.js", espree.parse],
             ["JSON", "bar.json", JSON.parse],
-            ["YAML", "foo.yaml", yaml.safeLoad],
-            ["YML", "foo.yml", yaml.safeLoad]
+            ["YAML", "foo.yaml", yaml.load],
+            ["YML", "foo.yml", yaml.load]
         ].forEach(([fileType, filename, validate]) => {
 
             it(`should write a file through fs when a ${fileType} path is passed`, () => {
index 84dddcba4e62b7ae5e32334579c5a7b6b21479bf..72708bcbddc85dfd8ab9f52595635b0e267edd9c 100644 (file)
@@ -5,15 +5,41 @@
 
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const assert = require("chai").assert;
 const applyDisableDirectives = require("../../../lib/linter/apply-disable-directives");
 
+//-----------------------------------------------------------------------------
+// Helpers
+//-----------------------------------------------------------------------------
+
+/**
+ * Creates a ParentComment for a given range.
+ * @param {[number, number]} range total range of the comment
+ * @param {string} value String value of the comment
+ * @param {string[]} ruleIds Rule IDs reported in the value
+ * @returns {ParentComment} Test-ready ParentComment object.
+ */
+function createParentComment(range, value, ruleIds = []) {
+    return {
+        commentToken: { range, value },
+        ruleIds
+    };
+}
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 describe("apply-disable-directives", () => {
     describe("/* eslint-disable */ comments without rules", () => {
         it("keeps problems before the comment on the same line", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable", line: 1, column: 8, ruleId: null }],
+                    directives: [{ parentComment: createParentComment([0, 7]), type: "disable", line: 1, column: 8, ruleId: null }],
                     problems: [{ line: 1, column: 7, ruleId: "foo" }]
                 }),
                 [{ ruleId: "foo", line: 1, column: 7 }]
@@ -23,7 +49,7 @@ describe("apply-disable-directives", () => {
         it("keeps problems on a previous line before the comment", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable", line: 2, column: 8, ruleId: null }],
+                    directives: [{ parentComment: createParentComment([21, 27]), type: "disable", line: 2, column: 1, ruleId: null }],
                     problems: [{ line: 1, column: 10, ruleId: "foo" }]
                 }),
                 [{ ruleId: "foo", line: 1, column: 10 }]
@@ -85,7 +111,13 @@ describe("apply-disable-directives", () => {
         it("keeps problems after the comment that have a different ruleId", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable", line: 1, column: 8, ruleId: "foo" }],
+                    directives: [{
+                        parentComment: createParentComment([26, 29]),
+                        type: "disable",
+                        line: 1,
+                        column: 1,
+                        ruleId: "foo"
+                    }],
                     problems: [{ line: 2, column: 3, ruleId: "not-foo" }]
                 }),
                 [{ line: 2, column: 3, ruleId: "not-foo" }]
@@ -95,7 +127,13 @@ describe("apply-disable-directives", () => {
         it("keeps problems before the comment that have the same ruleId", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable", line: 1, column: 8, ruleId: "foo" }],
+                    directives: [{
+                        parentComment: createParentComment([7, 31]),
+                        type: "disable",
+                        line: 1,
+                        column: 8,
+                        ruleId: "foo"
+                    }],
                     problems: [{ line: 1, column: 7, ruleId: "foo" }]
                 }),
                 [{ line: 1, column: 7, ruleId: "foo" }]
@@ -108,12 +146,24 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "enable", line: 1, column: 5, ruleId: null }
+                        {
+                            parentComment: createParentComment([0, 26]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([27, 45]),
+                            type: "enable",
+                            line: 1,
+                            column: 26,
+                            ruleId: null
+                        }
                     ],
-                    problems: [{ line: 1, column: 7, ruleId: "foo" }]
+                    problems: [{ line: 1, column: 27, ruleId: "foo" }]
                 }),
-                [{ line: 1, column: 7, ruleId: "foo" }]
+                [{ line: 1, column: 27, ruleId: "foo" }]
             );
         });
 
@@ -121,12 +171,24 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "enable", line: 1, column: 5, ruleId: null }
+                        {
+                            parentComment: createParentComment([0, 25]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([26, 40]),
+                            type: "enable",
+                            line: 1,
+                            column: 26,
+                            ruleId: null
+                        }
                     ],
-                    problems: [{ line: 1, column: 5, ruleId: "foo" }]
+                    problems: [{ line: 1, column: 26, ruleId: "foo" }]
                 }),
-                [{ line: 1, column: 5, ruleId: "foo" }]
+                [{ line: 1, column: 26, ruleId: "foo" }]
             );
         });
 
@@ -135,7 +197,7 @@ describe("apply-disable-directives", () => {
                 applyDisableDirectives({
                     directives: [
                         { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "enable", line: 1, column: 5, ruleId: null }
+                        { type: "enable", line: 1, column: 26, ruleId: null }
                     ],
                     problems: [{ line: 1, column: 3, ruleId: "foo" }]
                 }),
@@ -147,9 +209,27 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "enable", line: 1, column: 5, ruleId: "foo" },
-                        { type: "disable", line: 2, column: 1, ruleId: "foo" }
+                        {
+                            parentComment: createParentComment([0, 20]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([26, 44]),
+                            type: "enable",
+                            line: 1,
+                            column: 26,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([45, 63]),
+                            type: "disable",
+                            line: 2,
+                            column: 1,
+                            ruleId: "foo"
+                        }
                     ],
                     problems: [{ line: 3, column: 3, ruleId: "foo" }]
                 }),
@@ -161,9 +241,27 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "enable", line: 1, column: 5, ruleId: "foo" },
-                        { type: "disable", line: 2, column: 1, ruleId: null }
+                        {
+                            parentComment: createParentComment([0, 20]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([21, 44]),
+                            type: "enable",
+                            line: 1,
+                            column: 26,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([45, 63]),
+                            type: "disable",
+                            line: 2,
+                            column: 1,
+                            ruleId: null
+                        }
                     ],
                     problems: [{ line: 3, column: 3, ruleId: "foo" }]
                 }),
@@ -175,8 +273,20 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: "foo" },
-                        { type: "enable", line: 1, column: 5, ruleId: null }
+                        {
+                            parentComment: createParentComment([0, 20]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([25, 44]),
+                            type: "enable",
+                            line: 1,
+                            column: 26,
+                            ruleId: null
+                        }
                     ],
                     problems: [{ line: 1, column: 3, ruleId: "not-foo" }]
                 }),
@@ -190,8 +300,20 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 4, ruleId: null },
-                        { type: "enable", line: 2, column: 1, ruleId: "foo" }
+                        {
+                            parentComment: createParentComment([0, 20]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([21, 44]),
+                            type: "enable",
+                            line: 2,
+                            column: 1,
+                            ruleId: "foo"
+                        }
                     ],
                     problems: [{ line: 2, column: 4, ruleId: "foo" }]
                 }),
@@ -203,8 +325,20 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 4, ruleId: null },
-                        { type: "enable", line: 2, column: 1, ruleId: "foo" }
+                        {
+                            parentComment: createParentComment([0, 20]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([21, 44]),
+                            type: "enable",
+                            line: 2,
+                            column: 1,
+                            ruleId: "foo"
+                        }
                     ],
                     problems: [{ line: 2, column: 1, ruleId: "foo" }]
                 }),
@@ -216,7 +350,7 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 4, ruleId: null },
+                        { type: "disable", line: 1, column: 1, ruleId: null },
                         { type: "enable", line: 2, column: 1, ruleId: "foo" }
                     ],
                     problems: [{ line: 2, column: 4, ruleId: "not-foo" }]
@@ -230,22 +364,22 @@ describe("apply-disable-directives", () => {
                 applyDisableDirectives({
                     directives: [
                         { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "enable", line: 1, column: 3, ruleId: "foo" },
-                        { type: "enable", line: 1, column: 5, ruleId: "bar" }
+                        { type: "enable", line: 1, column: 22, ruleId: "foo" },
+                        { type: "enable", line: 1, column: 46, ruleId: "bar" }
                     ],
                     problems: [
-                        { line: 1, column: 2, ruleId: "foo" },
-                        { line: 1, column: 2, ruleId: "bar" },
-                        { line: 1, column: 4, ruleId: "foo" },
-                        { line: 1, column: 4, ruleId: "bar" },
-                        { line: 1, column: 6, ruleId: "foo" },
-                        { line: 1, column: 6, ruleId: "bar" }
+                        { line: 1, column: 10, ruleId: "foo" },
+                        { line: 1, column: 10, ruleId: "bar" },
+                        { line: 1, column: 30, ruleId: "foo" },
+                        { line: 1, column: 30, ruleId: "bar" },
+                        { line: 1, column: 50, ruleId: "foo" },
+                        { line: 1, column: 50, ruleId: "bar" }
                     ]
                 }),
                 [
-                    { line: 1, column: 4, ruleId: "foo" },
-                    { line: 1, column: 6, ruleId: "foo" },
-                    { line: 1, column: 6, ruleId: "bar" }
+                    { line: 1, column: 30, ruleId: "foo" },
+                    { line: 1, column: 50, ruleId: "foo" },
+                    { line: 1, column: 50, ruleId: "bar" }
                 ]
             );
         });
@@ -255,7 +389,13 @@ describe("apply-disable-directives", () => {
         it("keeps problems on a previous line", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-line", line: 2, column: 1, ruleId: null }],
+                    directives: [{
+                        parentComment: createParentComment([6, 27]),
+                        type: "disable-line",
+                        line: 2,
+                        column: 1,
+                        ruleId: null
+                    }],
                     problems: [{ line: 1, column: 5, ruleId: "foo" }]
                 }),
                 [{ line: 1, column: 5, ruleId: "foo" }]
@@ -265,7 +405,13 @@ describe("apply-disable-directives", () => {
         it("filters problems before the comment on the same line", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-line", line: 1, column: 5, ruleId: null }],
+                    directives: [{
+                        parentComment: createParentComment([7, 28]),
+                        type: "disable-line",
+                        line: 1,
+                        column: 8,
+                        ruleId: null
+                    }],
                     problems: [{ line: 1, column: 1, ruleId: "foo" }]
                 }),
                 []
@@ -275,7 +421,13 @@ describe("apply-disable-directives", () => {
         it("filters problems after the comment on the same line", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-line", line: 1, column: 5, ruleId: null }],
+                    directives: [{
+                        parentComment: createParentComment([7, 28]),
+                        type: "disable-line",
+                        line: 1,
+                        column: 8,
+                        ruleId: null
+                    }],
                     problems: [{ line: 1, column: 10, ruleId: "foo" }]
                 }),
                 []
@@ -285,7 +437,13 @@ describe("apply-disable-directives", () => {
         it("keeps problems on a following line", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-line", line: 1, column: 4 }],
+                    directives: [{
+                        parentComment: createParentComment([7, 34]),
+                        type: "disable-line",
+                        line: 1,
+                        column: 8,
+                        ruleId: "foo"
+                    }],
                     problems: [{ line: 2, column: 1, ruleId: "foo" }]
                 }),
                 [{ line: 2, column: 1, ruleId: "foo" }]
@@ -297,7 +455,13 @@ describe("apply-disable-directives", () => {
         it("filters problems on the current line that match the ruleId", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-line", line: 1, column: 4, ruleId: "foo" }],
+                    directives: [{
+                        parentComment: createParentComment([7, 34]),
+                        type: "disable-line",
+                        line: 1,
+                        column: 8,
+                        ruleId: "foo"
+                    }],
                     problems: [{ line: 1, column: 2, ruleId: "foo" }]
                 }),
                 []
@@ -307,7 +471,7 @@ describe("apply-disable-directives", () => {
         it("keeps problems on the current line that do not match the ruleId", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-line", line: 1, column: 4, ruleId: "foo" }],
+                    directives: [{ parentComment: createParentComment([0, 27]), type: "disable-line", line: 1, column: 1, ruleId: "foo" }],
                     problems: [{ line: 1, column: 2, ruleId: "not-foo" }]
                 }),
                 [{ line: 1, column: 2, ruleId: "not-foo" }]
@@ -318,8 +482,20 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "disable-line", line: 1, column: 3, ruleId: "foo" }
+                        {
+                            parentComment: createParentComment([0, 21]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([24, 28]),
+                            type: "disable-line",
+                            line: 1,
+                            column: 22,
+                            ruleId: "foo"
+                        }
                     ],
                     problems: [{ line: 1, column: 5, ruleId: "not-foo" }]
                 }),
@@ -331,12 +507,48 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable-line", line: 1, column: 5, ruleId: "foo" },
-                        { type: "disable-line", line: 2, column: 5, ruleId: "foo" },
-                        { type: "disable-line", line: 3, column: 5, ruleId: "foo" },
-                        { type: "disable-line", line: 4, column: 5, ruleId: "foo" },
-                        { type: "disable-line", line: 5, column: 5, ruleId: "foo" },
-                        { type: "disable-line", line: 6, column: 5, ruleId: "foo" }
+                        {
+                            parentComment: createParentComment([7, 34]),
+                            type: "disable-line",
+                            line: 1,
+                            column: 8,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([38, 73]),
+                            type: "disable-line",
+                            line: 2,
+                            column: 8,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([76, 111]),
+                            type: "disable-line",
+                            line: 3,
+                            column: 8,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([114, 149]),
+                            type: "disable-line",
+                            line: 4,
+                            column: 8,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([152, 187]),
+                            type: "disable-line",
+                            line: 5,
+                            column: 8,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([190, 225]),
+                            type: "disable-line",
+                            line: 6,
+                            column: 8,
+                            ruleId: "foo"
+                        }
                     ],
                     problems: [{ line: 2, column: 1, ruleId: "foo" }]
                 }),
@@ -349,7 +561,13 @@ describe("apply-disable-directives", () => {
         it("filters problems on the next line", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-next-line", line: 1, column: 1, ruleId: null }],
+                    directives: [{
+                        parentComment: createParentComment([0, 31]),
+                        type: "disable-next-line",
+                        line: 1,
+                        column: 1,
+                        ruleId: null
+                    }],
                     problems: [{ line: 2, column: 3, ruleId: "foo" }]
                 }),
                 []
@@ -359,7 +577,13 @@ describe("apply-disable-directives", () => {
         it("keeps problems on the same line", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-next-line", line: 1, column: 1, ruleId: null }],
+                    directives: [{
+                        parentComment: createParentComment([0, 31]),
+                        type: "disable-next-line",
+                        line: 1,
+                        column: 1,
+                        ruleId: null
+                    }],
                     problems: [{ line: 1, column: 3, ruleId: "foo" }]
                 }),
                 [{ line: 1, column: 3, ruleId: "foo" }]
@@ -369,7 +593,13 @@ describe("apply-disable-directives", () => {
         it("keeps problems after the next line", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-next-line", line: 1, column: 1, ruleId: null }],
+                    directives: [{
+                        parentComment: createParentComment([0, 31]),
+                        type: "disable-next-line",
+                        line: 1,
+                        column: 1,
+                        ruleId: null
+                    }],
                     problems: [{ line: 3, column: 3, ruleId: "foo" }]
                 }),
                 [{ line: 3, column: 3, ruleId: "foo" }]
@@ -404,7 +634,13 @@ describe("apply-disable-directives", () => {
         it("keeps problems on the next line that do not match the ruleId", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-next-line", line: 1, column: 1, ruleId: "foo" }],
+                    directives: [{
+                        parentComment: createParentComment([0, 31]),
+                        type: "disable-next-line",
+                        line: 1,
+                        column: 1,
+                        ruleId: "foo"
+                    }],
                     problems: [{ line: 2, column: 1, ruleId: "not-foo" }]
                 }),
                 [{ line: 2, column: 1, ruleId: "not-foo" }]
@@ -429,7 +665,12 @@ describe("apply-disable-directives", () => {
         it("Adds a problem for /* eslint-disable */", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable", line: 1, column: 5 }],
+                    directives: [{
+                        parentComment: createParentComment([0, 20]),
+                        type: "disable",
+                        line: 1,
+                        column: 1
+                    }],
                     problems: [],
                     reportUnusedDisableDirectives: "error"
                 }),
@@ -438,7 +679,37 @@ describe("apply-disable-directives", () => {
                         ruleId: null,
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
-                        column: 5,
+                        column: 1,
+                        fix: {
+                            range: [0, 20],
+                            text: " "
+                        },
+                        severity: 2,
+                        nodeType: null
+                    }
+                ]
+            );
+        });
+
+        it("Does not fix a problem for /* eslint-disable */ when disableFixes is enabled", () => {
+            assert.deepStrictEqual(
+                applyDisableDirectives({
+                    directives: [{
+                        parentComment: createParentComment([0, 20]),
+                        type: "disable",
+                        line: 1,
+                        column: 1
+                    }],
+                    disableFixes: true,
+                    problems: [],
+                    reportUnusedDisableDirectives: "error"
+                }),
+                [
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported).",
+                        line: 1,
+                        column: 1,
                         severity: 2,
                         nodeType: null
                     }
@@ -449,7 +720,7 @@ describe("apply-disable-directives", () => {
         it("Does not add a problem for /* eslint-disable */ /* (problem) */", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable", line: 1, column: 5, ruleId: null }],
+                    directives: [{ type: "disable", line: 1, column: 1, ruleId: null }],
                     problems: [{ line: 2, column: 1, ruleId: "foo" }],
                     reportUnusedDisableDirectives: "error"
                 }),
@@ -460,7 +731,13 @@ describe("apply-disable-directives", () => {
         it("Adds a problem for /* eslint-disable foo */", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable", line: 1, column: 5, ruleId: "foo" }],
+                    directives: [{
+                        parentComment: createParentComment([0, 21]),
+                        type: "disable",
+                        line: 1,
+                        column: 1,
+                        ruleId: "foo"
+                    }],
                     problems: [],
                     reportUnusedDisableDirectives: "error"
                 }),
@@ -469,7 +746,11 @@ describe("apply-disable-directives", () => {
                         ruleId: null,
                         message: "Unused eslint-disable directive (no problems were reported from 'foo').",
                         line: 1,
-                        column: 5,
+                        column: 1,
+                        fix: {
+                            range: [0, 21],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -480,7 +761,13 @@ describe("apply-disable-directives", () => {
         it("Adds a problem for /* eslint-disable foo */ /* (problem from another rule) */", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable", line: 1, column: 5, ruleId: "foo" }],
+                    directives: [{
+                        parentComment: createParentComment([0, 24]),
+                        type: "disable",
+                        line: 1,
+                        column: 1,
+                        ruleId: "foo"
+                    }],
                     problems: [{ line: 1, column: 20, ruleId: "not-foo" }],
                     reportUnusedDisableDirectives: "error"
                 }),
@@ -489,7 +776,11 @@ describe("apply-disable-directives", () => {
                         ruleId: null,
                         message: "Unused eslint-disable directive (no problems were reported from 'foo').",
                         line: 1,
-                        column: 5,
+                        column: 1,
+                        fix: {
+                            range: [0, 24],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     },
@@ -506,8 +797,20 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 5, ruleId: null },
-                        { type: "enable", line: 1, column: 6, ruleId: "foo" }
+                        {
+                            parentComment: createParentComment([0, 21]),
+                            type: "disable",
+                            line: 1,
+                            column: 8,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([0, 21]),
+                            type: "enable",
+                            line: 1,
+                            column: 24,
+                            ruleId: "foo"
+                        }
                     ],
                     problems: [{ line: 1, column: 2, ruleId: "foo" }],
                     reportUnusedDisableDirectives: "error"
@@ -521,8 +824,12 @@ describe("apply-disable-directives", () => {
                     {
                         ruleId: null,
                         message: "Unused eslint-disable directive (no problems were reported).",
+                        fix: {
+                            range: [0, 21],
+                            text: " "
+                        },
                         line: 1,
-                        column: 5,
+                        column: 8,
                         severity: 2,
                         nodeType: null
                     }
@@ -534,8 +841,20 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 5, ruleId: null },
-                        { type: "enable", line: 1, column: 6, ruleId: null }
+                        {
+                            parentComment: createParentComment([0, 20]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([21, 41]),
+                            type: "enable",
+                            line: 1,
+                            column: 12,
+                            ruleId: null
+                        }
                     ],
                     problems: [],
                     reportUnusedDisableDirectives: "error"
@@ -545,7 +864,11 @@ describe("apply-disable-directives", () => {
                         ruleId: null,
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
-                        column: 5,
+                        column: 1,
+                        fix: {
+                            range: [0, 20],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -557,8 +880,20 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "disable", line: 2, column: 1, ruleId: null }
+                        {
+                            parentComment: createParentComment([0, 21]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([21, 42]),
+                            type: "disable",
+                            line: 2,
+                            column: 1,
+                            ruleId: null
+                        }
                     ],
                     problems: [],
                     reportUnusedDisableDirectives: "error"
@@ -569,6 +904,10 @@ describe("apply-disable-directives", () => {
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
                         column: 1,
+                        fix: {
+                            range: [0, 21],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     },
@@ -577,6 +916,10 @@ describe("apply-disable-directives", () => {
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 2,
                         column: 1,
+                        fix: {
+                            range: [21, 42],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -588,8 +931,20 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "disable", line: 2, column: 1, ruleId: null }
+                        {
+                            parentComment: createParentComment([0, 21]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([22, 45]),
+                            type: "disable",
+                            line: 2,
+                            column: 1,
+                            ruleId: null
+                        }
                     ],
                     problems: [{ line: 3, column: 1, ruleId: "foo" }],
                     reportUnusedDisableDirectives: "error"
@@ -600,6 +955,10 @@ describe("apply-disable-directives", () => {
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
                         column: 1,
+                        fix: {
+                            range: [0, 21],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -611,8 +970,20 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: "foo" },
-                        { type: "disable", line: 2, column: 1, ruleId: null }
+                        {
+                            parentComment: createParentComment([0, 21]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([22, 45]),
+                            type: "disable",
+                            line: 2,
+                            column: 1,
+                            ruleId: null
+                        }
                     ],
                     problems: [{ line: 3, column: 1, ruleId: "foo" }],
                     reportUnusedDisableDirectives: "error"
@@ -623,6 +994,10 @@ describe("apply-disable-directives", () => {
                         message: "Unused eslint-disable directive (no problems were reported from 'foo').",
                         line: 1,
                         column: 1,
+                        fix: {
+                            range: [0, 21],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -633,7 +1008,7 @@ describe("apply-disable-directives", () => {
         it("Does not add a problem for /* eslint-disable foo */ /* (problem from foo) */", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable", line: 1, column: 5, ruleId: "foo" }],
+                    directives: [{ type: "disable", line: 1, column: 1, ruleId: "foo" }],
                     problems: [{ line: 1, column: 6, ruleId: "foo" }],
                     reportUnusedDisableDirectives: "error"
                 }),
@@ -645,8 +1020,20 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "disable", line: 2, column: 1, ruleId: "foo" }
+                        {
+                            parentComment: createParentComment([0, 21]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([22, 45]),
+                            type: "disable",
+                            line: 2,
+                            column: 1,
+                            ruleId: "foo"
+                        }
                     ],
                     problems: [{ line: 3, column: 1, ruleId: "foo" }],
                     reportUnusedDisableDirectives: "error"
@@ -657,6 +1044,10 @@ describe("apply-disable-directives", () => {
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
                         column: 1,
+                        fix: {
+                            range: [0, 21],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -668,8 +1059,20 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "disable", line: 2, column: 1, ruleId: "foo" }
+                        {
+                            parentComment: createParentComment([0, 20]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([21, 45]),
+                            type: "disable",
+                            line: 2,
+                            column: 1,
+                            ruleId: "foo"
+                        }
                     ],
                     problems: [{ line: 3, column: 1, ruleId: "bar" }],
                     reportUnusedDisableDirectives: "error"
@@ -680,6 +1083,10 @@ describe("apply-disable-directives", () => {
                         message: "Unused eslint-disable directive (no problems were reported from 'foo').",
                         line: 2,
                         column: 1,
+                        fix: {
+                            range: [21, 45],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -691,10 +1098,22 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 5, ruleId: "foo" },
-                        { type: "enable", line: 1, column: 8, ruleId: "foo" }
+                        {
+                            parentComment: createParentComment([0, 20]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([25, 46]),
+                            type: "enable",
+                            line: 1,
+                            column: 26,
+                            ruleId: "foo"
+                        }
                     ],
-                    problems: [{ line: 1, column: 10, ruleId: "foo" }],
+                    problems: [{ line: 1, column: 30, ruleId: "foo" }],
                     reportUnusedDisableDirectives: "error"
                 }),
                 [
@@ -702,14 +1121,18 @@ describe("apply-disable-directives", () => {
                         ruleId: null,
                         message: "Unused eslint-disable directive (no problems were reported from 'foo').",
                         line: 1,
-                        column: 5,
+                        column: 1,
+                        fix: {
+                            range: [0, 20],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     },
                     {
                         ruleId: "foo",
                         line: 1,
-                        column: 10
+                        column: 30
                     }
                 ]
             );
@@ -719,10 +1142,22 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 5, ruleId: "foo" },
-                        { type: "enable", line: 1, column: 8, ruleId: null }
+                        {
+                            parentComment: createParentComment([0, 24]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([25, 49]),
+                            type: "enable",
+                            line: 1,
+                            column: 26,
+                            ruleId: null
+                        }
                     ],
-                    problems: [{ line: 1, column: 10, ruleId: "foo" }],
+                    problems: [{ line: 1, column: 30, ruleId: "foo" }],
                     reportUnusedDisableDirectives: "error"
                 }),
                 [
@@ -730,14 +1165,18 @@ describe("apply-disable-directives", () => {
                         ruleId: null,
                         message: "Unused eslint-disable directive (no problems were reported from 'foo').",
                         line: 1,
-                        column: 5,
+                        column: 1,
+                        fix: {
+                            range: [0, 24],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     },
                     {
                         ruleId: "foo",
                         line: 1,
-                        column: 10
+                        column: 30
                     }
                 ]
             );
@@ -747,9 +1186,27 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "disable", line: 2, column: 1, ruleId: "foo" },
-                        { type: "enable", line: 3, column: 1, ruleId: "foo" }
+                        {
+                            parentComment: createParentComment([0, 21]),
+                            type: "disable",
+                            line: 1,
+                            column: 1,
+                            ruleId: null
+                        },
+                        {
+                            parentComment: createParentComment([22, 45]),
+                            type: "disable",
+                            line: 2,
+                            column: 1,
+                            ruleId: "foo"
+                        },
+                        {
+                            parentComment: createParentComment([46, 69]),
+                            type: "enable",
+                            line: 3,
+                            column: 1,
+                            ruleId: "foo"
+                        }
                     ],
                     problems: [{ line: 4, column: 1, ruleId: "foo" }],
                     reportUnusedDisableDirectives: "error"
@@ -760,6 +1217,10 @@ describe("apply-disable-directives", () => {
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
                         column: 1,
+                        fix: {
+                            range: [0, 21],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     },
@@ -768,8 +1229,11 @@ describe("apply-disable-directives", () => {
                         message: "Unused eslint-disable directive (no problems were reported from 'foo').",
                         line: 2,
                         column: 1,
+                        fix: {
+                            range: [22, 45],
+                            text: " "
+                        },
                         severity: 2,
-
                         nodeType: null
                     },
                     {
@@ -784,7 +1248,13 @@ describe("apply-disable-directives", () => {
         it("Adds a problem for // eslint-disable-line", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-line", line: 1, column: 5, ruleId: null }],
+                    directives: [{
+                        parentComment: createParentComment([0, 22]),
+                        type: "disable-line",
+                        line: 1,
+                        column: 1,
+                        ruleId: null
+                    }],
                     problems: [],
                     reportUnusedDisableDirectives: "error"
                 }),
@@ -793,7 +1263,11 @@ describe("apply-disable-directives", () => {
                         ruleId: null,
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
-                        column: 5,
+                        column: 1,
+                        fix: {
+                            range: [0, 22],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -805,7 +1279,7 @@ describe("apply-disable-directives", () => {
         it("Does not add a problem for // eslint-disable-line (problem)", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-line", line: 1, column: 5, ruleId: null }],
+                    directives: [{ type: "disable-line", line: 1, column: 1, ruleId: null }],
                     problems: [{ line: 1, column: 10, ruleId: "foo" }],
                     reportUnusedDisableDirectives: "error"
                 }),
@@ -816,7 +1290,13 @@ describe("apply-disable-directives", () => {
         it("Adds a problem for // eslint-disable-next-line", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-next-line", line: 1, column: 5, ruleId: null }],
+                    directives: [{
+                        parentComment: createParentComment([0, 27]),
+                        type: "disable-next-line",
+                        line: 1,
+                        column: 1,
+                        ruleId: null
+                    }],
                     problems: [],
                     reportUnusedDisableDirectives: "error"
                 }),
@@ -825,7 +1305,11 @@ describe("apply-disable-directives", () => {
                         ruleId: null,
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
-                        column: 5,
+                        column: 1,
+                        fix: {
+                            range: [0, 27],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -836,7 +1320,7 @@ describe("apply-disable-directives", () => {
         it("Does not add a problem for // eslint-disable-next-line \\n (problem)", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-next-line", line: 1, column: 5, ruleId: null }],
+                    directives: [{ type: "disable-next-line", line: 1, column: 1, ruleId: null }],
                     problems: [{ line: 2, column: 10, ruleId: "foo" }],
                     reportUnusedDisableDirectives: "error"
                 }),
@@ -848,8 +1332,8 @@ describe("apply-disable-directives", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
                     directives: [
-                        { type: "disable", line: 1, column: 1, ruleId: null },
-                        { type: "disable-line", line: 1, column: 5, ruleId: null }
+                        { parentComment: createParentComment([0, 20]), type: "disable", line: 1, column: 1, ruleId: null },
+                        { parentComment: createParentComment([20, 43]), type: "disable-line", line: 1, column: 22, ruleId: null }
                     ],
                     problems: [],
                     reportUnusedDisableDirectives: "error"
@@ -860,6 +1344,10 @@ describe("apply-disable-directives", () => {
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
                         column: 1,
+                        fix: {
+                            range: [0, 20],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     },
@@ -867,7 +1355,11 @@ describe("apply-disable-directives", () => {
                         ruleId: null,
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
-                        column: 5,
+                        column: 22,
+                        fix: {
+                            range: [20, 43],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -878,7 +1370,7 @@ describe("apply-disable-directives", () => {
         it("Does not add problems when reportUnusedDisableDirectives: \"off\" is used", () => {
             assert.deepStrictEqual(
                 applyDisableDirectives({
-                    directives: [{ type: "disable-next-line", line: 1, column: 5, ruleId: null }],
+                    directives: [{ parentComment: createParentComment([0, 27]), type: "disable-next-line", line: 1, column: 1, ruleId: null }],
                     problems: [],
                     reportUnusedDisableDirectives: "off"
                 }),
@@ -886,4 +1378,397 @@ describe("apply-disable-directives", () => {
             );
         });
     });
+
+    describe("unused rules within directives", () => {
+        it("Adds a problem for /* eslint-disable used, unused */", () => {
+            const parentComment = createParentComment([0, 32], " eslint-disable used, unused ", ["used", "unused"]);
+
+            assert.deepStrictEqual(
+                applyDisableDirectives({
+                    directives: [
+                        {
+                            parentComment,
+                            ruleId: "used",
+                            type: "disable",
+                            line: 1,
+                            column: 18
+                        },
+                        {
+                            parentComment,
+                            ruleId: "unused",
+                            type: "disable",
+                            line: 1,
+                            column: 22
+                        }
+                    ],
+                    problems: [{ line: 2, column: 1, ruleId: "used" }],
+                    reportUnusedDisableDirectives: "error"
+                }),
+                [
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'unused').",
+                        line: 1,
+                        column: 22,
+                        fix: {
+                            range: [22, 30],
+                            text: ""
+                        },
+                        severity: 2,
+                        nodeType: null
+                    }
+                ]
+            );
+        });
+        it("Adds a problem for /* eslint-disable used , unused , -- unused and used are ok */", () => {
+            const parentComment = createParentComment([0, 62], " eslint-disable used , unused , -- unused and used are ok ", ["used", "unused"]);
+
+            assert.deepStrictEqual(
+                applyDisableDirectives({
+                    directives: [
+                        {
+                            parentComment,
+                            ruleId: "used",
+                            type: "disable",
+                            line: 1,
+                            column: 18
+                        },
+                        {
+                            parentComment,
+                            ruleId: "unused",
+                            type: "disable",
+                            line: 1,
+                            column: 24
+                        }
+                    ],
+                    problems: [{ line: 2, column: 1, ruleId: "used" }],
+                    reportUnusedDisableDirectives: "error"
+                }),
+                [
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'unused').",
+                        line: 1,
+                        column: 24,
+                        fix: {
+                            range: [23, 32],
+                            text: ""
+                        },
+                        severity: 2,
+                        nodeType: null
+                    }
+                ]
+            );
+        });
+
+        it("Adds a problem for /* eslint-disable unused, used */", () => {
+            const parentComment = createParentComment([0, 32], " eslint-disable unused, used ", ["unused", "used"]);
+
+            assert.deepStrictEqual(
+                applyDisableDirectives({
+                    directives: [
+                        {
+                            parentComment,
+                            ruleId: "unused",
+                            type: "disable",
+                            line: 1,
+                            column: 18
+                        },
+                        {
+                            parentComment,
+                            ruleId: "used",
+                            type: "disable",
+                            line: 1,
+                            column: 25
+                        }
+                    ],
+                    problems: [{ line: 2, column: 1, ruleId: "used" }],
+                    reportUnusedDisableDirectives: "error"
+                }),
+                [
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'unused').",
+                        line: 1,
+                        column: 18,
+                        fix: {
+                            range: [18, 26],
+                            text: ""
+                        },
+                        severity: 2,
+                        nodeType: null
+                    }
+                ]
+            );
+        });
+
+        it("Adds a problem for /* eslint-disable unused,, ,, used */", () => {
+            const parentComment = createParentComment([0, 37], " eslint-disable unused,, ,, used ", ["unused", "used"]);
+
+            assert.deepStrictEqual(
+                applyDisableDirectives({
+                    directives: [
+                        {
+                            parentComment,
+                            ruleId: "unused",
+                            type: "disable",
+                            line: 1,
+                            column: 18
+                        },
+                        {
+                            parentComment,
+                            ruleId: "used",
+                            type: "disable",
+                            line: 1,
+                            column: 29
+                        }
+                    ],
+                    problems: [{ line: 2, column: 1, ruleId: "used" }],
+                    reportUnusedDisableDirectives: "error"
+                }),
+                [
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'unused').",
+                        line: 1,
+                        column: 18,
+                        fix: {
+                            range: [18, 25],
+                            text: ""
+                        },
+                        severity: 2,
+                        nodeType: null
+                    }
+                ]
+            );
+        });
+
+        it("Adds a problem for /* eslint-disable unused-1, unused-2, used */", () => {
+            const parentComment = createParentComment([0, 45], " eslint-disable unused-1, unused-2, used ", ["unused-1", "unused-2", "used"]);
+
+            assert.deepStrictEqual(
+                applyDisableDirectives({
+                    directives: [
+                        {
+                            parentComment,
+                            ruleId: "unused-1",
+                            type: "disable",
+                            line: 1,
+                            column: 18
+                        },
+                        {
+                            parentComment,
+                            ruleId: "unused-2",
+                            type: "disable",
+                            line: 1,
+                            column: 28
+                        },
+                        {
+                            parentComment,
+                            ruleId: "used",
+                            type: "disable",
+                            line: 1,
+                            column: 38
+                        }
+                    ],
+                    problems: [{ line: 2, column: 1, ruleId: "used" }],
+                    reportUnusedDisableDirectives: "error"
+                }),
+                [
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'unused-1').",
+                        line: 1,
+                        column: 18,
+                        fix: {
+                            range: [18, 28],
+                            text: ""
+                        },
+                        severity: 2,
+                        nodeType: null
+                    },
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'unused-2').",
+                        line: 1,
+                        column: 28,
+                        fix: {
+                            range: [26, 36],
+                            text: ""
+                        },
+                        severity: 2,
+                        nodeType: null
+                    }
+                ]
+            );
+        });
+
+        it("Adds a problem for /* eslint-disable unused-1, unused-2, used, unused-3 */", () => {
+            const parentComment = createParentComment([0, 55], " eslint-disable unused-1, unused-2, used, unused-3 ", ["unused-1", "unused-2", "used", "unused-3"]);
+
+            assert.deepStrictEqual(
+                applyDisableDirectives({
+                    directives: [
+                        {
+                            parentComment,
+                            ruleId: "unused-1",
+                            type: "disable",
+                            line: 1,
+                            column: 18
+                        },
+                        {
+                            parentComment,
+                            ruleId: "unused-2",
+                            type: "disable",
+                            line: 1,
+                            column: 28
+                        },
+                        {
+                            parentComment,
+                            ruleId: "used",
+                            type: "disable",
+                            line: 1,
+                            column: 38
+                        },
+                        {
+                            parentComment,
+                            ruleId: "unused-3",
+                            type: "disable",
+                            line: 1,
+                            column: 43
+                        }
+                    ],
+                    problems: [{ line: 2, column: 1, ruleId: "used" }],
+                    reportUnusedDisableDirectives: "error"
+                }),
+                [
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'unused-1').",
+                        line: 1,
+                        column: 18,
+                        fix: {
+                            range: [18, 28],
+                            text: ""
+                        },
+                        severity: 2,
+                        nodeType: null
+                    },
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'unused-2').",
+                        line: 1,
+                        column: 28,
+                        fix: {
+                            range: [26, 36],
+                            text: ""
+                        },
+                        severity: 2,
+                        nodeType: null
+                    },
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'unused-3').",
+                        line: 1,
+                        column: 43,
+                        fix: {
+                            range: [42, 52],
+                            text: ""
+                        },
+                        severity: 2,
+                        nodeType: null
+                    }
+                ]
+            );
+        });
+
+        it("Adds a problem for /* eslint-disable unused-1, unused-2 */", () => {
+            const parentComment = createParentComment([0, 39], " eslint-disable unused-1, unused-2 ", ["unused-1", "unused-2"]);
+
+            assert.deepStrictEqual(
+                applyDisableDirectives({
+                    directives: [
+                        {
+                            parentComment,
+                            ruleId: "unused-1",
+                            type: "disable",
+                            line: 1,
+                            column: 18
+                        },
+                        {
+                            parentComment,
+                            ruleId: "unused-2",
+                            type: "disable",
+                            line: 1,
+                            column: 28
+                        }
+                    ],
+                    problems: [],
+                    reportUnusedDisableDirectives: "error"
+                }),
+                [
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'unused-1' or 'unused-2').",
+                        line: 1,
+                        column: 18,
+                        fix: {
+                            range: [0, 39],
+                            text: " "
+                        },
+                        severity: 2,
+                        nodeType: null
+                    }
+                ]
+            );
+        });
+
+        it("Adds a problem for /* eslint-disable unused-1, unused-2, unused-3 */", () => {
+            const parentComment = createParentComment([0, 49], " eslint-disable unused-1, unused-2, unused-3 ", ["unused-1", "unused-2", "unused-3"]);
+
+            assert.deepStrictEqual(
+                applyDisableDirectives({
+                    directives: [
+                        {
+                            parentComment,
+                            ruleId: "unused-1",
+                            type: "disable",
+                            line: 1,
+                            column: 18
+                        },
+                        {
+                            parentComment,
+                            ruleId: "unused-2",
+                            type: "disable",
+                            line: 1,
+                            column: 28
+                        },
+                        {
+                            parentComment,
+                            ruleId: "unused-3",
+                            type: "disable",
+                            line: 1,
+                            column: 38
+                        }
+                    ],
+                    problems: [],
+                    reportUnusedDisableDirectives: "error"
+                }),
+                [
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'unused-1', 'unused-2', or 'unused-3').",
+                        line: 1,
+                        column: 18,
+                        fix: {
+                            range: [0, 49],
+                            text: " "
+                        },
+                        severity: 2,
+                        nodeType: null
+                    }
+                ]
+            );
+        });
+    });
 });
index b2356f24691b64deba14407fa26a9ccb940584ab..0b5dd33aab80c9803d7f93f88c6bdd0aefdeda13 100644 (file)
@@ -561,11 +561,11 @@ describe("CodePathAnalyzer", () => {
                     }
                 }));
                 const messages = linter.verify(source, {
-                    parserOptions: { ecmaVersion: 2021 },
+                    parserOptions: { ecmaVersion: 2022 },
                     rules: { test: 2 }
                 });
 
-                assert.strictEqual(messages.length, 0);
+                assert.strictEqual(messages.length, 0, "Unexpected linting error in code.");
                 assert.strictEqual(actual.length, expected.length, "a count of code paths is wrong.");
 
                 for (let i = 0; i < actual.length; ++i) {
index 0c4fdd18328fb031cca4725320606d858b702b4a..760d5b399898cad157a188528f6d93791b1c8032 100644 (file)
@@ -30,7 +30,11 @@ function parseCodePaths(code) {
             retv.push(codePath);
         }
     }));
-    linter.verify(code, { rules: { test: 2 } });
+
+    linter.verify(code, {
+        rules: { test: 2 },
+        parserOptions: { ecmaVersion: "latest" }
+    });
 
     return retv;
 }
@@ -50,7 +54,7 @@ function getOrderOfTraversing(codePath, options, callback) {
     codePath.traverseSegments(options, (segment, controller) => {
         retv.push(segment.id);
         if (callback) {
-            callback(segment, controller); // eslint-disable-line node/callback-return
+            callback(segment, controller); // eslint-disable-line node/callback-return -- At end of inner function
         }
     });
 
@@ -62,9 +66,58 @@ function getOrderOfTraversing(codePath, options, callback) {
 //------------------------------------------------------------------------------
 
 describe("CodePathAnalyzer", () => {
+
+    /*
+     * If you need to output the code paths and DOT graph information for a
+     * particular piece of code, udpate and uncomment the following test and
+     * then run:
+     * DEBUG=eslint:code-path npx mocha tests/lib/linter/code-path-analysis/
+     *
+     * All the information you need will be output to the console.
+     */
+    /*
+     * it.only("test", () => {
+     *     const codePaths = parseCodePaths("class Foo { a = () => b }");
+     * });
+     */
+
+    describe("CodePath#origin", () => {
+
+        it("should be 'program' when code path starts at root node", () => {
+            const codePath = parseCodePaths("foo(); bar(); baz();")[0];
+
+            assert.strictEqual(codePath.origin, "program");
+        });
+
+        it("should be 'function' when code path starts inside a function", () => {
+            const codePath = parseCodePaths("function foo() {}")[1];
+
+            assert.strictEqual(codePath.origin, "function");
+        });
+
+        it("should be 'function' when code path starts inside an arrow function", () => {
+            const codePath = parseCodePaths("let foo = () => {}")[1];
+
+            assert.strictEqual(codePath.origin, "function");
+        });
+
+        it("should be 'class-field-initializer' when code path starts inside a class field initializer", () => {
+            const codePath = parseCodePaths("class Foo { a=1; }")[1];
+
+            assert.strictEqual(codePath.origin, "class-field-initializer");
+        });
+
+        it("should be 'class-static-block' when code path starts inside a class static block", () => {
+            const codePath = parseCodePaths("class Foo { static { this.a=1; } }")[1];
+
+            assert.strictEqual(codePath.origin, "class-static-block");
+        });
+    });
+
     describe(".traverseSegments()", () => {
+
         describe("should traverse segments from the first to the end:", () => {
-            /* eslint-disable internal-rules/multiline-comment-style */
+            /* eslint-disable internal-rules/multiline-comment-style -- Commenting out */
             it("simple", () => {
                 const codePath = parseCodePaths("foo(); bar(); baz();")[0];
                 const order = getOrderOfTraversing(codePath);
@@ -301,6 +354,6 @@ describe("CodePathAnalyzer", () => {
             */
         });
 
-        /* eslint-enable internal-rules/multiline-comment-style */
+        /* eslint-enable internal-rules/multiline-comment-style -- Commenting out */
     });
 });
index a70579042be5819dfead2ae1c088deb4a545d1ba..04e7140956b3e1ffcb396bd715e0c8966efae9f7 100644 (file)
@@ -1,8 +1,16 @@
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const assert = require("chai").assert;
 const interpolate = require("../../../lib/linter/interpolate");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 describe("interpolate()", () => {
     it("passes through text without {{ }}", () => {
         const message = "This is a very important message!";
index e61804d3ae7a5b8001a1a0aa504c5da9eef16279..24dc4de43cef81239751894719a7a1a2d73e515f 100644 (file)
@@ -11,6 +11,7 @@
 
 const assert = require("chai").assert,
     sinon = require("sinon"),
+    espree = require("espree"),
     esprima = require("esprima"),
     testParsers = require("../../fixtures/parsers/linter-test-parsers");
 
@@ -53,7 +54,7 @@ const ESLINT_ENV = "eslint-env";
 describe("Linter", () => {
     const filename = "filename.js";
 
-    /** @type {InstanceType<import("../../../lib/linter/linter.js")["Linter"]>} */
+    /** @type {InstanceType<import("../../../lib/linter/linter.js").Linter>} */
     let linter;
 
     beforeEach(() => {
@@ -86,7 +87,7 @@ describe("Linter", () => {
 
             assert.throws(() => {
                 linter.verify(code, config, filename);
-            }, `Intentional error.\nOccurred while linting ${filename}:1`);
+            }, `Intentional error.\nOccurred while linting ${filename}:1\nRule: "checker"`);
         });
 
         it("does not call rule listeners with a `this` value", () => {
@@ -3202,7 +3203,7 @@ var a = "test2";
             "eslint-enable eqeqeq",
             "eslint-env es6"
         ]) {
-            // eslint-disable-next-line no-loop-func
+            // eslint-disable-next-line no-loop-func -- No closures
             it(`should warn '/* ${directive} */' if 'noInlineConfig' was given.`, () => {
                 const messages = linter.verify(`/* ${directive} */`, { noInlineConfig: true });
 
@@ -3218,7 +3219,7 @@ var a = "test2";
             "eslint-disable-line eqeqeq",
             "eslint-disable-next-line eqeqeq"
         ]) {
-            // eslint-disable-next-line no-loop-func
+            // eslint-disable-next-line no-loop-func -- No closures
             it(`should warn '// ${directive}' if 'noInlineConfig' was given.`, () => {
                 const messages = linter.verify(`// ${directive}`, { noInlineConfig: true });
 
@@ -3299,6 +3300,10 @@ var a = "test2";
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
                         column: 1,
+                        fix: {
+                            range: [0, 20],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -3315,6 +3320,10 @@ var a = "test2";
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
                         column: 1,
+                        fix: {
+                            range: [0, 20],
+                            text: " "
+                        },
                         severity: 2,
                         nodeType: null
                     }
@@ -3331,6 +3340,10 @@ var a = "test2";
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
                         column: 1,
+                        fix: {
+                            range: [0, 20],
+                            text: " "
+                        },
                         severity: 1,
                         nodeType: null
                     }
@@ -3347,12 +3360,608 @@ var a = "test2";
                         message: "Unused eslint-disable directive (no problems were reported).",
                         line: 1,
                         column: 1,
+                        fix: {
+                            range: [0, 20],
+                            text: " "
+                        },
                         severity: 1,
                         nodeType: null
                     }
                 ]
             );
         });
+
+        it("reports problems for partially unused eslint-disable comments (in config)", () => {
+            const code = "alert('test'); // eslint-disable-line no-alert, no-redeclare";
+            const config = {
+                reportUnusedDisableDirectives: true,
+                rules: {
+                    "no-alert": 1,
+                    "no-redeclare": 1
+                }
+            };
+
+            const messages = linter.verify(code, config, {
+                filename,
+                allowInlineConfig: true
+            });
+
+            assert.deepStrictEqual(
+                messages,
+                [
+                    {
+                        ruleId: null,
+                        message: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
+                        line: 1,
+                        column: 16,
+                        fix: {
+                            range: [46, 60],
+                            text: ""
+                        },
+                        severity: 1,
+                        nodeType: null
+                    }
+                ]
+            );
+        });
+
+        describe("autofix", () => {
+            const alwaysReportsRule = {
+                create(context) {
+                    return {
+                        Program(node) {
+                            context.report({ message: "bad code", loc: node.loc.end });
+                        }
+                    };
+                }
+            };
+
+            const neverReportsRule = {
+                create() {
+                    return {};
+                }
+            };
+
+            const ruleCount = 3;
+            const usedRules = Array.from(
+                { length: ruleCount },
+                (_, index) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2"
+            );
+            const unusedRules = usedRules.map(name => `un${name}`); // "unused", "unused-1", "unused-2"
+
+            const config = {
+                reportUnusedDisableDirectives: true,
+                rules: {
+                    ...Object.fromEntries(usedRules.map(name => [name, "error"])),
+                    ...Object.fromEntries(unusedRules.map(name => [name, "error"]))
+                }
+            };
+
+            beforeEach(() => {
+                linter.defineRules(Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])));
+                linter.defineRules(Object.fromEntries(unusedRules.map(name => [name, neverReportsRule])));
+            });
+
+            const tests = [
+
+                //-----------------------------------------------
+                // Removing the entire comment
+                //-----------------------------------------------
+
+                {
+                    code: "// eslint-disable-line unused",
+                    output: " "
+                },
+                {
+                    code: "foo// eslint-disable-line unused",
+                    output: "foo "
+                },
+                {
+                    code: "// eslint-disable-line ,unused,",
+                    output: " "
+                },
+                {
+                    code: "// eslint-disable-line unused-1, unused-2",
+                    output: " "
+                },
+                {
+                    code: "// eslint-disable-line ,unused-1,, unused-2,, -- comment",
+                    output: " "
+                },
+                {
+                    code: "// eslint-disable-next-line unused\n",
+                    output: " \n"
+                },
+                {
+                    code: "// eslint-disable-next-line unused\nfoo",
+                    output: " \nfoo"
+                },
+                {
+                    code: "/* eslint-disable \nunused\n*/",
+                    output: " "
+                },
+
+                //-----------------------------------------------
+                // Removing only individual rules
+                //-----------------------------------------------
+
+                // content before the first rule should not be changed
+                {
+                    code: "//eslint-disable-line unused, used",
+                    output: "//eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line unused, used",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "//  eslint-disable-line unused, used",
+                    output: "//  eslint-disable-line used"
+                },
+                {
+                    code: "/*\neslint-disable unused, used*/",
+                    output: "/*\neslint-disable used*/"
+                },
+                {
+                    code: "/*\n eslint-disable unused, used*/",
+                    output: "/*\n eslint-disable used*/"
+                },
+                {
+                    code: "/*\r\neslint-disable unused, used*/",
+                    output: "/*\r\neslint-disable used*/"
+                },
+                {
+                    code: "/*\u2028eslint-disable unused, used*/",
+                    output: "/*\u2028eslint-disable used*/"
+                },
+                {
+                    code: "/*\u00A0eslint-disable unused, used*/",
+                    output: "/*\u00A0eslint-disable used*/"
+                },
+                {
+                    code: "// eslint-disable-line  unused, used",
+                    output: "// eslint-disable-line  used"
+                },
+                {
+                    code: "/* eslint-disable\nunused, used*/",
+                    output: "/* eslint-disable\nused*/"
+                },
+                {
+                    code: "/* eslint-disable\n unused, used*/",
+                    output: "/* eslint-disable\n used*/"
+                },
+                {
+                    code: "/* eslint-disable\r\nunused, used*/",
+                    output: "/* eslint-disable\r\nused*/"
+                },
+                {
+                    code: "/* eslint-disable\u2028unused, used*/",
+                    output: "/* eslint-disable\u2028used*/"
+                },
+                {
+                    code: "/* eslint-disable\u00A0unused, used*/",
+                    output: "/* eslint-disable\u00A0used*/"
+                },
+
+                // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed
+                {
+                    code: "// eslint-disable-line unused,used",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line unused, used",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line unused , used",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line unused,  used",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line unused  ,used",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "/* eslint-disable unused\n,\nused */",
+                    output: "/* eslint-disable used */"
+                },
+                {
+                    code: "/* eslint-disable unused \n \n,\n\n used */",
+                    output: "/* eslint-disable used */"
+                },
+                {
+                    code: "/* eslint-disable unused\u2028,\u2028used */",
+                    output: "/* eslint-disable used */"
+                },
+                {
+                    code: "// eslint-disable-line unused\u00A0,\u00A0used",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line unused,,used",
+                    output: "// eslint-disable-line ,used"
+                },
+                {
+                    code: "// eslint-disable-line unused, ,used",
+                    output: "// eslint-disable-line ,used"
+                },
+                {
+                    code: "// eslint-disable-line unused,, used",
+                    output: "// eslint-disable-line , used"
+                },
+                {
+                    code: "// eslint-disable-line unused,used ",
+                    output: "// eslint-disable-line used "
+                },
+                {
+                    code: "// eslint-disable-next-line unused,used\n",
+                    output: "// eslint-disable-next-line used\n"
+                },
+
+                // when removing a rule in the middle, one comma and all whitespace between commas should also be removed
+                {
+                    code: "// eslint-disable-line used-1,unused,used-2",
+                    output: "// eslint-disable-line used-1,used-2"
+                },
+                {
+                    code: "// eslint-disable-line used-1, unused,used-2",
+                    output: "// eslint-disable-line used-1,used-2"
+                },
+                {
+                    code: "// eslint-disable-line used-1,unused ,used-2",
+                    output: "// eslint-disable-line used-1,used-2"
+                },
+                {
+                    code: "// eslint-disable-line used-1,  unused  ,used-2",
+                    output: "// eslint-disable-line used-1,used-2"
+                },
+                {
+                    code: "/* eslint-disable used-1,\nunused\n,used-2 */",
+                    output: "/* eslint-disable used-1,used-2 */"
+                },
+                {
+                    code: "/* eslint-disable used-1,\n\n unused \n \n ,used-2 */",
+                    output: "/* eslint-disable used-1,used-2 */"
+                },
+                {
+                    code: "/* eslint-disable used-1,\u2028unused\u2028,used-2 */",
+                    output: "/* eslint-disable used-1,used-2 */"
+                },
+                {
+                    code: "// eslint-disable-line used-1,\u00A0unused\u00A0,used-2",
+                    output: "// eslint-disable-line used-1,used-2"
+                },
+
+                // when removing a rule in the middle, content around commas should not be changed
+                {
+                    code: "// eslint-disable-line used-1, unused ,used-2",
+                    output: "// eslint-disable-line used-1,used-2"
+                },
+                {
+                    code: "// eslint-disable-line used-1,unused, used-2",
+                    output: "// eslint-disable-line used-1, used-2"
+                },
+                {
+                    code: "// eslint-disable-line used-1 ,unused,used-2",
+                    output: "// eslint-disable-line used-1 ,used-2"
+                },
+                {
+                    code: "// eslint-disable-line used-1 ,unused, used-2",
+                    output: "// eslint-disable-line used-1 , used-2"
+                },
+                {
+                    code: "// eslint-disable-line used-1  , unused ,  used-2",
+                    output: "// eslint-disable-line used-1  ,  used-2"
+                },
+                {
+                    code: "/* eslint-disable used-1\n,unused,\nused-2 */",
+                    output: "/* eslint-disable used-1\n,\nused-2 */"
+                },
+                {
+                    code: "/* eslint-disable used-1\u2028,unused,\u2028used-2 */",
+                    output: "/* eslint-disable used-1\u2028,\u2028used-2 */"
+                },
+                {
+                    code: "// eslint-disable-line used-1\u00A0,unused,\u00A0used-2",
+                    output: "// eslint-disable-line used-1\u00A0,\u00A0used-2"
+                },
+                {
+                    code: "// eslint-disable-line , unused ,used",
+                    output: "// eslint-disable-line ,used"
+                },
+                {
+                    code: "/* eslint-disable\n, unused ,used */",
+                    output: "/* eslint-disable\n,used */"
+                },
+                {
+                    code: "/* eslint-disable used-1,\n,unused,used-2 */",
+                    output: "/* eslint-disable used-1,\n,used-2 */"
+                },
+                {
+                    code: "/* eslint-disable used-1,unused,\n,used-2 */",
+                    output: "/* eslint-disable used-1,\n,used-2 */"
+                },
+                {
+                    code: "/* eslint-disable used-1,\n,unused,\n,used-2 */",
+                    output: "/* eslint-disable used-1,\n,\n,used-2 */"
+                },
+                {
+                    code: "// eslint-disable-line used, unused,",
+                    output: "// eslint-disable-line used,"
+                },
+                {
+                    code: "// eslint-disable-next-line used, unused,\n",
+                    output: "// eslint-disable-next-line used,\n"
+                },
+                {
+                    code: "// eslint-disable-line used, unused, ",
+                    output: "// eslint-disable-line used, "
+                },
+                {
+                    code: "// eslint-disable-line used, unused, -- comment",
+                    output: "// eslint-disable-line used, -- comment"
+                },
+                {
+                    code: "/* eslint-disable used, unused,\n*/",
+                    output: "/* eslint-disable used,\n*/"
+                },
+
+                // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed
+                {
+                    code: "// eslint-disable-line used,unused",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line used, unused",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line used ,unused",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line used , unused",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line used,  unused",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line used  ,unused",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "/* eslint-disable used\n,\nunused */",
+                    output: "/* eslint-disable used */"
+                },
+                {
+                    code: "/* eslint-disable used \n \n,\n\n unused */",
+                    output: "/* eslint-disable used */"
+                },
+                {
+                    code: "/* eslint-disable used\u2028,\u2028unused */",
+                    output: "/* eslint-disable used */"
+                },
+                {
+                    code: "// eslint-disable-line used\u00A0,\u00A0unused",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line used,,unused",
+                    output: "// eslint-disable-line used,"
+                },
+                {
+                    code: "// eslint-disable-line used, ,unused",
+                    output: "// eslint-disable-line used,"
+                },
+                {
+                    code: "/* eslint-disable used,\n,unused */",
+                    output: "/* eslint-disable used, */"
+                },
+                {
+                    code: "/* eslint-disable used\n, ,unused */",
+                    output: "/* eslint-disable used\n, */"
+                },
+
+                // content after the last rule should not be changed
+                {
+                    code: "// eslint-disable-line used,unused",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line used,unused ",
+                    output: "// eslint-disable-line used "
+                },
+                {
+                    code: "// eslint-disable-line used,unused  ",
+                    output: "// eslint-disable-line used  "
+                },
+                {
+                    code: "// eslint-disable-line used,unused -- comment",
+                    output: "// eslint-disable-line used -- comment"
+                },
+                {
+                    code: "// eslint-disable-next-line used,unused\n",
+                    output: "// eslint-disable-next-line used\n"
+                },
+                {
+                    code: "// eslint-disable-next-line used,unused \n",
+                    output: "// eslint-disable-next-line used \n"
+                },
+                {
+                    code: "/* eslint-disable used,unused\u2028*/",
+                    output: "/* eslint-disable used\u2028*/"
+                },
+                {
+                    code: "// eslint-disable-line used,unused\u00A0",
+                    output: "// eslint-disable-line used\u00A0"
+                },
+
+                // multiply rules to remove
+                {
+                    code: "// eslint-disable-line used, unused-1, unused-2",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line unused-1, used, unused-2",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line unused-1, unused-2, used",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line used-1, unused-1, used-2, unused-2",
+                    output: "// eslint-disable-line used-1, used-2"
+                },
+                {
+                    code: "// eslint-disable-line unused-1, used-1, unused-2, used-2",
+                    output: "// eslint-disable-line used-1, used-2"
+                },
+                {
+                    code: `
+                        /* eslint-disable unused-1,
+                           used-1,
+                           unused-2,
+                           used-2
+                        */
+                    `,
+                    output: `
+                        /* eslint-disable used-1,
+                           used-2
+                        */
+                    `
+                },
+                {
+                    code: `
+                        /* eslint-disable
+                               unused-1,
+                               used-1,
+                               unused-2,
+                               used-2
+                        */
+                    `,
+                    output: `
+                        /* eslint-disable
+                               used-1,
+                               used-2
+                        */
+                    `
+                },
+                {
+                    code: `
+                        /* eslint-disable
+                               used-1,
+                               unused-1,
+                               used-2,
+                               unused-2
+                        */
+                    `,
+                    output: `
+                        /* eslint-disable
+                               used-1,
+                               used-2
+                        */
+                    `
+                },
+                {
+                    code: `
+                        /* eslint-disable
+                               used-1,
+                               unused-1,
+                               used-2,
+                               unused-2,
+                        */
+                    `,
+                    output: `
+                        /* eslint-disable
+                               used-1,
+                               used-2,
+                        */
+                    `
+                },
+                {
+                    code: `
+                        /* eslint-disable
+                               ,unused-1
+                               ,used-1
+                               ,unused-2
+                               ,used-2
+                        */
+                    `,
+                    output: `
+                        /* eslint-disable
+                               ,used-1
+                               ,used-2
+                        */
+                    `
+                },
+                {
+                    code: `
+                        /* eslint-disable
+                               ,used-1
+                               ,unused-1
+                               ,used-2
+                               ,unused-2
+                        */
+                    `,
+                    output: `
+                        /* eslint-disable
+                               ,used-1
+                               ,used-2
+                        */
+                    `
+                },
+                {
+                    code: `
+                        /* eslint-disable
+                               used-1,
+                               unused-1,
+                               used-2,
+                               unused-2
+
+                               -- comment
+                        */
+                    `,
+                    output: `
+                        /* eslint-disable
+                               used-1,
+                               used-2
+
+                               -- comment
+                        */
+                    `
+                },
+
+                // duplicates in the list
+                {
+                    code: "// eslint-disable-line unused, unused, used",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line unused, used, unused",
+                    output: "// eslint-disable-line used"
+                },
+                {
+                    code: "// eslint-disable-line used, unused, unused, used",
+                    output: "// eslint-disable-line used, used"
+                }
+            ];
+
+            for (const { code, output } of tests) {
+                // eslint-disable-next-line no-loop-func -- `linter` is getting updated in beforeEach()
+                it(code, () => {
+                    assert.strictEqual(
+                        linter.verifyAndFix(code, config).output,
+                        output
+                    );
+                });
+            }
+        });
     });
 
     describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
@@ -3401,7 +4010,6 @@ var a = "test2";
                     return {};
                 });
 
-                linter.defineRule("checker", filenameChecker);
                 linter.defineRule("checker", filenameChecker);
                 linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
                 assert(filenameChecker.calledOnce);
@@ -3493,6 +4101,77 @@ var a = "test2";
         });
 
         describe("ecmaVersion", () => {
+
+            it("should not support ES6 when no ecmaVersion provided", () => {
+                const messages = linter.verify("let x = 0;");
+
+                assert.strictEqual(messages.length, 1);
+            });
+
+            it("supports ECMAScript version 'latest'", () => {
+                const messages = linter.verify("let x = 5 ** 7;", {
+                    parserOptions: { ecmaVersion: "latest" }
+                });
+
+                assert.strictEqual(messages.length, 0);
+            });
+
+            it("the 'latest' is equal to espree.lastEcmaVersion", () => {
+                let ecmaVersion = null;
+                const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } };
+
+                linter.defineRule("ecma-version", context => ({
+                    Program() {
+                        ecmaVersion = context.parserOptions.ecmaVersion;
+                    }
+                }));
+                linter.verify("", config);
+                assert.strictEqual(ecmaVersion, espree.latestEcmaVersion);
+            });
+
+            it("should pass normalized ecmaVersion to eslint-scope", () => {
+                let blockScope = null;
+
+                linter.defineRule("block-scope", context => ({
+                    BlockStatement() {
+                        blockScope = context.getScope();
+                    }
+                }));
+                linter.defineParser("custom-parser", {
+                    parse: (...args) => espree.parse(...args)
+                });
+
+                // Use standard parser
+                linter.verify("{}", {
+                    rules: { "block-scope": 2 },
+                    parserOptions: { ecmaVersion: "latest" }
+                });
+
+                assert.strictEqual(blockScope.type, "block");
+
+                linter.verify("{}", {
+                    rules: { "block-scope": 2 },
+                    parserOptions: {} // ecmaVersion defaults to 5
+                });
+                assert.strictEqual(blockScope.type, "global");
+
+                // Use custom parser
+                linter.verify("{}", {
+                    rules: { "block-scope": 2 },
+                    parser: "custom-parser",
+                    parserOptions: { ecmaVersion: "latest" }
+                });
+
+                assert.strictEqual(blockScope.type, "block");
+
+                linter.verify("{}", {
+                    rules: { "block-scope": 2 },
+                    parser: "custom-parser",
+                    parserOptions: {} // ecmaVersion defaults to 5
+                });
+                assert.strictEqual(blockScope.type, "global");
+            });
+
             describe("it should properly parse let declaration when", () => {
                 it("the ECMAScript version number is 6", () => {
                     const messages = linter.verify("let x = 5;", {
@@ -4748,21 +5427,24 @@ var a = "test2";
 
     describe("suggestions", () => {
         it("provides suggestion information for tools to use", () => {
-            linter.defineRule("rule-with-suggestions", context => ({
-                Program(node) {
-                    context.report({
-                        node,
-                        message: "Incorrect spacing",
-                        suggest: [{
-                            desc: "Insert space at the beginning",
-                            fix: fixer => fixer.insertTextBefore(node, " ")
-                        }, {
-                            desc: "Insert space at the end",
-                            fix: fixer => fixer.insertTextAfter(node, " ")
-                        }]
-                    });
-                }
-            }));
+            linter.defineRule("rule-with-suggestions", {
+                meta: { hasSuggestions: true },
+                create: context => ({
+                    Program(node) {
+                        context.report({
+                            node,
+                            message: "Incorrect spacing",
+                            suggest: [{
+                                desc: "Insert space at the beginning",
+                                fix: fixer => fixer.insertTextBefore(node, " ")
+                            }, {
+                                desc: "Insert space at the end",
+                                fix: fixer => fixer.insertTextAfter(node, " ")
+                            }]
+                        });
+                    }
+                })
+            });
 
             const messages = linter.verify("var a = 1;", { rules: { "rule-with-suggestions": "error" } });
 
@@ -4787,7 +5469,8 @@ var a = "test2";
                     messages: {
                         suggestion1: "Insert space at the beginning",
                         suggestion2: "Insert space at the end"
-                    }
+                    },
+                    hasSuggestions: true
                 },
                 create: context => ({
                     Program(node) {
@@ -4824,6 +5507,44 @@ var a = "test2";
                 }
             }]);
         });
+
+        it("should throw an error if suggestion is passed but `meta.hasSuggestions` property is not enabled", () => {
+            linter.defineRule("rule-with-suggestions", {
+                meta: { docs: {}, schema: [] },
+                create: context => ({
+                    Program(node) {
+                        context.report({
+                            node,
+                            message: "hello world",
+                            suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
+                        });
+                    }
+                })
+            });
+
+            assert.throws(() => {
+                linter.verify("0", { rules: { "rule-with-suggestions": "error" } });
+            }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`.");
+        });
+
+        it("should throw an error if suggestion is passed but `meta.hasSuggestions` property is not enabled and the rule has the obsolete `meta.docs.suggestion` property", () => {
+            linter.defineRule("rule-with-meta-docs-suggestion", {
+                meta: { docs: { suggestion: true }, schema: [] },
+                create: context => ({
+                    Program(node) {
+                        context.report({
+                            node,
+                            message: "hello world",
+                            suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
+                        });
+                    }
+                })
+            });
+
+            assert.throws(() => {
+                linter.verify("0", { rules: { "rule-with-meta-docs-suggestion": "error" } });
+            }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`. `meta.docs.suggestion` is ignored by ESLint.");
+        });
     });
 
     describe("mutability", () => {
@@ -5011,17 +5732,24 @@ var a = "test2";
             it("should use postprocessed problem ranges when applying autofixes", () => {
                 const code = "foo bar baz";
 
-                linter.defineRule("capitalize-identifiers", context => ({
-                    Identifier(node) {
-                        if (node.name !== node.name.toUpperCase()) {
-                            context.report({
-                                node,
-                                message: "Capitalize this identifier",
-                                fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
-                            });
-                        }
+                linter.defineRule("capitalize-identifiers", {
+                    meta: {
+                        fixable: "code"
+                    },
+                    create(context) {
+                        return {
+                            Identifier(node) {
+                                if (node.name !== node.name.toUpperCase()) {
+                                    context.report({
+                                        node,
+                                        message: "Capitalize this identifier",
+                                        fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
+                                    });
+                                }
+                            }
+                        };
                     }
-                }));
+                });
 
                 const fixResult = linter.verifyAndFix(
                     code,
@@ -5110,15 +5838,23 @@ var a = "test2";
         });
 
         it("stops fixing after 10 passes", () => {
-            linter.defineRule("add-spaces", context => ({
-                Program(node) {
-                    context.report({
-                        node,
-                        message: "Add a space before this node.",
-                        fix: fixer => fixer.insertTextBefore(node, " ")
-                    });
+
+            linter.defineRule("add-spaces", {
+                meta: {
+                    fixable: "whitespace"
+                },
+                create(context) {
+                    return {
+                        Program(node) {
+                            context.report({
+                                node,
+                                message: "Add a space before this node.",
+                                fix: fixer => fixer.insertTextBefore(node, " ")
+                            });
+                        }
+                    };
                 }
-            }));
+            });
 
             const fixResult = linter.verifyAndFix("a", { rules: { "add-spaces": "error" } });
 
@@ -5142,10 +5878,10 @@ var a = "test2";
 
             assert.throws(() => {
                 linter.verify("0", { rules: { "test-rule": "error" } });
-            }, /Fixable rules should export a `meta\.fixable` property.\nOccurred while linting <input>:1$/u);
+            }, /Fixable rules must set the `meta\.fixable` property to "code" or "whitespace".\nOccurred while linting <input>:1\nRule: "test-rule"$/u);
         });
 
-        it("should not throw an error if fix is passed and there is no metadata", () => {
+        it("should throw an error if fix is passed and there is no metadata", () => {
             linter.defineRule("test-rule", {
                 create: context => ({
                     Program(node) {
@@ -5154,7 +5890,21 @@ var a = "test2";
                 })
             });
 
-            linter.verify("0", { rules: { "test-rule": "error" } });
+            assert.throws(() => {
+                linter.verify("0", { rules: { "test-rule": "error" } });
+            }, /Fixable rules must set the `meta\.fixable` property/u);
+        });
+
+        it("should throw an error if fix is passed from a legacy-format rule", () => {
+            linter.defineRule("test-rule", context => ({
+                Program(node) {
+                    context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
+                }
+            }));
+
+            assert.throws(() => {
+                linter.verify("0", { rules: { "test-rule": "error" } });
+            }, /Fixable rules must set the `meta\.fixable` property/u);
         });
     });
 
@@ -5391,7 +6141,7 @@ var a = "test2";
 
             it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
                 assert.deepStrictEqual(
-                    scopeManager.__options.childVisitorKeys.ClassDeclaration, // eslint-disable-line no-underscore-dangle
+                    scopeManager.__options.childVisitorKeys.ClassDeclaration, // eslint-disable-line no-underscore-dangle -- ScopeManager API
                     ["experimentalDecorators", "id", "superClass", "body"]
                 );
             });
@@ -5470,4 +6220,25 @@ var a = "test2";
             assert.strictEqual(messages.length, 0);
         });
     });
+
+    describe("merging 'parserOptions'", () => {
+        it("should deeply merge 'parserOptions' from an environment with 'parserOptions' from the provided config", () => {
+            const code = "return <div/>";
+            const config = {
+                env: {
+                    node: true // ecmaFeatures: { globalReturn: true }
+                },
+                parserOptions: {
+                    ecmaFeatures: {
+                        jsx: true
+                    }
+                }
+            };
+
+            const messages = linter.verify(code, config);
+
+            // no parsing errors
+            assert.strictEqual(messages.length, 0);
+        });
+    });
 });
index f9010c32138c24e16148e99f5b37531b14171e0a..44f8e512d6796c78854e104ac027915b9e4018d6 100644 (file)
@@ -17,8 +17,9 @@ const assert = require("assert"),
     createEmitter = require("../../../lib/linter/safe-emitter"),
     NodeEventGenerator = require("../../../lib/linter/node-event-generator");
 
+
 //------------------------------------------------------------------------------
-// Tests
+// Constants
 //------------------------------------------------------------------------------
 
 const ESPREE_CONFIG = {
@@ -31,6 +32,10 @@ const ESPREE_CONFIG = {
 
 const STANDARD_ESQUERY_OPTION = { visitorKeys: vk.KEYS, fallback: Traverser.getKeys };
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 describe("NodeEventGenerator", () => {
     EventGeneratorTester.testEventGeneratorInterface(
         new NodeEventGenerator(createEmitter(), STANDARD_ESQUERY_OPTION)
@@ -252,6 +257,20 @@ describe("NodeEventGenerator", () => {
             ]
         );
 
+        assertEmissions(
+            "function foo(){} var x; (function (p){}); () => {};",
+            [":function", "ExpressionStatement > :function", "VariableDeclaration, :function[params.length=1]"],
+            ast => [
+                [":function", ast.body[0]], // function foo(){}
+                ["VariableDeclaration, :function[params.length=1]", ast.body[1]], // var x;
+                [":function", ast.body[2].expression], // function (p){}
+                ["ExpressionStatement > :function", ast.body[2].expression], // function (p){}
+                ["VariableDeclaration, :function[params.length=1]", ast.body[2].expression], // function (p){}
+                [":function", ast.body[3].expression], // () => {}
+                ["ExpressionStatement > :function", ast.body[3].expression] // () => {}
+            ]
+        );
+
         assertEmissions(
             "foo;",
             [
@@ -309,6 +328,15 @@ describe("NodeEventGenerator", () => {
                 ["[name.length=3]:exit", ast.body[1].expression]
             ]
         );
+
+        // https://github.com/eslint/eslint/issues/14799
+        assertEmissions(
+            "const {a = 1} = b;",
+            ["Property > .key"],
+            ast => [
+                ["Property > .key", ast.body[0].declarations[0].id.properties[0].key]
+            ]
+        );
     });
 
     describe("traversing the entire non-standard AST", () => {
index c6dd242e33007e2ac909326559461a86d16ee9d1..6feabb31b966704ea1618e21821e6e04c58cc044 100644 (file)
@@ -368,6 +368,35 @@ describe("createReportTranslator", () => {
             );
         });
 
+        it("should respect ranges of empty insertions when merging fixes to one.", () => {
+            const reportDescriptor = {
+                node,
+                loc: location,
+                message,
+                *fix() {
+                    yield { range: [4, 5], text: "cd" };
+                    yield { range: [2, 2], text: "" };
+                    yield { range: [7, 7], text: "" };
+                }
+            };
+
+            assert.deepStrictEqual(
+                translateReport(reportDescriptor),
+                {
+                    ruleId: "foo-rule",
+                    severity: 2,
+                    message: "foo",
+                    line: 2,
+                    column: 1,
+                    nodeType: "ExpressionStatement",
+                    fix: {
+                        range: [2, 7],
+                        text: "o\ncdar"
+                    }
+                }
+            );
+        });
+
         it("should pass through fixes if only one is present", () => {
             const reportDescriptor = {
                 node,
@@ -1037,7 +1066,7 @@ describe("createReportTranslator", () => {
 
             for (const badRange of [[0], [0, null], [null, 0], [void 0, 1], [0, void 0], [void 0, void 0], []]) {
                 assert.throws(
-                    // eslint-disable-next-line no-loop-func
+                    // eslint-disable-next-line no-loop-func -- Using arrow functions
                     () => translateReport(
                         { node, messageId: "testMessage", fix: () => ({ range: badRange, text: "foo" }) }
                     ),
@@ -1045,7 +1074,7 @@ describe("createReportTranslator", () => {
                 );
 
                 assert.throws(
-                    // eslint-disable-next-line no-loop-func
+                    // eslint-disable-next-line no-loop-func -- Using arrow functions
                     () => translateReport(
                         {
                             node,
index a70e735509cb65dcaada024a8f249f5d989ca40b..7cc350b6152c49d727823ff6568a81cabd6830a1 100644 (file)
@@ -30,6 +30,17 @@ describe("RuleFixer", () => {
 
         });
 
+        it("should allow inserting empty text", () => {
+
+            const result = ruleFixer.insertTextBefore({ range: [10, 20] }, "");
+
+            assert.deepStrictEqual(result, {
+                range: [10, 10],
+                text: ""
+            });
+
+        });
+
     });
 
     describe("insertTextBeforeRange", () => {
@@ -45,6 +56,17 @@ describe("RuleFixer", () => {
 
         });
 
+        it("should allow inserting empty text", () => {
+
+            const result = ruleFixer.insertTextBeforeRange([10, 20], "");
+
+            assert.deepStrictEqual(result, {
+                range: [10, 10],
+                text: ""
+            });
+
+        });
+
     });
 
     describe("insertTextAfter", () => {
@@ -60,6 +82,17 @@ describe("RuleFixer", () => {
 
         });
 
+        it("should allow inserting empty text", () => {
+
+            const result = ruleFixer.insertTextAfter({ range: [10, 20] }, "");
+
+            assert.deepStrictEqual(result, {
+                range: [20, 20],
+                text: ""
+            });
+
+        });
+
     });
 
     describe("insertTextAfterRange", () => {
@@ -75,6 +108,17 @@ describe("RuleFixer", () => {
 
         });
 
+        it("should allow inserting empty text", () => {
+
+            const result = ruleFixer.insertTextAfterRange([10, 20], "");
+
+            assert.deepStrictEqual(result, {
+                range: [20, 20],
+                text: ""
+            });
+
+        });
+
     });
 
     describe("removeAfter", () => {
index e76e1a07d8e5ca76de69d6aa98b6d72df82b6ea9..0808b4c84e82aedd89867091c7be2d72915141a3 100644 (file)
@@ -5,9 +5,17 @@
 
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const createEmitter = require("../../../lib/linter/safe-emitter");
 const assert = require("chai").assert;
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 describe("safe-emitter", () => {
     describe("emit() and on()", () => {
         it("allows listeners to be registered calls them when emitted", () => {
@@ -32,7 +40,7 @@ describe("safe-emitter", () => {
             let called = false;
 
             emitter.on("foo", function() {
-                assert.strictEqual(this, void 0); // eslint-disable-line no-invalid-this
+                assert.strictEqual(this, void 0); // eslint-disable-line no-invalid-this -- Checking `this` value
                 called = true;
             });
 
index e1052348a03ad2a826afc336308f32e5a5955765..c8c08d0cee9b5865f7f4fcc24fffd495d0cd5088 100644 (file)
@@ -1,8 +1,16 @@
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const { getListSize } = require("../../../lib/linter/timing");
 const assert = require("chai").assert;
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 describe("timing", () => {
     describe("getListSize()", () => {
         after(() => {
index d6b26d34fadb09938969461f4177e7788cfab819..8e5785cef98f5f3d9ab334505f8db270095ac4f5 100644 (file)
@@ -1,10 +1,10 @@
+/* eslint no-global-assign: off -- Resetting Mocha globals */
 /**
  * @fileoverview Tests for RuleTester without any test runner
  * @author Weijia Wang <starkwang@126.com>
  */
 "use strict";
 
-/* eslint-disable no-global-assign*/
 const assert = require("assert");
 const { RuleTester } = require("../../../lib/rule-tester");
 const tmpIt = it;
index 3aaf6f91f3f4e44c0f6ddaf18b4bd2e305ddbd84..6ebd82fb5ab283842b4ba8d2a102f4bc4197bb67 100644 (file)
@@ -11,7 +11,8 @@ const sinon = require("sinon"),
     EventEmitter = require("events"),
     { RuleTester } = require("../../../lib/rule-tester"),
     assert = require("chai").assert,
-    nodeAssert = require("assert");
+    nodeAssert = require("assert"),
+    espree = require("espree");
 
 const NODE_ASSERT_STRICT_EQUAL_OPERATOR = (() => {
     try {
@@ -97,6 +98,266 @@ describe("RuleTester", () => {
         ruleTester = new RuleTester();
     });
 
+    describe("only", () => {
+        describe("`itOnly` accessor", () => {
+            describe("when `itOnly` is set", () => {
+                before(() => {
+                    RuleTester.itOnly = sinon.spy();
+                });
+                after(() => {
+                    RuleTester.itOnly = void 0;
+                });
+                beforeEach(() => {
+                    RuleTester.itOnly.resetHistory();
+                    ruleTester = new RuleTester();
+                });
+
+                it("is called by exclusive tests", () => {
+                    ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
+                        valid: [{
+                            code: "const notVar = 42;",
+                            only: true
+                        }],
+                        invalid: []
+                    });
+
+                    sinon.assert.calledWith(RuleTester.itOnly, "const notVar = 42;");
+                });
+            });
+
+            describe("when `it` is set and has an `only()` method", () => {
+                before(() => {
+                    RuleTester.it.only = () => {};
+                    sinon.spy(RuleTester.it, "only");
+                });
+                after(() => {
+                    RuleTester.it.only = void 0;
+                });
+                beforeEach(() => {
+                    RuleTester.it.only.resetHistory();
+                    ruleTester = new RuleTester();
+                });
+
+                it("is called by tests with `only` set", () => {
+                    ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
+                        valid: [{
+                            code: "const notVar = 42;",
+                            only: true
+                        }],
+                        invalid: []
+                    });
+
+                    sinon.assert.calledWith(RuleTester.it.only, "const notVar = 42;");
+                });
+            });
+
+            describe("when global `it` is a function that has an `only()` method", () => {
+                let originalGlobalItOnly;
+
+                before(() => {
+
+                    /*
+                     * We run tests with `--forbid-only`, so we have to override
+                     * `it.only` to prevent the real one from being called.
+                     */
+                    originalGlobalItOnly = it.only;
+                    it.only = () => {};
+                    sinon.spy(it, "only");
+                });
+                after(() => {
+                    it.only = originalGlobalItOnly;
+                });
+                beforeEach(() => {
+                    it.only.resetHistory();
+                    ruleTester = new RuleTester();
+                });
+
+                it("is called by tests with `only` set", () => {
+                    ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
+                        valid: [{
+                            code: "const notVar = 42;",
+                            only: true
+                        }],
+                        invalid: []
+                    });
+
+                    sinon.assert.calledWith(it.only, "const notVar = 42;");
+                });
+            });
+
+            describe("when `describe` and `it` are overridden without `itOnly`", () => {
+                let originalGlobalItOnly;
+
+                before(() => {
+
+                    /*
+                     * These tests override `describe` and `it` already, so we
+                     * don't need to override them here. We do, however, need to
+                     * remove `only` from the global `it` to prevent it from
+                     * being used instead.
+                     */
+                    originalGlobalItOnly = it.only;
+                    it.only = void 0;
+                });
+                after(() => {
+                    it.only = originalGlobalItOnly;
+                });
+                beforeEach(() => {
+                    ruleTester = new RuleTester();
+                });
+
+                it("throws an error recommending overriding `itOnly`", () => {
+                    assert.throws(() => {
+                        ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
+                            valid: [{
+                                code: "const notVar = 42;",
+                                only: true
+                            }],
+                            invalid: []
+                        });
+                    }, "Set `RuleTester.itOnly` to use `only` with a custom test framework.");
+                });
+            });
+
+            describe("when global `it` is a function that does not have an `only()` method", () => {
+                let originalGlobalIt;
+                let originalRuleTesterDescribe;
+                let originalRuleTesterIt;
+
+                before(() => {
+                    originalGlobalIt = global.it;
+
+                    // eslint-disable-next-line no-global-assign -- Temporarily override Mocha global
+                    it = () => {};
+
+                    /*
+                     * These tests override `describe` and `it`, so we need to
+                     * un-override them here so they won't interfere.
+                     */
+                    originalRuleTesterDescribe = RuleTester.describe;
+                    RuleTester.describe = void 0;
+                    originalRuleTesterIt = RuleTester.it;
+                    RuleTester.it = void 0;
+                });
+                after(() => {
+
+                    // eslint-disable-next-line no-global-assign -- Restore Mocha global
+                    it = originalGlobalIt;
+                    RuleTester.describe = originalRuleTesterDescribe;
+                    RuleTester.it = originalRuleTesterIt;
+                });
+                beforeEach(() => {
+                    ruleTester = new RuleTester();
+                });
+
+                it("throws an error explaining that the current test framework does not support `only`", () => {
+                    assert.throws(() => {
+                        ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
+                            valid: [{
+                                code: "const notVar = 42;",
+                                only: true
+                            }],
+                            invalid: []
+                        });
+                    }, "The current test framework does not support exclusive tests with `only`.");
+                });
+            });
+        });
+
+        describe("test cases", () => {
+            const ruleName = "no-var";
+            const rule = require("../../fixtures/testers/rule-tester/no-var");
+
+            let originalRuleTesterIt;
+            let spyRuleTesterIt;
+            let originalRuleTesterItOnly;
+            let spyRuleTesterItOnly;
+
+            before(() => {
+                originalRuleTesterIt = RuleTester.it;
+                spyRuleTesterIt = sinon.spy();
+                RuleTester.it = spyRuleTesterIt;
+                originalRuleTesterItOnly = RuleTester.itOnly;
+                spyRuleTesterItOnly = sinon.spy();
+                RuleTester.itOnly = spyRuleTesterItOnly;
+            });
+            after(() => {
+                RuleTester.it = originalRuleTesterIt;
+                RuleTester.itOnly = originalRuleTesterItOnly;
+            });
+            beforeEach(() => {
+                spyRuleTesterIt.resetHistory();
+                spyRuleTesterItOnly.resetHistory();
+                ruleTester = new RuleTester();
+            });
+
+            it("isn't called for normal tests", () => {
+                ruleTester.run(ruleName, rule, {
+                    valid: ["const notVar = 42;"],
+                    invalid: []
+                });
+                sinon.assert.calledWith(spyRuleTesterIt, "const notVar = 42;");
+                sinon.assert.notCalled(spyRuleTesterItOnly);
+            });
+
+            it("calls it or itOnly for every test case", () => {
+
+                /*
+                 * `RuleTester` doesn't implement test case exclusivity itself.
+                 * Setting `only: true` just causes `RuleTester` to call
+                 * whatever `only()` function is provided by the test framework
+                 * instead of the regular `it()` function.
+                 */
+
+                ruleTester.run(ruleName, rule, {
+                    valid: [
+                        "const valid = 42;",
+                        {
+                            code: "const onlyValid = 42;",
+                            only: true
+                        }
+                    ],
+                    invalid: [
+                        {
+                            code: "var invalid = 42;",
+                            errors: [/^Bad var/u]
+                        },
+                        {
+                            code: "var onlyInvalid = 42;",
+                            errors: [/^Bad var/u],
+                            only: true
+                        }
+                    ]
+                });
+
+                sinon.assert.calledWith(spyRuleTesterIt, "const valid = 42;");
+                sinon.assert.calledWith(spyRuleTesterItOnly, "const onlyValid = 42;");
+                sinon.assert.calledWith(spyRuleTesterIt, "var invalid = 42;");
+                sinon.assert.calledWith(spyRuleTesterItOnly, "var onlyInvalid = 42;");
+            });
+        });
+
+        describe("static helper wrapper", () => {
+            it("adds `only` to string test cases", () => {
+                const test = RuleTester.only("const valid = 42;");
+
+                assert.deepStrictEqual(test, {
+                    code: "const valid = 42;",
+                    only: true
+                });
+            });
+
+            it("adds `only` to object test cases", () => {
+                const test = RuleTester.only({ code: "const valid = 42;" });
+
+                assert.deepStrictEqual(test, {
+                    code: "const valid = 42;",
+                    only: true
+                });
+            });
+        });
+    });
+
     it("should not throw an error when everything passes", () => {
         ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
             valid: [
@@ -781,6 +1042,203 @@ describe("RuleTester", () => {
         });
         assert.strictEqual(spy.args[1][1].parser, require.resolve("esprima"));
     });
+    it("should pass normalized ecmaVersion to the rule", () => {
+        const reportEcmaVersionRule = {
+            meta: {
+                messages: {
+                    ecmaVersionMessage: "context.parserOptions.ecmaVersion is {{type}} {{ecmaVersion}}."
+                }
+            },
+            create: context => ({
+                Program(node) {
+                    const { ecmaVersion } = context.parserOptions;
+
+                    context.report({
+                        node,
+                        messageId: "ecmaVersionMessage",
+                        data: { type: typeof ecmaVersion, ecmaVersion }
+                    });
+                }
+            })
+        };
+
+        const notEspree = require.resolve("../../fixtures/parsers/empty-program-parser");
+
+        ruleTester.run("report-ecma-version", reportEcmaVersionRule, {
+            valid: [],
+            invalid: [
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "undefined", ecmaVersion: "undefined" } }]
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "undefined", ecmaVersion: "undefined" } }],
+                    parserOptions: {}
+                },
+                {
+                    code: "<div/>",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "undefined", ecmaVersion: "undefined" } }],
+                    parserOptions: { ecmaFeatures: { jsx: true } }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "undefined", ecmaVersion: "undefined" } }],
+                    parser: require.resolve("espree")
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "6" } }],
+                    parserOptions: { ecmaVersion: 6 }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "6" } }],
+                    parserOptions: { ecmaVersion: 2015 }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "undefined", ecmaVersion: "undefined" } }],
+                    env: { browser: true }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "undefined", ecmaVersion: "undefined" } }],
+                    env: { es6: false }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "6" } }],
+                    env: { es6: true }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "8" } }],
+                    env: { es6: false, es2017: true }
+                },
+                {
+                    code: "let x",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "6" } }],
+                    env: { es6: "truthy" }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "8" } }],
+                    env: { es2017: true }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "11" } }],
+                    env: { es2020: true }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "12" } }],
+                    env: { es2021: true }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: String(espree.latestEcmaVersion) } }],
+                    parserOptions: { ecmaVersion: "latest" }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: String(espree.latestEcmaVersion) } }],
+                    parser: require.resolve("espree"),
+                    parserOptions: { ecmaVersion: "latest" }
+                },
+                {
+                    code: "<div/>",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: String(espree.latestEcmaVersion) } }],
+                    parserOptions: { ecmaVersion: "latest", ecmaFeatures: { jsx: true } }
+                },
+                {
+                    code: "import 'foo'",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: String(espree.latestEcmaVersion) } }],
+                    parserOptions: { ecmaVersion: "latest", sourceType: "module" }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: String(espree.latestEcmaVersion) } }],
+                    parserOptions: { ecmaVersion: "latest" },
+                    env: { es6: true }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: String(espree.latestEcmaVersion) } }],
+                    parserOptions: { ecmaVersion: "latest" },
+                    env: { es2020: true }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "undefined", ecmaVersion: "undefined" } }],
+                    parser: notEspree
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "undefined", ecmaVersion: "undefined" } }],
+                    parser: notEspree,
+                    parserOptions: {}
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "5" } }],
+                    parser: notEspree,
+                    parserOptions: { ecmaVersion: 5 }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "6" } }],
+                    parser: notEspree,
+                    parserOptions: { ecmaVersion: 6 }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "6" } }],
+                    parser: notEspree,
+                    parserOptions: { ecmaVersion: 2015 }
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "string", ecmaVersion: "latest" } }],
+                    parser: notEspree,
+                    parserOptions: { ecmaVersion: "latest" }
+                }
+            ]
+        });
+
+        [{ parserOptions: { ecmaVersion: 6 } }, { env: { es6: true } }].forEach(options => {
+            new RuleTester(options).run("report-ecma-version", reportEcmaVersionRule, {
+                valid: [],
+                invalid: [
+                    {
+                        code: "",
+                        errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "6" } }]
+                    },
+                    {
+                        code: "",
+                        errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "6" } }],
+                        parserOptions: {}
+                    }
+                ]
+            });
+        });
+
+        new RuleTester({ parser: notEspree }).run("report-ecma-version", reportEcmaVersionRule, {
+            valid: [],
+            invalid: [
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "undefined", ecmaVersion: "undefined" } }]
+                },
+                {
+                    code: "",
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "string", ecmaVersion: "latest" } }],
+                    parserOptions: { ecmaVersion: "latest" }
+                }
+            ]
+        });
+    });
 
     it("should pass-through services from parseForESLint to the rule", () => {
         const enhancedParserPath = require.resolve("../../fixtures/parsers/enhanced-parser");
@@ -1289,7 +1747,7 @@ describe("RuleTester", () => {
                     { code: "var foo = bar;", output: "5", errors: 1 }
                 ]
             });
-        }, "Fixable rules should export a `meta.fixable` property.");
+        }, /Fixable rules must set the `meta\.fixable` property/u);
     });
     it("should throw an error if a legacy-format rule produces fixes", () => {
 
@@ -1313,7 +1771,7 @@ describe("RuleTester", () => {
                     { code: "var foo = bar;", output: "5", errors: 1 }
                 ]
             });
-        }, "Fixable rules should export a `meta.fixable` property.");
+        }, /Fixable rules must set the `meta\.fixable` property/u);
     });
 
     describe("suggestions", () => {
@@ -1821,6 +2279,17 @@ describe("RuleTester", () => {
                 });
             }, /Invalid suggestion property name 'outpt'/u);
         });
+
+        it("should throw an error if a rule that doesn't have `meta.hasSuggestions` enabled produces suggestions", () => {
+            assert.throws(() => {
+                ruleTester.run("suggestions-missing-hasSuggestions-property", require("../../fixtures/testers/rule-tester/suggestions").withoutHasSuggestionsProperty, {
+                    valid: [],
+                    invalid: [
+                        { code: "var foo = bar;", output: "5", errors: 1 }
+                    ]
+                });
+            }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`.");
+        });
     });
 
     describe("naming test cases", () => {
@@ -1829,8 +2298,8 @@ describe("RuleTester", () => {
          * Asserts that a particular value will be emitted from an EventEmitter.
          * @param {EventEmitter} emitter The emitter that should emit a value
          * @param {string} emitType The type of emission to listen for
-         * @param {*} expectedValue The value that should be emitted
-         * @returns {Promise} A Promise that fulfills if the value is emitted, and rejects if something else is emitted.
+         * @param {any} expectedValue The value that should be emitted
+         * @returns {Promise<void>} A Promise that fulfills if the value is emitted, and rejects if something else is emitted.
          * The Promise will be indefinitely pending if no value is emitted.
          */
         function assertEmitted(emitter, emitType, expectedValue) {
@@ -1916,6 +2385,59 @@ describe("RuleTester", () => {
 
             return assertion;
         });
+
+        it('should use the "name" property if set to a non-empty string', () => {
+            const assertion = assertEmitted(ruleTesterTestEmitter, "it", "my test");
+
+            ruleTester.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
+                valid: [],
+                invalid: [
+                    {
+                        name: "my test",
+                        code: "var x = invalid(code);",
+                        output: " x = invalid(code);",
+                        errors: 1
+                    }
+                ]
+            });
+
+            return assertion;
+        });
+
+        it('should use the "name" property if set to a non-empty string for valid cases too', () => {
+            const assertion = assertEmitted(ruleTesterTestEmitter, "it", "my test");
+
+            ruleTester.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
+                valid: [
+                    {
+                        name: "my test",
+                        code: "valid(code);"
+                    }
+                ],
+                invalid: []
+            });
+
+            return assertion;
+        });
+
+
+        it('should use the test code as the name if the "name" property is set to an empty string', () => {
+            const assertion = assertEmitted(ruleTesterTestEmitter, "it", "var x = invalid(code);");
+
+            ruleTester.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
+                valid: [],
+                invalid: [
+                    {
+                        name: "",
+                        code: "var x = invalid(code);",
+                        output: " x = invalid(code);",
+                        errors: 1
+                    }
+                ]
+            });
+
+            return assertion;
+        });
     });
 
     // https://github.com/eslint/eslint/issues/11615
@@ -1923,17 +2445,24 @@ describe("RuleTester", () => {
         assert.throw(() => {
             ruleTester.run(
                 "foo",
-                context => ({
-                    Identifier(node) {
-                        context.report({
-                            node,
-                            message: "make a syntax error",
-                            fix(fixer) {
-                                return fixer.replaceText(node, "one two");
+                {
+                    meta: {
+                        fixable: "code"
+                    },
+                    create(context) {
+                        return {
+                            Identifier(node) {
+                                context.report({
+                                    node,
+                                    message: "make a syntax error",
+                                    fix(fixer) {
+                                        return fixer.replaceText(node, "one two");
+                                    }
+                                });
                             }
-                        });
+                        };
                     }
-                }),
+                },
                 {
                     valid: ["one()"],
                     invalid: []
@@ -2004,4 +2533,37 @@ describe("RuleTester", () => {
 
     });
 
+    describe("SourceCode#getComments()", () => {
+        const useGetCommentsRule = {
+            create: context => ({
+                Program(node) {
+                    const sourceCode = context.getSourceCode();
+
+                    sourceCode.getComments(node);
+                }
+            })
+        };
+
+        it("should throw if called from a valid test case", () => {
+            assert.throws(() => {
+                ruleTester.run("use-get-comments", useGetCommentsRule, {
+                    valid: [""],
+                    invalid: []
+                });
+            }, /`SourceCode#getComments\(\)` is deprecated/u);
+        });
+
+        it("should throw if called from an invalid test case", () => {
+            assert.throws(() => {
+                ruleTester.run("use-get-comments", useGetCommentsRule, {
+                    valid: [],
+                    invalid: [{
+                        code: "",
+                        errors: [{}]
+                    }]
+                });
+            }, /`SourceCode#getComments\(\)` is deprecated/u);
+        });
+    });
+
 });
index c6341326647460ffc00cf165763c8f12d48522a3..d74913f4e74d13bdd4f0f9ad5f6fb385ff1c54b5 100644 (file)
@@ -335,6 +335,11 @@ ruleTester.run("accessor-pairs", rule, {
             options: [{ enforceForClassMembers: true }],
             parserOptions: { ecmaVersion: 6 }
         },
+        {
+            code: "class A { get #a() {} }",
+            options: [{ enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 13 }
+        },
 
         // Explicitly disabled option
         {
@@ -1207,6 +1212,26 @@ ruleTester.run("accessor-pairs", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: [{ message: "Getter is not present for class static setter 'a'.", type: "MethodDefinition" }]
         },
+        {
+            code: "class A { set '#a'(foo) {} }",
+            parserOptions: { ecmaVersion: 13 },
+            errors: [{ message: "Getter is not present for class setter '#a'.", type: "MethodDefinition" }]
+        },
+        {
+            code: "class A { set #a(foo) {} }",
+            parserOptions: { ecmaVersion: 13 },
+            errors: [{ message: "Getter is not present for class private setter #a.", type: "MethodDefinition" }]
+        },
+        {
+            code: "class A { static set '#a'(foo) {} }",
+            parserOptions: { ecmaVersion: 13 },
+            errors: [{ message: "Getter is not present for class static setter '#a'.", type: "MethodDefinition" }]
+        },
+        {
+            code: "class A { static set #a(foo) {} }",
+            parserOptions: { ecmaVersion: 13 },
+            errors: [{ message: "Getter is not present for class static private setter #a.", type: "MethodDefinition" }]
+        },
 
         // Test that the accessor kind options do not affect each other
         {
@@ -1239,6 +1264,30 @@ ruleTester.run("accessor-pairs", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: [{ message: "Setter is not present for class getter 'a'.", type: "MethodDefinition" }]
         },
+        {
+            code: "class A { get '#a'() {} };",
+            options: [{ setWithoutGet: false, getWithoutSet: true, enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 13 },
+            errors: [{ message: "Setter is not present for class getter '#a'.", type: "MethodDefinition" }]
+        },
+        {
+            code: "class A { get #a() {} };",
+            options: [{ setWithoutGet: false, getWithoutSet: true, enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 13 },
+            errors: [{ message: "Setter is not present for class private getter #a.", type: "MethodDefinition" }]
+        },
+        {
+            code: "class A { static get '#a'() {} };",
+            options: [{ setWithoutGet: false, getWithoutSet: true, enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 13 },
+            errors: [{ message: "Setter is not present for class static getter '#a'.", type: "MethodDefinition" }]
+        },
+        {
+            code: "class A { static get #a() {} };",
+            options: [{ setWithoutGet: false, getWithoutSet: true, enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 13 },
+            errors: [{ message: "Setter is not present for class static private getter #a.", type: "MethodDefinition" }]
+        },
 
         // Various kinds of keys
         {
@@ -1424,6 +1473,24 @@ ruleTester.run("accessor-pairs", rule, {
                 { message: "Getter is not present for class setter.", type: "MethodDefinition", column: 28 }
             ]
         },
+        {
+            code: "class A { get #a() {} set '#a'(foo) {} }",
+            options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 13 },
+            errors: [
+                { message: "Setter is not present for class private getter #a.", type: "MethodDefinition", column: 11 },
+                { message: "Getter is not present for class setter '#a'.", type: "MethodDefinition", column: 23 }
+            ]
+        },
+        {
+            code: "class A { get '#a'() {} set #a(foo) {} }",
+            options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 13 },
+            errors: [
+                { message: "Setter is not present for class getter '#a'.", type: "MethodDefinition", column: 11 },
+                { message: "Getter is not present for class private setter #a.", type: "MethodDefinition", column: 25 }
+            ]
+        },
 
         // Prototype and static accessors with same keys
         {
index 6d6a4d86242c9b6c3b96e9fb9268a0d5a8bccd2d..13a01125c99df1e4cd412606d7f909390684dfcf 100644 (file)
 const rule = require("../../../lib/rules/array-callback-return"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 const allowImplicitOptions = [{ allowImplicit: true }];
index 61d7635844085e5174d4b1ccff4d8aa1f06b986f..d2f32a784a3a7a9b4a7aa722dd9b62bfd1892879 100644 (file)
@@ -18,6 +18,7 @@ const parser = baseParser.bind(null, "arrow-parens");
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
 
 const valid = [
index 596c3ba1cf97791473c38f6e531c78ae8f3e08c5..f401332b116dd1994e0859014c5a162d35c1b0d1 100644 (file)
@@ -110,7 +110,15 @@ ruleTester.run("block-scoped-var", rule, {
 
         // https://github.com/eslint/eslint/issues/2967
         "(function () { foo(); })(); function foo() {}",
-        { code: "(function () { foo(); })(); function foo() {}", parserOptions: { ecmaVersion: 6, sourceType: "module" } }
+        { code: "(function () { foo(); })(); function foo() {}", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
+
+        { code: "class C { static { var foo; foo; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo; var foo; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { if (bar) { foo; } var foo; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "var foo; class C { static { foo; } } ", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo; } } var foo;", parserOptions: { ecmaVersion: 2022 } },
+        { code: "var foo; class C { static {} [foo]; } ", parserOptions: { ecmaVersion: 2022 } },
+        { code: "foo; class C { static {} } var foo; ", parserOptions: { ecmaVersion: 2022 } }
     ],
     invalid: [
         { code: "function f(){ x; { var x; } }", errors: [{ messageId: "outOfScope", data: { name: "x" }, type: "Identifier" }] },
@@ -165,6 +173,11 @@ ruleTester.run("block-scoped-var", rule, {
                 { messageId: "outOfScope", data: { name: "i" }, type: "Identifier" },
                 { messageId: "outOfScope", data: { name: "i" }, type: "Identifier" }
             ]
+        },
+        {
+            code: "class C { static { if (bar) { var foo; } foo; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "outOfScope", data: { name: "foo" }, type: "Identifier" }]
         }
     ]
 });
index a80d07cc7d4a8a93ae58fdee6f2e24c068d73036..0dfef7ed652205fecbec86d56135ddd1427d2c98 100644 (file)
@@ -42,6 +42,9 @@ ruleTester.run("block-spacing", rule, {
         { code: "(() => { bar(); });", parserOptions: { ecmaVersion: 6 } },
         "if (a) { /* comment */ foo(); /* comment */ }",
         "if (a) { //comment\n foo(); }",
+        { code: "class C { static {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { /* comment */foo;/* comment */ } }", parserOptions: { ecmaVersion: 2022 } },
 
         // never
         { code: "{foo();}", options: ["never"] },
@@ -62,7 +65,13 @@ ruleTester.run("block-spacing", rule, {
         { code: "(function() {bar();});", options: ["never"] },
         { code: "(() => {bar();});", options: ["never"], parserOptions: { ecmaVersion: 6 } },
         { code: "if (a) {/* comment */ foo(); /* comment */}", options: ["never"] },
-        { code: "if (a) { //comment\n foo();}", options: ["never"] }
+        { code: "if (a) { //comment\n foo();}", options: ["never"] },
+        { code: "class C { static { } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static {foo;} }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static {/* comment */ foo; /* comment */} }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { // line comment is allowed\n foo;\n} }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static {\nfoo;\n} }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { \n foo; \n } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } }
     ],
 
     invalid: [
@@ -292,6 +301,170 @@ ruleTester.run("block-spacing", rule, {
             ]
         },
 
+        // class static blocks
+        {
+            code: "class C { static {foo; } }",
+            output: "class C { static { foo; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "missing",
+                    data: {
+                        location: "after",
+                        token: "{"
+                    },
+                    line: 1,
+                    column: 18,
+                    endLine: 1,
+                    endColumn: 19
+                }
+            ]
+        },
+        {
+            code: "class C { static { foo;} }",
+            output: "class C { static { foo; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "missing",
+                    data: {
+                        location: "before",
+                        token: "}"
+                    },
+                    line: 1,
+                    column: 24,
+                    endLine: 1,
+                    endColumn: 25
+                }
+            ]
+        },
+        {
+            code: "class C { static {foo;} }",
+            output: "class C { static { foo; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "missing",
+                    data: {
+                        location: "after",
+                        token: "{"
+                    },
+                    line: 1,
+                    column: 18,
+                    endLine: 1,
+                    endColumn: 19
+                },
+                {
+                    type: "StaticBlock",
+                    messageId: "missing",
+                    data: {
+                        location: "before",
+                        token: "}"
+                    },
+                    line: 1,
+                    column: 23,
+                    endLine: 1,
+                    endColumn: 24
+                }
+            ]
+        },
+        {
+            code: "class C { static {/* comment */} }",
+            output: "class C { static { /* comment */ } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "missing",
+                    data: {
+                        location: "after",
+                        token: "{"
+                    },
+                    line: 1,
+                    column: 18,
+                    endLine: 1,
+                    endColumn: 19
+                },
+                {
+                    type: "StaticBlock",
+                    messageId: "missing",
+                    data: {
+                        location: "before",
+                        token: "}"
+                    },
+                    line: 1,
+                    column: 32,
+                    endLine: 1,
+                    endColumn: 33
+                }
+            ]
+        },
+        {
+            code: "class C { static {/* comment 1 */ foo; /* comment 2 */} }",
+            output: "class C { static { /* comment 1 */ foo; /* comment 2 */ } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "missing",
+                    data: {
+                        location: "after",
+                        token: "{"
+                    },
+                    line: 1,
+                    column: 18,
+                    endLine: 1,
+                    endColumn: 19
+                },
+                {
+                    type: "StaticBlock",
+                    messageId: "missing",
+                    data: {
+                        location: "before",
+                        token: "}"
+                    },
+                    line: 1,
+                    column: 55,
+                    endLine: 1,
+                    endColumn: 56
+                }
+            ]
+        },
+        {
+            code: "class C {\n static {foo()\nbar()} }",
+            output: "class C {\n static { foo()\nbar() } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "missing",
+                    data: {
+                        location: "after",
+                        token: "{"
+                    },
+                    line: 2,
+                    column: 9,
+                    endLine: 2,
+                    endColumn: 10
+                },
+                {
+                    type: "StaticBlock",
+                    messageId: "missing",
+                    data: {
+                        location: "before",
+                        token: "}"
+                    },
+                    line: 3,
+                    column: 6,
+                    endLine: 3,
+                    endColumn: 7
+                }
+            ]
+        },
+
         //----------------------------------------------------------------------
         // never
         {
@@ -817,6 +990,176 @@ ruleTester.run("block-spacing", rule, {
                     endColumn: 21
                 }
             ]
+        },
+
+        // class static blocks
+        {
+            code: "class C { static { foo;} }",
+            output: "class C { static {foo;} }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "extra",
+                    data: {
+                        location: "after",
+                        token: "{"
+                    },
+                    line: 1,
+                    column: 19,
+                    endLine: 1,
+                    endColumn: 20
+                }
+            ]
+        },
+        {
+            code: "class C { static {foo; } }",
+            output: "class C { static {foo;} }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "extra",
+                    data: {
+                        location: "before",
+                        token: "}"
+                    },
+                    line: 1,
+                    column: 23,
+                    endLine: 1,
+                    endColumn: 24
+                }
+            ]
+        },
+        {
+            code: "class C { static { foo; } }",
+            output: "class C { static {foo;} }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "extra",
+                    data: {
+                        location: "after",
+                        token: "{"
+                    },
+                    line: 1,
+                    column: 19,
+                    endLine: 1,
+                    endColumn: 20
+                },
+                {
+                    type: "StaticBlock",
+                    messageId: "extra",
+                    data: {
+                        location: "before",
+                        token: "}"
+                    },
+                    line: 1,
+                    column: 24,
+                    endLine: 1,
+                    endColumn: 25
+                }
+            ]
+        },
+        {
+            code: "class C { static { /* comment */ } }",
+            output: "class C { static {/* comment */} }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "extra",
+                    data: {
+                        location: "after",
+                        token: "{"
+                    },
+                    line: 1,
+                    column: 19,
+                    endLine: 1,
+                    endColumn: 20
+                },
+                {
+                    type: "StaticBlock",
+                    messageId: "extra",
+                    data: {
+                        location: "before",
+                        token: "}"
+                    },
+                    line: 1,
+                    column: 33,
+                    endLine: 1,
+                    endColumn: 34
+                }
+            ]
+        },
+        {
+            code: "class C { static { /* comment 1 */ foo; /* comment 2 */ } }",
+            output: "class C { static {/* comment 1 */ foo; /* comment 2 */} }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "extra",
+                    data: {
+                        location: "after",
+                        token: "{"
+                    },
+                    line: 1,
+                    column: 19,
+                    endLine: 1,
+                    endColumn: 20
+                },
+                {
+                    type: "StaticBlock",
+                    messageId: "extra",
+                    data: {
+                        location: "before",
+                        token: "}"
+                    },
+                    line: 1,
+                    column: 56,
+                    endLine: 1,
+                    endColumn: 57
+                }
+            ]
+        },
+        {
+            code: "class C { static\n{   foo()\nbar()  } }",
+            output: "class C { static\n{foo()\nbar()} }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    type: "StaticBlock",
+                    messageId: "extra",
+                    data: {
+                        location: "after",
+                        token: "{"
+                    },
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 5
+                },
+                {
+                    type: "StaticBlock",
+                    messageId: "extra",
+                    data: {
+                        location: "before",
+                        token: "}"
+                    },
+                    line: 3,
+                    column: 6,
+                    endLine: 3,
+                    endColumn: 8
+                }
+            ]
         }
     ]
 });
index 70211608bb8a0cfb4cd1f87c58b67c57414dabb1..9629f42fc58dd2ae738e396a68c6c88b9cc52118 100644 (file)
@@ -10,7 +10,8 @@
 //------------------------------------------------------------------------------
 
 const rule = require("../../../lib/rules/brace-style"),
-    { RuleTester } = require("../../../lib/rule-tester");
+    { RuleTester } = require("../../../lib/rule-tester"),
+    { unIndent } = require("../../_utils");
 
 //------------------------------------------------------------------------------
 // Tests
@@ -204,7 +205,125 @@ ruleTester.run("brace-style", rule, {
         `
           if (foo) bar = function() {}
           else baz()
-        `
+        `,
+
+        // class static blocks
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        foo;
+                    }
+                }
+            `,
+            options: ["1tbs"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {}
+
+                    static {
+                    }
+                }
+            `,
+            options: ["1tbs"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static { foo; }
+                }
+            `,
+            options: ["1tbs", { allowSingleLine: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        foo;
+                    }
+                }
+            `,
+            options: ["stroustrup"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {}
+
+                    static {
+                    }
+                }
+            `,
+            options: ["stroustrup"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static { foo; }
+                }
+            `,
+            options: ["stroustrup", { allowSingleLine: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C
+                {
+                    static
+                    {
+                        foo;
+                    }
+                }
+            `,
+            options: ["allman"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C
+                {
+                    static
+                    {}
+                }
+            `,
+            options: ["allman"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C
+                {
+                    static {}
+
+                    static { foo; }
+
+                    static
+                    { foo; }
+                }
+            `,
+            options: ["allman", { allowSingleLine: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        {
+                            foo;
+                        }
+                    }
+                }
+            `,
+            options: ["1tbs"],
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
 
     invalid: [
@@ -664,6 +783,334 @@ ruleTester.run("brace-style", rule, {
                 { messageId: "nextLineOpen", type: "Punctuator" },
                 { messageId: "nextLineClose", type: "Punctuator" }
             ]
+        },
+
+        /*
+         * class static blocks
+         *
+         * Note about the autofix: this rule only inserts linebreaks and removes linebreaks.
+         * It does not aim to produce code with a valid indentation. Indentation and other formatting issues
+         * are expected to be fixed by `indent` and other rules in subsequent iterations.
+         */
+        {
+            code: unIndent`
+                class C {
+                    static
+                    {
+                        foo;
+                    }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                        foo;
+                    }
+                }
+            `,
+            options: ["1tbs"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "nextLineOpen", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {foo;
+                    }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                foo;
+                    }
+                }
+            `,
+            options: ["1tbs"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "blockSameLine", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        foo;}
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                        foo;
+                }
+                }
+            `,
+            options: ["1tbs"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "singleLineClose", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                    {foo;}
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                foo;
+                }
+                }
+            `,
+            options: ["1tbs"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "nextLineOpen", type: "Punctuator" },
+                { messageId: "blockSameLine", type: "Punctuator" },
+                { messageId: "singleLineClose", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                    {}
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {}
+                }
+            `,
+            options: ["1tbs"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "nextLineOpen", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                    {
+                        foo;
+                    }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                        foo;
+                    }
+                }
+            `,
+            options: ["stroustrup"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "nextLineOpen", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {foo;
+                    }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                foo;
+                    }
+                }
+            `,
+            options: ["stroustrup"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "blockSameLine", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        foo;}
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                        foo;
+                }
+                }
+            `,
+            options: ["stroustrup"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "singleLineClose", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                    {foo;}
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                foo;
+                }
+                }
+            `,
+            options: ["stroustrup"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "nextLineOpen", type: "Punctuator" },
+                { messageId: "blockSameLine", type: "Punctuator" },
+                { messageId: "singleLineClose", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                    {}
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {}
+                }
+            `,
+            options: ["stroustrup"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "nextLineOpen", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C
+                {
+                    static{
+                        foo;
+                    }
+                }
+            `,
+            output: unIndent`
+                class C
+                {
+                    static
+                {
+                        foo;
+                    }
+                }
+            `,
+            options: ["allman"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "sameLineOpen", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C
+                {
+                    static
+                    {foo;
+                    }
+                }
+            `,
+            output: unIndent`
+                class C
+                {
+                    static
+                    {
+                foo;
+                    }
+                }
+            `,
+            options: ["allman"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "blockSameLine", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C
+                {
+                    static
+                    {
+                        foo;}
+                }
+            `,
+            output: unIndent`
+                class C
+                {
+                    static
+                    {
+                        foo;
+                }
+                }
+            `,
+            options: ["allman"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "singleLineClose", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C
+                {
+                    static{foo;}
+                }
+            `,
+            output: unIndent`
+                class C
+                {
+                    static
+                {
+                foo;
+                }
+                }
+            `,
+            options: ["allman"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "sameLineOpen", type: "Punctuator" },
+                { messageId: "blockSameLine", type: "Punctuator" },
+                { messageId: "singleLineClose", type: "Punctuator" }
+            ]
+        },
+        {
+            code: unIndent`
+                class C
+                {
+                    static{}
+                }
+            `,
+            output: unIndent`
+                class C
+                {
+                    static
+                {}
+                }
+            `,
+            options: ["allman"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "sameLineOpen", type: "Punctuator" }
+            ]
         }
     ]
 });
index 4f9cdca78fe44399022362c923a8cce78167f46c..514eb85d2ef6901ad1ae166d3023c4a0f03bb812 100644 (file)
@@ -182,90 +182,90 @@ ruleTester.run("camelcase", rule, {
         {
             code: "var camelCased = a_global_variable",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "a_global_variable.foo()",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "a_global_variable[undefined]",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "var foo = a_global_variable.bar",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "a_global_variable.foo = bar",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "( { foo: a_global_variable.bar } = baz )",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "a_global_variable = foo",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "a_global_variable = foo",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "({ a_global_variable } = foo)",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "({ snake_cased: a_global_variable } = foo)",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "({ snake_cased: a_global_variable = foo } = bar)",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "[a_global_variable] = bar",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "[a_global_variable = foo] = bar",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "foo[a_global_variable] = bar",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "var foo = { [a_global_variable]: bar }",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "var { [a_global_variable]: foo } = bar",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase
+            globals: { a_global_variable: "readonly" } // eslint-disable-line camelcase -- Testing non-CamelCase
         },
         {
             code: "function foo({ no_camelcased: camelCased }) {};",
@@ -376,6 +376,16 @@ ruleTester.run("camelcase", rule, {
             code: "([obj.foo = obj.fo_o] = bar);",
             options: [{ properties: "always" }],
             parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "class C { camelCase; #camelCase; #camelCase2() {} }",
+            options: [{ properties: "always" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { snake_case; #snake_case; #snake_case2() {} }",
+            options: [{ properties: "never" }],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -753,7 +763,7 @@ ruleTester.run("camelcase", rule, {
         {
             code: "var camelCased = snake_cased",
             options: [{ ignoreGlobals: false }],
-            globals: { snake_cased: "readonly" }, // eslint-disable-line camelcase
+            globals: { snake_cased: "readonly" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -765,7 +775,7 @@ ruleTester.run("camelcase", rule, {
         {
             code: "a_global_variable.foo()",
             options: [{ ignoreGlobals: false }],
-            globals: { snake_cased: "readonly" }, // eslint-disable-line camelcase
+            globals: { snake_cased: "readonly" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -777,7 +787,7 @@ ruleTester.run("camelcase", rule, {
         {
             code: "a_global_variable[undefined]",
             options: [{ ignoreGlobals: false }],
-            globals: { snake_cased: "readonly" }, // eslint-disable-line camelcase
+            globals: { snake_cased: "readonly" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -788,7 +798,7 @@ ruleTester.run("camelcase", rule, {
         },
         {
             code: "var camelCased = snake_cased",
-            globals: { snake_cased: "readonly" }, // eslint-disable-line camelcase
+            globals: { snake_cased: "readonly" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -800,7 +810,7 @@ ruleTester.run("camelcase", rule, {
         {
             code: "var camelCased = snake_cased",
             options: [{}],
-            globals: { snake_cased: "readonly" }, // eslint-disable-line camelcase
+            globals: { snake_cased: "readonly" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -812,7 +822,7 @@ ruleTester.run("camelcase", rule, {
         {
             code: "foo.a_global_variable = bar",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -824,7 +834,7 @@ ruleTester.run("camelcase", rule, {
         {
             code: "var foo = { a_global_variable: bar }",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -836,7 +846,7 @@ ruleTester.run("camelcase", rule, {
         {
             code: "var foo = { a_global_variable: a_global_variable }",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -850,7 +860,7 @@ ruleTester.run("camelcase", rule, {
             code: "var foo = { a_global_variable() {} }",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -863,7 +873,7 @@ ruleTester.run("camelcase", rule, {
             code: "class Foo { a_global_variable() {} }",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -875,7 +885,7 @@ ruleTester.run("camelcase", rule, {
         {
             code: "a_global_variable: for (;;);",
             options: [{ ignoreGlobals: true }],
-            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -888,7 +898,7 @@ ruleTester.run("camelcase", rule, {
             code: "if (foo) { let a_global_variable; a_global_variable = bar; }",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -908,7 +918,7 @@ ruleTester.run("camelcase", rule, {
             code: "function foo(a_global_variable) { foo = a_global_variable; }",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -952,7 +962,7 @@ ruleTester.run("camelcase", rule, {
             code: "const a_global_variable = foo; bar = a_global_variable",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -972,7 +982,7 @@ ruleTester.run("camelcase", rule, {
             code: "bar = a_global_variable; var a_global_variable;",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase
+            globals: { a_global_variable: "writable" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -992,7 +1002,7 @@ ruleTester.run("camelcase", rule, {
             code: "var foo = { a_global_variable }",
             options: [{ ignoreGlobals: true }],
             parserOptions: { ecmaVersion: 6 },
-            globals: { a_global_variable: "readonly" }, // eslint-disable-line camelcase
+            globals: { a_global_variable: "readonly" }, // eslint-disable-line camelcase -- Testing non-CamelCase
             errors: [
                 {
                     messageId: "notCamelCase",
@@ -1001,6 +1011,28 @@ ruleTester.run("camelcase", rule, {
                 }
             ]
         },
+        {
+            code: "undefined_variable;",
+            options: [{ ignoreGlobals: true }],
+            errors: [
+                {
+                    messageId: "notCamelCase",
+                    data: { name: "undefined_variable" },
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "implicit_global = 1;",
+            options: [{ ignoreGlobals: true }],
+            errors: [
+                {
+                    messageId: "notCamelCase",
+                    data: { name: "implicit_global" },
+                    type: "Identifier"
+                }
+            ]
+        },
         {
             code: "export * as snake_cased from 'mod'",
             parserOptions: { ecmaVersion: 2020, sourceType: "module" },
@@ -1320,6 +1352,26 @@ ruleTester.run("camelcase", rule, {
             options: [{ properties: "always" }],
             parserOptions: { ecmaVersion: 2020 },
             errors: [{ messageId: "notCamelCase", data: { name: "non_camelcase" } }]
+        },
+
+        // class public/private fields, private methods.
+        {
+            code: "class C { snake_case; }",
+            options: [{ properties: "always" }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "notCamelCase", data: { name: "snake_case" } }]
+        },
+        {
+            code: "class C { #snake_case; foo() { this.#snake_case; } }",
+            options: [{ properties: "always" }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "notCamelCasePrivate", data: { name: "snake_case" }, column: 11 }]
+        },
+        {
+            code: "class C { #snake_case() {} }",
+            options: [{ properties: "always" }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "notCamelCasePrivate", data: { name: "snake_case" } }]
         }
     ]
 });
index e307d6a0c29519afb5b1ebb202951180bb1ab634..eeb122f75b0d5a931a6e64f4740b2f1be2e04ee6 100644 (file)
@@ -30,56 +30,68 @@ ruleTester.run("class-methods-use-this", rule, {
         { code: "({ a(){} });", parserOptions: { ecmaVersion: 6 } },
         { code: "class A { foo() { () => this; } }", parserOptions: { ecmaVersion: 6 } },
         { code: "({ a: function () {} });", parserOptions: { ecmaVersion: 6 } },
-        { code: "class A { foo() {this} bar() {} }", options: [{ exceptMethods: ["bar"] }], parserOptions: { ecmaVersion: 6 } }
+        { code: "class A { foo() {this} bar() {} }", options: [{ exceptMethods: ["bar"] }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { \"foo\"() { } }", options: [{ exceptMethods: ["foo"] }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { 42() { } }", options: [{ exceptMethods: ["42"] }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { foo = function() {this} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { foo = () => {this} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { foo = () => {super.toString} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { static foo = function() {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { static foo = () => {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { #bar() {} }", options: [{ exceptMethods: ["#bar"] }], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { foo = function () {} }", options: [{ enforceForClassFields: false }], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { foo = () => {} }", options: [{ enforceForClassFields: false }], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { foo() { return class { [this.foo] = 1 }; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { static {} }", parserOptions: { ecmaVersion: 2022 } }
     ],
     invalid: [
         {
             code: "class A { foo() {} }",
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { type: "FunctionExpression", line: 1, column: 14, messageId: "missingThis", data: { name: "method 'foo'" } }
+                { type: "FunctionExpression", line: 1, column: 11, messageId: "missingThis", data: { name: "method 'foo'" } }
             ]
         },
         {
             code: "class A { foo() {/**this**/} }",
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { type: "FunctionExpression", line: 1, column: 14, messageId: "missingThis", data: { name: "method 'foo'" } }
+                { type: "FunctionExpression", line: 1, column: 11, messageId: "missingThis", data: { name: "method 'foo'" } }
             ]
         },
         {
             code: "class A { foo() {var a = function () {this};} }",
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { type: "FunctionExpression", line: 1, column: 14, messageId: "missingThis", data: { name: "method 'foo'" } }
+                { type: "FunctionExpression", line: 1, column: 11, messageId: "missingThis", data: { name: "method 'foo'" } }
             ]
         },
         {
             code: "class A { foo() {var a = function () {var b = function(){this}};} }",
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { type: "FunctionExpression", line: 1, column: 14, messageId: "missingThis", data: { name: "method 'foo'" } }
+                { type: "FunctionExpression", line: 1, column: 11, messageId: "missingThis", data: { name: "method 'foo'" } }
             ]
         },
         {
             code: "class A { foo() {window.this} }",
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { type: "FunctionExpression", line: 1, column: 14, messageId: "missingThis", data: { name: "method 'foo'" } }
+                { type: "FunctionExpression", line: 1, column: 11, messageId: "missingThis", data: { name: "method 'foo'" } }
             ]
         },
         {
             code: "class A { foo() {that.this = 'this';} }",
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { type: "FunctionExpression", line: 1, column: 14, messageId: "missingThis", data: { name: "method 'foo'" } }
+                { type: "FunctionExpression", line: 1, column: 11, messageId: "missingThis", data: { name: "method 'foo'" } }
             ]
         },
         {
             code: "class A { foo() { () => undefined; } }",
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { type: "FunctionExpression", line: 1, column: 14, messageId: "missingThis", data: { name: "method 'foo'" } }
+                { type: "FunctionExpression", line: 1, column: 11, messageId: "missingThis", data: { name: "method 'foo'" } }
             ]
         },
         {
@@ -87,7 +99,7 @@ ruleTester.run("class-methods-use-this", rule, {
             options: [{ exceptMethods: ["bar"] }],
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { type: "FunctionExpression", line: 1, column: 14, messageId: "missingThis", data: { name: "method 'foo'" } }
+                { type: "FunctionExpression", line: 1, column: 11, messageId: "missingThis", data: { name: "method 'foo'" } }
             ]
         },
         {
@@ -95,7 +107,7 @@ ruleTester.run("class-methods-use-this", rule, {
             options: [{ exceptMethods: ["foo"] }],
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { type: "FunctionExpression", line: 1, column: 34, messageId: "missingThis", data: { name: "method 'hasOwnProperty'" } }
+                { type: "FunctionExpression", line: 1, column: 20, messageId: "missingThis", data: { name: "method 'hasOwnProperty'" } }
             ]
         },
         {
@@ -103,22 +115,101 @@ ruleTester.run("class-methods-use-this", rule, {
             options: [{ exceptMethods: ["foo"] }],
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { type: "FunctionExpression", line: 1, column: 16, messageId: "missingThis", data: { name: "method" } }
+                { type: "FunctionExpression", line: 1, column: 11, messageId: "missingThis", data: { name: "method" } }
+            ]
+        },
+        {
+            code: "class A { #foo() { } foo() {} #bar() {} }",
+            options: [{ exceptMethods: ["#foo"] }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { type: "FunctionExpression", line: 1, column: 22, messageId: "missingThis", data: { name: "method 'foo'" } },
+                { type: "FunctionExpression", line: 1, column: 31, messageId: "missingThis", data: { name: "private method #bar" } }
             ]
         },
         {
             code: "class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} }",
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { messageId: "missingThis", data: { name: "method 'foo'" }, type: "FunctionExpression", column: 14 },
-                { messageId: "missingThis", data: { name: "method 'bar'" }, type: "FunctionExpression", column: 24 },
-                { messageId: "missingThis", data: { name: "method '123'" }, type: "FunctionExpression", column: 32 },
-                { messageId: "missingThis", data: { name: "method 'baz'" }, type: "FunctionExpression", column: 44 },
-                { messageId: "missingThis", data: { name: "method" }, type: "FunctionExpression", column: 52 },
-                { messageId: "missingThis", data: { name: "method" }, type: "FunctionExpression", column: 63 },
-                { messageId: "missingThis", data: { name: "getter 'quux'" }, type: "FunctionExpression", column: 76 },
-                { messageId: "missingThis", data: { name: "setter" }, type: "FunctionExpression", column: 87 },
-                { messageId: "missingThis", data: { name: "generator method 'quuux'" }, type: "FunctionExpression", column: 99 }
+                { messageId: "missingThis", data: { name: "method 'foo'" }, type: "FunctionExpression", column: 11 },
+                { messageId: "missingThis", data: { name: "method 'bar'" }, type: "FunctionExpression", column: 19 },
+                { messageId: "missingThis", data: { name: "method '123'" }, type: "FunctionExpression", column: 29 },
+                { messageId: "missingThis", data: { name: "method 'baz'" }, type: "FunctionExpression", column: 37 },
+                { messageId: "missingThis", data: { name: "method" }, type: "FunctionExpression", column: 49 },
+                { messageId: "missingThis", data: { name: "method" }, type: "FunctionExpression", column: 57 },
+                { messageId: "missingThis", data: { name: "getter 'quux'" }, type: "FunctionExpression", column: 68 },
+                { messageId: "missingThis", data: { name: "setter" }, type: "FunctionExpression", column: 81 },
+                { messageId: "missingThis", data: { name: "generator method 'quuux'" }, type: "FunctionExpression", column: 93 }
+            ]
+        },
+        {
+            code: "class A { foo = function() {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "missingThis", data: { name: "method 'foo'" }, column: 11, endColumn: 25 }
+            ]
+        },
+        {
+            code: "class A { foo = () => {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "missingThis", data: { name: "method 'foo'" }, column: 11, endColumn: 17 }
+            ]
+        },
+        {
+            code: "class A { #foo = function() {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "missingThis", data: { name: "private method #foo" }, column: 11, endColumn: 26 }
+            ]
+        },
+        {
+            code: "class A { #foo = () => {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "missingThis", data: { name: "private method #foo" }, column: 11, endColumn: 18 }
+            ]
+        },
+        {
+            code: "class A { #foo() {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "missingThis", data: { name: "private method #foo" }, column: 11, endColumn: 15 }
+            ]
+        },
+        {
+            code: "class A { get #foo() {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "missingThis", data: { name: "private getter #foo" }, column: 11, endColumn: 19 }
+            ]
+        },
+        {
+            code: "class A { set #foo(x) {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "missingThis", data: { name: "private setter #foo" }, column: 11, endColumn: 19 }
+            ]
+        },
+        {
+            code: "class A { foo () { return class { foo = this }; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "missingThis", data: { name: "method 'foo'" }, column: 11, endColumn: 15 }
+            ]
+        },
+        {
+            code: "class A { foo () { return function () { foo = this }; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "missingThis", data: { name: "method 'foo'" }, column: 11, endColumn: 15 }
+            ]
+        },
+        {
+            code: "class A { foo () { return class { static { this; } } } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "missingThis", data: { name: "method 'foo'" }, column: 11, endColumn: 15 }
             ]
         }
     ]
index 1eb30825489c875c102e3bc7143731a188662daa..e0ac78d6813c8782d0dafc73a64e2ba7387ab8f4 100644 (file)
@@ -233,7 +233,45 @@ ruleTester.run("comma-style", rule, {
                     NewExpression: true
                 }
             }]
+        },
+        "var foo = [\n , \n 1, \n 2 \n];",
+        {
+            code: "const [\n , \n , \n a, \n b, \n] = arr;",
+            options: ["last", {
+                exceptions: {
+                    ArrayPattern: false
+                }
+            }],
+            parserOptions: {
+                ecmaVersion: 6
+            }
+        },
+        {
+            code: "const [\n ,, \n a, \n b, \n] = arr;",
+            options: ["last", {
+                exceptions: {
+                    ArrayPattern: false
+                }
+            }],
+            parserOptions: {
+                ecmaVersion: 6
+            }
+        },
+        {
+            code: "const arr = [\n 1 \n , \n ,2 \n]",
+            options: ["first"],
+            parserOptions: {
+                ecmaVersion: 6
+            }
+        },
+        {
+            code: "const arr = [\n ,'fifi' \n]",
+            options: ["first"],
+            parserOptions: {
+                ecmaVersion: 6
+            }
         }
+
     ],
 
     invalid: [
index cf7605baffab73c2c0fe23454c5368d59b9612d2..d6feb8a2f0520795cee82b0f8960f71216da53ce 100644 (file)
@@ -36,10 +36,10 @@ function createComplexity(complexity) {
 
 /**
  * Create an expected error object
- * @param   {string} name       The name of the symbol being tested
- * @param   {number} complexity The cyclomatic complexity value of the symbol
- * @param   {number} max        The maximum cyclomatic complexity value of the symbol
- * @returns {Object}            The error object
+ * @param {string} name The name of the symbol being tested
+ * @param {number} complexity The cyclomatic complexity value of the symbol
+ * @param {number} max The maximum cyclomatic complexity value of the symbol
+ * @returns {Object} The error object
  */
 function makeError(name, complexity, max) {
     return {
@@ -48,6 +48,10 @@ function makeError(name, complexity, max) {
     };
 }
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2021 } });
 
 ruleTester.run("complexity", rule, {
@@ -85,6 +89,40 @@ ruleTester.run("complexity", rule, {
         { code: "if (foo) { bar(); }", options: [3] },
         { code: "var a = (x) => {do {'foo';} while (true)}", options: [2], parserOptions: { ecmaVersion: 6 } },
 
+        // class fields
+        { code: "function foo() { class C { x = a || b; y = c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "function foo() { class C { static x = a || b; static y = c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "function foo() { class C { x = a || b; y = c || d; } e || f; }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "function foo() { a || b; class C { x = c || d; y = e || f; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "function foo() { class C { [x || y] = a || b; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { x = a || b; y() { c || d; } z = e || f; }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { x() { a || b; } y = c || d; z() { e || f; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { x = (() => { a || b }) || (() => { c || d }) }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { x = () => { a || b }; y = () => { c || d } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { x = a || (() => { b || c }); }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { x = class { y = a || b; z = c || d; }; }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { x = a || class { y = b || c; z = d || e; }; }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { x; y = a; static z; static q = b; }", options: [1], parserOptions: { ecmaVersion: 2022 } },
+
+        // class static blocks
+        { code: "function foo() { class C { static { a || b; } static { c || d; } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "function foo() { a || b; class C { static { c || d; } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "function foo() { class C { static { a || b; } } c || d; }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "function foo() { class C { static { a || b; } } class D { static { c || d; } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { a || b; } static { c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { a || b; } static { c || d; } static { e || f; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { () => a || b; c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { a || b; () => c || d; } static { c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { a } }", options: [1], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { a } static { b } }", options: [1], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { a || b; } } class D { static { c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { a || b; } static c = d || e; }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static a = b || c; static { c || d; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { a || b; } c = d || e; }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { a = b || c; static { d || e; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { a || b; c || d; } }", options: [3], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { if (a || b) c = d || e; } }", options: [4], parserOptions: { ecmaVersion: 2022 } },
+
         // object property options
         { code: "function b(x) {}", options: [{ max: 1 }] }
     ],
@@ -118,7 +156,7 @@ ruleTester.run("complexity", rule, {
         { code: "function a(x) {(function() {while(true){'foo';}})(); (function() {while(true){'bar';}})();}", options: [1], errors: 2 },
         { code: "function a(x) {(function() {while(true){'foo';}})(); (function() {'bar';})();}", options: [1], errors: 1 },
         { code: "var obj = { a(x) { return x ? 0 : 1; } };", options: [1], parserOptions: { ecmaVersion: 6 }, errors: [makeError("Method 'a'", 2, 1)] },
-        { code: "var obj = { a: function b(x) { return x ? 0 : 1; } };", options: [1], errors: [makeError("Method 'b'", 2, 1)] },
+        { code: "var obj = { a: function b(x) { return x ? 0 : 1; } };", options: [1], errors: [makeError("Method 'a'", 2, 1)] },
         {
             code: createComplexity(21),
             errors: [makeError("Function 'test'", 21, 20)]
@@ -129,6 +167,360 @@ ruleTester.run("complexity", rule, {
             errors: [makeError("Function 'test'", 21, 20)]
         },
 
+        // class fields
+        {
+            code: "function foo () { a || b; class C { x; } c || d; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Function 'foo'", 3, 2)]
+        },
+        {
+            code: "function foo () { a || b; class C { x = c; } d || e; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Function 'foo'", 3, 2)]
+        },
+        {
+            code: "function foo () { a || b; class C { [x || y]; } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Function 'foo'", 3, 2)]
+        },
+        {
+            code: "function foo () { a || b; class C { [x || y] = c; } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Function 'foo'", 3, 2)]
+        },
+        {
+            code: "function foo () { class C { [x || y]; } a || b; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Function 'foo'", 3, 2)]
+        },
+        {
+            code: "function foo () { class C { [x || y] = a; } b || c; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Function 'foo'", 3, 2)]
+        },
+        {
+            code: "function foo () { class C { [x || y]; [z || q]; } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Function 'foo'", 3, 2)]
+        },
+        {
+            code: "function foo () { class C { [x || y] = a; [z || q] = b; } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Function 'foo'", 3, 2)]
+        },
+        {
+            code: "function foo () { a || b; class C { x = c || d; } e || f; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Function 'foo'", 3, 2)]
+        },
+        {
+            code: "class C { x(){ a || b; } y = c || d || e; z() { f || g; } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class field initializer", 3, 2)]
+        },
+        {
+            code: "class C { x = a || b; y() { c || d || e; } z = f || g; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Method 'y'", 3, 2)]
+        },
+        {
+            code: "class C { x; y() { c || d || e; } z; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Method 'y'", 3, 2)]
+        },
+        {
+            code: "class C { x = a || b; }",
+            options: [1],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class field initializer", 2, 1)]
+        },
+        {
+            code: "(class { x = a || b; })",
+            options: [1],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class field initializer", 2, 1)]
+        },
+        {
+            code: "class C { static x = a || b; }",
+            options: [1],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class field initializer", 2, 1)]
+        },
+        {
+            code: "(class { x = a ? b : c; })",
+            options: [1],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class field initializer", 2, 1)]
+        },
+        {
+            code: "class C { x = a || b || c; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class field initializer", 3, 2)]
+        },
+        {
+            code: "class C { x = a || b; y = b || c || d; z = e || f; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                ...makeError("Class field initializer", 3, 2),
+                line: 1,
+                column: 27,
+                endLine: 1,
+                endColumn: 38
+            }]
+        },
+        {
+            code: "class C { x = a || b || c; y = d || e; z = f || g || h; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    ...makeError("Class field initializer", 3, 2),
+                    line: 1,
+                    column: 15,
+                    endLine: 1,
+                    endColumn: 26
+                },
+                {
+                    ...makeError("Class field initializer", 3, 2),
+                    line: 1,
+                    column: 44,
+                    endLine: 1,
+                    endColumn: 55
+                }
+            ]
+        },
+        {
+            code: "class C { x = () => a || b || c; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Method 'x'", 3, 2)]
+        },
+        {
+            code: "class C { x = (() => a || b || c) || d; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Arrow function", 3, 2)]
+        },
+        {
+            code: "class C { x = () => a || b || c; y = d || e; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Method 'x'", 3, 2)]
+        },
+        {
+            code: "class C { x = () => a || b || c; y = d || e || f; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                makeError("Method 'x'", 3, 2),
+                {
+                    ...makeError("Class field initializer", 3, 2),
+                    line: 1,
+                    column: 38,
+                    endLine: 1,
+                    endColumn: 49
+                }
+            ]
+        },
+        {
+            code: "class C { x = function () { a || b }; y = function () { c || d }; }",
+            options: [1],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                makeError("Method 'x'", 2, 1),
+                makeError("Method 'y'", 2, 1)
+            ]
+        },
+        {
+            code: "class C { x = class { [y || z]; }; }",
+            options: [1],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    ...makeError("Class field initializer", 2, 1),
+                    line: 1,
+                    column: 15,
+                    endLine: 1,
+                    endColumn: 34
+                }
+            ]
+        },
+        {
+            code: "class C { x = class { [y || z] = a; }; }",
+            options: [1],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    ...makeError("Class field initializer", 2, 1),
+                    line: 1,
+                    column: 15,
+                    endLine: 1,
+                    endColumn: 38
+                }
+            ]
+        },
+        {
+            code: "class C { x = class { y = a || b; }; }",
+            options: [1],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    ...makeError("Class field initializer", 2, 1),
+                    line: 1,
+                    column: 27,
+                    endLine: 1,
+                    endColumn: 33
+                }
+            ]
+        },
+
+        // class static blocks
+        {
+            code: "function foo () { a || b; class C { static {} } c || d; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Function 'foo'", 3, 2)]
+        },
+        {
+            code: "function foo () { a || b; class C { static { c || d; } } e || f; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Function 'foo'", 3, 2)]
+        },
+        {
+            code: "class C { static { a || b; }  }",
+            options: [1],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class static block", 2, 1)]
+        },
+        {
+            code: "class C { static { a || b || c; }  }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class static block", 3, 2)]
+        },
+        {
+            code: "class C { static { a || b; c || d; }  }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class static block", 3, 2)]
+        },
+        {
+            code: "class C { static { a || b; c || d; e || f; }  }",
+            options: [3],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class static block", 4, 3)]
+        },
+        {
+            code: "class C { static { a || b; c || d; { e || f; } }  }",
+            options: [3],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class static block", 4, 3)]
+        },
+        {
+            code: "class C { static { if (a || b) c = d || e; } }",
+            options: [3],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class static block", 4, 3)]
+        },
+        {
+            code: "class C { static { if (a || b) c = (d => e || f)() || (g => h || i)(); } }",
+            options: [3],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class static block", 4, 3)]
+        },
+        {
+            code: "class C { x(){ a || b; } static { c || d || e; } z() { f || g; } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class static block", 3, 2)]
+        },
+        {
+            code: "class C { x = a || b; static { c || d || e; } y = f || g; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class static block", 3, 2)]
+        },
+        {
+            code: "class C { static x = a || b; static { c || d || e; } static y = f || g; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Class static block", 3, 2)]
+        },
+        {
+            code: "class C { static { a || b; } static(){ c || d || e; } static { f || g; } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Method 'static'", 3, 2)]
+        },
+        {
+            code: "class C { static { a || b; } static static(){ c || d || e; } static { f || g; } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [makeError("Static method 'static'", 3, 2)]
+        },
+        {
+            code: "class C { static { a || b; } static x = c || d || e; static { f || g; } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                ...makeError("Class field initializer", 3, 2),
+                column: 41,
+                endColumn: 52
+            }]
+        },
+        {
+            code: "class C { static { a || b || c || d; } static { e || f || g; } }",
+            options: [3],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                ...makeError("Class static block", 4, 3),
+                column: 11,
+                endColumn: 39
+            }]
+        },
+        {
+            code: "class C { static { a || b || c; } static { d || e || f || g; } }",
+            options: [3],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                ...makeError("Class static block", 4, 3),
+                column: 35,
+                endColumn: 63
+            }]
+        },
+        {
+            code: "class C { static { a || b || c || d; } static { e || f || g || h; } }",
+            options: [3],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    ...makeError("Class static block", 4, 3),
+                    column: 11,
+                    endColumn: 39
+                },
+                {
+                    ...makeError("Class static block", 4, 3),
+                    column: 40,
+                    endColumn: 68
+                }
+            ]
+        },
+
         // object property options
         { code: "function a(x) {}", options: [{ max: 0 }], errors: [makeError("Function 'a'", 1, 0)] }
     ]
index a1e5834243ee440f0d411c892cc79f2587be4947..eff0c0a73bd633b8f70b2ecc86b1848817f5a455 100644 (file)
@@ -99,6 +99,16 @@ ruleTester.run("computed-property-spacing", rule, {
             options: ["always", { enforceForClassMembers: false }],
             parserOptions: { ecmaVersion: 6 }
         },
+        {
+            code: "class A { [ a ]; }",
+            options: ["never", { enforceForClassMembers: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class A { [a]; }",
+            options: ["always", { enforceForClassMembers: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
 
         // valid spacing
         {
@@ -141,6 +151,16 @@ ruleTester.run("computed-property-spacing", rule, {
             options: ["always", { enforceForClassMembers: true }],
             parserOptions: { ecmaVersion: 6 }
         },
+        {
+            code: "A = class { [a]; static [a]; [a] = 0; static [a] = 0; }",
+            options: ["never", { enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "A = class { [ a ]; static [ a ]; [ a ] = 0; static [ a ] = 0; }",
+            options: ["always", { enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
 
         // non-computed
         {
@@ -153,6 +173,16 @@ ruleTester.run("computed-property-spacing", rule, {
             options: ["always", { enforceForClassMembers: true }],
             parserOptions: { ecmaVersion: 6 }
         },
+        {
+            code: "A = class { foo; #a; static #b; #c = 0; static #d = 0; }",
+            options: ["never", { enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "A = class { foo; #a; static #b; #c = 0; static #d = 0; }",
+            options: ["always", { enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
 
         // handling of parens and comments
         {
@@ -1349,6 +1379,62 @@ ruleTester.run("computed-property-spacing", rule, {
                 }
             ]
         },
+        {
+            code: "class A { [ a]; [b ]; [ c ]; [ a] = 0; [b ] = 0; [ c ] = 0; }",
+            output: "class A { [a]; [b]; [c]; [a] = 0; [b] = 0; [c] = 0; }",
+            options: ["never", { enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "unexpectedSpaceAfter",
+                    data: { tokenValue: "[" },
+                    column: 12,
+                    endColumn: 13
+                },
+                {
+                    messageId: "unexpectedSpaceBefore",
+                    data: { tokenValue: "]" },
+                    column: 19,
+                    endColumn: 20
+                },
+                {
+                    messageId: "unexpectedSpaceAfter",
+                    data: { tokenValue: "[" },
+                    column: 24,
+                    endColumn: 25
+                },
+                {
+                    messageId: "unexpectedSpaceBefore",
+                    data: { tokenValue: "]" },
+                    column: 26,
+                    endColumn: 27
+                },
+                {
+                    messageId: "unexpectedSpaceAfter",
+                    data: { tokenValue: "[" },
+                    column: 31,
+                    endColumn: 32
+                },
+                {
+                    messageId: "unexpectedSpaceBefore",
+                    data: { tokenValue: "]" },
+                    column: 42,
+                    endColumn: 43
+                },
+                {
+                    messageId: "unexpectedSpaceAfter",
+                    data: { tokenValue: "[" },
+                    column: 51,
+                    endColumn: 52
+                },
+                {
+                    messageId: "unexpectedSpaceBefore",
+                    data: { tokenValue: "]" },
+                    column: 53,
+                    endColumn: 54
+                }
+            ]
+        },
 
         // always - classes
         {
@@ -1545,6 +1631,54 @@ ruleTester.run("computed-property-spacing", rule, {
                 }
             ]
         },
+        {
+            code: "class A { [ a]; [b ]; [c]; [ a] = 0; [b ] = 0; [c] = 0; }",
+            output: "class A { [ a ]; [ b ]; [ c ]; [ a ] = 0; [ b ] = 0; [ c ] = 0; }",
+            options: ["always", { enforceForClassMembers: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "missingSpaceBefore",
+                    column: 14,
+                    endColumn: 15
+                },
+                {
+                    messageId: "missingSpaceAfter",
+                    column: 17,
+                    endColumn: 18
+                },
+                {
+                    messageId: "missingSpaceAfter",
+                    column: 23,
+                    endColumn: 24
+                },
+                {
+                    messageId: "missingSpaceBefore",
+                    column: 25,
+                    endColumn: 26
+                },
+                {
+                    messageId: "missingSpaceBefore",
+                    column: 31,
+                    endColumn: 32
+                },
+                {
+                    messageId: "missingSpaceAfter",
+                    column: 38,
+                    endColumn: 39
+                },
+                {
+                    messageId: "missingSpaceAfter",
+                    column: 48,
+                    endColumn: 49
+                },
+                {
+                    messageId: "missingSpaceBefore",
+                    column: 50,
+                    endColumn: 51
+                }
+            ]
+        },
 
         // handling of parens and comments
         {
index 53781ea423dc12b59cd28422026a148d6f35a9fb..16a1ed97a19386c40c8ca8095c9fa8339b271b20 100644 (file)
@@ -52,7 +52,11 @@ ruleTester.run("consistent-return", rule, {
                 {
                     messageId: "missingReturnValue",
                     data: { name: "Function 'foo'" },
-                    type: "ReturnStatement"
+                    type: "ReturnStatement",
+                    line: 1,
+                    column: 46,
+                    endLine: 1,
+                    endColumn: 53
                 }
             ]
         },
@@ -63,7 +67,11 @@ ruleTester.run("consistent-return", rule, {
                 {
                     messageId: "missingReturnValue",
                     data: { name: "Arrow function" },
-                    type: "ReturnStatement"
+                    type: "ReturnStatement",
+                    line: 1,
+                    column: 47,
+                    endLine: 1,
+                    endColumn: 54
                 }
             ]
         },
@@ -73,7 +81,11 @@ ruleTester.run("consistent-return", rule, {
                 {
                     messageId: "unexpectedReturnValue",
                     data: { name: "Function 'foo'" },
-                    type: "ReturnStatement"
+                    type: "ReturnStatement",
+                    line: 1,
+                    column: 41,
+                    endLine: 1,
+                    endColumn: 54
                 }
             ]
         },
@@ -83,7 +95,11 @@ ruleTester.run("consistent-return", rule, {
                 {
                     messageId: "missingReturnValue",
                     data: { name: "Function" },
-                    type: "ReturnStatement"
+                    type: "ReturnStatement",
+                    line: 1,
+                    column: 44,
+                    endLine: 1,
+                    endColumn: 51
                 }
             ]
         },
@@ -93,7 +109,11 @@ ruleTester.run("consistent-return", rule, {
                 {
                     messageId: "unexpectedReturnValue",
                     data: { name: "Function" },
-                    type: "ReturnStatement"
+                    type: "ReturnStatement",
+                    line: 1,
+                    column: 39,
+                    endLine: 1,
+                    endColumn: 52
                 }
             ]
         },
@@ -104,7 +124,11 @@ ruleTester.run("consistent-return", rule, {
                 {
                     messageId: "unexpectedReturnValue",
                     data: { name: "Arrow function" },
-                    type: "ReturnStatement"
+                    type: "ReturnStatement",
+                    line: 1,
+                    column: 33,
+                    endLine: 1,
+                    endColumn: 46
                 }
             ]
         },
@@ -116,7 +140,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturnValue",
                     data: { name: "Function 'foo'" },
                     type: "ReturnStatement",
-                    column: 41
+                    line: 1,
+                    column: 41,
+                    endLine: 1,
+                    endColumn: 58
                 }
             ]
         },
@@ -128,7 +155,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturnValue",
                     data: { name: "Function 'foo'" },
                     type: "ReturnStatement",
-                    column: 41
+                    line: 1,
+                    column: 41,
+                    endLine: 1,
+                    endColumn: 55
                 }
             ]
         },
@@ -140,7 +170,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "unexpectedReturnValue",
                     data: { name: "Function 'foo'" },
                     type: "ReturnStatement",
-                    column: 46
+                    line: 1,
+                    column: 46,
+                    endLine: 1,
+                    endColumn: 58
                 }
             ]
         },
@@ -152,7 +185,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "unexpectedReturnValue",
                     data: { name: "Function 'foo'" },
                     type: "ReturnStatement",
-                    column: 43
+                    line: 1,
+                    column: 43,
+                    endLine: 1,
+                    endColumn: 55
                 }
             ]
         },
@@ -163,7 +199,11 @@ ruleTester.run("consistent-return", rule, {
                 {
                     messageId: "missingReturnValue",
                     data: { name: "Program" },
-                    type: "ReturnStatement"
+                    type: "ReturnStatement",
+                    line: 1,
+                    column: 25,
+                    endLine: 1,
+                    endColumn: 32
                 }
             ]
         },
@@ -174,7 +214,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturn",
                     data: { name: "function 'foo'" },
                     type: "FunctionDeclaration",
-                    column: 10
+                    line: 1,
+                    column: 10,
+                    endLine: 1,
+                    endColumn: 13
                 }
             ]
         },
@@ -185,7 +228,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturn",
                     data: { name: "function '_foo'" },
                     type: "FunctionDeclaration",
-                    column: 10
+                    line: 1,
+                    column: 10,
+                    endLine: 1,
+                    endColumn: 14
                 }
             ]
         },
@@ -196,7 +242,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturn",
                     data: { name: "function 'foo'" },
                     type: "FunctionExpression",
-                    column: 12
+                    line: 1,
+                    column: 12,
+                    endLine: 1,
+                    endColumn: 15
                 }
             ]
         },
@@ -207,7 +256,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturn",
                     data: { name: "function" },
                     type: "FunctionExpression",
-                    column: 3
+                    line: 1,
+                    column: 3,
+                    endLine: 1,
+                    endColumn: 11
                 }
             ]
         },
@@ -219,7 +271,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturn",
                     data: { name: "arrow function" },
                     type: "ArrowFunctionExpression",
-                    column: 6
+                    line: 1,
+                    column: 6,
+                    endLine: 1,
+                    endColumn: 8
                 }
             ]
         },
@@ -231,7 +286,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturn",
                     data: { name: "method 'foo'" },
                     type: "FunctionExpression",
-                    column: 12
+                    line: 1,
+                    column: 12,
+                    endLine: 1,
+                    endColumn: 15
                 }
             ]
         },
@@ -243,7 +301,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturn",
                     data: { name: "method 'foo'" },
                     type: "FunctionExpression",
-                    column: 10
+                    line: 1,
+                    column: 10,
+                    endLine: 1,
+                    endColumn: 13
                 }
             ]
         },
@@ -255,7 +316,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturn",
                     data: { name: "program" },
                     type: "Program",
-                    column: 1
+                    line: 1,
+                    column: 1,
+                    endLine: void 0,
+                    endColumn: void 0
                 }
             ]
         },
@@ -267,7 +331,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturn",
                     data: { name: "method 'CapitalizedFunction'" },
                     type: "FunctionExpression",
-                    column: 11
+                    line: 1,
+                    column: 11,
+                    endLine: 1,
+                    endColumn: 30
                 }
             ]
         },
@@ -279,7 +346,10 @@ ruleTester.run("consistent-return", rule, {
                     messageId: "missingReturn",
                     data: { name: "method 'constructor'" },
                     type: "FunctionExpression",
-                    column: 4
+                    line: 1,
+                    column: 4,
+                    endLine: 1,
+                    endColumn: 15
                 }
             ]
         }
index 155eec994dac7c15482d1c4c465ce10e7995dd45..e5fe88273ecb13fee99a3b98e7a57fa7778e04a7 100644 (file)
@@ -13,7 +13,7 @@ const rule = require("../../../lib/rules/curly"),
     { RuleTester } = require("../../../lib/rule-tester");
 
 //------------------------------------------------------------------------------
-// Helpers
+// Tests
 //------------------------------------------------------------------------------
 
 const ruleTester = new RuleTester();
@@ -429,7 +429,26 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "missingCurlyAfterCondition",
                     data: { name: "if" },
-                    type: "IfStatement"
+                    type: "IfStatement",
+                    line: 1,
+                    column: 10,
+                    endLine: 1,
+                    endColumn: 15
+                }
+            ]
+        },
+        {
+            code: "if (foo) \n bar()",
+            output: "if (foo) \n {bar()}",
+            errors: [
+                {
+                    messageId: "missingCurlyAfterCondition",
+                    data: { name: "if" },
+                    type: "IfStatement",
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 7
                 }
             ]
         },
@@ -462,7 +481,26 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "missingCurlyAfterCondition",
                     data: { name: "while" },
-                    type: "WhileStatement"
+                    type: "WhileStatement",
+                    line: 1,
+                    column: 13,
+                    endLine: 1,
+                    endColumn: 18
+                }
+            ]
+        },
+        {
+            code: "while (foo) \n bar()",
+            output: "while (foo) \n {bar()}",
+            errors: [
+                {
+                    messageId: "missingCurlyAfterCondition",
+                    data: { name: "while" },
+                    type: "WhileStatement",
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 7
                 }
             ]
         },
@@ -473,7 +511,26 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "missingCurlyAfter",
                     data: { name: "do" },
-                    type: "DoWhileStatement"
+                    type: "DoWhileStatement",
+                    line: 1,
+                    column: 4,
+                    endLine: 1,
+                    endColumn: 10
+                }
+            ]
+        },
+        {
+            code: "do \n bar(); while (foo)",
+            output: "do \n {bar();} while (foo)",
+            errors: [
+                {
+                    messageId: "missingCurlyAfter",
+                    data: { name: "do" },
+                    type: "DoWhileStatement",
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 8
                 }
             ]
         },
@@ -507,7 +564,93 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "missingCurlyAfter",
                     data: { name: "for-of" },
-                    type: "ForOfStatement"
+                    type: "ForOfStatement",
+                    line: 1,
+                    column: 22,
+                    endLine: 1,
+                    endColumn: 38
+                }
+            ]
+        },
+        {
+            code: "for (var foo of bar) \n console.log(foo)",
+            output: "for (var foo of bar) \n {console.log(foo)}",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    messageId: "missingCurlyAfter",
+                    data: { name: "for-of" },
+                    type: "ForOfStatement",
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 18
+                }
+            ]
+        },
+        {
+            code: "for (a;;) console.log(foo)",
+            output: "for (a;;) {console.log(foo)}",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    messageId: "missingCurlyAfterCondition",
+                    data: { name: "for" },
+                    type: "ForStatement",
+                    line: 1,
+                    column: 11,
+                    endLine: 1,
+                    endColumn: 27
+                }
+            ]
+        },
+        {
+            code: "for (a;;) \n console.log(foo)",
+            output: "for (a;;) \n {console.log(foo)}",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    messageId: "missingCurlyAfterCondition",
+                    data: { name: "for" },
+                    type: "ForStatement",
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 18
+                }
+            ]
+        },
+        {
+            code: "for (var foo of bar) {console.log(foo)}",
+            output: "for (var foo of bar) console.log(foo)",
+            options: ["multi"],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    messageId: "unexpectedCurlyAfter",
+                    data: { name: "for-of" },
+                    type: "ForOfStatement",
+                    line: 1,
+                    column: 22,
+                    endLine: 1,
+                    endColumn: 40
+                }
+            ]
+        },
+        {
+            code: "do{foo();} while(bar);",
+            output: "do foo(); while(bar);",
+            options: ["multi"],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    messageId: "unexpectedCurlyAfter",
+                    data: { name: "do" },
+                    type: "DoWhileStatement",
+                    line: 1,
+                    column: 3,
+                    endLine: 1,
+                    endColumn: 11
                 }
             ]
         },
@@ -519,7 +662,26 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "unexpectedCurlyAfterCondition",
                     data: { name: "for" },
-                    type: "ForStatement"
+                    type: "ForStatement",
+                    line: 1,
+                    column: 13,
+                    endLine: 1,
+                    endColumn: 22
+                }
+            ]
+        },
+        {
+            code: "for (;foo;) \n bar()",
+            output: "for (;foo;) \n {bar()}",
+            errors: [
+                {
+                    data: { name: "for" },
+                    type: "ForStatement",
+                    messageId: "missingCurlyAfterCondition",
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 7
                 }
             ]
         },
@@ -531,7 +693,11 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "unexpectedCurlyAfterCondition",
                     data: { name: "if" },
-                    type: "IfStatement"
+                    type: "IfStatement",
+                    line: 1,
+                    column: 10,
+                    endLine: 1,
+                    endColumn: 19
                 }
             ]
         },
@@ -567,7 +733,11 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "unexpectedCurlyAfterCondition",
                     data: { name: "while" },
-                    type: "WhileStatement"
+                    type: "WhileStatement",
+                    line: 1,
+                    column: 13,
+                    endLine: 1,
+                    endColumn: 22
                 }
             ]
         },
@@ -653,7 +823,9 @@ ruleTester.run("curly", rule, {
                     data: { name: "else" },
                     type: "IfStatement",
                     line: 6,
-                    column: 3
+                    column: 8,
+                    endLine: 11,
+                    endColumn: 2
                 }
             ]
         },
@@ -665,7 +837,11 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "unexpectedCurlyAfter",
                     data: { name: "for-in" },
-                    type: "ForInStatement"
+                    type: "ForInStatement",
+                    line: 1,
+                    column: 22,
+                    endLine: 1,
+                    endColumn: 42
                 }
             ]
         },
@@ -690,7 +866,26 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "missingCurlyAfterCondition",
                     data: { name: "if" },
-                    type: "IfStatement"
+                    type: "IfStatement",
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 7
+                }
+            ]
+        },
+        {
+            code: "if (foo) baz()",
+            output: "if (foo) {baz()}",
+            errors: [
+                {
+                    messageId: "missingCurlyAfterCondition",
+                    data: { name: "if" },
+                    type: "IfStatement",
+                    line: 1,
+                    column: 10,
+                    endLine: 1,
+                    endColumn: 15
                 }
             ]
         },
@@ -742,6 +937,22 @@ ruleTester.run("curly", rule, {
                 }
             ]
         },
+        {
+            code: "do foo(); while (bar)",
+            output: "do {foo();} while (bar)",
+            options: ["all"],
+            errors: [
+                {
+                    messageId: "missingCurlyAfter",
+                    data: { name: "do" },
+                    type: "DoWhileStatement",
+                    line: 1,
+                    column: 4,
+                    endLine: 1,
+                    endColumn: 10
+                }
+            ]
+        },
         {
             code: "do \n foo(); \n while (bar)",
             output: "do \n {foo();} \n while (bar)",
@@ -750,7 +961,27 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "missingCurlyAfter",
                     data: { name: "do" },
-                    type: "DoWhileStatement"
+                    type: "DoWhileStatement",
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 8
+                }
+            ]
+        },
+        {
+            code: "for (var foo in bar) {console.log(foo)}",
+            output: "for (var foo in bar) console.log(foo)",
+            options: ["multi"],
+            errors: [
+                {
+                    messageId: "unexpectedCurlyAfter",
+                    data: { name: "for-in" },
+                    type: "ForInStatement",
+                    line: 1,
+                    column: 22,
+                    endLine: 1,
+                    endColumn: 40
                 }
             ]
         },
@@ -787,7 +1018,11 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "missingCurlyAfter",
                     data: { name: "for-of" },
-                    type: "ForOfStatement"
+                    type: "ForOfStatement",
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 18
                 }
             ]
         },
@@ -910,7 +1145,45 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "unexpectedCurlyAfterCondition",
                     data: { name: "for" },
-                    type: "ForStatement"
+                    type: "ForStatement",
+                    line: 1,
+                    column: 27,
+                    endLine: 3,
+                    endColumn: 3
+                }
+            ]
+        },
+        {
+            code: "for (var foo in bar) if (foo) console.log(1); else console.log(2);",
+            output: "for (var foo in bar) {if (foo) console.log(1); else console.log(2);}",
+            options: ["all"],
+            errors: [
+                {
+                    messageId: "missingCurlyAfter",
+                    data: { name: "for-in" },
+                    type: "ForInStatement",
+                    line: 1,
+                    column: 22,
+                    endLine: 1,
+                    endColumn: 67
+                },
+                {
+                    messageId: "missingCurlyAfterCondition",
+                    data: { name: "if" },
+                    type: "IfStatement",
+                    line: 1,
+                    column: 31,
+                    endLine: 1,
+                    endColumn: 46
+                },
+                {
+                    messageId: "missingCurlyAfter",
+                    data: { name: "else" },
+                    type: "IfStatement",
+                    line: 1,
+                    column: 52,
+                    endLine: 1,
+                    endColumn: 67
                 }
             ]
         },
@@ -922,7 +1195,11 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "missingCurlyAfter",
                     data: { name: "for-in" },
-                    type: "ForInStatement"
+                    type: "ForInStatement",
+                    line: 2,
+                    column: 2,
+                    endLine: 3,
+                    endColumn: 22
                 }
             ]
         },
@@ -996,7 +1273,11 @@ ruleTester.run("curly", rule, {
                 {
                     messageId: "unexpectedCurlyAfter",
                     data: { name: "else" },
-                    type: "IfStatement"
+                    type: "IfStatement",
+                    line: 1,
+                    column: 23,
+                    endLine: 1,
+                    endColumn: 33
                 }
             ]
         },
@@ -1041,6 +1322,70 @@ ruleTester.run("curly", rule, {
                 }
             ]
         },
+        {
+            code: "do\n{foo();} while (bar)",
+            output: "do\nfoo(); while (bar)",
+            options: ["multi"],
+            errors: [
+                {
+                    messageId: "unexpectedCurlyAfter",
+                    data: { name: "do" },
+                    type: "DoWhileStatement",
+                    line: 2,
+                    column: 1,
+                    endLine: 2,
+                    endColumn: 9
+                }
+            ]
+        },
+        {
+            code: "while (bar) { foo(); }",
+            output: "while (bar)  foo(); ",
+            options: ["multi"],
+            errors: [
+                {
+                    messageId: "unexpectedCurlyAfterCondition",
+                    data: { name: "while" },
+                    type: "WhileStatement",
+                    line: 1,
+                    column: 13,
+                    endLine: 1,
+                    endColumn: 23
+                }
+            ]
+        },
+        {
+            code: "while (bar) \n{\n foo(); }",
+            output: "while (bar) \n\n foo(); ",
+            options: ["multi"],
+            errors: [
+                {
+                    messageId: "unexpectedCurlyAfterCondition",
+                    data: { name: "while" },
+                    type: "WhileStatement",
+                    line: 2,
+                    column: 1,
+                    endLine: 3,
+                    endColumn: 10
+                }
+            ]
+        },
+        {
+            code: "for (;;) { foo(); }",
+            output: "for (;;)  foo(); ",
+            options: ["multi"],
+            errors: [
+                {
+                    messageId: "unexpectedCurlyAfterCondition",
+                    data: { name: "for" },
+                    type: "ForStatement",
+                    line: 1,
+                    column: 10,
+                    endLine: 1,
+                    endColumn: 20
+                }
+            ]
+        },
         {
             code: "do{[1, 2, 3].map(bar);} while (bar)",
             output: "do[1, 2, 3].map(bar); while (bar)",
@@ -1313,7 +1658,132 @@ ruleTester.run("curly", rule, {
             code: "if (a) { while (cond) if (b) foo() } else bar();",
             output: "if (a) { while (cond) if (b) foo() } else {bar();}",
             options: ["multi", "consistent"],
-            errors: [{ messageId: "missingCurlyAfter", data: { name: "else" }, type: "IfStatement" }]
+            errors: [
+                {
+                    messageId: "missingCurlyAfter",
+                    data: { name: "else" },
+                    type: "IfStatement",
+                    line: 1,
+                    column: 43,
+                    endLine: 1,
+                    endColumn: 49
+                }
+            ]
+        },
+        {
+            code: "if (a)  while (cond) if (b) foo()  \nelse\n {bar();}",
+            output: "if (a)  while (cond) if (b) foo()  \nelse\n bar();",
+            options: ["multi", "consistent"],
+            errors: [
+                {
+                    messageId: "unexpectedCurlyAfter",
+                    data: { name: "else" },
+                    type: "IfStatement",
+                    line: 3,
+                    column: 2,
+                    endLine: 3,
+                    endColumn: 10
+                }
+            ]
+        },
+        {
+            code: "if (a) foo() \nelse\n bar();",
+            output: "if (a) {foo()} \nelse\n {bar();}",
+            errors: [{
+                type: "IfStatement",
+                messageId: "missingCurlyAfterCondition",
+                line: 1,
+                column: 8,
+                endLine: 1,
+                endColumn: 13
+            },
+            {
+                type: "IfStatement",
+                messageId: "missingCurlyAfter",
+                line: 3,
+                column: 2,
+                endLine: 3,
+                endColumn: 8
+            }]
+        },
+        {
+            code: "if (a) { while (cond) if (b) foo() } ",
+            output: "if (a)  while (cond) if (b) foo()  ",
+            options: ["multi", "consistent"],
+            errors: [{
+                messageId: "unexpectedCurlyAfterCondition",
+                data: { name: "if" },
+                type: "IfStatement",
+                line: 1,
+                column: 8,
+                endLine: 1,
+                endColumn: 37
+            }]
+        },
+        {
+            code: "if(a) { if (b) foo(); } if (c) bar(); else if(foo){bar();}",
+            output: "if(a)  if (b) foo();  if (c) bar(); else if(foo)bar();",
+            options: ["multi-or-nest"],
+            errors: [{
+                type: "IfStatement",
+                data: { name: "if" },
+                messageId: "unexpectedCurlyAfterCondition",
+                line: 1,
+                column: 7,
+                endLine: 1,
+                endColumn: 24
+            },
+            {
+                type: "IfStatement",
+                data: { name: "if" },
+                messageId: "unexpectedCurlyAfterCondition",
+                line: 1,
+                column: 51,
+                endLine: 1,
+                endColumn: 59
+            }]
+        },
+        {
+            code: "if (true) [1, 2, 3]\n.bar()",
+            output: "if (true) {[1, 2, 3]\n.bar()}",
+            options: ["multi-line"],
+            errors: [{
+                data: { name: "if" },
+                type: "IfStatement",
+                messageId: "missingCurlyAfterCondition",
+                line: 1,
+                column: 11,
+                endLine: 2,
+                endColumn: 7
+            }]
+        },
+        {
+            code: "for(\n;\n;\n) {foo()}",
+            output: "for(\n;\n;\n) foo()",
+            options: ["multi"],
+            errors: [{
+                data: { name: "for" },
+                type: "ForStatement",
+                messageId: "unexpectedCurlyAfterCondition",
+                line: 4,
+                column: 3,
+                endLine: 4,
+                endColumn: 10
+            }]
+        },
+        {
+            code: "for(\n;\n;\n) \nfoo()\n",
+            output: "for(\n;\n;\n) \n{foo()}\n",
+            options: ["multi-line"],
+            errors: [{
+                data: { name: "for" },
+                type: "ForStatement",
+                messageId: "missingCurlyAfterCondition",
+                line: 5,
+                column: 1,
+                endLine: 5,
+                endColumn: 6
+            }]
         },
         {
 
@@ -1330,6 +1800,46 @@ ruleTester.run("curly", rule, {
                 { messageId: "unexpectedCurlyAfterCondition", data: { name: "if" }, type: "IfStatement" },
                 { messageId: "unexpectedCurlyAfterCondition", data: { name: "while" }, type: "WhileStatement" }
             ]
+        },
+        {
+            code: "for(;;)foo()\n",
+            output: "for(;;){foo()}\n",
+            errors: [{
+                data: { name: "for" },
+                type: "ForStatement",
+                messageId: "missingCurlyAfterCondition",
+                line: 1,
+                column: 8,
+                endLine: 1,
+                endColumn: 13
+            }]
+        },
+        {
+            code: "for(var \ni \n in \n z)foo()\n",
+            output: "for(var \ni \n in \n z){foo()}\n",
+            errors: [{
+                data: { name: "for-in" },
+                type: "ForInStatement",
+                messageId: "missingCurlyAfter",
+                line: 4,
+                column: 4,
+                endLine: 4,
+                endColumn: 9
+            }]
+        },
+        {
+            code: "for(var i of \n z)\nfoo()\n",
+            output: "for(var i of \n z)\n{foo()}\n",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                data: { name: "for-of" },
+                type: "ForOfStatement",
+                messageId: "missingCurlyAfter",
+                line: 3,
+                column: 1,
+                endLine: 3,
+                endColumn: 6
+            }]
         }
     ]
 });
index 05321f5e265d77c35b773b987c16196ae82a4a47..e628db39e8e05573ab8fb8f1bccf492cf0437e81 100644 (file)
@@ -4,9 +4,17 @@
  */
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const rule = require("../../../lib/rules/default-param-last");
 const { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const SHOULD_BE_LAST = "shouldBeLast";
 
 const ruleTester = new RuleTester({
index bd56e1fc797a47e01c087de479cdc33450b46714..d1206575e4cf274d0bd2625c6e9ef734d9617b48 100644 (file)
@@ -198,6 +198,18 @@ ruleTester.run("dot-location", rule, {
             code: "obj?.[\nkey]",
             options: ["property"],
             parserOptions: { ecmaVersion: 2020 }
+        },
+
+        // Private properties
+        {
+            code: "class C { #a; foo() { this.\n#a; } }",
+            options: ["object"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #a; foo() { this\n.#a; } }",
+            options: ["property"],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -386,6 +398,22 @@ ruleTester.run("dot-location", rule, {
             options: ["property"],
             parserOptions: { ecmaVersion: 2020 },
             errors: [{ messageId: "expectedDotBeforeProperty" }]
+        },
+
+        // Private properties
+        {
+            code: "class C { #a; foo() { this\n.#a; } }",
+            output: "class C { #a; foo() { this.\n#a; } }",
+            options: ["object"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "expectedDotAfterObject" }]
+        },
+        {
+            code: "class C { #a; foo() { this.\n#a; } }",
+            output: "class C { #a; foo() { this\n.#a; } }",
+            options: ["property"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "expectedDotBeforeProperty" }]
         }
     ]
 });
index 39f3a676b0b22ddb8004881b5b959af24ae2fb9e..c2ced102b9c5f9400a9d93e7dc9eb6d52365b971 100644 (file)
@@ -21,13 +21,17 @@ const ruleTester = new RuleTester();
 /**
  * Quote a string in "double quotes" because it’s painful
  * with a double-quoted string literal
- * @param   {string} str The string to quote
- * @returns {string}     `"${str}"`
+ * @param {string} str The string to quote
+ * @returns {string} `"${str}"`
  */
 function q(str) {
     return `"${str}"`;
 }
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 ruleTester.run("dot-notation", rule, {
     valid: [
         "a.b;",
@@ -58,7 +62,13 @@ ruleTester.run("dot-notation", rule, {
         "a[undefined];",
         "a[void 0];",
         "a[b()];",
-        { code: "a[/(?<zero>0)/];", parserOptions: { ecmaVersion: 2018 } }
+        { code: "a[/(?<zero>0)/];", parserOptions: { ecmaVersion: 2018 } },
+        { code: "class C { foo() { this['#a'] } }", parserOptions: { ecmaVersion: 2022 } },
+        {
+            code: "class C { #in; foo() { this.#in; } }",
+            options: [{ allowKeywords: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
     invalid: [
         {
index 1e5d3ebf291a6403efbcbe7eef40ee9a4a1a4fa0..7474b019e0264d5e8ccc05960e36d8349440ba27 100644 (file)
@@ -55,48 +55,104 @@ ruleTester.run("eol-last", rule, {
         {
             code: "var a = 123;",
             output: "var a = 123;\n",
-            errors: [{ messageId: "missing", type: "Program" }]
+            errors: [{
+                messageId: "missing",
+                type: "Program",
+                line: 1,
+                column: 13,
+                endLine: void 0,
+                endColumn: void 0
+            }]
         },
         {
             code: "var a = 123;\n   ",
             output: "var a = 123;\n   \n",
-            errors: [{ messageId: "missing", type: "Program" }]
+            errors: [{
+                messageId: "missing",
+                type: "Program",
+                line: 2,
+                column: 4,
+                endLine: void 0,
+                endColumn: void 0
+            }]
         },
         {
             code: "var a = 123;\n",
             output: "var a = 123;",
             options: ["never"],
-            errors: [{ messageId: "unexpected", type: "Program" }]
+            errors: [{
+                messageId: "unexpected",
+                type: "Program",
+                line: 1,
+                column: 13,
+                endLine: 2,
+                endColumn: 1
+            }]
         },
         {
             code: "var a = 123;\r\n",
             output: "var a = 123;",
             options: ["never"],
-            errors: [{ messageId: "unexpected", type: "Program" }]
+            errors: [{
+                messageId: "unexpected",
+                type: "Program",
+                line: 1,
+                column: 13,
+                endLine: 2,
+                endColumn: 1
+            }]
         },
         {
             code: "var a = 123;\r\n\r\n",
             output: "var a = 123;",
             options: ["never"],
-            errors: [{ messageId: "unexpected", type: "Program" }]
+            errors: [{
+                messageId: "unexpected",
+                type: "Program",
+                line: 2,
+                column: 1,
+                endLine: 3,
+                endColumn: 1
+            }]
         },
         {
             code: "var a = 123;\nvar b = 456;\n",
             output: "var a = 123;\nvar b = 456;",
             options: ["never"],
-            errors: [{ messageId: "unexpected", type: "Program" }]
+            errors: [{
+                messageId: "unexpected",
+                type: "Program",
+                line: 2,
+                column: 13,
+                endLine: 3,
+                endColumn: 1
+            }]
         },
         {
             code: "var a = 123;\r\nvar b = 456;\r\n",
             output: "var a = 123;\r\nvar b = 456;",
             options: ["never"],
-            errors: [{ messageId: "unexpected", type: "Program" }]
+            errors: [{
+                messageId: "unexpected",
+                type: "Program",
+                line: 2,
+                column: 13,
+                endLine: 3,
+                endColumn: 1
+            }]
         },
         {
             code: "var a = 123;\n\n",
             output: "var a = 123;",
             options: ["never"],
-            errors: [{ messageId: "unexpected", type: "Program" }]
+            errors: [{
+                messageId: "unexpected",
+                type: "Program",
+                line: 2,
+                column: 1,
+                endLine: 3,
+                endColumn: 1
+            }]
         },
 
         // Deprecated: `"unix"` parameter
@@ -104,13 +160,27 @@ ruleTester.run("eol-last", rule, {
             code: "var a = 123;",
             output: "var a = 123;\n",
             options: ["unix"],
-            errors: [{ messageId: "missing", type: "Program" }]
+            errors: [{
+                messageId: "missing",
+                type: "Program",
+                line: 1,
+                column: 13,
+                endLine: void 0,
+                endColumn: void 0
+            }]
         },
         {
             code: "var a = 123;\n   ",
             output: "var a = 123;\n   \n",
             options: ["unix"],
-            errors: [{ messageId: "missing", type: "Program" }]
+            errors: [{
+                messageId: "missing",
+                type: "Program",
+                line: 2,
+                column: 4,
+                endLine: void 0,
+                endColumn: void 0
+            }]
         },
 
         // Deprecated: `"windows"` parameter
@@ -118,13 +188,27 @@ ruleTester.run("eol-last", rule, {
             code: "var a = 123;",
             output: "var a = 123;\r\n",
             options: ["windows"],
-            errors: [{ messageId: "missing", type: "Program" }]
+            errors: [{
+                messageId: "missing",
+                type: "Program",
+                line: 1,
+                column: 13,
+                endLine: void 0,
+                endColumn: void 0
+            }]
         },
         {
             code: "var a = 123;\r\n   ",
             output: "var a = 123;\r\n   \r\n",
             options: ["windows"],
-            errors: [{ messageId: "missing", type: "Program" }]
+            errors: [{
+                messageId: "missing",
+                type: "Program",
+                line: 2,
+                column: 4,
+                endLine: void 0,
+                endColumn: void 0
+            }]
         }
     ]
 });
index a04b10e118df841628b2f60f099b92dc5c9cdbc1..8ecd843c238ce69efdaabc30ed08c13d83ba2408 100644 (file)
@@ -13,7 +13,7 @@ const rule = require("../../../lib/rules/for-direction");
 const { RuleTester } = require("../../../lib/rule-tester");
 
 //------------------------------------------------------------------------------
-// Helpers
+// Tests
 //------------------------------------------------------------------------------
 
 const ruleTester = new RuleTester();
index 908e9108f3ea957edfd53369ef608f195c63dbde..7e2152544106bdb91f35036f374da5d77b09165b 100644 (file)
@@ -262,6 +262,248 @@ ruleTester.run("func-name-matching", rule, {
         {
             code: "foo({ value: function value() {} })",
             options: ["always", { considerPropertyDescriptor: true }]
+        },
+
+        // class fields, private names are ignored
+        {
+            code: "class C { x = function () {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { x = function () {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { 'x' = function () {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { 'x' = function () {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x = function () {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x = function () {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [x] = function () {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [x] = function () {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { ['x'] = function () {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { ['x'] = function () {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { x = function x() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { x = function y() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { 'x' = function x() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { 'x' = function y() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x = function x() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x = function x() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x = function y() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x = function y() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [x] = function x() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [x] = function x() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [x] = function y() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [x] = function y() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { ['x'] = function x() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { ['x'] = function y() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { 'xy ' = function foo() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { 'xy ' = function xy() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { ['xy '] = function foo() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { ['xy '] = function xy() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { 1 = function x0() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { 1 = function x1() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [1] = function x0() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [1] = function x1() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [f()] = function g() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [f()] = function f() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static x = function x() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static x = function y() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { x = (function y() {})(); }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { x = (function x() {})(); }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "(class { x = function x() {}; })",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "(class { x = function y() {}; })",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x; foo() { this.#x = function x() {}; } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x; foo() { this.#x = function x() {}; } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x; foo() { this.#x = function y() {}; } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x; foo() { this.#x = function y() {}; } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x; foo() { a.b.#x = function x() {}; } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x; foo() { a.b.#x = function x() {}; } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x; foo() { a.b.#x = function y() {}; } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #x; foo() { a.b.#x = function y() {}; } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -554,6 +796,88 @@ ruleTester.run("func-name-matching", rule, {
             errors: [
                 { messageId: "notMatchProperty", data: { funcName: "bar", name: "bar" } }
             ]
+        },
+
+        // class fields
+        {
+            code: "class C { x = function y() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "matchProperty", data: { funcName: "y", name: "x" } }
+            ]
+        },
+        {
+            code: "class C { x = function x() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "notMatchProperty", data: { funcName: "x", name: "x" } }
+            ]
+        },
+        {
+            code: "class C { 'x' = function y() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "matchProperty", data: { funcName: "y", name: "x" } }
+            ]
+        },
+        {
+            code: "class C { 'x' = function x() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "notMatchProperty", data: { funcName: "x", name: "x" } }
+            ]
+        },
+        {
+            code: "class C { ['x'] = function y() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "matchProperty", data: { funcName: "y", name: "x" } }
+            ]
+        },
+        {
+            code: "class C { ['x'] = function x() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "notMatchProperty", data: { funcName: "x", name: "x" } }
+            ]
+        },
+        {
+            code: "class C { static x = function y() {}; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "matchProperty", data: { funcName: "y", name: "x" } }
+            ]
+        },
+        {
+            code: "class C { static x = function x() {}; }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "notMatchProperty", data: { funcName: "x", name: "x" } }
+            ]
+        },
+        {
+            code: "(class { x = function y() {}; })",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "matchProperty", data: { funcName: "y", name: "x" } }
+            ]
+        },
+        {
+            code: "(class { x = function x() {}; })",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "notMatchProperty", data: { funcName: "x", name: "x" } }
+            ]
         }
     ]
 });
index bf930eecfc9562a0b90342ac2710bfaeb97c0050..3341df7f0166b2bee80bf2dfaff916cbf954f277 100644 (file)
@@ -262,6 +262,23 @@ ruleTester.run("func-names", rule, {
             code: "(function*() {}())",
             options: ["as-needed", { generators: "never" }],
             parserOptions: { ecmaVersion: 6 }
+        },
+
+        // class fields
+        {
+            code: "class C { foo = function() {}; }",
+            options: ["as-needed"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [foo] = function() {}; }",
+            options: ["as-needed"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #foo = function() {}; }",
+            options: ["as-needed"],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -805,6 +822,63 @@ ruleTester.run("func-names", rule, {
                 column: 15,
                 endColumn: 28
             }]
+        },
+
+        // class fields
+        {
+            code: "class C { foo = function() {} }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "unnamed",
+                data: { name: "method 'foo'" },
+                column: 11,
+                endColumn: 25
+            }]
+        },
+        {
+            code: "class C { [foo] = function() {} }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "unnamed",
+                data: { name: "method" },
+                column: 11,
+                endColumn: 27
+            }]
+        },
+        {
+            code: "class C { #foo = function() {} }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "unnamed",
+                data: { name: "private method #foo" },
+                column: 11,
+                endColumn: 26
+            }]
+        },
+        {
+            code: "class C { foo = bar(function() {}) }",
+            options: ["as-needed"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "unnamed",
+                data: { name: "function" },
+                column: 21,
+                endColumn: 29
+            }]
+        },
+        {
+            code: "class C { foo = function bar() {} }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "named",
+                data: { name: "method 'foo'" },
+                column: 11,
+                endColumn: 29
+            }]
         }
     ]
 });
index c4092d86d2d20a0d31b517a72df3d2af14e020a5..92032c066fbc339a470d95c5d3386de2b72fdbba 100644 (file)
@@ -16,7 +16,7 @@ const { RuleTester } = require("../../../lib/rule-tester");
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 const expectedError = { messageId: "expected", data: { name: "getter 'bar'" } };
 const expectedAlwaysError = { messageId: "expectedAlways", data: { name: "getter 'bar'" } };
 const options = [{ allowImplicit: true }];
@@ -72,7 +72,8 @@ ruleTester.run("getter-return", rule, {
         "var foo = { bar: function(){return;} };",
         "var foo = { bar: function(){return true;} };",
         "var foo = { get: function () {} }",
-        "var foo = { get: () => {}};"
+        "var foo = { get: () => {}};",
+        "class C { get; foo() {} }"
     ],
 
     invalid: [
@@ -187,7 +188,7 @@ ruleTester.run("getter-return", rule, {
             code: "Object.defineProperty(foo, 'bar', { get: function getfoo (){}});",
             errors: [{
                 messageId: "expected",
-                data: { name: "method 'getfoo'" },
+                data: { name: "method 'get'" },
                 line: 1,
                 column: 37,
                 endLine: 1,
@@ -209,11 +210,11 @@ ruleTester.run("getter-return", rule, {
             code: "Object.defineProperty(foo, 'bar', { get: () => {}});",
             errors: [{
                 messageId: "expected",
-                data: { name: "arrow function 'get'" },
+                data: { name: "method 'get'" },
                 line: 1,
-                column: 45,
+                column: 37,
                 endLine: 1,
-                endColumn: 47
+                endColumn: 42
             }]
         },
         { code: "Object.defineProperty(foo, \"bar\", { get: function (){if(bar) {return true;}}});", errors: [{ messageId: "expectedAlways" }] },
index 4993962bd05ad4d8adb29a446a76a0625729f6bb..df8337d41ecd7b311b7515fe1847956b6baa0f3c 100644 (file)
@@ -15,6 +15,7 @@ const rule = require("../../../lib/rules/global-require"),
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 const valid = [
index e94c3a46be2612a39136363e8779a1a2f033b946..75aa3a04ff680c031ed3d990db9f74fa2a0c0066 100644 (file)
@@ -16,7 +16,7 @@ const { RuleTester } = require("../../../lib/rule-tester");
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("grouped-accessor-pairs", rule, {
     valid: [
@@ -134,7 +134,19 @@ ruleTester.run("grouped-accessor-pairs", rule, {
         "({ get a(){}, b: 1, set a(foo){}, c: 2, get a(){} })",
         "({ set a(foo){}, b: 1, set 'a'(bar){}, c: 2, get a(){} })",
         "class A { get [a](){} b(){} get [a](){} c(){} set [a](foo){} }",
-        "(class { static set a(foo){} b(){} static get a(){} static c(){} static set a(bar){} })"
+        "(class { static set a(foo){} b(){} static get a(){} static c(){} static set a(bar){} })",
+
+        // public and private
+        "class A { get '#abc'(){} b(){} set #abc(foo){} }",
+        "class A { get #abc(){} b(){} set '#abc'(foo){} }",
+        {
+            code: "class A { set '#abc'(foo){} get #abc(){} }",
+            options: ["getBeforeSet"]
+        },
+        {
+            code: "class A { set #abc(foo){} get '#abc'(){} }",
+            options: ["getBeforeSet"]
+        }
     ],
 
     invalid: [
@@ -172,6 +184,14 @@ ruleTester.run("grouped-accessor-pairs", rule, {
             code: "class A { static get [a](){} b(){} static set [a](foo){} }",
             errors: [{ messageId: "notGrouped", data: { formerName: "static getter", latterName: "static setter" }, type: "MethodDefinition", column: 36 }]
         },
+        {
+            code: "class A { get '#abc'(){} b(){} set '#abc'(foo){} }",
+            errors: [{ messageId: "notGrouped", data: { formerName: "getter '#abc'", latterName: "setter '#abc'" }, type: "MethodDefinition", column: 32 }]
+        },
+        {
+            code: "class A { get #abc(){} b(){} set #abc(foo){} }",
+            errors: [{ messageId: "notGrouped", data: { formerName: "private getter #abc", latterName: "private setter #abc" }, type: "MethodDefinition", column: 30 }]
+        },
 
         // basic ordering tests with full messages
         {
@@ -214,6 +234,16 @@ ruleTester.run("grouped-accessor-pairs", rule, {
             options: ["getBeforeSet"],
             errors: [{ messageId: "invalidOrder", data: { formerName: "static setter", latterName: "static getter" }, type: "MethodDefinition", column: 35 }]
         },
+        {
+            code: "class A { set '#abc'(foo){} get '#abc'(){} }",
+            options: ["getBeforeSet"],
+            errors: [{ messageId: "invalidOrder", data: { latterName: "getter '#abc'", formerName: "setter '#abc'" }, type: "MethodDefinition", column: 29 }]
+        },
+        {
+            code: "class A { set #abc(foo){} get #abc(){} }",
+            options: ["getBeforeSet"],
+            errors: [{ messageId: "invalidOrder", data: { latterName: "private getter #abc", formerName: "private setter #abc" }, type: "MethodDefinition", column: 27 }]
+        },
 
         // ordering option does not affect the grouping check
         {
@@ -406,6 +436,11 @@ ruleTester.run("grouped-accessor-pairs", rule, {
             code: "class A { get a(){} a(){} set a(foo){} }",
             errors: [{ messageId: "notGrouped", data: { formerName: "getter 'a'", latterName: "setter 'a'" }, type: "MethodDefinition", column: 27 }]
         },
+        {
+            code: "class A { get a(){} a; set a(foo){} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "notGrouped", data: { formerName: "getter 'a'", latterName: "setter 'a'" }, type: "MethodDefinition", column: 24 }]
+        },
 
         // full location tests
         {
index 6da179d99ffcc360dd1f8ce39da7dea7238ab389..4e8248399e49794a3d5cda9b23e08c4e854e50ea 100644 (file)
@@ -204,6 +204,18 @@ ruleTester.run("id-denylist", rule, {
             code: "var foo = { bar: window.baz };",
             options: ["window"],
             env: { browser: true }
+        },
+
+        // Class fields
+        {
+            code: "class C { camelCase; #camelCase; #camelCase2() {} }",
+            options: ["foo"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { snake_case; #snake_case; #snake_case2() {} }",
+            options: ["foo"],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -1354,6 +1366,44 @@ ruleTester.run("id-denylist", rule, {
                     type: "Identifier"
                 }
             ]
+        },
+
+        // Class fields
+        {
+            code: "class C { camelCase; #camelCase; #camelCase2() {} }",
+            options: ["camelCase"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "restricted",
+                    data: { name: "camelCase" },
+                    type: "Identifier"
+                },
+                {
+                    messageId: "restrictedPrivate",
+                    data: { name: "camelCase" },
+                    type: "PrivateIdentifier"
+                }
+            ]
+
+        },
+        {
+            code: "class C { snake_case; #snake_case() {}; #snake_case2() {} }",
+            options: ["snake_case"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "restricted",
+                    data: { name: "snake_case" },
+                    type: "Identifier"
+                },
+                {
+                    messageId: "restrictedPrivate",
+                    data: { name: "snake_case" },
+                    type: "PrivateIdentifier"
+                }
+            ]
+
         }
     ]
 });
index 99a3957939c453cbf913fa416e3ad341255f1410..e9a023bcfd158862557c689add595a74065b8e8b 100644 (file)
@@ -18,7 +18,9 @@ const rule = require("../../../lib/rules/id-length"),
 
 const ruleTester = new RuleTester();
 const tooShortError = { messageId: "tooShort", type: "Identifier" };
+const tooShortErrorPrivate = { messageId: "tooShortPrivate", type: "PrivateIdentifier" };
 const tooLongError = { messageId: "tooLong", type: "Identifier" };
+const tooLongErrorPrivate = { messageId: "tooLongPrivate", type: "PrivateIdentifier" };
 
 ruleTester.run("id-length", rule, {
     valid: [
@@ -82,7 +84,36 @@ ruleTester.run("id-length", rule, {
         { code: "function BEFORE_send() {};", options: [{ min: 3, max: 5, exceptionPatterns: ["^BEFORE_", "send$"] }] },
         { code: "function BEFORE_send() {};", options: [{ min: 3, max: 5, exceptionPatterns: ["^BEFORE_", "^A", "^Z"] }] },
         { code: "function BEFORE_send() {};", options: [{ min: 3, max: 5, exceptionPatterns: ["^A", "^BEFORE_", "^Z"] }] },
-        { code: "var x = 1 ;", options: [{ min: 3, max: 5, exceptionPatterns: ["[x-z]"] }] }
+        { code: "var x = 1 ;", options: [{ min: 3, max: 5, exceptionPatterns: ["[x-z]"] }] },
+
+        // Class Fields
+        {
+            code: "class Foo { #xyz() {} }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class Foo { xyz = 1 }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class Foo { #xyz = 1 }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class Foo { #abc() {} }",
+            options: [{ max: 3 }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class Foo { abc = 1 }",
+            options: [{ max: 3 }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class Foo { #abc = 1 }",
+            options: [{ max: 3 }],
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
     invalid: [
         { code: "var x = 1;", errors: [tooShortError] },
@@ -486,6 +517,53 @@ ruleTester.run("id-length", rule, {
             errors: [
                 tooShortError
             ]
+        },
+
+        // Class Fields
+        {
+            code: "class Foo { #x() {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                tooShortErrorPrivate
+            ]
+        },
+        {
+            code: "class Foo { x = 1 }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                tooShortError
+            ]
+        },
+        {
+            code: "class Foo { #x = 1 }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                tooShortErrorPrivate
+            ]
+        },
+        {
+            code: "class Foo { #abcdefg() {} }",
+            options: [{ max: 3 }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                tooLongErrorPrivate
+            ]
+        },
+        {
+            code: "class Foo { abcdefg = 1 }",
+            options: [{ max: 3 }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                tooLongError
+            ]
+        },
+        {
+            code: "class Foo { #abcdefg = 1 }",
+            options: [{ max: 3 }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                tooLongErrorPrivate
+            ]
         }
     ]
 });
index f733dccbdff0d6ab06d3a374470a15269787ef5c..d1fad2ecb8b04d8725397baf925363b625092079 100644 (file)
@@ -183,7 +183,36 @@ ruleTester.run("id-match", rule, {
             options: ["^[^_]+$", {
                 properties: false
             }]
+        },
+
+        // Class Methods
+        {
+            code: "class x { foo() {} }",
+            options: ["^[^_]+$"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class x { #foo() {} }",
+            options: ["^[^_]+$"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+
+        // Class Fields
+        {
+            code: "class x { _foo = 1; }",
+            options: ["^[^_]+$", {
+                classFields: false
+            }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class x { #_foo = 1; }",
+            options: ["^[^_]+$", {
+                classFields: false
+            }],
+            parserOptions: { ecmaVersion: 2022 }
         }
+
     ],
     invalid: [
         {
@@ -610,6 +639,59 @@ ruleTester.run("id-match", rule, {
                     type: "Identifier"
                 }
             ]
+        },
+
+        // Class Methods
+        {
+            code: "class x { _foo() {} }",
+            options: ["^[^_]+$"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    message: "Identifier '_foo' does not match the pattern '^[^_]+$'.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "class x { #_foo() {} }",
+            options: ["^[^_]+$"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    message: "Identifier '#_foo' does not match the pattern '^[^_]+$'.",
+                    type: "PrivateIdentifier"
+                }
+            ]
+        },
+
+        // Class Fields
+        {
+            code: "class x { _foo = 1; }",
+            options: ["^[^_]+$", {
+                classFields: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    message: "Identifier '_foo' does not match the pattern '^[^_]+$'.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "class x { #_foo = 1; }",
+            options: ["^[^_]+$", {
+                classFields: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    message: "Identifier '#_foo' does not match the pattern '^[^_]+$'.",
+                    type: "PrivateIdentifier"
+                }
+            ]
         }
+
     ]
 });
index 02517ffaeeb1775d05a8161be9a21b85fddc8419..3947679142086ce202574862610b852056ae3c8f 100644 (file)
@@ -15,7 +15,7 @@ const fs = require("fs");
 const path = require("path");
 
 //------------------------------------------------------------------------------
-// Tests
+// Helpers
 //------------------------------------------------------------------------------
 
 const fixture = fs.readFileSync(path.join(__dirname, "../../fixtures/rules/indent/indent-invalid-fixture-1.js"), "utf8");
@@ -56,6 +56,10 @@ function expectedErrors(providedIndentType, providedErrors) {
     }));
 }
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 8, ecmaFeatures: { jsx: true } } });
 
 ruleTester.run("indent", rule, {
@@ -5843,6 +5847,241 @@ ruleTester.run("indent", rule, {
                                                         ar2){}
             `,
             options: [2, { FunctionDeclaration: { parameters: "first" }, FunctionExpression: { parameters: "first" } }]
+        },
+        {
+            code: unIndent`
+                class C {
+                  static {
+                    foo();
+                    bar();
+                  }
+                }
+            `,
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        foo();
+                        bar();
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                            foo();
+                            bar();
+                    }
+                }
+            `,
+            options: [4, { StaticBlock: { body: 2 } }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                    foo();
+                    bar();
+                    }
+                }
+            `,
+            options: [4, { StaticBlock: { body: 0 } }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                \tstatic {
+                \t\tfoo();
+                \t\tbar();
+                \t}
+                }
+            `,
+            options: ["tab"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                \tstatic {
+                \t\t\tfoo();
+                \t\t\tbar();
+                \t}
+                }
+            `,
+            options: ["tab", { StaticBlock: { body: 2 } }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                    {
+                        foo();
+                        bar();
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        var x,
+                            y;
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                    {
+                        var x,
+                            y;
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        if (foo) {
+                            bar;
+                        }
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        {
+                            bar;
+                        }
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {}
+
+                    static {
+                    }
+
+                    static
+                    {
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+
+                    static {
+                        foo;
+                    }
+
+                    static {
+                        bar;
+                    }
+
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+
+                    x = 1;
+
+                    static {
+                        foo;
+                    }
+
+                    y = 2;
+
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+
+                    method1(param) {
+                        foo;
+                    }
+
+                    static {
+                        bar;
+                    }
+
+                    method2(param) {
+                        foo;
+                    }
+
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                function f() {
+                    class C {
+                        static {
+                            foo();
+                            bar();
+                        }
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    method() {
+                            foo;
+                    }
+                    static {
+                            bar;
+                    }
+                }
+            `,
+            options: [4, { FunctionExpression: { body: 2 }, StaticBlock: { body: 2 } }],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
 
@@ -11627,6 +11866,763 @@ ruleTester.run("indent", rule, {
             errors: expectedErrors([
                 [2, 0, 1, "Identifier"]
             ])
+        },
+        {
+            code: unIndent`
+                class C {
+                field1;
+                static field2;
+                }
+            `,
+            output: unIndent`
+                class C {
+                    field1;
+                    static field2;
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Identifier"],
+                [3, 4, 0, "Keyword"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                field1
+                =
+                0
+                ;
+                static
+                field2
+                =
+                0
+                ;
+                }
+            `,
+            output: unIndent`
+                class C {
+                    field1
+                        =
+                            0
+                            ;
+                    static
+                        field2
+                            =
+                                0
+                                ;
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Identifier"],
+                [3, 8, 0, "Punctuator"],
+                [4, 12, 0, "Numeric"],
+                [5, 12, 0, "Punctuator"],
+                [6, 4, 0, "Keyword"],
+                [7, 8, 0, "Identifier"],
+                [8, 12, 0, "Punctuator"],
+                [9, 16, 0, "Numeric"],
+                [10, 16, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                [
+                field1
+                ]
+                =
+                0
+                ;
+                static
+                [
+                field2
+                ]
+                =
+                0
+                ;
+                [
+                field3
+                ] =
+                0;
+                [field4] =
+                0;
+                }
+            `,
+            output: unIndent`
+                class C {
+                    [
+                        field1
+                    ]
+                        =
+                            0
+                            ;
+                    static
+                    [
+                        field2
+                    ]
+                        =
+                            0
+                            ;
+                    [
+                        field3
+                    ] =
+                        0;
+                    [field4] =
+                        0;
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Punctuator"],
+                [3, 8, 0, "Identifier"],
+                [4, 4, 0, "Punctuator"],
+                [5, 8, 0, "Punctuator"],
+                [6, 12, 0, "Numeric"],
+                [7, 12, 0, "Punctuator"],
+                [8, 4, 0, "Keyword"],
+                [9, 4, 0, "Punctuator"],
+                [10, 8, 0, "Identifier"],
+                [11, 4, 0, "Punctuator"],
+                [12, 8, 0, "Punctuator"],
+                [13, 12, 0, "Numeric"],
+                [14, 12, 0, "Punctuator"],
+                [15, 4, 0, "Punctuator"],
+                [16, 8, 0, "Identifier"],
+                [17, 4, 0, "Punctuator"],
+                [18, 8, 0, "Numeric"],
+                [19, 4, 0, "Punctuator"],
+                [20, 8, 0, "Numeric"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                field1 = (
+                foo
+                + bar
+                );
+                }
+            `,
+            output: unIndent`
+                class C {
+                    field1 = (
+                        foo
+                + bar
+                    );
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Identifier"],
+                [3, 8, 0, "Identifier"],
+                [5, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                #aaa
+                foo() {
+                return this.#aaa
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    #aaa
+                    foo() {
+                        return this.#aaa
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "PrivateIdentifier"],
+                [3, 4, 0, "Identifier"],
+                [4, 8, 0, "Keyword"],
+                [5, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static {
+                foo();
+                bar();
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                  static {
+                    foo();
+                    bar();
+                  }
+                }
+            `,
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 2, 0, "Keyword"],
+                [3, 4, 0, "Identifier"],
+                [4, 4, 0, "Identifier"],
+                [5, 2, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static {
+                foo();
+                bar();
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                        foo();
+                        bar();
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Keyword"],
+                [3, 8, 0, "Identifier"],
+                [4, 8, 0, "Identifier"],
+                [5, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                        static {
+                    foo();
+                bar();
+                        }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                        foo();
+                        bar();
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 8, "Keyword"],
+                [3, 8, 4, "Identifier"],
+                [4, 8, 0, "Identifier"],
+                [5, 4, 8, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static {
+                foo();
+                bar();
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                            foo();
+                            bar();
+                    }
+                }
+            `,
+            options: [4, { StaticBlock: { body: 2 } }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Keyword"],
+                [3, 12, 0, "Identifier"],
+                [4, 12, 0, "Identifier"],
+                [5, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static {
+                foo();
+                bar();
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                    foo();
+                    bar();
+                    }
+                }
+            `,
+            options: [4, { StaticBlock: { body: 0 } }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Keyword"],
+                [3, 4, 0, "Identifier"],
+                [4, 4, 0, "Identifier"],
+                [5, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static {
+                foo();
+                bar();
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                \tstatic {
+                \t\tfoo();
+                \t\tbar();
+                \t}
+                }
+            `,
+            options: ["tab"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors("tab", [
+                [2, 1, 0, "Keyword"],
+                [3, 2, 0, "Identifier"],
+                [4, 2, 0, "Identifier"],
+                [5, 1, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static {
+                foo();
+                bar();
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                \tstatic {
+                \t\t\tfoo();
+                \t\t\tbar();
+                \t}
+                }
+            `,
+            options: ["tab", { StaticBlock: { body: 2 } }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors("tab", [
+                [2, 1, 0, "Keyword"],
+                [3, 3, 0, "Identifier"],
+                [4, 3, 0, "Identifier"],
+                [5, 1, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static
+                {
+                foo();
+                bar();
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static
+                    {
+                        foo();
+                        bar();
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Keyword"],
+                [3, 4, 0, "Punctuator"],
+                [4, 8, 0, "Identifier"],
+                [5, 8, 0, "Identifier"],
+                [6, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                        {
+                        foo();
+                        bar();
+                        }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static
+                    {
+                        foo();
+                        bar();
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [3, 4, 8, "Punctuator"],
+                [6, 4, 8, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static {
+                var x,
+                y;
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                        var x,
+                            y;
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Keyword"],
+                [3, 8, 0, "Keyword"],
+                [4, 12, 0, "Identifier"],
+                [5, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static
+                {
+                var x,
+                y;
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static
+                    {
+                        var x,
+                            y;
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Keyword"],
+                [3, 4, 0, "Punctuator"],
+                [4, 8, 0, "Keyword"],
+                [5, 12, 0, "Identifier"],
+                [6, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static {
+                if (foo) {
+                bar;
+                }
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                        if (foo) {
+                            bar;
+                        }
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Keyword"],
+                [3, 8, 0, "Keyword"],
+                [4, 12, 0, "Identifier"],
+                [5, 8, 0, "Punctuator"],
+                [6, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static {
+                {
+                bar;
+                }
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {
+                        {
+                            bar;
+                        }
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Keyword"],
+                [3, 8, 0, "Punctuator"],
+                [4, 12, 0, "Identifier"],
+                [5, 8, 0, "Punctuator"],
+                [6, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                static {}
+
+                static {
+                }
+
+                static
+                {
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    static {}
+
+                    static {
+                    }
+
+                    static
+                    {
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Keyword"],
+                [4, 4, 0, "Keyword"],
+                [5, 4, 0, "Punctuator"],
+                [7, 4, 0, "Keyword"],
+                [8, 4, 0, "Punctuator"],
+                [9, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+
+                static {
+                    foo;
+                }
+
+                static {
+                    bar;
+                }
+
+                }
+            `,
+            output: unIndent`
+                class C {
+
+                    static {
+                        foo;
+                    }
+
+                    static {
+                        bar;
+                    }
+
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [3, 4, 0, "Keyword"],
+                [4, 8, 4, "Identifier"],
+                [5, 4, 0, "Punctuator"],
+                [7, 4, 0, "Keyword"],
+                [8, 8, 4, "Identifier"],
+                [9, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+
+                x = 1;
+
+                static {
+                    foo;
+                }
+
+                y = 2;
+
+                }
+            `,
+            output: unIndent`
+                class C {
+
+                    x = 1;
+
+                    static {
+                        foo;
+                    }
+
+                    y = 2;
+
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [3, 4, 0, "Identifier"],
+                [5, 4, 0, "Keyword"],
+                [6, 8, 4, "Identifier"],
+                [7, 4, 0, "Punctuator"],
+                [9, 4, 0, "Identifier"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+
+                method1(param) {
+                    foo;
+                }
+
+                static {
+                    bar;
+                }
+
+                method2(param) {
+                    foo;
+                }
+
+                }
+            `,
+            output: unIndent`
+                class C {
+
+                    method1(param) {
+                        foo;
+                    }
+
+                    static {
+                        bar;
+                    }
+
+                    method2(param) {
+                        foo;
+                    }
+
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [3, 4, 0, "Identifier"],
+                [4, 8, 4, "Identifier"],
+                [5, 4, 0, "Punctuator"],
+                [7, 4, 0, "Keyword"],
+                [8, 8, 4, "Identifier"],
+                [9, 4, 0, "Punctuator"],
+                [11, 4, 0, "Identifier"],
+                [12, 8, 4, "Identifier"],
+                [13, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                function f() {
+                class C {
+                static {
+                foo();
+                bar();
+                }
+                }
+                }
+            `,
+            output: unIndent`
+                function f() {
+                    class C {
+                        static {
+                            foo();
+                            bar();
+                        }
+                    }
+                }
+            `,
+            options: [4],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Keyword"],
+                [3, 8, 0, "Keyword"],
+                [4, 12, 0, "Identifier"],
+                [5, 12, 0, "Identifier"],
+                [6, 8, 0, "Punctuator"],
+                [7, 4, 0, "Punctuator"]
+            ])
+        },
+        {
+            code: unIndent`
+                class C {
+                method() {
+                foo;
+                }
+                static {
+                bar;
+                }
+                }
+            `,
+            output: unIndent`
+                class C {
+                    method() {
+                            foo;
+                    }
+                    static {
+                            bar;
+                    }
+                }
+            `,
+            options: [4, { FunctionExpression: { body: 2 }, StaticBlock: { body: 2 } }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedErrors([
+                [2, 4, 0, "Identifier"],
+                [3, 12, 0, "Identifier"],
+                [4, 4, 0, "Punctuator"],
+                [5, 4, 0, "Keyword"],
+                [6, 12, 0, "Identifier"],
+                [7, 4, 0, "Punctuator"]
+            ])
         }
     ]
 });
index 6324418c17475e860dfeb713da7da049773977c7..556086abac7696b23e5d83d7fdb39993c7895006 100644 (file)
 const rule = require("../../../lib/rules/init-declarations"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("init-declarations", rule, {
index f4c72d9aeb7f5a6fc0e602961ba9605bafa87ab7..cdc1b043286851b79c7573640bb2a486fe8e3aa0 100644 (file)
 const rule = require("../../../lib/rules/jsx-quotes"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
 
 ruleTester.run("jsx-quotes", rule, {
index 849ce61994976e90b8e0574f43f3ee892637b7bc..fde9673a63bdbb035e40f8fa41579e8ded003a2f 100644 (file)
 const rule = require("../../../lib/rules/key-spacing"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("key-spacing", rule, {
index 64ee5a434742edd0837d4e8619785776b9b7ca31..35c8a06e43e6590667a29b0d9f6d0ee5fbade5da 100644 (file)
@@ -11,7 +11,8 @@
 
 const parser = require("../../fixtures/fixture-parser"),
     rule = require("../../../lib/rules/keyword-spacing"),
-    { RuleTester } = require("../../../lib/rule-tester");
+    { RuleTester } = require("../../../lib/rule-tester"),
+    fixtureParser = require("../../fixtures/fixture-parser");
 
 //------------------------------------------------------------------------------
 // Helpers
@@ -259,6 +260,12 @@ ruleTester.run("keyword-spacing", rule, {
         // not conflict with `space-infix-ops`
         { code: "async function wrap() { a =await a }", parserOptions: { ecmaVersion: 8 } },
         { code: "async function wrap() { a = await a }", options: [NEITHER], parserOptions: { ecmaVersion: 8 } },
+        { code: "async function wrap() { a+await a }", parserOptions: { ecmaVersion: 8 } },
+        { code: "async function wrap() { a + await a }", options: [NEITHER], parserOptions: { ecmaVersion: 8 } },
+        { code: "async function wrap() { a<await a }", parserOptions: { ecmaVersion: 8 } },
+        { code: "async function wrap() { a < await a }", options: [NEITHER], parserOptions: { ecmaVersion: 8 } },
+        { code: "async function wrap() { a>await a }", parserOptions: { ecmaVersion: 8 } },
+        { code: "async function wrap() { a > await a }", options: [NEITHER], parserOptions: { ecmaVersion: 8 } },
 
         // not conflict with `space-unary-ops`
         { code: "async function wrap() { !await'a' }", parserOptions: { ecmaVersion: 8 } },
@@ -366,6 +373,12 @@ ruleTester.run("keyword-spacing", rule, {
         // not conflict with `space-infix-ops`
         { code: "a =class {}", parserOptions: { ecmaVersion: 6 } },
         { code: "a = class{}", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
+        { code: "a+class {}", parserOptions: { ecmaVersion: 6 } },
+        { code: "a + class{}", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
+        { code: "a<class {}", parserOptions: { ecmaVersion: 6 } },
+        { code: "a < class{}", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
+        { code: "a>class {}", parserOptions: { ecmaVersion: 6 } },
+        { code: "a > class{}", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
 
         // not conflict with `space-unary-ops`
         { code: "!class {}", parserOptions: { ecmaVersion: 6 } },
@@ -379,6 +392,10 @@ ruleTester.run("keyword-spacing", rule, {
         { code: "<Foo onClick={class {}} />", parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } },
         { code: "<Foo onClick={ class{}} />", options: [NEITHER], parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } },
 
+        // private names
+        { code: "class C {\n#x;\nfoo() {\nfor (this.#x of bar){}}}", options: [{ before: false }], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C {\n#x;\nfoo() {\nfor (this.#x in bar){}}}", options: [{ before: false }], parserOptions: { ecmaVersion: 2022 } },
+
         //----------------------------------------------------------------------
         // const
         //----------------------------------------------------------------------
@@ -497,6 +514,12 @@ ruleTester.run("keyword-spacing", rule, {
         // not conflict with `space-infix-ops`
         "a =delete foo.a",
         { code: "a = delete foo.a", options: [NEITHER] },
+        "a+delete foo.a",
+        { code: "a + delete foo.a", options: [NEITHER] },
+        "a<delete foo.a",
+        { code: "a < delete foo.a", options: [NEITHER] },
+        "a>delete foo.a",
+        { code: "a > delete foo.a", options: [NEITHER] },
 
         // not conflict with `space-unary-ops`
         "!delete(foo.a)",
@@ -694,6 +717,12 @@ ruleTester.run("keyword-spacing", rule, {
         // not conflict with `space-infix-ops`
         "a =function() {}",
         { code: "a = function() {}", options: [NEITHER] },
+        "a+function() {}",
+        { code: "a + function() {}", options: [NEITHER] },
+        "a<function() {}",
+        { code: "a < function() {}", options: [NEITHER] },
+        "a>function() {}",
+        { code: "a > function() {}", options: [NEITHER] },
 
         // not conflict with `space-unary-ops`
         "!function() {}",
@@ -721,11 +750,17 @@ ruleTester.run("keyword-spacing", rule, {
         { code: "class A { a() {} get [b]() {} }", options: [override("get", BOTH)], parserOptions: { ecmaVersion: 6 } },
         { code: "({ get[b]() {} })", options: [override("get", NEITHER)], parserOptions: { ecmaVersion: 6 } },
         { code: "class A { a() {}get[b]() {} }", options: [override("get", NEITHER)], parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { a; get #b() {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a;get#b() {} }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
 
         // not conflict with `comma-spacing`
         { code: "({ a,get [b]() {} })", parserOptions: { ecmaVersion: 6 } },
         { code: "({ a, get[b]() {} })", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
 
+        // not conflict with `semi-spacing`
+        { code: "class A { ;get #b() {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { ; get#b() {} }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
+
         //----------------------------------------------------------------------
         // if
         //----------------------------------------------------------------------
@@ -850,6 +885,12 @@ ruleTester.run("keyword-spacing", rule, {
         // not conflict with `space-infix-ops`
         "a =new foo()",
         { code: "a = new foo()", options: [NEITHER] },
+        "a+new foo()",
+        { code: "a + new foo()", options: [NEITHER] },
+        "a<new foo()",
+        { code: "a < new foo()", options: [NEITHER] },
+        "a>new foo()",
+        { code: "a > new foo()", options: [NEITHER] },
 
         // not conflict with `space-unary-ops`
         "!new(foo)()",
@@ -878,7 +919,16 @@ ruleTester.run("keyword-spacing", rule, {
         //----------------------------------------------------------------------
 
         "function foo() { {} return +a }",
+        {
+            code: "function foo() { return <p/>; }",
+            parserOptions: { ecmaFeatures: { jsx: true } }
+        },
         { code: "function foo() { {}return+a }", options: [NEITHER] },
+        {
+            code: "function foo() { return<p/>; }",
+            options: [{ after: false }],
+            parserOptions: { ecmaFeatures: { jsx: true } }
+        },
         { code: "function foo() { {} return +a }", options: [override("return", BOTH)] },
         { code: "function foo() { {}return+a }", options: [override("return", NEITHER)] },
         "function foo() {\nreturn\n}",
@@ -905,11 +955,17 @@ ruleTester.run("keyword-spacing", rule, {
         { code: "class A { a() {} set [b](value) {} }", options: [override("set", BOTH)], parserOptions: { ecmaVersion: 6 } },
         { code: "({ set[b](value) {} })", options: [override("set", NEITHER)], parserOptions: { ecmaVersion: 6 } },
         { code: "class A { a() {}set[b](value) {} }", options: [override("set", NEITHER)], parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { a; set #b(value) {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a;set#b(value) {} }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
 
         // not conflict with `comma-spacing`
         { code: "({ a,set [b](value) {} })", parserOptions: { ecmaVersion: 6 } },
         { code: "({ a, set[b](value) {} })", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
 
+        // not conflict with `semi-spacing`
+        { code: "class A { ;set #b(value) {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { ; set#b(value) {} }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
+
         //----------------------------------------------------------------------
         // static
         //----------------------------------------------------------------------
@@ -918,6 +974,15 @@ ruleTester.run("keyword-spacing", rule, {
         { code: "class A { a() {}static[b]() {} }", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
         { code: "class A { a() {} static [b]() {} }", options: [override("static", BOTH)], parserOptions: { ecmaVersion: 6 } },
         { code: "class A { a() {}static[b]() {} }", options: [override("static", NEITHER)], parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { a; static [b]; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a;static[b]; }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a; static #b; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a;static#b; }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a() {} static {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a() {}static{} }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a() {} static {} }", options: [override("static", BOTH)], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a() {}static{} }", options: [override("static", NEITHER)], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a() {}\nstatic\n{} }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
 
         // not conflict with `generator-star-spacing`
         { code: "class A { static* [a]() {} }", parserOptions: { ecmaVersion: 6 } },
@@ -926,6 +991,10 @@ ruleTester.run("keyword-spacing", rule, {
         // not conflict with `semi-spacing`
         { code: "class A { ;static a() {} }", parserOptions: { ecmaVersion: 6 } },
         { code: "class A { ; static a() {} }", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { ;static a; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { ; static a ; }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { ;static {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { ; static{} }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
 
         //----------------------------------------------------------------------
         // super
@@ -975,6 +1044,12 @@ ruleTester.run("keyword-spacing", rule, {
         // not conflict with `space-infix-ops`
         { code: "class A extends B { constructor() { b =super() } }", parserOptions: { ecmaVersion: 6 } },
         { code: "class A extends B { constructor() { b = super() } }", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
+        { code: "class A extends B { constructor() { b+super() } }", parserOptions: { ecmaVersion: 6 } },
+        { code: "class A extends B { constructor() { b + super() } }", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
+        { code: "class A extends B { constructor() { b<super() } }", parserOptions: { ecmaVersion: 6 } },
+        { code: "class A extends B { constructor() { b < super() } }", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
+        { code: "class A extends B { constructor() { b>super() } }", parserOptions: { ecmaVersion: 6 } },
+        { code: "class A extends B { constructor() { b > super() } }", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
 
         // not conflict with `space-unary-ops`
         { code: "class A extends B { constructor() { !super() } }", parserOptions: { ecmaVersion: 6 } },
@@ -1014,6 +1089,16 @@ ruleTester.run("keyword-spacing", rule, {
         { code: "{} this[a]", options: [override("this", BOTH)] },
         { code: "{}this[a]", options: [override("this", NEITHER)] },
 
+        {
+            code: "<Thing> this.blah",
+            parser: fixtureParser("keyword-spacing", "prefix-cast-operator-space")
+        },
+        {
+            code: "<Thing>this.blah",
+            options: [override("this", { before: false })],
+            parser: fixtureParser("keyword-spacing", "prefix-cast-operator-no-space")
+        },
+
         // not conflict with `array-bracket-spacing`
         "[this]",
         { code: "[ this ]", options: [NEITHER] },
@@ -1051,6 +1136,18 @@ ruleTester.run("keyword-spacing", rule, {
         // not conflict with `space-infix-ops`
         "a =this",
         { code: "a = this", options: [NEITHER] },
+        "a+this",
+        { code: "a + this", options: [NEITHER] },
+        "a<this",
+        { code: "a < this", options: [NEITHER] },
+        "a>this",
+        { code: "a > this", options: [NEITHER] },
+        "this+a",
+        { code: "this + a", options: [NEITHER] },
+        "this<a",
+        { code: "this < a", options: [NEITHER] },
+        "this>a",
+        { code: "this > a", options: [NEITHER] },
 
         // not conflict with `space-unary-ops`
         "!this",
@@ -1146,6 +1243,12 @@ ruleTester.run("keyword-spacing", rule, {
         // not conflict with `space-infix-ops`
         "a =typeof foo",
         { code: "a = typeof foo", options: [NEITHER] },
+        "a+typeof foo",
+        { code: "a + typeof foo", options: [NEITHER] },
+        "a<typeof foo",
+        { code: "a < typeof foo", options: [NEITHER] },
+        "a>typeof foo",
+        { code: "a > typeof foo", options: [NEITHER] },
 
         // not conflict with `space-unary-ops`
         "!typeof+foo",
@@ -1223,6 +1326,12 @@ ruleTester.run("keyword-spacing", rule, {
         // not conflict with `space-infix-ops`
         "a =void foo",
         { code: "a = void foo", options: [NEITHER] },
+        "a+void foo",
+        { code: "a + void foo", options: [NEITHER] },
+        "a<void foo",
+        { code: "a < void foo", options: [NEITHER] },
+        "a>void foo",
+        { code: "a > void foo", options: [NEITHER] },
 
         // not conflict with `space-unary-ops`
         "!void+foo",
@@ -2453,6 +2562,19 @@ ruleTester.run("keyword-spacing", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: unexpectedBeforeAndAfter("get")
         },
+        {
+            code: "class A { a;get#b() {} }",
+            output: "class A { a;get #b() {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedAfter("get")
+        },
+        {
+            code: "class A { a; get #b() {} }",
+            output: "class A { a; get#b() {} }",
+            options: [NEITHER],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedAfter("get")
+        },
 
         //----------------------------------------------------------------------
         // if
@@ -2750,12 +2872,25 @@ ruleTester.run("keyword-spacing", rule, {
             output: "function foo() { {} return +a }",
             errors: expectedBeforeAndAfter("return")
         },
+        {
+            code: "function foo() { return<p/>; }",
+            output: "function foo() { return <p/>; }",
+            parserOptions: { ecmaFeatures: { jsx: true } },
+            errors: expectedAfter("return")
+        },
         {
             code: "function foo() { {} return +a }",
             output: "function foo() { {}return+a }",
             options: [NEITHER],
             errors: unexpectedBeforeAndAfter("return")
         },
+        {
+            code: "function foo() { return <p/>; }",
+            output: "function foo() { return<p/>; }",
+            options: [{ after: false }],
+            parserOptions: { ecmaFeatures: { jsx: true } },
+            errors: unexpectedAfter("return")
+        },
         {
             code: "function foo() { {}return+a }",
             output: "function foo() { {} return +a }",
@@ -2833,6 +2968,19 @@ ruleTester.run("keyword-spacing", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: unexpectedBeforeAndAfter("set")
         },
+        {
+            code: "class A { a;set#b(x) {} }",
+            output: "class A { a;set #b(x) {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedAfter("set")
+        },
+        {
+            code: "class A { a; set #b(x) {} }",
+            output: "class A { a; set#b(x) {} }",
+            options: [NEITHER],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedAfter("set")
+        },
 
         //----------------------------------------------------------------------
         // static
@@ -2878,6 +3026,85 @@ ruleTester.run("keyword-spacing", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: unexpectedBeforeAndAfter("static")
         },
+        {
+            code: "class A { a;static[b]; }",
+            output: "class A { a;static [b]; }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedAfter("static")
+        },
+        {
+            code: "class A { a; static [b]; }",
+            output: "class A { a; static[b]; }",
+            options: [NEITHER],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedAfter("static")
+        },
+        {
+            code: "class A { a;static#b; }",
+            output: "class A { a;static #b; }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedAfter("static")
+        },
+        {
+            code: "class A { a; static #b; }",
+            output: "class A { a; static#b; }",
+            options: [NEITHER],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedAfter("static")
+        },
+        {
+            code: "class A { a() {}static{} }",
+            output: "class A { a() {} static {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedBeforeAndAfter("static")
+        },
+        {
+            code: "class A { a() {}static{} }",
+            output: "class A { a() {} static {} }",
+            options: [override("static", BOTH)],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedBeforeAndAfter("static")
+        },
+        {
+            code: "class A {  a() {}static {} }",
+            output: "class A {  a() {} static {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedBefore("static")
+        },
+        {
+            code: "class A {  a() {} static{} }",
+            output: "class A {  a() {} static {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedAfter("static")
+        },
+        {
+            code: "class A { a() {} static {} }",
+            output: "class A { a() {}static{} }",
+            options: [NEITHER],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedBeforeAndAfter("static")
+        },
+        {
+            code: "class A { a() {} static {} }",
+            output: "class A { a() {}static{} }",
+            options: [override("static", NEITHER)],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedBeforeAndAfter("static")
+        },
+        {
+            code: "class A { a() {} static{} }",
+            output: "class A { a() {}static{} }",
+            options: [NEITHER],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedBefore("static")
+        },
+        {
+            code: "class A { a() {}static {} }",
+            output: "class A { a() {}static{} }",
+            options: [NEITHER],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedAfter("static")
+        },
 
         //----------------------------------------------------------------------
         // super
@@ -2966,6 +3193,19 @@ ruleTester.run("keyword-spacing", rule, {
             options: [override("this", NEITHER)],
             errors: unexpectedBefore("this")
         },
+        {
+            code: "<Thing> this.blah",
+            output: "<Thing>this.blah",
+            options: [override("this", { before: false })],
+            parser: fixtureParser("keyword-spacing", "prefix-cast-operator-space"),
+            errors: unexpectedBefore("this")
+        },
+        {
+            code: "<Thing>this.blah",
+            output: "<Thing> this.blah",
+            parser: fixtureParser("keyword-spacing", "prefix-cast-operator-no-space"),
+            errors: expectedBefore("this")
+        },
 
         //----------------------------------------------------------------------
         // throw
index a4fd3dd70e286434d368d2d8b76961e346182704..2687f1458aa3d9f79b20d602d858345b6090c51c 100644 (file)
@@ -9,7 +9,8 @@
 //------------------------------------------------------------------------------
 
 const rule = require("../../../lib/rules/lines-around-comment"),
-    { RuleTester } = require("../../../lib/rule-tester");
+    { RuleTester } = require("../../../lib/rule-tester"),
+    { unIndent } = require("../../_utils");
 
 //------------------------------------------------------------------------------
 // Tests
@@ -262,6 +263,106 @@ ruleTester.run("lines-around-comment", rule, {
                 allowBlockStart: true
             }]
         },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        // line comment
+                    }
+
+                    static {
+                        // line comment
+                        foo();
+                    }
+                }`,
+            options: [{
+                beforeLineComment: true,
+                allowBlockStart: true
+            }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                    {
+                        // line comment
+                    }
+
+                    static
+                    {
+                        // line comment
+                        foo();
+                    }
+                }`,
+            options: [{
+                beforeLineComment: true,
+                allowBlockStart: true
+            }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        /* block comment */
+                    }
+
+                    static {
+                        /* block
+                           comment */
+                    }
+
+                    static {
+                        /* block comment */
+                        foo();
+                    }
+
+                    static {
+                        /* block
+                           comment */
+                        foo();
+                    }
+                }`,
+            options: [{
+                beforeBlockComment: true,
+                allowBlockStart: true
+            }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                    {
+                        /* block comment */
+                    }
+
+                    static
+                    {
+                        /* block
+                        comment */
+                    }
+
+                    static
+                    {
+                        /* block comment */
+                        foo();
+                    }
+
+                    static
+                    {
+                        /* block
+                        comment */
+                        foo();
+                    }
+                }`,
+            options: [{
+                beforeBlockComment: true,
+                allowBlockStart: true
+            }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
 
         // check for block end comments
         {
@@ -472,6 +573,54 @@ ruleTester.run("lines-around-comment", rule, {
                 allowBlockEnd: true
             }]
         },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        // line comment
+                    }
+
+                    static {
+                        foo();
+                        // line comment
+                    }
+                }`,
+            options: [{
+                afterLineComment: true,
+                allowBlockEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        /* block comment */
+                    }
+
+                    static {
+                        /* block
+                           comment */
+                    }
+
+                    static {
+                        foo();
+                        /* block comment */
+                    }
+
+                    static {
+                        foo();
+                        /* block
+                           comment */
+                    }
+                }`,
+            options: [{
+                beforeBlockComment: false, // default is `true`
+                afterBlockComment: true,
+                allowBlockEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
 
         // check for object start comments
         {
@@ -1049,6 +1198,346 @@ ruleTester.run("lines-around-comment", rule, {
             }],
             errors: [{ messageId: "after", type: "Line", line: 8 }]
         },
+        {
+            code: unIndent`
+                class C {
+                    // line comment
+                    static{}
+                }`,
+            output: unIndent`
+                class C {
+                    // line comment
+
+                    static{}
+                }`,
+            options: [{
+                beforeLineComment: true,
+                afterLineComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true,
+                allowClassStart: true,
+                allowClassEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "after", type: "Line", line: 2 }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    /* block
+                       comment */
+                    static{}
+                }`,
+            output: unIndent`
+                class C {
+                    /* block
+                       comment */
+
+                    static{}
+                }`,
+            options: [{
+                beforeBlockComment: true,
+                afterBlockComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true,
+                allowClassStart: true,
+                allowClassEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "after", type: "Block", line: 2 }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                    // line comment
+                    {}
+                }`,
+            output: unIndent`
+            class C {
+                static
+
+                // line comment
+
+                {}
+            }`,
+            options: [{
+                beforeLineComment: true,
+                afterLineComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true,
+                allowClassStart: true,
+                allowClassEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "before", type: "Line", line: 3 },
+                { messageId: "after", type: "Line", line: 3 }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static
+                    /* block
+                       comment */
+                    {}
+                }`,
+            output: unIndent`
+            class C {
+                static
+
+                /* block
+                   comment */
+
+                {}
+            }`,
+            options: [{
+                beforeBlockComment: true,
+                afterBlockComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true,
+                allowClassStart: true,
+                allowClassEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "before", type: "Block", line: 3 },
+                { messageId: "after", type: "Block", line: 3 }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        // line comment
+                        foo();
+                    }
+                }`,
+            output: unIndent`
+                class C {
+                    static {
+                        // line comment
+
+                        foo();
+                    }
+                }`,
+            options: [{
+                beforeLineComment: true,
+                afterLineComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "after", type: "Line", line: 3 }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        /* block
+                           comment */
+                        foo();
+                    }
+                }`,
+            output: unIndent`
+                class C {
+                    static {
+                        /* block
+                           comment */
+
+                        foo();
+                    }
+                }`,
+            options: [{
+                beforeBlockComment: true,
+                afterBlockComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "after", type: "Block", line: 3 }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        foo();
+                        // line comment
+                    }
+                }`,
+            output: unIndent`
+                class C {
+                    static {
+                        foo();
+
+                        // line comment
+                    }
+                }`,
+            options: [{
+                beforeLineComment: true,
+                afterLineComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "before", type: "Line", line: 4 }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        foo();
+                        /* block
+                           comment */
+                    }
+                }`,
+            output: unIndent`
+                class C {
+                    static {
+                        foo();
+
+                        /* block
+                           comment */
+                    }
+                }`,
+            options: [{
+                beforeBlockComment: true,
+                afterBlockComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "before", type: "Block", line: 4 }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        foo();
+                        // line comment
+                        bar();
+                    }
+                }`,
+            output: unIndent`
+                class C {
+                    static {
+                        foo();
+
+                        // line comment
+
+                        bar();
+                    }
+                }`,
+            options: [{
+                beforeLineComment: true,
+                afterLineComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "before", type: "Line", line: 4 },
+                { messageId: "after", type: "Line", line: 4 }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static {
+                        foo();
+                        /* block
+                           comment */
+                        bar();
+                    }
+                }`,
+            output: unIndent`
+                class C {
+                    static {
+                        foo();
+
+                        /* block
+                           comment */
+
+                        bar();
+                    }
+                }`,
+            options: [{
+                beforeBlockComment: true,
+                afterBlockComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "before", type: "Block", line: 4 },
+                { messageId: "after", type: "Block", line: 4 }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static{}
+                    // line comment
+                }`,
+            output: unIndent`
+                class C {
+                    static{}
+
+                    // line comment
+                }`,
+            options: [{
+                beforeLineComment: true,
+                afterLineComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true,
+                allowClassStart: true,
+                allowClassEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "before", type: "Line", line: 3 }
+            ]
+        },
+        {
+            code: unIndent`
+                class C {
+                    static{}
+                    /* block
+                       comment */
+                }`,
+            output: unIndent`
+                class C {
+                    static{}
+
+                    /* block
+                       comment */
+                }`,
+            options: [{
+                beforeBlockComment: true,
+                afterBlockComment: true,
+                allowBlockStart: true,
+                allowBlockEnd: true,
+                allowClassStart: true,
+                allowClassEnd: true
+            }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "before", type: "Block", line: 3 }
+            ]
+        },
 
         // object start comments
         {
index e4b1c0c092ff10b871cccc712a333b3d283b120d..feb9c085e37eca64b0519e81b7b566b764067ed1 100644 (file)
@@ -15,6 +15,7 @@ const { RuleTester } = require("../../../lib/rule-tester");
 //------------------------------------------------------------------------------
 // Helpers
 //------------------------------------------------------------------------------
+
 const alwaysError = { messageId: "always" };
 const neverError = { messageId: "never" };
 
@@ -22,7 +23,7 @@ const neverError = { messageId: "never" };
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("lines-between-class-members", rule, {
     valid: [
@@ -46,6 +47,8 @@ ruleTester.run("lines-between-class-members", rule, {
         "class foo{ bar(){}\n\n;;baz(){}}",
         "class foo{ bar(){};\n\nbaz(){}}",
 
+        "class C {\naaa;\n\n#bbb;\n\nccc(){}\n\n#ddd(){}\n}",
+
         { code: "class foo{ bar(){}\nbaz(){}}", options: ["never"] },
         { code: "class foo{ bar(){}\n/*comments*/baz(){}}", options: ["never"] },
         { code: "class foo{ bar(){}\n//comments\nbaz(){}}", options: ["never"] },
@@ -58,7 +61,13 @@ ruleTester.run("lines-between-class-members", rule, {
         { code: "class foo{ bar(){}\n\n//comments\nbaz(){}}", options: ["always"] },
 
         { code: "class foo{ bar(){}\nbaz(){}}", options: ["always", { exceptAfterSingleLine: true }] },
-        { code: "class foo{ bar(){\n}\n\nbaz(){}}", options: ["always", { exceptAfterSingleLine: true }] }
+        { code: "class foo{ bar(){\n}\n\nbaz(){}}", options: ["always", { exceptAfterSingleLine: true }] },
+        { code: "class foo{\naaa;\n#bbb;\nccc(){\n}\n\n#ddd(){\n}\n}", options: ["always", { exceptAfterSingleLine: true }] },
+
+        // semicolon-less style (semicolons are at the beginning of lines)
+        { code: "class C { foo\n\n;bar }", options: ["always"] },
+        { code: "class C { foo\n;bar }", options: ["always", { exceptAfterSingleLine: true }] },
+        { code: "class C { foo\n;bar }", options: ["never"] }
     ],
     invalid: [
         {
@@ -141,6 +150,64 @@ ruleTester.run("lines-between-class-members", rule, {
             output: "class A {\nfoo() {}\n\n/* comment */;\n;\nbar() {}\n}",
             options: ["always"],
             errors: [alwaysError]
+        }, {
+            code: "class C {\nfield1\nfield2\n}",
+            output: "class C {\nfield1\n\nfield2\n}",
+            options: ["always"],
+            errors: [alwaysError]
+        }, {
+            code: "class C {\n#field1\n#field2\n}",
+            output: "class C {\n#field1\n\n#field2\n}",
+            options: ["always"],
+            errors: [alwaysError]
+        }, {
+            code: "class C {\nfield1\n\nfield2\n}",
+            output: "class C {\nfield1\nfield2\n}",
+            options: ["never"],
+            errors: [neverError]
+        }, {
+            code: "class C {\nfield1 = () => {\n}\nfield2\nfield3\n}",
+            output: "class C {\nfield1 = () => {\n}\n\nfield2\nfield3\n}",
+            options: ["always", { exceptAfterSingleLine: true }],
+            errors: [alwaysError]
+        },
+        {
+            code: "class C { foo;bar }",
+            output: "class C { foo;\nbar }",
+            options: ["always"],
+            errors: [alwaysError]
+        },
+        {
+            code: "class C { foo;\nbar; }",
+            output: "class C { foo;\n\nbar; }",
+            options: ["always"],
+            errors: [alwaysError]
+        },
+        {
+            code: "class C { foo;\n;bar }",
+            output: "class C { foo;\n\n;bar }",
+            options: ["always"],
+            errors: [alwaysError]
+        },
+
+        // semicolon-less style (semicolons are at the beginning of lines)
+        {
+            code: "class C { foo\n;bar }",
+            output: "class C { foo\n\n;bar }",
+            options: ["always"],
+            errors: [alwaysError]
+        },
+        {
+            code: "class C { foo\n\n;bar }",
+            output: "class C { foo\n;bar }",
+            options: ["never"],
+            errors: [neverError]
+        },
+        {
+            code: "class C { foo\n;;bar }",
+            output: "class C { foo\n\n;;bar }",
+            options: ["always"],
+            errors: [alwaysError]
         }
     ]
 });
index ca9cba7a4a254240caf0a7e9b0f7e359b943934f..b5e87261e720220aa912d634ab1a1e21fa62845d 100644 (file)
@@ -29,6 +29,29 @@ ruleTester.run("max-classes-per-file", rule, {
         {
             code: "class Foo {}\nclass Bar {}",
             options: [2]
+        },
+        {
+            code: "class Foo {}",
+            options: [{ max: 1 }]
+        },
+        {
+            code: "class Foo {}\nclass Bar {}",
+            options: [{ max: 2 }]
+        },
+        {
+            code: `
+                class Foo {}
+                const myExpression = class {}
+            `,
+            options: [{ ignoreExpressions: true, max: 1 }]
+        },
+        {
+            code: `
+                class Foo {}
+                class Bar {}
+                const myExpression = class {}
+            `,
+            options: [{ ignoreExpressions: true, max: 2 }]
         }
     ],
 
@@ -37,6 +60,10 @@ ruleTester.run("max-classes-per-file", rule, {
             code: "class Foo {}\nclass Bar {}",
             errors: [{ messageId: "maximumExceeded", type: "Program" }]
         },
+        {
+            code: "class Foo {}\nconst myExpression = class {}",
+            errors: [{ messageId: "maximumExceeded", type: "Program" }]
+        },
         {
             code: "var x = class {};\nvar y = class {};",
             errors: [{ messageId: "maximumExceeded", type: "Program" }]
@@ -54,6 +81,25 @@ ruleTester.run("max-classes-per-file", rule, {
             code: "class Foo {} class Bar {} class Baz {}",
             options: [2],
             errors: [{ messageId: "maximumExceeded", type: "Program" }]
+        },
+        {
+            code: `
+                class Foo {}
+                class Bar {}
+                const myExpression = class {}
+            `,
+            options: [{ ignoreExpressions: true, max: 1 }],
+            errors: [{ messageId: "maximumExceeded", type: "Program" }]
+        },
+        {
+            code: `
+                class Foo {}
+                class Bar {}
+                class Baz {}
+                const myExpression = class {}
+            `,
+            options: [{ ignoreExpressions: true, max: 2 }],
+            errors: [{ messageId: "maximumExceeded", type: "Program" }]
         }
     ]
 });
index 0b4f846dd49bbcb74bcd6e1cd411ef23780d31e0..b35b60301c5b20ece7981ada38bade04f77893b2 100644 (file)
@@ -26,7 +26,18 @@ ruleTester.run("max-depth", rule, {
         "function foo() { if (true) { if (false) { if (true) { } } } }",
 
         // object property options
-        { code: "function foo() { if (true) { if (false) { if (true) { } } } }", options: [{ max: 3 }] }
+        { code: "function foo() { if (true) { if (false) { if (true) { } } } }", options: [{ max: 3 }] },
+
+        { code: "class C { static { if (1) { if (2) {} } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { if (1) { if (2) {} } if (1) { if (2) {} } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { if (1) { if (2) {} } } static { if (1) { if (2) {} } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "if (1) { class C { static { if (1) { if (2) {} } } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "function foo() { if (1) { class C { static { if (1) { if (2) {} } } } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        {
+            code: "function foo() { if (1) { if (2) { class C { static { if (1) { if (2) {} } if (1) { if (2) {} } } } } } if (1) { if (2) {} } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
     invalid: [
         { code: "function foo() { if (true) { if (false) { if (true) { } } } }", options: [2], errors: [{ messageId: "tooDeeply", data: { depth: 3, maxDepth: 2 }, type: "IfStatement" }] },
@@ -41,6 +52,51 @@ ruleTester.run("max-depth", rule, {
         { code: "function foo() { if (true) { if (false) { if (true) { } } } }", options: [{ max: 2 }], errors: [{ messageId: "tooDeeply", data: { depth: 3, maxDepth: 2 }, type: "IfStatement" }] },
 
         { code: "function foo() { if (a) { if (b) { if (c) { if (d) { if (e) {} } } } } }", options: [{}], errors: [{ messageId: "tooDeeply", data: { depth: 5, maxDepth: 4 } }] },
-        { code: "function foo() { if (true) {} }", options: [{ max: 0 }], errors: [{ messageId: "tooDeeply", data: { depth: 1, maxDepth: 0 } }] }
+        { code: "function foo() { if (true) {} }", options: [{ max: 0 }], errors: [{ messageId: "tooDeeply", data: { depth: 1, maxDepth: 0 } }] },
+
+        {
+            code: "class C { static { if (1) { if (2) { if (3) {} } } } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "tooDeeply",
+                data: { depth: 3, maxDepth: 2 },
+                line: 1,
+                column: 38
+            }]
+        },
+        {
+            code: "if (1) { class C { static { if (1) { if (2) { if (3) {} } } } } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "tooDeeply",
+                data: { depth: 3, maxDepth: 2 },
+                line: 1,
+                column: 47
+            }]
+        },
+        {
+            code: "function foo() { if (1) { class C { static { if (1) { if (2) { if (3) {} } } } } } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "tooDeeply",
+                data: { depth: 3, maxDepth: 2 },
+                line: 1,
+                column: 64
+            }]
+        },
+        {
+            code: "function foo() { if (1) { class C { static { if (1) { if (2) {} } } } if (2) { if (3) {} } } }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "tooDeeply",
+                data: { depth: 3, maxDepth: 2 },
+                line: 1,
+                column: 80
+            }]
+        }
     ]
 });
index 008f2b01acb7de48d0bb56a9661bb43d7a82bb14..ba01206f4a3549bfd1b518b04d3789119b9dd41b 100644 (file)
@@ -7,6 +7,7 @@
 //------------------------------------------------------------------------------
 // Requirements
 //------------------------------------------------------------------------------
+
 const rule = require("../../../lib/rules/max-lines-per-function");
 const { RuleTester } = require("../../../lib/rule-tester");
 
index 2ea2defac8c5b084deb76f285bc93dd0db9e42ad..64341b1421f1f97eb7b63eb3de8b13a55b4a05c0 100644 (file)
 const rule = require("../../../lib/rules/max-nested-callbacks"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
 const OPENING = "foo(function() {",
     CLOSING = "});";
 
index fa3d3d1f1bda2aa37af2306a41faa65c7bec754d..decdd8a56dca538bbe5f310a1014d65517b07748 100644 (file)
@@ -33,7 +33,33 @@ ruleTester.run("max-statements", rule, {
         { code: "var foo = { thing() { var bar = 1; var baz = 2; } }", options: [2], parserOptions: { ecmaVersion: 6 } },
         { code: "var foo = { ['thing']() { var bar = 1; var baz = 2; } }", options: [2], parserOptions: { ecmaVersion: 6 } },
         { code: "var foo = { thing: () => { var bar = 1; var baz = 2; } }", options: [2], parserOptions: { ecmaVersion: 6 } },
-        { code: "var foo = { thing: function() { var bar = 1; var baz = 2; } }", options: [{ max: 2 }] }
+        { code: "var foo = { thing: function() { var bar = 1; var baz = 2; } }", options: [{ max: 2 }] },
+
+        // this rule does not apply to class static blocks, and statements in them should not count as statements in the enclosing function
+        { code: "class C { static { one; two; three; { four; five; six; } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "function foo() { class C { static { one; two; three; { four; five; six; } } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { one; two; three; function foo() { 1; 2; } four; five; six; } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { { one; two; three; function foo() { 1; 2; } four; five; six; } } }", options: [2], parserOptions: { ecmaVersion: 2022 } },
+        {
+            code: "function top_level() { 1; /* 2 */ class C { static { one; two; three; { four; five; six; } } } 3;}",
+            options: [2, { ignoreTopLevelFunctions: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "function top_level() { 1; 2; } class C { static { one; two; three; { four; five; six; } } }",
+            options: [1, { ignoreTopLevelFunctions: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { one; two; three; { four; five; six; } } } function top_level() { 1; 2; } ",
+            options: [1, { ignoreTopLevelFunctions: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "function foo() { let one; let two = class { static { let three; let four; let five; if (six) { let seven; let eight; let nine; } } }; }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
     invalid: [
         {
@@ -127,7 +153,7 @@ ruleTester.run("max-statements", rule, {
             code: "var foo = { thing: () => { var bar = 1; var baz = 2; var baz2; } }",
             options: [2],
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ messageId: "exceed", data: { name: "Arrow function 'thing'", count: "3", max: 2 } }]
+            errors: [{ messageId: "exceed", data: { name: "Method 'thing'", count: "3", max: 2 } }]
         },
         {
             code: "var foo = { thing: function() { var bar = 1; var baz = 2; var baz2; } }",
@@ -143,6 +169,30 @@ ruleTester.run("max-statements", rule, {
             code: "function foo() { 1; }",
             options: [{ max: 0 }],
             errors: [{ messageId: "exceed", data: { name: "Function 'foo'", count: 1, max: 0 } }]
+        },
+        {
+            code: "function foo() { foo_1; /* foo_ 2 */ class C { static { one; two; three; four; { five; six; seven; eight; } } } foo_3 }",
+            options: [2],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "exceed", data: { name: "Function 'foo'", count: 3, max: 2 } }]
+        },
+        {
+            code: "class C { static { one; two; three; four; function not_top_level() { 1; 2; 3; } five; six; seven; eight; } }",
+            options: [2, { ignoreTopLevelFunctions: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "exceed", data: { name: "Function 'not_top_level'", count: 3, max: 2 } }]
+        },
+        {
+            code: "class C { static { { one; two; three; four; function not_top_level() { 1; 2; 3; } five; six; seven; eight; } } }",
+            options: [2, { ignoreTopLevelFunctions: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "exceed", data: { name: "Function 'not_top_level'", count: 3, max: 2 } }]
+        },
+        {
+            code: "class C { static { { one; two; three; four; } function not_top_level() { 1; 2; 3; } { five; six; seven; eight; } } }",
+            options: [2, { ignoreTopLevelFunctions: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "exceed", data: { name: "Function 'not_top_level'", count: 3, max: 2 } }]
         }
     ]
 });
index 6c8d4698e06867a5f009662851657a6ffcb60ea1..1f2302f9a226b1d472a52c3300bc0aa39c32b6ed 100644 (file)
@@ -11,7 +11,6 @@
 const rule = require("../../../lib/rules/multiline-comment-style");
 const { RuleTester } = require("../../../lib/rule-tester");
 
-
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
index 3508ace8e96e153d754f290cfe483eed34696594..2b93711491b4f0136bf2c245f133e93696c9b64f 100644 (file)
@@ -16,6 +16,7 @@ const parser = require("../../fixtures/fixture-parser"),
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
+
 const error = { messageId: "missing", type: "NewExpression" };
 const neverError = { messageId: "unnecessary", type: "NewExpression" };
 
index 2a26937ae62b2a23c9b1642841d4ee980793669f..ce7719b714676ce482898c883c6c5b092066d50c 100644 (file)
 const rule = require("../../../lib/rules/newline-per-chained-call"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("newline-per-chained-call", rule, {
index 6ba1e7b1f05930cd53f16d0e3511df698eb31a59..efca8819b07f45c9bc4c22d0d03125969faed7cf 100644 (file)
@@ -5,9 +5,17 @@
 
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const rule = require("../../../lib/rules/no-await-in-loop"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const error = { messageId: "unexpectedAwait", type: "AwaitExpression" };
 
 const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } });
index f45b4e4a3de246ed5cf12bb36b15f5c6982dec59..14568f08aa35c5358f09c1e611f133732cfa908f 100644 (file)
@@ -11,7 +11,6 @@
 const rule = require("../../../lib/rules/no-buffer-constructor");
 const { RuleTester } = require("../../../lib/rule-tester");
 
-
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
index b9c63038715400392a971138e77631b120e5861e..68478dde908d2995badf4c0ef21d09bd7b96f381 100644 (file)
@@ -9,10 +9,8 @@
 //------------------------------------------------------------------------------
 
 const rule = require("../../../lib/rules/no-compare-neg-zero");
-
 const { RuleTester } = require("../../../lib/rule-tester");
 
-
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
index a1a534d2da924d96adbdcf58f59cf070af5cf261..a37784bc3faf09d4fdc17d74ec68b67149e3378c 100644 (file)
@@ -16,7 +16,7 @@ const { RuleTester } = require("../../../lib/rule-tester");
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("no-dupe-class-members", rule, {
     valid: [
@@ -51,7 +51,12 @@ ruleTester.run("no-dupe-class-members", rule, {
         "class A { [-1]() {} ['-1']() {} }",
 
         // not supported by this rule
-        "class A { [foo]() {} [foo]() {} }"
+        "class A { [foo]() {} [foo]() {} }",
+
+        // private and public
+        "class A { foo; static foo; }",
+        "class A { foo; #foo; }",
+        "class A { '#foo'; #foo; }"
     ],
     invalid: [
         {
@@ -217,6 +222,17 @@ ruleTester.run("no-dupe-class-members", rule, {
             errors: [
                 { type: "MethodDefinition", line: 1, column: 29, messageId: "unexpected", data: { name: "foo" } }
             ]
+        },
+        {
+            code: "class A { foo; foo; }",
+            errors: [
+                { type: "PropertyDefinition", line: 1, column: 16, messageId: "unexpected", data: { name: "foo" } }
+            ]
         }
+
+        /*
+         * This is syntax error
+         * { code: "class A { #foo; #foo; }" }
+         */
     ]
 });
index 1421c2ad6140a06fa579a73270015f47c82cf7ef..fd4cef8ed77bd028dad0f96f571c17e09f7ebd9e 100644 (file)
@@ -32,6 +32,7 @@ ruleTester.run("no-empty-character-class", rule, {
         "var foo = /\\s*:\\s*/gim;",
         { code: "var foo = /[\\]]/uy;", parserOptions: { ecmaVersion: 6 } },
         { code: "var foo = /[\\]]/s;", parserOptions: { ecmaVersion: 2018 } },
+        { code: "var foo = /[\\]]/d;", parserOptions: { ecmaVersion: 2022 } },
         "var foo = /\\[]/"
     ],
     invalid: [
@@ -41,6 +42,7 @@ ruleTester.run("no-empty-character-class", rule, {
         { code: "if (/^abc[]/.test(foo)) {}", errors: [{ messageId: "unexpected", type: "Literal" }] },
         { code: "var foo = /[]]/;", errors: [{ messageId: "unexpected", type: "Literal" }] },
         { code: "var foo = /\\[[]/;", errors: [{ messageId: "unexpected", type: "Literal" }] },
-        { code: "var foo = /\\[\\[\\]a-z[]/;", errors: [{ messageId: "unexpected", type: "Literal" }] }
+        { code: "var foo = /\\[\\[\\]a-z[]/;", errors: [{ messageId: "unexpected", type: "Literal" }] },
+        { code: "var foo = /[]]/d;", parserOptions: { ecmaVersion: 2022 }, errors: [{ messageId: "unexpected", type: "Literal" }] }
     ]
 });
index bc8d38471bbebe4c4c570674c2fc4448849657e5..79ba4a1eb11bfb26c8c53d54a11f04847919220e 100644 (file)
@@ -48,6 +48,9 @@ ruleTester.run("no-eval", rule, {
         "var obj = {}; obj.foo = function() { this.eval('foo'); }",
         { code: "class A { foo() { this.eval(); } }", parserOptions: { ecmaVersion: 6 } },
         { code: "class A { static foo() { this.eval(); } }", parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { field = this.eval(); }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { field = () => this.eval(); }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { static { this.eval(); } }", parserOptions: { ecmaVersion: 2022 } },
 
         // Allows indirect eval
         { code: "(0, eval)('foo')", options: [{ allowIndirect: true }] },
@@ -123,6 +126,19 @@ ruleTester.run("no-eval", rule, {
             parserOptions: { ecmaVersion: 2020 },
             globals: { window: "readonly" },
             errors: [{ messageId: "unexpected" }]
+        },
+
+        // Class fields
+        {
+            code: "class C { [this.eval('foo')] }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected" }]
+        },
+
+        {
+            code: "class A { static {} [this.eval()]; }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected" }]
         }
     ]
 });
index 8dda6992d2126da15d6e7e82245e6ab9e1afd656..8aecb4ca37f4d56fbfe425a5bbb45863b517e348 100644 (file)
@@ -13,7 +13,7 @@ const rule = require("../../../lib/rules/no-extra-boolean-cast"),
     { RuleTester } = require("../../../lib/rule-tester");
 
 //------------------------------------------------------------------------------
-// Helpers
+// Tests
 //------------------------------------------------------------------------------
 
 const ruleTester = new RuleTester();
index a14ececc09973edfab622eab6825163aeb3c9253..cb488c891e52c90be30c40ef09c323ca60580d95 100644 (file)
 const rule = require("../../../lib/rules/no-extra-parens"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
 /**
  * Create error message object for failure cases
  * @param {string} code source code
@@ -42,9 +46,13 @@ function invalid(code, output, type, line, config) {
     return result;
 }
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2021,
+        ecmaVersion: 2022,
         ecmaFeatures: {
             jsx: true
         }
@@ -182,6 +190,25 @@ ruleTester.run("no-extra-parens", rule, {
         "class foo { a(){} [b](){} c(){} [(d,e)](){} }",
         "class foo { [(a,b)](){} c(){} [d](){} e(){} }",
         "const foo = class { constructor(){} a(){} get b(){} set b(bar){} get c(){} set d(baz){} static e(){} }",
+        "class foo { x; }",
+        "class foo { static x; }",
+        "class foo { x = 1; }",
+        "class foo { static x = 1; }",
+        "class foo { #x; }",
+        "class foo { static #x; }",
+        "class foo { static #x = 1; }",
+        "class foo { #x(){} get #y() {} set #y(value) {} static #z(){} static get #q() {} static set #q(value) {} }",
+        "const foo  = class { #x(){} get #y() {} set #y(value) {} static #z(){} static get #q() {} static set #q(value) {} }",
+        "class foo { [(x, y)]; }",
+        "class foo { static [(x, y)]; }",
+        "class foo { [(x, y)] = 1; }",
+        "class foo { static [(x, y)] = 1; }",
+        "class foo { x = (y, z); }",
+        "class foo { static x = (y, z); }",
+        "class foo { #x = (y, z); }",
+        "class foo { static #x = (y, z); }",
+        "class foo { [(1, 2)] = (3, 4) }",
+        "const foo = class { [(1, 2)] = (3, 4) }",
 
         // ExpressionStatement restricted productions
         "({});",
@@ -771,6 +798,26 @@ ruleTester.run("no-extra-parens", rule, {
         invalid("class foo { [(a,b)](){} [(c+d)](){} }", "class foo { [(a,b)](){} [c+d](){} }", "BinaryExpression"),
         invalid("class foo { [a+(b*c)](){} }", "class foo { [a+b*c](){} }", "BinaryExpression"),
         invalid("const foo = class { [(a)](){} }", "const foo = class { [a](){} }", "Identifier"),
+        invalid("class foo { [(x)]; }", "class foo { [x]; }", "Identifier"),
+        invalid("class foo { static [(x)]; }", "class foo { static [x]; }", "Identifier"),
+        invalid("class foo { [(x)] = 1; }", "class foo { [x] = 1; }", "Identifier"),
+        invalid("class foo { static [(x)] = 1; }", "class foo { static [x] = 1; }", "Identifier"),
+        invalid("const foo = class { [(x)]; }", "const foo = class { [x]; }", "Identifier"),
+        invalid("class foo { [(x = y)]; }", "class foo { [x = y]; }", "AssignmentExpression"),
+        invalid("class foo { [(x + y)]; }", "class foo { [x + y]; }", "BinaryExpression"),
+        invalid("class foo { [(x ? y : z)]; }", "class foo { [x ? y : z]; }", "ConditionalExpression"),
+        invalid("class foo { [((x, y))]; }", "class foo { [(x, y)]; }", "SequenceExpression"),
+        invalid("class foo { x = (y); }", "class foo { x = y; }", "Identifier"),
+        invalid("class foo { static x = (y); }", "class foo { static x = y; }", "Identifier"),
+        invalid("class foo { #x = (y); }", "class foo { #x = y; }", "Identifier"),
+        invalid("class foo { static #x = (y); }", "class foo { static #x = y; }", "Identifier"),
+        invalid("const foo = class { x = (y); }", "const foo = class { x = y; }", "Identifier"),
+        invalid("class foo { x = (() => {}); }", "class foo { x = () => {}; }", "ArrowFunctionExpression"),
+        invalid("class foo { x = (y + z); }", "class foo { x = y + z; }", "BinaryExpression"),
+        invalid("class foo { x = (y ? z : q); }", "class foo { x = y ? z : q; }", "ConditionalExpression"),
+        invalid("class foo { x = ((y, z)); }", "class foo { x = (y, z); }", "SequenceExpression"),
+
+        //
         invalid(
             "var foo = (function*() { if ((yield foo())) { return; } }())",
             "var foo = (function*() { if (yield foo()) { return; } }())",
index 913290aaf85718b0272326bc01539b9f0abd3e38..4f19fb722fa844b0b7a5d144a4a95d1c37b5423f 100644 (file)
@@ -38,6 +38,9 @@ ruleTester.run("no-extra-semi", rule, {
         { code: "class A { a() { this; } }", parserOptions: { ecmaVersion: 6 } },
         { code: "var A = class { a() { this; } };", parserOptions: { ecmaVersion: 6 } },
         { code: "class A { } a;", parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { field; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { field = 0; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { static { foo; } }", parserOptions: { ecmaVersion: 2022 } },
 
         // modules
         { code: "export const x = 42;", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
@@ -110,6 +113,18 @@ ruleTester.run("no-extra-semi", rule, {
             output: "with(foo){}",
             errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
+        {
+            code: "class A { static { ; } }",
+            output: "class A { static {  } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected", type: "EmptyStatement", column: 20 }]
+        },
+        {
+            code: "class A { static { a;; } }",
+            output: "class A { static { a; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected", type: "EmptyStatement", column: 22 }]
+        },
 
         // Class body.
         {
@@ -157,6 +172,24 @@ ruleTester.run("no-extra-semi", rule, {
             output: "class A { a() {} get b() {} }",
             parserOptions: { ecmaVersion: 6 },
             errors: [{ messageId: "unexpected", type: "Punctuator", column: 17 }]
+        },
+        {
+            code: "class A { field;; }",
+            output: "class A { field; }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected", type: "Punctuator", column: 17 }]
+        },
+        {
+            code: "class A { static {}; }",
+            output: "class A { static {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected", type: "Punctuator", column: 20 }]
+        },
+        {
+            code: "class A { static { a; }; foo(){} }",
+            output: "class A { static { a; } foo(){} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected", type: "Punctuator", column: 24 }]
         }
     ]
 });
index e154c31d5f70e5077f997881c17252bf9779b2f4..0b98d3293e91cb485942ac4666a241c3fe084c36 100644 (file)
@@ -30,6 +30,14 @@ ruleTester.run("no-fallthrough", rule, {
         "switch(foo) { case 0: a(); /* fall through */ case 1: b(); }",
         "switch(foo) { case 0: a(); /* fallthrough */ case 1: b(); }",
         "switch(foo) { case 0: a(); /* FALLS THROUGH */ case 1: b(); }",
+        "switch(foo) { case 0: { a(); /* falls through */ } case 1: b(); }",
+        "switch(foo) { case 0: { a()\n /* falls through */ } case 1: b(); }",
+        "switch(foo) { case 0: { a(); /* fall through */ } case 1: b(); }",
+        "switch(foo) { case 0: { a(); /* fallthrough */ } case 1: b(); }",
+        "switch(foo) { case 0: { a(); /* FALLS THROUGH */ } case 1: b(); }",
+        "switch(foo) { case 0: { a(); } /* falls through */ case 1: b(); }",
+        "switch(foo) { case 0: { a(); /* falls through */ } /* comment */ case 1: b(); }",
+        "switch(foo) { case 0: { /* falls through */ } case 1: b(); }",
         "function foo() { switch(foo) { case 0: a(); return; case 1: b(); }; }",
         "switch(foo) { case 0: a(); throw 'foo'; case 1: b(); }",
         "while (a) { switch(foo) { case 0: a(); continue; case 1: b(); } }",
@@ -133,6 +141,30 @@ ruleTester.run("no-fallthrough", rule, {
             code: "switch(foo) { case 0:\n\n default: b() }",
             errors: errorsDefault
         },
+        {
+            code: "switch(foo) { case 0: {} default: b() }",
+            errors: errorsDefault
+        },
+        {
+            code: "switch(foo) { case 0: a(); { /* falls through */ } default: b() }",
+            errors: errorsDefault
+        },
+        {
+            code: "switch(foo) { case 0: { /* falls through */ } a(); default: b() }",
+            errors: errorsDefault
+        },
+        {
+            code: "switch(foo) { case 0: if (a) { /* falls through */ } default: b() }",
+            errors: errorsDefault
+        },
+        {
+            code: "switch(foo) { case 0: { { /* falls through */ } } default: b() }",
+            errors: errorsDefault
+        },
+        {
+            code: "switch(foo) { case 0: { /* comment */ } default: b() }",
+            errors: errorsDefault
+        },
         {
             code: "switch(foo) { case 0:\n // comment\n default: b() }",
             errors: errorsDefault
@@ -168,6 +200,20 @@ ruleTester.run("no-fallthrough", rule, {
                     column: 1
                 }
             ]
+        },
+        {
+            code: "switch(foo) { case 0: { a();\n/* no break */\n/* todo: fix readability */ }\ndefault: b() }",
+            options: [{
+                commentPattern: "no break"
+            }],
+            errors: [
+                {
+                    messageId: "default",
+                    type: "SwitchCase",
+                    line: 4,
+                    column: 1
+                }
+            ]
         }
     ]
 });
index a9f70fd3f5ef5037a7bd9031da6ba5adecdc9fde..5fcb2f3c866184ab5d34799d5ab3d4b47be3faf2 100644 (file)
@@ -73,8 +73,27 @@ ruleTester.run("no-inner-declarations", rule, {
         {
             code: "module.exports = function foo(){}",
             options: ["both"]
+        },
+        {
+            code: "class C { method() { function foo() {} } }",
+            options: ["both"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { method() { var x; } }",
+            options: ["both"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { function foo() {} } }",
+            options: ["both"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { var x; } }",
+            options: ["both"],
+            parserOptions: { ecmaVersion: 2022 }
         }
-
     ],
 
     // Examples of code that should trigger the rule
@@ -271,7 +290,54 @@ ruleTester.run("no-inner-declarations", rule, {
                 },
                 type: "VariableDeclaration"
             }]
+        }, {
+            code: "class C { method() { if(test) { var foo; } } }",
+            options: ["both"],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "moveDeclToRoot",
+                data: {
+                    type: "variable",
+                    body: "function body"
+                },
+                type: "VariableDeclaration"
+            }]
+        }, {
+            code: "class C { static { if (test) { function foo() {} } } }",
+            options: ["both"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "moveDeclToRoot",
+                data: {
+                    type: "function",
+                    body: "class static block body"
+                },
+                type: "FunctionDeclaration"
+            }]
+        }, {
+            code: "class C { static { if (test) { var foo; } } }",
+            options: ["both"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "moveDeclToRoot",
+                data: {
+                    type: "variable",
+                    body: "class static block body"
+                },
+                type: "VariableDeclaration"
+            }]
+        }, {
+            code: "class C { static { if (test) { if (anotherTest) { var foo; } } } }",
+            options: ["both"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "moveDeclToRoot",
+                data: {
+                    type: "variable",
+                    body: "class static block body"
+                },
+                type: "VariableDeclaration"
+            }]
         }
-
     ]
 });
index 6484db816db5cdc1781b66f377acaaa2c28da7ea..a34752dcda535c1491d44694efded559da73a280 100644 (file)
 const rule = require("../../../lib/rules/no-invalid-regexp"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("no-invalid-regexp", rule, {
@@ -59,6 +63,9 @@ ruleTester.run("no-invalid-regexp", rule, {
         "new RegExp('(?<𝒜>.)', 'g');",
         "new RegExp('\\\\p{Script=Nandinagari}', 'u');",
 
+        // ES2022
+        "new RegExp('a+(?<Z>z)?', 'd')",
+
         // allowConstructorFlags
         {
             code: "new RegExp('.', 'g')",
index 3b19d63696f382ec3a2570d15c53525ee00804d2..62033fd389e0bf09b01d46935c7ac96926ff21f7 100644 (file)
@@ -90,7 +90,6 @@ function extractPatterns(patterns, type) {
     return [].concat(...patternsList);
 }
 
-
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
@@ -724,6 +723,8 @@ const patterns = [
         valid: [NORMAL],
         invalid: [USE_STRICT, IMPLIED_STRICT, MODULES]
     },
+
+    // Logical assignments
     {
         code: "obj.method &&= function () { console.log(this); z(x => console.log(x, this)); }",
         parserOptions: { ecmaVersion: 2021 },
@@ -741,6 +742,87 @@ const patterns = [
         parserOptions: { ecmaVersion: 2021 },
         valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
         invalid: []
+    },
+
+    // Class fields.
+    {
+        code: "class C { field = console.log(this); }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+        invalid: []
+    },
+    {
+        code: "class C { field = z(x => console.log(x, this)); }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+        invalid: []
+    },
+    {
+        code: "class C { field = function () { console.log(this); z(x => console.log(x, this)); }; }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+        invalid: []
+    },
+    {
+        code: "class C { #field = function () { console.log(this); z(x => console.log(x, this)); }; }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+        invalid: []
+    },
+    {
+        code: "class C { [this.foo]; }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [NORMAL], // the global this in non-strict mode is OK.
+        invalid: [USE_STRICT, IMPLIED_STRICT, MODULES],
+        errors: [{ messageId: "unexpectedThis", type: "ThisExpression" }]
+    },
+
+    // Class static blocks
+    {
+        code: "class C { static { this.x; } }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+        invalid: []
+    },
+    {
+        code: "class C { static { () => { this.x; } } }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+        invalid: []
+    },
+    {
+        code: "class C { static { class D { [this.x]; } } }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+        invalid: []
+    },
+    {
+        code: "class C { static { function foo() { this.x; } } }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [],
+        invalid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+        errors: [{ messageId: "unexpectedThis", type: "ThisExpression" }]
+    },
+    {
+        code: "class C { static { (function() { this.x; }); } }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [],
+        invalid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+        errors: [{ messageId: "unexpectedThis", type: "ThisExpression" }]
+    },
+    {
+        code: "class C { static { (function() { this.x; })(); } }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [],
+        invalid: [NORMAL, USE_STRICT, IMPLIED_STRICT, MODULES],
+        errors: [{ messageId: "unexpectedThis", type: "ThisExpression" }]
+    },
+    {
+        code: "class C { static {} [this.x]; }",
+        parserOptions: { ecmaVersion: 2022 },
+        valid: [NORMAL],
+        invalid: [USE_STRICT, IMPLIED_STRICT, MODULES],
+        errors: [{ messageId: "unexpectedThis", type: "ThisExpression" }]
     }
 ];
 
index 6fa29d2b06f6f38d845f8aaacaefe54e4bfa44ac..ab81caf8ec7bd5a3afaed8fad7d8a982f964bcfe 100644 (file)
@@ -13,7 +13,7 @@ const rule = require("../../../lib/rules/no-lone-blocks"),
     { RuleTester } = require("../../../lib/rule-tester");
 
 //------------------------------------------------------------------------------
-// Helpers
+// Tests
 //------------------------------------------------------------------------------
 
 const ruleTester = new RuleTester();
@@ -57,7 +57,16 @@ ruleTester.run("no-lone-blocks", rule, {
             }
           }
         `,
-        { code: "function foo() { { const x = 4 } const x = 3 }", parserOptions: { ecmaVersion: 6 } }
+        { code: "function foo() { { const x = 4 } const x = 3 }", parserOptions: { ecmaVersion: 6 } },
+
+        { code: "class C { static {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { if (foo) { block; } } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { lbl: { block; } } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { { let block; } something; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { something; { const block = 1; } } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { { function block(){} } something; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { something; { class block {}  } } }", parserOptions: { ecmaVersion: 2022 } }
     ],
     invalid: [
         {
@@ -235,6 +244,202 @@ ruleTester.run("no-lone-blocks", rule, {
                 type: "BlockStatement",
                 line: 3
             }]
+        },
+        {
+            code: `
+              class C {
+                static {
+                  if (foo) {
+                    {
+                        let block;
+                    }
+                  }
+                }
+              }
+            `,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "redundantNestedBlock",
+                type: "BlockStatement",
+                line: 5
+            }]
+        },
+        {
+            code: `
+              class C {
+                static {
+                  if (foo) {
+                    {
+                        block;
+                    }
+                    something;
+                  }
+                }
+              }
+            `,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "redundantNestedBlock",
+                type: "BlockStatement",
+                line: 5
+            }]
+        },
+        {
+            code: `
+              class C {
+                static {
+                  {
+                    block;
+                  }
+                }
+              }
+            `,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "redundantNestedBlock",
+                type: "BlockStatement",
+                line: 4
+            }]
+        },
+        {
+            code: `
+              class C {
+                static {
+                  {
+                    let block;
+                  }
+                }
+              }
+            `,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "redundantNestedBlock",
+                type: "BlockStatement",
+                line: 4
+            }]
+        },
+        {
+            code: `
+              class C {
+                static {
+                  {
+                    const block = 1;
+                  }
+                }
+              }
+            `,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "redundantNestedBlock",
+                type: "BlockStatement",
+                line: 4
+            }]
+        },
+        {
+            code: `
+              class C {
+                static {
+                  {
+                    function block() {}
+                  }
+                }
+              }
+            `,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "redundantNestedBlock",
+                type: "BlockStatement",
+                line: 4
+            }]
+        },
+        {
+            code: `
+              class C {
+                static {
+                  {
+                    class block {}
+                  }
+                }
+              }
+            `,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "redundantNestedBlock",
+                type: "BlockStatement",
+                line: 4
+            }]
+        },
+        {
+            code: `
+              class C {
+                static {
+                  {
+                    var block;
+                  }
+                  something;
+                }
+              }
+            `,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "redundantNestedBlock",
+                type: "BlockStatement",
+                line: 4
+            }]
+        },
+        {
+            code: `
+              class C {
+                static {
+                  something;
+                  {
+                    var block;
+                  }
+                }
+              }
+            `,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "redundantNestedBlock",
+                type: "BlockStatement",
+                line: 5
+            }]
+        },
+        {
+            code: `
+              class C {
+                static {
+                  {
+                    block;
+                  }
+                  something;
+                }
+              }
+            `,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "redundantNestedBlock",
+                type: "BlockStatement",
+                line: 4
+            }]
+        },
+        {
+            code: `
+              class C {
+                static {
+                  something;
+                  {
+                    block;
+                  }
+                }
+              }
+            `,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "redundantNestedBlock",
+                type: "BlockStatement",
+                line: 5
+            }]
         }
     ]
 });
index c690dd7e1bc45cdd0a0f445e4f0660d6fe4d8e18..f268f117e92c45f4a34340e3cd89c5b5318b45bd 100644 (file)
@@ -13,7 +13,7 @@ const rule = require("../../../lib/rules/no-loss-of-precision"),
     { RuleTester } = require("../../../lib/rule-tester");
 
 //------------------------------------------------------------------------------
-// Helpers
+// Tests
 //------------------------------------------------------------------------------
 
 const ruleTester = new RuleTester();
index bbdf8ca984f9615c430d169d7ad770d392f4be1e..6afc328f04008d0442c9c3aa26c36eefc8f8225f 100644 (file)
@@ -13,7 +13,7 @@ const rule = require("../../../lib/rules/no-magic-numbers"),
     { RuleTester } = require("../../../lib/rule-tester");
 
 //------------------------------------------------------------------------------
-// Helpers
+// Tests
 //------------------------------------------------------------------------------
 
 const ruleTester = new RuleTester();
index a7bc04ef415b6154c8ca33f32b8bd13299902b44..a02e5e13e327ea45f1a9f9f25437c264617b72b4 100644 (file)
@@ -12,7 +12,7 @@ const rule = require("../../../lib/rules/no-misleading-character-class"),
     { RuleTester } = require("../../../lib/rule-tester");
 
 //------------------------------------------------------------------------------
-// Helpers
+// Tests
 //------------------------------------------------------------------------------
 
 const ruleTester = new RuleTester({
index c534f55b33f95e607b489ae6108b93e17359b8d2..7920d9900fbd89b8cd681c3c70d8662b5dad16da 100644 (file)
@@ -13,7 +13,7 @@ const rule = require("../../../lib/rules/no-multi-assign"),
     { RuleTester } = require("../../../lib/rule-tester");
 
 //------------------------------------------------------------------------------
-// Fixtures
+// Helpers
 //------------------------------------------------------------------------------
 
 /**
@@ -33,7 +33,6 @@ function errorAt(line, column, type) {
     };
 }
 
-
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
@@ -53,7 +52,11 @@ ruleTester.run("no-mutli-assign", rule, {
         { code: "export let a, b;", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
         { code: "export let a,\n b = 0;", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
         { code: "const x = {};const y = {};x.one = y.one = 1;", options: [{ ignoreNonDeclaration: true }], parserOptions: { ecmaVersion: 6 } },
-        { code: "let a, b;a = b = 1", options: [{ ignoreNonDeclaration: true }], parserOptions: { ecmaVersion: 6 } }
+        { code: "let a, b;a = b = 1", options: [{ ignoreNonDeclaration: true }], parserOptions: { ecmaVersion: 6 } },
+        {
+            code: "class C { [foo = 0] = 0 }",
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
 
     invalid: [
@@ -172,6 +175,21 @@ ruleTester.run("no-mutli-assign", rule, {
             errors: [
                 errorAt(1, 11, "AssignmentExpression")
             ]
+        },
+        {
+            code: "class C { field = foo = 0 }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                errorAt(1, 19, "AssignmentExpression")
+            ]
+        },
+        {
+            code: "class C { field = foo = 0 }",
+            options: [{ ignoreNonDeclaration: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                errorAt(1, 19, "AssignmentExpression")
+            ]
         }
     ]
 });
index 5291fbbf0a830bc1f81784ad76fc9a3e64f028f4..bcec4e44c4cafcc674dfef9ae4c1c5aeb05d6964 100644 (file)
@@ -12,11 +12,9 @@ const rule = require("../../../lib/rules/no-multiple-empty-lines"),
     { RuleTester } = require("../../../lib/rule-tester");
 
 //------------------------------------------------------------------------------
-// Tests
+// Helpers
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester();
-
 /**
  * Creates the expected error message object for the specified number of lines
  * @param {lines} lines The number of lines expected.
@@ -69,6 +67,11 @@ function getExpectedErrorBOF(lines) {
     };
 }
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester();
 
 ruleTester.run("no-multiple-empty-lines", rule, {
 
index aa0542090e24229bf28b5b1948b252bea1030bb3..1ac02d974659902d06048cfabc79c4416bd2826c 100644 (file)
@@ -38,7 +38,11 @@ ruleTester.run("no-new-func", rule, {
         "var fn = function () { function Function() {}; Function() }",
         "var x = function Function() { Function(); }",
         "call(Function)",
-        "new Class(Function)"
+        "new Class(Function)",
+        "foo[Function]()",
+        "foo(Function.bind)",
+        "Function.toString()",
+        "Function[call]()"
     ],
     invalid: [
         {
@@ -55,6 +59,49 @@ ruleTester.run("no-new-func", rule, {
                 type: "CallExpression"
             }]
         },
+        {
+            code: "var a = Function.call(null, \"b\", \"c\", \"return b+c\");",
+            errors: [{
+                messageId: "noFunctionConstructor",
+                type: "CallExpression"
+            }]
+        },
+        {
+            code: "var a = Function.apply(null, [\"b\", \"c\", \"return b+c\"]);",
+            errors: [{
+                messageId: "noFunctionConstructor",
+                type: "CallExpression"
+            }]
+        },
+        {
+            code: "var a = Function.bind(null, \"b\", \"c\", \"return b+c\")();",
+            errors: [{
+                messageId: "noFunctionConstructor",
+                type: "CallExpression"
+            }]
+        },
+        {
+            code: "var a = Function.bind(null, \"b\", \"c\", \"return b+c\");",
+            errors: [{
+                messageId: "noFunctionConstructor",
+                type: "CallExpression"
+            }]
+        },
+        {
+            code: "var a = Function[\"call\"](null, \"b\", \"c\", \"return b+c\");",
+            errors: [{
+                messageId: "noFunctionConstructor",
+                type: "CallExpression"
+            }]
+        },
+        {
+            code: "var a = (Function?.call)(null, \"b\", \"c\", \"return b+c\");",
+            parserOptions: { ecmaVersion: 2021 },
+            errors: [{
+                messageId: "noFunctionConstructor",
+                type: "CallExpression"
+            }]
+        },
         {
             code: "const fn = () => { class Function {} }; new Function('', '')",
             parserOptions: {
index 3e0e7e061550494905e9887eb5ffbb2a3d9e9ef1..c9b713571563a66bebd72513ce2e88f81d927705 100644 (file)
@@ -23,7 +23,8 @@ ruleTester.run("no-proto", rule, {
         "var a = test[__proto__];",
         "var __proto__ = null;",
         { code: "foo[`__proto`] = null;", parserOptions: { ecmaVersion: 6 } },
-        { code: "foo[`__proto__\n`] = null;", parserOptions: { ecmaVersion: 6 } }
+        { code: "foo[`__proto__\n`] = null;", parserOptions: { ecmaVersion: 6 } },
+        { code: "class C { #__proto__; foo() { this.#__proto__; } }", parserOptions: { ecmaVersion: 2022 } }
     ],
     invalid: [
         { code: "var a = test.__proto__;", errors: [{ messageId: "unexpectedProto", type: "MemberExpression" }] },
index a65b54d4d63680b948a37d5dc4628a71d2a46809..6152e8acf6e30f014e21acd18ed4073155029e4a 100644 (file)
@@ -15,6 +15,7 @@ const rule = require("../../../lib/rules/no-prototype-builtins"),
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("no-prototype-builtins", rule, {
@@ -43,6 +44,7 @@ ruleTester.run("no-prototype-builtins", rule, {
         { code: "foo?.['propertyIsEnumerabl']('bar')", parserOptions: { ecmaVersion: 2020 } },
         "foo[1]('bar')",
         "foo[null]('bar')",
+        { code: "class C { #hasOwnProperty; foo() { obj.#hasOwnProperty('bar'); } }", parserOptions: { ecmaVersion: 2022 } },
 
         // out of scope for this rule
         "foo['hasOwn' + 'Property']('bar')",
index f89d6853fa3197886b3a40e0b227bf9675ff4f0f..bbbe5808171b1ea00959c9a8e5854336c080f56a 100644 (file)
@@ -28,6 +28,72 @@ ruleTester.run("no-redeclare", rule, {
                 ecmaVersion: 6
             }
         },
+        {
+            code: "var a; class C { static { var a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: "class C { static { var a; } } var a; ",
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: "function a(){} class C { static { var a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: "var a; class C { static { function a(){} } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: "class C { static { var a; } static { var a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: "class C { static { function a(){} } static { function a(){} } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: "class C { static { var a; { function a(){} } } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: "class C { static { function a(){}; { function a(){} } } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: "class C { static { var a; { let a; } } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: "class C { static { let a; { let a; } } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: "class C { static { { let a; } { let a; } } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
         { code: "var Object = 0;", options: [{ builtinGlobals: false }] },
         { code: "var Object = 0;", options: [{ builtinGlobals: true }], parserOptions: { ecmaVersion: 6, sourceType: "module" } },
         { code: "var Object = 0;", options: [{ builtinGlobals: true }], parserOptions: { ecmaFeatures: { globalReturn: true } } },
@@ -80,6 +146,37 @@ ruleTester.run("no-redeclare", rule, {
         { code: "var a = 3; var a = 10; var a = 15;", errors: [{ message: "'a' is already defined.", type: "Identifier" }, { message: "'a' is already defined.", type: "Identifier" }] },
         { code: "var a; var a;", parserOptions: { ecmaVersion: 6, sourceType: "module" }, errors: [{ message: "'a' is already defined.", type: "Identifier" }] },
         { code: "export var a; var a;", parserOptions: { ecmaVersion: 6, sourceType: "module" }, errors: [{ message: "'a' is already defined.", type: "Identifier" }] },
+
+        // `var` redeclaration in class static blocks. Redeclaration of functions is not allowed in class static blocks.
+        {
+            code: "class C { static { var a; var a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ message: "'a' is already defined.", type: "Identifier" }]
+        },
+        {
+            code: "class C { static { var a; { var a; } } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ message: "'a' is already defined.", type: "Identifier" }]
+        },
+        {
+            code: "class C { static { { var a; } var a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ message: "'a' is already defined.", type: "Identifier" }]
+        },
+        {
+            code: "class C { static { { var a; } { var a; } } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ message: "'a' is already defined.", type: "Identifier" }]
+        },
+
         {
             code: "var Object = 0;",
             options: [{ builtinGlobals: true }],
index fdcd1cec27a4243ac08d75fc1cd99044fb2632fe..8995229754968f7308c9a9e8c72557730c31f97d 100644 (file)
 const rule = require("../../../lib/rules/no-regex-spaces"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("no-regex-spaces", rule, {
index f54d4df03540a102c1831ee87889844c12fb45f8..c86e67ecf65d6e045e3d69140c12a2a319a858d9 100644 (file)
@@ -29,6 +29,19 @@ ruleTester.run("no-restricted-imports", rule, {
         { code: "import \"foo/bar\";", options: ["foo"] },
         { code: "import withPaths from \"foo/bar\";", options: [{ paths: ["foo", "bar"] }] },
         { code: "import withPatterns from \"foo/bar\";", options: [{ patterns: ["foo/c*"] }] },
+        { code: "import foo from 'foo';", options: ["../foo"] },
+        { code: "import foo from 'foo';", options: [{ paths: ["../foo"] }] },
+        { code: "import foo from 'foo';", options: [{ patterns: ["../foo"] }] },
+        { code: "import foo from 'foo';", options: ["/foo"] },
+        { code: "import foo from 'foo';", options: [{ paths: ["/foo"] }] },
+        "import relative from '../foo';",
+        { code: "import relative from '../foo';", options: ["../notFoo"] },
+        { code: "import relativeWithPaths from '../foo';", options: [{ paths: ["../notFoo"] }] },
+        { code: "import relativeWithPatterns from '../foo';", options: [{ patterns: ["notFoo"] }] },
+        "import absolute from '/foo';",
+        { code: "import absolute from '/foo';", options: ["/notFoo"] },
+        { code: "import absoluteWithPaths from '/foo';", options: [{ paths: ["/notFoo"] }] },
+        { code: "import absoluteWithPatterns from '/foo';", options: [{ patterns: ["notFoo"] }] },
         {
             code: "import withPatternsAndPaths from \"foo/bar\";",
             options: [{ paths: ["foo"], patterns: ["foo/c*"] }]
@@ -815,6 +828,72 @@ ruleTester.run("no-restricted-imports", rule, {
             column: 15,
             endColumn: 29
         }]
+    },
+    {
+        code: "import relative from '../foo';",
+        options: ["../foo"],
+        errors: [{
+            message: "'../foo' import is restricted from being used.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 31
+        }]
+    },
+    {
+        code: "import relativeWithPaths from '../foo';",
+        options: [{ paths: ["../foo"] }],
+        errors: [{
+            message: "'../foo' import is restricted from being used.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 40
+        }]
+    },
+    {
+        code: "import relativeWithPatterns from '../foo';",
+        options: [{ patterns: ["../foo"] }],
+        errors: [{
+            message: "'../foo' import is restricted from being used by a pattern.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 43
+        }]
+    },
+    {
+        code: "import absolute from '/foo';",
+        options: ["/foo"],
+        errors: [{
+            message: "'/foo' import is restricted from being used.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 29
+        }]
+    },
+    {
+        code: "import absoluteWithPaths from '/foo';",
+        options: [{ paths: ["/foo"] }],
+        errors: [{
+            message: "'/foo' import is restricted from being used.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 38
+        }]
+    },
+    {
+        code: "import absoluteWithPatterns from '/foo';",
+        options: [{ patterns: ["foo"] }],
+        errors: [{
+            message: "'/foo' import is restricted from being used by a pattern.",
+            type: "ImportDeclaration",
+            line: 1,
+            column: 1,
+            endColumn: 41
+        }]
     }
     ]
 });
index 08cc7c092f527308861301125fe7c8890e294125..5e89a6a732963c85b704508371408103b1c4bdaf 100644 (file)
@@ -32,7 +32,20 @@ ruleTester.run("no-restricted-modules", rule, {
         { code: "var withPatternsAndPaths = require(\"foo/bar\");", options: [{ paths: ["foo"], patterns: ["foo/c*"] }] },
         { code: "var withGitignores = require(\"foo/bar\");", options: [{ paths: ["foo"], patterns: ["foo/*", "!foo/bar"] }] },
         { code: "require(`fs`)", options: ["crypto"], parserOptions: { ecmaVersion: 6 } },
-        { code: "require(`foo${bar}`)", options: ["foo"], parserOptions: { ecmaVersion: 6 } }
+        { code: "require(`foo${bar}`)", options: ["foo"], parserOptions: { ecmaVersion: 6 } },
+        { code: "var foo = require('foo');", options: ["../foo"] },
+        { code: "var foo = require('foo');", options: [{ paths: ["../foo"] }] },
+        { code: "var foo = require('foo');", options: [{ patterns: ["../foo"] }] },
+        { code: "var foo = require('foo');", options: ["/foo"] },
+        { code: "var foo = require('foo');", options: [{ paths: ["/foo"] }] },
+        "var relative = require('../foo');",
+        { code: "var relative = require('../foo');", options: ["../notFoo"] },
+        { code: "var relativeWithPaths = require('../foo');", options: [{ paths: ["../notFoo"] }] },
+        { code: "var relativeWithPatterns = require('../foo');", options: [{ patterns: ["notFoo"] }] },
+        "var absolute = require('/foo');",
+        { code: "var absolute = require('/foo');", options: ["/notFoo"] },
+        { code: "var absoluteWithPaths = require('/foo');", options: [{ paths: ["/notFoo"] }] },
+        { code: "var absoluteWithPatterns = require('/foo');", options: [{ patterns: ["notFoo"] }] }
     ],
     invalid: [{
         code: "require(\"fs\")",
@@ -111,5 +124,71 @@ ruleTester.run("no-restricted-modules", rule, {
         options: ["crypto"],
         parserOptions: { ecmaVersion: 6 },
         errors: [{ messageId: "defaultMessage", data: { name: "crypto" }, type: "CallExpression" }]
+    },
+    {
+        code: "var relative = require('../foo');",
+        options: ["../foo"],
+        errors: [{
+            message: "'../foo' module is restricted from being used.",
+            type: "CallExpression",
+            line: 1,
+            column: 16,
+            endColumn: 33
+        }]
+    },
+    {
+        code: "var relativeWithPaths = require('../foo');",
+        options: [{ paths: ["../foo"] }],
+        errors: [{
+            message: "'../foo' module is restricted from being used.",
+            type: "CallExpression",
+            line: 1,
+            column: 25,
+            endColumn: 42
+        }]
+    },
+    {
+        code: "var relativeWithPatterns = require('../foo');",
+        options: [{ patterns: ["../foo"] }],
+        errors: [{
+            message: "'../foo' module is restricted from being used by a pattern.",
+            type: "CallExpression",
+            line: 1,
+            column: 28,
+            endColumn: 45
+        }]
+    },
+    {
+        code: "var absolute = require('/foo');",
+        options: ["/foo"],
+        errors: [{
+            message: "'/foo' module is restricted from being used.",
+            type: "CallExpression",
+            line: 1,
+            column: 16,
+            endColumn: 31
+        }]
+    },
+    {
+        code: "var absoluteWithPaths = require('/foo');",
+        options: [{ paths: ["/foo"] }],
+        errors: [{
+            message: "'/foo' module is restricted from being used.",
+            type: "CallExpression",
+            line: 1,
+            column: 25,
+            endColumn: 40
+        }]
+    },
+    {
+        code: "var absoluteWithPatterns = require('/foo');",
+        options: [{ patterns: ["foo"] }],
+        errors: [{
+            message: "'/foo' module is restricted from being used by a pattern.",
+            type: "CallExpression",
+            line: 1,
+            column: 28,
+            endColumn: 43
+        }]
     }]
 });
index c9fb2b72afcee61fca38878e187e523eabccef8f..7c557bcdc06290cf879d786699ea19ee7f728867 100644 (file)
@@ -169,6 +169,10 @@ ruleTester.run("no-restricted-properties", rule, {
             code: "function qux([, bar] = foo) {}",
             options: [{ object: "foo", property: "1" }],
             parserOptions: { ecmaVersion: 6 }
+        }, {
+            code: "class C { #foo; foo() { this.#foo; } }",
+            options: [{ property: "#foo" }],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
 
@@ -530,6 +534,18 @@ ruleTester.run("no-restricted-properties", rule, {
                 },
                 type: "ObjectPattern"
             }]
+        }, {
+            code: "obj['#foo']",
+            options: [{ property: "#foo" }],
+            errors: [{
+                messageId: "restrictedProperty",
+                data: {
+                    objectName: "",
+                    propertyName: "#foo",
+                    message: ""
+                },
+                type: "MemberExpression"
+            }]
         }
     ]
 });
index 5a9bb6fcfed6926690f70afcf6e69b526119be27..04aa2dee4023d090152bb43eb8853309009b0c18 100644 (file)
@@ -79,6 +79,14 @@ ruleTester.run("no-self-assign", rule, {
         {
             code: "this.x = this.x",
             options: [{ props: false }]
+        },
+        {
+            code: "class C { #field; foo() { this['#field'] = this.#field; } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #field; foo() { this.#field = this['#field']; } }",
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -147,6 +155,18 @@ ruleTester.run("no-self-assign", rule, {
             code: "a.b = a?.b",
             parserOptions: { ecmaVersion: 2020 },
             errors: [{ messageId: "selfAssignment", data: { name: "a?.b" } }]
+        },
+
+        // Private members
+        {
+            code: "class C { #field; foo() { this.#field = this.#field; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "selfAssignment", data: { name: "this.#field" } }]
+        },
+        {
+            code: "class C { #field; foo() { [this.#field] = [this.#field]; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "selfAssignment", data: { name: "this.#field" } }]
         }
     ]
 });
index 85bc1e21c073935d496626126e72052dadafe283..ad4d30a892fd82324d2a2450eeff7d8ed6ce59c7 100644 (file)
@@ -23,7 +23,15 @@ ruleTester.run("no-self-compare", rule, {
         "if (x === y) { }",
         "if (1 === 2) { }",
         "y=x*x",
-        "foo.bar.baz === foo.bar.qux"
+        "foo.bar.baz === foo.bar.qux",
+        {
+            code: "class C { #field; foo() { this.#field === this['#field']; } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #field; foo() { this['#field'] === this.#field; } }",
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
     invalid: [
         { code: "if (x === x) { }", errors: [{ messageId: "comparingToSelf", type: "BinaryExpression" }] },
@@ -39,6 +47,11 @@ ruleTester.run("no-self-compare", rule, {
         { code: "x < x", errors: [{ messageId: "comparingToSelf", type: "BinaryExpression" }] },
         { code: "x >= x", errors: [{ messageId: "comparingToSelf", type: "BinaryExpression" }] },
         { code: "x <= x", errors: [{ messageId: "comparingToSelf", type: "BinaryExpression" }] },
-        { code: "foo.bar().baz.qux >= foo.bar ().baz .qux", errors: [{ messageId: "comparingToSelf", type: "BinaryExpression" }] }
+        { code: "foo.bar().baz.qux >= foo.bar ().baz .qux", errors: [{ messageId: "comparingToSelf", type: "BinaryExpression" }] },
+        {
+            code: "class C { #field; foo() { this.#field === this.#field; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "comparingToSelf", type: "BinaryExpression" }]
+        }
     ]
 });
index 0c64e8b48110d79c97516f3065dd09fda0457f63..ab5b196b9f3fcd5012642ca463669ecf9c1b4508 100644 (file)
@@ -39,7 +39,7 @@ function error(column, type = "ReturnStatement") {
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("no-setter-return", rule, {
     valid: [
@@ -114,6 +114,7 @@ ruleTester.run("no-setter-return", rule, {
         "class A { static set(val) { return 1; } }",
         "({ set: set = function set(val) { return 1; } } = {})",
         "({ set: set = (val) => 1 } = {})",
+        "class C { set; foo() { return 1; } }",
 
         // not returning from the setter
         "({ set foo(val) { function foo(val) { return 1; } } })",
index da2df0fb9d8a70db0921b822100655d9ab523d38..868cd8220b6a270b801f0627ef2b91387fc13026 100644 (file)
 const rule = require("../../../lib/rules/no-shadow-restricted-names"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("no-shadow-restricted-names", rule, {
index dc2cc63c4dea035a593ee84b80ce38e846f01088..13c4c1100d6c8c787b3fb64ff1759f0a95ea8c55 100644 (file)
@@ -57,7 +57,13 @@ ruleTester.run("no-shadow", rule, {
         { code: "function foo() { var top = 0; }", env: { browser: true } },
         { code: "var Object = 0;", options: [{ builtinGlobals: true }] },
         { code: "var top = 0;", options: [{ builtinGlobals: true }], env: { browser: true } },
-        { code: "function foo(cb) { (function (cb) { cb(42); })(cb); }", options: [{ allow: ["cb"] }] }
+        { code: "function foo(cb) { (function (cb) { cb(42); })(cb); }", options: [{ allow: ["cb"] }] },
+        { code: "class C { foo; foo() { let foo; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { var x; } static { var x; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { let x; } static { let x; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { var x; { var x; /* redeclaration */ } } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { { var x; } { var x; /* redeclaration */ } } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { { let x; } { let x; } } }", parserOptions: { ecmaVersion: 2022 } }
     ],
     invalid: [
         {
@@ -715,6 +721,144 @@ ruleTester.run("no-shadow", rule, {
                 line: 1,
                 column: 31
             }]
+        },
+        {
+            code: "class C { static { let a; { let a; } } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "noShadow",
+                data: {
+                    name: "a",
+                    shadowedLine: 1,
+                    shadowedColumn: 24
+                },
+                type: "Identifier",
+                line: 1,
+                column: 33
+            }]
+        },
+        {
+            code: "class C { static { var C; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "noShadow",
+                data: {
+                    name: "C",
+                    shadowedLine: 1,
+                    shadowedColumn: 7
+                },
+                type: "Identifier",
+                line: 1,
+                column: 24
+            }]
+        },
+        {
+            code: "class C { static { let C; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "noShadow",
+                data: {
+                    name: "C",
+                    shadowedLine: 1,
+                    shadowedColumn: 7
+                },
+                type: "Identifier",
+                line: 1,
+                column: 24
+            }]
+        },
+        {
+            code: "var a; class C { static { var a; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "noShadow",
+                data: {
+                    name: "a",
+                    shadowedLine: 1,
+                    shadowedColumn: 5
+                },
+                type: "Identifier",
+                line: 1,
+                column: 31
+            }]
+        },
+        {
+            code: "class C { static { var a; } } var a;",
+            options: [{ hoist: "all" }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "noShadow",
+                data: {
+                    name: "a",
+                    shadowedLine: 1,
+                    shadowedColumn: 35
+                },
+                type: "Identifier",
+                line: 1,
+                column: 24
+            }]
+        },
+        {
+            code: "class C { static { let a; } } let a;",
+            options: [{ hoist: "all" }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "noShadow",
+                data: {
+                    name: "a",
+                    shadowedLine: 1,
+                    shadowedColumn: 35
+                },
+                type: "Identifier",
+                line: 1,
+                column: 24
+            }]
+        },
+        {
+            code: "class C { static { var a; } } let a;",
+            options: [{ hoist: "all" }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "noShadow",
+                data: {
+                    name: "a",
+                    shadowedLine: 1,
+                    shadowedColumn: 35
+                },
+                type: "Identifier",
+                line: 1,
+                column: 24
+            }]
+        },
+        {
+            code: "class C { static { var a; class D { static { var a; } } } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "noShadow",
+                data: {
+                    name: "a",
+                    shadowedLine: 1,
+                    shadowedColumn: 24
+                },
+                type: "Identifier",
+                line: 1,
+                column: 50
+            }]
+        },
+        {
+            code: "class C { static { let a; class D { static { let a; } } } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "noShadow",
+                data: {
+                    name: "a",
+                    shadowedLine: 1,
+                    shadowedColumn: 24
+                },
+                type: "Identifier",
+                line: 1,
+                column: 50
+            }]
         }
     ]
 });
index 7a947c00b1cf5ceaa5c43bb4984bbd03f61d84e0..7b9a8a48a96de62c7bcf603db477dd004cb494db 100644 (file)
@@ -16,7 +16,7 @@ const { RuleTester } = require("../../../lib/rule-tester");
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("no-this-before-super", rule, {
     valid: [
@@ -97,7 +97,13 @@ ruleTester.run("no-this-before-super", rule, {
                     }
                 }
             }
-        `
+        `,
+
+        // Class field initializers are always evaluated after `super()`.
+        "class C { field = this.toString(); }",
+        "class C extends B { field = this.foo(); }",
+        "class C extends B { field = this.foo(); constructor() { super(); } }",
+        "class C extends B { field = this.foo(); constructor() { } }" // < in this case, initializers are never evaluated.
     ],
     invalid: [
 
index 3855b58fdfb295672db5372589b9cbb30ad89a3e..d50b2cea2b67f401c8bf3dc0ad924e153e472e46 100644 (file)
@@ -30,6 +30,7 @@ ruleTester.run("no-throw-literal", rule, {
         "throw new foo();", // NewExpression
         "throw foo.bar;", // MemberExpression
         "throw foo[bar];", // MemberExpression
+        { code: "class C { #field; foo() { throw foo.#field; } }", parserOptions: { ecmaVersion: 2022 } }, // MemberExpression
         "throw foo = new Error();", // AssignmentExpression with the `=` operator
         { code: "throw foo.bar ||= 'literal'", parserOptions: { ecmaVersion: 2021 } }, // AssignmentExpression with a logical operator
         { code: "throw foo[bar] ??= 'literal'", parserOptions: { ecmaVersion: 2021 } }, // AssignmentExpression with a logical operator
index b57b9cdc0bbf3577b0c817b51ba082ba7f4d0e9f..7f134ba0b3ae35835bd698c7b13d0b2adf7bc535 100644 (file)
@@ -22,7 +22,11 @@ ruleTester.run("no-undef-init", rule, {
     valid: [
         "var a;",
         { code: "const foo = undefined", parserOptions: { ecmaVersion: 6 } },
-        "var undefined = 5; var foo = undefined;"
+        "var undefined = 5; var foo = undefined;",
+
+        // doesn't apply to class fields
+        { code: "class C { field = undefined; }", parserOptions: { ecmaVersion: 2022 } }
+
     ],
     invalid: [
         {
index 43697ec957d7cbe0ab1d678c40459d940679ff96..956bbc991abea7d9afcf7fdcc0c5591676a92395 100644 (file)
@@ -91,6 +91,67 @@ ruleTester.run("no-undef", rule, {
         {
             code: "import.meta",
             parserOptions: { ecmaVersion: 2020, sourceType: "module" }
+        },
+
+        // class static blocks
+        {
+            code: "let a; class C { static {} } a;",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "undef", data: { name: "a" } }]
+        },
+        {
+            code: "var a; class C { static {} } a;",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "undef", data: { name: "a" } }]
+        },
+        {
+            code: "a; class C { static {} } var a;",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "undef", data: { name: "a" } }]
+        },
+        {
+            code: "class C { static { C; } }",
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" }
+        },
+        {
+            code: "const C = class { static { C; } }",
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" }
+        },
+        {
+            code: "class C { static { a; } } var a;",
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" }
+        },
+        {
+            code: "class C { static { a; } } let a;",
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" }
+        },
+        {
+            code: "class C { static { var a; a; } }",
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" }
+        },
+        {
+            code: "class C { static { a; var a; } }",
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" }
+        },
+        {
+            code: "class C { static { a; { var a; } } }",
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" }
+        },
+        {
+            code: "class C { static { let a; a; } }",
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" }
+        },
+        {
+            code: "class C { static { a; let a; } }",
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" }
+        },
+        {
+            code: "class C { static { function a() {} a; } }",
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" }
+        },
+        {
+            code: "class C { static { a; function a() {} } }",
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" }
         }
     ],
     invalid: [
@@ -114,6 +175,99 @@ ruleTester.run("no-undef", rule, {
                 ecmaVersion: 2018
             },
             errors: [{ messageId: "undef", data: { name: "b" } }]
+        },
+
+        // class static blocks
+        {
+            code: "class C { static { a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" } }]
+        },
+        {
+            code: "class C { static { { let a; } a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 31 }]
+        },
+        {
+            code: "class C { static { { function a() {} } a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 40 }]
+        },
+        {
+            code: "class C { static { function foo() { var a; }  a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 47 }]
+        },
+        {
+            code: "class C { static { var a; } static { a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 38 }]
+        },
+        {
+            code: "class C { static { let a; } static { a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 38 }]
+        },
+        {
+            code: "class C { static { function a(){} } static { a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 46 }]
+        },
+        {
+            code: "class C { static { var a; } foo() { a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 37 }]
+        },
+        {
+            code: "class C { static { let a; } foo() { a; } }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 37 }]
+        },
+        {
+            code: "class C { static { var a; } [a]; }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 30 }]
+        },
+        {
+            code: "class C { static { let a; } [a]; }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 30 }]
+        },
+        {
+            code: "class C { static { function a() {} } [a]; }",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 39 }]
+        },
+        {
+            code: "class C { static { var a; } } a;",
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ messageId: "undef", data: { name: "a" }, column: 31 }]
         }
     ]
 });
index 89f20835de234d17bc6cb8c50389ea7a4e4558f0..c83a5b3fe28435dad48abe97761d11767b233d00 100644 (file)
@@ -69,7 +69,9 @@ ruleTester.run("no-underscore-dangle", rule, {
         { code: "function foo([_bar] = []) {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 } },
         { code: "function foo( { _bar }) {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 } },
         { code: "function foo( { _bar = 0 } = {}) {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 } },
-        { code: "function foo(...[_bar]) {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 2016 } }
+        { code: "function foo(...[_bar]) {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 2016 } },
+        { code: "class foo { _field; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class foo { #_field; }", parserOptions: { ecmaVersion: 2022 } }
     ],
     invalid: [
         { code: "var _foo = 1", errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_foo" }, type: "VariableDeclarator" }] },
@@ -96,8 +98,17 @@ ruleTester.run("no-underscore-dangle", rule, {
         { code: "const foo = (_bar = 0) => {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_bar" }, type: "AssignmentPattern" }] },
         { code: "function foo(..._bar) {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_bar" }, type: "RestElement" }] },
         { code: "const foo = { onClick(..._bar) { } }", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_bar" }, type: "RestElement" }] },
-        { code: "const foo = (..._bar) => {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_bar" }, type: "RestElement" }] }
-
-
+        { code: "const foo = (..._bar) => {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_bar" }, type: "RestElement" }] },
+        {
+            code: "class foo { #_bar() {} }",
+            options: [{ enforceInMethodNames: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "#_bar" } }]
+        }, {
+            code: "class foo { #bar_() {} }",
+            options: [{ enforceInMethodNames: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "#bar_" } }]
+        }
     ]
 });
index 83c7bf67570a5481ee9f93db879be0f2f4f9a92b..87044efe2a4cf37470db4c9f0bfe12b31bc7889e 100644 (file)
 const rule = require("../../../lib/rules/no-unexpected-multiline"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("no-unexpected-multiline", rule, {
@@ -140,6 +144,28 @@ ruleTester.run("no-unexpected-multiline", rule, {
         {
             code: "var a = b?.\n  [a, b, c].forEach(doSomething)",
             parserOptions: { ecmaVersion: 2020 }
+        },
+
+        // Class fields
+        {
+            code: "class C { field1\n[field2]; }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { field1\n*gen() {} }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+
+            // ArrowFunctionExpression doesn't connect to computed properties.
+            code: "class C { field1 = () => {}\n[field2]; }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+
+            // ArrowFunctionExpression doesn't connect to binary operators.
+            code: "class C { field1 = () => {}\n*gen() {} }",
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -321,6 +347,36 @@ ruleTester.run("no-unexpected-multiline", rule, {
                     messageId: "taggedTemplate"
                 }
             ]
+        },
+
+        // Class fields
+        {
+            code: "class C { field1 = obj\n[field2]; }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    line: 2,
+                    column: 1,
+                    endLine: 2,
+                    endColumn: 2,
+                    messageId: "property"
+                }
+            ]
+        },
+        {
+            code: "class C { field1 = function() {}\n[field2]; }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    line: 2,
+                    column: 1,
+                    endLine: 2,
+                    endColumn: 2,
+                    messageId: "property"
+                }
+            ]
         }
+
+        // "class C { field1 = obj\n*gen() {} }" is syntax error: Unexpected token '{'
     ]
 });
index 70c04d039d0f75f1974f24d45928cdf4723ec120..90cc45d5f33f76d4038c596422b44ddf79281810 100644 (file)
 const rule = require("../../../lib/rules/no-unmodified-loop-condition"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("no-unmodified-loop-condition", rule, {
index e1609c8a81193776a53241f01468d6372c670c35..03816d7192fc2bc0179094872aa61e80caa57979 100644 (file)
@@ -65,6 +65,26 @@ ruleTester.run("no-unreachable", rule, {
             parserOptions: {
                 ecmaVersion: 6
             }
+        },
+        {
+            code: "class C { foo = reachable; }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo = reachable; constructor() {} }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C extends B { foo = reachable; }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C extends B { foo = reachable; constructor() { super(); } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C extends B { static foo = reachable; constructor() {} }",
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -302,6 +322,67 @@ ruleTester.run("no-unreachable", rule, {
                     endColumn: 22
                 }
             ]
+        },
+
+        /*
+         * If `extends` exists, constructor exists, and the constructor doesn't
+         * contain `super()`, then the fields are unreachable because the
+         * evaluation of `super()` initializes fields in that case.
+         * In most cases, such an instantiation throws runtime errors, but
+         * doesn't throw if the constructor returns a value.
+         */
+        {
+            code: "class C extends B { foo; constructor() {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unreachableCode", column: 21, endColumn: 25 }]
+        },
+        {
+            code: "class C extends B { foo = unreachable + code; constructor() {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unreachableCode", column: 21, endColumn: 46 }]
+        },
+        {
+            code: "class C extends B { foo; bar; constructor() {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unreachableCode", column: 21, endColumn: 30 }]
+        },
+        {
+            code: "class C extends B { foo; constructor() {} bar; }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "unreachableCode", column: 21, endColumn: 25 },
+                { messageId: "unreachableCode", column: 43, endColumn: 47 }
+            ]
+        },
+        {
+            code: "(class extends B { foo; constructor() {} bar; })",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "unreachableCode", column: 20, endColumn: 24 },
+                { messageId: "unreachableCode", column: 42, endColumn: 46 }
+            ]
+        },
+        {
+            code: "class B extends A { x; constructor() { class C extends D { [super().x]; constructor() {} } } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "unreachableCode", column: 60, endColumn: 72 }
+            ]
+        },
+        {
+            code: "class B extends A { x; constructor() { class C extends super().x { y; constructor() {} } } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "unreachableCode", column: 68, endColumn: 70 }
+            ]
+        },
+        {
+            code: "class B extends A { x; static y; z; static q; constructor() {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "unreachableCode", column: 21, endColumn: 23 },
+                { messageId: "unreachableCode", column: 34, endColumn: 36 }
+            ]
         }
     ]
 });
index 38a4f2462309dc26d77f54dc9c720615094d811b..95b87a9972abdc1fe7a638eb954f7385a19e273b 100644 (file)
@@ -6,9 +6,12 @@
 "use strict";
 
 const rule = require("../../../lib/rules/no-unsafe-optional-chaining");
-
 const { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const parserOptions = {
     ecmaVersion: 2021,
     sourceType: "module"
index 6f49a0a24fba523a69185ea53c30da5c86ed4cdf..7cf11a9a3eac3aaf533991e08cbdf414a952a323 100644 (file)
@@ -190,6 +190,29 @@ ruleTester.run("no-unused-expressions", rule, {
             options: [{ enforceForJSX: true }],
             parserOptions: { ecmaFeatures: { jsx: true } },
             errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }]
+        },
+
+        // class static blocks do not have directive prologues
+        {
+            code: "class C { static { 'use strict'; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }]
+        },
+        {
+            code: "class C { static { \n'foo'\n'bar'\n } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "unusedExpression",
+                    type: "ExpressionStatement",
+                    line: 2
+                },
+                {
+                    messageId: "unusedExpression",
+                    type: "ExpressionStatement",
+                    line: 3
+                }
+            ]
         }
     ]
 });
diff --git a/eslint/tests/lib/rules/no-unused-private-class-members.js b/eslint/tests/lib/rules/no-unused-private-class-members.js
new file mode 100644 (file)
index 0000000..6e80baa
--- /dev/null
@@ -0,0 +1,390 @@
+/**
+ * @fileoverview Tests for no-unused-private-class-members rule.
+ * @author Tim van der Lippe
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const rule = require("../../../lib/rules/no-unused-private-class-members"),
+    { RuleTester } = require("../../../lib/rule-tester");
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
+
+/**
+ * Returns an expected error for defined-but-not-used private class member.
+ * @param {string} classMemberName The name of the class member
+ * @returns {Object} An expected error object
+ */
+function definedError(classMemberName) {
+    return {
+        messageId: "unusedPrivateClassMember",
+        data: {
+            classMemberName: `#${classMemberName}`
+        }
+    };
+}
+
+ruleTester.run("no-unused-private-class-members", rule, {
+    valid: [
+        "class Foo {}",
+        `class Foo {
+    publicMember = 42;
+}`,
+        `class Foo {
+    #usedMember = 42;
+    method() {
+        return this.#usedMember;
+    }
+}`,
+        `class Foo {
+    #usedMember = 42;
+    anotherMember = this.#usedMember;
+}`,
+        `class Foo {
+    #usedMember = 42;
+    foo() {
+        anotherMember = this.#usedMember;
+    }
+}`,
+        `class C {
+    #usedMember;
+
+    foo() {
+        bar(this.#usedMember += 1);
+    }
+}`,
+        `class Foo {
+    #usedMember = 42;
+    method() {
+        return someGlobalMethod(this.#usedMember);
+    }
+}`,
+        `class C {
+    #usedInOuterClass;
+
+    foo() {
+        return class {};
+    }
+
+    bar() {
+        return this.#usedInOuterClass;
+    }
+}`,
+        `class Foo {
+    #usedInForInLoop;
+    method() {
+        for (const bar in this.#usedInForInLoop) {
+
+        }
+    }
+}`,
+        `class Foo {
+    #usedInForOfLoop;
+    method() {
+        for (const bar of this.#usedInForOfLoop) {
+
+        }
+    }
+}`,
+        `class Foo {
+    #usedInAssignmentPattern;
+    method() {
+        [bar = 1] = this.#usedInAssignmentPattern;
+    }
+}`,
+        `class Foo {
+    #usedInArrayPattern;
+    method() {
+        [bar] = this.#usedInArrayPattern;
+    }
+}`,
+        `class Foo {
+    #usedInAssignmentPattern;
+    method() {
+        [bar] = this.#usedInAssignmentPattern;
+    }
+}`,
+        `class C {
+    #usedInObjectAssignment;
+
+    method() {
+        ({ [this.#usedInObjectAssignment]: a } = foo);
+    }
+}`,
+        `class C {
+    set #accessorWithSetterFirst(value) {
+        doSomething(value);
+    }
+    get #accessorWithSetterFirst() {
+        return something();
+    }
+    method() {
+        this.#accessorWithSetterFirst += 1;
+    }
+}`,
+        `class Foo {
+    set #accessorUsedInMemberAccess(value) {}
+
+    method(a) {
+        [this.#accessorUsedInMemberAccess] = a;
+    }
+}`,
+        `class C {
+    get #accessorWithGetterFirst() {
+        return something();
+    }
+    set #accessorWithGetterFirst(value) {
+        doSomething(value);
+    }
+    method() {
+        this.#accessorWithGetterFirst += 1;
+    }
+}`,
+        `class C {
+    #usedInInnerClass;
+
+    method(a) {
+        return class {
+            foo = a.#usedInInnerClass;
+        }
+    }
+}`,
+
+        //--------------------------------------------------------------------------
+        // Method definitions
+        //--------------------------------------------------------------------------
+        `class Foo {
+    #usedMethod() {
+        return 42;
+    }
+    anotherMethod() {
+        return this.#usedMethod();
+    }
+}`,
+        `class C {
+    set #x(value) {
+        doSomething(value);
+    }
+
+    foo() {
+        this.#x = 1;
+    }
+}`
+    ],
+    invalid: [
+        {
+            code: `class Foo {
+    #unusedMember = 5;
+}`,
+            errors: [definedError("unusedMember")]
+        },
+        {
+            code: `class First {}
+class Second {
+    #unusedMemberInSecondClass = 5;
+}`,
+            errors: [definedError("unusedMemberInSecondClass")]
+        },
+        {
+            code: `class First {
+    #unusedMemberInFirstClass = 5;
+}
+class Second {}`,
+            errors: [definedError("unusedMemberInFirstClass")]
+        },
+        {
+            code: `class First {
+    #firstUnusedMemberInSameClass = 5;
+    #secondUnusedMemberInSameClass = 5;
+}`,
+            errors: [definedError("firstUnusedMemberInSameClass"), definedError("secondUnusedMemberInSameClass")]
+        },
+        {
+            code: `class Foo {
+    #usedOnlyInWrite = 5;
+    method() {
+        this.#usedOnlyInWrite = 42;
+    }
+}`,
+            errors: [definedError("usedOnlyInWrite")]
+        },
+        {
+            code: `class Foo {
+    #usedOnlyInWriteStatement = 5;
+    method() {
+        this.#usedOnlyInWriteStatement += 42;
+    }
+}`,
+            errors: [definedError("usedOnlyInWriteStatement")]
+        },
+        {
+            code: `class C {
+    #usedOnlyInIncrement;
+
+    foo() {
+        this.#usedOnlyInIncrement++;
+    }
+}`,
+            errors: [definedError("usedOnlyInIncrement")]
+        },
+        {
+            code: `class C {
+    #unusedInOuterClass;
+
+    foo() {
+        return class {
+            #unusedInOuterClass;
+
+            bar() {
+                return this.#unusedInOuterClass;
+            }
+        };
+    }
+}`,
+            errors: [definedError("unusedInOuterClass")]
+        },
+        {
+            code: `class C {
+    #unusedOnlyInSecondNestedClass;
+
+    foo() {
+        return class {
+            #unusedOnlyInSecondNestedClass;
+
+            bar() {
+                return this.#unusedOnlyInSecondNestedClass;
+            }
+        };
+    }
+
+    baz() {
+        return this.#unusedOnlyInSecondNestedClass;
+    }
+
+    bar() {
+        return class {
+            #unusedOnlyInSecondNestedClass;
+        }
+    }
+}`,
+            errors: [definedError("unusedOnlyInSecondNestedClass")]
+        },
+
+        //--------------------------------------------------------------------------
+        // Unused method definitions
+        //--------------------------------------------------------------------------
+        {
+            code: `class Foo {
+    #unusedMethod() {}
+}`,
+            errors: [definedError("unusedMethod")]
+        },
+        {
+            code: `class Foo {
+    #unusedMethod() {}
+    #usedMethod() {
+        return 42;
+    }
+    publicMethod() {
+        return this.#usedMethod();
+    }
+}`,
+            errors: [definedError("unusedMethod")]
+        },
+        {
+            code: `class Foo {
+    set #unusedSetter(value) {}
+}`,
+            errors: [definedError("unusedSetter")]
+        },
+        {
+            code: `class Foo {
+    #unusedForInLoop;
+    method() {
+        for (this.#unusedForInLoop in bar) {
+
+        }
+    }
+}`,
+            errors: [definedError("unusedForInLoop")]
+        },
+        {
+            code: `class Foo {
+    #unusedForOfLoop;
+    method() {
+        for (this.#unusedForOfLoop of bar) {
+
+        }
+    }
+}`,
+            errors: [definedError("unusedForOfLoop")]
+        },
+        {
+            code: `class Foo {
+    #unusedInDestructuring;
+    method() {
+        ({ x: this.#unusedInDestructuring } = bar);
+    }
+}`,
+            errors: [definedError("unusedInDestructuring")]
+        },
+        {
+            code: `class Foo {
+    #unusedInRestPattern;
+    method() {
+        [...this.#unusedInRestPattern] = bar;
+    }
+}`,
+            errors: [definedError("unusedInRestPattern")]
+        },
+        {
+            code: `class Foo {
+    #unusedInAssignmentPattern;
+    method() {
+        [this.#unusedInAssignmentPattern = 1] = bar;
+    }
+}`,
+            errors: [definedError("unusedInAssignmentPattern")]
+        },
+        {
+            code: `class Foo {
+    #unusedInAssignmentPattern;
+    method() {
+        [this.#unusedInAssignmentPattern] = bar;
+    }
+}`,
+            errors: [definedError("unusedInAssignmentPattern")]
+        },
+        {
+            code: `class C {
+    #usedOnlyInTheSecondInnerClass;
+
+    method(a) {
+        return class {
+            #usedOnlyInTheSecondInnerClass;
+
+            method2(b) {
+                foo = b.#usedOnlyInTheSecondInnerClass;
+            }
+
+            method3(b) {
+                foo = b.#usedOnlyInTheSecondInnerClass;
+            }
+        }
+    }
+}`,
+            errors: [{
+                ...definedError("usedOnlyInTheSecondInnerClass"),
+                line: 2
+            }]
+        }
+    ]
+});
index 48ccdb1d42ff9ad8517435062a1dfd7e41b7633d..7c5c87102a9062205f663fce385846703318c80a 100644 (file)
@@ -180,6 +180,7 @@ ruleTester.run("no-unused-vars", rule, {
         // Sequence Expressions (See https://github.com/eslint/eslint/issues/14325)
         { code: "let x = 0; foo = (0, x++);", parserOptions: { ecmaVersion: 6 } },
         { code: "let x = 0; foo = (0, x += 1);", parserOptions: { ecmaVersion: 6 } },
+        { code: "let x = 0; foo = (0, x = x + 1);", parserOptions: { ecmaVersion: 6 } },
 
         // caughtErrors
         {
@@ -1064,6 +1065,55 @@ ruleTester.run("no-unused-vars", rule, {
             parserOptions: { ecmaVersion: 2015 },
             errors: [{ ...assignedError("x"), line: 1, column: 23 }]
         },
+
+        // https://github.com/eslint/eslint/issues/14866
+        {
+            code: `let z = 0;
+            z = z + 1, z = 2;
+            `,
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ ...assignedError("z"), line: 2, column: 24 }]
+        },
+        {
+            code: `let z = 0;
+            z = z+1, z = 2;
+            z = 3;`,
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ ...assignedError("z"), line: 3, column: 13 }]
+        },
+        {
+            code: `let z = 0;
+            z = z+1, z = 2;
+            z = z+3;
+            `,
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ ...assignedError("z"), line: 3, column: 13 }]
+        },
+        {
+            code: "let x = 0; 0, x = x+1;",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ ...assignedError("x"), line: 1, column: 15 }]
+        },
+        {
+            code: "let x = 0; x = x+1, 0;",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ ...assignedError("x"), line: 1, column: 12 }]
+        },
+        {
+            code: "let x = 0; foo = ((0, x = x + 1), 0);",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ ...assignedError("x"), line: 1, column: 23 }]
+        },
+        {
+            code: "let x = 0; foo = (x = x+1, 0);",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ ...assignedError("x"), line: 1, column: 19 }]
+        },
+        {
+            code: "let x = 0; 0, (1, x=x+1);",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ ...assignedError("x"), line: 1, column: 19 }]
+        },
         {
             code: "(function ({ a, b }, { c } ) { return b; })();",
             parserOptions: { ecmaVersion: 2015 },
index 152fa991b9f7c96e7259fc8da243d9c7e81cf0d1..ba80e4075c24ce83273ec3825c8b4310d2730cc7 100644 (file)
@@ -20,6 +20,9 @@ const ruleTester = new RuleTester();
 
 ruleTester.run("no-use-before-define", rule, {
     valid: [
+        "unresolved",
+        "Array",
+        "function foo () { arguments; }",
         "var a=10; alert(a);",
         "function b(a) { alert(a); }",
         "Object.hasOwnProperty.call(a);",
@@ -35,6 +38,8 @@ ruleTester.run("no-use-before-define", rule, {
         "var foo = function() { foo(); };",
         "var a; for (a in a) {}",
         { code: "var a; for (a of a) {}", parserOptions: { ecmaVersion: 6 } },
+        { code: "let a; class C { static { a; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { let a; a; } }", parserOptions: { ecmaVersion: 2022 } },
 
         // Block-level bindings
         { code: "\"use strict\"; a(); { function a() {} }", parserOptions: { ecmaVersion: 6 } },
@@ -56,6 +61,154 @@ ruleTester.run("no-use-before-define", rule, {
             code: "var foo = () => bar; var bar;",
             options: [{ variables: false }],
             parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "class C { static { () => foo; let foo; } }",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+
+        // Tests related to class definition evaluation. These are not TDZ errors.
+        { code: "class C extends (class { method() { C; } }) {}", parserOptions: { ecmaVersion: 6 } },
+        { code: "(class extends (class { method() { C; } }) {});", parserOptions: { ecmaVersion: 6 } },
+        { code: "const C = (class extends (class { method() { C; } }) {});", parserOptions: { ecmaVersion: 6 } },
+        { code: "class C extends (class { field = C; }) {}", parserOptions: { ecmaVersion: 2022 } },
+        { code: "(class extends (class { field = C; }) {});", parserOptions: { ecmaVersion: 2022 } },
+        { code: "const C = (class extends (class { field = C; }) {});", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { [() => C](){} }", parserOptions: { ecmaVersion: 6 } },
+        { code: "(class C { [() => C](){} });", parserOptions: { ecmaVersion: 6 } },
+        { code: "const C = class { [() => C](){} };", parserOptions: { ecmaVersion: 6 } },
+        { code: "class C { static [() => C](){} }", parserOptions: { ecmaVersion: 6 } },
+        { code: "(class C { static [() => C](){} });", parserOptions: { ecmaVersion: 6 } },
+        { code: "const C = class { static [() => C](){} };", parserOptions: { ecmaVersion: 6 } },
+        { code: "class C { [() => C]; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "(class C { [() => C]; });", parserOptions: { ecmaVersion: 2022 } },
+        { code: "const C = class { [() => C]; };", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static [() => C]; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "(class C { static [() => C]; });", parserOptions: { ecmaVersion: 2022 } },
+        { code: "const C = class { static [() => C]; };", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { method() { C; } }", parserOptions: { ecmaVersion: 6 } },
+        { code: "(class C { method() { C; } });", parserOptions: { ecmaVersion: 6 } },
+        { code: "const C = class { method() { C; } };", parserOptions: { ecmaVersion: 6 } },
+        { code: "class C { static method() { C; } }", parserOptions: { ecmaVersion: 6 } },
+        { code: "(class C { static method() { C; } });", parserOptions: { ecmaVersion: 6 } },
+        { code: "const C = class { static method() { C; } };", parserOptions: { ecmaVersion: 6 } },
+        { code: "class C { field = C; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "(class C { field = C; });", parserOptions: { ecmaVersion: 2022 } },
+        { code: "const C = class { field = C; };", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static field = C; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "(class C { static field = C; });", parserOptions: { ecmaVersion: 2022 } }, // `const C = class { static field = C; };` is TDZ error
+        { code: "class C { static field = class { static field = C; }; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "(class C { static field = class { static field = C; }; });", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { field = () => C; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "(class C { field = () => C; });", parserOptions: { ecmaVersion: 2022 } },
+        { code: "const C = class { field = () => C; };", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static field = () => C; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "(class C { static field = () => C; });", parserOptions: { ecmaVersion: 2022 } },
+        { code: "const C = class { static field = () => C; };", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { field = class extends C {}; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "(class C { field = class extends C {}; });", parserOptions: { ecmaVersion: 2022 } },
+        { code: "const C = class { field = class extends C {}; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static field = class extends C {}; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "(class C { static field = class extends C {}; });", parserOptions: { ecmaVersion: 2022 } }, // `const C = class { static field = class extends C {}; };` is TDZ error
+        { code: "class C { static field = class { [C]; }; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "(class C { static field = class { [C]; }; });", parserOptions: { ecmaVersion: 2022 } }, // `const C = class { static field = class { [C]; } };` is TDZ error
+        { code: "const C = class { static field = class { field = C; }; };", parserOptions: { ecmaVersion: 2022 } },
+        {
+            code: "class C { method() { a; } } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "class C { static method() { a; } } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "class C { field = a; } let a;", // `class C { static field = a; } let a;` is TDZ error
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { field = D; } class D {}", // `class C { static field = D; } class D {}` is TDZ error
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { field = class extends D {}; } class D {}", // `class C { static field = class extends D {}; } class D {}` is TDZ error
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { field = () => a; } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static field = () => a; } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { field = () => D; } class D {}",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static field = () => D; } class D {}",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static field = class { field = a; }; } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { C; } }", // `const C = class { static { C; } }` is TDZ error
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { C; } static {} static { C; } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "(class C { static { C; } })",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { class D extends C {} } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { (class { static { C } }) } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { () => C; } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "(class C { static { () => C; } })",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "const C = class { static { () => C; } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { () => D; } } class D {}",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { () => a; } } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "const C = class C { static { C.x; } }",
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -495,6 +648,480 @@ ruleTester.run("no-use-before-define", rule, {
                 messageId: "usedBeforeDefined",
                 data: { name: "x" }
             }]
+        },
+
+        // Tests related to class definition evaluation. These are TDZ errors.
+        {
+            code: "class C extends C {}",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class extends C {};",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "class C extends (class { [C](){} }) {}",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class extends (class { [C](){} }) {};",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "class C extends (class { static field = C; }) {}",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class extends (class { static field = C; }) {};",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "class C { [C](){} }",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "(class C { [C](){} });",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class { [C](){} };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "class C { static [C](){} }",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "(class C { static [C](){} });",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class { static [C](){} };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "class C { [C]; }",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "(class C { [C]; });",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class { [C]; };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "class C { [C] = foo; }",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "(class C { [C] = foo; });",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class { [C] = foo; };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "class C { static [C]; }",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "(class C { static [C]; });",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class { static [C]; };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "class C { static [C] = foo; }",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "(class C { static [C] = foo; });",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class { static [C] = foo; };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class { static field = C; };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class { static field = class extends C {}; };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class { static field = class { [C]; } };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class { static field = class { static field = C; }; };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "class C extends D {} class D {}",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "D" }
+            }]
+        },
+        {
+            code: "class C extends (class { [a](){} }) {} let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C extends (class { static field = a; }) {} let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C { [a]() {} } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C { static [a]() {} } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C { [a]; } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C { static [a]; } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C { [a] = foo; } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C { static [a] = foo; } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C { static field = a; } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C { static field = D; } class D {}",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "D" }
+            }]
+        },
+        {
+            code: "class C { static field = class extends D {}; } class D {}",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "D" }
+            }]
+        },
+        {
+            code: "class C { static field = class { [a](){} } } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C { static field = class { static field = a; }; } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "const C = class { static { C; } };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "const C = class { static { (class extends C {}); } };",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "C" }
+            }]
+        },
+        {
+            code: "class C { static { a; } } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C { static { D; } } class D {}",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "D" }
+            }]
+        },
+        {
+            code: "class C { static { (class extends D {}); } } class D {}",
+            options: [{ classes: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "D" }
+            }]
+        },
+        {
+            code: "class C { static { (class { [a](){} }); } } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
+        },
+        {
+            code: "class C { static { (class { static field = a; }); } } let a;",
+            options: [{ variables: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "usedBeforeDefined",
+                data: { name: "a" }
+            }]
         }
+
+        /*
+         * TODO(mdjermanovic): Add the following test cases once https://github.com/eslint/eslint-scope/issues/59 gets fixed:
+         * {
+         *  code: "(class C extends C {});",
+         *  options: [{ classes: false }],
+         *  parserOptions: { ecmaVersion: 6 },
+         *  errors: [{
+         *      messageId: "usedBeforeDefined",
+         *      data: { name: "C" }
+         *  }]
+         * },
+         * {
+         *  code: "(class C extends (class { [C](){} }) {});",
+         *  options: [{ classes: false }],
+         *  parserOptions: { ecmaVersion: 6 },
+         *  errors: [{
+         *      messageId: "usedBeforeDefined",
+         *      data: { name: "C" }
+         *  }]
+         * },
+         * {
+         *  code: "(class C extends (class { static field = C; }) {});",
+         *  options: [{ classes: false }],
+         *  parserOptions: { ecmaVersion: 2022 },
+         *  errors: [{
+         *      messageId: "usedBeforeDefined",
+         *      data: { name: "C" }
+         *  }]
+         * }
+         */
     ]
 });
index 528f2f1b1292cb2e854d94f2253a1fd2dab9b793..43f9b7d71ea44111d8b85fb590bd1503f61e133b 100644 (file)
@@ -50,6 +50,12 @@ ruleTester.run("no-useless-call", rule, {
         {
             code: "obj?.foo.bar.call(obj.foo, 1, 2);",
             parserOptions: { ecmaVersion: 2020 }
+        },
+
+        // Private members
+        {
+            code: "class C { #call; wrap(foo) { foo.#call(undefined, 1, 2); } }",
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
index 100899c880e6a8730a4c8d3e1a46da86e94fd603..37f311188781045f50bc9238da0f59acb7e1d16f 100644 (file)
@@ -16,7 +16,7 @@ const rule = require("../../../lib/rules/no-useless-computed-key"),
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("no-useless-computed-key", rule, {
     valid: [
@@ -41,6 +41,10 @@ ruleTester.run("no-useless-computed-key", rule, {
         { code: "(class { ['x']() {} })", options: [{ enforceForClassMembers: false }] },
         { code: "class Foo { static ['constructor']() {} }", options: [{ enforceForClassMembers: false }] },
         { code: "class Foo { ['prototype']() {} }", options: [{ enforceForClassMembers: false }] },
+        { code: "class Foo { a }", options: [{ enforceForClassMembers: true }] },
+        { code: "class Foo { ['constructor'] }", options: [{ enforceForClassMembers: true }] },
+        { code: "class Foo { static ['constructor'] }", options: [{ enforceForClassMembers: true }] },
+        { code: "class Foo { static ['prototype'] }", options: [{ enforceForClassMembers: true }] },
 
         /*
          * Well-known browsers throw syntax error bigint literals on property names,
@@ -240,6 +244,22 @@ ruleTester.run("no-useless-computed-key", rule, {
                 data: { property: "2" },
                 type: "Property"
             }]
+        }, {
+            code: "({ ['constructor']: 1 })",
+            output: "({ 'constructor': 1 })",
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'constructor'" },
+                type: "Property"
+            }]
+        }, {
+            code: "({ ['prototype']: 1 })",
+            output: "({ 'prototype': 1 })",
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'prototype'" },
+                type: "Property"
+            }]
         }, {
             code: "class Foo { ['0']() {} }",
             output: "class Foo { '0'() {} }",
@@ -460,6 +480,24 @@ ruleTester.run("no-useless-computed-key", rule, {
                 data: { property: "'x'" },
                 type: "MethodDefinition"
             }]
+        }, {
+            code: "(class { ['__proto__']() {} })",
+            output: "(class { '__proto__'() {} })",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'__proto__'" },
+                type: "MethodDefinition"
+            }]
+        }, {
+            code: "(class { static ['__proto__']() {} })",
+            output: "(class { static '__proto__'() {} })",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'__proto__'" },
+                type: "MethodDefinition"
+            }]
         }, {
             code: "(class { static ['constructor']() {} })",
             output: "(class { static 'constructor'() {} })",
@@ -478,6 +516,69 @@ ruleTester.run("no-useless-computed-key", rule, {
                 data: { property: "'prototype'" },
                 type: "MethodDefinition"
             }]
+        }, {
+            code: "class Foo { ['0'] }",
+            output: "class Foo { '0' }",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'0'" },
+                type: "PropertyDefinition"
+            }]
+        }, {
+            code: "class Foo { ['0'] = 0 }",
+            output: "class Foo { '0' = 0 }",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'0'" },
+                type: "PropertyDefinition"
+            }]
+        }, {
+            code: "class Foo { static[0] }",
+            output: "class Foo { static 0 }",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "0" },
+                type: "PropertyDefinition"
+            }]
+        }, {
+            code: "class Foo { ['#foo'] }",
+            output: "class Foo { '#foo' }",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'#foo'" },
+                type: "PropertyDefinition"
+            }]
+        }, {
+            code: "(class { ['__proto__'] })",
+            output: "(class { '__proto__' })",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'__proto__'" },
+                type: "PropertyDefinition"
+            }]
+        }, {
+            code: "(class { static ['__proto__'] })",
+            output: "(class { static '__proto__' })",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'__proto__'" },
+                type: "PropertyDefinition"
+            }]
+        }, {
+            code: "(class { ['prototype'] })",
+            output: "(class { 'prototype' })",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'prototype'" },
+                type: "PropertyDefinition"
+            }]
         }
     ]
 });
index c15b88f18172b777f3bc8b586738ccca7cbef4b7..f19a1965673c7cf46df2c5cacc6fceca5be02824 100644 (file)
@@ -9,7 +9,6 @@
 //------------------------------------------------------------------------------
 
 const rule = require("../../../lib/rules/no-useless-concat"),
-
     { RuleTester } = require("../../../lib/rule-tester");
 
 
index ac3294b8c7f61cf49ce889f218cc1e9c4534819f..09f146ef8c669beb19ca9127664a31d830e17900 100644 (file)
 const rule = require("../../../lib/rules/no-useless-escape"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("no-useless-escape", rule, {
index f09a9d448cac580e537349067b26398583673b19..66077f2b4974861b554e5c0fab82fca5b5f6d7db 100644 (file)
 const rule = require("../../../lib/rules/no-useless-rename"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6, sourceType: "module" } });
 
 ruleTester.run("no-useless-rename", rule, {
index 3388c9d6489ab60e70ce953facafb665d9509fb3..935c464b3d0e4cf62897f2fcbf122fd7655b0bdc 100644 (file)
@@ -14,6 +14,10 @@ const rule = require("../../../lib/rules/one-var"),
 
 const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 ruleTester.run("one-var", rule, {
     valid: [
         "function foo() { var bar = true; }",
@@ -489,6 +493,143 @@ ruleTester.run("one-var", rule, {
         {
             code: "var foo = 1;\nlet bar = function() { var x; };\nvar baz = 2;",
             options: [{ var: "never" }]
+        },
+
+        // class static blocks
+        {
+            code: "class C { static { var a; let b; const c = 0; } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "const a = 0; class C { static { const b = 0; } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { const b = 0; } } const a = 0; ",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "let a; class C { static { let b; } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let b; } } let a;",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "var a; class C { static { var b; } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { var b; } } var a; ",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "var a; class C { static { if (foo) { var b; } } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { if (foo) { var b; } } } var a; ",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { const a = 0; if (foo) { const b = 0; } } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let a; if (foo) { let b; } } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { const a = 0; const b = 0; } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let a; let b; } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { var a; var b; } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let a; foo; let b; } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let a; const b = 0; let c; } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { var a; foo; var b; } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { var a; let b; var c; } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let a; if (foo) { let b; } } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { if (foo) { let b; } let a;  } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { const a = 0; if (foo) { const b = 0; } } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { if (foo) { const b = 0; } const a = 0; } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { var a; if (foo) var b; } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { if (foo) var b; var a; } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { if (foo) { var b; } var a; } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let a; let b = 0; } }",
+            options: [{ initialized: "consecutive" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { var a; var b = 0; } }",
+            options: [{ initialized: "consecutive" }],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -2114,6 +2255,129 @@ ruleTester.run("one-var", rule, {
                 data: { type: "var" },
                 type: "VariableDeclaration"
             }]
+        },
+
+        // class static blocks
+        {
+            code: "class C { static { let x, y; } }",
+            output: "class C { static { let x; let y; } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "split",
+                data: { type: "let" },
+                type: "VariableDeclaration"
+            }]
+        },
+        {
+            code: "class C { static { var x, y; } }",
+            output: "class C { static { var x; var y; } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "split",
+                data: { type: "var" },
+                type: "VariableDeclaration"
+            }]
+        },
+        {
+            code: "class C { static { let x; let y; } }",
+            output: "class C { static { let x,  y; } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "combine",
+                data: { type: "let" },
+                type: "VariableDeclaration"
+            }]
+        },
+        {
+            code: "class C { static { var x; var y; } }",
+            output: "class C { static { var x,  y; } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "combine",
+                data: { type: "var" },
+                type: "VariableDeclaration"
+            }]
+        },
+        {
+            code: "class C { static { let x; foo; let y; } }",
+            output: null,
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "combine",
+                data: { type: "let" },
+                type: "VariableDeclaration"
+            }]
+        },
+        {
+            code: "class C { static { var x; foo; var y; } }",
+            output: null,
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "combine",
+                data: { type: "var" },
+                type: "VariableDeclaration"
+            }]
+        },
+        {
+            code: "class C { static { var x; if (foo) { var y; } } }",
+            output: null,
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "combine",
+                data: { type: "var" },
+                type: "VariableDeclaration"
+            }]
+        },
+        {
+            code: "class C { static { let x; let y; } }",
+            output: "class C { static { let x,  y; } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "combine",
+                data: { type: "let" },
+                type: "VariableDeclaration"
+            }]
+        },
+        {
+            code: "class C { static { var x; var y; } }",
+            output: "class C { static { var x,  y; } }",
+            options: ["consecutive"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "combine",
+                data: { type: "var" },
+                type: "VariableDeclaration"
+            }]
+        },
+        {
+            code: "class C { static { let a = 0; let b = 1; } }",
+            output: "class C { static { let a = 0,  b = 1; } }",
+            options: [{ initialized: "consecutive" }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "combineInitialized",
+                data: { type: "let" },
+                type: "VariableDeclaration"
+            }]
+        },
+        {
+            code: "class C { static { var a = 0; var b = 1; } }",
+            output: "class C { static { var a = 0,  b = 1; } }",
+            options: [{ initialized: "consecutive" }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "combineInitialized",
+                data: { type: "var" },
+                type: "VariableDeclaration"
+            }]
         }
     ]
 });
index 1d07b0cd8ed32886c8c0bf4f239466280108b846..8f814840e299277b51aa41e637fcbb534414df13 100644 (file)
@@ -16,7 +16,7 @@ const rule = require("../../../lib/rules/operator-assignment"),
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2021 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 const EXPECTED_OPERATOR_ASSIGNMENT = [{ messageId: "replaced", type: "AssignmentExpression" }];
 const UNEXPECTED_OPERATOR_ASSIGNMENT = [{ messageId: "unexpected", type: "AssignmentExpression" }];
@@ -85,6 +85,7 @@ ruleTester.run("operator-assignment", rule, {
             code: "this.x = foo.this.x + y",
             options: ["always"]
         },
+        "const foo = 0; class C { foo = foo + 1; }",
 
         // does not check logical operators
         {
index 1eeb9f5790e31fb9f15d34b94ff9b9b16a9eccec..8810c040776faccf0ed24337038759ab087218d7 100644 (file)
@@ -56,6 +56,7 @@ ruleTester.run("operator-linebreak", rule, {
         { code: "\n1 + 1", options: ["none"] },
         { code: "1 + 1\n", options: ["none"] },
         { code: "answer = everything ? 42 : foo;", options: ["none"] },
+        { code: "(a\n) + (\nb)", options: ["none"] },
         { code: "answer = everything \n?\n 42 : foo;", options: [null, { overrides: { "?": "ignore" } }] },
         { code: "answer = everything ? 42 \n:\n foo;", options: [null, { overrides: { ":": "ignore" } }] },
 
@@ -98,6 +99,52 @@ ruleTester.run("operator-linebreak", rule, {
             code: "a ??= \n b",
             options: ["after", { overrides: { "??": "before" } }],
             parserOptions: { ecmaVersion: 2021 }
+        },
+
+        // class fields
+        {
+            code: "class C { foo =\n0 }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo\n= 0 }",
+            options: ["before"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [foo\n]= 0 }",
+            options: ["before"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [foo]\n= 0 }",
+            options: ["before"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [foo\n]\n= 0 }",
+            options: ["before"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [foo\n]= 0 }",
+            options: ["after"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [foo\n]=\n0 }",
+            options: ["after"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [foo\n]= 0 }",
+            options: ["none"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo\n=\n0 }",
+            options: ["none", { overrides: { "=": "ignore" } }],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
 
@@ -770,6 +817,98 @@ ruleTester.run("operator-linebreak", rule, {
                 endLine: 2,
                 endColumn: 4
             }]
+        },
+
+        // class fields
+        {
+            code: "class C { a\n= 0; }",
+            output: "class C { a =\n0; }",
+            options: ["after"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "operatorAtEnd",
+                data: { operator: "=" },
+                type: "PropertyDefinition",
+                line: 2,
+                column: 1,
+                endLine: 2,
+                endColumn: 2
+            }]
+        },
+        {
+            code: "class C { a =\n0; }",
+            output: "class C { a\n= 0; }",
+            options: ["before"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "operatorAtBeginning",
+                data: { operator: "=" },
+                type: "PropertyDefinition",
+                line: 1,
+                column: 13,
+                endLine: 1,
+                endColumn: 14
+            }]
+        },
+        {
+            code: "class C { a =\n0; }",
+            output: "class C { a =0; }",
+            options: ["none"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "noLinebreak",
+                data: { operator: "=" },
+                type: "PropertyDefinition",
+                line: 1,
+                column: 13,
+                endLine: 1,
+                endColumn: 14
+            }]
+        },
+        {
+            code: "class C { [a]\n= 0; }",
+            output: "class C { [a] =\n0; }",
+            options: ["after"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "operatorAtEnd",
+                data: { operator: "=" },
+                type: "PropertyDefinition",
+                line: 2,
+                column: 1,
+                endLine: 2,
+                endColumn: 2
+            }]
+        },
+        {
+            code: "class C { [a] =\n0; }",
+            output: "class C { [a]\n= 0; }",
+            options: ["before"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "operatorAtBeginning",
+                data: { operator: "=" },
+                type: "PropertyDefinition",
+                line: 1,
+                column: 15,
+                endLine: 1,
+                endColumn: 16
+            }]
+        },
+        {
+            code: "class C { [a]\n =0; }",
+            output: "class C { [a] =0; }",
+            options: ["none"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "noLinebreak",
+                data: { operator: "=" },
+                type: "PropertyDefinition",
+                line: 2,
+                column: 2,
+                endLine: 2,
+                endColumn: 3
+            }]
         }
     ]
 });
index 1e15da4d24596f6d7219696137c00f3966cd56b8..3dd0dc97e18d5d056f25fa0f5a989acb623fa97e 100644 (file)
@@ -74,6 +74,9 @@ ruleTester.run("padded-blocks", rule, {
         { code: "class A{\nfoo(){}\n}", options: ["never"], parserOptions: { ecmaVersion: 6 } },
         { code: "class A{\nfoo(){}\n}", options: [{ classes: "never" }], parserOptions: { ecmaVersion: 6 } },
 
+        { code: "class A{\n\nfoo;\n\n}", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A{\nfoo;\n}", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+
         // Ignore block statements if not configured
         { code: "{\na();\n}", options: [{ switches: "always" }] },
         { code: "{\n\na();\n\n}", options: [{ switches: "never" }] },
@@ -85,7 +88,109 @@ ruleTester.run("padded-blocks", rule, {
 
         // Ignore class statements if not configured
         { code: "class A{\nfoo(){}\n}", options: [{ blocks: "always" }], parserOptions: { ecmaVersion: 6 } },
-        { code: "class A{\n\nfoo(){}\n\n}", options: [{ blocks: "never" }], parserOptions: { ecmaVersion: 6 } }
+        { code: "class A{\n\nfoo(){}\n\n}", options: [{ blocks: "never" }], parserOptions: { ecmaVersion: 6 } },
+
+        // class static blocks
+        {
+            code: "class C {\n\n static {\n\nfoo;\n\n} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n\n static {// comment\n\nfoo;\n\n/* comment */} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n\n static {\n\n// comment\nfoo;\n// comment\n\n} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n\n static {\n\n// comment\n\nfoo;\n\n// comment\n\n} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n\n static { foo; } \n\n}",
+            options: ["always", { allowSingleLineBlocks: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n\n static\n { foo; } \n\n}",
+            options: ["always", { allowSingleLineBlocks: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n\n static {} static {\n} static {\n\n} \n\n}", // empty blocks are ignored
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n\n static\n\n { foo; } \n\n}",
+            options: ["always", { allowSingleLineBlocks: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {\n\nfoo;\n\n} \n}",
+            options: [{ blocks: "always", classes: "never" }], // "blocks" applies to static blocks
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {\nfoo;\n} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {foo;} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static\n {\nfoo;\n} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static\n\n {\nfoo;\n} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static\n\n {foo;} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {// comment\nfoo;\n/* comment */} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {\n// comment\nfoo;\n// comment\n} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {} static {\n} static {\n\n} \n}", // empty blocks are ignored
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n\n static {\nfoo;\n} \n\n}",
+            options: [{ blocks: "never", classes: "always" }], // "blocks" applies to static blocks
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n\n static {\nfoo;\n} static {\n\nfoo;\n\n} \n\n}",
+            options: [{ classes: "always" }], // if there's no "blocks" in the object option, static blocks are ignored
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {\nfoo;\n} static {\n\nfoo;\n\n} \n}",
+            options: [{ classes: "never" }], // if there's no "blocks" in the object option, static blocks are ignored
+            parserOptions: { ecmaVersion: 2022 }
+        }
 
     ],
     invalid: [
@@ -748,6 +853,177 @@ ruleTester.run("padded-blocks", rule, {
             output: "function foo() { /* a\n */ /* b\n */\n\n  bar;\n\n/* c\n *//* d\n */}",
             options: ["always"],
             errors: [{ messageId: "alwaysPadBlock" }, { messageId: "alwaysPadBlock" }]
+        },
+        {
+            code: "class A{\nfoo;\n}",
+            output: "class A{\n\nfoo;\n\n}",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "alwaysPadBlock" }, { messageId: "alwaysPadBlock" }]
+        },
+        {
+            code: "class A{\n\nfoo;\n\n}",
+            output: "class A{\nfoo;\n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "neverPadBlock" }, { messageId: "neverPadBlock" }]
+        },
+
+        // class static blocks
+        {
+            code: "class C {\n\n static {\nfoo;\n\n} \n\n}",
+            output: "class C {\n\n static {\n\nfoo;\n\n} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "alwaysPadBlock" }]
+        },
+        {
+            code: "class C {\n\n static\n {\nfoo;\n\n} \n\n}",
+            output: "class C {\n\n static\n {\n\nfoo;\n\n} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "alwaysPadBlock" }]
+        },
+        {
+            code: "class C {\n\n static\n\n {\nfoo;\n\n} \n\n}",
+            output: "class C {\n\n static\n\n {\n\nfoo;\n\n} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "alwaysPadBlock" }]
+        },
+        {
+            code: "class C {\n\n static {\n\nfoo;\n} \n\n}",
+            output: "class C {\n\n static {\n\nfoo;\n\n} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "alwaysPadBlock" }]
+        },
+        {
+            code: "class C {\n\n static {foo;} \n\n}",
+            output: "class C {\n\n static {\nfoo;\n} \n\n}", // this is still not padded, the subsequent fix below will add another pair of `\n`.
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "alwaysPadBlock" },
+                { messageId: "alwaysPadBlock" }
+            ]
+        },
+        {
+            code: "class C {\n\n static {\nfoo;\n} \n\n}",
+            output: "class C {\n\n static {\n\nfoo;\n\n} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "alwaysPadBlock" },
+                { messageId: "alwaysPadBlock" }
+            ]
+        },
+        {
+            code: "class C {\n\n static {// comment\nfoo;\n/* comment */} \n\n}",
+            output: "class C {\n\n static {// comment\n\nfoo;\n\n/* comment */} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "alwaysPadBlock" },
+                { messageId: "alwaysPadBlock" }
+            ]
+        },
+        {
+            code: "class C {\n\n static {\n// comment\nfoo;\n// comment\n} \n\n}",
+            output: "class C {\n\n static {\n\n// comment\nfoo;\n// comment\n\n} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "alwaysPadBlock" },
+                { messageId: "alwaysPadBlock" }
+            ]
+        },
+        {
+            code: "class C {\n\n static {\n// comment\n\nfoo;\n\n// comment\n} \n\n}",
+            output: "class C {\n\n static {\n\n// comment\n\nfoo;\n\n// comment\n\n} \n\n}",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "alwaysPadBlock" },
+                { messageId: "alwaysPadBlock" }
+            ]
+        },
+        {
+            code: "class C {\n static {\nfoo;\n} \n}",
+            output: "class C {\n static {\n\nfoo;\n\n} \n}",
+            options: [{ blocks: "always", classes: "never" }], // "blocks" applies to static blocks
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "alwaysPadBlock" },
+                { messageId: "alwaysPadBlock" }
+            ]
+        },
+        {
+            code: "class C {\n static {\n\nfoo;\n} \n}",
+            output: "class C {\n static {\nfoo;\n} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "neverPadBlock" }]
+        },
+        {
+            code: "class C {\n static\n {\n\nfoo;\n} \n}",
+            output: "class C {\n static\n {\nfoo;\n} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "neverPadBlock" }]
+        },
+        {
+            code: "class C {\n static\n\n {\n\nfoo;\n} \n}",
+            output: "class C {\n static\n\n {\nfoo;\n} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "neverPadBlock" }]
+        },
+        {
+            code: "class C {\n static {\nfoo;\n\n} \n}",
+            output: "class C {\n static {\nfoo;\n} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "neverPadBlock" }]
+        },
+        {
+            code: "class C {\n static {\n\nfoo;\n\n} \n}",
+            output: "class C {\n static {\nfoo;\n} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "neverPadBlock" },
+                { messageId: "neverPadBlock" }
+            ]
+        },
+        {
+            code: "class C {\n static {// comment\n\nfoo;\n\n/* comment */} \n}",
+            output: "class C {\n static {// comment\nfoo;\n/* comment */} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "neverPadBlock" },
+                { messageId: "neverPadBlock" }
+            ]
+        },
+        {
+            code: "class C {\n static {\n\n// comment\nfoo;\n// comment\n\n} \n}",
+            output: "class C {\n static {\n// comment\nfoo;\n// comment\n} \n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "neverPadBlock" },
+                { messageId: "neverPadBlock" }
+            ]
+        },
+        {
+            code: "class C {\n\n static {\n\nfoo;\n\n} \n\n}",
+            output: "class C {\n\n static {\nfoo;\n} \n\n}",
+            options: [{ blocks: "never", classes: "always" }], // "blocks" applies to static blocks
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "neverPadBlock" },
+                { messageId: "neverPadBlock" }
+            ]
         }
     ]
 });
index 931cdd24ccb4152823176dec22173da9f3ee8111..51ddf0eb3b50c0403b6526470189e454905a2d40 100644 (file)
@@ -2626,6 +2626,114 @@ ruleTester.run("padding-line-between-statements", rule, {
             options: [
                 { blankLine: "always", prev: "block-like", next: "block-like" }
             ]
+        },
+
+        // class static blocks
+        {
+            code: "class C {\n static {\n let x;\n\n foo();\n }\n }",
+            options: [
+                { blankLine: "always", prev: "let", next: "expression" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {\n let x;\n foo();\n }\n }",
+            options: [
+                { blankLine: "never", prev: "let", next: "expression" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {\n let x;\n foo();\n\n const y = 1;\n }\n }",
+            options: [
+                { blankLine: "always", prev: "expression", next: "const" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {\n let x;\n foo();\n const y = 1;\n }\n }",
+            options: [
+                { blankLine: "never", prev: "expression", next: "const" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {\n let x;\n foo();\n\n const y = 1;\n const z = 1;\n }\n }",
+            options: [
+                { blankLine: "always", prev: "expression", next: "const" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {\n let x;\n foo();\n const y = 1;\n const z = 1;\n }\n }",
+            options: [
+                { blankLine: "never", prev: "expression", next: "const" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C {\n static {\n let a = 0;\n let b =0;\n\n bar();\n }\n }",
+            options: [
+                { blankLine: "always", prev: ["const", "let", "var"], next: "*" },
+                { blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"] }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let x; { let y; } let z; } }",
+            options: [
+                { blankLine: "always", prev: "let", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { method() { let x; } static { let y; } }",
+            options: [
+                { blankLine: "always", prev: "let", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let y; } method() { let x; } }",
+            options: [
+                { blankLine: "always", prev: "let", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let x; } static { let y; } }",
+            options: [
+                { blankLine: "always", prev: "let", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "let x; class C { static { let y; } }",
+            options: [
+                { blankLine: "always", prev: "let", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let x; } } let y;",
+            options: [
+                { blankLine: "always", prev: "let", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let x; } }",
+            options: [
+                { blankLine: "always", prev: "class", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { 'use strict'; let x; } }", // 'use strict'; is "espression", because class static blocks don't have directives
+            options: [
+                { blankLine: "always", prev: "directive", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -4976,6 +5084,117 @@ ruleTester.run("padding-line-between-statements", rule, {
                 { messageId: "expectedBlankLine" },
                 { messageId: "expectedBlankLine" }
             ]
+        },
+
+        // class static blocks
+        {
+            code: "class C {\n static {\n let x;\n foo();\n }\n }",
+            output: "class C {\n static {\n let x;\n\n foo();\n }\n }",
+            options: [
+                { blankLine: "always", prev: "let", next: "expression" }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "expectedBlankLine" }]
+        },
+        {
+            code: "class C {\n static {\n let x;\n\n foo();\n }\n }",
+            output: "class C {\n static {\n let x;\n foo();\n }\n }",
+            options: [
+                { blankLine: "never", prev: "let", next: "expression" }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpectedBlankLine" }]
+        },
+        {
+            code: "class C {\n static {\n let x;\n foo();\n const y = 1;\n }\n }",
+            output: "class C {\n static {\n let x;\n foo();\n\n const y = 1;\n }\n }",
+            options: [
+                { blankLine: "always", prev: "expression", next: "const" }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "expectedBlankLine" }]
+        },
+        {
+            code: "class C {\n static {\n let x;\n foo();\n\n const y = 1;\n }\n }",
+            output: "class C {\n static {\n let x;\n foo();\n const y = 1;\n }\n }",
+            options: [
+                { blankLine: "never", prev: "expression", next: "const" }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpectedBlankLine" }]
+        },
+        {
+            code: "class C {\n static {\n let x;\n foo();\n const y = 1;\n const z = 1;\n }\n }",
+            output: "class C {\n static {\n let x;\n foo();\n\n const y = 1;\n const z = 1;\n }\n }",
+            options: [
+                { blankLine: "always", prev: "expression", next: "const" }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "expectedBlankLine" }]
+        },
+        {
+            code: "class C {\n static {\n let x;\n foo();\n\n const y = 1;\n const z = 1;\n }\n }",
+            output: "class C {\n static {\n let x;\n foo();\n const y = 1;\n const z = 1;\n }\n }",
+            options: [
+                { blankLine: "never", prev: "expression", next: "const" }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpectedBlankLine" }]
+        },
+        {
+            code: "class C {\n static {\n let a = 0;\n bar();\n }\n }",
+            output: "class C {\n static {\n let a = 0;\n\n bar();\n }\n }",
+            options: [
+                { blankLine: "always", prev: ["const", "let", "var"], next: "*" },
+                { blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"] }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "expectedBlankLine" }]
+        },
+        {
+            code: "class C { static { let x; { let y; let z; } let q; } }",
+            output: "class C { static { let x; { let y;\n\n let z; } let q; } }",
+            options: [
+                { blankLine: "always", prev: "let", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "expectedBlankLine" }]
+        },
+        {
+            code: "class C { static { { let x; } let y; let z; } }",
+            output: "class C { static { { let x; } let y;\n\n let z; } }",
+            options: [
+                { blankLine: "always", prev: "let", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "expectedBlankLine" }]
+        },
+        {
+            code: "class C { static { foo(); if (bar) {} } }",
+            output: "class C { static { foo();\n\n if (bar) {} } }",
+            options: [
+                { blankLine: "always", prev: "expression", next: "block-like" }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "expectedBlankLine" }]
+        },
+        {
+            code: "class C { static { let x; } } let y;",
+            output: "class C { static { let x; } }\n\n let y;",
+            options: [
+                { blankLine: "always", prev: "class", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "expectedBlankLine" }]
+        },
+        {
+            code: "class C { static { 'use strict'; let x; } }", // 'use strict'; is "espression", because class static blocks don't have directives
+            output: "class C { static { 'use strict';\n\n let x; } }",
+            options: [
+                { blankLine: "always", prev: "expression", next: "let" }
+            ],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "expectedBlankLine" }]
         }
     ]
 });
index c591d2365834cdf699468ca66e1bac220a0ee5cc..0a44b0bd01778db58e39435a4aea2f2adcb3b4f9 100644 (file)
@@ -173,7 +173,56 @@ ruleTester.run("prefer-const", rule, {
 
         // https://github.com/eslint/eslint/issues/10520
         "const x = [1,2]; let y; [,y] = x; y = 0;",
-        "const x = [1,2,3]; let y, z; [y,,z] = x; y = 0; z = 0;"
+        "const x = [1,2,3]; let y, z; [y,,z] = x; y = 0; z = 0;",
+
+        {
+            code: "class C { static { let a = 1; a = 2; } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let a; a = 1; a = 2; } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "let a; class C { static { a = 1; } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let a; if (foo) { a = 1; } } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let a; if (foo) a = 1; } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { let a, b; if (foo) { ({ a, b } = foo); } } }",
+            output: null,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "useConst", data: { name: "a" }, type: "Identifier" },
+                { messageId: "useConst", data: { name: "b" }, type: "Identifier" }
+            ]
+        },
+        {
+            code: "class C { static { let a, b; if (foo) ({ a, b } = foo); } }",
+            output: null,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "useConst", data: { name: "a" }, type: "Identifier" },
+                { messageId: "useConst", data: { name: "b" }, type: "Identifier" }
+            ]
+        },
+        {
+            code: "class C { static { a; } } let a = 1; ",
+            options: [{ ignoreReadBeforeAssign: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { () => a; let a = 1; } };",
+            options: [{ ignoreReadBeforeAssign: true }],
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
     invalid: [
         {
@@ -555,6 +604,86 @@ ruleTester.run("prefer-const", rule, {
             code: "/*eslint no-undef-init:error*/ let foo = undefined;",
             output: "/*eslint no-undef-init:error*/ const foo = undefined;",
             errors: 2
+        },
+
+        {
+            code: "let a = 1; class C { static { a; } }",
+            output: "const a = 1; class C { static { a; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "useConst", data: { name: "a" }, type: "Identifier" }]
+        },
+        {
+
+            // this is a TDZ error with either `let` or `const`, but that isn't a concern of this rule
+            code: "class C { static { a; } } let a = 1;",
+            output: "class C { static { a; } } const a = 1;",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "useConst", data: { name: "a" }, type: "Identifier" }]
+        },
+        {
+            code: "class C { static { let a = 1; } }",
+            output: "class C { static { const a = 1; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "useConst", data: { name: "a" }, type: "Identifier" }]
+        },
+        {
+            code: "class C { static { if (foo) { let a = 1; } } }",
+            output: "class C { static { if (foo) { const a = 1; } } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "useConst", data: { name: "a" }, type: "Identifier" }]
+        },
+        {
+            code: "class C { static { let a = 1; if (foo) { a; } } }",
+            output: "class C { static { const a = 1; if (foo) { a; } } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "useConst", data: { name: "a" }, type: "Identifier" }]
+        },
+        {
+            code: "class C { static { if (foo) { let a; a = 1; } } }",
+            output: null,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "useConst", data: { name: "a" }, type: "Identifier" }]
+        },
+        {
+            code: "class C { static { let a; a = 1; } }",
+            output: null,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "useConst", data: { name: "a" }, type: "Identifier", column: 27 }]
+        },
+        {
+            code: "class C { static { let { a, b } = foo; } }",
+            output: "class C { static { const { a, b } = foo; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "useConst", data: { name: "a" }, type: "Identifier" },
+                { messageId: "useConst", data: { name: "b" }, type: "Identifier" }
+            ]
+        },
+        {
+            code: "class C { static { let a, b; ({ a, b } = foo); } }",
+            output: null,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "useConst", data: { name: "a" }, type: "Identifier" },
+                { messageId: "useConst", data: { name: "b" }, type: "Identifier" }
+            ]
+        },
+        {
+            code: "class C { static { let a; let b; ({ a, b } = foo); } }",
+            output: null,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "useConst", data: { name: "a" }, type: "Identifier" },
+                { messageId: "useConst", data: { name: "b" }, type: "Identifier" }
+            ]
+        },
+        {
+            code: "class C { static { let a; a = 0; console.log(a); } }",
+            output: null,
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "useConst", data: { name: "a" }, type: "Identifier" }
+            ]
         }
     ]
 });
index 8f111a1d8c2f0f7d4fe1e2292e7c3ddaf86b4118..7f808a642180e514329e08e85d6be090743cbf5f 100644 (file)
@@ -16,7 +16,7 @@ const rule = require("../../../lib/rules/prefer-destructuring"),
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("prefer-destructuring", rule, {
     valid: [
@@ -157,7 +157,40 @@ ruleTester.run("prefer-destructuring", rule, {
 
         // Optional chaining
         "var foo = array?.[0];", // because the fixed code can throw TypeError.
-        "var foo = object?.foo;"
+        "var foo = object?.foo;",
+
+        // Private identifiers
+        "class C { #x; foo() { const x = this.#x; } }",
+        "class C { #x; foo() { x = this.#x; } }",
+        "class C { #x; foo(a) { x = a.#x; } }",
+        {
+            code: "class C { #x; foo() { const x = this.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo() { const y = this.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo() { x = this.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo() { y = this.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo(a) { x = a.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo(a) { y = a.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo() { x = this.a.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        }
     ],
 
     invalid: [
index 7bccdd64b4317d6f5dcec8849281d30392b8a9c7..2de358e2c8fcf705196b719bfb1404cde09736da 100644 (file)
@@ -40,7 +40,7 @@ function invalid(code, output) {
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("prefer-exponentiation-operator", rule, {
     valid: [
@@ -78,7 +78,9 @@ ruleTester.run("prefer-exponentiation-operator", rule, {
                 globalThis.Math.pow(a, b)
             `,
             env: { es2020: true }
-        }
+        },
+
+        "class C { #pow; foo() { Math.#pow(a, b); } }"
     ],
 
     invalid: [
index 39cc11a242551fc9ddcdf50481724a88205aa867..e3cacaa1318bfed734e0a68f36d0838e1323f27f 100644 (file)
@@ -58,6 +58,10 @@ ruleTester.run("prefer-numeric-literals", rule, {
         {
             code: "parseInt(1n, 2);",
             parserOptions: { ecmaVersion: 2020 }
+        },
+        {
+            code: "class C { #parseInt; foo() { Number.#parseInt(\"111110111\", 2); } }",
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
index 91bf08a2f74fa7c28ee87090f86950156088425c..92bbef4188b0bb7a22929ce250be2f254e0a3daf 100644 (file)
@@ -6,10 +6,17 @@
 
 "use strict";
 
-const rule = require("../../../lib/rules/prefer-object-spread");
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
 
+const rule = require("../../../lib/rules/prefer-object-spread");
 const { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const parserOptions = {
     ecmaVersion: 2018,
     sourceType: "module"
@@ -76,6 +83,10 @@ ruleTester.run("prefer-object-spread", rule, {
                 `,
             env: { es2020: true }
         },
+        {
+            code: "class C { #assign; foo() { Object.#assign({}, foo); } }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
 
         // ignore Object.assign() with > 1 arguments if any of the arguments is an object expression with a getter/setter
         "Object.assign({ get a() {} }, {})",
index b31ec33622b20e0f1fca98b0da4b883662a6167a..40428d6fa244ba8dda17beee7468294f7abf1ef2 100644 (file)
 const rule = require("../../../lib/rules/prefer-promise-reject-errors");
 const { RuleTester } = require("../../../lib/rule-tester");
 
-
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2021 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("prefer-promise-reject-errors", rule, {
 
@@ -54,7 +53,11 @@ ruleTester.run("prefer-promise-reject-errors", rule, {
         "Promise.reject(foo = new Error())",
         "Promise.reject(foo ||= 5)",
         "Promise.reject(foo.bar ??= 5)",
-        "Promise.reject(foo[bar] ??= 5)"
+        "Promise.reject(foo[bar] ??= 5)",
+
+        // Private fields
+        "class C { #reject; foo() { Promise.#reject(5); } }",
+        "class C { #error; foo() { Promise.reject(this.#error); } }"
     ],
 
     invalid: [
index 0ddaa8dae3df928aa4ba8a42292f59348fe13a1d..ccd88ae74fd5053033e8127ae32d8c954f1f3339 100644 (file)
@@ -16,7 +16,7 @@ const { RuleTester } = require("../../../lib/rule-tester");
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("prefer-regex-literals", rule, {
     valid: [
@@ -124,6 +124,10 @@ ruleTester.run("prefer-regex-literals", rule, {
         {
             code: "new globalThis.RegExp('a');",
             env: { es2017: true }
+        },
+        {
+            code: "class C { #RegExp; foo() { globalThis.#RegExp('a'); } }",
+            env: { es2020: true }
         }
     ],
 
index 3ab3fc531c9dc802217da9898a29c443e17d8184..c7040ea401d21026827f444275248c0a30a494c2 100644 (file)
 const rule = require("../../../lib/rules/prefer-rest-params"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
 
 ruleTester.run("prefer-rest-params", rule, {
index 7f48d845f1b43c2d0dfafb70509ac2ac74aa0a94..99c8c5fa41a3f9499d755e72e8ac15f01676edfd 100644 (file)
@@ -18,7 +18,7 @@ const { RuleTester } = require("../../../lib/rule-tester");
 
 const errors = [{ messageId: "preferSpread", type: "CallExpression" }];
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("prefer-spread", rule, {
     valid: [
@@ -43,7 +43,10 @@ ruleTester.run("prefer-spread", rule, {
 
         // Optional chaining
         "(a?.b).c.foo.apply(a?.b.c, args);",
-        "a?.b.c.foo.apply((a?.b).c, args);"
+        "a?.b.c.foo.apply((a?.b).c, args);",
+
+        // Private fields
+        "class C { #apply; foo() { foo.#apply(undefined, args); } }"
     ],
     invalid: [
         {
@@ -115,6 +118,12 @@ ruleTester.run("prefer-spread", rule, {
         {
             code: "(a?.b).c.foo.apply((a?.b).c, args);",
             errors
+        },
+
+        // Private fields
+        {
+            code: "class C { #foo; foo() { obj.#foo.apply(obj, args); } }",
+            errors
         }
     ]
 });
index 5186f87d140a85b9bf7d4969bbb0de7a99ca04a9..10f35155f03f676f080fc793dab80564aa803537 100644 (file)
 const rule = require("../../../lib/rules/quote-props"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("quote-props", rule, {
index a7e223aab9b360ab88ef69d8c5e7619f4b9285fb..faf3713019b91a8cdcf95e970250374332df8247 100644 (file)
 const rule = require("../../../lib/rules/quotes"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("quotes", rule, {
@@ -38,6 +42,8 @@ ruleTester.run("quotes", rule, {
         { code: "var foo = \"a string containing `backtick` quotes\";", options: ["backtick", { avoidEscape: true }] },
         { code: "var foo = <div id=\"foo\"></div>;", options: ["backtick"], parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } },
         { code: "var foo = <div>Hello world</div>;", options: ["backtick"], parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } },
+        { code: "class C { \"f\"; \"m\"() {} }", options: ["double"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { 'f'; 'm'() {} }", options: ["single"], parserOptions: { ecmaVersion: 2022 } },
 
         // Backticks are only okay if they have substitutions, contain a line break, or are tagged
         { code: "var foo = `back\ntick`;", options: ["single"], parserOptions: { ecmaVersion: 6 } },
@@ -74,7 +80,8 @@ ruleTester.run("quotes", rule, {
         // `backtick` should not warn property/method names (not computed).
         { code: "var obj = {\"key0\": 0, 'key1': 1};", options: ["backtick"], parserOptions: { ecmaVersion: 6 } },
         { code: "class Foo { 'bar'(){} }", options: ["backtick"], parserOptions: { ecmaVersion: 6 } },
-        { code: "class Foo { static ''(){} }", options: ["backtick"], parserOptions: { ecmaVersion: 6 } }
+        { code: "class Foo { static ''(){} }", options: ["backtick"], parserOptions: { ecmaVersion: 6 } },
+        { code: "class C { \"double\"; 'single'; }", options: ["backtick"], parserOptions: { ecmaVersion: 2022 } }
     ],
     invalid: [
         {
@@ -627,6 +634,87 @@ ruleTester.run("quotes", rule, {
                     type: "Literal"
                 }
             ]
+        },
+
+
+        // class members
+        {
+            code: "class C { 'foo'; }",
+            output: "class C { \"foo\"; }",
+            options: ["double"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "wrongQuotes",
+                    data: { description: "doublequote" },
+                    type: "Literal"
+                }
+            ]
+        },
+        {
+            code: "class C { 'foo'() {} }",
+            output: "class C { \"foo\"() {} }",
+            options: ["double"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "wrongQuotes",
+                    data: { description: "doublequote" },
+                    type: "Literal"
+                }
+            ]
+        },
+        {
+            code: "class C { \"foo\"; }",
+            output: "class C { 'foo'; }",
+            options: ["single"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "wrongQuotes",
+                    data: { description: "singlequote" },
+                    type: "Literal"
+                }
+            ]
+        },
+        {
+            code: "class C { \"foo\"() {} }",
+            output: "class C { 'foo'() {} }",
+            options: ["single"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "wrongQuotes",
+                    data: { description: "singlequote" },
+                    type: "Literal"
+                }
+            ]
+        },
+        {
+            code: "class C { [\"foo\"]; }",
+            output: "class C { [`foo`]; }",
+            options: ["backtick"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "wrongQuotes",
+                    data: { description: "backtick" },
+                    type: "Literal"
+                }
+            ]
+        },
+        {
+            code: "class C { foo = \"foo\"; }",
+            output: "class C { foo = `foo`; }",
+            options: ["backtick"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "wrongQuotes",
+                    data: { description: "backtick" },
+                    type: "Literal"
+                }
+            ]
         }
     ]
 });
index ddb8363c1d9d2c7b837bf20d25dc93439d314093..437d24653507aa948414ab323ad2b44463050e5f 100644 (file)
 const rule = require("../../../lib/rules/radix"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("radix", rule, {
@@ -44,6 +48,10 @@ ruleTester.run("radix", rule, {
         "parseInt",
         "Number.foo();",
         "Number[parseInt]();",
+        { code: "class C { #parseInt; foo() { Number.#parseInt(); } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { #parseInt; foo() { Number.#parseInt(foo); } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { #parseInt; foo() { Number.#parseInt(foo, 'bar'); } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { #parseInt; foo() { Number.#parseInt(foo, 10); } }", options: ["as-needed"], parserOptions: { ecmaVersion: 2022 } },
 
         // Ignores if it's shadowed or disabled.
         "var parseInt; parseInt();",
index bd3738ac979dda0f23d378174f5a0ec8649cee64..53d85b752898af22f7740e5809fa52c8cf347ce3 100644 (file)
 const rule = require("../../../lib/rules/require-atomic-updates");
 const { RuleTester } = require("../../../lib/rule-tester");
 
-
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 const VARIABLE_ERROR = {
     messageId: "nonAtomicUpdate",
@@ -25,14 +24,20 @@ const VARIABLE_ERROR = {
 };
 
 const STATIC_PROPERTY_ERROR = {
-    messageId: "nonAtomicUpdate",
-    data: { value: "foo.bar" },
+    messageId: "nonAtomicObjectUpdate",
+    data: { value: "foo.bar", object: "foo" },
     type: "AssignmentExpression"
 };
 
 const COMPUTED_PROPERTY_ERROR = {
-    messageId: "nonAtomicUpdate",
-    data: { value: "foo[bar].baz" },
+    messageId: "nonAtomicObjectUpdate",
+    data: { value: "foo[bar].baz", object: "foo" },
+    type: "AssignmentExpression"
+};
+
+const PRIVATE_PROPERTY_ERROR = {
+    messageId: "nonAtomicObjectUpdate",
+    data: { value: "foo.#bar", object: "foo" },
     type: "AssignmentExpression"
 };
 
@@ -209,7 +214,29 @@ ruleTester.run("require-atomic-updates", rule, {
                 await a;
                 b = 1;
             }
-        `
+        `,
+
+        // allowProperties
+        {
+            code: `
+                async function a(foo) {
+                    if (foo.bar) {
+                        foo.bar = await something;
+                    }
+                }
+            `,
+            options: [{ allowProperties: true }]
+        },
+        {
+            code: `
+                function* g(foo) {
+                    baz = foo.bar;
+                    yield something;
+                    foo.bar = 1;
+                }
+            `,
+            options: [{ allowProperties: true }]
+        }
     ],
 
     invalid: [
@@ -269,6 +296,10 @@ ruleTester.run("require-atomic-updates", rule, {
             code: "const foo = []; async function x() { foo[bar].baz += await result;  }",
             errors: [COMPUTED_PROPERTY_ERROR]
         },
+        {
+            code: "const foo = {}; class C { #bar; async wrap() { foo.#bar += await baz } }",
+            errors: [PRIVATE_PROPERTY_ERROR]
+        },
         {
             code: "let foo; async function* x() { foo = (yield foo) + await bar; }",
             errors: [VARIABLE_ERROR]
@@ -319,6 +350,129 @@ ruleTester.run("require-atomic-updates", rule, {
                 }
             `,
             errors: [STATIC_PROPERTY_ERROR]
+        },
+
+        // https://github.com/eslint/eslint/issues/15076
+        {
+            code: `
+                async () => {
+                    opts.spec = process.stdin;
+                    try {
+                        const { exit_code } = await run(opts);
+                        process.exitCode = exit_code;
+                    } catch (e) {
+                        process.exitCode = 1;
+                    }
+              };
+            `,
+            env: { node: true },
+            errors: [
+                {
+                    messageId: "nonAtomicObjectUpdate",
+                    data: { value: "process.exitCode", object: "process" },
+                    type: "AssignmentExpression",
+                    line: 6
+                },
+                {
+                    messageId: "nonAtomicObjectUpdate",
+                    data: { value: "process.exitCode", object: "process" },
+                    type: "AssignmentExpression",
+                    line: 8
+                }
+            ]
+        },
+
+        // allowProperties
+        {
+            code: `
+                async function a(foo) {
+                    if (foo.bar) {
+                        foo.bar = await something;
+                    }
+                }
+            `,
+            errors: [STATIC_PROPERTY_ERROR]
+        },
+        {
+            code: `
+                function* g(foo) {
+                    baz = foo.bar;
+                    yield something;
+                    foo.bar = 1;
+                }
+            `,
+            errors: [STATIC_PROPERTY_ERROR]
+        },
+        {
+            code: `
+                async function a(foo) {
+                    if (foo.bar) {
+                        foo.bar = await something;
+                    }
+                }
+            `,
+            options: [{}],
+            errors: [STATIC_PROPERTY_ERROR]
+
+        },
+        {
+            code: `
+                function* g(foo) {
+                    baz = foo.bar;
+                    yield something;
+                    foo.bar = 1;
+                }
+            `,
+            options: [{}],
+            errors: [STATIC_PROPERTY_ERROR]
+        },
+        {
+            code: `
+                async function a(foo) {
+                    if (foo.bar) {
+                        foo.bar = await something;
+                    }
+                }
+            `,
+            options: [{ allowProperties: false }],
+            errors: [STATIC_PROPERTY_ERROR]
+
+        },
+        {
+            code: `
+                function* g(foo) {
+                    baz = foo.bar;
+                    yield something;
+                    foo.bar = 1;
+                }
+            `,
+            options: [{ allowProperties: false }],
+            errors: [STATIC_PROPERTY_ERROR]
+        },
+        {
+            code: `
+                let foo;
+                async function a() {
+                    if (foo) {
+                        foo = await something;
+                    }
+                }
+            `,
+            options: [{ allowProperties: true }],
+            errors: [VARIABLE_ERROR]
+
+        },
+        {
+            code: `
+                let foo;
+                function* g() {
+                    baz = foo;
+                    yield something;
+                    foo = 1;
+                }
+            `,
+            options: [{ allowProperties: true }],
+            errors: [VARIABLE_ERROR]
         }
     ]
 });
index 603ece2357bd5c11b0f1219feaf785685fab4380..f2f19da43eaebc0308642fb80fdda853dbdba20b 100644 (file)
@@ -4,6 +4,10 @@
  */
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const rule = require("../../../lib/rules/require-jsdoc"),
     { RuleTester } = require("../../../lib/rule-tester");
 
index 6033d44ca325763368e0686e4bfbf6aacd4dee85..16b6be4ff11274e820dace2c8694229752a8a065 100644 (file)
@@ -40,7 +40,8 @@ ruleTester.run("require-unicode-regexp", rule, {
         { code: "globalThis.RegExp('foo', 'u')", env: { es2020: true } },
         { code: "const flags = 'u'; new globalThis.RegExp('', flags)", env: { es2020: true } },
         { code: "const flags = 'g'; new globalThis.RegExp('', flags + 'u')", env: { es2020: true } },
-        { code: "const flags = 'gimu'; new globalThis.RegExp('foo', flags[3])", env: { es2020: true } }
+        { code: "const flags = 'gimu'; new globalThis.RegExp('foo', flags[3])", env: { es2020: true } },
+        { code: "class C { #RegExp; foo() { new globalThis.#RegExp('foo') } }", parserOptions: { ecmaVersion: 2022 }, env: { es2020: true } }
     ],
     invalid: [
         {
index bcef06424af15117940e0f27d4776c1d62b65d29..73b4f3dd436af541ff6826ebb3475e27e93139f6 100644 (file)
 const rule = require("../../../lib/rules/rest-spread-spacing"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
 
 ruleTester.run("rest-spread-spacing", rule, {
index b01dac04b158c50eb19cbe9c8649d31d260815b7..ffd67854abcb21a9b05046ef5324b1ebff8c00c5 100644 (file)
@@ -57,7 +57,24 @@ ruleTester.run("semi-spacing", rule, {
         { code: "function foo() { return 2; }", options: [{ after: false }] },
         { code: "for ( var i = 0;i < results.length; ) {}", options: [{ after: false }] },
 
-        "do {} while (true); foo"
+        "do {} while (true); foo",
+
+        // Class fields
+        {
+            code: "class C { foo; bar; method() {} }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+
+        // Empty are ignored (`no-extra-semi` rule will remove those)
+        "foo; ;;;;;;;;;",
+        {
+            code: "class C { foo; ;;;;;;;;;; }",
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
     invalid: [
         {
@@ -417,6 +434,68 @@ ruleTester.run("semi-spacing", rule, {
                 endLine: 1,
                 endColumn: 22
             }]
+        },
+
+        // Class fields
+        {
+            code: "class C { foo ;bar;}",
+            output: "class C { foo; bar;}",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "unexpectedWhitespaceBefore",
+                    type: "PropertyDefinition",
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 15
+                },
+                {
+                    messageId: "missingWhitespaceAfter",
+                    type: "PropertyDefinition",
+                    line: 1,
+                    column: 15,
+                    endLine: 1,
+                    endColumn: 16
+                }
+            ]
+        },
+        {
+            code: "class C { foo; bar ; }",
+            output: "class C { foo ;bar ; }",
+            options: [{ before: true, after: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                {
+                    messageId: "missingWhitespaceBefore",
+                    type: "PropertyDefinition",
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 15
+                },
+                {
+                    messageId: "unexpectedWhitespaceAfter",
+                    type: "PropertyDefinition",
+                    line: 1,
+                    column: 15,
+                    endLine: 1,
+                    endColumn: 16
+                }
+            ]
+        },
+        {
+            code: "class C { foo;static {}}",
+            output: "class C { foo; static {}}",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingWhitespaceAfter",
+                type: "PropertyDefinition",
+                line: 1,
+                column: 14,
+                endLine: 1,
+                endColumn: 15
+            }]
         }
     ]
 });
index 319a80c9b4b1d20a7b0e4b8d5724ea86da2a1f65..7ab8f223fe8707537f126ec1fdb8ded7ba22e3fc 100644 (file)
@@ -33,6 +33,8 @@ ruleTester.run("semi-style", rule, {
         { code: "for(a;b;c);", options: ["last"] },
         { code: "for(a;\nb;\nc);", options: ["last"] },
         { code: "for((a\n);\n(b\n);\n(c));", options: ["last"] },
+        { code: "class C { a; b; }", options: ["last"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C {\na;\nb;\n}", options: ["last"], parserOptions: { ecmaVersion: 2022 } },
         { code: "if(a)foo;\nbar", options: ["last"] },
         { code: ";", options: ["first"] },
         { code: ";foo;bar;baz;", options: ["first"] },
@@ -40,6 +42,8 @@ ruleTester.run("semi-style", rule, {
         { code: "for(a;b;c);", options: ["first"] },
         { code: "for(a;\nb;\nc);", options: ["first"] },
         { code: "for((a\n);\n(b\n);\n(c));", options: ["first"] },
+        { code: "class C { a ;b }", options: ["first"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C {\na\n;b\n}", options: ["first"], parserOptions: { ecmaVersion: 2022 } },
 
         // edge cases
         {
@@ -123,6 +127,202 @@ ruleTester.run("semi-style", rule, {
                 while (a)
             `,
             options: ["last"]
+        },
+
+        // Class static blocks
+        {
+            code: `
+                class C {
+                    static {}
+                }
+            `,
+            options: ["last"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo
+                    }
+                }
+            `,
+            options: ["last"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo
+                        bar
+                    }
+                }
+            `,
+            options: ["last"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        ;
+                    }
+                }
+            `,
+            options: ["last"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo;
+                    }
+                }
+            `,
+            options: ["last"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo;
+                        bar;
+                    }
+                }
+            `,
+            options: ["last"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo;
+                        bar;
+                        baz;
+                    }
+                }
+            `,
+            options: ["last"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {}
+                }
+            `,
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo
+                    }
+                }
+            `,
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo
+                        bar
+                    }
+                }
+            `,
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        ;
+                    }
+                }
+            `,
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        ;foo
+                    }
+                }
+            `,
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo;
+                    }
+                }
+            `,
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo
+                        ;bar
+                    }
+                }
+            `,
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo
+                        ;bar;
+                    }
+                }
+            `,
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo
+                        ;bar
+                        ;baz
+                    }
+                }
+            `,
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    static {
+                        foo
+                        ;bar
+                        ;baz;
+                    }
+                }
+            `,
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -384,6 +584,82 @@ ruleTester.run("semi-style", rule, {
                     pos: "the beginning of the next line"
                 }
             }]
+        },
+
+        // Class fields
+        {
+            code: "class C { foo\n;bar }",
+            output: "class C { foo;\nbar }",
+            options: ["last"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "expectedSemiColon",
+                data: {
+                    pos: "the end of the previous line"
+                }
+            }]
+        },
+        {
+            code: "class C { foo;\nbar }",
+            output: "class C { foo\n;bar }",
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "expectedSemiColon",
+                data: {
+                    pos: "the beginning of the next line"
+                }
+            }]
+        },
+
+        // Class static blocks
+        {
+            code: "class C { static { foo\n; } }",
+            output: "class C { static { foo;\n} }",
+            options: ["last"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "expectedSemiColon",
+                data: {
+                    pos: "the end of the previous line"
+                }
+            }]
+        },
+        {
+            code: "class C { static { foo\n ;bar } }",
+            output: "class C { static { foo;\nbar } }",
+            options: ["last"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "expectedSemiColon",
+                data: {
+                    pos: "the end of the previous line"
+                }
+            }]
+        },
+        {
+            code: "class C { static { foo;\nbar\n ; } }",
+            output: "class C { static { foo;\nbar;\n} }",
+            options: ["last"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "expectedSemiColon",
+                data: {
+                    pos: "the end of the previous line"
+                }
+            }]
+        },
+        {
+            code: "class C { static { foo;\nbar } }",
+            output: "class C { static { foo\n;bar } }",
+            options: ["first"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "expectedSemiColon",
+                data: {
+                    pos: "the beginning of the next line"
+                }
+            }]
         }
     ]
 });
index ce6310e1be012ecd0a1eb5322abedc335f2511df..d87382182ea9b0d523a8c50065eb788727002284 100644 (file)
 const rule = require("../../../lib/rules/semi"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("semi", rule, {
@@ -44,14 +48,73 @@ ruleTester.run("semi", rule, {
         { code: "for (let thing of {}) {\n  console.log(thing);\n}", parserOptions: { ecmaVersion: 6 } },
         { code: "do{}while(true)", options: ["never"] },
         { code: "do{}while(true);", options: ["always"] },
+        { code: "class C { static {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static {} }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo(); } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo(); } }", options: ["always"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo(); bar(); } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo(); bar(); baz();} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo() } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\nbar() } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\nbar()\nbaz() } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo(); bar() } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo();\n (a) } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\n ;(a) } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo();\n [a] } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\n ;[a] } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo();\n +a } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\n ;+a } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo();\n -a } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\n ;-a } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo();\n /a/ } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\n ;/a/} }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        {
+            code: "class C { static { foo();\n (a) } }",
+            options: ["never", { beforeStatementContinuationChars: "never" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { do ; while (foo)\n (a)} }",
+            options: ["never", { beforeStatementContinuationChars: "never" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { do ; while (foo)\n ;(a)} }",
+            options: ["never", { beforeStatementContinuationChars: "always" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
 
+        // omitLastInOneLineBlock: true
         { code: "if (foo) { bar() }", options: ["always", { omitLastInOneLineBlock: true }] },
         { code: "if (foo) { bar(); baz() }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "if (foo)\n{ bar(); baz() }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "if (foo) {\n bar(); baz(); }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "if (foo) { bar(); baz(); \n}", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "function foo() { bar(); baz() }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "function foo()\n{ bar(); baz() }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "function foo(){\n bar(); baz(); }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "function foo(){ bar(); baz(); \n}", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "() => { bar(); baz() };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "() =>\n { bar(); baz() };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "() => {\n bar(); baz(); };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "() => { bar(); baz(); \n};", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "const obj = { method() { bar(); baz() } };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "const obj = { method()\n { bar(); baz() } };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "const obj = { method() {\n bar(); baz(); } };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "const obj = { method() { bar(); baz(); \n} };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class C {\n method() { bar(); baz() } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class C {\n method()\n { bar(); baz() } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class C {\n method() {\n bar(); baz(); } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class C {\n method() { bar(); baz(); \n} \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class C {\n static { bar(); baz() } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C {\n static\n { bar(); baz() } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C {\n static {\n bar(); baz(); } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C {\n static { bar(); baz(); \n} \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 2022 } },
 
-
-        // method definitions don't have a semicolon.
+        // method definitions and static blocks don't have a semicolon.
         { code: "class A { a() {} b() {} }", parserOptions: { ecmaVersion: 6 } },
         { code: "var A = class { a() {} b() {} };", parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { static {} }", parserOptions: { ecmaVersion: 2022 } },
 
         { code: "import theDefault, { named1, named2 } from 'src/mylib';", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
         { code: "import theDefault, { named1, named2 } from 'src/mylib'", options: ["never"], parserOptions: { ecmaVersion: 6, sourceType: "module" } },
@@ -230,6 +293,214 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
             parserOptions: { ecmaVersion: 2015 }
+        },
+
+        // Class fields
+        {
+            code: "class C { foo; }",
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo = obj\n;[bar] }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo;\n[bar]; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo\n;[bar] }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo\n[bar] }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo\n;[bar] }",
+            options: ["never", { beforeStatementContinuationChars: "always" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo\n[bar] }",
+            options: ["never", { beforeStatementContinuationChars: "never" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo = () => {}\n;[bar] }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo = () => {}\n[bar] }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo = () => {}\n;[bar] }",
+            options: ["never", { beforeStatementContinuationChars: "always" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo = () => {}\n[bar] }",
+            options: ["never", { beforeStatementContinuationChars: "never" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo() {} }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo() {}; }", // no-extra-semi reports it
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static {}; }", // no-extra-semi reports it
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { a=b;\n*foo() {} }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { get;\nfoo() {} }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { set;\nfoo() {} }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static;\nfoo() {} }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { a=b;\nin }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { a=b;\ninstanceof }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    x
+                    [foo]
+
+                    x;
+                    [foo]
+
+                    x = "a";
+                    [foo]
+                }
+            `,
+            options: ["never", { beforeStatementContinuationChars: "never" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: `
+                class C {
+                    x
+                    [foo]
+
+                    x;
+                    [foo]
+
+                    x = 1;
+                    [foo]
+                }
+            `,
+            options: ["never", { beforeStatementContinuationChars: "always" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo\n[bar] }",
+            options: ["never", { beforeStatementContinuationChars: "always" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo = () => {}\n[bar] }",
+            options: ["never", { beforeStatementContinuationChars: "always" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo\n;[bar] }",
+            options: ["never", { beforeStatementContinuationChars: "never" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { foo = () => {}\n;[bar] }",
+            options: ["never", { beforeStatementContinuationChars: "never" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [foo] = bar;\nin }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #foo = bar;\nin }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static static = bar;\nin }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [foo];\nin }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [get];\nin }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { [get] = 5;\nin }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #get;\nin }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { #set = 5;\nin }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static static;\nin }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
     invalid: [
@@ -240,6 +511,7 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "missingSemi",
                 type: "ImportDeclaration",
+                line: 1,
                 column: 33,
                 endLine: void 0,
                 endColumn: void 0
@@ -251,7 +523,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "missingSemi",
-                type: "ImportDeclaration"
+                type: "ImportDeclaration",
+                line: 1,
+                column: 35,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -260,7 +536,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "missingSemi",
-                type: "ImportDeclaration"
+                type: "ImportDeclaration",
+                line: 1,
+                column: 37,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -269,7 +549,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "missingSemi",
-                type: "ImportDeclaration"
+                type: "ImportDeclaration",
+                line: 1,
+                column: 19,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -278,7 +562,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "missingSemi",
-                type: "ImportDeclaration"
+                type: "ImportDeclaration",
+                line: 1,
+                column: 55,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -286,7 +574,11 @@ ruleTester.run("semi", rule, {
             output: "function foo() { return []; }",
             errors: [{
                 messageId: "missingSemi",
-                type: "ReturnStatement"
+                type: "ReturnStatement",
+                line: 1,
+                column: 27,
+                endLine: 1,
+                endColumn: 28
             }]
         },
         {
@@ -294,7 +586,11 @@ ruleTester.run("semi", rule, {
             output: "while(true) { break; }",
             errors: [{
                 messageId: "missingSemi",
-                type: "BreakStatement"
+                type: "BreakStatement",
+                line: 1,
+                column: 20,
+                endLine: 1,
+                endColumn: 21
             }]
         },
         {
@@ -302,7 +598,11 @@ ruleTester.run("semi", rule, {
             output: "while(true) { continue; }",
             errors: [{
                 messageId: "missingSemi",
-                type: "ContinueStatement"
+                type: "ContinueStatement",
+                line: 1,
+                column: 23,
+                endLine: 1,
+                endColumn: 24
             }]
         },
         {
@@ -311,7 +611,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: [{
                 messageId: "missingSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 10,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -319,7 +623,11 @@ ruleTester.run("semi", rule, {
             output: "var x = 5;",
             errors: [{
                 messageId: "missingSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 10,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -327,7 +635,11 @@ ruleTester.run("semi", rule, {
             output: "var x = 5, y;",
             errors: [{
                 messageId: "missingSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 13,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -335,7 +647,11 @@ ruleTester.run("semi", rule, {
             output: "debugger;",
             errors: [{
                 messageId: "missingSemi",
-                type: "DebuggerStatement"
+                type: "DebuggerStatement",
+                line: 1,
+                column: 9,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -344,7 +660,9 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "missingSemi",
                 type: "ExpressionStatement",
+                line: 1,
                 column: 6,
+                endLine: void 0,
                 endColumn: void 0
             }]
         },
@@ -354,6 +672,7 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "missingSemi",
                 type: "ExpressionStatement",
+                line: 1,
                 column: 6,
                 endLine: 2,
                 endColumn: 1
@@ -365,6 +684,7 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "missingSemi",
                 type: "ExpressionStatement",
+                line: 1,
                 column: 6,
                 endLine: 2,
                 endColumn: 1
@@ -376,6 +696,7 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "missingSemi",
                 type: "ExpressionStatement",
+                line: 1,
                 column: 6,
                 endLine: 2,
                 endColumn: 1
@@ -387,6 +708,7 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "missingSemi",
                 type: "ExpressionStatement",
+                line: 1,
                 column: 6,
                 endLine: 2,
                 endColumn: 1
@@ -397,7 +719,11 @@ ruleTester.run("semi", rule, {
             output: "for (var a in b) var i; ",
             errors: [{
                 messageId: "missingSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 23,
+                endLine: 1,
+                endColumn: 24
             }]
         },
         {
@@ -405,7 +731,11 @@ ruleTester.run("semi", rule, {
             output: "for (;;){var i;}",
             errors: [{
                 messageId: "missingSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 15,
+                endLine: 1,
+                endColumn: 16
             }]
         },
         {
@@ -413,7 +743,11 @@ ruleTester.run("semi", rule, {
             output: "for (;;) var i; ",
             errors: [{
                 messageId: "missingSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 15,
+                endLine: 1,
+                endColumn: 16
             }]
         },
         {
@@ -421,7 +755,11 @@ ruleTester.run("semi", rule, {
             output: "for (var j;;) {var i;}",
             errors: [{
                 messageId: "missingSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 21,
+                endLine: 1,
+                endColumn: 22
             }]
         },
         {
@@ -430,7 +768,10 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "missingSemi",
                 type: "VariableDeclaration",
-                line: 3
+                line: 3,
+                column: 2,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -439,7 +780,10 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "missingSemi",
                 type: "VariableDeclaration",
-                line: 1
+                line: 1,
+                column: 8,
+                endLine: 2,
+                endColumn: 1
             }]
         },
         {
@@ -448,7 +792,10 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "missingSemi",
                 type: "ThrowStatement",
-                line: 1
+                line: 1,
+                column: 23,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -457,7 +804,10 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "missingSemi",
                 type: "DoWhileStatement",
-                line: 1
+                line: 1,
+                column: 16,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -465,7 +815,9 @@ ruleTester.run("semi", rule, {
             output: "if (foo) {bar();}",
             errors: [{
                 messageId: "missingSemi",
+                line: 1,
                 column: 16,
+                endLine: 1,
                 endColumn: 17
             }]
         },
@@ -474,7 +826,9 @@ ruleTester.run("semi", rule, {
             output: "if (foo) {bar();} ",
             errors: [{
                 messageId: "missingSemi",
+                line: 1,
                 column: 16,
+                endLine: 1,
                 endColumn: 17
             }]
         },
@@ -483,6 +837,7 @@ ruleTester.run("semi", rule, {
             output: "if (foo) {bar();\n}",
             errors: [{
                 messageId: "missingSemi",
+                line: 1,
                 column: 16,
                 endLine: 2,
                 endColumn: 1
@@ -496,7 +851,10 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "extraSemi",
                 type: "ThrowStatement",
-                column: 23
+                line: 1,
+                column: 23,
+                endLine: 1,
+                endColumn: 24
             }]
         },
         {
@@ -505,7 +863,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
-                type: "ReturnStatement"
+                type: "ReturnStatement",
+                line: 1,
+                column: 27,
+                endLine: 1,
+                endColumn: 28
             }]
         },
         {
@@ -514,7 +876,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
-                type: "BreakStatement"
+                type: "BreakStatement",
+                line: 1,
+                column: 20,
+                endLine: 1,
+                endColumn: 21
             }]
         },
         {
@@ -523,7 +889,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
-                type: "ContinueStatement"
+                type: "ContinueStatement",
+                line: 1,
+                column: 23,
+                endLine: 1,
+                endColumn: 24
             }]
         },
         {
@@ -533,7 +903,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: [{
                 messageId: "extraSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 10,
+                endLine: 1,
+                endColumn: 11
             }]
         },
         {
@@ -542,7 +916,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 10,
+                endLine: 1,
+                endColumn: 11
             }]
         },
         {
@@ -551,7 +929,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 13,
+                endLine: 1,
+                endColumn: 14
             }]
         },
         {
@@ -560,7 +942,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
-                type: "DebuggerStatement"
+                type: "DebuggerStatement",
+                line: 1,
+                column: 9,
+                endLine: 1,
+                endColumn: 10
             }]
         },
         {
@@ -569,7 +955,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
-                type: "ExpressionStatement"
+                type: "ExpressionStatement",
+                line: 1,
+                column: 6,
+                endLine: 1,
+                endColumn: 7
             }]
         },
         {
@@ -578,7 +968,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 23,
+                endLine: 1,
+                endColumn: 24
             }]
         },
         {
@@ -587,7 +981,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 15,
+                endLine: 1,
+                endColumn: 16
             }]
         },
         {
@@ -596,7 +994,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 15,
+                endLine: 1,
+                endColumn: 16
             }]
         },
         {
@@ -605,7 +1007,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 21,
+                endLine: 1,
+                endColumn: 22
             }]
         },
         {
@@ -615,7 +1021,10 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "extraSemi",
                 type: "VariableDeclaration",
-                line: 3
+                line: 3,
+                column: 2,
+                endLine: 3,
+                endColumn: 3
             }]
         },
         {
@@ -625,7 +1034,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "extraSemi",
-                type: "ImportDeclaration"
+                type: "ImportDeclaration",
+                line: 1,
+                column: 55,
+                endLine: 1,
+                endColumn: 56
             }]
         },
         {
@@ -635,106 +1048,456 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "extraSemi",
                 type: "DoWhileStatement",
-                line: 1
+                line: 1,
+                column: 16,
+                endLine: 1,
+                endColumn: 17
             }]
         },
-
         {
-            code: "if (foo) { bar()\n }",
-            output: "if (foo) { bar();\n }",
-            options: ["always", { omitLastInOneLineBlock: true }],
+            code: "class C { static { foo() } }",
+            output: "class C { static { foo(); } }",
+            parserOptions: { ecmaVersion: 2022 },
             errors: [{
-                messageId: "missingSemi"
+                messageId: "missingSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 25,
+                endLine: 1,
+                endColumn: 26
             }]
         },
         {
-            code: "if (foo) {\n bar() }",
-            output: "if (foo) {\n bar(); }",
-            options: ["always", { omitLastInOneLineBlock: true }],
+            code: "class C { static { foo() } }",
+            output: "class C { static { foo(); } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
             errors: [{
-                messageId: "missingSemi"
+                messageId: "missingSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 25,
+                endLine: 1,
+                endColumn: 26
             }]
         },
         {
-            code: "if (foo) {\n bar(); baz() }",
-            output: "if (foo) {\n bar(); baz(); }",
-            options: ["always", { omitLastInOneLineBlock: true }],
+            code: "class C { static { foo(); bar() } }",
+            output: "class C { static { foo(); bar(); } }",
+            parserOptions: { ecmaVersion: 2022 },
             errors: [{
-                messageId: "missingSemi"
+                messageId: "missingSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 32,
+                endLine: 1,
+                endColumn: 33
             }]
         },
         {
-            code: "if (foo) { bar(); }",
-            output: "if (foo) { bar() }",
-            options: ["always", { omitLastInOneLineBlock: true }],
+            code: "class C { static { foo()\nbar(); } }",
+            output: "class C { static { foo();\nbar(); } }",
+            parserOptions: { ecmaVersion: 2022 },
             errors: [{
-                messageId: "extraSemi"
+                messageId: "missingSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 25,
+                endLine: 2,
+                endColumn: 1
             }]
         },
-
-
-        // exports, "always"
         {
-            code: "export * from 'foo'",
-            output: "export * from 'foo';",
-            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            code: "class C { static { foo(); bar()\nbaz(); } }",
+            output: "class C { static { foo(); bar();\nbaz(); } }",
+            parserOptions: { ecmaVersion: 2022 },
             errors: [{
                 messageId: "missingSemi",
-                type: "ExportAllDeclaration"
+                type: "ExpressionStatement",
+                line: 1,
+                column: 32,
+                endLine: 2,
+                endColumn: 1
             }]
         },
         {
-            code: "export { foo } from 'foo'",
-            output: "export { foo } from 'foo';",
-            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            code: "class C { static { foo(); } }",
+            output: "class C { static { foo() } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
             errors: [{
-                messageId: "missingSemi",
-                type: "ExportNamedDeclaration"
+                messageId: "extraSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 25,
+                endLine: 1,
+                endColumn: 26
             }]
         },
         {
-            code: "var foo = 0;export { foo }",
-            output: "var foo = 0;export { foo };",
-            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            code: "class C { static { foo();\nbar() } }",
+            output: "class C { static { foo()\nbar() } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
             errors: [{
-                messageId: "missingSemi",
-                type: "ExportNamedDeclaration"
+                messageId: "extraSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 25,
+                endLine: 1,
+                endColumn: 26
             }]
         },
         {
-            code: "export var foo",
-            output: "export var foo;",
-            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            code: "class C { static { foo()\nbar(); } }",
+            output: "class C { static { foo()\nbar() } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
             errors: [{
-                messageId: "missingSemi",
-                type: "VariableDeclaration"
+                messageId: "extraSemi",
+                type: "ExpressionStatement",
+                line: 2,
+                column: 6,
+                endLine: 2,
+                endColumn: 7
             }]
         },
         {
-            code: "export let foo",
-            output: "export let foo;",
-            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            code: "class C { static { foo()\nbar();\nbaz() } }",
+            output: "class C { static { foo()\nbar()\nbaz() } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
             errors: [{
-                messageId: "missingSemi",
-                type: "VariableDeclaration"
+                messageId: "extraSemi",
+                type: "ExpressionStatement",
+                line: 2,
+                column: 6,
+                endLine: 2,
+                endColumn: 7
             }]
         },
         {
-            code: "export const FOO = 42",
-            output: "export const FOO = 42;",
-            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            code: "class C { static { do ; while (foo)\n (a)} }",
+            output: "class C { static { do ; while (foo);\n (a)} }",
+            options: ["never", { beforeStatementContinuationChars: "always" }],
+            parserOptions: { ecmaVersion: 2022 },
             errors: [{
                 messageId: "missingSemi",
-                type: "VariableDeclaration"
-            }]
-        },
+                type: "DoWhileStatement",
+                line: 1,
+                column: 36,
+                endLine: 2,
+                endColumn: 1
+            }]
+        },
+        {
+            code: "class C { static { do ; while (foo)\n ;(a)} }",
+            output: "class C { static { do ; while (foo)\n (a)} }",
+            options: ["never", { beforeStatementContinuationChars: "never" }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                type: "DoWhileStatement",
+                line: 2,
+                column: 2,
+                endLine: 2,
+                endColumn: 3
+            }]
+        },
+
+        // omitLastInOneLineBlock: true
+        {
+            code: "if (foo) { bar()\n }",
+            output: "if (foo) { bar();\n }",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "missingSemi",
+                line: 1,
+                column: 17,
+                endLine: 2,
+                endColumn: 1
+            }]
+        },
+        {
+            code: "if (foo) {\n bar() }",
+            output: "if (foo) {\n bar(); }",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 7,
+                endLine: 2,
+                endColumn: 8
+            }]
+        },
+        {
+            code: "if (foo) {\n bar(); baz() }",
+            output: "if (foo) {\n bar(); baz(); }",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 14,
+                endLine: 2,
+                endColumn: 15
+            }]
+        },
+        {
+            code: "if (foo) { bar(); }",
+            output: "if (foo) { bar() }",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 17,
+                endLine: 1,
+                endColumn: 18
+            }]
+        },
+        {
+            code: "function foo() { bar(); baz(); }",
+            output: "function foo() { bar(); baz() }",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 30,
+                endLine: 1,
+                endColumn: 31
+            }]
+        },
+        {
+            code: "function foo()\n{ bar(); baz(); }",
+            output: "function foo()\n{ bar(); baz() }",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "extraSemi",
+                line: 2,
+                column: 15,
+                endLine: 2,
+                endColumn: 16
+            }]
+        },
+        {
+            code: "function foo() {\n bar(); baz() }",
+            output: "function foo() {\n bar(); baz(); }",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 14,
+                endLine: 2,
+                endColumn: 15
+            }]
+        },
+        {
+            code: "function foo() { bar(); baz() \n}",
+            output: "function foo() { bar(); baz(); \n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "missingSemi",
+                line: 1,
+                column: 30,
+                endLine: 1,
+                endColumn: 31
+            }]
+        },
+        {
+            code: "class C {\nfoo() { bar(); baz(); }\n}",
+            output: "class C {\nfoo() { bar(); baz() }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 2,
+                column: 21,
+                endLine: 2,
+                endColumn: 22
+            }]
+        },
+        {
+            code: "class C {\nfoo() \n{ bar(); baz(); }\n}",
+            output: "class C {\nfoo() \n{ bar(); baz() }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 3,
+                column: 15,
+                endLine: 3,
+                endColumn: 16
+            }]
+        },
+        {
+            code: "class C {\nfoo() {\n bar(); baz() }\n}",
+            output: "class C {\nfoo() {\n bar(); baz(); }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "missingSemi",
+                line: 3,
+                column: 14,
+                endLine: 3,
+                endColumn: 15
+            }]
+        },
+        {
+            code: "class C {\nfoo() { bar(); baz() \n}\n}",
+            output: "class C {\nfoo() { bar(); baz(); \n}\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 21,
+                endLine: 2,
+                endColumn: 22
+            }]
+        },
+        {
+            code: "class C {\nstatic { bar(); baz(); }\n}",
+            output: "class C {\nstatic { bar(); baz() }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 2,
+                column: 22,
+                endLine: 2,
+                endColumn: 23
+            }]
+        },
+        {
+            code: "class C {\nstatic \n{ bar(); baz(); }\n}",
+            output: "class C {\nstatic \n{ bar(); baz() }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 3,
+                column: 15,
+                endLine: 3,
+                endColumn: 16
+            }]
+        },
+        {
+            code: "class C {\nstatic {\n bar(); baz() }\n}",
+            output: "class C {\nstatic {\n bar(); baz(); }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                line: 3,
+                column: 14,
+                endLine: 3,
+                endColumn: 15
+            }]
+        },
+        {
+            code: "class C {\nfoo() { bar(); baz() \n}\n}",
+            output: "class C {\nfoo() { bar(); baz(); \n}\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 21,
+                endLine: 2,
+                endColumn: 22
+            }]
+        },
+
+
+        // exports, "always"
+        {
+            code: "export * from 'foo'",
+            output: "export * from 'foo';",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [{
+                messageId: "missingSemi",
+                type: "ExportAllDeclaration",
+                line: 1,
+                column: 20,
+                endLine: void 0,
+                endColumn: void 0
+            }]
+        },
+        {
+            code: "export { foo } from 'foo'",
+            output: "export { foo } from 'foo';",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [{
+                messageId: "missingSemi",
+                type: "ExportNamedDeclaration",
+                line: 1,
+                column: 26,
+                endLine: void 0,
+                endColumn: void 0
+            }]
+        },
+        {
+            code: "var foo = 0;export { foo }",
+            output: "var foo = 0;export { foo };",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [{
+                messageId: "missingSemi",
+                type: "ExportNamedDeclaration",
+                line: 1,
+                column: 27,
+                endLine: void 0,
+                endColumn: void 0
+            }]
+        },
+        {
+            code: "export var foo",
+            output: "export var foo;",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [{
+                messageId: "missingSemi",
+                type: "VariableDeclaration",
+                line: 1,
+                column: 15,
+                endLine: void 0,
+                endColumn: void 0
+            }]
+        },
+        {
+            code: "export let foo",
+            output: "export let foo;",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [{
+                messageId: "missingSemi",
+                type: "VariableDeclaration",
+                line: 1,
+                column: 15,
+                endLine: void 0,
+                endColumn: void 0
+            }]
+        },
+        {
+            code: "export const FOO = 42",
+            output: "export const FOO = 42;",
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [{
+                messageId: "missingSemi",
+                type: "VariableDeclaration",
+                line: 1,
+                column: 22,
+                endLine: void 0,
+                endColumn: void 0
+            }]
+        },
         {
             code: "export default foo || bar",
             output: "export default foo || bar;",
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "missingSemi",
-                type: "ExportDefaultDeclaration"
+                type: "ExportDefaultDeclaration",
+                line: 1,
+                column: 26,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -743,7 +1506,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "missingSemi",
-                type: "ExportDefaultDeclaration"
+                type: "ExportDefaultDeclaration",
+                line: 1,
+                column: 34,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -752,7 +1519,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "missingSemi",
-                type: "ExportDefaultDeclaration"
+                type: "ExportDefaultDeclaration",
+                line: 1,
+                column: 24,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
         {
@@ -761,7 +1532,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "missingSemi",
-                type: "ExportDefaultDeclaration"
+                type: "ExportDefaultDeclaration",
+                line: 1,
+                column: 25,
+                endLine: void 0,
+                endColumn: void 0
             }]
         },
 
@@ -774,7 +1549,9 @@ ruleTester.run("semi", rule, {
             errors: [{
                 messageId: "extraSemi",
                 type: "ExportAllDeclaration",
+                line: 1,
                 column: 20,
+                endLine: 1,
                 endColumn: 21
             }]
         },
@@ -785,7 +1562,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "extraSemi",
-                type: "ExportNamedDeclaration"
+                type: "ExportNamedDeclaration",
+                line: 1,
+                column: 26,
+                endLine: 1,
+                endColumn: 27
             }]
         },
         {
@@ -795,7 +1576,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "extraSemi",
-                type: "ExportNamedDeclaration"
+                type: "ExportNamedDeclaration",
+                line: 1,
+                column: 27,
+                endLine: 1,
+                endColumn: 28
             }]
         },
         {
@@ -805,7 +1590,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "extraSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 15,
+                endLine: 1,
+                endColumn: 16
             }]
         },
         {
@@ -815,7 +1604,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "extraSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 15,
+                endLine: 1,
+                endColumn: 16
             }]
         },
         {
@@ -825,7 +1618,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "extraSemi",
-                type: "VariableDeclaration"
+                type: "VariableDeclaration",
+                line: 1,
+                column: 22,
+                endLine: 1,
+                endColumn: 23
             }]
         },
         {
@@ -835,7 +1632,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "extraSemi",
-                type: "ExportDefaultDeclaration"
+                type: "ExportDefaultDeclaration",
+                line: 1,
+                column: 26,
+                endLine: 1,
+                endColumn: 27
             }]
         },
         {
@@ -845,7 +1646,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "extraSemi",
-                type: "ExportDefaultDeclaration"
+                type: "ExportDefaultDeclaration",
+                line: 1,
+                column: 34,
+                endLine: 1,
+                endColumn: 35
             }]
         },
         {
@@ -855,7 +1660,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "extraSemi",
-                type: "ExportDefaultDeclaration"
+                type: "ExportDefaultDeclaration",
+                line: 1,
+                column: 24,
+                endLine: 1,
+                endColumn: 25
             }]
         },
         {
@@ -865,7 +1674,11 @@ ruleTester.run("semi", rule, {
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
             errors: [{
                 messageId: "extraSemi",
-                type: "ExportDefaultDeclaration"
+                type: "ExportDefaultDeclaration",
+                line: 1,
+                column: 25,
+                endLine: 1,
+                endColumn: 26
             }]
         },
         {
@@ -874,7 +1687,9 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             errors: [{
                 messageId: "extraSemi",
+                line: 1,
                 column: 2,
+                endLine: 1,
                 endColumn: 3
             }]
         },
@@ -893,8 +1708,20 @@ ruleTester.run("semi", rule, {
             ].join("\n"),
             options: ["never"],
             errors: [
-                "Extra semicolon.",
-                "Unnecessary semicolon."
+                {
+                    messageId: "extraSemi",
+                    line: 2,
+                    column: 6,
+                    endLine: 2,
+                    endColumn: 7
+                },
+                {
+                    message: "Unnecessary semicolon.",
+                    line: 3,
+                    column: 1,
+                    endLine: 3,
+                    endColumn: 2
+                }
             ]
         },
 
@@ -910,7 +1737,13 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "always" }],
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
-            errors: ["Missing semicolon."]
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 34,
+                endLine: 3,
+                endColumn: 1
+            }]
         },
         {
             code: `
@@ -923,7 +1756,13 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "always" }],
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
-            errors: ["Missing semicolon."]
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 38,
+                endLine: 3,
+                endColumn: 1
+            }]
         },
         {
             code: `
@@ -940,7 +1779,13 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "always" }],
             parserOptions: { ecmaVersion: 2015 },
-            errors: ["Missing semicolon."]
+            errors: [{
+                messageId: "missingSemi",
+                line: 3,
+                column: 27,
+                endLine: 4,
+                endColumn: 1
+            }]
         },
         {
             code: `
@@ -956,7 +1801,13 @@ ruleTester.run("semi", rule, {
                 }
             `,
             options: ["never", { beforeStatementContinuationChars: "always" }],
-            errors: ["Missing semicolon."]
+            errors: [{
+                messageId: "missingSemi",
+                line: 3,
+                column: 26,
+                endLine: 4,
+                endColumn: 1
+            }]
         },
         {
             code: `
@@ -972,7 +1823,13 @@ ruleTester.run("semi", rule, {
                 }
             `,
             options: ["never", { beforeStatementContinuationChars: "always" }],
-            errors: ["Missing semicolon."]
+            errors: [{
+                messageId: "missingSemi",
+                line: 3,
+                column: 29,
+                endLine: 4,
+                endColumn: 1
+            }]
         },
         {
             code: `
@@ -984,7 +1841,13 @@ ruleTester.run("semi", rule, {
                 [1,2,3].forEach(doSomething)
             `,
             options: ["never", { beforeStatementContinuationChars: "always" }],
-            errors: ["Missing semicolon."]
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 29,
+                endLine: 3,
+                endColumn: 1
+            }]
         },
         {
             code: `
@@ -997,7 +1860,13 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "always" }],
             parserOptions: { ecmaVersion: 2015 },
-            errors: ["Missing semicolon."]
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 35,
+                endLine: 3,
+                endColumn: 1
+            }]
         },
         {
             code: `
@@ -1010,7 +1879,13 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 2,
+                column: 34,
+                endLine: 2,
+                endColumn: 35
+            }]
         },
         {
             code: `
@@ -1023,7 +1898,13 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 2,
+                column: 38,
+                endLine: 2,
+                endColumn: 39
+            }]
         },
         {
             code: `
@@ -1040,7 +1921,13 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
             parserOptions: { ecmaVersion: 2015 },
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 3,
+                column: 27,
+                endLine: 3,
+                endColumn: 28
+            }]
         },
         {
             code: `
@@ -1056,7 +1943,13 @@ ruleTester.run("semi", rule, {
                 }
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 3,
+                column: 26,
+                endLine: 3,
+                endColumn: 27
+            }]
         },
         {
             code: `
@@ -1072,7 +1965,13 @@ ruleTester.run("semi", rule, {
                 }
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 3,
+                column: 29,
+                endLine: 3,
+                endColumn: 30
+            }]
         },
         {
             code: `
@@ -1084,7 +1983,13 @@ ruleTester.run("semi", rule, {
                 [1,2,3].forEach(doSomething)
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 2,
+                column: 29,
+                endLine: 2,
+                endColumn: 30
+            }]
         },
         {
             code: `
@@ -1097,7 +2002,13 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
             parserOptions: { ecmaVersion: 2015 },
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 2,
+                column: 35,
+                endLine: 2,
+                endColumn: 36
+            }]
         },
         {
             code: `
@@ -1110,7 +2021,13 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 3,
+                column: 17,
+                endLine: 3,
+                endColumn: 18
+            }]
         },
         {
             code: `
@@ -1123,7 +2040,13 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 3,
+                column: 17,
+                endLine: 3,
+                endColumn: 18
+            }]
         },
         {
             code: `
@@ -1139,7 +2062,13 @@ ruleTester.run("semi", rule, {
                 }
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 4,
+                column: 21,
+                endLine: 4,
+                endColumn: 22
+            }]
         },
         {
             code: `
@@ -1155,7 +2084,13 @@ ruleTester.run("semi", rule, {
                 }
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 4,
+                column: 21,
+                endLine: 4,
+                endColumn: 22
+            }]
         },
         {
             code: `
@@ -1171,7 +2106,13 @@ ruleTester.run("semi", rule, {
                 }
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 4,
+                column: 21,
+                endLine: 4,
+                endColumn: 22
+            }]
         },
         {
             code: `
@@ -1183,7 +2124,13 @@ ruleTester.run("semi", rule, {
                 [1,2,3].forEach(doSomething)
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 3,
+                column: 17,
+                endLine: 3,
+                endColumn: 18
+            }]
         },
         {
             code: `
@@ -1196,7 +2143,172 @@ ruleTester.run("semi", rule, {
             `,
             options: ["never", { beforeStatementContinuationChars: "never" }],
             parserOptions: { ecmaVersion: 2015 },
-            errors: ["Extra semicolon."]
+            errors: [{
+                messageId: "extraSemi",
+                line: 3,
+                column: 17,
+                endLine: 3,
+                endColumn: 18
+            }]
+        },
+
+        // Class fields
+        {
+            code: "class C { foo }",
+            output: "class C { foo; }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                line: 1,
+                column: 14,
+                endLine: 1,
+                endColumn: 15
+            }]
+        },
+        {
+            code: "class C { foo }",
+            output: "class C { foo; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                line: 1,
+                column: 14,
+                endLine: 1,
+                endColumn: 15
+            }]
+        },
+        {
+            code: "class C { foo; }",
+            output: "class C { foo }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 14,
+                endLine: 1,
+                endColumn: 15
+            }]
+        },
+        {
+            code: "class C { foo\n[bar]; }",
+            output: "class C { foo;\n[bar]; }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                line: 1,
+                column: 14,
+                endLine: 2,
+                endColumn: 1
+            }]
+        },
+
+        // class fields
+        {
+            code: "class C { [get];\nfoo\n}",
+            output: "class C { [get]\nfoo\n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 16,
+                endLine: 1,
+                endColumn: 17
+            }]
+        },
+        {
+            code: "class C { [set];\nfoo\n}",
+            output: "class C { [set]\nfoo\n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 16,
+                endLine: 1,
+                endColumn: 17
+            }]
+        },
+        {
+            code: "class C { #get;\nfoo\n}",
+            output: "class C { #get\nfoo\n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 15,
+                endLine: 1,
+                endColumn: 16
+            }]
+        },
+        {
+            code: "class C { #set;\nfoo\n}",
+            output: "class C { #set\nfoo\n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 15,
+                endLine: 1,
+                endColumn: 16
+            }]
+        },
+        {
+            code: "class C { #static;\nfoo\n}",
+            output: "class C { #static\nfoo\n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 18,
+                endLine: 1,
+                endColumn: 19
+            }]
+        },
+        {
+            code: "class C { get=1;\nfoo\n}",
+            output: "class C { get=1\nfoo\n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 16,
+                endLine: 1,
+                endColumn: 17
+            }]
+        },
+        {
+            code: "class C { static static;\nfoo\n}",
+            output: "class C { static static\nfoo\n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 24,
+                endLine: 1,
+                endColumn: 25
+            }]
+        },
+        {
+            code: "class C { static;\n}",
+            output: "class C { static\n}",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 17,
+                endLine: 1,
+                endColumn: 18
+            }]
         }
     ]
 });
index 7424aec84584a06d272a7aa0d85f1a5e295333c2..49af96116501c662adca941543a7ac086cb9dc96 100644 (file)
@@ -18,6 +18,7 @@ const rule = require("../../../lib/rules/space-before-blocks"),
 //------------------------------------------------------------------------------
 
 const ruleTester = new RuleTester(),
+    alwaysArgs = ["always"],
     neverArgs = ["never"],
     functionsOnlyArgs = [{ functions: "always", keywords: "never", classes: "never" }],
     keywordOnlyArgs = [{ functions: "never", keywords: "always", classes: "never" }],
@@ -193,7 +194,27 @@ ruleTester.run("space-before-blocks", rule, {
         "if(a) {}else{}",
         { code: "if(a){}else {}", options: neverArgs },
         { code: "try {}catch(a){}", options: functionsOnlyArgs },
-        { code: "export default class{}", options: classesOnlyArgs, parserOptions: { ecmaVersion: 6, sourceType: "module" } }
+        { code: "export default class{}", options: classesOnlyArgs, parserOptions: { ecmaVersion: 6, sourceType: "module" } },
+
+        // https://github.com/eslint/eslint/issues/15082
+        { code: "switch(x) { case 9:{ break; } }", options: alwaysArgs },
+        { code: "switch(x){ case 9: { break; } }", options: neverArgs },
+        { code: "switch(x) { case (9):{ break; } }", options: alwaysArgs },
+        { code: "switch(x){ case (9): { break; } }", options: neverArgs },
+        { code: "switch(x) { default:{ break; } }", options: alwaysArgs },
+        { code: "switch(x){ default: { break; } }", options: neverArgs },
+
+        // not conflict with `keyword-spacing`
+        {
+            code: "(class{ static{} })",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "(class { static {} })",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
     invalid: [
         {
@@ -571,6 +592,68 @@ ruleTester.run("space-before-blocks", rule, {
             options: neverArgs,
             parser: fixtureParser("space-before-blocks", "return-type-keyword-2"),
             errors: [expectedNoSpacingError]
+        },
+
+        // https://github.com/eslint/eslint/issues/15082 regression tests (only blocks after switch case colons should be excluded)
+        {
+            code: "label:{}",
+            output: "label: {}",
+            options: alwaysArgs,
+            errors: [expectedSpacingError]
+        },
+        {
+            code: "label: {}",
+            output: "label:{}",
+            options: neverArgs,
+            errors: [expectedNoSpacingError]
+        },
+        {
+            code: "switch(x) { case 9: label:{ break; } }",
+            output: "switch(x) { case 9: label: { break; } }",
+            options: alwaysArgs,
+            errors: [expectedSpacingError]
+        },
+        {
+            code: "switch(x){ case 9: label: { break; } }",
+            output: "switch(x){ case 9: label:{ break; } }",
+            options: neverArgs,
+            errors: [expectedNoSpacingError]
+        },
+        {
+            code: "switch(x) { case 9: if(y){ break; } }",
+            output: "switch(x) { case 9: if(y) { break; } }",
+            options: alwaysArgs,
+            errors: [expectedSpacingError]
+        },
+        {
+            code: "switch(x){ case 9: if(y) { break; } }",
+            output: "switch(x){ case 9: if(y){ break; } }",
+            options: neverArgs,
+            errors: [expectedNoSpacingError]
+        },
+        {
+            code: "switch(x) { case 9: y;{ break; } }",
+            output: "switch(x) { case 9: y; { break; } }",
+            options: alwaysArgs,
+            errors: [expectedSpacingError]
+        },
+        {
+            code: "switch(x){ case 9: y; { break; } }",
+            output: "switch(x){ case 9: y;{ break; } }",
+            options: neverArgs,
+            errors: [expectedNoSpacingError]
+        },
+        {
+            code: "switch(x) { case 9: switch(y){} }",
+            output: "switch(x) { case 9: switch(y) {} }",
+            options: alwaysArgs,
+            errors: [expectedSpacingError]
+        },
+        {
+            code: "switch(x){ case 9: switch(y) {} }",
+            output: "switch(x){ case 9: switch(y){} }",
+            options: neverArgs,
+            errors: [expectedNoSpacingError]
         }
     ]
 });
index 2e6f423e9199d1b61ad0989d8a5f5cafd5c05920..c2a5c5546496d36c96af4a29aa8615bc667af5c6 100644 (file)
@@ -13,6 +13,10 @@ const rule = require("../../../lib/rules/space-infix-ops"),
     { RuleTester } = require("../../../lib/rule-tester"),
     parser = require("../../fixtures/fixture-parser");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("space-infix-ops", rule, {
@@ -49,9 +53,17 @@ ruleTester.run("space-infix-ops", rule, {
         // TypeScript Type Aliases
         { code: "type Foo<T> = T;", parser: parser("typescript-parsers/type-alias"), parserOptions: { ecmaVersion: 6 } },
 
+        // Logical Assignments
         { code: "a &&= b", parserOptions: { ecmaVersion: 2021 } },
         { code: "a ||= b", parserOptions: { ecmaVersion: 2021 } },
-        { code: "a ??= b", parserOptions: { ecmaVersion: 2021 } }
+        { code: "a ??= b", parserOptions: { ecmaVersion: 2021 } },
+
+        // Class Fields
+        { code: "class C { a; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { a = b; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { 'a' = b; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { [a] = b; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { #a = b; }", parserOptions: { ecmaVersion: 2022 } }
     ],
     invalid: [
         {
@@ -492,6 +504,7 @@ ruleTester.run("space-infix-ops", rule, {
             }]
         },
 
+        // Logical Assignments
         {
             code: "a&&=b",
             output: "a &&= b",
@@ -533,6 +546,36 @@ ruleTester.run("space-infix-ops", rule, {
                 endColumn: 5,
                 type: "AssignmentExpression"
             }]
+        },
+
+        // Class Fields
+        {
+            code: "class C { a=b; }",
+            output: "class C { a = b; }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSpace",
+                data: { operator: "=" },
+                line: 1,
+                column: 12,
+                endLine: 1,
+                endColumn: 13,
+                type: "PropertyDefinition"
+            }]
+        },
+        {
+            code: "class C { [a ]= b; }",
+            output: "class C { [a ] = b; }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSpace",
+                data: { operator: "=" },
+                line: 1,
+                column: 15,
+                endLine: 1,
+                endColumn: 16,
+                type: "PropertyDefinition"
+            }]
         }
     ]
 });
index c56e0135d5e102c49a0efd8de3355cb7eb3311bc..ccee19c663dc3e1d2fb71f96f0b6b4a7070ec724 100644 (file)
@@ -250,6 +250,11 @@ ruleTester.run("space-unary-ops", rule, {
             code: "function *foo () { yield(0) }",
             options: [{ words: false, overrides: { yield: false } }],
             parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "class C { #x; *foo(bar) { yield#x in bar; } }",
+            options: [{ words: false }],
+            parserOptions: { ecmaVersion: 2022 }
         }
     ],
 
@@ -805,6 +810,19 @@ ruleTester.run("space-unary-ops", rule, {
                 line: 1,
                 column: 24
             }]
+        },
+        {
+            code: "class C { #x; *foo(bar) { yield #x in bar; } }",
+            output: "class C { #x; *foo(bar) { yield#x in bar; } }",
+            options: [{ words: false }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "unexpectedAfterWord",
+                data: { word: "yield" },
+                type: "YieldExpression",
+                line: 1,
+                column: 27
+            }]
         }
     ]
 });
index 5d3d68fc06b4ebb2219d0215b386edf079e0799c..7ca6384364a695ba81db3157b1ad1aeaf866058c 100644 (file)
@@ -4,9 +4,17 @@
  */
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const rule = require("../../../lib/rules/spaced-comment"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester(),
     validShebangProgram = "#!/path/to/node\nvar a = 3;";
 
index 92abffda16ccc9b41152b564ebb4b752de05e2a3..e3ea705fe7ef60df0019068d2b48081cd6f1d02b 100644 (file)
 const rule = require("../../../lib/rules/strict"),
     { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("strict", rule, {
@@ -84,7 +88,20 @@ ruleTester.run("strict", rule, {
         "function foo() { 'use strict'; return; }",
         { code: "'use strict'; function foo() { return; }", parserOptions: { ecmaFeatures: { globalReturn: true } } },
         { code: "function foo() { return; }", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
-        { code: "function foo() { return; }", parserOptions: { ecmaFeatures: { impliedStrict: true } } }
+        { code: "function foo() { return; }", parserOptions: { ecmaFeatures: { impliedStrict: true } } },
+
+        // class static blocks do not have directive prologues, therefore this rule should never require od disallow "use strict" statement in them.
+        { code: "'use strict'; class C { static { foo; } }", options: ["global"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "'use strict'; class C { static { 'use strict'; } }", options: ["global"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "'use strict'; class C { static { 'use strict'; 'use strict'; } }", options: ["global"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo; } }", options: ["function"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { 'use strict'; } }", options: ["function"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { 'use strict'; 'use strict'; } }", options: ["function"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo; } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { 'use strict'; } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { 'use strict'; 'use strict'; } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { 'use strict'; } }", options: ["safe"], parserOptions: { ecmaVersion: 2022, sourceType: "module" } },
+        { code: "class C { static { 'use strict'; } }", options: ["safe"], parserOptions: { ecmaVersion: 2022, ecmaFeatures: { impliedStrict: true } } }
 
     ],
     invalid: [
@@ -404,7 +421,20 @@ ruleTester.run("strict", rule, {
             parserOptions: { ecmaVersion: 6 },
             errors: [{ messageId: "unnecessaryInClasses", type: "ExpressionStatement" }]
         },
-
+        {
+            code: "class A { field = () => { \"use strict\"; } }",
+            output: "class A { field = () => {  } }",
+            options: ["function"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unnecessaryInClasses", type: "ExpressionStatement" }]
+        },
+        {
+            code: "class A { field = function() { \"use strict\"; } }",
+            output: "class A { field = function() {  } }",
+            options: ["function"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unnecessaryInClasses", type: "ExpressionStatement" }]
+        },
 
         // "safe" mode corresponds to "global" if ecmaFeatures.globalReturn is true, otherwise "function"
         {
@@ -572,7 +602,60 @@ ruleTester.run("strict", rule, {
             options: ["function"],
             parserOptions: { ecmaVersion: 6 },
             errors: ["Use the function form of 'use strict'."]
-        }
+        },
 
+        // functions inside class static blocks should be checked
+        {
+            code: "'use strict'; class C { static { function foo() { \n'use strict'; } } }",
+            output: null,
+            options: ["global"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "global", line: 2 }]
+        },
+        {
+            code: "class C { static { function foo() { \n'use strict'; } } }",
+            output: null,
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "never", line: 2 }]
+        },
+        {
+            code: "class C { static { function foo() { \n'use strict'; } } }",
+            output: "class C { static { function foo() { \n } } }",
+            options: ["safe"],
+            parserOptions: { ecmaVersion: 2022, sourceType: "module" },
+            errors: [{ messageId: "module", line: 2 }]
+        },
+        {
+            code: "class C { static { function foo() { \n'use strict'; } } }",
+            output: "class C { static { function foo() { \n } } }",
+            options: ["safe"],
+            parserOptions: { ecmaVersion: 2022, ecmaFeatures: { impliedStrict: true } },
+            errors: [{ messageId: "implied", line: 2 }]
+        },
+        {
+            code: "function foo() {'use strict'; class C { static { function foo() { \n'use strict'; } } } }",
+            output: "function foo() {'use strict'; class C { static { function foo() { \n } } } }",
+            options: ["function"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unnecessary", line: 2 }]
+        },
+        {
+            code: "class C { static { function foo() { \n'use strict'; } } }",
+            output: "class C { static { function foo() { \n } } }",
+            options: ["function"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unnecessaryInClasses", line: 2 }]
+        },
+        {
+            code: "class C { static { function foo() { \n'use strict';\n'use strict'; } } }",
+            output: "class C { static { function foo() { \n\n } } }",
+            options: ["function"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [
+                { messageId: "unnecessaryInClasses", line: 2 },
+                { messageId: "multiple", line: 3 }
+            ]
+        }
     ]
 });
index 36391fc78082d5cbad0f352ac205af7f1cf6fc50..55cbbbc89c8ab1e70379f2351c25119cbe6393f6 100644 (file)
 const rule = require("../../../lib/rules/symbol-description");
 const { RuleTester } = require("../../../lib/rule-tester");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester({ env: { es6: true } });
 
 ruleTester.run("symbol-description", rule, {
index 0b94a6902910c076762976d51b6020b5185f9f6e..62abee6987081d8e8d567d4797f84aba94a46aa6 100644 (file)
@@ -41,24 +41,52 @@ ruleTester.run("unicode-bom", rule, {
             code: "var a = 123;",
             output: "\uFEFFvar a = 123;",
             options: ["always"],
-            errors: [expectedError]
+            errors: [{
+                ...expectedError,
+                line: 1,
+                column: 1,
+                endLine: void 0,
+                endColumn: void 0
+
+            }]
         },
         {
             code: " // here's a comment \nvar a = 123;",
             output: "\uFEFF // here's a comment \nvar a = 123;",
             options: ["always"],
-            errors: [expectedError]
+            errors: [{
+                ...expectedError,
+                line: 1,
+                column: 1,
+                endLine: void 0,
+                endColumn: void 0
+
+            }]
         },
         {
             code: "\uFEFF var a = 123;",
             output: " var a = 123;",
-            errors: [unexpectedError]
+            errors: [{
+                ...unexpectedError,
+                line: 1,
+                column: 1,
+                endLine: void 0,
+                endColumn: void 0
+
+            }]
         },
         {
             code: "\uFEFF var a = 123;",
             output: " var a = 123;",
             options: ["never"],
-            errors: [unexpectedError]
+            errors: [{
+                ...unexpectedError,
+                line: 1,
+                column: 1,
+                endLine: void 0,
+                endColumn: void 0
+
+            }]
         }
     ]
 });
index c5a4b6bd6863e368f6c6bf8e23a68703bcb4de38..a9bfe3a67d7a4bfacc1725f4d93dde7f03b152e2 100644 (file)
@@ -36,6 +36,19 @@ ruleTester.run("use-isnan", rule, {
         "foo(NaN / 2)",
         "foo(2 / NaN)",
         "var x; if (x = NaN) { }",
+        "var x = Number.NaN;",
+        "isNaN(Number.NaN) === true;",
+        "Number.isNaN(Number.NaN) === true;",
+        "foo(Number.NaN + 1);",
+        "foo(1 + Number.NaN);",
+        "foo(Number.NaN - 1)",
+        "foo(1 - Number.NaN)",
+        "foo(Number.NaN * 2)",
+        "foo(2 * Number.NaN)",
+        "foo(Number.NaN / 2)",
+        "foo(2 / Number.NaN)",
+        "var x; if (x = Number.NaN) { }",
+        "x === Number[NaN];",
 
         //------------------------------------------------------------------------------
         // enforceForSwitchCase
@@ -105,6 +118,62 @@ ruleTester.run("use-isnan", rule, {
             code: "switch(foo) { case bar: break; case 1: break; default: break; }",
             options: [{ enforceForSwitchCase: true }]
         },
+        {
+            code: "switch(Number.NaN) { case foo: break; }",
+            options: [{ enforceForSwitchCase: false }]
+        },
+        {
+            code: "switch(foo) { case Number.NaN: break; }",
+            options: [{ enforceForSwitchCase: false }]
+        },
+        {
+            code: "switch(NaN) { case Number.NaN: break; }",
+            options: [{ enforceForSwitchCase: false }]
+        },
+        {
+            code: "switch(foo) { case bar: break; case Number.NaN: break; default: break; }",
+            options: [{ enforceForSwitchCase: false }]
+        },
+        {
+            code: "switch(foo) { case bar: Number.NaN; }",
+            options: [{ enforceForSwitchCase: true }]
+        },
+        {
+            code: "switch(foo) { default: Number.NaN; }",
+            options: [{ enforceForSwitchCase: true }]
+        },
+        {
+            code: "switch(Number.Nan) {}",
+            options: [{ enforceForSwitchCase: true }]
+        },
+        {
+            code: "switch('Number.NaN') { default: break; }",
+            options: [{ enforceForSwitchCase: true }]
+        },
+        {
+            code: "switch(foo(Number.NaN)) {}",
+            options: [{ enforceForSwitchCase: true }]
+        },
+        {
+            code: "switch(foo.Number.NaN) {}",
+            options: [{ enforceForSwitchCase: true }]
+        },
+        {
+            code: "switch(foo) { case Number.Nan: break }",
+            options: [{ enforceForSwitchCase: true }]
+        },
+        {
+            code: "switch(foo) { case 'Number.NaN': break }",
+            options: [{ enforceForSwitchCase: true }]
+        },
+        {
+            code: "switch(foo) { case foo(Number.NaN): break }",
+            options: [{ enforceForSwitchCase: true }]
+        },
+        {
+            code: "switch(foo) { case foo.Number.NaN: break }",
+            options: [{ enforceForSwitchCase: true }]
+        },
 
         //------------------------------------------------------------------------------
         // enforceForIndexOf
@@ -112,6 +181,8 @@ ruleTester.run("use-isnan", rule, {
 
         "foo.indexOf(NaN)",
         "foo.lastIndexOf(NaN)",
+        "foo.indexOf(Number.NaN)",
+        "foo.lastIndexOf(Number.NaN)",
         {
             code: "foo.indexOf(NaN)",
             options: [{}]
@@ -200,6 +271,79 @@ ruleTester.run("use-isnan", rule, {
         {
             code: "foo.lastIndexOf(NaN())",
             options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "foo.indexOf(Number.NaN)",
+            options: [{}]
+        },
+        {
+            code: "foo.lastIndexOf(Number.NaN)",
+            options: [{}]
+        },
+        {
+            code: "foo.indexOf(Number.NaN)",
+            options: [{ enforceForIndexOf: false }]
+        },
+        {
+            code: "foo.lastIndexOf(Number.NaN)",
+            options: [{ enforceForIndexOf: false }]
+        },
+        {
+            code: "indexOf(Number.NaN)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "lastIndexOf(Number.NaN)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "new foo.indexOf(Number.NaN)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "foo.bar(Number.NaN)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "foo.IndexOf(Number.NaN)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "foo[indexOf](Number.NaN)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "foo[lastIndexOf](Number.NaN)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "indexOf.foo(Number.NaN)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "foo.lastIndexOf(Number.Nan)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "foo.indexOf(a, Number.NaN)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "foo.lastIndexOf(Number.NaN, b)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "foo.lastIndexOf(Number.NaN, NaN)",
+            options: [{ enforceForIndexOf: true }]
+        },
+        {
+            code: "foo.indexOf(...Number.NaN)",
+            options: [{ enforceForIndexOf: true }],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "foo.lastIndexOf(Number.NaN())",
+            options: [{ enforceForIndexOf: true }]
         }
     ],
     invalid: [
@@ -267,6 +411,79 @@ ruleTester.run("use-isnan", rule, {
             code: "\"abc\" >= NaN;",
             errors: [comparisonError]
         },
+        {
+            code: "123 == Number.NaN;",
+            errors: [comparisonError]
+        },
+        {
+            code: "123 === Number.NaN;",
+            errors: [comparisonError]
+        },
+        {
+            code: "Number.NaN === \"abc\";",
+            errors: [comparisonError]
+        },
+        {
+            code: "Number.NaN == \"abc\";",
+            errors: [comparisonError]
+        },
+        {
+            code: "123 != Number.NaN;",
+            errors: [comparisonError]
+        },
+        {
+            code: "123 !== Number.NaN;",
+            errors: [comparisonError]
+        },
+        {
+            code: "Number.NaN !== \"abc\";",
+            errors: [comparisonError]
+        },
+        {
+            code: "Number.NaN != \"abc\";",
+            errors: [comparisonError]
+        },
+        {
+            code: "Number.NaN < \"abc\";",
+            errors: [comparisonError]
+        },
+        {
+            code: "\"abc\" < Number.NaN;",
+            errors: [comparisonError]
+        },
+        {
+            code: "Number.NaN > \"abc\";",
+            errors: [comparisonError]
+        },
+        {
+            code: "\"abc\" > Number.NaN;",
+            errors: [comparisonError]
+        },
+        {
+            code: "Number.NaN <= \"abc\";",
+            errors: [comparisonError]
+        },
+        {
+            code: "\"abc\" <= Number.NaN;",
+            errors: [comparisonError]
+        },
+        {
+            code: "Number.NaN >= \"abc\";",
+            errors: [comparisonError]
+        },
+        {
+            code: "\"abc\" >= Number.NaN;",
+            errors: [comparisonError]
+        },
+        {
+            code: "x === Number?.NaN;",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [comparisonError]
+        },
+        {
+            code: "x === Number['NaN'];",
+            errors: [comparisonError]
+        },
 
         //------------------------------------------------------------------------------
         // enforceForSwitchCase
@@ -351,6 +568,85 @@ ruleTester.run("use-isnan", rule, {
                 { messageId: "caseNaN", type: "SwitchCase", column: 15 }
             ]
         },
+        {
+            code: "switch(Number.NaN) { case foo: break; }",
+            errors: [{ messageId: "switchNaN", type: "SwitchStatement", column: 1 }]
+        },
+        {
+            code: "switch(foo) { case Number.NaN: break; }",
+            errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 15 }]
+        },
+        {
+            code: "switch(Number.NaN) { case foo: break; }",
+            options: [{}],
+            errors: [{ messageId: "switchNaN", type: "SwitchStatement", column: 1 }]
+        },
+        {
+            code: "switch(foo) { case Number.NaN: break; }",
+            options: [{}],
+            errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 15 }]
+        },
+        {
+            code: "switch(Number.NaN) {}",
+            options: [{ enforceForSwitchCase: true }],
+            errors: [{ messageId: "switchNaN", type: "SwitchStatement", column: 1 }]
+        },
+        {
+            code: "switch(Number.NaN) { case foo: break; }",
+            options: [{ enforceForSwitchCase: true }],
+            errors: [{ messageId: "switchNaN", type: "SwitchStatement", column: 1 }]
+        },
+        {
+            code: "switch(Number.NaN) { default: break; }",
+            options: [{ enforceForSwitchCase: true }],
+            errors: [{ messageId: "switchNaN", type: "SwitchStatement", column: 1 }]
+        },
+        {
+            code: "switch(Number.NaN) { case foo: break; default: break; }",
+            options: [{ enforceForSwitchCase: true }],
+            errors: [{ messageId: "switchNaN", type: "SwitchStatement", column: 1 }]
+        },
+        {
+            code: "switch(foo) { case Number.NaN: }",
+            options: [{ enforceForSwitchCase: true }],
+            errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 15 }]
+        },
+        {
+            code: "switch(foo) { case Number.NaN: break; }",
+            options: [{ enforceForSwitchCase: true }],
+            errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 15 }]
+        },
+        {
+            code: "switch(foo) { case (Number.NaN): break; }",
+            options: [{ enforceForSwitchCase: true }],
+            errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 15 }]
+        },
+        {
+            code: "switch(foo) { case bar: break; case Number.NaN: break; default: break; }",
+            options: [{ enforceForSwitchCase: true }],
+            errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 32 }]
+        },
+        {
+            code: "switch(foo) { case bar: case Number.NaN: default: break; }",
+            options: [{ enforceForSwitchCase: true }],
+            errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 25 }]
+        },
+        {
+            code: "switch(foo) { case bar: break; case NaN: break; case baz: break; case Number.NaN: break; }",
+            options: [{ enforceForSwitchCase: true }],
+            errors: [
+                { messageId: "caseNaN", type: "SwitchCase", column: 32 },
+                { messageId: "caseNaN", type: "SwitchCase", column: 66 }
+            ]
+        },
+        {
+            code: "switch(Number.NaN) { case Number.NaN: break; }",
+            options: [{ enforceForSwitchCase: true }],
+            errors: [
+                { messageId: "switchNaN", type: "SwitchStatement", column: 1 },
+                { messageId: "caseNaN", type: "SwitchCase", column: 22 }
+            ]
+        },
 
         //------------------------------------------------------------------------------
         // enforceForIndexOf
@@ -403,6 +699,54 @@ ruleTester.run("use-isnan", rule, {
             options: [{ enforceForIndexOf: true }],
             parserOptions: { ecmaVersion: 2020 },
             errors: [{ messageId: "indexOfNaN", data: { methodName: "indexOf" } }]
+        },
+        {
+            code: "foo.indexOf(Number.NaN)",
+            options: [{ enforceForIndexOf: true }],
+            errors: [{ messageId: "indexOfNaN", type: "CallExpression", data: { methodName: "indexOf" } }]
+        },
+        {
+            code: "foo.lastIndexOf(Number.NaN)",
+            options: [{ enforceForIndexOf: true }],
+            errors: [{ messageId: "indexOfNaN", type: "CallExpression", data: { methodName: "lastIndexOf" } }]
+        },
+        {
+            code: "foo['indexOf'](Number.NaN)",
+            options: [{ enforceForIndexOf: true }],
+            errors: [{ messageId: "indexOfNaN", type: "CallExpression", data: { methodName: "indexOf" } }]
+        },
+        {
+            code: "foo['lastIndexOf'](Number.NaN)",
+            options: [{ enforceForIndexOf: true }],
+            errors: [{ messageId: "indexOfNaN", type: "CallExpression", data: { methodName: "lastIndexOf" } }]
+        },
+        {
+            code: "foo().indexOf(Number.NaN)",
+            options: [{ enforceForIndexOf: true }],
+            errors: [{ messageId: "indexOfNaN", type: "CallExpression", data: { methodName: "indexOf" } }]
+        },
+        {
+            code: "foo.bar.lastIndexOf(Number.NaN)",
+            options: [{ enforceForIndexOf: true }],
+            errors: [{ messageId: "indexOfNaN", type: "CallExpression", data: { methodName: "lastIndexOf" } }]
+        },
+        {
+            code: "foo.indexOf?.(Number.NaN)",
+            options: [{ enforceForIndexOf: true }],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ messageId: "indexOfNaN", data: { methodName: "indexOf" } }]
+        },
+        {
+            code: "foo?.indexOf(Number.NaN)",
+            options: [{ enforceForIndexOf: true }],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ messageId: "indexOfNaN", data: { methodName: "indexOf" } }]
+        },
+        {
+            code: "(foo?.indexOf)(Number.NaN)",
+            options: [{ enforceForIndexOf: true }],
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ messageId: "indexOfNaN", data: { methodName: "indexOf" } }]
         }
     ]
 });
index d0ad2748431634fbee4c26f71ad37b5ad1a01eab..7789de7742c66f071480b7b115a584a766f3da5a 100644 (file)
@@ -185,7 +185,6 @@ describe("ast-utils", () => {
          * Asserts the node is NOT a directive comment
          * @param {ASTNode} node node to assert
          * @returns {void}
-         *
          */
         function assertFalse(node) {
             assert.isFalse(astUtils.isDirectiveComment(node));
@@ -195,7 +194,6 @@ describe("ast-utils", () => {
          * Asserts the node is a directive comment
          * @param {ASTNode} node node to assert
          * @returns {void}
-         *
          */
         function assertTrue(node) {
             assert.isTrue(astUtils.isDirectiveComment(node));
@@ -406,7 +404,7 @@ describe("ast-utils", () => {
 
     describe("getStaticStringValue", () => {
 
-        /* eslint-disable quote-props */
+        /* eslint-disable quote-props -- Make consistent here for readability */
         const expectedResults = {
 
             // string literals
@@ -470,7 +468,7 @@ describe("ast-utils", () => {
             "this": null,
             "(function () {})": null
         };
-        /* eslint-enable quote-props */
+        /* eslint-enable quote-props -- Make consistent here for readability */
 
         Object.keys(expectedResults).forEach(key => {
             it(`should return ${expectedResults[key]} for ${key}`, () => {
@@ -878,7 +876,17 @@ describe("ast-utils", () => {
             "class A { static *foo() {} }": "static generator method 'foo'",
             "class A { static async foo() {} }": "static async method 'foo'",
             "class A { static get foo() {} }": "static getter 'foo'",
-            "class A { static set foo(a) {} }": "static setter 'foo'"
+            "class A { static set foo(a) {} }": "static setter 'foo'",
+            "class A { foo = () => {}; }": "method 'foo'",
+            "class A { foo = function() {}; }": "method 'foo'",
+            "class A { foo = function bar() {}; }": "method 'foo'",
+            "class A { static foo = () => {}; }": "static method 'foo'",
+            "class A { '#foo' = () => {}; }": "method '#foo'",
+            "class A { #foo = () => {}; }": "private method #foo",
+            "class A { static #foo = () => {}; }": "static private method #foo",
+            "class A { '#foo'() {} }": "method '#foo'",
+            "class A { #foo() {} }": "private method #foo",
+            "class A { static #foo() {} }": "static private method #foo"
         };
 
         Object.keys(expectedResults).forEach(key => {
@@ -892,7 +900,7 @@ describe("ast-utils", () => {
                     })
                 })));
 
-                linter.verify(key, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 8 } });
+                linter.verify(key, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 13 } });
             });
         });
     });
@@ -940,7 +948,12 @@ describe("ast-utils", () => {
             "class A { static *foo() {} }": [10, 21],
             "class A { static async foo() {} }": [10, 26],
             "class A { static get foo() {} }": [10, 24],
-            "class A { static set foo(a) {} }": [10, 24]
+            "class A { static set foo(a) {} }": [10, 24],
+            "class A { foo = function() {}; }": [10, 24],
+            "class A { foo = function bar() {}; }": [10, 28],
+            "class A { static foo = function() {}; }": [10, 31],
+            "class A { foo = () => {}; }": [10, 16],
+            "class A { foo = arg => {}; }": [10, 16]
         };
 
         Object.keys(expectedResults).forEach(key => {
@@ -965,7 +978,7 @@ describe("ast-utils", () => {
                     })
                 })));
 
-                linter.verify(key, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 8 } }, "test.js", true);
+                linter.verify(key, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 13 } }, "test.js", true);
             });
         });
     });
@@ -1006,7 +1019,7 @@ describe("ast-utils", () => {
 
     describe("getNextLocation", () => {
 
-        /* eslint-disable quote-props */
+        /* eslint-disable quote-props -- Make consistent here for readability */
         const expectedResults = {
             "": [[1, 0], null],
             "\n": [[1, 0], [2, 0], null],
@@ -1028,7 +1041,7 @@ describe("ast-utils", () => {
             "a\t": [[1, 0], [1, 1], [1, 2], null],
             "a \n": [[1, 0], [1, 1], [1, 2], [2, 0], null]
         };
-        /* eslint-enable quote-props */
+        /* eslint-enable quote-props -- Make consistent here for readability */
 
         Object.keys(expectedResults).forEach(code => {
             it(`should return expected locations for "${code}".`, () => {
@@ -1482,7 +1495,10 @@ describe("ast-utils", () => {
             [["(", "123invalidtoken"], false],
             [["(", "1n"], true],
             [["1n", "+"], true],
-            [["1n", "in"], false]
+            [["1n", "in"], false],
+            [["return", "#x"], true],
+            [["yield", "#x"], true],
+            [["get", "#x"], true]
         ]);
 
         CASES.forEach((expectedResult, tokenStrings) => {
@@ -1557,7 +1573,7 @@ describe("ast-utils", () => {
                     },
                     nodeB: {
                         type: "Literal",
-                        value: /(?:)/, // eslint-disable-line require-unicode-regexp
+                        value: /(?:)/, // eslint-disable-line require-unicode-regexp -- Checking non-Unicode regex
                         regex: { pattern: "(?:)", flags: "" }
                     },
                     expected: false
@@ -1650,7 +1666,7 @@ describe("ast-utils", () => {
 
     describe("hasOctalOrNonOctalDecimalEscapeSequence", () => {
 
-        /* eslint-disable quote-props */
+        /* eslint-disable quote-props -- Make consistent here for readability */
         const expectedResults = {
             "\\1": true,
             "\\2": true,
@@ -1722,7 +1738,7 @@ describe("ast-utils", () => {
             "foo\\\nbar": false,
             "128\\\n349": false
         };
-        /* eslint-enable quote-props */
+        /* eslint-enable quote-props -- Make consistent here for readability */
 
         Object.keys(expectedResults).forEach(key => {
             it(`should return ${expectedResults[key]} for ${key}`, () => {
index 92e1a0d1cc038bfd33aa7acf3bd4900f46f6bd49..e2b1bb689c5cabf89dce4edfd5d56c8f9eeb40e3 100644 (file)
@@ -191,6 +191,84 @@ ruleTester.run("vars-on-top", rule, {
                 ecmaVersion: 6,
                 sourceType: "module"
             }
+        },
+        {
+            code: [
+                "class C {",
+                "    static {",
+                "        var x;",
+                "    }",
+                "}"
+            ].join("\n"),
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: [
+                "class C {",
+                "    static {",
+                "        var x;",
+                "        foo();",
+                "    }",
+                "}"
+            ].join("\n"),
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: [
+                "class C {",
+                "    static {",
+                "        var x;",
+                "        var y;",
+                "    }",
+                "}"
+            ].join("\n"),
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: [
+                "class C {",
+                "    static {",
+                "        var x;",
+                "        var y;",
+                "        foo();",
+                "    }",
+                "}"
+            ].join("\n"),
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: [
+                "class C {",
+                "    static {",
+                "        let x;",
+                "        var y;",
+                "    }",
+                "}"
+            ].join("\n"),
+            parserOptions: {
+                ecmaVersion: 2022
+            }
+        },
+        {
+            code: [
+                "class C {",
+                "    static {",
+                "        foo();",
+                "        let x;",
+                "    }",
+                "}"
+            ].join("\n"),
+            parserOptions: {
+                ecmaVersion: 2022
+            }
         }
     ],
 
@@ -427,6 +505,78 @@ ruleTester.run("vars-on-top", rule, {
                 sourceType: "module"
             },
             errors: [error]
+        },
+        {
+            code: [
+                "class C {",
+                "    static {",
+                "        foo();",
+                "        var x;",
+                "    }",
+                "}"
+            ].join("\n"),
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [error]
+        },
+        {
+            code: [
+                "class C {",
+                "    static {",
+                "        'use strict';", // static blocks do not have directives
+                "        var x;",
+                "    }",
+                "}"
+            ].join("\n"),
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [error]
+        },
+        {
+            code: [
+                "class C {",
+                "    static {",
+                "        var x;",
+                "        foo();",
+                "        var y;",
+                "    }",
+                "}"
+            ].join("\n"),
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [{ ...error, line: 5 }]
+        },
+        {
+            code: [
+                "class C {",
+                "    static {",
+                "        if (foo) {",
+                "            var x;",
+                "        }",
+                "    }",
+                "}"
+            ].join("\n"),
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [error]
+        },
+        {
+            code: [
+                "class C {",
+                "    static {",
+                "        if (foo)",
+                "            var x;",
+                "    }",
+                "}"
+            ].join("\n"),
+            parserOptions: {
+                ecmaVersion: 2022
+            },
+            errors: [error]
         }
     ]
 });
index 0ac92f2d848336fa44e750949b142ee7fe3e5894..6aea21118ad2db7293725e899ae2f2d34b3ba566 100644 (file)
@@ -16,7 +16,6 @@ const rule = require("../../../lib/rules/wrap-regex"),
 // Tests
 //------------------------------------------------------------------------------
 
-
 const ruleTester = new RuleTester();
 
 ruleTester.run("wrap-regex", rule, {
index 9ba99aea6ba718cf795ebad15b99903e2089d0b8..61d616b3ae01a1eabf7cf6ead46f1b14ad29e9f7 100644 (file)
@@ -25,12 +25,13 @@ const assert = require("chai").assert,
     { Linter } = require("../../../lib/linter"),
     validator = require("../../../lib/shared/config-validator"),
     Rules = require("../../../lib/linter/rules");
-const linter = new Linter();
 
 //------------------------------------------------------------------------------
-// Tests
+// Helpers
 //------------------------------------------------------------------------------
 
+const linter = new Linter();
+
 /**
  * Fake a rule object
  * @param {Object} context context passed to the rules by eslint
@@ -101,6 +102,10 @@ const mockRequiredOptionsRule = {
     }
 };
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 describe("Validator", () => {
 
     /**
index 5fbfb861c27b7546d2555b6f0c0ccef5060cb7d3..8e6ef28e18a60d0dd3fc4392b3f37a083fac7273 100644 (file)
@@ -1,8 +1,16 @@
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const assert = require("chai").assert;
 const Traverser = require("../../../lib/shared/traverser");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 describe("Traverser", () => {
     it("traverses all keys except 'parent', 'leadingComments', and 'trailingComments'", () => {
         const traverser = new Traverser();
diff --git a/eslint/tests/lib/unsupported-api.js b/eslint/tests/lib/unsupported-api.js
new file mode 100644 (file)
index 0000000..dd88be5
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * @fileoverview Tests for unsupported-api.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
+const assert = require("chai").assert,
+    { LazyLoadingRuleMap } = require("../../lib/rules/utils/lazy-loading-rule-map"),
+    api = require("../../lib/unsupported-api");
+
+//-----------------------------------------------------------------------------
+// Tests
+//-----------------------------------------------------------------------------
+
+describe("unsupported-api", () => {
+
+    it("should have FileEnumerator exposed", () => {
+        assert.isFunction(api.FileEnumerator);
+    });
+
+    it("should have builtinRules exposed", () => {
+        assert.instanceOf(api.builtinRules, LazyLoadingRuleMap);
+    });
+
+});
index 9a56c8c61b58354fae964a897852e5bbe275fd00..84cbea784d9b7bc8cef6d67f3b588cbd4ac0933e 100644 (file)
@@ -1,8 +1,16 @@
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const { assert } = require("chai");
 const reduceBadExampleSize = require("../../tools/code-sample-minimizer");
 
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 describe("reduceBadExampleSize()", () => {
     it("extracts relevant part of deeply nested code", () => {
         const initialCode = `
diff --git a/eslint/tests/tools/internal-rules/consistent-docs-url.js b/eslint/tests/tools/internal-rules/consistent-docs-url.js
deleted file mode 100644 (file)
index 4b3d3a0..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * @fileoverview Tests for internal-consistent-docs-url rule.
- * @author Patrick McElhaney
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const rule = require("../../../tools/internal-rules/consistent-docs-url"),
-    { RuleTester } = require("../../../lib/rule-tester");
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-const ruleTester = new RuleTester();
-
-ruleTester.run("consistent-docs-url", rule, {
-    valid: [
-
-        // wrong exports format: "internal-no-invalid-meta" reports this already
-        [
-            "module.exports = function(context) {",
-            "    return {",
-            "        Program: function(node) {}",
-            "    };",
-            "};"
-        ].join("\n"),
-        [
-            "module.exports = {",
-            "    meta: {",
-            "        docs: {",
-            "            url: 'https://eslint.org/docs/rules/<input>'",
-            "        }",
-            "    },",
-            "    create: function(context) {",
-            "        return {};",
-            "    }",
-            "};"
-        ].join("\n")
-    ],
-    invalid: [
-        {
-            code: [
-                "module.exports = {",
-                "    meta: {",
-                "    },",
-
-                "    create: function(context) {",
-                "        return {};",
-                "    }",
-                "};"
-            ].join("\n"),
-            errors: [{
-                messageId: "missingMetaDocs",
-                line: 2,
-                column: 5
-            }]
-        },
-        {
-            code: [
-                "module.exports = {",
-                "    meta: {",
-                "        docs: {}",
-                "    },",
-
-                "    create: function(context) {",
-                "        return {};",
-                "    }",
-                "};"
-            ].join("\n"),
-            errors: [{
-                messageId: "missingMetaDocsUrl",
-                line: 3,
-                column: 9
-            }]
-        },
-        {
-            code: [
-                "module.exports = {",
-                "    meta: {",
-                "        docs: {",
-                "            url: 'http://example.com/wrong-url'",
-                "        }",
-                "    },",
-                "    create: function(context) {",
-                "        return {};",
-                "    }",
-                "};"
-            ].join("\n"),
-            errors: [{
-                messageId: "incorrectUrl",
-                data: {
-                    expected: "https://eslint.org/docs/rules/<input>",
-                    url: "http://example.com/wrong-url"
-                },
-                line: 4,
-                column: 18
-            }]
-        }
-    ]
-});
diff --git a/eslint/tests/tools/internal-rules/consistent-meta-messages.js b/eslint/tests/tools/internal-rules/consistent-meta-messages.js
deleted file mode 100644 (file)
index 4df8be9..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * @fileoverview Tests for consistent-meta-messages rule.
- * @author 薛定谔的猫<hh_2013@foxmail.com>
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const rule = require("../../../tools/internal-rules/consistent-meta-messages");
-const { RuleTester } = require("../../../lib/rule-tester");
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-const ruleTester = new RuleTester();
-
-ruleTester.run("consistent-meta-messages", rule, {
-    valid: [
-        `module.exports = {
-            meta: {
-                messages: {unexpected: "an error occurs."}
-            }
-        };`
-    ],
-    invalid: [
-        {
-            code: `
-            module.exports = {
-                meta: {}
-            };`,
-            errors: [{ messageId: "expectedMessages" }]
-        }
-    ]
-});
index d6dcff717289113ca7a6814889f4d5546bb62275..e4b104c1fe949b8ac39aa54e3d1dd47f650d5991 100644 (file)
@@ -1,7 +1,16 @@
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
 const rule = require("../../../tools/internal-rules/multiline-comment-style");
 const { RuleTester } = require("../../../lib/rule-tester");
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
 const ruleTester = new RuleTester();
 
 ruleTester.run("internal-rules/multiline-comment-style", rule, {
index a5ff4d55a3f3bfcd695bddfde8a5bd1eecff42d6..5013e4bdbe3debdaec45d62f4109d1413e5e5349 100644 (file)
@@ -27,7 +27,6 @@ ruleTester.run("no-invalid-meta", rule, {
             "    meta: {",
             "        docs: {",
             "            description: 'some rule',",
-            "            category: 'Internal',",
             "            recommended: false",
             "        },",
             "        schema: []",
@@ -51,7 +50,6 @@ ruleTester.run("no-invalid-meta", rule, {
             "    meta: {",
             "        docs: {",
             "            description: 'some rule',",
-            "            category: 'Internal',",
             "            recommended: false",
             "        },",
             "        schema: []",
@@ -73,7 +71,6 @@ ruleTester.run("no-invalid-meta", rule, {
             "    meta: {",
             "        docs: {",
             "            description: 'some rule',",
-            "            category: 'Internal',",
             "            recommended: false",
             "        },",
             "        schema: [],",
@@ -96,20 +93,6 @@ ruleTester.run("no-invalid-meta", rule, {
         ].join("\n")
     ],
     invalid: [
-        {
-            code: [
-                "module.exports = function(context) {",
-                "    return {",
-                "        Program: function(node) {}",
-                "    };",
-                "};"
-            ].join("\n"),
-            errors: [{
-                messageId: "incorrectExport",
-                line: 1,
-                column: 18
-            }]
-        },
         {
             code: [
                 "module.exports = {",
@@ -164,61 +147,12 @@ ruleTester.run("no-invalid-meta", rule, {
                 column: 5
             }]
         },
-        {
-            code: [
-                "module.exports = {",
-                "    meta: {",
-                "        docs: {",
-                "            category: 'Internal',",
-                "            recommended: false",
-                "        },",
-                "        schema: []",
-                "    },",
-
-                "    create: function(context) {",
-                "        return {",
-                "            Program: function(node) {}",
-                "        };",
-                "    }",
-                "};"
-            ].join("\n"),
-            errors: [{
-                messageId: "missingMetaDocsDescription",
-                line: 2,
-                column: 5
-            }]
-        },
-        {
-            code: [
-                "module.exports = {",
-                "    meta: {",
-                "        docs: {",
-                "            description: 'some rule',",
-                "            recommended: false",
-                "        },",
-                "        schema: []",
-                "    },",
-
-                "    create: function(context) {",
-                "        return {",
-                "            Program: function(node) {}",
-                "        };",
-                "    }",
-                "};"
-            ].join("\n"),
-            errors: [{
-                messageId: "missingMetaDocsCategory",
-                line: 2,
-                column: 5
-            }]
-        },
         {
             code: [
                 "module.exports = {",
                 "    meta: {",
                 "        docs: {",
                 "            description: 'some rule',",
-                "            category: 'Internal'",
                 "        },",
                 "        schema: []",
                 "    },",
@@ -236,30 +170,6 @@ ruleTester.run("no-invalid-meta", rule, {
                 column: 5
             }]
         },
-        {
-            code: [
-                "module.exports = {",
-                "    meta: {",
-                "        docs: {",
-                "            description: 'some rule',",
-                "            category: 'Internal',",
-                "            recommended: false",
-                "        }",
-                "    },",
-
-                "    create: function(context) {",
-                "        return {",
-                "            Program: function(node) {}",
-                "        };",
-                "    }",
-                "};"
-            ].join("\n"),
-            errors: [{
-                messageId: "missingMetaSchema",
-                line: 2,
-                column: 5
-            }]
-        },
         {
             code: "",
             errors: [{
diff --git a/eslint/tools/internal-rules/consistent-docs-url.js b/eslint/tools/internal-rules/consistent-docs-url.js
deleted file mode 100644 (file)
index 052fe55..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/**
- * @fileoverview Internal rule to enforce meta.docs.url conventions.
- * @author Patrick McElhaney
- */
-
-"use strict";
-
-const path = require("path");
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Gets the property of the Object node passed in that has the name specified.
- * @param {string} property Name of the property to return.
- * @param {ASTNode} node The ObjectExpression node.
- * @returns {ASTNode} The Property node or null if not found.
- */
-function getPropertyFromObject(property, node) {
-    const properties = node.properties;
-
-    if (!Array.isArray(properties)) {
-
-        // if properties is not an array, "internal-no-invalid-meta" will already report this.
-        return null;
-    }
-
-    for (let i = 0; i < properties.length; i++) {
-        if (properties[i].key.name === property) {
-            return properties[i];
-        }
-    }
-
-    return null;
-}
-
-/**
- * Verifies that the meta.docs.url property is present and has the correct value.
- * @param {RuleContext} context The ESLint rule context.
- * @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
- * @returns {void}
- */
-function checkMetaDocsUrl(context, exportsNode) {
-    if (exportsNode.type !== "ObjectExpression") {
-
-        // if the exported node is not the correct format, "internal-no-invalid-meta" will already report this.
-        return;
-    }
-
-    const metaProperty = getPropertyFromObject("meta", exportsNode);
-    const metaDocs = metaProperty && getPropertyFromObject("docs", metaProperty.value);
-    const metaDocsUrl = metaDocs && getPropertyFromObject("url", metaDocs.value);
-
-    if (!metaDocs) {
-        context.report({
-            node: metaProperty,
-            messageId: "missingMetaDocs"
-        });
-        return;
-    }
-
-    if (!metaDocsUrl) {
-        context.report({
-            node: metaDocs,
-            messageId: "missingMetaDocsUrl"
-        });
-        return;
-    }
-
-    const ruleId = path.basename(context.getFilename().replace(/.js$/u, ""));
-    const expected = `https://eslint.org/docs/rules/${ruleId}`;
-    const url = metaDocsUrl.value.value;
-
-    if (url !== expected) {
-        context.report({
-            node: metaDocsUrl.value,
-            messageId: "incorrectUrl",
-            data: { expected, url }
-        });
-    }
-
-}
-
-//------------------------------------------------------------------------------
-// Rule Definition
-//------------------------------------------------------------------------------
-
-module.exports = {
-    meta: {
-        docs: {
-            description: "enforce correct conventions of `meta.docs.url` property in core rules",
-            category: "Internal",
-            recommended: false
-        },
-        type: "suggestion",
-        schema: [],
-        messages: {
-            missingMetaDocs: "Rule is missing a meta.docs property.",
-            missingMetaDocsUrl: "Rule is missing a meta.docs.url property.",
-            incorrectUrl: 'Incorrect url. Expected "{{ expected }}" but got "{{ url }}".'
-        }
-    },
-
-    create(context) {
-        return {
-            AssignmentExpression(node) {
-                if (node.left &&
-                    node.right &&
-                    node.left.type === "MemberExpression" &&
-                    node.left.object.name === "module" &&
-                    node.left.property.name === "exports") {
-
-                    checkMetaDocsUrl(context, node.right);
-                }
-            }
-        };
-    }
-};
diff --git a/eslint/tools/internal-rules/consistent-meta-messages.js b/eslint/tools/internal-rules/consistent-meta-messages.js
deleted file mode 100644 (file)
index b094c86..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * @fileoverview A rule to enforce using `meta.messages` property in core rules
- * @author 薛定谔的猫<hh_2013@foxmail.com>
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Gets the property of the Object node passed in that has the name specified.
- * @param {string} property Name of the property to return.
- * @param {ASTNode} node The ObjectExpression node.
- * @returns {ASTNode} The Property node or null if not found.
- */
-function getPropertyFromObject(property, node) {
-    const properties = node.properties;
-
-    for (let i = 0; i < properties.length; i++) {
-        if (properties[i].key.name === property) {
-            return properties[i];
-        }
-    }
-
-    return null;
-}
-
-/**
- * Verifies that the meta.messages property is present.
- * TODO: check it has the correct value
- * @param {RuleContext} context The ESLint rule context.
- * @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
- * @returns {void}
- */
-function checkMetaMessages(context, exportsNode) {
-    if (exportsNode.type !== "ObjectExpression") {
-
-        // if the exported node is not the correct format, "internal-no-invalid-meta" will already report this.
-        return;
-    }
-
-    const metaProperty = getPropertyFromObject("meta", exportsNode);
-    const messages = metaProperty && getPropertyFromObject("messages", metaProperty.value);
-
-    if (!messages) {
-        context.report({
-            node: metaProperty,
-            messageId: "expectedMessages"
-        });
-    }
-}
-
-//------------------------------------------------------------------------------
-// Rule Definition
-//------------------------------------------------------------------------------
-
-module.exports = {
-    meta: {
-        docs: {
-            description: "enforce using `meta.messages` property in core rules",
-            category: "Internal",
-            recommended: false
-        },
-        schema: [],
-        type: "suggestion",
-        messages: {
-            expectedMessages: "Expected `meta.messages` property."
-        }
-    },
-
-    create(context) {
-        return {
-            "AssignmentExpression[left.object.name='module'][left.property.name='exports']"(node) {
-                checkMetaMessages(context, node.right);
-            }
-        };
-    }
-};
index b0a0c9d854f934be21c1085b5d129fb852d8b82f..4492b989d10fe4155974c725d7084faa755193b0 100644 (file)
@@ -13,7 +13,7 @@ const multilineCommentStyle = require("../../lib/rules/multiline-comment-style")
 //------------------------------------------------------------------------------
 
 // The `no-invalid-meta` internal rule has a false positive here.
-// eslint-disable-next-line internal-rules/no-invalid-meta
+// eslint-disable-next-line internal-rules/no-invalid-meta -- Using rule composer
 module.exports = ruleComposer.filterReports(
     multilineCommentStyle,
     (problem, metadata) => {
index e5a74b359b5ab3faf6554d605990650fa56915f0..2bfa2e21a4aee76f951e2392633d77aa18adfd75 100644 (file)
@@ -50,28 +50,6 @@ function hasMetaDocs(metaPropertyNode) {
     return Boolean(getPropertyFromObject("docs", metaPropertyNode.value));
 }
 
-/**
- * Whether this `meta` ObjectExpression has a `docs.description` property defined or not.
- * @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
- * @returns {boolean} `true` if a `docs.description` property exists.
- */
-function hasMetaDocsDescription(metaPropertyNode) {
-    const metaDocs = getPropertyFromObject("docs", metaPropertyNode.value);
-
-    return metaDocs && getPropertyFromObject("description", metaDocs.value);
-}
-
-/**
- * Whether this `meta` ObjectExpression has a `docs.category` property defined or not.
- * @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
- * @returns {boolean} `true` if a `docs.category` property exists.
- */
-function hasMetaDocsCategory(metaPropertyNode) {
-    const metaDocs = getPropertyFromObject("docs", metaPropertyNode.value);
-
-    return metaDocs && getPropertyFromObject("category", metaDocs.value);
-}
-
 /**
  * Whether this `meta` ObjectExpression has a `docs.recommended` property defined or not.
  * @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
@@ -83,15 +61,6 @@ function hasMetaDocsRecommended(metaPropertyNode) {
     return metaDocs && getPropertyFromObject("recommended", metaDocs.value);
 }
 
-/**
- * Whether this `meta` ObjectExpression has a `schema` property defined or not.
- * @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
- * @returns {boolean} `true` if a `schema` property exists.
- */
-function hasMetaSchema(metaPropertyNode) {
-    return getPropertyFromObject("schema", metaPropertyNode.value);
-}
-
 /**
  * Checks the validity of the meta definition of this rule and reports any errors found.
  * @param {RuleContext} context The ESLint rule context.
@@ -111,33 +80,9 @@ function checkMetaValidity(context, exportsNode) {
         return;
     }
 
-    if (!hasMetaDocsDescription(metaProperty)) {
-        context.report({ node: metaProperty, messageId: "missingMetaDocsDescription" });
-        return;
-    }
-
-    if (!hasMetaDocsCategory(metaProperty)) {
-        context.report({ node: metaProperty, messageId: "missingMetaDocsCategory" });
-        return;
-    }
-
     if (!hasMetaDocsRecommended(metaProperty)) {
         context.report({ node: metaProperty, messageId: "missingMetaDocsRecommended" });
-        return;
     }
-
-    if (!hasMetaSchema(metaProperty)) {
-        context.report({ node: metaProperty, messageId: "missingMetaSchema" });
-    }
-}
-
-/**
- * Whether this node is the correct format for a rule definition or not.
- * @param {ASTNode} node node that the rule exports.
- * @returns {boolean} `true` if the exported node is the correct format for a rule definition
- */
-function isCorrectExportsFormat(node) {
-    return node.type === "ObjectExpression";
 }
 
 //------------------------------------------------------------------------------
@@ -148,7 +93,6 @@ module.exports = {
     meta: {
         docs: {
             description: "enforce correct use of `meta` property in core rules",
-            category: "Internal",
             recommended: false
         },
         type: "problem",
@@ -156,12 +100,8 @@ module.exports = {
         messages: {
             missingMeta: "Rule is missing a meta property.",
             missingMetaDocs: "Rule is missing a meta.docs property.",
-            missingMetaDocsDescription: "Rule is missing a meta.docs.description property.",
-            missingMetaDocsCategory: "Rule is missing a meta.docs.category property.",
             missingMetaDocsRecommended: "Rule is missing a meta.docs.recommended property.",
-            missingMetaSchema: "Rule is missing a meta.schema property.",
-            noExport: "Rule does not export anything. Make sure rule exports an object according to new rule format.",
-            incorrectExport: "Rule does not export an Object. Make sure the rule follows the new rule format."
+            noExport: "Rule does not export anything. Make sure rule exports an object according to new rule format."
         }
     },
 
@@ -186,11 +126,6 @@ module.exports = {
                         node,
                         messageId: "noExport"
                     });
-                } else if (!isCorrectExportsFormat(exportsNode)) {
-                    context.report({
-                        node: exportsNode,
-                        messageId: "incorrectExport"
-                    });
                 } else {
                     checkMetaValidity(context, exportsNode);
                 }
index a5ec8e192bfcfdc0d55f5244349fa09d486dff61..ce4449a6b6ae6e1141d4a7f118405a2a41357998 100644 (file)
@@ -4,7 +4,7 @@
  */
 "use strict";
 
-/* global describe, it */
+/* eslint-env mocha -- Mocha */
 
 //------------------------------------------------------------------------------
 // Requirements
index d35446d9dfb9d5e6b42a17b32ca9e857827338ae..4a71a8b09edddf317da923448f1706fe45c470b4 100644 (file)
     "no-unsafe-optional-chaining": "problem",
     "no-unused-expressions": "suggestion",
     "no-unused-labels": "suggestion",
+    "no-unused-private-class-members": "problem",
     "no-unused-vars": "problem",
     "no-use-before-define": "problem",
     "no-useless-backreference": "problem",
index 24fe3c01d2b3ecda426803c4912d4830eded7cf2..55efb6f2c521a0f43c488083902f2c6dadbb9193 100644 (file)
@@ -48,7 +48,7 @@ delete allSponsors.backers;
  * @returns {string} The HTML for the members list.
  */
 function formatTeamMembers(members) {
-    /* eslint-disable indent*/
+    /* eslint-disable indent -- Allow deeper template substitution indent */
     return stripIndents`
         <table><tbody><tr>${
         members.map((member, index) => `<td align="center" valign="top" width="11%">
@@ -58,7 +58,7 @@ function formatTeamMembers(members) {
             </a>
             </td>${(index + 1) % 9 === 0 ? "</tr><tr>" : ""}`).join("")
         }</tr></tbody></table>`;
-    /* eslint-enable indent*/
+    /* eslint-enable indent -- Allow deeper template substitution indent */
 }
 
 /**
@@ -69,7 +69,7 @@ function formatTeamMembers(members) {
 function formatSponsors(sponsors) {
     const nonEmptySponsors = Object.keys(sponsors).filter(tier => sponsors[tier].length > 0);
 
-    /* eslint-disable indent*/
+    /* eslint-disable indent -- Allow deeper template substitution indent */
     return stripIndents`<!--sponsorsstart-->
         ${
             nonEmptySponsors.map(tier => `<h3>${tier[0].toUpperCase()}${tier.slice(1)} Sponsors</h3>
@@ -78,7 +78,7 @@ function formatSponsors(sponsors) {
             }</p>`).join("")
         }
     <!--sponsorsend-->`;
-    /* eslint-enable indent*/
+    /* eslint-enable indent -- Allow deeper template substitution indent */
 }
 
 //-----------------------------------------------------------------------------
index 0cb40a7b1769ddc7227ae2dd4e8b6b2faada3388..6a68586d6de9035ce3e1d262aca92088b8782927 100644 (file)
@@ -30,29 +30,11 @@ module.exports = (fileInfo, api) => {
         }
 
         const typeNode = metaNode.value.properties.find(node => node.key.name === "type");
-        const docsNode = metaNode.value.properties.find(node => node.key.name === "docs");
-        const categoryNode = docsNode.value.properties.find(node => node.key.name === "category").value;
 
         let ruleType;
 
-        // the rule-types.json file takes highest priority
         if (ruleName in ruleTypes) {
             ruleType = ruleTypes[ruleName];
-        } else {
-
-            // otherwise fallback to category
-            switch (categoryNode.value) {
-                case "Stylistic Issues":
-                    ruleType = "style";
-                    break;
-
-                case "Possible Errors":
-                    ruleType = "problem";
-                    break;
-
-                default:
-                    ruleType = "suggestion";
-            }
         }
 
         if (typeNode) {