src/language-js/print/class.js/printClass
function printClass(path, options, print) {
  const node = path.getValue();
  const parts = [];

  if (node.declare) {
    parts.push("declare ");
  }

  if (node.abstract) {
    parts.push("abstract ");
  }

  parts.push("class");

  // Keep old behaviour of extends in same line
  // If there is only on extends and there are not comments
  const groupMode =
    (node.id && hasComment(node.id, CommentCheckFlags.Trailing)) ||
    (node.typeParameters &&
      hasComment(node.typeParameters, CommentCheckFlags.Trailing)) ||
    (node.superClass && hasComment(node.superClass)) ||
    isNonEmptyArray(node.extends) || // DeclareClass
    isNonEmptyArray(node.mixins) ||
    isNonEmptyArray(node.implements);

  const partsGroup = [];
  const extendsParts = [];

  if (node.id) {
    partsGroup.push(" ", print("id"));
  }

  partsGroup.push(print("typeParameters"));

  if (node.superClass) {
    const printed = [
      printSuperClass(path, options, print),
      print("superTypeParameters"),
    ];
    const printedWithComments = path.call(
      (superClass) => ["extends ", printComments(superClass, printed, options)],
      "superClass"
    );
    if (groupMode) {
      extendsParts.push(line, group(printedWithComments));
    } else {
      extendsParts.push(" ", printedWithComments);
    }
  } else {
    extendsParts.push(printList(path, options, print, "extends"));
  }

  extendsParts.push(
    printList(path, options, print, "mixins"),
    printList(path, options, print, "implements")
  );

  if (groupMode) {
    let printedPartsGroup;
    if (shouldIndentOnlyHeritageClauses(node)) {
      printedPartsGroup = [...partsGroup, indent(extendsParts)];
    } else {
      printedPartsGroup = indent([...partsGroup, extendsParts]);
    }
    parts.push(group(printedPartsGroup, { id: getHeritageGroupId(node) }));
  } else {
    parts.push(...partsGroup, ...extendsParts);
  }

  parts.push(" ", print("body"));

  return parts;
}