-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(compiler-sfc):
<style vars>
CSS variable injection
- Loading branch information
Showing
8 changed files
with
280 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { | ||
processExpression, | ||
createTransformContext, | ||
createSimpleExpression, | ||
createRoot, | ||
NodeTypes, | ||
SimpleExpressionNode | ||
} from '@vue/compiler-dom' | ||
import { SFCDescriptor } from './parse' | ||
import { rewriteDefault } from './rewriteDefault' | ||
import { ParserPlugin } from '@babel/parser' | ||
|
||
export function genCssVarsCode( | ||
varsExp: string, | ||
scoped: boolean, | ||
knownBindings?: Record<string, boolean> | ||
) { | ||
const exp = createSimpleExpression(varsExp, false) | ||
const context = createTransformContext(createRoot([]), { | ||
prefixIdentifiers: true | ||
}) | ||
if (knownBindings) { | ||
// when compiling <script setup> we already know what bindings are exposed | ||
// so we can avoid prefixing them from the ctx. | ||
for (const key in knownBindings) { | ||
context.identifiers[key] = 1 | ||
} | ||
} | ||
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 `__useCSSVars__(_ctx => (${transformedString})${ | ||
scoped ? `, true` : `` | ||
})` | ||
} | ||
|
||
// <script setup> already gets the calls injected as part of the transform | ||
// this is only for single normal <script> | ||
export function injectCssVarsCalls( | ||
sfc: SFCDescriptor, | ||
parserPlugins: ParserPlugin[] | ||
): string { | ||
const script = rewriteDefault( | ||
sfc.script!.content, | ||
`__default__`, | ||
parserPlugins | ||
) | ||
|
||
let calls = `` | ||
for (const style of sfc.styles) { | ||
const vars = style.attrs.vars | ||
if (typeof vars === 'string') { | ||
calls += genCssVarsCode(vars, !!style.scoped) + '\n' | ||
} | ||
} | ||
|
||
return ( | ||
script + | ||
`\nimport { useCSSVars as __useCSSVars__ } from 'vue'\n` + | ||
`const __injectCSSVars__ = () => {\n${calls}}\n` + | ||
`const __setup__ = __default__.setup\n` + | ||
`__default__.setup = __setup__\n` + | ||
` ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }\n` + | ||
` : __injectCSSVars__\n` + | ||
`export default __default__` | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { parse, ParserPlugin } from '@babel/parser' | ||
import MagicString from 'magic-string' | ||
|
||
const defaultExportRE = /((?:^|\n|;)\s*)export default/ | ||
|
||
/** | ||
* Utility for rewriting `export default` in a script block into a varaible | ||
* declaration so that we can inject things into it | ||
*/ | ||
export function rewriteDefault( | ||
input: string, | ||
as: string, | ||
parserPlugins?: ParserPlugin[] | ||
): string { | ||
if (!defaultExportRE.test(input)) { | ||
return input + `\nconst ${as} = {}` | ||
} | ||
|
||
const replaced = input.replace(defaultExportRE, `$1const ${as} =`) | ||
if (!defaultExportRE.test(replaced)) { | ||
return replaced | ||
} | ||
|
||
// if the script somehow still contains `default export`, it probably has | ||
// multi-line comments or template strings. fallback to a full parse. | ||
const s = new MagicString(input) | ||
const ast = parse(input, { | ||
plugins: parserPlugins | ||
}).program.body | ||
ast.forEach(node => { | ||
if (node.type === 'ExportDefaultDeclaration') { | ||
s.overwrite(node.start!, node.declaration.start!, `const ${as} = `) | ||
} | ||
}) | ||
return s.toString() | ||
} |
Oops, something went wrong.