src/language-markdown/printer-markdown.js/genericPrint
function genericPrint(path, options, print) {
  const node = path.getValue();

  if (shouldRemainTheSameContent(path)) {
    return concat(
      privateUtil
        .splitText(
          options.originalText.slice(
            node.position.start.offset,
            node.position.end.offset
          )
        )
        .map(
          node =>
            node.type === "word"
              ? node.value
              : node.value === "" ? "" : printLine(path, node.value, options)
        )
    );
  }

  switch (node.type) {
    case "root":
      return concat([
        normalizeDoc(printChildren(path, options, print)),
        hardline
      ]);
    case "paragraph":
      return printChildren(path, options, print, {
        postprocessor: fill
      });
    case "sentence":
      return printChildren(path, options, print);
    case "word":
      return node.value
        .replace(/[*]/g, "\\*") // escape all `*`
        .replace(
          new RegExp(
            [
              `(^|[${privateUtil.punctuationCharRange}])(_+)`,
              `(_+)([${privateUtil.punctuationCharRange}]|$)`
            ].join("|"),
            "g"
          ),
          (_, text1, underscore1, underscore2, text2) =>
            (underscore1
              ? `${text1}${underscore1}`
              : `${underscore2}${text2}`
            ).replace(/_/g, "\\_")
        ); // escape all `_` except concating with non-punctuation, e.g. `1_2_3` is not considered emphasis
    case "whitespace": {
      const parentNode = path.getParentNode();
      const index = parentNode.children.indexOf(node);
      const nextNode = parentNode.children[index + 1];

      const proseWrap =
        // leading char that may cause different syntax
        nextNode && /^>|^([-+*]|#{1,6}|[0-9]+[.)])$/.test(nextNode.value)
          ? "never"
          : options.proseWrap;

      return printLine(path, node.value, { proseWrap });
    }
    case "emphasis": {
      const parentNode = path.getParentNode();
      const index = parentNode.children.indexOf(node);
      const prevNode = parentNode.children[index - 1];
      const nextNode = parentNode.children[index + 1];
      const hasPrevOrNextWord = // `1*2*3` is considered emphais but `1_2_3` is not
        (prevNode &&
          prevNode.type === "sentence" &&
          prevNode.children.length > 0 &&
          privateUtil.getLast(prevNode.children).type === "word" &&
          !privateUtil.getLast(prevNode.children).hasTrailingPunctuation) ||
        (nextNode &&
          nextNode.type === "sentence" &&
          nextNode.children.length > 0 &&
          nextNode.children[0].type === "word" &&
          !nextNode.children[0].hasLeadingPunctuation);
      const style =
        hasPrevOrNextWord || getAncestorNode(path, "emphasis") ? "*" : "_";
      return concat([style, printChildren(path, options, print), style]);
    }
    case "strong":
      return concat(["**", printChildren(path, options, print), "**"]);
    case "delete":
      return concat(["~~", printChildren(path, options, print), "~~"]);
    case "inlineCode": {
      const backtickCount = privateUtil.getMaxContinuousCount(node.value, "`");
      const style = backtickCount === 1 ? "``" : "`";
      const gap = backtickCount ? " " : "";
      return concat([style, gap, node.value, gap, style]);
    }
    case "link":
      switch (options.originalText[node.position.start.offset]) {
        case "<":
          return concat(["<", node.url, ">"]);
        case "[":
          return concat([
            "[",
            printChildren(path, options, print),
            "](",
            printUrl(node.url, ")"),
            printTitle(node.title, options),
            ")"
          ]);
        default:
          return options.originalText.slice(
            node.position.start.offset,
            node.position.end.offset
          );
      }
    case "image":
      return concat([
        "![",
        node.alt || "",
        "](",
        printUrl(node.url, ")"),
        printTitle(node.title, options),
        ")"
      ]);
    case "blockquote":
      return concat(["> ", align("> ", printChildren(path, options, print))]);
    case "heading":
      return concat([
        "#".repeat(node.depth) + " ",
        printChildren(path, options, print)
      ]);
    case "code": {
      if (
        // the first char may point to `\n`, e.g. `\n\t\tbar`, just ignore it
        /^\n?( {4,}|\t)/.test(
          options.originalText.slice(
            node.position.start.offset,
            node.position.end.offset
          )
        )
      ) {
        // indented code block
        const alignment = " ".repeat(4);
        return align(
          alignment,
          concat([alignment, join(hardline, node.value.split("\n"))])
        );
      }

      // fenced code block
      const styleUnit = options.__inJsTemplate ? "~" : "`";
      const style = styleUnit.repeat(
        Math.max(
          3,
          privateUtil.getMaxContinuousCount(node.value, styleUnit) + 1
        )
      );
      return concat([
        style,
        node.lang || "",
        hardline,
        join(hardline, node.value.split("\n")),
        hardline,
        style
      ]);
    }
    case "yaml":
      return concat(["---", hardline, node.value, hardline, "---"]);
    case "toml":
      return concat(["+++", hardline, node.value, hardline, "+++"]);
    case "html": {
      const parentNode = path.getParentNode();
      return parentNode.type === "root" &&
        privateUtil.getLast(parentNode.children) === node
        ? node.value.trimRight()
        : node.value;
    }
    case "list": {
      const nthSiblingIndex = getNthListSiblingIndex(
        node,
        path.getParentNode()
      );

      const isGitDiffFriendlyOrderedList =
        node.ordered &&
        node.children.length > 1 &&
        /^\s*1(\.|\))/.test(
          options.originalText.slice(
            node.children[1].position.start.offset,
            node.children[1].position.end.offset
          )
        );

      return printChildren(path, options, print, {
        processor: (childPath, index) => {
          const prefix = getPrefix();
          return concat([
            prefix,
            align(
              " ".repeat(prefix.length),
              printListItem(childPath, options, print, prefix)
            )
          ]);

          function getPrefix() {
            const rawPrefix = node.ordered
              ? (index === 0
                  ? node.start
                  : isGitDiffFriendlyOrderedList ? 1 : node.start + index) +
                (nthSiblingIndex % 2 === 0 ? ". " : ") ")
              : nthSiblingIndex % 2 === 0 ? "* " : "- ";

            // do not print trailing spaces for empty list item since it might be treated as `break` node
            // by [doc-printer](https://github.com/prettier/prettier/blob/1.10.2/src/doc/doc-printer.js#L395-L405),
            // we don't want to preserve unnecessary trailing spaces.
            const listItem = childPath.getValue();
            return listItem.children.length
              ? alignListPrefix(rawPrefix, options)
              : rawPrefix;
          }
        }
      });
    }
    case "thematicBreak": {
      const counter = getAncestorCounter(path, "list");
      if (counter === -1) {
        return "---";
      }
      const nthSiblingIndex = getNthListSiblingIndex(
        path.getParentNode(counter),
        path.getParentNode(counter + 1)
      );
      return nthSiblingIndex % 2 === 0 ? "---" : "***";
    }
    case "linkReference":
      return concat([
        "[",
        printChildren(path, options, print),
        "]",
        node.referenceType === "full"
          ? concat(["[", node.identifier, "]"])
          : node.referenceType === "collapsed" ? "[]" : ""
      ]);
    case "imageReference":
      switch (node.referenceType) {
        case "full":
          return concat(["![", node.alt || "", "][", node.identifier, "]"]);
        default:
          return concat([
            "![",
            node.alt,
            "]",
            node.referenceType === "collapsed" ? "[]" : ""
          ]);
      }
    case "definition":
      return concat([
        "[",
        node.identifier,
        "]: ",
        printUrl(node.url),
        printTitle(node.title, options)
      ]);
    case "footnote":
      return concat(["[^", printChildren(path, options, print), "]"]);
    case "footnoteReference":
      return concat(["[^", node.identifier, "]"]);
    case "footnoteDefinition": {
      const nextNode = path.getParentNode().children[path.getName() + 1];
      return concat([
        "[^",
        node.identifier,
        "]: ",
        group(
          concat([
            align(
              " ".repeat(options.tabWidth),
              printChildren(path, options, print, {
                processor: (childPath, index) =>
                  index === 0
                    ? group(concat([softline, softline, childPath.call(print)]))
                    : childPath.call(print)
              })
            ),
            nextNode && nextNode.type === "footnoteDefinition" ? softline : ""
          ])
        )
      ]);
    }
    case "table":
      return printTable(path, options, print);
    case "tableCell":
      return printChildren(path, options, print);
    case "break":
      return concat([
        /\s/.test(options.originalText[node.position.start.offset])
          ? "  "
          : "\\",
        hardline
      ]);
    case "tableRow": // handled in "table"
    case "listItem": // handled in "list"
    default:
      throw new Error(`Unknown markdown type ${JSON.stringify(node.type)}`);
  }
}