@@ -26,11 +26,15 @@ import {
26
26
createSequenceExpression ,
27
27
InterpolationNode ,
28
28
isStaticExp ,
29
- AttributeNode
29
+ AttributeNode ,
30
+ buildDirectiveArgs ,
31
+ TransformContext ,
32
+ PropsExpression
30
33
} from '@vue/compiler-dom'
31
34
import {
32
35
escapeHtml ,
33
36
isBooleanAttr ,
37
+ isBuiltInDirective ,
34
38
isSSRSafeAttrName ,
35
39
NO ,
36
40
propsToAttrMap
@@ -44,7 +48,8 @@ import {
44
48
SSR_RENDER_ATTRS ,
45
49
SSR_INTERPOLATE ,
46
50
SSR_GET_DYNAMIC_MODEL_PROPS ,
47
- SSR_INCLUDE_BOOLEAN_ATTR
51
+ SSR_INCLUDE_BOOLEAN_ATTR ,
52
+ SSR_GET_DIRECTIVE_PROPS
48
53
} from '../runtimeHelpers'
49
54
import { SSRTransformContext , processChildren } from '../ssrCodegenTransform'
50
55
@@ -71,16 +76,26 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
71
76
const needTagForRuntime =
72
77
node . tag === 'textarea' || node . tag . indexOf ( '-' ) > 0
73
78
74
- // v-bind="obj" or v-bind:[key] can potentially overwrite other static
75
- // attrs and can affect final rendering result, so when they are present
76
- // we need to bail out to full `renderAttrs`
79
+ // v-bind="obj", v-bind:[key] and custom directives can potentially
80
+ // overwrite other static attrs and can affect final rendering result,
81
+ // so when they are present we need to bail out to full `renderAttrs`
77
82
const hasDynamicVBind = hasDynamicKeyVBind ( node )
78
- if ( hasDynamicVBind ) {
79
- const { props } = buildProps ( node , context , node . props , true /* ssr */ )
80
- if ( props ) {
83
+ const hasCustomDir = node . props . some (
84
+ p => p . type === NodeTypes . DIRECTIVE && ! isBuiltInDirective ( p . name )
85
+ )
86
+ const needMergeProps = hasDynamicVBind || hasCustomDir
87
+ if ( needMergeProps ) {
88
+ const { props, directives } = buildProps (
89
+ node ,
90
+ context ,
91
+ node . props ,
92
+ true /* ssr */
93
+ )
94
+ if ( props || directives . length ) {
95
+ const mergedProps = buildSSRProps ( props , directives , context )
81
96
const propsExp = createCallExpression (
82
97
context . helper ( SSR_RENDER_ATTRS ) ,
83
- [ props ]
98
+ [ mergedProps ]
84
99
)
85
100
86
101
if ( node . tag === 'textarea' ) {
@@ -99,7 +114,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
99
114
propsExp . arguments = [
100
115
createAssignmentExpression (
101
116
createSimpleExpression ( tempId , false ) ,
102
- props
117
+ mergedProps
103
118
)
104
119
]
105
120
rawChildrenMap . set (
@@ -128,7 +143,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
128
143
const tempExp = createSimpleExpression ( tempId , false )
129
144
propsExp . arguments = [
130
145
createSequenceExpression ( [
131
- createAssignmentExpression ( tempExp , props ) ,
146
+ createAssignmentExpression ( tempExp , mergedProps ) ,
132
147
createCallExpression ( context . helper ( MERGE_PROPS ) , [
133
148
tempExp ,
134
149
createCallExpression (
@@ -176,10 +191,10 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
176
191
createCompilerError ( ErrorCodes . X_V_SLOT_MISPLACED , prop . loc )
177
192
)
178
193
} else if ( isTextareaWithValue ( node , prop ) && prop . exp ) {
179
- if ( ! hasDynamicVBind ) {
194
+ if ( ! needMergeProps ) {
180
195
node . children = [ createInterpolation ( prop . exp , prop . loc ) ]
181
196
}
182
- } else if ( ! hasDynamicVBind ) {
197
+ } else if ( ! needMergeProps ) {
183
198
// Directive transforms.
184
199
const directiveTransform = context . directiveTransforms [ prop . name ]
185
200
if ( directiveTransform ) {
@@ -277,7 +292,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
277
292
// special case: value on <textarea>
278
293
if ( node . tag === 'textarea' && prop . name === 'value' && prop . value ) {
279
294
rawChildrenMap . set ( node , escapeHtml ( prop . value . content ) )
280
- } else if ( ! hasDynamicVBind ) {
295
+ } else if ( ! needMergeProps ) {
281
296
if ( prop . name === 'key' || prop . name === 'ref' ) {
282
297
continue
283
298
}
@@ -307,6 +322,37 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
307
322
}
308
323
}
309
324
325
+ export function buildSSRProps (
326
+ props : PropsExpression | undefined ,
327
+ directives : DirectiveNode [ ] ,
328
+ context : TransformContext
329
+ ) : JSChildNode {
330
+ let mergePropsArgs : JSChildNode [ ] = [ ]
331
+ if ( props ) {
332
+ if ( props . type === NodeTypes . JS_CALL_EXPRESSION ) {
333
+ // already a mergeProps call
334
+ mergePropsArgs = props . arguments as JSChildNode [ ]
335
+ } else {
336
+ mergePropsArgs . push ( props )
337
+ }
338
+ }
339
+ if ( directives . length ) {
340
+ for ( const dir of directives ) {
341
+ context . directives . add ( dir . name )
342
+ mergePropsArgs . push (
343
+ createCallExpression ( context . helper ( SSR_GET_DIRECTIVE_PROPS ) , [
344
+ `_ctx` ,
345
+ ...buildDirectiveArgs ( dir , context ) . elements
346
+ ] as JSChildNode [ ] )
347
+ )
348
+ }
349
+ }
350
+
351
+ return mergePropsArgs . length > 1
352
+ ? createCallExpression ( context . helper ( MERGE_PROPS ) , mergePropsArgs )
353
+ : mergePropsArgs [ 0 ]
354
+ }
355
+
310
356
function isTrueFalseValue ( prop : DirectiveNode | AttributeNode ) {
311
357
if ( prop . type === NodeTypes . DIRECTIVE ) {
312
358
return (
0 commit comments