@@ -12,8 +12,6 @@ import { PluginCreator } from 'postcss'
1212import hash from 'hash-sum'
1313
1414export 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
1715
1816export function genCssVarsFromList (
1917 vars : string [ ] ,
@@ -47,22 +45,71 @@ function normalizeExpression(exp: string) {
4745 return exp
4846}
4947
48+ const vBindRE = / v - b i n d \s * \( / g
49+
5050export function parseCssVars ( sfc : SFCDescriptor ) : string [ ] {
5151 const vars : string [ ] = [ ]
5252 sfc . styles . forEach ( style => {
5353 let match
5454 // ignore v-bind() in comments /* ... */
5555 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+ }
6064 }
6165 }
6266 } )
6367 return vars
6468}
6569
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+
66113// for compileStyle
67114export interface CssVarsPluginOptions {
68115 id : string
@@ -75,10 +122,24 @@ export const cssVarsPlugin: PluginCreator<CssVarsPluginOptions> = opts => {
75122 postcssPlugin : 'vue-sfc-vars' ,
76123 Declaration ( decl ) {
77124 // 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 )
82143 }
83144 }
84145 }
0 commit comments