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__`
|
|
)
|
|
}
|