108 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			108 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import {
 | |
|   processExpression,
 | |
|   createTransformContext,
 | |
|   createSimpleExpression,
 | |
|   createRoot,
 | |
|   NodeTypes,
 | |
|   SimpleExpressionNode,
 | |
|   BindingMetadata
 | |
| } from '@vue/compiler-dom'
 | |
| import { SFCDescriptor } from './parse'
 | |
| import { rewriteDefault } from './rewriteDefault'
 | |
| import { ParserPlugin } from '@babel/parser'
 | |
| import postcss, { Root } from 'postcss'
 | |
| 
 | |
| export const CSS_VARS_HELPER = `useCssVars`
 | |
| export const cssVarRE = /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g
 | |
| 
 | |
| export function convertCssVarCasing(raw: string): string {
 | |
|   return raw.replace(/([^\w-])/g, '_')
 | |
| }
 | |
| 
 | |
| export function parseCssVars(sfc: SFCDescriptor): string[] {
 | |
|   const vars: string[] = []
 | |
|   sfc.styles.forEach(style => {
 | |
|     let match
 | |
|     while ((match = cssVarRE.exec(style.content))) {
 | |
|       vars.push(match[1] || match[2] || match[3])
 | |
|     }
 | |
|   })
 | |
|   return vars
 | |
| }
 | |
| 
 | |
| // for compileStyle
 | |
| export const cssVarsPlugin = postcss.plugin(
 | |
|   'vue-scoped',
 | |
|   (id: any) => (root: Root) => {
 | |
|     const shortId = id.replace(/^data-v-/, '')
 | |
|     root.walkDecls(decl => {
 | |
|       // rewrite CSS variables
 | |
|       if (cssVarRE.test(decl.value)) {
 | |
|         decl.value = decl.value.replace(cssVarRE, (_, $1, $2, $3) => {
 | |
|           return `var(--${shortId}-${convertCssVarCasing($1 || $2 || $3)})`
 | |
|         })
 | |
|       }
 | |
|     })
 | |
|   }
 | |
| )
 | |
| 
 | |
| export function genCssVarsCode(
 | |
|   vars: string[],
 | |
|   bindings: BindingMetadata,
 | |
|   id: string
 | |
| ) {
 | |
|   const varsExp = `{\n  ${vars
 | |
|     .map(v => `"${id}-${convertCssVarCasing(v)}": (${v})`)
 | |
|     .join(',\n  ')}\n}`
 | |
|   const exp = createSimpleExpression(varsExp, false)
 | |
|   const context = createTransformContext(createRoot([]), {
 | |
|     prefixIdentifiers: true,
 | |
|     inline: true,
 | |
|     bindingMetadata: bindings
 | |
|   })
 | |
|   const transformed = processExpression(exp, context)
 | |
|   const transformedString =
 | |
|     transformed.type === NodeTypes.SIMPLE_EXPRESSION
 | |
|       ? transformed.content
 | |
|       : transformed.children
 | |
|           .map(c => {
 | |
|             return typeof c === 'string'
 | |
|               ? c
 | |
|               : (c as SimpleExpressionNode).content
 | |
|           })
 | |
|           .join('')
 | |
| 
 | |
|   return `_${CSS_VARS_HELPER}(_ctx => (${transformedString}))`
 | |
| }
 | |
| 
 | |
| // <script setup> already gets the calls injected as part of the transform
 | |
| // this is only for single normal <script>
 | |
| export function injectCssVarsCalls(
 | |
|   sfc: SFCDescriptor,
 | |
|   cssVars: string[],
 | |
|   bindings: BindingMetadata,
 | |
|   id: string,
 | |
|   parserPlugins: ParserPlugin[]
 | |
| ): string {
 | |
|   const script = rewriteDefault(
 | |
|     sfc.script!.content,
 | |
|     `__default__`,
 | |
|     parserPlugins
 | |
|   )
 | |
| 
 | |
|   return (
 | |
|     script +
 | |
|     `\nimport { ${CSS_VARS_HELPER} as _${CSS_VARS_HELPER} } from 'vue'\n` +
 | |
|     `const __injectCSSVars__ = () => {\n${genCssVarsCode(
 | |
|       cssVars,
 | |
|       bindings,
 | |
|       id
 | |
|     )}}\n` +
 | |
|     `const __setup__ = __default__.setup\n` +
 | |
|     `__default__.setup = __setup__\n` +
 | |
|     `  ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }\n` +
 | |
|     `  : __injectCSSVars__\n` +
 | |
|     `export default __default__`
 | |
|   )
 | |
| }
 |