]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: new cssVars SSR integration + fix cssVars SSR injection for suspense
authorEvan You <yyx990803@gmail.com>
Tue, 17 Nov 2020 23:54:47 +0000 (18:54 -0500)
committerEvan You <yyx990803@gmail.com>
Tue, 17 Nov 2020 23:54:47 +0000 (18:54 -0500)
15 files changed:
packages/compiler-core/src/options.ts
packages/compiler-core/src/transforms/transformElement.ts
packages/compiler-sfc/src/compileScript.ts
packages/compiler-sfc/src/compileTemplate.ts
packages/compiler-sfc/src/cssVars.ts
packages/compiler-sfc/src/index.ts
packages/compiler-sfc/src/stylePluginScoped.ts
packages/compiler-sfc/src/warn.ts [new file with mode: 0644]
packages/compiler-ssr/__tests__/ssrInjectCssVars.spec.ts
packages/compiler-ssr/src/runtimeHelpers.ts
packages/compiler-ssr/src/ssrCodegenTransform.ts
packages/compiler-ssr/src/transforms/ssrInjectCssVars.ts
packages/server-renderer/__tests__/ssrResolveCssVars.spec.ts [deleted file]
packages/server-renderer/src/helpers/ssrResolveCssVars.ts [deleted file]
packages/server-renderer/src/index.ts

