Skip to content

Commit d24d175

Browse files
blicklyTyler Breisacher
authored andcommitted
Make ES6 module transpilation a "real" compiler pass.
This moves it from happening at the end of parsing into DefaultPassConfig, where it can be more easily reordered around other passes. Many of the existing passes were relying on module rewriting happening at parse time, so much of the churn is from porting those. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=159139260
1 parent 7df8330 commit d24d175

13 files changed

+119
-95
lines changed

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

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,10 +1823,6 @@ Node parseInputs() {
18231823
if (!inputsToRewrite.isEmpty()) {
18241824
forceToEs6Modules(inputsToRewrite.values());
18251825
}
1826-
1827-
if (options.needsTranspilationFrom(FeatureSet.ES6_MODULES)) {
1828-
processEs6Modules();
1829-
}
18301826
} else {
18311827
// Use an empty module loader if we're not actually dealing with modules.
18321828
this.moduleLoader = ModuleLoader.EMPTY;
@@ -2060,10 +2056,6 @@ Map<String, String> processJsonInputs(List<CompilerInput> inputsToProcess) {
20602056
return rewriteJson.getPackageJsonMainEntries();
20612057
}
20622058

2063-
void processEs6Modules() {
2064-
processEs6Modules(parsePotentialModules(inputs));
2065-
}
2066-
20672059
void forceToEs6Modules(Collection<CompilerInput> inputsToProcess) {
20682060
for (CompilerInput input : inputsToProcess) {
20692061
input.setCompiler(this);
@@ -2098,20 +2090,6 @@ private List<CompilerInput> parsePotentialModules(List<CompilerInput> inputsToPr
20982090
return filteredInputs;
20992091
}
21002092

2101-
void processEs6Modules(List<CompilerInput> inputsToProcess) {
2102-
for (CompilerInput input : inputsToProcess) {
2103-
input.setCompiler(this);
2104-
Node root = input.getAstRoot(this);
2105-
if (root == null) {
2106-
continue;
2107-
}
2108-
if (Es6RewriteModules.isEs6ModuleRoot(root)) {
2109-
new Es6RewriteModules(this).processFile(root);
2110-
}
2111-
}
2112-
setFeatureSet(featureSet.without(Feature.MODULES));
2113-
}
2114-
21152093
/**
21162094
* Transforms AMD and CJS modules to something closure compiler can
21172095
* process and creates JSModules and the corresponding dependency tree

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1915,6 +1915,11 @@ public boolean needsTranspilationFrom(FeatureSet languageLevel) {
19151915
&& !getLanguageOut().toFeatureSet().contains(languageLevel);
19161916
}
19171917

1918+
public boolean needsTranspilationOf(FeatureSet.Feature feature) {
1919+
return getLanguageIn().toFeatureSet().has(feature)
1920+
&& !getLanguageOut().toFeatureSet().has(feature);
1921+
}
1922+
19181923
/**
19191924
* Set which set of builtin externs to use.
19201925
*/

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ protected List<PassFactory> getTranspileOnlyPasses() {
191191
passes.add(convertEs6TypedToEs6);
192192
}
193193

194+
if (options.needsTranspilationOf(FeatureSet.Feature.MODULES)) {
195+
TranspilationPasses.addEs6ModulePass(passes);
196+
}
197+
194198
passes.add(checkMissingSuper);
195199
passes.add(checkVariableReferencesForTranspileOnly);
196200

@@ -261,6 +265,7 @@ protected List<PassFactory> getChecks() {
261265
List<PassFactory> checks = new ArrayList<>();
262266

263267
if (options.shouldGenerateTypedExterns()) {
268+
TranspilationPasses.addEs6ModulePass(checks);
264269
checks.add(closureGoogScopeAliases);
265270
checks.add(closureRewriteClass);
266271
checks.add(generateIjs);
@@ -273,6 +278,14 @@ protected List<PassFactory> getChecks() {
273278
// Verify JsDoc annotations
274279
checks.add(checkJsDoc);
275280

281+
if (options.needsTranspilationFrom(TYPESCRIPT)) {
282+
checks.add(convertEs6TypedToEs6);
283+
}
284+
285+
if (options.needsTranspilationOf(FeatureSet.Feature.MODULES)) {
286+
TranspilationPasses.addEs6ModulePass(checks);
287+
}
288+
276289
if (options.enables(DiagnosticGroups.LINT_CHECKS)) {
277290
checks.add(lintChecks);
278291
}
@@ -294,10 +307,6 @@ protected List<PassFactory> getChecks() {
294307
checks.add(closureRewriteModule);
295308
}
296309

297-
if (options.needsTranspilationFrom(TYPESCRIPT)) {
298-
checks.add(convertEs6TypedToEs6);
299-
}
300-
301310
if (options.declaredGlobalExternsOnWindow) {
302311
checks.add(declaredGlobalExternsOnWindow);
303312
}

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

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package com.google.javascript.jscomp;
1717

18+
import static com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature.MODULES;
19+
1820
import com.google.common.base.Preconditions;
1921
import com.google.common.base.Splitter;
2022
import com.google.common.collect.ImmutableSet;
@@ -34,13 +36,13 @@
3436
import java.util.Set;
3537

3638
/**
37-
* Rewrites a ES6 module into a form that can be safely concatenated.
38-
* Note that we treat a file as an ES6 module if it has at least one import or
39-
* export statement.
39+
* Rewrites a ES6 module into a form that can be safely concatenated. Note that we treat a file as
40+
* an ES6 module if it has at least one import or export statement.
4041
*
4142
* @author moz@google.com (Michael Zhou)
4243
*/
43-
public final class Es6RewriteModules extends AbstractPostOrderCallback {
44+
public final class Es6RewriteModules extends AbstractPostOrderCallback
45+
implements HotSwapCompilerPass {
4446
private static final String DEFAULT_EXPORT_NAME = "$jscompDefaultExport";
4547

4648
static final DiagnosticType LHS_OF_GOOG_REQUIRE_MUST_BE_CONST =
@@ -54,13 +56,13 @@ public final class Es6RewriteModules extends AbstractPostOrderCallback {
5456
"Namespace imports ('goog:some.Namespace') cannot use import * as. "
5557
+ "Did you mean to import {0} from ''{1}'';?");
5658

57-
private final Compiler compiler;
58-
private int scriptNodeCount = 0;
59+
private final AbstractCompiler compiler;
60+
private int scriptNodeCount;
5961

6062
/**
6163
* Maps exported names to their names in current module.
6264
*/
63-
private Map<String, NameNodePair> exportMap = new LinkedHashMap<>();
65+
private Map<String, NameNodePair> exportMap;
6466

6567
/**
6668
* Maps symbol names to a pair of (moduleName, originalName). The original
@@ -69,12 +71,12 @@ public final class Es6RewriteModules extends AbstractPostOrderCallback {
6971
* object. Eg: "import {foo as f} from 'm'" maps 'f' to the pair ('m', 'foo').
7072
* In the entry for "import * as ns", the originalName will be the empty string.
7173
*/
72-
private Map<String, ModuleOriginalNamePair> importMap = new HashMap<>();
74+
private Map<String, ModuleOriginalNamePair> importMap;
7375

74-
private Set<String> classes = new HashSet<>();
75-
private Set<String> typedefs = new HashSet<>();
76+
private Set<String> classes;
77+
private Set<String> typedefs;
7678

77-
private Set<String> alreadyRequired = new HashSet<>();
79+
private Set<String> alreadyRequired;
7880

7981
private boolean forceRewrite;
8082

@@ -84,7 +86,7 @@ public final class Es6RewriteModules extends AbstractPostOrderCallback {
8486
* Creates a new Es6RewriteModules instance which can be used to rewrite
8587
* ES6 modules to a concatenable form.
8688
*/
87-
public Es6RewriteModules(Compiler compiler) {
89+
public Es6RewriteModules(AbstractCompiler compiler) {
8890
this.compiler = compiler;
8991
}
9092

@@ -104,6 +106,8 @@ public static boolean isEs6ModuleRoot(Node scriptNode) {
104106
* "import" or "export" statements. Fails if the file contains a goog.provide or goog.module.
105107
*
106108
* @return True, if the file is now an ES6 module. False, if the file must remain a script.
109+
* TODO(blickly): Move this logic out of this pass, since it is independent of whether or
110+
* not we are actually transpiling modules
107111
*/
108112
public boolean forceToEs6Module(Node root) {
109113
if (isEs6ModuleRoot(root)) {
@@ -120,15 +124,42 @@ public boolean forceToEs6Module(Node root) {
120124
return true;
121125
}
122126

127+
@Override
128+
public void process(Node externs, Node root) {
129+
Preconditions.checkState(compiler.getOptions().getLanguageIn().toFeatureSet().has(MODULES));
130+
for (Node file = root.getFirstChild(); file != null; file = file.getNext()) {
131+
hotSwapScript(file, null);
132+
}
133+
compiler.setFeatureSet(compiler.getFeatureSet().without(MODULES));
134+
}
135+
136+
@Override
137+
public void hotSwapScript(Node scriptNode, Node originalRoot) {
138+
if (isEs6ModuleRoot(scriptNode)) {
139+
processFile(scriptNode);
140+
}
141+
}
142+
123143
/**
124144
* Rewrite a single ES6 module file to a global script version.
125145
*/
126-
public void processFile(Node root) {
146+
private void processFile(Node root) {
127147
Preconditions.checkArgument(isEs6ModuleRoot(root), root);
128-
this.forceRewrite = true;
148+
clearState();
129149
NodeTraversal.traverseEs6(compiler, root, this);
130150
}
131151

152+
public void clearState() {
153+
this.scriptNodeCount = 0;
154+
this.exportMap = new LinkedHashMap<>();
155+
this.importMap = new HashMap<>();
156+
this.classes = new HashSet<>();
157+
this.typedefs = new HashSet<>();
158+
this.alreadyRequired = new HashSet<>();
159+
this.forceRewrite = true;
160+
this.googRequireInsertSpot = null;
161+
}
162+
132163
/**
133164
* Avoid processing if we find the appearance of goog.provide or goog.module.
134165
*
@@ -284,8 +315,7 @@ private void visitExport(NodeTraversal t, Node export, Node parent) {
284315
}
285316

286317
if (name != null) {
287-
Node decl = child.cloneTree();
288-
decl.setJSDocInfo(child.getJSDocInfo());
318+
Node decl = child.detach();
289319
parent.replaceChild(export, decl);
290320
exportMap.put("default", new NameNodePair(name, child));
291321
} else {
@@ -556,6 +586,7 @@ public void visit(NodeTraversal t, Node n, Node parent) {
556586
n.setString(newName);
557587
n.setOriginalName(name);
558588
}
589+
t.reportCodeChange(n);
559590
} else if (var == null && importMap.containsKey(name)) {
560591
// Change to property access on the imported module object.
561592
if (parent.isCall() && parent.getFirstChild() == n) {
@@ -571,6 +602,7 @@ public void visit(NodeTraversal t, Node n, Node parent) {
571602
IR.getprop(moduleAccess, IR.string(pair.originalName))
572603
.useSourceInfoIfMissingFromForTree(n));
573604
}
605+
t.reportCodeChange(moduleAccess);
574606
}
575607
}
576608
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
public class TranspilationPasses {
3131
private TranspilationPasses() {}
3232

33+
public static void addEs6ModulePass(List<PassFactory> passes) {
34+
passes.add(es6RewriteModule);
35+
}
36+
3337
public static void addEs2017Passes(List<PassFactory> passes) {
3438
passes.add(rewriteAsyncFunctions);
3539
}
@@ -73,6 +77,20 @@ public static void addRewritePolyfillPass(List<PassFactory> passes) {
7377
passes.add(rewritePolyfills);
7478
}
7579

80+
/** Rewrites ES6 modules */
81+
private static final HotSwapPassFactory es6RewriteModule =
82+
new HotSwapPassFactory("es6RewriteModule", true) {
83+
@Override
84+
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
85+
return new Es6RewriteModules(compiler);
86+
}
87+
88+
@Override
89+
protected FeatureSet featureSet() {
90+
return FeatureSet.ES8_MODULES;
91+
}
92+
};
93+
7694
private static final PassFactory rewriteAsyncFunctions =
7795
new PassFactory("rewriteAsyncFunctions", true) {
7896
@Override

test/com/google/javascript/jscomp/CompilerTestCase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,7 @@ private Compiler multistageSerializeAndDeserialize(
17121712

17131713
private static void transpileToEs5(AbstractCompiler compiler, Node externsRoot, Node codeRoot) {
17141714
List<PassFactory> factories = new ArrayList<>();
1715+
TranspilationPasses.addEs6ModulePass(factories);
17151716
TranspilationPasses.addEs2017Passes(factories);
17161717
TranspilationPasses.addEs6EarlyPasses(factories);
17171718
TranspilationPasses.addEs6LatePasses(factories);

test/com/google/javascript/jscomp/Es6RewriteClassTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import static com.google.javascript.jscomp.Es6RewriteClass.DYNAMIC_EXTENDS_TYPE;
2323
import static com.google.javascript.jscomp.Es6ToEs3Converter.CANNOT_CONVERT;
2424
import static com.google.javascript.jscomp.Es6ToEs3Converter.CANNOT_CONVERT_YET;
25-
import static com.google.javascript.jscomp.parsing.parser.FeatureSet.ES6;
25+
import static com.google.javascript.jscomp.parsing.parser.FeatureSet.ES6_MODULES;
2626

2727
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
2828
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
@@ -84,7 +84,7 @@ protected CompilerPass create(AbstractCompiler compiler) {
8484

8585
@Override
8686
protected FeatureSet featureSet() {
87-
return ES6;
87+
return ES6_MODULES;
8888
}
8989
};
9090
}

0 commit comments

Comments
 (0)