diff --git a/.changeset/shiny-kiwis-hammer.md b/.changeset/shiny-kiwis-hammer.md
new file mode 100644
index 00000000..80805217
--- /dev/null
+++ b/.changeset/shiny-kiwis-hammer.md
@@ -0,0 +1,6 @@
+---
+"eslint-plugin-primer-react": major
+---
+
+- Remove `no-deprecated-colors` plugin
+- Remove dependency on `primer/primitives`
diff --git a/README.md b/README.md
index 5ab44c86..9163120c 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,6 @@ ESLint rules for Primer React
## Rules
- [direct-slot-children](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/direct-slot-children.md)
-- [no-deprecated-colors](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/no-deprecated-colors.md)
- [no-system-props](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/no-system-props.md)
- [a11y-tooltip-interactive-trigger](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-tooltip-interactive-trigger.md)
- [a11y-explicit-heading](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-explicit-heading.md)
diff --git a/docs/rules/no-deprecated-colors.md b/docs/rules/no-deprecated-colors.md
deleted file mode 100644
index 151bc716..00000000
--- a/docs/rules/no-deprecated-colors.md
+++ /dev/null
@@ -1,91 +0,0 @@
-# Disallow references to deprecated color variables (no-deprecated-colors)
-
-🔧 The `--fix` option on the [ESLint CLI](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can
-automatically fix some of the problems reported by this rule.
-
-[Theming](https://primer.style/react/theming) in Primer React is made possible by a theme object that defines your
-application's colors, spacing, fonts, and more. The color variables in Primer React's
-[default theme object](https://primer.style/react/theme-reference) are pulled from
-[Primer Primitives](https://github.com/primer/primitives). When a color variable is deprecated in Primer Primitives,
-it's important to remove references to that color variable in your application before it's removed from the library.
-
-## Rule details
-
-This rule disallows references to color variables that are deprecated in
-[Primer Primitives](https://github.com/primer/primitives).
-
-👎 Examples of **incorrect** code for this rule:
-
-```jsx
-/* eslint primer-react/no-deprecated-colors: "error" */
-import {Box, themeGet} from '@primer/react'
-import styled from 'styled-components'
-
-const SystemPropExample() = () => Incorrect
-
-const SxPropExample() = () => Incorrect
-
-const SxPropExample2() = () => `0 1px 2px ${theme.colors.some.deprecated.color}`}}>Incorrect
-
-const ThemeGetExample = styled.div`
- color: ${themeGet('colors.some.deprecated.color')};
-`
-```
-
-👍 Examples of **correct** code for this rule:
-
-```jsx
-/* eslint primer-react/no-deprecated-colors: "error" */
-import {Box, themeGet} from '@primer/react'
-import styled from 'styled-components'
-
-const SystemPropExample() = () => Correct
-
-const SxPropExample() = () => Correct
-
-const SxPropExample2() = () => `0 1px 2px ${theme.colors.some.color}`}}>Correct
-
-const ThemeGetExample = styled.div`
- color: ${themeGet('colors.some.color')};
-`
-```
-
-## Options
-
-- `skipImportCheck` (default: `false`)
-
- By default, the `no-deprecated-colors` rule will only check for deprecated colors used in functions and components
- that are imported from `@primer/react`. You can disable this behavior by setting `skipImportCheck` to `true`. This is
- useful for linting custom components that pass color-related props down to Primer React components.
-
- ```js
- /* eslint primer-react/no-deprecated-colors: ["warn", {"skipImportCheck": true}] */
- import {Box} from '@primer/react'
-
- function MyBox({color, children}) {
- return {children}
- }
-
- function App() {
- // Enabling `skipImportCheck` will find deprecated colors used like this:
- return Hello
- }
- ```
-
-- `checkAllStrings` (default: `false`)
-
- If `checkAllStrings` is set to `true`, the `no-deprecated-colors` rule will check for deprecated colors in all
- strings. This is useful for catching uses of deprecated colors outside system props and the `sx` prop.
-
- ```js
- /* eslint primer-react/no-deprecated-colors: ["warn", {"checkAllStrings": true}] */
- import {Box} from '@primer/react'
-
- function ExampleComponent() {
- const styles = {
- // Enabling `checkAllStrings` will find deprecated colors used like this:
- color: 'text.primary',
- }
- return Hello
- }
- ```
diff --git a/package-lock.json b/package-lock.json
index 62196bb1..365bfc06 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21,7 +21,6 @@
"@changesets/cli": "^2.16.0",
"@github/markdownlint-github": "^0.6.0",
"@github/prettier-config": "0.0.6",
- "@primer/primitives": "^7.14.0",
"eslint": "^8.42.0",
"eslint-plugin-prettier": "^5.0.1",
"jest": "^29.7.0",
@@ -29,7 +28,6 @@
"markdownlint-cli2-formatter-pretty": "^0.0.5"
},
"peerDependencies": {
- "@primer/primitives": ">=4.6.2",
"eslint": "^8.42.0"
}
},
@@ -1897,12 +1895,6 @@
"url": "https://opencollective.com/unts"
}
},
- "node_modules/@primer/primitives": {
- "version": "7.15.15",
- "resolved": "https://registry.npmjs.org/@primer/primitives/-/primitives-7.15.15.tgz",
- "integrity": "sha512-K9fPgKVBtdmBMl+lHKUK4ctsRf+DUUKdJYEHnFhkua71m9FORK16ycJ4gHDpc4g/0xgNvEwQ9/ArsV1i8w3ZmQ==",
- "dev": true
- },
"node_modules/@sinclair/typebox": {
"version": "0.27.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
@@ -9972,12 +9964,6 @@
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz",
"integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ=="
},
- "@primer/primitives": {
- "version": "7.15.15",
- "resolved": "https://registry.npmjs.org/@primer/primitives/-/primitives-7.15.15.tgz",
- "integrity": "sha512-K9fPgKVBtdmBMl+lHKUK4ctsRf+DUUKdJYEHnFhkua71m9FORK16ycJ4gHDpc4g/0xgNvEwQ9/ArsV1i8w3ZmQ==",
- "dev": true
- },
"@sinclair/typebox": {
"version": "0.27.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
diff --git a/package.json b/package.json
index 4cf48af1..50cb0f04 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,6 @@
},
"homepage": "https://github.com/primer/eslint-plugin-primer-react#readme",
"peerDependencies": {
- "@primer/primitives": ">=4.6.2",
"eslint": "^8.42.0"
},
"dependencies": {
@@ -41,7 +40,6 @@
"@changesets/cli": "^2.16.0",
"@github/markdownlint-github": "^0.6.0",
"@github/prettier-config": "0.0.6",
- "@primer/primitives": "^7.14.0",
"eslint": "^8.42.0",
"eslint-plugin-prettier": "^5.0.1",
"jest": "^29.7.0",
diff --git a/src/configs/recommended.js b/src/configs/recommended.js
index e1c62bee..887941fc 100644
--- a/src/configs/recommended.js
+++ b/src/configs/recommended.js
@@ -11,7 +11,6 @@ module.exports = {
extends: ['plugin:github/react'],
rules: {
'primer-react/direct-slot-children': 'error',
- 'primer-react/no-deprecated-colors': 'warn',
'primer-react/no-system-props': 'warn',
'primer-react/a11y-tooltip-interactive-trigger': 'error',
'primer-react/new-color-css-vars': 'error',
diff --git a/src/index.js b/src/index.js
index e5968c9a..f82e6749 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,7 +1,6 @@
module.exports = {
rules: {
'direct-slot-children': require('./rules/direct-slot-children'),
- 'no-deprecated-colors': require('./rules/no-deprecated-colors'),
'no-deprecated-entrypoints': require('./rules/no-deprecated-entrypoints'),
'no-system-props': require('./rules/no-system-props'),
'a11y-tooltip-interactive-trigger': require('./rules/a11y-tooltip-interactive-trigger'),
diff --git a/src/rules/__tests__/no-deprecated-colors.test.js b/src/rules/__tests__/no-deprecated-colors.test.js
deleted file mode 100644
index eeccfbfc..00000000
--- a/src/rules/__tests__/no-deprecated-colors.test.js
+++ /dev/null
@@ -1,221 +0,0 @@
-const rule = require('../no-deprecated-colors')
-const {RuleTester} = require('eslint')
-
-const deprecatedVars = {
- 'text.primary': 'fg.default',
- 'bg.primary': 'canvas.default',
- 'auto.green.5': ['success.fg', 'success.emphasis'],
-}
-
-const removedVars = {
- 'fade.fg10': null,
- 'autocomplete.shadow': 'shadow.medium',
-}
-
-jest.mock('@primer/primitives/dist/deprecated/colors', () => deprecatedVars)
-jest.mock('@primer/primitives/dist/removed/colors', () => removedVars)
-
-const ruleTester = new RuleTester({
- parserOptions: {
- ecmaVersion: 'latest',
- sourceType: 'module',
- ecmaFeatures: {
- jsx: true,
- },
- },
-})
-
-ruleTester.run('no-deprecated-colors', rule, {
- valid: [
- `import {Box} from '@other/design-system'; Hello`,
- `import {Box} from "@primer/react"; Hello`,
- `import {hello} from "@primer/react"; hello("colors.text.primary")`,
- `import {themeGet} from "@primer/react"; themeGet("space.text.primary")`,
- `import {themeGet} from "@primer/react"; themeGet(props.backgroundColorThemeValue)`,
- `import {themeGet} from "@primer/react"; themeGet(2)`,
- `import {themeGet} from "@other/design-system"; themeGet("colors.text.primary")`,
- `import {get} from "@other/constants"; get("space.text.primary")`,
- `import {Box} from '@primer/react'; Hello`,
- `import {Box} from '@primer/react'; Hello`,
- `import {Box} from '@primer/react'; Hello`,
- `{color: 'text.primary'}`,
- ],
- invalid: [
- {
- code: `{color: 'text.primary'}`,
- output: `{color: "fg.default"}`,
- options: [{checkAllStrings: true}],
- errors: [
- {
- message: '"text.primary" is deprecated. Use "fg.default" instead.',
- },
- ],
- },
- {
- code: `import {Box} from "@primer/react"; function Example() { return Hello }`,
- output: `import {Box} from "@primer/react"; function Example() { return Hello }`,
- errors: [
- {
- message: '"text.primary" is deprecated. Use "fg.default" instead.',
- },
- ],
- },
- {
- code: `import {Box} from "../components"; function Example() { return Hello }`,
- output: `import {Box} from "../components"; function Example() { return Hello }`,
- options: [{skipImportCheck: true}],
- errors: [
- {
- message: '"text.primary" is deprecated. Use "fg.default" instead.',
- },
- ],
- },
- {
- code: `import Box from '@primer/react/lib-esm/Box'; function Example() { return Hello }`,
- output: `import Box from '@primer/react/lib-esm/Box'; function Example() { return Hello }`,
- errors: [
- {
- message: '"text.primary" is deprecated. Use "fg.default" instead.',
- },
- ],
- },
- {
- code: `import {Box} from "@primer/react"; const Example = () => Hello`,
- output: `import {Box} from "@primer/react"; const Example = () => Hello`,
- errors: [
- {
- message: '"text.primary" is deprecated. Use "fg.default" instead.',
- },
- ],
- },
- {
- code: `import {Box} from "@primer/react"; `,
- output: `import {Box} from "@primer/react"; `,
- errors: [
- {
- message: '"bg.primary" is deprecated. Use "canvas.default" instead.',
- },
- ],
- },
- {
- code: `import {Box} from "@primer/react"; `,
- output: `import {Box} from "@primer/react"; `,
- errors: [
- {
- message: '"bg.primary" is deprecated. Use "canvas.default" instead.',
- },
- ],
- },
- {
- code: `import {Box} from "@primer/react"; theme.shadows.autocomplete.shadow}} />`,
- output: `import {Box} from "@primer/react"; theme.shadows.shadow.medium}} />`,
- errors: [
- {
- message: '"theme.shadows.autocomplete.shadow" is deprecated. Use "theme.shadows.shadow.medium" instead.',
- },
- ],
- },
- {
- code: `import {Box} from "@primer/react"; \`0 1px 2px \${theme.colors.text.primary}\`}} />`,
- output: `import {Box} from "@primer/react"; \`0 1px 2px \${theme.colors.fg.default}\`}} />`,
- errors: [
- {
- message: '"theme.colors.text.primary" is deprecated. Use "theme.colors.fg.default" instead.',
- },
- ],
- },
- {
- code: `import {Box} from "@primer/react"; \`0 1px 2px \${t.colors.text.primary}\`}} />`,
- output: `import {Box} from "@primer/react"; \`0 1px 2px \${t.colors.fg.default}\`}} />`,
- errors: [
- {
- message: '"t.colors.text.primary" is deprecated. Use "t.colors.fg.default" instead.',
- },
- ],
- },
- {
- code: `import {Box} from "@primer/react"; `,
- output: `import {Box} from "@primer/react"; `,
- errors: [
- {
- message: '"bg.primary" is deprecated. Use "canvas.default" instead.',
- },
- ],
- },
- {
- code: `import {Box} from "@primer/react"; `,
- errors: [
- {
- message: '"auto.green.5" is deprecated.',
- suggestions: [
- {
- desc: 'Use "success.fg" instead.',
- output: `import {Box} from "@primer/react"; `,
- },
- {
- desc: 'Use "success.emphasis" instead.',
- output: `import {Box} from "@primer/react"; `,
- },
- ],
- },
- ],
- },
- {
- code: `import {Box} from "@primer/react"; `,
- errors: [
- {
- message:
- '"fade.fg10" is deprecated. Go to https://primer.style/primitives or reach out in the #primer channel on Slack to find a suitable replacement.',
- },
- ],
- },
- {
- code: `import {Box, Text} from "@primer/react"; Hello`,
- output: `import {Box, Text} from "@primer/react"; Hello`,
- errors: [
- {
- message: '"bg.primary" is deprecated. Use "canvas.default" instead.',
- },
- {
- message: '"text.primary" is deprecated. Use "fg.default" instead.',
- },
- ],
- },
- {
- code: `import {themeGet} from "@primer/react"; themeGet("colors.text.primary")`,
- output: `import {themeGet} from "@primer/react"; themeGet("colors.fg.default")`,
- errors: [
- {
- message: '"colors.text.primary" is deprecated. Use "colors.fg.default" instead.',
- },
- ],
- },
- {
- code: `import {themeGet} from "@primer/react"; themeGet("shadows.autocomplete.shadow")`,
- output: `import {themeGet} from "@primer/react"; themeGet("shadows.shadow.medium")`,
- errors: [
- {
- message: '"shadows.autocomplete.shadow" is deprecated. Use "shadows.shadow.medium" instead.',
- },
- ],
- },
- {
- code: `import {get} from "./constants"; get("colors.text.primary")`,
- output: `import {get} from "./constants"; get("colors.fg.default")`,
- errors: [
- {
- message: '"colors.text.primary" is deprecated. Use "colors.fg.default" instead.',
- },
- ],
- },
- {
- code: `import {get} from "../constants"; get("colors.text.primary")`,
- output: `import {get} from "../constants"; get("colors.fg.default")`,
- errors: [
- {
- message: '"colors.text.primary" is deprecated. Use "colors.fg.default" instead.',
- },
- ],
- },
- ],
-})
diff --git a/src/rules/no-deprecated-colors.js b/src/rules/no-deprecated-colors.js
deleted file mode 100644
index a1e2eb0e..00000000
--- a/src/rules/no-deprecated-colors.js
+++ /dev/null
@@ -1,193 +0,0 @@
-const deprecatedVars = require('@primer/primitives/dist/deprecated/colors')
-const removedVars = require('@primer/primitives/dist/removed/colors')
-const traverse = require('eslint-traverse')
-const {isImportedFrom} = require('../utils/is-imported-from')
-const {isPrimerComponent} = require('../utils/is-primer-component')
-
-const styledSystemColorProps = ['color', 'bg', 'backgroundColor', 'borderColor', 'textShadow', 'boxShadow']
-const deprecations = {...deprecatedVars, ...removedVars}
-
-module.exports = {
- meta: {
- type: 'suggestion',
- hasSuggestions: true,
- fixable: 'code',
- schema: [
- {
- type: 'object',
- properties: {
- skipImportCheck: {
- type: 'boolean',
- },
- checkAllStrings: {
- type: 'boolean',
- },
- },
- additionalProperties: false,
- },
- ],
- },
- create(context) {
- // If `skipImportCheck` is true, this rule will check for deprecated colors
- // used in any components (not just ones that are imported from `@primer/react`).
- const skipImportCheck = context.options[0] ? context.options[0].skipImportCheck : false
-
- const checkAllStrings = context.options[0] ? context.options[0].checkAllStrings : false
-
- // Track visited string literals to avoid reporting the same string multiple times
- const visitedStrings = new Set()
-
- return {
- Literal(node) {
- if (checkAllStrings && Object.keys(deprecations).includes(node.value) && !visitedStrings.has(node)) {
- replaceDeprecatedColor(context, node, node.value)
- }
- },
- JSXOpeningElement(node) {
- // Skip if component was not imported from @primer/react
- if (!skipImportCheck && !isPrimerComponent(node.name, context.getScope(node))) {
- return
- }
-
- for (const attribute of node.attributes) {
- if (!attribute.name || !attribute.value) {
- continue
- }
-
- const propName = attribute.name.name
- const propValue = attribute.value.value
-
- // Check for the sx prop
- if (propName === 'sx' && attribute.value.expression.type === 'ObjectExpression') {
- // Search all properties of the sx object (even nested properties)
- traverse(context, attribute.value, path => {
- if (path.node.type === 'Property' && path.node.value.type === 'Literal') {
- const prop = path.node
- const propName = prop.key.name
- const propValue = prop.value.value
-
- if (styledSystemColorProps.includes(propName) && Object.keys(deprecations).includes(propValue)) {
- replaceDeprecatedColor(context, prop.value, propValue)
- visitedStrings.add(prop.value)
- }
- }
-
- // Check functions passed to sx object properties
- // (e.g. boxShadow: theme => `0 1px 2px ${theme.colors.text.primary}` )
- if (path.node.type === 'Property' && path.node.value.type === 'ArrowFunctionExpression') {
- traverse(context, path.node.value.body, path => {
- if (path.node.type === 'MemberExpression') {
- // Convert MemberExpression AST to string
- const code = context.getSourceCode().getText(path.node)
-
- const [param, key, ...rest] = code.split('.')
- const name = rest.join('.')
-
- if (['colors', 'shadows'].includes(key) && Object.keys(deprecations).includes(name)) {
- replaceDeprecatedColor(
- context,
- path.node,
- name,
- str => [param, key, str].join('.'),
- str => str,
- )
- }
-
- // Don't traverse any nested member expressions.
- // The root-level member expression gives us all the data we need.
- return traverse.SKIP
- }
- })
- }
- })
- }
-
- // Check if styled-system color prop is using a deprecated color
- if (styledSystemColorProps.includes(propName) && Object.keys(deprecations).includes(propValue)) {
- replaceDeprecatedColor(context, attribute.value, propValue)
- visitedStrings.add(attribute.value)
- }
- }
- },
- CallExpression(node) {
- // Skip if not calling the `themeGet` or `get` function
- // `get` is the internal version of `themeGet` that's used in the primer/react repository
- if (
- !isThemeGet(node.callee, context.getScope(node), skipImportCheck) &&
- !isGet(node.callee, context.getScope(node))
- ) {
- return
- }
-
- const argument = node.arguments[0]
- // Skip if the argument is not a Literal (themeGet(props.backgroundColor))
- // or a string themeGet(2)
- if (argument.type !== 'Literal' || typeof argument.value !== 'string') {
- return
- }
-
- const [key, ...path] = argument.value.split('.')
- const name = path.join('.')
-
- if (['colors', 'shadows'].includes(key) && Object.keys(deprecations).includes(name)) {
- replaceDeprecatedColor(context, argument, name, str => [key, str].join('.'))
- }
- },
- }
- },
-}
-
-function isThemeGet(identifier, scope, skipImportCheck = false) {
- if (!skipImportCheck) {
- return isImportedFrom(/^@primer\/react/, identifier, scope) && identifier.name === 'themeGet'
- }
-
- return identifier.name === 'themeGet'
-}
-
-// `get` is the internal version of `themeGet` that's used in the primer/react repository.
-function isGet(identifier, scope) {
- // This is a flaky way to check for the `get` function and should probably be improved.
- return isImportedFrom(/^\.\.?\/constants$/, identifier, scope) && identifier.name === 'get'
-}
-
-function replaceDeprecatedColor(
- context,
- node,
- deprecatedName,
- transformName = str => str,
- transformReplacementValue = str => JSON.stringify(str),
-) {
- const replacement = deprecations[deprecatedName]
-
- if (replacement === null) {
- // No replacement
- context.report({
- node,
- message: `"${transformName(
- deprecatedName,
- )}" is deprecated. Go to https://primer.style/primitives or reach out in the #primer channel on Slack to find a suitable replacement.`,
- })
- } else if (Array.isArray(replacement)) {
- // Multiple possible replacements
- context.report({
- node,
- message: `"${transformName(deprecatedName)}" is deprecated.`,
- suggest: replacement.map(replacementValue => ({
- desc: `Use "${transformName(replacementValue)}" instead.`,
- fix(fixer) {
- return fixer.replaceText(node, transformReplacementValue(transformName(replacementValue)))
- },
- })),
- })
- } else {
- // One replacement
- context.report({
- node,
- message: `"${transformName(deprecatedName)}" is deprecated. Use "${transformName(replacement)}" instead.`,
- fix(fixer) {
- return fixer.replaceText(node, transformReplacementValue(transformName(replacement)))
- },
- })
- }
-}