From 5781a7674b0ee9797f50d92a780ea059dac0ba35 Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Tue, 8 Apr 2025 20:20:20 +0000 Subject: [PATCH 01/11] Add new rule `no-deprecated-experimental-components` --- README.md | 1 + .../no-deprecated-experimental-components.md | 29 +++++++++ src/configs/recommended.js | 1 + src/index.js | 1 + ...deprecated-experimental-components.test.js | 40 ++++++++++++ .../no-deprecated-experimental-components.js | 64 +++++++++++++++++++ 6 files changed, 136 insertions(+) create mode 100644 docs/rules/no-deprecated-experimental-components.md create mode 100644 src/rules/__tests__/no-deprecated-experimental-components.test.js create mode 100644 src/rules/no-deprecated-experimental-components.js diff --git a/README.md b/README.md index 03a4c86b..793f57b8 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/docs/rules/no-deprecated-experimental-components.md b/docs/rules/no-deprecated-experimental-components.md new file mode 100644 index 00000000..d236759a --- /dev/null +++ b/docs/rules/no-deprecated-experimental-components.md @@ -0,0 +1,29 @@ +# No experimental SelectPanel + +## Rule Details + +This rule enforces 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 +} +``` + +👍 Examples of **correct** code for this rule: + +You can satisfy the rule by either converting to the non-experimental version: + +```jsx +import {SelectPane} from '@primer/react' + +function ExampleComponent() { + return +} +``` + +Or by removing usage of the component. \ No newline at end of file diff --git a/src/configs/recommended.js b/src/configs/recommended.js index e29c3c00..c08f6e10 100644 --- a/src/configs/recommended.js +++ b/src/configs/recommended.js @@ -12,6 +12,7 @@ module.exports = { rules: { 'primer-react/direct-slot-children': 'error', 'primer-react/no-system-props': 'warn', + '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', diff --git a/src/index.js b/src/index.js index 9ba22767..1cb3a431 100644 --- a/src/index.js +++ b/src/index.js @@ -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'), diff --git a/src/rules/__tests__/no-deprecated-experimental-components.test.js b/src/rules/__tests__/no-deprecated-experimental-components.test.js new file mode 100644 index 00000000..68035112 --- /dev/null +++ b/src/rules/__tests__/no-deprecated-experimental-components.test.js @@ -0,0 +1,40 @@ +'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: ['SelectPanelV2 is deprecated. Please import SelectPanelV1 from `@primer/react`'], + }, + // Multiple experimental import + { + code: `import {SelectPanel, DataTable, ActionBar} from '@primer/react/experimental'`, + errors: ['SelectPanelV2 is deprecated. Please import SelectPanelV1 from `@primer/react`'], + }, + ], +}) diff --git a/src/rules/no-deprecated-experimental-components.js b/src/rules/no-deprecated-experimental-components.js new file mode 100644 index 00000000..f7b3e4e5 --- /dev/null +++ b/src/rules/no-deprecated-experimental-components.js @@ -0,0 +1,64 @@ +'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 deprecated components from the `@primer/react/deprecated` entrypoint', + recommended: true, + url: url(module), + }, + fixable: true, + schema: [], + }, + create(context) { + const sourceCode = context.getSourceCode() + + 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) + }) + + if (experimental.length === 0) { + return + } + + // All imports are deprecated + if (experimental.length > 0) { + context.report({ + node, + message: 'SelectPanelV2 is deprecated. Please import SelectPanelV1 from `@primer/react`', + }) + } + }, + } + }, +} From 8aebb76a5369d519cbb6c6b00cf4efd71a038532 Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Tue, 8 Apr 2025 20:59:12 +0000 Subject: [PATCH 02/11] Lint and format --- docs/rules/no-deprecated-experimental-components.md | 4 ++-- src/rules/no-deprecated-experimental-components.js | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/rules/no-deprecated-experimental-components.md b/docs/rules/no-deprecated-experimental-components.md index d236759a..1b686c29 100644 --- a/docs/rules/no-deprecated-experimental-components.md +++ b/docs/rules/no-deprecated-experimental-components.md @@ -1,4 +1,4 @@ -# No experimental SelectPanel +# No deprecated experimental components ## Rule Details @@ -26,4 +26,4 @@ function ExampleComponent() { } ``` -Or by removing usage of the component. \ No newline at end of file +Or by removing usage of the component. diff --git a/src/rules/no-deprecated-experimental-components.js b/src/rules/no-deprecated-experimental-components.js index f7b3e4e5..00b4a339 100644 --- a/src/rules/no-deprecated-experimental-components.js +++ b/src/rules/no-deprecated-experimental-components.js @@ -33,8 +33,6 @@ module.exports = { schema: [], }, create(context) { - const sourceCode = context.getSourceCode() - return { ImportDeclaration(node) { if (!entrypoints.has(node.source.value)) { From 0f06ed2df851e2a9f030ff951c24c1331102b6f9 Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Tue, 8 Apr 2025 21:00:50 +0000 Subject: [PATCH 03/11] Add changeset --- .changeset/wicked-areas-jog.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wicked-areas-jog.md diff --git a/.changeset/wicked-areas-jog.md b/.changeset/wicked-areas-jog.md new file mode 100644 index 00000000..5582f9e1 --- /dev/null +++ b/.changeset/wicked-areas-jog.md @@ -0,0 +1,5 @@ +--- +'eslint-plugin-primer-react': minor +--- + +Add `no-deprecated-experimental-components` rule From 364ff172623c5233356de2ae4f35a9ea01435088 Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Wed, 9 Apr 2025 10:15:39 -0400 Subject: [PATCH 04/11] Update README.md Co-authored-by: Marie Lucca <40550942+francinelucca@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 793f57b8..e0bf39c5 100644 --- a/README.md +++ b/README.md @@ -40,4 +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) +- [no-deprecated-experimental-components](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/no-deprecated-experimental-components.md) From a5d1f8ee73271969b9a7c44af1c8ec035fce88cf Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Wed, 9 Apr 2025 10:15:54 -0400 Subject: [PATCH 05/11] Update docs/rules/no-deprecated-experimental-components.md Co-authored-by: Marie Lucca <40550942+francinelucca@users.noreply.github.com> --- docs/rules/no-deprecated-experimental-components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/no-deprecated-experimental-components.md b/docs/rules/no-deprecated-experimental-components.md index 1b686c29..21bc1d51 100644 --- a/docs/rules/no-deprecated-experimental-components.md +++ b/docs/rules/no-deprecated-experimental-components.md @@ -2,7 +2,7 @@ ## Rule Details -This rule enforces the usage of specific imports from `@primer/react/experimental`. +This rule discourages the usage of specific imports from `@primer/react/experimental`. 👎 Examples of **incorrect** code for this rule From ad1531bf3ed4cfec796dfaf10170822c97f28b24 Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Wed, 9 Apr 2025 10:39:17 -0400 Subject: [PATCH 06/11] Update message --- .../no-deprecated-experimental-components.test.js | 8 ++++++-- src/rules/no-deprecated-experimental-components.js | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/rules/__tests__/no-deprecated-experimental-components.test.js b/src/rules/__tests__/no-deprecated-experimental-components.test.js index 68035112..d6f9e41e 100644 --- a/src/rules/__tests__/no-deprecated-experimental-components.test.js +++ b/src/rules/__tests__/no-deprecated-experimental-components.test.js @@ -29,12 +29,16 @@ ruleTester.run('no-deprecated-experimental-components', rule, { // Single experimental import { code: `import {SelectPanel} from '@primer/react/experimental'`, - errors: ['SelectPanelV2 is deprecated. Please import SelectPanelV1 from `@primer/react`'], + errors: [ + 'SelectPanel is deprecated. Please import them from the stable entrypoint (@primer/react) if available.', + ], }, // Multiple experimental import { code: `import {SelectPanel, DataTable, ActionBar} from '@primer/react/experimental'`, - errors: ['SelectPanelV2 is deprecated. Please import SelectPanelV1 from `@primer/react`'], + errors: [ + 'SelectPanel is deprecated. Please import them from the stable entrypoint (@primer/react) if available.', + ], }, ], }) diff --git a/src/rules/no-deprecated-experimental-components.js b/src/rules/no-deprecated-experimental-components.js index 00b4a339..7d676e0d 100644 --- a/src/rules/no-deprecated-experimental-components.js +++ b/src/rules/no-deprecated-experimental-components.js @@ -45,15 +45,20 @@ module.exports = { return entrypoint.has(specifier.imported.name) }) + const components = experimental.map(specifier => specifier.imported.name) + if (experimental.length === 0) { return } - // All imports are deprecated if (experimental.length > 0) { + const message = `${components.join(', ')} ${ + components.length > 1 ? 'are' : 'is' + } deprecated. Please import them from the stable entrypoint (@primer/react) if available.` + context.report({ node, - message: 'SelectPanelV2 is deprecated. Please import SelectPanelV1 from `@primer/react`', + message, }) } }, From d4353f43c52ca5bef4069cb31704bf67f8a97044 Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Wed, 9 Apr 2025 15:33:13 -0400 Subject: [PATCH 07/11] Add link to docs --- .../__tests__/no-deprecated-experimental-components.test.js | 4 ++-- src/rules/no-deprecated-experimental-components.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rules/__tests__/no-deprecated-experimental-components.test.js b/src/rules/__tests__/no-deprecated-experimental-components.test.js index d6f9e41e..d4d70259 100644 --- a/src/rules/__tests__/no-deprecated-experimental-components.test.js +++ b/src/rules/__tests__/no-deprecated-experimental-components.test.js @@ -30,14 +30,14 @@ ruleTester.run('no-deprecated-experimental-components', rule, { { code: `import {SelectPanel} from '@primer/react/experimental'`, errors: [ - 'SelectPanel is deprecated. Please import them from the stable entrypoint (@primer/react) if available.', + 'SelectPanel is deprecated. Please import them 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 them from the stable entrypoint (@primer/react) if available.', + 'SelectPanel is deprecated. Please import them from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.', ], }, ], diff --git a/src/rules/no-deprecated-experimental-components.js b/src/rules/no-deprecated-experimental-components.js index 7d676e0d..c5c80c4d 100644 --- a/src/rules/no-deprecated-experimental-components.js +++ b/src/rules/no-deprecated-experimental-components.js @@ -54,7 +54,7 @@ module.exports = { if (experimental.length > 0) { const message = `${components.join(', ')} ${ components.length > 1 ? 'are' : 'is' - } deprecated. Please import them from the stable entrypoint (@primer/react) if available.` + } deprecated. Please import them from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.` context.report({ node, From 50f33efb1b6a6156bd5b920f9768c760773e4e51 Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Wed, 9 Apr 2025 15:33:36 -0400 Subject: [PATCH 08/11] Update docs/rules/no-deprecated-experimental-components.md Co-authored-by: Hector Garcia --- docs/rules/no-deprecated-experimental-components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/no-deprecated-experimental-components.md b/docs/rules/no-deprecated-experimental-components.md index 21bc1d51..f57130de 100644 --- a/docs/rules/no-deprecated-experimental-components.md +++ b/docs/rules/no-deprecated-experimental-components.md @@ -19,7 +19,7 @@ function ExampleComponent() { You can satisfy the rule by either converting to the non-experimental version: ```jsx -import {SelectPane} from '@primer/react' +import {SelectPanel} from '@primer/react' function ExampleComponent() { return From d099c40797d9bdd7fc948d59ef33647686a9f228 Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Wed, 9 Apr 2025 15:58:40 -0400 Subject: [PATCH 09/11] Update src/rules/no-deprecated-experimental-components.js --- src/rules/no-deprecated-experimental-components.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/no-deprecated-experimental-components.js b/src/rules/no-deprecated-experimental-components.js index c5c80c4d..c4d96f05 100644 --- a/src/rules/no-deprecated-experimental-components.js +++ b/src/rules/no-deprecated-experimental-components.js @@ -25,7 +25,7 @@ module.exports = { meta: { type: 'problem', docs: { - description: 'Use deprecated components from the `@primer/react/deprecated` entrypoint', + description: 'Use a stable component from the `@primer/react` entrypoint, or check the docs for alternatives', recommended: true, url: url(module), }, From 0f96e8d79b911f0cbddaa8c6424696566d883f73 Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Thu, 10 Apr 2025 10:59:58 -0400 Subject: [PATCH 10/11] Update src/configs/recommended.js --- src/configs/recommended.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configs/recommended.js b/src/configs/recommended.js index c08f6e10..1d4a9f33 100644 --- a/src/configs/recommended.js +++ b/src/configs/recommended.js @@ -12,7 +12,7 @@ module.exports = { rules: { 'primer-react/direct-slot-children': 'error', 'primer-react/no-system-props': 'warn', - 'no-deprecated-experimental-components': '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', From 481a89c63aa069fe6c10c8f98133b56ed5da2fe6 Mon Sep 17 00:00:00 2001 From: Tyler Jones Date: Thu, 10 Apr 2025 15:03:28 +0000 Subject: [PATCH 11/11] Edit to message --- .../__tests__/no-deprecated-experimental-components.test.js | 4 ++-- src/rules/no-deprecated-experimental-components.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rules/__tests__/no-deprecated-experimental-components.test.js b/src/rules/__tests__/no-deprecated-experimental-components.test.js index d4d70259..3488328a 100644 --- a/src/rules/__tests__/no-deprecated-experimental-components.test.js +++ b/src/rules/__tests__/no-deprecated-experimental-components.test.js @@ -30,14 +30,14 @@ ruleTester.run('no-deprecated-experimental-components', rule, { { code: `import {SelectPanel} from '@primer/react/experimental'`, errors: [ - 'SelectPanel is deprecated. Please import them from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.', + '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 them from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.', + 'SelectPanel is deprecated. Please import from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.', ], }, ], diff --git a/src/rules/no-deprecated-experimental-components.js b/src/rules/no-deprecated-experimental-components.js index c4d96f05..3746c9fa 100644 --- a/src/rules/no-deprecated-experimental-components.js +++ b/src/rules/no-deprecated-experimental-components.js @@ -54,7 +54,7 @@ module.exports = { if (experimental.length > 0) { const message = `${components.join(', ')} ${ components.length > 1 ? 'are' : 'is' - } deprecated. Please import them from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.` + } deprecated. Please import from the stable entrypoint (@primer/react) if available, or check https://primer.style/product/components/ for alternative components.` context.report({ node,