-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
DefaultDependencyResolver.java
167 lines (149 loc) · 5.69 KB
/
DefaultDependencyResolver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
* 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;
/**
* 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<DependencyFile> 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<DependencyFile> depsFiles, boolean strictRequires) {
this.depsFiles = depsFiles;
this.strictRequires = strictRequires;
}
/** Gets a list of dependencies for the provided code. */
@Override
public List<String> getDependencies(String code)
throws ServiceException {
return getDependencies(parseRequires(code, true));
}
/** Gets a list of dependencies for the provided list of symbols. */
@Override
public List<String> getDependencies(Collection<String> symbols)
throws ServiceException {
return getDependencies(symbols, new HashSet<String>());
}
/**
* @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.
* @throws ServiceException
*/
@Override
public List<String> getDependencies(String code, Set<String> 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.
* @throws ServiceException
*/
@Override
public List<String> getDependencies(Collection<String> symbols,
Set<String> seen) throws ServiceException {
List<String> 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<String> seen, List<String> 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<String> parseRequires(String code, boolean addClosureBase) {
ErrorManager errorManager = new LoggerErrorManager(logger);
JsFileRegexParser parser = new JsFileRegexParser(errorManager);
DependencyInfo deps =
parser.parseFile("<unknown path>", "<unknown path>", code);
List<String> 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 DependencyInfo getDependencyInfo(String symbol) {
for (DependencyFile depsFile : depsFiles) {
DependencyInfo di = depsFile.getDependencyInfo(symbol);
if (di != null) {
return di;
}
}
return null;
}
}