eslint-plugin-primer-react 8.0.0 → 8.1.0-rc.ce584c0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const url = require('../url')
|
|
4
|
+
const {getJSXOpeningElementName} = require('../utils/get-jsx-opening-element-name')
|
|
5
|
+
|
|
6
|
+
// Components that should be imported from @primer/styled-react when used with sx prop
|
|
7
|
+
const styledComponents = new Set([
|
|
8
|
+
'ActionList',
|
|
9
|
+
'ActionMenu',
|
|
10
|
+
'Box',
|
|
11
|
+
'Breadcrumbs',
|
|
12
|
+
'Button',
|
|
13
|
+
'Flash',
|
|
14
|
+
'FormControl',
|
|
15
|
+
'Heading',
|
|
16
|
+
'IconButton',
|
|
17
|
+
'Label',
|
|
18
|
+
'Link',
|
|
19
|
+
'LinkButton',
|
|
20
|
+
'PageLayout',
|
|
21
|
+
'Text',
|
|
22
|
+
'TextInput',
|
|
23
|
+
'Truncate',
|
|
24
|
+
'Octicon',
|
|
25
|
+
'Dialog',
|
|
26
|
+
])
|
|
27
|
+
|
|
28
|
+
// Types that should be imported from @primer/styled-react
|
|
29
|
+
const styledTypes = new Set(['BoxProps', 'SxProp', 'BetterSystemStyleObject'])
|
|
30
|
+
|
|
31
|
+
// Utilities that should be imported from @primer/styled-react
|
|
32
|
+
const styledUtilities = new Set(['sx'])
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @type {import('eslint').Rule.RuleModule}
|
|
36
|
+
*/
|
|
37
|
+
module.exports = {
|
|
38
|
+
meta: {
|
|
39
|
+
type: 'suggestion',
|
|
40
|
+
docs: {
|
|
41
|
+
description: 'Enforce importing components that use sx prop from @primer/styled-react',
|
|
42
|
+
recommended: false,
|
|
43
|
+
url: url(module),
|
|
44
|
+
},
|
|
45
|
+
fixable: 'code',
|
|
46
|
+
schema: [],
|
|
47
|
+
messages: {
|
|
48
|
+
useStyledReactImport: 'Import {{ componentName }} from "@primer/styled-react" when using with sx prop',
|
|
49
|
+
useStyledReactImportWithAlias:
|
|
50
|
+
'Import {{ componentName }} as {{ aliasName }} from "@primer/styled-react" when using with sx prop (conflicts with non-sx usage)',
|
|
51
|
+
useAliasedComponent: 'Use {{ aliasName }} instead of {{ componentName }} when using sx prop',
|
|
52
|
+
moveToStyledReact: 'Move {{ importName }} import to "@primer/styled-react"',
|
|
53
|
+
usePrimerReactImport: 'Import {{ componentName }} from "@primer/react" when not using sx prop',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
create(context) {
|
|
57
|
+
const componentsWithSx = new Set()
|
|
58
|
+
const componentsWithoutSx = new Set() // Track components used without sx
|
|
59
|
+
const allUsedComponents = new Set() // Track all used components
|
|
60
|
+
const primerReactImports = new Map() // Map of component name to import node
|
|
61
|
+
const styledReactImports = new Map() // Map of components imported from styled-react to import node
|
|
62
|
+
const aliasMapping = new Map() // Map local name to original component name for aliased imports
|
|
63
|
+
const jsxElementsWithSx = [] // Track JSX elements that use sx prop
|
|
64
|
+
const jsxElementsWithoutSx = [] // Track JSX elements that don't use sx prop
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
ImportDeclaration(node) {
|
|
68
|
+
const importSource = node.source.value
|
|
69
|
+
|
|
70
|
+
if (importSource === '@primer/react') {
|
|
71
|
+
// Track imports from @primer/react
|
|
72
|
+
for (const specifier of node.specifiers) {
|
|
73
|
+
if (specifier.type === 'ImportSpecifier') {
|
|
74
|
+
const importedName = specifier.imported.name
|
|
75
|
+
if (
|
|
76
|
+
styledComponents.has(importedName) ||
|
|
77
|
+
styledTypes.has(importedName) ||
|
|
78
|
+
styledUtilities.has(importedName)
|
|
79
|
+
) {
|
|
80
|
+
primerReactImports.set(importedName, {node, specifier})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} else if (importSource === '@primer/styled-react') {
|
|
85
|
+
// Track what's imported from styled-react
|
|
86
|
+
for (const specifier of node.specifiers) {
|
|
87
|
+
if (specifier.type === 'ImportSpecifier') {
|
|
88
|
+
const importedName = specifier.imported.name
|
|
89
|
+
const localName = specifier.local.name
|
|
90
|
+
styledReactImports.set(importedName, {node, specifier})
|
|
91
|
+
|
|
92
|
+
// Track alias mapping for styled-react imports
|
|
93
|
+
if (localName !== importedName) {
|
|
94
|
+
aliasMapping.set(localName, importedName)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
JSXElement(node) {
|
|
102
|
+
const openingElement = node.openingElement
|
|
103
|
+
const componentName = getJSXOpeningElementName(openingElement)
|
|
104
|
+
|
|
105
|
+
// Check if this is an aliased component from styled-react
|
|
106
|
+
const originalComponentName = aliasMapping.get(componentName) || componentName
|
|
107
|
+
|
|
108
|
+
// Track all used components that are in our styled components list
|
|
109
|
+
if (styledComponents.has(originalComponentName)) {
|
|
110
|
+
allUsedComponents.add(originalComponentName)
|
|
111
|
+
|
|
112
|
+
// Check if this component has an sx prop
|
|
113
|
+
const hasSxProp = openingElement.attributes.some(
|
|
114
|
+
attr => attr.type === 'JSXAttribute' && attr.name && attr.name.name === 'sx',
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if (hasSxProp) {
|
|
118
|
+
componentsWithSx.add(originalComponentName)
|
|
119
|
+
jsxElementsWithSx.push({node, componentName: originalComponentName, openingElement})
|
|
120
|
+
} else {
|
|
121
|
+
componentsWithoutSx.add(originalComponentName)
|
|
122
|
+
|
|
123
|
+
// If this is an aliased component without sx, we need to track it for renaming
|
|
124
|
+
if (aliasMapping.has(componentName)) {
|
|
125
|
+
jsxElementsWithoutSx.push({
|
|
126
|
+
node,
|
|
127
|
+
localName: componentName,
|
|
128
|
+
originalName: originalComponentName,
|
|
129
|
+
openingElement,
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
'Program:exit': function () {
|
|
137
|
+
// Group components by import node to handle multiple changes to same import
|
|
138
|
+
const importNodeChanges = new Map()
|
|
139
|
+
|
|
140
|
+
// Collect all changes needed for components used with sx prop
|
|
141
|
+
for (const componentName of componentsWithSx) {
|
|
142
|
+
const importInfo = primerReactImports.get(componentName)
|
|
143
|
+
if (importInfo && !styledReactImports.has(componentName)) {
|
|
144
|
+
const hasConflict = componentsWithoutSx.has(componentName)
|
|
145
|
+
const {node: importNode} = importInfo
|
|
146
|
+
|
|
147
|
+
if (!importNodeChanges.has(importNode)) {
|
|
148
|
+
importNodeChanges.set(importNode, {
|
|
149
|
+
toMove: [],
|
|
150
|
+
toAlias: [],
|
|
151
|
+
originalSpecifiers: [...importNode.specifiers],
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const changes = importNodeChanges.get(importNode)
|
|
156
|
+
if (hasConflict) {
|
|
157
|
+
changes.toAlias.push(componentName)
|
|
158
|
+
} else {
|
|
159
|
+
changes.toMove.push(componentName)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Report errors for components used with sx prop that are imported from @primer/react
|
|
165
|
+
for (const componentName of componentsWithSx) {
|
|
166
|
+
const importInfo = primerReactImports.get(componentName)
|
|
167
|
+
if (importInfo && !styledReactImports.has(componentName)) {
|
|
168
|
+
// Check if this component is also used without sx prop (conflict scenario)
|
|
169
|
+
const hasConflict = componentsWithoutSx.has(componentName)
|
|
170
|
+
|
|
171
|
+
context.report({
|
|
172
|
+
node: importInfo.specifier,
|
|
173
|
+
messageId: hasConflict ? 'useStyledReactImportWithAlias' : 'useStyledReactImport',
|
|
174
|
+
data: hasConflict ? {componentName, aliasName: `Styled${componentName}`} : {componentName},
|
|
175
|
+
fix(fixer) {
|
|
176
|
+
const {node: importNode, specifier} = importInfo
|
|
177
|
+
const changes = importNodeChanges.get(importNode)
|
|
178
|
+
|
|
179
|
+
if (!changes) {
|
|
180
|
+
return null
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Only apply the fix once per import node (for the first component processed)
|
|
184
|
+
const isFirstComponent =
|
|
185
|
+
changes.originalSpecifiers[0] === specifier ||
|
|
186
|
+
(changes.toMove.length > 0 && changes.toMove[0] === componentName) ||
|
|
187
|
+
(changes.toAlias.length > 0 && changes.toAlias[0] === componentName)
|
|
188
|
+
|
|
189
|
+
if (!isFirstComponent) {
|
|
190
|
+
return null
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const fixes = []
|
|
194
|
+
const componentsToMove = new Set(changes.toMove)
|
|
195
|
+
|
|
196
|
+
// Find specifiers that remain in original import
|
|
197
|
+
const remainingSpecifiers = changes.originalSpecifiers.filter(spec => {
|
|
198
|
+
const name = spec.imported.name
|
|
199
|
+
// Keep components that are not being moved (only aliased components stay for non-sx usage)
|
|
200
|
+
return !componentsToMove.has(name)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
// If no components remain, replace with new imports directly
|
|
204
|
+
if (remainingSpecifiers.length === 0) {
|
|
205
|
+
// Build the new imports to replace the original
|
|
206
|
+
const newImports = []
|
|
207
|
+
|
|
208
|
+
// Add imports for moved components
|
|
209
|
+
for (const componentName of changes.toMove) {
|
|
210
|
+
newImports.push(`import { ${componentName} } from '@primer/styled-react'`)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Add aliased imports for conflicted components
|
|
214
|
+
for (const componentName of changes.toAlias) {
|
|
215
|
+
const aliasName = `Styled${componentName}`
|
|
216
|
+
newImports.push(`import { ${componentName} as ${aliasName} } from '@primer/styled-react'`)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
fixes.push(fixer.replaceText(importNode, newImports.join('\n')))
|
|
220
|
+
} else {
|
|
221
|
+
// Otherwise, update the import to only include remaining components
|
|
222
|
+
const remainingNames = remainingSpecifiers.map(spec => spec.imported.name)
|
|
223
|
+
fixes.push(
|
|
224
|
+
fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '@primer/react'`),
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
// Combine all styled-react imports into a single import statement
|
|
228
|
+
const styledReactImports = []
|
|
229
|
+
|
|
230
|
+
// Add aliased components first
|
|
231
|
+
for (const componentName of changes.toAlias) {
|
|
232
|
+
const aliasName = `Styled${componentName}`
|
|
233
|
+
styledReactImports.push(`${componentName} as ${aliasName}`)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Add moved components second
|
|
237
|
+
for (const componentName of changes.toMove) {
|
|
238
|
+
styledReactImports.push(componentName)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (styledReactImports.length > 0) {
|
|
242
|
+
fixes.push(
|
|
243
|
+
fixer.insertTextAfter(
|
|
244
|
+
importNode,
|
|
245
|
+
`\nimport { ${styledReactImports.join(', ')} } from '@primer/styled-react'`,
|
|
246
|
+
),
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return fixes
|
|
252
|
+
},
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Report on JSX elements that should use aliased components
|
|
258
|
+
for (const {node: jsxNode, componentName, openingElement} of jsxElementsWithSx) {
|
|
259
|
+
const hasConflict = componentsWithoutSx.has(componentName)
|
|
260
|
+
const isImportedFromPrimerReact = primerReactImports.has(componentName)
|
|
261
|
+
|
|
262
|
+
if (hasConflict && isImportedFromPrimerReact && !styledReactImports.has(componentName)) {
|
|
263
|
+
const aliasName = `Styled${componentName}`
|
|
264
|
+
context.report({
|
|
265
|
+
node: openingElement,
|
|
266
|
+
messageId: 'useAliasedComponent',
|
|
267
|
+
data: {componentName, aliasName},
|
|
268
|
+
fix(fixer) {
|
|
269
|
+
const fixes = []
|
|
270
|
+
|
|
271
|
+
// Replace the component name in the JSX opening tag
|
|
272
|
+
fixes.push(fixer.replaceText(openingElement.name, aliasName))
|
|
273
|
+
|
|
274
|
+
// Replace the component name in the JSX closing tag if it exists
|
|
275
|
+
if (jsxNode.closingElement) {
|
|
276
|
+
fixes.push(fixer.replaceText(jsxNode.closingElement.name, aliasName))
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return fixes
|
|
280
|
+
},
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Group styled-react imports that need to be moved to primer-react
|
|
286
|
+
const styledReactImportNodeChanges = new Map()
|
|
287
|
+
|
|
288
|
+
// Collect components that need to be moved from styled-react to primer-react
|
|
289
|
+
for (const componentName of allUsedComponents) {
|
|
290
|
+
if (!componentsWithSx.has(componentName) && styledReactImports.has(componentName)) {
|
|
291
|
+
const importInfo = styledReactImports.get(componentName)
|
|
292
|
+
const {node: importNode} = importInfo
|
|
293
|
+
|
|
294
|
+
if (!styledReactImportNodeChanges.has(importNode)) {
|
|
295
|
+
styledReactImportNodeChanges.set(importNode, {
|
|
296
|
+
toMove: [],
|
|
297
|
+
originalSpecifiers: [...importNode.specifiers],
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
styledReactImportNodeChanges.get(importNode).toMove.push(componentName)
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Find existing primer-react import nodes to merge with
|
|
306
|
+
const primerReactImportNodes = new Set()
|
|
307
|
+
for (const [, {node}] of primerReactImports) {
|
|
308
|
+
primerReactImportNodes.add(node)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Report errors for components used WITHOUT sx prop that are imported from @primer/styled-react
|
|
312
|
+
for (const componentName of allUsedComponents) {
|
|
313
|
+
// If component is used but NOT with sx prop, and it's imported from styled-react
|
|
314
|
+
if (!componentsWithSx.has(componentName) && styledReactImports.has(componentName)) {
|
|
315
|
+
const importInfo = styledReactImports.get(componentName)
|
|
316
|
+
context.report({
|
|
317
|
+
node: importInfo.specifier,
|
|
318
|
+
messageId: 'usePrimerReactImport',
|
|
319
|
+
data: {componentName},
|
|
320
|
+
fix(fixer) {
|
|
321
|
+
const {node: importNode} = importInfo
|
|
322
|
+
const changes = styledReactImportNodeChanges.get(importNode)
|
|
323
|
+
|
|
324
|
+
if (!changes) {
|
|
325
|
+
return null
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Only apply the fix once per import node (for the first component processed)
|
|
329
|
+
const isFirstComponent = changes.toMove[0] === componentName
|
|
330
|
+
|
|
331
|
+
if (!isFirstComponent) {
|
|
332
|
+
return null
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const fixes = []
|
|
336
|
+
const componentsToMove = new Set(changes.toMove)
|
|
337
|
+
|
|
338
|
+
// Find specifiers that remain in styled-react import
|
|
339
|
+
const remainingSpecifiers = changes.originalSpecifiers.filter(spec => {
|
|
340
|
+
const name = spec.imported.name
|
|
341
|
+
return !componentsToMove.has(name)
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
// Check if there's an existing primer-react import to merge with
|
|
345
|
+
const existingPrimerReactImport = Array.from(primerReactImportNodes)[0]
|
|
346
|
+
|
|
347
|
+
if (existingPrimerReactImport && remainingSpecifiers.length === 0) {
|
|
348
|
+
// Case: No remaining styled-react imports, merge with existing primer-react import
|
|
349
|
+
const existingSpecifiers = existingPrimerReactImport.specifiers.map(spec => spec.imported.name)
|
|
350
|
+
const newSpecifiers = [...existingSpecifiers, ...changes.toMove].filter(
|
|
351
|
+
(name, index, arr) => arr.indexOf(name) === index,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
fixes.push(
|
|
355
|
+
fixer.replaceText(
|
|
356
|
+
existingPrimerReactImport,
|
|
357
|
+
`import { ${newSpecifiers.join(', ')} } from '@primer/react'`,
|
|
358
|
+
),
|
|
359
|
+
)
|
|
360
|
+
fixes.push(fixer.remove(importNode))
|
|
361
|
+
} else if (existingPrimerReactImport && remainingSpecifiers.length > 0) {
|
|
362
|
+
// Case: Some styled-react imports remain, merge moved components with existing primer-react
|
|
363
|
+
const existingSpecifiers = existingPrimerReactImport.specifiers.map(spec => spec.imported.name)
|
|
364
|
+
const newSpecifiers = [...existingSpecifiers, ...changes.toMove].filter(
|
|
365
|
+
(name, index, arr) => arr.indexOf(name) === index,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
fixes.push(
|
|
369
|
+
fixer.replaceText(
|
|
370
|
+
existingPrimerReactImport,
|
|
371
|
+
`import { ${newSpecifiers.join(', ')} } from '@primer/react'`,
|
|
372
|
+
),
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
const remainingNames = remainingSpecifiers.map(spec => spec.imported.name)
|
|
376
|
+
fixes.push(
|
|
377
|
+
fixer.replaceText(
|
|
378
|
+
importNode,
|
|
379
|
+
`import { ${remainingNames.join(', ')} } from '@primer/styled-react'`,
|
|
380
|
+
),
|
|
381
|
+
)
|
|
382
|
+
} else if (remainingSpecifiers.length === 0) {
|
|
383
|
+
// Case: No existing primer-react import, no remaining styled-react imports
|
|
384
|
+
const movedComponents = changes.toMove.join(', ')
|
|
385
|
+
fixes.push(fixer.replaceText(importNode, `import { ${movedComponents} } from '@primer/react'`))
|
|
386
|
+
} else {
|
|
387
|
+
// Case: No existing primer-react import, some styled-react imports remain
|
|
388
|
+
const remainingNames = remainingSpecifiers.map(spec => spec.imported.name)
|
|
389
|
+
fixes.push(
|
|
390
|
+
fixer.replaceText(
|
|
391
|
+
importNode,
|
|
392
|
+
`import { ${remainingNames.join(', ')} } from '@primer/styled-react'`,
|
|
393
|
+
),
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
const movedComponents = changes.toMove.join(', ')
|
|
397
|
+
fixes.push(fixer.insertTextAfter(importNode, `\nimport { ${movedComponents} } from '@primer/react'`))
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return fixes
|
|
401
|
+
},
|
|
402
|
+
})
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Report and fix JSX elements that use aliased components without sx prop
|
|
407
|
+
for (const {node: jsxNode, originalName, openingElement} of jsxElementsWithoutSx) {
|
|
408
|
+
if (!componentsWithSx.has(originalName) && styledReactImports.has(originalName)) {
|
|
409
|
+
context.report({
|
|
410
|
+
node: openingElement,
|
|
411
|
+
messageId: 'usePrimerReactImport',
|
|
412
|
+
data: {componentName: originalName},
|
|
413
|
+
fix(fixer) {
|
|
414
|
+
const fixes = []
|
|
415
|
+
|
|
416
|
+
// Replace the aliased component name with the original component name in JSX opening tag
|
|
417
|
+
fixes.push(fixer.replaceText(openingElement.name, originalName))
|
|
418
|
+
|
|
419
|
+
// Replace the aliased component name in JSX closing tag if it exists
|
|
420
|
+
if (jsxNode.closingElement) {
|
|
421
|
+
fixes.push(fixer.replaceText(jsxNode.closingElement.name, originalName))
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return fixes
|
|
425
|
+
},
|
|
426
|
+
})
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Also report for types and utilities that should always be from styled-react
|
|
431
|
+
for (const [importName, importInfo] of primerReactImports) {
|
|
432
|
+
if ((styledTypes.has(importName) || styledUtilities.has(importName)) && !styledReactImports.has(importName)) {
|
|
433
|
+
context.report({
|
|
434
|
+
node: importInfo.specifier,
|
|
435
|
+
messageId: 'moveToStyledReact',
|
|
436
|
+
data: {importName},
|
|
437
|
+
fix(fixer) {
|
|
438
|
+
const {node: importNode, specifier} = importInfo
|
|
439
|
+
const otherSpecifiers = importNode.specifiers.filter(s => s !== specifier)
|
|
440
|
+
|
|
441
|
+
// If this is the only import, replace the whole import
|
|
442
|
+
if (otherSpecifiers.length === 0) {
|
|
443
|
+
const prefix = styledTypes.has(importName) ? 'type ' : ''
|
|
444
|
+
return fixer.replaceText(importNode, `import { ${prefix}${importName} } from '@primer/styled-react'`)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Otherwise, remove from current import and add new import
|
|
448
|
+
const fixes = []
|
|
449
|
+
|
|
450
|
+
// Remove the specifier from current import
|
|
451
|
+
if (importNode.specifiers.length === 1) {
|
|
452
|
+
fixes.push(fixer.remove(importNode))
|
|
453
|
+
} else {
|
|
454
|
+
const isFirst = importNode.specifiers[0] === specifier
|
|
455
|
+
const isLast = importNode.specifiers[importNode.specifiers.length - 1] === specifier
|
|
456
|
+
|
|
457
|
+
if (isFirst) {
|
|
458
|
+
const nextSpecifier = importNode.specifiers[1]
|
|
459
|
+
fixes.push(fixer.removeRange([specifier.range[0], nextSpecifier.range[0]]))
|
|
460
|
+
} else if (isLast) {
|
|
461
|
+
const prevSpecifier = importNode.specifiers[importNode.specifiers.length - 2]
|
|
462
|
+
fixes.push(fixer.removeRange([prevSpecifier.range[1], specifier.range[1]]))
|
|
463
|
+
} else {
|
|
464
|
+
const nextSpecifier = importNode.specifiers[importNode.specifiers.indexOf(specifier) + 1]
|
|
465
|
+
fixes.push(fixer.removeRange([specifier.range[0], nextSpecifier.range[0]]))
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Add new import
|
|
470
|
+
const prefix = styledTypes.has(importName) ? 'type ' : ''
|
|
471
|
+
fixes.push(
|
|
472
|
+
fixer.insertTextAfter(importNode, `\nimport { ${prefix}${importName} } from '@primer/styled-react'`),
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
return fixes
|
|
476
|
+
},
|
|
477
|
+
})
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
}
|