Skip to content

Commit

Permalink
Support the node module resolution algorithm for ES6 modules.
Browse files Browse the repository at this point in the history
Adds a --module_resolution flag to specify whether to use the legacy,
node, or browser module resolution algorithm to locate modules.

Closes #2130

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=145837856
  • Loading branch information
ChadKillingsworth authored and blickly committed Jan 28, 2017
1 parent ca2f0f2 commit 35e1e1b
Show file tree
Hide file tree
Showing 15 changed files with 644 additions and 163 deletions.
27 changes: 22 additions & 5 deletions src/com/google/javascript/jscomp/CommandLineRunner.java
Expand Up @@ -710,10 +710,23 @@ private static class Flags {
usage = "Rewrite ES6 library calls to use polyfills provided by the compiler's runtime.") usage = "Rewrite ES6 library calls to use polyfills provided by the compiler's runtime.")
private boolean rewritePolyfills = true; private boolean rewritePolyfills = true;


@Option(name = "--print_source_after_each_pass", @Option(
hidden = true, name = "--print_source_after_each_pass",
usage = "Whether to iteratively print resulting JS source per pass.") hidden = true,
private boolean printSourceAfterEachPass = false; usage = "Whether to iteratively print resulting JS source per pass."
)
private boolean printSourceAfterEachPass = false;

@Option(
name = "--module_resolution",
hidden = false,
usage =
"Specifies how the compiler locates modules. BROWSER requires all module imports "
+ "to begin with a '.' or '/' and have a file extension. NODE uses the node module "
+ "rules. LEGACY prepends a '/' to any import not already beginning with a "
+ "'.' or '/'."
)
private ModuleLoader.ResolutionMode moduleResolutionMode = ModuleLoader.ResolutionMode.LEGACY;


