-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat(eslint-plugin): [prefer-optional-chain] support if
statement as part of chain
#10137
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
Open
omril1
wants to merge
51
commits into
typescript-eslint:main
Choose a base branch
from
omril1:feat-eslint-plugin-issue-6309-prefer-optional-chain-support-if-statement
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
fd1c08d
feat(eslint-plugin): [prefer-optional-chain] support if statement as …
387a3ab
Add docs mdx
a21c197
Order matters + snapshots
eaa203f
Handle semi colons
a7258b8
Coverage
0289162
Use `lastOperand` to fix false-negative coverage report
958d09d
fix comment placement
4490802
Support chaining in `if` body - only for logicals that end with `Call…
f3ad6bd
coverage
ed1ccbe
reformat a bit and test with `requireNullish: true`
36f074c
comment
2c21587
Handle `if` without braces
23943b1
better node type
5f3f9df
hit the missing coverage
0d4b539
Merge remote-tracking branch 'upstream/main' into feat-eslint-plugin-…
665e903
Merge branch 'main' into feat-eslint-plugin-issue-6309-prefer-optiona…
omril1 00c220f
Merge branch 'main' into feat-eslint-plugin-issue-6309-prefer-optiona…
omril1 3aab4ba
`insert option description`
bd7ee28
CR: change arroy function to method to reduce indent
353202f
CR: less specific type
e180f60
CR: combine early returns
5cdeb5e
CR: remove unncessary return
b6b3491
CR: rename `allowSuggestingOnIfStatements` to `allowIfStatements`
1beff72
CR: Fix title for mdx TabItem
7b12732
CR: Remove comments on test cases
3e25c65
Merge remote-tracking branch 'upstream/main' into feat-eslint-plugin-…
c2c6ae3
fix snapshot
98cb43d
CR: Make the rule work with comments to some degree
6a96898
Merge remote-tracking branch 'upstream/main' into feat-eslint-plugin-…
1a3bee2
Remove unncessary `suggestions: null`
bfbf55c
better comments
5d9ef8f
change to suggestion fixer when `if` contains comments between
6465402
CR: add types to tests
061022d
Merge branch 'main' into feat-eslint-plugin-issue-6309-prefer-optiona…
omril1 26095c4
Merge remote-tracking branch 'upstream/main' into feat-eslint-plugin-…
omril1 f3f98a9
recreate comments only if the exist in ranges that are being removed
omril1 20c6eea
Merge remote-tracking branch 'upstream/main' into feat-eslint-plugin-…
omril1 6671148
CR: Fix rule descrition
omril1 310b56e
CR: Fix ignored non-CallExpression
omril1 36e306f
CR: Revert change to `context.sourceCode` input
omril1 6ccb9be
fix coverage and bug with comment included twice
omril1 bf84196
update snapshot
omril1 bb2b2aa
change rule name, invert value and fix tests
omril1 c843a4d
Fix internal lint error
omril1 9b6a32e
Merge branch 'main' into feat-eslint-plugin-issue-6309-prefer-optiona…
omril1 f16358f
add non-null assertions because I couldn't find a way to make this co…
omril1 c8e7980
Merge branch 'main' into feat-eslint-plugin-issue-6309-prefer-optiona…
omril1 ec6389b
Update comment
omril1 b4fd091
Merge branch 'main' into feat-eslint-plugin-issue-6309-prefer-optiona…
omril1 6d2f69b
Merge branch 'main' into feat-eslint-plugin-issue-6309-prefer-optiona…
omril1 cbfb329
Merge branch 'main' into feat-eslint-plugin-issue-6309-prefer-optiona…
omril1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -9,7 +9,7 @@ import type { | |||||
SourceCode, | ||||||
} from '@typescript-eslint/utils/ts-eslint'; | ||||||
|
||||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'; | ||||||
import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; | ||||||
import { unionTypeParts } from 'ts-api-utils'; | ||||||
import * as ts from 'typescript'; | ||||||
|
||||||
|
@@ -208,9 +208,13 @@ const analyzeOrChainOperand: OperandAnalyzer = ( | |||||
*/ | ||||||
function getReportRange( | ||||||
chain: ValidOperand[], | ||||||
boundary: TSESTree.Range, | ||||||
node: TSESTree.Node, | ||||||
sourceCode: SourceCode, | ||||||
): TSESTree.Range { | ||||||
if (node.type === AST_NODE_TYPES.IfStatement) { | ||||||
return node.range; | ||||||
} | ||||||
const boundary = node.range; | ||||||
const leftNode = chain[0].node; | ||||||
const rightNode = chain[chain.length - 1].node; | ||||||
let leftMost = nullThrows( | ||||||
|
@@ -324,7 +328,7 @@ function getReportDescriptor( | |||||
// 6) diff(`foo.bar.baz?.bam`, `foo.bar.baz.bam()`) = `()` | ||||||
// 7) result = `foo?.bar?.baz?.bam?.()` | ||||||
|
||||||
const parts = []; | ||||||
const parts: FlattenedChain[] = []; | ||||||
for (const current of chain) { | ||||||
const nextOperand = flattenChainExpression( | ||||||
sourceCode, | ||||||
|
@@ -401,7 +405,85 @@ function getReportDescriptor( | |||||
newCode = `!${newCode}`; | ||||||
} | ||||||
|
||||||
const reportRange = getReportRange(chain, node.range, sourceCode); | ||||||
const reportRange = getReportRange(chain, node, sourceCode); | ||||||
|
||||||
if (node.type === AST_NODE_TYPES.IfStatement) { | ||||||
const chainEndedWithSemicolon = | ||||||
sourceCode.getTokenAfter(lastOperand.node)?.value === ';'; | ||||||
if (chainEndedWithSemicolon) { | ||||||
newCode += ';'; | ||||||
} | ||||||
|
||||||
const nodeBeforeTheComment = chainEndedWithSemicolon | ||||||
? lastOperand.node.parent | ||||||
: lastOperand.node; | ||||||
|
||||||
const commentsBefore = sourceCode.getCommentsBefore(chain[1].node); | ||||||
const commentsAfter = sourceCode.getCommentsAfter(nodeBeforeTheComment); | ||||||
JoshuaKGoldberg marked this conversation as resolved.
Show resolved
Hide resolved
JoshuaKGoldberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||||||
const tokenAfterNodeTest = sourceCode.getTokenAfter(node.test)!; // if (foo) /* this */, there is always a token here | ||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||||||
const tokenBeforeNodeTest = sourceCode.getTokenBefore(node.test)!; // if /* this */ (foo), there is always a token here | ||||||
const tokenAfterAfterNodeTest = | ||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||||||
sourceCode.getTokenAfter(tokenAfterNodeTest)!; // if (foo) /* this */, there is always a token here | ||||||
const commentsBeforeNodeTest = | ||||||
sourceCode.getCommentsBefore(tokenBeforeNodeTest); // if /* this */ (foo) | ||||||
const beforeNodeTestComments = sourceCode.getCommentsBefore(node.test); // if (/* this */ foo) | ||||||
const afterNodeTestComments1 = sourceCode.getCommentsAfter(node.test); // if (foo /* this */) | ||||||
const afterNodeTestComments2 = | ||||||
JoshuaKGoldberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
tokenAfterAfterNodeTest.type === AST_TOKEN_TYPES.Punctuator | ||||||
? sourceCode.getCommentsBefore(tokenAfterAfterNodeTest) | ||||||
: []; // if (foo) /* this */ | ||||||
|
||||||
const commentsToReloacte = [ | ||||||
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. [Typo]
Suggested change
|
||||||
...commentsBeforeNodeTest, | ||||||
...beforeNodeTestComments, | ||||||
...afterNodeTestComments1, | ||||||
...afterNodeTestComments2, | ||||||
]; | ||||||
|
||||||
if (commentsToReloacte.length) { | ||||||
commentsBefore.unshift(...commentsToReloacte); | ||||||
useSuggestionFixer = true; | ||||||
const indentationCount = node.loc.start.column; | ||||||
const indentation = ' '.repeat(indentationCount); | ||||||
const newLineIndentation = `\n${indentation}`; | ||||||
function getTextFromCommentsArray(comments: TSESTree.Comment[]): string { | ||||||
return comments | ||||||
.map(({ type, value }) => | ||||||
type === AST_TOKEN_TYPES.Line ? `//${value}` : `/*${value}*/`, | ||||||
) | ||||||
.join(newLineIndentation); | ||||||
} | ||||||
if (commentsBefore.length > 0) { | ||||||
const commentsText = getTextFromCommentsArray(commentsBefore); | ||||||
newCode = `${commentsText}\n${indentation}${newCode}`; | ||||||
} | ||||||
if (commentsAfter.length > 0) { | ||||||
const commentsText = getTextFromCommentsArray(commentsAfter); | ||||||
newCode = `${newCode}\n${indentation}${commentsText}`; | ||||||
} | ||||||
} else if (commentsBefore.length || commentsAfter.length) { | ||||||
const indentationCount = node.loc.start.column; | ||||||
const indentation = ' '.repeat(indentationCount); | ||||||
function getTextFromCommentsArray(comments: TSESTree.Comment[]): string { | ||||||
return sourceCode | ||||||
.getText() | ||||||
.slice(comments[0].range[0], comments[comments.length - 1].range[1]); | ||||||
} | ||||||
if (commentsBefore.length > 0) { | ||||||
const commentsTextBeforeWithoutIndent = | ||||||
getTextFromCommentsArray(commentsBefore); | ||||||
newCode = `${commentsTextBeforeWithoutIndent}\n${indentation}${newCode}`; | ||||||
} | ||||||
if (commentsAfter.length > 0) { | ||||||
const commentsTextAfterWithoutIndent = | ||||||
getTextFromCommentsArray(commentsAfter); | ||||||
newCode = `${newCode}\n${indentation}${commentsTextAfterWithoutIndent}`; | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
const fix: ReportFixFunction = fixer => | ||||||
fixer.replaceTextRange(reportRange, newCode); | ||||||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 22 additions & 1 deletion
23
packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-optional-chain.shot
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.