diff --git a/.gitignore b/.gitignore index da93220..affbdc8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /node_modules/ /public/build/ - -.DS_Store +/public/service-worker.js diff --git a/package.json b/package.json index 978b090..be5bc0c 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,11 @@ "homepage": "https://shark.tobot.dev/", "scripts": { "postinstall": "yarn simple-git-hooks", - "build": "rollup -c rollup.config.mjs", - "dev": "yarn build -w", + "build": "yarn build:game && yarn build:pwa", + "build:game": "rollup -c rollup.config.mjs", + "build:pwa": "rollup -c pwa.rollup.config.mjs", + "dev": "yarn build:game -w", + "dev:pwa": "yarn build:pwa -w", "start": "sirv public --no-clear", "preview": "yarn build && yarn start", "check": "svelte-check --tsconfig ./tsconfig.json", diff --git a/public/index.html b/public/index.html index f709553..200714e 100644 --- a/public/index.html +++ b/public/index.html @@ -36,6 +36,7 @@ + @@ -81,5 +82,21 @@ '-'` + diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..e3e53a8 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,26 @@ +{ + "name": "Shark Game", + "short_name": "Sharg", + "icons": [ + { + "src": "favicon.png", + "sizes": "16x16", + "type": "image/png" + }, + { + "src": "sharkgame.png", + "sizes": "200x200", + "type": "image/png" + }, + { + "purpose": "maskable", + "src": "maskable_icon.png", + "sizes": "32x32", + "type": "image/png" + } + ], + "start_url": "/index.html", + "display": "standalone", + "background_color": "#536892", + "theme_color": "#2e4372" +} diff --git a/public/maskable_icon.png b/public/maskable_icon.png new file mode 100644 index 0000000..098c76a Binary files /dev/null and b/public/maskable_icon.png differ diff --git a/pwa.rollup.config.mjs b/pwa.rollup.config.mjs new file mode 100644 index 0000000..27ba2a0 --- /dev/null +++ b/pwa.rollup.config.mjs @@ -0,0 +1,20 @@ +import typescript from "@rollup/plugin-typescript"; +import { terser } from "rollup-plugin-terser"; + +const production = !process.env.ROLLUP_WATCH; + +export default { + input: "src/pwa/service-worker.ts", + output: { + file: "public/service-worker.js", + preferConst: true, + strict: true, + }, + plugins: [ + typescript({ sourceMap: false, include: "**/service-worker.ts", lib: ["WebWorker"] }), + production && terser({ output: { comments: false } }), + ], + watch: { + clearScreen: false, + }, +}; diff --git a/rollup.config.mjs b/rollup.config.mjs index 78ae1c8..47523be 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -42,6 +42,7 @@ export default { externalLiveBindings: false, preferConst: true, strict: true, + compact: production, }, plugins: [ // Svelte adds "stdin" to the sourceMap @@ -79,6 +80,7 @@ export default { typescript({ sourceMap: true, inlineSources: true, + exclude: ["node_modules/*", "public/*", "**/service-worker.ts"], }), // In dev mode, call `yarn start` once diff --git a/src/pwa/service-worker.ts b/src/pwa/service-worker.ts new file mode 100644 index 0000000..42c7353 --- /dev/null +++ b/src/pwa/service-worker.ts @@ -0,0 +1,65 @@ +const CACHE_NAME = "sharg-static-cache"; +const CACHE_VERSION = "v0.0.1"; +const STATIC_CACHE_NAME = CACHE_NAME + "-" + CACHE_VERSION; + +const FILES_TO_CACHE = ["/", "/index.html", "/build/bundle.js", "/build/bundle.css", "/favicon.png"]; + +const globalScope = self as WorkerGlobalScope as ServiceWorkerGlobalScope; + +globalScope.addEventListener("install", (event) => { + console.debug("[ServiceWorker] Install"); + + event.waitUntil( + caches.open(STATIC_CACHE_NAME).then((cache) => { + console.debug("[ServiceWorker] Pre-caching offline page"); + return cache.addAll(FILES_TO_CACHE); + }) + ); +}); + +globalScope.addEventListener("activate", (evt) => { + console.log("[ServiceWorker] Activate"); + // Remove previous cached data from disk. + evt.waitUntil( + caches.keys().then((keyList) => { + return Promise.all( + keyList.map((key) => { + if (key.startsWith(CACHE_NAME) && key !== STATIC_CACHE_NAME) { + console.log("[ServiceWorker] Removing old cache", key); + return caches.delete(key); + } + }) + ); + }) + ); + + globalScope.clients.claim(); +}); + +globalScope.addEventListener("fetch", (event) => { + console.debug("[ServiceWorker] Fetch", event.request.url); + + function getFromFetch(cache: Cache) { + console.debug("getFromFetch"); + return fetch(event.request).then((response) => { + cache.put(event.request.url, response.clone()); + return response; + }); + } + + async function getFromCache() { + const cache = await caches.open(STATIC_CACHE_NAME); + const cached = await cache.match(event.request); + if (cached !== undefined) { + return cached; + } + try { + return await getFromFetch(cache); + } catch (e) { + console.error(e); + return Response.error(); + } + } + + event.respondWith(getFromCache()); +}); diff --git a/src/pwa/tsconfig.json b/src/pwa/tsconfig.json new file mode 100644 index 0000000..db1fd96 --- /dev/null +++ b/src/pwa/tsconfig.json @@ -0,0 +1,7 @@ +{ + "target": "es2021", + "include": ["service-worker.ts"], + "compilerOptions": { + "lib": ["WebWorker"] + } +} diff --git a/tsconfig.json b/tsconfig.json index f9eac3a..4e10601 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,12 +1,10 @@ { "extends": "@tsconfig/svelte/tsconfig.json", - "target": "es2021", - "include": ["src/**/*"], - "exclude": ["node_modules/*", "__sapper__/*", "public/*"], - + "exclude": ["src/service-worker.ts"], "compilerOptions": { - "strict": true + "strict": true, + "isolatedModules": false } }