1
1
import { isIdentifier , isLiteral , isObjectExpression , isTemplateLiteral } from '../utils/nodes'
2
2
import { type Rule , createRule } from '../utils'
3
- import { isInJSXProp , isInPandaFunction , isStyledProperty } from '../utils/helpers'
3
+ import { getImports , isInJSXProp , isInPandaFunction , isStyledProperty } from '../utils/helpers'
4
+ import type { TSESTree } from '@typescript-eslint/utils'
5
+ import type { RuleContext } from '@typescript-eslint/utils/ts-eslint'
4
6
5
7
export const RULE_NAME = 'no-invalid-nesting'
6
8
@@ -21,22 +23,57 @@ const rule: Rule = createRule({
21
23
return {
22
24
Property ( node ) {
23
25
if ( ! isObjectExpression ( node . value ) || isIdentifier ( node . key ) ) return
24
- if ( ! isInPandaFunction ( node , context ) && ! isInJSXProp ( node , context ) ) return
26
+ const caller = isInPandaFunction ( node , context )
27
+ if ( ! caller && ! isInJSXProp ( node , context ) ) return
25
28
if ( isStyledProperty ( node , context ) ) return
26
29
27
- const invalidLiteral =
28
- isLiteral ( node . key ) && typeof node . key . value === 'string' && ! node . key . value . includes ( '&' )
29
- const invalidTemplateLiteral = isTemplateLiteral ( node . key ) && ! node . key . quasis [ 0 ] . value . raw . includes ( '&' )
30
+ const invalidNesting = isInvalidNesting ( node , context , caller )
31
+ if ( ! invalidNesting ) return
30
32
31
- if ( invalidLiteral || invalidTemplateLiteral ) {
32
- context . report ( {
33
- node : node . key ,
34
- messageId : 'nesting' ,
35
- } )
36
- }
33
+ context . report ( {
34
+ node : node . key ,
35
+ messageId : 'nesting' ,
36
+ } )
37
37
} ,
38
38
}
39
39
} ,
40
40
} )
41
41
42
42
export default rule
43
+
44
+ function isInvalidNesting ( node : TSESTree . Property , context : RuleContext < any , any > , caller : string | undefined ) {
45
+ // Check if the caller is either 'cva' or 'sva'
46
+ const recipe = getImports ( context ) . find ( ( imp ) => [ 'cva' , 'sva' ] . includes ( imp . name ) && imp . alias === caller )
47
+ if ( ! recipe ) return checkNode ( node )
48
+
49
+ //* Nesting is different here because of slots and variants. We don't want to warn about those.
50
+ let currentNode : any = node
51
+ let length = 0
52
+ let styleObjectParent = null
53
+
54
+ // Traverse up the AST
55
+ while ( currentNode ) {
56
+ if ( currentNode . key && [ 'base' , 'variants' ] . includes ( currentNode . key . name ) ) {
57
+ styleObjectParent = currentNode . key . name
58
+ }
59
+ currentNode = currentNode . parent
60
+ if ( ! styleObjectParent ) length ++
61
+ }
62
+
63
+ // Determine the required length based on caller and styleObjectParent
64
+ const requiredLength = caller === 'cva' ? 2 : 4
65
+ const extraLength = styleObjectParent === 'base' ? 0 : 4
66
+
67
+ if ( length >= requiredLength + extraLength ) {
68
+ return checkNode ( node )
69
+ }
70
+
71
+ return false
72
+ }
73
+
74
+ function checkNode ( node : TSESTree . Property ) {
75
+ const invalidLiteral = isLiteral ( node . key ) && typeof node . key . value === 'string' && ! node . key . value . includes ( '&' )
76
+ const invalidTemplateLiteral = isTemplateLiteral ( node . key ) && ! node . key . quasis [ 0 ] . value . raw . includes ( '&' )
77
+
78
+ return invalidLiteral || invalidTemplateLiteral
79
+ }
0 commit comments