eslint-plugin-perfectionist
Version:
ESLint plugin for sorting various data such as objects, imports, types, enums, JSX props, etc.
187 lines (186 loc) • 6.32 kB
JavaScript
import { UnreachableCaseError } from '../../utils/unreachable-case-error.js'
import { matches } from '../../utils/matches.js'
import { computeDependenciesBySortingNode as computeDependenciesBySortingNode$1 } from '../../utils/compute-dependencies-by-sorting-node.js'
import { computeParentNodesWithTypes } from '../../utils/compute-parent-nodes-with-types.js'
import { computeIdentifierNameDetails } from './compute-identifier-name-details.js'
import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'
function computeDependenciesBySortingNode({
ignoreCallbackDependenciesPatterns,
sortingNodes,
sourceCode,
classBody,
}) {
let dependenciesBySortingNode = computeDependenciesBySortingNode$1({
additionalIdentifierDependenciesComputer:
buildAdditionalIdentifierDependenciesComputer({
ignoreCallbackDependenciesPatterns,
staticSortingNodes: sortingNodes.filter(node => node.isStatic),
classBody,
}),
shouldIgnoreSortingNodeComputer: sortingNode =>
shouldIgnoreDependencyComputation(sortingNode.node),
sortingNodes,
sourceCode,
})
let thisDependenciesBySortingNode =
computeThisExpressionDependenciesBySortingNode({
ignoreCallbackDependenciesPatterns,
sortingNodes,
sourceCode,
})
for (let [sortingNode, dependencies] of thisDependenciesBySortingNode) {
let existingDependencies = dependenciesBySortingNode.get(sortingNode) ?? []
dependenciesBySortingNode.set(sortingNode, [
...existingDependencies,
...dependencies,
])
}
return dependenciesBySortingNode
}
function computeIdentifierOrThisExpressionDependency({
ignoreCallbackDependenciesPatterns,
sortingNodes,
classElement,
node,
}) {
if (shouldIgnoreCallbackDependency()) {
return null
}
let { parent } = node
/* v8 ignore if -- @preserve Unsure how we can reach that case */
if (parent.type !== AST_NODE_TYPES.MemberExpression) {
return null
}
let dependencyName = computeDependencyNameFromMemberExpression(parent)
/* v8 ignore if -- @preserve Unsure how we can reach that case */
if (!dependencyName) {
return null
}
return (
sortingNodes.find(
currentSortingNode => currentSortingNode.name === dependencyName.name,
) ?? null
)
function computeDependencyNameFromMemberExpression(memberExpression) {
switch (memberExpression.property.type) {
case AST_NODE_TYPES.PrivateIdentifier:
case AST_NODE_TYPES.Identifier:
case AST_NODE_TYPES.Literal:
return computeIdentifierNameDetails(memberExpression.property)
/* v8 ignore next 2 -- @preserve Unhandled cases */
default:
return null
}
}
function shouldIgnoreCallbackDependency() {
let [firstCallExpressionParent] = computeParentNodesWithTypes({
allowedTypes: [AST_NODE_TYPES.CallExpression],
maxParent: classElement,
consecutiveOnly: false,
node,
})
if (!firstCallExpressionParent) {
return false
}
if (!('name' in firstCallExpressionParent.callee)) {
return false
}
return matches(
firstCallExpressionParent.callee.name,
ignoreCallbackDependenciesPatterns,
)
}
}
function computeThisExpressionDependenciesBySortingNode({
ignoreCallbackDependenciesPatterns,
sortingNodes,
sourceCode,
}) {
let dependenciesBySortingNode = /* @__PURE__ */ new Map()
let staticSortingNodes = sortingNodes.filter(node => node.isStatic)
let nonStaticSortingNodes = sortingNodes.filter(node => !node.isStatic)
let relevantSortingNodes = sortingNodes.filter(
sortingNode => !shouldIgnoreDependencyComputation(sortingNode.node),
)
for (let sortingNode of relevantSortingNodes) {
let dependencies = computeThisExpressionsInsideClassElement({
classElement: sortingNode.node,
sourceCode,
})
.map(thisExpression =>
computeIdentifierOrThisExpressionDependency({
sortingNodes:
sortingNode.isStatic ? staticSortingNodes : nonStaticSortingNodes,
ignoreCallbackDependenciesPatterns,
classElement: sortingNode.node,
node: thisExpression,
}),
)
.filter(dependency => dependency !== null)
if (dependencies.length === 0) {
continue
}
dependenciesBySortingNode.set(sortingNode, dependencies)
}
return dependenciesBySortingNode
}
function buildAdditionalIdentifierDependenciesComputer({
ignoreCallbackDependenciesPatterns,
staticSortingNodes,
classBody,
}) {
return ({ referencingSortingNode, reference }) => {
if (!reference.resolved?.identifiers[0]) {
return []
}
let classIdentifier = classBody.parent.id
if (reference.resolved?.identifiers[0] !== classIdentifier) {
return []
}
let dependency = computeIdentifierOrThisExpressionDependency({
classElement: referencingSortingNode.node,
ignoreCallbackDependenciesPatterns,
sortingNodes: staticSortingNodes,
node: reference.identifier,
})
return dependency ? [dependency] : []
}
}
function shouldIgnoreDependencyComputation(node) {
switch (node.type) {
case AST_NODE_TYPES.TSAbstractPropertyDefinition:
case AST_NODE_TYPES.TSAbstractMethodDefinition:
case AST_NODE_TYPES.StaticBlock:
return false
case AST_NODE_TYPES.TSAbstractAccessorProperty:
case AST_NODE_TYPES.AccessorProperty:
case AST_NODE_TYPES.MethodDefinition:
case AST_NODE_TYPES.TSIndexSignature:
return true
case AST_NODE_TYPES.PropertyDefinition:
return (
node.value?.type === AST_NODE_TYPES.ArrowFunctionExpression ||
node.value?.type === AST_NODE_TYPES.FunctionExpression
)
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
default:
throw new UnreachableCaseError(node)
}
}
function computeThisExpressionsInsideClassElement({
classElement,
sourceCode,
}) {
return sourceCode
.getTokens(classElement)
.filter(isThisToken)
.map(computeTokenNode)
.filter(node => node?.type === AST_NODE_TYPES.ThisExpression)
function computeTokenNode(token) {
return sourceCode.getNodeByRangeIndex(token.range[0])
}
function isThisToken(token) {
return token.type === AST_TOKEN_TYPES.Keyword && token.value === 'this'
}
}
export { computeDependenciesBySortingNode }