Skip to content

Add new rule no-deprecated-experimental-components #325

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 10, 2025
5 changes: 5 additions & 0 deletions .changeset/wicked-areas-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-primer-react': minor
---

Add `no-deprecated-experimental-components` rule
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ ESLint rules for Primer React
- [a11y-link-in-text-block](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-link-in-text-block.md)
- [a11y-remove-disable-tooltip](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-remove-disable-tooltip.md)
- [a11y-use-accessible-tooltip](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-use-accessible-tooltip.md)
- [no-deprecated-experimental-components](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/no-deprecated-experimental-components.md)
29 changes: 29 additions & 0 deletions docs/rules/no-deprecated-experimental-components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# No deprecated experimental components

## Rule Details

This rule discourages the usage of specific imports from `@primer/react/experimental`.

👎 Examples of **incorrect** code for this rule

```jsx
import {SelectPanel} from '@primer/react/experimental'

function ExampleComponent() {
return <SelectPanel />
}
```

👍 Examples of **correct** code for this rule:

You can satisfy the rule by either converting to the non-experimental version:

```jsx
import {SelectPanel} from '@primer/react'

function ExampleComponent() {
return <SelectPanel />
}
```

Or by removing usage of the component.
1 change: 1 addition & 0 deletions src/configs/recommended.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
rules: {
'primer-react/direct-slot-children': 'error',
'primer-react/no-system-props': 'warn',
'primer-react/no-deprecated-experimental-components': 'warn',
'primer-react/a11y-tooltip-interactive-trigger': 'error',
'primer-react/new-color-css-vars': 'error',
'primer-react/a11y-explicit-heading': 'error',
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module.exports = {
'direct-slot-children': require('./rules/direct-slot-children'),
'no-deprecated-entrypoints': require('./rules/no-deprecated-entrypoints'),
'no-system-props': require('./rules/no-system-props'),
'no-deprecated-experimental-components': require('./rules/no-deprecated-experimental-components'),
'a11y-tooltip-interactive-trigger': require('./rules/a11y-tooltip-interactive-trigger'),
'new-color-css-vars': require('./rules/new-color-css-vars'),
'a11y-explicit-heading': require('./rules/a11y-explicit-heading'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use strict'

const {RuleTester} = require('eslint')
const rule = require('../no-deprecated-experimental-components')

const ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
})

ruleTester.run('no-deprecated-experimental-components', rule, {
valid: [
{
code: `import {SelectPanel} from '@primer/react'`,
},
{
code: `import {DataTable} from '@primer/react/experimental'`,
},
{
code: `import {DataTable, ActionBar} from '@primer/react/experimental'`,
},
],
invalid: [
// Single experimental import
{
code: `import {SelectPanel} from '@primer/react/experimental'`,
errors: [
'SelectPanel is deprecated. Please import from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.',
],
},
// Multiple experimental import
{
code: `import {SelectPanel, DataTable, ActionBar} from '@primer/react/experimental'`,
errors: [
'SelectPanel is deprecated. Please import from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.',
],
},
],
})
67 changes: 67 additions & 0 deletions src/rules/no-deprecated-experimental-components.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use strict'

const url = require('../url')

const components = [
{
identifier: 'SelectPanel',
entrypoint: '@primer/react/experimental',
},
]

const entrypoints = new Map()

for (const component of components) {
if (!entrypoints.has(component.entrypoint)) {
entrypoints.set(component.entrypoint, new Set())
}
entrypoints.get(component.entrypoint).add(component.identifier)
}

/**
* @type {import('eslint').Rule.RuleModule}
*/
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Use a stable component from the `@primer/react` entrypoint, or check the docs for alternatives',
recommended: true,
url: url(module),
},
fixable: true,
schema: [],
},
create(context) {
return {
ImportDeclaration(node) {
if (!entrypoints.has(node.source.value)) {
return
}

const entrypoint = entrypoints.get(node.source.value)

const experimental = node.specifiers.filter(specifier => {
return entrypoint.has(specifier.imported.name)
})

const components = experimental.map(specifier => specifier.imported.name)

if (experimental.length === 0) {
return
}

if (experimental.length > 0) {
const message = `${components.join(', ')} ${
components.length > 1 ? 'are' : 'is'
} deprecated. Please import from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.`

context.report({
node,
message,
})
}
},
}
},
}