UNPKG

eslint-plugin-perfectionist

Version:

ESLint plugin for sorting various data such as objects, imports, types, enums, JSX props, etc.

131 lines (130 loc) 3.76 kB
import { getGroupIndex } from './get-group-index.js' import { sortNodes } from './sort-nodes.js' /** * Sorts nodes by distributing them into groups and sorting each group * independently. * * This is the core sorting function used by all Perfectionist rules. It * implements a two-phase sorting strategy: * * 1. Distribute nodes into their respective groups based on group configuration * 2. Sort each group independently using group-specific options. * * Ignored nodes (ESLint-disabled or manually ignored) maintain their original * positions to preserve intentional code organization. * * @example * * ```ts * // React component with grouped imports * const nodes = [ * { name: './Button', group: 'internal' }, * { name: 'react', group: 'external' }, * { name: '@mui/material', group: 'external' }, * { name: './utils', group: 'internal' }, * ] * * sortNodesByGroups({ * groups: ['external', 'internal'], * nodes, * optionsByGroupIndexComputer: index => ({ * options: { type: 'alphabetical', order: 'asc' }, * }), * }) * // Returns: ['@mui/material', 'react', './Button', './utils'] * // External group sorted first, then internal group * ``` * * @example * * ```ts * // Class members with different sorting for each group * class UserService { * // Static members group - sorted alphabetically * static VERSION = '1.0.0'; * static API_URL = 'https://api.example.com'; * * // Properties group - sorted by line length * id: string; * cache: Map<string, User>; * * // Methods group - sorted naturally * async getUser() { ... } * async updateUser() { ... } * } * ``` * * @example * * ```ts * // Object with ignored properties * const config = { * apiUrl: 'https://api.example.com', * timeout: 5000, * // eslint-disable-next-line * DEBUG_MODE: true, // This stays in place due to ESLint disable * retries: 3, * } * // DEBUG_MODE maintains its position despite sorting * ``` * * @template T - Type of sorting node. * @template Options - Type of sorting options. * @param params - Parameters for group-based sorting. * @returns Array of nodes sorted within their groups. */ function sortNodesByGroups({ comparatorByOptionsComputer, optionsByGroupIndexComputer, ignoreEslintDisabledNodes, isNodeIgnoredForGroup, isNodeIgnored, groups, nodes, }) { let nodesByNonIgnoredGroupIndex = {} let ignoredNodeIndices = [] for (let [index, sortingNode] of nodes.entries()) { if ( (sortingNode.isEslintDisabled && ignoreEslintDisabledNodes) || isNodeIgnored?.(sortingNode) ) { ignoredNodeIndices.push(index) continue } let groupIndex = getGroupIndex(groups, sortingNode) nodesByNonIgnoredGroupIndex[groupIndex] ??= [] nodesByNonIgnoredGroupIndex[groupIndex].push(sortingNode) } let sortedNodes = [] for (let groupIndexString of Object.keys( nodesByNonIgnoredGroupIndex, ).toSorted((a, b) => Number(a) - Number(b))) { let groupIndex = Number(groupIndexString) let options = optionsByGroupIndexComputer(groupIndex) let nodesToPush = nodesByNonIgnoredGroupIndex[groupIndex] let groupIgnoredNodes = new Set( nodesToPush.filter(node => isNodeIgnoredForGroup?.({ groupOptions: options, groupIndex, node, }), ), ) sortedNodes.push( ...sortNodes({ isNodeIgnored: node => groupIgnoredNodes.has(node), ignoreEslintDisabledNodes: false, comparatorByOptionsComputer, nodes: nodesToPush, options, }), ) } for (let ignoredIndex of ignoredNodeIndices) { sortedNodes.splice(ignoredIndex, 0, nodes[ignoredIndex]) } return sortedNodes } export { sortNodesByGroups }