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

com.google.javascript.jscomp.gwt.client.GwtRunner Maven / Gradle / Ivy

/*
 * Copyright 2016 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.gwt.client;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.javascript.jscomp.BasicErrorManager;
import com.google.javascript.jscomp.CheckLevel;
import com.google.javascript.jscomp.CompilationLevel;
import com.google.javascript.jscomp.Compiler;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import com.google.javascript.jscomp.DiagnosticType;
import com.google.javascript.jscomp.JSError;
import com.google.javascript.jscomp.SourceFile;
import com.google.javascript.jscomp.WarningLevel;

import jsinterop.annotations.JsPackage;
import jsinterop.annotations.JsProperty;
import jsinterop.annotations.JsType;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Runner for the GWT-compiled JSCompiler as a single exported method.
 */
public final class GwtRunner implements EntryPoint {

  private static final Map COMPILATION_LEVEL_MAP =
      ImmutableMap.of(
          "WHITESPACE_ONLY",
          CompilationLevel.WHITESPACE_ONLY,
          "SIMPLE",
          CompilationLevel.SIMPLE_OPTIMIZATIONS,
          "SIMPLE_OPTIMIZATIONS",
          CompilationLevel.SIMPLE_OPTIMIZATIONS,
          "ADVANCED",
          CompilationLevel.ADVANCED_OPTIMIZATIONS,
          "ADVANCED_OPTIMIZATIONS",
          CompilationLevel.ADVANCED_OPTIMIZATIONS);

  private static final Map WARNING_LEVEL_MAP =
      ImmutableMap.of(
          "QUIET",
          WarningLevel.QUIET,
          "DEFAULT",
          WarningLevel.DEFAULT,
          "VERBOSE",
          WarningLevel.VERBOSE);

  private GwtRunner() {}

  @JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)
  private interface Flags {
    @JsProperty
    String getCompilationLevel();

    @JsProperty
    String getWarningLevel();

    @JsProperty
    String getLanguageIn();

    @JsProperty
    String getLanguageOut();

    @JsProperty
    boolean getChecksOnly();

    @JsProperty
    boolean getNewTypeInf();

    @JsProperty
    boolean getPreserveTypeAnnotations();

    @JsProperty
    boolean getRewritePolyfills();

    @JsProperty
    File[] getJsCode();

    @JsProperty
    File[] getExterns();
  }

  @JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)
  private interface File {
    @JsProperty String getName();
    @JsProperty String getSource();
  }

  @JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)
  private static class ModuleOutput {
    @JsProperty String compiledCode;
    @JsProperty JavaScriptObject[] errors;
    @JsProperty JavaScriptObject[] warnings;
  }

  private static native JavaScriptObject createError(String file, String description, String type,
        int lineNo, int charNo) /*-{
    return {file: file, description: description, type: type, lineNo: lineNo, charNo: charNo};
  }-*/;

  /**
   * Convert a list of {@link JSError} instances to a JS array containing plain objects.
   */
  private static JavaScriptObject[] toNativeErrorArray(List errors) {
    JavaScriptObject out[] = new JavaScriptObject[errors.size()];
    for (int i = 0; i < errors.size(); ++i) {
      JSError error = errors.get(i);
      DiagnosticType type = error.getType();
      out[i] = createError(error.sourceName, error.description, type != null ? type.key : null,
          error.lineNumber, error.getCharno());
    }
    return out;
  }

  private static void applyDefaultOptions(CompilerOptions options) {
    CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
    WarningLevel.DEFAULT.setOptionsForWarningLevel(options);
    options.setLanguageIn(LanguageMode.ECMASCRIPT5);
    options.setLanguageOut(LanguageMode.ECMASCRIPT3);
    options.setPrettyPrint(true);
  }

  private static void applyOptionsFromFlags(CompilerOptions options, Flags flags) {
    if (flags == null) {
      return;
    }

    if (flags.getCompilationLevel() != null) {
      CompilationLevel level =
          COMPILATION_LEVEL_MAP.get(flags.getCompilationLevel().toUpperCase());
      if (level != null) {
        level.setOptionsForCompilationLevel(options);
      }
    }

    if (flags.getWarningLevel() != null) {
      WarningLevel level = WARNING_LEVEL_MAP.get(flags.getWarningLevel().toUpperCase());
      if (level != null) {
        level.setOptionsForWarningLevel(options);
      }
    }

    if (flags.getLanguageIn() != null) {
      LanguageMode languageIn = LanguageMode.fromString(flags.getLanguageIn());
      if (languageIn != null) {
        options.setLanguageIn(languageIn);
      }
    }

    if (flags.getLanguageOut() != null) {
      LanguageMode languageOut = LanguageMode.fromString(flags.getLanguageOut());
      if (languageOut != null) {
        options.setLanguageOut(languageOut);
      }
    }

    options.setChecksOnly(flags.getChecksOnly());
    options.setNewTypeInference(flags.getNewTypeInf());
    options.setPreserveTypeAnnotations(flags.getPreserveTypeAnnotations());
    options.setRewritePolyfills(flags.getRewritePolyfills());
  }

  private static void disableUnsupportedOptions(CompilerOptions options) {
    options.getDependencyOptions().setDependencySorting(false);
  }

  private static List fromFileArray(File[] src, String unknownPrefix) {
    List out = new ArrayList<>();
    if (src != null) {
      for (int i = 0; i < src.length; ++i) {
        File file = src[i];
        String name = file.getName();
        if (name == null) {
          name = unknownPrefix + i;
        }
        String source = file.getSource();
        if (source == null) {
          source = "";
        }
        out.add(SourceFile.fromCode(name, source));
      }
    }
    return ImmutableList.copyOf(out);
  }

  /**
   * Public compiler call. Exposed in {@link #exportCompile}.
   */
  public static ModuleOutput compile(Flags flags) {
    CompilerOptions options = new CompilerOptions();
    applyDefaultOptions(options);
    applyOptionsFromFlags(options, flags);
    disableUnsupportedOptions(options);

    NodeErrorManager errorManager = new NodeErrorManager();
    Compiler compiler = new Compiler();
    compiler.setErrorManager(errorManager);

    List externs = fromFileArray(flags.getExterns(), "Extern_");
    List jsCode = fromFileArray(flags.getJsCode(), "Input_");
    compiler.compile(externs, jsCode, options);

    ModuleOutput output = new ModuleOutput();
    output.compiledCode = compiler.toSource();
    output.errors = toNativeErrorArray(errorManager.errors);
    output.warnings = toNativeErrorArray(errorManager.warnings);
    return output;
  }

  /**
   * Exports the {@link #compile} method via JSNI.
   *
   * This will be placed on {@code module.exports} or {@code this}.
   */
  public native void exportCompile() /*-{
    var fn = $entry(@com.google.javascript.jscomp.gwt.client.GwtRunner::compile(*));
    if (typeof module !== 'undefined' && module.exports) {
      module.exports = fn;
    } else {
      this.compile = fn;
    }
  }-*/;

  @Override
  public void onModuleLoad() {
    exportCompile();
  }

  /**
   * Custom {@link BasicErrorManager} to record {@link JSError} instances.
   */
  private static class NodeErrorManager extends BasicErrorManager {
    final List errors = new ArrayList<>();
    final List warnings = new ArrayList<>();

    @Override
    public void println(CheckLevel level, JSError error) {
      if (level == CheckLevel.ERROR) {
        errors.add(error);
      } else if (level == CheckLevel.WARNING) {
        warnings.add(error);
      }
    }

    @Override
    public void printSummary() {}
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy