All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.JSDocInfoPrinter Maven / Gradle / Ivy

/*
 * Copyright 2014 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.JSDocInfo.Visibility;
import com.google.javascript.rhino.JSTypeExpression;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

import java.util.Iterator;

/**
 * Prints a JSDocInfo, used for preserving type annotations in ES6 transpilation.
 *
 */
public final class JSDocInfoPrinter {
  public static String print(JSDocInfo info) {
    StringBuilder sb = new StringBuilder("/**");
    if (info.isConstructor()) {
      sb.append("@constructor ");
    }
    if (info.isInterface() && !info.usesImplicitMatch()) {
      sb.append("@interface ");
    }
    if (info.isInterface() && info.usesImplicitMatch()) {
      sb.append("@record ");
    }
    if (info.makesDicts()) {
      sb.append("@dict ");
    }
    if (info.makesStructs()) {
      sb.append("@struct ");
    }
    if (info.makesUnrestricted()) {
      sb.append("@unrestricted ");
    }
    if (info.isDefine()) {
      sb.append("@define {");
      appendTypeNode(sb, info.getType().getRoot());
      sb.append("} ");
    }

    if (info.isOverride()) {
      sb.append("@override ");
    }
    if (info.isConstant() && !info.isDefine()) {
      sb.append("@const ");
    }
    if (info.isDeprecated()) {
      sb.append("@deprecated ");
      sb.append(info.getDeprecationReason() + "\n");
    }
    if (info.isExport()) {
      sb.append("@export ");
    } else if (info.getVisibility() != null
        && info.getVisibility() != Visibility.INHERITED) {
      sb.append("@" + info.getVisibility().toString().toLowerCase() + " ");
    }

    Iterator suppressions = info.getSuppressions().iterator();
    if (suppressions.hasNext()) {
      sb.append("@suppress {");
      while (suppressions.hasNext()) {
        sb.append(suppressions.next());
        if (suppressions.hasNext()) {
          sb.append(",");
        }
      }
      sb.append("} ");
    }

    ImmutableList names = info.getTemplateTypeNames();
    if (!names.isEmpty()) {
      sb.append("@template ");
      Joiner.on(',').appendTo(sb, names);
      sb.append("\n"); // @template needs a newline afterwards
    }

    if (info.getParameterCount() > 0) {
      for (String name : info.getParameterNames()) {
        sb.append("\n@param ");
        if (info.getParameterType(name) != null) {
          sb.append("{");
          appendTypeNode(sb, info.getParameterType(name).getRoot());
          sb.append("} ");
        }
        sb.append(name);
        sb.append(' ');
      }
    }
    if (info.hasReturnType()) {
      sb.append("\n@return {");
      appendTypeNode(sb, info.getReturnType().getRoot());
      sb.append("} ");
    }
    if (info.hasThisType()) {
      sb.append("\n@this {");
      Node typeNode = info.getThisType().getRoot();
      if (typeNode.getType() == Token.BANG) {
        appendTypeNode(sb, typeNode.getFirstChild());
      } else {
        appendTypeNode(sb, typeNode);
      }
      sb.append("} ");
    }
    if (info.hasBaseType()) {
      sb.append("\n@extends {");
      Node typeNode = info.getBaseType().getRoot();
      if (typeNode.getType() == Token.BANG) {
        appendTypeNode(sb, typeNode.getFirstChild());
      } else {
        appendTypeNode(sb, typeNode);
      }
      sb.append("} ");
    }
    for (JSTypeExpression type : info.getExtendedInterfaces()) {
      sb.append("\n@extends {");
      Node typeNode = type.getRoot();
      if (typeNode.getType() == Token.BANG) {
        appendTypeNode(sb, typeNode.getFirstChild());
      } else {
        appendTypeNode(sb, typeNode);
      }
      sb.append("} ");
    }
    for (JSTypeExpression type : info.getImplementedInterfaces()) {
      sb.append("\n@implements {");
      Node typeNode = type.getRoot();
      if (typeNode.getType() == Token.BANG) {
        appendTypeNode(sb, typeNode.getFirstChild());
      } else {
        appendTypeNode(sb, typeNode);
      }
      sb.append("} ");
    }
    if (info.hasTypedefType()) {
      sb.append("@typedef {");
      appendTypeNode(sb, info.getTypedefType().getRoot());
      sb.append("} ");
    }
    if (info.hasType() && !info.isDefine()) {
      if (info.isInlineType()) {
        sb.append(" ");
        appendTypeNode(sb, info.getType().getRoot());
        sb.append(" ");
      } else {
        sb.append("@type {");
        appendTypeNode(sb, info.getType().getRoot());
        sb.append("} ");
      }
    }
    if (!info.getThrownTypes().isEmpty()) {
      sb.append("@throws {");
      appendTypeNode(sb, info.getThrownTypes().get(0).getRoot());
      sb.append("} ");
    }
    if (info.hasEnumParameterType()) {
      sb.append("@enum {");
      appendTypeNode(sb, info.getEnumParameterType().getRoot());
      sb.append("} ");
    }
    sb.append("*/");
    return sb.toString();
  }

