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
fix(eslint-plugin): [member-delimiter-style] correct invalid fix for multiline with params on the same line #3177
Changes from 2 commits
74d7e53
14491bc
a27e2e3
e5353a9
75e83de
f636acf
5d2b9c2
8c0e78f
4697939
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,10 @@ import { | |
TSESTree, | ||
AST_NODE_TYPES, | ||
} from '@typescript-eslint/experimental-utils'; | ||
import { | ||
RuleFix, | ||
RuleFixer, | ||
} from '@typescript-eslint/experimental-utils/src/ts-eslint'; | ||
import * as util from '../util'; | ||
|
||
type Delimiter = 'comma' | 'none' | 'semi'; | ||
|
@@ -11,6 +15,9 @@ type TypeOptions = { | |
delimiter?: Delimiter; | ||
requireLast?: boolean; | ||
}; | ||
type TypeOptionsWithType = TypeOptions & { | ||
type: string; | ||
}; | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions | ||
type BaseOptions = { | ||
multiline?: TypeOptions; | ||
|
@@ -21,14 +28,26 @@ type Config = BaseOptions & { | |
typeLiteral?: BaseOptions; | ||
interface?: BaseOptions; | ||
}; | ||
multilineDetection?: 'brackets' | 'last-member'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why have you removed this option entirely? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed here: e5353a9 |
||
multilineDetection?: 'last-member'; | ||
}; | ||
type Options = [Config]; | ||
type MessageIds = | ||
| 'unexpectedComma' | ||
| 'unexpectedSemi' | ||
| 'expectedComma' | ||
| 'expectedSemi'; | ||
type LastTokenType = TSESTree.Token; | ||
|
||
interface MakeFixFunctionParams { | ||
optsNone: boolean; | ||
optsSemi: boolean; | ||
lastToken: LastTokenType; | ||
missingDelimiter: boolean; | ||
lastTokenLine: string; | ||
isSingleLine: boolean; | ||
} | ||
|
||
type MakeFixFunctionReturnType = ((fixer: RuleFixer) => RuleFix) | null; | ||
|
||
const definition = { | ||
type: 'object', | ||
|
@@ -54,6 +73,47 @@ const definition = { | |
additionalProperties: false, | ||
}; | ||
|
||
const isLastTokenEndOfLine = (token: string, line: string): boolean => { | ||
const positionInLine = line.indexOf(token); | ||
|
||
return positionInLine === line.length - 1; | ||
}; | ||
|
||
const makeFixFunction = ({ | ||
optsNone, | ||
optsSemi, | ||
lastToken, | ||
missingDelimiter, | ||
lastTokenLine, | ||
isSingleLine, | ||
}: MakeFixFunctionParams): MakeFixFunctionReturnType => { | ||
// if removing is the action but last token is not the end of the line | ||
if ( | ||
optsNone && | ||
!isLastTokenEndOfLine(lastToken.value, lastTokenLine) && | ||
!isSingleLine | ||
) { | ||
return null; | ||
} | ||
|
||
return (fixer: RuleFixer): RuleFix => { | ||
if (optsNone) { | ||
// remove the unneeded token | ||
return fixer.remove(lastToken); | ||
} | ||
|
||
const token = optsSemi ? ';' : ','; | ||
|
||
if (missingDelimiter) { | ||
// add the missing delimiter | ||
return fixer.insertTextAfter(lastToken, token); | ||
} | ||
|
||
// correct the current delimiter | ||
return fixer.replaceText(lastToken, token); | ||
}; | ||
}; | ||
|
||
export default util.createRule<Options, MessageIds>({ | ||
name: 'member-delimiter-style', | ||
meta: { | ||
|
@@ -83,9 +143,6 @@ export default util.createRule<Options, MessageIds>({ | |
}, | ||
additionalProperties: false, | ||
}, | ||
multilineDetection: { | ||
enum: ['brackets', 'last-member'], | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why have you removed this from the schema entirely? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed here: 75e83de |
||
}), | ||
additionalProperties: false, | ||
}, | ||
|
@@ -101,7 +158,6 @@ export default util.createRule<Options, MessageIds>({ | |
delimiter: 'semi', | ||
requireLast: false, | ||
}, | ||
multilineDetection: 'brackets', | ||
}, | ||
], | ||
create(context, [options]) { | ||
|
@@ -127,7 +183,7 @@ export default util.createRule<Options, MessageIds>({ | |
*/ | ||
function checkLastToken( | ||
member: TSESTree.TypeElement, | ||
opts: TypeOptions, | ||
opts: TypeOptionsWithType, | ||
isLast: boolean, | ||
): void { | ||
/** | ||
|
@@ -147,10 +203,14 @@ export default util.createRule<Options, MessageIds>({ | |
const lastToken = sourceCode.getLastToken(member, { | ||
includeComments: false, | ||
}); | ||
|
||
if (!lastToken) { | ||
return; | ||
} | ||
|
||
const sourceCodeLines = sourceCode.getLines(); | ||
const lastTokenLine = sourceCodeLines[lastToken?.loc.start.line - 1]; | ||
|
||
const optsSemi = getOption('semi'); | ||
const optsComma = getOption('comma'); | ||
const optsNone = getOption('none'); | ||
|
@@ -193,22 +253,14 @@ export default util.createRule<Options, MessageIds>({ | |
}, | ||
}, | ||
messageId, | ||
fix(fixer) { | ||
if (optsNone) { | ||
// remove the unneeded token | ||
return fixer.remove(lastToken); | ||
} | ||
|
||
const token = optsSemi ? ';' : ','; | ||
|
||
if (missingDelimiter) { | ||
// add the missing delimiter | ||
return fixer.insertTextAfter(lastToken, token); | ||
} | ||
|
||
// correct the current delimiter | ||
return fixer.replaceText(lastToken, token); | ||
}, | ||
fix: makeFixFunction({ | ||
optsNone, | ||
optsSemi, | ||
lastToken, | ||
missingDelimiter, | ||
lastTokenLine, | ||
isSingleLine: opts.type === 'single-line', | ||
}), | ||
}); | ||
} | ||
} | ||
|
@@ -239,7 +291,9 @@ export default util.createRule<Options, MessageIds>({ | |
node.type === AST_NODE_TYPES.TSInterfaceBody | ||
? interfaceOptions | ||
: typeLiteralOptions; | ||
const opts = isSingleLine ? typeOpts.singleline : typeOpts.multiline; | ||
const opts = isSingleLine | ||
? { ...typeOpts.singleline, type: 'single-line' } | ||
: { ...typeOpts.multiline, type: 'multi-line' }; | ||
|
||
members.forEach((member, index) => { | ||
checkLastToken(member, opts ?? {}, index === members.length - 1); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't add deep imports on
/src
from plugins.This is availaible on the
TSESLint
export of@typescript-eslint/experimental-utils
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed here: a27e2e3