fix(compiler-sfc): fix css v-bind inside other css functions

fix #5302, close #5306
This commit is contained in:
Evan You 2022-01-23 20:58:43 +08:00
parent 283df0ad68
commit 16fa18da6d
3 changed files with 27 additions and 8 deletions

View File

@ -74,14 +74,16 @@ export default {
expose(); expose();
_useCssVars(_ctx => ({ _useCssVars(_ctx => ({
\\"xxxxxxxx-_a___b____2____px__\\": ((_unref(a) + _unref(b)) / 2 + 'px' ), \\"xxxxxxxx-foo\\": (_unref(foo)),
\\"xxxxxxxx-_a___b____2____px_\\": ((_unref(a) + _unref(b)) / 2 + 'px'),
\\"xxxxxxxx-__a___b______2___a_\\": (((_unref(a) + _unref(b))) / (2 * _unref(a))) \\"xxxxxxxx-__a___b______2___a_\\": (((_unref(a) + _unref(b))) / (2 * _unref(a)))
})) }))
let a = 100 let a = 100
let b = 200 let b = 200
let foo = 300
return { a, b } return { a, b, foo }
} }
}" }"

View File

@ -204,8 +204,13 @@ describe('CSS vars injection', () => {
`<script setup> `<script setup>
let a = 100 let a = 100
let b = 200 let b = 200
let foo = 300
</script>\n` + </script>\n` +
`<style> `<style>
p{
width: calc(v-bind(foo) - 3px);
height: calc(v-bind('foo') - 3px);
}
div { div {
color: v-bind((a + b) / 2 + 'px' ); color: v-bind((a + b) / 2 + 'px' );
} }
@ -218,7 +223,8 @@ describe('CSS vars injection', () => {
</style>` </style>`
) )
expect(content).toMatch(`_useCssVars(_ctx => ({ expect(content).toMatch(`_useCssVars(_ctx => ({
"${mockId}-_a___b____2____px__": ((_unref(a) + _unref(b)) / 2 + 'px' ), "${mockId}-foo": (_unref(foo)),
"${mockId}-_a___b____2____px_": ((_unref(a) + _unref(b)) / 2 + 'px'),
"${mockId}-__a___b______2___a_": (((_unref(a) + _unref(b))) / (2 * _unref(a))) "${mockId}-__a___b______2___a_": (((_unref(a) + _unref(b))) / (2 * _unref(a)))
})`) })`)
assertCode(content) assertCode(content)

View File

@ -12,8 +12,8 @@ import { PluginCreator } from 'postcss'
import hash from 'hash-sum' import hash from 'hash-sum'
export const CSS_VARS_HELPER = `useCssVars` export const CSS_VARS_HELPER = `useCssVars`
export const cssVarRE = // match v-bind() with max 2-levels of nested parens.
/\bv-bind\s*\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^;]*))\s*\)/g const cssVarRE = /v-bind\s*\(((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*)\)/g
export function genCssVarsFromList( export function genCssVarsFromList(
vars: string[], vars: string[],
@ -33,6 +33,17 @@ function genVarName(id: string, raw: string, isProd: boolean): string {
} }
} }
function noramlizeExpression(exp: string) {
exp = exp.trim()
if (
(exp[0] === `'` && exp[exp.length - 1] === `'`) ||
(exp[0] === `"` && exp[exp.length - 1] === `"`)
) {
return exp.slice(1, -1)
}
return exp
}
export function parseCssVars(sfc: SFCDescriptor): string[] { export function parseCssVars(sfc: SFCDescriptor): string[] {
const vars: string[] = [] const vars: string[] = []
sfc.styles.forEach(style => { sfc.styles.forEach(style => {
@ -40,7 +51,7 @@ export function parseCssVars(sfc: SFCDescriptor): string[] {
// ignore v-bind() in comments /* ... */ // ignore v-bind() in comments /* ... */
const content = style.content.replace(/\/\*([\s\S]*?)\*\//g, '') const content = style.content.replace(/\/\*([\s\S]*?)\*\//g, '')
while ((match = cssVarRE.exec(content))) { while ((match = cssVarRE.exec(content))) {
const variable = match[1] || match[2] || match[3] const variable = noramlizeExpression(match[1])
if (!vars.includes(variable)) { if (!vars.includes(variable)) {
vars.push(variable) vars.push(variable)
} }
@ -62,8 +73,8 @@ export const cssVarsPlugin: PluginCreator<CssVarsPluginOptions> = opts => {
Declaration(decl) { Declaration(decl) {
// rewrite CSS variables // rewrite CSS variables
if (cssVarRE.test(decl.value)) { if (cssVarRE.test(decl.value)) {
decl.value = decl.value.replace(cssVarRE, (_, $1, $2, $3) => { decl.value = decl.value.replace(cssVarRE, (_, $1) => {
return `var(--${genVarName(id, $1 || $2 || $3, isProd)})` return `var(--${genVarName(id, noramlizeExpression($1), isProd)})`
}) })
} }
} }