3
3
* @flow
4
4
*/
5
5
6
+ import { createHash } from 'crypto' ;
6
7
import invariant from 'invariant' ;
7
8
import * as LoaderUtils from 'loader-utils' ;
8
9
9
10
import { identifier , stringLiteral , program } from 'babel-types' ;
10
11
import generate from 'babel-generator' ;
12
+ import traverse from 'babel-traverse' ;
13
+ import * as types from 'babel-types' ;
14
+ import { parse } from 'babylon' ;
11
15
12
16
import * as postcss from 'postcss' ;
13
17
import createSelectorParser , { className } from 'postcss-selector-parser' ;
@@ -26,6 +30,7 @@ type CSSNode = Object;
26
30
type VariantSpec = {
27
31
componentName : string ;
28
32
variantName: string ;
33
+ expression: ?string ;
29
34
} ;
30
35
31
36
type ComponentSpec = {
@@ -41,11 +46,55 @@ const LOADER = require.resolve('../webpack');
41
46
42
47
const COMPONENT_RE = / ^ [ A - Z ] [ a - z A - Z _ 0 - 9 ] * $ / ;
43
48
49
+ const PROP_VARIANT_NAME = ':prop' ;
50
+
51
+ function hash ( value ) {
52
+ let hasher = createHash ( 'md5' ) ;
53
+ hasher . update ( value ) ;
54
+ return hasher . digest ( 'hex' ) ;
55
+ }
56
+
44
57
function parseSelector ( selector ) {
45
58
let parser = createSelectorParser ( ) ;
46
59
return parser . process ( selector ) . res ;
47
60
}
48
61
62
+ function isPropReference ( path ) {
63
+ if ( ! types . isIdentifier ( path . node ) ) {
64
+ return false ;
65
+ }
66
+ if ( path . node . __seen ) {
67
+ return false ;
68
+ }
69
+ if ( path . scope . parent !== undefined ) {
70
+ return false ;
71
+ }
72
+ if ( types . isMemberExpression ( path . parentPath . node ) ) {
73
+ while ( types . isMemberExpression ( path . parentPath . node ) ) {
74
+ if ( path . node === path . parentPath . node . property ) {
75
+ return false ;
76
+ }
77
+ path = path . parentPath ;
78
+ }
79
+ }
80
+ return true ;
81
+ }
82
+
83
+ function parsePropVariantExpression ( expression ) {
84
+ let node = parse ( expression ) ;
85
+ traverse ( node , {
86
+ enter ( path ) {
87
+ if ( isPropReference ( path ) ) {
88
+ let nextNode = expr `props.${ path . node } ` ;
89
+ nextNode . object . __seen = true ;
90
+ path . replaceWith ( nextNode ) ;
91
+ }
92
+ }
93
+ } ) ;
94
+ node = node . program . body [ 0 ] . expression ;
95
+ return node ;
96
+ }
97
+
49
98
function findComponentNames ( node : CSSNode ) : Array < string > {
50
99
let componentNames = [ ] ;
51
100
let selector = parseSelector ( node . selector ) ;
@@ -61,18 +110,40 @@ function findVariants(node: CSSNode): Array<VariantSpec> {
61
110
let variantNames = [ ] ;
62
111
let selector = parseSelector ( node . selector ) ;
63
112
selector . eachPseudo ( selector => {
113
+ let expression = null ;
114
+ let variantName = selector . value . slice ( 1 ) ;
115
+
116
+ if ( selector . value === PROP_VARIANT_NAME ) {
117
+ expression = node . selector . slice (
118
+ selector . source . start . column + PROP_VARIANT_NAME . length ,
119
+ selector . source . end . column - 1
120
+ ) ;
121
+ variantName = variantName + '__' + hash ( expression ) . slice ( 0 , 6 ) ;
122
+ expression = parsePropVariantExpression ( expression ) ;
123
+ }
124
+
64
125
let idx = selector . parent . nodes . indexOf ( selector ) ;
65
126
let prev = selector . parent . nodes [ idx - 1 ] ;
66
127
if ( prev && prev . type === 'tag' && COMPONENT_RE . exec ( prev . value ) ) {
67
128
variantNames . push ( {
68
129
componentName : prev . value ,
69
- variantName : selector . value . slice ( 1 ) ,
130
+ variantName,
131
+ expression,
70
132
} ) ;
71
133
}
72
134
} ) ;
73
135
return variantNames ;
74
136
}
75
137
138
+ function renderPropVariantCondition ( selector ) {
139
+ let value = '' ;
140
+ selector . eachInside ( selector => {
141
+ console . log ( selector ) ;
142
+ value = value + ( selector . value || '' ) ;
143
+ } ) ;
144
+ return value ;
145
+ }
146
+
76
147
function isPrimaryComponent ( node : CSSNode ) : boolean {
77
148
let selector = parseSelector ( node . selector ) ;
78
149
return (
@@ -131,6 +202,13 @@ function localizeComponentRule(node) {
131
202
let prev = parent . nodes [ idx - 1 ] ;
132
203
let componentName = prev . value ;
133
204
let variantName = selector . value . slice ( 1 ) ;
205
+ if ( selector . value === PROP_VARIANT_NAME ) {
206
+ let expression = node . selector . slice (
207
+ selector . source . start . column + PROP_VARIANT_NAME . length ,
208
+ selector . source . end . column - 1
209
+ ) ;
210
+ variantName = variantName + '__' + hash ( expression ) . slice ( 0 , 6 ) ;
211
+ }
134
212
let nextSelector = className ( { value : componentName + '__' + variantName } ) ;
135
213
prev . removeSelf ( ) ;
136
214
selector . replaceWith ( nextSelector ) ;
@@ -164,12 +242,12 @@ function renderToJS(source: string, config: RenderConfig): string {
164
242
}
165
243
}
166
244
167
- function registerComponentVariants ( componentName , variantName ) {
245
+ function registerComponentVariants ( { componentName, variantName, expression } ) {
168
246
invariant (
169
247
components [ componentName ] ,
170
248
'Trying to configure base for an unknown component %s' , componentName
171
249
) ;
172
- components [ componentName ] . variants [ variantName ] = true ;
250
+ components [ componentName ] . variants [ variantName ] = { expression } ;
173
251
}
174
252
175
253
function configureComponentBase ( componentName , base ) {
@@ -220,10 +298,7 @@ function renderToJS(source: string, config: RenderConfig): string {
220
298
let variants = findVariants ( node ) ;
221
299
for ( let i = 0 ; i < variants . length ; i ++ ) {
222
300
let variant = variants [ i ] ;
223
- registerComponentVariants (
224
- variant . componentName ,
225
- variant . variantName
226
- ) ;
301
+ registerComponentVariants ( variant ) ;
227
302
}
228
303
} ) ;
229
304
@@ -233,11 +308,20 @@ function renderToJS(source: string, config: RenderConfig): string {
233
308
if ( components . hasOwnProperty ( componentName ) ) {
234
309
let className = expr `styles.${ identifier ( componentName ) } ` ;
235
310
for ( let variantName in component . variants ) {
236
- className = expr `
237
- ${ className } + (variant.${ identifier ( variantName ) }
238
- ? ' ' + styles.${ identifier ( componentName + '__' + variantName ) }
239
- : '')
240
- ` ;
311
+ let variant = component . variants [ variantName ] ;
312
+ if ( variant . expression ) {
313
+ className = expr `
314
+ ${ className } + (${ variant . expression }
315
+ ? ' ' + styles.${ identifier ( componentName + '__' + variantName ) }
316
+ : '')
317
+ ` ;
318
+ } else {
319
+ className = expr `
320
+ ${ className } + (variant.${ identifier ( variantName ) }
321
+ ? ' ' + styles.${ identifier ( componentName + '__' + variantName ) }
322
+ : '')
323
+ ` ;
324
+ }
241
325
}
242
326
statements . push ( stmt `
243
327
export function ${ identifier ( componentName ) } ({variant, ...props}) {
0 commit comments