src/language-js/print/function.js/printArrowFunctionExpression
function printArrowFunctionExpression(path, options, print, args) {
  const n = path.getValue();
  const parts = [];

  if (n.async) {
    parts.push("async ");
  }

  if (shouldPrintParamsWithoutParens(path, options)) {
    parts.push(path.call(print, "params", 0));
  } else {
    parts.push(
      group(
        concat([
          printFunctionParameters(
            path,
            print,
            options,
            /* expandLast */ args &&
              (args.expandLastArg || args.expandFirstArg),
            /* printTypeParams */ true
          ),
          printReturnType(path, print, options),
        ])
      )
    );
  }

  const dangling = printDanglingComments(
    path,
    options,
    /* sameIndent */ true,
    (comment) => {
      const nextCharacter = getNextNonSpaceNonCommentCharacterIndex(
        options.originalText,
        comment,
        locEnd
      );
      return (
        nextCharacter !== false &&
        options.originalText.slice(nextCharacter, nextCharacter + 2) === "=>"
      );
    }
  );
  if (dangling) {
    parts.push(" ", dangling);
  }

  parts.push(" =>");

  const body = path.call((bodyPath) => print(bodyPath, args), "body");

  // We want to always keep these types of nodes on the same line
  // as the arrow.
  if (
    !hasLeadingOwnLineComment(options.originalText, n.body) &&
    (n.body.type === "ArrayExpression" ||
      n.body.type === "ObjectExpression" ||
      n.body.type === "BlockStatement" ||
      isJSXNode(n.body) ||
      isTemplateOnItsOwnLine(n.body, options.originalText) ||
      n.body.type === "ArrowFunctionExpression" ||
      n.body.type === "DoExpression")
  ) {
    return group(concat([concat(parts), " ", body]));
  }

  // We handle sequence expressions as the body of arrows specially,
  // so that the required parentheses end up on their own lines.
  if (n.body.type === "SequenceExpression") {
    return group(
      concat([
        concat(parts),
        group(concat([" (", indent(concat([softline, body])), softline, ")"])),
      ])
    );
  }

  // if the arrow function is expanded as last argument, we are adding a
  // level of indentation and need to add a softline to align the closing )
  // with the opening (, or if it's inside a JSXExpression (e.g. an attribute)
  // we should align the expression's closing } with the line with the opening {.
  const shouldAddSoftLine =
    ((args && args.expandLastArg) ||
      path.getParentNode().type === "JSXExpressionContainer") &&
    !hasComment(n);

  const printTrailingComma =
    args && args.expandLastArg && shouldPrintComma(options, "all");

  // In order to avoid confusion between
  // a => a ? a : a
  // a <= a ? a : a
  const shouldAddParens =
    n.body.type === "ConditionalExpression" &&
    !startsWithNoLookaheadToken(n.body, /* forbidFunctionAndClass */ false);

  return group(
    concat([
      concat(parts),
      group(
        concat([
          indent(
            concat([
              line,
              shouldAddParens ? ifBreak("", "(") : "",
              body,
              shouldAddParens ? ifBreak("", ")") : "",
            ])
          ),
          shouldAddSoftLine
            ? concat([ifBreak(printTrailingComma ? "," : ""), softline])
            : "",
        ])
      ),
    ])
  );
}