@@ -42,9 +42,12 @@ module.exports = {
4242 }
4343
4444 let iconName = null
45- let isDynamic = false
45+ let isConditional = false
46+ let isMemberExpression = false
47+ let conditionalExpression = null
48+ let memberExpression = null
4649
47- // Analyze the icon prop to determine the icon name
50+ // Analyze the icon prop to determine the icon name and type
4851 if ( iconProp . value ?. type === 'JSXExpressionContainer' ) {
4952 const expression = iconProp . value . expression
5053
@@ -53,20 +56,25 @@ module.exports = {
5356 iconName = expression . name
5457 } else if ( expression . type === 'ConditionalExpression' ) {
5558 // 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
5861 } else if ( expression . type === 'MemberExpression' ) {
5962 // Dynamic lookup: icon={icons.x}
60- isDynamic = true
63+ isMemberExpression = true
64+ memberExpression = expression
6165 }
6266 }
6367
64- if ( ! iconName && ! isDynamic ) {
68+ if ( ! iconName && ! isConditional && ! isMemberExpression ) {
6569 return
6670 }
6771
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+
6876 // For simple cases, we can provide an autofix
69- if ( iconName && ! isDynamic ) {
77+ if ( iconName ) {
7078 context . report ( {
7179 node : openingElement ,
7280 messageId : 'replaceDeprecatedOcticon' ,
@@ -111,8 +119,86 @@ module.exports = {
111119 }
112120 } ,
113121 } )
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+ } )
114200 } else {
115- // For complex cases, just report without autofix
201+ // For other complex cases, just report without autofix
116202 context . report ( {
117203 node : openingElement ,
118204 messageId : 'replaceDeprecatedOcticon' ,
0 commit comments