Loads gr-app.js directly instead of gr-app.html

The change includes:
* Update PolyGerritIndexHtml.soy template to load gr-app.js instead
  of gr-app.html
* Update server.go to allow use es6-modules instead of bundled
  gr-app.js
* Add comments to gr-router.js about <gr-app>

Change-Id: I2cd443ef0c8e4304e7937f588e5215426f5d86bd
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index e461d1d..5ec063f 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -203,6 +203,8 @@
 
 // Polymer makes `app` intrinsically defined on the window by virtue of the
 // custom element having the id "app", but it is made explicit here.
+// If you move this code to other place, please update comment about
+// gr-router and gr-app in the PolyGerritIndexHtml.soy file if needed
 const app = document.querySelector('#app');
 if (!app) {
   console.log('No gr-app found (running tests)');
diff --git a/polygerrit-ui/server.go b/polygerrit-ui/server.go
index 339812b..120aff5 100644
--- a/polygerrit-ui/server.go
+++ b/polygerrit-ui/server.go
@@ -150,7 +150,28 @@
 }
 
 func getContent(normalizedContentPath string) ([]byte, error) {
-  //normalizedContentPath must always starts with '/'
+	// normalizedContentPath must always starts with '/'
+
+	// gerrit loads gr-app.js as an ordinary script, without type="module" attribute.
+	// If server.go serves this file as is, browser shows the error:
+	// Uncaught SyntaxError: Cannot use import statement outside a module
+	//
+	// To load non-bundled gr-app.js as a module, we "virtually" renames original
+	// gr-app.js to gr-app.mjs and load it with dynamic import.
+	//
+	// Another option is to patch rewriteHostPage function and add type="module" attribute
+	// to <script src=".../elements/gr-app.js"> tag, but this solution is incompatible
+	// with --dev-cdn options. If you run local gerrit instance with --dev-cdn parameter,
+	// the server.go is used as cdn and it doesn't handle host page (i.e. rewriteHostPage
+	// method is not called).
+	if normalizedContentPath == "/elements/gr-app.js" {
+		return []byte("import('./gr-app.mjs')"), nil
+	}
+
+	if normalizedContentPath == "/elements/gr-app.mjs" {
+		normalizedContentPath = "/elements/gr-app.js"
+	}
+
 	pathsToTry := []string{"app" + normalizedContentPath}
 	bowerComponentsSuffix := "/bower_components/"
 	nodeModulesPrefix := "/node_modules/"
diff --git a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index 212f504..6d0404d 100644
--- a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+++ b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -140,8 +140,6 @@
     <link rel="import" href="{$assetsPath}/{$assetsBundle}">{\n}
   {/if}
 
-  <link rel="import" href="{$staticResourcePath}/elements/gr-app.html">{\n}
-
   // Now use preloaded resources
   {if $useGoogleFonts}
     <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto+Mono:400,500,700|Roboto:400,500,700|Open+Sans:400,600,700&display=swap">{\n}
@@ -152,4 +150,8 @@
 
   <body unresolved>{\n}
   <gr-app id="app"></gr-app>{\n}
+
+  // Load gr-app.js after <gr-app ...> tag because gr-router expects that
+  // <gr-app ...> already exists in the document when script is executed.
+  <script src="{$staticResourcePath}/elements/gr-app.js"></script>{\n}
 {/template}