Skip to content

Commit

Permalink
Refactor CommonJS rewriting and force script to module rewriting to b…
Browse files Browse the repository at this point in the history
…e normal passes.

Closes #2691

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=175609661
  • Loading branch information
ChadKillingsworth authored and Tyler Breisacher committed Nov 14, 2017
1 parent f7bae0d commit 93cea5b
Show file tree
Hide file tree
Showing 13 changed files with 444 additions and 235 deletions.
88 changes: 23 additions & 65 deletions src/com/google/javascript/jscomp/Compiler.java
Expand Up @@ -1722,13 +1722,16 @@ Node parseInputs() {
externsRoot.addChildToBack(n); externsRoot.addChildToBack(n);
} }


if (options.transformAMDToCJSModules) {
processAMDModules();
}

if (options.getLanguageIn().toFeatureSet().has(FeatureSet.Feature.MODULES) if (options.getLanguageIn().toFeatureSet().has(FeatureSet.Feature.MODULES)
|| options.transformAMDToCJSModules
|| options.processCommonJSModules) { || options.processCommonJSModules) {


this.moduleLoader = this.moduleLoader =
new ModuleLoader( new ModuleLoader(
this, null,
options.moduleRoots, options.moduleRoots,
inputs, inputs,
ModuleLoader.PathResolver.RELATIVE, ModuleLoader.PathResolver.RELATIVE,
Expand All @@ -1740,7 +1743,7 @@ Node parseInputs() {
// so we redefine it afterwards with the package.json inputs // so we redefine it afterwards with the package.json inputs
this.moduleLoader = this.moduleLoader =
new ModuleLoader( new ModuleLoader(
this, null,
options.moduleRoots, options.moduleRoots,
inputs, inputs,
ModuleLoader.PathResolver.RELATIVE, ModuleLoader.PathResolver.RELATIVE,
Expand All @@ -1755,20 +1758,13 @@ Node parseInputs() {
if (options.getDependencyOptions().needsManagement()) { if (options.getDependencyOptions().needsManagement()) {
findDependenciesFromEntryPoints( findDependenciesFromEntryPoints(
options.getLanguageIn().toFeatureSet().has(Feature.MODULES), options.getLanguageIn().toFeatureSet().has(Feature.MODULES),
options.processCommonJSModules, options.processCommonJSModules);
options.transformAMDToCJSModules); } else if (options.needsTranspilationFrom(FeatureSet.ES6_MODULES)
} else if (options.needsTranspilationOf(Feature.MODULES)
|| options.transformAMDToCJSModules
|| options.processCommonJSModules) { || options.processCommonJSModules) {
if (options.getLanguageIn().toFeatureSet().has(Feature.MODULES)) { if (options.getLanguageIn().toFeatureSet().has(Feature.MODULES)) {
parsePotentialModules(inputs); parsePotentialModules(inputs);
} }


// Modules inferred in ProcessCommonJS pass.
if (options.transformAMDToCJSModules || options.processCommonJSModules) {
processAMDAndCommonJSModules();
}

// Build a map of module identifiers for any input which provides no namespace. // Build a map of module identifiers for any input which provides no namespace.
// These files could be imported modules which have no exports, but do have side effects. // These files could be imported modules which have no exports, but do have side effects.
Map<String, CompilerInput> inputModuleIdentifiers = new HashMap<>(); Map<String, CompilerInput> inputModuleIdentifiers = new HashMap<>();
Expand All @@ -1793,13 +1789,14 @@ Node parseInputs() {
} }


for (CompilerInput input : inputsToRewrite.values()) { for (CompilerInput input : inputsToRewrite.values()) {
forceInputToPathBasedModule( input.setJsModuleType(CompilerInput.ModuleType.IMPORTED_SCRIPT);
input,
options.getLanguageIn().toFeatureSet().has(Feature.MODULES),
options.processCommonJSModules);
} }
} }


if (this.moduleLoader != null) {
this.moduleLoader.setErrorHandler(this);
}

orderInputs(); orderInputs();


// If in IDE mode, we ignore the error and keep going. // If in IDE mode, we ignore the error and keep going.
Expand Down Expand Up @@ -1915,8 +1912,7 @@ void orderInputs() {
* <p>If the dependency mode is set to LOOSE, inputs for which the deps package did not find a * <p>If the dependency mode is set to LOOSE, inputs for which the deps package did not find a
* provide statement or detect as a module will be treated as entry points. * provide statement or detect as a module will be treated as entry points.
*/ */
void findDependenciesFromEntryPoints( void findDependenciesFromEntryPoints(boolean supportEs6Modules, boolean supportCommonJSModules) {
boolean supportEs6Modules, boolean supportCommonJSModules, boolean supportAmdModules) {
hoistExterns(); hoistExterns();
List<CompilerInput> entryPoints = new ArrayList<>(); List<CompilerInput> entryPoints = new ArrayList<>();
Map<String, CompilerInput> inputsByProvide = new HashMap<>(); Map<String, CompilerInput> inputsByProvide = new HashMap<>();
Expand Down Expand Up @@ -1951,16 +1947,7 @@ void findDependenciesFromEntryPoints(
inputsByIdentifier, inputsByIdentifier,
inputsByProvide, inputsByProvide,
supportEs6Modules, supportEs6Modules,
supportCommonJSModules, supportCommonJSModules));
supportAmdModules));
}

// TODO(ChadKillingsworth) Move this into the standard compilation passes
if (supportCommonJSModules) {
for (CompilerInput input : orderedInputs) {
new ProcessCommonJSModules(this)
.process(/* externs */ null, input.getAstRoot(this), /* forceModuleDetection */ false);
}
} }
} }


Expand All @@ -1972,32 +1959,27 @@ private List<CompilerInput> depthFirstDependenciesFromInput(
Map<String, CompilerInput> inputsByIdentifier, Map<String, CompilerInput> inputsByIdentifier,
Map<String, CompilerInput> inputsByProvide, Map<String, CompilerInput> inputsByProvide,
boolean supportEs6Modules, boolean supportEs6Modules,
boolean supportCommonJSModules, boolean supportCommonJSModules) {
boolean supportAmdModules) {
List<CompilerInput> orderedInputs = new ArrayList<>(); List<CompilerInput> orderedInputs = new ArrayList<>();
if (!inputs.remove(input)) { if (!inputs.remove(input)) {
// It's possible for a module to be included as both a script // It's possible for a module to be included as both a script
// and a module in the same compilation. In these cases, it should // and a module in the same compilation. In these cases, it should
// be forced to be a module. // be forced to be a module.
if (wasImportedByModule && input.getJsModuleType() == CompilerInput.ModuleType.NONE) { if (wasImportedByModule && input.getJsModuleType() == CompilerInput.ModuleType.NONE) {
forceInputToPathBasedModule(input, supportEs6Modules, supportCommonJSModules); input.setJsModuleType(CompilerInput.ModuleType.IMPORTED_SCRIPT);
} }


return orderedInputs; return orderedInputs;
} }


if (supportAmdModules) {
new TransformAMDToCJSModule(this).process(null, input.getAstRoot(this));
}

FindModuleDependencies findDeps = FindModuleDependencies findDeps =
new FindModuleDependencies(this, supportEs6Modules, supportCommonJSModules); new FindModuleDependencies(this, supportEs6Modules, supportCommonJSModules);
findDeps.process(input.getAstRoot(this)); findDeps.process(input.getAstRoot(this));


// If this input was imported by another module, it is itself a module // If this input was imported by another module, it is itself a module
// so we force it to be detected as such. // so we force it to be detected as such.
if (wasImportedByModule && input.getJsModuleType() == CompilerInput.ModuleType.NONE) { if (wasImportedByModule && input.getJsModuleType() == CompilerInput.ModuleType.NONE) {
forceInputToPathBasedModule(input, supportEs6Modules, supportCommonJSModules); input.setJsModuleType(CompilerInput.ModuleType.IMPORTED_SCRIPT);
} }


for (String requiredNamespace : input.getRequires()) { for (String requiredNamespace : input.getRequires()) {
Expand All @@ -2019,28 +2001,13 @@ private List<CompilerInput> depthFirstDependenciesFromInput(
inputsByIdentifier, inputsByIdentifier,
inputsByProvide, inputsByProvide,
supportEs6Modules, supportEs6Modules,
supportCommonJSModules, supportCommonJSModules));
supportAmdModules));
} }
} }
orderedInputs.add(input); orderedInputs.add(input);
return orderedInputs; return orderedInputs;
} }


