text: "use strict";
  
  const getLast = require("../../utils/get-last");
  const { getStringWidth, getIndentSize } = require("../../common/util");
  const {
    builders: {
      join,
      hardline,
      softline,
      // [prettierx] --template-curly-spacing option support (...)
      line,
      group,
      indent,
      align,
      lineSuffixBoundary,
      addAlignmentToDoc,
    },
    printer: { printDocToString },
    utils: { mapDoc },
  } = require("../../document");
  const {
    isBinaryish,
    isJestEachTemplateLiteral,
    isSimpleTemplateLiteral,
    hasComment,
    isMemberExpression,
  } = require("../utils");
  
  function printTemplateLiteral(path, print, options) {
    const node = path.getValue();
    const isTemplateLiteral = node.type === "TemplateLiteral";
  
    // [prettierx] --template-curly-spacing option support (...)
    const templateCurlySpace = options.templateCurlySpacing ? " " : "";
    const templateCurlyLine = options.templateCurlySpacing ? line : softline;
  
    if (
      isTemplateLiteral &&
      isJestEachTemplateLiteral(node, path.getParentNode())
    ) {
      const printed = printJestEachTemplateLiteral(path, options, print);
      if (printed) {
        return printed;
      }
    }
    let expressionsKey = "expressions";
    if (node.type === "TSTemplateLiteralType") {
      expressionsKey = "types";
    }
    const parts = [];
  
    let expressions = path.map(print, expressionsKey);
    const isSimple = isSimpleTemplateLiteral(node);
  
    if (isSimple) {
      expressions = expressions.map(
        (doc) =>
          printDocToString(doc, {
            ...options,
            printWidth: Number.POSITIVE_INFINITY,
          }).formatted
      );
    }
  
    parts.push(lineSuffixBoundary, "`");
  
    path.each((childPath) => {
      const i = childPath.getName();
  
      parts.push(print());
  
      if (i < expressions.length) {
        // For a template literal of the following form:
        //   `someQuery {
        //     ${call({
        //       a,
        //       b,
        //     })}
        //   }`
        // the expression is on its own line (there is a \n in the previous
        // quasi literal), therefore we want to indent the JavaScript
        // expression inside at the beginning of ${ instead of the beginning
        // of the `.
        const { tabWidth } = options;
        const quasi = childPath.getValue();
        const indentSize = getIndentSize(quasi.value.raw, tabWidth);
  
        let printed = expressions[i];
  
        if (!isSimple) {
          const expression = node[expressionsKey][i];
          // Breaks at the template element boundaries (${ and }) are preferred to breaking
          // in the middle of a MemberExpression
          if (
            hasComment(expression) ||
            isMemberExpression(expression) ||
            expression.type === "ConditionalExpression" ||
            expression.type === "SequenceExpression" ||
            expression.type === "TSAsExpression" ||
            isBinaryish(expression)
          ) {
            // [prettierx] --template-curly-spacing option support (...)
            printed = [indent([templateCurlyLine, printed]), templateCurlyLine];
          } else {
            // [prettierx] --template-curly-spacing option support (...)
            printed = [templateCurlySpace, printed, templateCurlySpace];
          }
        }
  
        const aligned =
          indentSize === 0 && quasi.value.raw.endsWith("\n")
            ? align(Number.NEGATIVE_INFINITY, printed)
            : addAlignmentToDoc(printed, indentSize, tabWidth);
  
        parts.push(group(["${", aligned, lineSuffixBoundary, "}"]));
      }
    }, "quasis");
  
    parts.push("`");
  
    return parts;
  }
  
  function printJestEachTemplateLiteral(path, options, print) {
    /**
     * a    | b    | expected
     * ${1} | ${1} | ${2}
     * ${1} | ${2} | ${3}
     * ${2} | ${1} | ${3}
     */
    const node = path.getNode();
    const headerNames = node.quasis[0].value.raw.trim().split(/\s*\|\s*/);
    if (
      headerNames.length > 1 ||
      headerNames.some((headerName) => headerName.length > 0)
    ) {
      options.__inJestEach = true;
      const expressions = path.map(print, "expressions");
      options.__inJestEach = false;
      const parts = [];
  
      // [prettierx] --template-curly-spacing option support (...)
      const templateCurlySpace = options.templateCurlySpacing ? " " : "";
  
      const stringifiedExpressions = expressions.map(
        (doc) =>
          // [prettierx] --template-curly-spacing option support (...)
          "${" +
          templateCurlySpace +
          printDocToString(doc, {
            ...options,
            printWidth: Number.POSITIVE_INFINITY,
            endOfLine: "lf",
          }).formatted +
          templateCurlySpace +
          "}"
      );
  
      const tableBody = [{ hasLineBreak: false, cells: [] }];
      for (let i = 1; i < node.quasis.length; i++) {
        const row = getLast(tableBody);
        const correspondingExpression = stringifiedExpressions[i - 1];
  
        row.cells.push(correspondingExpression);
        if (correspondingExpression.includes("\n")) {
          row.hasLineBreak = true;
        }
  
        if (node.quasis[i].value.raw.includes("\n")) {
          tableBody.push({ hasLineBreak: false, cells: [] });
        }
      }
  
      const maxColumnCount = Math.max(
        headerNames.length,
        ...tableBody.map((row) => row.cells.length)
      );
  
      const maxColumnWidths = Array.from({ length: maxColumnCount }).fill(0);
      const table = [
        { cells: headerNames },
        ...tableBody.filter((row) => row.cells.length > 0),
      ];
      for (const { cells } of table.filter((row) => !row.hasLineBreak)) {
        for (const [index, cell] of cells.entries()) {
          maxColumnWidths[index] = Math.max(
            maxColumnWidths[index],
            getStringWidth(cell)
          );
        }
      }
  
      parts.push(
        lineSuffixBoundary,
        "`",
        indent([
          hardline,
          join(
            hardline,
            table.map((row) =>
              join(
                " | ",
                row.cells.map((cell, index) =>
                  row.hasLineBreak
                    ? cell
                    : cell +
                      " ".repeat(maxColumnWidths[index] - getStringWidth(cell))
                )
              )
            )
          ),
        ]),
        hardline,
        "`"
      );
      return parts;
    }
  }
  
  // [prettierx] --template-curly-spacing option support (...)
  function printTemplateExpression(path, print, options) {
    const node = path.getValue();
    let printed = print();
    if (hasComment(node)) {
      printed = group([indent([softline, printed]), softline]);
    }
  
    // [prettierx] --template-curly-spacing option support (...)
    const templateCurlySpace = options.templateCurlySpacing ? " " : "";
  
    // [prettierx] --template-curly-spacing option support (...)
    return [
      "${",
      templateCurlySpace,
      printed,
      lineSuffixBoundary,
      templateCurlySpace,
      "}",
    ];
  }
  
  // [prettierx] --template-curly-spacing option support (...)
  function printTemplateExpressions(path, print, options) {
    return path.map(
      // [prettierx] --template-curly-spacing option support (...)
      (path) => printTemplateExpression(path, print, options),
      "expressions"
    );
  }
  
  function escapeTemplateCharacters(doc, raw) {
    return mapDoc(doc, (currentDoc) => {
      if (typeof currentDoc === "string") {
        return raw
          ? currentDoc.replace(/(\\*)`/g, "$1$1\\`")
          : uncookTemplateElementValue(currentDoc);
      }
  
      return currentDoc;
    });
  }
  
  function uncookTemplateElementValue(cookedValue) {
    return cookedValue.replace(/([\\`]|\${)/g, "\\$1");
  }
  
  module.exports = {
    printTemplateLiteral,
    printTemplateExpressions,
    escapeTemplateCharacters,
    uncookTemplateElementValue,
  };
  