eslint-plugin-primer-react 6.1.3-rc.a9a6d84 → 6.1.4-rc.93bd380

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,5 +1,11 @@
1
1
  # eslint-plugin-primer-react
2
2
 
3
+ ## 6.1.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#249](https://github.com/primer/eslint-plugin-primer-react/pull/249) [`f8c8a9d`](https://github.com/primer/eslint-plugin-primer-react/commit/f8c8a9df1b49cb7f487eb0dba3e7f46ac7f7e23b) Thanks [@joshblack](https://github.com/joshblack)! - Update no-wildcard-imports rule to not create separate imports for type only imports. This prevents an issue downstream with autofixers
8
+
3
9
  ## 6.1.3
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-primer-react",
3
- "version": "6.1.3-rc.a9a6d84",
3
+ "version": "6.1.4-rc.93bd380",
4
4
  "description": "ESLint rules for Primer React",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -30,7 +30,7 @@ ruleTester.run('no-wildcard-imports', rule, {
30
30
  // Test type import
31
31
  {
32
32
  code: `import type {SxProp} from '@primer/react/lib-esm/sx'`,
33
- output: `import type {SxProp} from '@primer/react'`,
33
+ output: `import {type SxProp} from '@primer/react'`,
34
34
  errors: [
35
35
  {
36
36
  messageId: 'wildcardMigration',
@@ -44,7 +44,7 @@ ruleTester.run('no-wildcard-imports', rule, {
44
44
  // Test multiple type imports
45
45
  {
46
46
  code: `import type {BetterSystemStyleObject, SxProp, BetterCssProperties} from '@primer/react/lib-esm/sx'`,
47
- output: `import type {BetterSystemStyleObject, SxProp, BetterCssProperties} from '@primer/react'`,
47
+ output: `import {type BetterSystemStyleObject, type SxProp, type BetterCssProperties} from '@primer/react'`,
48
48
  errors: [
49
49
  {
50
50
  messageId: 'wildcardMigration',
@@ -58,7 +58,7 @@ ruleTester.run('no-wildcard-imports', rule, {
58
58
  // Test import alias
59
59
  {
60
60
  code: `import type {SxProp as RenamedSxProp} from '@primer/react/lib-esm/sx'`,
61
- output: `import type {SxProp as RenamedSxProp} from '@primer/react'`,
61
+ output: `import {type SxProp as RenamedSxProp} from '@primer/react'`,
62
62
  errors: [
63
63
  {
64
64
  messageId: 'wildcardMigration',
@@ -108,7 +108,7 @@ ruleTester.run('no-wildcard-imports', rule, {
108
108
  // Test renamed wildcard imports
109
109
  {
110
110
  code: `import type {ItemProps} from '@primer/react/lib-esm/deprecated/ActionList/Item'`,
111
- output: `import type {ActionListItemProps as ItemProps} from '@primer/react/deprecated'`,
111
+ output: `import {type ActionListItemProps as ItemProps} from '@primer/react/deprecated'`,
112
112
  errors: [
113
113
  {
114
114
  messageId: 'wildcardMigration',
@@ -122,8 +122,7 @@ ruleTester.run('no-wildcard-imports', rule, {
122
122
  // Test mixed imports
123
123
  {
124
124
  code: `import {Dialog, type DialogProps} from '@primer/react/lib-esm/Dialog/Dialog'`,
125
- output: `import {Dialog} from '@primer/react/experimental'
126
- import type {DialogProps} from '@primer/react/experimental'`,
125
+ output: `import {Dialog, type DialogProps} from '@primer/react/experimental'`,
127
126
  errors: [
128
127
  {
129
128
  messageId: 'wildcardMigration',
@@ -134,6 +133,22 @@ import type {DialogProps} from '@primer/react/experimental'`,
134
133
  ],
135
134
  },
136
135
 
136
+ // Use existing imports
137
+ {
138
+ code: `import {Box, type BoxProps} from '@primer/react'
139
+ import type {BetterSystemStyleObject} from '@primer/react/lib-esm/sx'`,
140
+ output: `import {Box, type BoxProps, type BetterSystemStyleObject} from '@primer/react'
141
+ `,
142
+ errors: [
143
+ {
144
+ messageId: 'wildcardMigration',
145
+ data: {
146
+ wildcardEntrypoint: '@primer/react/lib-esm/sx',
147
+ },
148
+ },
149
+ ],
150
+ },
151
+
137
152
  // Test migrations
138
153
 
139
154
  // Test helpers ------------------------------------------------------------
@@ -155,7 +170,7 @@ import type {DialogProps} from '@primer/react/experimental'`,
155
170
  code: `import {ButtonBase} from '@primer/react/lib-esm/Button/ButtonBase';
156
171
  import type {ButtonBaseProps} from '@primer/react/lib-esm/Button/ButtonBase'`,
157
172
  output: `import {ButtonBase} from '@primer/react'
158
- import type {ButtonBaseProps} from '@primer/react'`,
173
+ import {type ButtonBaseProps} from '@primer/react'`,
159
174
  errors: [
160
175
  {
161
176
  messageId: 'wildcardMigration',
@@ -173,7 +188,7 @@ import type {ButtonBaseProps} from '@primer/react'`,
173
188
  },
174
189
  {
175
190
  code: `import type {ButtonBaseProps} from '@primer/react/lib-esm/Button/types'`,
176
- output: `import type {ButtonBaseProps} from '@primer/react'`,
191
+ output: `import {type ButtonBaseProps} from '@primer/react'`,
177
192
  errors: [
178
193
  {
179
194
  messageId: 'wildcardMigration',
@@ -209,7 +224,7 @@ import type {ButtonBaseProps} from '@primer/react'`,
209
224
  },
210
225
  {
211
226
  code: `import type {SelectPanelProps} from '@primer/react/lib-esm/SelectPanel/SelectPanel'`,
212
- output: `import type {SelectPanelProps} from '@primer/react'`,
227
+ output: `import {type SelectPanelProps} from '@primer/react'`,
213
228
  errors: [
214
229
  {
215
230
  messageId: 'wildcardMigration',
@@ -221,7 +236,7 @@ import type {ButtonBaseProps} from '@primer/react'`,
221
236
  },
222
237
  {
223
238
  code: `import type {LabelColorOptions} from '@primer/react/lib-esm/Label/Label'`,
224
- output: `import type {LabelColorOptions} from '@primer/react'`,
239
+ output: `import {type LabelColorOptions} from '@primer/react'`,
225
240
  errors: [
226
241
  {
227
242
  messageId: 'wildcardMigration',
@@ -245,7 +260,7 @@ import type {ButtonBaseProps} from '@primer/react'`,
245
260
  },
246
261
  {
247
262
  code: `import type {IssueLabelTokenProps} from '@primer/react/lib-esm/Token/IssueLabelToken'`,
248
- output: `import type {IssueLabelTokenProps} from '@primer/react'`,
263
+ output: `import {type IssueLabelTokenProps} from '@primer/react'`,
249
264
  errors: [
250
265
  {
251
266
  messageId: 'wildcardMigration',
@@ -257,7 +272,7 @@ import type {ButtonBaseProps} from '@primer/react'`,
257
272
  },
258
273
  {
259
274
  code: `import type {TokenSizeKeys} from '@primer/react/lib-esm/Token/TokenBase'`,
260
- output: `import type {TokenSizeKeys} from '@primer/react'`,
275
+ output: `import {type TokenSizeKeys} from '@primer/react'`,
261
276
  errors: [
262
277
  {
263
278
  messageId: 'wildcardMigration',
@@ -269,7 +284,7 @@ import type {ButtonBaseProps} from '@primer/react'`,
269
284
  },
270
285
  {
271
286
  code: `import type {ItemProps} from '@primer/react/lib-esm/deprecated/ActionList'`,
272
- output: `import type {ActionListItemProps as ItemProps} from '@primer/react/deprecated'`,
287
+ output: `import {type ActionListItemProps as ItemProps} from '@primer/react/deprecated'`,
273
288
  errors: [
274
289
  {
275
290
  messageId: 'wildcardMigration',
@@ -281,7 +296,7 @@ import type {ButtonBaseProps} from '@primer/react'`,
281
296
  },
282
297
  {
283
298
  code: `import type {GroupedListProps} from '@primer/react/lib-esm/deprecated/ActionList/List'`,
284
- output: `import type {ActionListGroupedListProps as GroupedListProps} from '@primer/react/deprecated'`,
299
+ output: `import {type ActionListGroupedListProps as GroupedListProps} from '@primer/react/deprecated'`,
285
300
  errors: [
286
301
  {
287
302
  messageId: 'wildcardMigration',
@@ -305,7 +320,7 @@ import type {ButtonBaseProps} from '@primer/react'`,
305
320
  },
306
321
  {
307
322
  code: `import type {ItemProps} from '@primer/react/lib-esm/deprecated/ActionList/Item'`,
308
- output: `import type {ActionListItemProps as ItemProps} from '@primer/react/deprecated'`,
323
+ output: `import {type ActionListItemProps as ItemProps} from '@primer/react/deprecated'`,
309
324
  errors: [
310
325
  {
311
326
  messageId: 'wildcardMigration',
@@ -375,7 +390,7 @@ import type {ButtonBaseProps} from '@primer/react'`,
375
390
  },
376
391
  {
377
392
  code: `import type {ResponsiveValue} from '@primer/react/lib-esm/hooks/useResponsiveValue'`,
378
- output: `import type {ResponsiveValue} from '@primer/react'`,
393
+ output: `import {type ResponsiveValue} from '@primer/react'`,
379
394
  errors: [
380
395
  {
381
396
  messageId: 'wildcardMigration',
@@ -391,7 +406,7 @@ import type {ButtonBaseProps} from '@primer/react'`,
391
406
  // @primer/react/lib-esm/sx
392
407
  {
393
408
  code: `import type {BetterSystemStyleObject, SxProp, BetterCssProperties} from '@primer/react/lib-esm/sx'`,
394
- output: `import type {BetterSystemStyleObject, SxProp, BetterCssProperties} from '@primer/react'`,
409
+ output: `import {type BetterSystemStyleObject, type SxProp, type BetterCssProperties} from '@primer/react'`,
395
410
  errors: [
396
411
  {
397
412
  messageId: 'wildcardMigration',
@@ -265,6 +265,20 @@ module.exports = {
265
265
  create(context) {
266
266
  return {
267
267
  ImportDeclaration(node) {
268
+ if (node.source.value === '@primer/react/lib-esm/utils/test-helpers') {
269
+ context.report({
270
+ node,
271
+ messageId: 'wildcardMigration',
272
+ data: {
273
+ wildcardEntrypoint: node.source.value,
274
+ },
275
+ fix(fixer) {
276
+ return fixer.replaceText(node.source, `'@primer/react/test-helpers'`)
277
+ },
278
+ })
279
+ return
280
+ }
281
+
268
282
  if (!node.source.value.startsWith('@primer/react/lib-esm')) {
269
283
  return
270
284
  }
@@ -340,64 +354,124 @@ module.exports = {
340
354
  },
341
355
  *fix(fixer) {
342
356
  for (const [entrypoint, importSpecifiers] of changes) {
343
- const typeSpecifiers = importSpecifiers.filter(([, , type]) => {
344
- return type === 'type'
357
+ const importDeclaration = node.parent.body.find(node => {
358
+ return (
359
+ node.type === 'ImportDeclaration' && node.source.value === entrypoint && node.importKind !== 'type'
360
+ )
361
+ })
362
+ const typeImportDeclaration = node.parent.body.find(node => {
363
+ return (
364
+ node.type === 'ImportDeclaration' && node.source.value === entrypoint && node.importKind === 'type'
365
+ )
366
+ })
367
+ let originalImportReplaced = false
368
+ const namedSpecifiers = importSpecifiers.filter(([imported, , type]) => {
369
+ return imported !== 'default' && type !== 'type'
370
+ })
371
+ const namedTypeSpecifiers = importSpecifiers.filter(([imported, , type]) => {
372
+ return imported !== 'default' && type === 'type'
373
+ })
374
+ let defaultSpecifier = importSpecifiers.find(([imported, , type]) => {
375
+ return imported === 'default' && type !== 'type'
376
+ })
377
+ if (defaultSpecifier) {
378
+ defaultSpecifier = defaultSpecifier[1]
379
+ }
380
+ let defaultTypeSpecifier = importSpecifiers.find(([imported, , type]) => {
381
+ return imported === 'default' && type === 'type'
345
382
  })
383
+ if (defaultTypeSpecifier) {
384
+ defaultTypeSpecifier = `type ${defaultTypeSpecifier[1]}`
385
+ }
346
386
 
347
- // If all imports are type imports, emit emit as `import type {specifier} from '...'`
348
- if (typeSpecifiers.length === importSpecifiers.length) {
349
- const namedSpecifiers = typeSpecifiers.filter(([imported]) => {
350
- return imported !== 'default'
351
- })
352
- const defaultSpecifier = typeSpecifiers.find(([imported]) => {
353
- return imported === 'default'
354
- })
387
+ // Reuse a type import if it exists
388
+ if (typeImportDeclaration) {
389
+ const firstSpecifier = typeImportDeclaration.specifiers[0]
390
+ const lastSpecifier = typeImportDeclaration.specifiers[typeImportDeclaration.specifiers.length - 1]
355
391
 
356
- if (namedSpecifiers.length > 0 && !defaultSpecifier) {
357
- const specifiers = namedSpecifiers.map(([imported, local]) => {
392
+ if (defaultTypeSpecifier) {
393
+ const postfix =
394
+ namedTypeSpecifiers.length > 0 || typeImportDeclaration.specifiers.length > 0 ? ', ' : ' '
395
+ yield fixer.insertTextBeforeRange(
396
+ [firstSpecifier.range[0] - 2, firstSpecifier.range[1]],
397
+ `${defaultTypeSpecifier}${postfix}`,
398
+ )
399
+ }
400
+
401
+ if (namedTypeSpecifiers.length > 0) {
402
+ const specifiers = namedTypeSpecifiers.map(([imported, local]) => {
358
403
  if (imported !== local) {
359
404
  return `${imported} as ${local}`
360
405
  }
361
406
  return imported
362
407
  })
363
- yield fixer.replaceText(node, `import type {${specifiers.join(', ')}} from '${entrypoint}'`)
364
- } else if (namedSpecifiers.length > 0 && defaultSpecifier) {
365
- yield fixer.replaceText(
366
- node,
367
- `import type ${defaultSpecifier[1]}, ${specifiers.join(', ')} from '${entrypoint}'`,
368
- )
369
- } else if (defaultSpecifier && namedSpecifiers.length === 0) {
370
- yield fixer.replaceText(node, `import type ${defaultSpecifier[1]} from '${entrypoint}'`)
408
+ yield fixer.insertTextAfter(lastSpecifier, `, ${specifiers.join(', ')}`)
371
409
  }
372
-
373
- return
374
410
  }
375
411
 
376
- // Otherwise, we have a mix of type and value imports to emit
377
- const valueSpecifiers = importSpecifiers.filter(([, , type]) => {
378
- return type !== 'type'
379
- })
380
-
381
- if (valueSpecifiers.length === 0) {
382
- return
383
- }
412
+ // Reuse an import declaration if one exists
413
+ if (importDeclaration) {
414
+ const firstSpecifier = importDeclaration.specifiers[0]
415
+ const lastSpecifier = importDeclaration.specifiers[importDeclaration.specifiers.length - 1]
384
416
 
385
- const specifiers = valueSpecifiers.map(([imported, local]) => {
386
- if (imported !== local) {
387
- return `${imported} as ${local}`
417
+ if (defaultSpecifier) {
418
+ const postfix = namedSpecifiers.length > 0 || importDeclaration.specifiers.length > 0 ? ', ' : ' '
419
+ yield fixer.insertTextBeforeRange(
420
+ [firstSpecifier.range[0] - 2, firstSpecifier.range[1]],
421
+ `${defaultSpecifier}${postfix}`,
422
+ )
388
423
  }
389
- return imported
390
- })
391
- yield fixer.replaceText(node, `import {${specifiers.join(', ')}} from '${entrypoint}'`)
392
424
 
393
- if (typeSpecifiers.length > 0) {
394
- const specifiers = typeSpecifiers.map(([imported, local]) => {
425
+ if (namedSpecifiers.length > 0 || (!typeImportDeclaration && namedTypeSpecifiers.length > 0)) {
426
+ let specifiers = [...namedSpecifiers]
427
+ if (!typeImportDeclaration) {
428
+ specifiers.push(...namedTypeSpecifiers)
429
+ }
430
+ specifiers = specifiers.map(([imported, local, type]) => {
431
+ const prefix = type === 'type' ? 'type ' : ''
432
+ if (imported !== local) {
433
+ return `${prefix}${imported} as ${local}`
434
+ }
435
+ return `${prefix}${imported}`
436
+ })
437
+ yield fixer.insertTextAfter(lastSpecifier, `, ${specifiers.join(', ')}`)
438
+ }
439
+ } else {
440
+ let specifiers = [...namedSpecifiers]
441
+ if (!typeImportDeclaration) {
442
+ specifiers.push(...namedTypeSpecifiers)
443
+ }
444
+ specifiers = specifiers.map(([imported, local, type]) => {
445
+ const prefix = type === 'type' ? 'type ' : ''
395
446
  if (imported !== local) {
396
- return `${imported} as ${local}`
447
+ return `${prefix}${imported} as ${local}`
397
448
  }
398
- return imported
449
+ return `${prefix}${imported}`
399
450
  })
400
- yield fixer.insertTextAfter(node, `\nimport type {${specifiers.join(', ')}} from '${entrypoint}'`)
451
+ let declaration = 'import '
452
+
453
+ if (defaultSpecifier) {
454
+ declaration += defaultSpecifier
455
+ }
456
+
457
+ if (defaultTypeSpecifier && !typeImportDeclaration) {
458
+ declaration += defaultTypeSpecifier
459
+ }
460
+
461
+ if (specifiers.length > 0) {
462
+ if (defaultSpecifier || defaultTypeSpecifier) {
463
+ declaration += ', '
464
+ }
465
+ declaration += `{${specifiers.join(', ')}}`
466
+ }
467
+
468
+ declaration += ` from '${entrypoint}'`
469
+ yield fixer.replaceText(node, declaration)
470
+ originalImportReplaced = true
471
+ }
472
+
473
+ if (!originalImportReplaced) {
474
+ yield fixer.remove(node)
401
475
  }
402
476
  }
403
477
  },