private void forceInputToPathBasedModule(
CompilerInput input, boolean supportEs6Modules, boolean supportCommonJSModules) {

if (supportEs6Modules) {
FindModuleDependencies findDeps =
new FindModuleDependencies(this, supportEs6Modules, supportCommonJSModules);
findDeps.convertToEs6Module(input.getAstRoot(this));
input.setJsModuleType(CompilerInput.ModuleType.ES6);
} else if (supportCommonJSModules) {
new ProcessCommonJSModules(this).process(null, input.getAstRoot(this), true);
input.setJsModuleType(CompilerInput.ModuleType.COMMONJS);
}
}

/** /**
* Hoists inputs with the @externs annotation into the externs list. * Hoists inputs with the @externs annotation into the externs list.
*/ */
Expand Down Expand Up @@ -2145,6 +2112,7 @@ Map<String, String> processJsonInputs(List<CompilerInput> inputsToProcess) {
if (root == null) { if (root == null) {
continue; continue;
} }
input.setJsModuleType(CompilerInput.ModuleType.JSON);
rewriteJson.process(null, root); rewriteJson.process(null, root);
} }
return rewriteJson.getPackageJsonMainEntries(); return rewriteJson.getPackageJsonMainEntries();
Expand Down Expand Up @@ -2172,25 +2140,15 @@ private List<CompilerInput> parsePotentialModules(List<CompilerInput> inputsToPr
return filteredInputs; return filteredInputs;
} }


