11import { isIdentifier , isLiteral , isObjectExpression , isTemplateLiteral } from '../utils/nodes'
22import { 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'
46
57export const RULE_NAME = 'no-invalid-nesting'
68
@@ -21,22 +23,57 @@ const rule: Rule = createRule({
2123 return {
2224 Property ( node ) {
2325 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
2528 if ( isStyledProperty ( node , context ) ) return
2629
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
3032
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+ } )
3737 } ,
3838 }
3939 } ,
4040} )
4141
4242export 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