Package com.google.javascript.jscomp

Source Code of com.google.javascript.jscomp.Es6TemplateLiterals

/*
* 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.javascript.rhino.IR;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

/**
* Helper class for transpiling ES6 template literals.
*
* @author moz@google.com (Michael Zhou)
*/
class Es6TemplateLiterals {
  private static final String TEMPLATELIT_VAR = "$jscomp$templatelit$";

  static void visitTemplateLiteral(NodeTraversal t, Node n) {
    if (n.getFirstChild().isString()) {
      createUntaggedTemplateLiteral(n);
    } else {
      createTaggedTemplateLiteral(t, n);
    }
    t.getCompiler().reportCodeChange();
  }

  /**
   * Converts `${a} b ${c} d ${e}` to (a + " b " + c + " d " + e)
   *
   * @param n A TEMPLATELIT node that is not prefixed with a tag
   */
  private static void createUntaggedTemplateLiteral(Node n) {
    int length = n.getChildCount();
    if (length == 0) {
      n.getParent().replaceChild(n, IR.string("\"\""));
    } else {
      Node first = n.removeFirstChild(); // first is always a STRING node
      if (length == 1) {
        n.getParent().replaceChild(n, first);
      } else {
        // Add the first string with the first substitution expression
        Node add = IR.add(first, n.removeFirstChild().removeFirstChild());
        // Process the rest of the template literal
        for (int i = 2; i < length; i++) {
          Node child = n.removeFirstChild();
          if (child.isString()) {
            if (child.getString().isEmpty()) {
              continue;
            } else if (i == 2 && first.getString().isEmpty()) {
              // So that `${hello} world` gets translated into (hello + " world")
              // instead of ("" + hello + " world").
              add = add.getChildAtIndex(1).detachFromParent();
            }
          }
          add = IR.add(add, child.isString() ? child : child.removeFirstChild());
        }
        n.getParent().replaceChild(n, add.useSourceInfoIfMissingFromForTree(n));
      }
    }
  }

  /**
   * Converts tag`a\tb${bar}` to:
   *   // A global (module) scoped variable
   *   var $jscomp$templatelit$0 = ["a\tb"];    // cooked string array
   *   $jscomp$templatelit$0["raw"] = ["a\\tb"]; // raw string array
   *   ...
   *   // A call to the tagging function
   *   tag($jscomp$templatelit$0, bar);
   *
   *   See template_literal_test.js for more examples.
   *
   * @param n A TEMPLATELIT node that is prefixed with a tag
   */
  private static void createTaggedTemplateLiteral(NodeTraversal t, Node n) {
    // Prepare the raw and cooked string arrays.
    Node raw = createRawStringArray(n);
    Node cooked = createCookedStringArray(n);

    // Create a variable representing the template literal.
    Node callsiteId = IR.name(
        TEMPLATELIT_VAR + t.getCompiler().getUniqueNameIdSupplier().get());
    Node var = IR.var(callsiteId, cooked).useSourceInfoIfMissingFromForTree(n);
    Node script = NodeUtil.getEnclosingType(n, Token.SCRIPT);
    script.addChildrenToFront(var);

    // Define the "raw" property on the introduced variable.
    Node defineRaw = IR.exprResult(IR.assign(IR.getelem(
        callsiteId.cloneNode(), IR.string("raw")), raw))
            .useSourceInfoIfMissingFromForTree(n);
    script.addChildAfter(defineRaw, var);

    // Generate the call expression.
    Node[] args = new Node[n.getChildCount() / 2];
    args[0] = callsiteId.cloneNode();
    for (int i = 1, j = 2; i < args.length; i++, j += 2) {
      args[i] = n.getChildAtIndex(j).removeFirstChild();
    }
    Node call = IR.call(n.removeFirstChild(), args)
        .useSourceInfoIfMissingFromForTree(n);
    call.putBooleanProp(Node.FREE_CALL, !call.getFirstChild().isGetProp());
    n.getParent().replaceChild(n, call);
  }

  private static Node createRawStringArray(Node n) {
    Node[] items = new Node[n.getChildCount() / 2];
    for (int i = 0, j = 1; i < items.length; i++, j += 2) {
      items[i] = IR.string(
          (String) n.getChildAtIndex(j).getProp(Node.RAW_STRING_VALUE));
    }
    return IR.arraylit(items);
  }

  private static Node createCookedStringArray(Node n) {
    Node[] items = new Node[n.getChildCount() / 2];
    for (int i = 0, j = 1; i < items.length; i++, j += 2) {
      items[i] = IR.string(cookString(
          (String) n.getChildAtIndex(j).getProp(Node.RAW_STRING_VALUE)));
      items[i].putBooleanProp(Node.COOKED_STRING, true);
    }
    return IR.arraylit(items);
  }

  /**
   * Takes a raw string and returns a string that is suitable for the cooked
   * value (the Template Value or TV as described in the specs). This involves
   * removing line continuations.
   */
  private static String cookString(String s) {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < s.length();) {
      char c = s.charAt(i++);
      switch (c) {
        case '\\':
          char c2 = s.charAt(i++);
          switch (c2) {
            // Strip line continuation.
            case '\n':
            case '\u2028':
            case '\u2029':
              break;
            case '\r':
              // \ \r \n should be stripped as one
              if (s.charAt(i + 1) == '\n') {
                i++;
              }
              break;

            default:
              sb.append(c);
              sb.append(c2);
          }
          break;

        // Whitespace
        case '\n':
          sb.append("\\n");
          break;
        // <CR><LF> and <CR> LineTerminatorSequences are normalized to <LF>
        // for both TV and TRV.
        case '\r':
          if (s.charAt(i) == '\n') {
            i++;
          }
          sb.append("\\n");
          break;
        case '\u2028':
          sb.append("\\u2028");
          break;
        case '\u2029':
          sb.append("\\u2029");
          break;

        default:
          sb.append(c);
      }
    }
    return sb.toString();
  }
}
TOP

Related Classes of com.google.javascript.jscomp.Es6TemplateLiterals

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.