Skip to content
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,
})
}
},
}
},
}