]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip(ssr): handle `<textarea>` with dynamic key v-bind
authorEvan You <yyx990803@gmail.com>
Wed, 5 Feb 2020 03:49:47 +0000 (22:49 -0500)
committerEvan You <yyx990803@gmail.com>
Wed, 5 Feb 2020 03:49:47 +0000 (22:49 -0500)
packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap
packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap
packages/compiler-core/__tests__/codegen.spec.ts
packages/compiler-core/src/ast.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/parse.ts
packages/compiler-core/src/transform.ts
packages/compiler-ssr/__tests__/ssrElement.spec.ts
packages/compiler-ssr/src/transforms/ssrTransformElement.ts

index 6b09fa2f7d6189b6db8e2993261ce4d7dfbec356..aae2643b2b6a7a0ba91f3b7984b986ec1ef66d04 100644 (file)
@@ -189,3 +189,13 @@ return function render() {
   }
 }"
 `;
+
+exports[`compiler: codegen temps 1`] = `
+"
+return function render() {
+  with (this) {
+    let _temp0, _temp1, _temp2
+    return null
+  }
+}"
+`;
index 978f4c9bc3b5f239f6f8c5d558e6b435eef3d2b6..c7af84fff73299e5002905f0218314f8a5c6da74 100644 (file)
@@ -65,6 +65,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -134,6 +135,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -203,6 +205,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -289,6 +292,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -375,6 +379,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -461,6 +466,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -547,6 +553,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -616,6 +623,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -685,6 +693,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -754,6 +763,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -823,6 +833,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -892,6 +903,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -985,6 +997,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1054,6 +1067,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1123,6 +1137,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1192,6 +1207,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1336,6 +1352,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1411,6 +1428,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1486,6 +1504,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1555,6 +1574,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1624,6 +1644,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1699,6 +1720,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1792,6 +1814,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1861,6 +1884,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1930,6 +1954,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -1999,6 +2024,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -2068,6 +2094,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -2137,6 +2164,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -2206,6 +2234,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -2275,6 +2304,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -2350,6 +2380,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -2425,6 +2456,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -2519,6 +2551,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -2613,6 +2646,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -2707,6 +2741,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -2817,6 +2852,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -2927,6 +2963,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3037,6 +3074,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3147,6 +3185,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3257,6 +3296,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3367,6 +3407,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3477,6 +3518,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3587,6 +3629,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3656,6 +3699,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3701,6 +3745,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3770,6 +3815,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3839,6 +3885,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3908,6 +3955,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -3977,6 +4025,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4046,6 +4095,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4133,6 +4183,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4202,6 +4253,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4288,6 +4340,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4398,6 +4451,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4492,6 +4546,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4586,6 +4641,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4637,6 +4693,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4706,6 +4763,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4775,6 +4833,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4844,6 +4903,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -4991,6 +5051,7 @@ class=\\"bar\\"></div></template>",
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -5135,6 +5196,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -5204,6 +5266,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -5273,6 +5336,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -5342,6 +5406,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -5411,6 +5476,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -5480,6 +5546,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -5549,6 +5616,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -5618,6 +5686,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -5687,6 +5756,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -5797,6 +5867,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -5907,6 +5978,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -6017,6 +6089,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -6127,6 +6200,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -6237,6 +6311,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -6347,6 +6422,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -6457,6 +6533,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -6567,6 +6644,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -6661,6 +6739,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -6771,6 +6850,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -6840,6 +6920,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -6952,6 +7033,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7021,6 +7103,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7090,6 +7173,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7141,6 +7225,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7192,6 +7277,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7279,6 +7365,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7348,6 +7435,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7456,6 +7544,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7531,6 +7620,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7606,6 +7696,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7651,6 +7742,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7696,6 +7788,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7759,6 +7852,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -7840,6 +7934,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -8029,6 +8124,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
@@ -8241,6 +8337,7 @@ Object {
       "offset": 0,
     },
   },
+  "temps": 0,
   "type": 0,
 }
 `;
