src/language-js/print/jsx.js/printJsxOpeningElement
function printJsxOpeningElement(path, options, print) {
  const node = path.getValue();

  const nameHasComments =
    (node.name && hasComment(node.name)) ||
    (node.typeParameters && hasComment(node.typeParameters));

  // Don't break self-closing elements with no attributes and no comments
  if (node.selfClosing && node.attributes.length === 0 && !nameHasComments) {
    return ["<", print("name"), print("typeParameters"), " />"];
  }

  // don't break up opening elements with a single long text attribute
  if (
    node.attributes &&
    node.attributes.length === 1 &&
    node.attributes[0].value &&
    isStringLiteral(node.attributes[0].value) &&
    !node.attributes[0].value.value.includes("\n") &&
    // We should break for the following cases:
    // <div
    //   // comment
    //   attr="value"
    // >
    // <div
    //   attr="value"
    //   // comment
    // >
    !nameHasComments &&
    !hasComment(node.attributes[0])
  ) {
    return group([
      "<",
      print("name"),
      print("typeParameters"),
      " ",
      ...path.map(print, "attributes"),
      node.selfClosing ? " />" : ">",
    ]);
  }

  const lastAttrHasTrailingComments =
    node.attributes.length > 0 &&
    hasComment(getLast(node.attributes), CommentCheckFlags.Trailing);

  const bracketSameLine =
    // Simple tags (no attributes and no comment in tag name) should be
    // kept unbroken regardless of `bracketSameLine`.
    // jsxBracketSameLine is deprecated in favour of bracketSameLine,
    // but is still needed for backwards compatibility.
    (node.attributes.length === 0 && !nameHasComments) ||
    ((options.bracketSameLine || options.jsxBracketSameLine) &&
      // We should print the bracket in a new line for the following cases:
      // <div
      //   // comment
      // >
      // <div
      //   attr // comment
      // >
      (!nameHasComments || node.attributes.length > 0) &&
      !lastAttrHasTrailingComments);

  // We should print the opening element expanded if any prop value is a
  // string literal with newlines
  const shouldBreak =
    node.attributes &&
    node.attributes.some(
      (attr) =>
        attr.value &&
        isStringLiteral(attr.value) &&
        attr.value.value.includes("\n")
    );

  const attributeLine =
    options.singleAttributePerLine && node.attributes.length > 1
      ? hardline
      : line;

  return group(
    [
      "<",
      print("name"),
      print("typeParameters"),
      indent(path.map(() => [attributeLine, print()], "attributes")),
      node.selfClosing ? line : bracketSameLine ? ">" : softline,
      node.selfClosing ? "/>" : bracketSameLine ? "" : ">",
    ],
    { shouldBreak }
  );
}