@@ -113,6 +113,33 @@ module.exports = {
113
113
} ,
114
114
115
115
'Program:exit' : function ( ) {
116
+ // Group components by import node to handle multiple changes to same import
117
+ const importNodeChanges = new Map ( )
118
+
119
+ // Collect all changes needed for components used with sx prop
120
+ for ( const componentName of componentsWithSx ) {
121
+ const importInfo = primerReactImports . get ( componentName )
122
+ if ( importInfo && ! styledReactImports . has ( componentName ) ) {
123
+ const hasConflict = componentsWithoutSx . has ( componentName )
124
+ const { node : importNode } = importInfo
125
+
126
+ if ( ! importNodeChanges . has ( importNode ) ) {
127
+ importNodeChanges . set ( importNode , {
128
+ toMove : [ ] ,
129
+ toAlias : [ ] ,
130
+ originalSpecifiers : [ ...importNode . specifiers ] ,
131
+ } )
132
+ }
133
+
134
+ const changes = importNodeChanges . get ( importNode )
135
+ if ( hasConflict ) {
136
+ changes . toAlias . push ( componentName )
137
+ } else {
138
+ changes . toMove . push ( componentName )
139
+ }
140
+ }
141
+ }
142
+
116
143
// Report errors for components used with sx prop that are imported from @primer /react
117
144
for ( const componentName of componentsWithSx ) {
118
145
const importInfo = primerReactImports . get ( componentName )
@@ -126,51 +153,76 @@ module.exports = {
126
153
data : hasConflict ? { componentName, aliasName : `Styled${ componentName } ` } : { componentName} ,
127
154
fix ( fixer ) {
128
155
const { node : importNode , specifier} = importInfo
129
- const otherSpecifiers = importNode . specifiers . filter ( s => s !== specifier )
156
+ const changes = importNodeChanges . get ( importNode )
130
157
131
- if ( hasConflict ) {
132
- // Use alias when there's a conflict - keep original import and add aliased import
133
- const aliasName = `Styled${ componentName } `
134
- return fixer . insertTextAfter (
135
- importNode ,
136
- `\nimport { ${ componentName } as ${ aliasName } } from '@primer/styled-react'` ,
137
- )
138
- } else {
139
- // No conflict - use the original logic
140
- // If this is the only import, replace the whole import
141
- if ( otherSpecifiers . length === 0 ) {
142
- return fixer . replaceText ( importNode , `import { ${ componentName } } from '@primer/styled-react'` )
143
- }
158
+ if ( ! changes ) {
159
+ return null
160
+ }
144
161
145
- // Otherwise, remove from current import and add new import
146
- const fixes = [ ]
162
+ // Only apply the fix once per import node (for the first component processed)
163
+ const isFirstComponent =
164
+ changes . originalSpecifiers [ 0 ] === specifier ||
165
+ ( changes . toMove . length > 0 && changes . toMove [ 0 ] === componentName ) ||
166
+ ( changes . toAlias . length > 0 && changes . toAlias [ 0 ] === componentName )
147
167
148
- // Remove the specifier from current import
149
- if ( importNode . specifiers . length === 1 ) {
150
- fixes . push ( fixer . remove ( importNode ) )
151
- } else {
152
- const isFirst = importNode . specifiers [ 0 ] === specifier
153
- const isLast = importNode . specifiers [ importNode . specifiers . length - 1 ] === specifier
154
-
155
- if ( isFirst ) {
156
- const nextSpecifier = importNode . specifiers [ 1 ]
157
- fixes . push ( fixer . removeRange ( [ specifier . range [ 0 ] , nextSpecifier . range [ 0 ] ] ) )
158
- } else if ( isLast ) {
159
- const prevSpecifier = importNode . specifiers [ importNode . specifiers . length - 2 ]
160
- fixes . push ( fixer . removeRange ( [ prevSpecifier . range [ 1 ] , specifier . range [ 1 ] ] ) )
161
- } else {
162
- const nextSpecifier = importNode . specifiers [ importNode . specifiers . indexOf ( specifier ) + 1 ]
163
- fixes . push ( fixer . removeRange ( [ specifier . range [ 0 ] , nextSpecifier . range [ 0 ] ] ) )
164
- }
168
+ if ( ! isFirstComponent ) {
169
+ return null
170
+ }
171
+
172
+ const fixes = [ ]
173
+ const componentsToMove = new Set ( changes . toMove )
174
+
175
+ // Find specifiers that remain in original import
176
+ const remainingSpecifiers = changes . originalSpecifiers . filter ( spec => {
177
+ const name = spec . imported . name
178
+ // Keep components that are not being moved (only aliased components stay for non-sx usage)
179
+ return ! componentsToMove . has ( name )
180
+ } )
181
+
182
+ // If no components remain, replace with new imports directly
183
+ if ( remainingSpecifiers . length === 0 ) {
184
+ // Build the new imports to replace the original
185
+ const newImports = [ ]
186
+
187
+ // Add imports for moved components
188
+ for ( const componentName of changes . toMove ) {
189
+ newImports . push ( `import { ${ componentName } } from '@primer/styled-react'` )
190
+ }
191
+
192
+ // Add aliased imports for conflicted components
193
+ for ( const componentName of changes . toAlias ) {
194
+ const aliasName = `Styled${ componentName } `
195
+ newImports . push ( `import { ${ componentName } as ${ aliasName } } from '@primer/styled-react'` )
165
196
}
166
197
167
- // Add new import
198
+ fixes . push ( fixer . replaceText ( importNode , newImports . join ( '\n' ) ) )
199
+ } else {
200
+ // Otherwise, update the import to only include remaining components
201
+ const remainingNames = remainingSpecifiers . map ( spec => spec . imported . name )
168
202
fixes . push (
169
- fixer . insertTextAfter ( importNode , `\nimport { ${ componentName } } from '@primer/styled- react'` ) ,
203
+ fixer . replaceText ( importNode , `import { ${ remainingNames . join ( ', ' ) } } from '@primer/react'` ) ,
170
204
)
171
205
172
- return fixes
206
+ // Add new imports for moved components
207
+ for ( const componentName of changes . toMove ) {
208
+ fixes . push (
209
+ fixer . insertTextAfter ( importNode , `\nimport { ${ componentName } } from '@primer/styled-react'` ) ,
210
+ )
211
+ }
212
+
213
+ // Add new aliased imports for conflicted components
214
+ for ( const componentName of changes . toAlias ) {
215
+ const aliasName = `Styled${ componentName } `
216
+ fixes . push (
217
+ fixer . insertTextAfter (
218
+ importNode ,
219
+ `\nimport { ${ componentName } as ${ aliasName } } from '@primer/styled-react'` ,
220
+ ) ,
221
+ )
222
+ }
173
223
}
224
+
225
+ return fixes
174
226
} ,
175
227
} )
176
228
}
0 commit comments