]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: more consistent compiler-sfc usage + inline mode for ssr
authorEvan You <yyx990803@gmail.com>
Fri, 20 Nov 2020 19:22:51 +0000 (14:22 -0500)
committerEvan You <yyx990803@gmail.com>
Fri, 20 Nov 2020 19:23:09 +0000 (14:23 -0500)
18 files changed:
packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap
packages/compiler-core/__tests__/scopeId.spec.ts
packages/compiler-core/src/codegen.ts
packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
packages/compiler-sfc/__tests__/compileScript.spec.ts
packages/compiler-sfc/__tests__/compileStyle.spec.ts
packages/compiler-sfc/__tests__/compileTemplate.spec.ts
packages/compiler-sfc/src/compileScript.ts
packages/compiler-sfc/src/compileStyle.ts
packages/compiler-sfc/src/compileTemplate.ts
packages/compiler-sfc/src/cssVars.ts
packages/compiler-sfc/src/index.ts
packages/compiler-ssr/__tests__/ssrScopeId.spec.ts
packages/runtime-core/src/component.ts
packages/runtime-core/src/componentOptions.ts
packages/runtime-dom/src/helpers/useCssVars.ts
packages/server-renderer/src/render.ts
rollup.config.js

index 8d7613eec58199eb74e80beb6aeafdf4483b2397..e1408a7a4539b2113dfad233f539db06dd99d80f 100644 (file)
@@ -9,7 +9,7 @@ const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"hello\\", -1 /*
 const _hoisted_2 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"world\\", -1 /* HOISTED */)
 _popScopeId()
 
