123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- 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;
|