Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/large-cherries-cheat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-primer-react": minor
---

Make `use-styled-react-import` rule configurable
50 changes: 49 additions & 1 deletion docs/rules/use-styled-react-import.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,55 @@ const Component2 = () => <StyledButton sx={{color: 'red'}}>Styled me</StyledButt

## Options

This rule has no options.
This rule accepts an optional configuration object with the following properties:

- `styledComponents` (array of strings): Components that should be imported from `@primer/styled-react` when used with `sx` prop. Defaults to the list shown above.
- `styledTypes` (array of strings): Types that should always be imported from `@primer/styled-react`. Defaults to `['BoxProps', 'SxProp', 'BetterSystemStyleObject']`.
- `styledUtilities` (array of strings): Utilities that should always be imported from `@primer/styled-react`. Defaults to `['sx']`.

### Example Configuration

```json
{
"rules": {
"@primer/primer-react/use-styled-react-import": [
"error",
{
"styledComponents": ["Button", "Box", "CustomComponent"],
"styledTypes": ["BoxProps", "CustomProps"],
"styledUtilities": ["sx", "customSx"]
}
]
}
}
```

### Configuration Examples

#### ❌ Incorrect with custom configuration

```jsx
// With styledComponents: ["CustomButton"]
import {CustomButton} from '@primer/react'

const Component = () => <CustomButton sx={{color: 'red'}}>Click me</CustomButton>
```

#### βœ… Correct with custom configuration

```jsx
// With styledComponents: ["CustomButton"]
import {CustomButton} from '@primer/styled-react'

const Component = () => <CustomButton sx={{color: 'red'}}>Click me</CustomButton>
```

```jsx
// Box is not in custom styledComponents list, so it can be used with sx from @primer/react
import {Box} from '@primer/react'

const Component = () => <Box sx={{color: 'red'}}>Content</Box>
```

## When Not To Use It

Expand Down
61 changes: 61 additions & 0 deletions src/rules/__tests__/use-styled-react-import.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,64 @@ import { Button as StyledButton, Link } from '@primer/styled-react'
},
],
})

// Test configuration options
ruleTester.run('use-styled-react-import with custom configuration', rule, {
valid: [
// Valid: Custom component not in default list
{
code: `import { CustomButton } from '@primer/react'
const Component = () => <CustomButton sx={{ color: 'red' }}>Click me</CustomButton>`,
options: [{}], // Using default configuration
},

// Valid: Custom component in custom list used without sx prop
{
code: `import { CustomButton } from '@primer/react'
const Component = () => <CustomButton>Click me</CustomButton>`,
options: [{styledComponents: ['CustomButton']}],
},

// Valid: Custom component with sx prop imported from styled-react
{
code: `import { CustomButton } from '@primer/styled-react'
const Component = () => <CustomButton sx={{ color: 'red' }}>Click me</CustomButton>`,
options: [{styledComponents: ['CustomButton']}],
},

// Valid: Box not in custom list, so sx usage is allowed from @primer/react
{
code: `import { Box } from '@primer/react'
const Component = () => <Box sx={{ color: 'red' }}>Content</Box>`,
options: [{styledComponents: ['CustomButton']}], // Box not included
},
],
invalid: [
// Invalid: Custom component with sx prop should be from styled-react
{
code: `import { CustomButton } from '@primer/react'
const Component = () => <CustomButton sx={{ color: 'red' }}>Click me</CustomButton>`,
output: `import { CustomButton } from '@primer/styled-react'
const Component = () => <CustomButton sx={{ color: 'red' }}>Click me</CustomButton>`,
options: [{styledComponents: ['CustomButton']}],
errors: [
{
messageId: 'useStyledReactImport',
data: {componentName: 'CustomButton'},
},
],
},
// Invalid: Custom utility should be from styled-react
{
code: `import { customSx } from '@primer/react'`,
output: `import { customSx } from '@primer/styled-react'`,
options: [{styledUtilities: ['customSx']}],
errors: [
{
messageId: 'moveToStyledReact',
data: {importName: 'customSx'},
},
],
},
],
})
43 changes: 35 additions & 8 deletions src/rules/use-styled-react-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
const url = require('../url')
const {getJSXOpeningElementName} = require('../utils/get-jsx-opening-element-name')

// Components that should be imported from @primer/styled-react when used with sx prop
const styledComponents = new Set([
// Default components that should be imported from @primer/styled-react when used with sx prop
const defaultStyledComponents = [
'ActionList',
'ActionMenu',
'Box',
Expand All @@ -23,13 +23,13 @@ const styledComponents = new Set([
'Truncate',
'Octicon',
'Dialog',
])
]

// Types that should be imported from @primer/styled-react
const styledTypes = new Set(['BoxProps', 'SxProp', 'BetterSystemStyleObject'])
// Default types that should be imported from @primer/styled-react
const defaultStyledTypes = ['BoxProps', 'SxProp', 'BetterSystemStyleObject']

// Utilities that should be imported from @primer/styled-react
const styledUtilities = new Set(['sx'])
// Default utilities that should be imported from @primer/styled-react
const defaultStyledUtilities = ['sx']

/**
* @type {import('eslint').Rule.RuleModule}
Expand All @@ -43,7 +43,29 @@ module.exports = {
url: url(module),
},
fixable: 'code',
schema: [],
schema: [
{
type: 'object',
properties: {
styledComponents: {
type: 'array',
items: {type: 'string'},
description: 'Components that should be imported from @primer/styled-react when used with sx prop',
},
styledTypes: {
type: 'array',
items: {type: 'string'},
description: 'Types that should be imported from @primer/styled-react',
},
styledUtilities: {
type: 'array',
items: {type: 'string'},
description: 'Utilities that should be imported from @primer/styled-react',
},
},
additionalProperties: false,
},
],
messages: {
useStyledReactImport: 'Import {{ componentName }} from "@primer/styled-react" when using with sx prop',
useStyledReactImportWithAlias:
Expand All @@ -54,6 +76,11 @@ module.exports = {
},
},
create(context) {
// Get configuration options or use defaults
const options = context.options[0] || {}
const styledComponents = new Set(options.styledComponents || defaultStyledComponents)
const styledTypes = new Set(options.styledTypes || defaultStyledTypes)
const styledUtilities = new Set(options.styledUtilities || defaultStyledUtilities)
const componentsWithSx = new Set()
const componentsWithoutSx = new Set() // Track components used without sx
const allUsedComponents = new Set() // Track all used components
Expand Down