-export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) {
+export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
   return (_openBlock(), _createBlock(\\"div\\", null, [
     _hoisted_1,
     _createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */),
@@ -22,7 +22,7 @@ exports[`scopeId compiler support should wrap default slot 1`] = `
 "import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
 const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
 
-export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) {
+export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
   const _component_Child = _resolveComponent(\\"Child\\")
 
   return (_openBlock(), _createBlock(_component_Child, null, {
@@ -38,7 +38,7 @@ exports[`scopeId compiler support should wrap dynamic slots 1`] = `
 "import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
 const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
 
-export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) {
+export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
   const _component_Child = _resolveComponent(\\"Child\\")
 
   return (_openBlock(), _createBlock(_component_Child, null, _createSlots({ _: 2 }, [
@@ -66,7 +66,7 @@ exports[`scopeId compiler support should wrap named slots 1`] = `
 "import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
 const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
 
-export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) {
+export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
   const _component_Child = _resolveComponent(\\"Child\\")
 
   return (_openBlock(), _createBlock(_component_Child, null, {
@@ -85,7 +85,7 @@ exports[`scopeId compiler support should wrap render function 1`] = `
 "import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
 const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
 
-export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) {
+export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
   return (_openBlock(), _createBlock(\\"div\\"))
 })"
 `;
index 4ca5a585e2c7201e719e803bf6bc934707c554fb..710c65bff8d27da334ab4b124d122f9a7c1df14d 100644 (file)
@@ -22,7 +22,7 @@ describe('scopeId compiler support', () => {
     expect(ast.helpers).toContain(WITH_SCOPE_ID)
     expect(code).toMatch(`const _withId = /*#__PURE__*/_withScopeId("test")`)
     expect(code).toMatch(
-      `export const render = /*#__PURE__*/_withId(function render(`
+      `export const render = /*#__PURE__*/_withId((_ctx, _cache) => {`
     )
     expect(code).toMatchSnapshot()
   })
index a92f2597b2a68eaa0bb5d7a94676f7e0c5d348f8..20f7e33ec880a97d5022d617540f91243cf92b5d 100644 (file)
@@ -203,7 +203,7 @@ export function generate(
   const hasHelpers = ast.helpers.length > 0
   const useWithBlock = !prefixIdentifiers && mode !== 'module'
   const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module'
-  const isSetupInlined = !!options.inline
+  const isSetupInlined = !__BROWSER__ && !!options.inline
 
   // preambles
   // in setup() inline mode, the preamble is generated in a sub context
@@ -217,6 +217,8 @@ export function generate(
     genFunctionPreamble(ast, preambleContext)
   }
 
+  // enter render function
+  const functionName = ssr ? `ssrRender` : `render`
   const args = ssr ? ['_ctx', '_push', '_parent', '_attrs'] : ['_ctx', '_cache']
   if (!__BROWSER__ && options.bindingMetadata && !options.inline) {
     // binding optimization args
@@ -226,24 +228,18 @@ export function generate(
     !__BROWSER__ && options.isTS
       ? args.map(arg => `${arg}: any`).join(',')
       : args.join(', ')
-  // enter render function
-  if (!ssr) {
+
+  if (genScopeId) {
     if (isSetupInlined) {
-      if (genScopeId) {
-        push(`${PURE_ANNOTATION}_withId(`)
-      }
-      push(`(${signature}) => {`)
+      push(`${PURE_ANNOTATION}_withId(`)
     } else {
-      if (genScopeId) {
-        push(`const render = ${PURE_ANNOTATION}_withId(`)
-      }
-      push(`function render(${signature}) {`)
+      push(`const ${functionName} = ${PURE_ANNOTATION}_withId(`)
     }
+  }
+  if (isSetupInlined || genScopeId) {
+    push(`(${signature}) => {`)
   } else {
-    if (genScopeId) {
-      push(`const ssrRender = ${PURE_ANNOTATION}_withId(`)
-    }
-    push(`function ssrRender(${signature}) {`)
+    push(`function ${functionName}(${signature}) {`)
   }
   indent()
 
index c2407f8aca15f1aae5a4d81c65e805cceff8ca1f..80a3bfa6c9e99b0bdf81f4a61cf403ba078b37d5 100644 (file)
@@ -201,6 +201,40 @@ return (_ctx, _cache) => {
 }"
 `;
 
+exports[`SFC compile <script setup> inlineTemplate mode ssr codegen 1`] = `
+"import { useCssVars as _useCssVars, unref as _unref } from 'vue'
+import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from \\"@vue/server-renderer\\"
+
+import { ref } from 'vue'
+        
+export default {
+  expose: [],
+  __ssrInlineRender: true,
+  setup(__props) {
+
+_useCssVars(_ctx => ({
+  \\"xxxxxxxx-count\\": (count.value)
+}))
+
+        const count = ref(0)
+        
+return (_ctx, _push, _parent, _attrs) => {
+  const _cssVars = { style: {
+  \\"xxxxxxxx-count\\": (count.value)
+}}
+  _push(\`<!--[--><div\${
+    _ssrRenderAttrs(_cssVars)
+  }>\${
+    _ssrInterpolate(count.value)
+  }</div><div\${
+    _ssrRenderAttrs(_cssVars)
+  }>static</div><!--]-->\`)
+}
+}
+
+}"
+`;
+
 exports[`SFC compile <script setup> inlineTemplate mode template assignment expression codegen 1`] = `
 "import { createVNode as _createVNode, isRef as _isRef, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
 
index 064a026d122b63df963b38d94daa43f743e35184..2e4f621b4e1396dab6eeddbf1b19043ef0206e88 100644 (file)
@@ -1,4 +1,4 @@
-import { BindingTypes } from '@vue/compiler-dom/src'
+import { BindingTypes } from '@vue/compiler-dom'
 import { compileSFCScript as compile, assertCode } from './utils'
 
 describe('SFC compile <script setup>', () => {
@@ -297,6 +297,34 @@ const bar = 1
       expect(content).toMatch(`{ lett: lett } = val`)
       assertCode(content)
     })
+
+    test('ssr codegen', () => {
+      const { content } = compile(
+        `
+        <script setup>
+        import { ref } from 'vue'
+        const count = ref(0)
+        </script>
+        <template>
+          <div>{{ count }}</div>
+          <div>static</div>
+        </template>
+        <style>
+        div { color: v-bind(count) }
+        </style>
+        `,
+        {
+          inlineTemplate: true,
+          templateOptions: {
+            ssr: true
+          }
+        }
+      )
+      expect(content).toMatch(`\n  __ssrInlineRender: true,\n`)
+      expect(content).toMatch(`return (_ctx, _push`)
+      expect(content).toMatch(`ssrInterpolate`)
+      assertCode(content)
+    })
   })
 
   describe('with TypeScript', () => {
index a1b3b21eeba0be84590cdc9b3868c1e319b3da6d..8e5db0704cf1cc22e0e02157e7b42e6cc8a0a1b0 100644 (file)
@@ -16,7 +16,7 @@ export function compileScoped(
   const res = compileStyle({
     source,
     filename: 'test.css',
-    id: 'test',
+    id: 'data-v-test',
     scoped: true,
     ...options
   })
@@ -32,61 +32,61 @@ export function compileScoped(
 describe('SFC scoped CSS', () => {
   test('simple selectors', () => {
     expect(compileScoped(`h1 { color: red; }`)).toMatch(
-      `h1[test] { color: red;`
+      `h1[data-v-test] { color: red;`
     )
     expect(compileScoped(`.foo { color: red; }`)).toMatch(
-      `.foo[test] { color: red;`
+      `.foo[data-v-test] { color: red;`
     )
   })
 
   test('descendent selector', () => {
     expect(compileScoped(`h1 .foo { color: red; }`)).toMatch(
-      `h1 .foo[test] { color: red;`
+      `h1 .foo[data-v-test] { color: red;`
     )
   })
 
   test('multiple selectors', () => {
     expect(compileScoped(`h1 .foo, .bar, .baz { color: red; }`)).toMatch(
-      `h1 .foo[test], .bar[test], .baz[test] { color: red;`
+      `h1 .foo[data-v-test], .bar[data-v-test], .baz[data-v-test] { color: red;`
     )
   })
 
   test('pseudo class', () => {
     expect(compileScoped(`.foo:after { color: red; }`)).toMatch(
-      `.foo[test]:after { color: red;`
+      `.foo[data-v-test]:after { color: red;`
     )
   })
 
   test('pseudo element', () => {
     expect(compileScoped(`::selection { display: none; }`)).toMatch(
-      '[test]::selection {'
+      '[data-v-test]::selection {'
     )
   })
 
   test('spaces before pseudo element', () => {
     const code = compileScoped(`.abc, ::selection { color: red; }`)
-    expect(code).toMatch('.abc[test],')
-    expect(code).toMatch('[test]::selection {')
+    expect(code).toMatch('.abc[data-v-test],')
+    expect(code).toMatch('[data-v-test]::selection {')
   })
 
   test('::v-deep', () => {
     expect(compileScoped(`:deep(.foo) { color: red; }`)).toMatchInlineSnapshot(`
-      "[test] .foo { color: red;
+      "[data-v-test] .foo { color: red;
       }"
     `)
     expect(compileScoped(`::v-deep(.foo) { color: red; }`))
       .toMatchInlineSnapshot(`
-      "[test] .foo { color: red;
+      "[data-v-test] .foo { color: red;
       }"
     `)
     expect(compileScoped(`::v-deep(.foo .bar) { color: red; }`))
       .toMatchInlineSnapshot(`
-      "[test] .foo .bar { color: red;
+      "[data-v-test] .foo .bar { color: red;
       }"
     `)
     expect(compileScoped(`.baz .qux ::v-deep(.foo .bar) { color: red; }`))
       .toMatchInlineSnapshot(`
-      ".baz .qux[test] .foo .bar { color: red;
+      ".baz .qux[data-v-test] .foo .bar { color: red;
       }"
     `)
   })
@@ -94,22 +94,22 @@ describe('SFC scoped CSS', () => {
   test('::v-slotted', () => {
     expect(compileScoped(`:slotted(.foo) { color: red; }`))
       .toMatchInlineSnapshot(`
-    ".foo[test-s] { color: red;
+    ".foo[data-v-test-s] { color: red;
     }"
   `)
     expect(compileScoped(`::v-slotted(.foo) { color: red; }`))
       .toMatchInlineSnapshot(`
-      ".foo[test-s] { color: red;
+      ".foo[data-v-test-s] { color: red;
       }"
     `)
     expect(compileScoped(`::v-slotted(.foo .bar) { color: red; }`))
       .toMatchInlineSnapshot(`
-      ".foo .bar[test-s] { color: red;
+      ".foo .bar[data-v-test-s] { color: red;
       }"
     `)
     expect(compileScoped(`.baz .qux ::v-slotted(.foo .bar) { color: red; }`))
       .toMatchInlineSnapshot(`
-      ".baz .qux .foo .bar[test-s] { color: red;
+      ".baz .qux .foo .bar[data-v-test-s] { color: red;
       }"
     `)
   })
@@ -142,7 +142,7 @@ describe('SFC scoped CSS', () => {
     expect(compileScoped(`@media print { .foo { color: red }}`))
       .toMatchInlineSnapshot(`
       "@media print {
-      .foo[test] { color: red
+      .foo[data-v-test] { color: red
       }}"
     `)
   })
@@ -151,7 +151,7 @@ describe('SFC scoped CSS', () => {
     expect(compileScoped(`@supports(display: grid) { .foo { display: grid }}`))
       .toMatchInlineSnapshot(`
       "@supports(display: grid) {
-      .foo[test] { display: grid
+      .foo[data-v-test] { display: grid
       }}"
     `)
   })
@@ -222,7 +222,7 @@ describe('SFC scoped CSS', () => {
   // vue-loader/#1370
   test('spaces after selector', () => {
     expect(compileScoped(`.foo , .bar { color: red; }`)).toMatchInlineSnapshot(`
-      ".foo[test], .bar[test] { color: red;
+      ".foo[data-v-test], .bar[data-v-test] { color: red;
       }"
     `)
   })
@@ -231,12 +231,12 @@ describe('SFC scoped CSS', () => {
     test('::v-deep as combinator', () => {
       expect(compileScoped(`::v-deep .foo { color: red; }`))
         .toMatchInlineSnapshot(`
-        "[test] .foo { color: red;
+        "[data-v-test] .foo { color: red;
         }"
       `)
       expect(compileScoped(`.bar ::v-deep .foo { color: red; }`))
         .toMatchInlineSnapshot(`
-        ".bar[test] .foo { color: red;
+        ".bar[data-v-test] .foo { color: red;
         }"
       `)
       expect(
@@ -247,7 +247,7 @@ describe('SFC scoped CSS', () => {
     test('>>> (deprecated syntax)', () => {
       const code = compileScoped(`>>> .foo { color: red; }`)
       expect(code).toMatchInlineSnapshot(`
-        "[test] .foo { color: red;
+        "[data-v-test] .foo { color: red;
         }"
       `)
       expect(
@@ -258,7 +258,7 @@ describe('SFC scoped CSS', () => {
     test('/deep/ (deprecated syntax)', () => {
       const code = compileScoped(`/deep/ .foo { color: red; }`)
       expect(code).toMatchInlineSnapshot(`
-        "[test] .foo { color: red;
+        "[data-v-test] .foo { color: red;
         }"
       `)
       expect(
index 2a456d8ef5b702b32c383990e0f90d7fe2473785..576240b5e1d81dbc8aad1b156e541ca415053920 100644 (file)
@@ -1,10 +1,20 @@
-import { compileTemplate } from '../src/compileTemplate'
+import {
+  compileTemplate,
+  SFCTemplateCompileOptions
+} from '../src/compileTemplate'
 import { parse, SFCTemplateBlock } from '../src/parse'
 
+function compile(opts: Omit<SFCTemplateCompileOptions, 'id'>) {
+  return compileTemplate({
+    ...opts,
+    id: ''
+  })
+}
+
 test('should work', () => {
   const source = `<div><p>{{ render }}</p></div>`
 
-  const result = compileTemplate({ filename: 'example.vue', source })
+  const result = compile({ filename: 'example.vue', source })
 
   expect(result.errors.length).toBe(0)
   expect(result.source).toBe(source)
@@ -25,7 +35,7 @@ body
     { filename: 'example.vue', sourceMap: true }
   ).descriptor.template as SFCTemplateBlock
 
-  const result = compileTemplate({
+  const result = compile({
     filename: 'example.vue',
     source: template.content,
     preprocessLang: template.lang
@@ -40,7 +50,7 @@ test('warn missing preprocessor', () => {
     sourceMap: true
   }).descriptor.template as SFCTemplateBlock
 
-  const result = compileTemplate({
+  const result = compile({
     filename: 'example.vue',
     source: template.content,
     preprocessLang: template.lang
@@ -52,7 +62,7 @@ test('warn missing preprocessor', () => {
 test('transform asset url options', () => {
   const input = { source: `<foo bar="~baz"/>`, filename: 'example.vue' }
   // Object option
-  const { code: code1 } = compileTemplate({
+  const { code: code1 } = compile({
     ...input,
     transformAssetUrls: {
       tags: { foo: ['bar'] }
@@ -61,7 +71,7 @@ test('transform asset url options', () => {
   expect(code1).toMatch(`import _imports_0 from 'baz'\n`)
 
   // legacy object option (direct tags config)
-  const { code: code2 } = compileTemplate({
+  const { code: code2 } = compile({
     ...input,
     transformAssetUrls: {
       foo: ['bar']
@@ -70,7 +80,7 @@ test('transform asset url options', () => {
   expect(code2).toMatch(`import _imports_0 from 'baz'\n`)
 
   // false option
-  const { code: code3 } = compileTemplate({
+  const { code: code3 } = compile({
     ...input,
     transformAssetUrls: false
   })
@@ -87,7 +97,7 @@ test('source map', () => {
     { filename: 'example.vue', sourceMap: true }
   ).descriptor.template as SFCTemplateBlock
 
-  const result = compileTemplate({
+  const result = compile({
     filename: 'example.vue',
     source: template.content
   })
@@ -96,7 +106,7 @@ test('source map', () => {
 })
 
 test('template errors', () => {
-  const result = compileTemplate({
+  const result = compile({
     filename: 'example.vue',
     source: `<div :foo
       :bar="a[" v-model="baz"/>`
@@ -114,7 +124,7 @@ test('preprocessor errors', () => {
     { filename: 'example.vue', sourceMap: true }
   ).descriptor.template as SFCTemplateBlock
 
-  const result = compileTemplate({
+  const result = compile({
     filename: 'example.vue',
     source: template.content,
     preprocessLang: template.lang
index f0f2b5112a9356c6defca455d8e425257832cd21..2e06b5fd6ccab08d9df0bf339bbede699381113c 100644 (file)
@@ -167,6 +167,7 @@ export function compileScript(
   let optionsArg: ObjectExpression | undefined
   let optionsType: TSTypeLiteral | undefined
   let hasAwait = false
+  let hasInlinedSsrRenderFn = false
   // context types to generate
   let propsType = `{}`
   let emitType = `(e: string, ...args: any[]) => void`
@@ -820,15 +821,24 @@ export function compileScript(
   // 10. generate return statement
   let returned
   if (options.inlineTemplate) {
-    if (sfc.template) {
+    if (sfc.template && !sfc.template.src) {
+      if (options.templateOptions && options.templateOptions.ssr) {
+        hasInlinedSsrRenderFn = true
+      }
       // inline render function mode - we are going to compile the template and
       // inline it right here
       const { code, ast, preamble, tips, errors } = compileTemplate({
-        ...options.templateOptions,
         filename,
         source: sfc.template.content,
         inMap: sfc.template.map,
+        ...options.templateOptions,
+        id: scopeId,
+        scoped: sfc.styles.some(s => s.scoped),
+        isProd: options.isProd,
+        ssrCssVars: sfc.cssVars,
         compilerOptions: {
+          ...(options.templateOptions &&
+            options.templateOptions.compilerOptions),
           inline: true,
           isTS,
           bindingMetadata
@@ -883,6 +893,9 @@ export function compileScript(
   // 11. finalize default export
   // expose: [] makes <script setup> components "closed" by default.
   let runtimeOptions = `\n  expose: [],`
+  if (hasInlinedSsrRenderFn) {
+    runtimeOptions += `\n  __ssrInlineRender: true,`
+  }
   if (optionsArg) {
     runtimeOptions += `\n  ${scriptSetup.content
       .slice(optionsArg.start! + 1, optionsArg.end! - 1)
index eea553d4ec270484afbda00adf5b2c35d4c5920f..f9b7cbe417d49237e936ea193b38d32567b5a007 100644 (file)
@@ -20,15 +20,19 @@ export interface SFCStyleCompileOptions {
   source: string
   filename: string
   id: string
-  map?: RawSourceMap
   scoped?: boolean
   trim?: boolean
   isProd?: boolean
+  inMap?: RawSourceMap
   preprocessLang?: PreprocessLang
   preprocessOptions?: any
   preprocessCustomRequire?: (id: string) => any
   postcssOptions?: any
   postcssPlugins?: any[]
+  /**
+   * @deprecated
+   */
+  map?: RawSourceMap
 }
 
 export interface SFCAsyncStyleCompileOptions extends SFCStyleCompileOptions {
@@ -92,16 +96,21 @@ export function doCompileStyle(
   } = options
   const preprocessor = preprocessLang && processors[preprocessLang]
   const preProcessedSource = preprocessor && preprocess(options, preprocessor)
-  const map = preProcessedSource ? preProcessedSource.map : options.map
+  const map = preProcessedSource
+    ? preProcessedSource.map
+    : options.inMap || options.map
   const source = preProcessedSource ? preProcessedSource.code : options.source
 
+  const shortId = id.replace(/^data-v-/, '')
+  const longId = `data-v-${shortId}`
+
   const plugins = (postcssPlugins || []).slice()
-  plugins.unshift(cssVarsPlugin({ id, isProd }))
+  plugins.unshift(cssVarsPlugin({ id: shortId, isProd }))
   if (trim) {
     plugins.push(trimPlugin())
   }
   if (scoped) {
-    plugins.push(scopedPlugin(id))
+    plugins.push(scopedPlugin(longId))
   }
   let cssModules: Record<string, string> | undefined
   if (modules) {
index 9a99c7a09e16ccc8325ae0b33794681c633be636..36a86461386fe14dbe3620b68ee9f945b9c123bd 100644 (file)
@@ -23,6 +23,7 @@ import * as CompilerDOM from '@vue/compiler-dom'
 import * as CompilerSSR from '@vue/compiler-ssr'
 import consolidate from 'consolidate'
 import { warnOnce } from './warn'
+import { genCssVarsFromList } from './cssVars'
 
 export interface TemplateCompiler {
   compile(template: string, options: CompilerOptions): CodegenResult
@@ -42,7 +43,11 @@ export interface SFCTemplateCompileResults {
 export interface SFCTemplateCompileOptions {
   source: string
   filename: string
+  id: string
+  scoped?: boolean
+  isProd?: boolean
   ssr?: boolean
+  ssrCssVars?: string[]
   inMap?: RawSourceMap
   compiler?: TemplateCompiler
   compilerOptions?: CompilerOptions
@@ -151,9 +156,13 @@ export function compileTemplate(
 
 function doCompileTemplate({
   filename,
+  id,
+  scoped,
   inMap,
   source,
   ssr = false,
+  ssrCssVars,
+  isProd = false,
   compiler = ssr ? (CompilerSSR as TemplateCompiler) : CompilerDOM,
   compilerOptions = {},
   transformAssetUrls
@@ -171,19 +180,30 @@ function doCompileTemplate({
     nodeTransforms = [transformAssetUrl, transformSrcset]
   }
 
-  if (ssr && compilerOptions.ssrCssVars == null) {
+  if (ssr && !ssrCssVars) {
     warnOnce(
       `compileTemplate is called with \`ssr: true\` but no ` +
-        `corresponding \`ssrCssVars\` option. The value can be generated by ` +
-        `calling \`generateCssVars(sfcDescriptor, scopeId, isProduction)\`.`
+        `corresponding \`cssVars\` option.\`.`
     )
   }
+  if (!id) {
+    warnOnce(`compileTemplate now requires the \`id\` option.\`.`)
+    id = ''
+  }
+
+  const shortId = id.replace(/^data-v-/, '')
+  const longId = `data-v-${shortId}`
 
   let { code, ast, preamble, map } = compiler.compile(source, {
     mode: 'module',
     prefixIdentifiers: true,
     hoistStatic: true,
     cacheHandlers: true,
+    ssrCssVars:
+      ssr && ssrCssVars && ssrCssVars.length
+        ? genCssVarsFromList(ssrCssVars, shortId, isProd)
+        : '',
+    scopeId: scoped ? longId : undefined,
     ...compilerOptions,
     nodeTransforms: nodeTransforms.concat(compilerOptions.nodeTransforms || []),
     filename,
index 69ee7583dfe1c896e3b9347dc9fa2431ed820a9b..2028c399294227352df6dd3702abba7512261a84 100644 (file)
@@ -16,26 +16,13 @@ import hash from 'hash-sum'
 export const CSS_VARS_HELPER = `useCssVars`
 export const cssVarRE = /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g
 
-/**
- * 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 sfc.cssVars.length ? genCssVarsFromList(sfc.cssVars, id, isProd) : ''
-}
-
-function genCssVarsFromList(
+export function genCssVarsFromList(
   vars: string[],
   id: string,
   isProd: boolean
 ): string {
   return `{\n  ${vars
-    .map(v => `"${genVarName(id, v, isProd)}": (${v})`)
+    .map(key => `"${genVarName(id, key, isProd)}": (${key})`)
     .join(',\n  ')}\n}`
 }
 
@@ -68,12 +55,11 @@ export const cssVarsPlugin = postcss.plugin<CssVarsPluginOptions>(
   'vue-scoped',
   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(--${genVarName(shortId, $1 || $2 || $3, isProd)})`
+          return `var(--${genVarName(id, $1 || $2 || $3, isProd)})`
         })
       }
     })
index 8a373a774764821a505a992fafd1dce62a627c83..2015dcb6d9c21a2ff287de52d9c56121e0669048 100644 (file)
@@ -5,7 +5,6 @@ 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 03755ab3fb02d51bfc20fe2cebff50d3dd4854f1..f680fcea9524d08cf2f13aadfefd8e2a9a047c37 100644 (file)
@@ -14,7 +14,7 @@ describe('ssr: scopeId', () => {
       import { ssrRenderAttrs as _ssrRenderAttrs } from \\"@vue/server-renderer\\"
       const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
 
-      export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
+      export const ssrRender = /*#__PURE__*/_withId((_ctx, _push, _parent, _attrs) => {
         _push(\`<div\${_ssrRenderAttrs(_attrs)} data-v-xxxxxxx><span data-v-xxxxxxx>hello</span></div>\`)
       })"
     `)
@@ -32,7 +32,7 @@ describe('ssr: scopeId', () => {
       import { ssrRenderComponent as _ssrRenderComponent } from \\"@vue/server-renderer\\"
       const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
 
-      export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
+      export const ssrRender = /*#__PURE__*/_withId((_ctx, _push, _parent, _attrs) => {
         const _component_foo = _resolveComponent(\\"foo\\")
 
         _push(_ssrRenderComponent(_component_foo, _attrs, {
@@ -62,7 +62,7 @@ describe('ssr: scopeId', () => {
       import { ssrRenderComponent as _ssrRenderComponent } from \\"@vue/server-renderer\\"
       const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
 
-      export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
+      export const ssrRender = /*#__PURE__*/_withId((_ctx, _push, _parent, _attrs) => {
         const _component_foo = _resolveComponent(\\"foo\\")
 
         _push(_ssrRenderComponent(_component_foo, _attrs, {
@@ -92,7 +92,7 @@ describe('ssr: scopeId', () => {
       import { ssrRenderComponent as _ssrRenderComponent } from \\"@vue/server-renderer\\"
       const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
 
-      export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
+      export const ssrRender = /*#__PURE__*/_withId((_ctx, _push, _parent, _attrs) => {
         const _component_foo = _resolveComponent(\\"foo\\")
         const _component_bar = _resolveComponent(\\"bar\\")
 
index 61949c4c5ee61da2c26a62716e455a3cfe1593e0..a29a0aeaf85da7130020e797b83096615cb912b6 100644 (file)
@@ -223,6 +223,11 @@ export interface ComponentInternalInstance {
    * @internal
    */
   render: InternalRenderFunction | null
+  /**
+   * SSR render function
+   * @internal
+   */
+  ssrRender?: Function | null
   /**
    * Object containing values this component provides for its descendents
    * @internal
@@ -610,7 +615,13 @@ export function handleSetupResult(
 ) {
   if (isFunction(setupResult)) {
     // setup returned an inline render function
-    instance.render = setupResult as InternalRenderFunction
+    if (!__BROWSER__ && (instance.type as ComponentOptions).__ssrInlineRender) {
+      // when the function's name is `ssrRender` (compiled by SFC inline mode),
+      // set it as ssrRender instead.
+      instance.ssrRender = setupResult
+    } else {
+      instance.render = setupResult as InternalRenderFunction
+    }
   } else if (isObject(setupResult)) {
     if (__DEV__ && isVNode(setupResult)) {
       warn(
index dd95e29ad76dbe1cfac604c5461ac3d1ce6d62bd..52ec1004bd8ea9c79c9e773fa196673a47a1a30c 100644 (file)
@@ -121,7 +121,6 @@ export interface ComponentOptionsBase<
   /**
    * SSR only. This is produced by compiler-ssr and attached in compiler-sfc
    * not user facing, so the typing is lax and for test only.
-   *
    * @internal
    */
   ssrRender?: (
@@ -136,6 +135,13 @@ export interface ComponentOptionsBase<
     $options: ComponentInternalInstance['ctx']
   ) => void
 
+  /**
+   * Only generated by compiler-sfc to mark a ssr render function inlined and
+   * returned from setup()
+   * @internal
+   */
+  __ssrInlineRender?: boolean
+
   /**
    * marker for AsyncComponentWrapper
    * @internal
index c05ddbacf1534df4ae084119a2584f59c48371c9..82c48e33a04251c6a04abe499d9a181afc03fb85 100644 (file)
@@ -14,6 +14,8 @@ import { ShapeFlags } from '@vue/shared'
  * @private
  */
 export function useCssVars(getter: (ctx: any) => Record<string, string>) {
+  if (!__BROWSER__ && !__TEST__) return
+
   const instance = getCurrentInstance()
   /* istanbul ignore next */
   if (!instance) {
index f92e3d2bea9840e467e19a51cf85a833e0407340..938a4f83a4f9c583ac0f5a2a4908090b5dbc2d5a 100644 (file)
@@ -115,11 +115,17 @@ function renderComponentSubTree(
       instance
     )
   } else {
-    if (!instance.render && !comp.ssrRender && isString(comp.template)) {
+    if (
+      !instance.render &&
+      !instance.ssrRender &&
+      !comp.ssrRender &&
+      isString(comp.template)
+    ) {
       comp.ssrRender = ssrCompile(comp.template, instance)
     }
 
-    if (comp.ssrRender) {
+    const ssrRender = instance.ssrRender || comp.ssrRender
+    if (ssrRender) {
       // optimized
       // resolve fallthrough attrs
       let attrs =
@@ -138,7 +144,7 @@ function renderComponentSubTree(
 
       // set current rendering instance for asset resolution
       setCurrentRenderingInstance(instance)
-      comp.ssrRender(
+      ssrRender(
         instance.proxy,
         push,
         instance,
index 023e3bd8bb465074f73b7d4a4418884aac5f8857..67856f135b8a9455832ea1ed5e7cd19d8a02eaff 100644 (file)
@@ -129,7 +129,7 @@ function createConfig(format, output, plugins = []) {
         [
           ...Object.keys(pkg.dependencies || {}),
           ...Object.keys(pkg.peerDependencies || {}),
-          ...['path', 'url'] // for @vue/compiler-sfc
+          ...['path', 'url', 'stream'] // for @vue/compiler-sfc / server-renderer
         ]
 
   // the browser builds of @vue/compiler-sfc requires postcss to be available