Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | 8x 8x 8x 8x 8x 13x 20x 24x 4x 20x 24x 24x 4x 20x 8x 164x 164x 19x 19x 20x 20x 17x 164x 8x 34x 34x 57x 4x 4x 8x 8x 12x 12x 12x 12x 12x 60x 12x 8x 6x | 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` // match v-bind() with max 2-levels of nested parens. const cssVarRE = /v-bind\s*\(((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*)\)/g export function genCssVarsFromList( vars: string[], id: string, isProd: boolean ): string { return `{\n ${vars .map(key => `"${genVarName(id, key, isProd)}": (${key})`) .join(',\n ')}\n}` } function genVarName(id: string, raw: string, isProd: boolean): string { if (isProd) { return hash(id + raw) } else { return `${id}-${raw.replace(/([^\w-])/g, '_')}` } } 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[] { const vars: string[] = [] sfc.styles.forEach(style => { let match // ignore v-bind() in comments /* ... */ const content = style.content.replace(/\/\*([\s\S]*?)\*\//g, '') while ((match = cssVarRE.exec(content))) { const variable = noramlizeExpression(match[1]) if (!vars.includes(variable)) { vars.push(variable) } } }) return vars } // for compileStyle export interface CssVarsPluginOptions { id: string isProd: boolean } export const cssVarsPlugin: PluginCreator<CssVarsPluginOptions> = opts => { const { id, isProd } = opts! return { postcssPlugin: 'vue-sfc-vars', Declaration(decl) { // rewrite CSS variables if (cssVarRE.test(decl.value)) { decl.value = decl.value.replace(cssVarRE, (_, $1) => { return `var(--${genVarName(id, noramlizeExpression($1), isProd)})` }) } } } } cssVarsPlugin.postcss = true export function genCssVarsCode( vars: string[], bindings: BindingMetadata, id: string, isProd: boolean ) { 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}))` } // <script setup> already gets the calls injected as part of the transform // this is only for single normal <script> export function genNormalScriptCssVarsCode( cssVars: string[], bindings: BindingMetadata, id: string, isProd: boolean ): string { 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` ) } |