├── .npmrc ├── src ├── global.d.ts ├── lib │ ├── types.d.ts │ ├── Header │ │ ├── svelte-logo.svg │ │ └── index.svelte │ ├── form.ts │ ├── Counter.svelte │ └── ReloadPrompt │ │ └── index.svelte ├── app.html ├── hooks.ts ├── routes │ ├── todos │ │ ├── _api.ts │ │ ├── index.ts │ │ └── index.svelte │ ├── index.svelte │ ├── about.svelte │ └── __layout.svelte ├── service-worker.ts └── app.css ├── .prettierignore ├── static ├── favicon.ico ├── robots.txt ├── pwa-192x192.png ├── pwa-512x512.png ├── apple-icon-180.png ├── svelte-welcome.png ├── svelte-welcome.webp ├── apple-splash-1125-2436.jpg ├── apple-splash-1136-640.jpg ├── apple-splash-1170-2532.jpg ├── apple-splash-1242-2208.jpg ├── apple-splash-1242-2688.jpg ├── apple-splash-1284-2778.jpg ├── apple-splash-1334-750.jpg ├── apple-splash-1536-2048.jpg ├── apple-splash-1620-2160.jpg ├── apple-splash-1668-2224.jpg ├── apple-splash-1668-2388.jpg ├── apple-splash-1792-828.jpg ├── apple-splash-2048-1536.jpg ├── apple-splash-2048-2732.jpg ├── apple-splash-2160-1620.jpg ├── apple-splash-2208-1242.jpg ├── apple-splash-2224-1668.jpg ├── apple-splash-2388-1668.jpg ├── apple-splash-2436-1125.jpg ├── apple-splash-2532-1170.jpg ├── apple-splash-2688-1242.jpg ├── apple-splash-2732-2048.jpg ├── apple-splash-2778-1284.jpg ├── apple-splash-640-1136.jpg ├── apple-splash-750-1334.jpg ├── apple-splash-828-1792.jpg └── manifest.webmanifest ├── .gitignore ├── .prettierrc ├── svelte.config.js ├── .eslintrc.cjs ├── tsconfig.json ├── package.json └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .svelte-kit/** 2 | static/** 3 | build/** 4 | node_modules/** 5 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /static/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /static/pwa-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/pwa-192x192.png -------------------------------------------------------------------------------- /static/pwa-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/pwa-512x512.png -------------------------------------------------------------------------------- /static/apple-icon-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-icon-180.png -------------------------------------------------------------------------------- /static/svelte-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/svelte-welcome.png -------------------------------------------------------------------------------- /static/svelte-welcome.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/svelte-welcome.webp -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /.svelte-kit 4 | /build 5 | /functions 6 | # intellij stuff 7 | /.idea 8 | -------------------------------------------------------------------------------- /static/apple-splash-1125-2436.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1125-2436.jpg -------------------------------------------------------------------------------- /static/apple-splash-1136-640.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1136-640.jpg -------------------------------------------------------------------------------- /static/apple-splash-1170-2532.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1170-2532.jpg -------------------------------------------------------------------------------- /static/apple-splash-1242-2208.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1242-2208.jpg -------------------------------------------------------------------------------- /static/apple-splash-1242-2688.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1242-2688.jpg -------------------------------------------------------------------------------- /static/apple-splash-1284-2778.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1284-2778.jpg -------------------------------------------------------------------------------- /static/apple-splash-1334-750.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1334-750.jpg -------------------------------------------------------------------------------- /static/apple-splash-1536-2048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1536-2048.jpg -------------------------------------------------------------------------------- /static/apple-splash-1620-2160.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1620-2160.jpg -------------------------------------------------------------------------------- /static/apple-splash-1668-2224.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1668-2224.jpg -------------------------------------------------------------------------------- /static/apple-splash-1668-2388.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1668-2388.jpg -------------------------------------------------------------------------------- /static/apple-splash-1792-828.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-1792-828.jpg -------------------------------------------------------------------------------- /static/apple-splash-2048-1536.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-2048-1536.jpg -------------------------------------------------------------------------------- /static/apple-splash-2048-2732.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-2048-2732.jpg -------------------------------------------------------------------------------- /static/apple-splash-2160-1620.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-2160-1620.jpg -------------------------------------------------------------------------------- /static/apple-splash-2208-1242.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-2208-1242.jpg -------------------------------------------------------------------------------- /static/apple-splash-2224-1668.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-2224-1668.jpg -------------------------------------------------------------------------------- /static/apple-splash-2388-1668.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-2388-1668.jpg -------------------------------------------------------------------------------- /static/apple-splash-2436-1125.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-2436-1125.jpg -------------------------------------------------------------------------------- /static/apple-splash-2532-1170.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-2532-1170.jpg -------------------------------------------------------------------------------- /static/apple-splash-2688-1242.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-2688-1242.jpg -------------------------------------------------------------------------------- /static/apple-splash-2732-2048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-2732-2048.jpg -------------------------------------------------------------------------------- /static/apple-splash-2778-1284.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-2778-1284.jpg -------------------------------------------------------------------------------- /static/apple-splash-640-1136.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-640-1136.jpg -------------------------------------------------------------------------------- /static/apple-splash-750-1334.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-750-1334.jpg -------------------------------------------------------------------------------- /static/apple-splash-828-1792.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FunMiles/SveltekitPWA/HEAD/static/apple-splash-828-1792.jpg -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/types.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Can be made globally available by placing this 3 | * inside `global.d.ts` and removing `export` keyword 4 | */ 5 | export interface Locals { 6 | userid: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | %sveltekit.head% 10 | 11 | 12 | 13 |
%sveltekit.body%
14 | 15 | 16 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import preprocess from 'svelte-preprocess'; 2 | import adapter from '@sveltejs/adapter-auto'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://github.com/sveltejs/svelte-preprocess 7 | // for more information about preprocessors 8 | preprocess: preprocess(), 9 | 10 | kit: { 11 | adapter: adapter(), 12 | 13 | // Override http methods in the Todo forms 14 | methodOverride: { 15 | allowed: ['PATCH', 'DELETE'] 16 | } 17 | } 18 | }; 19 | 20 | export default config; 21 | -------------------------------------------------------------------------------- /static/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "short_name": "Demo PWA", 4 | "name": "Svelte-kit Demo PWA", 5 | "icons": [ 6 | { 7 | "src": "pwa-192x192.png", 8 | "sizes": "192x192", 9 | "type": "image/png" 10 | }, 11 | { 12 | "src": "pwa-512x512.png", 13 | "sizes": "512x512", 14 | "type": "image/png" 15 | } 16 | ], 17 | "start_url": "/", 18 | "scope": "/", 19 | "display": "standalone", 20 | "theme_color": "#3050f0", 21 | "background_color": "#ffffff" 22 | } -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2019 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/hooks.ts: -------------------------------------------------------------------------------- 1 | import type { Handle } from '@sveltejs/kit'; 2 | import * as cookie from 'cookie'; 3 | 4 | export const handle: Handle = async ({ event, resolve }) => { 5 | const cookies = cookie.parse(event.request.headers.get('cookie') || ''); 6 | event.locals.userid = cookies['userid'] || crypto.randomUUID(); 7 | 8 | const response = await resolve(event); 9 | 10 | if (!cookies['userid']) { 11 | // if this is the first time the user has visited this app, 12 | // set a cookie so that we recognise them when they return 13 | response.headers.set( 14 | 'set-cookie', 15 | cookie.serialize('userid', event.locals.userid, { 16 | path: '/', 17 | httpOnly: true 18 | }) 19 | ); 20 | } 21 | 22 | return response; 23 | }; 24 | -------------------------------------------------------------------------------- /src/routes/todos/_api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This module is used by the /todos endpoint to 3 | make calls to api.svelte.dev, which stores todos 4 | for each user. The leading underscore indicates that this is 5 | a private module, _not_ an endpoint — visiting /todos/_api 6 | will net you a 404 response. 7 | 8 | (The data on the todo app will expire periodically; no 9 | guarantees are made. Don't use it to organise your life.) 10 | */ 11 | 12 | const base = 'https://api.svelte.dev'; 13 | 14 | export function api(method: string, resource: string, data?: Record) { 15 | return fetch(`${base}/${resource}`, { 16 | method, 17 | headers: { 18 | 'content-type': 'application/json' 19 | }, 20 | body: data && JSON.stringify(data) 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "es2020", 5 | "lib": ["es2020", "WebWorker"], 6 | "target": "es2019", 7 | /** 8 | svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript 9 | to enforce using \`import type\` instead of \`import\` for Types. 10 | */ 11 | "importsNotUsedAsValues": "error", 12 | "isolatedModules": true, 13 | "resolveJsonModule": true, 14 | /** 15 | To have warnings/errors of the Svelte compiler at the correct position, 16 | enable source maps by default. 17 | */ 18 | "sourceMap": true, 19 | "esModuleInterop": true, 20 | "skipLibCheck": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "baseUrl": ".", 23 | "allowJs": true, 24 | "checkJs": true, 25 | "paths": { 26 | "$lib/*": ["src/lib/*"] 27 | } 28 | }, 29 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] 30 | } 31 | -------------------------------------------------------------------------------- /src/routes/index.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 10 | Home 11 | 12 | 13 |
14 |

15 |
16 | 17 | 18 | Welcome 19 | 20 |
21 | 22 | to your new
SvelteKit app 23 |

24 | 25 |

26 | try editing src/routes/index.svelte 27 |

28 | 29 | 30 |
31 | 32 | 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "~TODO~", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "dev": "svelte-kit dev", 6 | "build": "svelte-kit build", 7 | "preview": "svelte-kit preview", 8 | "lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", 9 | "format": "prettier --write --plugin-search-dir=. ." 10 | }, 11 | "devDependencies": { 12 | "@sveltejs/adapter-auto": "next", 13 | "@sveltejs/kit": "1.0.0-next.358", 14 | "@types/cookie": "^0.5.1", 15 | "@typescript-eslint/eslint-plugin": "^4.19.0", 16 | "@typescript-eslint/parser": "^4.19.0", 17 | "eslint": "^7.22.0", 18 | "eslint-config-prettier": "^8.1.0", 19 | "eslint-plugin-svelte3": "^3.2.0", 20 | "prettier": "~2.2.1", 21 | "prettier-plugin-svelte": "^2.2.0", 22 | "svelte": "^3.48.0", 23 | "svelte-preprocess": "^4.10.5", 24 | "tslib": "^2.3.1", 25 | "typescript": "^4.7.2", 26 | "workbox": "*", 27 | "workbox-routing": "^6.1.5", 28 | "workbox-strategies": "^6.1.5" 29 | }, 30 | "type": "module", 31 | "dependencies": { 32 | "@fontsource/fira-mono": "^4.2.2", 33 | "cookie": "^0.4.1", 34 | "workbox-precaching": "^6.1.5", 35 | "workbox-window": "^6.1.5" 36 | } 37 | } -------------------------------------------------------------------------------- /src/service-worker.ts: -------------------------------------------------------------------------------- 1 | import { build, files, version } from '$service-worker'; 2 | import { precacheAndRoute, precache } from 'workbox-precaching'; 3 | import { registerRoute } from 'workbox-routing'; 4 | import { StaleWhileRevalidate } from 'workbox-strategies'; 5 | 6 | declare let self: ServiceWorkerGlobalScope 7 | 8 | self.addEventListener('message', (event) => { 9 | if (event.data && event.data.type === 'SKIP_WAITING') { 10 | self.skipWaiting(); 11 | } 12 | }); 13 | 14 | 15 | precacheAndRoute([ 16 | ...build.map(f => { 17 | return { 18 | url: f, 19 | revision: null 20 | } 21 | }), 22 | ...files.map(f => { 23 | return { 24 | url: f, 25 | revision: `${version}` 26 | } 27 | }) 28 | ]); 29 | 30 | // Edit the list of routes so they get cached and routed correctly, allowing 31 | // cold start or hot reload to work offline. 32 | const skRoutes = ['/', '/about', '/todos']; 33 | 34 | precache(skRoutes.map(f => { 35 | return { 36 | url: f, 37 | revision: `${version}` 38 | } 39 | })); 40 | 41 | const matchCb = ({ url, request, event }) => { 42 | return skRoutes.some(path => url.pathname === path); 43 | }; 44 | registerRoute(matchCb, new StaleWhileRevalidate({})); 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/routes/about.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | About 19 | 20 | 21 |
22 |

