eslint-plugin-primer-react 6.0.2 → 6.1.0

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/.eslintignore ADDED
@@ -0,0 +1,2 @@
1
+ **/dist/**
2
+ **/node_modules/**
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # eslint-plugin-primer-react
2
2
 
3
+ ## 6.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#225](https://github.com/primer/eslint-plugin-primer-react/pull/225) [`b4698df`](https://github.com/primer/eslint-plugin-primer-react/commit/b4698dfd4686067df6cf73788531fc3f835f7747) Thanks [@joshblack](https://github.com/joshblack)! - Add eslint rule for discouraging use of wildcard imports from @primer/react
8
+
3
9
  ## 6.0.2
4
10
 
5
11
  ### Patch Changes
@@ -0,0 +1,25 @@
1
+ # No Wildcard Imports
2
+
3
+ ## Rule Details
4
+
5
+ This rule enforces that no wildcard imports are used from `@primer/react`.
6
+
7
+ 👎 Examples of **incorrect** code for this rule
8
+
9
+ ```jsx
10
+ import {Dialog} from '@primer/react/lib-esm/Dialog/Dialog'
11
+
12
+ function ExampleComponent() {
13
+ return <Dialog>{/* ... */}</Dialog>
14
+ }
15
+ ```
16
+
17
+ 👍 Examples of **correct** code for this rule:
18
+
19
+ ```jsx
20
+ import {Dialog} from '@primer/react/experimental'
21
+
22
+ function ExampleComponent() {
23
+ return <Dialog>{/* ... */}</Dialog>
24
+ }
25
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-primer-react",
3
- "version": "6.0.2",
3
+ "version": "6.1.0",
4
4
  "description": "ESLint rules for Primer React",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -45,8 +45,8 @@
45
45
  "eslint": "^8.42.0",
46
46
  "eslint-plugin-prettier": "^5.0.1",
47
47
  "jest": "^29.7.0",
48
- "markdownlint-cli2": "^0.13.0",
49
- "markdownlint-cli2-formatter-pretty": "^0.0.6",
48
+ "markdownlint-cli2": "^0.14.0",
49
+ "markdownlint-cli2-formatter-pretty": "^0.0.7",
50
50
  "@typescript-eslint/rule-tester": "7.16.0",
51
51
  "@types/jest": "^29.5.12"
52
52
  },
package/src/index.js CHANGED
@@ -11,6 +11,7 @@ module.exports = {
11
11
  'a11y-remove-disable-tooltip': require('./rules/a11y-remove-disable-tooltip'),
12
12
  'a11y-use-next-tooltip': require('./rules/a11y-use-next-tooltip'),
13
13
  'use-deprecated-from-deprecated': require('./rules/use-deprecated-from-deprecated'),
14
+ 'no-wildcard-imports': require('./rules/no-wildcard-imports'),
14
15
  'no-unnecessary-components': require('./rules/no-unnecessary-components'),
15
16
  'prefer-action-list-item-onselect': require('./rules/prefer-action-list-item-onselect'),
16
17
  },
