Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin): add extension rule lines-between-class-members #1684

Merged
merged 6 commits into from May 12, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/eslint-plugin/README.md
Expand Up @@ -185,6 +185,7 @@ In these cases, we create what we call an extension rule; a rule within our plug
| [`@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/lines-between-class-members`](./docs/rules/lines-between-class-members.md) | Require or disallow an empty line between class members | | :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/no-array-constructor`](./docs/rules/no-array-constructor.md) | Disallow generic `Array` constructors | :heavy_check_mark: | :wrench: | |
Expand Down
73 changes: 73 additions & 0 deletions packages/eslint-plugin/docs/rules/lines-between-class-members.md
@@ -0,0 +1,73 @@
# Require or disallow an empty line between class members (`lines-between-class-members`)

This rule improves readability by enforcing lines between class members. It will not check empty lines before the first member and after the last member. This rule require or disallow an empty line between class members.

## Rule Details

This rule extends the base [`eslint/lines-between-class-members`](https://eslint.org/docs/rules/lines-between-class-members) rule.
It adds support for ignoring overload methods in a class.

See the [ESLint documentation](https://eslint.org/docs/rules/lines-between-class-members) for more details on the `lines-between-class-members` rule.

## Rule Changes

```cjson
{
// note you must disable the base rule as it can report incorrect errors
"lines-between-class-members": "off",
"@typescript-eslint/lines-between-class-members": ["error"]
}
```

In addition to the options supported by the `lines-between-class-members` rule in ESLint core, the rule adds the following options:

## Options

This rule has a string option and an object option.

- Object option:

- `"exceptAfterOverload": true` (default) - Skip checking empty lines after overload class members
- `"exceptAfterOverload": false` - **do not** skip checking empty lines after overload class members

- [See the other options allowed](https://github.com/eslint/eslint/blob/master/docs/rules/lines-between-class-members.md#options)

### `exceptAfterOverload: true`

Examples of **correct** code for the `{ "exceptAfterOverload": true }` option:

```ts
/*eslint @typescript-eslint/lines-between-class-members: ["error", "always", { "exceptAfterOverload": true }]*/

class foo {
bar(a: string): void;
bar(a: string, b: string): void;
bar(a: string, b: string) {}

baz() {}

qux() {}
}
```

### `exceptAfterOverload: false`

Examples of **correct** code for the `{ "exceptAfterOverload": false }` option:

```ts
/*eslint @typescript-eslint/lines-between-class-members: ["error", "always", { "exceptAfterOverload": false }]*/

class foo {
bar(a: string): void;

bar(a: string, b: string): void;

bar(a: string, b: string) {}

baz() {}

qux() {}
}
```

<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/lines-between-class-members.md)</sup>
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/configs/all.json
Expand Up @@ -24,6 +24,8 @@
"@typescript-eslint/func-call-spacing": "error",
"indent": "off",
"@typescript-eslint/indent": "error",
"lines-between-class-members": "off",
"@typescript-eslint/lines-between-class-members": "error",
"init-declarations": "off",
"@typescript-eslint/init-declarations": "error",
"keyword-spacing": "off",
Expand Down
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/rules/index.ts
Expand Up @@ -100,6 +100,7 @@ import typeAnnotationSpacing from './type-annotation-spacing';
import typedef from './typedef';
import unboundMethod from './unbound-method';
import unifiedSignatures from './unified-signatures';
import linesBetweenClassMembers from './lines-between-class-members';

export default {
'adjacent-overload-signatures': adjacentOverloadSignatures,
Expand Down Expand Up @@ -204,4 +205,5 @@ export default {
typedef: typedef,
'unbound-method': unboundMethod,
'unified-signatures': unifiedSignatures,
'lines-between-class-members': linesBetweenClassMembers,
};
66 changes: 66 additions & 0 deletions packages/eslint-plugin/src/rules/lines-between-class-members.ts
@@ -0,0 +1,66 @@
import {
AST_NODE_TYPES,
TSESTree,
} from '@typescript-eslint/experimental-utils';
import baseRule from 'eslint/lib/rules/lines-between-class-members';
import * as util from '../util';

type Options = util.InferOptionsTypeFromRule<typeof baseRule>;
type MessageIds = util.InferMessageIdsTypeFromRule<typeof baseRule>;

const schema = util.deepMerge(
{ ...baseRule.meta.schema },
{
1: {
exceptAfterOverload: {
type: 'booleean',
default: true,
},
},
},
);

export default util.createRule<Options, MessageIds>({
name: 'lines-between-class-members',
meta: {
type: 'layout',
docs: {
description: 'Require or disallow an empty line between class members',
category: 'Stylistic Issues',
recommended: false,
extendsBaseRule: true,
},
fixable: 'whitespace',
schema,
messages: baseRule.meta.messages,
},
defaultOptions: [
'always',
{
exceptAfterOverload: true,
exceptAfterSingleLine: false,
},
],
create(context, options) {
const rules = baseRule.create(context);
const exceptAfterOverload =
options[1]?.exceptAfterOverload && options[0] === 'always';

function isOverload(node: TSESTree.Node): boolean {
return (
node.type === AST_NODE_TYPES.MethodDefinition &&
node.value.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression
);
}

return {
ClassBody(node): void {
const body = exceptAfterOverload
? node.body.filter(node => !isOverload(node))
: node.body;

rules.ClassBody({ ...node, body });
},
};
},
});