eslint-plugin-primer-react 8.6.0 → 8.6.1-rc.78da63c

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.
@@ -12,7 +12,7 @@ jobs:
12
12
  PROJECT_ID: 4503
13
13
  steps:
14
14
  - id: get-primer-access-token
15
- uses: actions/create-github-app-token@v3
15
+ uses: actions/create-github-app-token@v3.1.1
16
16
  with:
17
17
  app-id: ${{ vars.PRIMER_ISSUE_TRIAGE_APP_ID }}
18
18
  private-key: ${{ secrets.PRIMER_ISSUE_TRIAGE_APP_PRIVATE_KEY }}
@@ -22,7 +22,7 @@ jobs:
22
22
  env:
23
23
  GH_TOKEN: ${{ steps.get-primer-access-token.outputs.token }}
24
24
  - id: get-github-access-token
25
- uses: actions/create-github-app-token@v3
25
+ uses: actions/create-github-app-token@v3.1.1
26
26
  with:
27
27
  app-id: ${{ vars.PRIMER_ISSUE_TRIAGE_APP_ID_FOR_GITHUB }}
28
28
  private-key: ${{ secrets.PRIMER_ISSUE_TRIAGE_APP_PRIVATE_KEY_FOR_GITHUB }}
@@ -26,7 +26,7 @@ jobs:
26
26
  fetch-depth: 0
27
27
  persist-credentials: false
28
28
 
29
- - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859
29
+ - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3
30
30
  id: app-token
31
31
  with:
32
32
  app-id: ${{ vars.PRIMER_APP_ID_SHARED }}
@@ -82,7 +82,7 @@ jobs:
82
82
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
83
83
 
84
84
  - name: Output canary version number
85
- uses: actions/github-script@v7.0.1
85
+ uses: actions/github-script@v9.0.0
86
86
  with:
87
87
  script: |
88
88
  const package = require(`${process.env.GITHUB_WORKSPACE}/package.json`)
@@ -131,7 +131,7 @@ jobs:
131
131
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
132
132
 
133
133
  - name: Output release candidate version number
134
- uses: actions/github-script@v7.0.1
134
+ uses: actions/github-script@v9.0.0
135
135
  with:
136
136
  script: |
137
137
  const package = require(`${process.env.GITHUB_WORKSPACE}/package.json`)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # eslint-plugin-primer-react
2
2
 
3
+ ## 8.6.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#548](https://github.com/primer/eslint-plugin-primer-react/pull/548) [`7f5af96`](https://github.com/primer/eslint-plugin-primer-react/commit/7f5af9610d0dde4c0e9941dc485ef1487c8d48ac) Thanks [@copilot-swe-agent](https://github.com/apps/copilot-swe-agent)! - Fix use-styled-react-import fixer stripping type keyword from import specifiers
8
+
3
9
  ## 8.6.0
4
10
 
5
11
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-primer-react",
3
- "version": "8.6.0",
3
+ "version": "8.6.1-rc.78da63c",
4
4
  "description": "ESLint rules for Primer React",
5
5
  "main": "src/index.js",
6
6
  "engines": {
@@ -38,7 +38,7 @@
38
38
  "eslint-traverse": "^1.0.0",
39
39
  "lodash": "^4.17.21",
40
40
  "styled-system": "^5.1.5",
41
- "typescript": "^5.8.2"
41
+ "typescript": "^6.0.3"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@changesets/changelog-github": "^0.6.0",
@@ -57,7 +57,7 @@
57
57
  "eslint-plugin-prettier": "^5.5.4",
58
58
  "globals": "^17.0.0",
59
59
  "jest": "^30.0.5",
60
- "markdownlint-cli2": "^0.21.0",
60
+ "markdownlint-cli2": "^0.22.0",
61
61
  "markdownlint-cli2-formatter-pretty": "^0.0.10"
62
62
  },
63
63
  "prettier": "@github/prettier-config"
@@ -364,7 +364,7 @@ import { Button } from '@primer/react'
364
364
  <Button sx={{ color: 'red' }}>Styled button</Button>
365
365
  </div>