index 3d12c2157720ae234e0196bec75eaa370a69b125..bf4febe67464ef783edab1869abd667bf54bf626 100644 (file)
@@ -17,7 +17,8 @@ import {
   createCacheExpression,
   createTemplateLiteral,
   createBlockStatement,
-  createIfStatement
+  createIfStatement,
+  createAssignmentExpression
 } from '../src'
 import {
   CREATE_VNODE,
@@ -40,6 +41,7 @@ function createRoot(options: Partial<RootNode> = {}): RootNode {
     imports: [],
     hoists: [],
     cached: 0,
+    temps: 0,
     codegenNode: createSimpleExpression(`null`, false),
     loc: locStub,
     ...options
@@ -141,6 +143,15 @@ describe('compiler: codegen', () => {
     expect(code).toMatchSnapshot()
   })
 
+  test('temps', () => {
+    const root = createRoot({
+      temps: 3
+    })
+    const { code } = generate(root)
+    expect(code).toMatch(`let _temp0, _temp1, _temp2`)
+    expect(code).toMatchSnapshot()
+  })
+
   test('prefixIdentifiers: true should inject _ctx statement', () => {
     const { code } = generate(createRoot(), { prefixIdentifiers: true })
     expect(code).toMatch(`const _ctx = this\n`)
@@ -540,4 +551,23 @@ describe('compiler: codegen', () => {
       `)
     })
   })
+
+  test('AssignmentExpression', () => {
+    const { code } = generate(
+      createRoot({
+        codegenNode: createAssignmentExpression(
+          createSimpleExpression(`foo`, false),
+          createSimpleExpression(`bar`, false)
+        )
+      })
+    )
+    expect(code).toMatchInlineSnapshot(`
+      "
+      return function render() {
+        with (this) {
+          return (foo = bar)
+        }
+      }"
+    `)
+  })
 })
index eb824a0035f09d3e01e3ed9d5edee8cf3c3f590b..97d64b20392da2408acb13d44e0bab4cb2a19d83 100644 (file)
@@ -50,7 +50,8 @@ export const enum NodeTypes {
   // ssr codegen
   JS_BLOCK_STATEMENT,
   JS_TEMPLATE_LITERAL,
-  JS_IF_STATEMENT
+  JS_IF_STATEMENT,
+  JS_ASSIGNMENT_EXPRESSION
 }
 
 export const enum ElementTypes {
@@ -102,8 +103,9 @@ export interface RootNode extends Node {
   hoists: JSChildNode[]
   imports: ImportItem[]
   cached: number
-  codegenNode?: TemplateChildNode | JSChildNode | BlockStatement | undefined
+  temps: number
   ssrHelpers?: symbol[]
+  codegenNode?: TemplateChildNode | JSChildNode | BlockStatement | undefined
 }
 
 export type ElementNode =
@@ -255,6 +257,7 @@ export type JSChildNode =
   | ConditionalExpression
   | SequenceExpression
   | CacheExpression
+  | AssignmentExpression
 
 export interface CallExpression extends Node {
   type: NodeTypes.JS_CALL_EXPRESSION
@@ -335,6 +338,12 @@ export interface IfStatement extends Node {
   alternate: IfStatement | BlockStatement | undefined
 }
 
+export interface AssignmentExpression extends Node {
+  type: NodeTypes.JS_ASSIGNMENT_EXPRESSION
+  left: SimpleExpressionNode
+  right: JSChildNode
+}
+
 // Codegen Node Types ----------------------------------------------------------
 
 // createVNode(...)
@@ -709,3 +718,15 @@ export function createIfStatement(
     loc: locStub
   }
 }
+
+export function createAssignmentExpression(
+  left: AssignmentExpression['left'],
+  right: AssignmentExpression['right']
+): AssignmentExpression {
+  return {
+    type: NodeTypes.JS_ASSIGNMENT_EXPRESSION,
+    left,
+    right,
+    loc: locStub
+  }
+}
index eba40cb0ce6bf152f0063e4fb2007e10f77acf7a..250c7fb683a3bae3682ec0853272e75a11bfd32e 100644 (file)
@@ -21,7 +21,8 @@ import {
   locStub,
   SSRCodegenNode,
   TemplateLiteral,
-  IfStatement
+  IfStatement,
+  AssignmentExpression
 } from './ast'
 import { SourceMapGenerator, RawSourceMap } from 'source-map'
 import {
@@ -232,7 +233,14 @@ export function generate(
   if (ast.directives.length) {
     genAssets(ast.directives, 'directive', context)
   }
-  if (ast.components.length || ast.directives.length) {
+  if (ast.temps > 0) {
+    push(`let `)
+    for (let i = 0; i < ast.temps; i++) {
+      push(`${i > 0 ? `, ` : ``}_temp${i}`)
+    }
+    newline()
+  }
+  if (ast.components.length || ast.directives.length || ast.temps) {
     newline()
   }
 
@@ -520,6 +528,9 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
     case NodeTypes.JS_IF_STATEMENT:
       !__BROWSER__ && genIfStatement(node, context)
       break
+    case NodeTypes.JS_ASSIGNMENT_EXPRESSION:
+      !__BROWSER__ && genAssignmentExpression(node, context)
+      break
 
     /* istanbul ignore next */
     default:
@@ -790,3 +801,12 @@ function genIfStatement(node: IfStatement, context: CodegenContext) {
     }
   }
 }
+
+function genAssignmentExpression(
+  node: AssignmentExpression,
+  context: CodegenContext
+) {
+  genNode(node.left, context)
+  context.push(` = `)
+  genNode(node.right, context)
+}
index e593979fa672d4dcf577900f91ed5092deb055cd..421f32a5f15313a3a691620c96e06dfee3ca1d01 100644 (file)
@@ -82,6 +82,7 @@ export function baseParse(
     hoists: [],
     imports: [],
     cached: 0,
+    temps: 0,
     codegenNode: undefined,
     loc: getSelection(context, start)
   }
index 4959fb7fc2f1989dc8c90e335dc9d94832c7ba60..3d7167c04c1d23f1b1badb5b5cc52bca1941f2f7 100644 (file)
@@ -83,6 +83,7 @@ export interface TransformContext extends Required<TransformOptions> {
   components: Set<string>
   directives: Set<string>
   hoists: JSChildNode[]
+  temps: number
   imports: Set<ImportItem>
   cached: number
   identifiers: { [name: string]: number | undefined }
@@ -136,6 +137,7 @@ function createTransformContext(
     components: new Set(),
     directives: new Set(),
     hoists: [],
+    temps: 0,
     imports: new Set(),
     cached: 0,
     identifiers: {},
@@ -267,6 +269,7 @@ export function transform(root: RootNode, options: TransformOptions) {
   root.directives = [...context.directives]
   root.imports = [...context.imports]
   root.hoists = context.hoists
+  root.temps = context.temps
   root.cached = context.cached
 }
 
index 7d807e4692b6d0a5843130bcd84935360f46835a..61df86ce1e8f8645b3c526f7e9b2f8a0f5f4ac53 100644 (file)
@@ -1,4 +1,5 @@
 import { getCompiledString } from './utils'
+import { compile } from '../src'
 
 describe('ssr: element', () => {
   test('basic elements', () => {
@@ -48,7 +49,20 @@ describe('ssr: element', () => {
     })
 
     test('<textarea> with dynamic v-bind', () => {
-      // TODO
+      expect(compile(`<textarea v-bind="obj">fallback</textarea>`).code)
+        .toMatchInlineSnapshot(`
+        "const { _renderAttrs, _interpolate } = require(\\"vue\\")
+
+        return function ssrRender(_ctx, _push, _parent) {
+          let _temp0
+          
+          _push(\`<textarea\${
+            _renderAttrs(_temp0 = _ctx.obj)
+          }>\${
+            _interpolate((\\"value\\" in _temp0) ? _temp0.value : \\"fallback\\")
+          }</textarea>\`)
+        }"
+      `)
     })
   })
 
@@ -95,7 +109,7 @@ describe('ssr: element', () => {
       expect(
         getCompiledString(`<input type="checkbox" :checked="checked">`)
       ).toMatchInlineSnapshot(
-        `"\`<input type=\\"checkbox\\"\${(_ctx.checked)? \\" checked\\": \\"\\"}>\`"`
+        `"\`<input type=\\"checkbox\\"\${(_ctx.checked) ? \\" checked\\" : \\"\\"}>\`"`
       )
     })
 
index 1c1d1157462be8476db0f1c91e6a7c65dc6bc230..4f76441089c8bbe89e80538050c29e1115abc312 100644 (file)
@@ -17,7 +17,9 @@ import {
   createArrayExpression,
   ExpressionNode,
   JSChildNode,
-  ArrayExpression
+  ArrayExpression,
+  createAssignmentExpression,
+  TextNode
 } from '@vue/compiler-dom'
 import { escapeHtml, isBooleanAttr, isSSRSafeAttrName } from '@vue/shared'
 import { createSSRCompilerError, SSRErrorCodes } from '../errors'
@@ -26,7 +28,8 @@ import {
   SSR_RENDER_CLASS,
   SSR_RENDER_STYLE,
   SSR_RENDER_DYNAMIC_ATTR,
-  SSR_RENDER_ATTRS
+  SSR_RENDER_ATTRS,
+  SSR_INTERPOLATE
 } from '../runtimeHelpers'
 
 export const ssrTransformElement: NodeTransform = (node, context) => {
@@ -54,9 +57,38 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
       if (hasDynamicVBind) {
         const { props } = buildProps(node, context, node.props, true /* ssr */)
         if (props) {
-          openTag.push(
-            createCallExpression(context.helper(SSR_RENDER_ATTRS), [props])
+          const propsExp = createCallExpression(
+            context.helper(SSR_RENDER_ATTRS),
+            [props]
           )
+          if (node.tag === 'textarea') {
+            // <textarea> with dynamic v-bind. We don't know if the final props
+            // will contain .value, so we will have to do something special:
+            // assign the merged props to a temp variable, and check whether
+            // it contains value (if yes, render is as children).
+            const tempId = `_temp${context.temps++}`
+            propsExp.arguments[0] = createAssignmentExpression(
+              createSimpleExpression(tempId, false),
+              props
+            )
+            const existingText = node.children[0] as TextNode | undefined
+            node.children = []
+            rawChildren = createCallExpression(
+              context.helper(SSR_INTERPOLATE),
+              [
+                createConditionalExpression(
+                  createSimpleExpression(`"value" in ${tempId}`, false),
+                  createSimpleExpression(`${tempId}.value`, false),
+                  createSimpleExpression(
+                    existingText ? existingText.content : ``,
+                    true
+                  ),
+                  false
+                )
+              ]
+            )
+          }
+          openTag.push(propsExp)
         }
       }
 
@@ -83,8 +115,6 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
           } else if (isTextareaWithValue(node, prop) && prop.exp) {
             if (!hasDynamicVBind) {
               node.children = [createInterpolation(prop.exp, prop.loc)]
-            } else {
-              // TODO handle <textrea> with dynamic v-bind
             }
           } else {
             // Directive transforms.