From 37a537d6c82f38542694332ffdad22629c77bfd7 Mon Sep 17 00:00:00 2001 From: Azat S Date: Tue, 6 Jun 2023 19:19:13 +0300 Subject: [PATCH] fix: fix sorting objects with inline comments --- rules/sort-imports.ts | 4 +-- rules/sort-objects.ts | 18 +------------ test/sort-objects.test.ts | 22 +++++++++++++-- utils/get-comment-after.ts | 25 +++++++++++++++++ utils/get-comment-before.ts | 27 +++++++++++++++++++ utils/get-comment.ts | 53 ------------------------------------- utils/get-node-range.ts | 12 +++------ utils/make-fixes.ts | 46 +++++++++++++++++++++++++++----- 8 files changed, 118 insertions(+), 89 deletions(-) create mode 100644 utils/get-comment-after.ts create mode 100644 utils/get-comment-before.ts delete mode 100644 utils/get-comment.ts diff --git a/rules/sort-imports.ts b/rules/sort-imports.ts index 6d6730f72..da2e0a343 100644 --- a/rules/sort-imports.ts +++ b/rules/sort-imports.ts @@ -7,10 +7,10 @@ import isCoreModule from 'is-core-module' import { minimatch } from 'minimatch' import { createEslintRule } from '../utils/create-eslint-rule' +import { getCommentBefore } from '../utils/get-comment-before' import { getNodeRange } from '../utils/get-node-range' import { rangeToDiff } from '../utils/range-to-diff' import { TSConfig } from '../utils/read-ts-config' -import { getComment } from '../utils/get-comment' import { SortType, SortOrder } from '../typings' import { sortNodes } from '../utils/sort-nodes' import { complete } from '../utils/complete' @@ -272,7 +272,7 @@ export default createEslintRule({ ): boolean => !!source.getTokensBetween( left.node, - getComment(right.node, source).before || right.node, + getCommentBefore(right.node, source) || right.node, { includeComments: true, }, diff --git a/rules/sort-objects.ts b/rules/sort-objects.ts index d87c9c837..36ee36e19 100644 --- a/rules/sort-objects.ts +++ b/rules/sort-objects.ts @@ -2,12 +2,11 @@ import type { TSESTree } from '@typescript-eslint/types' import type { TSESLint } from '@typescript-eslint/utils' import type { SortingNode } from '../typings' -import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/types' +import { AST_NODE_TYPES } from '@typescript-eslint/types' import { createEslintRule } from '../utils/create-eslint-rule' import { toSingleLine } from '../utils/to-single-line' import { rangeToDiff } from '../utils/range-to-diff' -import { getComment } from '../utils/get-comment' import { SortType, SortOrder } from '../typings' import { makeFixes } from '../utils/make-fixes' import { sortNodes } from '../utils/sort-nodes' @@ -161,14 +160,6 @@ export default createEslintRule({ } if (comparison) { - let nextToken = source.getTokenAfter(nodes.at(-1)!.node, { - includeComments: true, - }) - - let hasTrailingComma = - nextToken?.type === AST_TOKEN_TYPES.Punctuator && - nextToken.value === ',' - let fix: | ((fixer: TSESLint.RuleFixer) => TSESLint.RuleFix[]) | undefined = fixer => { @@ -189,13 +180,6 @@ export default createEslintRule({ return makeFixes(fixer, nodes, sortedNodes, source) } - if ( - !hasTrailingComma && - getComment(nodes.at(-1)!.node, source).after - ) { - fix = undefined - } - context.report({ messageId: 'unexpectedObjectsOrder', data: { diff --git a/test/sort-objects.test.ts b/test/sort-objects.test.ts index 8ac70c422..23bb1e3c7 100644 --- a/test/sort-objects.test.ts +++ b/test/sort-objects.test.ts @@ -391,7 +391,7 @@ describe(RULE_NAME, () => { { code: dedent` let yokokawaFamily = { - sister: 'Setsuko', // Setsuko completely adores her older brother Seita + sister: 'Setsuko',// Setsuko completely adores her older brother Seita 'mrs-yokokawa': 'Mrs. Yokokawa', // Seita's and Setsuko's mother brother: 'Seita', // Seita is responsible, mature, and tough } @@ -400,7 +400,7 @@ describe(RULE_NAME, () => { let yokokawaFamily = { brother: 'Seita', // Seita is responsible, mature, and tough 'mrs-yokokawa': 'Mrs. Yokokawa', // Seita's and Setsuko's mother - sister: 'Setsuko', // Setsuko completely adores her older brother Seita + sister: 'Setsuko',// Setsuko completely adores her older brother Seita } `, options: [ @@ -442,6 +442,12 @@ describe(RULE_NAME, () => { kazuki: 'Kazuki Kurusu' // daddy #2 } `, + output: dedent` + let daddies = { + kazuki: 'Kazuki Kurusu', // daddy #2 + rei: 'Rei Suwa' // daddy #1 + } + `, options: [ { type: SortType.alphabetical, @@ -895,6 +901,12 @@ describe(RULE_NAME, () => { kazuki: 'Kazuki Kurusu' // daddy #2 } `, + output: dedent` + let daddies = { + kazuki: 'Kazuki Kurusu', // daddy #2 + rei: 'Rei Suwa' // daddy #1 + } + `, options: [ { type: SortType.natural, @@ -1348,6 +1360,12 @@ describe(RULE_NAME, () => { kazuki: 'Kazuki Kurusu' // daddy #2 } `, + output: dedent` + let daddies = { + kazuki: 'Kazuki Kurusu', // daddy #2 + rei: 'Rei Suwa' // daddy #1 + } + `, options: [ { type: SortType['line-length'], diff --git a/utils/get-comment-after.ts b/utils/get-comment-after.ts new file mode 100644 index 000000000..0233343c8 --- /dev/null +++ b/utils/get-comment-after.ts @@ -0,0 +1,25 @@ +import type { TSESLint } from '@typescript-eslint/utils' +import type { TSESTree } from '@typescript-eslint/types' + +import { AST_TOKEN_TYPES } from '@typescript-eslint/types' + +export let getCommentAfter = ( + node: TSESTree.Node, + source: TSESLint.SourceCode, +): TSESTree.Comment | null => { + let token = source.getTokenAfter(node, { + includeComments: true, + filter: ({ type, value }) => + !(type === AST_TOKEN_TYPES.Punctuator && [',', ';'].includes(value)), + }) + + if ( + (token?.type === AST_TOKEN_TYPES.Block || + token?.type === AST_TOKEN_TYPES.Line) && + node.loc.end.line === token.loc.end.line + ) { + return token + } + + return null +} diff --git a/utils/get-comment-before.ts b/utils/get-comment-before.ts new file mode 100644 index 000000000..e53fb57b7 --- /dev/null +++ b/utils/get-comment-before.ts @@ -0,0 +1,27 @@ +import type { TSESLint } from '@typescript-eslint/utils' +import type { TSESTree } from '@typescript-eslint/types' + +import { AST_TOKEN_TYPES } from '@typescript-eslint/types' + +export let getCommentBefore = ( + node: TSESTree.Node, + source: TSESLint.SourceCode, +): TSESTree.Comment | null => { + let [tokenBefore, tokenOrCommentBefore] = source.getTokensBefore(node, { + includeComments: true, + count: 2, + filter: ({ type, value }) => + !(type === AST_TOKEN_TYPES.Punctuator && [',', ';'].includes(value)), + }) as (TSESTree.Token | null)[] + + if ( + (tokenOrCommentBefore?.type === AST_TOKEN_TYPES.Block || + tokenOrCommentBefore?.type === AST_TOKEN_TYPES.Line) && + node.loc.start.line - tokenOrCommentBefore.loc.end.line <= 1 && + tokenBefore?.loc.end.line !== tokenOrCommentBefore.loc.start.line + ) { + return tokenOrCommentBefore + } + + return null +} diff --git a/utils/get-comment.ts b/utils/get-comment.ts deleted file mode 100644 index 68434095a..000000000 --- a/utils/get-comment.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { TSESLint } from '@typescript-eslint/utils' -import type { TSESTree } from '@typescript-eslint/types' - -import { AST_TOKEN_TYPES } from '@typescript-eslint/types' - -export let getComment = ( - node: TSESTree.Node, - source: TSESLint.SourceCode, -): { - before: TSESTree.Token | null - after: TSESTree.Token | null -} => { - let isComment = (token: TSESTree.Token | null) => - token?.type === AST_TOKEN_TYPES.Block || - token?.type === AST_TOKEN_TYPES.Line - - let filter = ({ type, value }: TSESTree.Token) => - !(type === AST_TOKEN_TYPES.Punctuator && [',', ';'].includes(value)) - - let [tokenBefore, tokenOrCommentBefore] = source.getTokensBefore(node, { - includeComments: true, - count: 2, - filter, - }) - - let tokenOrCommentAfter = source.getTokenAfter(node, { - includeComments: true, - filter, - }) - - let before = null - let after = null - - if ( - isComment(tokenOrCommentBefore) && - node.loc.start.line - tokenOrCommentBefore.loc.end.line <= 1 && - tokenBefore.loc.end.line !== tokenOrCommentBefore.loc.start.line - ) { - before = tokenOrCommentBefore - } - - if ( - isComment(tokenOrCommentAfter) && - node.loc.end.line === tokenOrCommentAfter!.loc.end.line - ) { - after = tokenOrCommentAfter - } - - return { - before, - after, - } -} diff --git a/utils/get-node-range.ts b/utils/get-node-range.ts index 86f0023c6..23bd836a5 100644 --- a/utils/get-node-range.ts +++ b/utils/get-node-range.ts @@ -3,7 +3,7 @@ import type { TSESTree } from '@typescript-eslint/types' import { ASTUtils } from '@typescript-eslint/utils' -import { getComment } from './get-comment' +import { getCommentBefore } from './get-comment-before' export let getNodeRange = ( node: TSESTree.Node, @@ -29,18 +29,14 @@ export let getNodeRange = ( end = bodyClosingParen.range.at(1)! } - let comment = getComment(node, sourceCode) + let comment = getCommentBefore(node, sourceCode) if (raw.endsWith(';') || raw.endsWith(',')) { end -= 1 } - if (comment.before) { - start = comment.before.range.at(0)! - } - - if (comment.after) { - end = comment.after.range.at(1)! + if (comment) { + start = comment.range.at(0)! } return [start, end] diff --git a/utils/make-fixes.ts b/utils/make-fixes.ts index 28a79b4f4..e2b22a99b 100644 --- a/utils/make-fixes.ts +++ b/utils/make-fixes.ts @@ -1,6 +1,8 @@ import type { TSESLint } from '@typescript-eslint/utils' +import type { TSESTree } from '@typescript-eslint/types' import type { SortingNode } from '../typings' +import { getCommentAfter } from './get-comment-after' import { getNodeRange } from './get-node-range' export let makeFixes = ( @@ -8,10 +10,40 @@ export let makeFixes = ( nodes: SortingNode[], sortedNodes: SortingNode[], source: TSESLint.SourceCode, -) => - nodes.map(({ node }, index) => - fixer.replaceTextRange( - getNodeRange(node, source), - source.text.slice(...getNodeRange(sortedNodes[index].node, source)), - ), - ) +) => { + let fixes: TSESLint.RuleFix[] = [] + + nodes.forEach(({ node }, index) => { + fixes.push( + fixer.replaceTextRange( + getNodeRange(node, source), + source.text.slice(...getNodeRange(sortedNodes[index].node, source)), + ), + ) + + let commentAfter = getCommentAfter(sortedNodes[index].node, source) + + if (commentAfter) { + let tokenBefore = source.getTokenBefore(commentAfter) + + let range: TSESTree.Range = [ + tokenBefore!.range.at(1)!, + commentAfter.range.at(1)!, + ] + + fixes.push(fixer.replaceTextRange(range, '')) + + let tokenAfterNode = source.getTokenAfter(node) + + fixes.push( + fixer.insertTextAfter( + tokenAfterNode?.loc.end.line === node.loc.end.line + ? tokenAfterNode + : node, + source.text.slice(...range), + ), + ) + } + }) + return fixes +}