366
366
  )`,
367
- output: `import { Link, Button } from '@primer/styled-react'
367
+ output: `import { Button, Link } from '@primer/styled-react'
368
368
  const Component = () => (
369
369
  <div>
370
370
  <Link sx={{ color: 'red' }} />
@@ -438,6 +438,40 @@ import { ThemeProvider } from '@primer/styled-react'
438
438
  },
439
439
  ],
440
440
  },
441
+
442
+ // Invalid: type keyword preserved when splitting imports - component with sx moved to styled-react
443
+ {
444
+ code: `import { Button, type ButtonProps } from '@primer/react'
445
+ const Component = () => <Button sx={{ color: 'red' }}>Click me</Button>`,
446
+ output: `import { type ButtonProps } from '@primer/react'
447
+ import { Button } from '@primer/styled-react'
448
+ const Component = () => <Button sx={{ color: 'red' }}>Click me</Button>`,
449
+ errors: [
450
+ {
451
+ messageId: 'useStyledReactImport',
452
+ data: {componentName: 'Button'},
453
+ },
454
+ ],
455
+ },
456
+
457
+ // Invalid: type keyword preserved on moved specifiers (type + utility)
458
+ {
459
+ code: `import { type SxProp, sx, Button } from '@primer/react'
460
+ const Component = () => <Button>Click me</Button>`,
461
+ output: `import { Button } from '@primer/react'
462
+ import { type SxProp, sx } from '@primer/styled-react'
463
+ const Component = () => <Button>Click me</Button>`,
464
+ errors: [
465
+ {
466
+ messageId: 'moveToStyledReact',
467
+ data: {importName: 'SxProp'},
468
+ },
469
+ {
470
+ messageId: 'moveToStyledReact',
471
+ data: {importName: 'sx'},
472
+ },
473
+ ],
474
+ },
441
475
  ],
442
476
  })
443
477
 
@@ -3,6 +3,15 @@
3
3
  const url = require('../url')
4
4
  const {getJSXOpeningElementName} = require('../utils/get-jsx-opening-element-name')
5
5
 
6
+ /**
7
+ * Format a specifier name, preserving the `type` keyword if present.
8
+ * @param {import('estree').ImportSpecifier} spec
9
+ * @returns {string}
10
+ */
11
+ function formatSpecifier(spec) {
12
+ return spec.importKind === 'type' ? `type ${spec.imported.name}` : spec.imported.name
13
+ }
14
+
6
15
  // Default components that should be imported from @primer/styled-react when used with sx prop
7
16
  const defaultStyledComponents = [
8
17
  'ActionList',
@@ -197,19 +206,24 @@ module.exports = {
197
206
  // Convert @primer/react path to @primer/styled-react path
198
207
  const styledReactPath = importSource.replace('@primer/react', '@primer/styled-react')
199
208
 
209
+ // Find the original specifier nodes for moved components to preserve importKind
210
+ const movedSpecifiers = changes.originalSpecifiers.filter(spec =>
211
+ componentsToMove.has(spec.imported.name),
212
+ )
213
+
200
214
  // If no components remain, replace with new import directly
201
215
  if (remainingSpecifiers.length === 0) {
202
- const movedComponents = changes.toMove.join(', ')
216
+ const movedComponents = movedSpecifiers.map(formatSpecifier).join(', ')
203
217
  fixes.push(fixer.replaceText(importNode, `import { ${movedComponents} } from '${styledReactPath}'`))
204
218
  } else {
205
219
  // Otherwise, update the import to only include remaining components
206
- const remainingNames = remainingSpecifiers.map(spec => spec.imported.name)
220
+ const remainingNames = remainingSpecifiers.map(formatSpecifier)
207
221
  fixes.push(
208
222
  fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '${importSource}'`),
209
223
  )
210
224
 
211
225
  // Add new styled-react import
212
- const movedComponents = changes.toMove.join(', ')
226
+ const movedComponents = movedSpecifiers.map(formatSpecifier).join(', ')
213
227
  fixes.push(
214
228
  fixer.insertTextAfter(importNode, `\nimport { ${movedComponents} } from '${styledReactPath}'`),
215
229
  )