index 02ac12e62275e3858a92421b356c0e5d1b392d76..bb18faf0e466db8b3f22bbf4470a44bf5fb21d6d 100644 (file)
@@ -166,6 +166,7 @@ export interface TransformOptions extends SharedTransformCodegenOptions {
   scopeId?: string | null
   /**
    * SFC `<style vars>` injection string
+   * Should already be an object expression, e.g. `{ 'xxxx-color': color }`
    * needed to render inline CSS variables on component root
    */
   ssrCssVars?: string
index 905eded29cdb8715c45c679c6bfc7bdd6ac38885..f5f026bef8758194e454d9a3f9977bc64386f385 100644 (file)
@@ -245,7 +245,7 @@ export function resolveComponentType(
   const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag)
   if (builtIn) {
     // built-ins are simply fallthroughs / have special handling during ssr
-    // no we don't need to import their runtime equivalents
+    // so we don't need to import their runtime equivalents
     if (!ssr) context.helper(builtIn)
     return builtIn
   }
index f4ddd5042b323cc3ce711a38d98505d5032c081a..c9f0932cffee559f362904ca25678e65861f9bc3 100644 (file)
@@ -31,6 +31,7 @@ import {
   injectCssVarsCalls
 } from './cssVars'
 import { compileTemplate, SFCTemplateCompileOptions } from './compileTemplate'
+import { warnOnce } from './warn'
 
 const DEFINE_OPTIONS = 'defineOptions'
 
@@ -65,15 +66,6 @@ export interface SFCScriptCompileOptions {
   templateOptions?: Partial<SFCTemplateCompileOptions>
 }
 
-const hasWarned: Record<string, boolean> = {}
-
-function warnOnce(msg: string) {
-  if (!hasWarned[msg]) {
-    hasWarned[msg] = true
-    console.log(`\x1b[33m[@vue/compiler-sfc] %s\x1b[0m\n`, msg)
-  }
-}
-
 /**
  * Compile `<script setup>`
  * It requires the whole SFC descriptor because we need to handle and merge
index 4f8fc11eecb941c2625a4813882cc84e854c6c9c..21130813347cfce678702af30bbdc5ca039d2d0b 100644 (file)
@@ -22,6 +22,7 @@ import { isObject } from '@vue/shared'
 import * as CompilerDOM from '@vue/compiler-dom'
 import * as CompilerSSR from '@vue/compiler-ssr'
 import consolidate from 'consolidate'
+import { warnOnce } from './warn'
 
 export interface TemplateCompiler {
   compile(template: string, options: CompilerOptions): CodegenResult
@@ -170,6 +171,14 @@ function doCompileTemplate({
     nodeTransforms = [transformAssetUrl, transformSrcset]
   }
 
+  if (ssr && !compilerOptions.ssrCssVars) {
+    warnOnce(
+      `compileTemplate is called with \`ssr: true\` but no ` +
+        `corresponding \`ssrCssVars\` option. The value can be generated by ` +
+        `calling \`generateCssVars(sfcDescriptor, scopeId, isProduction)\`.`
+    )
+  }
+
   let { code, ast, preamble, map } = compiler.compile(source, {
     mode: 'module',
     prefixIdentifiers: true,
index 9252a26b7269556efc286ad4b31969dcb38d11db..7cfcce1f81002b79f3ab3bceded903c2f85ef7b2 100644 (file)
@@ -16,7 +16,30 @@ import hash from 'hash-sum'
 export const CSS_VARS_HELPER = `useCssVars`
 export const cssVarRE = /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g
 
-export function genVarName(id: string, raw: string, isProd: boolean): string {
+/**
+ * Given an SFC descriptor, generate the CSS variables object string that can be
+ * passed to `compileTemplate` as `compilerOptions.ssrCssVars`.
+ * @public
+ */
+export function generateCssVars(
+  sfc: SFCDescriptor,
+  id: string,
+  isProd: boolean
+): string {
+  return genCssVarsFromList(parseCssVars(sfc), id, isProd)
+}
+
+function genCssVarsFromList(
+  vars: string[],
+  id: string,
+  isProd: boolean
+): string {
+  return `{\n  ${vars
+    .map(v => `"${genVarName(id, v, isProd)}": (${v})`)
+    .join(',\n  ')}\n}`
+}
+
+function genVarName(id: string, raw: string, isProd: boolean): string {
   if (isProd) {
     return hash(id + raw)
   } else {
@@ -63,9 +86,7 @@ export function genCssVarsCode(
   id: string,
   isProd: boolean
 ) {
-  const varsExp = `{\n  ${vars
-    .map(v => `"${genVarName(id, v, isProd)}": (${v})`)
-    .join(',\n  ')}\n}`
+  const varsExp = genCssVarsFromList(vars, id, isProd)
   const exp = createSimpleExpression(varsExp, false)
   const context = createTransformContext(createRoot([]), {
     prefixIdentifiers: true,
index 2015dcb6d9c21a2ff287de52d9c56121e0669048..8a373a774764821a505a992fafd1dce62a627c83 100644 (file)
@@ -5,6 +5,7 @@ export { compileStyle, compileStyleAsync } from './compileStyle'
 export { compileScript } from './compileScript'
 export { rewriteDefault } from './rewriteDefault'
 export { generateCodeFrame } from '@vue/compiler-core'
+export { generateCssVars } from './cssVars'
 
 // Types
 export {
index 359fdea3d5c5afe3071d95b56a5cfea71fb78dc4..44265d5ba3133e54b3bc648ffa644227b20faee6 100644 (file)
@@ -1,5 +1,6 @@
 import postcss, { Root } from 'postcss'
 import selectorParser, { Node, Selector } from 'postcss-selector-parser'
+import { warn } from './warn'
 
 const animationNameRE = /^(-\w+-)?animation-name$/
 const animationRE = /^(-\w+-)?animation$/
@@ -35,9 +36,9 @@ export default postcss.plugin('vue-scoped', (id: any) => (root: Root) => {
           ) {
             n.value = ' '
             n.spaces.before = n.spaces.after = ''
-            console.warn(
-              `[@vue/compiler-sfc] the >>> and /deep/ combinators have ` +
-                `been deprecated. Use ::v-deep instead.`
+            warn(
+              `the >>> and /deep/ combinators have been deprecated. ` +
+                `Use :deep() instead.`
             )
             return false
           }
@@ -69,9 +70,9 @@ export default postcss.plugin('vue-scoped', (id: any) => (root: Root) => {
               } else {
                 // DEPRECATED usage
                 // .foo ::v-deep .bar -> .foo[xxxxxxx] .bar
-                console.warn(
-                  `[@vue/compiler-sfc] ::v-deep usage as a combinator has ` +
-                    `been deprecated. Use ::v-deep(<inner-selector>) instead.`
+                warn(
+                  `::v-deep usage as a combinator has ` +
+                    `been deprecated. Use :deep(<inner-selector>) instead.`
                 )
                 const prev = selector.at(selector.index(n) - 1)
                 if (prev && isSpaceCombinator(prev)) {
diff --git a/packages/compiler-sfc/src/warn.ts b/packages/compiler-sfc/src/warn.ts
new file mode 100644 (file)
index 0000000..00a8e3a
--- /dev/null
@@ -0,0 +1,12 @@
+const hasWarned: Record<string, boolean> = {}
+
+export function warnOnce(msg: string) {
+  if (!hasWarned[msg]) {
+    hasWarned[msg] = true
+    warn(msg)
+  }
+}
+
+export function warn(msg: string) {
+  console.warn(`\x1b[33m[@vue/compiler-sfc] ${msg}\x1b[0m\n`)
+}
index ec7010de3dc48407b15b150b5253000aa3fb3088..cfce2226ac36d7fc36ae4e6f559104f2e041b687 100644 (file)
@@ -8,10 +8,10 @@ describe('ssr: inject <style vars>', () => {
       }).code
     ).toMatchInlineSnapshot(`
       "const { mergeProps: _mergeProps } = require(\\"vue\\")
-      const { ssrResolveCssVars: _ssrResolveCssVars, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+      const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent, _attrs) {
-        const _cssVars = _ssrResolveCssVars({ color: _ctx.color })
+        const _cssVars = { style: { color: _ctx.color }}
         _push(\`<div\${_ssrRenderAttrs(_mergeProps(_attrs, _cssVars))}></div>\`)
       }"
     `)
@@ -23,10 +23,10 @@ describe('ssr: inject <style vars>', () => {
         ssrCssVars: `{ color }`
       }).code
     ).toMatchInlineSnapshot(`
-      "const { ssrResolveCssVars: _ssrResolveCssVars, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+      "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent, _attrs) {
-        const _cssVars = _ssrResolveCssVars({ color: _ctx.color })
+        const _cssVars = { style: { color: _ctx.color }}
         _push(\`<!--[--><div\${
           _ssrRenderAttrs(_cssVars)
         }></div><div\${
@@ -43,12 +43,12 @@ describe('ssr: inject <style vars>', () => {
       }).code
     ).toMatchInlineSnapshot(`
       "const { resolveComponent: _resolveComponent } = require(\\"vue\\")
-      const { ssrResolveCssVars: _ssrResolveCssVars, ssrRenderAttrs: _ssrRenderAttrs, ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
+      const { ssrRenderAttrs: _ssrRenderAttrs, ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent, _attrs) {
         const _component_foo = _resolveComponent(\\"foo\\")
 
-        const _cssVars = _ssrResolveCssVars({ color: _ctx.color })
+        const _cssVars = { style: { color: _ctx.color }}
         _push(\`<!--[--><div\${_ssrRenderAttrs(_cssVars)}></div>\`)
         _push(_ssrRenderComponent(_component_foo, _cssVars, null, _parent))
         _push(\`<!--]-->\`)
@@ -63,10 +63,10 @@ describe('ssr: inject <style vars>', () => {
       }).code
     ).toMatchInlineSnapshot(`
       "const { mergeProps: _mergeProps } = require(\\"vue\\")
-      const { ssrResolveCssVars: _ssrResolveCssVars, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+      const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent, _attrs) {
-        const _cssVars = _ssrResolveCssVars({ color: _ctx.color })
+        const _cssVars = { style: { color: _ctx.color }}
         if (_ctx.ok) {
           _push(\`<div\${_ssrRenderAttrs(_mergeProps(_attrs, _cssVars))}></div>\`)
         } else {
@@ -80,19 +80,34 @@ describe('ssr: inject <style vars>', () => {
     `)
   })
 
-  test('w/ scopeId', () => {
+  test('w/ suspense', () => {
     expect(
-      compile(`<div/>`, {
-        ssrCssVars: `{ color }`,
-        scopeId: 'data-v-foo'
-      }).code
+      compile(
+        `<Suspense>
+          <div>ok</div>
+          <template #fallback>
+            <div>fallback</div>
+          </template>
+        </Suspense>`,
+        {
+          ssrCssVars: `{ color }`
+        }
+      ).code
     ).toMatchInlineSnapshot(`
-      "const { mergeProps: _mergeProps } = require(\\"vue\\")
-      const { ssrResolveCssVars: _ssrResolveCssVars, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+      "const { withCtx: _withCtx } = require(\\"vue\\")
+      const { ssrRenderAttrs: _ssrRenderAttrs, ssrRenderSuspense: _ssrRenderSuspense } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent, _attrs) {
-        const _cssVars = _ssrResolveCssVars({ color: _ctx.color }, \\"data-v-foo\\")
-        _push(\`<div\${_ssrRenderAttrs(_mergeProps(_attrs, _cssVars))} data-v-foo></div>\`)
+        const _cssVars = { style: { color: _ctx.color }}
+        _ssrRenderSuspense(_push, {
+          fallback: () => {
+            _push(\`<div\${_ssrRenderAttrs(_cssVars)}>fallback</div>\`)
+          },
+          default: () => {
+            _push(\`<div\${_ssrRenderAttrs(_cssVars)}>ok</div>\`)
+          },
+          _: 1
+        })
       }"
     `)
   })
index ee0b7a2eafdf441746b3312c328bb8025606ee7c..2aa93c0bf1589ba7d5c0b582a69b23573be7e12f 100644 (file)
@@ -16,7 +16,6 @@ export const SSR_RENDER_DYNAMIC_MODEL = Symbol(`ssrRenderDynamicModel`)
 export const SSR_GET_DYNAMIC_MODEL_PROPS = Symbol(`ssrGetDynamicModelProps`)
 export const SSR_RENDER_TELEPORT = Symbol(`ssrRenderTeleport`)
 export const SSR_RENDER_SUSPENSE = Symbol(`ssrRenderSuspense`)
-export const SSR_RESOLVE_CSS_VARS = Symbol(`ssrResolveCssVars`)
 
 export const ssrHelpers = {
   [SSR_INTERPOLATE]: `ssrInterpolate`,
@@ -34,8 +33,7 @@ export const ssrHelpers = {
   [SSR_RENDER_DYNAMIC_MODEL]: `ssrRenderDynamicModel`,
   [SSR_GET_DYNAMIC_MODEL_PROPS]: `ssrGetDynamicModelProps`,
   [SSR_RENDER_TELEPORT]: `ssrRenderTeleport`,
-  [SSR_RENDER_SUSPENSE]: `ssrRenderSuspense`,
-  [SSR_RESOLVE_CSS_VARS]: `ssrResolveCssVars`
+  [SSR_RENDER_SUSPENSE]: `ssrRenderSuspense`
 }
 
 // Note: these are helpers imported from @vue/server-renderer
index eda02822ed8015598e54fce7750ea0b604f35265..949db9c58130e00b571946d18edf25b5db20aa06 100644 (file)
@@ -19,11 +19,7 @@ import {
   createRoot
 } from '@vue/compiler-dom'
 import { isString, escapeHtml } from '@vue/shared'
-import {
-  SSR_INTERPOLATE,
-  ssrHelpers,
-  SSR_RESOLVE_CSS_VARS
-} from './runtimeHelpers'
+import { SSR_INTERPOLATE, ssrHelpers } from './runtimeHelpers'
 import { ssrProcessIf } from './transforms/ssrVIf'
 import { ssrProcessFor } from './transforms/ssrVFor'
 import { ssrProcessSlotOutlet } from './transforms/ssrTransformSlotOutlet'
@@ -40,7 +36,7 @@ import { createSSRCompilerError, SSRErrorCodes } from './errors'
 export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
   const context = createSSRTransformContext(ast, options)
 
-  // inject <style vars> resolution
+  // inject SFC <style> CSS variables
   // we do this instead of inlining the expression to ensure the vars are
   // only resolved once per render
   if (options.ssrCssVars) {
@@ -49,12 +45,7 @@ export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
       createTransformContext(createRoot([]), options)
     )
     context.body.push(
-      createCompoundExpression([
-        `const _cssVars = _${ssrHelpers[SSR_RESOLVE_CSS_VARS]}(`,
-        varsExp,
-        options.scopeId ? `, ${JSON.stringify(options.scopeId)}` : ``,
-        `)`
-      ])
+      createCompoundExpression([`const _cssVars = { style: `, varsExp, `}`])
     )
   }
 
index c848598a286727b8d405b26525ef19e4702d2c35..d5c0daee7cf3f64343cf6b923d41dd21946f3415 100644 (file)
@@ -6,9 +6,9 @@ import {
   createSimpleExpression,
   RootNode,
   TemplateChildNode,
-  findDir
+  findDir,
+  isBuiltInType
 } from '@vue/compiler-dom'
-import { SSR_RESOLVE_CSS_VARS } from '../runtimeHelpers'
 
 export const ssrInjectCssVars: NodeTransform = (node, context) => {
   if (!context.ssrCssVars) {
@@ -27,8 +27,6 @@ export const ssrInjectCssVars: NodeTransform = (node, context) => {
     return
   }
 
-  context.helper(SSR_RESOLVE_CSS_VARS)
-
   if (node.type === NodeTypes.IF_BRANCH) {
     for (const child of node.children) {
       injectCssVars(child)
@@ -45,13 +43,27 @@ function injectCssVars(node: RootNode | TemplateChildNode) {
       node.tagType === ElementTypes.COMPONENT) &&
     !findDir(node, 'for')
   ) {
-    node.props.push({
-      type: NodeTypes.DIRECTIVE,
-      name: 'bind',
-      arg: undefined,
-      exp: createSimpleExpression(`_cssVars`, false),
-      modifiers: [],
-      loc: locStub
-    })
+    if (isBuiltInType(node.tag, 'Suspense')) {
+      for (const child of node.children) {
+        if (
+          child.type === NodeTypes.ELEMENT &&
+          child.tagType === ElementTypes.TEMPLATE
+        ) {
+          // suspense slot
+          child.children.forEach(injectCssVars)
+        } else {
+          injectCssVars(child)
+        }
+      }
+    } else {
+      node.props.push({
+        type: NodeTypes.DIRECTIVE,
+        name: 'bind',
+        arg: undefined,
+        exp: createSimpleExpression(`_cssVars`, false),
+        modifiers: [],
+        loc: locStub
+      })
+    }
   }
 }
diff --git a/packages/server-renderer/__tests__/ssrResolveCssVars.spec.ts b/packages/server-renderer/__tests__/ssrResolveCssVars.spec.ts
deleted file mode 100644 (file)
index c4bccfd..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-import { ssrResolveCssVars } from '../src'
-
-describe('ssr: resolveCssVars', () => {
-  test('should work', () => {
-    expect(ssrResolveCssVars({ color: 'red' })).toMatchObject({
-      style: {
-        '--color': 'red'
-      }
-    })
-  })
-
-  test('should work with scopeId', () => {
-    expect(ssrResolveCssVars({ color: 'red' }, 'scoped')).toMatchObject({
-      style: {
-        '--scoped-color': 'red'
-      }
-    })
-  })
-
-  test('should strip data-v prefix', () => {
-    expect(ssrResolveCssVars({ color: 'red' }, 'data-v-123456')).toMatchObject({
-      style: {
-        '--123456-color': 'red'
-      }
-    })
-  })
-})
diff --git a/packages/server-renderer/src/helpers/ssrResolveCssVars.ts b/packages/server-renderer/src/helpers/ssrResolveCssVars.ts
deleted file mode 100644 (file)
index 7165dcc..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-export function ssrResolveCssVars(
-  source: Record<string, string>,
-  scopeId?: string
-) {
-  const style: Record<string, string> = {}
-  const prefix = scopeId ? `${scopeId.replace(/^data-v-/, '')}-` : ``
-  for (const key in source) {
-    style[`--${prefix}${key}`] = source[key]
-  }
-  return { style }
-}
index 7b59d05224a9c7f07edb160633cd210bfcba4c3c..9c5066e851eca2bd60edbb6e481ee082e2a5f01f 100644 (file)
@@ -18,7 +18,6 @@ export {
 export { ssrInterpolate } from './helpers/ssrInterpolate'
 export { ssrRenderList } from './helpers/ssrRenderList'
 export { ssrRenderSuspense } from './helpers/ssrRenderSuspense'
-export { ssrResolveCssVars } from './helpers/ssrResolveCssVars'
 
 // v-model helpers
 export {