I investigated this a couple weeks ago. Here are the notes I took at the time.
When publishing to HTML5 on Windows, during the closure compiler step, the build may error with a message like this:
[haxelib.exe] java.nio.file.InvalidPathException: Illegal char <:> at index 4: file:///C:/Users/Username/AppData/Local/Stencyl/libs/haxelib/shohei909/tweenxcore/1.0.4/tweenxcore/Tools.hx
[haxelib.exe] at sun.nio.fs.WindowsPathParser.normalize(WindowsPathParser.java:182)
[haxelib.exe] at sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:153)
[haxelib.exe] at sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77)
[haxelib.exe] at sun.nio.fs.WindowsPath.parse(WindowsPath.java:94)
[haxelib.exe] at sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:255)
[haxelib.exe] at sun.nio.fs.AbstractPath.resolveSibling(AbstractPath.java:66)
[haxelib.exe] at com.google.javascript.jscomp.SourceMapResolver.getRelativePath(SourceMapResolver.java:102)
[haxelib.exe] at com.google.javascript.jscomp.Compiler.getSourceMapping(Compiler.java:2883)
[haxelib.exe] at com.google.javascript.jscomp.SourceMap.addMapping(SourceMap.java:175)
[haxelib.exe] at com.google.javascript.jscomp.CodePrinter$MappedCodePrinter.generateSourceMap(CodePrinter.java:153)
[haxelib.exe] at com.google.javascript.jscomp.CodePrinter.toSource(CodePrinter.java:849)
[haxelib.exe] at com.google.javascript.jscomp.CodePrinter.access$300(CodePrinter.java:40)
[haxelib.exe] at com.google.javascript.jscomp.CodePrinter$Builder.build(CodePrinter.java:778)
[haxelib.exe] at com.google.javascript.jscomp.Compiler.toSource(Compiler.java:2278)
[haxelib.exe] at com.google.javascript.jscomp.Compiler.lambda$toSource$11(Compiler.java:2239)
[haxelib.exe] at com.google.javascript.jscomp.CompilerExecutor.runInCompilerThread(CompilerExecutor.java:129)
[haxelib.exe] at com.google.javascript.jscomp.Compiler.runInCompilerThread(Compiler.java:826)
[haxelib.exe] at com.google.javascript.jscomp.Compiler.toSource(Compiler.java:2201)
[haxelib.exe] at com.google.javascript.jscomp.Compiler.lambda$toSource$9(Compiler.java:2158)
[haxelib.exe] at com.google.javascript.jscomp.CompilerExecutor$2.call(CompilerExecutor.java:102)
[haxelib.exe] at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[haxelib.exe] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[haxelib.exe] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[haxelib.exe] at java.lang.Thread.run(Thread.java:748)
It would appear that there are various issues with source map support with Haxe-generated JS and the closure compiler.
First, Haxe uses url-encoding for the source map path in the generated javascript file. This is correct. Closure, on the other hand, thinks that it's a file path, and uses
java.nio.file.Path to look it up relative to the js file. This causes, for example, paths with spaces in them not to work at all, since the space will be represented as
%20. This can be resolved by the following patch to closure:
--- a/src/com/google/javascript/jscomp/Compiler.java
+++ b/src/com/google/javascript/jscomp/Compiler.java
@@ -3030,6 +3030,11 @@ public class Compiler extends AbstractCompiler implements ErrorHandler, SourceFi
String resultOriginalPath = result.getOriginalFile();
final String relativePath;
+ if(resultOriginalPath.startsWith("file:///"))
+ {
+ resultOriginalPath = resultOriginalPath.substring("file:///".length());
+ }
+
// Resolving the paths to a source file is expensive, so check the cache first.
if (sourceMapOriginalPath.equals(resolvedSourceMap.originalPath)
&& resultOriginalPath.equals(resolvedSourceMap.sourceMapPath)) {
@@ -3040,7 +3045,7 @@ public class Compiler extends AbstractCompiler implements ErrorHandler, SourceFi
if (source == null && !isNullOrEmpty(resultOriginalPath)) {
source =
SourceMapResolver.getRelativePath(
- sourceMap.getOriginalPath(), result.getOriginalFile());
+ sourceMap.getOriginalPath(), resultOriginalPath);
if (source != null) {
sourceMapOriginalSources.putIfAbsent(relativePath, source);
}
Second, absolute paths using the
file:// protocol in sourcemap
sources aren't handled correctly. Closure thinks that everything either starts with a
/ and is an absolute path, or it's a relative file path. Again,
java.nio.file.Path is used, leading to possible errors like that reported by Krimm. This can be worked around with the following patch, but it's not particularly robust.
--- a/src/com/google/javascript/jscomp/Compiler.java
+++ b/src/com/google/javascript/jscomp/Compiler.java
@@ -3030,6 +3030,11 @@ public class Compiler extends AbstractCompiler implements ErrorHandler, SourceFi
String resultOriginalPath = result.getOriginalFile();
final String relativePath;
+ if(resultOriginalPath.startsWith("file:///"))
+ {
+ resultOriginalPath = resultOriginalPath.substring("file:///".length());
+ }
+
// Resolving the paths to a source file is expensive, so check the cache first.
if (sourceMapOriginalPath.equals(resolvedSourceMap.originalPath)
&& resultOriginalPath.equals(resolvedSourceMap.sourceMapPath)) {
@@ -3040,7 +3045,7 @@ public class Compiler extends AbstractCompiler implements ErrorHandler, SourceFi
if (source == null && !isNullOrEmpty(resultOriginalPath)) {
source =
SourceMapResolver.getRelativePath(
- sourceMap.getOriginalPath(), result.getOriginalFile());
+ sourceMap.getOriginalPath(), resultOriginalPath);
if (source != null) {
sourceMapOriginalSources.putIfAbsent(relativePath, source);
}
The reason I never ran into this with my test game is that my typical test game has a space in the name, so I was running into issue #1, and closure never event attempted to resolve file paths in the first place.
However, if everything was working as intended (apart from this bug), I would expect that publishing an HTML5 game would always fail, but
Krimm's later reports seem to indicate that the bugs occurrence is a bit hard to predict. I feel like there must be more bugs with closure's sourcemap processing feature, and I don't think I can afford the time to look into them.
In the end, I think I'll just remove sourcemap processing from the build in order to avoid this bug in the future.