diff --git a/CHANGELOG.md b/CHANGELOG.md index e844a0102464..587bb83aa715 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,27 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + + +### Bug Fixes + +* **ast-spec:** remove duplicate union types from `Expression` ([#3770](https://github.com/typescript-eslint/typescript-eslint/issues/3770)) ([463e768](https://github.com/typescript-eslint/typescript-eslint/commit/463e768978731d019345f6552d7fd7a073a80192)) +* **utils:** support immutable arrays in `ReportFixFunction` ([#3830](https://github.com/typescript-eslint/typescript-eslint/issues/3830)) ([8218055](https://github.com/typescript-eslint/typescript-eslint/commit/8218055d6dfd94c9e6c8645848f981d9d51ce08c)) + + +### Features + +* **eslint-plugin:** [prefer-readonly-parameter-types] add option treatMethodsAsReadonly ([#3733](https://github.com/typescript-eslint/typescript-eslint/issues/3733)) ([a46e318](https://github.com/typescript-eslint/typescript-eslint/commit/a46e3182c8a0b07c914605d6d9fe28ef36a7c32a)) +* **eslint-plugin:** [restrict-template-expressions] add option to allow RegExp ([#3709](https://github.com/typescript-eslint/typescript-eslint/issues/3709)) ([363b3dc](https://github.com/typescript-eslint/typescript-eslint/commit/363b3dc4dd0dc343311c729d75935b10f9d2fd5e)) +* **eslint-plugin:** add `no-meaningless-void-operator` rule ([#3641](https://github.com/typescript-eslint/typescript-eslint/issues/3641)) ([ea40ab6](https://github.com/typescript-eslint/typescript-eslint/commit/ea40ab659351ae7cf7235ea063d42ac155b11e5f)) +* **eslint-plugin:** add extension rule `padding-line-between-statements` ([#3418](https://github.com/typescript-eslint/typescript-eslint/issues/3418)) ([f79ae9b](https://github.com/typescript-eslint/typescript-eslint/commit/f79ae9b58e82f4fddef640a34a1d7ff92b763e65)) +* **experimental-utils:** extract `isNodeOfType` out of `ast-utils`' `predicates` ([#3677](https://github.com/typescript-eslint/typescript-eslint/issues/3677)) ([4bfa437](https://github.com/typescript-eslint/typescript-eslint/commit/4bfa4375aff8f65057d4aa116e435803cbc6b464)) + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) diff --git a/lerna.json b/lerna.json index 9a59e7c6919a..bcd9745966f0 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "4.30.0", + "version": "4.31.0", "npmClient": "yarn", "useWorkspaces": true, "stream": true diff --git a/packages/ast-spec/CHANGELOG.md b/packages/ast-spec/CHANGELOG.md index bec6a7f1f74b..9383371e9b2f 100644 --- a/packages/ast-spec/CHANGELOG.md +++ b/packages/ast-spec/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + + +### Bug Fixes + +* **ast-spec:** remove duplicate union types from `Expression` ([#3770](https://github.com/typescript-eslint/typescript-eslint/issues/3770)) ([463e768](https://github.com/typescript-eslint/typescript-eslint/commit/463e768978731d019345f6552d7fd7a073a80192)) + + +### Features + +* **eslint-plugin:** add extension rule `padding-line-between-statements` ([#3418](https://github.com/typescript-eslint/typescript-eslint/issues/3418)) ([f79ae9b](https://github.com/typescript-eslint/typescript-eslint/commit/f79ae9b58e82f4fddef640a34a1d7ff92b763e65)) + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) diff --git a/packages/ast-spec/package.json b/packages/ast-spec/package.json index b0af29d0e957..b9b92b3361f6 100644 --- a/packages/ast-spec/package.json +++ b/packages/ast-spec/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/ast-spec", - "version": "4.30.0", + "version": "4.31.0", "description": "TypeScript-ESTree AST spec", "private": true, "keywords": [ diff --git a/packages/ast-spec/src/base/LiteralBase.ts b/packages/ast-spec/src/base/LiteralBase.ts index 01a480ddc3cb..c878fe7b7aa5 100644 --- a/packages/ast-spec/src/base/LiteralBase.ts +++ b/packages/ast-spec/src/base/LiteralBase.ts @@ -1,6 +1,8 @@ +import type { AST_NODE_TYPES } from '../ast-node-types'; import type { BaseNode } from './BaseNode'; export interface LiteralBase extends BaseNode { + type: AST_NODE_TYPES.Literal; raw: string; value: RegExp | bigint | boolean | number | string | null; } diff --git a/packages/ast-spec/src/expression/literal/BigIntLiteral/spec.ts b/packages/ast-spec/src/expression/literal/BigIntLiteral/spec.ts index c27a85543ff8..2df33369cf74 100644 --- a/packages/ast-spec/src/expression/literal/BigIntLiteral/spec.ts +++ b/packages/ast-spec/src/expression/literal/BigIntLiteral/spec.ts @@ -1,8 +1,6 @@ -import type { AST_NODE_TYPES } from '../../../ast-node-types'; import type { LiteralBase } from '../../../base/LiteralBase'; export interface BigIntLiteral extends LiteralBase { - type: AST_NODE_TYPES.Literal; value: bigint | null; bigint: string; } diff --git a/packages/ast-spec/src/expression/literal/BooleanLiteral/spec.ts b/packages/ast-spec/src/expression/literal/BooleanLiteral/spec.ts index a2310a698d02..be7477f015cb 100644 --- a/packages/ast-spec/src/expression/literal/BooleanLiteral/spec.ts +++ b/packages/ast-spec/src/expression/literal/BooleanLiteral/spec.ts @@ -1,8 +1,6 @@ -import type { AST_NODE_TYPES } from '../../../ast-node-types'; import type { LiteralBase } from '../../../base/LiteralBase'; export interface BooleanLiteral extends LiteralBase { - type: AST_NODE_TYPES.Literal; value: boolean; raw: 'false' | 'true'; } diff --git a/packages/ast-spec/src/expression/literal/NullLiteral/spec.ts b/packages/ast-spec/src/expression/literal/NullLiteral/spec.ts index f520b7b3d454..03ff8a43c866 100644 --- a/packages/ast-spec/src/expression/literal/NullLiteral/spec.ts +++ b/packages/ast-spec/src/expression/literal/NullLiteral/spec.ts @@ -1,8 +1,6 @@ -import type { AST_NODE_TYPES } from '../../../ast-node-types'; import type { LiteralBase } from '../../../base/LiteralBase'; export interface NullLiteral extends LiteralBase { - type: AST_NODE_TYPES.Literal; value: null; raw: 'null'; } diff --git a/packages/ast-spec/src/expression/literal/NumberLiteral/spec.ts b/packages/ast-spec/src/expression/literal/NumberLiteral/spec.ts index 8155bb45cafc..5a6cc12a59c1 100644 --- a/packages/ast-spec/src/expression/literal/NumberLiteral/spec.ts +++ b/packages/ast-spec/src/expression/literal/NumberLiteral/spec.ts @@ -1,7 +1,5 @@ -import type { AST_NODE_TYPES } from '../../../ast-node-types'; import type { LiteralBase } from '../../../base/LiteralBase'; export interface NumberLiteral extends LiteralBase { - type: AST_NODE_TYPES.Literal; value: number; } diff --git a/packages/ast-spec/src/expression/literal/RegExpLiteral/spec.ts b/packages/ast-spec/src/expression/literal/RegExpLiteral/spec.ts index ab45f651b768..f72b53c7956b 100644 --- a/packages/ast-spec/src/expression/literal/RegExpLiteral/spec.ts +++ b/packages/ast-spec/src/expression/literal/RegExpLiteral/spec.ts @@ -1,8 +1,6 @@ -import type { AST_NODE_TYPES } from '../../../ast-node-types'; import type { LiteralBase } from '../../../base/LiteralBase'; export interface RegExpLiteral extends LiteralBase { - type: AST_NODE_TYPES.Literal; value: RegExp | null; regex: { pattern: string; diff --git a/packages/ast-spec/src/expression/literal/StringLiteral/spec.ts b/packages/ast-spec/src/expression/literal/StringLiteral/spec.ts index de83d9d20e1d..32734f443e29 100644 --- a/packages/ast-spec/src/expression/literal/StringLiteral/spec.ts +++ b/packages/ast-spec/src/expression/literal/StringLiteral/spec.ts @@ -1,7 +1,5 @@ -import type { AST_NODE_TYPES } from '../../../ast-node-types'; import type { LiteralBase } from '../../../base/LiteralBase'; export interface StringLiteral extends LiteralBase { - type: AST_NODE_TYPES.Literal; value: string; } diff --git a/packages/ast-spec/src/unions/Expression.ts b/packages/ast-spec/src/unions/Expression.ts index fd1a14b114e2..5359dc9c69c7 100644 --- a/packages/ast-spec/src/unions/Expression.ts +++ b/packages/ast-spec/src/unions/Expression.ts @@ -50,10 +50,8 @@ export type Expression = | CallExpression | ChainExpression | ClassExpression - | ClassExpression | ConditionalExpression | FunctionExpression - | FunctionExpression | Identifier | ImportExpression | JSXElement diff --git a/packages/ast-spec/src/unions/LeftHandSideExpression.ts b/packages/ast-spec/src/unions/LeftHandSideExpression.ts index 5d6922ed42d4..fc3a80b23618 100644 --- a/packages/ast-spec/src/unions/LeftHandSideExpression.ts +++ b/packages/ast-spec/src/unions/LeftHandSideExpression.ts @@ -9,6 +9,7 @@ import type { JSXFragment } from '../expression/JSXFragment/spec'; import type { MemberExpression } from '../expression/MemberExpression/spec'; import type { MetaProperty } from '../expression/MetaProperty/spec'; import type { ObjectExpression } from '../expression/ObjectExpression/spec'; +import type { SequenceExpression } from '../expression/spec'; import type { Super } from '../expression/Super/spec'; import type { TaggedTemplateExpression } from '../expression/TaggedTemplateExpression/spec'; import type { ThisExpression } from '../expression/ThisExpression/spec'; @@ -34,6 +35,7 @@ export type LeftHandSideExpression = | MetaProperty | ObjectExpression | ObjectPattern + | SequenceExpression | Super | TaggedTemplateExpression | ThisExpression diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md index a704b20f06e4..73b587a00800 100644 --- a/packages/eslint-plugin-internal/CHANGELOG.md +++ b/packages/eslint-plugin-internal/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index 2b6a5a56a690..0364bccbc9bc 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-internal", - "version": "4.30.0", + "version": "4.31.0", "private": true, "main": "dist/index.js", "scripts": { @@ -14,7 +14,7 @@ }, "dependencies": { "@types/prettier": "*", - "@typescript-eslint/experimental-utils": "4.30.0", + "@typescript-eslint/experimental-utils": "4.31.0", "prettier": "*" } } diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index ef836910ccfd..c5e66c769339 100644 --- a/packages/eslint-plugin-tslint/CHANGELOG.md +++ b/packages/eslint-plugin-tslint/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index df35db555106..5f0f25a2d312 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-tslint", - "version": "4.30.0", + "version": "4.31.0", "main": "dist/index.js", "typings": "src/index.ts", "description": "TSLint wrapper plugin for ESLint", @@ -38,7 +38,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "4.30.0", + "@typescript-eslint/experimental-utils": "4.31.0", "lodash": "^4.17.21" }, "peerDependencies": { @@ -48,6 +48,6 @@ }, "devDependencies": { "@types/lodash": "*", - "@typescript-eslint/parser": "4.30.0" + "@typescript-eslint/parser": "4.31.0" } } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index a433e2fc77eb..caae41c1f917 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + + +### Bug Fixes + +* **utils:** support immutable arrays in `ReportFixFunction` ([#3830](https://github.com/typescript-eslint/typescript-eslint/issues/3830)) ([8218055](https://github.com/typescript-eslint/typescript-eslint/commit/8218055d6dfd94c9e6c8645848f981d9d51ce08c)) + + +### Features + +* **eslint-plugin:** [prefer-readonly-parameter-types] add option treatMethodsAsReadonly ([#3733](https://github.com/typescript-eslint/typescript-eslint/issues/3733)) ([a46e318](https://github.com/typescript-eslint/typescript-eslint/commit/a46e3182c8a0b07c914605d6d9fe28ef36a7c32a)) +* **eslint-plugin:** [restrict-template-expressions] add option to allow RegExp ([#3709](https://github.com/typescript-eslint/typescript-eslint/issues/3709)) ([363b3dc](https://github.com/typescript-eslint/typescript-eslint/commit/363b3dc4dd0dc343311c729d75935b10f9d2fd5e)) +* **eslint-plugin:** add `no-meaningless-void-operator` rule ([#3641](https://github.com/typescript-eslint/typescript-eslint/issues/3641)) ([ea40ab6](https://github.com/typescript-eslint/typescript-eslint/commit/ea40ab659351ae7cf7235ea063d42ac155b11e5f)) +* **eslint-plugin:** add extension rule `padding-line-between-statements` ([#3418](https://github.com/typescript-eslint/typescript-eslint/issues/3418)) ([f79ae9b](https://github.com/typescript-eslint/typescript-eslint/commit/f79ae9b58e82f4fddef640a34a1d7ff92b763e65)) + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 6f890d272562..5b32c4ef29e7 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -128,6 +128,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/no-implicit-any-catch`](./docs/rules/no-implicit-any-catch.md) | Disallow usage of the implicit `any` type in catch clauses | | :wrench: | | | [`@typescript-eslint/no-inferrable-types`](./docs/rules/no-inferrable-types.md) | Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean | :white_check_mark: | :wrench: | | | [`@typescript-eslint/no-invalid-void-type`](./docs/rules/no-invalid-void-type.md) | Disallows usage of `void` type outside of generic or return types | | | | +| [`@typescript-eslint/no-meaningless-void-operator`](./docs/rules/no-meaningless-void-operator.md) | Disallow the `void` operator except when used to discard a value | | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-misused-new`](./docs/rules/no-misused-new.md) | Enforce valid definition of `new` and `constructor` | :white_check_mark: | | | | [`@typescript-eslint/no-misused-promises`](./docs/rules/no-misused-promises.md) | Avoid using promises in places not designed to handle them | :white_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-namespace`](./docs/rules/no-namespace.md) | Disallow the use of custom TypeScript modules and namespaces | :white_check_mark: | | | @@ -190,43 +191,44 @@ In these cases, we create what we call an extension rule; a rule within our plug **Key**: :white_check_mark: = recommended, :wrench: = fixable, :thought_balloon: = requires type information -| Name | Description | :white_check_mark: | :wrench: | :thought_balloon: | -| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------ | -------- | ----------------- | -| [`@typescript-eslint/brace-style`](./docs/rules/brace-style.md) | Enforce consistent brace style for blocks | | :wrench: | | -| [`@typescript-eslint/comma-dangle`](./docs/rules/comma-dangle.md) | Require or disallow trailing comma | | :wrench: | | -| [`@typescript-eslint/comma-spacing`](./docs/rules/comma-spacing.md) | Enforces consistent spacing before and after commas | | :wrench: | | -| [`@typescript-eslint/default-param-last`](./docs/rules/default-param-last.md) | Enforce default parameters to be last | | | | -| [`@typescript-eslint/dot-notation`](./docs/rules/dot-notation.md) | enforce dot notation whenever possible | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/func-call-spacing`](./docs/rules/func-call-spacing.md) | Require or disallow spacing between function identifiers and their invocations | | :wrench: | | -| [`@typescript-eslint/indent`](./docs/rules/indent.md) | Enforce consistent indentation | | :wrench: | | -| [`@typescript-eslint/init-declarations`](./docs/rules/init-declarations.md) | require or disallow initialization in variable declarations | | | | -| [`@typescript-eslint/keyword-spacing`](./docs/rules/keyword-spacing.md) | Enforce consistent spacing before and after keywords | | :wrench: | | -| [`@typescript-eslint/lines-between-class-members`](./docs/rules/lines-between-class-members.md) | Require or disallow an empty line between class members | | :wrench: | | -| [`@typescript-eslint/no-array-constructor`](./docs/rules/no-array-constructor.md) | Disallow generic `Array` constructors | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-dupe-class-members`](./docs/rules/no-dupe-class-members.md) | Disallow duplicate class members | | | | -| [`@typescript-eslint/no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate imports | | | | -| [`@typescript-eslint/no-empty-function`](./docs/rules/no-empty-function.md) | Disallow empty functions | :white_check_mark: | | | -| [`@typescript-eslint/no-extra-parens`](./docs/rules/no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | | -| [`@typescript-eslint/no-extra-semi`](./docs/rules/no-extra-semi.md) | Disallow unnecessary semicolons | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-implied-eval`](./docs/rules/no-implied-eval.md) | Disallow the use of `eval()`-like methods | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-invalid-this`](./docs/rules/no-invalid-this.md) | Disallow `this` keywords outside of classes or class-like objects | | | | -| [`@typescript-eslint/no-loop-func`](./docs/rules/no-loop-func.md) | Disallow function declarations that contain unsafe references inside loop statements | | | | -| [`@typescript-eslint/no-loss-of-precision`](./docs/rules/no-loss-of-precision.md) | Disallow literal numbers that lose precision | | | | -| [`@typescript-eslint/no-magic-numbers`](./docs/rules/no-magic-numbers.md) | Disallow magic numbers | | | | -| [`@typescript-eslint/no-redeclare`](./docs/rules/no-redeclare.md) | Disallow variable redeclaration | | | | -| [`@typescript-eslint/no-shadow`](./docs/rules/no-shadow.md) | Disallow variable declarations from shadowing variables declared in the outer scope | | | | -| [`@typescript-eslint/no-throw-literal`](./docs/rules/no-throw-literal.md) | Disallow throwing literals as exceptions | | | :thought_balloon: | -| [`@typescript-eslint/no-unused-expressions`](./docs/rules/no-unused-expressions.md) | Disallow unused expressions | | | | -| [`@typescript-eslint/no-unused-vars`](./docs/rules/no-unused-vars.md) | Disallow unused variables | :white_check_mark: | | | -| [`@typescript-eslint/no-use-before-define`](./docs/rules/no-use-before-define.md) | Disallow the use of variables before they are defined | | | | -| [`@typescript-eslint/no-useless-constructor`](./docs/rules/no-useless-constructor.md) | Disallow unnecessary constructors | | | | -| [`@typescript-eslint/object-curly-spacing`](./docs/rules/object-curly-spacing.md) | Enforce consistent spacing inside braces | | :wrench: | | -| [`@typescript-eslint/quotes`](./docs/rules/quotes.md) | Enforce the consistent use of either backticks, double, or single quotes | | :wrench: | | -| [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/return-await`](./docs/rules/return-await.md) | Enforces consistent returning of awaited values | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/semi`](./docs/rules/semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | | -| [`@typescript-eslint/space-before-function-paren`](./docs/rules/space-before-function-paren.md) | Enforces consistent spacing before function parenthesis | | :wrench: | | -| [`@typescript-eslint/space-infix-ops`](./docs/rules/space-infix-ops.md) | This rule is aimed at ensuring there are spaces around infix operators. | | :wrench: | | +| Name | Description | :white_check_mark: | :wrench: | :thought_balloon: | +| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------ | -------- | ----------------- | +| [`@typescript-eslint/brace-style`](./docs/rules/brace-style.md) | Enforce consistent brace style for blocks | | :wrench: | | +| [`@typescript-eslint/comma-dangle`](./docs/rules/comma-dangle.md) | Require or disallow trailing comma | | :wrench: | | +| [`@typescript-eslint/comma-spacing`](./docs/rules/comma-spacing.md) | Enforces consistent spacing before and after commas | | :wrench: | | +| [`@typescript-eslint/default-param-last`](./docs/rules/default-param-last.md) | Enforce default parameters to be last | | | | +| [`@typescript-eslint/dot-notation`](./docs/rules/dot-notation.md) | enforce dot notation whenever possible | | :wrench: | :thought_balloon: | +| [`@typescript-eslint/func-call-spacing`](./docs/rules/func-call-spacing.md) | Require or disallow spacing between function identifiers and their invocations | | :wrench: | | +| [`@typescript-eslint/indent`](./docs/rules/indent.md) | Enforce consistent indentation | | :wrench: | | +| [`@typescript-eslint/init-declarations`](./docs/rules/init-declarations.md) | require or disallow initialization in variable declarations | | | | +| [`@typescript-eslint/keyword-spacing`](./docs/rules/keyword-spacing.md) | Enforce consistent spacing before and after keywords | | :wrench: | | +| [`@typescript-eslint/lines-between-class-members`](./docs/rules/lines-between-class-members.md) | Require or disallow an empty line between class members | | :wrench: | | +| [`@typescript-eslint/no-array-constructor`](./docs/rules/no-array-constructor.md) | Disallow generic `Array` constructors | :white_check_mark: | :wrench: | | +| [`@typescript-eslint/no-dupe-class-members`](./docs/rules/no-dupe-class-members.md) | Disallow duplicate class members | | | | +| [`@typescript-eslint/no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate imports | | | | +| [`@typescript-eslint/no-empty-function`](./docs/rules/no-empty-function.md) | Disallow empty functions | :white_check_mark: | | | +| [`@typescript-eslint/no-extra-parens`](./docs/rules/no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | | +| [`@typescript-eslint/no-extra-semi`](./docs/rules/no-extra-semi.md) | Disallow unnecessary semicolons | :white_check_mark: | :wrench: | | +| [`@typescript-eslint/no-implied-eval`](./docs/rules/no-implied-eval.md) | Disallow the use of `eval()`-like methods | :white_check_mark: | | :thought_balloon: | +| [`@typescript-eslint/no-invalid-this`](./docs/rules/no-invalid-this.md) | Disallow `this` keywords outside of classes or class-like objects | | | | +| [`@typescript-eslint/no-loop-func`](./docs/rules/no-loop-func.md) | Disallow function declarations that contain unsafe references inside loop statements | | | | +| [`@typescript-eslint/no-loss-of-precision`](./docs/rules/no-loss-of-precision.md) | Disallow literal numbers that lose precision | | | | +| [`@typescript-eslint/no-magic-numbers`](./docs/rules/no-magic-numbers.md) | Disallow magic numbers | | | | +| [`@typescript-eslint/no-redeclare`](./docs/rules/no-redeclare.md) | Disallow variable redeclaration | | | | +| [`@typescript-eslint/no-shadow`](./docs/rules/no-shadow.md) | Disallow variable declarations from shadowing variables declared in the outer scope | | | | +| [`@typescript-eslint/no-throw-literal`](./docs/rules/no-throw-literal.md) | Disallow throwing literals as exceptions | | | :thought_balloon: | +| [`@typescript-eslint/no-unused-expressions`](./docs/rules/no-unused-expressions.md) | Disallow unused expressions | | | | +| [`@typescript-eslint/no-unused-vars`](./docs/rules/no-unused-vars.md) | Disallow unused variables | :white_check_mark: | | | +| [`@typescript-eslint/no-use-before-define`](./docs/rules/no-use-before-define.md) | Disallow the use of variables before they are defined | | | | +| [`@typescript-eslint/no-useless-constructor`](./docs/rules/no-useless-constructor.md) | Disallow unnecessary constructors | | | | +| [`@typescript-eslint/object-curly-spacing`](./docs/rules/object-curly-spacing.md) | Enforce consistent spacing inside braces | | :wrench: | | +| [`@typescript-eslint/padding-line-between-statements`](./docs/rules/padding-line-between-statements.md) | require or disallow padding lines between statements | | :wrench: | | +| [`@typescript-eslint/quotes`](./docs/rules/quotes.md) | Enforce the consistent use of either backticks, double, or single quotes | | :wrench: | | +| [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :white_check_mark: | | :thought_balloon: | +| [`@typescript-eslint/return-await`](./docs/rules/return-await.md) | Enforces consistent returning of awaited values | | :wrench: | :thought_balloon: | +| [`@typescript-eslint/semi`](./docs/rules/semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | | +| [`@typescript-eslint/space-before-function-paren`](./docs/rules/space-before-function-paren.md) | Enforces consistent spacing before function parenthesis | | :wrench: | | +| [`@typescript-eslint/space-infix-ops`](./docs/rules/space-infix-ops.md) | This rule is aimed at ensuring there are spaces around infix operators. | | :wrench: | | diff --git a/packages/eslint-plugin/docs/rules/no-meaningless-void-operator.md b/packages/eslint-plugin/docs/rules/no-meaningless-void-operator.md new file mode 100644 index 000000000000..4aa69e5714b5 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-meaningless-void-operator.md @@ -0,0 +1,50 @@ +# Disallow the `void` operator except when used to discard a value (`no-meaningless-void-operator`) + +Disallow the `void` operator when its argument is already of type `void` or `undefined`. + +## Rule Details + +The `void` operator is a useful tool to convey the programmer's intent to discard a value. For example, it is recommended as one way of suppressing [`@typescript-eslint/no-floating-promises`](./no-floating-promises.md) instead of adding `.catch()` to a promise. + +This rule helps an author catch API changes where previously a value was being discarded at a call site, but the callee changed so it no longer returns a value. When combined with [no-unused-expressions](https://eslint.org/docs/rules/no-unused-expressions), it also helps _readers_ of the code by ensuring consistency: a statement that looks like `void foo();` is **always** discarding a return value, and a statement that looks like `foo();` is **never** discarding a return value. + +Examples of **incorrect** code for this rule: + +```ts +void (() => {})(); + +function foo() {} +void foo(); +``` + +Examples of **correct** code for this rule: + +```ts +(() => {})(); + +function foo() {} +foo(); // nothing to discard + +function bar(x: number) { + void x; // discarding a number + return 2; +} +void bar(); // discarding a number +``` + +### Options + +This rule accepts a single object option with the following default configuration: + +```json +{ + "@typescript-eslint/no-meaningless-void-operator": [ + "error", + { + "checkNever": false + } + ] +} +``` + +- `checkNever: true` will suggest removing `void` when the argument has type `never`. diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md b/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md index 4464b6483ef1..2851791f0b05 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md @@ -70,4 +70,4 @@ If you don't care about having no-op type assertions in your code, then you can ## Related to -- TSLint: ['no-unnecessary-type-assertion`](https://palantir.github.io/tslint/rules/no-unnecessary-type-assertion/) +- TSLint: [`no-unnecessary-type-assertion`](https://palantir.github.io/tslint/rules/no-unnecessary-type-assertion/) diff --git a/packages/eslint-plugin/docs/rules/padding-line-between-statements.md b/packages/eslint-plugin/docs/rules/padding-line-between-statements.md new file mode 100644 index 000000000000..b23ce702b788 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/padding-line-between-statements.md @@ -0,0 +1,48 @@ +# require or disallow padding lines between statements (`padding-line-between-statements`) + +## Rule Details + +This rule extends the base [`eslint/padding-line-between-statements`](https://eslint.org/docs/rules/padding-line-between-statements) rule. + +**It adds support for TypeScript constructs such as `interface` and `type`.** + +## How to use + +```jsonc +{ + // note you must disable the base rule as it can report incorrect errors + "padding-line-between-statements": "off", + "@typescript-eslint/padding-line-between-statements": [ + "error", + { + "blankLine": "always", + "prev": "var", + "next": "return" + } + ] +} +``` + +```jsonc +{ + // Example - Add blank lines before interface and type definitions. + // note you must disable the base rule as it can report incorrect errors + "padding-line-between-statements": "off", + "@typescript-eslint/padding-line-between-statements": [ + "error", + { + "blankLine": "always", + "prev": "*", + "next": ["interface", "type"] + } + ] +} +``` + +## Options + +See [`eslint/padding-line-between-statements` options](https://eslint.org/docs/rules/padding-line-between-statements#options). + +**Note** - In addition to options provided by ESLint, we have also added options for `interface` and `type`. + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/padding-line-between-statements.md) diff --git a/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md b/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md index 34544456f09e..3b6f1346d7c9 100644 --- a/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md +++ b/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md @@ -130,6 +130,7 @@ interface Options { const defaultOptions: Options = { checkParameterProperties: true, ignoreInferredTypes: false, + treatMethodsAsReadonly: false, }; ``` @@ -214,3 +215,43 @@ export const acceptsCallback: AcceptsCallback; ``` + +### `treatMethodsAsReadonly` + +This option allows you to treat all mutable methods as though they were readonly. This may be desirable in when you are never reassigning methods. + +Examples of **incorrect** code for this rule with `{treatMethodsAsReadonly: false}`: + +```ts +type MyType = { + readonly prop: string; + method(): string; // note: this method is mutable +}; +function foo(arg: MyType) {} +``` + +Examples of **correct** code for this rule with `{treatMethodsAsReadonly: false}`: + +```ts +type MyType = Readonly<{ + prop: string; + method(): string; +}>; +function foo(arg: MyType) {} + +type MyOtherType = { + readonly prop: string; + readonly method: () => string; +}; +function bar(arg: MyOtherType) {} +``` + +Examples of **correct** code for this rule with `{treatMethodsAsReadonly: true}`: + +```ts +type MyType = { + readonly prop: string; + method(): string; // note: this method is mutable +}; +function foo(arg: MyType) {} +``` diff --git a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md index 0ee7427ddac1..e567b6571b66 100644 --- a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md +++ b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md @@ -35,6 +35,8 @@ type Options = { allowAny?: boolean; // if true, also allow null and undefined in template expressions allowNullish?: boolean; + // if true, also allow RegExp in template expressions + allowRegExp?: boolean; }; const defaults = { @@ -42,6 +44,7 @@ const defaults = { allowBoolean: false, allowAny: false, allowNullish: false, + allowRegExp: false, }; ``` @@ -83,3 +86,17 @@ Examples of additional **correct** code for this rule with `{ allowNullish: true const arg = condition ? 'ok' : null; const msg1 = `arg = ${arg}`; ``` + +### `allowRegExp` + +Examples of additional **correct** code for this rule with `{ allowRegExp: true }`: + +```ts +const arg = new RegExp('foo'); +const msg1 = `arg = ${arg}`; +``` + +```ts +const arg = /foo/; +const msg1 = `arg = ${arg}`; +``` diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 9ac333083e52..c5c4b6a76808 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "4.30.0", + "version": "4.31.0", "description": "TypeScript plugin for ESLint", "keywords": [ "eslint", @@ -44,8 +44,8 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "4.30.0", - "@typescript-eslint/scope-manager": "4.30.0", + "@typescript-eslint/experimental-utils": "4.31.0", + "@typescript-eslint/scope-manager": "4.31.0", "debug": "^4.3.1", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.1.0", diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index 66209b806233..9d7fde81314f 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -78,6 +78,7 @@ export = { '@typescript-eslint/no-loss-of-precision': 'error', 'no-magic-numbers': 'off', '@typescript-eslint/no-magic-numbers': 'error', + '@typescript-eslint/no-meaningless-void-operator': 'error', '@typescript-eslint/no-misused-new': 'error', '@typescript-eslint/no-misused-promises': 'error', '@typescript-eslint/no-namespace': 'error', @@ -116,6 +117,8 @@ export = { '@typescript-eslint/non-nullable-type-assertion-style': 'error', 'object-curly-spacing': 'off', '@typescript-eslint/object-curly-spacing': 'error', + 'padding-line-between-statements': 'off', + '@typescript-eslint/padding-line-between-statements': 'error', '@typescript-eslint/prefer-as-const': 'error', '@typescript-eslint/prefer-enum-initializers': 'error', '@typescript-eslint/prefer-for-of': 'error', diff --git a/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts b/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts index a30102dd123e..fcce191b2b53 100644 --- a/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts +++ b/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts @@ -19,7 +19,7 @@ export default createRule({ recommended: false, }, messages: { - preferRecord: 'A record is preferred over an index signature', + preferRecord: 'A record is preferred over an index signature.', preferIndexSignature: 'An index signature is preferred over a record.', }, fixable: 'code', diff --git a/packages/eslint-plugin/src/rules/consistent-type-imports.ts b/packages/eslint-plugin/src/rules/consistent-type-imports.ts index 795014507d06..34f264ff0c7d 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-imports.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-imports.ts @@ -61,13 +61,14 @@ export default util.createRule({ }, messages: { typeOverValue: - 'All imports in the declaration are only used as types. Use `import type`', - someImportsAreOnlyTypes: 'Imports {{typeImports}} are only used as types', - aImportIsOnlyTypes: 'Import {{typeImports}} is only used as types', + 'All imports in the declaration are only used as types. Use `import type`.', + someImportsAreOnlyTypes: + 'Imports {{typeImports}} are only used as types.', + aImportIsOnlyTypes: 'Import {{typeImports}} is only used as types.', someImportsInDecoMeta: - 'Type imports {{typeImports}} are used by decorator metadata', + 'Type imports {{typeImports}} are used by decorator metadata.', aImportInDecoMeta: - 'Type import {{typeImports}} is used by decorator metadata', + 'Type import {{typeImports}} is used by decorator metadata.', valueOverType: 'Use an `import` instead of an `import type`.', noImportTypeAnnotations: '`import()` type annotations are forbidden.', }, diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 1b6fa0f6ea12..1f6aa56c7cba 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -50,6 +50,7 @@ import noInvalidVoidType from './no-invalid-void-type'; import noLoopFunc from './no-loop-func'; import noLossOfPrecision from './no-loss-of-precision'; import noMagicNumbers from './no-magic-numbers'; +import noMeaninglessVoidOperator from './no-meaningless-void-operator'; import noMisusedNew from './no-misused-new'; import noMisusedPromises from './no-misused-promises'; import noNamespace from './no-namespace'; @@ -81,6 +82,7 @@ import noUselessConstructor from './no-useless-constructor'; import noVarRequires from './no-var-requires'; import nonNullableTypeAssertionStyle from './non-nullable-type-assertion-style'; import objectCurlySpacing from './object-curly-spacing'; +import paddingLineBetweenStatements from './padding-line-between-statements'; import preferAsConst from './prefer-as-const'; import preferEnumInitializers from './prefer-enum-initializers'; import preferForOf from './prefer-for-of'; @@ -169,6 +171,7 @@ export default { 'no-loop-func': noLoopFunc, 'no-loss-of-precision': noLossOfPrecision, 'no-magic-numbers': noMagicNumbers, + 'no-meaningless-void-operator': noMeaninglessVoidOperator, 'no-misused-new': noMisusedNew, 'no-misused-promises': noMisusedPromises, 'no-namespace': noNamespace, @@ -200,6 +203,7 @@ export default { 'no-var-requires': noVarRequires, 'non-nullable-type-assertion-style': nonNullableTypeAssertionStyle, 'object-curly-spacing': objectCurlySpacing, + 'padding-line-between-statements': paddingLineBetweenStatements, 'prefer-as-const': preferAsConst, 'prefer-enum-initializers': preferEnumInitializers, 'prefer-for-of': preferForOf, diff --git a/packages/eslint-plugin/src/rules/no-confusing-non-null-assertion.ts b/packages/eslint-plugin/src/rules/no-confusing-non-null-assertion.ts index bcf561b58562..446bdd25932b 100644 --- a/packages/eslint-plugin/src/rules/no-confusing-non-null-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-confusing-non-null-assertion.ts @@ -19,14 +19,14 @@ export default util.createRule({ fixable: 'code', messages: { confusingEqual: - 'Confusing combinations of non-null assertion and equal test like "a! == b", which looks very similar to not equal "a !== b"', + 'Confusing combinations of non-null assertion and equal test like "a! == b", which looks very similar to not equal "a !== b".', confusingAssign: - 'Confusing combinations of non-null assertion and equal test like "a! = b", which looks very similar to not equal "a != b"', - notNeedInEqualTest: 'Unnecessary non-null assertion (!) in equal test', + 'Confusing combinations of non-null assertion and equal test like "a! = b", which looks very similar to not equal "a != b".', + notNeedInEqualTest: 'Unnecessary non-null assertion (!) in equal test.', notNeedInAssign: - 'Unnecessary non-null assertion (!) in assignment left hand', + 'Unnecessary non-null assertion (!) in assignment left hand.', wrapUpLeft: - 'Wrap up left hand to avoid putting non-null assertion "!" and "=" together', + 'Wrap up left hand to avoid putting non-null assertion "!" and "=" together.', }, schema: [], }, diff --git a/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts b/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts index a9b715350e99..fd4e97e7360a 100644 --- a/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts +++ b/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts @@ -57,7 +57,7 @@ export default util.createRule({ invalidVoidExprReturnWrapVoid: 'Void expressions returned from a function ' + 'must be marked explicitly with the `void` operator.', - voidExprWrapVoid: 'Mark with an explicit `void` operator', + voidExprWrapVoid: 'Mark with an explicit `void` operator.', }, schema: [ { diff --git a/packages/eslint-plugin/src/rules/no-duplicate-imports.ts b/packages/eslint-plugin/src/rules/no-duplicate-imports.ts index a6f368902e38..f58ef76e5fcf 100644 --- a/packages/eslint-plugin/src/rules/no-duplicate-imports.ts +++ b/packages/eslint-plugin/src/rules/no-duplicate-imports.ts @@ -21,10 +21,10 @@ export default util.createRule({ schema: baseRule.meta.schema, messages: { ...baseRule.meta.messages, - importType: '{{module}} type import is duplicated', - importTypeAs: '{{module}} type import is duplicated as type export', - exportType: '{{module}} type export is duplicated', - exportTypeAs: '{{module}} type export is duplicated as type import', + importType: '{{module}} type import is duplicated.', + importTypeAs: '{{module}} type import is duplicated as type export.', + exportType: '{{module}} type export is duplicated.', + exportTypeAs: '{{module}} type export is duplicated as type import.', }, }, defaultOptions: [ diff --git a/packages/eslint-plugin/src/rules/no-explicit-any.ts b/packages/eslint-plugin/src/rules/no-explicit-any.ts index 4e6b8c34fc1d..9df51ba3d520 100644 --- a/packages/eslint-plugin/src/rules/no-explicit-any.ts +++ b/packages/eslint-plugin/src/rules/no-explicit-any.ts @@ -1,9 +1,9 @@ import { - TSESTree, AST_NODE_TYPES, + TSESLint, + TSESTree, } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; -import { TSESLint } from '@typescript-eslint/experimental-utils'; export type Options = [ { @@ -199,8 +199,8 @@ export default util.createRule({ }; if (fixToUnknown) { - fixOrSuggest.fix = (fixer => - fixer.replaceText(node, 'unknown')) as TSESLint.ReportFixFunction; + fixOrSuggest.fix = (fixer): TSESLint.RuleFix => + fixer.replaceText(node, 'unknown'); } context.report({ diff --git a/packages/eslint-plugin/src/rules/no-implicit-any-catch.ts b/packages/eslint-plugin/src/rules/no-implicit-any-catch.ts index aed526f8ab01..fbbdf59eb31a 100644 --- a/packages/eslint-plugin/src/rules/no-implicit-any-catch.ts +++ b/packages/eslint-plugin/src/rules/no-implicit-any-catch.ts @@ -26,8 +26,8 @@ export default util.createRule({ }, fixable: 'code', messages: { - implicitAnyInCatch: 'Implicit any in catch clause', - explicitAnyInCatch: 'Explicit any in catch clause', + implicitAnyInCatch: 'Implicit any in catch clause.', + explicitAnyInCatch: 'Explicit any in catch clause.', suggestExplicitUnknown: 'Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct.', }, diff --git a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts index 0dff489e585f..62c1aa679cf1 100644 --- a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts +++ b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts @@ -28,14 +28,14 @@ export default util.createRule<[Options], MessageIds>({ }, messages: { invalidVoidForGeneric: - '{{ generic }} may not have void as a type variable', + '{{ generic }} may not have void as a type variable.', invalidVoidNotReturnOrGeneric: - 'void is only valid as a return type or generic type variable', - invalidVoidNotReturn: 'void is only valid as a return type', + 'void is only valid as a return type or generic type variable.', + invalidVoidNotReturn: 'void is only valid as a return type.', invalidVoidNotReturnOrThisParam: - 'void is only valid as return type or type of `this` parameter', + 'void is only valid as return type or type of `this` parameter.', invalidVoidNotReturnOrThisParamOrGeneric: - 'void is only valid as a return type or generic type variable or the type of a `this` parameter', + 'void is only valid as a return type or generic type variable or the type of a `this` parameter.', }, schema: [ { diff --git a/packages/eslint-plugin/src/rules/no-meaningless-void-operator.ts b/packages/eslint-plugin/src/rules/no-meaningless-void-operator.ts new file mode 100644 index 000000000000..3640cb34630b --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-meaningless-void-operator.ts @@ -0,0 +1,100 @@ +import { + ESLintUtils, + TSESLint, + TSESTree, +} from '@typescript-eslint/experimental-utils'; +import * as tsutils from 'tsutils'; +import * as util from '../util'; +import * as ts from 'typescript'; + +type Options = [ + { + checkNever: boolean; + }, +]; + +export default util.createRule< + Options, + 'meaninglessVoidOperator' | 'removeVoid' +>({ + name: 'no-meaningless-void-operator', + meta: { + type: 'suggestion', + docs: { + description: + 'Disallow the `void` operator except when used to discard a value', + category: 'Best Practices', + recommended: false, + suggestion: true, + requiresTypeChecking: true, + }, + fixable: 'code', + messages: { + meaninglessVoidOperator: + "void operator shouldn't be used on {{type}}; it should convey that a return value is being ignored", + removeVoid: "Remove 'void'", + }, + schema: [ + { + type: 'object', + properties: { + checkNever: { + type: 'boolean', + default: false, + }, + }, + additionalProperties: false, + }, + ], + }, + defaultOptions: [{ checkNever: false }], + + create(context, [{ checkNever }]) { + const parserServices = ESLintUtils.getParserServices(context); + const checker = parserServices.program.getTypeChecker(); + const sourceCode = context.getSourceCode(); + + return { + 'UnaryExpression[operator="void"]'(node: TSESTree.UnaryExpression): void { + const fix = (fixer: TSESLint.RuleFixer): TSESLint.RuleFix => { + return fixer.removeRange([ + sourceCode.getTokens(node)[0].range[0], + sourceCode.getTokens(node)[1].range[0], + ]); + }; + + const argTsNode = parserServices.esTreeNodeToTSNodeMap.get( + node.argument, + ); + const argType = checker.getTypeAtLocation(argTsNode); + const unionParts = tsutils.unionTypeParts(argType); + if ( + unionParts.every( + part => part.flags & (ts.TypeFlags.Void | ts.TypeFlags.Undefined), + ) + ) { + context.report({ + node, + messageId: 'meaninglessVoidOperator', + data: { type: checker.typeToString(argType) }, + fix, + }); + } else if ( + checkNever && + unionParts.every( + part => + part.flags & + (ts.TypeFlags.Void | ts.TypeFlags.Undefined | ts.TypeFlags.Never), + ) + ) { + context.report({ + node, + messageId: 'meaninglessVoidOperator', + data: { type: checker.typeToString(argType) }, + suggest: [{ messageId: 'removeVoid', fix }], + }); + } + }, + }; + }, +}); diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 9079769c216e..5ae3f93408a5 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -121,11 +121,11 @@ export default createRule({ alwaysNullish: 'Unnecessary conditional, left-hand side of `??` operator is always `null` or `undefined`.', literalBooleanExpression: - 'Unnecessary conditional, both sides of the expression are literal values', + 'Unnecessary conditional, both sides of the expression are literal values.', noOverlapBooleanExpression: - 'Unnecessary conditional, the types have no overlap', - never: 'Unnecessary conditional, value is `never`', - neverOptionalChain: 'Unnecessary optional chain on a non-nullish value', + 'Unnecessary conditional, the types have no overlap.', + never: 'Unnecessary conditional, value is `never`.', + neverOptionalChain: 'Unnecessary optional chain on a non-nullish value.', noStrictNullCheck: 'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.', }, diff --git a/packages/eslint-plugin/src/rules/padding-line-between-statements.ts b/packages/eslint-plugin/src/rules/padding-line-between-statements.ts new file mode 100644 index 000000000000..f686476e5dee --- /dev/null +++ b/packages/eslint-plugin/src/rules/padding-line-between-statements.ts @@ -0,0 +1,775 @@ +import { + AST_NODE_TYPES, + TSESLint, + TSESTree, +} from '@typescript-eslint/experimental-utils'; +import * as util from '../util'; + +/** + * This rule is a replica of padding-line-between-statements. + * + * Ideally we would want to extend the rule support typescript specific support. + * But since not all the state is exposed by the eslint and eslint has frozen stylistic rules, + * (see - https://eslint.org/blog/2020/05/changes-to-rules-policies for details.) + * we are forced to re-implement the rule here. + * + * We have tried to keep the implementation as close as possible to the eslint implementation, to make + * patching easier for future contributors. + * + * Reference rule - https://github.com/eslint/eslint/blob/master/lib/rules/padding-line-between-statements.js + */ + +type NodeTest = ( + node: TSESTree.Node, + sourceCode: TSESLint.SourceCode, +) => boolean; + +interface NodeTestObject { + test: NodeTest; +} + +interface PaddingOption { + blankLine: keyof typeof PaddingTypes; + prev: string | string[]; + next: string | string[]; +} + +type MessageIds = 'expectedBlankLine' | 'unexpectedBlankLine'; +type Options = PaddingOption[]; + +const LT = `[${Array.from( + new Set(['\r\n', '\r', '\n', '\u2028', '\u2029']), +).join('')}]`; +const PADDING_LINE_SEQUENCE = new RegExp( + String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$`, + 'u', +); + +/** + * Creates tester which check if a node starts with specific keyword. + * @param keyword The keyword to test. + * @returns the created tester. + * @private + */ +function newKeywordTester(keyword: string): NodeTestObject { + return { + test(node, sourceCode): boolean { + return sourceCode.getFirstToken(node)?.value === keyword; + }, + }; +} + +/** + * Creates tester which check if a node starts with specific keyword and spans a single line. + * @param keyword The keyword to test. + * @returns the created tester. + * @private + */ +function newSinglelineKeywordTester(keyword: string): NodeTestObject { + return { + test(node, sourceCode): boolean { + return ( + node.loc.start.line === node.loc.end.line && + sourceCode.getFirstToken(node)!.value === keyword + ); + }, + }; +} + +/** + * Creates tester which check if a node starts with specific keyword and spans multiple lines. + * @param keyword The keyword to test. + * @returns the created tester. + * @private + */ +function newMultilineKeywordTester(keyword: string): NodeTestObject { + return { + test(node, sourceCode): boolean { + return ( + node.loc.start.line !== node.loc.end.line && + sourceCode.getFirstToken(node)!.value === keyword + ); + }, + }; +} + +/** + * Creates tester which check if a node is specific type. + * @param type The node type to test. + * @returns the created tester. + * @private + */ +function newNodeTypeTester(type: AST_NODE_TYPES): NodeTestObject { + return { + test: (node): boolean => node.type === type, + }; +} + +/** + * Skips a chain expression node + * @paramnode The node to test + * @returnsA non-chain expression + * @private + */ +function skipChainExpression(node: TSESTree.Node): TSESTree.Node { + return node && node.type === AST_NODE_TYPES.ChainExpression + ? node.expression + : node; +} + +/** + * Checks the given node is an expression statement of IIFE. + * @paramnode The node to check. + * @returns `true` if the node is an expression statement of IIFE. + * @private + */ +function isIIFEStatement(node: TSESTree.Node): boolean { + if (node.type === AST_NODE_TYPES.ExpressionStatement) { + let expression = skipChainExpression(node.expression); + if (expression.type === AST_NODE_TYPES.UnaryExpression) { + expression = skipChainExpression(expression.argument); + } + if (expression.type === AST_NODE_TYPES.CallExpression) { + let node: TSESTree.Node = expression.callee; + while (node.type === AST_NODE_TYPES.SequenceExpression) { + node = node.expressions[node.expressions.length - 1]; + } + return util.isFunction(node); + } + } + return false; +} + +/** + * Checks the given node is a CommonJS require statement + * @paramnode The node to check. + * @returns `true` if the node is a CommonJS require statement. + * @private + */ +function isCJSRequire(node: TSESTree.Node): boolean { + if (node.type === AST_NODE_TYPES.VariableDeclaration) { + const declaration = node.declarations[0]; + if (declaration?.init) { + let call = declaration?.init; + while (call.type === AST_NODE_TYPES.MemberExpression) { + call = call.object; + } + if ( + call.type === AST_NODE_TYPES.CallExpression && + call.callee.type === AST_NODE_TYPES.Identifier + ) { + return call.callee.name === 'require'; + } + } + } + return false; +} + +/** + * Checks whether the given node is a block-like statement. + * This checks the last token of the node is the closing brace of a block. + * @param sourceCode The source code to get tokens. + * @paramnode The node to check. + * @returns `true` if the node is a block-like statement. + * @private + */ +function isBlockLikeStatement( + node: TSESTree.Node, + sourceCode: TSESLint.SourceCode, +): boolean { + // do-while with a block is a block-like statement. + if ( + node.type === AST_NODE_TYPES.DoWhileStatement && + node.body.type === AST_NODE_TYPES.BlockStatement + ) { + return true; + } + + /** + * IIFE is a block-like statement specially from + * JSCS#disallowPaddingNewLinesAfterBlocks. + */ + if (isIIFEStatement(node)) { + return true; + } + + // Checks the last token is a closing brace of blocks. + const lastToken = sourceCode.getLastToken(node, util.isNotSemicolonToken); + const belongingNode = + lastToken && util.isClosingBraceToken(lastToken) + ? sourceCode.getNodeByRangeIndex(lastToken.range[0]) + : null; + + return ( + !!belongingNode && + (belongingNode.type === AST_NODE_TYPES.BlockStatement || + belongingNode.type === AST_NODE_TYPES.SwitchStatement) + ); +} + +/** + * Check whether the given node is a directive or not. + * @paramnode The node to check. + * @param sourceCode The source code object to get tokens. + * @returns `true` if the node is a directive. + */ +function isDirective( + node: TSESTree.Node, + sourceCode: TSESLint.SourceCode, +): boolean { + return ( + node.type === AST_NODE_TYPES.ExpressionStatement && + (node.parent?.type === AST_NODE_TYPES.Program || + (node.parent?.type === AST_NODE_TYPES.BlockStatement && + util.isFunction(node.parent.parent))) && + node.expression.type === AST_NODE_TYPES.Literal && + typeof node.expression.value === 'string' && + !util.isParenthesized(node.expression, sourceCode) + ); +} + +/** + * Check whether the given node is a part of directive prologue or not. + * @paramnode The node to check. + * @param sourceCode The source code object to get tokens. + * @returns `true` if the node is a part of directive prologue. + */ +function isDirectivePrologue( + node: TSESTree.Node, + sourceCode: TSESLint.SourceCode, +): boolean { + if ( + isDirective(node, sourceCode) && + node.parent && + 'body' in node.parent && + Array.isArray(node.parent.body) + ) { + for (const sibling of node.parent.body) { + if (sibling === node) { + break; + } + if (!isDirective(sibling, sourceCode)) { + return false; + } + } + return true; + } + return false; +} + +/** + * Checks the given node is a CommonJS export statement + * @paramnode The node to check. + * @returns `true` if the node is a CommonJS export statement. + * @private + */ +function isCJSExport(node: TSESTree.Node): boolean { + if (node.type === AST_NODE_TYPES.ExpressionStatement) { + const expression = node.expression; + if (expression.type === AST_NODE_TYPES.AssignmentExpression) { + let left = expression.left; + if (left.type === AST_NODE_TYPES.MemberExpression) { + while (left.object.type === AST_NODE_TYPES.MemberExpression) { + left = left.object; + } + return ( + left.object.type === AST_NODE_TYPES.Identifier && + (left.object.name === 'exports' || + (left.object.name === 'module' && + left.property.type === AST_NODE_TYPES.Identifier && + left.property.name === 'exports')) + ); + } + } + } + return false; +} + +/** + * Check whether the given node is an expression + * @paramnode The node to check. + * @param sourceCode The source code object to get tokens. + * @returns `true` if the node is an expression + */ +function isExpression( + node: TSESTree.Node, + sourceCode: TSESLint.SourceCode, +): boolean { + return ( + node.type === AST_NODE_TYPES.ExpressionStatement && + !isDirectivePrologue(node, sourceCode) + ); +} + +/** + * Gets the actual last token. + * + * If a semicolon is semicolon-less style's semicolon, this ignores it. + * For example: + * + * foo() + * ;[1, 2, 3].forEach(bar) + * @param sourceCode The source code to get tokens. + * @paramnode The node to get. + * @returns The actual last token. + * @private + */ +function getActualLastToken( + node: TSESTree.Node, + sourceCode: TSESLint.SourceCode, +): TSESTree.Token | null { + const semiToken = sourceCode.getLastToken(node)!; + const prevToken = sourceCode.getTokenBefore(semiToken); + const nextToken = sourceCode.getTokenAfter(semiToken); + const isSemicolonLessStyle = + prevToken && + nextToken && + prevToken.range[0] >= node.range[0] && + util.isSemicolonToken(semiToken) && + semiToken.loc.start.line !== prevToken.loc.end.line && + semiToken.loc.end.line === nextToken.loc.start.line; + + return isSemicolonLessStyle ? prevToken : semiToken; +} + +/** + * This returns the concatenation of the first 2 captured strings. + * @param _ Unused. Whole matched string. + * @param trailingSpaces The trailing spaces of the first line. + * @param indentSpaces The indentation spaces of the last line. + * @returns The concatenation of trailingSpaces and indentSpaces. + * @private + */ +function replacerToRemovePaddingLines( + _: string, + trailingSpaces: string, + indentSpaces: string, +): string { + return trailingSpaces + indentSpaces; +} + +/** + * Check and report statements for `any` configuration. + * It does nothing. + * + * @private + */ +function verifyForAny(): void { + // Empty +} + +/** + * Check and report statements for `never` configuration. + * This autofix removes blank lines between the given 2 statements. + * However, if comments exist between 2 blank lines, it does not remove those + * blank lines automatically. + * @param context The rule context to report. + * @param_ Unused. The previous node to check. + * @paramnextNode The next node to check. + * @param paddingLines The array of token pairs that blank + * lines exist between the pair. + * + * @private + */ +function verifyForNever( + context: TSESLint.RuleContext, + _: TSESTree.Node, + nextNode: TSESTree.Node, + paddingLines: [TSESTree.Token, TSESTree.Token][], +): void { + if (paddingLines.length === 0) { + return; + } + + context.report({ + node: nextNode, + messageId: 'unexpectedBlankLine', + fix(fixer) { + if (paddingLines.length >= 2) { + return null; + } + + const prevToken = paddingLines[0][0]; + const nextToken = paddingLines[0][1]; + const start = prevToken.range[1]; + const end = nextToken.range[0]; + const text = context + .getSourceCode() + .text.slice(start, end) + .replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines); + + return fixer.replaceTextRange([start, end], text); + }, + }); +} + +/** + * Check and report statements for `always` configuration. + * This autofix inserts a blank line between the given 2 statements. + * If the `prevNode` has trailing comments, it inserts a blank line after the + * trailing comments. + * @param context The rule context to report. + * @paramprevNode The previous node to check. + * @paramnextNode The next node to check. + * @param paddingLines The array of token pairs that blank + * lines exist between the pair. + * + * @private + */ +function verifyForAlways( + context: TSESLint.RuleContext, + prevNode: TSESTree.Node, + nextNode: TSESTree.Node, + paddingLines: [TSESTree.Token, TSESTree.Token][], +): void { + if (paddingLines.length > 0) { + return; + } + + context.report({ + node: nextNode, + messageId: 'expectedBlankLine', + fix(fixer) { + const sourceCode = context.getSourceCode(); + let prevToken = getActualLastToken( + prevNode, + sourceCode, + ) as TSESTree.Token; + const nextToken = + (sourceCode.getFirstTokenBetween(prevToken, nextNode, { + includeComments: true, + + /** + * Skip the trailing comments of the previous node. + * This inserts a blank line after the last trailing comment. + * + * For example: + * + * foo(); // trailing comment. + * // comment. + * bar(); + * + * Get fixed to: + * + * foo(); // trailing comment. + * + * // comment. + * bar(); + * @param token The token to check. + * @returns `true` if the token is not a trailing comment. + * @private + */ + filter(token) { + if (util.isTokenOnSameLine(prevToken, token)) { + prevToken = token; + return false; + } + return true; + }, + }) as TSESTree.Token) || nextNode; + const insertText = util.isTokenOnSameLine(prevToken, nextToken) + ? '\n\n' + : '\n'; + + return fixer.insertTextAfter(prevToken, insertText); + }, + }); +} + +/** + * Types of blank lines. + * `any`, `never`, and `always` are defined. + * Those have `verify` method to check and report statements. + * @private + */ +const PaddingTypes = { + any: { verify: verifyForAny }, + never: { verify: verifyForNever }, + always: { verify: verifyForAlways }, +}; + +/** + * Types of statements. + * Those have `test` method to check it matches to the given statement. + * @private + */ +const StatementTypes: Record = { + '*': { test: (): boolean => true }, + 'block-like': { test: isBlockLikeStatement }, + exports: { test: isCJSExport }, + require: { test: isCJSRequire }, + directive: { test: isDirectivePrologue }, + expression: { test: isExpression }, + iife: { test: isIIFEStatement }, + + 'multiline-block-like': { + test: (node, sourceCode) => + node.loc.start.line !== node.loc.end.line && + isBlockLikeStatement(node, sourceCode), + }, + 'multiline-expression': { + test: (node, sourceCode) => + node.loc.start.line !== node.loc.end.line && + node.type === AST_NODE_TYPES.ExpressionStatement && + !isDirectivePrologue(node, sourceCode), + }, + + 'multiline-const': newMultilineKeywordTester('const'), + 'multiline-let': newMultilineKeywordTester('let'), + 'multiline-var': newMultilineKeywordTester('var'), + 'singleline-const': newSinglelineKeywordTester('const'), + 'singleline-let': newSinglelineKeywordTester('let'), + 'singleline-var': newSinglelineKeywordTester('var'), + + block: newNodeTypeTester(AST_NODE_TYPES.BlockStatement), + empty: newNodeTypeTester(AST_NODE_TYPES.EmptyStatement), + function: newNodeTypeTester(AST_NODE_TYPES.FunctionDeclaration), + + break: newKeywordTester('break'), + case: newKeywordTester('case'), + class: newKeywordTester('class'), + const: newKeywordTester('const'), + continue: newKeywordTester('continue'), + debugger: newKeywordTester('debugger'), + default: newKeywordTester('default'), + do: newKeywordTester('do'), + export: newKeywordTester('export'), + for: newKeywordTester('for'), + if: newKeywordTester('if'), + import: newKeywordTester('import'), + let: newKeywordTester('let'), + return: newKeywordTester('return'), + switch: newKeywordTester('switch'), + throw: newKeywordTester('throw'), + try: newKeywordTester('try'), + var: newKeywordTester('var'), + while: newKeywordTester('while'), + with: newKeywordTester('with'), + + // Additional Typescript constructs + interface: newKeywordTester('interface'), + type: newKeywordTester('type'), +}; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +export default util.createRule({ + name: 'padding-line-between-statements', + meta: { + type: 'layout', + docs: { + description: 'require or disallow padding lines between statements', + category: 'Stylistic Issues', + recommended: false, + extendsBaseRule: true, + }, + fixable: 'whitespace', + schema: { + definitions: { + paddingType: { + enum: Object.keys(PaddingTypes), + }, + statementType: { + anyOf: [ + { enum: Object.keys(StatementTypes) }, + { + type: 'array', + items: { enum: Object.keys(StatementTypes) }, + minItems: 1, + uniqueItems: true, + additionalItems: false, + }, + ], + }, + }, + type: 'array', + items: { + type: 'object', + properties: { + blankLine: { $ref: '#/definitions/paddingType' }, + prev: { $ref: '#/definitions/statementType' }, + next: { $ref: '#/definitions/statementType' }, + }, + additionalProperties: false, + required: ['blankLine', 'prev', 'next'], + }, + additionalItems: false, + }, + messages: { + unexpectedBlankLine: 'Unexpected blank line before this statement.', + expectedBlankLine: 'Expected blank line before this statement.', + }, + }, + defaultOptions: [], + create(context) { + const sourceCode = context.getSourceCode(); + const configureList = context.options || []; + + type Scope = null | { + upper: Scope; + prevNode: TSESTree.Node | null; + }; + + let scopeInfo: Scope = null; + + /** + * Processes to enter to new scope. + * This manages the current previous statement. + * + * @private + */ + function enterScope(): void { + scopeInfo = { + upper: scopeInfo, + prevNode: null, + }; + } + + /** + * Processes to exit from the current scope. + * + * @private + */ + function exitScope(): void { + if (scopeInfo) { + scopeInfo = scopeInfo.upper; + } + } + + /** + * Checks whether the given node matches the given type. + * @paramnode The statement node to check. + * @param type The statement type to check. + * @returns `true` if the statement node matched the type. + * @private + */ + function match(node: TSESTree.Node, type: string | string[]): boolean { + let innerStatementNode = node; + + while (innerStatementNode.type === AST_NODE_TYPES.LabeledStatement) { + innerStatementNode = innerStatementNode.body; + } + + if (Array.isArray(type)) { + return type.some(match.bind(null, innerStatementNode)); + } + + return StatementTypes[type].test(innerStatementNode, sourceCode); + } + + /** + * Finds the last matched configure from configureList. + * @paramprevNode The previous statement to match. + * @paramnextNode The current statement to match. + * @returns The tester of the last matched configure. + * @private + */ + function getPaddingType( + prevNode: TSESTree.Node, + nextNode: TSESTree.Node, + ): typeof PaddingTypes[keyof typeof PaddingTypes] { + for (let i = configureList.length - 1; i >= 0; --i) { + const configure = configureList[i]; + if ( + match(prevNode, configure.prev) && + match(nextNode, configure.next) + ) { + return PaddingTypes[configure.blankLine]; + } + } + return PaddingTypes.any; + } + + /** + * Gets padding line sequences between the given 2 statements. + * Comments are separators of the padding line sequences. + * @paramprevNode The previous statement to count. + * @paramnextNode The current statement to count. + * @returns The array of token pairs. + * @private + */ + function getPaddingLineSequences( + prevNode: TSESTree.Node, + nextNode: TSESTree.Node, + ): [TSESTree.Token, TSESTree.Token][] { + const pairs: [TSESTree.Token, TSESTree.Token][] = []; + let prevToken: TSESTree.Token = getActualLastToken(prevNode, sourceCode)!; + + if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) { + do { + const token: TSESTree.Token = sourceCode.getTokenAfter(prevToken, { + includeComments: true, + })!; + + if (token.loc.start.line - prevToken.loc.end.line >= 2) { + pairs.push([prevToken, token]); + } + prevToken = token; + } while (prevToken.range[0] < nextNode.range[0]); + } + + return pairs; + } + + /** + * Verify padding lines between the given node and the previous node. + * @paramnode The node to verify. + * + * @private + */ + function verify(node: TSESTree.Node): void { + if ( + !node.parent || + ![ + AST_NODE_TYPES.SwitchStatement, + AST_NODE_TYPES.BlockStatement, + AST_NODE_TYPES.Program, + AST_NODE_TYPES.SwitchCase, + ].includes(node.parent.type) + ) { + return; + } + + // Save this node as the current previous statement. + const prevNode = scopeInfo!.prevNode; + + // Verify. + if (prevNode) { + const type = getPaddingType(prevNode, node); + const paddingLines = getPaddingLineSequences(prevNode, node); + + type.verify(context, prevNode, node, paddingLines); + } + + scopeInfo!.prevNode = node; + } + + /** + * Verify padding lines between the given node and the previous node. + * Then process to enter to new scope. + * @paramnode The node to verify. + * + * @private + */ + function verifyThenEnterScope(node: TSESTree.Node): void { + verify(node); + enterScope(); + } + + return { + Program: enterScope, + BlockStatement: enterScope, + SwitchStatement: enterScope, + 'Program:exit': exitScope, + 'BlockStatement:exit': exitScope, + 'SwitchStatement:exit': exitScope, + + ':statement': verify, + + SwitchCase: verifyThenEnterScope, + 'SwitchCase:exit': exitScope, + }; + }, +}); diff --git a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts index 1a5c1248c89a..663caa5bedda 100644 --- a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts +++ b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts @@ -1,6 +1,5 @@ -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; -import { TSESLint } from '@typescript-eslint/experimental-utils'; type MessageIds = 'defineInitializer' | 'defineInitializerSuggestion'; @@ -16,7 +15,7 @@ export default util.createRule<[], MessageIds>({ }, messages: { defineInitializer: - "The value of the member '{{ name }}' should be explicitly defined", + "The value of the member '{{ name }}' should be explicitly defined.", defineInitializerSuggestion: 'Can be fixed to {{ name }} = {{ suggested }}', }, diff --git a/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts b/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts index a36020e93d3c..fdf48395eae5 100644 --- a/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts +++ b/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts @@ -8,7 +8,7 @@ type Options = [ { checkParameterProperties?: boolean; ignoreInferredTypes?: boolean; - }, + } & util.ReadonlynessOptions, ]; type MessageIds = 'shouldBeReadonly'; @@ -34,6 +34,7 @@ export default util.createRule({ ignoreInferredTypes: { type: 'boolean', }, + ...util.readonlynessOptionsSchema.properties, }, }, ], @@ -45,10 +46,13 @@ export default util.createRule({ { checkParameterProperties: true, ignoreInferredTypes: false, + ...util.readonlynessOptionsDefaults, }, ], create(context, options) { - const [{ checkParameterProperties, ignoreInferredTypes }] = options; + const [ + { checkParameterProperties, ignoreInferredTypes, treatMethodsAsReadonly }, + ] = options; const { esTreeNodeToTSNodeMap, program } = util.getParserServices(context); const checker = program.getTypeChecker(); @@ -94,7 +98,9 @@ export default util.createRule({ const tsNode = esTreeNodeToTSNodeMap.get(actualParam); const type = checker.getTypeAtLocation(tsNode); - const isReadOnly = util.isTypeReadonly(checker, type); + const isReadOnly = util.isTypeReadonly(checker, type, { + treatMethodsAsReadonly: treatMethodsAsReadonly!, + }); if (!isReadOnly) { context.report({ diff --git a/packages/eslint-plugin/src/rules/prefer-return-this-type.ts b/packages/eslint-plugin/src/rules/prefer-return-this-type.ts index 4287d6c5dfaa..5a41a29d0a04 100644 --- a/packages/eslint-plugin/src/rules/prefer-return-this-type.ts +++ b/packages/eslint-plugin/src/rules/prefer-return-this-type.ts @@ -27,7 +27,7 @@ export default createRule({ requiresTypeChecking: true, }, messages: { - useThisType: 'use `this` type instead.', + useThisType: 'Use `this` type instead.', }, schema: [], fixable: 'code', diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts index a4756266ffca..b20bd5c62bc2 100644 --- a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts +++ b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts @@ -11,6 +11,7 @@ type Options = [ allowBoolean?: boolean; allowAny?: boolean; allowNullish?: boolean; + allowRegExp?: boolean; }, ]; @@ -37,6 +38,7 @@ export default util.createRule({ allowBoolean: { type: 'boolean' }, allowAny: { type: 'boolean' }, allowNullish: { type: 'boolean' }, + allowRegExp: { type: 'boolean' }, }, }, ], @@ -76,6 +78,13 @@ export default util.createRule({ return true; } + if ( + options.allowRegExp && + util.getTypeName(typeChecker, type) === 'RegExp' + ) { + return true; + } + if ( options.allowNullish && util.isTypeFlagSet(type, ts.TypeFlags.Null | ts.TypeFlags.Undefined) diff --git a/packages/eslint-plugin/src/util/isTypeReadonly.ts b/packages/eslint-plugin/src/util/isTypeReadonly.ts index 4a42c90c1e05..8b3efc27856e 100644 --- a/packages/eslint-plugin/src/util/isTypeReadonly.ts +++ b/packages/eslint-plugin/src/util/isTypeReadonly.ts @@ -4,6 +4,7 @@ import { isUnionOrIntersectionType, unionTypeParts, isPropertyReadonlyInType, + isSymbolFlagSet, } from 'tsutils'; import * as ts from 'typescript'; import { getTypeOfPropertyOfType, nullThrows, NullThrowsReasons } from '.'; @@ -17,9 +18,32 @@ const enum Readonlyness { Readonly = 3, } +export interface ReadonlynessOptions { + readonly treatMethodsAsReadonly?: boolean; +} + +export const readonlynessOptionsSchema = { + type: 'object', + additionalProperties: false, + properties: { + treatMethodsAsReadonly: { + type: 'boolean', + }, + }, +}; + +export const readonlynessOptionsDefaults: ReadonlynessOptions = { + treatMethodsAsReadonly: false, +}; + +function hasSymbol(node: ts.Node): node is ts.Node & { symbol: ts.Symbol } { + return Object.prototype.hasOwnProperty.call(node, 'symbol'); +} + function isTypeReadonlyArrayOrTuple( checker: ts.TypeChecker, type: ts.Type, + options: ReadonlynessOptions, seenTypes: Set, ): Readonlyness { function checkTypeArguments(arrayType: ts.TypeReference): Readonlyness { @@ -40,7 +64,7 @@ function isTypeReadonlyArrayOrTuple( if ( typeArguments.some( typeArg => - isTypeReadonlyRecurser(checker, typeArg, seenTypes) === + isTypeReadonlyRecurser(checker, typeArg, options, seenTypes) === Readonlyness.Mutable, ) ) { @@ -76,6 +100,7 @@ function isTypeReadonlyArrayOrTuple( function isTypeReadonlyObject( checker: ts.TypeChecker, type: ts.Type, + options: ReadonlynessOptions, seenTypes: Set, ): Readonlyness { function checkIndexSignature(kind: ts.IndexKind): Readonlyness { @@ -93,7 +118,18 @@ function isTypeReadonlyObject( if (properties.length) { // ensure the properties are marked as readonly for (const property of properties) { - if (!isPropertyReadonlyInType(type, property.getEscapedName(), checker)) { + if ( + !( + isPropertyReadonlyInType(type, property.getEscapedName(), checker) || + (options.treatMethodsAsReadonly && + property.valueDeclaration !== undefined && + hasSymbol(property.valueDeclaration) && + isSymbolFlagSet( + property.valueDeclaration.symbol, + ts.SymbolFlags.Method, + )) + ) + ) { return Readonlyness.Mutable; } } @@ -117,7 +153,7 @@ function isTypeReadonlyObject( } if ( - isTypeReadonlyRecurser(checker, propertyType, seenTypes) === + isTypeReadonlyRecurser(checker, propertyType, options, seenTypes) === Readonlyness.Mutable ) { return Readonlyness.Mutable; @@ -142,6 +178,7 @@ function isTypeReadonlyObject( function isTypeReadonlyRecurser( checker: ts.TypeChecker, type: ts.Type, + options: ReadonlynessOptions, seenTypes: Set, ): Readonlyness.Readonly | Readonlyness.Mutable { seenTypes.add(type); @@ -149,7 +186,7 @@ function isTypeReadonlyRecurser( if (isUnionType(type)) { // all types in the union must be readonly const result = unionTypeParts(type).every(t => - isTypeReadonlyRecurser(checker, t, seenTypes), + isTypeReadonlyRecurser(checker, t, options, seenTypes), ); const readonlyness = result ? Readonlyness.Readonly : Readonlyness.Mutable; return readonlyness; @@ -169,12 +206,22 @@ function isTypeReadonlyRecurser( return Readonlyness.Readonly; } - const isReadonlyArray = isTypeReadonlyArrayOrTuple(checker, type, seenTypes); + const isReadonlyArray = isTypeReadonlyArrayOrTuple( + checker, + type, + options, + seenTypes, + ); if (isReadonlyArray !== Readonlyness.UnknownType) { return isReadonlyArray; } - const isReadonlyObject = isTypeReadonlyObject(checker, type, seenTypes); + const isReadonlyObject = isTypeReadonlyObject( + checker, + type, + options, + seenTypes, + ); /* istanbul ignore else */ if ( isReadonlyObject !== Readonlyness.UnknownType ) { @@ -187,9 +234,14 @@ function isTypeReadonlyRecurser( /** * Checks if the given type is readonly */ -function isTypeReadonly(checker: ts.TypeChecker, type: ts.Type): boolean { +function isTypeReadonly( + checker: ts.TypeChecker, + type: ts.Type, + options: ReadonlynessOptions, +): boolean { return ( - isTypeReadonlyRecurser(checker, type, new Set()) === Readonlyness.Readonly + isTypeReadonlyRecurser(checker, type, options, new Set()) === + Readonlyness.Readonly ); } diff --git a/packages/eslint-plugin/tests/rules/no-meaningless-void-operator.test.ts b/packages/eslint-plugin/tests/rules/no-meaningless-void-operator.test.ts new file mode 100644 index 000000000000..5e034f2904b0 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-meaningless-void-operator.test.ts @@ -0,0 +1,90 @@ +import rule from '../../src/rules/no-meaningless-void-operator'; +import { RuleTester, getFixturesRootDir } from '../RuleTester'; + +const rootDir = getFixturesRootDir(); + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2018, + tsconfigRootDir: rootDir, + project: './tsconfig.json', + }, + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('no-meaningless-void-operator', rule, { + valid: [ + ` +(() => {})(); + +function foo() {} +foo(); // nothing to discard + +function bar(x: number) { + void x; + return 2; +} +void bar(); // discarding a number + `, + ` +function bar(x: never) { + void x; +} + `, + ], + invalid: [ + { + code: 'void (() => {})();', + output: '(() => {})();', + errors: [ + { + messageId: 'meaninglessVoidOperator', + line: 1, + column: 1, + }, + ], + }, + { + code: ` +function foo() {} +void foo(); + `, + output: ` +function foo() {} +foo(); + `, + errors: [ + { + messageId: 'meaninglessVoidOperator', + line: 3, + column: 1, + }, + ], + }, + { + options: [{ checkNever: true }], + code: ` +function bar(x: never) { + void x; +} + `.trimRight(), + errors: [ + { + messageId: 'meaninglessVoidOperator', + line: 3, + column: 3, + suggestions: [ + { + messageId: 'removeVoid', + output: ` +function bar(x: never) { + x; +} + `.trimRight(), + }, + ], + }, + ], + }, + ], +}); diff --git a/packages/eslint-plugin/tests/rules/padding-line-between-statements.test.ts b/packages/eslint-plugin/tests/rules/padding-line-between-statements.test.ts new file mode 100644 index 000000000000..d018f189929f --- /dev/null +++ b/packages/eslint-plugin/tests/rules/padding-line-between-statements.test.ts @@ -0,0 +1,5060 @@ +/* eslint-disable eslint-comments/no-use */ +// this rule tests new lines which prettier tries to fix, breaking the tests +/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ +/* eslint-enable eslint-comments/no-use */ + +import rule from '../../src/rules/padding-line-between-statements'; +import { RuleTester } from '../RuleTester'; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('padding-line-between-statements', rule, { + valid: [ + // do nothing if no options. + "'use strict'; foo(); if (a) { bar(); }", + + // do nothing for single statement. + { + code: 'foo()', + options: [{ blankLine: 'never', prev: '*', next: '*' }], + }, + { + code: 'foo()', + options: [{ blankLine: 'always', prev: '*', next: '*' }], + }, + + //---------------------------------------------------------------------- + // wildcard + //---------------------------------------------------------------------- + + { + code: 'foo();bar();', + options: [{ blankLine: 'never', prev: '*', next: '*' }], + }, + { + code: 'foo();\nbar();', + options: [{ blankLine: 'never', prev: '*', next: '*' }], + }, + { + code: 'foo();\n//comment\nbar();', + options: [{ blankLine: 'never', prev: '*', next: '*' }], + }, + { + code: 'foo();\n/*comment*/\nbar();', + options: [{ blankLine: 'never', prev: '*', next: '*' }], + }, + { + code: 'foo();\n\nbar();', + options: [{ blankLine: 'always', prev: '*', next: '*' }], + }, + { + code: 'foo();\n\n//comment\nbar();', + options: [{ blankLine: 'always', prev: '*', next: '*' }], + }, + { + code: 'foo();\n//comment\n\nbar();', + options: [{ blankLine: 'always', prev: '*', next: '*' }], + }, + { + code: 'foo();\n//comment\n\n//comment\nbar();', + options: [{ blankLine: 'always', prev: '*', next: '*' }], + }, + { + code: 'if(a){}\n\n;[].map(b)', + options: [ + { blankLine: 'always', prev: 'if', next: '*' }, + { blankLine: 'never', prev: 'empty', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // block-like + //---------------------------------------------------------------------- + + { + code: 'foo();\n\n{ foo() }\n\nfoo();', + options: [ + { blankLine: 'always', prev: '*', next: '*' }, + { blankLine: 'never', prev: 'block-like', next: 'block-like' }, + ], + }, + { + code: '{ foo() } { foo() }', + options: [ + { blankLine: 'always', prev: '*', next: '*' }, + { blankLine: 'never', prev: 'block-like', next: 'block-like' }, + ], + }, + { + code: '{ foo() }\n{ foo() }', + options: [ + { blankLine: 'always', prev: '*', next: '*' }, + { blankLine: 'never', prev: 'block-like', next: 'block-like' }, + ], + }, + { + code: '{ foo() }\n\n{ foo() }', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block-like', next: 'block-like' }, + ], + }, + { + code: '{ foo() }\n\n//comment\n{ foo() }', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block-like', next: 'block-like' }, + ], + }, + { + code: 'if(a);\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block-like', next: '*' }, + ], + }, + { + code: 'do;while(a);\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block-like', next: '*' }, + ], + }, + { + code: 'do{}while(a);\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block-like', next: '*' }, + ], + }, + { + code: 'a={}\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block-like', next: '*' }, + ], + }, + { + code: 'let a={}\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block-like', next: '*' }, + ], + }, + { + code: 'foo(function(){})\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block-like', next: '*' }, + ], + }, + { + code: '(function(){})()\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block-like', next: '*' }, + ], + }, + { + code: '!function(){}()\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block-like', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // exports + //---------------------------------------------------------------------- + + { + code: 'module.exports=1', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'exports', next: '*' }, + ], + }, + { + code: 'module.exports=1\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'exports', next: '*' }, + ], + }, + { + code: 'module.exports.foo=1\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'exports', next: '*' }, + ], + }, + { + code: 'exports.foo=1\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'exports', next: '*' }, + ], + }, + { + code: 'm.exports=1\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'exports', next: '*' }, + ], + }, + { + code: 'module.foo=1\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'exports', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // require + //---------------------------------------------------------------------- + + { + code: 'foo=require("foo")\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'require', next: '*' }, + ], + }, + { + code: 'const foo=a.require("foo")\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'require', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // directive + //---------------------------------------------------------------------- + + { + code: '"use strict"\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'directive', next: '*' }, + ], + }, + { + code: 'function foo(){"use strict"\n\nfoo()}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'directive', next: '*' }, + ], + }, + { + code: '(function foo(){"use strict"\n\nfoo()})', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'directive', next: '*' }, + ], + }, + { + code: '(()=>{"use strict"\n\nfoo()})', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'directive', next: '*' }, + ], + }, + { + code: "'use strict'\n\nfoo()", + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'directive', next: '*' }, + ], + }, + { + code: 'foo("use strict")\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'directive', next: '*' }, + ], + }, + { + code: '`use strict`\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'directive', next: '*' }, + ], + }, + { + code: '("use strict")\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'directive', next: '*' }, + ], + }, + { + code: "'use '+'strict'\nfoo()", + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'directive', next: '*' }, + ], + }, + { + code: 'foo()\n"use strict"\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'directive', next: '*' }, + ], + }, + { + code: '{"use strict"\nfoo()}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'directive', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // multiline-block-like + //---------------------------------------------------------------------- + + { + code: '{}\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'if(a){}\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'while(a){}\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: '{\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'if(a){\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'while(a){\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'do{\n}while(a)\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'for(;;){\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'for(a in b){\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'for(a of b){\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'switch(a){\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'function foo(a){\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'var a=function foo(a){\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // block + //---------------------------------------------------------------------- + + { + code: '{}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block', next: '*' }, + ], + }, + { + code: '{\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block', next: '*' }, + ], + }, + { + code: '{\nfoo()\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block', next: '*' }, + ], + }, + { + code: 'if(a){}\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block', next: '*' }, + ], + }, + { + code: 'a={}\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'block', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // empty + //---------------------------------------------------------------------- + + { + code: ';\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'empty', next: '*' }, + ], + }, + { + code: '1;\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'empty', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // expression + //---------------------------------------------------------------------- + + { + code: 'foo()\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'expression', next: '*' }, + ], + }, + { + code: 'a=b+c\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'expression', next: '*' }, + ], + }, + { + code: 'var a=1\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'expression', next: '*' }, + ], + }, + { + code: "'use strict'\nfoo()", + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'expression', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // multiline-expression + //---------------------------------------------------------------------- + + { + code: 'foo()\n\nfoo(\n\tx,\n\ty\n)', + options: [ + { blankLine: 'always', prev: '*', next: 'multiline-expression' }, + ], + }, + { + code: 'foo()\nfoo()', + options: [ + { blankLine: 'always', prev: '*', next: 'multiline-expression' }, + ], + }, + { + code: '() => {\n\tsomeArray.forEach(x => doSomething(x));\n\treturn theThing;\n}', + options: [ + { blankLine: 'always', prev: 'multiline-expression', next: 'return' }, + ], + }, + { + code: '() => {\n\tsomeArray.forEach(\n\t\tx => doSomething(x)\n\t);\n\n\treturn theThing;\n}', + options: [ + { blankLine: 'always', prev: 'multiline-expression', next: 'return' }, + ], + }, + + //---------------------------------------------------------------------- + // break + //---------------------------------------------------------------------- + + { + code: 'A:{break A\n\nfoo()}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'break', next: '*' }, + ], + }, + { + code: 'while(a){break\n\nfoo()}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'break', next: '*' }, + ], + }, + { + code: 'switch(a){case 0:break\n\nfoo()}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'break', next: '*' }, + ], + }, + { + code: 'switch(a){case 0:break\ncase 1:break}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'break', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // case + //---------------------------------------------------------------------- + + { + code: 'switch(a){case 0:\nfoo()\n\ncase 1:\nfoo()}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'case', next: '*' }, + ], + }, + { + code: 'switch(a){case 0:\nfoo()\n\ndefault:\nfoo()}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'case', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // class + //---------------------------------------------------------------------- + + { + code: 'class A{}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'class', next: '*' }, + ], + }, + { + code: 'var A = class{}\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'class', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // const + //---------------------------------------------------------------------- + + { + code: 'const a=1\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'const', next: '*' }, + ], + }, + { + code: 'let a=1\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'const', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // type + //---------------------------------------------------------------------- + + { + code: 'type a=number\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'type', next: '*' }, + ], + }, + { + code: 'let a=number\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'type', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // interface + //---------------------------------------------------------------------- + + { + code: 'interface Test{\na:number;\n}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'interface', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // continue + //---------------------------------------------------------------------- + + { + code: 'while(a){continue\n\nfoo()}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'continue', next: '*' }, + ], + }, + { + code: 'while(a){break\nfoo()}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'continue', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // debugger + //---------------------------------------------------------------------- + + { + code: 'debugger\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'debugger', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // default + //---------------------------------------------------------------------- + + { + code: 'switch(a){default:\nfoo()\n\ncase 0:\nfoo()\ncase 1:}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'default', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // do + //---------------------------------------------------------------------- + + { + code: 'do;while(a)\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'do', next: '*' }, + ], + }, + { + code: 'while(a);\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'do', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // export + //---------------------------------------------------------------------- + + { + code: 'export default 1\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'export', next: '*' }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: 'export let a=1\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'export', next: '*' }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: 'var a = 0; export {a}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'export', next: '*' }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: 'exports.foo=1\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'export', next: '*' }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: 'module.exports={}\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'export', next: '*' }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + + //---------------------------------------------------------------------- + // for + //---------------------------------------------------------------------- + + { + code: 'for(;;);\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'for', next: '*' }, + ], + }, + { + code: 'for(a in b);\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'for', next: '*' }, + ], + }, + { + code: 'for(a of b);\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'for', next: '*' }, + ], + }, + { + code: 'while(a);\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'for', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // function + //---------------------------------------------------------------------- + + { + code: 'function foo(){}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'function', next: '*' }, + ], + }, + { + code: 'var foo=function(){}\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'function', next: '*' }, + ], + }, + { + code: 'async function foo(){}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'function', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // if + //---------------------------------------------------------------------- + + { + code: 'if(a);\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'if', next: '*' }, + ], + }, + { + code: 'if(a);else;\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'if', next: '*' }, + ], + }, + { + code: 'if(a);else if(b);else;\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'if', next: '*' }, + ], + }, + { + code: 'for(;;);\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'if', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // iife + //---------------------------------------------------------------------- + + { + code: '(function(){\n})()\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'iife', next: '*' }], + }, + { + code: '+(function(){\n})()\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'iife', next: '*' }], + }, + { + code: '(function(){\n})()\nvar a = 2;', + options: [{ blankLine: 'never', prev: 'iife', next: '*' }], + }, + { + code: '+(function(){\n})()\nvar a = 2;', + options: [{ blankLine: 'never', prev: 'iife', next: '*' }], + }, + { + code: '(1, 2, 3, function(){\n})()\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'iife', next: '*' }], + }, + + //---------------------------------------------------------------------- + // import + //---------------------------------------------------------------------- + + { + code: "import 'a'\n\nfoo()", + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'import', next: '*' }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: "import a from 'a'\n\nfoo()", + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'import', next: '*' }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: "import * as a from 'a'\n\nfoo()", + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'import', next: '*' }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: "import {a} from 'a'\n\nfoo()", + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'import', next: '*' }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: "const a=require('a')\nfoo()", + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'import', next: '*' }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + + //---------------------------------------------------------------------- + // let + //---------------------------------------------------------------------- + + { + code: 'let a=1\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'let', next: '*' }, + ], + }, + { + code: 'var a=1\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'let', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // return + //---------------------------------------------------------------------- + + { + code: 'function foo(){return\n\nfoo()}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'return', next: '*' }, + ], + }, + { + code: 'throw a\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'return', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // switch + //---------------------------------------------------------------------- + + { + code: 'switch(a){}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'switch', next: '*' }, + ], + }, + { + code: 'if(a){}\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'switch', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // throw + //---------------------------------------------------------------------- + + { + code: 'throw a\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'throw', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // try + //---------------------------------------------------------------------- + + { + code: 'try{}catch(e){}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'try', next: '*' }, + ], + }, + { + code: 'try{}finally{}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'try', next: '*' }, + ], + }, + { + code: 'try{}catch(e){}finally{}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'try', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // var + //---------------------------------------------------------------------- + + { + code: 'var a=1\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'var', next: '*' }, + ], + }, + { + code: 'const a=1\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'var', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // while + //---------------------------------------------------------------------- + + { + code: 'while(a);\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'while', next: '*' }, + ], + }, + { + code: 'do;while(a)\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'while', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // with + //---------------------------------------------------------------------- + + { + code: 'with(a);\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'with', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // multiline-const + //---------------------------------------------------------------------- + + { + code: 'const a={\nb:1,\nc:2\n}\n\nconst d=3', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-const', next: '*' }, + ], + }, + { + code: 'const a=1\n\nconst b={\nc:2,\nd:3\n}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'multiline-const' }, + ], + }, + { + code: 'const a=1\nconst b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-const', next: '*' }, + ], + }, + { + code: 'const a=1\nconst b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'multiline-const' }, + ], + }, + + //---------------------------------------------------------------------- + // multiline-let + //---------------------------------------------------------------------- + + { + code: 'let a={\nb:1,\nc:2\n}\n\nlet d=3', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-let', next: '*' }, + ], + }, + { + code: 'let a=1\n\nlet b={\nc:2,\nd:3\n}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'multiline-let' }, + ], + }, + { + code: 'let a=1\nlet b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-let', next: '*' }, + ], + }, + { + code: 'let a=1\nlet b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'multiline-let' }, + ], + }, + + //---------------------------------------------------------------------- + // multiline-var + //---------------------------------------------------------------------- + + { + code: 'var a={\nb:1,\nc:2\n}\n\nvar d=3', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-var', next: '*' }, + ], + }, + { + code: 'var a=1\n\nvar b={\nc:2,\nd:3\n}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'multiline-var' }, + ], + }, + { + code: 'var a=1\nvar b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'multiline-var', next: '*' }, + ], + }, + { + code: 'var a=1\nvar b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'multiline-var' }, + ], + }, + + //---------------------------------------------------------------------- + // single line const + //---------------------------------------------------------------------- + + { + code: 'const a=1\n\nconst b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'singleline-const', next: '*' }, + ], + }, + { + code: 'const a=1\n\nconst b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'singleline-const' }, + ], + }, + { + code: 'const a={\nb:1,\nc:2\n}\nconst d={\ne:3,\nf:4\n}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'singleline-const', next: '*' }, + ], + }, + { + code: 'const a={\nb:1,\nc:2\n}\nconst d={\ne:3,\nf:4\n}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'singleline-const' }, + ], + }, + + //---------------------------------------------------------------------- + // single line let + //---------------------------------------------------------------------- + + { + code: 'let a=1\n\nlet b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'singleline-let', next: '*' }, + ], + }, + { + code: 'let a=1\n\nlet b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'singleline-let' }, + ], + }, + { + code: 'let a={\nb:1,\nc:2\n}\nlet d={\ne:3,\nf:4\n}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'singleline-let', next: '*' }, + ], + }, + { + code: 'let a={\nb:1,\nc:2\n}\nlet d={\ne:3,\nf:4\n}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'singleline-let' }, + ], + }, + + //---------------------------------------------------------------------- + // single line var + //---------------------------------------------------------------------- + + { + code: 'var a=1\n\nvar b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'singleline-var', next: '*' }, + ], + }, + { + code: 'var a=1\n\nvar b=2', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'singleline-var' }, + ], + }, + { + code: 'var a={\nb:1,\nc:2\n}\nvar d={\ne:3,\nf:4\n}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'singleline-var', next: '*' }, + ], + }, + { + code: 'var a={\nb:1,\nc:2\n}\nvar d={\ne:3,\nf:4\n}', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: '*', next: 'singleline-var' }, + ], + }, + + //---------------------------------------------------------------------- + // Tests from newline-after-var + //---------------------------------------------------------------------- + + // should skip rule entirely + { + code: 'console.log(greet);', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'console.log(greet);', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should ignore a `var` with no following token + { + code: "var greet = 'hello';", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello';", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should allow no line break in "never" mode + { + code: "var greet = 'hello';console.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should allow no blank line in "never" mode + { + code: "var greet = 'hello';\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should allow one blank line in "always" mode + { + code: "var greet = 'hello';\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should allow two or more blank lines in "always" mode + { + code: "var greet = 'hello';\n\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello';\n\n\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should allow trailing whitespace after the `var` + { + code: "var greet = 'hello'; \n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello'; \nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should allow inline comments after the `var` + { + code: "var greet = 'hello'; // inline comment\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello'; // inline comment\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should allow a comment on the next line in "never" mode + { + code: "var greet = 'hello';\n// next-line comment\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello';\n/* block comment\nblock comment */\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should allow comments on the next line followed by a blank in "always" mode + { + code: "var greet = 'hello';\n// next-line comment\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello';\n/* block comment\nblock comment */\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello';\n// next-line comment\n// second-line comment\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should allow comments on the next line followed by no blank in "never" mode + { + code: "var greet = 'hello';\n// next-line comment\n// second-line comment\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello';\n// next-line comment\n/* block comment\nblock comment */\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should allow another `var` statement to follow without blank line + { + code: "var greet = 'hello';var name = 'world';console.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello';\nvar name = 'world';\nconsole.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello';\nvar name = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should allow a comment directly between `var` statements + { + code: "var greet = 'hello';\n// inline comment\nvar name = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello';\n/* block comment\nblock comment */\nvar name = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello';\n// inline comment\nvar name = 'world';\nconsole.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello';\n/* block comment\nblock comment */\nvar name = 'world';\nconsole.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should handle single `var` statement with multiple declarations + { + code: "var greet = 'hello', name = 'world';console.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello', name = 'world';\nconsole.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello', name = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should handle single `var` statement with multi-line declaration + { + code: "var greet = 'hello',\nname = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello',\nname = 'world';\nconsole.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello', // inline comment\nname = 'world'; // inline comment\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello', // inline comment\nname = 'world'; // inline comment\nconsole.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello',\nname = 'world';\n// next-line comment\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var greet = 'hello',\nname = 'world';\n/* block comment\nblock comment */\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should handle ES6 `let` block binding + { + code: "let greet = 'hello';\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "let greet = 'hello';\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should handle ES6 `const` block binding + { + code: "const greet = 'hello';\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "const greet = 'hello';\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should handle a mix of `var`, `let`, or `const` + { + code: "let greet = 'hello';\nvar name = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "const greet = 'hello';\nvar name = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "let greet = 'hello';\nconst name = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should handle a mix of `var` or `let` inside for variations + { + code: 'for(let a = 1; a < 1; a++){\n break;\n}', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'for(var a = 1; a < 1; a++){\n break;\n}', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'for(let a = 1; a < 1; a++){\n break;\n}', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'for(var a = 1; a < 1; a++){\n break;\n}', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'for(let a in obj){\n break;\n}', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'for(var a in obj){\n break;\n}', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'for(let a in obj){\n break;\n}', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'for(var a in obj){\n break;\n}', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should handle export specifiers + { + code: 'export let a = 1;\nexport let b = 2;', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: 'export let a = 1;\nexport let b = 2;', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: 'export var a = 1;\nexport var b = 2;', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: 'export var a = 1;\nexport var b = 2;', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: 'export const a = 1;\nexport const b = 2;', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + { + code: 'export const a = 1;\nexport const b = 2;', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + }, + + // should allow no blank line at end of block + { + code: "function example() {\nvar greet = 'hello'\n}", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "function example() {\nvar greet = 'hello'\n}", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "function example() {\nvar greet = 'hello';\nconsole.log(greet);\n}", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var f = function() {\nvar greet = 'hello'\n};", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var f = function() {\nvar greet = 'hello'\n};", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "var f = function() {\nvar greet = 'hello';\nconsole.log(greet);\n};", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "() => {\nvar greet = 'hello';\n}", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "() => {\nvar greet = 'hello';\n}", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: "() => {\nvar greet = 'hello';\nconsole.log(greet);\n}", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: '{\nvar foo;\n}', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: '{\nvar foo;\n}', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'if(true) {\nvar foo;\n}', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'if(true) {\nvar foo;\n}', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'switch(a) {\ncase 0:\nvar foo;\n}', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'switch(a) {\ncase 0:\nvar foo;\n}', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // should handle one/no blank before case. + { + code: 'switch(a) {\ncase 0:\nvar foo;\n\ncase 1:}', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'switch(a) {\ncase 0:\nvar foo;\ncase 1:}', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + // https://github.com/eslint/eslint/issues/6834 + { + code: ` +var a = 1 + +;(b || c).doSomething() + `, + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: ` +var a = 1 +;(b || c).doSomething() + `, + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: ` +var a = 1 +; +(b || c).doSomething(); + `, + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + { + code: 'switch(a) {\ncase 0:\nvar foo;\n\ncase 1:}', + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: 'switch(a) {\ncase 0:\nvar foo;\ncase 1:}', + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + { + code: ` +var a = 1 + +; +(b || c).doSomething(); + `, + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + }, + + //---------------------------------------------------------------------- + // Tests from newline-before-return + //---------------------------------------------------------------------- + + { + code: 'function a() {\nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\n\nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nvar b;\n\nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) return;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) { return; }\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) {\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) {\n\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) {\nreturn;\n}\n\nreturn c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) {\n\nreturn;\n}\n\nreturn c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (!b) {\nreturn;\n} else {\nreturn b;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (!b) {\nreturn;\n} else {\n\nreturn b;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) {\nreturn b;\n} else if (c) {\nreturn c;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) {\nreturn b;\n} else if (c) {\nreturn c;\n} else {\nreturn d;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) {\nreturn b;\n} else if (c) {\nreturn c;\n} else {\nreturn d;\n}\n\nreturn a;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) return b;\nelse if (c) return c;\nelse return d;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) return b;\nelse if (c) return c;\nelse {\nreturn d;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) return b;\nelse if (c) return c;\nelse {\ne();\n\nreturn d;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nwhile (b) return;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\n while (b) \nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\n while (b) { return; }\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\n while (b) {\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\n while (b) {\nc();\n\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nvar c;\nwhile (b) {\n c = d; //comment\n}\n\nreturn c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\ndo return;\nwhile (b);\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\ndo \nreturn;\nwhile (b);\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\ndo { return; } while (b);\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\ndo { return; }\nwhile (b);\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\ndo {\nreturn;\n} while (b);\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\ndo {\nc();\n\nreturn;\n} while (b);\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (var b; b < c; b++) return;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (var b; b < c; b++)\nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (var b; b < c; b++) {\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (var b; b < c; b++) {\nc();\n\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (var b; b < c; b++) {\nif (d) {\nbreak; //comment\n}\n\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (b in c)\nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (b in c) { return; }\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (b in c) {\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (b in c) {\nd();\n\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (b of c) return;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (b of c)\nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (b of c) {\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nfor (b of c) {\nd();\n\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: "function a() {\nswitch (b) {\ncase 'b': return;\n}\n}", + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: "function a() {\nswitch (b) {\ncase 'b':\nreturn;\n}\n}", + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: "function a() {\nswitch (b) {\ncase 'b': {\nreturn;\n}\n}\n}", + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\n//comment\nreturn b;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\n{\n//comment\n}\n\nreturn\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nvar b = {\n//comment\n};\n\nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {/*multi-line\ncomment*/return b;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\n/*comment\ncomment*/\n//comment\nreturn b;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\n/*comment\ncomment*/\n//comment\nif (b) return;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\n/*comment\ncomment*/\n//comment\nif (b) {\nc();\n\nreturn b;\n} else {\n//comment\nreturn d;\n}\n\n/*multi-line\ncomment*/\nreturn e;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) { //comment\nreturn;\n}\n\nreturn c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) { return; } //comment\n\nreturn c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) { return; } /*multi-line\ncomment*/\n\nreturn c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'function a() {\nif (b) { return; }\n\n/*multi-line\ncomment*/ return c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + }, + { + code: 'return;', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + parserOptions: { ecmaFeatures: { globalReturn: true } }, + }, + { + code: 'var a;\n\nreturn;', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + parserOptions: { ecmaFeatures: { globalReturn: true } }, + }, + { + code: '// comment\nreturn;', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + parserOptions: { ecmaFeatures: { globalReturn: true } }, + }, + { + code: '/* comment */\nreturn;', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + parserOptions: { ecmaFeatures: { globalReturn: true } }, + }, + { + code: '/* multi-line\ncomment */\nreturn;', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + parserOptions: { ecmaFeatures: { globalReturn: true } }, + }, + + //---------------------------------------------------------------------- + // From JSCS disallowPaddingNewLinesAfterBlocks + // https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/test/specs/rules/disallow-padding-newlines-after-blocks.js + //---------------------------------------------------------------------- + + { + code: 'if(true){}', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true){}\n', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true){}\nvar a = 2;', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true){\nif(true) {}\n}', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: 'var a = {\nfoo: function() {\n},\nbar: function() {\n}}', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: '(function(){\n})()\nvar a = 2;', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true) {\n}\nelse\n{\n}', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true) {\n} else {\n var a = 2; }', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true) {\n}\nelse if(true)\n{\n}\nelse {\n}', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: 'do{\n}\nwhile(true)', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: 'try{\n}\ncatch(e) {}', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: 'try{\n}\nfinally {}', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: 'try{\n}\ncatch(e) {\n}\nfinally {\n}', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + { + code: '[].map(function() {})\n.filter(function(){})', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + }, + + //---------------------------------------------------------------------- + // From JSCS disallowPaddingNewLinesBeforeExport + // https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/test/specs/rules/disallow-padding-newlines-before-export.js + //---------------------------------------------------------------------- + + { + code: 'var a = 2;\nmodule.exports = a;', + options: [{ blankLine: 'never', prev: '*', next: 'exports' }], + }, + { + code: 'module.exports = 2;', + options: [{ blankLine: 'never', prev: '*', next: 'exports' }], + }, + { + code: 'var a = 2;\n// foo\nmodule.exports = a;', + options: [{ blankLine: 'never', prev: '*', next: 'exports' }], + }, + + /* + * TODO: May it need an option to ignore blank lines followed by comments? + * { + * code: "var a = 2;\n\n// foo\nmodule.exports = a;", + * options: [ + * { blankLine: "never", prev: "*", next: "exports" } + * ] + * }, + */ + { + code: 'var a = 2;\n\nfoo.exports = a;', + options: [{ blankLine: 'never', prev: '*', next: 'exports' }], + }, + { + code: 'var a = 2;\n\nmodule.foo = a;', + options: [{ blankLine: 'never', prev: '*', next: 'exports' }], + }, + { + code: 'var a = 2;\n\nfoo = a;', + options: [{ blankLine: 'never', prev: '*', next: 'exports' }], + }, + + //---------------------------------------------------------------------- + // From JSCS requirePaddingNewLinesAfterBlocks + // https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/test/specs/rules/require-padding-newlines-after-blocks.js + //---------------------------------------------------------------------- + + { + code: '{}', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true){}', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true){}\n', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true){}\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true){}\n\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true){\nif(true) {}\n}', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'var a = {\nfoo: function() {\n},\n\nbar: function() {\n}}', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: '(function(){\n})()\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true) {\n}\nelse\n{\n}', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true) {\n} else {\n var a = 2; }', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'if(true) {\n}\nelse if(true)\n{\n}\nelse {\n}', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'do{\n}\nwhile(true)', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'try{\n}\ncatch(e) {}', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'try{\n}\nfinally {}', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'try{\n}\ncatch(e) {\n}\nfinally {\n}', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: '[].map(function() {})\n.filter(function(){})', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'func(\n2,\n3,\nfunction() {\n}\n)', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: '[\n2,\n3,\nfunction() {\n}\n]', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'a(res => {\n})\n.b();', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + }, + { + code: 'var foo = (\n\nfoo\n\n);', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + parserOptions: { ecmaFeatures: { jsx: true } }, + }, + { + code: 'var i = 0;\nwhile (i < 100) {\nif(i % 2 === 0) {continue;}\n++i;\n}', + options: [ + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + { + code: 'var i = 0;\nwhile (i < 100) {\nif(i % 2 === 0) {if(i === 4) {continue;}}\n++i;\n}', + options: [ + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + }, + + //---------------------------------------------------------------------- + // From JSCS requirePaddingNewLinesBeforeExport + // https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/test/specs/rules/require-padding-newlines-before-export.js + //---------------------------------------------------------------------- + + { + code: 'module.exports = 2;', + options: [{ blankLine: 'always', prev: '*', next: 'exports' }], + }, + { + code: 'var a = 2;\n\nmodule.exports = a;', + options: [{ blankLine: 'always', prev: '*', next: 'exports' }], + }, + { + code: 'var a = 2;\nfoo.exports = a;', + options: [{ blankLine: 'always', prev: '*', next: 'exports' }], + }, + { + code: 'var a = 2;\nmodule.foo = a;', + options: [{ blankLine: 'always', prev: '*', next: 'exports' }], + }, + { + code: 'if (true) {\nmodule.exports = a;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'exports' }], + }, + + //---------------------------------------------------------------------- + // From JSCS requirePaddingNewlinesBeforeKeywords + // https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/test/specs/rules/require-padding-newlines-before-keywords.js + //---------------------------------------------------------------------- + + { + code: 'function x() { return; }', + options: [ + { + blankLine: 'always', + prev: '*', + next: [ + 'if', + 'for', + 'return', + 'switch', + 'case', + 'break', + 'throw', + 'while', + 'default', + ], + }, + ], + }, + { + code: 'if (true) {} else if (false) {}', + options: [ + { + blankLine: 'always', + prev: '*', + next: [ + 'if', + 'for', + 'return', + 'switch', + 'case', + 'break', + 'throw', + 'while', + 'default', + ], + }, + ], + }, + { + code: 'function x() { var a = true; do { a = !a; } while (a); }', + options: [ + { + blankLine: 'always', + prev: '*', + next: [ + 'if', + 'for', + 'return', + 'switch', + 'case', + 'break', + 'throw', + 'while', + 'default', + ], + }, + ], + }, + { + code: 'function x() { if (true) return; }', + options: [ + { + blankLine: 'always', + prev: '*', + next: [ + 'if', + 'for', + 'return', + 'switch', + 'case', + 'break', + 'throw', + 'while', + 'default', + ], + }, + ], + }, + { + code: 'function test() {};', + options: [ + { blankLine: 'always', prev: 'block-like', next: 'block-like' }, + ], + }, + ], + invalid: [ + //---------------------------------------------------------------------- + // wildcard + //---------------------------------------------------------------------- + + { + code: 'foo();\n\nfoo();', + output: 'foo();\nfoo();', + options: [{ blankLine: 'never', prev: '*', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'foo();\n\n//comment\nfoo();', + output: 'foo();\n//comment\nfoo();', + options: [{ blankLine: 'never', prev: '*', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: ' foo();\n \n //comment\n foo();', + output: ' foo();\n //comment\n foo();', + options: [{ blankLine: 'never', prev: '*', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'if (a) {}\n\nfor (;;) {}', + output: 'if (a) {}\nfor (;;) {}', + options: [{ blankLine: 'never', prev: '*', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'foo();\nfoo();', + output: 'foo();\n\nfoo();', + options: [{ blankLine: 'always', prev: '*', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: ' function a() {}\n do {} while (a)', + output: ' function a() {}\n\n do {} while (a)', + options: [{ blankLine: 'always', prev: '*', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'foo();//trailing-comment\n//comment\n//comment\nfoo();', + output: 'foo();//trailing-comment\n\n//comment\n//comment\nfoo();', + options: [{ blankLine: 'always', prev: '*', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // block-like + //---------------------------------------------------------------------- + + { + code: '{}\n\nfoo()', + output: '{}\nfoo()', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: '{}\nfoo()', + output: '{}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'if(a){}\nfoo()', + output: 'if(a){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'if(a){}else{}\nfoo()', + output: 'if(a){}else{}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'if(a){}else if(b){}\nfoo()', + output: 'if(a){}else if(b){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'if(a){}else if(b){}else{}\nfoo()', + output: 'if(a){}else if(b){}else{}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'switch(a){}\nfoo()', + output: 'switch(a){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'switch(a){case 0:}\nfoo()', + output: 'switch(a){case 0:}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'try{}catch(e){}\nfoo()', + output: 'try{}catch(e){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'try{}finally{}\nfoo()', + output: 'try{}finally{}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'try{}catch(e){}finally{}\nfoo()', + output: 'try{}catch(e){}finally{}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'while(a){}\nfoo()', + output: 'while(a){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'do{}while(a)\nfoo()', + output: 'do{}while(a)\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'for(;;){}\nfoo()', + output: 'for(;;){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'for(a in b){}\nfoo()', + output: 'for(a in b){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'for(a of b){}\nfoo()', + output: 'for(a of b){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'a=function(){}\nfoo()', + output: 'a=function(){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'a=()=>{}\nfoo()', + output: 'a=()=>{}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a(){}\nfoo()', + output: 'function a(){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'let a=function(){}\nfoo()', + output: 'let a=function(){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // exports + //---------------------------------------------------------------------- + + { + code: 'module.exports=1\n\nfoo()', + output: 'module.exports=1\nfoo()', + options: [{ blankLine: 'never', prev: 'exports', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'module.exports=1\nfoo()', + output: 'module.exports=1\n\nfoo()', + options: [{ blankLine: 'always', prev: 'exports', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'module.exports.foo=1\nfoo()', + output: 'module.exports.foo=1\n\nfoo()', + options: [{ blankLine: 'always', prev: 'exports', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'module.exports[foo]=1\nfoo()', + output: 'module.exports[foo]=1\n\nfoo()', + options: [{ blankLine: 'always', prev: 'exports', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'exports.foo=1\nfoo()', + output: 'exports.foo=1\n\nfoo()', + options: [{ blankLine: 'always', prev: 'exports', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'exports[foo]=1\nfoo()', + output: 'exports[foo]=1\n\nfoo()', + options: [{ blankLine: 'always', prev: 'exports', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // require + //---------------------------------------------------------------------- + + { + code: 'const foo=require("foo")\n\nfoo()', + output: 'const foo=require("foo")\nfoo()', + options: [{ blankLine: 'never', prev: 'require', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'const foo=require("foo")\nfoo()', + output: 'const foo=require("foo")\n\nfoo()', + options: [{ blankLine: 'always', prev: 'require', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'const foo=require("foo").Foo\nfoo()', + output: 'const foo=require("foo").Foo\n\nfoo()', + options: [{ blankLine: 'always', prev: 'require', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'const foo=require("foo")[a]\nfoo()', + output: 'const foo=require("foo")[a]\n\nfoo()', + options: [{ blankLine: 'always', prev: 'require', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // directive + //---------------------------------------------------------------------- + + { + code: '"use strict"\n\nfoo()', + output: '"use strict"\nfoo()', + options: [{ blankLine: 'never', prev: 'directive', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: '"use strict"\nfoo()', + output: '"use strict"\n\nfoo()', + options: [{ blankLine: 'always', prev: 'directive', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "'use strict'\nfoo()", + output: "'use strict'\n\nfoo()", + options: [{ blankLine: 'always', prev: 'directive', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "'use asm'\nfoo()", + output: "'use asm'\n\nfoo()", + options: [{ blankLine: 'always', prev: 'directive', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // multiline-block-like + //---------------------------------------------------------------------- + + { + code: '{\n}\n\nfoo()', + output: '{\n}\nfoo()', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: '{\n}\nfoo()', + output: '{\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'if(a){\n}\nfoo()', + output: 'if(a){\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'if(a){\n}else{\n}\nfoo()', + output: 'if(a){\n}else{\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'if(a){\n}else if(b){\n}\nfoo()', + output: 'if(a){\n}else if(b){\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'if(a){\n}else if(b){\n}else{\n}\nfoo()', + output: 'if(a){\n}else if(b){\n}else{\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'switch(a){\n}\nfoo()', + output: 'switch(a){\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'try{\n}catch(e){\n}\nfoo()', + output: 'try{\n}catch(e){\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'try{\n}finally{\n}\nfoo()', + output: 'try{\n}finally{\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'try{\n}catch(e){\n}finally{\n}\nfoo()', + output: 'try{\n}catch(e){\n}finally{\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'while(a){\n}\nfoo()', + output: 'while(a){\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'do{\n}while(a)\nfoo()', + output: 'do{\n}while(a)\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'for(;;){\n}\nfoo()', + output: 'for(;;){\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'for(a in b){\n}\nfoo()', + output: 'for(a in b){\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'for(a of b){\n}\nfoo()', + output: 'for(a of b){\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'a=function(){\n}\nfoo()', + output: 'a=function(){\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'a=()=>{\n}\nfoo()', + output: 'a=()=>{\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a(){\n}\nfoo()', + output: 'function a(){\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'let a=function(){\n}\nfoo()', + output: 'let a=function(){\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // block + //---------------------------------------------------------------------- + + { + code: '{}\n\nfoo()', + output: '{}\nfoo()', + options: [{ blankLine: 'never', prev: 'block', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: '{}\nfoo()', + output: '{}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'block', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // empty + //---------------------------------------------------------------------- + + { + code: ';\n\nfoo()', + output: ';\nfoo()', + options: [{ blankLine: 'never', prev: 'empty', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: ';\nfoo()', + output: ';\n\nfoo()', + options: [{ blankLine: 'always', prev: 'empty', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // expression + //---------------------------------------------------------------------- + + { + code: 'foo()\n\nfoo()', + output: 'foo()\nfoo()', + options: [{ blankLine: 'never', prev: 'expression', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'foo()\nfoo()', + output: 'foo()\n\nfoo()', + options: [{ blankLine: 'always', prev: 'expression', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // multiline-expression + //---------------------------------------------------------------------- + + { + code: 'foo()\n\nfoo(\n\tx,\n\ty\n)', + output: 'foo()\nfoo(\n\tx,\n\ty\n)', + options: [ + { blankLine: 'never', prev: '*', next: 'multiline-expression' }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'foo()\nfoo(\n\tx,\n\ty\n)', + output: 'foo()\n\nfoo(\n\tx,\n\ty\n)', + options: [ + { blankLine: 'always', prev: '*', next: 'multiline-expression' }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: '() => {\n\tsomeArray.forEach(\n\t\tx => doSomething(x)\n\t);\n\treturn theThing;\n}', + output: + '() => {\n\tsomeArray.forEach(\n\t\tx => doSomething(x)\n\t);\n\n\treturn theThing;\n}', + options: [ + { blankLine: 'always', prev: 'multiline-expression', next: 'return' }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // break + //---------------------------------------------------------------------- + + { + code: 'while(a){break\n\nfoo()}', + output: 'while(a){break\nfoo()}', + options: [{ blankLine: 'never', prev: 'break', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'switch(a){case 0:break\n\nfoo()}', + output: 'switch(a){case 0:break\nfoo()}', + options: [{ blankLine: 'never', prev: 'break', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'while(a){break\nfoo()}', + output: 'while(a){break\n\nfoo()}', + options: [{ blankLine: 'always', prev: 'break', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'switch(a){case 0:break\nfoo()}', + output: 'switch(a){case 0:break\n\nfoo()}', + options: [{ blankLine: 'always', prev: 'break', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // case + //---------------------------------------------------------------------- + + { + code: 'switch(a){case 0:\nfoo()\n\ndefault:}', + output: 'switch(a){case 0:\nfoo()\ndefault:}', + options: [{ blankLine: 'never', prev: 'case', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'switch(a){case 0:\nfoo()\ndefault:}', + output: 'switch(a){case 0:\nfoo()\n\ndefault:}', + options: [{ blankLine: 'always', prev: 'case', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // class + //---------------------------------------------------------------------- + + { + code: 'class A{}\n\nfoo()', + output: 'class A{}\nfoo()', + options: [{ blankLine: 'never', prev: 'class', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'class A{}\nfoo()', + output: 'class A{}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'class', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // const + //---------------------------------------------------------------------- + + { + code: 'const a=1\n\nfoo()', + output: 'const a=1\nfoo()', + options: [{ blankLine: 'never', prev: 'const', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'const a=1\nfoo()', + output: 'const a=1\n\nfoo()', + options: [{ blankLine: 'always', prev: 'const', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // type + //---------------------------------------------------------------------- + + { + code: 'type a=number\n\nfoo()', + output: 'type a=number\nfoo()', + options: [{ blankLine: 'never', prev: 'type', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'type a=number\nfoo()', + output: 'type a=number\n\nfoo()', + options: [{ blankLine: 'always', prev: 'type', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // interface + //---------------------------------------------------------------------- + + { + code: 'interface Test{\na:number;\n}\nfoo()', + output: 'interface Test{\na:number;\n}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'interface', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // continue + //---------------------------------------------------------------------- + + { + code: 'while(a){continue\n\nfoo()}', + output: 'while(a){continue\nfoo()}', + options: [{ blankLine: 'never', prev: 'continue', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'while(a){continue\nfoo()}', + output: 'while(a){continue\n\nfoo()}', + options: [{ blankLine: 'always', prev: 'continue', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // debugger + //---------------------------------------------------------------------- + + { + code: 'debugger\n\nfoo()', + output: 'debugger\nfoo()', + options: [{ blankLine: 'never', prev: 'debugger', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'debugger\nfoo()', + output: 'debugger\n\nfoo()', + options: [{ blankLine: 'always', prev: 'debugger', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // default + //---------------------------------------------------------------------- + + { + code: 'switch(a){default:\nfoo()\n\ncase 0:}', + output: 'switch(a){default:\nfoo()\ncase 0:}', + options: [{ blankLine: 'never', prev: 'default', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'switch(a){default:\nfoo()\ncase 0:}', + output: 'switch(a){default:\nfoo()\n\ncase 0:}', + options: [{ blankLine: 'always', prev: 'default', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // do + //---------------------------------------------------------------------- + + { + code: 'do;while(a)\n\nfoo()', + output: 'do;while(a)\nfoo()', + options: [{ blankLine: 'never', prev: 'do', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'do;while(a)\nfoo()', + output: 'do;while(a)\n\nfoo()', + options: [{ blankLine: 'always', prev: 'do', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // export + //---------------------------------------------------------------------- + + { + code: 'export default 1\n\nfoo()', + output: 'export default 1\nfoo()', + options: [{ blankLine: 'never', prev: 'export', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'export let a=1\n\nfoo()', + output: 'export let a=1\nfoo()', + options: [{ blankLine: 'never', prev: 'export', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'var a = 0;export {a}\n\nfoo()', + output: 'var a = 0;export {a}\nfoo()', + options: [{ blankLine: 'never', prev: 'export', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'export default 1\nfoo()', + output: 'export default 1\n\nfoo()', + options: [{ blankLine: 'always', prev: 'export', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'export let a=1\nfoo()', + output: 'export let a=1\n\nfoo()', + options: [{ blankLine: 'always', prev: 'export', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'var a = 0;export {a}\nfoo()', + output: 'var a = 0;export {a}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'export', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // for + //---------------------------------------------------------------------- + + { + code: 'for(;;);\n\nfoo()', + output: 'for(;;);\nfoo()', + options: [{ blankLine: 'never', prev: 'for', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'for(a in b);\n\nfoo()', + output: 'for(a in b);\nfoo()', + options: [{ blankLine: 'never', prev: 'for', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'for(a of b);\n\nfoo()', + output: 'for(a of b);\nfoo()', + options: [{ blankLine: 'never', prev: 'for', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'for(;;);\nfoo()', + output: 'for(;;);\n\nfoo()', + options: [{ blankLine: 'always', prev: 'for', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'for(a in b);\nfoo()', + output: 'for(a in b);\n\nfoo()', + options: [{ blankLine: 'always', prev: 'for', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'for(a of b);\nfoo()', + output: 'for(a of b);\n\nfoo()', + options: [{ blankLine: 'always', prev: 'for', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // function + //---------------------------------------------------------------------- + + { + code: 'function foo(){}\n\nfoo()', + output: 'function foo(){}\nfoo()', + options: [{ blankLine: 'never', prev: 'function', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'function foo(){}\nfoo()', + output: 'function foo(){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'function', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'async function foo(){}\nfoo()', + output: 'async function foo(){}\n\nfoo()', + options: [ + { blankLine: 'never', prev: '*', next: '*' }, + { blankLine: 'always', prev: 'function', next: '*' }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // if + //---------------------------------------------------------------------- + + { + code: 'if(a);\n\nfoo()', + output: 'if(a);\nfoo()', + options: [{ blankLine: 'never', prev: 'if', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'if(a);else;\n\nfoo()', + output: 'if(a);else;\nfoo()', + options: [{ blankLine: 'never', prev: 'if', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'if(a);\nfoo()', + output: 'if(a);\n\nfoo()', + options: [{ blankLine: 'always', prev: 'if', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'if(a);else;\nfoo()', + output: 'if(a);else;\n\nfoo()', + options: [{ blankLine: 'always', prev: 'if', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // iife + //---------------------------------------------------------------------- + + { + code: '(function(){\n})()\n\nvar a = 2;', + output: '(function(){\n})()\nvar a = 2;', + options: [{ blankLine: 'never', prev: 'iife', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: '+(function(){\n})()\n\nvar a = 2;', + output: '+(function(){\n})()\nvar a = 2;', + options: [{ blankLine: 'never', prev: 'iife', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: '(function(){\n})()\nvar a = 2;', + output: '(function(){\n})()\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'iife', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: '+(function(){\n})()\nvar a = 2;', + output: '+(function(){\n})()\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'iife', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + // Optional chaining + { + code: '(function(){\n})?.()\nvar a = 2;', + output: '(function(){\n})?.()\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'iife', next: '*' }], + parserOptions: { ecmaVersion: 2020 }, + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'void (function(){\n})?.()\nvar a = 2;', + output: 'void (function(){\n})?.()\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'iife', next: '*' }], + parserOptions: { ecmaVersion: 2020 }, + errors: [{ messageId: 'expectedBlankLine' }], + }, + + // Sequenced function + { + code: '(1,2,3,function(){\n})()\nvar a = 2;', + output: '(1,2,3,function(){\n})()\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'iife', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // import + //---------------------------------------------------------------------- + + { + code: "import a from 'a'\n\nfoo()", + output: "import a from 'a'\nfoo()", + options: [{ blankLine: 'never', prev: 'import', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "import * as a from 'a'\n\nfoo()", + output: "import * as a from 'a'\nfoo()", + options: [{ blankLine: 'never', prev: 'import', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "import {a} from 'a'\n\nfoo()", + output: "import {a} from 'a'\nfoo()", + options: [{ blankLine: 'never', prev: 'import', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "import a from 'a'\nfoo()", + output: "import a from 'a'\n\nfoo()", + options: [{ blankLine: 'always', prev: 'import', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "import * as a from 'a'\nfoo()", + output: "import * as a from 'a'\n\nfoo()", + options: [{ blankLine: 'always', prev: 'import', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "import {a} from 'a'\nfoo()", + output: "import {a} from 'a'\n\nfoo()", + options: [{ blankLine: 'always', prev: 'import', next: '*' }], + parserOptions: { ecmaVersion: 6, sourceType: 'module' }, + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // let + //---------------------------------------------------------------------- + + { + code: 'let a\n\nfoo()', + output: 'let a\nfoo()', + options: [{ blankLine: 'never', prev: 'let', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'let a\nfoo()', + output: 'let a\n\nfoo()', + options: [{ blankLine: 'always', prev: 'let', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // return + //---------------------------------------------------------------------- + + { + code: 'function foo(){return\n\nfoo()}', + output: 'function foo(){return\nfoo()}', + options: [{ blankLine: 'never', prev: 'return', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'function foo(){return\nfoo()}', + output: 'function foo(){return\n\nfoo()}', + options: [{ blankLine: 'always', prev: 'return', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // switch + //---------------------------------------------------------------------- + + { + code: 'switch(a){}\n\nfoo()', + output: 'switch(a){}\nfoo()', + options: [{ blankLine: 'never', prev: 'switch', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'switch(a){}\nfoo()', + output: 'switch(a){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'switch', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // throw + //---------------------------------------------------------------------- + + { + code: 'throw a\n\nfoo()', + output: 'throw a\nfoo()', + options: [{ blankLine: 'never', prev: 'throw', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'throw a\nfoo()', + output: 'throw a\n\nfoo()', + options: [{ blankLine: 'always', prev: 'throw', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // try + //---------------------------------------------------------------------- + + { + code: 'try{}catch(e){}\n\nfoo()', + output: 'try{}catch(e){}\nfoo()', + options: [{ blankLine: 'never', prev: 'try', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'try{}finally{}\n\nfoo()', + output: 'try{}finally{}\nfoo()', + options: [{ blankLine: 'never', prev: 'try', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'try{}catch(e){}finally{}\n\nfoo()', + output: 'try{}catch(e){}finally{}\nfoo()', + options: [{ blankLine: 'never', prev: 'try', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'try{}catch(e){}\nfoo()', + output: 'try{}catch(e){}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'try', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'try{}finally{}\nfoo()', + output: 'try{}finally{}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'try', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'try{}catch(e){}finally{}\nfoo()', + output: 'try{}catch(e){}finally{}\n\nfoo()', + options: [{ blankLine: 'always', prev: 'try', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // var + //---------------------------------------------------------------------- + + { + code: 'var a\n\nfoo()', + output: 'var a\nfoo()', + options: [{ blankLine: 'never', prev: 'var', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'var a\nfoo()', + output: 'var a\n\nfoo()', + options: [{ blankLine: 'always', prev: 'var', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // while + //---------------------------------------------------------------------- + + { + code: 'while(a);\n\nfoo()', + output: 'while(a);\nfoo()', + options: [{ blankLine: 'never', prev: 'while', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'while(a);\nfoo()', + output: 'while(a);\n\nfoo()', + options: [{ blankLine: 'always', prev: 'while', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // with + //---------------------------------------------------------------------- + + { + code: 'with(a);\n\nfoo()', + output: 'with(a);\nfoo()', + options: [{ blankLine: 'never', prev: 'with', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'with(a);\nfoo()', + output: 'with(a);\n\nfoo()', + options: [{ blankLine: 'always', prev: 'with', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // multiline-const + //---------------------------------------------------------------------- + + { + code: 'const a={\nb:1,\nc:2\n}\n\nconst d=3', + output: 'const a={\nb:1,\nc:2\n}\nconst d=3', + options: [{ blankLine: 'never', prev: 'multiline-const', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'const a={\nb:1,\nc:2\n}\nconst d=3', + output: 'const a={\nb:1,\nc:2\n}\n\nconst d=3', + options: [{ blankLine: 'always', prev: 'multiline-const', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'const a=1\n\nconst b={\nc:2,\nd:3\n}', + output: 'const a=1\nconst b={\nc:2,\nd:3\n}', + options: [{ blankLine: 'never', prev: '*', next: 'multiline-const' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'const a=1\nconst b={\nc:2,\nd:3\n}', + output: 'const a=1\n\nconst b={\nc:2,\nd:3\n}', + options: [{ blankLine: 'always', prev: '*', next: 'multiline-const' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // multiline-let + //---------------------------------------------------------------------- + + { + code: 'let a={\nb:1,\nc:2\n}\n\nlet d=3', + output: 'let a={\nb:1,\nc:2\n}\nlet d=3', + options: [{ blankLine: 'never', prev: 'multiline-let', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'let a={\nb:1,\nc:2\n}\nlet d=3', + output: 'let a={\nb:1,\nc:2\n}\n\nlet d=3', + options: [{ blankLine: 'always', prev: 'multiline-let', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'let a=1\n\nlet b={\nc:2,\nd:3\n}', + output: 'let a=1\nlet b={\nc:2,\nd:3\n}', + options: [{ blankLine: 'never', prev: '*', next: 'multiline-let' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'let a=1\nlet b={\nc:2,\nd:3\n}', + output: 'let a=1\n\nlet b={\nc:2,\nd:3\n}', + options: [{ blankLine: 'always', prev: '*', next: 'multiline-let' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // multiline-var + //---------------------------------------------------------------------- + + { + code: 'var a={\nb:1,\nc:2\n}\n\nvar d=3', + output: 'var a={\nb:1,\nc:2\n}\nvar d=3', + options: [{ blankLine: 'never', prev: 'multiline-var', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'var a={\nb:1,\nc:2\n}\nvar d=3', + output: 'var a={\nb:1,\nc:2\n}\n\nvar d=3', + options: [{ blankLine: 'always', prev: 'multiline-var', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'var a=1\n\nvar b={\nc:2,\nd:3\n}', + output: 'var a=1\nvar b={\nc:2,\nd:3\n}', + options: [{ blankLine: 'never', prev: '*', next: 'multiline-var' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'var a=1\nvar b={\nc:2,\nd:3\n}', + output: 'var a=1\n\nvar b={\nc:2,\nd:3\n}', + options: [{ blankLine: 'always', prev: '*', next: 'multiline-var' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // single line const + //---------------------------------------------------------------------- + + { + code: 'const a=1\n\nconst b=2', + output: 'const a=1\nconst b=2', + options: [{ blankLine: 'never', prev: 'singleline-const', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'const a=1\nconst b=2', + output: 'const a=1\n\nconst b=2', + options: [{ blankLine: 'always', prev: 'singleline-const', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'const a=1\n\nconst b=2', + output: 'const a=1\nconst b=2', + options: [{ blankLine: 'never', prev: '*', next: 'singleline-const' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'const a=1\nconst b=2', + output: 'const a=1\n\nconst b=2', + options: [{ blankLine: 'always', prev: '*', next: 'singleline-const' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // single line let + //---------------------------------------------------------------------- + + { + code: 'let a=1\n\nlet b=2', + output: 'let a=1\nlet b=2', + options: [{ blankLine: 'never', prev: 'singleline-let', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'let a=1\nlet b=2', + output: 'let a=1\n\nlet b=2', + options: [{ blankLine: 'always', prev: 'singleline-let', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'let a=1\n\nlet b=2', + output: 'let a=1\nlet b=2', + options: [{ blankLine: 'never', prev: '*', next: 'singleline-let' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'let a=1\nlet b=2', + output: 'let a=1\n\nlet b=2', + options: [{ blankLine: 'always', prev: '*', next: 'singleline-let' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // single line var + //---------------------------------------------------------------------- + + { + code: 'var a=1\n\nvar b=2', + output: 'var a=1\nvar b=2', + options: [{ blankLine: 'never', prev: 'singleline-var', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'var a=1\nvar b=2', + output: 'var a=1\n\nvar b=2', + options: [{ blankLine: 'always', prev: 'singleline-var', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'var a=1\n\nvar b=2', + output: 'var a=1\nvar b=2', + options: [{ blankLine: 'never', prev: '*', next: 'singleline-var' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'var a=1\nvar b=2', + output: 'var a=1\n\nvar b=2', + options: [{ blankLine: 'always', prev: '*', next: 'singleline-var' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // Tests from newline-after-var + //---------------------------------------------------------------------- + + // should disallow no line break in "always" mode + { + code: "var greet = 'hello';console.log(greet);", + output: "var greet = 'hello';\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello';var name = 'world';console.log(greet, name);", + output: + "var greet = 'hello';var name = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello', name = 'world';console.log(greet, name);", + output: + "var greet = 'hello', name = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + // should disallow no blank line in "always" mode + { + code: "var greet = 'hello';\nconsole.log(greet);", + output: "var greet = 'hello';\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello'; \nconsole.log(greet);", + output: "var greet = 'hello';\n \nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello'; // inline comment\nconsole.log(greet);", + output: "var greet = 'hello'; // inline comment\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello';\nvar name = 'world';\nconsole.log(greet, name);", + output: + "var greet = 'hello';\nvar name = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello', name = 'world';\nconsole.log(greet, name);", + output: + "var greet = 'hello', name = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello',\nname = 'world';\nconsole.log(greet, name);", + output: + "var greet = 'hello',\nname = 'world';\n\nconsole.log(greet, name);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "let greet = 'hello';\nconsole.log(greet);", + output: "let greet = 'hello';\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "const greet = 'hello';\nconsole.log(greet);", + output: "const greet = 'hello';\n\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "function example() {\nvar greet = 'hello';\nconsole.log(greet);\n}", + output: + "function example() {\nvar greet = 'hello';\n\nconsole.log(greet);\n}", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var f = function() {\nvar greet = 'hello';\nconsole.log(greet);\n};", + output: + "var f = function() {\nvar greet = 'hello';\n\nconsole.log(greet);\n};", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "() => {\nvar greet = 'hello';\nconsole.log(greet);\n}", + output: "() => {\nvar greet = 'hello';\n\nconsole.log(greet);\n}", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + // should disallow blank lines in "never" mode + { + code: "var greet = 'hello';\n\nconsole.log(greet);", + output: "var greet = 'hello';\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "var greet = 'hello';\n\n\nconsole.log(greet);", + output: "var greet = 'hello';\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "var greet = 'hello';\n\n\n\nconsole.log(greet);", + output: "var greet = 'hello';\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "var greet = 'hello'; \n\nconsole.log(greet);", + output: "var greet = 'hello'; \nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "var greet = 'hello'; // inline comment\n\nconsole.log(greet);", + output: "var greet = 'hello'; // inline comment\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "var greet = 'hello';\nvar name = 'world';\n\nconsole.log(greet, name);", + output: + "var greet = 'hello';\nvar name = 'world';\nconsole.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "var greet = 'hello', name = 'world';\n\nconsole.log(greet, name);", + output: "var greet = 'hello', name = 'world';\nconsole.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "var greet = 'hello',\nname = 'world';\n\nconsole.log(greet, name);", + output: + "var greet = 'hello',\nname = 'world';\nconsole.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "var greet = 'hello', // inline comment\nname = 'world'; // inline comment\n\nconsole.log(greet, name);", + output: + "var greet = 'hello', // inline comment\nname = 'world'; // inline comment\nconsole.log(greet, name);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "let greet = 'hello';\n\nconsole.log(greet);", + output: "let greet = 'hello';\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: "const greet = 'hello';\n\nconsole.log(greet);", + output: "const greet = 'hello';\nconsole.log(greet);", + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + + // should disallow a comment on the next line that's not in turn followed by a blank in "always" mode + { + code: "var greet = 'hello';\n// next-line comment\nconsole.log(greet);", + output: + "var greet = 'hello';\n\n// next-line comment\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello';\n/* block comment\nblock comment */\nconsole.log(greet);", + output: + "var greet = 'hello';\n\n/* block comment\nblock comment */\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello',\nname = 'world';\n// next-line comment\nconsole.log(greet);", + output: + "var greet = 'hello',\nname = 'world';\n\n// next-line comment\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello',\nname = 'world';\n/* block comment\nblock comment */\nconsole.log(greet);", + output: + "var greet = 'hello',\nname = 'world';\n\n/* block comment\nblock comment */\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello';\n// next-line comment\n// second-line comment\nconsole.log(greet);", + output: + "var greet = 'hello';\n\n// next-line comment\n// second-line comment\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: "var greet = 'hello';\n// next-line comment\n/* block comment\nblock comment */\nconsole.log(greet);", + output: + "var greet = 'hello';\n\n// next-line comment\n/* block comment\nblock comment */\nconsole.log(greet);", + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + // https://github.com/eslint/eslint/issues/6834 + { + code: ` +var a = 1 +;(b || c).doSomething() + `, + output: ` +var a = 1 + +;(b || c).doSomething() + `, + options: [ + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: ` +var a = 1 + +;(b || c).doSomething() + `, + output: ` +var a = 1 +;(b || c).doSomething() + `, + options: [ + { blankLine: 'never', prev: ['const', 'let', 'var'], next: '*' }, + { + blankLine: 'any', + prev: ['const', 'let', 'var'], + next: ['const', 'let', 'var'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // Tests from newline-before-return + //---------------------------------------------------------------------- + + { + code: 'function a() {\nvar b; return;\n}', + output: 'function a() {\nvar b;\n\n return;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b;\nreturn;\n}', + output: 'function a() {\nvar b;\n\nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nif (b) return b;\nelse if (c) return c;\nelse {\ne();\nreturn d;\n}\n}', + output: + 'function a() {\nif (b) return b;\nelse if (c) return c;\nelse {\ne();\n\nreturn d;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nif (b) return b;\nelse if (c) return c;\nelse {\ne(); return d;\n}\n}', + output: + 'function a() {\nif (b) return b;\nelse if (c) return c;\nelse {\ne();\n\n return d;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\n while (b) {\nc();\nreturn;\n}\n}', + output: 'function a() {\n while (b) {\nc();\n\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\ndo {\nc();\nreturn;\n} while (b);\n}', + output: 'function a() {\ndo {\nc();\n\nreturn;\n} while (b);\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nfor (var b; b < c; b++) {\nc();\nreturn;\n}\n}', + output: + 'function a() {\nfor (var b; b < c; b++) {\nc();\n\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nfor (b in c) {\nd();\nreturn;\n}\n}', + output: 'function a() {\nfor (b in c) {\nd();\n\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nfor (b of c) {\nd();\nreturn;\n}\n}', + output: 'function a() {\nfor (b of c) {\nd();\n\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nif (b) {\nc();\n}\n//comment\nreturn b;\n}', + output: 'function a() {\nif (b) {\nc();\n}\n\n//comment\nreturn b;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\n/*comment\ncomment*/\nif (b) {\nc();\nreturn b;\n} else {\n//comment\n\nreturn d;\n}\n/*multi-line\ncomment*/\nreturn e;\n}', + output: + 'function a() {\n/*comment\ncomment*/\nif (b) {\nc();\n\nreturn b;\n} else {\n//comment\n\nreturn d;\n}\n\n/*multi-line\ncomment*/\nreturn e;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [ + { messageId: 'expectedBlankLine' }, + { messageId: 'expectedBlankLine' }, + ], + }, + { + code: 'function a() {\nif (b) { return; } //comment\nreturn c;\n}', + output: 'function a() {\nif (b) { return; } //comment\n\nreturn c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nif (b) { return; } /*multi-line\ncomment*/\nreturn c;\n}', + output: + 'function a() {\nif (b) { return; } /*multi-line\ncomment*/\n\nreturn c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nif (b) { return; }\n/*multi-line\ncomment*/ return c;\n}', + output: + 'function a() {\nif (b) { return; }\n\n/*multi-line\ncomment*/ return c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nif (b) { return; } /*multi-line\ncomment*/ return c;\n}', + output: + 'function a() {\nif (b) { return; } /*multi-line\ncomment*/\n\n return c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'var a;\nreturn;', + output: 'var a;\n\nreturn;', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + parserOptions: { ecmaFeatures: { globalReturn: true } }, + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'var a; return;', + output: 'var a;\n\n return;', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + parserOptions: { ecmaFeatures: { globalReturn: true } }, + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\n{\n//comment\n}\nreturn\n}', + output: 'function a() {\n{\n//comment\n}\n\nreturn\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\n{\n//comment\n} return\n}', + output: 'function a() {\n{\n//comment\n}\n\n return\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar c;\nwhile (b) {\n c = d; //comment\n}\nreturn c;\n}', + output: + 'function a() {\nvar c;\nwhile (b) {\n c = d; //comment\n}\n\nreturn c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nfor (var b; b < c; b++) {\nif (d) {\nbreak; //comment\n}\nreturn;\n}\n}', + output: + 'function a() {\nfor (var b; b < c; b++) {\nif (d) {\nbreak; //comment\n}\n\nreturn;\n}\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b; /*multi-line\ncomment*/\nreturn c;\n}', + output: 'function a() {\nvar b; /*multi-line\ncomment*/\n\nreturn c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b;\n/*multi-line\ncomment*/ return c;\n}', + output: 'function a() {\nvar b;\n\n/*multi-line\ncomment*/ return c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b; /*multi-line\ncomment*/ return c;\n}', + output: 'function a() {\nvar b; /*multi-line\ncomment*/\n\n return c;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b;\n//comment\nreturn;\n}', + output: 'function a() {\nvar b;\n\n//comment\nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b; //comment\nreturn;\n}', + output: 'function a() {\nvar b; //comment\n\nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b;\n/* comment */ return;\n}', + output: 'function a() {\nvar b;\n\n/* comment */ return;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b;\n//comment\n/* comment */ return;\n}', + output: 'function a() {\nvar b;\n\n//comment\n/* comment */ return;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b; /* comment */ return;\n}', + output: 'function a() {\nvar b; /* comment */\n\n return;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b; /* comment */\nreturn;\n}', + output: 'function a() {\nvar b; /* comment */\n\nreturn;\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b;\nreturn; //comment\n}', + output: 'function a() {\nvar b;\n\nreturn; //comment\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function a() {\nvar b; return; //comment\n}', + output: 'function a() {\nvar b;\n\n return; //comment\n}', + options: [{ blankLine: 'always', prev: '*', next: 'return' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // From JSCS disallowPaddingNewLinesAfterBlocks + // https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/test/specs/rules/disallow-padding-newlines-after-blocks.js + //---------------------------------------------------------------------- + + { + code: 'if(true){}\n\nvar a = 2;', + output: 'if(true){}\nvar a = 2;', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'if(true){\nif(true) {}\n\nvar a = 2;}', + output: 'if(true){\nif(true) {}\nvar a = 2;}', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: '(function(){\n})()\n\nvar a = 2;', + output: '(function(){\n})()\nvar a = 2;', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: '+(function(){\n})()\n\nvar a = 2;', + output: '+(function(){\n})()\nvar a = 2;', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'var a = function() {};\n\nvar b = 2;', + output: 'var a = function() {};\nvar b = 2;', + options: [{ blankLine: 'never', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // From JSCS disallowPaddingNewLinesBeforeExport + // https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/test/specs/rules/disallow-padding-newlines-before-export.js + //---------------------------------------------------------------------- + + { + code: 'var a = 2;\n\nmodule.exports = a;', + output: 'var a = 2;\nmodule.exports = a;', + options: [{ blankLine: 'never', prev: '*', next: 'exports' }], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // From JSCS disallowPaddingNewLinesBeforeExport + // https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/test/specs/rules/disallow-padding-newlines-before-keywords.js + //---------------------------------------------------------------------- + + { + code: 'function x() { var a;\n\nreturn; }', + output: 'function x() { var a;\nreturn; }', + options: [ + { + blankLine: 'never', + prev: '*', + next: ['if', 'for', 'return', 'switch', 'case', 'break', 'throw'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'function x() { var a = true;\n\nif (a) { a = !a; }; }', + output: 'function x() { var a = true;\nif (a) { a = !a; }; }', + options: [ + { + blankLine: 'never', + prev: '*', + next: ['if', 'for', 'return', 'switch', 'case', 'break', 'throw'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'function x() { var a = true;\n\nfor (var i = 0; i < 10; i++) { a = !a; }; }', + output: + 'function x() { var a = true;\nfor (var i = 0; i < 10; i++) { a = !a; }; }', + options: [ + { + blankLine: 'never', + prev: '*', + next: ['if', 'for', 'return', 'switch', 'case', 'break', 'throw'], + }, + ], + errors: [{ messageId: 'unexpectedBlankLine' }], + }, + { + code: 'function x() { var y = true;\n\nswitch ("Oranges") { case "Oranges": y = !y;\n\nbreak;\n\ncase "Apples": y = !y;\n\nbreak; default: y = !y; } }', + output: + 'function x() { var y = true;\nswitch ("Oranges") { case "Oranges": y = !y;\nbreak;\ncase "Apples": y = !y;\nbreak; default: y = !y; } }', + options: [ + { + blankLine: 'never', + prev: '*', + next: ['if', 'for', 'return', 'switch', 'case', 'break', 'throw'], + }, + ], + errors: [ + { messageId: 'unexpectedBlankLine' }, + { messageId: 'unexpectedBlankLine' }, + { messageId: 'unexpectedBlankLine' }, + { messageId: 'unexpectedBlankLine' }, + ], + }, + { + code: 'function x() {try { var a;\n\nthrow 0; } catch (e) { var b = 0;\n\nthrow e; } }', + output: + 'function x() {try { var a;\nthrow 0; } catch (e) { var b = 0;\nthrow e; } }', + options: [ + { + blankLine: 'never', + prev: '*', + next: ['if', 'for', 'return', 'switch', 'case', 'break', 'throw'], + }, + ], + errors: [ + { messageId: 'unexpectedBlankLine' }, + { messageId: 'unexpectedBlankLine' }, + ], + }, + { + code: 'function x(a) { var b = 0;\n\nif (!a) { return false; };\n\nfor (var i = 0; i < b; i++) { if (!a[i]) return false; }\n\nreturn true; }', + output: + 'function x(a) { var b = 0;\nif (!a) { return false; };\nfor (var i = 0; i < b; i++) { if (!a[i]) return false; }\nreturn true; }', + options: [ + { + blankLine: 'never', + prev: '*', + next: ['if', 'for', 'return', 'switch', 'case', 'break', 'throw'], + }, + ], + errors: [ + { messageId: 'unexpectedBlankLine' }, + { messageId: 'unexpectedBlankLine' }, + { messageId: 'unexpectedBlankLine' }, + ], + }, + + //---------------------------------------------------------------------- + // From JSCS requirePaddingNewLinesAfterBlocks + // https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/test/specs/rules/require-padding-newlines-after-blocks.js + //---------------------------------------------------------------------- + + { + code: 'if(true){}\nvar a = 2;', + output: 'if(true){}\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'var a = function() {\n};\nvar b = 2;', + output: 'var a = function() {\n};\n\nvar b = 2;', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'if(true){\nif(true) {}\nvar a = 2;}', + output: 'if(true){\nif(true) {}\n\nvar a = 2;}', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: '(function(){\n})()\nvar a = 2;', + output: '(function(){\n})()\n\nvar a = 2;', + options: [{ blankLine: 'always', prev: 'block-like', next: '*' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'var a = function() {\n};\nvar b = 2;', + output: 'var a = function() {\n};\n\nvar b = 2;', + options: [ + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: '(function(){\n})()\nvar a = 2;', + output: '(function(){\n})()\n\nvar a = 2;', + options: [ + { blankLine: 'always', prev: 'multiline-block-like', next: '*' }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // From JSCS requirePaddingNewLinesBeforeExport + // https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/test/specs/rules/require-padding-newlines-before-export.js + //---------------------------------------------------------------------- + + { + code: 'var a = 2;\nmodule.exports = a;', + output: 'var a = 2;\n\nmodule.exports = a;', + options: [{ blankLine: 'always', prev: '*', next: 'exports' }], + errors: [{ messageId: 'expectedBlankLine' }], + }, + + //---------------------------------------------------------------------- + // From JSCS requirePaddingNewlinesBeforeKeywords + // https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/test/specs/rules/require-padding-newlines-before-keywords.js + //---------------------------------------------------------------------- + + { + code: 'function x() { var a; return; }', + output: 'function x() { var a;\n\n return; }', + options: [ + { + blankLine: 'always', + prev: '*', + next: [ + 'if', + 'for', + 'return', + 'switch', + 'case', + 'break', + 'throw', + 'while', + 'default', + ], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function x() { var a = true; for (var i = 0; i < 10; i++) { a = !a; }; }', + output: + 'function x() { var a = true;\n\n for (var i = 0; i < 10; i++) { a = !a; }; }', + options: [ + { + blankLine: 'always', + prev: '*', + next: [ + 'if', + 'for', + 'return', + 'switch', + 'case', + 'break', + 'throw', + 'while', + 'default', + ], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function x() { var y = true; switch ("Oranges") { case "Oranges": y = !y; break; case "Apples": y = !y; break; default: y = !y; } }', + output: + 'function x() { var y = true;\n\n switch ("Oranges") { case "Oranges": y = !y;\n\n break;\n\n case "Apples": y = !y;\n\n break;\n\n default: y = !y; } }', + options: [ + { + blankLine: 'always', + prev: '*', + next: [ + 'if', + 'for', + 'return', + 'switch', + 'case', + 'break', + 'throw', + 'while', + 'default', + ], + }, + ], + errors: [ + { messageId: 'expectedBlankLine' }, + { messageId: 'expectedBlankLine' }, + { messageId: 'expectedBlankLine' }, + { messageId: 'expectedBlankLine' }, + { messageId: 'expectedBlankLine' }, + ], + }, + { + code: 'function x() { var a = true; while (!a) { a = !a; }; }', + output: 'function x() { var a = true;\n\n while (!a) { a = !a; }; }', + options: [ + { + blankLine: 'always', + prev: '*', + next: [ + 'if', + 'for', + 'return', + 'switch', + 'case', + 'break', + 'throw', + 'while', + 'default', + ], + }, + ], + errors: [{ messageId: 'expectedBlankLine' }], + }, + { + code: 'function x() {try { var a; throw 0; } catch (e) { var b = 0; throw e; } }', + output: + 'function x() {try { var a;\n\n throw 0; } catch (e) { var b = 0;\n\n throw e; } }', + options: [ + { + blankLine: 'always', + prev: '*', + next: [ + 'if', + 'for', + 'return', + 'switch', + 'case', + 'break', + 'throw', + 'while', + 'default', + ], + }, + ], + errors: [ + { messageId: 'expectedBlankLine' }, + { messageId: 'expectedBlankLine' }, + ], + }, + { + code: 'function x(a) { var b = 0; if (!a) { return false; }; for (var i = 0; i < b; i++) { if (!a[i]) return false; } return true; }', + output: + 'function x(a) { var b = 0;\n\n if (!a) { return false; };\n\n for (var i = 0; i < b; i++) { if (!a[i]) return false; }\n\n return true; }', + options: [ + { + blankLine: 'always', + prev: '*', + next: [ + 'if', + 'for', + 'return', + 'switch', + 'case', + 'break', + 'throw', + 'while', + 'default', + ], + }, + ], + errors: [ + { messageId: 'expectedBlankLine' }, + { messageId: 'expectedBlankLine' }, + { messageId: 'expectedBlankLine' }, + ], + }, + ], +}); diff --git a/packages/eslint-plugin/tests/rules/prefer-readonly-parameter-types.test.ts b/packages/eslint-plugin/tests/rules/prefer-readonly-parameter-types.test.ts index 9278420a7647..0a6ee1038073 100644 --- a/packages/eslint-plugin/tests/rules/prefer-readonly-parameter-types.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-readonly-parameter-types.test.ts @@ -154,6 +154,74 @@ ruleTester.run('prefer-readonly-parameter-types', rule, { } function foo(arg: Readonly) {} `, + // immutable methods + ` + type MyType = Readonly<{ + prop: string; + method(): string; + }>; + function foo(arg: MyType) {} + `, + ` + type MyType = { + readonly prop: string; + readonly method: () => string; + }; + function bar(arg: MyType) {} + `, + // methods treated as readonly + { + code: ` + type MyType = { + readonly prop: string; + method(): string; + }; + function foo(arg: MyType) {} + `, + options: [ + { + treatMethodsAsReadonly: true, + }, + ], + }, + { + code: ` + class Foo { + method() {} + } + function foo(arg: Foo) {} + `, + options: [ + { + treatMethodsAsReadonly: true, + }, + ], + }, + { + code: ` + interface Foo { + method(): void; + } + function foo(arg: Foo) {} + `, + options: [ + { + treatMethodsAsReadonly: true, + }, + ], + }, + // ReadonlySet and ReadonlyMap are seen as readonly when methods are treated as readonly + { + code: ` + function foo(arg: ReadonlySet) {} + function bar(arg: ReadonlyMap) {} + `, + options: [ + { + treatMethodsAsReadonly: true, + }, + ], + }, // parameter properties should work fine { @@ -715,5 +783,23 @@ ruleTester.run('prefer-readonly-parameter-types', rule, { }, ], }, + // Mutable methods. + { + code: ` + type MyType = { + readonly prop: string; + method(): string; + }; + function foo(arg: MyType) {} + `, + errors: [ + { + messageId: 'shouldBeReadonly', + line: 6, + column: 22, + endColumn: 33, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts index df5d5aefb259..fea8b82501ae 100644 --- a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts @@ -189,11 +189,56 @@ ruleTester.run('restrict-template-expressions', rule, { } `, }, + // allowRegExp + { + options: [{ allowRegExp: true }], + code: ` + const arg = new RegExp('foo'); + const msg = \`arg = \${arg}\`; + `, + }, + { + options: [{ allowRegExp: true }], + code: ` + const arg = /foo/; + const msg = \`arg = \${arg}\`; + `, + }, + { + options: [{ allowRegExp: true }], + code: ` + declare const arg: string | RegExp; + const msg = \`arg = \${arg}\`; + `, + }, + { + options: [{ allowRegExp: true }], + code: ` + function test(arg: T) { + return \`arg = \${arg}\`; + } + `, + }, + { + options: [{ allowRegExp: true }], + code: ` + function test(arg: T) { + return \`arg = \${arg}\`; + } + `, + }, // allow ALL { - options: [{ allowNumber: true, allowBoolean: true, allowNullish: true }], + options: [ + { + allowNumber: true, + allowBoolean: true, + allowNullish: true, + allowRegExp: true, + }, + ], code: ` - type All = string | number | boolean | null | undefined; + type All = string | number | boolean | null | undefined | RegExp; function test(arg: T) { return \`arg = \${arg}\`; } @@ -338,6 +383,36 @@ ruleTester.run('restrict-template-expressions', rule, { }, ], }, + { + options: [{ allowRegExp: false }], + code: ` + const arg = new RegExp('foo'); + const msg = \`arg = \${arg}\`; + `, + errors: [ + { + messageId: 'invalidType', + data: { type: 'RegExp' }, + line: 3, + column: 30, + }, + ], + }, + { + options: [{ allowRegExp: false }], + code: ` + const arg = /foo/; + const msg = \`arg = \${arg}\`; + `, + errors: [ + { + messageId: 'invalidType', + data: { type: 'RegExp' }, + line: 3, + column: 30, + }, + ], + }, // TS 3.9 change { options: [{ allowAny: true }], diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md index 6c7d3f4676f0..4124025771ad 100644 --- a/packages/experimental-utils/CHANGELOG.md +++ b/packages/experimental-utils/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + + +### Bug Fixes + +* **utils:** support immutable arrays in `ReportFixFunction` ([#3830](https://github.com/typescript-eslint/typescript-eslint/issues/3830)) ([8218055](https://github.com/typescript-eslint/typescript-eslint/commit/8218055d6dfd94c9e6c8645848f981d9d51ce08c)) + + +### Features + +* **experimental-utils:** extract `isNodeOfType` out of `ast-utils`' `predicates` ([#3677](https://github.com/typescript-eslint/typescript-eslint/issues/3677)) ([4bfa437](https://github.com/typescript-eslint/typescript-eslint/commit/4bfa4375aff8f65057d4aa116e435803cbc6b464)) + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index 779d7f1e61b6..911ef7112905 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/experimental-utils", - "version": "4.30.0", + "version": "4.31.0", "description": "(Experimental) Utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -40,9 +40,9 @@ }, "dependencies": { "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.30.0", - "@typescript-eslint/types": "4.30.0", - "@typescript-eslint/typescript-estree": "4.30.0", + "@typescript-eslint/scope-manager": "4.31.0", + "@typescript-eslint/types": "4.31.0", + "@typescript-eslint/typescript-estree": "4.31.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, diff --git a/packages/experimental-utils/src/ast-utils/predicates.ts b/packages/experimental-utils/src/ast-utils/predicates.ts index 919820d7653d..7a7bcf520b1a 100644 --- a/packages/experimental-utils/src/ast-utils/predicates.ts +++ b/packages/experimental-utils/src/ast-utils/predicates.ts @@ -1,5 +1,12 @@ import { AST_NODE_TYPES, AST_TOKEN_TYPES, TSESTree } from '../ts-estree'; +const isNodeOfType = + (nodeType: NodeType) => + ( + node: TSESTree.Node | null | undefined, + ): node is TSESTree.Node & { type: NodeType } => + node?.type === nodeType; + function isOptionalChainPunctuator( token: TSESTree.Token, ): token is TSESTree.PunctuatorToken & { value: '?.' } { @@ -69,11 +76,7 @@ function isTypeAssertion( ); } -function isVariableDeclarator( - node: TSESTree.Node | undefined, -): node is TSESTree.VariableDeclarator { - return node?.type === AST_NODE_TYPES.VariableDeclarator; -} +const isVariableDeclarator = isNodeOfType(AST_NODE_TYPES.VariableDeclarator); function isFunction( node: TSESTree.Node | undefined, @@ -130,17 +133,9 @@ function isFunctionOrFunctionType( return isFunction(node) || isFunctionType(node); } -function isTSFunctionType( - node: TSESTree.Node | undefined, -): node is TSESTree.TSFunctionType { - return node?.type === AST_NODE_TYPES.TSFunctionType; -} +const isTSFunctionType = isNodeOfType(AST_NODE_TYPES.TSFunctionType); -function isTSConstructorType( - node: TSESTree.Node | undefined, -): node is TSESTree.TSConstructorType { - return node?.type === AST_NODE_TYPES.TSConstructorType; -} +const isTSConstructorType = isNodeOfType(AST_NODE_TYPES.TSConstructorType); function isClassOrTypeElement( node: TSESTree.Node | undefined, @@ -193,20 +188,12 @@ function isSetter( ); } -function isIdentifier( - node: TSESTree.Node | undefined, -): node is TSESTree.Identifier { - return node?.type === AST_NODE_TYPES.Identifier; -} +const isIdentifier = isNodeOfType(AST_NODE_TYPES.Identifier); /** * Checks if a node represents an `await …` expression. */ -function isAwaitExpression( - node: TSESTree.Node | undefined | null, -): node is TSESTree.AwaitExpression { - return node?.type === AST_NODE_TYPES.AwaitExpression; -} +const isAwaitExpression = isNodeOfType(AST_NODE_TYPES.AwaitExpression); /** * Checks if a possible token is the `await` keyword. diff --git a/packages/experimental-utils/src/ts-eslint/Rule.ts b/packages/experimental-utils/src/ts-eslint/Rule.ts index f813b9d17309..24b8fd4fa114 100644 --- a/packages/experimental-utils/src/ts-eslint/Rule.ts +++ b/packages/experimental-utils/src/ts-eslint/Rule.ts @@ -114,7 +114,7 @@ interface RuleFixer { type ReportFixFunction = ( fixer: RuleFixer, -) => null | RuleFix | RuleFix[] | IterableIterator; +) => null | RuleFix | readonly RuleFix[] | IterableIterator; type ReportSuggestionArray = ReportDescriptorBase[]; diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index 5f6f603f8148..42187282c959 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + +**Note:** Version bump only for package @typescript-eslint/parser + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) diff --git a/packages/parser/package.json b/packages/parser/package.json index 5e802a17872b..b5847320a1b0 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "4.30.0", + "version": "4.31.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -44,14 +44,14 @@ "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" }, "dependencies": { - "@typescript-eslint/scope-manager": "4.30.0", - "@typescript-eslint/types": "4.30.0", - "@typescript-eslint/typescript-estree": "4.30.0", + "@typescript-eslint/scope-manager": "4.31.0", + "@typescript-eslint/types": "4.31.0", + "@typescript-eslint/typescript-estree": "4.31.0", "debug": "^4.3.1" }, "devDependencies": { "@types/glob": "*", - "@typescript-eslint/experimental-utils": "4.30.0", + "@typescript-eslint/experimental-utils": "4.31.0", "glob": "*", "typescript": "*" }, diff --git a/packages/scope-manager/CHANGELOG.md b/packages/scope-manager/CHANGELOG.md index 23932d879409..f7221c1ec4c5 100644 --- a/packages/scope-manager/CHANGELOG.md +++ b/packages/scope-manager/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + +**Note:** Version bump only for package @typescript-eslint/scope-manager + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) diff --git a/packages/scope-manager/package.json b/packages/scope-manager/package.json index 63d3a69cecbb..bf054505fe23 100644 --- a/packages/scope-manager/package.json +++ b/packages/scope-manager/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/scope-manager", - "version": "4.30.0", + "version": "4.31.0", "description": "TypeScript scope analyser for ESLint", "keywords": [ "eslint", @@ -39,12 +39,12 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "4.30.0", - "@typescript-eslint/visitor-keys": "4.30.0" + "@typescript-eslint/types": "4.31.0", + "@typescript-eslint/visitor-keys": "4.31.0" }, "devDependencies": { "@types/glob": "*", - "@typescript-eslint/typescript-estree": "4.30.0", + "@typescript-eslint/typescript-estree": "4.31.0", "glob": "*", "jest-specific-snapshot": "*", "make-dir": "*", diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md index 52a786b215ff..8c0f70d151ad 100644 --- a/packages/shared-fixtures/CHANGELOG.md +++ b/packages/shared-fixtures/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + +**Note:** Version bump only for package @typescript-eslint/shared-fixtures + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json index b5610cdd4378..83939580e4ea 100644 --- a/packages/shared-fixtures/package.json +++ b/packages/shared-fixtures/package.json @@ -1,5 +1,5 @@ { "name": "@typescript-eslint/shared-fixtures", - "version": "4.30.0", + "version": "4.31.0", "private": true } diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index cb86be96ec30..50538a768c59 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + +**Note:** Version bump only for package @typescript-eslint/types + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) **Note:** Version bump only for package @typescript-eslint/types diff --git a/packages/types/package.json b/packages/types/package.json index 6e23a521e748..18aba8b01bcd 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/types", - "version": "4.30.0", + "version": "4.31.0", "description": "Types for the TypeScript-ESTree AST spec", "keywords": [ "eslint", diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index 94a9165463b9..7406d424d1ed 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + +**Note:** Version bump only for package @typescript-eslint/typescript-estree + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) diff --git a/packages/typescript-estree/README.md b/packages/typescript-estree/README.md index 9cbcb2727cfc..605836b3c692 100644 --- a/packages/typescript-estree/README.md +++ b/packages/typescript-estree/README.md @@ -209,11 +209,11 @@ interface ParseAndGenerateServicesOptions extends ParseOptions { tsconfigRootDir?: string; /** - * Instance of a TypeScript Program object to be used for type information. + * An array of one or more instances of TypeScript Program objects to be used for type information. * This overrides any program or programs that would have been computed from the `project` option. - * All linted files must be part of the provided program. + * All linted files must be part of the provided program(s). */ - program?: import('typescript').Program; + programs?: Program[]; /** *************************************************************************************** @@ -333,7 +333,7 @@ Types for the AST produced by the parse functions. #### `createProgram(configFile, projectDirectory)` -This serves as a utility method for users of the `ParseOptions.program` feature to create a TypeScript program instance from a config file. +This serves as a utility method for users of the `ParseOptions.programs` feature to create a TypeScript program instance from a config file. ```ts declare function createProgram( diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index 4366789dfa39..dbe7db1d75f0 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "4.30.0", + "version": "4.31.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -41,8 +41,8 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "4.30.0", - "@typescript-eslint/visitor-keys": "4.30.0", + "@typescript-eslint/types": "4.31.0", + "@typescript-eslint/visitor-keys": "4.31.0", "debug": "^4.3.1", "globby": "^11.0.3", "is-glob": "^4.0.1", @@ -59,7 +59,7 @@ "@types/is-glob": "*", "@types/semver": "*", "@types/tmp": "*", - "@typescript-eslint/shared-fixtures": "4.30.0", + "@typescript-eslint/shared-fixtures": "4.31.0", "glob": "*", "jest-specific-snapshot": "*", "make-dir": "*", diff --git a/packages/visitor-keys/CHANGELOG.md b/packages/visitor-keys/CHANGELOG.md index 8b1f5b13d4d0..6d2b3716988a 100644 --- a/packages/visitor-keys/CHANGELOG.md +++ b/packages/visitor-keys/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.31.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.30.0...v4.31.0) (2021-09-06) + +**Note:** Version bump only for package @typescript-eslint/visitor-keys + + + + + # [4.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.29.3...v4.30.0) (2021-08-30) diff --git a/packages/visitor-keys/package.json b/packages/visitor-keys/package.json index 90167ed5d1be..58334b5bc78c 100644 --- a/packages/visitor-keys/package.json +++ b/packages/visitor-keys/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/visitor-keys", - "version": "4.30.0", + "version": "4.31.0", "description": "Visitor keys used to help traverse the TypeScript-ESTree AST", "keywords": [ "eslint", @@ -38,7 +38,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "4.30.0", + "@typescript-eslint/types": "4.31.0", "eslint-visitor-keys": "^2.0.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 0b88e0d193b0..26293b720315 100644 --- a/yarn.lock +++ b/yarn.lock @@ -461,25 +461,25 @@ dependencies: chalk "^4.0.0" -"@cspell/cspell-bundled-dicts@^5.7.2": - version "5.7.2" - resolved "https://registry.yarnpkg.com/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-5.7.2.tgz#077264d9aeed15e33dda12dcbde441f0e77db63a" - integrity sha512-Vd//xmvA1YeTpyCBbt3fVjYKNbCI6tvS9/Cfjt8PmV91loysX4TWJnI+mRY+2Eltf4WfW6SAtbC7/YUWrvBgQA== +"@cspell/cspell-bundled-dicts@^5.9.0": + version "5.9.0" + resolved "https://registry.yarnpkg.com/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-5.9.0.tgz#4afef125ddedf13d43bb4114fdfcc3b42450e91e" + integrity sha512-qI/MVTYjGh0Uhhx2YiqCIodfazHojcwJx6yzNkQQXZN7px0rGUPsRPiPx83LuHl1aE+APc5x8cblCwnnZUbPxg== dependencies: "@cspell/dict-ada" "^1.1.2" "@cspell/dict-aws" "^1.0.14" "@cspell/dict-bash" "^1.0.15" "@cspell/dict-companies" "^1.0.40" - "@cspell/dict-cpp" "^1.1.39" + "@cspell/dict-cpp" "^1.1.40" "@cspell/dict-cryptocurrencies" "^1.0.10" "@cspell/dict-csharp" "^1.0.11" "@cspell/dict-css" "^1.0.12" "@cspell/dict-django" "^1.0.26" - "@cspell/dict-dotnet" "^1.0.29" + "@cspell/dict-dotnet" "^1.0.30" "@cspell/dict-elixir" "^1.0.25" - "@cspell/dict-en-gb" "^1.1.32" - "@cspell/dict-en_us" "^1.2.45" - "@cspell/dict-filetypes" "^1.1.7" + "@cspell/dict-en-gb" "^1.1.33" + "@cspell/dict-en_us" "^2.0.2" + "@cspell/dict-filetypes" "^1.1.8" "@cspell/dict-fonts" "^1.0.14" "@cspell/dict-fullstack" "^1.0.38" "@cspell/dict-golang" "^1.1.24" @@ -491,7 +491,7 @@ "@cspell/dict-lorem-ipsum" "^1.0.22" "@cspell/dict-lua" "^1.0.16" "@cspell/dict-node" "^1.0.12" - "@cspell/dict-npm" "^1.0.15" + "@cspell/dict-npm" "^1.0.16" "@cspell/dict-php" "^1.0.24" "@cspell/dict-powershell" "^1.0.18" "@cspell/dict-public-licenses" "^1.0.3" @@ -499,13 +499,13 @@ "@cspell/dict-ruby" "^1.0.14" "@cspell/dict-rust" "^1.0.23" "@cspell/dict-scala" "^1.0.21" - "@cspell/dict-software-terms" "^1.0.40" + "@cspell/dict-software-terms" "^1.0.41" "@cspell/dict-typescript" "^1.0.19" -"@cspell/cspell-types@^5.7.2": - version "5.7.2" - resolved "https://registry.yarnpkg.com/@cspell/cspell-types/-/cspell-types-5.7.2.tgz#93c2632cf8ce9ae98f55503380709b13da891b9c" - integrity sha512-hRInos9xQxPk8Wv0Y23UwFddN5RkyKVhqqH6kEeWir1sok2eMy58n/eIJvVPyhx1LVeP4QS4a/n6/OigUQrNVw== +"@cspell/cspell-types@^5.9.0": + version "5.9.0" + resolved "https://registry.yarnpkg.com/@cspell/cspell-types/-/cspell-types-5.9.0.tgz#1d74c2cb191f9dc7d899364a3ac366d00df181d8" + integrity sha512-S3O/4CNTTljQmWZpbBrG8IHPg9zWQ5rYpQ0tc9zoM1q/RzZev8xc55rggRu3IqtkwzyZUjVFo8apwQF5UZHXMg== "@cspell/dict-ada@^1.1.2": version "1.1.2" @@ -527,10 +527,10 @@ resolved "https://registry.yarnpkg.com/@cspell/dict-companies/-/dict-companies-1.0.40.tgz#edd7f47fc683dfa1b02cd48fb12ad732d2eece61" integrity sha512-Aw07qiTroqSST2P5joSrC4uOA05zTXzI2wMb+me3q4Davv1D9sCkzXY0TGoC2vzhNv5ooemRi9KATGaBSdU1sw== -"@cspell/dict-cpp@^1.1.39": - version "1.1.39" - resolved "https://registry.yarnpkg.com/@cspell/dict-cpp/-/dict-cpp-1.1.39.tgz#7e119e2c009f9200127733cbca3435180c405c70" - integrity sha512-zrQjzMaT5YqAa4PMEaLfOWnfyh4uJjW53kwtuTo9nfJPaga2+FfrqdeWD8XYMxvTGCtzjivXhAn4FDIMh+66YQ== +"@cspell/dict-cpp@^1.1.40": + version "1.1.40" + resolved "https://registry.yarnpkg.com/@cspell/dict-cpp/-/dict-cpp-1.1.40.tgz#f9a859e19d31b83f07a106e4c3c8720a2d93595b" + integrity sha512-sscfB3woNDNj60/yGXAdwNtIRWZ89y35xnIaJVDMk5TPMMpaDvuk0a34iOPIq0g4V+Y8e3RyAg71SH6ADwSjGw== "@cspell/dict-cryptocurrencies@^1.0.10": version "1.0.10" @@ -552,30 +552,30 @@ resolved "https://registry.yarnpkg.com/@cspell/dict-django/-/dict-django-1.0.26.tgz#b97ce0112fbe8c3c3ada0387c68971b5e27483ab" integrity sha512-mn9bd7Et1L2zuibc08GVHTiD2Go3/hdjyX5KLukXDklBkq06r+tb0OtKtf1zKodtFDTIaYekGADhNhA6AnKLkg== -"@cspell/dict-dotnet@^1.0.29": - version "1.0.29" - resolved "https://registry.yarnpkg.com/@cspell/dict-dotnet/-/dict-dotnet-1.0.29.tgz#e362a85b28c2862f49da7949a7cc347f872b0df4" - integrity sha512-41fx7YQM986MBAyJpqL0fH6WOKLG/tNev4NbydNy3avYxz/smr+VwIwGN9/GLNORL5hQLiSQxPU5jfpx+bN94g== +"@cspell/dict-dotnet@^1.0.30": + version "1.0.30" + resolved "https://registry.yarnpkg.com/@cspell/dict-dotnet/-/dict-dotnet-1.0.30.tgz#0e80294fe2894b65d059130115a54c33adcb76f8" + integrity sha512-86kC5191GACB95IGtLnmYHZjuNl/Ee7lvZRcwEyvktoYiRAryd1YKSX+c/qU1OEx7Y52FTaEl07tf9uYS1wKNQ== "@cspell/dict-elixir@^1.0.25": version "1.0.25" resolved "https://registry.yarnpkg.com/@cspell/dict-elixir/-/dict-elixir-1.0.25.tgz#bec4fd754c99f646e553184df12df88b54da1c04" integrity sha512-ZmawoBYjM5k+8fNudRMkK+PpHjhyAFAZt2rUu1EGj2rbCvE3Fn2lhRbDjbreN7nWRvcLRTW+xuPXtKP11X0ahQ== -"@cspell/dict-en-gb@^1.1.32": - version "1.1.32" - resolved "https://registry.yarnpkg.com/@cspell/dict-en-gb/-/dict-en-gb-1.1.32.tgz#587057d1cfac67aa3f2a950ec00ec3e33a239689" - integrity sha512-iu83IDyCefo0T4NipEa3jtpJ/WDSr1IWt35kljolj7HFDFrjLr4Y/2t8yqtmB4otturWpa5T/G3/k90y2KU08Q== +"@cspell/dict-en-gb@^1.1.33": + version "1.1.33" + resolved "https://registry.yarnpkg.com/@cspell/dict-en-gb/-/dict-en-gb-1.1.33.tgz#7f1fd90fc364a5cb77111b5438fc9fcf9cc6da0e" + integrity sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g== -"@cspell/dict-en_us@^1.2.45": - version "1.2.45" - resolved "https://registry.yarnpkg.com/@cspell/dict-en_us/-/dict-en_us-1.2.45.tgz#1314a9d81a1fd3cc7ed381dc6a0da10e7c2d02f9" - integrity sha512-UPwR4rfiJCxnS+Py+EK9E4AUj3aPZE4p/yBRSHN+5aBQConlI0lLDtMceH5wlupA/sQTU1ERZGPJA9L96jVSyQ== +"@cspell/dict-en_us@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@cspell/dict-en_us/-/dict-en_us-2.0.2.tgz#9087b99ef9efa9e77d1b2546feb454e4067cbaac" + integrity sha512-g6TL4ynWHoxVKvQ0kdiMCbh4+FeXwmHFTL/dWgQbyfy6cqKXjsOqPK0OPVFIbqI5rV8QeBx5IH6ny/q/XQOvXQ== -"@cspell/dict-filetypes@^1.1.7": - version "1.1.7" - resolved "https://registry.yarnpkg.com/@cspell/dict-filetypes/-/dict-filetypes-1.1.7.tgz#98c69f006041c145e3205c2f7fa617645a5b78ec" - integrity sha512-b0e+eiBzTiL1yJZgPBGHP8G7Z0Kkpr/35dXlR9LWoP/EnrAlVj0ulXwErPgTwSoFdxWBgbDJjKZsrMdxWCckuA== +"@cspell/dict-filetypes@^1.1.8": + version "1.1.8" + resolved "https://registry.yarnpkg.com/@cspell/dict-filetypes/-/dict-filetypes-1.1.8.tgz#c161ab48667b6539cbc91a70ff0b037fa436a64e" + integrity sha512-EllahNkhzvLWo0ptwu0l3oEeAJOQSUpZnDfnKRIh6mJVehuSovNHwA9vrdZ8jBUjuqcfaN2e7c32zN0D/qvWJQ== "@cspell/dict-fonts@^1.0.14": version "1.0.14" @@ -632,10 +632,10 @@ resolved "https://registry.yarnpkg.com/@cspell/dict-node/-/dict-node-1.0.12.tgz#a7236be30340ff8fe365f62c8d13121fdbe7f51c" integrity sha512-RPNn/7CSkflAWk0sbSoOkg0ORrgBARUjOW3QjB11KwV1gSu8f5W/ij/S50uIXtlrfoBLqd4OyE04jyON+g/Xfg== -"@cspell/dict-npm@^1.0.15": - version "1.0.15" - resolved "https://registry.yarnpkg.com/@cspell/dict-npm/-/dict-npm-1.0.15.tgz#4eac51a4e5258b48e2fd1af277c12cb1fd189f4d" - integrity sha512-6N1G1rGi5AsCaDu9mu+VmrrAj5S9gHv8TvJlarauDeEMS6uVl+ce2JpzDf7ld3Qu/4Dkr0sKS63OeA0DKeQTkw== +"@cspell/dict-npm@^1.0.16": + version "1.0.16" + resolved "https://registry.yarnpkg.com/@cspell/dict-npm/-/dict-npm-1.0.16.tgz#86870686cd0af6354a206ab297872db1d84e9c1b" + integrity sha512-RwkuZGcYBxL3Yux3cSG/IOWGlQ1e9HLCpHeyMtTVGYKAIkFAVUnGrz20l16/Q7zUG7IEktBz5O42kAozrEnqMQ== "@cspell/dict-php@^1.0.24": version "1.0.24" @@ -672,10 +672,10 @@ resolved "https://registry.yarnpkg.com/@cspell/dict-scala/-/dict-scala-1.0.21.tgz#bfda392329061e2352fbcd33d228617742c93831" integrity sha512-5V/R7PRbbminTpPS3ywgdAalI9BHzcEjEj9ug4kWYvBIGwSnS7T6QCFCiu+e9LvEGUqQC+NHgLY4zs1NaBj2vA== -"@cspell/dict-software-terms@^1.0.40": - version "1.0.40" - resolved "https://registry.yarnpkg.com/@cspell/dict-software-terms/-/dict-software-terms-1.0.40.tgz#b9d02e62b81e8213f3ec8d44227af6610eb8ff30" - integrity sha512-aHEIgjZJwqn4I+tlQ2XvC4l106VS2bSzNU8crRadpcbpuL5UC2nzMgEbEOAHK4gP1P9i009ttFB0PTKTGisKgg== +"@cspell/dict-software-terms@^1.0.41": + version "1.0.41" + resolved "https://registry.yarnpkg.com/@cspell/dict-software-terms/-/dict-software-terms-1.0.41.tgz#c8b9ca8e8891e09c2e55afd889bde38f6d7b109a" + integrity sha512-/RWiv99XoDUYh3eCo5PyF2nwDh0U8kuKmSW+UXpzbT4sj7oBl/la19h6Ahgq0Qmr0JGjTF/RxbQkz85Hcp1t5w== "@cspell/dict-typescript@^1.0.19": version "1.0.19" @@ -1695,16 +1695,16 @@ "@rushstack/node-core-library" "3.40.0" "@microsoft/api-extractor@^7.15.2": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.18.5.tgz#cc2804d7c8b9d0f1e63fd85d0448569b767db102" - integrity sha512-NUGS6WxexziEnroHUOI3KKVmMX02god7SLA8Y4a5GKCL5k7AHuHFqP2bpd5Otx2odfbdj15ObO7FU/XA3Oxh8w== + version "7.18.7" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.18.7.tgz#851d2413a3c5d696f7cc914eb59de7a7882b2e8b" + integrity sha512-JhtV8LoyLuIecbgCPyZQg08G1kngIRWpai2UzwNil9mGVGYiDZVeeKx8c2phmlPcogmMDm4oQROxyuiYt5sJiw== dependencies: "@microsoft/api-extractor-model" "7.13.5" "@microsoft/tsdoc" "0.13.2" "@microsoft/tsdoc-config" "~0.15.2" "@rushstack/node-core-library" "3.40.0" - "@rushstack/rig-package" "0.2.13" - "@rushstack/ts-command-line" "4.8.1" + "@rushstack/rig-package" "0.3.0" + "@rushstack/ts-command-line" "4.9.0" colors "~1.2.1" lodash "~4.17.15" resolve "~1.17.0" @@ -1991,18 +1991,18 @@ timsort "~0.3.0" z-schema "~3.18.3" -"@rushstack/rig-package@0.2.13": - version "0.2.13" - resolved "https://registry.yarnpkg.com/@rushstack/rig-package/-/rig-package-0.2.13.tgz#418f0aeb4c9b33bd8bd2547759fc0ae91fd970c7" - integrity sha512-qQMAFKvfb2ooaWU9DrGIK9d8QfyHy/HiuITJbWenlKgzcDXQvQgEduk57YF4Y7LLasDJ5ZzLaaXwlfX8qCRe5Q== +"@rushstack/rig-package@0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@rushstack/rig-package/-/rig-package-0.3.0.tgz#334ad2846797861361b3445d4cc9ae9164b1885c" + integrity sha512-Lj6noF7Q4BBm1hKiBDw94e6uZvq1xlBwM/d2cBFaPqXeGdV+G6r3qaCWfRiSXK0pcHpGGpV5Tb2MdfhVcO6G/g== dependencies: resolve "~1.17.0" strip-json-comments "~3.1.1" -"@rushstack/ts-command-line@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.8.1.tgz#c233a0226112338e58e7e4fd219247b4e7cec883" - integrity sha512-rmxvYdCNRbyRs+DYAPye3g6lkCkWHleqO40K8UPvUAzFqEuj6+YCVssBiOmrUDCoM5gaegSNT0wFDYhz24DWtw== +"@rushstack/ts-command-line@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.9.0.tgz#781ba42cff73cae097b6d5241b6441e7cc2fe6e0" + integrity sha512-kmT8t+JfnvphISF1C5WwY56RefjwgajhSjs9J4ckvAFXZDXR6F5cvF5/RTh7fGCzIomg8esy2PHO/b52zFoZvA== dependencies: "@types/argparse" "1.0.38" argparse "~1.0.9" @@ -2226,9 +2226,9 @@ integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog== "@types/rimraf@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.1.tgz#1bbc106f0978742289103e080d4b41b3b4656e58" - integrity sha512-CAoSlbco40aKZ0CkelBF2g3JeN6aioRaTVnqSX5pWsn/WApm6IDxI4e4tD9D0dY/meCkyyleP1IQDVN13F4maA== + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.2.tgz#a63d175b331748e5220ad48c901d7bbf1f44eef8" + integrity sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ== dependencies: "@types/glob" "*" "@types/node" "*" @@ -3037,7 +3037,7 @@ chokidar@^3.5.1: optionalDependencies: fsevents "~2.3.2" -chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.3: +chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.3, chownr@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== @@ -3496,6 +3496,17 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -3526,35 +3537,35 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== -cspell-glob@^5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/cspell-glob/-/cspell-glob-5.7.0.tgz#f14ad50fb469717220fd4e3e92a0174d3e3b31e2" - integrity sha512-A6LlFT1MWLT8bb0o3xDlXysbuYWM/1U1Rt/iOI/Pi3C9Myqig/WHVKEZ/Lmda6ZI5eRLyDhgi+Y90sUwpd9iEQ== +cspell-glob@^5.9.0: + version "5.9.0" + resolved "https://registry.yarnpkg.com/cspell-glob/-/cspell-glob-5.9.0.tgz#ecb493cd3e12ea9cbefe4aca1cb9e86638825288" + integrity sha512-4yhDg/oJpujKNOIX6EBmeb935vyBVXgd0SVb7AR183En9lTUPkT7PR4lTYKxolNs3/z4Ajx+xwA8DORW7E5Rtw== dependencies: micromatch "^4.0.4" -cspell-io@^5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/cspell-io/-/cspell-io-5.7.0.tgz#5735893dda2e3e37dd981434553118bcf953843f" - integrity sha512-WOn8KwrYYpTfHJkmNG3fd+pT+NX7D1v6AcjYN6sm99fQtbY6z7xMAnjXcneGabUbQMNhAvaW56sB1pR0ABEvGA== +cspell-io@^5.9.0: + version "5.9.0" + resolved "https://registry.yarnpkg.com/cspell-io/-/cspell-io-5.9.0.tgz#f74867e43823965ddd3018920b1dddf71fe27d60" + integrity sha512-T+JO9RZrgW9StS6pg2yVQfNpR503lCzPCLM/24lBykPChaXOk2VTGiBa5TPu7qKRxP9x9p7zHLiawlTkgfIjzQ== dependencies: iconv-lite "^0.6.3" iterable-to-stream "^2.0.0" -cspell-lib@^5.7.2: - version "5.7.2" - resolved "https://registry.yarnpkg.com/cspell-lib/-/cspell-lib-5.7.2.tgz#5e2d47bb2a63931abebefd3030ba830de5657314" - integrity sha512-9C0d96+6GeLgyek//lCXcVpk2VeuwBUU4T+0gmnzPGYEOV1uNNK79SspFsSFZvL6+un5ztO+sQ+2MxR9zMF+gw== +cspell-lib@^5.9.0: + version "5.9.0" + resolved "https://registry.yarnpkg.com/cspell-lib/-/cspell-lib-5.9.0.tgz#2efec17a98ac4b7e2da294fac747f236a820a125" + integrity sha512-86SsD7lOAu9ia5j2o7tBUIUrTrAjwEjMgP96+bE8NdSCArjvnY7FiTkes7AHGZ9g3KhluMEaDKhzJhK0TaZenQ== dependencies: - "@cspell/cspell-bundled-dicts" "^5.7.2" - "@cspell/cspell-types" "^5.7.2" + "@cspell/cspell-bundled-dicts" "^5.9.0" + "@cspell/cspell-types" "^5.9.0" clear-module "^4.1.1" comment-json "^4.1.1" configstore "^5.0.1" - cosmiconfig "^7.0.0" - cspell-glob "^5.7.0" - cspell-io "^5.7.0" - cspell-trie-lib "^5.7.0" + cosmiconfig "^7.0.1" + cspell-glob "^5.9.0" + cspell-io "^5.9.0" + cspell-trie-lib "^5.9.0" find-up "^5.0.0" fs-extra "^10.0.0" gensequence "^3.1.1" @@ -3563,25 +3574,24 @@ cspell-lib@^5.7.2: resolve-global "^1.0.0" vscode-uri "^3.0.2" -cspell-trie-lib@^5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/cspell-trie-lib/-/cspell-trie-lib-5.7.0.tgz#d80978e3da682290ae8cccb71af9c4455ad3cfca" - integrity sha512-XDJrQG2SuQOJRVj6kNUdehLHJdj+1SGzKEvFJbRAzvIRPW75zDe3+yYHHmNOZgXJXLfe0Vtr3RjSjhaQOWZ42Q== +cspell-trie-lib@^5.9.0: + version "5.9.0" + resolved "https://registry.yarnpkg.com/cspell-trie-lib/-/cspell-trie-lib-5.9.0.tgz#ca857a7468fa443bf6a9de4143849e770ca70a48" + integrity sha512-/m0kgxYuLyxeZGjajVC/oxrbi2zpv+1boKSjHBElPrE8MytDgdBgdCojsYAYNAl30QEF4GtxB+q0NlWV4laLdA== dependencies: fs-extra "^10.0.0" gensequence "^3.1.1" cspell@^5.5.2: - version "5.7.2" - resolved "https://registry.yarnpkg.com/cspell/-/cspell-5.7.2.tgz#7218ceaa71e1e87e6d67b375c6371f61940d7ded" - integrity sha512-FJAPt3GkwdVaX6I+GR1A95WsraqHPzYqkBI1rIHNE5JnJIbWW6N+pnlbhDjsUlPWXmQYHXMfUlhFtMVLUVs3mw== + version "5.9.0" + resolved "https://registry.yarnpkg.com/cspell/-/cspell-5.9.0.tgz#2db3c85a9b0bf68bb4d49b0f72e35325a68170c3" + integrity sha512-TrPg5fmdd09S+alS8HjRvYPFBd8Qoxb8XxWGrH3JDsQGA7m6EljGcNjR6Mh19mMP5cQVOv7XmwEMy/EqwaHWTA== dependencies: - "@cspell/cspell-types" "^5.7.2" chalk "^4.1.2" commander "^8.1.0" comment-json "^4.1.1" - cspell-glob "^5.7.0" - cspell-lib "^5.7.2" + cspell-glob "^5.9.0" + cspell-lib "^5.9.0" fs-extra "^10.0.0" get-stdin "^8.0.0" glob "^7.1.7" @@ -4084,10 +4094,10 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-import-resolver-node@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.5.tgz#939bbb0f74e179e757ca87f7a4a890dabed18ac4" - integrity sha512-XMoPKjSpXbkeJ7ZZ9icLnJMTY5Mc1kZbCakHquaFsXPpyWOwK0TK6CODO+0ca54UoM9LKOxyUNnoVZRl8TeaAg== +eslint-import-resolver-node@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== dependencies: debug "^3.2.7" resolve "^1.20.0" @@ -4116,25 +4126,25 @@ eslint-plugin-eslint-plugin@^3.0.3: eslint-utils "^2.1.0" eslint-plugin-import@^2.23.4: - version "2.24.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.24.0.tgz#697ffd263e24da5e84e03b282f5fb62251777177" - integrity sha512-Kc6xqT9hiYi2cgybOc0I2vC9OgAYga5o/rAFinam/yF/t5uBqxQbauNPMC6fgb640T/89P0gFoO27FOilJ/Cqg== + version "2.24.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz#2c8cd2e341f3885918ee27d18479910ade7bb4da" + integrity sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q== dependencies: array-includes "^3.1.3" array.prototype.flat "^1.2.4" debug "^2.6.9" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.5" + eslint-import-resolver-node "^0.3.6" eslint-module-utils "^2.6.2" find-up "^2.0.0" has "^1.0.3" - is-core-module "^2.4.0" + is-core-module "^2.6.0" minimatch "^3.0.4" - object.values "^1.1.3" + object.values "^1.1.4" pkg-up "^2.0.0" read-pkg-up "^3.0.0" resolve "^1.20.0" - tsconfig-paths "^3.9.0" + tsconfig-paths "^3.11.0" eslint-plugin-jest@^24.3.6: version "24.4.0" @@ -4678,7 +4688,7 @@ fs-extra@~7.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^1.2.5: +fs-minipass@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== @@ -5471,10 +5481,10 @@ is-ci@^3.0.0: dependencies: ci-info "^3.1.1" -is-core-module@^2.1.0, is-core-module@^2.2.0, is-core-module@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" - integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== +is-core-module@^2.1.0, is-core-module@^2.2.0, is-core-module@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.6.0.tgz#d7553b2526fe59b92ba3e40c8df757ec8a709e19" + integrity sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ== dependencies: has "^1.0.3" @@ -7103,7 +7113,7 @@ minimist@1.2.5, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2 resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: +minipass@^2.3.5, minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== @@ -7118,7 +7128,7 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" -minizlib@^1.2.1: +minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== @@ -7169,7 +7179,7 @@ mkdirp@*, mkdirp@1.x: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3: +mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -7507,7 +7517,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.3: +object.values@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30" integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg== @@ -8507,7 +8517,7 @@ rxjs@^6.4.0, rxjs@^6.5.4, rxjs@^6.6.0, rxjs@^6.6.7: dependencies: tslib "^1.9.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -9115,17 +9125,17 @@ tar@5.0.7: yallist "^4.0.0" tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: - version "4.4.15" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.15.tgz#3caced4f39ebd46ddda4d6203d48493a919697f8" - integrity sha512-ItbufpujXkry7bHH9NpQyTXPbJ72iTlXgkBAYsAjDXk3Ds8t/3NfO5P4xZGy7u+sYuQUbimgzswX4uQIEeNVOA== + version "4.4.19" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" + chownr "^1.1.4" + fs-minipass "^1.2.7" + minipass "^2.9.0" + minizlib "^1.3.3" + mkdirp "^0.5.5" + safe-buffer "^5.2.1" + yallist "^3.1.1" temp-dir@^1.0.0: version "1.0.0" @@ -9340,9 +9350,9 @@ ts-jest@^27.0.1: yargs-parser "20.x" ts-node@^10.0.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.2.0.tgz#f1e88249a00e26aa95e9a93c50f70241a8a1c4bb" - integrity sha512-FstYHtQz6isj8rBtYMN4bZdnXN1vq4HCbqn9vdNQcInRqtB86PePJQIxE6es0PhxKWhj2PHuwbG40H+bxkZPmg== + version "10.2.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.2.1.tgz#4cc93bea0a7aba2179497e65bb08ddfc198b3ab5" + integrity sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw== dependencies: "@cspotcode/source-map-support" "0.6.1" "@tsconfig/node10" "^1.0.7" @@ -9357,10 +9367,10 @@ ts-node@^10.0.0: make-error "^1.1.1" yn "3.1.1" -tsconfig-paths@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" - integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== +tsconfig-paths@^3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz#954c1fe973da6339c78e06b03ce2e48810b65f36" + integrity sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" @@ -9916,7 +9926,7 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy