Skip to content

Commit a5a5831

Browse files
ChadKillingsworthTyler Breisacher
authored andcommitted
When --dependency_mode is set to STRICT or LOOSE, order dependencies in a deterministic depth-first order from entry points.
Avoids a full parse on every input when CommonJS module processing is enabled. ES6 and CommonJS modules no longer generate synthetic goog.require and goog.provide calls. ES6 Module Transpilation no longer depends on Closure-Library primitives. Closes #2641 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172126284
1 parent 240f04b commit a5a5831

12 files changed

+858
-453
lines changed

src/com/google/javascript/jscomp/Compiler.java

Lines changed: 154 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@
6464
import java.io.Serializable;
6565
import java.util.AbstractSet;
6666
import java.util.ArrayList;
67-
import java.util.Collection;
6867
import java.util.Collections;
6968
import java.util.HashMap;
7069
import java.util.HashSet;
@@ -1750,7 +1749,19 @@ Node parseInputs() {
17501749
options.moduleResolutionMode,
17511750
processJsonInputs(inputs));
17521751
}
1752+
} else {
1753+
// Use an empty module loader if we're not actually dealing with modules.
1754+
this.moduleLoader = ModuleLoader.EMPTY;
1755+
}
17531756

1757+
if (options.getDependencyOptions().needsManagement()) {
1758+
findDependenciesFromEntryPoints(
1759+
options.getLanguageIn().toFeatureSet().has(Feature.MODULES),
1760+
options.processCommonJSModules,
1761+
options.transformAMDToCJSModules);
1762+
} else if (options.needsTranspilationOf(Feature.MODULES)
1763+
|| options.transformAMDToCJSModules
1764+
|| options.processCommonJSModules) {
17541765
if (options.getLanguageIn().toFeatureSet().has(Feature.MODULES)) {
17551766
parsePotentialModules(inputs);
17561767
}
@@ -1783,12 +1794,12 @@ Node parseInputs() {
17831794
}
17841795
}
17851796

1786-
if (!inputsToRewrite.isEmpty()) {
1787-
forceToEs6Modules(inputsToRewrite.values());
1797+
for (CompilerInput input : inputsToRewrite.values()) {
1798+
forceInputToPathBasedModule(
1799+
input,
1800+
options.getLanguageIn().toFeatureSet().has(Feature.MODULES),
1801+
options.processCommonJSModules);
17881802
}
1789-
} else {
1790-
// Use an empty module loader if we're not actually dealing with modules.
1791-
this.moduleLoader = ModuleLoader.EMPTY;
17921803
}
17931804

17941805
orderInputs();
@@ -1896,6 +1907,142 @@ void orderInputs() {
18961907
}
18971908
}
18981909

1910+
/**
1911+
* Find dependencies by recursively traversing each dependency of an input starting with the entry
1912+
* points. Causes a full parse of each file, but since the file is reachable by walking the graph,
1913+
* this would be required in later compilation passes regardless.
1914+
*
1915+
* <p>Inputs which are not reachable during graph traversal will be dropped.
1916+
*
1917+
* <p>If the dependency mode is set to LOOSE, inputs for which the deps package did not find a
1918+
* provide statement or detect as a module will be treated as entry points.
1919+
*/
1920+
void findDependenciesFromEntryPoints(
1921+
boolean supportEs6Modules, boolean supportCommonJSModules, boolean supportAmdModules) {
1922+
hoistExterns();
1923+
List<CompilerInput> entryPoints = new ArrayList<>();
1924+
Map<String, CompilerInput> inputsByProvide = new HashMap<>();
1925+
Map<String, CompilerInput> inputsByIdentifier = new HashMap<>();
1926+
for (CompilerInput input : inputs) {
1927+
if (!options.getDependencyOptions().shouldDropMoochers() && input.getProvides().isEmpty()) {
1928+
entryPoints.add(input);
1929+
}
1930+
inputsByIdentifier.put(
1931+
ModuleIdentifier.forFile(input.getPath().toString()).toString(), input);
1932+
for (String provide : input.getProvides()) {
1933+
if (!provide.startsWith("module$")) {
1934+
inputsByProvide.put(provide, input);
1935+
}
1936+
}
1937+
}
1938+
for (ModuleIdentifier moduleIdentifier : options.getDependencyOptions().getEntryPoints()) {
1939+
CompilerInput input = inputsByIdentifier.get(moduleIdentifier.toString());
1940+
if (input != null) {
1941+
entryPoints.add(input);
1942+
}
1943+
}
1944+
1945+
Set<CompilerInput> workingInputSet = new HashSet<>(inputs);
1946+
List<CompilerInput> orderedInputs = new ArrayList<>();
1947+
for (CompilerInput entryPoint : entryPoints) {
1948+
orderedInputs.addAll(
1949+
depthFirstDependenciesFromInput(
1950+
entryPoint,
1951+
/* wasImportedByModule = */ false,
1952+
workingInputSet,
1953+
inputsByIdentifier,
1954+
inputsByProvide,
1955+
supportEs6Modules,
1956+
supportCommonJSModules,
1957+
supportAmdModules));
1958+
}
1959+
1960+
// TODO(ChadKillingsworth) Move this into the standard compilation passes
1961+
if (supportCommonJSModules) {
1962+
for (CompilerInput input : orderedInputs) {
1963+
new ProcessCommonJSModules(this)
1964+
.process(/* externs */ null, input.getAstRoot(this), /* forceModuleDetection */ false);
1965+
}
1966+
}
1967+
}
1968+
1969+
/** For a given input, order it's dependencies in a depth first traversal */
1970+
private List<CompilerInput> depthFirstDependenciesFromInput(
1971+
CompilerInput input,
1972+
boolean wasImportedByModule,
1973+
Set<CompilerInput> inputs,
1974+
Map<String, CompilerInput> inputsByIdentifier,
1975+
Map<String, CompilerInput> inputsByProvide,
1976+
boolean supportEs6Modules,
1977+
boolean supportCommonJSModules,
1978+
boolean supportAmdModules) {
1979+
List<CompilerInput> orderedInputs = new ArrayList<>();
1980+
if (!inputs.remove(input)) {
1981+
// It's possible for a module to be included as both a script
1982+
// and a module in the same compilation. In these cases, it should
1983+
// be forced to be a module.
1984+
if (wasImportedByModule && input.getJsModuleType() == CompilerInput.ModuleType.NONE) {
1985+
forceInputToPathBasedModule(input, supportEs6Modules, supportCommonJSModules);
1986+
}
1987+
1988+
return orderedInputs;
1989+
}
1990+
1991+
if (supportAmdModules) {
1992+
new TransformAMDToCJSModule(this).process(null, input.getAstRoot(this));
1993+
}
1994+
1995+
FindModuleDependencies findDeps =
1996+
new FindModuleDependencies(this, supportEs6Modules, supportCommonJSModules);
1997+
findDeps.process(input.getAstRoot(this));
1998+
1999+
// If this input was imported by another module, it is itself a module
2000+
// so we force it to be detected as such.
2001+
if (wasImportedByModule && input.getJsModuleType() == CompilerInput.ModuleType.NONE) {
2002+
forceInputToPathBasedModule(input, supportEs6Modules, supportCommonJSModules);
2003+
}
2004+
2005+
for (String requiredNamespace : input.getRequires()) {
2006+
CompilerInput requiredInput = null;
2007+
boolean requiredByModuleImport = false;
2008+
if (inputsByProvide.containsKey(requiredNamespace)) {
2009+
requiredInput = inputsByProvide.get(requiredNamespace);
2010+
} else if (inputsByIdentifier.containsKey(requiredNamespace)) {
2011+
requiredByModuleImport = true;
2012+
requiredInput = inputsByIdentifier.get(requiredNamespace);
2013+
}
2014+
2015+
if (requiredInput != null) {
2016+
orderedInputs.addAll(
2017+
depthFirstDependenciesFromInput(
2018+
requiredInput,
2019+
requiredByModuleImport,
2020+
inputs,
2021+
inputsByIdentifier,
2022+
inputsByProvide,
2023+
supportEs6Modules,
2024+
supportCommonJSModules,
2025+
supportAmdModules));
2026+
}
2027+
}
2028+
orderedInputs.add(input);
2029+
return orderedInputs;
2030+
}
2031+
2032+
private void forceInputToPathBasedModule(
2033+
CompilerInput input, boolean supportEs6Modules, boolean supportCommonJSModules) {
2034+
2035+
if (supportEs6Modules) {
2036+
FindModuleDependencies findDeps =
2037+
new FindModuleDependencies(this, supportEs6Modules, supportCommonJSModules);
2038+
findDeps.convertToEs6Module(input.getAstRoot(this));
2039+
input.setJsModuleType(CompilerInput.ModuleType.ES6);
2040+
} else if (supportCommonJSModules) {
2041+
new ProcessCommonJSModules(this).process(null, input.getAstRoot(this), true);
2042+
input.setJsModuleType(CompilerInput.ModuleType.COMMONJS);
2043+
}
2044+
}
2045+
18992046
/**
19002047
* Hoists inputs with the @externs annotation into the externs list.
19012048
*/
@@ -2005,18 +2152,6 @@ Map<String, String> processJsonInputs(List<CompilerInput> inputsToProcess) {
20052152
return rewriteJson.getPackageJsonMainEntries();
20062153
}
20072154

