com.google.javascript.jscomp.CommandLineRunner Maven / Gradle / Ivy
Show all versions of closure-compiler-unshaded Show documentation
/*
* Copyright 2009 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 static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Ascii;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Files;
import com.google.errorprone.annotations.InlineMe;
import com.google.errorprone.annotations.Keep;
import com.google.javascript.jscomp.AbstractCommandLineRunner.CommandLineConfig.ErrorFormatOption;
import com.google.javascript.jscomp.CompilerOptions.ChunkOutputType;
import com.google.javascript.jscomp.CompilerOptions.ExtractPrototypeMemberDeclarationsMode;
import com.google.javascript.jscomp.CompilerOptions.InstrumentOption;
import com.google.javascript.jscomp.CompilerOptions.IsolationMode;
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import com.google.javascript.jscomp.DependencyOptions.DependencyMode;
import com.google.javascript.jscomp.SourceMap.LocationMapping;
import com.google.javascript.jscomp.deps.ClosureBundler;
import com.google.javascript.jscomp.deps.ModuleLoader;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature;
import com.google.javascript.jscomp.transpile.BaseTranspiler;
import com.google.javascript.jscomp.transpile.BaseTranspiler.CompilerSupplier;
import com.google.javascript.jscomp.transpile.Transpiler;
import com.google.javascript.rhino.TokenStream;
import com.google.protobuf.TextFormat;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.StringWriter;
import java.lang.reflect.AnnotatedElement;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jspecify.nullness.Nullable;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.NamedOptionDef;
import org.kohsuke.args4j.Option;
import org.kohsuke.args4j.OptionDef;
import org.kohsuke.args4j.spi.FieldSetter;
import org.kohsuke.args4j.spi.IntOptionHandler;
import org.kohsuke.args4j.spi.OptionHandler;
import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;
import org.kohsuke.args4j.spi.StringOptionHandler;
/**
* CommandLineRunner translates flags into Java API calls on the Compiler.
*
* This class may be extended and used to create other Java classes that behave the same as
* running the Compiler from the command line. If you want to run the compiler in-process in Java,
* you should look at this class for hints on what API calls to make, but you should not use this
* class directly.
*
*
Example:
*
*
* class MyCommandLineRunner extends CommandLineRunner {
* MyCommandLineRunner(String[] args) {
* super(args);
* }
*
* {@code @Override} protected CompilerOptions createOptions() {
* CompilerOptions options = super.createOptions();
* addMyCrazyCompilerPassThatOutputsAnExtraFile(options);
* return options;
* }
*
* public static void main(String[] args) {
* MyCommandLineRunner runner = new MyCommandLineRunner(args);
* if (runner.shouldRunCompiler()) {
* runner.run();
* }
* if (runner.hasErrors()) {
* System.exit(-1);
* }
* }
* }
*
*
* This class is totally not thread-safe.
*/
@GwtIncompatible("Unnecessary")
public class CommandLineRunner extends AbstractCommandLineRunner {
public static final String OUTPUT_MARKER = AbstractCommandLineRunner.OUTPUT_MARKER;
// UTF-8 BOM is 0xEF, 0xBB, 0xBF, of which character code is 65279.
public static final int UTF8_BOM_CODE = 65279;
// Allowable chunk name characters that aren't valid in a JS identifier
private static final Pattern extraChunkNameChars = Pattern.compile("[-.]+");
// I don't really care about unchecked warnings in this class.
@SuppressWarnings("unchecked")
private static class Flags {
// Some clients run a few copies of the compiler through CommandLineRunner
// on parallel threads (thankfully, with the same flags),
// so the access to these lists should be synchronized.
private static final List> guardLevels =
Collections.synchronizedList(new ArrayList>());
private static final List> mixedJsSources =
Collections.synchronizedList(new ArrayList>());
@Option(
name = "--browser_featureset_year",
usage =
"shortcut for defining "
+ "goog.FEATURESET_YEAR=YYYY."
+ " The minimum valid value of the browser year is 2012")
private Integer browserFeaturesetYear = 0;
@Option(
name = "--emit_async_functions_with_zonejs",
handler = BooleanOptionHandler.class,
usage = "Relax the restriction on disallowing --language_out=ES_2017 together with Zone.js")
private boolean emitAsyncFunctionsWithZonejs = false;
@Option(
name = "--help",
handler = BooleanOptionHandler.class,
usage = "Displays this message on stdout and exit")
private boolean displayHelp = false;
@Option(
name = "--print_tree",
hidden = true,
handler = BooleanOptionHandler.class,
usage = "Prints out the parse tree and exits")
private boolean printTree = false;
@Option(
name = "--print_tree_json",
hidden = true,
handler = BooleanOptionHandler.class,
usage = "Prints out the parse tree as json and exits")
private boolean printTreeJson = false;
@Option(
name = "--print_ast",
hidden = true,
handler = BooleanOptionHandler.class,
usage = "Prints a dot file describing the internal abstract syntax" + " tree and exits")
private boolean printAst = false;
@Option(
name = "--emit_use_strict",
handler = BooleanOptionHandler.class,
usage = "Start output with \"'use strict';\".")
private boolean emitUseStrict = false;
@Option(
name = "--strict_mode_input",
handler = BooleanOptionHandler.class,
usage = "Assume input sources are to run in strict mode.")
private boolean strictModeInput = true;
// Turn on (very slow) extra validity checks for use when modifying the compiler.
@Option(
name = "--jscomp_dev_mode",
hidden = true,
// no usage
aliases = {"--dev_mode"})
private CompilerOptions.DevMode jscompDevMode = CompilerOptions.DevMode.OFF;
@Option(
name = "--logging_level",
hidden = true,
usage =
"The logging level (standard java.util.logging.Level"
+ " values) for Compiler progress. Does not control errors or"
+ " warnings for the JavaScript code under compilation")
private String loggingLevel = Level.WARNING.getName();
@Option(
name = "--externs",
usage = "The file containing JavaScript externs. You may specify" + " multiple")
private List externs = new ArrayList<>();
@Keep
@Option(
name = "--js",
handler = JsOptionHandler.class,
usage =
"The JavaScript filename. You may specify multiple. "
+ "The flag name is optional, because args are interpreted as files by default. "
+ "You may also use minimatch-style glob patterns. For example, use "
+ "--js='**.js' --js='!**_test.js' to recursively include all "
+ "js files that do not end in _test.js")
private List js = new ArrayList<>();
@Option(
name = "--jszip",
hidden = true,
handler = JsZipOptionHandler.class,
usage = "The JavaScript zip filename. You may specify multiple.")
private @Nullable List unusedJsZip = null;
@Option(
name = "--js_output_file",
usage = "Primary output filename. If not specified, output is " + "written to stdout")
private String jsOutputFile = "";
@Option(
name = "--chunk",
usage =
"A JavaScript chunk specification. The format is "
+ ":[:[,...][:]]]. Chunk names must be "
+ "unique. Each dep is the name of a chunk that this chunk "
+ "depends on. Chunks must be listed in dependency order, and JS "
+ "source files must be listed in the corresponding order. Where "
+ "--chunk flags occur in relation to --js flags is unimportant. "
+ " may be set to 'auto' for the first chunk if it "
+ "has no dependencies. "
+ "Provide the value 'auto' to trigger chunk creation from CommonJS"
+ "modules.")
private List chunk = new ArrayList<>();
// TODO(bradfordcsmith): deprecate and remove this in favor of --restore_stage1_from_file
@Option(
name = "--continue-saved-compilation",
usage = "Filename where a stage 1 compilation state was previously saved.",
hidden = true)
private @Nullable String continueSavedCompilationFile = null;
@Option(
name = "--restore_stage1_from_file",
usage = "Filename where a stage 1 compilation state was previously saved.",
hidden = true)
private @Nullable String restoreStage1FromFile = null;
@Option(
name = "--restore_stage2_from_file",
usage = "Filename where a stage 2 compilation state was previously saved.",
hidden = true)
private @Nullable String restoreStage2FromFile = null;
// TODO(bradfordcsmith): deprecate and remove this in favor of --save_stage1_to_file
@Option(
name = "--save-after-checks",
usage = "Filename to save stage 1 state so that the compilation can be resumed later.",
hidden = true)
private @Nullable String saveAfterChecksFile = null;
@Option(
name = "--save_stage1_to_file",
usage = "Filename to save stage 1 state so that the compilation can be resumed later.",
hidden = true)
private @Nullable String saveStage1ToFile = null;
@Option(
name = "--save_stage2_to_file",
usage = "Filename to save stage 2 state so that the compilation can be resumed later.",
hidden = true)
private @Nullable String saveStage2ToFile = null;
@Option(
name = "--variable_renaming_report",
usage =
"File where the serialized version of the variable "
+ "renaming map produced should be saved")
private String variableMapOutputFile = "";
@Option(
name = "--instrument_mapping_report",
usage =
"File where the encoded parameters created by Production Instrumentation are mapped to"
+ " their pre-encoded values. The %outname% placeholder will expand to the name of"
+ " the output file that the source map corresponds to. Must be used in tandem with"
+ " --instrument_for_coverage_option=PRODUCTION")
private String instrumentationMappingOutputFile = "";
@Option(
name = "--create_renaming_reports",
hidden = true,
handler = BooleanOptionHandler.class,
usage =
"If true, variable renaming and property renaming report "
+ "files will be produced as {binary name}_vars_renaming_report.out "
+ "and {binary name}_props_renaming_report.out. Note that this flag "
+ "cannot be used in conjunction with either variable_renaming_report "
+ "or property_renaming_report")
private boolean createNameMapFiles = false;
@Option(
name = "--source_map_include_content",
handler = BooleanOptionHandler.class,
usage =
"Includes sources content into source map. Greatly increases "
+ "the size of source maps but offers greater portability")
private boolean sourceMapIncludeSourcesContent = false;
@Option(
name = "--property_renaming_report",
usage =
"File where the serialized version of the property "
+ "renaming map produced should be saved")
private String propertyMapOutputFile = "";
@Option(
name = "--third_party",
handler = BooleanOptionHandler.class,
usage = "Check source validity but do not enforce Closure style " + "rules and conventions")
private boolean thirdParty = false;
@Option(
name = "--summary_detail_level",
hidden = true,
usage =
"Controls how detailed the compilation summary is. Values:"
+ " 0 (never print summary), 1 (print summary only if there are "
+ "errors or warnings), 2 (print summary if the 'checkTypes' "
+ "diagnostic group is enabled, see --jscomp_warning), "
+ "3 (always print summary). The default level is 1")
private int summaryDetailLevel = 1;
@Option(
name = "--isolation_mode",
usage =
"If set to IIFE the compiler output will follow the form:\n"
+ " (function(){%output%}).call(this);\n"
+ "Options: NONE, IIFE")
private IsolationMode isolationMode = IsolationMode.NONE;
@Option(
name = "--output_wrapper",
usage =
"Interpolate output into this string at the place denoted"
+ " by the marker token %output%. Use marker token %output|jsstring%"
+ " to do js string escaping on the output."
+ " Consider using the --isolation_mode flag instead.")
private String outputWrapper = "";
@Option(
name = "--output_wrapper_file",
usage =
"Loads the specified file and passes the file contents to the --output_wrapper flag,"
+ " replacing the value if it exists. This is useful if you want special"
+ " characters like newline in the wrapper.")
private String outputWrapperFile = "";
@Option(
name = "--chunk_wrapper",
usage =
"An output wrapper for a JavaScript chunk (optional). "
+ "The format is :. The chunk name must correspond "
+ "with a chunk specified using --chunk. The wrapper must "
+ "contain %s as the code placeholder. "
+ "Alternately, %output% can be used in place of %s. "
+ "%n% can be used to represent a newline. "
+ "The %basename% placeholder can "
+ "also be used to substitute the base name of the chunk output file.")
private List chunkWrapper = new ArrayList<>();
@Option(
name = "--chunk_output_path_prefix",
usage =
"Prefix for filenames of compiled JS chunks. "
+ ".js will be appended to this prefix. Directories "
+ "will be created as needed. Use with --chunk")
private String chunkOutputPathPrefix = "./";
@Option(
name = "--create_source_map",
usage =
"If specified, a source map file mapping the generated "
+ "source files back to the original source file will be "
+ "output to the specified path. The %outname% placeholder will "
+ "expand to the name of the output file that the source map "
+ "corresponds to.")
private String createSourceMap = "";
@Option(
name = "--source_map_format",
hidden = true,
usage =
"The source map format to produce. "
+ "Options are V3 and DEFAULT, which are equivalent.")
private SourceMap.Format sourceMapFormat = SourceMap.Format.DEFAULT;
@Option(
name = "--source_map_location_mapping",
usage =
"Source map location mapping separated by a '|' "
+ "(i.e. filesystem-path|webserver-path)")
private List sourceMapLocationMapping = new ArrayList<>();
@Option(
name = "--source_map_input",
usage =
"Source map locations for input files, separated by a '|', "
+ "(i.e. input-file-path|input-source-map)")
private List sourceMapInputs = new ArrayList<>();
@Option(
name = "--parse_inline_source_maps",
handler = BooleanOptionHandler.class,
usage = "Parse inline source maps (//# sourceMappingURL=data:...)")
private Boolean parseInlineSourceMaps = true;
@Option(
name = "--apply_input_source_maps",
handler = BooleanOptionHandler.class,
usage =
"Apply input source maps to the output source map, i.e. have the result map back"
+ " to original inputs. Input sourcemaps can be located in 2 ways:\n"
+ " 1) by the//# sourceMappingURL=. \n"
+ " 2) using the--source_map_location_mapping flag.\n"
+ "sourceMappingURL= can read both paths and inline Base64 encoded"
+ " sourcemaps. For inline Base64 encoded sourcemaps, see"
+ " --parse_inline_source_maps.")
private boolean applyInputSourceMaps = true;
// Used to define the flag, values are stored by the handler.
@SuppressWarnings("unused")
@Option(
name = "--jscomp_error",
handler = WarningGuardErrorOptionHandler.class,
usage =
"Make the named class of warnings an error. Must be one "
+ "of the error group items. '*' adds all supported.")
private List jscompError = new ArrayList<>();
// Used to define the flag, values are stored by the handler.
@SuppressWarnings("unused")
@Option(
name = "--jscomp_warning",
handler = WarningGuardWarningOptionHandler.class,
usage =
"Make the named class of warnings a normal warning. Must be one "
+ "of the error group items. '*' adds all supported.")
private List jscompWarning = new ArrayList<>();
// Used to define the flag, values are stored by the handler.
@SuppressWarnings("unused")
@Option(
name = "--jscomp_off",
handler = WarningGuardOffOptionHandler.class,
usage =
"Turn off the named class of warnings. Must be one "
+ "of the error group items. '*' adds all supported.")
private List jscompOff = new ArrayList<>();
@Option(
name = "--define",
aliases = {"--D", "-D"},
usage =
"Override the value of a variable annotated @define. "
+ "The format is [=], where is the name of a @define "
+ "variable and is a boolean, number, or a single-quoted string "
+ "that contains no single quotes. If [=] is omitted, "
+ "the variable is marked true")
private List define = new ArrayList<>();
@Option(
name = "--charset",
usage =
"Input and output charset for all files. By default, we "
+ "accept UTF-8 as input and output US_ASCII")
private String charset = "";
@Option(
name = "--compilation_level",
aliases = {"-O"},
usage =
"Specifies the compilation level to use. Options: "
+ "BUNDLE, "
+ "WHITESPACE_ONLY, "
+ "SIMPLE (default), "
+ "ADVANCED")
private String compilationLevel = "SIMPLE";
private @Nullable CompilationLevel compilationLevelParsed = null;
@Option(
name = "--num_parallel_threads",
hidden = true,
handler = IntOptionHandler.class,
usage = "Use multiple threads to parallelize parts of the compilation.")
private int numParallelThreads = 1;
@Option(
name = "--checks_only",
aliases = {"--checks-only"},
handler = BooleanOptionHandler.class,
usage = "Don't generate output. Run checks, but no optimization passes.")
private boolean checksOnly = false;
@Option(
name = "--incremental_check_mode",
usage = "Generate or check externs-like .i.js files representing individual libraries.")
private CompilerOptions.IncrementalCheckMode incrementalCheckMode =
CompilerOptions.IncrementalCheckMode.OFF;
@Option(
name = "--continue_after_errors",
handler = BooleanOptionHandler.class,
usage = "Continue trying to compile after an error is encountered.")
private boolean continueAfterErrors = false;
@Option(
name = "--use_types_for_optimization",
handler = BooleanOptionHandler.class,
usage =
"Enable or disable the optimizations "
+ "based on available type information. Inaccurate type annotations "
+ "may result in incorrect results.")
private boolean useTypesForOptimization = true;
@Option(
name = "--assume_function_wrapper",
handler = BooleanOptionHandler.class,
usage =
"Enable additional optimizations based on the assumption that the output will be"
+ " wrapped with a function wrapper. This flag is used to indicate that"
+ " \"global\" declarations will not actually be global but instead isolated to"
+ " the compilation unit. This enables additional optimizations.")
private boolean assumeFunctionWrapper = false;
@Option(
name = "--warning_level",
aliases = {"-W"},
usage = "Specifies the warning level to use. Options: " + "QUIET, DEFAULT, VERBOSE")
private WarningLevel warningLevel = WarningLevel.DEFAULT;
@Option(
name = "--debug",
handler = BooleanOptionHandler.class,
usage =
"Enable debugging options. Property renaming uses long mangled names which can be "
+ "mapped back to the original name.")
private boolean debug = false;
@Option(
name = "--typed_ast_output_file__INTENRNAL_USE_ONLY",
usage = "Sets file to output in-progress typedAST format. DO NOT USE!",
hidden = true)
private @Nullable String typedAstOutputFile = null;
@Option(
name = "--generate_exports",
handler = BooleanOptionHandler.class,
usage = "Generates export code for those marked with @export")
private boolean generateExports = true;
@Option(
name = "--export_local_property_definitions",
handler = BooleanOptionHandler.class,
usage = "Generates export code for local properties marked with @export")
private boolean exportLocalPropertyDefinitions = true;
@Option(
name = "--formatting",
usage =
"Specifies which formatting options, if any, should be "
+ "applied to the output JS. Options: "
+ "PRETTY_PRINT, PRINT_INPUT_DELIMITER, SINGLE_QUOTES")
private List formatting = new ArrayList<>();
@Option(
name = "--process_common_js_modules",
handler = BooleanOptionHandler.class,
usage = "Process CommonJS modules to a concatenable form.")
private boolean processCommonJsModules = false;
@Option(
name = "--js_module_root",
usage = "Path prefixes to be removed from ES6 & CommonJS modules.")
private List moduleRoot = new ArrayList<>();
@Option(
name = "--process_closure_primitives",
handler = BooleanOptionHandler.class,
usage =
"Processes built-ins from the Closure library, such as "
+ "goog.require(), goog.provide(), and goog.exportSymbol(). "
+ "True by default.")
private boolean processClosurePrimitives = true;
@Option(
name = "--angular_pass",
handler = BooleanOptionHandler.class,
usage =
"Generate $inject properties for AngularJS for functions " + "annotated with @ngInject")
private boolean angularPass = false;
@Option(name = "--polymer_version", usage = "Which version of Polymer is being used (1 or 2).")
private @Nullable Integer polymerVersion = null;
@Option(
name = "--polymer_export_policy",
usage =
"How to handle exports/externs for Polymer properties and methods. "
+ "Values: LEGACY, EXPORT_ALL.")
private String polymerExportPolicy = PolymerExportPolicy.LEGACY.name();
@Option(
name = "--chrome_pass",
handler = BooleanOptionHandler.class,
usage = "Enable Chrome-specific options for handling cr.* functions.",
hidden = true)
private boolean chromePass = false;
@Option(
name = "--j2cl_pass",
hidden = true,
usage =
"Rewrite J2CL output to be compiler-friendly if enabled (ON or AUTO). "
+ "Options:OFF, ON, AUTO(default)")
private String j2clPassMode = "AUTO";
@Option(
name = "--remove_j2cl_asserts",
hidden = true,
usage = "Remove calls to J2CL assertions.")
private boolean removeJ2cLAsserts = true;
@Option(
name = "--output_manifest",
usage =
"Prints out a list of all the files in the compilation. "
+ "If --dependency_mode=PRUNE or PRUNE_LEGACY is specified, this will not include "
+ "files that got dropped because they were not required. "
+ "The %outname% placeholder expands to the JS output file. "
+ "If you're using modularization, using %outname% will create "
+ "a manifest for each module.")
private String outputManifest = "";
@Option(
name = "--output_chunk_dependencies",
usage = "Prints out a JSON file of dependencies between chunks.")
private String outputChunkDependencies = "";
@Option(
name = "--language_in",
usage =
"Sets the language spec to which input sources should conform. "
+ "Options: ECMASCRIPT3, ECMASCRIPT5, ECMASCRIPT5_STRICT, "
+ "ECMASCRIPT_2015, ECMASCRIPT_2016, ECMASCRIPT_2017, "
+ "ECMASCRIPT_2018, ECMASCRIPT_2019, ECMASCRIPT_2020,"
+ "ECMASCRIPT_2021, STABLE, ECMASCRIPT_NEXT (latest features supported),"
+ "UNSTABLE (for testing only)")
private String languageIn = "STABLE";
@Option(
name = "--language_out",
usage =
"Sets the language spec to which output should conform. "
+ "Options: ECMASCRIPT3, ECMASCRIPT5, "
+ "ECMASCRIPT_2015, ECMASCRIPT_2016, ECMASCRIPT_2017, "
+ "ECMASCRIPT_2018, ECMASCRIPT_2019, ECMASCRIPT_2020, "
+ "ECMASCRIPT_2021, STABLE, ECMASCRIPT_NEXT (latest features supported)")
private String languageOut = "ECMASCRIPT_NEXT";
@Option(
name = "--version",
handler = BooleanOptionHandler.class,
usage = "Prints the compiler version to stdout and exit.")
private boolean version = false;
@Option(
name = "--translations_file",
hidden = true,
usage = "Source of translated messages. Currently only supports XTB.")
private String translationsFile = "";
@Option(
name = "--translations_project",
hidden = true,
usage =
"Scopes all translations to the specified project."
+ "When specified, we will use different message ids so that messages "
+ "in different projects can have different translations.")
private @Nullable String translationsProject = null;
@Option(
name = "--flagfile",
hidden = true,
usage = "A file (or files) containing additional command-line options.")
private List flagFiles = new ArrayList<>();
@Option(
name = "--warnings_allowlist_file",
usage =
"A file containing warnings to suppress. Each line should be "
+ "of the form\n"
+ ":? ",
aliases = {"--warnings_whitelist_file"})
private String warningsAllowlistFile = "";
@Option(
name = "--hide_warnings_for",
usage =
"If specified, files whose path contains this string will "
+ "have their warnings hidden. You may specify multiple.")
private List hideWarningsFor = new ArrayList<>();
@Option(
name = "--extra_annotation_name",
usage = "A allowlist of tag names in JSDoc. You may specify multiple")
private List extraAnnotationName = new ArrayList<>();
@Option(
name = "--tracer_mode",
hidden = true,
usage =
"Shows the duration of each compiler pass and the impact to "
+ "the compiled output size. "
+ "Options: ALL, AST_SIZE, RAW_SIZE, TIMING_ONLY, OFF")
private CompilerOptions.TracerMode tracerMode = CompilerOptions.TracerMode.OFF;
@Option(
name = "--rename_variable_prefix",
usage = "Specifies a prefix that will be prepended to all variables.")
private @Nullable String renamePrefix = null;
@Option(
name = "--rename_prefix_namespace",
usage =
"Specifies the name of an object that will be used to store all "
+ "non-extern globals")
private @Nullable String renamePrefixNamespace = null;
@Option(
name = "--conformance_configs",
usage = "A list of JS Conformance configurations in text protocol buffer format.")
private List conformanceConfigs = new ArrayList<>();
@Option(
name = "--env",
usage =
"Determines the set of builtin externs to load. "
+ "Options: BROWSER, CUSTOM. Defaults to BROWSER.")
private CompilerOptions.Environment environment = CompilerOptions.Environment.BROWSER;
@Option(
name = "--json_streams",
usage =
"Specifies whether standard input and output streams will be "
+ "a JSON array of sources. Each source will be an object of the "
+ "form {path: filename, src: file_contents, source_map: srcmap_contents }. "
+ "Intended for use by stream-based build systems such as gulpjs. "
+ "Options: NONE, IN, OUT, BOTH. Defaults to NONE.")
private CompilerOptions.JsonStreamMode jsonStreamMode = CompilerOptions.JsonStreamMode.NONE;
@Option(
name = "--preserve_type_annotations",
hidden = true,
handler = BooleanOptionHandler.class,
usage = "Preserves type annotations.")
private boolean preserveTypeAnnotations = false;
@Option(
name = "--inject_libraries",
handler = BooleanOptionHandler.class,
usage = "Allow injecting runtime libraries.")
private boolean injectLibraries = true;
@Option(
name = "--force_inject_library",
usage =
"Force injection of named runtime libraries. "
+ "The format is where is the name of a runtime library. "
+ "Possible libraries include: base, es6_runtime")
private List forceInjectLibraries = new ArrayList<>();
@Option(
name = "--dependency_mode",
usage =
"Specifies how the compiler should determine the set and order of files for a "
+ "compilation. Options: NONE the compiler will include all src files in the order "
+ "listed, SORT_ONLY the compiler will include all source files in dependency "
+ "order, PRUNE files will only be included if they are transitive dependencies "
+ "of files listed in the --entry_point flag and then sorted in dependency order, "
+ "PRUNE_LEGACY same as PRUNE but files that do not goog.provide a namespace and "
+ "are not modules will be automatically added as --entry_point entries. Defaults "
+ "to PRUNE_LEGACY if entry points are defined, otherwise to NONE.")
private @Nullable DependencyMode dependencyMode =
null; // so we can tell whether it was explicitly set
@Option(
name = "--entry_point",
usage =
"A file or namespace to use as the starting point for determining "
+ "which src files to include in the compilation. ES6 and CommonJS "
+ "modules are specified as file paths (without the extension). "
+ "Closure-library namespaces are specified with a \"goog:\" prefix. "
+ "Example: --entry_point=goog:goog.Promise")
private List entryPoint = new ArrayList<>();
@Option(
name = "--rewrite_polyfills",
handler = BooleanOptionHandler.class,
usage =
"Injects polyfills for ES2015+ library classes and methods used in source. See also"
+ " the \"Polyfills\" GitHub Wiki page.")
private boolean rewritePolyfills = true;
@Option(
name = "--isolate_polyfills",
handler = BooleanOptionHandler.class,
usage =
"Hides injected polyfills from the global scope and any external code. See the"
+ " the \"Polyfills\" GitHub Wiki page for details.")
private boolean isolatePolyfills = false;
@Option(
name = "--print_source_after_each_pass",
handler = BooleanOptionHandler.class,
hidden = true,
usage = "Whether to iteratively print resulting JS source per pass.")
private boolean printSourceAfterEachPass = false;
@Option(
name = "--module_resolution",
usage =
"Specifies how the compiler locates modules. BROWSER requires all module imports "
+ "to begin with a '.' or '/' and have a file extension. NODE uses the node module "
+ "rules. WEBPACK looks up modules from a special lookup map.")
private ModuleLoader.ResolutionMode moduleResolutionMode = ModuleLoader.ResolutionMode.BROWSER;
@Option(
name = "--browser_resolver_prefix_replacements",
usage =
"Prefixes to replace in ES6 import paths before resolving. "
+ "module_resolution must be BROWSER_WITH_TRANSFORMED_PREFIXES to take effect.")
private Map browserResolverPrefixReplacements = new HashMap<>();
@Option(
name = "--package_json_entry_names",
usage =
"Ordered list of entries to look for in package.json files when processing modules"
+ " with the NODE module resolution strategy (i.e. esnext:main,browser,main)."
+ " Defaults to a list with the following entries: \"browser\", \"module\","
+ " \"main\".")
private @Nullable String packageJsonEntryNames = null;
@Option(name = "--error_format", usage = "Specifies format for error messages.")
private ErrorFormatOption errorFormat = ErrorFormatOption.STANDARD;
@Option(
name = "--renaming",
handler = BooleanOptionHandler.class,
usage = "Disables variable renaming. Cannot be used with ADVANCED optimizations.")
private boolean renaming = true;
@Option(
name = "--help_markdown",
handler = BooleanOptionHandler.class,
hidden = true,
usage = "Prints markdown formatted flag usage")
private boolean helpMarkdown = false;
@Option(
name = "--instrument_for_coverage_option",
usage =
"Enable code instrumentation to perform code coverage analysis. Options are:\n"
+ " 1. NONE (default)\n"
+ " 2. LINE - Instrument code by line.\n"
+ " 3. BRANCH - Instrument code by branch.\n"
+ " 4. PRODUCTION - Function Instrumentation on compiled JS code.\n")
private String instrumentForCoverageOption = "NONE";
@Option(
name = "--production_instrumentation_array_name",
usage =
"Name of the global array used by production instrumentation. The array name "
+ "should be declared as an extern so it is not renamed by the compiler. A function"
+ "that parses the global array should also be included. This flag is to be used in"
+ "tandem with --instrument_code=PRODUCTION")
private String productionInstrumentationArrayName = "";
@Option(
name = "--chunk_output_type",
usage =
"Indicates what format the compiler should use for output chunks. GLOBAL_NAMESPACE is "
+ "typically used in conjunction with --rename_prefix_namespace. ES_MODULES "
+ "outputs chunks as proper modules with 'import' and 'export' statements.")
private ChunkOutputType chunkOutputType = ChunkOutputType.GLOBAL_NAMESPACE;
private InstrumentOption instrumentCodeParsed = InstrumentOption.NONE;
@Option(
name = "--allow_dynamic_import",
handler = BooleanOptionHandler.class,
usage =
"Indicates that the compiler should allow dynamic import expressions. Dynamic import "
+ "expressions are not yet fully supported and may lead to broken output code.")
private boolean allowDynamicImport = true;
@Option(
name = "--dynamic_import_alias",
usage =
"Instructs the compiler to replace dynamic imports expressions with a function call "
+ "using the specified name. Allows dynamic import expressions to be externally "
+ "polyfilled when the output language level does not natively support them. "
+ "An alias of 'import' is allowed.")
private @Nullable String dynamicImportAlias = null;
@Option(
name = "--assume_static_inheritance_is_not_used",
handler = BooleanOptionHandler.class,
usage =
"Assume that static (class-side) inheritance is not being used and that static"
+ " methods will not be referenced via `this` or through subclasses. This enables"
+ " optimizations that could break code that did those things.")
private boolean assumeStaticInheritanceIsNotUsed = true;
@Option(
name = "--assume_no_prototype_method_enumeration",
handler = BooleanOptionHandler.class,
usage =
"Assume that prototype method enumeration is not being used. This allows the compiler "
+ "to move a prototype method declaration into a deeper chunk without creating "
+ "stub functions in a parent chunk.")
private boolean assumeNoPrototypeMethodEnumeration = false;
@Argument private List arguments = new ArrayList<>();
private final CmdLineParser parser;
Flags() {
parser = new CmdLineParser(this);
}
/** Parse the given args list. */
private void parse(List args) throws CmdLineException {
parser.parseArgument(args);
compilationLevelParsed = CompilationLevel.fromString(Ascii.toUpperCase(compilationLevel));
if (compilationLevelParsed == null) {
throw new CmdLineException(
parser, "Bad value for --compilation_level: " + compilationLevel);
}
instrumentCodeParsed =
InstrumentOption.fromString(Ascii.toUpperCase(instrumentForCoverageOption));
if (instrumentCodeParsed == null) {
throw new CmdLineException(
parser,
"Bad value for --instrument_for_coverage_option: " + instrumentForCoverageOption);
}
}
private static final ImmutableMultimap categories =
new ImmutableMultimap.Builder()
.putAll(
"Basic Usage",
ImmutableList.of(
"compilation_level",
"env",
"externs",
"js",
"js_output_file",
"language_in",
"language_out",
"warning_level"))
.putAll(
"Warning and Error Management",
ImmutableList.of(
"conformance_configs",
"error_format",
"extra_annotation_name",
"hide_warnings_for",
"jscomp_error",
"jscomp_off",
"jscomp_warning",
"strict_mode_input",
"warnings_allowlist_file"))
.putAll(
"Output",
ImmutableList.of(
"assume_function_wrapper",
"debug",
"emit_use_strict",
"export_local_property_definitions",
"formatting",
"generate_exports",
"isolation_mode",
"output_wrapper",
"output_wrapper_file",
"rename_variable_prefix"))
.putAll("Dependency Management", ImmutableList.of("dependency_mode", "entry_point"))
.putAll(
"JS Modules",
ImmutableList.of(
"dynamic_import_alias",
"js_module_root",
"module_resolution",
"process_common_js_modules",
"package_json_entry_names"))
.putAll(
"Library and Framework Specific",
ImmutableList.of(
"angular_pass",
"force_inject_library",
"inject_libraries",
"polymer_version",
"process_closure_primitives",
"rewrite_polyfills",
"isolate_polyfills"))
.putAll(
"Code Splitting",
ImmutableList.of(
"chunk",
"chunk_output_path_prefix",
"chunk_output_type",
"chunk_wrapper",
"rename_prefix_namespace"))
.putAll(
"Reports",
ImmutableList.of(
"create_source_map",
"output_manifest",
"output_chunk_dependencies",
"property_renaming_report",
"source_map_input",
"source_map_include_content",
"source_map_location_mapping",
"variable_renaming_report"))
.putAll(
"Miscellaneous",
ImmutableList.of(
"assume_static_inheritance_is_not_used",
"browser_featureset_year",
"charset",
"checks_only",
"define",
"flagfile",
"help",
"json_streams",
"third_party",
"use_types_for_optimization",
"version"))
.build();
private void printUsage(PrintStream ps) {
OutputStreamWriter outputStream = new OutputStreamWriter(ps, UTF_8);
boolean isFirst = true;
for (Map.Entry> entry : categories.asMap().entrySet()) {
String prefix = "\n\n";
String suffix = "";
if (isFirst) {
isFirst = false;
prefix = "";
}
if (entry.getKey().equals("Warning and Error Management")) {
if (helpMarkdown) {
suffix =
"\n## Available Error Groups\n\n"
+ " - "
+ DiagnosticGroups.DIAGNOSTIC_GROUP_NAMES.replace(", ", "\n - ");
} else {
suffix =
"\n"
+ BOLD_PREFIX
+ "Available Error Groups: "
+ NORMAL_PREFIX
+ DiagnosticGroups.DIAGNOSTIC_GROUP_NAMES;
}
}
if (this.helpMarkdown) {
// For markdown docs we don't want any line wrapping so we just set a very
// large line length.
maxLineLength = 5000;
parser.setUsageWidth(maxLineLength);
}
printCategoryUsage(entry.getKey(), entry.getValue(), outputStream, prefix, suffix);
}
ps.flush();
}
private static final String BOLD_PREFIX = "\033[1m";
private static final String NORMAL_PREFIX = "\033[0m";
private static final String MARKDOWN_CHARS_TO_ESCAPE = "[-*\\`\\[\\]{}\\(\\)#+\\.!<>]";
private void printCategoryUsage(
String categoryName,
final Collection options,
OutputStreamWriter outputStream,
String prefix,
String suffix) {
try {
if (prefix != null) {
printStringLineWrapped(prefix, outputStream);
}
if (this.helpMarkdown) {
outputStream.write("# " + categoryName + "\n");
for (String optionName : options) {
StringWriter stringWriter = new StringWriter();
parser.printUsage(
stringWriter,
null,
(optionHandler) -> {
if (optionHandler.option instanceof NamedOptionDef) {
return !optionHandler.option.hidden()
&& optionName.equals(
((NamedOptionDef) optionHandler.option).name().replaceFirst("^--", ""));
}
return false;
});
stringWriter.flush();
String rawOptionUsage = stringWriter.toString();
Matcher optionNameMatches = Pattern.compile(" *--([a-z0-9_]+)").matcher(rawOptionUsage);
int delimiterIndex = rawOptionUsage.indexOf(" : ");
if (delimiterIndex > 0) {
outputStream.write(
"\n**" + rawOptionUsage.substring(0, delimiterIndex).trim() + "** \n");
String optionDescription =
rawOptionUsage
.substring(delimiterIndex + 3)
.replaceAll(MARKDOWN_CHARS_TO_ESCAPE, "\\\\$0")
.trim();
outputStream.write(optionDescription + "\n");
} else {
outputStream.write(rawOptionUsage.replaceAll(MARKDOWN_CHARS_TO_ESCAPE, "\\\\$0"));
}
outputStream.flush();
}
} else {
outputStream.write(BOLD_PREFIX + categoryName + ":\n" + NORMAL_PREFIX);
parser.printUsage(
outputStream,
null,
(optionHandler) -> {
if (optionHandler.option instanceof NamedOptionDef) {
return !optionHandler.option.hidden()
&& options.contains(
((NamedOptionDef) optionHandler.option).name().replaceFirst("^--", ""));
}
return false;
});
}
if (suffix != null) {
printStringLineWrapped(suffix, outputStream);
}
} catch (IOException e) {
// Ignore.
}
}
private int maxLineLength = 80;
private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s");
private void printStringLineWrapped(String input, OutputStreamWriter outputStream)
throws IOException {
if (input.length() < maxLineLength) {
outputStream.write(input);
return;
}
int endIndex = maxLineLength;
String subString = input.substring(0, maxLineLength);
Matcher whitespaceMatcher = WHITESPACE_PATTERN.matcher(subString);
boolean foundMatch = false;
while (whitespaceMatcher.find()) {
endIndex = whitespaceMatcher.start();
foundMatch = true;
}
outputStream.write(input.substring(0, endIndex) + "\n");
printStringLineWrapped(
" " + input.substring(foundMatch ? endIndex + 1 : endIndex), outputStream);
}
private static void printShortUsageAfterErrors(PrintStream ps) {
ps.print("Sample usage: ");
ps.println(
"--compilation_level (-O) VAL --externs VAL --js VAL"
+ " --js_output_file VAL"
+ " --warning_level (-W) [QUIET | DEFAULT | VERBOSE]");
ps.println("Run with --help for all options and details");
ps.flush();
}
/**
* Users may specify JS inputs via the {@code --js} flag, as well as via additional arguments to
* the Closure Compiler. For example, it is convenient to leverage the additional arguments
* feature when using the Closure Compiler in combination with {@code find} and {@code xargs}:
*
*
* find MY_JS_SRC_DIR -name '*.js' \
* | xargs java -jar compiler.jar --dependency_mode=PRUNE_LEGACY
*
*
* The {@code find} command will produce a list of '*.js' source files in the {@code
* MY_JS_SRC_DIR} directory while {@code xargs} will convert them to a single, space-delimited
* set of arguments that are appended to the {@code java} command to run the Compiler.
*
* Note that it is important to use the {@code --dependency_mode=PRUNE or PRUNE_LEGACY}
* option in this case because the order produced by {@code find} is unlikely to be sorted
* correctly with respect to {@code goog.provide()} and {@code goog.requires()}.
*/
protected List> getMixedJsSources()
throws CmdLineException, IOException {
List> mixedSources = new ArrayList<>();
Set excludes = new HashSet<>();
for (FlagEntry source : Flags.mixedJsSources) {
if (source.getValue().endsWith(".zip")) {
mixedSources.add(source);
} else if (source.getValue().startsWith("!")) {
for (String filename : findJsFiles(ImmutableList.of(source.getValue().substring(1)))) {
excludes.add(filename);
mixedSources.remove(new FlagEntry<>(JsSourceType.JS, filename));
}
} else {
for (String filename : findJsFiles(Collections.singletonList(source.getValue()), true)) {
if (!excludes.contains(filename)) {
mixedSources.add(new FlagEntry<>(JsSourceType.JS, filename));
}
}
}
}
List fromArguments = findJsFiles(arguments);
for (String filename : fromArguments) {
mixedSources.add(new FlagEntry<>(JsSourceType.JS, filename));
}
if (!Flags.mixedJsSources.isEmpty() && !arguments.isEmpty() && mixedSources.isEmpty()) {
throw new CmdLineException(parser, "No inputs matched");
}
return mixedSources;
}
List getSourceMapLocationMappings() throws CmdLineException {
ImmutableList.Builder locationMappings = ImmutableList.builder();
ImmutableMap split =
splitPipeParts(sourceMapLocationMapping, "--source_map_location_mapping");
for (Map.Entry mapping : split.entrySet()) {
locationMappings.add(
new SourceMap.PrefixLocationMapping(mapping.getKey(), mapping.getValue()));
}
return locationMappings.build();
}
ImmutableMap getSourceMapInputs() throws CmdLineException {
return splitPipeParts(sourceMapInputs, "--source_map_input");
}
private ImmutableMap splitPipeParts(Iterable input, String flagName)
throws CmdLineException {
ImmutableMap.Builder result = new ImmutableMap.Builder<>();
Splitter splitter = Splitter.on('|').limit(2);
for (String inputSourceMap : input) {
List parts = splitter.splitToList(inputSourceMap);
if (parts.size() != 2) {
throw new CmdLineException(
parser, "Bad value for " + flagName + " (duplicate key): " + input);
}
result.put(parts.get(0), parts.get(1));
}
return result.buildOrThrow();
}
List getPackageJsonEntryNames() throws CmdLineException {
return Splitter.on(',').splitToList(packageJsonEntryNames);
}
// Our own option parser to be backwards-compatible.
// It needs to be public because of the crazy reflection that args4j does.
public static class BooleanOptionHandler extends OptionHandler {
private static final ImmutableSet TRUES = ImmutableSet.of("true", "on", "yes", "1");
private static final ImmutableSet FALSES = ImmutableSet.of("false", "off", "no", "0");
// Handlers are used reflectively by args4j
@Keep
public BooleanOptionHandler(
CmdLineParser parser, OptionDef option, Setter super Boolean> setter) {
super(parser, option, setter);
}
@Override
public int parseArguments(Parameters params) throws CmdLineException {
String param = null;
try {
param = params.getParameter(0);
} catch (CmdLineException e) {
param = null; // to stop linter complaints
}
if (param == null) {
setter.addValue(true);
return 0;
} else {
String lowerParam = Ascii.toLowerCase(param);
if (TRUES.contains(lowerParam)) {
setter.addValue(true);
} else if (FALSES.contains(lowerParam)) {
setter.addValue(false);
} else {
setter.addValue(true);
return 0;
}
return 1;
}
}
@Override
public String getDefaultMetaVariable() {
return null;
}
}
// Our own parser for warning guards that preserves the original order
// of the flags.
public static class WarningGuardErrorOptionHandler extends StringOptionHandler {
// Handlers are used reflectively by args4j
@Keep
public WarningGuardErrorOptionHandler(
CmdLineParser parser, OptionDef option, Setter super String> setter) {
super(parser, option, new MultiFlagSetter<>(setter, CheckLevel.ERROR, guardLevels));
}
}
public static class WarningGuardWarningOptionHandler extends StringOptionHandler {
// Handlers are used reflectively by args4j
@Keep
public WarningGuardWarningOptionHandler(
CmdLineParser parser, OptionDef option, Setter super String> setter) {
super(parser, option, new MultiFlagSetter<>(setter, CheckLevel.WARNING, guardLevels));
}
}
public static class WarningGuardOffOptionHandler extends StringOptionHandler {
// Handlers are used reflectively by args4j
@Keep
public WarningGuardOffOptionHandler(
CmdLineParser parser, OptionDef option, Setter super String> setter) {
super(parser, option, new MultiFlagSetter<>(setter, CheckLevel.OFF, guardLevels));
}
}
public static class JsOptionHandler extends StringOptionHandler {
// Handlers are used reflectively by args4j
@Keep
public JsOptionHandler(
CmdLineParser parser, OptionDef option, Setter super String> setter) {
super(parser, option, new MultiFlagSetter<>(setter, JsSourceType.JS, mixedJsSources));
}
}
public static class JsZipOptionHandler extends StringOptionHandler {
// Handlers are used reflectively by args4j
@Keep
public JsZipOptionHandler(
CmdLineParser parser, OptionDef option, Setter super String> setter) {
super(parser, option, new MultiFlagSetter<>(setter, JsSourceType.JS_ZIP, mixedJsSources));
}
}
private static class MultiFlagSetter implements Setter {
private final Setter super String> proxy;
private final T flag;
private final List> entries;
private MultiFlagSetter(Setter super String> proxy, T flag, List> entries) {
this.proxy = proxy;
this.flag = flag;
this.entries = entries;
}
@Override
public boolean isMultiValued() {
return proxy.isMultiValued();
}
@Override
public Class getType() {
return (Class) proxy.getType();
}
@Override
public void addValue(String value) throws CmdLineException {
// On windows, some quoted values seem to preserve the quotes as part of the value.
String normalizedValue = value;
if (value != null
&& value.length() > 0
&& (value.substring(0, 1).equals("'") || value.substring(0, 1).equals("\""))
&& value.substring(value.length() - 1).equals(value.substring(0, 1))) {
normalizedValue = value.substring(1, value.length() - 1);
}
proxy.addValue(normalizedValue);
entries.add(new FlagEntry<>(flag, normalizedValue));
}
@Override
public FieldSetter asFieldSetter() {
return proxy.asFieldSetter();
}
@Override
public AnnotatedElement asAnnotatedElement() {
return proxy.asAnnotatedElement();
}
}
}
/** Set of options that can be used with the --formatting flag. */
public static enum FormattingOption {
PRETTY_PRINT,
PRINT_INPUT_DELIMITER,
SINGLE_QUOTES;
private void applyToOptions(CompilerOptions options) {
switch (this) {
case PRETTY_PRINT:
options.setPrettyPrint(true);
break;
case PRINT_INPUT_DELIMITER:
options.printInputDelimiter = true;
break;
case SINGLE_QUOTES:
options.setPreferSingleQuotes(true);
break;
}
}
}
private final Flags flags = new Flags();
private boolean errors = false;
private boolean runCompiler = false;
/** Cached error stream to avoid passing it as a parameter to helper functions. */
private @Nullable PrintStream errorStream;
/**
* Create a new command-line runner. You should only need to call the constructor if you're
* extending this class. Otherwise, the main method should instantiate it.
*/
protected CommandLineRunner(String[] args) {
super();
initConfigFromFlags(args, System.out, System.err);
}
protected CommandLineRunner(String[] args, PrintStream out, PrintStream err) {
super(out, err);
initConfigFromFlags(args, out, err);
}
protected CommandLineRunner(String[] args, InputStream in, PrintStream out, PrintStream err) {
super(in, out, err);
initConfigFromFlags(args, out, err);
}
private static List processArgs(String[] args) {
// Args4j has a different format that the old command-line parser.
// So we use some voodoo to get the args into the format that args4j
// expects.
Pattern argPattern = Pattern.compile("(--?[a-zA-Z_]+)=(.*)", Pattern.DOTALL);
Pattern quotesPattern = Pattern.compile("^['\"](.*)['\"]$");
List processedArgs = new ArrayList<>();
for (String arg : args) {
Matcher matcher = argPattern.matcher(arg);
if (matcher.matches()) {
processedArgs.add(matcher.group(1));
String value = matcher.group(2);
Matcher quotesMatcher = quotesPattern.matcher(value);
if (quotesMatcher.matches()) {
processedArgs.add(quotesMatcher.group(1));
} else {
processedArgs.add(value);
}
} else {
processedArgs.add(arg);
}
}
return processedArgs;
}
private void reportError(String message) {
errors = true;
errorStream.println(message);
errorStream.flush();
}
private void processFlagFiles() throws CmdLineException {
for (String flagFile : flags.flagFiles) {
try {
processFlagFile(flagFile);
} catch (IOException ioErr) {
reportError("ERROR - " + flagFile + " read error.");
}
}
}
private void processFlagFile(String flagFileString) throws CmdLineException, IOException {
Path flagFile = Paths.get(flagFileString);
BufferedReader buffer = java.nio.file.Files.newBufferedReader(flagFile, UTF_8);
// Builds the tokens.
StringBuilder builder = new StringBuilder();
// Stores the built tokens.
List tokens = new ArrayList<>();
// Indicates if we are in a "quoted" token.
boolean quoted = false;
// Indicates if the char being processed has been escaped.
boolean escaped = false;
// Indicates whether this is the beginning of the file.
boolean isFirstCharacter = true;
int c;
while ((c = buffer.read()) != -1) {
// Ignoring the BOM.
if (isFirstCharacter) {
isFirstCharacter = false;
if (c == UTF8_BOM_CODE) {
continue;
}
}
if (c == 32 || c == 9 || c == 10 || c == 13) {
if (quoted) {
builder.append((char) c);
} else if (builder.length() != 0) {
tokens.add(builder.toString());
builder.setLength(0);
}
} else if (c == 34) {
if (escaped) {
if (quoted) {
builder.setCharAt(builder.length() - 1, (char) c);
} else {
builder.append((char) c);
}
} else {
quoted = !quoted;
}
} else {
builder.append((char) c);
}
escaped = c == 92;
}
buffer.close();
if (builder.length() != 0) {
tokens.add(builder.toString());
}
flags.flagFiles = new ArrayList<>();
tokens = processArgs(tokens.toArray(new String[0]));
// Command-line warning levels should override flag file settings,
// which means they should go last.
List> previousGuardLevels = new ArrayList<>(Flags.guardLevels);
List> previousMixedJsSources = new ArrayList<>(Flags.mixedJsSources);
Flags.guardLevels.clear();
Flags.mixedJsSources.clear();
flags.parse(tokens);
Flags.guardLevels.addAll(previousGuardLevels);
Flags.mixedJsSources.addAll(previousMixedJsSources);
// Currently we are not supporting this (prevent direct/indirect loops)
if (!flags.flagFiles.isEmpty()) {
reportError("ERROR - Arguments in the file cannot contain " + "--flagfile option.");
}
}
protected final String getVersionText() {
return String.join(
"\n", //
"Closure Compiler (http://github.com/google/closure-compiler)",
// CommandLineRunnerVersion refers to class created from CommandLineRunnerVersion.template
// with its static field COMPILER_VERSION substituted at build time.
"Version: " + CommandLineRunnerVersion.COMPILER_VERSION);
}
private void initConfigFromFlags(String[] args, PrintStream out, PrintStream err) {
errorStream = err;
List processedArgs = processArgs(args);
Flags.guardLevels.clear();
Flags.mixedJsSources.clear();
List> mixedSources = null;
List mappings = null;
ImmutableMap sourceMapInputs = null;
boolean parseInlineSourceMaps = false;
boolean applyInputSourceMaps = false;
try {
flags.parse(processedArgs);
processFlagFiles();
mixedSources = flags.getMixedJsSources();
mappings = flags.getSourceMapLocationMappings();
sourceMapInputs = flags.getSourceMapInputs();
parseInlineSourceMaps = flags.parseInlineSourceMaps;
applyInputSourceMaps = flags.applyInputSourceMaps;
} catch (CmdLineException e) {
reportError(e.getMessage());
} catch (IOException ioErr) {
reportError("ERROR - ioException: " + ioErr);
}
if (flags.processCommonJsModules) {
flags.processClosurePrimitives = true;
}
if (flags.browserFeaturesetYear != 0 && flags.languageOut != "ECMASCRIPT_NEXT") {
throw new FlagUsageException(
"ERROR - both flags `--browser_featureset_year` and `--language_out` specified.");
}
if (flags.outputWrapper == null) {
flags.outputWrapper = "";
}
if (flags.outputWrapperFile != null && !flags.outputWrapperFile.isEmpty()) {
try {
flags.outputWrapper = Files.asCharSource(new File(flags.outputWrapperFile), UTF_8).read();
} catch (Exception e) {
reportError("ERROR - invalid output_wrapper_file specified.");
}
}
if (!flags.outputWrapper.isEmpty()
&& !flags.outputWrapper.contains(CommandLineRunner.OUTPUT_MARKER)) {
reportError(
"ERROR - invalid output_wrapper specified. Missing '"
+ CommandLineRunner.OUTPUT_MARKER
+ "'.");
}
if (!flags.outputWrapper.isEmpty() && flags.isolationMode != IsolationMode.NONE) {
reportError("--output_wrapper and --isolation_mode may not be used together.");
}
if (flags.isolationMode == IsolationMode.IIFE) {
flags.outputWrapper = "(function(){%output%}).call(this);";
}
// Handle --compilation_level=BUNDLE
ImmutableList bundleFiles = ImmutableList.of();
boolean skipNormalOutputs = false;
if (flags.compilationLevelParsed == CompilationLevel.BUNDLE) {
if (flags.jsOutputFile.isEmpty()) {
reportError("--compilation_level=BUNDLE cannot be used without a --js_output_file.");
} else {
bundleFiles = ImmutableList.of(flags.jsOutputFile);
flags.jsOutputFile = "";
skipNormalOutputs = true;
}
}
CodingConvention conv;
if (flags.thirdParty) {
conv = CodingConventions.getDefault();
} else if (flags.chromePass) {
conv = new ChromeCodingConvention();
} else {
conv = new ClosureCodingConvention();
}
if (!flags.renaming
&& flags.compilationLevelParsed == CompilationLevel.ADVANCED_OPTIMIZATIONS) {
reportError("ERROR - renaming cannot be disabled when ADVANCED_OPTIMIZATIONS is used.");
}
DependencyOptions dependencyOptions = null;
try {
dependencyOptions =
DependencyOptions.fromFlags(
flags.dependencyMode, flags.entryPoint, ImmutableList.of(), null, false, false);
} catch (FlagUsageException e) {
reportError(e.getMessage());
}
if (errors) {
Flags.printShortUsageAfterErrors(errorStream);
} else if (flags.displayHelp || flags.helpMarkdown) {
flags.printUsage(out);
} else {
runCompiler = true;
final CommandLineConfig config = getCommandLineConfig();
config
.setPrintVersion(flags.version)
.setPrintTree(flags.printTree)
.setPrintTreeJson(flags.printTreeJson)
.setPrintAst(flags.printAst)
.setJscompDevMode(flags.jscompDevMode)
.setLoggingLevel(flags.loggingLevel)
.setExterns(flags.externs)
.setMixedJsSources(mixedSources)
.setDefaultToStdin()
.setJsOutputFile(flags.jsOutputFile)
.setModule(flags.chunk)
.setVariableMapOutputFile(flags.variableMapOutputFile)
.setCreateNameMapFiles(flags.createNameMapFiles)
.setPropertyMapOutputFile(flags.propertyMapOutputFile)
.setInstrumentationMappingFile(flags.instrumentationMappingOutputFile)
.setCodingConvention(conv)
.setSummaryDetailLevel(flags.summaryDetailLevel)
.setOutputWrapper(flags.outputWrapper)
.setModuleWrapper(flags.chunkWrapper)
.setModuleOutputPathPrefix(flags.chunkOutputPathPrefix)
.setCreateSourceMap(flags.createSourceMap)
.setSourceMapFormat(flags.sourceMapFormat)
.setSourceMapLocationMappings(mappings)
.setSourceMapInputFiles(sourceMapInputs)
.setParseInlineSourceMaps(parseInlineSourceMaps)
.setApplyInputSourceMaps(applyInputSourceMaps)
.setWarningGuards(Flags.guardLevels)
.setDefine(flags.define)
.setBrowserFeaturesetYear(flags.browserFeaturesetYear)
.setEmitAsyncFunctionsWithZonejs(flags.emitAsyncFunctionsWithZonejs)
.setCharset(flags.charset)
.setDependencyOptions(dependencyOptions)
.setOutputManifest(ImmutableList.of(flags.outputManifest))
.setOutputBundle(bundleFiles)
.setSkipNormalOutputs(skipNormalOutputs)
.setOutputModuleDependencies(flags.outputChunkDependencies)
.setProcessCommonJSModules(flags.processCommonJsModules)
.setModuleRoots(flags.moduleRoot)
.setWarningsAllowlistFile(flags.warningsAllowlistFile)
.setHideWarningsFor(flags.hideWarningsFor)
.setAngularPass(flags.angularPass)
.setJsonStreamMode(flags.jsonStreamMode)
.setErrorFormat(flags.errorFormat);
String stage1RestoreFile = flags.restoreStage1FromFile;
if (stage1RestoreFile == null) {
// TODO(bradfordcsmith): deprecate and remove this flag
stage1RestoreFile = flags.continueSavedCompilationFile;
}
if (stage1RestoreFile != null) {
config.setContinueSavedCompilationFileName(stage1RestoreFile, /* stage= */ 1);
}
String stage2RestoreFile = flags.restoreStage2FromFile;
if (stage1RestoreFile != null) {
checkState(stage2RestoreFile == null, "cannot restore both from stage 1 and from stage 2");
config.setContinueSavedCompilationFileName(stage1RestoreFile, 1);
} else if (stage2RestoreFile != null) {
config.setContinueSavedCompilationFileName(stage2RestoreFile, 2);
}
String stage1SaveFile = flags.saveStage1ToFile;
if (stage1SaveFile == null) {
// TODO(bradfordcsmith): deprecate and remove this flag
stage1SaveFile = flags.saveAfterChecksFile;
}
String stage2SaveFile = flags.saveStage2ToFile;
if (stage1SaveFile != null) {
checkState(stage2SaveFile == null, "cannot save both stage 1 and stage 2");
checkState(stage1RestoreFile == null, "cannot perform stage 1 on a restored stage 1");
config.setSaveCompilationStateToFilename(stage1SaveFile, 1);
} else if (stage2SaveFile != null) {
checkState(stage2RestoreFile == null, "Cannot perform stage 2 on a restored stage 2");
checkState(stage1RestoreFile != null, "Saving stage 2 requires restoring from stage 1");
config.setSaveCompilationStateToFilename(stage2SaveFile, 2);
}
}
errorStream = null;
}
@Override
protected void addAllowlistWarningsGuard(CompilerOptions options, File allowlistFile) {
options.addWarningsGuard(AllowlistWarningsGuard.fromFile(allowlistFile));
}
@Override
protected void checkModuleName(String name) {
if (!TokenStream.isJSIdentifier(extraChunkNameChars.matcher(name).replaceAll("_"))) {
throw new FlagUsageException("Invalid chunk name: '" + name + "'");
}
}
@Override
protected CompilerOptions createOptions() {
CompilerOptions options = new CompilerOptions();
if (!flags.languageIn.isEmpty()) {
LanguageMode languageMode = LanguageMode.fromString(flags.languageIn);
if (languageMode == LanguageMode.UNSUPPORTED) {
throw new FlagUsageException(
"Cannot specify the unsupported set of features for language_in.");
}
if (languageMode != null) {
options.setLanguageIn(languageMode);
} else {
throw new FlagUsageException("Unknown language `" + flags.languageIn + "' specified.");
}
}
LanguageMode languageMode = LanguageMode.fromString(flags.languageOut);
if (languageMode == LanguageMode.UNSUPPORTED) {
throw new FlagUsageException(
"Cannot specify the unsupported set of features for language_out.");
}
if (languageMode != null) {
options.setLanguageOut(languageMode);
} else {
throw new FlagUsageException("Unknown language `" + flags.languageOut + "' specified.");
}
options.setCodingConvention(new ClosureCodingConvention());
options.setExtraAnnotationNames(flags.extraAnnotationName);
CompilationLevel level = flags.compilationLevelParsed;
level.setOptionsForCompilationLevel(options);
if (flags.debug) {
level.setDebugOptionsForCompilationLevel(options);
}
options.setNumParallelThreads(flags.numParallelThreads);
options.setEnvironment(flags.environment);
options.setChecksOnly(flags.checksOnly);
if (flags.checksOnly) {
options.setOutputJs(CompilerOptions.OutputJs.NONE);
}
options.setIncrementalChecks(flags.incrementalCheckMode);
options.setContinueAfterErrors(flags.continueAfterErrors);
// TODO(b/144593112): remove this flag.
options.setBadRewriteModulesBeforeTypecheckingThatWeWantToGetRidOf(true);
if (flags.useTypesForOptimization) {
level.setTypeBasedOptimizationOptions(options);
}
if (flags.assumeFunctionWrapper
|| flags.isolationMode == IsolationMode.IIFE
|| flags.chunkOutputType == ChunkOutputType.ES_MODULES) {
level.setWrappedOutputOptimizations(options);
}
if (flags.typedAstOutputFile != null) {
options.setTypedAstOutputFile(Paths.get(flags.typedAstOutputFile));
}
options.setGenerateExports(flags.generateExports);
options.setExportLocalPropertyDefinitions(flags.exportLocalPropertyDefinitions);
WarningLevel wLevel = flags.warningLevel;
wLevel.setOptionsForWarningLevel(options);
for (FormattingOption formattingOption : flags.formatting) {
formattingOption.applyToOptions(options);
}
options.closurePass = flags.processClosurePrimitives;
options.angularPass = flags.angularPass;
options.polymerVersion = flags.polymerVersion;
try {
options.polymerExportPolicy =
PolymerExportPolicy.valueOf(Ascii.toUpperCase(flags.polymerExportPolicy));
} catch (IllegalArgumentException ex) {
throw new FlagUsageException(
"Unknown PolymerExportPolicy `" + flags.polymerExportPolicy + "' specified.");
}
options.setChromePass(flags.chromePass);
if (!flags.j2clPassMode.isEmpty()) {
try {
CompilerOptions.J2clPassMode j2clPassMode =
CompilerOptions.J2clPassMode.valueOf(Ascii.toUpperCase(flags.j2clPassMode));
options.setJ2clPass(j2clPassMode);
} catch (IllegalArgumentException ex) {
throw new FlagUsageException(
"Unknown J2clPassMode `" + flags.j2clPassMode + "' specified.");
}
}
options.removeJ2clAsserts = flags.removeJ2cLAsserts;
options.renamePrefix = flags.renamePrefix;
options.renamePrefixNamespace = flags.renamePrefixNamespace;
options.setPreserveTypeAnnotations(flags.preserveTypeAnnotations);
options.setPreventLibraryInjection(!flags.injectLibraries);
if (!flags.forceInjectLibraries.isEmpty()) {
options.setForceLibraryInjection(flags.forceInjectLibraries);
}
options.rewritePolyfills =
flags.rewritePolyfills
&& options.getLanguageIn().toFeatureSet().contains(FeatureSet.ES2015);
options.setIsolatePolyfills(flags.isolatePolyfills);
if (!flags.translationsFile.isEmpty()) {
try {
options.messageBundle =
new XtbMessageBundle(
new FileInputStream(flags.translationsFile), flags.translationsProject);
} catch (IOException e) {
throw new RuntimeException("Reading XTB file", e);
}
} else if (CompilationLevel.ADVANCED_OPTIMIZATIONS == level) {
// In SIMPLE or WHITESPACE mode, if the user hasn't specified a
// translations file, they might reasonably try to write their own
// implementation of goog.getMsg that makes the substitution at
// run-time.
//
// In ADVANCED mode, goog.getMsg is going to be renamed anyway,
// so we might as well inline it. But shut off the i18n warnings,
// because the user didn't really ask for i18n.
options.messageBundle = new EmptyMessageBundle();
options.setWarningLevel(DiagnosticGroups.MSG_CONVENTIONS, CheckLevel.OFF);
}
options.setConformanceConfigs(loadConformanceConfigs(flags.conformanceConfigs));
options.setPrintSourceAfterEachPass(flags.printSourceAfterEachPass);
options.setTracerMode(flags.tracerMode);
options.setStrictModeInput(flags.strictModeInput);
options.setEmitUseStrict(flags.emitUseStrict);
options.setSourceMapIncludeSourcesContent(flags.sourceMapIncludeSourcesContent);
options.setModuleResolutionMode(flags.moduleResolutionMode);
options.setBrowserResolverPrefixReplacements(
ImmutableMap.copyOf(flags.browserResolverPrefixReplacements));
if (flags.packageJsonEntryNames != null) {
try {
List packageJsonEntryNames = flags.getPackageJsonEntryNames();
options.setPackageJsonEntryNames(packageJsonEntryNames);
} catch (CmdLineException e) {
reportError("ERROR - invalid package_json_entry_names format specified.");
}
}
if (!flags.renaming) {
options.setVariableRenaming(VariableRenamingPolicy.OFF);
options.setPropertyRenaming(PropertyRenamingPolicy.OFF);
}
if (flags.instrumentCodeParsed == InstrumentOption.PRODUCTION
&& Strings.isNullOrEmpty(flags.instrumentationMappingOutputFile)) {
throw new FlagUsageException(
"Expected --instrument_mapping_report to be set when "
+ "--instrument_for_coverage_option is set to Production");
}
if (!Strings.isNullOrEmpty(flags.instrumentationMappingOutputFile)
&& flags.instrumentCodeParsed != InstrumentOption.PRODUCTION) {
throw new FlagUsageException(
"Expected --instrument_for_coverage_option to be passed with PRODUCTION "
+ "when --instrument_mapping_report is set");
}
if (Strings.isNullOrEmpty(flags.productionInstrumentationArrayName)
&& flags.instrumentCodeParsed == InstrumentOption.PRODUCTION) {
throw new FlagUsageException(
"Expected --production_instrumentation_array_name to be set when "
+ "--instrument_for_coverage_option is set to Production");
}
options.setInstrumentForCoverageOption(flags.instrumentCodeParsed);
options.setProductionInstrumentationArrayName(flags.productionInstrumentationArrayName);
options.setAllowDynamicImport(flags.allowDynamicImport);
options.setDynamicImportAlias(flags.dynamicImportAlias);
options.setAssumeStaticInheritanceIsNotUsed(flags.assumeStaticInheritanceIsNotUsed);
options.setCrossChunkCodeMotionNoStubMethods(flags.assumeNoPrototypeMethodEnumeration);
if (flags.chunkOutputType == ChunkOutputType.ES_MODULES) {
if (flags.renamePrefixNamespace != null) {
throw new FlagUsageException(
"Expected --rename_prefix_namespace not to be specified when "
+ "--chunk_output_type is set to ES_MODULES.");
}
if (flags.emitUseStrict) {
throw new FlagUsageException(
"Expected --emit_use_strict should not be specified when "
+ "--chunk_output_type is set to ES_MODULES.");
}
options.chunkOutputType = flags.chunkOutputType;
options.setEmitUseStrict(false);
if (level == CompilationLevel.ADVANCED_OPTIMIZATIONS) {
options.setExtractPrototypeMemberDeclarations(
ExtractPrototypeMemberDeclarationsMode.USE_CHUNK_TEMP);
}
}
return options;
}
@Override
protected Compiler createCompiler() {
return new Compiler(getErrorPrintStream());
}
private ClosureBundler bundler;
private ClosureBundler getBundler() {
if (bundler != null) {
return bundler;
}
ImmutableList moduleRoots;
if (!flags.moduleRoot.isEmpty()) {
moduleRoots = ImmutableList.copyOf(flags.moduleRoot);
} else {
moduleRoots = ImmutableList.of(ModuleLoader.DEFAULT_FILENAME_PREFIX);
}
CompilerOptions options = createOptions();
return bundler =
new ClosureBundler(
Transpiler.NULL,
new BaseTranspiler(
new CompilerSupplier(
LanguageMode.ECMASCRIPT_NEXT.toFeatureSet().without(Feature.MODULES),
options.getModuleResolutionMode(),
moduleRoots,
options.getBrowserResolverPrefixReplacements()),
/* runtimeLibraryName= */ ""));
}
@Override
protected void prepForBundleAndAppendTo(Appendable out, CompilerInput input, String content)
throws IOException {
getBundler().withPath(input.getName()).appendTo(out, input, content);
}
@Override
protected void appendRuntimeTo(Appendable out) throws IOException {
getBundler().appendRuntimeTo(out);
}
@Override
protected List createExterns(CompilerOptions options) throws IOException {
List externs = super.createExterns(options);
if (isInTestMode()) {
return externs;
} else {
List builtinExterns = getBuiltinExterns(options.getEnvironment());
builtinExterns.addAll(externs);
return builtinExterns;
}
}
private static ImmutableList loadConformanceConfigs(List configPaths) {
ImmutableList.Builder configs = ImmutableList.builder();
for (String configPath : configPaths) {
try {
configs.add(loadConformanceConfig(configPath));
} catch (IOException e) {
throw new RuntimeException("Error loading conformance config", e);
}
}
return configs.build();
}
private static ConformanceConfig loadConformanceConfig(String configFile) throws IOException {
String textProto = Files.asCharSource(new File(configFile), UTF_8).read();
ConformanceConfig.Builder builder = ConformanceConfig.newBuilder();
// Looking for BOM.
if (!textProto.isEmpty() && textProto.charAt(0) == UTF8_BOM_CODE) {
// Stripping the BOM.
textProto = textProto.substring(1);
}
try {
TextFormat.merge(textProto, builder);
} catch (Exception e) {
throw new RuntimeException(e);
}
return builder.build();
}
@InlineMe(
replacement = "CommandLineRunner.getBuiltinExterns(CompilerOptions.Environment.BROWSER)",
imports = {
"com.google.javascript.jscomp.CommandLineRunner",
"com.google.javascript.jscomp.CompilerOptions"
})
@Deprecated
public static List getDefaultExterns() throws IOException {
return getBuiltinExterns(CompilerOptions.Environment.BROWSER);
}
/**
* Returns all the JavaScript files from the set of patterns. The patterns support globs, such as
* '*.js' for all JS files in a directory and '**.js' for all JS files within the directory and
* sub-directories.
*/
public static List findJsFiles(Collection patterns) throws IOException {
return findJsFiles(patterns, false);
}
/**
* Returns all the JavaScript files from the set of patterns.
*
* @param patterns A collection of filename patterns.
* @param sortAlphabetically Whether the output filenames should be in alphabetical order.
* @return The list of JS filenames found by expanding the patterns.
*/
private static List findJsFiles(Collection patterns, boolean sortAlphabetically)
throws IOException {
// A map from normalized absolute paths to original paths. We need to return original paths to
// support allowlist files that depend on them.
Map allJsInputs = sortAlphabetically ? new TreeMap<>() : new LinkedHashMap<>();
Set excludes = new HashSet<>();
for (String pattern : patterns) {
if (!pattern.contains("*") && !pattern.startsWith("!")) {
File matchedFile = new File(pattern);
if (matchedFile.isDirectory()) {
matchPaths(new File(matchedFile, "**.js").toString(), allJsInputs, excludes);
} else {
Path original = Paths.get(pattern);
String pathStringAbsolute = original.normalize().toAbsolutePath().toString();
if (!excludes.contains(pathStringAbsolute)) {
allJsInputs.put(pathStringAbsolute, original.toString());
}
}
} else {
matchPaths(pattern, allJsInputs, excludes);
}
}
return new ArrayList<>(allJsInputs.values());
}
private static void matchPaths(
String pattern, final Map allJsInputs, final Set excludes)
throws IOException {
FileSystem fs = FileSystems.getDefault();
final boolean remove = pattern.indexOf('!') == 0;
if (remove) {
pattern = pattern.substring(1);
}
String separator = File.separator.equals("\\") ? "\\\\" : File.separator;
// Split the pattern into two pieces: the globbing part
// and the non-globbing prefix.
List patternParts = Splitter.on(File.separator).splitToList(pattern);
String prefix = ".";
for (int i = 0; i < patternParts.size(); i++) {
if (patternParts.get(i).contains("*")) {
if (i > 0) {
prefix = Joiner.on(separator).join(patternParts.subList(0, i));
pattern = Joiner.on(separator).join(patternParts.subList(i, patternParts.size()));
}
break;
}
}
final PathMatcher matcher = fs.getPathMatcher("glob:" + prefix + separator + pattern);
java.nio.file.Files.walkFileTree(
fs.getPath(prefix),
EnumSet.of(FileVisitOption.FOLLOW_LINKS),
Integer.MAX_VALUE,
new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(Path p, BasicFileAttributes attrs) {
if (matcher.matches(p) || matcher.matches(p.normalize())) {
String pathStringAbsolute = p.normalize().toAbsolutePath().toString();
if (remove) {
excludes.add(pathStringAbsolute);
allJsInputs.remove(pathStringAbsolute);
} else if (!excludes.contains(pathStringAbsolute)) {
allJsInputs.put(pathStringAbsolute, p.toString());
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException e) {
return FileVisitResult.SKIP_SUBTREE;
}
});
}
/**
* @return Whether the configuration is valid and specifies to run the compiler.
*/
public boolean shouldRunCompiler() {
return this.runCompiler;
}
/**
* @return Whether the configuration has errors.
*/
public boolean hasErrors() {
return this.errors;
}
private static final Logger phaseLogger = Logger.getLogger(PhaseOptimizer.class.getName());
/** Runs the Compiler. Exits cleanly in the event of an error. */
public static void main(String[] args) {
// disable any logging messages that can interfere with standard error reporting
if (phaseLogger != null) {
phaseLogger.setLevel(Level.OFF);
}
CommandLineRunner runner = new CommandLineRunner(args);
if (runner.shouldRunCompiler()) {
runner.run();
}
if (runner.hasErrors()) {
System.exit(-1);
}
}
}