From 3761bdb640f1aeb9c68b3b491640eb9b0f9f9b0e Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 24 May 2020 09:36:40 +0200
Subject: [PATCH] Fix serviceworker output file and misc improvements (#11562)

* Fix serviceworker output file and misc improvements

- Fix output file location for production build
- Cache more asset types: fonts and worker variants
- Parallelize a few tasks during initalization
- Only invalidate caches starting with our prefix
- Remove public/serviceworker.js before building
- Remove font preloads, they cause strange cors issues
- Misc eslint config adjustments

* remove webpack output files on watch-frontend
---
 .eslintrc                            |  3 ++-
 Makefile                             |  7 ++---
 templates/base/head.tmpl             |  2 --
 web_src/js/features/serviceworker.js | 38 ++++++++++++++++++----------
 web_src/js/index.js                  |  2 +-
 web_src/js/serviceworker.js          |  7 +++++
 6 files changed, 38 insertions(+), 21 deletions(-)

diff --git a/.eslintrc b/.eslintrc
index 3a731cbf6b..3156f88375 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -25,7 +25,7 @@ globals:
   Tribute: false
 
 overrides:
-  - files: ["web_src/**/*.worker.js"]
+  - files: ["web_src/**/*.worker.js", "web_src/js/serviceworker.js"]
     env:
       worker: true
     rules:
@@ -58,6 +58,7 @@ rules:
   no-restricted-syntax: [0]
   no-return-await: [0]
   no-shadow: [0]
+  no-underscore-dangle: [0]
   no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, ignoreRestSiblings: true}]
   no-use-before-define: [0]
   no-var: [2]
