]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: css var injection production mode
authorEvan You <yyx990803@gmail.com>
Tue, 17 Nov 2020 20:58:46 +0000 (15:58 -0500)
committerEvan You <yyx990803@gmail.com>
Tue, 17 Nov 2020 20:58:46 +0000 (15:58 -0500)
package.json
packages/compiler-sfc/__tests__/cssVars.spec.ts
packages/compiler-sfc/package.json
packages/compiler-sfc/src/compileScript.ts
packages/compiler-sfc/src/compileStyle.ts
packages/compiler-sfc/src/cssVars.ts
yarn.lock

index e1a781b2090bc15f978d7a0efe6735c3cd013b37..1345a65b201733dfd675b3d17fca4c4de6ec0014 100644 (file)
@@ -49,6 +49,7 @@
     "@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",
index d8a8d1a6bc4001ba173b94bcda3ea06b4e99a37c..cdd67698955c4e8471bc7036053b222323e22d4d 100644 (file)
@@ -52,7 +52,7 @@ describe('CSS vars injection', () => {
     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);
@@ -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', () => {
     test('<script> w/ no default export', () => {
       assertCode(
index 3e2fed2353faea7fa032cf23b2ab720289980ae0..f041dde1e1cd8e94a381be2290b4a832f3fe4780 100644 (file)
@@ -43,6 +43,7 @@
     "@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",
index b13729c1485731011366b0efb429767555c3dfad..f4ddd5042b323cc3ce711a38d98505d5032c081a 100644 (file)
@@ -40,6 +40,10 @@ export interface SFCScriptCompileOptions {
    * 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
    */
@@ -128,7 +132,14 @@ export function compileScript(
       return {
         ...script,
         content: cssVars.length
-          ? injectCssVarsCalls(sfc, cssVars, bindings, scopeId, plugins)
+          ? injectCssVarsCalls(
+              sfc,
+              cssVars,
+              bindings,
+              scopeId,
+              !!options.isProd,
+              plugins
+            )
           : script.content,
         bindings,
         scriptAst
@@ -511,13 +522,15 @@ export function compileScript(
       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,
@@ -800,7 +813,12 @@ export function compileScript(
     helperImports.add('unref')
     s.prependRight(
       startOffset,
-      `\n${genCssVarsCode(cssVars, bindingMetadata, scopeId)}\n`
+      `\n${genCssVarsCode(
+        cssVars,
+        bindingMetadata,
+        scopeId,
+        !!options.isProd
+      )}\n`
     )
   }
 
index 529682fbe8122bebeaa768b939636763a2580902..eea553d4ec270484afbda00adf5b2c35d4c5920f 100644 (file)
@@ -23,6 +23,7 @@ export interface SFCStyleCompileOptions {
   map?: RawSourceMap
   scoped?: boolean
   trim?: boolean
+  isProd?: boolean
   preprocessLang?: PreprocessLang
   preprocessOptions?: any
   preprocessCustomRequire?: (id: string) => any
@@ -82,6 +83,7 @@ export function doCompileStyle(
     id,
     scoped = false,
     trim = true,
+    isProd = false,
     modules = false,
     modulesOptions = {},
     preprocessLang,
@@ -94,7 +96,7 @@ export function doCompileStyle(
   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())
   }
index bf82af0842aa1e9c869d058f2db6467eb357de98..9252a26b7269556efc286ad4b31969dcb38d11db 100644 (file)
@@ -11,12 +11,17 @@ import { SFCDescriptor } from './parse'
 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[] {
@@ -31,15 +36,21 @@ 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)})`
         })
       }
     })
@@ -49,10 +60,11 @@ export const cssVarsPlugin = postcss.plugin(
 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([]), {
@@ -82,6 +94,7 @@ export function injectCssVarsCalls(
   cssVars: string[],
   bindings: BindingMetadata,
   id: string,
+  isProd: boolean,
   parserPlugins: ParserPlugin[]
 ): string {
   const script = rewriteDefault(
@@ -96,7 +109,8 @@ export function injectCssVarsCalls(
     `const __injectCSSVars__ = () => {\n${genCssVarsCode(
       cssVars,
       bindings,
-      id
+      id,
+      isProd
     )}}\n` +
     `const __setup__ = __default__.setup\n` +
     `__default__.setup = __setup__\n` +
index 358fb854e4d31a103723d4d99afa735668ca8ae0..22a340511ef749f1b49f8af8c1aad09e647060a2 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
   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"
@@ -3398,6 +3403,11 @@ hash-base@^3.0.0:
     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"