diff --git a/.changeset/dry-tips-burn.md b/.changeset/dry-tips-burn.md
new file mode 100644
index 00000000..f867a420
--- /dev/null
+++ b/.changeset/dry-tips-burn.md
@@ -0,0 +1,5 @@
+---
+'eslint-plugin-primer-react': minor
+---
+
+Add use-deprecated-from-deprecated rule
diff --git a/docs/rules/use-deprecated-from-deprecated.md b/docs/rules/use-deprecated-from-deprecated.md
new file mode 100644
index 00000000..01036ded
--- /dev/null
+++ b/docs/rules/use-deprecated-from-deprecated.md
@@ -0,0 +1,25 @@
+# Use Deprecated from Deprecated
+
+## Rule Details
+
+This rule enforces the usage of deprecated imports from `@primer/react/deprecated`.
+
+👎 Examples of **incorrect** code for this rule
+
+```jsx
+import {Dialog} from '@primer/react'
+
+function ExampleComponent() {
+ return
+}
+```
+
+👍 Examples of **correct** code for this rule:
+
+```jsx
+import {Dialog} from '@primer/react/deprecated'
+
+function ExampleComponent() {
+ return
+}
+```
diff --git a/src/index.js b/src/index.js
index 20e1fb01..d9ce57df 100644
--- a/src/index.js
+++ b/src/index.js
@@ -10,6 +10,7 @@ module.exports = {
'a11y-link-in-text-block': require('./rules/a11y-link-in-text-block'),
'a11y-remove-disable-tooltip': require('./rules/a11y-remove-disable-tooltip'),
'a11y-use-next-tooltip': require('./rules/a11y-use-next-tooltip'),
+ 'use-deprecated-from-deprecated': require('./rules/use-deprecated-from-deprecated'),
},
configs: {
recommended: require('./configs/recommended'),
diff --git a/src/rules/__tests__/use-deprecated-from-deprecated.test.js b/src/rules/__tests__/use-deprecated-from-deprecated.test.js
new file mode 100644
index 00000000..2b3cbd98
--- /dev/null
+++ b/src/rules/__tests__/use-deprecated-from-deprecated.test.js
@@ -0,0 +1,66 @@
+'use strict'
+
+const {RuleTester} = require('eslint')
+const rule = require('../use-deprecated-from-deprecated')
+
+const ruleTester = new RuleTester({
+ parserOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+})
+
+ruleTester.run('use-deprecated-from-deprecated', rule, {
+ valid: [],
+ invalid: [
+ // Single deprecated import
+ {
+ code: `import {Tooltip} from '@primer/react'`,
+ output: `import {Tooltip} from '@primer/react/deprecated'`,
+ errors: ['Import deprecated components from @primer/react/deprecated'],
+ },
+
+ // Single deprecated import with existing deprecated entrypoint
+ {
+ code: `import {Tooltip} from '@primer/react'
+import {Dialog} from '@primer/react/deprecated'`,
+ output: `\nimport {Dialog, Tooltip} from '@primer/react/deprecated'`,
+ errors: ['Import deprecated components from @primer/react/deprecated'],
+ },
+
+ // Multiple deprecated imports
+ {
+ code: `import {Dialog, Tooltip} from '@primer/react'`,
+ output: `import {Dialog, Tooltip} from '@primer/react/deprecated'`,
+ errors: ['Import deprecated components from @primer/react/deprecated'],
+ },
+
+ // Mixed deprecated and non-deprecated imports
+ {
+ code: `import {Button, Tooltip} from '@primer/react'`,
+ output: `import {Button, } from '@primer/react'
+import {Tooltip} from '@primer/react/deprecated'`,
+ errors: ['Import deprecated components from @primer/react/deprecated'],
+ },
+
+ // Mixed deprecated and non-deprecated imports with existing deprecated
+ {
+ code: `import {Button, Tooltip} from '@primer/react'
+import {Dialog} from '@primer/react/deprecated'`,
+ output: `import {Button, } from '@primer/react'
+import {Dialog, Tooltip} from '@primer/react/deprecated'`,
+ errors: ['Import deprecated components from @primer/react/deprecated'],
+ },
+
+ // Multiple mixed deprecated and non-deprecated imports
+ {
+ code: `import {Button, Dialog, Tooltip} from '@primer/react'`,
+ output: `import {Button, } from '@primer/react'
+import {Dialog, Tooltip} from '@primer/react/deprecated'`,
+ errors: ['Import deprecated components from @primer/react/deprecated'],
+ },
+ ],
+})
diff --git a/src/rules/use-deprecated-from-deprecated.js b/src/rules/use-deprecated-from-deprecated.js
new file mode 100644
index 00000000..5b6c220c
--- /dev/null
+++ b/src/rules/use-deprecated-from-deprecated.js
@@ -0,0 +1,130 @@
+'use strict'
+
+const url = require('../url')
+
+const components = [
+ {
+ identifier: 'Dialog',
+ entrypoint: '@primer/react',
+ },
+ {
+ identifier: 'Octicon',
+ entrypoint: '@primer/react',
+ },
+ {
+ identifier: 'Pagehead',
+ entrypoint: '@primer/react',
+ },
+ {
+ identifier: 'TabNav',
+ entrypoint: '@primer/react',
+ },
+ {
+ identifier: 'Tooltip',
+ entrypoint: '@primer/react',
+ },
+]
+
+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 deprecated = node.specifiers.filter(specifier => {
+ return entrypoint.has(specifier.imported.name)
+ })
+
+ if (deprecated.length === 0) {
+ return
+ }
+
+ const deprecatedEntrypoint = node.parent.body.find(node => {
+ if (node.type !== 'ImportDeclaration') {
+ return false
+ }
+
+ return node.source.value === '@primer/react/deprecated'
+ })
+
+ // All imports are deprecated
+ if (deprecated.length === node.specifiers.length) {
+ context.report({
+ node,
+ message: 'Import deprecated components from @primer/react/deprecated',
+ *fix(fixer) {
+ if (deprecatedEntrypoint) {
+ const lastSpecifier = deprecatedEntrypoint.specifiers[deprecatedEntrypoint.specifiers.length - 1]
+
+ yield fixer.remove(node)
+ yield fixer.insertTextAfter(
+ lastSpecifier,
+ `, ${node.specifiers.map(specifier => specifier.imported.name).join(', ')}`,
+ )
+ } else {
+ yield fixer.replaceText(node.source, `'@primer/react/deprecated'`)
+ }
+ },
+ })
+ } else {
+ // There is a mix of deprecated and non-deprecated imports
+ context.report({
+ node,
+ message: 'Import deprecated components from @primer/react/deprecated',
+ *fix(fixer) {
+ for (const specifier of deprecated) {
+ yield fixer.remove(specifier)
+ const comma = sourceCode.getTokenAfter(specifier)
+ if (comma.value === ',') {
+ yield fixer.remove(comma)
+ }
+ }
+
+ if (deprecatedEntrypoint) {
+ const lastSpecifier = deprecatedEntrypoint.specifiers[deprecatedEntrypoint.specifiers.length - 1]
+ yield fixer.insertTextAfter(
+ lastSpecifier,
+ `, ${deprecated.map(specifier => specifier.imported.name).join(', ')}`,
+ )
+ } else {
+ yield fixer.insertTextAfter(
+ node,
+ `\nimport {${deprecated
+ .map(specifier => specifier.imported.name)
+ .join(', ')}} from '@primer/react/deprecated'`,
+ )
+ }
+ },
+ })
+ }
+ },
+ }
+ },
+}