in src/rules/no-deprecated-colors.js [29:136]
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('.'))
}
}
}
}