diff --git a/.changeset/large-cherries-cheat.md b/.changeset/large-cherries-cheat.md new file mode 100644 index 00000000..cbde7665 --- /dev/null +++ b/.changeset/large-cherries-cheat.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-primer-react": minor +--- + +Make `use-styled-react-import` rule configurable diff --git a/docs/rules/use-styled-react-import.md b/docs/rules/use-styled-react-import.md index 78a06128..526b5d0f 100644 --- a/docs/rules/use-styled-react-import.md +++ b/docs/rules/use-styled-react-import.md @@ -121,7 +121,55 @@ const Component2 = () => Styled me Click me +``` + +#### ✅ Correct with custom configuration + +```jsx +// With styledComponents: ["CustomButton"] +import {CustomButton} from '@primer/styled-react' + +const Component = () => Click me +``` + +```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 = () => Content +``` ## When Not To Use It diff --git a/src/rules/__tests__/use-styled-react-import.test.js b/src/rules/__tests__/use-styled-react-import.test.js index bd11ae7d..860f473c 100644 --- a/src/rules/__tests__/use-styled-react-import.test.js +++ b/src/rules/__tests__/use-styled-react-import.test.js @@ -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 = () => Click me`, + options: [{}], // Using default configuration + }, + + // Valid: Custom component in custom list used without sx prop + { + code: `import { CustomButton } from '@primer/react' + const Component = () => Click me`, + options: [{styledComponents: ['CustomButton']}], + }, + + // Valid: Custom component with sx prop imported from styled-react + { + code: `import { CustomButton } from '@primer/styled-react' + const Component = () => Click me`, + 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 = () => Content`, + 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 = () => Click me`, + output: `import { CustomButton } from '@primer/styled-react' + const Component = () => Click me`, + 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'}, + }, + ], + }, + ], +}) diff --git a/src/rules/use-styled-react-import.js b/src/rules/use-styled-react-import.js index b4ba55bd..136ff644 100644 --- a/src/rules/use-styled-react-import.js +++ b/src/rules/use-styled-react-import.js @@ -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', @@ -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} @@ -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: @@ -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