/** /** Transforms AMD to CJS modules */
* Transforms AMD and CJS modules to something closure compiler can void processAMDModules() {
* process and creates JSModules and the corresponding dependency tree
* on the way.
*/
void processAMDAndCommonJSModules() {
for (CompilerInput input : inputs) { for (CompilerInput input : inputs) {
input.setCompiler(this); input.setCompiler(this);
Node root = input.getAstRoot(this); Node root = input.getAstRoot(this);
if (root == null) { if (root == null) {
continue; continue;
} }
if (options.transformAMDToCJSModules) { new TransformAMDToCJSModule(this).process(null, root);
new TransformAMDToCJSModule(this).process(null, root);
}
if (options.processCommonJSModules) {
ProcessCommonJSModules cjs = new ProcessCommonJSModules(this);
cjs.process(null, root);
}
} }
} }


Expand Down
6 changes: 4 additions & 2 deletions src/com/google/javascript/jscomp/CompilerInput.java
Expand Up @@ -521,8 +521,10 @@ public void reset() {


enum ModuleType { enum ModuleType {
NONE, NONE,
GOOG_MODULE, GOOG,
ES6, ES6,
COMMONJS COMMONJS,
JSON,
IMPORTED_SCRIPT
} }
} }
39 changes: 39 additions & 0 deletions src/com/google/javascript/jscomp/DefaultPassConfig.java
Expand Up @@ -203,6 +203,13 @@ protected List<PassFactory> getTranspileOnlyPasses() {
@Override @Override
protected List<PassFactory> getWhitespaceOnlyPasses() { protected List<PassFactory> getWhitespaceOnlyPasses() {
List<PassFactory> passes = new ArrayList<>(); List<PassFactory> passes = new ArrayList<>();

if (options.processCommonJSModules) {
passes.add(rewriteCommonJsModules);
} else if (options.getLanguageIn().toFeatureSet().has(FeatureSet.Feature.MODULES)) {
passes.add(rewriteScriptsToEs6Modules);
}

if (options.wrapGoogModulesForWhitespaceOnly) { if (options.wrapGoogModulesForWhitespaceOnly) {
passes.add(whitespaceWrapGoogModules); passes.add(whitespaceWrapGoogModules);
} }
Expand Down Expand Up @@ -251,6 +258,12 @@ protected List<PassFactory> getChecks() {


checks.add(createEmptyPass("beforeStandardChecks")); checks.add(createEmptyPass("beforeStandardChecks"));


if (options.processCommonJSModules) {
checks.add(rewriteCommonJsModules);
} else if (options.getLanguageIn().toFeatureSet().has(FeatureSet.Feature.MODULES)) {
checks.add(rewriteScriptsToEs6Modules);
}

// Note: ChromePass can rewrite invalid @type annotations into valid ones, so should run before // Note: ChromePass can rewrite invalid @type annotations into valid ones, so should run before
// JsDoc checks. // JsDoc checks.
if (options.isChromePassEnabled()) { if (options.isChromePassEnabled()) {
Expand Down Expand Up @@ -3432,4 +3445,30 @@ protected CompilerPass create(AbstractCompiler compiler) {
return new RemoveSuperMethodsPass(compiler); return new RemoveSuperMethodsPass(compiler);
} }
}; };

private final PassFactory rewriteCommonJsModules =
new PassFactory(PassNames.REWRITE_COMMON_JS_MODULES, true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new ProcessCommonJSModules(compiler);
}

@Override
public FeatureSet featureSet() {
return ES8_MODULES;
}
};

private final PassFactory rewriteScriptsToEs6Modules =
new PassFactory(PassNames.REWRITE_SCRIPTS_TO_ES6_MODULES, true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new Es6RewriteScriptsToModules(compiler);
}

@Override
protected FeatureSet featureSet() {
return ES8_MODULES;
}
};
} }
76 changes: 76 additions & 0 deletions src/com/google/javascript/jscomp/Es6RewriteScriptsToModules.java
@@ -0,0 +1,76 @@
/*
* Copyright 2017 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;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.javascript.jscomp.NodeTraversal.AbstractPreOrderCallback;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

/** Rewrites a script which was imported as a module into an ES6 module. */
public final class Es6RewriteScriptsToModules extends AbstractPreOrderCallback
implements HotSwapCompilerPass {
private final AbstractCompiler compiler;

/**
* Creates a new Es6RewriteModules instance which can be used to rewrite ES6 modules to a
* concatenable form.
*/
public Es6RewriteScriptsToModules(AbstractCompiler compiler) {
this.compiler = compiler;
}

/**
* Force rewriting of a script into an ES6 module, such as for imported files that contain no
* "import" or "export" statements.
*/
void forceToEs6Module(Node root) {
if (Es6RewriteModules.isEs6ModuleRoot(root)) {
return;
}
Node moduleNode = new Node(Token.MODULE_BODY).srcref(root);
moduleNode.addChildrenToBack(root.removeChildren());
root.addChildToBack(moduleNode);
}

@Override
public void process(Node externs, Node root) {
for (Node file = root.getFirstChild(); file != null; file = file.getNext()) {
hotSwapScript(file, null);
}
}

@Override
public void hotSwapScript(Node scriptNode, Node originalRoot) {
checkArgument(scriptNode.isScript());
NodeTraversal.traverseEs6(compiler, scriptNode, this);
}

@Override
public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) {
if (n.isScript()) {
CompilerInput.ModuleType moduleType = compiler.getInput(n.getInputId()).getJsModuleType();

if (moduleType == CompilerInput.ModuleType.IMPORTED_SCRIPT) {
forceToEs6Module(n);
compiler.reportChangeToChangeScope(n);
}
}

return false;
}
}

0 comments on commit 93cea5b

Please sign in to comment.