wip: css var injection production mode
This commit is contained in:
parent
f92bc5a19a
commit
94736f7729
@ -49,6 +49,7 @@
|
|||||||
"@rollup/plugin-json": "^4.0.0",
|
"@rollup/plugin-json": "^4.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||||
"@rollup/plugin-replace": "^2.2.1",
|
"@rollup/plugin-replace": "^2.2.1",
|
||||||
|
"@types/hash-sum": "^1.0.0",
|
||||||
"@types/jest": "^26.0.0",
|
"@types/jest": "^26.0.0",
|
||||||
"@types/node": "^14.10.1",
|
"@types/node": "^14.10.1",
|
||||||
"@types/puppeteer": "^2.0.0",
|
"@types/puppeteer": "^2.0.0",
|
||||||
|
@ -52,7 +52,7 @@ describe('CSS vars injection', () => {
|
|||||||
assertCode(content)
|
assertCode(content)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should rewrite CSS vars in scoped mode', () => {
|
test('should rewrite CSS vars in compileStyle', () => {
|
||||||
const { code } = compileStyle({
|
const { code } = compileStyle({
|
||||||
source: `.foo {
|
source: `.foo {
|
||||||
color: v-bind(color);
|
color: v-bind(color);
|
||||||
@ -69,6 +69,37 @@ describe('CSS vars injection', () => {
|
|||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('prod mode', () => {
|
||||||
|
const { content } = compileSFCScript(
|
||||||
|
`<script>const a = 1</script>\n` +
|
||||||
|
`<style>div{
|
||||||
|
color: v-bind(color);
|
||||||
|
font-size: v-bind('font.size');
|
||||||
|
}</style>`,
|
||||||
|
{ isProd: true }
|
||||||
|
)
|
||||||
|
expect(content).toMatch(`_useCssVars(_ctx => ({
|
||||||
|
"4003f1a6": (_ctx.color),
|
||||||
|
"41b6490a": (_ctx.font.size)
|
||||||
|
}))}`)
|
||||||
|
|
||||||
|
const { code } = compileStyle({
|
||||||
|
source: `.foo {
|
||||||
|
color: v-bind(color);
|
||||||
|
font-size: v-bind('font.size');
|
||||||
|
}`,
|
||||||
|
filename: 'test.css',
|
||||||
|
id: mockId,
|
||||||
|
isProd: true
|
||||||
|
})
|
||||||
|
expect(code).toMatchInlineSnapshot(`
|
||||||
|
".foo {
|
||||||
|
color: var(--4003f1a6);
|
||||||
|
font-size: var(--41b6490a);
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
describe('codegen', () => {
|
describe('codegen', () => {
|
||||||
test('<script> w/ no default export', () => {
|
test('<script> w/ no default export', () => {
|
||||||
assertCode(
|
assertCode(
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"@vue/shared": "3.0.2",
|
"@vue/shared": "3.0.2",
|
||||||
"consolidate": "^0.16.0",
|
"consolidate": "^0.16.0",
|
||||||
"estree-walker": "^2.0.1",
|
"estree-walker": "^2.0.1",
|
||||||
|
"hash-sum": "^2.0.0",
|
||||||
"lru-cache": "^5.1.1",
|
"lru-cache": "^5.1.1",
|
||||||
"magic-string": "^0.25.7",
|
"magic-string": "^0.25.7",
|
||||||
"merge-source-map": "^1.1.0",
|
"merge-source-map": "^1.1.0",
|
||||||
|
@ -40,6 +40,10 @@ export interface SFCScriptCompileOptions {
|
|||||||
* This must be consistent with the `id` passed to `compileStyle`.
|
* This must be consistent with the `id` passed to `compileStyle`.
|
||||||
*/
|
*/
|
||||||
id: string
|
id: string
|
||||||
|
/**
|
||||||
|
* Production mode. Used to determine whether to generate hashed CSS variables
|
||||||
|
*/
|
||||||
|
isProd?: boolean
|
||||||
/**
|
/**
|
||||||
* https://babeljs.io/docs/en/babel-parser#plugins
|
* https://babeljs.io/docs/en/babel-parser#plugins
|
||||||
*/
|
*/
|
||||||
@ -128,7 +132,14 @@ export function compileScript(
|
|||||||
return {
|
return {
|
||||||
...script,
|
...script,
|
||||||
content: cssVars.length
|
content: cssVars.length
|
||||||
? injectCssVarsCalls(sfc, cssVars, bindings, scopeId, plugins)
|
? injectCssVarsCalls(
|
||||||
|
sfc,
|
||||||
|
cssVars,
|
||||||
|
bindings,
|
||||||
|
scopeId,
|
||||||
|
!!options.isProd,
|
||||||
|
plugins
|
||||||
|
)
|
||||||
: script.content,
|
: script.content,
|
||||||
bindings,
|
bindings,
|
||||||
scriptAst
|
scriptAst
|
||||||
@ -511,6 +522,7 @@ export function compileScript(
|
|||||||
node.body.type === 'ExpressionStatement'
|
node.body.type === 'ExpressionStatement'
|
||||||
) {
|
) {
|
||||||
if (enableRefSugar) {
|
if (enableRefSugar) {
|
||||||
|
if (__DEV__ && !__TEST__) {
|
||||||
warnOnce(
|
warnOnce(
|
||||||
`ref: sugar is still an experimental proposal and is not ` +
|
`ref: sugar is still an experimental proposal and is not ` +
|
||||||
`guaranteed to be a part of <script setup>.\n` +
|
`guaranteed to be a part of <script setup>.\n` +
|
||||||
@ -518,6 +530,7 @@ export function compileScript(
|
|||||||
`It's also recommended to pin your vue dependencies to exact versions ` +
|
`It's also recommended to pin your vue dependencies to exact versions ` +
|
||||||
`to avoid breakage.`
|
`to avoid breakage.`
|
||||||
)
|
)
|
||||||
|
}
|
||||||
s.overwrite(
|
s.overwrite(
|
||||||
node.label.start! + startOffset,
|
node.label.start! + startOffset,
|
||||||
node.body.start! + startOffset,
|
node.body.start! + startOffset,
|
||||||
@ -800,7 +813,12 @@ export function compileScript(
|
|||||||
helperImports.add('unref')
|
helperImports.add('unref')
|
||||||
s.prependRight(
|
s.prependRight(
|
||||||
startOffset,
|
startOffset,
|
||||||
`\n${genCssVarsCode(cssVars, bindingMetadata, scopeId)}\n`
|
`\n${genCssVarsCode(
|
||||||
|
cssVars,
|
||||||
|
bindingMetadata,
|
||||||
|
scopeId,
|
||||||
|
!!options.isProd
|
||||||
|
)}\n`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ export interface SFCStyleCompileOptions {
|
|||||||
map?: RawSourceMap
|
map?: RawSourceMap
|
||||||
scoped?: boolean
|
scoped?: boolean
|
||||||
trim?: boolean
|
trim?: boolean
|
||||||
|
isProd?: boolean
|
||||||
preprocessLang?: PreprocessLang
|
preprocessLang?: PreprocessLang
|
||||||
preprocessOptions?: any
|
preprocessOptions?: any
|
||||||
preprocessCustomRequire?: (id: string) => any
|
preprocessCustomRequire?: (id: string) => any
|
||||||
@ -82,6 +83,7 @@ export function doCompileStyle(
|
|||||||
id,
|
id,
|
||||||
scoped = false,
|
scoped = false,
|
||||||
trim = true,
|
trim = true,
|
||||||
|
isProd = false,
|
||||||
modules = false,
|
modules = false,
|
||||||
modulesOptions = {},
|
modulesOptions = {},
|
||||||
preprocessLang,
|
preprocessLang,
|
||||||
@ -94,7 +96,7 @@ export function doCompileStyle(
|
|||||||
const source = preProcessedSource ? preProcessedSource.code : options.source
|
const source = preProcessedSource ? preProcessedSource.code : options.source
|
||||||
|
|
||||||
const plugins = (postcssPlugins || []).slice()
|
const plugins = (postcssPlugins || []).slice()
|
||||||
plugins.unshift(cssVarsPlugin(id))
|
plugins.unshift(cssVarsPlugin({ id, isProd }))
|
||||||
if (trim) {
|
if (trim) {
|
||||||
plugins.push(trimPlugin())
|
plugins.push(trimPlugin())
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,17 @@ import { SFCDescriptor } from './parse'
|
|||||||
import { rewriteDefault } from './rewriteDefault'
|
import { rewriteDefault } from './rewriteDefault'
|
||||||
import { ParserPlugin } from '@babel/parser'
|
import { ParserPlugin } from '@babel/parser'
|
||||||
import postcss, { Root } from 'postcss'
|
import postcss, { Root } from 'postcss'
|
||||||
|
import hash from 'hash-sum'
|
||||||
|
|
||||||
export const CSS_VARS_HELPER = `useCssVars`
|
export const CSS_VARS_HELPER = `useCssVars`
|
||||||
export const cssVarRE = /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g
|
export const cssVarRE = /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g
|
||||||
|
|
||||||
export function convertCssVarCasing(raw: string): string {
|
export function genVarName(id: string, raw: string, isProd: boolean): string {
|
||||||
return raw.replace(/([^\w-])/g, '_')
|
if (isProd) {
|
||||||
|
return hash(id + raw)
|
||||||
|
} else {
|
||||||
|
return `${id}-${raw.replace(/([^\w-])/g, '_')}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseCssVars(sfc: SFCDescriptor): string[] {
|
export function parseCssVars(sfc: SFCDescriptor): string[] {
|
||||||
@ -31,15 +36,21 @@ export function parseCssVars(sfc: SFCDescriptor): string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// for compileStyle
|
// for compileStyle
|
||||||
export const cssVarsPlugin = postcss.plugin(
|
export interface CssVarsPluginOptions {
|
||||||
|
id: string
|
||||||
|
isProd: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cssVarsPlugin = postcss.plugin<CssVarsPluginOptions>(
|
||||||
'vue-scoped',
|
'vue-scoped',
|
||||||
(id: any) => (root: Root) => {
|
opts => (root: Root) => {
|
||||||
|
const { id, isProd } = opts!
|
||||||
const shortId = id.replace(/^data-v-/, '')
|
const shortId = id.replace(/^data-v-/, '')
|
||||||
root.walkDecls(decl => {
|
root.walkDecls(decl => {
|
||||||
// rewrite CSS variables
|
// rewrite CSS variables
|
||||||
if (cssVarRE.test(decl.value)) {
|
if (cssVarRE.test(decl.value)) {
|
||||||
decl.value = decl.value.replace(cssVarRE, (_, $1, $2, $3) => {
|
decl.value = decl.value.replace(cssVarRE, (_, $1, $2, $3) => {
|
||||||
return `var(--${shortId}-${convertCssVarCasing($1 || $2 || $3)})`
|
return `var(--${genVarName(shortId, $1 || $2 || $3, isProd)})`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -49,10 +60,11 @@ export const cssVarsPlugin = postcss.plugin(
|
|||||||
export function genCssVarsCode(
|
export function genCssVarsCode(
|
||||||
vars: string[],
|
vars: string[],
|
||||||
bindings: BindingMetadata,
|
bindings: BindingMetadata,
|
||||||
id: string
|
id: string,
|
||||||
|
isProd: boolean
|
||||||
) {
|
) {
|
||||||
const varsExp = `{\n ${vars
|
const varsExp = `{\n ${vars
|
||||||
.map(v => `"${id}-${convertCssVarCasing(v)}": (${v})`)
|
.map(v => `"${genVarName(id, v, isProd)}": (${v})`)
|
||||||
.join(',\n ')}\n}`
|
.join(',\n ')}\n}`
|
||||||
const exp = createSimpleExpression(varsExp, false)
|
const exp = createSimpleExpression(varsExp, false)
|
||||||
const context = createTransformContext(createRoot([]), {
|
const context = createTransformContext(createRoot([]), {
|
||||||
@ -82,6 +94,7 @@ export function injectCssVarsCalls(
|
|||||||
cssVars: string[],
|
cssVars: string[],
|
||||||
bindings: BindingMetadata,
|
bindings: BindingMetadata,
|
||||||
id: string,
|
id: string,
|
||||||
|
isProd: boolean,
|
||||||
parserPlugins: ParserPlugin[]
|
parserPlugins: ParserPlugin[]
|
||||||
): string {
|
): string {
|
||||||
const script = rewriteDefault(
|
const script = rewriteDefault(
|
||||||
@ -96,7 +109,8 @@ export function injectCssVarsCalls(
|
|||||||
`const __injectCSSVars__ = () => {\n${genCssVarsCode(
|
`const __injectCSSVars__ = () => {\n${genCssVarsCode(
|
||||||
cssVars,
|
cssVars,
|
||||||
bindings,
|
bindings,
|
||||||
id
|
id,
|
||||||
|
isProd
|
||||||
)}}\n` +
|
)}}\n` +
|
||||||
`const __setup__ = __default__.setup\n` +
|
`const __setup__ = __default__.setup\n` +
|
||||||
`__default__.setup = __setup__\n` +
|
`__default__.setup = __setup__\n` +
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -845,6 +845,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/hash-sum@^1.0.0":
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/hash-sum/-/hash-sum-1.0.0.tgz#838f4e8627887d42b162d05f3d96ca636c2bc504"
|
||||||
|
integrity sha512-FdLBT93h3kcZ586Aee66HPCVJ6qvxVjBlDWNmxSGSbCZe9hTsjRKdSsl4y1T+3zfujxo9auykQMnFsfyHWD7wg==
|
||||||
|
|
||||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
|
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
|
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
|
||||||
@ -3398,6 +3403,11 @@ hash-base@^3.0.0:
|
|||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
|
hash-sum@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a"
|
||||||
|
integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==
|
||||||
|
|
||||||
hash.js@^1.0.0, hash.js@^1.0.3:
|
hash.js@^1.0.0, hash.js@^1.0.3:
|
||||||
version "1.1.7"
|
version "1.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
|
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
|
||||||
|
Loading…
Reference in New Issue
Block a user