├── .gitignore
├── .travis.yml
├── README.md
├── elements.js
├── index.js
├── init.js
├── modules
├── attributes.js
├── class.js
├── dataset.js
├── index.js
├── props.js
└── style.js
├── package-lock.json
├── package.json
├── test
└── index.js
└── type-definitions
└── snabbdom-to-html.d.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - '6'
5 | - '5'
6 | - '4'
7 | script: npm run test
8 | branches:
9 | only:
10 | - master
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Snabbdom to HTML
2 |
3 | Render [Snabbdom](https://github.com/paldepind/snabbdom) Vnode’s to HTML strings
4 |
5 | ## Install
6 |
7 | With [`npm`](https://www.npmjs.com/) do:
8 |
9 | ```bash
10 | npm install snabbdom-to-html
11 | ```
12 |
13 | ## Usage
14 |
15 | ```js
16 | var h = require('snabbdom/h')
17 | var toHTML = require('snabbdom-to-html')
18 |
19 | var output = toHTML(
20 | h('div', { style: { color: 'red' } }, 'The quick brown fox jumps')
21 | )
22 |
23 | console.log(output)
24 | // =>
The quick brown fox jumps
25 | ```
26 |
27 | ### Advanced usage
28 |
29 | This library is built replicating the modular approach used in Snabbdom. So you can do the following if you need to implement any custom functionality.
30 |
31 | ```js
32 | var h = require('snabbdom/h')
33 |
34 | var init = require('snabbdom-to-html/init')
35 | var modules = require('snabbdom-to-html/modules')
36 | var toHTML = init([
37 | modules.class,
38 | modules.props,
39 | modules.attributes,
40 | modules.style
41 | ])
42 |
43 | var output = toHTML(
44 | h('div', { style: { color: 'lime' } }, 'over the lazy fox')
45 | )
46 |
47 | console.log(output)
48 | // => over the lazy fox
49 | ```
50 |
51 | The `init` function accepts an array of functions (modules). Modules have the following signature: `(vnode, attributes) => undefined`, where `attributes` is an [ES2015 Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) instance.
52 |
53 | You can do `attributes.set(key, value)`, `attributes.get(key)` and `attributes.delete(key)` and so on. You can check out the built-in modules to get the idea.
54 |
55 | The built-in modules are available from `snabbdom-to-html/modules`, and these are:
56 |
57 | - `attributes`
58 | - `class`
59 | - `props`
60 | - `style`
61 |
62 | ## Support
63 |
64 | This is tested against Node.js 4.x and up. If you need to run this in the browser you might need to include something like [`es6-shim`](https://github.com/paulmillr/es6-shim) to ensure `Map` support.
65 |
66 | ## License
67 |
68 | MIT
69 |
--------------------------------------------------------------------------------
/elements.js:
--------------------------------------------------------------------------------
1 |
2 | // All SVG children elements, not in this list, should self-close
3 |
4 | exports.CONTAINER = {
5 | // http://www.w3.org/TR/SVG/intro.html#TermContainerElement
6 | 'a': true,
7 | 'defs': true,
8 | 'glyph': true,
9 | 'g': true,
10 | 'marker': true,
11 | 'mask': true,
12 | 'missing-glyph': true,
13 | 'pattern': true,
14 | 'svg': true,
15 | 'switch': true,
16 | 'symbol': true,
17 | 'text': true,
18 | 'clipPath': true,
19 | 'linearGradient': true,
20 |
21 | 'style': true,
22 | 'script': true,
23 |
24 | // http://www.w3.org/TR/SVG/intro.html#TermDescriptiveElement
25 | 'desc': true,
26 | 'metadata': true,
27 | 'title': true
28 | }
29 |
30 | // http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
31 |
32 | exports.VOID = {
33 | area: true,
34 | base: true,
35 | br: true,
36 | col: true,
37 | embed: true,
38 | hr: true,
39 | img: true,
40 | input: true,
41 | keygen: true,
42 | link: true,
43 | meta: true,
44 | param: true,
45 | source: true,
46 | track: true,
47 | wbr: true
48 | }
49 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 |
2 | var init = require('./init')
3 | var modules = require('./modules')
4 |
5 | var toHTML = init([
6 | modules.attributes,
7 | modules.props,
8 | modules.class,
9 | modules.style,
10 | modules.dataset
11 | ])
12 |
13 | module.exports = toHTML
14 |
--------------------------------------------------------------------------------
/init.js:
--------------------------------------------------------------------------------
1 |
2 | var escape = require('lodash.escape')
3 | var parseSelector = require('parse-sel')
4 | var VOID_ELEMENTS = require('./elements').VOID
5 | var CONTAINER_ELEMENTS = require('./elements').CONTAINER
6 |
7 | module.exports = function init (modules) {
8 | function parse (vnode, node) {
9 | var result = []
10 | var attributes = new Map([
11 | // These can be overwritten because that’s what happens in snabbdom
12 | ['id', node.id],
13 | ['class', node.className]
14 | ])
15 |
16 | modules.forEach(function (fn, index) {
17 | fn(vnode, attributes)
18 | })
19 | attributes.forEach(function (value, key) {
20 | if (value && value !== '') {
21 | result.push(key + '="' + value + '"')
22 | }
23 | })
24 |
25 | return result.join(' ')
26 | }
27 |
28 | return function renderToString (vnode) {
29 | if (typeof vnode === 'undefined' || vnode === null) {
30 | return ''
31 | }
32 |
33 | if (!vnode.sel && typeof vnode.text === 'string') {
34 | return escape(vnode.text)
35 | }
36 |
37 | vnode.data = vnode.data || {}
38 |
39 | // Support thunks
40 | if (vnode.data.hook &&
41 | typeof vnode.data.hook.init === 'function' &&
42 | typeof vnode.data.fn === 'function') {
43 | vnode.data.hook.init(vnode)
44 | }
45 |
46 | var node = parseSelector(vnode.sel)
47 | var tagName = node.tagName
48 | var attributes = parse(vnode, node)
49 | var svg = vnode.data.ns === 'http://www.w3.org/2000/svg'
50 | var tag = []
51 |
52 | if (tagName === '!') {
53 | return ''
54 | }
55 |
56 | // Open tag
57 | tag.push('<' + tagName)
58 | if (attributes.length) {
59 | tag.push(' ' + attributes)
60 | }
61 | if (svg && CONTAINER_ELEMENTS[tagName] !== true) {
62 | tag.push(' /')
63 | }
64 | tag.push('>')
65 |
66 | // Close tag, if needed
67 | if ((VOID_ELEMENTS[tagName] !== true && !svg) ||
68 | (svg && CONTAINER_ELEMENTS[tagName] === true)) {
69 | if (vnode.data.props && vnode.data.props.innerHTML) {
70 | tag.push(vnode.data.props.innerHTML)
71 | } else if (vnode.text) {
72 | tag.push(escape(vnode.text))
73 | } else if (vnode.children) {
74 | vnode.children.forEach(function (child) {
75 | tag.push(renderToString(child))
76 | })
77 | }
78 | tag.push('' + tagName + '>')
79 | }
80 |
81 | return tag.join('')
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/modules/attributes.js:
--------------------------------------------------------------------------------
1 |
2 | var forOwn = require('lodash.forown')
3 | var escape = require('lodash.escape')
4 |
5 | // data.attrs
6 |
7 | module.exports = function attrsModule (vnode, attributes) {
8 | var attrs = vnode.data.attrs || {}
9 |
10 | forOwn(attrs, function (value, key) {
11 | attributes.set(key, escape(value))
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/modules/class.js:
--------------------------------------------------------------------------------
1 |
2 | var forOwn = require('lodash.forown')
3 | var remove = require('lodash.remove')
4 | var uniq = require('lodash.uniq')
5 |
6 | // data.class
7 |
8 | module.exports = function classModule (vnode, attributes) {
9 | var values
10 | var _add = []
11 | var _remove = []
12 | var classes = vnode.data.class || {}
13 | var existing = attributes.get('class')
14 | existing = existing.length > 0 ? existing.split(' ') : []
15 |
16 | forOwn(classes, function (value, key) {
17 | if (value) {
18 | _add.push(key)
19 | } else {
20 | _remove.push(key)
21 | }
22 | })
23 |
24 | values = remove(uniq(existing.concat(_add)), function (value) {
25 | return _remove.indexOf(value) < 0
26 | })
27 |
28 | if (values.length) {
29 | attributes.set('class', values.join(' '))
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/modules/dataset.js:
--------------------------------------------------------------------------------
1 |
2 | var forOwn = require('lodash.forown')
3 | var escape = require('lodash.escape')
4 |
5 | // data.dataset
6 |
7 | module.exports = function datasetModule (vnode, attributes) {
8 | var dataset = vnode.data.dataset || {}
9 |
10 | forOwn(dataset, function (value, key) {
11 | attributes.set(`data-${key}`, escape(value))
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/modules/index.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = {
3 | class: require('./class'),
4 | props: require('./props'),
5 | attributes: require('./attributes'),
6 | style: require('./style'),
7 | dataset: require('./dataset')
8 | }
9 |
--------------------------------------------------------------------------------
/modules/props.js:
--------------------------------------------------------------------------------
1 |
2 | var forOwn = require('lodash.forown')
3 | var escape = require('lodash.escape')
4 |
5 | // https://developer.mozilla.org/en-US/docs/Web/API/element
6 | var omit = [
7 | 'attributes',
8 | 'childElementCount',
9 | 'children',
10 | 'classList',
11 | 'clientHeight',
12 | 'clientLeft',
13 | 'clientTop',
14 | 'clientWidth',
15 | 'currentStyle',
16 | 'firstElementChild',
17 | 'innerHTML',
18 | 'lastElementChild',
19 | 'nextElementSibling',
20 | 'ongotpointercapture',
21 | 'onlostpointercapture',
22 | 'onwheel',
23 | 'outerHTML',
24 | 'previousElementSibling',
25 | 'runtimeStyle',
26 | 'scrollHeight',
27 | 'scrollLeft',
28 | 'scrollLeftMax',
29 | 'scrollTop',
30 | 'scrollTopMax',
31 | 'scrollWidth',
32 | 'tabStop',
33 | 'tagName'
34 | ]
35 |
36 | // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes
37 | var booleanAttributes = [
38 | 'disabled',
39 | 'visible',
40 | 'checked',
41 | 'readonly',
42 | 'required',
43 | 'allowfullscreen',
44 | 'autofocus',
45 | 'autoplay',
46 | 'compact',
47 | 'controls',
48 | 'default',
49 | 'formnovalidate',
50 | 'hidden',
51 | 'ismap',
52 | 'itemscope',
53 | 'loop',
54 | 'multiple',
55 | 'muted',
56 | 'noresize',
57 | 'noshade',
58 | 'novalidate',
59 | 'nowrap',
60 | 'open',
61 | 'reversed',
62 | 'seamless',
63 | 'selected',
64 | 'sortable',
65 | 'truespeed',
66 | 'typemustmatch'
67 | ]
68 |
69 | // data.props
70 |
71 | module.exports = function propsModule (vnode, attributes) {
72 | var props = vnode.data.props || {}
73 |
74 | forOwn(props, function (value, key) {
75 | if (omit.indexOf(key) > -1) {
76 | return
77 | }
78 | if (key === 'htmlFor') {
79 | key = 'for'
80 | }
81 | if (key === 'className') {
82 | key = 'class'
83 | }
84 |
85 | var lkey = key.toLowerCase()
86 | if (~booleanAttributes.indexOf(lkey)) {
87 | if (value) { // set attr only when truthy
88 | attributes.set(lkey, lkey)
89 | }
90 | } else {
91 | attributes.set(lkey, escape(value))
92 | }
93 | })
94 | }
95 |
--------------------------------------------------------------------------------
/modules/style.js:
--------------------------------------------------------------------------------
1 |
2 | var assign = require('object-assign')
3 | var forOwn = require('lodash.forown')
4 | var escape = require('lodash.escape')
5 | var kebabCase = require('lodash.kebabcase')
6 |
7 | // data.style
8 |
9 | module.exports = function styleModule (vnode, attributes) {
10 | var values = []
11 | var style = vnode.data.style || {}
12 |
13 | // merge in `delayed` properties
14 | if (style.delayed) {
15 | assign(style, style.delayed)
16 | }
17 |
18 | forOwn(style, function (value, key) {
19 | // omit hook objects
20 | if (typeof value === 'string' || typeof value === 'number') {
21 | var kebabKey = kebabCase(key)
22 | values.push((key.match(/^--.*/) ? '--' + kebabKey : kebabKey) + ': ' + escape(value))
23 | }
24 | })
25 |
26 | if (values.length) {
27 | attributes.set('style', values.join('; '))
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "snabbdom-to-html",
3 | "version": "7.1.0",
4 | "description": "Render Snabbdom Vnodes to HTML strings",
5 | "main": "index.js",
6 | "typings": "./type-definitions/snabbdom-to-html.d.ts",
7 | "scripts": {
8 | "pretest": "standard",
9 | "test": "tape test/*.js",
10 | "disc": "browserify ./index.js --full-paths | discify --open"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/acstll/snabbdom-to-html.git"
15 | },
16 | "keywords": [
17 | "virtual",
18 | "dom",
19 | "html",
20 | "snabbdom"
21 | ],
22 | "author": "acstll",
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/acstll/snabbdom-to-html/issues"
26 | },
27 | "homepage": "https://github.com/acstll/snabbdom-to-html#readme",
28 | "dependencies": {
29 | "lodash.escape": "^4.0.1",
30 | "lodash.forown": "^4.4.0",
31 | "lodash.kebabcase": "^4.1.1",
32 | "lodash.remove": "^4.7.0",
33 | "lodash.uniq": "^4.5.0",
34 | "object-assign": "^4.1.0",
35 | "parse-sel": "^1.0.0"
36 | },
37 | "devDependencies": {
38 | "browserify": "^17.0.0",
39 | "disc": "^1.3.2",
40 | "snabbdom": "^3.0.3",
41 | "standard": "^12.0.1",
42 | "tape": "^5.2.2"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 |
2 | var test = require('tape')
3 | var snabbdom = require('snabbdom')
4 | var toHTML = require('../')
5 | var init = require('../init')
6 | var modules = require('../modules')
7 | var h = snabbdom.h
8 | var thunk = snabbdom.thunk
9 |
10 | test('Main export', function (t) {
11 | t.equal(typeof toHTML, 'function', 'is function')
12 | t.equal(toHTML(h('i', { props: { title: 'Italics' } }, ':-)')), ':-) ', 'and it works')
13 |
14 | t.end()
15 | })
16 |
17 | test('No modules', function (t) {
18 | var vnode
19 | var renderToString = init([])
20 |
21 | vnode = h('div')
22 | t.equal(renderToString(vnode), '
', 'no content')
23 |
24 | vnode = h('div', 'Hello World')
25 | t.equal(renderToString(vnode), 'Hello World
', 'tag and content')
26 |
27 | vnode = h('br')
28 | t.equal(renderToString(vnode), ' ', 'void element')
29 |
30 | vnode = h('div', [
31 | h('p', 'Paragraph 1'),
32 | h('p', [h('img')]),
33 | h('ul', [
34 | h('li', '1'),
35 | h('li', '2'),
36 | h('li', '3')
37 | ])
38 | ])
39 | t.equal(renderToString(vnode), 'Paragraph 1
', 'nesting')
40 |
41 | vnode = h('div', [
42 | undefined,
43 | null
44 | ])
45 | t.equal(renderToString(vnode), '
', 'null / undefined')
46 |
47 | vnode = h('!', 'comment text')
48 | t.equal(renderToString(vnode), '', 'comment')
49 |
50 | t.end()
51 | })
52 |
53 | test('Modules', function (t) {
54 | var vnode
55 | var html
56 | var renderToString = init([
57 | modules.class,
58 | modules.props,
59 | modules.attributes,
60 | modules.style,
61 | modules.dataset
62 | ])
63 |
64 | // style
65 |
66 | vnode = h('div', {
67 | style: {
68 | backgroundColor: 'cyan'
69 | }
70 | })
71 | t.equal(renderToString(vnode), '
', 'style 1')
72 |
73 | vnode = h('div', {
74 | style: {
75 | color: 'red',
76 | fontSize: '2em',
77 | lineHeight: 1.3
78 | }
79 | })
80 | t.equal(renderToString(vnode), '
', 'style 2')
81 |
82 | // `delayed` and hook properties
83 |
84 | vnode = h('div', {
85 | style: {
86 | fontSize: '100%',
87 | color: 'blue',
88 | delayed: {
89 | color: 'white'
90 | },
91 | remove: {
92 | color: 'black'
93 | },
94 | destroy: {
95 | color: 'cyan'
96 | }
97 | }
98 | })
99 | t.equal(renderToString(vnode), '
', 'style 3')
100 |
101 | // props
102 |
103 | vnode = h('a', {
104 | props: {
105 | href: 'http://github.com',
106 | target: '_blank'
107 | }
108 | }, 'Github')
109 | t.equal(renderToString(vnode), 'Github ', 'props 1')
110 |
111 | vnode = h('a#github', {
112 | props: {
113 | className: 'a b',
114 | href: 'http://github.com',
115 | target: '_blank'
116 | }
117 | }, 'Github')
118 | t.equal(renderToString(vnode), 'Github ', 'props 2')
119 |
120 | vnode = h('a#github', {
121 | props: {
122 | id: 'overridden',
123 | href: 'http://github.com'
124 | }
125 | }, 'Github')
126 | t.equal(renderToString(vnode), 'Github ', 'props 3, id override')
127 |
128 | vnode = h('a#github', {
129 | props: {
130 | innerHTML: 'Github ',
131 | href: 'http://github.com',
132 | target: '_blank'
133 | }
134 | }, 'Github')
135 | t.equal(renderToString(vnode), 'Github ', 'props 4, innerHTML')
136 |
137 | // attrs
138 |
139 | vnode = h('a', {
140 | attrs: {
141 | href: 'http://github.com',
142 | target: '_blank'
143 | }
144 | }, 'Github')
145 | t.equal(renderToString(vnode), 'Github ', 'attrs 1')
146 |
147 | vnode = h('a#github', {
148 | attrs: {
149 | class: 'a b',
150 | href: 'http://github.com',
151 | target: '_blank'
152 | }
153 | }, 'Github')
154 | t.equal(renderToString(vnode), 'Github ', 'attrs 2')
155 |
156 | vnode = h('img', {
157 | attrs: {
158 | src: 'https://test.com'
159 | },
160 | class: {
161 | selected: 'true'
162 | }
163 | })
164 | t.equal(renderToString(vnode), ' ', 'class truthy check')
165 |
166 | vnode = h('a#github', {
167 | attrs: {
168 | id: 'overridden',
169 | href: 'http://github.com'
170 | }
171 | }, 'Github')
172 | t.equal(renderToString(vnode), 'Github ', 'attrs 3, id override')
173 |
174 | html =
175 | '' +
176 | 'Balls ' +
177 | '' +
178 | ' ' +
179 | ' ' +
180 | ' '
181 | vnode = h('svg', {
182 | attrs: {
183 | width: '92',
184 | height: '38',
185 | viewBox: '0 0 92 38',
186 | xmlns: 'http://www.w3.org/2000/svg',
187 | 'xmlns:xlink': 'http://www.w3.org/1999/xlink'
188 | }
189 | }, [
190 | h('title', 'Balls'),
191 | h('g', {
192 | attrs: {
193 | fill: 'none',
194 | 'fill-rule': 'evenodd',
195 | stroke: '#979797',
196 | 'stroke-width': 3
197 | }
198 | }, [
199 | h('circle', { props: { cx: '19', cy: '19', r: '17' } }),
200 | h('circle', { props: { cx: '73', cy: '19', r: '17' } })
201 | ])
202 | ])
203 | t.equal(renderToString(vnode), html, 'svg')
204 |
205 | html = 'Hello world '
206 | vnode = h('svg', [
207 | h('text', 'Hello world')
208 | ])
209 | t.equal(renderToString(vnode), html, 'svg')
210 |
211 | html = ' '
212 | vnode = h('svg', [
213 | h('style', '.foo: { fill: red; }')
214 | ])
215 | t.equal(renderToString(vnode), html, 'svg style')
216 |
217 | html = ' '
218 | vnode = h('svg', [
219 | h('script', 'var a = 42;')
220 | ])
221 | t.equal(renderToString(vnode), html, 'svg script')
222 |
223 | vnode = h('label', {
224 | props: {
225 | htmlFor: 'beep'
226 | }
227 | }, [
228 | 'Edge case ',
229 | h('input', { attrs: { type: 'text', value: 'Shit' } })
230 | ])
231 | t.equal(renderToString(vnode), 'Edge case ', 'htmlFor, nested tag and text together')
232 |
233 | // class
234 |
235 | vnode = h('p', {
236 | class: {
237 | yes: true,
238 | no: false
239 | }
240 | }, 'Text')
241 | t.equal(renderToString(vnode), 'Text
', 'class 1')
242 |
243 | vnode = h('p.yes.no', {
244 | class: {
245 | yes: true,
246 | no: false
247 | }
248 | }, 'Text')
249 | t.equal(renderToString(vnode), 'Text
', 'class 2')
250 |
251 | // classList behaviour
252 | vnode = h('p.yes.no', {
253 | class: {
254 | no: false,
255 | else: true
256 | }
257 | }, 'Text')
258 | t.equal(renderToString(vnode), 'Text
', 'class 3')
259 |
260 | vnode = h('p.yes.no', {
261 | attrs: {
262 | class: 'something else'
263 | }
264 | }, 'Text')
265 | t.equal(renderToString(vnode), 'Text
', 'class 4')
266 |
267 | // dataset
268 |
269 | vnode = h('div', {
270 | dataset: {
271 | foo: 'bar',
272 | answer: 42
273 | }
274 | }, '')
275 | t.equal(renderToString(vnode), '
', 'dataset 1')
276 |
277 | // altogether "randomly"
278 |
279 | vnode = h('h1#happy.regular', { props: { title: 'Cheers' } }, 'Happy Birthday')
280 | t.equal(renderToString(vnode), 'Happy Birthday ', 'altogether')
281 |
282 | t.end()
283 | })
284 |
285 | test('Protect against `data` being undefined', function (t) {
286 | var vnode = h('div')
287 | vnode.data = undefined
288 |
289 | t.doesNotThrow(function () {
290 | return toHTML(vnode)
291 | })
292 |
293 | t.end()
294 | })
295 |
296 | test('Support thunks', function (t) {
297 | var vnode = thunk('span', numberInSpan, [22])
298 |
299 | function numberInSpan (n) {
300 | return h('span', 'Number is ' + n)
301 | }
302 |
303 | t.equal(toHTML(vnode), 'Number is 22 ')
304 |
305 | t.end()
306 | })
307 |
308 | test('Custom CSS properties', function (t) {
309 | var vnode = h('div', { style: { '--customColor': '#000' } })
310 |
311 | t.equal(toHTML(vnode), '
')
312 |
313 | t.end()
314 | })
315 |
316 | test('Escape HTML in text', function (t) {
317 | var vnode = h('div', ['
'])
318 |
319 | t.equal(toHTML(vnode), '<p></p>
')
320 |
321 | vnode = h('div', '
')
322 |
323 | t.equal(toHTML(vnode), '<p></p>
')
324 |
325 | t.end()
326 | })
327 |
328 | test('Empty string in children', function (t) {
329 | var vnode = h('span', [''])
330 | var htmlString = ' '
331 |
332 | t.equal(toHTML(vnode), htmlString)
333 |
334 | vnode = h('span', [])
335 |
336 | t.equal(toHTML(vnode), htmlString)
337 |
338 | vnode = h('span', '')
339 |
340 | t.equal(toHTML(vnode), htmlString)
341 |
342 | t.end()
343 | })
344 |
345 | test('Boolean attributes', function (t) {
346 | // truthy case
347 | var vnode = h('input', { props: { type: 'checkbox', checked: true } })
348 | var htmlString = ' '
349 |
350 | t.equal(toHTML(vnode), htmlString)
351 |
352 | // falsy case
353 | vnode = h('input', { props: { type: 'checkbox', checked: false } })
354 | htmlString = ' '
355 |
356 | t.equal(toHTML(vnode), htmlString)
357 |
358 | t.end()
359 | })
360 |
361 | test('svg container elements', function (t) {
362 | var vnode = h('svg',
363 | { attrs: { xmlns: 'http://www.w3.org/2000/svg' } },
364 | [h('defs', [h('clipPath', [h('rect', { attrs: { x: 0, y: 1, width: 2, height: 3 } })])])]
365 | )
366 | var htmlString = ' '
367 |
368 | t.equal(toHTML(vnode), htmlString)
369 |
370 | vnode = h('svg', [
371 | h('linearGradient', [
372 | h('stop')
373 | ])
374 | ])
375 | htmlString = ' '
376 | t.equal(toHTML(vnode), htmlString)
377 |
378 | t.end()
379 | })
380 |
--------------------------------------------------------------------------------
/type-definitions/snabbdom-to-html.d.ts:
--------------------------------------------------------------------------------
1 | declare module "snabbdom-to-html-common" {
2 | import {VNode} from "snabbdom/vnode"
3 |
4 | interface Module {
5 | (vnode: VNode, attributes: Map): void;
6 | }
7 |
8 | interface ModuleIndex {
9 | class: Module;
10 | props: Module;
11 | attributes: Module;
12 | style: Module;
13 | }
14 |
15 | export {
16 | VNode,
17 | ModuleIndex,
18 | Module
19 | }
20 | }
21 |
22 | declare module "snabbdom-to-html" {
23 | import {VNode} from "snabbdom-to-html-common";
24 | function toHTML(vnode: VNode): string;
25 | export = toHTML
26 | }
27 |
28 | declare module "snabbdom-to-html/init" {
29 | import {VNode, Module, ModuleIndex} from "snabbdom-to-html-common";
30 | function init (modules: Module[]): (vnode: VNode) => string;
31 | export = init
32 | }
33 |
34 |
35 | declare module "snabbdom-to-html/modules" {
36 | import {ModuleIndex} from "snabbdom-to-html-common";
37 | let modules: ModuleIndex;
38 | export = modules
39 | }
40 |
41 | declare module "snabbdom-to-html/modules/index" {
42 | import {ModuleIndex} from "snabbdom-to-html-common";
43 | let modules: ModuleIndex;
44 | export = modules
45 | }
46 |
47 | declare module "snabbdom-to-html/modules/attributes" {
48 | import {Module} from "snabbdom-to-html-common";
49 | let attrModule: Module;
50 | export = attrModule;
51 | }
52 |
53 | declare module "snabbdom-to-html/modules/class" {
54 | import {Module} from "snabbdom-to-html-common";
55 | let classModule: Module;
56 | export = classModule;
57 | }
58 |
59 | declare module "snabbdom-to-html/modules/props" {
60 | import {Module} from "snabbdom-to-html-common";
61 | let propsModule: Module;
62 | export = propsModule;
63 | }
64 |
65 | declare module "snabbdom-to-html/modules/style" {
66 | import {Module} from "snabbdom-to-html-common";
67 | let styleModule: Module;
68 | export = styleModule;
69 | }
70 |
--------------------------------------------------------------------------------