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

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

/*
 * Copyright 2020 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.jscomp.OptionalChainRewriter.TmpVarNameCreator;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature;
import com.google.javascript.rhino.Node;
import java.util.ArrayList;
import java.util.function.Function;

/** Replaces the ES2020 `?.` operator with conditional (? :). */
final class RewriteOptionalChainingOperator implements HotSwapCompilerPass {

  private static final FeatureSet TRANSPILED_FEATURES =
      FeatureSet.BARE_MINIMUM.with(Feature.OPTIONAL_CHAINING);

  private final AbstractCompiler compiler;
  /**
   * Produces the object responsible for creating temporary variable names for a given
   * CompilerInput.
   */
  private final Function getTmpVarNameCreatorForInput;

  /** Constructor to be used for actual transpilation */
  RewriteOptionalChainingOperator(AbstractCompiler compiler) {
    this.compiler = compiler;
    // Temporary variable names are generated based on the compiler input they will go into and use
    // a recognizable prefix to aid debugging.
    final UniqueIdSupplier uniqueIdSupplier = compiler.getUniqueIdSupplier();
    this.getTmpVarNameCreatorForInput =
        (CompilerInput input) -> () -> "$jscomp$optchain$tmp" + uniqueIdSupplier.getUniqueId(input);
  }

  /** Constructor for testing. */
  RewriteOptionalChainingOperator(AbstractCompiler compiler, TmpVarNameCreator tmpVarNameCreator) {
    this.compiler = compiler;
    // The test provides a simple variable name creator that makes for more readable test cases.
    this.getTmpVarNameCreatorForInput = (CompilerInput input) -> tmpVarNameCreator;
  }

  @Override
  public void process(Node externs, Node root) {
    TranspilationPasses.processTranspile(
        compiler, externs, TRANSPILED_FEATURES, new TranspilationCallback());
    TranspilationPasses.processTranspile(
        compiler, root, TRANSPILED_FEATURES, new TranspilationCallback());
    TranspilationPasses.maybeMarkFeaturesAsTranspiledAway(compiler, TRANSPILED_FEATURES);
  }

  @Override
  public void hotSwapScript(Node scriptRoot, Node originalRoot) {
    TranspilationPasses.hotSwapTranspile(
        compiler, scriptRoot, TRANSPILED_FEATURES, new TranspilationCallback());
    TranspilationPasses.maybeMarkFeaturesAsTranspiledAway(compiler, TRANSPILED_FEATURES);
  }

  /** Locates and transpiles all optional chains. */
  private class TranspilationCallback implements NodeTraversal.Callback {
    private final OptionalChainRewriter.Builder rewriterBuilder =
        OptionalChainRewriter.builder(compiler);
    private final ArrayList optionalChains = new ArrayList<>();

    @Override
    public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
      if (n.isScript()) {
        // Set the TmpVarNameCreator to be used when rewriting optional chains in this script.
        rewriterBuilder.setTmpVarNameCreator(getTmpVarNameCreatorForInput.apply(t.getInput()));
      }
      return true;
    }

    @Override
    public void visit(NodeTraversal t, Node n, Node parent) {
      if (NodeUtil.isEndOfFullOptChain(n)) {
        optionalChains.add(rewriterBuilder.build(n));
      } else if (n.isScript()) {
        // We transpile all of the optional chains in a single script as a batch because,
        // rewriting changes the AST in ways that could interfere with traversal
        // if we change the chains as we visit them.
        if (!optionalChains.isEmpty()) {
          for (OptionalChainRewriter optionalChain : optionalChains) {
            optionalChain.rewrite();
          }
          optionalChains.clear();
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy