Skip to content

Commit cf86f42

Browse files
authored
feat(eslint-plugin): [member-delimiter-style] Add an option 'multilineDetection' to treat types and interfaces as single line if the last member ends on the same line as the closing bracket (#2970)
1 parent c32f803 commit cf86f42

File tree

3 files changed

+190
-3
lines changed

3 files changed

+190
-3
lines changed

packages/eslint-plugin/docs/rules/member-delimiter-style.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ type Config = BaseConfig & {
7272
interface?: BaseConfig;
7373
typeLiteral?: BaseConfig;
7474
};
75+
multilineDetection?: 'brackets' | 'last-member';
7576
};
7677
```
7778

@@ -86,14 +87,20 @@ Default config:
8687
"singleline": {
8788
"delimiter": "semi",
8889
"requireLast": false
89-
}
90+
},
91+
"multilineDetection": "brackets"
9092
}
9193
```
9294

9395
`multiline` config only applies to multiline `interface`/`type` definitions.
9496
`singleline` config only applies to single line `interface`/`type` definitions.
9597
The two configs are entirely separate, and do not effect one another.
9698

99+
`multilineDetection` determines what counts as multiline
100+
101+
- `"brackets"` (default) any newlines in the type or interface make it multiline.
102+
- `"last-member"` if the last member of the interface is on the same line as the last bracket, it is counted as a single line.
103+
97104
### `delimiter`
98105

99106
Accepts three values (or two for `singleline`):

packages/eslint-plugin/src/rules/member-delimiter-style.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type Config = BaseOptions & {
2121
typeLiteral?: BaseOptions;
2222
interface?: BaseOptions;
2323
};
24+
multilineDetection?: 'brackets' | 'last-member';
2425
};
2526
type Options = [Config];
2627
type MessageIds =
@@ -82,6 +83,9 @@ export default util.createRule<Options, MessageIds>({
8283
},
8384
additionalProperties: false,
8485
},
86+
multilineDetection: {
87+
enum: ['brackets', 'last-member'],
88+
},
8589
}),
8690
additionalProperties: false,
8791
},
@@ -97,6 +101,7 @@ export default util.createRule<Options, MessageIds>({
97101
delimiter: 'semi',
98102
requireLast: false,
99103
},
104+
multilineDetection: 'brackets',
100105
},
101106
],
102107
create(context, [options]) {
@@ -215,11 +220,21 @@ export default util.createRule<Options, MessageIds>({
215220
function checkMemberSeparatorStyle(
216221
node: TSESTree.TSInterfaceBody | TSESTree.TSTypeLiteral,
217222
): void {
218-
const isSingleLine = node.loc.start.line === node.loc.end.line;
219-
220223
const members =
221224
node.type === AST_NODE_TYPES.TSInterfaceBody ? node.body : node.members;
222225

226+
let isSingleLine = node.loc.start.line === node.loc.end.line;
227+
if (
228+
options.multilineDetection === 'last-member' &&
229+
!isSingleLine &&
230+
members.length > 0
231+
) {
232+
const lastMember = members[members.length - 1];
233+
if (lastMember.loc.end.line === node.loc.end.line) {
234+
isSingleLine = true;
235+
}
236+
}
237+
223238
const typeOpts =
224239
node.type === AST_NODE_TYPES.TSInterfaceBody
225240
? interfaceOptions

packages/eslint-plugin/tests/rules/member-delimiter-style.test.ts

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,75 @@ type Bar = {
705705
},
706706
],
707707
},
708+
{
709+
code: `
710+
type Foo = {a: {
711+
b: true;
712+
}};
713+
`,
714+
options: [
715+
{
716+
multilineDetection: 'last-member',
717+
},
718+
],
719+
},
720+
`
721+
type Foo = {a: {
722+
b: true;
723+
};};
724+
`,
725+
{
726+
code: `
727+
type Foo = {a: {
728+
b: true;
729+
};};
730+
`,
731+
options: [
732+
{
733+
multilineDetection: 'brackets',
734+
},
735+
],
736+
},
737+
{
738+
code: `
739+
type Foo = {
740+
a: {
741+
b: true;
742+
};
743+
};
744+
`,
745+
options: [
746+
{
747+
multilineDetection: 'last-member',
748+
},
749+
],
750+
},
751+
{
752+
code: `
753+
type Foo = {a: {
754+
b: true;
755+
};};
756+
`,
757+
options: [
758+
{
759+
singleline: { delimiter: 'semi', requireLast: true },
760+
multilineDetection: 'last-member',
761+
},
762+
],
763+
},
764+
{
765+
code: `
766+
type Foo = {
767+
768+
};
769+
`,
770+
options: [
771+
{
772+
singleline: { delimiter: 'semi', requireLast: true },
773+
multilineDetection: 'last-member',
774+
},
775+
],
776+
},
708777

709778
{
710779
code: `
@@ -3365,5 +3434,101 @@ type Foo = {
33653434
},
33663435
],
33673436
},
3437+
{
3438+
code: `
3439+
type Foo = {a: {
3440+
b: true;
3441+
};};
3442+
`,
3443+
output: `
3444+
type Foo = {a: {
3445+
b: true;
3446+
}};
3447+
`,
3448+
options: [
3449+
{
3450+
multilineDetection: 'last-member',
3451+
},
3452+
],
3453+
errors: [
3454+
{
3455+
messageId: 'unexpectedSemi',
3456+
line: 4,
3457+
column: 3,
3458+
},
3459+
],
3460+
},
3461+
{
3462+
code: `
3463+
type Foo = {a: {
3464+
b: true;
3465+
}};
3466+
`,
3467+
output: `
3468+
type Foo = {a: {
3469+
b: true;
3470+
};};
3471+
`,
3472+
errors: [
3473+
{
3474+
messageId: 'expectedSemi',
3475+
line: 4,
3476+
column: 2,
3477+
},
3478+
],
3479+
},
3480+
{
3481+
code: `
3482+
type Foo = {
3483+
a: {
3484+
b: true;
3485+
}
3486+
};
3487+
`,
3488+
output: `
3489+
type Foo = {
3490+
a: {
3491+
b: true;
3492+
};
3493+
};
3494+
`,
3495+
options: [
3496+
{
3497+
multilineDetection: 'last-member',
3498+
},
3499+
],
3500+
errors: [
3501+
{
3502+
messageId: 'expectedSemi',
3503+
line: 5,
3504+
column: 4,
3505+
},
3506+
],
3507+
},
3508+
{
3509+
code: `
3510+
type Foo = {a: {
3511+
b: true;
3512+
}};
3513+
`,
3514+
output: `
3515+
type Foo = {a: {
3516+
b: true;
3517+
};};
3518+
`,
3519+
options: [
3520+
{
3521+
singleline: { delimiter: 'semi', requireLast: true },
3522+
multilineDetection: 'last-member',
3523+
},
3524+
],
3525+
errors: [
3526+
{
3527+
messageId: 'expectedSemi',
3528+
line: 4,
3529+
column: 2,
3530+
},
3531+
],
3532+
},
33683533
],
33693534
});

0 commit comments

Comments
 (0)