@@ -0,0 +1,363 @@
1
+ 'use strict'
2
+
3
+ const {RuleTester} = require('eslint')
4
+ const rule = require('../no-wildcard-imports')
5
+
6
+ const ruleTester = new RuleTester({
7
+ parser: require.resolve('@typescript-eslint/parser'),
8
+ parserOptions: {
9
+ ecmaVersion: 'latest',
10
+ sourceType: 'module',
11
+ ecmaFeatures: {
12
+ jsx: true,
13
+ },
14
+ },
15
+ })
16
+
17
+ ruleTester.run('no-wildcard-imports', rule, {
18
+ valid: [`import {Button} from '@primer/react'`],
19
+ invalid: [
20
+ // Test unknown path from wildcard import
21
+ {
22
+ code: `import type {UnknownImport} from '@primer/react/lib-esm/unknown-path'`,
23
+ errors: [
24
+ {
25
+ messageId: 'unknownWildcardImport',
26
+ },
27
+ ],
28
+ },
29
+
30
+ // Test type import
31
+ {
32
+ code: `import type {SxProp} from '@primer/react/lib-esm/sx'`,
33
+ output: `import type {SxProp} from '@primer/react'`,
34
+ errors: [
35
+ {
36
+ messageId: 'wildcardMigration',
37
+ data: {
38
+ wildcardEntrypoint: '@primer/react/lib-esm/sx',
39
+ },
40
+ },
41
+ ],
42
+ },
43
+
44
+ // Test multiple type imports
45
+ {
46
+ code: `import type {BetterSystemStyleObject, SxProp, BetterCssProperties} from '@primer/react/lib-esm/sx'`,
47
+ output: `import type {BetterSystemStyleObject, SxProp, BetterCssProperties} from '@primer/react'`,
48
+ errors: [
49
+ {
50
+ messageId: 'wildcardMigration',
51
+ data: {
52
+ wildcardEntrypoint: '@primer/react/lib-esm/sx',
53
+ },
54
+ },
55
+ ],
56
+ },
57
+
58
+ // Test import alias
59
+ {
60
+ code: `import type {SxProp as RenamedSxProp} from '@primer/react/lib-esm/sx'`,
61
+ output: `import type {SxProp as RenamedSxProp} from '@primer/react'`,
62
+ errors: [
63
+ {
64
+ messageId: 'wildcardMigration',
65
+ data: {
66
+ wildcardEntrypoint: '@primer/react/lib-esm/sx',
67
+ },
68
+ },
69
+ ],
70
+ },
71
+
72
+ // Test default import
73
+ {
74
+ code: `import useIsomorphicLayoutEffect from '@primer/react/lib-esm/useIsomorphicLayoutEffect'`,
75
+ output: `import {useIsomorphicLayoutEffect} from '@primer/react'`,
76
+ errors: [
77
+ {
78
+ messageId: 'wildcardMigration',
79
+ data: {
80
+ wildcardEntrypoint: '@primer/react/lib-esm/useIsomorphicLayoutEffect',
81
+ },
82
+ },
83
+ ],
84
+ },
85
+
86
+ // Test multiple wildcard imports into single entrypoint
87
+ {
88
+ code: `import useResizeObserver from '@primer/react/lib-esm/hooks/useResizeObserver'
89
+ import useIsomorphicLayoutEffect from '@primer/react/lib-esm/useIsomorphicLayoutEffect'`,
90
+ output: `import {useResizeObserver} from '@primer/react'
91
+ import {useIsomorphicLayoutEffect} from '@primer/react'`,
92
+ errors: [
93
+ {
94
+ messageId: 'wildcardMigration',
95
+ data: {
96
+ wildcardEntrypoint: '@primer/react/lib-esm/hooks/useResizeObserver',
97
+ },
98
+ },
99
+ {
100
+ messageId: 'wildcardMigration',
101
+ data: {
102
+ wildcardEntrypoint: '@primer/react/lib-esm/useIsomorphicLayoutEffect',
103
+ },
104
+ },
105
+ ],
106
+ },
107
+
108
+ // Test migrations
109
+
110
+ // Components --------------------------------------------------------------
111
+ {
112
+ code: `import {ButtonBase} from '@primer/react/lib-esm/Button/ButtonBase';
113
+ import type {ButtonBaseProps} from '@primer/react/lib-esm/Button/ButtonBase'`,
114
+ output: `import {ButtonBase} from '@primer/react'
115
+ import type {ButtonBaseProps} from '@primer/react'`,
116
+ errors: [
117
+ {
118
+ messageId: 'wildcardMigration',
119
+ data: {
120
+ wildcardEntrypoint: '@primer/react/lib-esm/Button/ButtonBase',
121
+ },
122
+ },
123
+ {
124
+ messageId: 'wildcardMigration',
125
+ data: {
126
+ wildcardEntrypoint: '@primer/react/lib-esm/Button/ButtonBase',
127
+ },
128
+ },
129
+ ],
130
+ },
131
+ {
132
+ code: `import type {ButtonBaseProps} from '@primer/react/lib-esm/Button/types'`,
133
+ output: `import type {ButtonBaseProps} from '@primer/react'`,
134
+ errors: [
135
+ {
136
+ messageId: 'wildcardMigration',
137
+ data: {
138
+ wildcardEntrypoint: '@primer/react/lib-esm/Button/types',
139
+ },
140
+ },
141
+ ],
142
+ },
143
+ {
144
+ code: `import {Dialog} from '@primer/react/lib-esm/Dialog/Dialog'`,
145
+ output: `import {Dialog} from '@primer/react/experimental'`,
146
+ errors: [
147
+ {
148
+ messageId: 'wildcardMigration',
149
+ data: {
150
+ wildcardEntrypoint: '@primer/react/lib-esm/Dialog/Dialog',
151
+ },
152
+ },
153
+ ],
154
+ },
155
+ {
156
+ code: `import {SelectPanel} from '@primer/react/lib-esm/SelectPanel/SelectPanel'`,
157
+ output: `import {SelectPanel} from '@primer/react/experimental'`,
158
+ errors: [
159
+ {
160
+ messageId: 'wildcardMigration',
161
+ data: {
162
+ wildcardEntrypoint: '@primer/react/lib-esm/SelectPanel/SelectPanel',
163
+ },
164
+ },
165
+ ],
166
+ },
167
+ {
168
+ code: `import type {SelectPanelProps} from '@primer/react/lib-esm/SelectPanel/SelectPanel'`,
169
+ output: `import type {SelectPanelProps} from '@primer/react/experimental'`,
170
+ errors: [
171
+ {
172
+ messageId: 'wildcardMigration',
173
+ data: {
174
+ wildcardEntrypoint: '@primer/react/lib-esm/SelectPanel/SelectPanel',
175
+ },
176
+ },
177
+ ],
178
+ },
179
+ {
180
+ code: `import type {LabelColorOptions} from '@primer/react/lib-esm/Label/Label'`,
181
+ output: `import type {LabelColorOptions} from '@primer/react'`,
182
+ errors: [
183
+ {
184
+ messageId: 'wildcardMigration',
185
+ data: {
186
+ wildcardEntrypoint: '@primer/react/lib-esm/Label/Label',
187
+ },
188
+ },
189
+ ],
190
+ },
191
+ {
192
+ code: `import VisuallyHidden from '@primer/react/lib-esm/_VisuallyHidden'`,
193
+ output: `import {VisuallyHidden} from '@primer/react'`,
194
+ errors: [
195
+ {
196
+ messageId: 'wildcardMigration',
197
+ data: {
198
+ wildcardEntrypoint: '@primer/react/lib-esm/_VisuallyHidden',
199
+ },
200
+ },
201
+ ],
202
+ },
203
+ {
204
+ code: `import type {IssueLabelTokenProps} from '@primer/react/lib-esm/Token/IssueLabelToken'`,
205
+ output: `import type {IssueLabelTokenProps} from '@primer/react'`,
206
+ errors: [
207
+ {
208
+ messageId: 'wildcardMigration',
209
+ data: {
210
+ wildcardEntrypoint: '@primer/react/lib-esm/Token/IssueLabelToken',
211
+ },
212
+ },
213
+ ],
214
+ },
215
+ {
216
+ code: `import type {TokenSizeKeys} from '@primer/react/lib-esm/Token/TokenBase'`,
217
+ output: `import type {TokenSizeKeys} from '@primer/react'`,
218
+ errors: [
219
+ {
220
+ messageId: 'wildcardMigration',
221
+ data: {
222
+ wildcardEntrypoint: '@primer/react/lib-esm/Token/TokenBase',
223
+ },
224
+ },
225
+ ],
226
+ },
227
+ {
228
+ code: `import type {ItemProps} from '@primer/react/lib-esm/deprecated/ActionList'`,
229
+ output: `import type {ActionListItemProps} from '@primer/react/deprecated'`,
230
+ errors: [
231
+ {
232
+ messageId: 'wildcardMigration',
233
+ data: {
234
+ wildcardEntrypoint: '@primer/react/lib-esm/deprecated/ActionList',
235
+ },
236
+ },
237
+ ],
238
+ },
239
+ {
240
+ code: `import type {GroupedListProps} from '@primer/react/lib-esm/deprecated/ActionList/List'`,
241
+ output: `import type {ActionListGroupedListProps} from '@primer/react/deprecated'`,
242
+ errors: [
243
+ {
244
+ messageId: 'wildcardMigration',
245
+ data: {
246
+ wildcardEntrypoint: '@primer/react/lib-esm/deprecated/ActionList/List',
247
+ },
248
+ },
249
+ ],
250
+ },
251
+ {
252
+ code: `import {ItemInput} from '@primer/react/lib-esm/deprecated/ActionList/List'`,
253
+ output: `import {ActionListItemInput} from '@primer/react/deprecated'`,
254
+ errors: [
255
+ {
256
+ messageId: 'wildcardMigration',
257
+ data: {
258
+ wildcardEntrypoint: '@primer/react/lib-esm/deprecated/ActionList/List',
259
+ },
260
+ },
261
+ ],
262
+ },
263
+ {
264
+ code: `import type {ItemProps} from '@primer/react/lib-esm/deprecated/ActionList/Item'`,
265
+ output: `import type {ActionListItemProps} from '@primer/react/deprecated'`,
266
+ errors: [
267
+ {
268
+ messageId: 'wildcardMigration',
269
+ data: {
270
+ wildcardEntrypoint: '@primer/react/lib-esm/deprecated/ActionList/Item',
271
+ },
272
+ },
273
+ ],
274
+ },
275
+
276
+ // Hooks -------------------------------------------------------------------
277
+
278
+ // @primer/react/lib-esm/useIsomorphicLayoutEffect
279
+ {
280
+ code: `import useIsomorphicLayoutEffect from '@primer/react/lib-esm/useIsomorphicLayoutEffect'`,
281
+ output: `import {useIsomorphicLayoutEffect} from '@primer/react'`,
282
+ errors: [
283
+ {
284
+ messageId: 'wildcardMigration',
285
+ data: {
286
+ wildcardEntrypoint: '@primer/react/lib-esm/useIsomorphicLayoutEffect',
287
+ },
288
+ },
289
+ ],
290
+ },
291
+
292
+ // @primer/react/lib-esm/hooks/useResizeObserver
293
+ {
294
+ code: `import useResizeObserver from '@primer/react/lib-esm/hooks/useResizeObserver'`,
295
+ output: `import {useResizeObserver} from '@primer/react'`,
296
+ errors: [
297
+ {
298
+ messageId: 'wildcardMigration',
299
+ data: {
300
+ wildcardEntrypoint: '@primer/react/lib-esm/hooks/useResizeObserver',
301
+ },
302
+ },
303
+ ],
304
+ },
305
+
306
+ // @primer/react/lib-esm/hooks/useProvidedRefOrCreate
307
+ {
308
+ code: `import useProvidedRefOrCreate from '@primer/react/lib-esm/hooks/useProvidedRefOrCreate'`,
309
+ output: `import {useProvidedRefOrCreate} from '@primer/react'`,
310
+ errors: [
311
+ {
312
+ messageId: 'wildcardMigration',
313
+ data: {
314
+ wildcardEntrypoint: '@primer/react/lib-esm/hooks/useProvidedRefOrCreate',
315
+ },
316
+ },
317
+ ],
318
+ },
319
+
320
+ // @primer/react/lib-esm/hooks/useResponsiveValue
321
+ {
322
+ code: `import useResponsiveValue from '@primer/react/lib-esm/hooks/useResponsiveValue'`,
323
+ output: `import {useResponsiveValue} from '@primer/react'`,
324
+ errors: [
325
+ {
326
+ messageId: 'wildcardMigration',
327
+ data: {
328
+ wildcardEntrypoint: '@primer/react/lib-esm/hooks/useResponsiveValue',
329
+ },
330
+ },
331
+ ],
332
+ },
333
+
334
+ // Utilities ---------------------------------------------------------------
335
+
336
+ // @primer/react/lib-esm/sx
337
+ {
338
+ code: `import type {BetterSystemStyleObject, SxProp, BetterCssProperties} from '@primer/react/lib-esm/sx'`,
339
+ output: `import type {BetterSystemStyleObject, SxProp, BetterCssProperties} from '@primer/react'`,
340
+ errors: [
341
+ {
342
+ messageId: 'wildcardMigration',
343
+ data: {
344
+ wildcardEntrypoint: '@primer/react/lib-esm/sx',
345
+ },
346
+ },
347
+ ],
348
+ },
349
+ // @primer/react/lib-esm/FeatureFlags/DefaultFeatureFlags
350
+ {
351
+ code: `import {DefaultFeatureFlags} from '@primer/react/lib-esm/FeatureFlags/DefaultFeatureFlags'`,
352
+ output: `import {DefaultFeatureFlags} from '@primer/react/experimental'`,
353
+ errors: [
354
+ {
355
+ messageId: 'wildcardMigration',
356
+ data: {
357
+ wildcardEntrypoint: '@primer/react/lib-esm/FeatureFlags/DefaultFeatureFlags',
358
+ },
359
+ },
360
+ ],
361
+ },
362
+ ],
363
+ })
@@ -0,0 +1,368 @@
1
+ 'use strict'
2
+
3
+ const url = require('../url')
4
+
5
+ const wildcardImports = new Map([
6
+ // Components
7
+ [
8
+ '@primer/react/lib-esm/Button/ButtonBase',
9
+ [
10
+ {
11
+ type: 'type',
12
+ name: 'ButtonBaseProps',
13
+ from: '@primer/react',
14
+ },
15
+ {
16
+ name: 'ButtonBase',
17
+ from: '@primer/react',
18
+ },
19
+ ],
20
+ ],
21
+ [
22
+ '@primer/react/lib-esm/Button/types',
23
+ [
24
+ {
25
+ type: 'type',
26
+ name: 'ButtonBaseProps',
27
+ from: '@primer/react',
28
+ },
29
+ ],
30
+ ],
31
+ [
32
+ '@primer/react/lib-esm/Dialog/Dialog',
33
+ [
34
+ {
35
+ name: 'Dialog',
36
+ from: '@primer/react/experimental',
37
+ },
38
+ ],
39
+ ],
40
+ [
41
+ '@primer/react/lib-esm/SelectPanel/SelectPanel',
42
+ [
43
+ {
44
+ name: 'SelectPanel',
45
+ from: '@primer/react/experimental',
46
+ },
47
+ {
48
+ type: 'type',
49
+ name: 'SelectPanelProps',
50
+ from: '@primer/react/experimental',
51
+ },
52
+ ],
53
+ ],
54
+ [
55
+ '@primer/react/lib-esm/Label/Label',
56
+ [
57
+ {
58
+ type: 'type',
59
+ name: 'LabelColorOptions',
60
+ from: '@primer/react',
61
+ },
62
+ ],
63
+ ],
64
+ [
65
+ '@primer/react/lib-esm/_VisuallyHidden',
66
+ [
67
+ {
68
+ name: 'default',
69
+ from: '@primer/react',
70
+ as: 'VisuallyHidden',
71
+ },
72
+ ],
73
+ ],
74
+ [
75
+ '@primer/react/lib-esm/Token/IssueLabelToken',
76
+ [
77
+ {
78
+ type: 'type',
79
+ name: 'IssueLabelTokenProps',
80
+ from: '@primer/react',
81
+ },
82
+ ],
83
+ ],
84
+ [
85
+ '@primer/react/lib-esm/Token/TokenBase',
86
+ [
87
+ {
88
+ type: 'type',
89
+ name: 'TokenSizeKeys',
90
+ from: '@primer/react',
91
+ },
92
+ ],
93
+ ],
94
+ [
95
+ '@primer/react/lib-esm/deprecated/ActionList',
96
+ [
97
+ {
98
+ type: 'type',
99
+ name: 'ItemProps',
100
+ from: '@primer/react/deprecated',
101
+ as: 'ActionListItemProps',
102
+ },
103
+ ],
104
+ ],
105
+ [
106
+ '@primer/react/lib-esm/deprecated/ActionList/List',
107
+ [
108
+ {
109
+ type: 'type',
110
+ name: 'GroupedListProps',
111
+ from: '@primer/react/deprecated',
112
+ as: 'ActionListGroupedListProps',
113
+ },
114
+ {
115
+ name: 'ItemInput',
116
+ from: '@primer/react/deprecated',
117
+ as: 'ActionListItemInput',
118
+ },
119
+ ],
120
+ ],
121
+ [
122
+ '@primer/react/lib-esm/deprecated/ActionList/Item',
123
+ [
124
+ {
125
+ type: 'type',
126
+ name: 'ItemProps',
127
+ from: '@primer/react/deprecated',
128
+ as: 'ActionListItemProps',
129
+ },
130
+ ],
131
+ ],
132
+
133
+ // Hooks
134
+ [
135
+ '@primer/react/lib-esm/useIsomorphicLayoutEffect',
136
+ [
137
+ {
138
+ name: 'default',
139
+ from: '@primer/react',
140
+ as: 'useIsomorphicLayoutEffect',
141
+ },
142
+ ],
143
+ ],
144
+ [
145
+ '@primer/react/lib-esm/hooks/useResizeObserver',
146
+ [
147
+ {
148
+ name: 'default',
149
+ from: '@primer/react',
150
+ as: 'useResizeObserver',
151
+ },
152
+ ],
153
+ ],
154
+ [
155
+ '@primer/react/lib-esm/hooks/useProvidedRefOrCreate',
156
+ [
157
+ {
158
+ name: 'default',
159
+ from: '@primer/react',
160
+ as: 'useProvidedRefOrCreate',
161
+ },
162
+ ],
163
+ ],
164
+ [
165
+ '@primer/react/lib-esm/hooks/useResponsiveValue',
166
+ [
167
+ {
168
+ name: 'default',
169
+ from: '@primer/react',
170
+ as: 'useResponsiveValue',
171
+ },
172
+ ],
173
+ ],
174
+
175
+ // Utilities
176
+ [
177
+ '@primer/react/lib-esm/sx',
178
+ [
179
+ {
180
+ type: 'type',
181
+ name: 'BetterSystemStyleObject',
182
+ from: '@primer/react',
183
+ },
184
+ {
185
+ type: 'type',
186
+ name: 'SxProp',
187
+ from: '@primer/react',
188
+ },
189
+ {
190
+ type: 'type',
191
+ name: 'BetterCssProperties',
192
+ from: '@primer/react',
193
+ },
194
+ ],
195
+ ],
196
+ [
197
+ '@primer/react/lib-esm/FeatureFlags/DefaultFeatureFlags',
198
+ [
199
+ {
200
+ name: 'DefaultFeatureFlags',
201
+ from: '@primer/react/experimental',
202
+ },
203
+ ],
204
+ ],
205
+ [
206
+ '@primer/react/lib-esm/theme',
207
+ [
208
+ {
209
+ name: 'default',
210
+ from: '@primer/react',
211
+ as: 'theme',
212
+ },
213
+ ],
214
+ ],
215
+ ])
216
+
217
+ /**
218
+ * @type {import('eslint').Rule.RuleModule}
219
+ */
220
+ module.exports = {
221
+ meta: {
222
+ type: 'problem',
223
+ docs: {
224
+ description: 'Wildcard imports are discouraged. Import from a main entrypoint instead',
225
+ recommended: true,
226
+ url: url(module),
227
+ },
228
+ fixable: true,
229
+ schema: [],
230
+ messages: {
231
+ unknownWildcardImport:
232
+ 'Wildcard imports from @primer/react are not allowed. Import from @primer/react, @primer/react/experimental, or @primer/react/deprecated instead',
233
+ knownWildcardImport:
234
+ 'Wildcard import {{ specifier }} from {{ wildcardEntrypoint }} are not allowed. Import from @primer/react, @primer/react/experimental, or @primer/react/deprecated instead',
235
+ wildcardMigration:
236
+ 'Wildcard imports from {{ wildcardEntrypoint }} are not allowed. Import from @primer/react, @primer/react/experimental, or @primer/react/deprecated instead',
237
+ },
238
+ },
239
+ create(context) {
240
+ return {
241
+ ImportDeclaration(node) {
242
+ if (!node.source.value.startsWith('@primer/react/lib-esm')) {
243
+ return
244
+ }
245
+
246
+ const wildcardImportMigrations = wildcardImports.get(node.source.value)
247
+ if (!wildcardImportMigrations) {
248
+ context.report({
249
+ node,
250
+ messageId: 'unknownWildcardImport',
251
+ })
252
+ return
253
+ }
254
+
255
+ /**
256
+ * Maps entrypoint to array of changes. This tuple contains the new
257
+ * imported name from the entrypoint along with the existing local name
258
+ * @type {Map<string, Array<[string, string]>>}
259
+ */
260
+ const changes = new Map()
261
+
262
+ for (const specifier of node.specifiers) {
263
+ const migration = wildcardImportMigrations.find(migration => {
264
+ if (specifier.type === 'ImportDefaultSpecifier') {
265
+ return migration.name === 'default'
266
+ }
267
+ return specifier.imported.name === migration.name
268
+ })
269
+
270
+ // If we do not have a migration, we should report an error even if we
271
+ // cannot autofix it
272
+ if (!migration) {
273
+ context.report({
274
+ node,
275
+ messageId: 'unknownWildcardImport',
276
+ })
277
+ return
278
+ }
279
+
280
+ if (!changes.has(migration.from)) {
281
+ changes.set(migration.from, [])
282
+ }
283
+
284
+ if (migration.as) {
285
+ changes.get(migration.from).push([migration.as, migration.as, migration.type])
286
+ } else {
287
+ changes.get(migration.from).push([migration.name, specifier.local.name, migration.type])
288
+ }
289
+ }
290
+
291
+ if (changes.length === 0) {
292
+ return
293
+ }
294
+
295
+ context.report({
296
+ node,
297
+ messageId: 'wildcardMigration',
298
+ data: {
299
+ wildcardEntrypoint: node.source.value,
300
+ },
301
+ *fix(fixer) {
302
+ for (const [entrypoint, importSpecifiers] of changes) {
303
+ const typeSpecifiers = importSpecifiers.filter(([, , type]) => {
304
+ return type === 'type'
305
+ })
306
+
307
+ // If all imports are type imports, emit emit as `import type {specifier} from '...'`
308
+ if (typeSpecifiers.length === importSpecifiers.length) {
309
+ const namedSpecifiers = typeSpecifiers.filter(([imported]) => {
310
+ return imported !== 'default'
311
+ })
312
+ const defaultSpecifier = typeSpecifiers.find(([imported]) => {
313
+ return imported === 'default'
314
+ })
315
+
316
+ if (namedSpecifiers.length > 0 && !defaultSpecifier) {
317
+ const specifiers = namedSpecifiers.map(([imported, local]) => {
318
+ if (imported !== local) {
319
+ return `${imported} as ${local}`
320
+ }
321
+ return imported
322
+ })
323
+ yield fixer.replaceText(node, `import type {${specifiers.join(', ')}} from '${entrypoint}'`)
324
+ } else if (namedSpecifiers.length > 0 && defaultSpecifier) {
325
+ yield fixer.replaceText(
326
+ node,
327
+ `import type ${defaultSpecifier[1]}, ${specifiers.join(', ')} from '${entrypoint}'`,
328
+ )
329
+ } else if (defaultSpecifier && namedSpecifiers.length === 0) {
330
+ yield fixer.replaceText(node, `import type ${defaultSpecifier[1]} from '${entrypoint}'`)
331
+ }
332
+
333
+ return
334
+ }
335
+
336
+ // Otherwise, we have a mix of type and value imports to emit
337
+ const valueSpecifiers = importSpecifiers.filter(([, , type]) => {
338
+ return type !== 'type'
339
+ })
340
+
341
+ if (valueSpecifiers.length === 0) {
342
+ return
343
+ }
344
+
345
+ const specifiers = valueSpecifiers.map(([imported, local]) => {
346
+ if (imported !== local) {
347
+ return `${imported} as ${local}`
348
+ }
349
+ return imported
350
+ })
351
+ yield fixer.replaceText(node, `import {${specifiers.join(', ')}} from '${entrypoint}'`)
352
+
353
+ if (typeSpecifiers.length > 0) {
354
+ const specifiers = valueSpecifiers.map(([imported, local]) => {
355
+ if (imported !== local) {
356
+ return `${imported} as ${local}`
357
+ }
358
+ return imported
359
+ })
360
+ yield fixer.insertTextAfter(node, `import type {${specifiers.join(', ')}} from '${entrypoint}'`)
361
+ }
362
+ }
363
+ },
364
+ })
365
+ },
366
+ }
367
+ },
368
+ }