Skip to content

Commit 422d825

Browse files
Support the node module resolution algorithm for ES6 modules.
Adds a --module_resolution flag to specify whether to use the legacy, node or browser module resolution algortithm to locate modules.
1 parent 9b70a1f commit 422d825

15 files changed

+595
-169
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2525,6 +2525,12 @@ public CommandLineConfig setJsonStreamMode(JsonStreamMode mode) {
25252525
return this;
25262526
}
25272527

2528+
public CommandLineConfig setModuleResolutionMode(ModuleLoader.ResolutionMode mode) {
2529+
this.moduleResolutionMode = mode;
2530+
return this;
2531+
}
2532+
2533+
private ModuleLoader.ResolutionMode moduleResolutionMode = ModuleLoader.ResolutionMode.LEGACY;
25282534
}
25292535

25302536
/**

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

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -687,10 +687,23 @@ private static class Flags {
687687
usage = "Rewrite ES6 library calls to use polyfills provided by the compiler's runtime.")
688688
private boolean rewritePolyfills = true;
689689

690-
@Option(name = "--print_source_after_each_pass",
691-
hidden = true,
692-
usage = "Whether to iteratively print resulting JS source per pass.")
693-
private boolean printSourceAfterEachPass = false;
690+
@Option(
691+
name = "--print_source_after_each_pass",
692+
hidden = true,
693+
usage = "Whether to iteratively print resulting JS source per pass."
694+
)
695+
private boolean printSourceAfterEachPass = false;
696+
697+
@Option(
698+
name = "--module_resolution",
699+
hidden = false,
700+
usage =
701+
"Specifies how the compiler locates modules. BROWSER requires all module imports "
702+
+ "to begin with a '.' or '/' and have a file extension. NODE uses the node module "
703+
+ "rules. LEGACY prepends a '/' to any import not already beginning with a "
704+
+ "'.' or '/'."
705+
)
706+
private ModuleLoader.ResolutionMode moduleResolutionMode = ModuleLoader.ResolutionMode.NODE;
694707

695708
@Argument
696709
private List<String> arguments = new ArrayList<>();
@@ -764,7 +777,10 @@ private void parse(List<String> args) throws CmdLineException {
764777
.putAll(
765778
"JS Modules",
766779
ImmutableList.of(
767-
"js_module_root", "process_common_js_modules", "transform_amd_modules"))
780+
"js_module_root",
781+
"module_resolution",
782+
"process_common_js_modules",
783+
"transform_amd_modules"))
768784
.putAll(
769785
"Library and Framework Specific",
770786
ImmutableList.of(
@@ -1487,7 +1503,8 @@ private void initConfigFromFlags(String[] args, PrintStream out, PrintStream err
14871503
.setTracerMode(flags.tracerMode)
14881504
.setInstrumentationTemplateFile(flags.instrumentationFile)
14891505
.setNewTypeInference(flags.useNewTypeInference)
1490-
.setJsonStreamMode(flags.jsonStreamMode);
1506+
.setJsonStreamMode(flags.jsonStreamMode)
1507+
.setModuleResolutionMode(flags.moduleResolutionMode);
14911508
}
14921509
errorStream = null;
14931510
}
@@ -1656,6 +1673,7 @@ protected CompilerOptions createOptions() {
16561673
options.setPrintSourceAfterEachPass(flags.printSourceAfterEachPass);
16571674
options.setStrictModeInput(flags.strictModeInput);
16581675
options.setEmitUseStrict(flags.emitUseStrict);
1676+
options.setModuleResolutionMode(flags.moduleResolutionMode);
16591677

16601678
return options;
16611679
}

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,9 +1482,15 @@ Node parseInputs() {
14821482
|| options.transformAMDToCJSModules
14831483
|| options.processCommonJSModules) {
14841484

1485-
this.moduleLoader = new ModuleLoader(this, options.moduleRoots, inputs);
1486-
1487-
if (options.processCommonJSModules) {
1485+
this.moduleLoader =
1486+
new ModuleLoader(
1487+
this,
1488+
options.moduleRoots,
1489+
inputs,
1490+
ModuleLoader.PathResolver.RELATIVE,
1491+
options.moduleResolutionMode);
1492+
1493+
if (options.moduleResolutionMode == ModuleLoader.ResolutionMode.NODE) {
14881494
this.moduleLoader.setPackageJsonMainEntries(processJsonInputs(inputs));
14891495
}
14901496

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,10 +1075,13 @@ public void setWrapGoogModulesForWhitespaceOnly(boolean enable) {
10751075
*/
10761076
private boolean isStrictModeInput = true;
10771077