2008-
void forceToEs6Modules(Collection<CompilerInput> inputsToProcess) {
2009-
for (CompilerInput input : inputsToProcess) {
2010-
input.setCompiler(this);
2011-
input.addProvide(input.getPath().toModuleName());
2012-
Node root = input.getAstRoot(this);
2013-
if (root == null) {
2014-
continue;
2015-
}
2016-
Es6RewriteModules moduleRewriter = new Es6RewriteModules(this);
2017-
moduleRewriter.forceToEs6Module(root);
2018-
}
2019-
}
20202155

20212156
private List<CompilerInput> parsePotentialModules(List<CompilerInput> inputsToProcess) {
20222157
List<CompilerInput> filteredInputs = new ArrayList<>();
@@ -2055,7 +2190,7 @@ void processAMDAndCommonJSModules() {
20552190
new TransformAMDToCJSModule(this).process(null, root);
20562191
}
20572192
if (options.processCommonJSModules) {
2058-
ProcessCommonJSModules cjs = new ProcessCommonJSModules(this, true);
2193+
ProcessCommonJSModules cjs = new ProcessCommonJSModules(this);
20592194
cjs.process(null, root);
20602195
}
20612196
}

src/com/google/javascript/jscomp/CompilerInput.java

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ public class CompilerInput implements SourceAst, DependencyInfo {
6161
private DependencyInfo dependencyInfo;
6262
private final List<String> extraRequires = new ArrayList<>();
6363
private final List<String> extraProvides = new ArrayList<>();
64+
private final List<String> orderedRequires = new ArrayList<>();
65+
private boolean hasFullParseDependencyInfo = false;
66+
private ModuleType jsModuleType = ModuleType.NONE;
6467

6568
// An AbstractCompiler for doing parsing.
6669
// We do not want to persist this across serialized state.
@@ -160,6 +163,10 @@ public void setCompiler(AbstractCompiler compiler) {
160163
/** Gets a list of types depended on by this input. */
161164
@Override
162165
public Collection<String> getRequires() {
166+
if (hasFullParseDependencyInfo) {
167+
return orderedRequires;
168+
}
169+
163170
return getDependencyInfo().getRequires();
164171
}
165172

@@ -191,19 +198,36 @@ Collection<String> getKnownProvides() {
191198
extraProvides);
192199
}
193200

194-
// TODO(nicksantos): Remove addProvide/addRequire/removeRequire once
195-
// there is better support for discovering non-closure dependencies.
196-
197201
/**
198-
* Registers a type that this input defines.
202+
* Registers a type that this input defines. Includes both explicitly declared namespaces via
203+
* goog.provide and goog.module calls as well as implicit namespaces provided by module rewriting.
199204
*/
200205
public void addProvide(String provide) {
201206
extraProvides.add(provide);
202207
}
203208

204-
/**
205-
* Registers a type that this input depends on.
206-
*/
209+
/** Registers a type that this input depends on in the order seen in the file. */
210+
public boolean addOrderedRequire(String require) {
211+
if (!orderedRequires.contains(require)) {
212+
orderedRequires.add(require);
213+
return true;
214+
}
215+
return false;
216+
}
217+
218+
public void setHasFullParseDependencyInfo(boolean hasFullParseDependencyInfo) {
219+
this.hasFullParseDependencyInfo = hasFullParseDependencyInfo;
220+
}
221+
222+
public ModuleType getJsModuleType() {
223+
return jsModuleType;
224+
}
225+
226+
public void setJsModuleType(ModuleType moduleType) {
227+
jsModuleType = moduleType;
228+
}
229+
230+
/** Registers a type that this input depends on. */
207231
public void addRequire(String require) {
208232
extraRequires.add(require);
209233
}
@@ -493,4 +517,11 @@ public void reset() {
493517
this.module = null;
494518
this.ast.clearAst();
495519
}
520+
521+
public enum ModuleType {
522+
NONE,
523+
GOOG_MODULE,
524+
ES6,
525+
COMMONJS
526+
}
496527
}

src/com/google/javascript/jscomp/DependencyOptions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import java.io.Serializable;
2222
import java.util.Collection;
23-
import java.util.HashSet;
23+
import java.util.LinkedHashSet;
2424
import java.util.Set;
2525

2626
/**
@@ -43,7 +43,7 @@ public final class DependencyOptions implements Serializable {
4343
private boolean sortDependencies = false;
4444
private boolean pruneDependencies = false;
4545
private boolean dropMoochers = false;
46-
private final Set<ModuleIdentifier> entryPoints = new HashSet<>();
46+
private final Set<ModuleIdentifier> entryPoints = new LinkedHashSet<>();
4747

4848
/**
4949
* Enables or disables dependency sorting mode.

0 commit comments

Comments
 (0)