About this app

23 | 24 |

25 | This is a SvelteKit app. You can make your own by typing the 26 | following into your command line and following the prompts: 27 |

28 | 29 | 30 |
npm init svelte@next
31 | 32 |

33 | The page you're looking at is purely static HTML, with no client-side interactivity needed. 34 | Because of that, we don't need to load any JavaScript. Try viewing the page's source, or opening 35 | the devtools network panel and reloading. 36 |

37 | 38 |

39 | The TODOs page illustrates SvelteKit's data loading and form handling. Try using 40 | it with JavaScript disabled! 41 |

42 |
43 | 44 | 51 | -------------------------------------------------------------------------------- /src/routes/todos/index.ts: -------------------------------------------------------------------------------- 1 | import { api } from './_api'; 2 | import type { RequestHandler } from './__types'; 3 | 4 | export const get: RequestHandler = async ({ locals }) => { 5 | // locals.userid comes from src/hooks.js 6 | const response = await api('get', `todos/${locals.userid}`); 7 | 8 | if (response.status === 404) { 9 | // user hasn't created a todo list. 10 | // start with an empty array 11 | return { 12 | body: { 13 | todos: [] 14 | } 15 | }; 16 | } 17 | 18 | if (response.status === 200) { 19 | return { 20 | body: { 21 | todos: await response.json() 22 | } 23 | }; 24 | } 25 | 26 | return { 27 | status: response.status 28 | }; 29 | }; 30 | 31 | export const post: RequestHandler = async ({ request, locals }) => { 32 | const form = await request.formData(); 33 | 34 | await api('post', `todos/${locals.userid}`, { 35 | text: form.get('text') 36 | }); 37 | 38 | return {}; 39 | }; 40 | 41 | // If the user has JavaScript disabled, the URL will change to 42 | // include the method override unless we redirect back to /todos 43 | const redirect = { 44 | status: 303, 45 | headers: { 46 | location: '/todos' 47 | } 48 | }; 49 | 50 | export const patch: RequestHandler = async ({ request, locals }) => { 51 | const form = await request.formData(); 52 | 53 | await api('patch', `todos/${locals.userid}/${form.get('uid')}`, { 54 | text: form.has('text') ? form.get('text') : undefined, 55 | done: form.has('done') ? !!form.get('done') : undefined 56 | }); 57 | 58 | return redirect; 59 | }; 60 | 61 | export const del: RequestHandler = async ({ request, locals }) => { 62 | const form = await request.formData(); 63 | 64 | await api('delete', `todos/${locals.userid}/${form.get('uid')}`); 65 | 66 | return redirect; 67 | }; 68 | -------------------------------------------------------------------------------- /src/lib/Header/svelte-logo.svg: -------------------------------------------------------------------------------- 1 | svelte-logo -------------------------------------------------------------------------------- /src/lib/form.ts: -------------------------------------------------------------------------------- 1 | import { invalidate } from '$app/navigation'; 2 | 3 | // this action (https://svelte.dev/tutorial/actions) allows us to 4 | // progressively enhance a
that already works without JS 5 | export function enhance( 6 | form: HTMLFormElement, 7 | { 8 | pending, 9 | error, 10 | result 11 | }: { 12 | pending?: ({ data, form }: { data: FormData; form: HTMLFormElement }) => void; 13 | error?: ({ 14 | data, 15 | form, 16 | response, 17 | error 18 | }: { 19 | data: FormData; 20 | form: HTMLFormElement; 21 | response: Response | null; 22 | error: Error | null; 23 | }) => void; 24 | result?: ({ 25 | data, 26 | form, 27 | response 28 | }: { 29 | data: FormData; 30 | response: Response; 31 | form: HTMLFormElement; 32 | }) => void; 33 | } = {} 34 | ) { 35 | let current_token: unknown; 36 | 37 | async function handle_submit(e: SubmitEvent) { 38 | const token = (current_token = {}); 39 | 40 | e.preventDefault(); 41 | 42 | const data = new FormData(form); 43 | 44 | if (pending) pending({ data, form }); 45 | 46 | try { 47 | const response = await fetch(form.action, { 48 | method: form.method, 49 | headers: { 50 | accept: 'application/json' 51 | }, 52 | body: data 53 | }); 54 | 55 | if (token !== current_token) return; 56 | 57 | if (response.ok) { 58 | if (result) result({ data, form, response }); 59 | 60 | const url = new URL(form.action); 61 | url.search = url.hash = ''; 62 | invalidate(url.href); 63 | } else if (error) { 64 | error({ data, form, error: null, response }); 65 | } else { 66 | console.error(await response.text()); 67 | } 68 | } catch (e: unknown) { 69 | if (error && e instanceof Error) { 70 | error({ data, form, error: e, response: null }); 71 | } else { 72 | throw e; 73 | } 74 | } 75 | } 76 | 77 | form.addEventListener('submit', handle_submit); 78 | 79 | return { 80 | destroy() { 81 | form.removeEventListener('submit', handle_submit); 82 | } 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @import '@fontsource/fira-mono'; 2 | 3 | :root { 4 | font-family: Arial, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, 5 | Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 6 | --font-mono: 'Fira Mono', monospace; 7 | --pure-white: #ffffff; 8 | --primary-color: #b9c6d2; 9 | --secondary-color: #d0dde9; 10 | --tertiary-color: #edf0f8; 11 | --accent-color: #ff3e00; 12 | --heading-color: rgba(0, 0, 0, 0.7); 13 | --text-color: #444444; 14 | --background-without-opacity: rgba(255, 255, 255, 0.7); 15 | --column-width: 42rem; 16 | --column-margin-top: 4rem; 17 | } 18 | 19 | body { 20 | font-family: var(--font-mono); 21 | min-height: 100vh; 22 | margin: 0; 23 | background-color: var(--primary-color); 24 | background: linear-gradient( 25 | 180deg, 26 | var(--primary-color) 0%, 27 | var(--secondary-color) 10.45%, 28 | var(--tertiary-color) 41.35% 29 | ); 30 | } 31 | 32 | body::before { 33 | content: ''; 34 | width: 80vw; 35 | height: 100vh; 36 | position: absolute; 37 | top: 0; 38 | left: 10vw; 39 | z-index: -1; 40 | background: radial-gradient( 41 | 50% 50% at 50% 50%, 42 | var(--pure-white) 0%, 43 | rgba(255, 255, 255, 0) 100% 44 | ); 45 | opacity: 0.05; 46 | } 47 | 48 | #svelte { 49 | min-height: 100vh; 50 | display: flex; 51 | flex-direction: column; 52 | } 53 | 54 | h1, 55 | h2, 56 | p { 57 | font-weight: 400; 58 | color: var(--heading-color); 59 | } 60 | 61 | p { 62 | line-height: 1.5; 63 | } 64 | 65 | a { 66 | color: var(--accent-color); 67 | text-decoration: none; 68 | } 69 | 70 | a:hover { 71 | text-decoration: underline; 72 | } 73 | 74 | h1 { 75 | font-size: 2rem; 76 | margin-bottom: 0 0 1em 0; 77 | text-align: center; 78 | } 79 | 80 | h2 { 81 | font-size: 1rem; 82 | } 83 | 84 | pre { 85 | font-size: 16px; 86 | font-family: var(--font-mono); 87 | background-color: rgba(255, 255, 255, 0.45); 88 | border-radius: 3px; 89 | box-shadow: 2px 2px 6px rgb(255 255 255 / 25%); 90 | padding: 0.5em; 91 | overflow-x: auto; 92 | color: var(--text-color); 93 | } 94 | 95 | input, 96 | button { 97 | font-size: inherit; 98 | font-family: inherit; 99 | } 100 | 101 | button:focus:not(:focus-visible) { 102 | outline: none; 103 | } 104 | 105 | @media (min-width: 720px) { 106 | h1 { 107 | font-size: 2.4rem; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/lib/Counter.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 |
17 | 22 | 23 |
24 |
25 | 26 | {Math.floor($displayed_count)} 27 |
28 |
29 | 30 | 35 |
36 | 37 | 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Svelte-kit Progressive Web App skeleton. 2 | 3 | Everything you need to build a Progressive Web App Svelte-kit project. 4 | 5 | ## Creating a project 6 | 7 | ```bash 8 | # Clone the project, free of git for your own project 9 | degit git@github.com:FunMiles/SveltekitPWA.git MyApp 10 | cd MyApp 11 | # Install the dependencies 12 | npm install 13 | # or use pnpm install 14 | ``` 15 | 16 | ## Developing your code 17 | 18 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 19 | 20 | ```bash 21 | npm run dev 22 | 23 | # or start the server and open the app in a new browser tab 24 | npm run dev -- --open 25 | ``` 26 | ## Testing the service-worker locally 27 | To test the service worker code and obtaining a lighthouse score, do the following operations: 28 | ```bash 29 | # build the compiled code. 30 | npm run build 31 | # run the preview server. 32 | npm run preview 33 | ``` 34 | ## Building 35 | 36 | Before creating a production version of your app, install an [adapter](https://kit.svelte.dev/docs#adapters) for your target environment. Then: 37 | 38 | ```bash 39 | npm run build 40 | ``` 41 | 42 | > You can still preview the built app with `npm run preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production. 43 | 44 | The degit code has the vercel adapter by default. If you plan to use vercel, you are set. Otherwise, replace the default with your prefered adapter. 45 | 46 | ## Basic App 47 | The git app is available as a vercel app for yours to try installing as a PWA: https://sveltekit-pwa.vercel.app/ 48 | 49 | ## Adjusting the list of routes 50 | Because of the way svelte-kit serves the various routes, to be able to cold start or force-reload the PWA while offline, the service-worker needs to pre-cache all of the routes URLs. Whenever you add or remove routes, edit service-worker.js and modify the line specifying the routes to cache with your own list of routes: 51 | ```ts 52 | // Edit the list of routes so they get cached and routed correctly, allowing 53 | // cold start or hot reload to work offline. 54 | const skRoutes = [ '/', '/about', '/todos' ]; 55 | ``` 56 | ## Manifest, icons and description meta-data 57 | Before publishing your own app, do not forget to modify the manifest file (static/manifest.webmanifest), overwriting the icon files (see https://github.com/onderceylan/pwa-asset-generator for an easy generator) and the meta description tag in src/routes/__layout.svelte -------------------------------------------------------------------------------- /src/lib/Header/index.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 |
8 | 9 | SvelteKit 10 | 11 |
12 | 13 | 30 | 31 |
32 | 33 |
34 |
35 | 36 | 125 | -------------------------------------------------------------------------------- /src/lib/ReloadPrompt/index.svelte: -------------------------------------------------------------------------------- 1 | 81 | 82 | {#if toast} 83 | 107 | {/if} 108 | 109 | 134 | -------------------------------------------------------------------------------- /src/routes/todos/index.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Todos 19 | 20 | 21 | 22 |
23 |

Todos

24 | 25 | { 31 | form.reset(); 32 | } 33 | }} 34 | > 35 | 36 | 37 | 38 | {#each todos as todo (todo.uid)} 39 |
45 |
{ 50 | todo.done = !!data.get('done'); 51 | } 52 | }} 53 | > 54 | 55 | 56 |
76 | {/each} 77 |
78 | 79 | 188 | -------------------------------------------------------------------------------- /src/routes/__layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 61 | 66 | 71 | 76 | 81 | 86 | 91 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 152 | 153 |
154 | 155 |
156 | 157 |
158 | 159 | 162 | 163 | 164 | 165 | 195 | --------------------------------------------------------------------------------