function printTernaryOperator(path, options, print, operatorOptions) {
  const node = path.getValue();
  const consequentNode = node[operatorOptions.consequentNodePropertyName];
  const alternateNode = node[operatorOptions.alternateNodePropertyName];
  const parts = []; // We print a ConditionalExpression in either "JSX mode" or "normal mode".
  // See tests/jsx/conditional-expression.js for more info.

  let jsxMode = false;
  const parent = path.getParentNode();
  const isParentTest = parent.type === operatorOptions.conditionalNodeType && operatorOptions.testNodePropertyNames.some(prop => parent[prop] === node);
  let forceNoIndent = parent.type === operatorOptions.conditionalNodeType && !isParentTest; // Find the outermost non-ConditionalExpression parent, and the outermost
  // ConditionalExpression parent. We'll use these to determine if we should
  // print in JSX mode.

  let currentParent;
  let previousParent;
  let i = 0;

  do {
    previousParent = currentParent || node;
    currentParent = path.getParentNode(i);
    i++;
  } while (currentParent && currentParent.type === operatorOptions.conditionalNodeType && operatorOptions.testNodePropertyNames.every(prop => currentParent[prop] !== previousParent));

  const firstNonConditionalParent = currentParent || parent;
  const lastConditionalParent = previousParent;

  if (operatorOptions.shouldCheckJsx && (isJSXNode$2(node[operatorOptions.testNodePropertyNames[0]]) || isJSXNode$2(consequentNode) || isJSXNode$2(alternateNode) || conditionalExpressionChainContainsJSX$1(lastConditionalParent))) {
    jsxMode = true;
    forceNoIndent = true; // Even though they don't need parens, we wrap (almost) everything in
    // parens when using ?: within JSX, because the parens are analogous to
    // curly braces in an if statement.

    const wrap = doc => concat$9([ifBreak$2("(", ""), indent$6(concat$9([softline$4, doc])), softline$4, ifBreak$2(")", "")]); // The only things we don't wrap are:
    // * Nested conditional expressions in alternates
    // * null
    // * undefined


    const isNil = node => node.type === "NullLiteral" || node.type === "Literal" && node.value === null || node.type === "Identifier" && node.name === "undefined";

    parts.push(" ? ", isNil(consequentNode) ? path.call(print, operatorOptions.consequentNodePropertyName) : wrap(path.call(print, operatorOptions.consequentNodePropertyName)), " : ", alternateNode.type === operatorOptions.conditionalNodeType || isNil(alternateNode) ? path.call(print, operatorOptions.alternateNodePropertyName) : wrap(path.call(print, operatorOptions.alternateNodePropertyName)));
  } else {
    // normal mode
    const part = concat$9([line$5, "? ", consequentNode.type === operatorOptions.conditionalNodeType ? ifBreak$2("", "(") : "", align$1(2, path.call(print, operatorOptions.consequentNodePropertyName)), consequentNode.type === operatorOptions.conditionalNodeType ? ifBreak$2("", ")") : "", line$5, ": ", alternateNode.type === operatorOptions.conditionalNodeType ? path.call(print, operatorOptions.alternateNodePropertyName) : align$1(2, path.call(print, operatorOptions.alternateNodePropertyName))]);
    parts.push(parent.type !== operatorOptions.conditionalNodeType || parent[operatorOptions.alternateNodePropertyName] === node || isParentTest ? part : options.useTabs ? dedent$1(indent$6(part)) : align$1(Math.max(0, options.tabWidth - 2), part));
  } // We want a whole chain of ConditionalExpressions to all
  // break if any of them break. That means we should only group around the
  // outer-most ConditionalExpression.


  const maybeGroup = doc => parent === firstNonConditionalParent ? group$5(doc) : doc; // Break the closing paren to keep the chain right after it:
  // (a
  //   ? b
  //   : c
  // ).call()


  const breakClosingParen = !jsxMode && (parent.type === "MemberExpression" || parent.type === "OptionalMemberExpression" || parent.type === "NGPipeExpression" && parent.left === node) && !parent.computed;
  const result = maybeGroup(concat$9([].concat((testDoc =>
  /**
   *     a
   *       ? b
   *       : multiline
   *         test
   *         node
   *       ^^ align(2)
   *       ? d
   *       : e
   */
  parent.type === operatorOptions.conditionalNodeType && parent[operatorOptions.alternateNodePropertyName] === node ? align$1(2, testDoc) : testDoc)(concat$9(operatorOptions.beforeParts())), forceNoIndent ? concat$9(parts) : indent$6(concat$9(parts)), operatorOptions.afterParts(breakClosingParen))));
  return isParentTest ? group$5(concat$9([indent$6(concat$9([softline$4, result])), softline$4])) : result;
}
The following is the shared logic for
ternary operators, namely ConditionalExpression
and TSConditionalType
@typedef {Object} OperatorOptions
@property {() => Array<string | Doc>} beforeParts - Parts to print before the `?`.
@property {(breakClosingParen: boolean) => Array<string | Doc>} afterParts - Parts to print after the conditional expression.
@property {boolean} shouldCheckJsx - Whether to check for and print in JSX mode.
@property {string} conditionalNodeType - The type of the conditional expression node, ie "ConditionalExpression" or "TSConditionalType".
@property {string} consequentNodePropertyName - The property at which the consequent node can be found on the main node, eg "consequent".
@property {string} alternateNodePropertyName - The property at which the alternate node can be found on the main node, eg "alternate".
@property {string[]} testNodePropertyNames - The properties at which the test nodes can be found on the main node, eg "test".
@param {FastPath} path - The path to the ConditionalExpression/TSConditionalType node.
@param {Options} options - Prettier options
@param {Function} print - Print function to call recursively
@param {OperatorOptions} operatorOptions
@returns Doc