@@ -26,11 +26,15 @@ import {
2626 createSequenceExpression ,
2727 InterpolationNode ,
2828 isStaticExp ,
29- AttributeNode
29+ AttributeNode ,
30+ buildDirectiveArgs ,
31+ TransformContext ,
32+ PropsExpression
3033} from '@vue/compiler-dom'
3134import {
3235 escapeHtml ,
3336 isBooleanAttr ,
37+ isBuiltInDirective ,
3438 isSSRSafeAttrName ,
3539 NO ,
3640 propsToAttrMap
@@ -44,7 +48,8 @@ import {
4448 SSR_RENDER_ATTRS ,
4549 SSR_INTERPOLATE ,
4650 SSR_GET_DYNAMIC_MODEL_PROPS ,
47- SSR_INCLUDE_BOOLEAN_ATTR
51+ SSR_INCLUDE_BOOLEAN_ATTR ,
52+ SSR_GET_DIRECTIVE_PROPS
4853} from '../runtimeHelpers'
4954import { SSRTransformContext , processChildren } from '../ssrCodegenTransform'
5055
@@ -71,16 +76,26 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
7176 const needTagForRuntime =
7277 node . tag === 'textarea' || node . tag . indexOf ( '-' ) > 0
7378
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`
7782 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 )
8196 const propsExp = createCallExpression (
8297 context . helper ( SSR_RENDER_ATTRS ) ,
83- [ props ]
98+ [ mergedProps ]
8499 )
85100
86101 if ( node . tag === 'textarea' ) {
@@ -99,7 +114,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
99114 propsExp . arguments = [
100115 createAssignmentExpression (
101116 createSimpleExpression ( tempId , false ) ,
102- props
117+ mergedProps
103118 )
104119 ]
105120 rawChildrenMap . set (
@@ -128,7 +143,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
128143 const tempExp = createSimpleExpression ( tempId , false )
129144 propsExp . arguments = [
130145 createSequenceExpression ( [
131- createAssignmentExpression ( tempExp , props ) ,
146+ createAssignmentExpression ( tempExp , mergedProps ) ,
132147 createCallExpression ( context . helper ( MERGE_PROPS ) , [
133148 tempExp ,
134149 createCallExpression (
@@ -176,10 +191,10 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
176191 createCompilerError ( ErrorCodes . X_V_SLOT_MISPLACED , prop . loc )
177192 )
178193 } else if ( isTextareaWithValue ( node , prop ) && prop . exp ) {
179- if ( ! hasDynamicVBind ) {
194+ if ( ! needMergeProps ) {
180195 node . children = [ createInterpolation ( prop . exp , prop . loc ) ]
181196 }
182- } else if ( ! hasDynamicVBind ) {
197+ } else if ( ! needMergeProps ) {
183198 // Directive transforms.
184199 const directiveTransform = context . directiveTransforms [ prop . name ]
185200 if ( directiveTransform ) {
@@ -277,7 +292,7 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
277292 // special case: value on <textarea>
278293 if ( node . tag === 'textarea' && prop . name === 'value' && prop . value ) {
279294 rawChildrenMap . set ( node , escapeHtml ( prop . value . content ) )
280- } else if ( ! hasDynamicVBind ) {
295+ } else if ( ! needMergeProps ) {
281296 if ( prop . name === 'key' || prop . name === 'ref' ) {
282297 continue
283298 }
@@ -307,6 +322,37 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
307322 }
308323}
309324
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+
310356function isTrueFalseValue ( prop : DirectiveNode | AttributeNode ) {
311357 if ( prop . type === NodeTypes . DIRECTIVE ) {
312358 return (
0 commit comments