  private static void appendTypeNode(StringBuilder sb, Node typeNode) {
    if (typeNode.getType() == Token.BANG) {
      sb.append("!");
      appendTypeNode(sb, typeNode.getFirstChild());
    } else if (typeNode.getType() == Token.EQUALS) {
      appendTypeNode(sb, typeNode.getFirstChild());
      sb.append("=");
    } else if (typeNode.getType() == Token.PIPE) {
      sb.append("(");
      for (int i = 0; i < typeNode.getChildCount() - 1; i++) {
        appendTypeNode(sb, typeNode.getChildAtIndex(i));
        sb.append("|");
      }
      appendTypeNode(sb, typeNode.getLastChild());
      sb.append(")");
    } else if (typeNode.getType() == Token.ELLIPSIS) {
      sb.append("...");
      if (typeNode.hasChildren()) {
        appendTypeNode(sb, typeNode.getFirstChild());
      }
    } else if (typeNode.getType() == Token.STAR) {
      sb.append("*");
    } else if (typeNode.getType() == Token.QMARK) {
      sb.append("?");
      if (typeNode.hasChildren()) {
        appendTypeNode(sb, typeNode.getFirstChild());
      }
    } else if (typeNode.isFunction()) {
      appendFunctionNode(sb, typeNode);
    } else if (typeNode.getType() == Token.LC) {
      sb.append("{");
      Node lb = typeNode.getFirstChild();
      for (int i = 0; i < lb.getChildCount() - 1; i++) {
        Node colon = lb.getChildAtIndex(i);
        if (colon.hasChildren()) {
          sb.append(colon.getFirstChild().getString() + ":");
          appendTypeNode(sb, colon.getLastChild());
        } else {
          sb.append(colon.getString());
        }
        sb.append(",");
      }
      Node lastColon = lb.getLastChild();
      if (lastColon.hasChildren()) {
        sb.append(lastColon.getFirstChild().getString() + ":");
        appendTypeNode(sb, lastColon.getLastChild());
      } else {
        sb.append(lastColon.getString());
      }
      sb.append("}");
    } else if (typeNode.getType() == Token.VOID) {
      sb.append("void");
    } else {
      if (typeNode.hasChildren()) {
        sb.append(typeNode.getString())
            .append("<");
        Node child = typeNode.getFirstChild();
        appendTypeNode(sb, child.getFirstChild());
        for (int i = 1; i < child.getChildCount(); i++) {
          sb.append(",");
          appendTypeNode(sb, child.getChildAtIndex(i));
        }
        sb.append(">");
      } else {
        sb.append(typeNode.getString());
      }
    }
  }

  private static void appendFunctionNode(StringBuilder sb, Node function) {
    boolean hasNewOrThis = false;
    sb.append("function(");
    Node first = function.getFirstChild();
    if (first.isNew()) {
      sb.append("new:");
      appendTypeNode(sb, first.getFirstChild());
      hasNewOrThis = true;
    } else if (first.isThis()) {
      sb.append("this:");
      appendTypeNode(sb, first.getFirstChild());
      hasNewOrThis = true;
    } else if (first.isEmpty()) {
      sb.append(")");
      return;
    } else if (!first.isParamList()) {
      sb.append("):");
      appendTypeNode(sb, first);
      return;
    }
    Node paramList = null;
    if (first.isParamList()) {
      paramList = first;
    } else if (first.getNext().isParamList()) {
      paramList = first.getNext();
    }
    if (paramList != null) {
      boolean firstParam = true;
      for (Node param : paramList.children()) {
        if (!firstParam || hasNewOrThis) {
          sb.append(",");
        }
        appendTypeNode(sb, param);
        firstParam = false;
      }
    }
    sb.append(")");

    Node returnType = function.getLastChild();
    if (!returnType.isEmpty()) {
      sb.append(":");
      appendTypeNode(sb, returnType);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy