All files / src/libraries/parse index.ts

94.12% Statements 48/51
88.24% Branches 30/34
100% Functions 6/6
93.62% Lines 44/47

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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 985x 5x   5x 5x     5x     925x 925x       5x     74x 27x           36x     36x 3x 33x 1x 32x   32x       32x 2x 30x 30x   30x     36x           27x   26x 26x 26x           74x 39x   39x 12x       27x     47x 8x   8x 8x             47x   47x     5x 14x 48x 48x 48x     5x  
import { css, decode } from "frontity";
import { parse as himalaya } from "himalaya";
import { Element, Node, Parse, Attributes, AdaptNode } from "../../../types";
import htmlAttributes from "./attributes/html.json";
import svgAttributes from "./attributes/svg.json";
 
// Map of lowercased HTML and SVG attributes to get their camelCase version.
const attributesMap: Attributes = htmlAttributes
  .concat(svgAttributes)
  .reduce((map, value) => {
    map[value.toLowerCase()] = value;
    return map;
  }, {});
 
// Adapts the Himalaya AST Specification v1 to our format.
const adaptNode: AdaptNode = (himalayaNode, parent) => {
  let node: Node;
 
  if (himalayaNode.type === "element") {
    node = {
      type: himalayaNode.type,
      component: himalayaNode.tagName,
      props: himalayaNode.attributes.reduce(
        (props: Element["props"], { key, value }) => {
          // Wordpress returns links with HTML escaped entitites so we have to decode them
          if (typeof value === "string") value = decode(value);
 
          // mapping from HTML attribute names to react names:  // https://github.com/facebook/react/blob/58b8797b7372c9296e65e08ce8297e4a394b7972/packages/react-dom/src/shared/DOMProperty.js#L241-L244
          if (key === "class") {
            props.className = value;
          } else if (key === "for") {
            props.htmlFor = value;
          } else Iif (key === "accept-charset") {
            props["acceptCharset"] = value;
          } else Iif (key === "http-equiv") {
            props["httpEquiv"] = value;
 
            // Add inline styles to the component with `emotion`.
          } else if (key === "style") {
            props.css = css(value);
          } else Eif (!/^on/.test(key)) {
            const camelCaseKey = attributesMap[key.toLowerCase()];
            // Map keys with no value to `true` booleans.
            props[camelCaseKey || key] = value === null ? true : value;
          }
 
          return props;
        },
        {}
      ),
    };
 
    node.children = himalayaNode.children.reduce(
      (tree: Node[], child): Node[] => {
        const childAdapted = adaptNode(child, node as Element);
        if (childAdapted) tree.push(childAdapted);
        return tree;
      },
      []
    );
  }
 
  if (himalayaNode.type === "text") {
    const content = decode(himalayaNode.content);
 
    if (content.trim().length) {
      node = {
        type: himalayaNode.type,
        content: content,
      };
    } else return null;
  }
 
  if (himalayaNode.type === "comment") {
    const content = decode(himalayaNode.content);
 
    Eif (content.trim().length) {
      node = {
        type: himalayaNode.type,
        content: content,
      };
    } else return null;
  }
 
  if (parent) node.parent = parent;
 
  return node;
};
 
const parse: Parse = (html) =>
  himalaya(html).reduce((tree: Node[], element) => {
    const adapted = adaptNode(element);
    if (adapted) tree.push(adapted);
    return tree;
  }, []);
 
export default parse;