213 lines
6.9 KiB
JavaScript
213 lines
6.9 KiB
JavaScript
import { getGroupIndex } from './get-group-index.js'
|
|
import { getCommentAboveThatShouldExist } from './get-comment-above-that-should-exist.js'
|
|
import { reportErrors } from './report-errors.js'
|
|
import { computeNodesInCircularDependencies } from './compute-nodes-in-circular-dependencies.js'
|
|
import { isNodeDependentOnOtherNode } from './is-node-dependent-on-other-node.js'
|
|
import { getNewlinesBetweenErrors } from './get-newlines-between-errors.js'
|
|
import { createNodeIndexMap } from './create-node-index-map.js'
|
|
import { pairwise } from './pairwise.js'
|
|
/**
|
|
* Detects and reports all sorting violations in a comprehensive manner.
|
|
*
|
|
* Performs a complete analysis of sorting errors including:
|
|
*
|
|
* - Order violations (elements in wrong positions)
|
|
* - Group order violations (elements in wrong groups)
|
|
* - Dependency violations (dependent elements before their dependencies)
|
|
* - Spacing issues (missing or extra newlines between groups)
|
|
* - Missing comment separators above groups.
|
|
*
|
|
* The function uses pairwise comparison to check each adjacent pair of nodes
|
|
* and accumulates all applicable error messages for each violation.
|
|
*
|
|
* @example
|
|
*
|
|
* ```ts
|
|
* // Import statements with violations
|
|
* import { useState } from 'react' // Should be after React import
|
|
* import React from 'react' // Group order violation
|
|
* import type { User } from './types' // Missing newline between groups
|
|
* ```
|
|
*
|
|
* @example
|
|
*
|
|
* ```ts
|
|
* // Object properties with dependency violation
|
|
* const config = {
|
|
* apiUrl: process.env.API_URL,
|
|
* baseUrl: this.apiUrl + '/v1', // Depends on apiUrl
|
|
* timeout: 5000,
|
|
* headers: {
|
|
* Authorization: this.token, // Should be after token definition
|
|
* },
|
|
* token: getAuthToken(),
|
|
* }
|
|
* ```
|
|
*
|
|
* @template MessageIds - Union of available message IDs.
|
|
* @template T - Type of sorting node.
|
|
* @param params - Parameters for error detection and reporting.
|
|
*/
|
|
function reportAllErrors({
|
|
ignoreFirstNodeHighestBlockComment,
|
|
sortNodesExcludingEslintDisabled,
|
|
newlinesBetweenValueGetter,
|
|
availableMessageIds,
|
|
context,
|
|
options,
|
|
nodes,
|
|
}) {
|
|
let { sourceCode } = context
|
|
let sortedNodes = sortNodesExcludingEslintDisabled(false)
|
|
let sortedNodesExcludingEslintDisabled =
|
|
sortNodesExcludingEslintDisabled(true)
|
|
let nodeIndexMap = createNodeIndexMap(sortedNodes)
|
|
let nodesInCircularDependencies =
|
|
availableMessageIds.unexpectedDependencyOrder ?
|
|
computeNodesInCircularDependencies(nodes)
|
|
: /* @__PURE__ */ new Set()
|
|
pairwise(nodes, (left, right) => {
|
|
let leftInfo =
|
|
left ?
|
|
{
|
|
groupIndex: getGroupIndex(options.groups, left),
|
|
index: nodeIndexMap.get(left),
|
|
}
|
|
: null
|
|
let rightGroupIndex = getGroupIndex(options.groups, right)
|
|
let rightIndex = nodeIndexMap.get(right)
|
|
let indexOfRightExcludingEslintDisabled =
|
|
sortedNodesExcludingEslintDisabled.indexOf(right)
|
|
let messageIds = []
|
|
let firstUnorderedNodeDependentOnRight
|
|
if (availableMessageIds.unexpectedDependencyOrder) {
|
|
firstUnorderedNodeDependentOnRight = getFirstUnorderedNodeDependentOn({
|
|
nodes,
|
|
node: right,
|
|
nodesInCircularDependencies,
|
|
})
|
|
}
|
|
if (
|
|
leftInfo &&
|
|
(firstUnorderedNodeDependentOnRight ||
|
|
leftInfo.index > rightIndex ||
|
|
(left?.isEslintDisabled &&
|
|
leftInfo.index >= indexOfRightExcludingEslintDisabled))
|
|
) {
|
|
if (firstUnorderedNodeDependentOnRight) {
|
|
messageIds.push(availableMessageIds.unexpectedDependencyOrder)
|
|
} else {
|
|
messageIds.push(
|
|
(
|
|
leftInfo.groupIndex === rightGroupIndex ||
|
|
!availableMessageIds.unexpectedGroupOrder
|
|
) ?
|
|
availableMessageIds.unexpectedOrder
|
|
: availableMessageIds.unexpectedGroupOrder,
|
|
)
|
|
}
|
|
}
|
|
if (
|
|
left &&
|
|
availableMessageIds.missedSpacingBetweenMembers &&
|
|
availableMessageIds.extraSpacingBetweenMembers
|
|
) {
|
|
messageIds.push(
|
|
...getNewlinesBetweenErrors({
|
|
options: {
|
|
...options,
|
|
newlinesBetween: options.newlinesBetween,
|
|
},
|
|
missedSpacingError: availableMessageIds.missedSpacingBetweenMembers,
|
|
extraSpacingError: availableMessageIds.extraSpacingBetweenMembers,
|
|
leftGroupIndex: leftInfo.groupIndex,
|
|
newlinesBetweenValueGetter,
|
|
rightGroupIndex,
|
|
sourceCode,
|
|
right,
|
|
left,
|
|
}),
|
|
)
|
|
}
|
|
let commentAboveMissing
|
|
if (availableMessageIds.missedCommentAbove) {
|
|
let commentAboveThatShouldExist = getCommentAboveThatShouldExist({
|
|
leftGroupIndex: leftInfo?.groupIndex ?? null,
|
|
sortingNode: right,
|
|
rightGroupIndex,
|
|
sourceCode,
|
|
options,
|
|
})
|
|
if (commentAboveThatShouldExist && !commentAboveThatShouldExist.exists) {
|
|
commentAboveMissing = commentAboveThatShouldExist.comment
|
|
messageIds.push(availableMessageIds.missedCommentAbove)
|
|
}
|
|
}
|
|
reportErrors({
|
|
sortedNodes: sortedNodesExcludingEslintDisabled,
|
|
ignoreFirstNodeHighestBlockComment,
|
|
firstUnorderedNodeDependentOnRight,
|
|
newlinesBetweenValueGetter,
|
|
commentAboveMissing,
|
|
messageIds,
|
|
sourceCode,
|
|
options,
|
|
context,
|
|
nodes,
|
|
right,
|
|
left,
|
|
})
|
|
})
|
|
}
|
|
/**
|
|
* Finds the first node that depends on the given node but appears before it.
|
|
*
|
|
* Detects dependency violations where a dependent element appears before its
|
|
* dependency in the current order. This is crucial for maintaining logical
|
|
* ordering in code where some elements reference others.
|
|
*
|
|
* Nodes in circular dependencies are excluded from this check as they cannot be
|
|
* properly ordered.
|
|
*
|
|
* @example
|
|
*
|
|
* ```ts
|
|
* // TypeScript interface extending another
|
|
* interface AdminUser extends User {
|
|
* // This depends on User
|
|
* permissions: string[]
|
|
* }
|
|
* interface User {
|
|
* // But User appears after AdminUser
|
|
* id: string
|
|
* name: string
|
|
* }
|
|
* // Returns: AdminUser node (appears before its dependency).
|
|
* ```
|
|
*
|
|
* @template T - Type of sorting node with dependencies.
|
|
* @param params - Parameters for finding dependent nodes.
|
|
* @param params.nodesInCircularDependencies - Set of nodes in circular
|
|
* dependencies.
|
|
* @param params.nodes - All nodes in current order.
|
|
* @param params.node - The node to check for dependents.
|
|
* @returns First dependent node appearing before its dependency, or undefined.
|
|
*/
|
|
function getFirstUnorderedNodeDependentOn({
|
|
nodesInCircularDependencies,
|
|
nodes,
|
|
node,
|
|
}) {
|
|
return nodes
|
|
.filter(
|
|
currentlyOrderedNode =>
|
|
!nodesInCircularDependencies.has(currentlyOrderedNode) &&
|
|
isNodeDependentOnOtherNode(node, currentlyOrderedNode),
|
|
)
|
|
.find(firstNodeDependentOnNode => {
|
|
let currentIndexOfNode = nodes.indexOf(node)
|
|
return nodes.indexOf(firstNodeDependentOnNode) < currentIndexOfNode
|
|
})
|
|
}
|
|
export { reportAllErrors }
|