Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7ad343b

Browse files
authored
fix(eslint-plugin): [member-delimiter-style] correct invalid fix for multiline with params on the same line (typescript-eslint#3177)
1 parent 5cc5d2e commit 7ad343b

File tree

2 files changed

+93
-18
lines changed

2 files changed

+93
-18
lines changed

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

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
TSESLint,
23
TSESTree,
34
AST_NODE_TYPES,
45
} from '@typescript-eslint/experimental-utils';
@@ -11,6 +12,9 @@ type TypeOptions = {
1112
delimiter?: Delimiter;
1213
requireLast?: boolean;
1314
};
15+
type TypeOptionsWithType = TypeOptions & {
16+
type: string;
17+
};
1418
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
1519
type BaseOptions = {
1620
multiline?: TypeOptions;
@@ -29,6 +33,20 @@ type MessageIds =
2933
| 'unexpectedSemi'
3034
| 'expectedComma'
3135
| 'expectedSemi';
36+
type LastTokenType = TSESTree.Token;
37+
38+
interface MakeFixFunctionParams {
39+
optsNone: boolean;
40+
optsSemi: boolean;
41+
lastToken: LastTokenType;
42+
missingDelimiter: boolean;
43+
lastTokenLine: string;
44+
isSingleLine: boolean;
45+
}
46+
47+
type MakeFixFunctionReturnType =
48+
| ((fixer: TSESLint.RuleFixer) => TSESLint.RuleFix)
49+
| null;
3250

3351
const definition = {
3452
type: 'object',
@@ -54,6 +72,47 @@ const definition = {
5472
additionalProperties: false,
5573
};
5674

75+
const isLastTokenEndOfLine = (token: string, line: string): boolean => {
76+
const positionInLine = line.indexOf(token);
77+
78+
return positionInLine === line.length - 1;
79+
};
80+
81+
const makeFixFunction = ({
82+
optsNone,
83+
optsSemi,
84+
lastToken,
85+
missingDelimiter,
86+
lastTokenLine,
87+
isSingleLine,
88+
}: MakeFixFunctionParams): MakeFixFunctionReturnType => {
89+
// if removing is the action but last token is not the end of the line
90+
if (
91+
optsNone &&
92+
!isLastTokenEndOfLine(lastToken.value, lastTokenLine) &&
93+
!isSingleLine
94+
) {
95+
return null;
96+
}
97+
98+
return (fixer: TSESLint.RuleFixer): TSESLint.RuleFix => {
99+
if (optsNone) {
100+
// remove the unneeded token
101+
return fixer.remove(lastToken);
102+
}
103+
104+
const token = optsSemi ? ';' : ',';
105+
106+
if (missingDelimiter) {
107+
// add the missing delimiter
108+
return fixer.insertTextAfter(lastToken, token);
109+
}
110+
111+
// correct the current delimiter
112+
return fixer.replaceText(lastToken, token);
113+
};
114+
};
115+
57116
export default util.createRule<Options, MessageIds>({
58117
name: 'member-delimiter-style',
59118
meta: {
@@ -127,7 +186,7 @@ export default util.createRule<Options, MessageIds>({
127186
*/
128187
function checkLastToken(
129188
member: TSESTree.TypeElement,
130-
opts: TypeOptions,
189+
opts: TypeOptionsWithType,
131190
isLast: boolean,
132191
): void {
133192
/**
@@ -147,10 +206,14 @@ export default util.createRule<Options, MessageIds>({
147206
const lastToken = sourceCode.getLastToken(member, {
148207
includeComments: false,
149208
});
209+
150210
if (!lastToken) {
151211
return;
152212
}
153213

214+
const sourceCodeLines = sourceCode.getLines();
215+
const lastTokenLine = sourceCodeLines[lastToken?.loc.start.line - 1];
216+
154217
const optsSemi = getOption('semi');
155218
const optsComma = getOption('comma');
156219
const optsNone = getOption('none');
@@ -193,22 +256,14 @@ export default util.createRule<Options, MessageIds>({
193256
},
194257
},
195258
messageId,
196-
fix(fixer) {
197-
if (optsNone) {
198-
// remove the unneeded token
199-
return fixer.remove(lastToken);
200-
}
201-
202-
const token = optsSemi ? ';' : ',';
203-
204-
if (missingDelimiter) {
205-
// add the missing delimiter
206-
return fixer.insertTextAfter(lastToken, token);
207-
}
208-
209-
// correct the current delimiter
210-
return fixer.replaceText(lastToken, token);
211-
},
259+
fix: makeFixFunction({
260+
optsNone,
261+
optsSemi,
262+
lastToken,
263+
missingDelimiter,
264+
lastTokenLine,
265+
isSingleLine: opts.type === 'single-line',
266+
}),
212267
});
213268
}
214269
}
@@ -239,7 +294,9 @@ export default util.createRule<Options, MessageIds>({
239294
node.type === AST_NODE_TYPES.TSInterfaceBody
240295
? interfaceOptions
241296
: typeLiteralOptions;
242-
const opts = isSingleLine ? typeOpts.singleline : typeOpts.multiline;
297+
const opts = isSingleLine
298+
? { ...typeOpts.singleline, type: 'single-line' }
299+
: { ...typeOpts.multiline, type: 'multi-line' };
243300

244301
members.forEach((member, index) => {
245302
checkLastToken(member, opts ?? {}, index === members.length - 1);

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,24 @@ interface Foo {
851851
},
852852
{
853853
code: `
854+
type Test = {
855+
a: {
856+
one: 1
857+
}; b: 2
858+
};
859+
`,
860+
output: null,
861+
options: [{ multiline: { delimiter: 'none' } }],
862+
errors: [
863+
{
864+
messageId: 'unexpectedSemi',
865+
line: 5,
866+
column: 5,
867+
},
868+
],
869+
},
870+
{
871+
code: `
854872
interface Foo {
855873
name: string
856874
age: number

0 commit comments

Comments
 (0)