Skip to content

Commit

Permalink
Make ES6 module transpilation a "real" compiler pass.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
blickly authored and Tyler Breisacher committed Jun 16, 2017
1 parent 7df8330 commit d24d175
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 95 deletions.
22 changes: 0 additions & 22 deletions src/com/google/javascript/jscomp/Compiler.java
Expand Up @@ -1823,10 +1823,6 @@ Node parseInputs() {
if (!inputsToRewrite.isEmpty()) { if (!inputsToRewrite.isEmpty()) {
forceToEs6Modules(inputsToRewrite.values()); forceToEs6Modules(inputsToRewrite.values());
} }

if (options.needsTranspilationFrom(FeatureSet.ES6_MODULES)) {
processEs6Modules();
}
} else { } else {
// Use an empty module loader if we're not actually dealing with modules. // Use an empty module loader if we're not actually dealing with modules.
this.moduleLoader = ModuleLoader.EMPTY; this.moduleLoader = ModuleLoader.EMPTY;
Expand Down Expand Up @@ -2060,10 +2056,6 @@ Map<String, String> processJsonInputs(List<CompilerInput> inputsToProcess) {
return rewriteJson.getPackageJsonMainEntries(); return rewriteJson.getPackageJsonMainEntries();
} }


void processEs6Modules() {
processEs6Modules(parsePotentialModules(inputs));
}

