@@ -8,7 +8,10 @@ import {
88 normalizeVNode ,
99 createVNode ,
1010 Comment ,
11- cloneVNode
11+ cloneVNode ,
12+ Fragment ,
13+ VNodeArrayChildren ,
14+ isVNode
1215} from './vnode'
1316import { handleError , ErrorCodes } from './errorHandling'
1417import { PatchFlags , ShapeFlags , EMPTY_OBJ , isOn } from '@vue/shared'
@@ -80,22 +83,30 @@ export function renderComponentRoot(
8083 }
8184
8285 // attr merging
86+ // in dev mode, comments are preserved, and it's possible for a template
87+ // to have comments along side the root element which makes it a fragment
88+ let root = result
89+ let setRoot : ( ( root : VNode ) => void ) | undefined = undefined
90+ if ( __DEV__ ) {
91+ ; [ root , setRoot ] = getChildRoot ( result )
92+ }
93+
8394 if (
8495 Component . inheritAttrs !== false &&
8596 fallthroughAttrs &&
8697 fallthroughAttrs !== EMPTY_OBJ
8798 ) {
8899 if (
89- result . shapeFlag & ShapeFlags . ELEMENT ||
90- result . shapeFlag & ShapeFlags . COMPONENT
100+ root . shapeFlag & ShapeFlags . ELEMENT ||
101+ root . shapeFlag & ShapeFlags . COMPONENT
91102 ) {
92- result = cloneVNode ( result , fallthroughAttrs )
103+ root = cloneVNode ( root , fallthroughAttrs )
93104 // If the child root node is a compiler optimized vnode, make sure it
94105 // force update full props to account for the merged attrs.
95- if ( result . dynamicChildren ) {
96- result . patchFlag |= PatchFlags . FULL_PROPS
106+ if ( root . dynamicChildren ) {
107+ root . patchFlag |= PatchFlags . FULL_PROPS
97108 }
98- } else if ( __DEV__ && ! accessedAttrs && result . type !== Comment ) {
109+ } else if ( __DEV__ && ! accessedAttrs && root . type !== Comment ) {
99110 warn (
100111 `Extraneous non-props attributes (` +
101112 `${ Object . keys ( attrs ) . join ( ', ' ) } ) ` +
@@ -108,27 +119,33 @@ export function renderComponentRoot(
108119 // inherit scopeId
109120 const parentScopeId = parent && parent . type . __scopeId
110121 if ( parentScopeId ) {
111- result = cloneVNode ( result , { [ parentScopeId ] : '' } )
122+ root = cloneVNode ( root , { [ parentScopeId ] : '' } )
112123 }
113124 // inherit directives
114125 if ( vnode . dirs ) {
115- if ( __DEV__ && ! isElementRoot ( result ) ) {
126+ if ( __DEV__ && ! isElementRoot ( root ) ) {
116127 warn (
117128 `Runtime directive used on component with non-element root node. ` +
118129 `The directives will not function as intended.`
119130 )
120131 }
121- result . dirs = vnode . dirs
132+ root . dirs = vnode . dirs
122133 }
123134 // inherit transition data
124135 if ( vnode . transition ) {
125- if ( __DEV__ && ! isElementRoot ( result ) ) {
136+ if ( __DEV__ && ! isElementRoot ( root ) ) {
126137 warn (
127138 `Component inside <Transition> renders non-element root node ` +
128139 `that cannot be animated.`
129140 )
130141 }
131- result . transition = vnode . transition
142+ root . transition = vnode . transition
143+ }
144+
145+ if ( __DEV__ && setRoot ) {
146+ setRoot ( root )
147+ } else {
148+ result = root
132149 }
133150 } catch ( err ) {
134151 handleError ( err , instance , ErrorCodes . RENDER_FUNCTION )
@@ -139,6 +156,25 @@ export function renderComponentRoot(
139156 return result
140157}
141158
159+ const getChildRoot = (
160+ vnode : VNode
161+ ) : [ VNode , ( ( root : VNode ) => void ) | undefined ] => {
162+ if ( vnode . type !== Fragment ) {
163+ return [ vnode , undefined ]
164+ }
165+ const rawChildren = vnode . children as VNodeArrayChildren
166+ const children = rawChildren . filter ( child => {
167+ return ! ( isVNode ( child ) && child . type === Comment )
168+ } )
169+ if ( children . length !== 1 ) {
170+ return [ vnode , undefined ]
171+ }
172+ const childRoot = children [ 0 ]
173+ const index = rawChildren . indexOf ( childRoot )
174+ const setRoot = ( updatedRoot : VNode ) => ( rawChildren [ index ] = updatedRoot )
175+ return [ normalizeVNode ( childRoot ) , setRoot ]
176+ }
177+
142178const getFallthroughAttrs = ( attrs : Data ) : Data | undefined => {
143179 let res : Data | undefined
144180 for ( const key in attrs ) {
0 commit comments