"@rollup/plugin-json": "^4.0.0",
"@rollup/plugin-node-resolve": "^9.0.0",
"@rollup/plugin-replace": "^2.2.1",
+ "@types/hash-sum": "^1.0.0",
"@types/jest": "^26.0.0",
"@types/node": "^14.10.1",
"@types/puppeteer": "^2.0.0",
assertCode(content)
})
- test('should rewrite CSS vars in scoped mode', () => {
+ test('should rewrite CSS vars in compileStyle', () => {
const { code } = compileStyle({
source: `.foo {
color: v-bind(color);
`)
})
+ 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', () => {
test('<script> w/ no default export', () => {
assertCode(
"@vue/shared": "3.0.2",
"consolidate": "^0.16.0",
"estree-walker": "^2.0.1",
+ "hash-sum": "^2.0.0",
"lru-cache": "^5.1.1",
"magic-string": "^0.25.7",
"merge-source-map": "^1.1.0",
* This must be consistent with the `id` passed to `compileStyle`.
*/
id: string
+ /**
+ * Production mode. Used to determine whether to generate hashed CSS variables
+ */
+ isProd?: boolean
/**
* https://babeljs.io/docs/en/babel-parser#plugins
*/
return {
...script,
content: cssVars.length
- ? injectCssVarsCalls(sfc, cssVars, bindings, scopeId, plugins)
+ ? injectCssVarsCalls(
+ sfc,
+ cssVars,
+ bindings,
+ scopeId,
+ !!options.isProd,
+ plugins
+ )
: script.content,
bindings,
scriptAst
node.body.type === 'ExpressionStatement'
) {
if (enableRefSugar) {
- warnOnce(
- `ref: sugar is still an experimental proposal and is not ` +
- `guaranteed to be a part of <script setup>.\n` +
- `Follow its status at https://github.com/vuejs/rfcs/pull/228.\n` +
- `It's also recommended to pin your vue dependencies to exact versions ` +
- `to avoid breakage.`
- )
+ if (__DEV__ && !__TEST__) {
+ warnOnce(
+ `ref: sugar is still an experimental proposal and is not ` +
+ `guaranteed to be a part of <script setup>.\n` +
+ `Follow its status at https://github.com/vuejs/rfcs/pull/228.\n` +
+ `It's also recommended to pin your vue dependencies to exact versions ` +
+ `to avoid breakage.`
+ )
+ }
s.overwrite(
node.label.start! + startOffset,
node.body.start! + startOffset,
helperImports.add('unref')
s.prependRight(
startOffset,
- `\n${genCssVarsCode(cssVars, bindingMetadata, scopeId)}\n`
+ `\n${genCssVarsCode(
+ cssVars,
+ bindingMetadata,
+ scopeId,
+ !!options.isProd
+ )}\n`
)
}
map?: RawSourceMap
scoped?: boolean
trim?: boolean
+ isProd?: boolean
preprocessLang?: PreprocessLang
preprocessOptions?: any
preprocessCustomRequire?: (id: string) => any
id,
scoped = false,
trim = true,
+ isProd = false,
modules = false,
modulesOptions = {},
preprocessLang,
const source = preProcessedSource ? preProcessedSource.code : options.source
const plugins = (postcssPlugins || []).slice()
- plugins.unshift(cssVarsPlugin(id))
+ plugins.unshift(cssVarsPlugin({ id, isProd }))
if (trim) {
plugins.push(trimPlugin())
}
import { rewriteDefault } from './rewriteDefault'
import { ParserPlugin } from '@babel/parser'
import postcss, { Root } from 'postcss'
+import hash from 'hash-sum'
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 genVarName(id: string, raw: string, isProd: boolean): string {
+ if (isProd) {
+ return hash(id + raw)
+ } else {
+ return `${id}-${raw.replace(/([^\w-])/g, '_')}`
+ }
}
export function parseCssVars(sfc: SFCDescriptor): string[] {
}
// for compileStyle
-export const cssVarsPlugin = postcss.plugin(
+export interface CssVarsPluginOptions {
+ id: string
+ isProd: boolean
+}
+
+export const cssVarsPlugin = postcss.plugin<CssVarsPluginOptions>(
'vue-scoped',
- (id: any) => (root: Root) => {
+ opts => (root: Root) => {
+ const { id, isProd } = opts!
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)})`
+ return `var(--${genVarName(shortId, $1 || $2 || $3, isProd)})`
})
}
})
export function genCssVarsCode(
vars: string[],
bindings: BindingMetadata,
- id: string
+ id: string,
+ isProd: boolean
) {
const varsExp = `{\n ${vars
- .map(v => `"${id}-${convertCssVarCasing(v)}": (${v})`)
+ .map(v => `"${genVarName(id, v, isProd)}": (${v})`)
.join(',\n ')}\n}`
const exp = createSimpleExpression(varsExp, false)
const context = createTransformContext(createRoot([]), {
cssVars: string[],
bindings: BindingMetadata,
id: string,
+ isProd: boolean,
parserPlugins: ParserPlugin[]
): string {
const script = rewriteDefault(
`const __injectCSSVars__ = () => {\n${genCssVarsCode(
cssVars,
bindings,
- id
+ id,
+ isProd
)}}\n` +
`const __setup__ = __default__.setup\n` +
`__default__.setup = __setup__\n` +
dependencies:
"@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":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
inherits "^2.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:
version "1.1.7"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"