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

com.google.javascript.refactoring.RefasterJs Maven / Gradle / Ivy

Go to download

Closure Compiler is a JavaScript optimizing compiler. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. It is used in many of Google's JavaScript apps, including Gmail, Google Web Search, Google Maps, and Google Docs. This binary checks for style issues such as incorrect or missing JSDoc usage, and missing goog.require() statements. It does not do more advanced checks such as typechecking.

There is a newer version: v20200830
Show newest version
/*
 * 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.refactoring;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.javascript.jscomp.CommandLineRunner;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.ErrorManager;
import com.google.javascript.jscomp.TypeMatchingStrategy;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.kohsuke.args4j.spi.BooleanOptionHandler;

/**
 * Main binary that drives a RefasterJS refactoring.
 */
final class RefasterJs {

  @Option(name = "--help",
        hidden = true,
        handler = BooleanOptionHandler.class,
        usage = "Show instructions for how to use RefasterJS")
  private boolean displayHelp = false;

  @Option(
      name = "--inputs",
      usage = "List of input files for the refactoring. You may also use glob patterns to "
          + "match files. For example, use --js='**.js' --js='!**_test.js' "
          + "to recursively include all js files that do not end in _test.js")
  private List inputs = new ArrayList<>();

  @Option(name = "--externs", usage = "List of externs files to use in the compilation.")
  private List externs = new ArrayList<>();

  @Option(
      name = "--refasterjs_template",
      usage = "Location of the JS file to use as the RefasterJS template.")
  private String refasterJsTemplate = null;

  @Option(name = "--env",
      usage = "Which set of externs to include. Defaults to BROWSER.")
  private CompilerOptions.Environment environment =
      CompilerOptions.Environment.BROWSER;

  @Option(name = "--type_matching",
    usage = "Which type matching strategy to use. Defaults to SUBTYPES.")
  private TypeMatchingStrategy typeMatchingStrategy = TypeMatchingStrategy.SUBTYPES;

  @Option(name = "--dry_run",
      usage = "Use this to display what changes would be made without applying the changes.")
  private boolean dryRun = false;

  @Option(name = "--verbose", usage = "Use this to print verbose statements from RefasterJS.")
  private boolean verbose = false;

  @Argument
  private List arguments = new ArrayList<>();

  private void doMain(String[] args) throws Exception {
    CmdLineParser parser = new CmdLineParser(this);
    parser.parseArgument(args);
    if (args.length < 1 || displayHelp) {
      CmdLineParser p = new CmdLineParser(this);
      p.printUsage(System.out);
      return;
    }
    checkArgument(
        !Strings.isNullOrEmpty(refasterJsTemplate), "--refasterjs_template must be provided");
    List fileInputs = getInputs();
    checkArgument(
        !fileInputs.isEmpty(), "At least one input must be provided in the --inputs flag.");
    for (String input : fileInputs) {
      Preconditions.checkArgument(
          new File(input).exists(), "Input file %s does not exist.", input);
    }

    if (!verbose) {
      // This is done here instead of using the Compiler#setLoggingLevel function since the
      // Compiler is created and then run inside of RefactoringDriver.
      Logger errorManagerLogger = Logger.getLogger("com.google.javascript.jscomp");
      errorManagerLogger.setLevel(Level.OFF);
    }

    RefasterJsScanner scanner = new RefasterJsScanner();
    scanner.setTypeMatchingStrategy(typeMatchingStrategy);
    scanner.loadRefasterJsTemplate(refasterJsTemplate);
    CompilerOptions options = new CompilerOptions();
    options.setEnvironment(environment);
    RefactoringDriver driver =
        new RefactoringDriver.Builder()
            .addExterns(CommandLineRunner.getBuiltinExterns(environment))
            .addExternsFromFile(getExterns())
            .addInputsFromFile(fileInputs)
            .build();
    System.out.println("Compiling JavaScript code and searching for suggested fixes.");
    // TODO(bangert): allow picking a non-default choice in RefasterJS, e.g. via a switch.
    List fixes = driver.drive(scanner);

    if (!verbose) {
      // When running in quiet mode, the Compiler's error manager will not have printed
      // this information itself.
      ErrorManager errorManager = driver.getCompiler().getErrorManager();
      System.out.println("Compiler results: " + errorManager.getErrorCount()
          + " errors and " + errorManager.getWarningCount() + " warnings.");
    }
    System.out.println("Found " + fixes.size() + " suggested fixes.");
    if (dryRun) {
      if (!fixes.isEmpty()) {
        System.out.println("SuggestedFixes: " + fixes);
      }
    } else {
      Set affectedFiles = new TreeSet<>();
      for (SuggestedFix fix : fixes) {
        affectedFiles.addAll(fix.getReplacements().keySet());
      }
      System.out.println("Modifying affected files: " + affectedFiles);
      ApplySuggestedFixes.applySuggestedFixesToFiles(fixes);
    }
  }

  private List getInputs() throws IOException {
    Set patterns = new HashSet<>();
    // The args4j library can't handle multiple files provided within the same flag option,
    // like --inputs=file1.js,file2.js so handle that here.
    Splitter commaSplitter = Splitter.on(',');
    for (String input : inputs) {
      patterns.addAll(commaSplitter.splitToList(input));
    }
    patterns.addAll(arguments);
    return CommandLineRunner.findJsFiles(patterns);
  }

  private List getExterns() throws IOException {
    Set patterns = new HashSet<>();
    // The args4j library can't handle multiple files provided within the same flag option,
    // like --externs=file1.js,file2.js so handle that here.
    Splitter commaSplitter = Splitter.on(',');
    for (String extern : externs) {
      patterns.addAll(commaSplitter.splitToList(extern));
    }
    return CommandLineRunner.findJsFiles(patterns);
  }

  public static void main(String[] args) throws Exception {
    new RefasterJs().doMain(args);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy