refactor: only rewrite css varaiable in <style scoped> when vars is present
				
					
				
			This commit is contained in:
		
							parent
							
								
									f3cc41f0c8
								
							
						
					
					
						commit
						73bfce3706
					
				@ -1,15 +1,23 @@
 | 
				
			|||||||
import { compileStyle, compileStyleAsync } from '../src/compileStyle'
 | 
					import {
 | 
				
			||||||
 | 
					  compileStyle,
 | 
				
			||||||
 | 
					  compileStyleAsync,
 | 
				
			||||||
 | 
					  SFCStyleCompileOptions
 | 
				
			||||||
 | 
					} from '../src/compileStyle'
 | 
				
			||||||
import { mockWarn } from '@vue/shared'
 | 
					import { mockWarn } from '@vue/shared'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('SFC scoped CSS', () => {
 | 
					describe('SFC scoped CSS', () => {
 | 
				
			||||||
  mockWarn()
 | 
					  mockWarn()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function compileScoped(source: string): string {
 | 
					  function compileScoped(
 | 
				
			||||||
 | 
					    source: string,
 | 
				
			||||||
 | 
					    options?: Partial<SFCStyleCompileOptions>
 | 
				
			||||||
 | 
					  ): string {
 | 
				
			||||||
    const res = compileStyle({
 | 
					    const res = compileStyle({
 | 
				
			||||||
      source,
 | 
					      source,
 | 
				
			||||||
      filename: 'test.css',
 | 
					      filename: 'test.css',
 | 
				
			||||||
      id: 'test',
 | 
					      id: 'test',
 | 
				
			||||||
      scoped: true
 | 
					      scoped: true,
 | 
				
			||||||
 | 
					      ...options
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    if (res.errors.length) {
 | 
					    if (res.errors.length) {
 | 
				
			||||||
      res.errors.forEach(err => {
 | 
					      res.errors.forEach(err => {
 | 
				
			||||||
@ -254,10 +262,15 @@ describe('SFC scoped CSS', () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  describe('<style vars>', () => {
 | 
					  describe('<style vars>', () => {
 | 
				
			||||||
    test('should rewrite CSS vars in scoped mode', () => {
 | 
					    test('should rewrite CSS vars in scoped mode', () => {
 | 
				
			||||||
      const code = compileScoped(`.foo {
 | 
					      const code = compileScoped(
 | 
				
			||||||
 | 
					        `.foo {
 | 
				
			||||||
        color: var(--color);
 | 
					        color: var(--color);
 | 
				
			||||||
        font-size: var(--global:font);
 | 
					        font-size: var(--global:font);
 | 
				
			||||||
      }`)
 | 
					      }`,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          vars: true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
      expect(code).toMatchInlineSnapshot(`
 | 
					      expect(code).toMatchInlineSnapshot(`
 | 
				
			||||||
        ".foo[test] {
 | 
					        ".foo[test] {
 | 
				
			||||||
                color: var(--test-color);
 | 
					                color: var(--test-color);
 | 
				
			||||||
 | 
				
			|||||||
@ -174,7 +174,7 @@ export function compileScript(
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 2. check <script setup="xxx"> function signature
 | 
					  // 2. check <script setup="xxx"> function signature
 | 
				
			||||||
  const setupValue = scriptSetup.attrs.setup
 | 
					  const setupValue = scriptSetup.setup
 | 
				
			||||||
  const hasExplicitSignature = typeof setupValue === 'string'
 | 
					  const hasExplicitSignature = typeof setupValue === 'string'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let propsVar: string | undefined
 | 
					  let propsVar: string | undefined
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@ export interface SFCStyleCompileOptions {
 | 
				
			|||||||
  id: string
 | 
					  id: string
 | 
				
			||||||
  map?: RawSourceMap
 | 
					  map?: RawSourceMap
 | 
				
			||||||
  scoped?: boolean
 | 
					  scoped?: boolean
 | 
				
			||||||
 | 
					  vars?: boolean
 | 
				
			||||||
  trim?: boolean
 | 
					  trim?: boolean
 | 
				
			||||||
  preprocessLang?: PreprocessLang
 | 
					  preprocessLang?: PreprocessLang
 | 
				
			||||||
  preprocessOptions?: any
 | 
					  preprocessOptions?: any
 | 
				
			||||||
@ -73,6 +74,7 @@ export function doCompileStyle(
 | 
				
			|||||||
    filename,
 | 
					    filename,
 | 
				
			||||||
    id,
 | 
					    id,
 | 
				
			||||||
    scoped = false,
 | 
					    scoped = false,
 | 
				
			||||||
 | 
					    vars = false,
 | 
				
			||||||
    trim = true,
 | 
					    trim = true,
 | 
				
			||||||
    modules = false,
 | 
					    modules = false,
 | 
				
			||||||
    modulesOptions = {},
 | 
					    modulesOptions = {},
 | 
				
			||||||
@ -90,7 +92,7 @@ export function doCompileStyle(
 | 
				
			|||||||
    plugins.push(trimPlugin())
 | 
					    plugins.push(trimPlugin())
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (scoped) {
 | 
					  if (scoped) {
 | 
				
			||||||
    plugins.push(scopedPlugin(id))
 | 
					    plugins.push(scopedPlugin({ id, vars }))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  let cssModules: Record<string, string> | undefined
 | 
					  let cssModules: Record<string, string> | undefined
 | 
				
			||||||
  if (modules) {
 | 
					  if (modules) {
 | 
				
			||||||
 | 
				
			|||||||
@ -40,12 +40,14 @@ export interface SFCTemplateBlock extends SFCBlock {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export interface SFCScriptBlock extends SFCBlock {
 | 
					export interface SFCScriptBlock extends SFCBlock {
 | 
				
			||||||
  type: 'script'
 | 
					  type: 'script'
 | 
				
			||||||
 | 
					  setup?: string | boolean
 | 
				
			||||||
  bindings?: BindingMetadata
 | 
					  bindings?: BindingMetadata
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface SFCStyleBlock extends SFCBlock {
 | 
					export interface SFCStyleBlock extends SFCBlock {
 | 
				
			||||||
  type: 'style'
 | 
					  type: 'style'
 | 
				
			||||||
  scoped?: boolean
 | 
					  scoped?: boolean
 | 
				
			||||||
 | 
					  vars?: string
 | 
				
			||||||
  module?: string | boolean
 | 
					  module?: string | boolean
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -266,11 +268,15 @@ function createBlock(
 | 
				
			|||||||
      } else if (type === 'style') {
 | 
					      } else if (type === 'style') {
 | 
				
			||||||
        if (p.name === 'scoped') {
 | 
					        if (p.name === 'scoped') {
 | 
				
			||||||
          ;(block as SFCStyleBlock).scoped = true
 | 
					          ;(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') {
 | 
					        } else if (p.name === 'module') {
 | 
				
			||||||
          ;(block as SFCStyleBlock).module = attrs[p.name]
 | 
					          ;(block as SFCStyleBlock).module = attrs[p.name]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } else if (type === 'template' && p.name === 'functional') {
 | 
					      } else if (type === 'template' && p.name === 'functional') {
 | 
				
			||||||
        ;(block as SFCTemplateBlock).functional = true
 | 
					        ;(block as SFCTemplateBlock).functional = true
 | 
				
			||||||
 | 
					      } else if (type === 'script' && p.name === 'setup') {
 | 
				
			||||||
 | 
					        ;(block as SFCScriptBlock).setup = attrs.setup
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ const animationRE = /^(-\w+-)?animation$/
 | 
				
			|||||||
const cssVarRE = /\bvar\(--(global:)?([^)]+)\)/g
 | 
					const cssVarRE = /\bvar\(--(global:)?([^)]+)\)/g
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default postcss.plugin('vue-scoped', (options: any) => (root: Root) => {
 | 
					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)
 | 
					  const keyframes = Object.create(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  root.each(function rewriteSelectors(node) {
 | 
					  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
 | 
					  const hasKeyframes = Object.keys(keyframes).length
 | 
				
			||||||
  root.walkDecls(decl => {
 | 
					  if (hasKeyframes || hasInjectedVars)
 | 
				
			||||||
    // If keyframes are found in this <style>, find and rewrite animation names
 | 
					    root.walkDecls(decl => {
 | 
				
			||||||
    // in declarations.
 | 
					      // If keyframes are found in this <style>, find and rewrite animation names
 | 
				
			||||||
    // Caveat: this only works for keyframes and animation rules in the same
 | 
					      // in declarations.
 | 
				
			||||||
    // <style> element.
 | 
					      // Caveat: this only works for keyframes and animation rules in the same
 | 
				
			||||||
    if (hasKeyframes) {
 | 
					      // <style> element.
 | 
				
			||||||
      // individual animation-name declaration
 | 
					      if (hasKeyframes) {
 | 
				
			||||||
      if (animationNameRE.test(decl.prop)) {
 | 
					        // individual animation-name declaration
 | 
				
			||||||
        decl.value = decl.value
 | 
					        if (animationNameRE.test(decl.prop)) {
 | 
				
			||||||
          .split(',')
 | 
					          decl.value = decl.value
 | 
				
			||||||
          .map(v => keyframes[v.trim()] || v.trim())
 | 
					            .split(',')
 | 
				
			||||||
          .join(',')
 | 
					            .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
 | 
					      // rewrite CSS variables
 | 
				
			||||||
    if (cssVarRE.test(decl.value)) {
 | 
					      if (hasInjectedVars && cssVarRE.test(decl.value)) {
 | 
				
			||||||
      decl.value = decl.value.replace(cssVarRE, (_, $1, $2) => {
 | 
					        decl.value = decl.value.replace(cssVarRE, (_, $1, $2) => {
 | 
				
			||||||
        return $1 ? `var(--${$2})` : `var(--${id}-${$2})`
 | 
					          return $1 ? `var(--${$2})` : `var(--${id}-${$2})`
 | 
				
			||||||
      })
 | 
					        })
 | 
				
			||||||
    }
 | 
					      }
 | 
				
			||||||
  })
 | 
					    })
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function isSpaceCombinator(node: Node) {
 | 
					function isSpaceCombinator(node: Node) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user