eslint-plugin-primer-react 8.2.0-rc.b646af2 → 8.2.1-rc.aedddaa
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.
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
# eslint-plugin-primer-react
|
|
2
2
|
|
|
3
|
+
## 8.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#410](https://github.com/primer/eslint-plugin-primer-react/pull/410) [`f3e47b1`](https://github.com/primer/eslint-plugin-primer-react/commit/f3e47b1a75ad27aceaed89d50175cc59754c8b9d) Thanks [@jonrohan](https://github.com/jonrohan)! - Fix for deprecated and experimental import paths
|
|
8
|
+
|
|
3
9
|
## 8.2.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
6
12
|
|
|
7
13
|
- [#407](https://github.com/primer/eslint-plugin-primer-react/pull/407) [`2f25480`](https://github.com/primer/eslint-plugin-primer-react/commit/2f25480c3341c1d1afb6fc040c5c5deee416d71c) Thanks [@jonrohan](https://github.com/jonrohan)! - Make `use-styled-react-import` rule configurable
|
|
8
14
|
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- [#406](https://github.com/primer/eslint-plugin-primer-react/pull/406) [`d72e8c4`](https://github.com/primer/eslint-plugin-primer-react/commit/d72e8c4c172d1c37da201a20cde0b7b2bd9ab283) Thanks [@jonrohan](https://github.com/jonrohan)! - Fixes for `use-styled-react-import` rule for compound components.
|
|
18
|
+
|
|
9
19
|
## 8.1.0
|
|
10
20
|
|
|
11
21
|
### Minor Changes
|
|
@@ -12,7 +12,7 @@ Enforce importing components that use `sx` prop from `@primer/styled-react` inst
|
|
|
12
12
|
|
|
13
13
|
This rule detects when certain Primer React components are used with the `sx` prop and ensures they are imported from the temporary `@primer/styled-react` package instead of `@primer/react`. When the same components are used without the `sx` prop, it ensures they are imported from `@primer/react` instead of `@primer/styled-react`.
|
|
14
14
|
|
|
15
|
-
When a component is used
|
|
15
|
+
When a component is used with the `sx` prop anywhere in the file, the entire component import is moved to `@primer/styled-react`, simplifying the import structure.
|
|
16
16
|
|
|
17
17
|
It also moves certain types and utilities to the styled-react package.
|
|
18
18
|
|
|
@@ -111,12 +111,11 @@ const Component = () => <Button>Click me</Button>
|
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
```jsx
|
|
114
|
-
// When a component is used
|
|
115
|
-
import {Button} from '@primer/react'
|
|
116
|
-
import {Button as StyledButton} from '@primer/styled-react'
|
|
114
|
+
// When a component is used with sx prop anywhere, import from styled-react
|
|
115
|
+
import {Button} from '@primer/styled-react'
|
|
117
116
|
|
|
118
117
|
const Component1 = () => <Button>Click me</Button>
|
|
119
|
-
const Component2 = () => <
|
|
118
|
+
const Component2 = () => <Button sx={{color: 'red'}}>Styled me</Button>
|
|
120
119
|
```
|
|
121
120
|
|
|
122
121
|
## Options
|
package/package-lock.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@styled-system/props": "^5.1.5",
|
|
13
|
-
"@typescript-eslint/utils": "8.
|
|
13
|
+
"@typescript-eslint/utils": "^8.39.0",
|
|
14
14
|
"eslint-plugin-github": "^6.0.0",
|
|
15
15
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
|
16
16
|
"eslint-traverse": "^1.0.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-primer-react",
|
|
3
|
-
"version": "8.2.
|
|
3
|
+
"version": "8.2.1-rc.aedddaa",
|
|
4
4
|
"description": "ESLint rules for Primer React",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"engines": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@styled-system/props": "^5.1.5",
|
|
35
|
-
"@typescript-eslint/utils": "8.
|
|
35
|
+
"@typescript-eslint/utils": "^8.39.0",
|
|
36
36
|
"eslint-plugin-github": "^6.0.0",
|
|
37
37
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
|
38
38
|
"eslint-traverse": "^1.0.0",
|
|
@@ -56,6 +56,68 @@ ruleTester.run('use-styled-react-import', rule, {
|
|
|
56
56
|
],
|
|
57
57
|
},
|
|
58
58
|
|
|
59
|
+
// Invalid: Imports from /experimental and /deprecated paths
|
|
60
|
+
{
|
|
61
|
+
code: `import { Button } from '@primer/react/experimental'
|
|
62
|
+
import { Box } from '@primer/react/deprecated'
|
|
63
|
+
const Component = () => <Box sx={{ color: 'red' }}><Button sx={{ margin: 2 }}>Click me</Button></Box>`,
|
|
64
|
+
output: `import { Button } from '@primer/styled-react/experimental'
|
|
65
|
+
import { Box } from '@primer/styled-react/deprecated'
|
|
66
|
+
const Component = () => <Box sx={{ color: 'red' }}><Button sx={{ margin: 2 }}>Click me</Button></Box>`,
|
|
67
|
+
errors: [
|
|
68
|
+
{
|
|
69
|
+
messageId: 'useStyledReactImport',
|
|
70
|
+
data: {componentName: 'Button'},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
messageId: 'useStyledReactImport',
|
|
74
|
+
data: {componentName: 'Box'},
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// Invalid: ActionList.Item with sx prop and ActionList imported from @primer/react
|
|
80
|
+
{
|
|
81
|
+
code: `import { ActionList } from '@primer/react'
|
|
82
|
+
const Component = () => <ActionList.Item sx={{ color: 'red' }}>Content</ActionList.Item>`,
|
|
83
|
+
output: `import { ActionList } from '@primer/styled-react'
|
|
84
|
+
const Component = () => <ActionList.Item sx={{ color: 'red' }}>Content</ActionList.Item>`,
|
|
85
|
+
errors: [
|
|
86
|
+
{
|
|
87
|
+
messageId: 'useStyledReactImport',
|
|
88
|
+
data: {componentName: 'ActionList'},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// Invalid: FormControl used both with and without sx prop - should move to styled-react
|
|
94
|
+
{
|
|
95
|
+
code: `import { FormControl } from '@primer/react'
|
|
96
|
+
const Component = () => (
|
|
97
|
+
<div>
|
|
98
|
+
<FormControl></FormControl>
|
|
99
|
+
<FormControl sx={{ color: 'red' }}>
|
|
100
|
+
<FormControl.Label visuallyHidden>Label</FormControl.Label>
|
|
101
|
+
</FormControl>
|
|
102
|
+
</div>
|
|
103
|
+
)`,
|
|
104
|
+
output: `import { FormControl } from '@primer/styled-react'
|
|
105
|
+
const Component = () => (
|
|
106
|
+
<div>
|
|
107
|
+
<FormControl></FormControl>
|
|
108
|
+
<FormControl sx={{ color: 'red' }}>
|
|
109
|
+
<FormControl.Label visuallyHidden>Label</FormControl.Label>
|
|
110
|
+
</FormControl>
|
|
111
|
+
</div>
|
|
112
|
+
)`,
|
|
113
|
+
errors: [
|
|
114
|
+
{
|
|
115
|
+
messageId: 'useStyledReactImport',
|
|
116
|
+
data: {componentName: 'FormControl'},
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
|
|
59
121
|
// Invalid: Button with sx prop imported from @primer/react
|
|
60
122
|
{
|
|
61
123
|
code: `import { Button } from '@primer/react'
|
|
@@ -70,6 +132,41 @@ ruleTester.run('use-styled-react-import', rule, {
|
|
|
70
132
|
],
|
|
71
133
|
},
|
|
72
134
|
|
|
135
|
+
// Invalid: ActionList used without sx, ActionList.Item used with sx - should move ActionList to styled-react
|
|
136
|
+
{
|
|
137
|
+
code: `import { ActionList, ActionMenu } from '@primer/react'
|
|
138
|
+
const Component = () => (
|
|
139
|
+
<ActionMenu>
|
|
140
|
+
<ActionMenu.Overlay>
|
|
141
|
+
<ActionList>
|
|
142
|
+
<ActionList.Item sx={{ paddingLeft: 'calc(1 * var(--base-size-12))' }}>
|
|
143
|
+
Item
|
|
144
|
+
</ActionList.Item>
|
|
145
|
+
</ActionList>
|
|
146
|
+
</ActionMenu.Overlay>
|
|
147
|
+
</ActionMenu>
|
|
148
|
+
)`,
|
|
149
|
+
output: `import { ActionMenu } from '@primer/react'
|
|
150
|
+
import { ActionList } from '@primer/styled-react'
|
|
151
|
+
const Component = () => (
|
|
152
|
+
<ActionMenu>
|
|
153
|
+
<ActionMenu.Overlay>
|
|
154
|
+
<ActionList>
|
|
155
|
+
<ActionList.Item sx={{ paddingLeft: 'calc(1 * var(--base-size-12))' }}>
|
|
156
|
+
Item
|
|
157
|
+
</ActionList.Item>
|
|
158
|
+
</ActionList>
|
|
159
|
+
</ActionMenu.Overlay>
|
|
160
|
+
</ActionMenu>
|
|
161
|
+
)`,
|
|
162
|
+
errors: [
|
|
163
|
+
{
|
|
164
|
+
messageId: 'useStyledReactImport',
|
|
165
|
+
data: {componentName: 'ActionList'},
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
|
|
73
170
|
// Invalid: Multiple components, one with sx prop
|
|
74
171
|
{
|
|
75
172
|
code: `import { Button, Box, Avatar } from '@primer/react'
|
|
@@ -138,7 +235,7 @@ import { Button } from '@primer/styled-react'
|
|
|
138
235
|
],
|
|
139
236
|
},
|
|
140
237
|
|
|
141
|
-
// Invalid: <Link /> and <
|
|
238
|
+
// Invalid: <Link /> and <Button /> imported from styled-react but used without sx prop
|
|
142
239
|
{
|
|
143
240
|
code: `import { Button } from '@primer/react'
|
|
144
241
|
import { Button as StyledButton, Link } from '@primer/styled-react'
|
|
@@ -155,7 +252,7 @@ import { Button as StyledButton, Link } from '@primer/styled-react'
|
|
|
155
252
|
<div>
|
|
156
253
|
<Link />
|
|
157
254
|
<Button>Regular button</Button>
|
|
158
|
-
<
|
|
255
|
+
<StyledButton>Styled button</StyledButton>
|
|
159
256
|
</div>
|
|
160
257
|
)`,
|
|
161
258
|
errors: [
|
|
@@ -167,10 +264,6 @@ import { Button as StyledButton, Link } from '@primer/styled-react'
|
|
|
167
264
|
messageId: 'usePrimerReactImport',
|
|
168
265
|
data: {componentName: 'Link'},
|
|
169
266
|
},
|
|
170
|
-
{
|
|
171
|
-
messageId: 'usePrimerReactImport',
|
|
172
|
-
data: {componentName: 'Button'},
|
|
173
|
-
},
|
|
174
267
|
],
|
|
175
268
|
},
|
|
176
269
|
|
|
@@ -213,7 +306,7 @@ import { Button } from '@primer/react'
|
|
|
213
306
|
],
|
|
214
307
|
},
|
|
215
308
|
|
|
216
|
-
// Invalid: Button used
|
|
309
|
+
// Invalid: Button and Link used with sx prop - should move both to styled-react
|
|
217
310
|
{
|
|
218
311
|
code: `import { Button, Link } from '@primer/react'
|
|
219
312
|
const Component = () => (
|
|
@@ -223,28 +316,23 @@ import { Button } from '@primer/react'
|
|
|
223
316
|
<Button sx={{ color: 'red' }}>Styled button</Button>
|
|
224
317
|
</div>
|
|
225
318
|
)`,
|
|
226
|
-
output: `import { Button } from '@primer/react'
|
|
227
|
-
import { Button as StyledButton, Link } from '@primer/styled-react'
|
|
319
|
+
output: `import { Link, Button } from '@primer/styled-react'
|
|
228
320
|
const Component = () => (
|
|
229
321
|
<div>
|
|
230
322
|
<Link sx={{ color: 'red' }} />
|
|
231
323
|
<Button>Regular button</Button>
|
|
232
|
-
<
|
|
324
|
+
<Button sx={{ color: 'red' }}>Styled button</Button>
|
|
233
325
|
</div>
|
|
234
326
|
)`,
|
|
235
327
|
errors: [
|
|
236
328
|
{
|
|
237
|
-
messageId: '
|
|
238
|
-
data: {componentName: 'Button'
|
|
329
|
+
messageId: 'useStyledReactImport',
|
|
330
|
+
data: {componentName: 'Button'},
|
|
239
331
|
},
|
|
240
332
|
{
|
|
241
333
|
messageId: 'useStyledReactImport',
|
|
242
334
|
data: {componentName: 'Link'},
|
|
243
335
|
},
|
|
244
|
-
{
|
|
245
|
-
messageId: 'useAliasedComponent',
|
|
246
|
-
data: {componentName: 'Button', aliasName: 'StyledButton'},
|
|
247
|
-
},
|
|
248
336
|
],
|
|
249
337
|
},
|
|
250
338
|
],
|
|
@@ -68,9 +68,6 @@ module.exports = {
|
|
|
68
68
|
],
|
|
69
69
|
messages: {
|
|
70
70
|
useStyledReactImport: 'Import {{ componentName }} from "@primer/styled-react" when using with sx prop',
|
|
71
|
-
useStyledReactImportWithAlias:
|
|
72
|
-
'Import {{ componentName }} as {{ aliasName }} from "@primer/styled-react" when using with sx prop (conflicts with non-sx usage)',
|
|
73
|
-
useAliasedComponent: 'Use {{ aliasName }} instead of {{ componentName }} when using sx prop',
|
|
74
71
|
moveToStyledReact: 'Move {{ importName }} import to "@primer/styled-react"',
|
|
75
72
|
usePrimerReactImport: 'Import {{ componentName }} from "@primer/react" when not using sx prop',
|
|
76
73
|
},
|
|
@@ -86,16 +83,13 @@ module.exports = {
|
|
|
86
83
|
const allUsedComponents = new Set() // Track all used components
|
|
87
84
|
const primerReactImports = new Map() // Map of component name to import node
|
|
88
85
|
const styledReactImports = new Map() // Map of components imported from styled-react to import node
|
|
89
|
-
const aliasMapping = new Map() // Map local name to original component name for aliased imports
|
|
90
|
-
const jsxElementsWithSx = [] // Track JSX elements that use sx prop
|
|
91
|
-
const jsxElementsWithoutSx = [] // Track JSX elements that don't use sx prop
|
|
92
86
|
|
|
93
87
|
return {
|
|
94
88
|
ImportDeclaration(node) {
|
|
95
89
|
const importSource = node.source.value
|
|
96
90
|
|
|
97
|
-
if (importSource === '@primer/react') {
|
|
98
|
-
// Track imports from @primer/react
|
|
91
|
+
if (importSource === '@primer/react' || importSource.startsWith('@primer/react/')) {
|
|
92
|
+
// Track imports from @primer/react and its subpaths
|
|
99
93
|
for (const specifier of node.specifiers) {
|
|
100
94
|
if (specifier.type === 'ImportSpecifier') {
|
|
101
95
|
const importedName = specifier.imported.name
|
|
@@ -104,22 +98,16 @@ module.exports = {
|
|
|
104
98
|
styledTypes.has(importedName) ||
|
|
105
99
|
styledUtilities.has(importedName)
|
|
106
100
|
) {
|
|
107
|
-
primerReactImports.set(importedName, {node, specifier})
|
|
101
|
+
primerReactImports.set(importedName, {node, specifier, importSource})
|
|
108
102
|
}
|
|
109
103
|
}
|
|
110
104
|
}
|
|
111
|
-
} else if (importSource === '@primer/styled-react') {
|
|
112
|
-
// Track what's imported from styled-react
|
|
105
|
+
} else if (importSource === '@primer/styled-react' || importSource.startsWith('@primer/styled-react/')) {
|
|
106
|
+
// Track what's imported from styled-react and its subpaths
|
|
113
107
|
for (const specifier of node.specifiers) {
|
|
114
108
|
if (specifier.type === 'ImportSpecifier') {
|
|
115
109
|
const importedName = specifier.imported.name
|
|
116
|
-
|
|
117
|
-
styledReactImports.set(importedName, {node, specifier})
|
|
118
|
-
|
|
119
|
-
// Track alias mapping for styled-react imports
|
|
120
|
-
if (localName !== importedName) {
|
|
121
|
-
aliasMapping.set(localName, importedName)
|
|
122
|
-
}
|
|
110
|
+
styledReactImports.set(importedName, {node, specifier, importSource})
|
|
123
111
|
}
|
|
124
112
|
}
|
|
125
113
|
}
|
|
@@ -129,12 +117,12 @@ module.exports = {
|
|
|
129
117
|
const openingElement = node.openingElement
|
|
130
118
|
const componentName = getJSXOpeningElementName(openingElement)
|
|
131
119
|
|
|
132
|
-
//
|
|
133
|
-
const
|
|
120
|
+
// For compound components like "ActionList.Item", we need to check the parent component
|
|
121
|
+
const parentComponentName = componentName.includes('.') ? componentName.split('.')[0] : componentName
|
|
134
122
|
|
|
135
123
|
// Track all used components that are in our styled components list
|
|
136
|
-
if (styledComponents.has(
|
|
137
|
-
allUsedComponents.add(
|
|
124
|
+
if (styledComponents.has(parentComponentName)) {
|
|
125
|
+
allUsedComponents.add(parentComponentName)
|
|
138
126
|
|
|
139
127
|
// Check if this component has an sx prop
|
|
140
128
|
const hasSxProp = openingElement.attributes.some(
|
|
@@ -142,20 +130,9 @@ module.exports = {
|
|
|
142
130
|
)
|
|
143
131
|
|
|
144
132
|
if (hasSxProp) {
|
|
145
|
-
componentsWithSx.add(
|
|
146
|
-
jsxElementsWithSx.push({node, componentName: originalComponentName, openingElement})
|
|
133
|
+
componentsWithSx.add(parentComponentName)
|
|
147
134
|
} else {
|
|
148
|
-
componentsWithoutSx.add(
|
|
149
|
-
|
|
150
|
-
// If this is an aliased component without sx, we need to track it for renaming
|
|
151
|
-
if (aliasMapping.has(componentName)) {
|
|
152
|
-
jsxElementsWithoutSx.push({
|
|
153
|
-
node,
|
|
154
|
-
localName: componentName,
|
|
155
|
-
originalName: originalComponentName,
|
|
156
|
-
openingElement,
|
|
157
|
-
})
|
|
158
|
-
}
|
|
135
|
+
componentsWithoutSx.add(parentComponentName)
|
|
159
136
|
}
|
|
160
137
|
}
|
|
161
138
|
},
|
|
@@ -168,23 +145,16 @@ module.exports = {
|
|
|
168
145
|
for (const componentName of componentsWithSx) {
|
|
169
146
|
const importInfo = primerReactImports.get(componentName)
|
|
170
147
|
if (importInfo && !styledReactImports.has(componentName)) {
|
|
171
|
-
const hasConflict = componentsWithoutSx.has(componentName)
|
|
172
148
|
const {node: importNode} = importInfo
|
|
173
149
|
|
|
174
150
|
if (!importNodeChanges.has(importNode)) {
|
|
175
151
|
importNodeChanges.set(importNode, {
|
|
176
152
|
toMove: [],
|
|
177
|
-
toAlias: [],
|
|
178
153
|
originalSpecifiers: [...importNode.specifiers],
|
|
179
154
|
})
|
|
180
155
|
}
|
|
181
156
|
|
|
182
|
-
|
|
183
|
-
if (hasConflict) {
|
|
184
|
-
changes.toAlias.push(componentName)
|
|
185
|
-
} else {
|
|
186
|
-
changes.toMove.push(componentName)
|
|
187
|
-
}
|
|
157
|
+
importNodeChanges.get(importNode).toMove.push(componentName)
|
|
188
158
|
}
|
|
189
159
|
}
|
|
190
160
|
|
|
@@ -192,15 +162,12 @@ module.exports = {
|
|
|
192
162
|
for (const componentName of componentsWithSx) {
|
|
193
163
|
const importInfo = primerReactImports.get(componentName)
|
|
194
164
|
if (importInfo && !styledReactImports.has(componentName)) {
|
|
195
|
-
// Check if this component is also used without sx prop (conflict scenario)
|
|
196
|
-
const hasConflict = componentsWithoutSx.has(componentName)
|
|
197
|
-
|
|
198
165
|
context.report({
|
|
199
166
|
node: importInfo.specifier,
|
|
200
|
-
messageId:
|
|
201
|
-
data:
|
|
167
|
+
messageId: 'useStyledReactImport',
|
|
168
|
+
data: {componentName},
|
|
202
169
|
fix(fixer) {
|
|
203
|
-
const {node: importNode,
|
|
170
|
+
const {node: importNode, importSource} = importInfo
|
|
204
171
|
const changes = importNodeChanges.get(importNode)
|
|
205
172
|
|
|
206
173
|
if (!changes) {
|
|
@@ -208,10 +175,7 @@ module.exports = {
|
|
|
208
175
|
}
|
|
209
176
|
|
|
210
177
|
// Only apply the fix once per import node (for the first component processed)
|
|
211
|
-
const isFirstComponent =
|
|
212
|
-
changes.originalSpecifiers[0] === specifier ||
|
|
213
|
-
(changes.toMove.length > 0 && changes.toMove[0] === componentName) ||
|
|
214
|
-
(changes.toAlias.length > 0 && changes.toAlias[0] === componentName)
|
|
178
|
+
const isFirstComponent = changes.toMove[0] === componentName
|
|
215
179
|
|
|
216
180
|
if (!isFirstComponent) {
|
|
217
181
|
return null
|
|
@@ -223,84 +187,28 @@ module.exports = {
|
|
|
223
187
|
// Find specifiers that remain in original import
|
|
224
188
|
const remainingSpecifiers = changes.originalSpecifiers.filter(spec => {
|
|
225
189
|
const name = spec.imported.name
|
|
226
|
-
// Keep components that are not being moved (only aliased components stay for non-sx usage)
|
|
227
190
|
return !componentsToMove.has(name)
|
|
228
191
|
})
|
|
229
192
|
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
// Build the new imports to replace the original
|
|
233
|
-
const newImports = []
|
|
234
|
-
|
|
235
|
-
// Add imports for moved components
|
|
236
|
-
for (const componentName of changes.toMove) {
|
|
237
|
-
newImports.push(`import { ${componentName} } from '@primer/styled-react'`)
|
|
238
|
-
}
|
|
193
|
+
// Convert @primer/react path to @primer/styled-react path
|
|
194
|
+
const styledReactPath = importSource.replace('@primer/react', '@primer/styled-react')
|
|
239
195
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
fixes.push(fixer.replaceText(importNode, newImports.join('\n')))
|
|
196
|
+
// If no components remain, replace with new import directly
|
|
197
|
+
if (remainingSpecifiers.length === 0) {
|
|
198
|
+
const movedComponents = changes.toMove.join(', ')
|
|
199
|
+
fixes.push(fixer.replaceText(importNode, `import { ${movedComponents} } from '${styledReactPath}'`))
|
|
247
200
|
} else {
|
|
248
201
|
// Otherwise, update the import to only include remaining components
|
|
249
202
|
const remainingNames = remainingSpecifiers.map(spec => spec.imported.name)
|
|
250
203
|
fixes.push(
|
|
251
|
-
fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '
|
|
204
|
+
fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '${importSource}'`),
|
|
252
205
|
)
|
|
253
206
|
|
|
254
|
-
//
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const aliasName = `Styled${componentName}`
|
|
260
|
-
styledReactImports.push(`${componentName} as ${aliasName}`)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Add moved components second
|
|
264
|
-
for (const componentName of changes.toMove) {
|
|
265
|
-
styledReactImports.push(componentName)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if (styledReactImports.length > 0) {
|
|
269
|
-
fixes.push(
|
|
270
|
-
fixer.insertTextAfter(
|
|
271
|
-
importNode,
|
|
272
|
-
`\nimport { ${styledReactImports.join(', ')} } from '@primer/styled-react'`,
|
|
273
|
-
),
|
|
274
|
-
)
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return fixes
|
|
279
|
-
},
|
|
280
|
-
})
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Report on JSX elements that should use aliased components
|
|
285
|
-
for (const {node: jsxNode, componentName, openingElement} of jsxElementsWithSx) {
|
|
286
|
-
const hasConflict = componentsWithoutSx.has(componentName)
|
|
287
|
-
const isImportedFromPrimerReact = primerReactImports.has(componentName)
|
|
288
|
-
|
|
289
|
-
if (hasConflict && isImportedFromPrimerReact && !styledReactImports.has(componentName)) {
|
|
290
|
-
const aliasName = `Styled${componentName}`
|
|
291
|
-
context.report({
|
|
292
|
-
node: openingElement,
|
|
293
|
-
messageId: 'useAliasedComponent',
|
|
294
|
-
data: {componentName, aliasName},
|
|
295
|
-
fix(fixer) {
|
|
296
|
-
const fixes = []
|
|
297
|
-
|
|
298
|
-
// Replace the component name in the JSX opening tag
|
|
299
|
-
fixes.push(fixer.replaceText(openingElement.name, aliasName))
|
|
300
|
-
|
|
301
|
-
// Replace the component name in the JSX closing tag if it exists
|
|
302
|
-
if (jsxNode.closingElement) {
|
|
303
|
-
fixes.push(fixer.replaceText(jsxNode.closingElement.name, aliasName))
|
|
207
|
+
// Add new styled-react import
|
|
208
|
+
const movedComponents = changes.toMove.join(', ')
|
|
209
|
+
fixes.push(
|
|
210
|
+
fixer.insertTextAfter(importNode, `\nimport { ${movedComponents} } from '${styledReactPath}'`),
|
|
211
|
+
)
|
|
304
212
|
}
|
|
305
213
|
|
|
306
214
|
return fixes
|
|
@@ -345,7 +253,7 @@ module.exports = {
|
|
|
345
253
|
messageId: 'usePrimerReactImport',
|
|
346
254
|
data: {componentName},
|
|
347
255
|
fix(fixer) {
|
|
348
|
-
const {node: importNode} = importInfo
|
|
256
|
+
const {node: importNode, importSource} = importInfo
|
|
349
257
|
const changes = styledReactImportNodeChanges.get(importNode)
|
|
350
258
|
|
|
351
259
|
if (!changes) {
|
|
@@ -368,6 +276,9 @@ module.exports = {
|
|
|
368
276
|
return !componentsToMove.has(name)
|
|
369
277
|
})
|
|
370
278
|
|
|
279
|
+
// Convert @primer/styled-react path to @primer/react path
|
|
280
|
+
const primerReactPath = importSource.replace('@primer/styled-react', '@primer/react')
|
|
281
|
+
|
|
371
282
|
// Check if there's an existing primer-react import to merge with
|
|
372
283
|
const existingPrimerReactImport = Array.from(primerReactImportNodes)[0]
|
|
373
284
|
|
|
@@ -381,7 +292,7 @@ module.exports = {
|
|
|
381
292
|
fixes.push(
|
|
382
293
|
fixer.replaceText(
|
|
383
294
|
existingPrimerReactImport,
|
|
384
|
-
`import { ${newSpecifiers.join(', ')} } from '
|
|
295
|
+
`import { ${newSpecifiers.join(', ')} } from '${primerReactPath}'`,
|
|
385
296
|
),
|
|
386
297
|
)
|
|
387
298
|
fixes.push(fixer.remove(importNode))
|
|
@@ -395,57 +306,29 @@ module.exports = {
|
|
|
395
306
|
fixes.push(
|
|
396
307
|
fixer.replaceText(
|
|
397
308
|
existingPrimerReactImport,
|
|
398
|
-
`import { ${newSpecifiers.join(', ')} } from '
|
|
309
|
+
`import { ${newSpecifiers.join(', ')} } from '${primerReactPath}'`,
|
|
399
310
|
),
|
|
400
311
|
)
|
|
401
312
|
|
|
402
313
|
const remainingNames = remainingSpecifiers.map(spec => spec.imported.name)
|
|
403
314
|
fixes.push(
|
|
404
|
-
fixer.replaceText(
|
|
405
|
-
importNode,
|
|
406
|
-
`import { ${remainingNames.join(', ')} } from '@primer/styled-react'`,
|
|
407
|
-
),
|
|
315
|
+
fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '${importSource}'`),
|
|
408
316
|
)
|
|
409
317
|
} else if (remainingSpecifiers.length === 0) {
|
|
410
318
|
// Case: No existing primer-react import, no remaining styled-react imports
|
|
411
319
|
const movedComponents = changes.toMove.join(', ')
|
|
412
|
-
fixes.push(fixer.replaceText(importNode, `import { ${movedComponents} } from '
|
|
320
|
+
fixes.push(fixer.replaceText(importNode, `import { ${movedComponents} } from '${primerReactPath}'`))
|
|
413
321
|
} else {
|
|
414
322
|
// Case: No existing primer-react import, some styled-react imports remain
|
|
415
323
|
const remainingNames = remainingSpecifiers.map(spec => spec.imported.name)
|
|
416
324
|
fixes.push(
|
|
417
|
-
fixer.replaceText(
|
|
418
|
-
importNode,
|
|
419
|
-
`import { ${remainingNames.join(', ')} } from '@primer/styled-react'`,
|
|
420
|
-
),
|
|
325
|
+
fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '${importSource}'`),
|
|
421
326
|
)
|
|
422
327
|
|
|
423
328
|
const movedComponents = changes.toMove.join(', ')
|
|
424
|
-
fixes.push(
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
return fixes
|
|
428
|
-
},
|
|
429
|
-
})
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Report and fix JSX elements that use aliased components without sx prop
|
|
434
|
-
for (const {node: jsxNode, originalName, openingElement} of jsxElementsWithoutSx) {
|
|
435
|
-
if (!componentsWithSx.has(originalName) && styledReactImports.has(originalName)) {
|
|
436
|
-
context.report({
|
|
437
|
-
node: openingElement,
|
|
438
|
-
messageId: 'usePrimerReactImport',
|
|
439
|
-
data: {componentName: originalName},
|
|
440
|
-
fix(fixer) {
|
|
441
|
-
const fixes = []
|
|
442
|
-
|
|
443
|
-
// Replace the aliased component name with the original component name in JSX opening tag
|
|
444
|
-
fixes.push(fixer.replaceText(openingElement.name, originalName))
|
|
445
|
-
|
|
446
|
-
// Replace the aliased component name in JSX closing tag if it exists
|
|
447
|
-
if (jsxNode.closingElement) {
|
|
448
|
-
fixes.push(fixer.replaceText(jsxNode.closingElement.name, originalName))
|
|
329
|
+
fixes.push(
|
|
330
|
+
fixer.insertTextAfter(importNode, `\nimport { ${movedComponents} } from '${primerReactPath}'`),
|
|
331
|
+
)
|
|
449
332
|
}
|
|
450
333
|
|
|
451
334
|
return fixes
|
|
@@ -462,13 +345,16 @@ module.exports = {
|
|
|
462
345
|
messageId: 'moveToStyledReact',
|
|
463
346
|
data: {importName},
|
|
464
347
|
fix(fixer) {
|
|
465
|
-
const {node: importNode, specifier} = importInfo
|
|
348
|
+
const {node: importNode, specifier, importSource} = importInfo
|
|
466
349
|
const otherSpecifiers = importNode.specifiers.filter(s => s !== specifier)
|
|
467
350
|
|
|
351
|
+
// Convert @primer/react path to @primer/styled-react path
|
|
352
|
+
const styledReactPath = importSource.replace('@primer/react', '@primer/styled-react')
|
|
353
|
+
|
|
468
354
|
// If this is the only import, replace the whole import
|
|
469
355
|
if (otherSpecifiers.length === 0) {
|
|
470
356
|
const prefix = styledTypes.has(importName) ? 'type ' : ''
|
|
471
|
-
return fixer.replaceText(importNode, `import { ${prefix}${importName} } from '
|
|
357
|
+
return fixer.replaceText(importNode, `import { ${prefix}${importName} } from '${styledReactPath}'`)
|
|
472
358
|
}
|
|
473
359
|
|
|
474
360
|
// Otherwise, remove from current import and add new import
|
|
@@ -496,7 +382,7 @@ module.exports = {
|
|
|
496
382
|
// Add new import
|
|
497
383
|
const prefix = styledTypes.has(importName) ? 'type ' : ''
|
|
498
384
|
fixes.push(
|
|
499
|
-
fixer.insertTextAfter(importNode, `\nimport { ${prefix}${importName} } from '
|
|
385
|
+
fixer.insertTextAfter(importNode, `\nimport { ${prefix}${importName} } from '${styledReactPath}'`),
|
|
500
386
|
)
|
|
501
387
|
|
|
502
388
|
return fixes
|