var SINGLE_TAGS = [ 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr', // Custom (PostHTML) 'import', 'include', 'extend' ] /** * Render PostHTML Tree to HTML * * @param {Array|Object} tree PostHTML Tree * @param {Object} options Options * * @return {String} HTML */ function render (tree, options) { /** * Options * * @type {Object} * * @prop {Array} singleTags Custom single tags (selfClosing) * @prop {String} closingSingleTag Closing format for single tag * * Formats: * * ``` tag: `

` ```, slash: `
` ```, ```default: `
` ``` */ options = options || {} var singleTags = SINGLE_TAGS.concat(options.singleTags || []) var singleRegExp = singleTags.filter(function (tag) { return tag instanceof RegExp ? tag : false }) var closingSingleTag = options.closingSingleTag return html(tree) /** * HTML Stringifier * * @param {Array|Object} tree PostHTML Tree * * @return {String} result HTML */ function html (tree) { var result = '' traverse([].concat(tree), function (node) { if (!node) return if (typeof node === 'string' || typeof node === 'number') { result += node return } if (typeof node.tag === 'boolean' && !node.tag) { typeof node.content !== 'object' && (result += node.content) return node.content } // treat as new root tree if node is an array if (Array.isArray(node)) { result += html(node) return } var tag = node.tag || 'div' if (isSingleTag(tag, singleTags, singleRegExp)) { result += '<' + tag + attrs(node.attrs) switch (closingSingleTag) { case 'tag': result += '>' break case 'slash': result += ' />' break default: result += '>' } result += node.content ? html(node.content) : '' } else { result += '<' + tag + (node.attrs ? attrs(node.attrs) : '') + '>' + (node.content ? html(node.content) : '') + '' } }) return result } } /** * @module posthtml-render * * @version 1.0.7 * @license MIT */ module.exports = render /** @private */ function attrs (obj) { var attr = '' for (var key in obj) { if (typeof obj[key] === 'boolean' && obj[key]) { attr += ' ' + key } else if (typeof obj[key] === 'number') { attr += ' ' + key + '="' + obj[key] + '"' } else if (typeof obj[key] === 'string') { attr += ' ' + key + '="' + obj[key].replace(/"/g, '"') + '"' } } return attr } /** @private */ function traverse (tree, cb) { if (Array.isArray(tree)) { for (var i = 0, length = tree.length; i < length; i++) { traverse(cb(tree[i]), cb) } } else if (typeof tree === 'object' && tree.hasOwnProperty('content')) { traverse(tree.content, cb) } return tree } /** @private */ function isSingleTag (tag, singleTags, singleRegExp) { if (singleRegExp.length) { for (var i = 0; i < singleRegExp.length; i++) { return !!tag.match(singleRegExp[i]) } } if (singleTags.indexOf(tag) === -1) { return false } return true }