src/language-js/print/statement.js/printStatement
function printStatement({ path, index, bodyNode, isClass }, options, print) {
  const node = path.getValue();

  // Just in case the AST has been modified to contain falsy
  // "statements," it's safer simply to skip them.
  /* istanbul ignore if */
  if (!node) {
    return;
  }

  // Skip printing EmptyStatement nodes to avoid leaving stray
  // semicolons lying around.
  if (node.type === "EmptyStatement") {
    return;
  }

  const printed = print(path);
  const text = options.originalText;
  const parts = [];

  // in no-semi mode, prepend statement with semicolon if it might break ASI
  // don't prepend the only JSX element in a program with semicolon
  if (
    !options.semi &&
    !isClass &&
    !isTheOnlyJSXElementInMarkdown(options, path) &&
    statementNeedsASIProtection(path, options)
  ) {
    if (hasComment(node, CommentCheckFlags.Leading)) {
      parts.push(print(path, { needsSemi: true }));
    } else {
      parts.push(";", printed);
    }
  } else {
    parts.push(printed);
  }

  if (!options.semi && isClass) {
    if (classPropMayCauseASIProblems(path)) {
      parts.push(";");
    } else if (
      node.type === "ClassProperty" ||
      node.type === "FieldDefinition"
    ) {
      const nextChild = bodyNode.body[index + 1];
      if (classChildNeedsASIProtection(nextChild)) {
        parts.push(";");
      }
    }
  }

  if (isNextLineEmpty(text, node, locEnd) && !isLastStatement(path)) {
    parts.push(hardline);
  }

  return concat(parts);
}