@Argument @Argument
private List<String> arguments = new ArrayList<>(); private List<String> arguments = new ArrayList<>();
Expand Down Expand Up @@ -787,7 +800,10 @@ private void parse(List<String> args) throws CmdLineException {
.putAll( .putAll(
"JS Modules", "JS Modules",
ImmutableList.of( ImmutableList.of(
"js_module_root", "process_common_js_modules", "transform_amd_modules")) "js_module_root",
"module_resolution",
"process_common_js_modules",
"transform_amd_modules"))
.putAll( .putAll(
"Library and Framework Specific", "Library and Framework Specific",
ImmutableList.of( ImmutableList.of(
Expand Down Expand Up @@ -1690,6 +1706,7 @@ protected CompilerOptions createOptions() {
options.setStrictModeInput(flags.strictModeInput); options.setStrictModeInput(flags.strictModeInput);
options.setEmitUseStrict(flags.emitUseStrict); options.setEmitUseStrict(flags.emitUseStrict);
options.setSourceMapIncludeSourcesContent(flags.sourceMapIncludeSourcesContent); options.setSourceMapIncludeSourcesContent(flags.sourceMapIncludeSourcesContent);
options.setModuleResolutionMode(flags.moduleResolutionMode);


return options; return options;
} }
Expand Down
12 changes: 9 additions & 3 deletions src/com/google/javascript/jscomp/Compiler.java
Expand Up @@ -1491,9 +1491,15 @@ Node parseInputs() {
|| options.transformAMDToCJSModules || options.transformAMDToCJSModules
|| options.processCommonJSModules) { || options.processCommonJSModules) {


this.moduleLoader = new ModuleLoader(this, options.moduleRoots, inputs); this.moduleLoader =

new ModuleLoader(
if (options.processCommonJSModules) { this,
options.moduleRoots,
inputs,
ModuleLoader.PathResolver.RELATIVE,
options.moduleResolutionMode);

if (options.moduleResolutionMode == ModuleLoader.ResolutionMode.NODE) {
this.moduleLoader.setPackageJsonMainEntries(processJsonInputs(inputs)); this.moduleLoader.setPackageJsonMainEntries(processJsonInputs(inputs));
} }


Expand Down
16 changes: 15 additions & 1 deletion src/com/google/javascript/jscomp/CompilerOptions.java
Expand Up @@ -1086,10 +1086,13 @@ public void setWrapGoogModulesForWhitespaceOnly(boolean enable) {
*/ */
private boolean isStrictModeInput = true; private boolean isStrictModeInput = true;


/** Which algorithm to use for locating ES6 and CommonJS modules */
ModuleLoader.ResolutionMode moduleResolutionMode;

/** /**
* Should the compiler print its configuration options to stderr when they are initialized? * Should the compiler print its configuration options to stderr when they are initialized?
* *
* <p> Default {@code false}. * <p>Default {@code false}.
*/ */
public void setPrintConfig(boolean printConfig) { public void setPrintConfig(boolean printConfig) {
this.printConfig = printConfig; this.printConfig = printConfig;
Expand All @@ -1109,6 +1112,9 @@ public CompilerOptions() {
// Which environment to use // Which environment to use
environment = Environment.BROWSER; environment = Environment.BROWSER;


// Modules
moduleResolutionMode = ModuleLoader.ResolutionMode.LEGACY;

// Checks // Checks
skipNonTranspilationPasses = false; skipNonTranspilationPasses = false;
devMode = DevMode.OFF; devMode = DevMode.OFF;
Expand Down Expand Up @@ -2648,6 +2654,14 @@ public CompilerOptions setEmitUseStrict(boolean emitUseStrict) {
return this; return this;
} }


public ModuleLoader.ResolutionMode getModuleResolutionMode() {
return this.moduleResolutionMode;
}

public void setModuleResolutionMode(ModuleLoader.ResolutionMode mode) {
this.moduleResolutionMode = mode;
}

@Override @Override
public String toString() { public String toString() {
String strValue = String strValue =
Expand Down
5 changes: 3 additions & 2 deletions src/com/google/javascript/jscomp/DiagnosticGroups.java
Expand Up @@ -143,8 +143,9 @@ public DiagnosticGroup forName(String name) {
+ "visibility"; + "visibility";


public static final DiagnosticGroup COMMON_JS_MODULE_LOAD = public static final DiagnosticGroup COMMON_JS_MODULE_LOAD =
DiagnosticGroups.registerGroup( DiagnosticGroups.registerGroup("commonJsModuleLoad",
"commonJsModuleLoad", ProcessCommonJSModules.COMMON_JS_MODULE_LOAD_ERROR); ProcessCommonJSModules.SUSPICIOUS_EXPORTS_ASSIGNMENT,
ProcessCommonJSModules.UNKNOWN_REQUIRE_ENSURE);


public static final DiagnosticGroup GLOBAL_THIS = public static final DiagnosticGroup GLOBAL_THIS =
DiagnosticGroups.registerGroup("globalThis", DiagnosticGroups.registerGroup("globalThis",
Expand Down
43 changes: 32 additions & 11 deletions src/com/google/javascript/jscomp/ProcessCommonJSModules.java
Expand Up @@ -43,10 +43,6 @@ public final class ProcessCommonJSModules implements CompilerPass {
private static final String EXPORTS = "exports"; private static final String EXPORTS = "exports";
private static final String MODULE = "module"; private static final String MODULE = "module";


public static final DiagnosticType COMMON_JS_MODULE_LOAD_ERROR = DiagnosticType.error(
"JSC_COMMONJS_MODULE_LOAD_ERROR",
"Failed to load module \"{0}\"");

public static final DiagnosticType UNKNOWN_REQUIRE_ENSURE = public static final DiagnosticType UNKNOWN_REQUIRE_ENSURE =
DiagnosticType.warning( DiagnosticType.warning(
"JSC_COMMONJS_UNKNOWN_REQUIRE_ENSURE_ERROR", "Unrecognized require.ensure call: {0}"); "JSC_COMMONJS_UNKNOWN_REQUIRE_ENSURE_ERROR", "Unrecognized require.ensure call: {0}");
Expand Down Expand Up @@ -356,9 +352,16 @@ public void visit(NodeTraversal t, Node n, Node parent) {
/** Visit require calls. Emit corresponding goog.require call. */ /** Visit require calls. Emit corresponding goog.require call. */
private void visitRequireCall(NodeTraversal t, Node require, Node parent) { private void visitRequireCall(NodeTraversal t, Node require, Node parent) {
String requireName = require.getSecondChild().getString(); String requireName = require.getSecondChild().getString();
ModulePath modulePath = t.getInput().getPath().resolveCommonJsModule(requireName); ModulePath modulePath =
t.getInput()
.getPath()
.resolveJsModule(
requireName,
require.getSourceFileName(),
require.getLineno(),
require.getCharno());
if (modulePath == null) { if (modulePath == null) {
compiler.report(t.makeError(require, COMMON_JS_MODULE_LOAD_ERROR, requireName)); // The module loader will issue an error
return; return;
} }


Expand Down Expand Up @@ -701,9 +704,16 @@ public void visit(NodeTraversal t, Node n, Node parent) {
*/ */
private void visitRequireCall(NodeTraversal t, Node require, Node parent) { private void visitRequireCall(NodeTraversal t, Node require, Node parent) {
String requireName = require.getSecondChild().getString(); String requireName = require.getSecondChild().getString();
ModulePath modulePath = t.getInput().getPath().resolveCommonJsModule(requireName); ModulePath modulePath =
t.getInput()
.getPath()
.resolveJsModule(
requireName,
require.getSourceFileName(),
require.getLineno(),
require.getCharno());
if (modulePath == null) { if (modulePath == null) {
compiler.report(t.makeError(require, COMMON_JS_MODULE_LOAD_ERROR, requireName)); // The module loader will issue an error
return; return;
} }


Expand Down Expand Up @@ -1190,7 +1200,11 @@ private String getModuleImportName(NodeTraversal t, Node n) {
&& rValue.getSecondChild().isString() && rValue.getSecondChild().isString()
&& t.getScope().getVar(rValue.getFirstChild().getQualifiedName()) == null) { && t.getScope().getVar(rValue.getFirstChild().getQualifiedName()) == null) {
String requireName = rValue.getSecondChild().getString(); String requireName = rValue.getSecondChild().getString();
ModulePath modulePath = t.getInput().getPath().resolveCommonJsModule(requireName); ModulePath modulePath =
t.getInput()
.getPath()
.resolveJsModule(
requireName, n.getSourceFileName(), n.getLineno(), n.getCharno());
if (modulePath == null) { if (modulePath == null) {
return null; return null;
} }
Expand Down Expand Up @@ -1227,9 +1241,16 @@ private void fixTypeNode(NodeTraversal t, Node typeNode) {
} }


String moduleName = name.substring(0, endIndex); String moduleName = name.substring(0, endIndex);
ModulePath modulePath = t.getInput().getPath().resolveCommonJsModule(moduleName); ModulePath modulePath =
t.getInput()
.getPath()
.resolveJsModule(
moduleName,
typeNode.getSourceFileName(),
typeNode.getLineno(),
typeNode.getCharno());
if (modulePath == null) { if (modulePath == null) {
t.makeError(typeNode, COMMON_JS_MODULE_LOAD_ERROR, moduleName); // The module loader will issue an error
return; return;
} }


Expand Down
46 changes: 41 additions & 5 deletions src/com/google/javascript/jscomp/ProcessEs6Modules.java
Expand Up @@ -165,7 +165,21 @@ private void visitImport(NodeTraversal t, Node importDecl, Node parent) {
// These are rewritten to plain namespace object accesses. // These are rewritten to plain namespace object accesses.
moduleName = importName.substring("goog:".length()); moduleName = importName.substring("goog:".length());
} else { } else {
moduleName = t.getInput().getPath().resolveEs6Module(importName).toModuleName(); ModuleLoader.ModulePath modulePath =
t.getInput()
.getPath()
.resolveJsModule(
importName,
importDecl.getSourceFileName(),
importDecl.getLineno(),
importDecl.getCharno());
if (modulePath == null) {
// The module loader issues an error
// Fall back to assuming the module is a file path
modulePath = t.getInput().getPath().resolveModuleAsPath(importName);
}

moduleName = modulePath.toModuleName();
} }


for (Node child : importDecl.children()) { for (Node child : importDecl.children()) {
Expand Down Expand Up @@ -270,8 +284,18 @@ private void visitExport(NodeTraversal t, Node export, Node parent) {
parent.addChildBefore(importNode, export); parent.addChildBefore(importNode, export);
visit(t, importNode, parent); visit(t, importNode, parent);


String moduleName = ModuleLoader.ModulePath path =
t.getInput().getPath().resolveEs6Module(moduleIdentifier.getString()).toModuleName(); t.getInput()
.getPath()
.resolveJsModule(
moduleIdentifier.getString(),
export.getSourceFileName(),
export.getLineno(),
export.getCharno());
if (path == null) {
path = t.getInput().getPath().resolveModuleAsPath(moduleIdentifier.getString());
}
String moduleName = path.toModuleName();


for (Node exportSpec : export.getFirstChild().children()) { for (Node exportSpec : export.getFirstChild().children()) {
String nameFromOtherModule = exportSpec.getFirstChild().getString(); String nameFromOtherModule = exportSpec.getFirstChild().getString();
Expand Down Expand Up @@ -539,8 +563,20 @@ private void fixTypeNode(NodeTraversal t, Node typeNode) {
} }


String moduleName = name.substring(0, endIndex); String moduleName = name.substring(0, endIndex);
String globalModuleName = ModuleLoader.ModulePath path =
t.getInput().getPath().resolveEs6Module(moduleName).toModuleName(); t.getInput()
.getPath()
.resolveJsModule(
moduleName,
typeNode.getSourceFileName(),
typeNode.getLineno(),
typeNode.getCharno());

if (path == null) {
path = t.getInput().getPath().resolveModuleAsPath(moduleName);
}
String globalModuleName = path.toModuleName();

typeNode.setString( typeNode.setString(
localTypeName == null ? globalModuleName : globalModuleName + localTypeName); localTypeName == null ? globalModuleName : globalModuleName + localTypeName);
} else { } else {
Expand Down
6 changes: 5 additions & 1 deletion src/com/google/javascript/jscomp/deps/JsFileParser.java
Expand Up @@ -285,7 +285,11 @@ protected boolean parseLine(String line) throws ParseException {
if (arg.startsWith("goog:")) { if (arg.startsWith("goog:")) {
requires.add(arg.substring(5)); // cut off the "goog:" prefix requires.add(arg.substring(5)); // cut off the "goog:" prefix
} else { } else {
requires.add(file.resolveEs6Module(arg).toModuleName()); ModuleLoader.ModulePath path = file.resolveJsModule(arg);
if (path == null) {
path = file.resolveModuleAsPath(arg);
}
requires.add(path.toModuleName());
} }
} }
} }
Expand Down

0 comments on commit 35e1e1b

Please sign in to comment.