eslint-plugin-perfectionist
Version:
ESLint plugin for sorting various data such as objects, imports, types, enums, JSX props, etc.
172 lines (171 loc) • 5.43 kB
JavaScript
import { getNodeDecorators } from '../../utils/get-node-decorators.js'
import { getDecoratorName } from '../../utils/get-decorator-name.js'
import { isPropertyOrAccessorNode } from './is-property-or-accessor-node.js'
import { isArrowFunctionNode } from './is-arrow-function-node.js'
import { computeDependencies } from './compute-dependencies.js'
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
/**
* Compute details about a module-related node.
*
* @param params - The parameters object.
* @param params.sourceCode - The source code object.
* @param params.node - The AST node to compute details for.
* @param params.useExperimentalDependencyDetection - Whether to use
* experimental dependency detection.
* @returns The computed details about the node, such as whether it should be
* ignored, if a module block was found, and information about the node.
*/
function computeNodeDetails({
useExperimentalDependencyDetection,
sourceCode,
node,
}) {
let selector
let name
let modifiers = []
let dependencies = []
let decorators = []
let addSafetySemicolonWhenInline = false
let moduleBlock = null
let shouldPartitionAfterNode = false
let ignoredDueToDecoratorBeforeExportClass = false
let dependencyDetection = 'soft'
let exportNode
parseNode(node)
if (!selector || !name || ignoredDueToDecoratorBeforeExportClass) {
return {
shouldPartitionAfterNode,
moduleBlock,
}
}
return {
nodeDetails: {
addSafetySemicolonWhenInline,
dependencyDetection,
dependencies,
decorators,
modifiers,
selector,
name,
},
}
function parseNode(nodeToParse) {
if ('declare' in nodeToParse && nodeToParse.declare) {
modifiers.push('declare')
}
switch (nodeToParse.type) {
case AST_NODE_TYPES.ExportDefaultDeclaration:
exportNode = nodeToParse
modifiers.push('default', 'export')
parseNode(nodeToParse.declaration)
break
case AST_NODE_TYPES.ExportNamedDeclaration:
exportNode = nodeToParse
if (nodeToParse.declaration) {
parseNode(nodeToParse.declaration)
}
modifiers.push('export')
break
case AST_NODE_TYPES.TSInterfaceDeclaration:
selector = 'interface'
;({ name } = nodeToParse.id)
break
case AST_NODE_TYPES.TSTypeAliasDeclaration:
selector = 'type'
;({ name } = nodeToParse.id)
addSafetySemicolonWhenInline = true
break
case AST_NODE_TYPES.FunctionDeclaration:
case AST_NODE_TYPES.TSDeclareFunction:
selector = 'function'
if (nodeToParse.async) {
modifiers.push('async')
}
if (modifiers.includes('declare')) {
addSafetySemicolonWhenInline = true
}
name = nodeToParse.id?.name
break
case AST_NODE_TYPES.TSModuleDeclaration:
shouldPartitionAfterNode = true
moduleBlock = nodeToParse.body ?? null
break
case AST_NODE_TYPES.VariableDeclaration:
shouldPartitionAfterNode = true
break
case AST_NODE_TYPES.TSEnumDeclaration:
selector = 'enum'
;({ name } = nodeToParse.id)
dependencyDetection = 'hard'
dependencies.push(
...extractDependencies(
nodeToParse,
useExperimentalDependencyDetection,
),
)
break
case AST_NODE_TYPES.ClassDeclaration:
selector = 'class'
name = nodeToParse.id?.name
let nodeDecorators = getNodeDecorators(nodeToParse)
if (nodeDecorators[0]) {
modifiers.push('decorated')
ignoredDueToDecoratorBeforeExportClass = isExportAfterDecorators({
firstDecorator: nodeDecorators[0],
exportNode,
})
}
decorators = nodeDecorators.map(decorator =>
getDecoratorName({
sourceCode,
decorator,
}),
)
dependencyDetection = 'hard'
dependencies.push(
...extractDependencies(
nodeToParse,
useExperimentalDependencyDetection,
),
)
break
/* v8 ignore next 2 -- @preserve Unhandled cases */
default:
break
}
}
}
/**
* Extract dependencies from an enum or class declaration.
*
* @deprecated - To remove when experimental dependency detection is the only
* option.
* @param expression - The enum or class declaration node.
* @param useExperimentalDependencyDetection - Whether to use experimental
* dependency detection.
* @returns The list of dependencies.
*/
function extractDependencies(expression, useExperimentalDependencyDetection) {
if (useExperimentalDependencyDetection) {
return []
}
return computeDependencies(expression, {
searchStaticMethodsAndFunctionProperties:
expression.type === AST_NODE_TYPES.ClassDeclaration &&
expression.body.body.some(
classElement =>
classElement.type === AST_NODE_TYPES.StaticBlock ||
(classElement.static &&
isPropertyOrAccessorNode(classElement) &&
!isArrowFunctionNode(classElement)),
),
type: 'hard',
})
}
function isExportAfterDecorators({ firstDecorator, exportNode }) {
if (!exportNode) {
return false
}
return exportNode.range[0] > firstDecorator.range[0]
}
export { computeNodeDetails }