refactor: only rewrite css varaiable in <style scoped> when vars is present

This commit is contained in:
Evan You 2020-07-10 17:10:48 -04:00
parent f3cc41f0c8
commit 73bfce3706
5 changed files with 66 additions and 44 deletions

View File

@ -1,15 +1,23 @@
import { compileStyle, compileStyleAsync } from '../src/compileStyle'
import {
compileStyle,
compileStyleAsync,
SFCStyleCompileOptions
} from '../src/compileStyle'
import { mockWarn } from '@vue/shared'
describe('SFC scoped CSS', () => {
mockWarn()
function compileScoped(source: string): string {
function compileScoped(
source: string,
options?: Partial<SFCStyleCompileOptions>
): string {
const res = compileStyle({
source,
filename: 'test.css',
id: 'test',
scoped: true
scoped: true,
...options
})
if (res.errors.length) {
res.errors.forEach(err => {
@ -254,10 +262,15 @@ describe('SFC scoped CSS', () => {
describe('<style vars>', () => {
test('should rewrite CSS vars in scoped mode', () => {
const code = compileScoped(`.foo {
const code = compileScoped(
`.foo {
color: var(--color);
font-size: var(--global:font);
}`)
}`,
{
vars: true
}
)
expect(code).toMatchInlineSnapshot(`
".foo[test] {
color: var(--test-color);

View File

@ -174,7 +174,7 @@ export function compileScript(
}
// 2. check <script setup="xxx"> function signature
const setupValue = scriptSetup.attrs.setup
const setupValue = scriptSetup.setup
const hasExplicitSignature = typeof setupValue === 'string'
let propsVar: string | undefined

View File

@ -15,6 +15,7 @@ export interface SFCStyleCompileOptions {
id: string
map?: RawSourceMap
scoped?: boolean
vars?: boolean
trim?: boolean
preprocessLang?: PreprocessLang
preprocessOptions?: any
@ -73,6 +74,7 @@ export function doCompileStyle(
filename,
id,
scoped = false,
vars = false,
trim = true,
modules = false,
modulesOptions = {},
@ -90,7 +92,7 @@ export function doCompileStyle(
plugins.push(trimPlugin())
}
if (scoped) {
plugins.push(scopedPlugin(id))
plugins.push(scopedPlugin({ id, vars }))
}
let cssModules: Record<string, string> | undefined
if (modules) {

View File

@ -40,12 +40,14 @@ export interface SFCTemplateBlock extends SFCBlock {
export interface SFCScriptBlock extends SFCBlock {
type: 'script'
setup?: string | boolean
bindings?: BindingMetadata
}
export interface SFCStyleBlock extends SFCBlock {
type: 'style'
scoped?: boolean
vars?: string
module?: string | boolean
}
@ -266,11 +268,15 @@ function createBlock(
} else if (type === 'style') {
if (p.name === 'scoped') {
;(block as SFCStyleBlock).scoped = true
} else if (p.name === 'vars' && typeof attrs.vars === 'string') {
;(block as SFCStyleBlock).vars = attrs.vars
} else if (p.name === 'module') {
;(block as SFCStyleBlock).module = attrs[p.name]
}
} else if (type === 'template' && p.name === 'functional') {
;(block as SFCTemplateBlock).functional = true
} else if (type === 'script' && p.name === 'setup') {
;(block as SFCScriptBlock).setup = attrs.setup
}
}
})

View File

@ -6,7 +6,7 @@ const animationRE = /^(-\w+-)?animation$/
const cssVarRE = /\bvar\(--(global:)?([^)]+)\)/g
export default postcss.plugin('vue-scoped', (options: any) => (root: Root) => {
const id: string = options
const { id, vars: hasInjectedVars } = options as { id: string; vars: boolean }
const keyframes = Object.create(null)
root.each(function rewriteSelectors(node) {
@ -135,44 +135,45 @@ export default postcss.plugin('vue-scoped', (options: any) => (root: Root) => {
})
const hasKeyframes = Object.keys(keyframes).length
root.walkDecls(decl => {
// If keyframes are found in this <style>, find and rewrite animation names
// in declarations.
// Caveat: this only works for keyframes and animation rules in the same
// <style> element.
if (hasKeyframes) {
// individual animation-name declaration
if (animationNameRE.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => keyframes[v.trim()] || v.trim())
.join(',')
if (hasKeyframes || hasInjectedVars)
root.walkDecls(decl => {
// If keyframes are found in this <style>, find and rewrite animation names
// in declarations.
// Caveat: this only works for keyframes and animation rules in the same
// <style> element.
if (hasKeyframes) {
// individual animation-name declaration
if (animationNameRE.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => keyframes[v.trim()] || v.trim())
.join(',')
}
// shorthand
if (animationRE.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => {
const vals = v.trim().split(/\s+/)
const i = vals.findIndex(val => keyframes[val])
if (i !== -1) {
vals.splice(i, 1, keyframes[vals[i]])
return vals.join(' ')
} else {
return v
}
})
.join(',')
}
}
// shorthand
if (animationRE.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => {
const vals = v.trim().split(/\s+/)
const i = vals.findIndex(val => keyframes[val])
if (i !== -1) {
vals.splice(i, 1, keyframes[vals[i]])
return vals.join(' ')
} else {
return v
}
})
.join(',')
}
}
// rewrite CSS variables
if (cssVarRE.test(decl.value)) {
decl.value = decl.value.replace(cssVarRE, (_, $1, $2) => {
return $1 ? `var(--${$2})` : `var(--${id}-${$2})`
})
}
})
// rewrite CSS variables
if (hasInjectedVars && cssVarRE.test(decl.value)) {
decl.value = decl.value.replace(cssVarRE, (_, $1, $2) => {
return $1 ? `var(--${$2})` : `var(--${id}-${$2})`
})
}
})
})
function isSpaceCombinator(node: Node) {