void forceToEs6Modules(Collection<CompilerInput> inputsToProcess) { void forceToEs6Modules(Collection<CompilerInput> inputsToProcess) {
for (CompilerInput input : inputsToProcess) { for (CompilerInput input : inputsToProcess) {
input.setCompiler(this); input.setCompiler(this);
Expand Down Expand Up @@ -2098,20 +2090,6 @@ private List<CompilerInput> parsePotentialModules(List<CompilerInput> inputsToPr
return filteredInputs; return filteredInputs;
} }


void processEs6Modules(List<CompilerInput> inputsToProcess) {
for (CompilerInput input : inputsToProcess) {
input.setCompiler(this);
Node root = input.getAstRoot(this);
if (root == null) {
continue;
}
if (Es6RewriteModules.isEs6ModuleRoot(root)) {
new Es6RewriteModules(this).processFile(root);
}
}
setFeatureSet(featureSet.without(Feature.MODULES));
}

/** /**
* Transforms AMD and CJS modules to something closure compiler can * Transforms AMD and CJS modules to something closure compiler can
* process and creates JSModules and the corresponding dependency tree * process and creates JSModules and the corresponding dependency tree
Expand Down
5 changes: 5 additions & 0 deletions src/com/google/javascript/jscomp/CompilerOptions.java
Expand Up @@ -1915,6 +1915,11 @@ public boolean needsTranspilationFrom(FeatureSet languageLevel) {
&& !getLanguageOut().toFeatureSet().contains(languageLevel); && !getLanguageOut().toFeatureSet().contains(languageLevel);
} }


public boolean needsTranspilationOf(FeatureSet.Feature feature) {
return getLanguageIn().toFeatureSet().has(feature)
&& !getLanguageOut().toFeatureSet().has(feature);
}

/** /**
* Set which set of builtin externs to use. * Set which set of builtin externs to use.
*/ */
Expand Down
17 changes: 13 additions & 4 deletions src/com/google/javascript/jscomp/DefaultPassConfig.java
Expand Up @@ -191,6 +191,10 @@ protected List<PassFactory> getTranspileOnlyPasses() {
passes.add(convertEs6TypedToEs6); passes.add(convertEs6TypedToEs6);
} }


if (options.needsTranspilationOf(FeatureSet.Feature.MODULES)) {
TranspilationPasses.addEs6ModulePass(passes);
}

passes.add(checkMissingSuper); passes.add(checkMissingSuper);
passes.add(checkVariableReferencesForTranspileOnly); passes.add(checkVariableReferencesForTranspileOnly);


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


if (options.shouldGenerateTypedExterns()) { if (options.shouldGenerateTypedExterns()) {
TranspilationPasses.addEs6ModulePass(checks);
checks.add(closureGoogScopeAliases); checks.add(closureGoogScopeAliases);
checks.add(closureRewriteClass); checks.add(closureRewriteClass);
checks.add(generateIjs); checks.add(generateIjs);
Expand All @@ -273,6 +278,14 @@ protected List<PassFactory> getChecks() {
// Verify JsDoc annotations // Verify JsDoc annotations
checks.add(checkJsDoc); checks.add(checkJsDoc);


if (options.needsTranspilationFrom(TYPESCRIPT)) {
checks.add(convertEs6TypedToEs6);
}

if (options.needsTranspilationOf(FeatureSet.Feature.MODULES)) {
TranspilationPasses.addEs6ModulePass(checks);
}

if (options.enables(DiagnosticGroups.LINT_CHECKS)) { if (options.enables(DiagnosticGroups.LINT_CHECKS)) {
checks.add(lintChecks); checks.add(lintChecks);
} }
Expand All @@ -294,10 +307,6 @@ protected List<PassFactory> getChecks() {
checks.add(closureRewriteModule); checks.add(closureRewriteModule);
} }


if (options.needsTranspilationFrom(TYPESCRIPT)) {
checks.add(convertEs6TypedToEs6);
}

if (options.declaredGlobalExternsOnWindow) { if (options.declaredGlobalExternsOnWindow) {
checks.add(declaredGlobalExternsOnWindow); checks.add(declaredGlobalExternsOnWindow);
} }
Expand Down
64 changes: 48 additions & 16 deletions src/com/google/javascript/jscomp/Es6RewriteModules.java
Expand Up @@ -15,6 +15,8 @@
*/ */
package com.google.javascript.jscomp; package com.google.javascript.jscomp;


import static com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature.MODULES;

import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
Expand All @@ -34,13 +36,13 @@
import java.util.Set; import java.util.Set;


/** /**
* Rewrites a ES6 module into a form that can be safely concatenated. * Rewrites a ES6 module into a form that can be safely concatenated. Note that we treat a file as
* Note that we treat a file as an ES6 module if it has at least one import or * an ES6 module if it has at least one import or export statement.
* export statement.
* *
* @author moz@google.com (Michael Zhou) * @author moz@google.com (Michael Zhou)
*/ */
public final class Es6RewriteModules extends AbstractPostOrderCallback { public final class Es6RewriteModules extends AbstractPostOrderCallback
implements HotSwapCompilerPass {
private static final String DEFAULT_EXPORT_NAME = "$jscompDefaultExport"; private static final String DEFAULT_EXPORT_NAME = "$jscompDefaultExport";


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


private final Compiler compiler; private final AbstractCompiler compiler;
private int scriptNodeCount = 0; private int scriptNodeCount;


/** /**
* Maps exported names to their names in current module. * Maps exported names to their names in current module.
*/ */
private Map<String, NameNodePair> exportMap = new LinkedHashMap<>(); private Map<String, NameNodePair> exportMap;


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


private Set<String> classes = new HashSet<>(); private Set<String> classes;
private Set<String> typedefs = new HashSet<>(); private Set<String> typedefs;


private Set<String> alreadyRequired = new HashSet<>(); private Set<String> alreadyRequired;


private boolean forceRewrite; private boolean forceRewrite;


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


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


@Override
public void process(Node externs, Node root) {
Preconditions.checkState(compiler.getOptions().getLanguageIn().toFeatureSet().has(MODULES));
for (Node file = root.getFirstChild(); file != null; file = file.getNext()) {
hotSwapScript(file, null);
}
compiler.setFeatureSet(compiler.getFeatureSet().without(MODULES));
}

@Override
public void hotSwapScript(Node scriptNode, Node originalRoot) {
if (isEs6ModuleRoot(scriptNode)) {
processFile(scriptNode);
}
}

/** /**
* Rewrite a single ES6 module file to a global script version. * Rewrite a single ES6 module file to a global script version.
*/ */
public void processFile(Node root) { private void processFile(Node root) {
Preconditions.checkArgument(isEs6ModuleRoot(root), root); Preconditions.checkArgument(isEs6ModuleRoot(root), root);
this.forceRewrite = true; clearState();
NodeTraversal.traverseEs6(compiler, root, this); NodeTraversal.traverseEs6(compiler, root, this);
} }


public void clearState() {
this.scriptNodeCount = 0;
this.exportMap = new LinkedHashMap<>();
this.importMap = new HashMap<>();
this.classes = new HashSet<>();
this.typedefs = new HashSet<>();
this.alreadyRequired = new HashSet<>();
this.forceRewrite = true;
this.googRequireInsertSpot = null;
}

/** /**
* Avoid processing if we find the appearance of goog.provide or goog.module. * Avoid processing if we find the appearance of goog.provide or goog.module.
* *
Expand Down Expand Up @@ -284,8 +315,7 @@ private void visitExport(NodeTraversal t, Node export, Node parent) {
} }


if (name != null) { if (name != null) {
Node decl = child.cloneTree(); Node decl = child.detach();
decl.setJSDocInfo(child.getJSDocInfo());
parent.replaceChild(export, decl); parent.replaceChild(export, decl);
exportMap.put("default", new NameNodePair(name, child)); exportMap.put("default", new NameNodePair(name, child));
} else { } else {
Expand Down Expand Up @@ -556,6 +586,7 @@ public void visit(NodeTraversal t, Node n, Node parent) {
n.setString(newName); n.setString(newName);
n.setOriginalName(name); n.setOriginalName(name);
} }
t.reportCodeChange(n);
} else if (var == null && importMap.containsKey(name)) { } else if (var == null && importMap.containsKey(name)) {
// Change to property access on the imported module object. // Change to property access on the imported module object.
if (parent.isCall() && parent.getFirstChild() == n) { if (parent.isCall() && parent.getFirstChild() == n) {
Expand All @@ -571,6 +602,7 @@ public void visit(NodeTraversal t, Node n, Node parent) {
IR.getprop(moduleAccess, IR.string(pair.originalName)) IR.getprop(moduleAccess, IR.string(pair.originalName))
.useSourceInfoIfMissingFromForTree(n)); .useSourceInfoIfMissingFromForTree(n));
} }
t.reportCodeChange(moduleAccess);
} }
} }
} }
Expand Down
18 changes: 18 additions & 0 deletions src/com/google/javascript/jscomp/TranspilationPasses.java
Expand Up @@ -30,6 +30,10 @@
public class TranspilationPasses { public class TranspilationPasses {
private TranspilationPasses() {} private TranspilationPasses() {}


public static void addEs6ModulePass(List<PassFactory> passes) {
passes.add(es6RewriteModule);
}

public static void addEs2017Passes(List<PassFactory> passes) { public static void addEs2017Passes(List<PassFactory> passes) {
passes.add(rewriteAsyncFunctions); passes.add(rewriteAsyncFunctions);
} }
Expand Down Expand Up @@ -73,6 +77,20 @@ public static void addRewritePolyfillPass(List<PassFactory> passes) {
passes.add(rewritePolyfills); passes.add(rewritePolyfills);
} }


/** Rewrites ES6 modules */
private static final HotSwapPassFactory es6RewriteModule =
new HotSwapPassFactory("es6RewriteModule", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
return new Es6RewriteModules(compiler);
}

@Override
protected FeatureSet featureSet() {
return FeatureSet.ES8_MODULES;
}
};

private static final PassFactory rewriteAsyncFunctions = private static final PassFactory rewriteAsyncFunctions =
new PassFactory("rewriteAsyncFunctions", true) { new PassFactory("rewriteAsyncFunctions", true) {
@Override @Override
Expand Down
1 change: 1 addition & 0 deletions test/com/google/javascript/jscomp/CompilerTestCase.java
Expand Up @@ -1712,6 +1712,7 @@ private Compiler multistageSerializeAndDeserialize(


private static void transpileToEs5(AbstractCompiler compiler, Node externsRoot, Node codeRoot) { private static void transpileToEs5(AbstractCompiler compiler, Node externsRoot, Node codeRoot) {
List<PassFactory> factories = new ArrayList<>(); List<PassFactory> factories = new ArrayList<>();
TranspilationPasses.addEs6ModulePass(factories);
TranspilationPasses.addEs2017Passes(factories); TranspilationPasses.addEs2017Passes(factories);
TranspilationPasses.addEs6EarlyPasses(factories); TranspilationPasses.addEs6EarlyPasses(factories);
TranspilationPasses.addEs6LatePasses(factories); TranspilationPasses.addEs6LatePasses(factories);
Expand Down
4 changes: 2 additions & 2 deletions test/com/google/javascript/jscomp/Es6RewriteClassTest.java
Expand Up @@ -22,7 +22,7 @@
import static com.google.javascript.jscomp.Es6RewriteClass.DYNAMIC_EXTENDS_TYPE; import static com.google.javascript.jscomp.Es6RewriteClass.DYNAMIC_EXTENDS_TYPE;
import static com.google.javascript.jscomp.Es6ToEs3Converter.CANNOT_CONVERT; import static com.google.javascript.jscomp.Es6ToEs3Converter.CANNOT_CONVERT;
import static com.google.javascript.jscomp.Es6ToEs3Converter.CANNOT_CONVERT_YET; import static com.google.javascript.jscomp.Es6ToEs3Converter.CANNOT_CONVERT_YET;
import static com.google.javascript.jscomp.parsing.parser.FeatureSet.ES6; import static com.google.javascript.jscomp.parsing.parser.FeatureSet.ES6_MODULES;


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


@Override @Override
protected FeatureSet featureSet() { protected FeatureSet featureSet() {
return ES6; return ES6_MODULES;
} }
}; };
} }
Expand Down

0 comments on commit d24d175

Please sign in to comment.