From 45e82acf0a444577ac125000168c067405412692 Mon Sep 17 00:00:00 2001 From: "hugo.prunaux" Date: Tue, 15 Oct 2024 19:48:07 +0200 Subject: [PATCH] feat(sort-imports): adds utility function `makeNewlineFixes` --- rules/sort-imports.ts | 122 +++++++---------------------------- utils/get-newlines-errors.ts | 51 +++++++++++++++ utils/make-newlines-fixes.ts | 67 +++++++++++++++++++ 3 files changed, 143 insertions(+), 97 deletions(-) create mode 100644 utils/get-newlines-errors.ts create mode 100644 utils/make-newlines-fixes.ts diff --git a/rules/sort-imports.ts b/rules/sort-imports.ts index 1e91dbe6..90ec0614 100644 --- a/rules/sort-imports.ts +++ b/rules/sort-imports.ts @@ -1,5 +1,4 @@ import type { TSESTree } from '@typescript-eslint/types' -import type { TSESLint } from '@typescript-eslint/utils' import { builtinModules } from 'node:module' @@ -9,11 +8,11 @@ import { validateGroupsConfiguration } from '../utils/validate-groups-configurat import { getOptionsWithCleanGroups } from '../utils/get-options-with-clean-groups' import { sortNodesByGroups } from '../utils/sort-nodes-by-groups' import { getCommentsBefore } from '../utils/get-comments-before' +import { makeNewlinesFixes } from '../utils/make-newlines-fixes' +import { getNewlinesErrors } from '../utils/get-newlines-errors' import { createEslintRule } from '../utils/create-eslint-rule' -import { getLinesBetween } from '../utils/get-lines-between' import { getGroupNumber } from '../utils/get-group-number' import { getSourceCode } from '../utils/get-source-code' -import { getNodeRange } from '../utils/get-node-range' import { rangeToDiff } from '../utils/range-to-diff' import { getSettings } from '../utils/get-settings' import { useGroups } from '../utils/use-groups' @@ -596,28 +595,19 @@ export default createEslintRule, MESSAGE_ID>({ ) } - let numberOfEmptyLinesBetween = getLinesBetween( - sourceCode, - left, - right, - ) - if ( - options.newlinesBetween === 'never' && - numberOfEmptyLinesBetween > 0 - ) { - messageIds.push('extraSpacingBetweenImports') - } - - if (options.newlinesBetween === 'always') { - if (leftNum < rightNum && numberOfEmptyLinesBetween === 0) { - messageIds.push('missedSpacingBetweenImports') - } else if ( - numberOfEmptyLinesBetween > 1 || - (leftNum === rightNum && numberOfEmptyLinesBetween > 0) - ) { - messageIds.push('extraSpacingBetweenImports') - } - } + messageIds = [ + ...messageIds, + ...getNewlinesErrors({ + left, + leftNum, + right, + rightNum, + sourceCode, + missedSpacingError: 'missedSpacingBetweenImports', + extraSpacingError: 'extraSpacingBetweenImports', + options, + }), + ] for (let messageId of messageIds) { context.report({ @@ -629,78 +619,16 @@ export default createEslintRule, MESSAGE_ID>({ rightGroup: right.group, }, node: right.node, - fix: fixer => { - let newlinesFixes: TSESLint.RuleFix[] = [] - - for (let max = sortedNodes.length, i = 0; i < max; i++) { - let node = sortedNodes.at(i)! - let nextNode = sortedNodes.at(i + 1) - - if (options.newlinesBetween === 'ignore' || !nextNode) { - continue - } - - let nodeGroupNumber = getGroupNumber(options.groups, node) - let nextNodeGroupNumber = getGroupNumber( - options.groups, - nextNode, - ) - let currentNodeRange = getNodeRange( - nodeList.at(i)!.node, - sourceCode, - options, - ) - let nextNodeRange = - getNodeRange( - nodeList.at(i + 1)!.node, - sourceCode, - options, - ).at(0)! - 1 - - let linesBetweenImports = getLinesBetween( - sourceCode, - nodeList.at(i)!, - nodeList.at(i + 1)!, - ) - - if ( - (options.newlinesBetween === 'always' && - nodeGroupNumber === nextNodeGroupNumber && - linesBetweenImports !== 0) || - (options.newlinesBetween === 'never' && - linesBetweenImports > 0) - ) { - newlinesFixes.push( - fixer.removeRange([ - currentNodeRange.at(1)!, - nextNodeRange, - ]), - ) - } - if ( - options.newlinesBetween === 'always' && - nodeGroupNumber !== nextNodeGroupNumber - ) { - if (linesBetweenImports > 1) { - newlinesFixes.push( - fixer.replaceTextRange( - [currentNodeRange.at(1)!, nextNodeRange], - '\n', - ), - ) - } else if (linesBetweenImports === 0) { - newlinesFixes.push( - fixer.insertTextAfterRange(currentNodeRange, '\n'), - ) - } - } - } - - return [ - ...makeFixes(fixer, nodeList, sortedNodes, sourceCode), - ...newlinesFixes, - ] - }, + fix: fixer => [ + ...makeFixes(fixer, nodeList, sortedNodes, sourceCode), + ...makeNewlinesFixes( + fixer, + nodeList, + sortedNodes, + sourceCode, + options, + ), + ], }) } }) diff --git a/utils/get-newlines-errors.ts b/utils/get-newlines-errors.ts new file mode 100644 index 00000000..fa83bbb7 --- /dev/null +++ b/utils/get-newlines-errors.ts @@ -0,0 +1,51 @@ +import type { TSESLint } from '@typescript-eslint/utils' + +import type { SortingNode } from '../typings' + +import { getLinesBetween } from './get-lines-between' + +interface Options { + newlinesBetween: 'ignore' | 'always' | 'never' +} + +interface Props { + sourceCode: TSESLint.SourceCode + missedSpacingError: T + extraSpacingError: T + right: SortingNode + left: SortingNode + rightNum: number + options: Options + leftNum: number +} + +export let getNewlinesErrors = ({ + missedSpacingError, + extraSpacingError, + sourceCode, + rightNum, + leftNum, + options, + right, + left, +}: Props) => { + let errors: T[] = [] + + let numberOfEmptyLinesBetween = getLinesBetween(sourceCode, left, right) + if (options.newlinesBetween === 'never' && numberOfEmptyLinesBetween > 0) { + errors.push(extraSpacingError) + } + + if (options.newlinesBetween === 'always') { + if (leftNum < rightNum && numberOfEmptyLinesBetween === 0) { + errors.push(missedSpacingError) + } else if ( + numberOfEmptyLinesBetween > 1 || + (leftNum === rightNum && numberOfEmptyLinesBetween > 0) + ) { + errors.push(extraSpacingError) + } + } + + return errors +} diff --git a/utils/make-newlines-fixes.ts b/utils/make-newlines-fixes.ts new file mode 100644 index 00000000..ddb8f5ac --- /dev/null +++ b/utils/make-newlines-fixes.ts @@ -0,0 +1,67 @@ +import type { TSESLint } from '@typescript-eslint/utils' + +import type { SortingNode } from '../typings' + +import { getLinesBetween } from './get-lines-between' +import { getGroupNumber } from './get-group-number' +import { getNodeRange } from './get-node-range' + +interface Options { + newlinesBetween: 'ignore' | 'always' | 'never' + groups: (string[] | string)[] +} + +export let makeNewlinesFixes = ( + fixer: TSESLint.RuleFixer, + nodes: SortingNode[], + sortedNodes: SortingNode[], + source: TSESLint.SourceCode, + options: Options, +) => { + let fixes: TSESLint.RuleFix[] = [] + + for (let max = sortedNodes.length, i = 0; i < max; i++) { + let node = sortedNodes.at(i)! + let nextNode = sortedNodes.at(i + 1) + + if (options.newlinesBetween === 'ignore' || !nextNode) { + continue + } + + let nodeGroupNumber = getGroupNumber(options.groups, node) + let nextNodeGroupNumber = getGroupNumber(options.groups, nextNode) + let currentNodeRange = getNodeRange(nodes.at(i)!.node, source) + let nextNodeRange = getNodeRange(nodes.at(i + 1)!.node, source).at(0)! - 1 + + let linesBetweenImports = getLinesBetween( + source, + nodes.at(i)!, + nodes.at(i + 1)!, + ) + + if ( + (options.newlinesBetween === 'always' && + nodeGroupNumber === nextNodeGroupNumber && + linesBetweenImports !== 0) || + (options.newlinesBetween === 'never' && linesBetweenImports > 0) + ) { + fixes.push(fixer.removeRange([currentNodeRange.at(1)!, nextNodeRange])) + } + if ( + options.newlinesBetween === 'always' && + nodeGroupNumber !== nextNodeGroupNumber + ) { + if (linesBetweenImports > 1) { + fixes.push( + fixer.replaceTextRange( + [currentNodeRange.at(1)!, nextNodeRange], + '\n', + ), + ) + } else if (linesBetweenImports === 0) { + fixes.push(fixer.insertTextAfterRange(currentNodeRange, '\n')) + } + } + } + return fixes +}