1
1
const { isPrimerComponent} = require ( '../utils/isPrimerComponent' )
2
2
const { pick} = require ( '@styled-system/props' )
3
+ const { some, last} = require ( 'lodash' )
3
4
4
5
// Components for which we allow all styled system props
5
6
const excludedComponents = new Set ( [ 'Box' , 'Text' ] )
@@ -13,7 +14,7 @@ module.exports = {
13
14
fixable : 'code' ,
14
15
schema : [ ] ,
15
16
messages : {
16
- noSystemProps : 'Styled-system props are deprecated ({{ propNames }})'
17
+ noSystemProps : 'Styled-system props are deprecated ({{componentName}} called with props: {{ propNames }})'
17
18
}
18
19
} ,
19
20
create ( context ) {
@@ -22,13 +23,20 @@ module.exports = {
22
23
if ( ! isPrimerComponent ( jsxNode . name , context . getScope ( jsxNode ) ) ) return
23
24
if ( excludedComponents . has ( jsxNode . name . name ) ) return
24
25
25
- const propsByNameMap = jsxNode . attributes . reduce ( ( prev , cur ) => {
26
- prev [ cur . name . name ] = cur
27
- return prev
26
+ // Create an object mapping from prop name to the AST node for that attribute
27
+ const propsByNameObject = jsxNode . attributes . reduce ( ( object , cur ) => {
28
+ // We don't do anything about spreads for now — only named attributes:
29
+ if ( cur . type === 'JSXAttribute' ) {
30
+ object [ cur . name . name ] = cur
31
+ }
32
+
33
+ return object
28
34
} , { } )
29
35
30
- let badProps = Object . values ( pick ( propsByNameMap ) )
36
+ // Create an array of bad prop attribute nodes
37
+ let badProps = Object . values ( pick ( propsByNameObject ) )
31
38
39
+ // Filter out our exceptional props
32
40
badProps = badProps . filter ( prop => {
33
41
const excludedProps = excludedComponentProps . get ( jsxNode . name . name )
34
42
if ( ! excludedProps ) {
@@ -42,25 +50,37 @@ module.exports = {
42
50
node : jsxNode ,
43
51
messageId : 'noSystemProps' ,
44
52
data : {
53
+ componentName : jsxNode . name . name ,
45
54
propNames : badProps . map ( a => a . name . name ) . join ( ', ' )
46
55
} ,
47
56
fix ( fixer ) {
48
- const existingSxProp = jsxNode . attributes . find ( attribute => attribute . name . name === 'sx' )
57
+ const existingSxProp = jsxNode . attributes . find (
58
+ attribute => attribute . type === 'JSXAttribute' && attribute . name . name === 'sx'
59
+ )
49
60
const badPropStylesMap = stylesMapFromPropNodes ( badProps , context )
50
61
if ( existingSxProp && existingSxProp . value . expression . type !== 'ObjectExpression' ) {
51
62
return
52
63
}
64
+
65
+ const stylesToAdd = existingSxProp
66
+ ? excludeSxEntriesFromStyleMap ( badPropStylesMap , existingSxProp )
67
+ : badPropStylesMap
68
+
53
69
return [
70
+ // Remove the bad props:
54
71
...badProps . map ( node => fixer . remove ( node ) ) ,
55
- existingSxProp
56
- ? fixer . replaceText (
57
- existingSxProp ,
58
- sxPropTextFromStylesMap ( new Map ( [ ...badPropStylesMap , ...stylesMapfromSxProp ( existingSxProp ) ] ) )
59
- )
60
- : fixer . insertTextAfter (
61
- jsxNode . attributes [ jsxNode . attributes . length - 1 ] ,
62
- sxPropTextFromStylesMap ( badPropStylesMap )
63
- )
72
+ ...( stylesToAdd . size > 0
73
+ ? [
74
+ existingSxProp
75
+ ? // Update an existing sx prop:
76
+ fixer . insertTextAfter (
77
+ last ( existingSxProp . value . expression . properties ) ,
78
+ `, ${ objectEntriesStringFromStylesMap ( stylesToAdd ) } `
79
+ )
80
+ : // Insert new sx prop
81
+ fixer . insertTextAfter ( last ( jsxNode . attributes ) , sxPropTextFromStylesMap ( badPropStylesMap ) )
82
+ ]
83
+ : [ ] )
64
84
]
65
85
}
66
86
} )
@@ -71,13 +91,31 @@ module.exports = {
71
91
}
72
92
73
93
const sxPropTextFromStylesMap = styles => {
74
- return `sx={{${ [ ... styles ] . map ( ( [ name , value ] ) => ` ${ name } : ${ value } ` ) . join ( ', ' ) } }}`
94
+ return `sx={{${ objectEntriesStringFromStylesMap ( styles ) } }}`
75
95
}
76
96
77
- const stylesMapfromSxProp = sxProp => {
78
- return new Map ( sxProp . value . expression . properties . map ( prop => [ prop . key . name , prop . value . raw ] ) )
97
+ const objectEntriesStringFromStylesMap = styles => {
98
+ return [ ... styles ] . map ( ( [ name , value ] ) => ` ${ name } : ${ value } ` ) . join ( ', ' )
79
99
}
80
100
101
+ // Given an array of styled prop attributes, return a mapping from attribute to expression
81
102
const stylesMapFromPropNodes = ( badProps , context ) => {
82
103
return new Map ( badProps . map ( a => [ a . name . name , a . value . raw || context . getSourceCode ( ) . getText ( a . value . expression ) ] ) )
83
104
}
105
+
106
+ // Given a style map and an existing sx prop, return a style map containing
107
+ // only the entries that aren't already overridden by an sx object entry
108
+ const excludeSxEntriesFromStyleMap = ( stylesMap , sxProp ) => {
109
+ if (
110
+ ! sxProp . value ||
111
+ sxProp . value . type !== 'JSXExpressionContainer' ||
112
+ sxProp . value . expression . type != 'ObjectExpression'
113
+ ) {
114
+ return stylesMap
115
+ }
116
+ return new Map (
117
+ [ ...stylesMap ] . filter ( ( [ key , _value ] ) => {
118
+ return ! some ( sxProp . value . expression . properties , p => p . type === 'Property' && p . key . name === key )
119
+ } )
120
+ )
121
+ }
0 commit comments