code: function buildOpeningElementAttributes(attribs, file) {
      let _props = []
      let objs = []
  
      function pushProps() {
        if (_props.length === 0) return
        objs.push(t.objectExpression(_props))
        _props = []
      }
  
      while (attribs.length) {
        const prop = attribs.shift()
        if (t.isJSXSpreadAttribute(prop)) {
          pushProps()
          prop.argument._isSpread = true
          objs.push(prop.argument)
        } else {
          _props.push(convertAttribute(prop))
        }
      }
  
      pushProps()
  
      objs = objs.map(o => {
        return o._isSpread ? o : groupProps(o.properties, t)
      })
  
      if (objs.length === 1) {
        // Only one object
        attribs = objs[0]
      } else if (objs.length > 0) {
        // Add prop merging helper
        const helper = file.addImport('babel-helper-vue-jsx-merge-props', 'default', '_mergeJSXProps')
        // Spread it
        attribs = t.callExpression(
          helper,
          [t.arrayExpression(objs)]
        )
      }
      return attribs
    }
docs: The logic for this is quite terse. It's because we need to
  support spread elements. We loop over all attributes,
  breaking on spreads, we then push a new object containing
  all prior attributes to an array for later processing.