import { processExpression , createTransformContext , createSimpleExpression , createRoot , NodeTypes , SimpleExpressionNode , BindingMetadata } from '@vue/compiler-dom' ; import { SFCDescriptor } from './parse' ; import { PluginCreator } from 'postcss' ; import hash from 'hash-sum' ; export const CSS_VARS_HELPER = `useCssVars` ; export const cssVarRE = /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g ; export function genCssVarsFromList ( vars , id , isProd ) { return `{\n  ${ vars . map ( key => `"${ genVarName ( id , key , isProd ) }": (${ key })` ) . join ( ',\n  ' ) }\n}` ; } function genVarName ( id , raw , isProd ) { if ( isProd ) { return hash ( id + raw ) ; } else { return `${ id }-${ raw . replace ( /([^\w-])/g , '_' ) }` ; } } export function parseCssVars ( sfc ) { const vars = [ ] ; sfc . styles . forEach ( style => { let match ; const content = style . content . replace ( /\/\*([\s\S]*?)\*\//g , '' ) ; while ( ( match = cssVarRE . exec ( content ) ) ) { const variable = match [ 1 ] || match [ 2 ] || match [ 3 ] ; if ( ! vars . includes ( variable ) ) { vars . push ( variable ) ; } } } ) ; return vars ; } export interface CssVarsPluginOptions { id : string ; isProd : boolean ; } export const cssVarsPlugin = opts => { const { id , isProd } = opts ! ; return { postcssPlugin : 'vue-sfc-vars' , Declaration ( decl ) { if ( cssVarRE . test ( decl . value ) ) { decl . value = decl . value . replace ( cssVarRE , ( _ , $1 , $2 , $3 ) => { return `var(--${ genVarName ( id , $1 || $2 || $3 , isProd ) })` ; } ) ; } } } ; } ; cssVarsPlugin . postcss = true ; export function genCssVarsCode ( vars , bindings , id , isProd ) { const varsExp = genCssVarsFromList ( vars , id , isProd ) ; const exp = createSimpleExpression ( varsExp , false ) ; const context = createTransformContext ( createRoot ( [ ] ) , { prefixIdentifiers : true , inline : true , bindingMetadata : bindings . __isScriptSetup === false ? undefined : 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 }))` ; } export function genNormalScriptCssVarsCode ( cssVars , bindings , id , isProd ) { return ( `\nimport { ${ CSS_VARS_HELPER } as _${ CSS_VARS_HELPER } } from 'vue'\n` + `const __injectCSSVars__ = () => {\n${ genCssVarsCode ( cssVars , bindings , id , isProd ) }}\n` + `const __setup__ = __default__.setup\n` + `__default__.setup = __setup__\n` + `  ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }\n` + `  : __injectCSSVars__\n` ) ; }