All files / json-ml toHtml.ts

100% Statements 36/36
100% Branches 22/22
100% Functions 4/4
100% Lines 23/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36    19x   6x   1x 38x 31x 31x 31x 31x 31x 31x 31x 31x 28x 14x 14x   31x   14x 26x 31x 25x 25x 25x 25x              
import type {JsonMlNode} from './types';
 
const escapeText = (str: string): string => str.replace(/[\u00A0-\u9999<>\&]/gim, (i) => '&#' + i.charCodeAt(0) + ';');
 
const escapeAttr = (str: string): string => str.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;');
 
export const toHtml = (node: JsonMlNode, tab: string = '', ident: string = ''): string => {
  if (typeof node === 'string') return ident + escapeText(node);
  const [tag, attrs, ...children] = node;
  const childrenLength = children.length;
  const isFragment = !tag;
  const childrenIdent = ident + (isFragment ? '' : tab);
  const doIdent = !!tab;
  let childrenStr = '';
  let textOnlyChildren = true;
  for (let i = 0; i < childrenLength; i++)
    if (typeof children[i] !== 'string') {
      textOnlyChildren = false;
      break;
    }
  if (textOnlyChildren) for (let i = 0; i < childrenLength; i++) childrenStr += escapeText(children[i] as string);
  else
    for (let i = 0; i < childrenLength; i++)
      childrenStr += (doIdent ? (!isFragment || i ? '\n' : '') : '') + toHtml(children[i], tab, childrenIdent);
  if (isFragment) return childrenStr;
  let attrStr = '';
  if (attrs) for (const key in attrs) attrStr += ' ' + key + '="' + escapeAttr(attrs[key] + '') + '"';
  const htmlHead = '<' + tag + attrStr;
  return (
    ident +
    (childrenStr
      ? htmlHead + '>' + childrenStr + (doIdent && !textOnlyChildren ? '\n' + ident : '') + '</' + tag + '>'
      : htmlHead + ' />')
  );
};