@@ -42,9 +42,12 @@ module.exports = {
42
42
}
43
43
44
44
let iconName = null
45
- let isDynamic = false
45
+ let isConditional = false
46
+ let isMemberExpression = false
47
+ let conditionalExpression = null
48
+ let memberExpression = null
46
49
47
- // Analyze the icon prop to determine the icon name
50
+ // Analyze the icon prop to determine the icon name and type
48
51
if ( iconProp . value ?. type === 'JSXExpressionContainer' ) {
49
52
const expression = iconProp . value . expression
50
53
@@ -53,20 +56,25 @@ module.exports = {
53
56
iconName = expression . name
54
57
} else if ( expression . type === 'ConditionalExpression' ) {
55
58
// Conditional case: icon={condition ? XIcon : YIcon}
56
- // For now, we'll skip auto-fixing complex conditionals
57
- isDynamic = true
59
+ isConditional = true
60
+ conditionalExpression = expression
58
61
} else if ( expression . type === 'MemberExpression' ) {
59
62
// Dynamic lookup: icon={icons.x}
60
- isDynamic = true
63
+ isMemberExpression = true
64
+ memberExpression = expression
61
65
}
62
66
}
63
67
64
- if ( ! iconName && ! isDynamic ) {
68
+ if ( ! iconName && ! isConditional && ! isMemberExpression ) {
65
69
return
66
70
}
67
71
72
+ // Get all props except the icon prop to preserve them
73
+ const otherProps = openingElement . attributes . filter ( attr => attr !== iconProp )
74
+ const propsText = otherProps . map ( attr => sourceCode . getText ( attr ) ) . join ( ' ' )
75
+
68
76
// For simple cases, we can provide an autofix
69
- if ( iconName && ! isDynamic ) {
77
+ if ( iconName ) {
70
78
context . report ( {
71
79
node : openingElement ,
72
80
messageId : 'replaceDeprecatedOcticon' ,
@@ -111,8 +119,86 @@ module.exports = {
111
119
}
112
120
} ,
113
121
} )
122
+ } else if ( isConditional ) {
123
+ // Handle conditional expressions: icon={condition ? XIcon : YIcon}
124
+ // Transform to: condition ? <XIcon otherProps /> : <YIcon otherProps />
125
+ context . report ( {
126
+ node : openingElement ,
127
+ messageId : 'replaceDeprecatedOcticon' ,
128
+ * fix ( fixer ) {
129
+ const test = sourceCode . getText ( conditionalExpression . test )
130
+ const consequentName = conditionalExpression . consequent . type === 'Identifier'
131
+ ? conditionalExpression . consequent . name
132
+ : sourceCode . getText ( conditionalExpression . consequent )
133
+ const alternateName = conditionalExpression . alternate . type === 'Identifier'
134
+ ? conditionalExpression . alternate . name
135
+ : sourceCode . getText ( conditionalExpression . alternate )
136
+
137
+ const propsString = propsText ? ` ${ propsText } ` : ''
138
+ let replacement = `${ test } ? <${ consequentName } ${ propsString } /> : <${ alternateName } ${ propsString } />`
139
+
140
+ // If it has children, we need to include them in both branches
141
+ if ( node . children && node . children . length > 0 ) {
142
+ const childrenText = node . children . map ( child => sourceCode . getText ( child ) ) . join ( '' )
143
+ replacement = `${ test } ? <${ consequentName } ${ propsString } >${ childrenText } </${ consequentName } > : <${ alternateName } ${ propsString } >${ childrenText } </${ alternateName } >`
144
+ }
145
+
146
+ yield fixer . replaceText ( node , replacement )
147
+ } ,
148
+ } )
149
+ } else if ( isMemberExpression ) {
150
+ // Handle member expressions: icon={icons.x}
151
+ // Transform to: React.createElement(icons.x, otherProps)
152
+ context . report ( {
153
+ node : openingElement ,
154
+ messageId : 'replaceDeprecatedOcticon' ,
155
+ * fix ( fixer ) {
156
+ const memberText = sourceCode . getText ( memberExpression )
157
+
158
+ // Build props object
159
+ let propsObject = '{}'
160
+ if ( otherProps . length > 0 ) {
161
+ const propStrings = otherProps . map ( attr => {
162
+ if ( attr . type === 'JSXSpreadAttribute' ) {
163
+ return `...${ sourceCode . getText ( attr . argument ) } `
164
+ } else {
165
+ const name = attr . name . name
166
+ const value = attr . value
167
+ if ( ! value ) {
168
+ return `${ name } : true`
169
+ } else if ( value . type === 'Literal' ) {
170
+ return `${ name } : ${ JSON . stringify ( value . value ) } `
171
+ } else if ( value . type === 'JSXExpressionContainer' ) {
172
+ return `${ name } : ${ sourceCode . getText ( value . expression ) } `
173
+ }
174
+ return `${ name } : ${ sourceCode . getText ( value ) } `
175
+ }
176
+ } )
177
+ propsObject = `{${ propStrings . join ( ', ' ) } }`
178
+ }
179
+
180
+ let replacement = `React.createElement(${ memberText } , ${ propsObject } )`
181
+
182
+ // If it has children, include them as additional arguments
183
+ if ( node . children && node . children . length > 0 ) {
184
+ const childrenArgs = node . children . map ( child => {
185
+ if ( child . type === 'JSXText' ) {
186
+ return JSON . stringify ( child . value . trim ( ) ) . replace ( / \n \s * / g, ' ' )
187
+ } else {
188
+ return sourceCode . getText ( child )
189
+ }
190
+ } ) . filter ( child => child !== '""' ) // Filter out empty text nodes
191
+
192
+ if ( childrenArgs . length > 0 ) {
193
+ replacement = `React.createElement(${ memberText } , ${ propsObject } , ${ childrenArgs . join ( ', ' ) } )`
194
+ }
195
+ }
196
+
197
+ yield fixer . replaceText ( node , replacement )
198
+ } ,
199
+ } )
114
200
} else {
115
- // For complex cases, just report without autofix
201
+ // For other complex cases, just report without autofix
116
202
context . report ( {
117
203
node : openingElement ,
118
204
messageId : 'replaceDeprecatedOcticon' ,
0 commit comments