You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							222 lines
						
					
					
						
							4.5 KiB
						
					
					
				
			
		
		
	
	
							222 lines
						
					
					
						
							4.5 KiB
						
					
					
				const SINGLE_TAGS = [ | 
						|
  'area', | 
						|
  'base', | 
						|
  'br', | 
						|
  'col', | 
						|
  'command', | 
						|
  'embed', | 
						|
  'hr', | 
						|
  'img', | 
						|
  'input', | 
						|
  'keygen', | 
						|
  'link', | 
						|
  'menuitem', | 
						|
  'meta', | 
						|
  'param', | 
						|
  'source', | 
						|
  'track', | 
						|
  'wbr' | 
						|
]; | 
						|
 | 
						|
const ATTRIBUTE_QUOTES_REQUIRED = /[\t\n\f\r "'`=<>]/; | 
						|
 | 
						|
/** 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<String|RegExp>} singleTags  Custom single tags (selfClosing) | 
						|
   * @prop {String} closingSingleTag Closing format for single tag @prop | 
						|
   * @prop {Boolean} quoteAllAttributes If all attributes should be quoted. | 
						|
   * Otherwise attributes will be unquoted when allowed. | 
						|
   * @prop {Boolean} replaceQuote Replaces quotes in attribute values with `"e;` | 
						|
   * | 
						|
   * Formats: | 
						|
   * | 
						|
   * ``` tag: `<br></br>` ```, slash: `<br />` ```, ```default: `<br>` ``` | 
						|
   */ | 
						|
  options = options || {}; | 
						|
 | 
						|
  const singleTags = options.singleTags ? SINGLE_TAGS.concat(options.singleTags) : SINGLE_TAGS; | 
						|
  const singleRegExp = singleTags.filter(tag => { | 
						|
    return tag instanceof RegExp; | 
						|
  }); | 
						|
 | 
						|
  const {closingSingleTag} = options; | 
						|
  let {quoteAllAttributes} = options; | 
						|
  if (quoteAllAttributes === undefined) { | 
						|
    quoteAllAttributes = true; | 
						|
  } | 
						|
 | 
						|
  let {replaceQuote} = options; | 
						|
  if (replaceQuote === undefined) { | 
						|
    replaceQuote = true; | 
						|
  } | 
						|
 | 
						|
  let {quoteStyle} = options; | 
						|
  if (quoteStyle === undefined) { | 
						|
    quoteStyle = 2; | 
						|
  } | 
						|
 | 
						|
  return html(tree); | 
						|
 | 
						|
  /** @private */ | 
						|
  function isSingleTag(tag) { | 
						|
    if (singleRegExp.length > 0) { | 
						|
      return singleRegExp.some(reg => reg.test(tag)); | 
						|
    } | 
						|
 | 
						|
    if (!singleTags.includes(tag)) { | 
						|
      return false; | 
						|
    } | 
						|
 | 
						|
    return true; | 
						|
  } | 
						|
 | 
						|
  /** @private */ | 
						|
  function attrs(object) { | 
						|
    let attr = ''; | 
						|
 | 
						|
    for (const key in object) { | 
						|
      if (typeof object[key] === 'string') { | 
						|
        if (quoteAllAttributes || object[key].match(ATTRIBUTE_QUOTES_REQUIRED)) { | 
						|
          let attrValue = object[key]; | 
						|
 | 
						|
          if (replaceQuote) { | 
						|
            attrValue = object[key].replace(/"/g, '"'); | 
						|
          } | 
						|
 | 
						|
          attr += makeAttr(key, attrValue, quoteStyle); | 
						|
        } else if (object[key] === '') { | 
						|
          attr += ' ' + key; | 
						|
        } else { | 
						|
          attr += ' ' + key + '=' + object[key]; | 
						|
        } | 
						|
      } else if (object[key] === true) { | 
						|
        attr += ' ' + key; | 
						|
      } else if (typeof object[key] === 'number') { | 
						|
        attr += makeAttr(key, object[key], quoteStyle); | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    return attr; | 
						|
  } | 
						|
 | 
						|
  /** @private */ | 
						|
  function traverse(tree, cb) { | 
						|
    if (tree !== undefined) { | 
						|
      for (let i = 0, {length} = tree; i < length; i++) { | 
						|
        traverse(cb(tree[i]), cb); | 
						|
      } | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  /** @private */ | 
						|
  function makeAttr(key, attrValue, quoteStyle = 1) { | 
						|
    if (quoteStyle === 1) { | 
						|
      // Single Quote | 
						|
      return ` ${key}='${attrValue}'`; | 
						|
    } | 
						|
 | 
						|
    if (quoteStyle === 2) { | 
						|
      // Double Quote | 
						|
      return ` ${key}="${attrValue}"`; | 
						|
    } | 
						|
 | 
						|
    // Smart Quote | 
						|
    if (attrValue.includes('"')) { | 
						|
      return ` ${key}='${attrValue}'`; | 
						|
    } | 
						|
 | 
						|
    return ` ${key}="${attrValue}"`; | 
						|
  } | 
						|
 | 
						|
  /** | 
						|
   * HTML Stringifier | 
						|
   * | 
						|
   * @param  {Array|Object} tree PostHTML Tree | 
						|
   * | 
						|
   * @return {String} result HTML | 
						|
   */ | 
						|
  function html(tree) { | 
						|
    let result = ''; | 
						|
 | 
						|
    if (!Array.isArray(tree)) { | 
						|
      tree = [tree]; | 
						|
    } | 
						|
 | 
						|
    traverse(tree, node => { | 
						|
      // Undefined, null, '', [], NaN | 
						|
      if (node === undefined || | 
						|
        node === null || | 
						|
        node === false || | 
						|
        node.length === 0 || | 
						|
        Number.isNaN(node)) { | 
						|
        return; | 
						|
      } | 
						|
 | 
						|
      // Treat as new root tree if node is an array | 
						|
      if (Array.isArray(node)) { | 
						|
        result += html(node); | 
						|
 | 
						|
        return; | 
						|
      } | 
						|
 | 
						|
      if (typeof node === 'string' || typeof node === 'number') { | 
						|
        result += node; | 
						|
 | 
						|
        return; | 
						|
      } | 
						|
 | 
						|
      // Skip node | 
						|
      if (node.tag === false) { | 
						|
        result += html(node.content); | 
						|
 | 
						|
        return; | 
						|
      } | 
						|
 | 
						|
      const tag = node.tag || 'div'; | 
						|
 | 
						|
      result += '<' + tag; | 
						|
 | 
						|
      if (node.attrs) { | 
						|
        result += attrs(node.attrs); | 
						|
      } | 
						|
 | 
						|
      if (isSingleTag(tag)) { | 
						|
        switch (closingSingleTag) { | 
						|
          case 'tag': | 
						|
            result += '></' + tag + '>'; | 
						|
 | 
						|
            break; | 
						|
          case 'slash': | 
						|
            result += ' />'; | 
						|
 | 
						|
            break; | 
						|
          default: | 
						|
            result += '>'; | 
						|
        } | 
						|
 | 
						|
        result += html(node.content); | 
						|
      } else { | 
						|
        result += '>' + html(node.content) + '</' + tag + '>'; | 
						|
      } | 
						|
    }); | 
						|
 | 
						|
    return result; | 
						|
  } | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * @module posthtml-render | 
						|
 * | 
						|
 * @version 1.1.5 | 
						|
 * @license MIT | 
						|
 */ | 
						|
module.exports = render;
 | 
						|
 |