@@ -12,11 +12,13 @@ const components = {
12
12
replacement : 'div' ,
13
13
messageId : 'unecessaryBox' ,
14
14
message : 'Prefer plain HTML elements over `Box` when not using `sx` for styling.' ,
15
+ allowedProps : new Set ( [ 'sx' ] ) , // + styled-system props
15
16
} ,
16
17
Text : {
17
18
replacement : 'span' ,
18
19
messageId : 'unecessarySpan' ,
19
20
message : 'Prefer plain HTML elements over `Text` when not using `sx` for styling.' ,
21
+ allowedProps : new Set ( [ 'sx' , 'size' , 'weight' ] ) , // + styled-system props
20
22
} ,
21
23
}
22
24
@@ -68,33 +70,36 @@ const rule = ESLintUtils.RuleCreator.withoutDocs({
68
70
const isPrimer = skipImportCheck || isPrimerComponent ( name , context . sourceCode . getScope ( openingElement ) )
69
71
if ( ! isPrimer ) return
70
72
71
- // Validate the attributes and ensure an `sx` prop is present or spreaded in
73
+ /** @param {string } name */
74
+ const isAllowedProp = name => componentConfig . allowedProps . has ( name ) || isStyledSystemProp ( name )
75
+
76
+ // Validate the attributes and ensure an allowed prop is present or spreaded in
72
77
/** @type {typeof attributes[number] | undefined | null } */
73
78
let asProp = undefined
74
79
for ( const attribute of attributes ) {
75
- // If there is a spread type, check if the type of the spreaded value has an `sx` property
80
+ // If there is a spread type, check if the type of the spreaded value has an allowed property
76
81
if ( attribute . type === 'JSXSpreadAttribute' ) {
77
82
const services = ESLintUtils . getParserServices ( context )
78
83
const typeChecker = services . program . getTypeChecker ( )
79
84
80
85
const spreadType = services . getTypeAtLocation ( attribute . argument )
81
- if ( typeChecker . getPropertyOfType ( spreadType , 'sx' ) !== undefined ) return
82
86
83
- // Check if the spread type has a string index signature - this could hide an `sx` property
87
+ // Check if the spread type has a string index signature - this could hide an allowed property
84
88
if ( typeChecker . getIndexTypeOfType ( spreadType , IndexKind . String ) !== undefined ) return
85
89
90
+ const spreadPropNames = typeChecker . getPropertiesOfType ( spreadType ) . map ( prop => prop . getName ( ) )
91
+
92
+ // If an allowed prop gets spread in, this is a valid use of the component
93
+ if ( spreadPropNames . some ( isAllowedProp ) ) return
94
+
86
95
// If there is an `as` inside the spread object, we can't autofix reliably
87
- if ( typeChecker . getPropertyOfType ( spreadType , 'as' ) !== undefined ) asProp = null
96
+ if ( spreadPropNames . includes ( 'as' ) ) asProp = null
88
97
89
98
continue
90
99
}
91
100
92
- // Has sx prop, so should keep using this component
93
- if (
94
- attribute . name . type === 'JSXIdentifier' &&
95
- ( attribute . name . name === 'sx' || isStyledSystemProp ( attribute . name . name ) )
96
- )
97
- return
101
+ // Has an allowed prop, so should keep using this component
102
+ if ( attribute . name . type === 'JSXIdentifier' && isAllowedProp ( attribute . name . name ) ) return
98
103
99
104
// If there is an `as` prop we will need to account for that when autofixing
100
105
if ( attribute . name . type === 'JSXIdentifier' && attribute . name . name === 'as' ) asProp = attribute
0 commit comments