diff --git a/index.js b/index.js index cdfdcda..19e9832 100644 --- a/index.js +++ b/index.js @@ -11,6 +11,22 @@ var is = require('unist-util-is') var dashes = /-([a-z])/g +var vueUtils = { + topLevel: [ + 'class', + 'staticClass', + 'style', + 'key', + 'ref', + 'refInFor', + 'slot', + 'scopedSlots' + ], + nestableRE: /^(props|domProps|on|nativeOn|hook)([-_A-Z])/, + dirRE: /^v-/, + xlinkRE: /^xlink([A-Z])/ +} + module.exports = wrapper function wrapper(h, node, options) { @@ -18,6 +34,7 @@ function wrapper(h, node, options) { var prefix var r var v + var vu if (typeof h !== 'function') { throw new Error('h is not a function') @@ -32,6 +49,7 @@ function wrapper(h, node, options) { r = react(h) v = vdom(h) + vu = vue(h) if (prefix === null || prefix === undefined) { prefix = r === true || v === true ? 'h-' : false @@ -60,6 +78,7 @@ function wrapper(h, node, options) { key: 0, react: r, vdom: v, + vue: vu, hyperscript: hyperscript(h) }) } @@ -98,9 +117,9 @@ function toH(h, node, ctx) { if ( typeof attributes.style === 'string' && - (ctx.vdom === true || ctx.react === true) + (ctx.vdom === true || ctx.react === true || ctx.vue === true) ) { - // VDOM and React accept `style` as object. + // VDOM, React and Vue accept `style` as object. attributes.style = parseStyle(attributes.style, name) } @@ -170,9 +189,11 @@ function addAttribute(props, prop, value, ctx) { subprop = 'attributes' } else if (ctx.hyperscript === true) { subprop = 'attrs' + } else if (ctx.vue === true) { + vueMergeAttributes(props, info, value) + return } } - if (subprop) { if (props[subprop] === undefined) { props[subprop] = {} @@ -184,6 +205,76 @@ function addAttribute(props, prop, value, ctx) { } } +function vueMergeAttributes(props, info, value) { + var groupProp = vueGroupAttribute(info, value) + var propName = groupProp.name + var propValue = groupProp.value + + props[propName] = + typeof propValue === 'object' + ? Object.assign({}, props[propName] || {}, propValue) + : Array.isArray(propValue) + ? [].concat(props[propName] || [], propValue) + : propValue +} + +function vueGroupAttribute(info, value) { + var name = info.property + var props = {} + + var processeddName + var processedValue + + if (vueUtils.topLevel.indexOf(name) > -1) { + processeddName = name + processedValue = value + } else { + // Nested modules + var nestMatch = name.match(vueUtils.nestableRE) + if (nestMatch) { + var prefix = nestMatch[1] + var suffix = name.replace(vueUtils.nestableRE, function(_, $1, $2) { + return $2 === '-' ? '' : $2.toLowerCase() + }) + + var subProp = {} + subProp[suffix] = value + processeddName = prefix + processedValue = subProp + } else if (vueUtils.dirRE.test(info.attribute)) { + // Custom directive + processeddName = 'directives' + processedValue = [ + { + name: info.attribute.replace(vueUtils.dirRE, ''), + value: value + } + ] + } else { + // Rest are nested under attrs + // guard xlink attributes + var attribute = + info.space && info.space === 'xlink' + ? JSON.stringify( + info.attribute.replace(vueUtils.xlinkRE, function(m, p1) { + return 'xlink:' + p1.toLowerCase() + }) + ) + : info.attribute + + var attrs = {} + attrs[attribute] = value + processeddName = 'attrs' + processedValue = attrs + } + } + + props.name = processeddName + props.value = processedValue + + return props +} + // Check if `h` is `react.createElement`. function react(h) { var node = h && h('div') @@ -192,6 +283,12 @@ function react(h) { ) } +// Check if `h` is vue `createElement`. +function vue(h) { + var div = h && h('div') + return div && div.context && div.context._isVue === true +} + // Check if `h` is `hyperscript`. function hyperscript(h) { return Boolean(h && h.context && h.cleanup) diff --git a/package.json b/package.json index 06ed336..25f5754 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,8 @@ "unist-builder": "^1.0.1", "vdom-to-html": "^2.3.1", "virtual-dom": "^2.1.1", + "vue": "^2.5.17", + "vue-server-renderer": "^2.5.17", "xo": "^0.23.0" }, "scripts": { diff --git a/test.js b/test.js index e959512..410c60d 100644 --- a/test.js +++ b/test.js @@ -10,6 +10,9 @@ var r = require('react').createElement var rehype = require('rehype') var vToString = require('vdom-to-html') var rToString = require('react-dom/server').renderToStaticMarkup +var vueToString = require('vue-server-renderer').createRenderer({ + template: '' +}).renderToString var toH = require('.') var processor = rehype().data('settings', {fragment: true, position: false})