diff --git a/index.js b/index.js index 55758fe..c601b44 100644 --- a/index.js +++ b/index.js @@ -18,6 +18,7 @@ function wrapper(h, node, options) { var prefix var r var v + var vd if (typeof h !== 'function') { throw new Error('h is not a function') @@ -31,10 +32,11 @@ function wrapper(h, node, options) { } r = react(h) - v = vdom(h) + v = vue(h) + vd = vdom(h) if (prefix === null || prefix === undefined) { - prefix = r === true || v === true ? 'h-' : false + prefix = r === true || v === true || vd === true ? 'h-' : false } if (is('root', node)) { @@ -59,7 +61,8 @@ function wrapper(h, node, options) { prefix: prefix, key: 0, react: r, - vdom: v, + vue: v, + vdom: vd, hyperscript: hyperscript(h) }) } @@ -98,9 +101,9 @@ function toH(h, node, ctx) { if ( typeof attributes.style === 'string' && - (ctx.vdom === true || ctx.react === true) + (ctx.vdom === true || ctx.vue === true || ctx.react === true) ) { - // VDOM and React accept `style` as object. + // VDOM, Vue, and React accept `style` as object. attributes.style = parseStyle(attributes.style, name) } @@ -140,7 +143,7 @@ function toH(h, node, ctx) { } function addAttribute(props, prop, value, ctx) { - var hyperlike = ctx.hyperscript || ctx.vdom + var hyperlike = ctx.hyperscript || ctx.vdom || ctx.vue var schema = ctx.schema var info = find(schema, prop) var subprop @@ -167,7 +170,11 @@ function addAttribute(props, prop, value, ctx) { value = '' } - if (!info.mustUseProperty) { + if (ctx.vue) { + if (prop !== 'style') { + subprop = 'attrs' + } + } else if (!info.mustUseProperty) { if (ctx.vdom === true) { subprop = 'attributes' } else if (ctx.hyperscript === true) { @@ -204,6 +211,11 @@ function vdom(h) { return h && h('div').type === 'VirtualNode' } +function vue(h) { + var node = h && h('div') + return Boolean(node && node.context && node.context._isVue) +} + function parseStyle(value, tagName) { var result = {} diff --git a/package.json b/package.json index 3737d63..cbc55e0 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.6.10", + "vue-server-renderer": "^2.6.10", "xo": "^0.24.0" }, "scripts": { diff --git a/test.js b/test.js index 9c175e4..175a062 100644 --- a/test.js +++ b/test.js @@ -10,6 +10,8 @@ var r = require('react').createElement var rehype = require('rehype') var vToString = require('vdom-to-html') var rToString = require('react-dom/server').renderToStaticMarkup +var Vue = require('vue') +var VueSSR = require('vue-server-renderer') var toH = require('.') var processor = rehype().data('settings', {fragment: true, position: false}) @@ -386,6 +388,119 @@ test('hast-to-hyperscript', function(t) { st.end() }) + t.test('should support `Vue`', function(st) { + var baseline = doc.replace(/
/, '
') + var actual + var expected + + st.plan(3) + + Promise.all([vueToString(actualRender), vueToString(expectedRender)]) + .then(function(all) { + var actualString = all[0] + var expectedString = all[0] + + st.deepEqual(clean(actual), clean(expected), 'equal syntax trees') + st.deepEqual(html(actualString), html(baseline), 'equal output') + + st.deepEqual( + html(expectedString), + html(baseline), + 'equal output baseline' + ) + }) + .catch(function(error) { + st.ifErr(error, 'did not expect an error') + }) + + function actualRender(h) { + actual = toH(h, hast) + return actual + } + + function expectedRender(h) { + expected = h( + 'div', + {key: 'h-1', attrs: {'data-server-rendered': 'true'}}, + [ + h( + 'h1', + { + key: 'h-2', + attrs: { + class: 'b c', + id: 'a', + hidden: true, + height: 2 + } + }, + [ + 'bravo ', + h( + 'strong', + { + key: 'h-3', + style: {color: 'red'}, + attrs: { + foo: 'bar', + camelCase: 'on off', + 'data-123': '456', + 'data-some': 'yes', + 'aria-valuenow': '1' + } + }, + ['charlie'] + ), + ' delta', + h('input', { + key: 'h-4', + attrs: { + checked: true, + type: 'file', + accept: '.jpg, .jpeg' + } + }) + ] + ), + h( + 'svg', + { + key: 'h-5', + attrs: { + xmlns: 'http://www.w3.org/2000/svg', + viewBox: '0 0 500 500' + } + }, + [h('circle', {key: 'h-6', attrs: {cx: 120, cy: 120, r: 100}})] + ) + ] + ) + return expected + } + + function identity(value) { + return value + } + + function vueToString(render) { + return VueSSR.createRenderer({template: identity}).renderToString( + new Vue({render: render}).$mount() + ) + } + + function clean(node) { + remove(node) + return json(node) + } + + function remove(node) { + delete node.context + if (node.children) { + node.children.forEach(remove) + } + } + }) + t.test('should support keys', function(st) { st.equal( toH(h, u('element', {tagName: 'div'})).key, @@ -508,9 +623,7 @@ test('hast-to-hyperscript', function(t) { st.end() }) - t.test('flattens a `root` with one element child to that child', function( - st - ) { + t.test('flattens a `root` with one element to that child', function(st) { var actual = toH( h, u('root', [u('element', {tagName: 'h1', properties: {id: 'a'}}, [])])