@@ -290,10 +304,16 @@ module.exports = {
290
304
  // Check if there's an existing primer-react import to merge with
291
305
  const existingPrimerReactImport = Array.from(primerReactImportNodes)[0]
292
306
 
307
+ // Find the original specifier nodes for moved components to preserve importKind
308
+ const movedSpecifiers = changes.originalSpecifiers.filter(spec =>
309
+ componentsToMove.has(spec.imported.name),
310
+ )
311
+
293
312
  if (existingPrimerReactImport && remainingSpecifiers.length === 0) {
294
313
  // Case: No remaining styled-react imports, merge with existing primer-react import
295
- const existingSpecifiers = existingPrimerReactImport.specifiers.map(spec => spec.imported.name)
296
- const newSpecifiers = [...existingSpecifiers, ...changes.toMove].filter(
314
+ const existingNames = existingPrimerReactImport.specifiers.map(formatSpecifier)
315
+ const movedNames = movedSpecifiers.map(formatSpecifier)
316
+ const newSpecifiers = [...existingNames, ...movedNames].filter(
297
317
  (name, index, arr) => arr.indexOf(name) === index,
298
318
  )
299
319
 
@@ -306,8 +326,9 @@ module.exports = {
306
326
  fixes.push(fixer.remove(importNode))
307
327
  } else if (existingPrimerReactImport && remainingSpecifiers.length > 0) {
308
328
  // Case: Some styled-react imports remain, merge moved components with existing primer-react
309
- const existingSpecifiers = existingPrimerReactImport.specifiers.map(spec => spec.imported.name)
310
- const newSpecifiers = [...existingSpecifiers, ...changes.toMove].filter(
329
+ const existingNames = existingPrimerReactImport.specifiers.map(formatSpecifier)
330
+ const movedNames = movedSpecifiers.map(formatSpecifier)
331
+ const newSpecifiers = [...existingNames, ...movedNames].filter(
311
332
  (name, index, arr) => arr.indexOf(name) === index,
312
333
  )
313
334
 
@@ -318,22 +339,22 @@ module.exports = {
318
339
  ),
319
340
  )
320
341
 
321
- const remainingNames = remainingSpecifiers.map(spec => spec.imported.name)
342
+ const remainingNames = remainingSpecifiers.map(formatSpecifier)
322
343
  fixes.push(
323
344
  fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '${importSource}'`),
324
345
  )
325
346
  } else if (remainingSpecifiers.length === 0) {
326
347
  // Case: No existing primer-react import, no remaining styled-react imports
327
- const movedComponents = changes.toMove.join(', ')
348
+ const movedComponents = movedSpecifiers.map(formatSpecifier).join(', ')
328
349
  fixes.push(fixer.replaceText(importNode, `import { ${movedComponents} } from '${primerReactPath}'`))
329
350
  } else {
330
351
  // Case: No existing primer-react import, some styled-react imports remain
331
- const remainingNames = remainingSpecifiers.map(spec => spec.imported.name)
352
+ const remainingNames = remainingSpecifiers.map(formatSpecifier)
332
353
  fixes.push(
333
354
  fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '${importSource}'`),
334
355
  )
335
356
 
336
- const movedComponents = changes.toMove.join(', ')
357
+ const movedComponents = movedSpecifiers.map(formatSpecifier).join(', ')
337
358
  fixes.push(
338
359
  fixer.insertTextAfter(importNode, `\nimport { ${movedComponents} } from '${primerReactPath}'`),
339
360
  )
@@ -386,18 +407,14 @@ module.exports = {
386
407
  // if there are no remaining specifiers, we can remove the whole import
387
408
  fixes.push(fixer.remove(importNode))
388
409
  } else {
389
- const remainingNames = remainingSpecifiers.map(spec =>
390
- spec.importKind === 'type' ? `type ${spec.imported.name}` : spec.imported.name,
391
- )
410
+ const remainingNames = remainingSpecifiers.map(formatSpecifier)
392
411
  fixes.push(
393
412
  fixer.replaceText(importNode, `import { ${remainingNames.join(', ')} } from '${importSource}'`),
394
413
  )
395
414
  }
396
415
 
397
416
  if (specifiersToMove.length > 0) {
398
- const movedComponents = specifiersToMove.map(spec =>
399
- spec.importKind === 'type' ? `type ${spec.imported.name}` : spec.imported.name,
400
- )
417
+ const movedComponents = specifiersToMove.map(formatSpecifier)
401
418
  const onNewLine = remainingSpecifiers.length > 0
402
419
  fixes.push(
403
420
  fixer.insertTextAfter(