1078+
/** Which algorithm to use for locating ES6 and CommonJS modules */
1079+
ModuleLoader.ResolutionMode moduleResolutionMode;
1080+
10781081
/**
10791082
* Should the compiler print its configuration options to stderr when they are initialized?
10801083
*
1081-
* <p> Default {@code false}.
1084+
* <p>Default {@code false}.
10821085
*/
10831086
public void setPrintConfig(boolean printConfig) {
10841087
this.printConfig = printConfig;
@@ -1098,6 +1101,9 @@ public CompilerOptions() {
10981101
// Which environment to use
10991102
environment = Environment.BROWSER;
11001103

1104+
// Modules
1105+
moduleResolutionMode = ModuleLoader.ResolutionMode.LEGACY;
1106+
11011107
// Checks
11021108
skipNonTranspilationPasses = false;
11031109
devMode = DevMode.OFF;
@@ -2637,6 +2643,14 @@ public CompilerOptions setEmitUseStrict(boolean emitUseStrict) {
26372643
return this;
26382644
}
26392645

2646+
public ModuleLoader.ResolutionMode getModuleResolutionMode() {
2647+
return this.moduleResolutionMode;
2648+
}
2649+
2650+
public void setModuleResolutionMode(ModuleLoader.ResolutionMode mode) {
2651+
this.moduleResolutionMode = mode;
2652+
}
2653+
26402654
@Override
26412655
public String toString() {
26422656
String strValue =

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ public DiagnosticGroup forName(String name) {
143143

144144
public static final DiagnosticGroup COMMON_JS_MODULE_LOAD =
145145
DiagnosticGroups.registerGroup(
146-
"commonJsModuleLoad", ProcessCommonJSModules.COMMON_JS_MODULE_LOAD_ERROR);
146+
"commonJsModuleLoad",
147+
ProcessCommonJSModules.SUSPICIOUS_EXPORTS_ASSIGNMENT,
148+
ProcessCommonJSModules.UNKNOWN_REQUIRE_ENSURE);
147149

148150
public static final DiagnosticGroup GLOBAL_THIS =
149151
DiagnosticGroups.registerGroup("globalThis",

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

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@ public final class ProcessCommonJSModules implements CompilerPass {
4343
private static final String EXPORTS = "exports";
4444
private static final String MODULE = "module";
4545

46-
public static final DiagnosticType COMMON_JS_MODULE_LOAD_ERROR = DiagnosticType.error(
47-
"JSC_COMMONJS_MODULE_LOAD_ERROR",
48-
"Failed to load module \"{0}\"");
49-
5046
public static final DiagnosticType UNKNOWN_REQUIRE_ENSURE =
5147
DiagnosticType.warning(
5248
"JSC_COMMONJS_UNKNOWN_REQUIRE_ENSURE_ERROR", "Unrecognized require.ensure call: {0}");
@@ -280,9 +276,16 @@ public void visit(NodeTraversal t, Node n, Node parent) {
280276
/** Visit require calls. Emit corresponding goog.require call. */
281277
private void visitRequireCall(NodeTraversal t, Node require, Node parent) {
282278
String requireName = require.getSecondChild().getString();
283-
ModulePath modulePath = t.getInput().getPath().resolveCommonJsModule(requireName);
279+
ModulePath modulePath =
280+
t.getInput()
281+
.getPath()
282+
.resolveJsModule(
283+
requireName,
284+
require.getSourceFileName(),
285+
require.getLineno(),
286+
require.getCharno());
284287
if (modulePath == null) {
285-
compiler.report(t.makeError(require, COMMON_JS_MODULE_LOAD_ERROR, requireName));
288+
// The module loader will issue an error
286289
return;
287290
}
288291

@@ -618,9 +621,16 @@ public void visit(NodeTraversal t, Node n, Node parent) {
618621
*/
619622
private void visitRequireCall(NodeTraversal t, Node require, Node parent) {
620623
String requireName = require.getSecondChild().getString();
621-
ModulePath modulePath = t.getInput().getPath().resolveCommonJsModule(requireName);
624+
ModulePath modulePath =
625+
t.getInput()
626+
.getPath()
627+
.resolveJsModule(
628+
requireName,
629+
require.getSourceFileName(),
630+
require.getLineno(),
631+
require.getCharno());
622632
if (modulePath == null) {
623-
compiler.report(t.makeError(require, COMMON_JS_MODULE_LOAD_ERROR, requireName));
633+
// The module loader will issue an error
624634
return;
625635
}
626636

@@ -1099,7 +1109,11 @@ private String getModuleImportName(NodeTraversal t, Node n) {
10991109
&& rValue.getSecondChild().isString()
11001110
&& t.getScope().getVar(rValue.getFirstChild().getQualifiedName()) == null) {
11011111
String requireName = rValue.getSecondChild().getString();
1102-
ModulePath modulePath = t.getInput().getPath().resolveCommonJsModule(requireName);
1112+
ModulePath modulePath =
1113+
t.getInput()
1114+
.getPath()
1115+
.resolveJsModule(
1116+
requireName, n.getSourceFileName(), n.getLineno(), n.getCharno());
11031117
if (modulePath == null) {
11041118
return null;
11051119
}
@@ -1136,9 +1150,16 @@ private void fixTypeNode(NodeTraversal t, Node typeNode) {
11361150
}
11371151

11381152
String moduleName = name.substring(0, endIndex);
1139-
ModulePath modulePath = t.getInput().getPath().resolveCommonJsModule(moduleName);
1153+
ModulePath modulePath =
1154+
t.getInput()
1155+
.getPath()
1156+
.resolveJsModule(
1157+
moduleName,
1158+
typeNode.getSourceFileName(),
1159+
typeNode.getLineno(),
1160+
typeNode.getCharno());
11401161
if (modulePath == null) {
1141-
t.makeError(typeNode, COMMON_JS_MODULE_LOAD_ERROR, moduleName);
1162+
// The module loader will issue an error
11421163
return;
11431164
}
11441165

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

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,21 @@ private void visitImport(NodeTraversal t, Node importDecl, Node parent) {
165165
// These are rewritten to plain namespace object accesses.
166166
moduleName = importName.substring("goog:".length());
167167
} else {
168-
moduleName = t.getInput().getPath().resolveEs6Module(importName).toModuleName();
168+
ModuleLoader.ModulePath modulePath =
169+
t.getInput()
170+
.getPath()
171+
.resolveJsModule(
172+
importName,
173+
importDecl.getSourceFileName(),
174+
importDecl.getLineno(),
175+
importDecl.getCharno());
176+
if (modulePath == null) {
177+
// The module loader issues an error
178+
// Fall back to assuming the module is a file path
179+
modulePath = t.getInput().getPath().resolveModuleAsPath(importName);
180+
}
181+
182+
moduleName = modulePath.toModuleName();
169183
}
170184

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

273-
String moduleName =
274-
t.getInput().getPath().resolveEs6Module(moduleIdentifier.getString()).toModuleName();
287+
ModuleLoader.ModulePath path =
288+
t.getInput()
289+
.getPath()
290+
.resolveJsModule(
291+
moduleIdentifier.getString(),
292+
export.getSourceFileName(),
293+
export.getLineno(),
294+
export.getCharno());
295+
if (path == null) {
296+
path = t.getInput().getPath().resolveModuleAsPath(moduleIdentifier.getString());
297+
}
298+
String moduleName = path.toModuleName();
275299

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

541565
String moduleName = name.substring(0, endIndex);
542-
String globalModuleName =
543-
t.getInput().getPath().resolveEs6Module(moduleName).toModuleName();
566+
ModuleLoader.ModulePath path =
567+
t.getInput()
568+
.getPath()
569+
.resolveJsModule(
570+
moduleName,
571+
typeNode.getSourceFileName(),
572+
typeNode.getLineno(),
573+
typeNode.getCharno());
574+
575+
if (path == null) {
576+
path = t.getInput().getPath().resolveModuleAsPath(moduleName);
577+
}
578+
String globalModuleName = path.toModuleName();
579+
544580
typeNode.setString(
545581
localTypeName == null ? globalModuleName : globalModuleName + localTypeName);
546582
} else {

src/com/google/javascript/jscomp/deps/JsFileParser.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,11 @@ protected boolean parseLine(String line) throws ParseException {
281281
if (arg.startsWith("goog:")) {
282282
requires.add(arg.substring(5)); // cut off the "goog:" prefix
283283
} else {
284-
requires.add(file.resolveEs6Module(arg).toModuleName());
284+
ModuleLoader.ModulePath path = file.resolveJsModule(arg);
285+
if (path == null) {
286+
path = file.resolveModuleAsPath(arg);
287+
}
288+
requires.add(path.toModuleName());
285289
}
286290
}
287291
}

0 commit comments

Comments
 (0)