@@ -12,8 +12,6 @@ import { PluginCreator } from 'postcss'
12
12
import hash from 'hash-sum'
13
13
14
14
export const CSS_VARS_HELPER = `useCssVars`
15
- // match v-bind() with max 2-levels of nested parens.
16
- const cssVarRE = / v - b i n d \s * \( ( (?: [ ^ ) ( ] + | \( (?: [ ^ ) ( ] + | \( [ ^ ) ( ] * \) ) * \) ) * ) \) / g
17
15
18
16
export function genCssVarsFromList (
19
17
vars : string [ ] ,
@@ -47,22 +45,71 @@ function normalizeExpression(exp: string) {
47
45
return exp
48
46
}
49
47
48
+ const vBindRE = / v - b i n d \s * \( / g
49
+
50
50
export function parseCssVars ( sfc : SFCDescriptor ) : string [ ] {
51
51
const vars : string [ ] = [ ]
52
52
sfc . styles . forEach ( style => {
53
53
let match
54
54
// ignore v-bind() in comments /* ... */
55
55
const content = style . content . replace ( / \/ \* ( [ \s \S ] * ?) \* \/ / g, '' )
56
- while ( ( match = cssVarRE . exec ( content ) ) ) {
57
- const variable = normalizeExpression ( match [ 1 ] )
58
- if ( ! vars . includes ( variable ) ) {
59
- vars . push ( variable )
56
+ while ( ( match = vBindRE . exec ( content ) ) ) {
57
+ const start = match . index + match [ 0 ] . length
58
+ const end = lexBinding ( content , start )
59
+ if ( end !== null ) {
60
+ const variable = normalizeExpression ( content . slice ( start , end ) )
61
+ if ( ! vars . includes ( variable ) ) {
62
+ vars . push ( variable )
63
+ }
60
64
}
61
65
}
62
66
} )
63
67
return vars
64
68
}
65
69
70
+ const enum LexerState {
71
+ inParens ,
72
+ inSingleQuoteString ,
73
+ inDoubleQuoteString
74
+ }
75
+
76
+ function lexBinding ( content : string , start : number ) : number | null {
77
+ let state : LexerState = LexerState . inParens
78
+ let parenDepth = 0
79
+
80
+ for ( let i = start ; i < content . length ; i ++ ) {
81
+ const char = content . charAt ( i )
82
+ switch ( state ) {
83
+ case LexerState . inParens :
84
+ if ( char === `'` ) {
85
+ state = LexerState . inSingleQuoteString
86
+ } else if ( char === `"` ) {
87
+ state = LexerState . inDoubleQuoteString
88
+ } else if ( char === `(` ) {
89
+ parenDepth ++
90
+ } else if ( char === `)` ) {
91
+ if ( parenDepth > 0 ) {
92
+ parenDepth --
93
+ } else {
94
+ return i
95
+ }
96
+ }
97
+ break
98
+ case LexerState . inSingleQuoteString :
99
+ if ( char === `'` ) {
100
+ state = LexerState . inParens
101
+ }
102
+ break
103
+ case LexerState . inDoubleQuoteString :
104
+ if ( char === `"` ) {
105
+ state = LexerState . inParens
106
+ }
107
+ break
108
+ }
109
+ }
110
+ return null
111
+ }
112
+
66
113
// for compileStyle
67
114
export interface CssVarsPluginOptions {
68
115
id : string
@@ -75,10 +122,24 @@ export const cssVarsPlugin: PluginCreator<CssVarsPluginOptions> = opts => {
75
122
postcssPlugin : 'vue-sfc-vars' ,
76
123
Declaration ( decl ) {
77
124
// rewrite CSS variables
78
- if ( cssVarRE . test ( decl . value ) ) {
79
- decl . value = decl . value . replace ( cssVarRE , ( _ , $1 ) => {
80
- return `var(--${ genVarName ( id , normalizeExpression ( $1 ) , isProd ) } )`
81
- } )
125
+ const value = decl . value
126
+ if ( vBindRE . test ( value ) ) {
127
+ vBindRE . lastIndex = 0
128
+ let transformed = ''
129
+ let lastIndex = 0
130
+ let match
131
+ while ( ( match = vBindRE . exec ( value ) ) ) {
132
+ const start = match . index + match [ 0 ] . length
133
+ const end = lexBinding ( value , start )
134
+ if ( end !== null ) {
135
+ const variable = normalizeExpression ( value . slice ( start , end ) )
136
+ transformed +=
137
+ value . slice ( lastIndex , match . index ) +
138
+ `var(--${ genVarName ( id , variable , isProd ) } )`
139
+ lastIndex = end + 1
140
+ }
141
+ }
142
+ decl . value = transformed + value . slice ( lastIndex )
82
143
}
83
144
}
84
145
}
0 commit comments