function remarkEscapeMarkdownEntities() {
  function transform(node, index) {
    /**
     * Shortcode nodes will intentionally inject markdown entities in text node
     * children not be escaped.
     */
    if (has(node.data, 'shortcode')) return node;

    const children = node.children ? { children: node.children.map(transform) } : {};

    /**
     * Escape characters in text and html nodes only. We store a lot of normal
     * text in html nodes to keep Remark from escaping html entities.
     */
    if (['text', 'html'].includes(node.type) && node.value) {
      /**
       * Escape all characters if this is the first child node, otherwise only
       * common characters.
       */
      const value = index === 0 ? escapeAllChars(node.value) : escapeCommonChars(node.value);
      return { ...node, value, ...children };
    }

    /**
     * Always return nodes with recursively mapped children.
     */
    return { ...node, ...children };
  }

  return transform;
}
A Remark plugin for escaping markdown entities.

When markdown entities are entered in raw markdown, they don't appear as
characters in the resulting AST; for example, dashes surrounding a piece of
text cause the text to be inserted in a special node type, but the asterisks
themselves aren't present as text. Therefore, we generally don't expect to
encounter markdown characters in text nodes.

However, the CMS visual editor does not interpret markdown characters, and
users will expect these characters to be represented literally. In that case,
we need to escape them, otherwise they'll be interpreted during
stringification.