1
1
const deprecations = require ( '@primer/primitives/dist/deprecations/colors' )
2
+ const traverse = require ( 'eslint-traverse' )
2
3
3
4
const styledSystemColorProps = [ 'color' , 'bg' , 'backgroundColor' , 'borderColor' , 'textShadow' , 'boxShadow' ]
4
5
@@ -25,19 +26,46 @@ module.exports = {
25
26
26
27
// Check for the sx prop
27
28
if ( propName === 'sx' && attribute . value . expression . type === 'ObjectExpression' ) {
28
- // Ignore non-literal properties
29
- const sxProperties = attribute . value . expression . properties . filter (
30
- property => property . type === 'Property' && property . value . type === 'Literal'
31
- )
32
-
33
- for ( const prop of sxProperties ) {
34
- const propName = prop . key . name
35
- const propValue = prop . value . value
29
+ // Search all properties of the sx object (even nested properties)
30
+ traverse ( context , attribute . value , path => {
31
+ if ( path . node . type === 'Property' && path . node . value . type === 'Literal' ) {
32
+ const prop = path . node
33
+ const propName = prop . key . name
34
+ const propValue = prop . value . value
35
+
36
+ if ( styledSystemColorProps . includes ( propName ) && Object . keys ( deprecations ) . includes ( propValue ) ) {
37
+ replaceDeprecatedColor ( context , prop . value , propValue )
38
+ }
39
+ }
36
40
37
- if ( styledSystemColorProps . includes ( propName ) && Object . keys ( deprecations ) . includes ( propValue ) ) {
38
- replaceDeprecatedColor ( context , prop . value , propValue )
41
+ // Check functions passed to sx object properties
42
+ // (e.g. boxShadow: theme => `0 1px 2px ${theme.colors.text.primary}` )
43
+ if ( path . node . type === 'Property' && path . node . value . type === 'ArrowFunctionExpression' ) {
44
+ traverse ( context , path . node . value . body , path => {
45
+ if ( path . node . type === 'MemberExpression' ) {
46
+ // Convert MemberExpression AST to string
47
+ const code = context . getSourceCode ( ) . getText ( path . node )
48
+
49
+ const [ param , key , ...rest ] = code . split ( '.' )
50
+ const name = rest . join ( '.' )
51
+
52
+ if ( [ 'colors' , 'shadows' ] . includes ( key ) && Object . keys ( deprecations ) . includes ( name ) ) {
53
+ replaceDeprecatedColor (
54
+ context ,
55
+ path . node ,
56
+ name ,
57
+ str => [ param , key , str ] . join ( '.' ) ,
58
+ str => str
59
+ )
60
+ }
61
+
62
+ // Don't traverse any nested member expressions.
63
+ // The root-level member expression gives us all the data we need.
64
+ return traverse . SKIP
65
+ }
66
+ } )
39
67
}
40
- }
68
+ } )
41
69
}
42
70
43
71
// Check if styled-system color prop is using a deprecated color
@@ -103,36 +131,42 @@ function isGet(identifier, scope) {
103
131
return isImportedFrom ( / ^ \. \. ? \/ c o n s t a n t s $ / , identifier , scope ) && identifier . name === 'get'
104
132
}
105
133
106
- function replaceDeprecatedColor ( context , node , deprecatedName , getDisplayName = str => str ) {
134
+ function replaceDeprecatedColor (
135
+ context ,
136
+ node ,
137
+ deprecatedName ,
138
+ transformName = str => str ,
139
+ transformReplacementValue = str => JSON . stringify ( str )
140
+ ) {
107
141
const replacement = deprecations [ deprecatedName ]
108
142
109
143
if ( replacement === null ) {
110
144
// No replacement
111
145
context . report ( {
112
146
node,
113
- message : `"${ getDisplayName (
147
+ message : `"${ transformName (
114
148
deprecatedName
115
149
) } " is deprecated. Go to https://primer.style/primitives or reach out in the #primer channel on Slack to find a suitable replacement.`
116
150
} )
117
151
} else if ( Array . isArray ( replacement ) ) {
118
152
// Multiple possible replacements
119
153
context . report ( {
120
154
node,
121
- message : `"${ getDisplayName ( deprecatedName ) } " is deprecated.` ,
155
+ message : `"${ transformName ( deprecatedName ) } " is deprecated.` ,
122
156
suggest : replacement . map ( replacementValue => ( {
123
- desc : `Use "${ getDisplayName ( replacementValue ) } " instead.` ,
157
+ desc : `Use "${ transformName ( replacementValue ) } " instead.` ,
124
158
fix ( fixer ) {
125
- return fixer . replaceText ( node , JSON . stringify ( getDisplayName ( replacementValue ) ) )
159
+ return fixer . replaceText ( node , transformReplacementValue ( transformName ( replacementValue ) ) )
126
160
}
127
161
} ) )
128
162
} )
129
163
} else {
130
164
// One replacement
131
165
context . report ( {
132
166
node,
133
- message : `"${ getDisplayName ( deprecatedName ) } " is deprecated. Use "${ getDisplayName ( replacement ) } " instead.` ,
167
+ message : `"${ transformName ( deprecatedName ) } " is deprecated. Use "${ transformName ( replacement ) } " instead.` ,
134
168
fix ( fixer ) {
135
- return fixer . replaceText ( node , JSON . stringify ( getDisplayName ( replacement ) ) )
169
+ return fixer . replaceText ( node , transformReplacementValue ( transformName ( replacement ) ) )
136
170
}
137
171
} )
138
172
}
0 commit comments