diff --git a/Makefile b/Makefile
index 37c9a46d81..1afdcf7d3f 100644
--- a/Makefile
+++ b/Makefile
@@ -88,7 +88,7 @@ GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/integrations/migration-test,$(fi
 WEBPACK_SOURCES := $(shell find web_src/js web_src/less -type f)
 WEBPACK_CONFIGS := webpack.config.js
 WEBPACK_DEST := public/js/index.js public/css/index.css
-WEBPACK_DEST_DIRS := public/js public/css public/fonts
+WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/serviceworker.js
 
 BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
 BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
@@ -194,7 +194,7 @@ node-check:
 
 .PHONY: clean-all
 clean-all: clean
-	rm -rf $(WEBPACK_DEST_DIRS) $(FOMANTIC_DEST_DIR)
+	rm -rf $(WEBPACK_DEST_ENTRIES) $(FOMANTIC_DEST_DIR)
 
 .PHONY: clean
 clean:
@@ -295,6 +295,7 @@ lint-frontend: node_modules
 
 .PHONY: watch-frontend
 watch-frontend: node_modules
+	rm -rf $(WEBPACK_DEST_ENTRIES)
 	NODE_ENV=development npx webpack --hide-modules --display-entrypoints=false --watch --progress
 
 .PHONY: test
@@ -598,7 +599,7 @@ $(FOMANTIC_DEST): $(FOMANTIC_CONFIGS) package-lock.json | node_modules
 webpack: $(WEBPACK_DEST)
 
 $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json | node_modules
-	rm -rf $(WEBPACK_DEST_DIRS)
+	rm -rf $(WEBPACK_DEST_ENTRIES)
 	npx webpack --hide-modules --display-entrypoints=false
 	@touch $(WEBPACK_DEST)
 
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl
index a6a715531d..9ad7f8496c 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -92,8 +92,6 @@
 	<link rel="mask-icon" href="{{StaticUrlPrefix}}/img/gitea-safari.svg" color="#609926">
 	<link rel="fluid-icon" href="{{StaticUrlPrefix}}/img/gitea-lg.png" title="{{AppName}}">
 	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css">
-	<link rel="preload" as="font" href="{{StaticUrlPrefix}}/fomantic/themes/default/assets/fonts/icons.woff2" type="font/woff2" crossorigin="anonymous">
-	<link rel="preload" as="font" href="{{StaticUrlPrefix}}/fomantic/themes/default/assets/fonts/outline-icons.woff2" type="font/woff2" crossorigin="anonymous">
 {{if .RequireSimpleMDE}}
 	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css">
 {{end}}
diff --git a/web_src/js/features/serviceworker.js b/web_src/js/features/serviceworker.js
index a8fd2d41df..fa415866cd 100644
--- a/web_src/js/features/serviceworker.js
+++ b/web_src/js/features/serviceworker.js
@@ -1,16 +1,18 @@
 const {UseServiceWorker, AppSubUrl, AppVer} = window.config;
-const cacheName = 'static-cache-v2';
+const cachePrefix = 'static-cache-v'; // actual version is set in the service worker script
 
 async function unregister() {
-  for (const registration of await navigator.serviceWorker.getRegistrations()) {
-    const serviceWorker = registration.active;
-    if (!serviceWorker) continue;
-    registration.unregister();
-  }
+  const registrations = await navigator.serviceWorker.getRegistrations();
+  await Promise.all(registrations.map((registration) => {
+    return registration.active && registration.unregister();
+  }));
 }
 
 async function invalidateCache() {
-  await caches.delete(cacheName);
+  const cacheKeys = await caches.keys();
+  await Promise.all(cacheKeys.map((key) => {
+    return key.startsWith(cachePrefix) && caches.delete(key);
+  }));
 }
 
 async function checkCacheValidity() {
@@ -19,7 +21,7 @@ async function checkCacheValidity() {
 
   // invalidate cache if it belongs to a different gitea version
   if (cacheKey && storedCacheKey !== cacheKey) {
-    invalidateCache();
+    await invalidateCache();
     localStorage.setItem('staticCacheKey', cacheKey);
   }
 }
@@ -28,16 +30,24 @@ export default async function initServiceWorker() {
   if (!('serviceWorker' in navigator)) return;
 
   if (UseServiceWorker) {
-    await checkCacheValidity();
     try {
-      await navigator.serviceWorker.register(`${AppSubUrl}/serviceworker.js`);
+      // normally we'd serve the service worker as a static asset from StaticUrlPrefix but
+      // the spec strictly requires it to be same-origin so it has to be AppSubUrl to work
+      await Promise.all([
+        checkCacheValidity(),
+        navigator.serviceWorker.register(`${AppSubUrl}/serviceworker.js`),
+      ]);
     } catch (err) {
       console.error(err);
-      await invalidateCache();
-      await unregister();
+      await Promise.all([
+        invalidateCache(),
+        unregister(),
+      ]);
     }
   } else {
-    await invalidateCache();
-    await unregister();
+    await Promise.all([
+      invalidateCache(),
+      unregister(),
+    ]);
   }
 }
diff --git a/web_src/js/index.js b/web_src/js/index.js
index 58cbd42933..5c749ce4ca 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -2469,7 +2469,7 @@ $(document).ready(async () => {
     }
   });
 
-  // parallel init of lazy-loaded features
+  // parallel init of async loaded features
   await Promise.all([
     highlight(document.querySelectorAll('pre code')),
     attachTribute(document.querySelectorAll('#content, .emoji-input')),
diff --git a/web_src/js/serviceworker.js b/web_src/js/serviceworker.js
index e9dfde22f9..c96ef8bd97 100644
--- a/web_src/js/serviceworker.js
+++ b/web_src/js/serviceworker.js
@@ -3,9 +3,16 @@ import {StaleWhileRevalidate} from 'workbox-strategies';
 
 const cacheName = 'static-cache-v2';
 
+// disable workbox debug logging in development, remove when debugging the service worker
+self.__WB_DISABLE_DEV_LOGS = true;
+
+// see https://developer.mozilla.org/en-US/docs/Web/API/RequestDestination for possible values
 const cachedDestinations = new Set([
+  'font',
   'manifest',
+  'paintworklet',
   'script',
+  'sharedworker',
   'style',
   'worker',
 ]);