@@ -25,8 +25,15 @@ module.exports = {
2525 create ( context ) {
2626 const sourceCode = context . getSourceCode ( )
2727
28- // Track Octicon imports
28+ // Track Octicon imports and usages
2929 const octiconImports = [ ]
30+ let totalOcticonUsages = 0
31+ let processedOcticonUsages = 0
32+
33+ // Count total Octicon usages with icon props at the start
34+ const sourceText = sourceCode . getText ( )
35+ const octiconMatches = sourceText . match ( / < O c t i c o n [ ^ > ] * i c o n = / g)
36+ totalOcticonUsages = octiconMatches ? octiconMatches . length : 0
3037
3138 return {
3239 ImportDeclaration ( node ) {
@@ -90,72 +97,6 @@ module.exports = {
9097 const otherProps = openingElement . attributes . filter ( attr => attr !== iconProp )
9198 const propsText = otherProps . map ( attr => sourceCode . getText ( attr ) ) . join ( ' ' )
9299
93- // Helper function to determine if this is the last Octicon in the file that needs fixing
94- function isLastOcticonToFix ( ) {
95- // Get all JSX elements in the source code that are Octicons with icon props
96- const sourceText = sourceCode . getText ( )
97- const lines = sourceText . split ( '\n' )
98-
99- // Find all potential Octicon lines
100- const octiconLines = [ ]
101- for ( const [ index , line ] of lines . entries ( ) ) {
102- if ( line . includes ( '<Octicon' ) && line . includes ( 'icon=' ) ) {
103- octiconLines . push ( index + 1 ) // 1-based line numbers
104- }
105- }
106-
107- // Get the line number of the current node
108- const currentLine = sourceCode . getLocFromIndex ( node . range [ 0 ] ) . line
109-
110- // Check if this is the last one
111- const currentIndex = octiconLines . indexOf ( currentLine )
112- return currentIndex === octiconLines . length - 1
113- }
114-
115- // Helper function to generate import fixes if this is the last Octicon usage
116- function * generateImportFixes ( fixer ) {
117- if ( isLastOcticonToFix ( ) && octiconImports . length > 0 ) {
118- const importNode = octiconImports [ 0 ]
119- const octiconSpecifier = importNode . specifiers . find (
120- specifier => specifier . imported && specifier . imported . name === 'Octicon' ,
121- )
122-
123- if ( importNode . specifiers . length === 1 ) {
124- // Octicon is the only import, remove the entire import statement
125- // Also remove trailing newline if present
126- const nextToken = sourceCode . getTokenAfter ( importNode )
127- const importEnd = importNode . range [ 1 ]
128- const nextStart = nextToken ? nextToken . range [ 0 ] : sourceCode . getText ( ) . length
129- const textBetween = sourceCode . getText ( ) . substring ( importEnd , nextStart )
130- const hasTrailingNewline = / ^ \s * \n / . test ( textBetween )
131-
132- if ( hasTrailingNewline ) {
133- const newlineMatch = textBetween . match ( / ^ \s * \n / )
134- const endRange = importEnd + newlineMatch [ 0 ] . length
135- yield fixer . removeRange ( [ importNode . range [ 0 ] , endRange ] )
136- } else {
137- yield fixer . remove ( importNode )
138- }
139- } else {
140- // Remove just the Octicon specifier from the import
141- const previousToken = sourceCode . getTokenBefore ( octiconSpecifier )
142- const nextToken = sourceCode . getTokenAfter ( octiconSpecifier )
143- const hasTrailingComma = nextToken && nextToken . value === ','
144- const hasLeadingComma = previousToken && previousToken . value === ','
145-
146- let rangeToRemove
147- if ( hasTrailingComma ) {
148- rangeToRemove = [ octiconSpecifier . range [ 0 ] , nextToken . range [ 1 ] + 1 ]
149- } else if ( hasLeadingComma ) {
150- rangeToRemove = [ previousToken . range [ 0 ] , octiconSpecifier . range [ 1 ] ]
151- } else {
152- rangeToRemove = [ octiconSpecifier . range [ 0 ] , octiconSpecifier . range [ 1 ] ]
153- }
154- yield fixer . removeRange ( rangeToRemove )
155- }
156- }
157- }
158-
159100 // For simple cases, we can provide an autofix
160101 if ( iconName ) {
161102 context . report ( {
@@ -202,7 +143,48 @@ module.exports = {
202143 }
203144
204145 // Handle import removal if this is the last Octicon usage
205- yield * generateImportFixes ( fixer )
146+ // Only check for last octicon after incrementing counter to ensure accurate count
147+ processedOcticonUsages ++
148+ if ( processedOcticonUsages === totalOcticonUsages && octiconImports . length > 0 ) {
149+ const importNode = octiconImports [ 0 ]
150+ const octiconSpecifier = importNode . specifiers . find (
151+ specifier => specifier . imported && specifier . imported . name === 'Octicon' ,
152+ )
153+
154+ if ( importNode . specifiers . length === 1 ) {
155+ // Octicon is the only import, remove the entire import statement
156+ // Also remove trailing newline if present
157+ const nextToken = sourceCode . getTokenAfter ( importNode )
158+ const importEnd = importNode . range [ 1 ]
159+ const nextStart = nextToken ? nextToken . range [ 0 ] : sourceCode . getText ( ) . length
160+ const textBetween = sourceCode . getText ( ) . substring ( importEnd , nextStart )
161+ const hasTrailingNewline = / ^ \s * \n / . test ( textBetween )
162+
163+ if ( hasTrailingNewline ) {
164+ const newlineMatch = textBetween . match ( / ^ \s * \n / )
165+ const endRange = importEnd + newlineMatch [ 0 ] . length
166+ yield fixer . removeRange ( [ importNode . range [ 0 ] , endRange ] )
167+ } else {
168+ yield fixer . remove ( importNode )
169+ }
170+ } else {
171+ // Remove just the Octicon specifier from the import
172+ const previousToken = sourceCode . getTokenBefore ( octiconSpecifier )
173+ const nextToken = sourceCode . getTokenAfter ( octiconSpecifier )
174+ const hasTrailingComma = nextToken && nextToken . value === ','
175+ const hasLeadingComma = previousToken && previousToken . value === ','
176+
177+ let rangeToRemove
178+ if ( hasTrailingComma ) {
179+ rangeToRemove = [ octiconSpecifier . range [ 0 ] , nextToken . range [ 1 ] + 1 ]
180+ } else if ( hasLeadingComma ) {
181+ rangeToRemove = [ previousToken . range [ 0 ] , octiconSpecifier . range [ 1 ] ]
182+ } else {
183+ rangeToRemove = [ octiconSpecifier . range [ 0 ] , octiconSpecifier . range [ 1 ] ]
184+ }
185+ yield fixer . removeRange ( rangeToRemove )
186+ }
187+ }
206188 } ,
207189 } )
208190 } else if ( isConditional ) {
@@ -234,7 +216,47 @@ module.exports = {
234216 yield fixer . replaceText ( node , replacement )
235217
236218 // Handle import removal if this is the last Octicon usage
237- yield * generateImportFixes ( fixer )
219+ processedOcticonUsages ++
220+ if ( processedOcticonUsages === totalOcticonUsages && octiconImports . length > 0 ) {
221+ const importNode = octiconImports [ 0 ]
222+ const octiconSpecifier = importNode . specifiers . find (
223+ specifier => specifier . imported && specifier . imported . name === 'Octicon' ,
224+ )
225+
226+ if ( importNode . specifiers . length === 1 ) {
227+ // Octicon is the only import, remove the entire import statement
228+ // Also remove trailing newline if present
229+ const nextToken = sourceCode . getTokenAfter ( importNode )
230+ const importEnd = importNode . range [ 1 ]
231+ const nextStart = nextToken ? nextToken . range [ 0 ] : sourceCode . getText ( ) . length
232+ const textBetween = sourceCode . getText ( ) . substring ( importEnd , nextStart )
233+ const hasTrailingNewline = / ^ \s * \n / . test ( textBetween )
234+
235+ if ( hasTrailingNewline ) {
236+ const newlineMatch = textBetween . match ( / ^ \s * \n / )
237+ const endRange = importEnd + newlineMatch [ 0 ] . length
238+ yield fixer . removeRange ( [ importNode . range [ 0 ] , endRange ] )
239+ } else {
240+ yield fixer . remove ( importNode )
241+ }
242+ } else {
243+ // Remove just the Octicon specifier from the import
244+ const previousToken = sourceCode . getTokenBefore ( octiconSpecifier )
245+ const nextToken = sourceCode . getTokenAfter ( octiconSpecifier )
246+ const hasTrailingComma = nextToken && nextToken . value === ','
247+ const hasLeadingComma = previousToken && previousToken . value === ','
248+
249+ let rangeToRemove
250+ if ( hasTrailingComma ) {
251+ rangeToRemove = [ octiconSpecifier . range [ 0 ] , nextToken . range [ 1 ] + 1 ]
252+ } else if ( hasLeadingComma ) {
253+ rangeToRemove = [ previousToken . range [ 0 ] , octiconSpecifier . range [ 1 ] ]
254+ } else {
255+ rangeToRemove = [ octiconSpecifier . range [ 0 ] , octiconSpecifier . range [ 1 ] ]
256+ }
257+ yield fixer . removeRange ( rangeToRemove )
258+ }
259+ }
238260 } ,
239261 } )
240262 } else if ( isMemberExpression ) {
@@ -290,7 +312,47 @@ module.exports = {
290312 yield fixer . replaceText ( node , replacement )
291313
292314 // Handle import removal if this is the last Octicon usage
293- yield * generateImportFixes ( fixer )
315+ processedOcticonUsages ++
316+ if ( processedOcticonUsages === totalOcticonUsages && octiconImports . length > 0 ) {
317+ const importNode = octiconImports [ 0 ]
318+ const octiconSpecifier = importNode . specifiers . find (
319+ specifier => specifier . imported && specifier . imported . name === 'Octicon' ,
320+ )
321+
322+ if ( importNode . specifiers . length === 1 ) {
323+ // Octicon is the only import, remove the entire import statement
324+ // Also remove trailing newline if present
325+ const nextToken = sourceCode . getTokenAfter ( importNode )
326+ const importEnd = importNode . range [ 1 ]
327+ const nextStart = nextToken ? nextToken . range [ 0 ] : sourceCode . getText ( ) . length
328+ const textBetween = sourceCode . getText ( ) . substring ( importEnd , nextStart )
329+ const hasTrailingNewline = / ^ \s * \n / . test ( textBetween )
330+
331+ if ( hasTrailingNewline ) {
332+ const newlineMatch = textBetween . match ( / ^ \s * \n / )
333+ const endRange = importEnd + newlineMatch [ 0 ] . length
334+ yield fixer . removeRange ( [ importNode . range [ 0 ] , endRange ] )
335+ } else {
336+ yield fixer . remove ( importNode )
337+ }
338+ } else {
339+ // Remove just the Octicon specifier from the import
340+ const previousToken = sourceCode . getTokenBefore ( octiconSpecifier )
341+ const nextToken = sourceCode . getTokenAfter ( octiconSpecifier )
342+ const hasTrailingComma = nextToken && nextToken . value === ','
343+ const hasLeadingComma = previousToken && previousToken . value === ','
344+
345+ let rangeToRemove
346+ if ( hasTrailingComma ) {
347+ rangeToRemove = [ octiconSpecifier . range [ 0 ] , nextToken . range [ 1 ] + 1 ]
348+ } else if ( hasLeadingComma ) {
349+ rangeToRemove = [ previousToken . range [ 0 ] , octiconSpecifier . range [ 1 ] ]
350+ } else {
351+ rangeToRemove = [ octiconSpecifier . range [ 0 ] , octiconSpecifier . range [ 1 ] ]
352+ }
353+ yield fixer . removeRange ( rangeToRemove )
354+ }
355+ }
294356 } ,
295357 } )
296358 } else {
0 commit comments