com.google.javascript.jscomp.deps.DefaultDependencyResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of closure-compiler-unshaded Show documentation
Show all versions of closure-compiler-unshaded Show documentation
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.
The newest version!
/*
* Copyright 2006 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.deps;
import com.google.javascript.jscomp.ErrorManager;
import com.google.javascript.jscomp.LoggerErrorManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import org.jspecify.nullness.Nullable;
/**
* Class for resolving Closure dependencies.
*
* Given a valid deps.js file the dependency tree is parsed and stored in
* memory. The DependencyResolver can then be used to calculate the full list of
* transitive dependencies from: a block of code (
* {@link #getDependencies(String)}), or a list of symbols
* {@link #getDependencies(Collection)}.
*/
public final class DefaultDependencyResolver implements DependencyResolver {
/** Filename for Closure's base.js file which is always added. */
static final String CLOSURE_BASE = "base.js";
/** Provide for Closure's base.js. */
static final String CLOSURE_BASE_PROVIDE = "goog";
/** Source files used to look up the dependencies. */
private final List depsFiles;
/**
* Flag that determines if the resolver will strictly require all
* goog.requires.
* */
private final boolean strictRequires;
/** Logger for DependencyResolver. */
private static final Logger logger = Logger.getLogger(DefaultDependencyResolver.class.getName());
/**
* Creates a new dependency resolver.
* @param depsFiles List of deps file.
* @param strictRequires Determines if the resolver will through an exception
* on a missing dependency.
*/
public DefaultDependencyResolver(
List depsFiles, boolean strictRequires) {
this.depsFiles = depsFiles;
this.strictRequires = strictRequires;
}
/** Gets a list of dependencies for the provided code. */
@Override
public List getDependencies(String code)
throws ServiceException {
return getDependencies(parseRequires(code, true));
}
/** Gets a list of dependencies for the provided list of symbols. */
@Override
public List getDependencies(Collection symbols)
throws ServiceException {
return getDependencies(symbols, new HashSet());
}
/**
* @param code The raw code to be parsed for requires.
* @param seen The set of already seen symbols.
* @param addClosureBaseFile Indicates whether the closure base file should be added to the
* dependency list.
* @return A list of filenames for each of the dependencies for the provided code.
*/
@Override
public List getDependencies(String code, Set seen, boolean addClosureBaseFile)
throws ServiceException {
return getDependencies(parseRequires(code, addClosureBaseFile), seen);
}
/**
* @param symbols A list of required symbols.
* @param seen The set of already seen symbols.
* @return A list of filenames for each of the required symbols.
*/
@Override
public List getDependencies(Collection symbols, Set seen)
throws ServiceException {
List list = new ArrayList<>();
for (DependencyFile depsFile : depsFiles) {
depsFile.ensureUpToDate();
}
for (String symbol : symbols) {
addDependency(symbol, seen, list);
}
return list;
}
/**
* Adds all the transitive dependencies for a symbol to the provided list. The
* set is used to avoid adding dupes while keeping the correct order. NOTE:
* Use of a LinkedHashSet would require reversing the results to get correct
* dependency ordering.
*/
private void addDependency(String symbol, Set seen, List list)
throws ServiceException {
DependencyInfo dependency = getDependencyInfo(symbol);
if (dependency == null) {
if (this.strictRequires) {
throw new ServiceException("Unknown require of " + symbol);
}
} else if (!seen.containsAll(dependency.getProvides())) {
seen.addAll(dependency.getProvides());
for (String require : dependency.getRequiredSymbols()) {
addDependency(require, seen, list);
}
list.add(dependency.getPathRelativeToClosureBase());
}
}
/** Parses a block of code for goog.require statements and extracts the required symbols. */
private static Collection parseRequires(String code, boolean addClosureBase) {
ErrorManager errorManager = new LoggerErrorManager(logger);
JsFileRegexParser parser = new JsFileRegexParser(errorManager);
DependencyInfo deps =
parser.parseFile("", "", code);
List requires = new ArrayList<>();
if (addClosureBase) {
requires.add(CLOSURE_BASE_PROVIDE);
}
requires.addAll(deps.getRequiredSymbols());
errorManager.generateReport();
return requires;
}
/** Looks at each of the dependency files for dependency information. */
private @Nullable DependencyInfo getDependencyInfo(String symbol) {
for (DependencyFile depsFile : depsFiles) {
DependencyInfo di = depsFile.getDependencyInfo(symbol);
if (di != null) {
return di;
}
}
return null;
}
}