Skip to content

Commit f689fcc

Browse files
TylerJDevfrancineluccahectahertz
authored
Add new rule no-deprecated-experimental-components (#325)
* Add new rule `no-deprecated-experimental-components` * Lint and format * Add changeset * Update README.md Co-authored-by: Marie Lucca <[email protected]> * Update docs/rules/no-deprecated-experimental-components.md Co-authored-by: Marie Lucca <[email protected]> * Update message * Add link to docs * Update docs/rules/no-deprecated-experimental-components.md Co-authored-by: Hector Garcia <[email protected]> * Update src/rules/no-deprecated-experimental-components.js * Update src/configs/recommended.js * Edit to message --------- Co-authored-by: Marie Lucca <[email protected]> Co-authored-by: Hector Garcia <[email protected]>
1 parent c5f2e8e commit f689fcc

7 files changed

+148
-0
lines changed

.changeset/wicked-areas-jog.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-primer-react': minor
3+
---
4+
5+
Add `no-deprecated-experimental-components` rule

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ ESLint rules for Primer React
4040
- [a11y-link-in-text-block](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-link-in-text-block.md)
4141
- [a11y-remove-disable-tooltip](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-remove-disable-tooltip.md)
4242
- [a11y-use-accessible-tooltip](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-use-accessible-tooltip.md)
43+
- [no-deprecated-experimental-components](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/no-deprecated-experimental-components.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# No deprecated experimental components
2+
3+
## Rule Details
4+
5+
This rule discourages the usage of specific imports from `@primer/react/experimental`.
6+
7+
👎 Examples of **incorrect** code for this rule
8+
9+
```jsx
10+
import {SelectPanel} from '@primer/react/experimental'
11+
12+
function ExampleComponent() {
13+
return <SelectPanel />
14+
}
15+
```
16+
17+
👍 Examples of **correct** code for this rule:
18+
19+
You can satisfy the rule by either converting to the non-experimental version:
20+
21+
```jsx
22+
import {SelectPanel} from '@primer/react'
23+
24+
function ExampleComponent() {
25+
return <SelectPanel />
26+
}
27+
```
28+
29+
Or by removing usage of the component.

src/configs/recommended.js

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module.exports = {
1212
rules: {
1313
'primer-react/direct-slot-children': 'error',
1414
'primer-react/no-system-props': 'warn',
15+
'primer-react/no-deprecated-experimental-components': 'warn',
1516
'primer-react/a11y-tooltip-interactive-trigger': 'error',
1617
'primer-react/new-color-css-vars': 'error',
1718
'primer-react/a11y-explicit-heading': 'error',

src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module.exports = {
33
'direct-slot-children': require('./rules/direct-slot-children'),
44
'no-deprecated-entrypoints': require('./rules/no-deprecated-entrypoints'),
55
'no-system-props': require('./rules/no-system-props'),
6+
'no-deprecated-experimental-components': require('./rules/no-deprecated-experimental-components'),
67
'a11y-tooltip-interactive-trigger': require('./rules/a11y-tooltip-interactive-trigger'),
78
'new-color-css-vars': require('./rules/new-color-css-vars'),
89
'a11y-explicit-heading': require('./rules/a11y-explicit-heading'),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict'
2+
3+
const {RuleTester} = require('eslint')
4+
const rule = require('../no-deprecated-experimental-components')
5+
6+
const ruleTester = new RuleTester({
7+
parserOptions: {
8+
ecmaVersion: 'latest',
9+
sourceType: 'module',
10+
ecmaFeatures: {
11+
jsx: true,
12+
},
13+
},
14+
})
15+
16+
ruleTester.run('no-deprecated-experimental-components', rule, {
17+
valid: [
18+
{
19+
code: `import {SelectPanel} from '@primer/react'`,
20+
},
21+
{
22+
code: `import {DataTable} from '@primer/react/experimental'`,
23+
},
24+
{
25+
code: `import {DataTable, ActionBar} from '@primer/react/experimental'`,
26+
},
27+
],
28+
invalid: [
29+
// Single experimental import
30+
{
31+
code: `import {SelectPanel} from '@primer/react/experimental'`,
32+
errors: [
33+
'SelectPanel is deprecated. Please import from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.',
34+
],
35+
},
36+
// Multiple experimental import
37+
{
38+
code: `import {SelectPanel, DataTable, ActionBar} from '@primer/react/experimental'`,
39+
errors: [
40+
'SelectPanel is deprecated. Please import from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.',
41+
],
42+
},
43+
],
44+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict'
2+
3+
const url = require('../url')
4+
5+
const components = [
6+
{
7+
identifier: 'SelectPanel',
8+
entrypoint: '@primer/react/experimental',
9+
},
10+
]
11+
12+
const entrypoints = new Map()
13+
14+
for (const component of components) {
15+
if (!entrypoints.has(component.entrypoint)) {
16+
entrypoints.set(component.entrypoint, new Set())
17+
}
18+
entrypoints.get(component.entrypoint).add(component.identifier)
19+
}
20+
21+
/**
22+
* @type {import('eslint').Rule.RuleModule}
23+
*/
24+
module.exports = {
25+
meta: {
26+
type: 'problem',
27+
docs: {
28+
description: 'Use a stable component from the `@primer/react` entrypoint, or check the docs for alternatives',
29+
recommended: true,
30+
url: url(module),
31+
},
32+
fixable: true,
33+
schema: [],
34+
},
35+
create(context) {
36+
return {
37+
ImportDeclaration(node) {
38+
if (!entrypoints.has(node.source.value)) {
39+
return
40+
}
41+
42+
const entrypoint = entrypoints.get(node.source.value)
43+
44+
const experimental = node.specifiers.filter(specifier => {
45+
return entrypoint.has(specifier.imported.name)
46+
})
47+
48+
const components = experimental.map(specifier => specifier.imported.name)
49+
50+
if (experimental.length === 0) {
51+
return
52+
}
53+
54+
if (experimental.length > 0) {
55+
const message = `${components.join(', ')} ${
56+
components.length > 1 ? 'are' : 'is'
57+
} deprecated. Please import from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.`
58+
59+
context.report({
60+
node,
61+
message,
62+
})
63+
}
64+
},
65+
}
66+
},
67+
}

0 commit comments

Comments
 (0)