UNPKG

eslint-plugin-perfectionist

Version:

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

325 lines (324 loc) 12.3 kB
import { UnreachableCaseError } from '../../utils/unreachable-case-error.js' import { getGroupIndex } from '../../utils/get-group-index.js' import { populateSortingNodeGroupsWithDependencies } from '../../utils/populate-sorting-node-groups-with-dependencies.js' import { validateNewlinesAndPartitionConfiguration } from '../../utils/validate-newlines-and-partition-configuration.js' import { defaultComparatorByOptionsComputer } from '../../utils/compare/default-comparator-by-options-computer.js' import { buildOptionsByGroupIndexComputer } from '../../utils/build-options-by-group-index-computer.js' import { validateCustomSortConfiguration } from '../../utils/validate-custom-sort-configuration.js' import { validateGroupsConfiguration } from '../../utils/validate-groups-configuration.js' import { generatePredefinedGroups } from '../../utils/generate-predefined-groups.js' import { sortNodesByDependencies } from '../../utils/sort-nodes-by-dependencies.js' import { getEslintDisabledLines } from '../../utils/get-eslint-disabled-lines.js' import { doesCustomGroupMatch } from '../../utils/does-custom-group-match.js' import { isNodeEslintDisabled } from '../../utils/is-node-eslint-disabled.js' import { sortNodesByGroups } from '../../utils/sort-nodes-by-groups.js' import { reportAllErrors } from '../../utils/report-all-errors.js' import { shouldPartition } from '../../utils/should-partition.js' import { computeGroup } from '../../utils/compute-group.js' import { rangeToDiff } from '../../utils/range-to-diff.js' import { getSettings } from '../../utils/get-settings.js' import { isSortable } from '../../utils/is-sortable.js' import { complete } from '../../utils/complete.js' import { getNodeDecorators } from '../../utils/get-node-decorators.js' import { getDecoratorName } from '../../utils/get-decorator-name.js' import { DEPENDENCY_ORDER_ERROR_ID, EXTRA_SPACING_ERROR_ID, GROUP_ORDER_ERROR_ID, MISSED_SPACING_ERROR_ID, ORDER_ERROR_ID, allModifiers, allSelectors, } from './types.js' import { buildOverloadSignatureNewlinesBetweenValueGetter } from '../../utils/overload-signature/build-overload-signature-newlines-between-value-getter.js' import { populateSortingNodeGroupsWithOverloadSignature } from '../../utils/overload-signature/populate-sorting-node-groups-with-overload-signature.js' import { computeIndexSignatureDetails } from './node-info/compute-index-signature-details.js' import { computeDependenciesBySortingNode } from './compute-dependencies-by-sorting-node.js' import { computeStaticBlockDetails } from './node-info/compute-static-block-details.js' import { computeOverloadSignatureGroups } from './compute-overload-signature-groups.js' import { computeMatchedContextOptions } from './compute-matched-context-options.js' import { computePropertyDetails } from './node-info/compute-property-details.js' import { computeAccessorDetails } from './node-info/compute-accessor-details.js' import { computeMethodDetails } from './node-info/compute-method-details.js' import { isKnownClassElement } from './is-known-class-element.js' import { AST_NODE_TYPES } from '@typescript-eslint/utils' /** * Cache computed groups by modifiers and selectors for performance. */ var cachedGroupsByModifiersAndSelectors = /* @__PURE__ */ new Map() var defaultOptions = { groups: [ 'index-signature', ['static-property', 'static-accessor-property'], ['static-get-method', 'static-set-method'], ['protected-static-property', 'protected-static-accessor-property'], ['protected-static-get-method', 'protected-static-set-method'], ['private-static-property', 'private-static-accessor-property'], ['private-static-get-method', 'private-static-set-method'], 'static-block', ['property', 'accessor-property'], ['get-method', 'set-method'], ['protected-property', 'protected-accessor-property'], ['protected-get-method', 'protected-set-method'], ['private-property', 'private-accessor-property'], ['private-get-method', 'private-set-method'], 'constructor', ['static-method', 'static-function-property'], ['protected-static-method', 'protected-static-function-property'], ['private-static-method', 'private-static-function-property'], ['method', 'function-property'], ['protected-method', 'protected-function-property'], ['private-method', 'private-function-property'], 'unknown', ], useExperimentalDependencyDetection: true, ignoreCallbackDependenciesPatterns: [], newlinesBetweenOverloadSignatures: 0, fallbackSort: { type: 'unsorted' }, newlinesInside: 'newlinesBetween', partitionByComment: false, partitionByNewLine: false, newlinesBetween: 'ignore', specialCharacters: 'keep', useConfigurationIf: {}, type: 'alphabetical', ignoreCase: true, customGroups: [], locales: 'en-US', alphabet: '', order: 'asc', } function sortClass({ matchedAstSelectors, context, node }) { let classElements = node.body.filter(isKnownClassElement) if (!isSortable(classElements)) { return } let settings = getSettings(context.settings) let options = complete( computeMatchedContextOptions({ matchedAstSelectors, classElements, context, }), settings, defaultOptions, ) validateCustomSortConfiguration(options) validateGroupsConfiguration({ modifiers: allModifiers, selectors: allSelectors, options, }) validateNewlinesAndPartitionConfiguration(options) let { sourceCode, id } = context let eslintDisabledLines = getEslintDisabledLines({ ruleName: id, sourceCode, }) let optionsByGroupIndexComputer = buildOptionsByGroupIndexComputer(options) let overloadSignatureNewlinesBetweenValueGetter = buildOverloadSignatureNewlinesBetweenValueGetter( options.newlinesBetweenOverloadSignatures, ) let className = node.parent.id?.name let sortingNodeGroupsWithoutOverloadSignature = classElements.reduce( (accumulator, member) => { let dependencies = [] let isDecorated = false let decorators = [] if ('decorators' in member) { decorators = getNodeDecorators(member).map(decorator => getDecoratorName({ sourceCode, decorator, }), ) isDecorated = decorators.length > 0 } let addSafetySemicolonWhenInline let dependencyNames let name let nameDetails let memberValue let isStatic let modifiers let selectors switch (member.type) { case AST_NODE_TYPES.TSAbstractPropertyDefinition: case AST_NODE_TYPES.PropertyDefinition: addSafetySemicolonWhenInline = true ;({ dependencyNames, dependencies, memberValue, nameDetails, modifiers, selectors, isStatic, } = computePropertyDetails({ ignoreCallbackDependenciesPatterns: options.ignoreCallbackDependenciesPatterns, useExperimentalDependencyDetection: options.useExperimentalDependencyDetection, property: member, isDecorated, sourceCode, className, })) ;({ name } = nameDetails) break case AST_NODE_TYPES.TSAbstractMethodDefinition: case AST_NODE_TYPES.MethodDefinition: dependencyNames = [] ;({ addSafetySemicolonWhenInline, nameDetails, selectors, modifiers, isStatic, } = computeMethodDetails({ hasParentDeclare: node.parent.declare, method: member, isDecorated, sourceCode, })) ;({ name } = nameDetails) break case AST_NODE_TYPES.TSAbstractAccessorProperty: case AST_NODE_TYPES.AccessorProperty: addSafetySemicolonWhenInline = true ;({ dependencyNames, nameDetails, selectors, modifiers, isStatic } = computeAccessorDetails({ accessor: member, isDecorated, sourceCode, })) ;({ name } = nameDetails) break case AST_NODE_TYPES.TSIndexSignature: addSafetySemicolonWhenInline = true dependencyNames = [] nameDetails = null isStatic = false ;({ modifiers, selectors, name } = computeIndexSignatureDetails({ indexSignature: member, sourceCode, })) break case AST_NODE_TYPES.StaticBlock: addSafetySemicolonWhenInline = false dependencyNames = [] name = 'static' nameDetails = null isStatic = true ;({ dependencies, selectors, modifiers } = computeStaticBlockDetails({ useExperimentalDependencyDetection: options.useExperimentalDependencyDetection, ignoreCallbackDependenciesPatterns: options.ignoreCallbackDependenciesPatterns, staticBlock: member, className, })) break /* v8 ignore next 2 -- @preserve Exhaustive guard. */ default: throw new UnreachableCaseError(member) } let group = computeGroup({ customGroupMatcher: customGroup => doesCustomGroupMatch({ elementValue: memberValue, elementName: name, customGroup, decorators, modifiers, selectors, }), predefinedGroups: generatePredefinedGroups({ cache: cachedGroupsByModifiersAndSelectors, selectors, modifiers, }), options, }) let sortingNode = { isEslintDisabled: isNodeEslintDisabled(member, eslintDisabledLines), size: rangeToDiff(member, sourceCode), addSafetySemicolonWhenInline, dependencyNames, node: member, dependencies, nameDetails, isStatic, group, name, } let lastSortingNode = accumulator.at(-1)?.at(-1) if ( shouldPartition({ lastSortingNode, sortingNode, sourceCode, options, }) ) { accumulator.push([]) } accumulator.at(-1).push({ ...sortingNode, partitionId: accumulator.length, }) return accumulator }, [[]], ) let sortingNodeGroups = populateSortingNodeGroupsWithOverloadSignature({ overloadSignatureGroups: computeOverloadSignatureGroups(classElements), sortingNodeGroups: sortingNodeGroupsWithoutOverloadSignature, }) if (options.useExperimentalDependencyDetection) { sortingNodeGroups = populateSortingNodeGroupsWithDependencies({ dependenciesBySortingNode: computeDependenciesBySortingNode({ ignoreCallbackDependenciesPatterns: options.ignoreCallbackDependenciesPatterns, sortingNodes: sortingNodeGroups.flat(), classBody: node, sourceCode, }), sortingNodeGroups, }) } let sortingNodes = sortingNodeGroups.flat() reportAllErrors({ availableMessageIds: { missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID, unexpectedDependencyOrder: DEPENDENCY_ORDER_ERROR_ID, extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID, unexpectedGroupOrder: GROUP_ORDER_ERROR_ID, unexpectedOrder: ORDER_ERROR_ID, }, newlinesBetweenValueGetter: overloadSignatureNewlinesBetweenValueGetter, sortNodesExcludingEslintDisabled, nodes: sortingNodes, options, context, }) function sortNodesExcludingEslintDisabled(ignoreEslintDisabledNodes) { return sortNodesByDependencies( sortingNodeGroups.flatMap(sortingNodeGroup => sortNodesByGroups({ isNodeIgnored: sortingNode => getGroupIndex(options.groups, sortingNode) === options.groups.length, comparatorByOptionsComputer: defaultComparatorByOptionsComputer, optionsByGroupIndexComputer, ignoreEslintDisabledNodes, nodes: sortingNodeGroup, groups: options.groups, }), ), { ignoreEslintDisabledNodes }, ) } } export { defaultOptions, sortClass }