Skip to content

Commit 5a5cc5c

Browse files
committed
fix: fixes complex decorators not being handled
1 parent 8eac6cd commit 5a5cc5c

8 files changed

+181
-109
lines changed

rules/sort-classes.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ import { doesCustomGroupMatch } from './sort-classes/does-custom-group-match'
4040
import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
4141
import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
4242
import { sortNodesByGroups } from '../utils/sort-nodes-by-groups'
43+
import { getNodeDecorators } from '../utils/get-node-decorators'
4344
import { createEslintRule } from '../utils/create-eslint-rule'
45+
import { getDecoratorName } from '../utils/get-decorator-name'
4446
import { reportAllErrors } from '../utils/report-all-errors'
4547
import { shouldPartition } from '../utils/should-partition'
4648
import { getGroupNumber } from '../utils/get-group-number'
@@ -319,17 +321,10 @@ export default createEslintRule<SortClassesOptions, MESSAGE_ID>({
319321
let decorators: string[] = []
320322

321323
if ('decorators' in member) {
322-
decorated = member.decorators.length > 0
323-
for (let decorator of member.decorators) {
324-
if (decorator.expression.type === 'Identifier') {
325-
decorators.push(decorator.expression.name)
326-
} else if (
327-
decorator.expression.type === 'CallExpression' &&
328-
decorator.expression.callee.type === 'Identifier'
329-
) {
330-
decorators.push(decorator.expression.callee.name)
331-
}
332-
}
324+
decorators = getNodeDecorators(member).map(decorator =>
325+
getDecoratorName({ sourceCode, decorator }),
326+
)
327+
decorated = decorators.length > 0
333328
}
334329

335330
let memberValue: undefined | string

rules/sort-decorators.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ let sortDecorators = (
204204
(accumulator: SortDecoratorsSortingNode[][], decorator) => {
205205
let { setCustomGroups, getGroup } = useGroups(options)
206206
let name = getDecoratorName({
207+
sourceCode,
207208
decorator,
208209
})
209210

rules/sort-modules.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
4343
import { sortNodesByGroups } from '../utils/sort-nodes-by-groups'
4444
import { getNodeDecorators } from '../utils/get-node-decorators'
4545
import { createEslintRule } from '../utils/create-eslint-rule'
46+
import { getDecoratorName } from '../utils/get-decorator-name'
4647
import { reportAllErrors } from '../utils/report-all-errors'
4748
import { shouldPartition } from '../utils/should-partition'
4849
import { getGroupNumber } from '../utils/get-group-number'
@@ -257,16 +258,12 @@ let analyzeModule = ({
257258
if (nodeDecorators.length > 0) {
258259
modifiers.push('decorated')
259260
}
260-
for (let decorator of nodeDecorators) {
261-
if (decorator.expression.type === 'Identifier') {
262-
decorators.push(decorator.expression.name)
263-
} else if (
264-
decorator.expression.type === 'CallExpression' &&
265-
decorator.expression.callee.type === 'Identifier'
266-
) {
267-
decorators.push(decorator.expression.callee.name)
268-
}
269-
}
261+
decorators = nodeDecorators.map(decorator =>
262+
getDecoratorName({
263+
sourceCode,
264+
decorator,
265+
}),
266+
)
270267
dependencies = [
271268
...dependencies,
272269
...(nodeToParse.superClass && 'name' in nodeToParse.superClass

test/rules/sort-classes.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7721,6 +7721,54 @@ describe(ruleName, () => {
77217721
}
77227722
`,
77237723
},
7724+
{
7725+
options: [
7726+
{
7727+
customGroups: [
7728+
{
7729+
decoratorNamePattern: 'B',
7730+
groupName: 'B',
7731+
},
7732+
{
7733+
decoratorNamePattern: 'A',
7734+
groupName: 'A',
7735+
},
7736+
],
7737+
type: 'alphabetical',
7738+
groups: ['B', 'A'],
7739+
order: 'asc',
7740+
},
7741+
],
7742+
errors: [
7743+
{
7744+
data: {
7745+
rightGroup: 'B',
7746+
leftGroup: 'A',
7747+
right: 'b',
7748+
left: 'a',
7749+
},
7750+
messageId: 'unexpectedClassesGroupOrder',
7751+
},
7752+
],
7753+
output: dedent`
7754+
class Class {
7755+
@B.B()
7756+
b() {}
7757+
7758+
@A.A.A(() => A)
7759+
a() {}
7760+
}
7761+
`,
7762+
code: dedent`
7763+
class Class {
7764+
@A.A.A(() => A)
7765+
a() {}
7766+
7767+
@B.B()
7768+
b() {}
7769+
}
7770+
`,
7771+
},
77247772
],
77257773
valid: [],
77267774
})

test/rules/sort-decorators.test.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,74 @@ describe(ruleName, () => {
578578
},
579579
],
580580
},
581+
{
582+
output: dedent`
583+
@B.B()
584+
@A.A.A(() => A)
585+
class Class {
586+
587+
@B.B()
588+
@A.A.A(() => A)
589+
property
590+
591+
@B.B()
592+
@A.A.A(() => A)
593+
accessor field
594+
595+
@B.B()
596+
@A.A.A(() => A)
597+
method(
598+
@B.B()
599+
@A.A.A(() => A)
600+
parameter) {}
601+
602+
}
603+
`,
604+
code: dedent`
605+
@A.A.A(() => A)
606+
@B.B()
607+
class Class {
608+
609+
@A.A.A(() => A)
610+
@B.B()
611+
property
612+
613+
@A.A.A(() => A)
614+
@B.B()
615+
accessor field
616+
617+
@A.A.A(() => A)
618+
@B.B()
619+
method(
620+
@A.A.A(() => A)
621+
@B.B()
622+
parameter) {}
623+
624+
}
625+
`,
626+
errors: duplicate5Times([
627+
{
628+
data: {
629+
rightGroup: 'B',
630+
leftGroup: 'A',
631+
left: 'A.A.A',
632+
right: 'B.B',
633+
},
634+
messageId: 'unexpectedDecoratorsGroupOrder',
635+
},
636+
]),
637+
options: [
638+
{
639+
customGroups: {
640+
A: 'A',
641+
B: 'B',
642+
},
643+
type: 'alphabetical',
644+
groups: ['B', 'A'],
645+
order: 'asc',
646+
},
647+
],
648+
},
581649
],
582650
valid: [],
583651
},

test/rules/sort-decorators/get-decorator-name.test.ts

Lines changed: 0 additions & 69 deletions
This file was deleted.

test/rules/sort-modules.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,50 @@ describe(ruleName, () => {
489489
class AnotherClass {}
490490
`,
491491
},
492+
{
493+
options: [
494+
{
495+
customGroups: [
496+
{
497+
decoratorNamePattern: 'B',
498+
groupName: 'B',
499+
},
500+
{
501+
decoratorNamePattern: 'A',
502+
groupName: 'A',
503+
},
504+
],
505+
type: 'alphabetical',
506+
groups: ['B', 'A'],
507+
order: 'asc',
508+
},
509+
],
510+
errors: [
511+
{
512+
data: {
513+
rightGroup: 'B',
514+
leftGroup: 'A',
515+
right: 'B',
516+
left: 'A',
517+
},
518+
messageId: 'unexpectedModulesGroupOrder',
519+
},
520+
],
521+
output: dedent`
522+
@B.B()
523+
class B {}
524+
525+
@A.A.A(() => A)
526+
class A {}
527+
`,
528+
code: dedent`
529+
@A.A.A(() => A)
530+
class A {}
531+
532+
@B.B()
533+
class B {}
534+
`,
535+
},
492536
],
493537
valid: [],
494538
})

utils/get-decorator-name.ts

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,16 @@
11
import type { TSESTree } from '@typescript-eslint/types'
2-
3-
import { AST_NODE_TYPES } from '@typescript-eslint/types'
2+
import type { TSESLint } from '@typescript-eslint/utils'
43

54
export let getDecoratorName = ({
5+
sourceCode,
66
decorator,
77
}: {
8+
sourceCode: TSESLint.SourceCode
89
decorator: TSESTree.Decorator
910
}): string => {
10-
switch (decorator.expression.type) {
11-
case AST_NODE_TYPES.CallExpression:
12-
if (decorator.expression.callee.type !== AST_NODE_TYPES.Identifier) {
13-
throw new Error(
14-
"Unexpected decorator expression's callee type. Please 'report this " +
15-
'issue here: ' +
16-
'https://github.com/azat-io/eslint-plugin-perfectionist/issues',
17-
)
18-
}
19-
return decorator.expression.callee.name
20-
case AST_NODE_TYPES.Identifier:
21-
return decorator.expression.name
22-
default:
23-
throw new Error(
24-
'Unexpected decorator expression type. Please report this issue here: ' +
25-
'https://github.com/azat-io/eslint-plugin-perfectionist/issues',
26-
)
11+
let fullName = sourceCode.getText(decorator)
12+
if (fullName.startsWith('@')) {
13+
fullName = fullName.slice(1)
2714
}
15+
return fullName.split('(')[0]!
2816
}

0 commit comments

Comments
 (0)