Last active
December 3, 2023 16:47
-
-
Save PatrickJS/b2cb051c68441e6b31016a45ce102ef5 to your computer and use it in GitHub Desktop.
qwik pwa service-worker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { defineConfig} from '@vite-pwa/assets-generator/config' | |
| import type { Preset } from '@vite-pwa/assets-generator/config'; | |
| export const minimalPreset: Preset = { | |
| transparent: { | |
| sizes: [64, 144, 192, 512], | |
| favicons: [[64, 'favicon.ico']] | |
| }, | |
| maskable: { | |
| sizes: [512] | |
| }, | |
| apple: { | |
| sizes: [180] | |
| } | |
| } | |
| export default defineConfig({ | |
| preset: minimalPreset, | |
| images: ['public/favicon.svg'] | |
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * WHAT IS THIS FILE? | |
| * | |
| * The service-worker.ts file is used to have state of the art prefetching. | |
| * https://qwik.builder.io/qwikcity/prefetching/overview/ | |
| * | |
| * Qwik uses a service worker to speed up your site and reduce latency, ie, not used in the traditional way of offline. | |
| * You can also use this file to add more functionality that runs in the service worker. | |
| */ | |
| import { setupServiceWorker } from "@builder.io/qwik-city/service-worker"; | |
| import type { PrecacheEntry } from "workbox-precaching"; | |
| import { | |
| cleanupOutdatedCaches, | |
| createHandlerBoundToURL, | |
| precacheAndRoute, | |
| } from "workbox-precaching"; | |
| import { NavigationRoute, registerRoute } from "workbox-routing"; | |
| const assets = [...publicDirAssets, ...emittedAssets]; | |
| cleanupOutdatedCaches(); | |
| precacheAndRoute( | |
| urlsToEntries([...routes.map((r) => r.pathname), ...assets], manifestHash) | |
| ); | |
| // should be registered after precacheAndRoute | |
| for (const route of routes) { | |
| registerRoute( | |
| new NavigationRoute(createHandlerBoundToURL(route.pathname), { | |
| allowlist: [route.pattern], | |
| }) | |
| ); | |
| } | |
| setupServiceWorker(); | |
| addEventListener("install", () => self.skipWaiting()); | |
| addEventListener("activate", () => self.clients.claim()); | |
| const base = "/build/"; // temp, it should be dynamic based on the build | |
| const qprefetchEvent = new MessageEvent<ServiceWorkerMessage>("message", { | |
| data: { | |
| type: "qprefetch", | |
| base, | |
| links: routes.map((route) => route.pathname), | |
| bundles: appBundles.map((appBundle) => appBundle[0]), | |
| }, | |
| }); | |
| self.dispatchEvent(qprefetchEvent); | |
| function urlsToEntries(urls: string[], hash: string): PrecacheEntry[] { | |
| const matcher = /^build\/q-([a-f0-9]{8})\./; | |
| return urls.map((url) => { | |
| // we should think about enabling this https://github.com/GoogleChrome/workbox/issues/2024 | |
| // revision: hash | |
| const match = url.match(matcher); | |
| return { | |
| url, | |
| revision: `${match ? match[1] : hash}`, | |
| }; | |
| }); | |
| } | |
| declare const self: ServiceWorkerGlobalScope; | |
| export type AppSymbols = Map<string, string>; | |
| export type AppBundle = | |
| | [bundleName: string, importedBundleIds: number[]] | |
| | [ | |
| bundleName: string, | |
| importedBundleIds: number[], | |
| symbolHashesInBundle: string[], | |
| ]; | |
| export type LinkBundle = [routePattern: RegExp, bundleIds: number[]]; | |
| export interface QPrefetchData { | |
| links?: string[]; | |
| bundles?: string[]; | |
| symbols?: string[]; | |
| } | |
| export interface QPrefetchMessage extends QPrefetchData { | |
| type: "qprefetch"; | |
| base: string; | |
| } | |
| export type ServiceWorkerMessage = QPrefetchMessage; | |
| export interface ServiceWorkerMessageEvent { | |
| data: ServiceWorkerMessage; | |
| } | |
| declare const appBundles: AppBundle[]; | |
| declare const libraryBundleIds: number[]; | |
| declare const linkBundles: LinkBundle[]; | |
| declare const publicDirAssets: string[]; | |
| declare const emittedAssets: string[]; | |
| declare const routes: { pathname: string; pattern: RegExp }[]; | |
| declare const manifestHash: string; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { routes } from '@qwik-city-plan'; | |
| import { PluginOption, defineConfig, Plugin, } from "vite"; | |
| import { QwikBuildTarget, QwikVitePlugin, qwikVite } from "@builder.io/qwik/optimizer"; | |
| import { QwikCityPlugin, qwikCity } from "@builder.io/qwik-city/vite"; | |
| import tsconfigPaths from "vite-tsconfig-paths"; | |
| import path from "node:path"; | |
| import fs from "node:fs/promises"; | |
| import fg from 'fast-glob' | |
| export default defineConfig(() => { | |
| return { | |
| plugins: [ | |
| qwikCity(), qwikVite(), tsconfigPaths(), | |
| qwikPwa() | |
| // VitePWA({ | |
| // strategies: 'injectManifest', | |
| // srcDir: 'src', | |
| // filename: 'routes/service-worker.ts' | |
| // }), | |
| ], | |
| preview: { | |
| headers: { | |
| "Cache-Control": "public, max-age=600", | |
| }, | |
| }, | |
| }; | |
| }); | |
| let tempGenerateFunc: NonNullable<Plugin['generateBundle']> = (() => {}) satisfies Plugin['generateBundle'] | |
| type OutputBundle = Parameters<typeof tempGenerateFunc>[1] | |
| export function qwikPwa(): PluginOption { | |
| let qwikPlugin: QwikVitePlugin | null = null; | |
| let qwikCityPlugin: QwikCityPlugin | null = null; | |
| let rootDir: string | null = null | |
| let outDir: string | null = null; | |
| let publicDir: string | null = null; | |
| let target: QwikBuildTarget | null = null; | |
| // make the type an argument of the generateBundle function | |
| let bundle: OutputBundle | |
| let clientOutDir: string | |
| let basePathRelDir: string | |
| let clientOutBaseDir: string | |
| let swClientDistPath: string | |
| return [ | |
| { | |
| name: 'qwik-pwa-mutual', | |
| enforce: 'post', | |
| configResolved(config) { | |
| rootDir = path.resolve(config.root); | |
| qwikPlugin = config.plugins.find((p) => p.name === 'vite-plugin-qwik') as QwikVitePlugin; | |
| qwikCityPlugin = config.plugins.find( | |
| (p) => p.name === 'vite-plugin-qwik-city' | |
| ) as QwikCityPlugin; | |
| target = qwikPlugin!.api.getOptions().target | |
| publicDir = config.publicDir; | |
| outDir = config.build?.outDir; | |
| clientOutDir = qwikPlugin!.api.getClientOutDir()!; | |
| basePathRelDir = qwikCityPlugin!.api.getBasePathname().replace(/^\/|\/$/, ''); | |
| clientOutBaseDir = path.join(clientOutDir, basePathRelDir); | |
| swClientDistPath = path.join(clientOutBaseDir, 'service-worker.js'); | |
| }, | |
| }, | |
| { | |
| name: 'qwik-pwa', | |
| enforce: 'post', | |
| generateBundle(_, _bundle) { | |
| bundle = _bundle | |
| }, | |
| closeBundle: { | |
| sequential: true, | |
| order: 'post', | |
| async handler() { | |
| if (target !== 'client') { | |
| return | |
| } | |
| const publicDirAssets = await fg.glob('**/*' , {cwd: publicDir!}) | |
| // the q-*.js files are going to be handled by qwik itself | |
| const emittedAssets = Object.keys(bundle).filter((key) => !/.*q-.*\.js$/.test(key)) | |
| const routes = qwikCityPlugin!.api.getRoutes() | |
| console.log(qwikCityPlugin!.api.getRoutes()) | |
| const swCode = await fs.readFile(swClientDistPath, 'utf-8'); | |
| const swCodeUpdate = ` | |
| const publicDirAssets = ${JSON.stringify(publicDirAssets)}; | |
| const emittedAssets = ${JSON.stringify(emittedAssets)}; | |
| const routes = [${routes.map(route => `{ pathname: ${JSON.stringify(route.pathname)}, pattern: new RegExp(${JSON.stringify(route.pattern.source)}) }`).join(',\n')}]; | |
| ${swCode} | |
| ` | |
| await fs.writeFile(swClientDistPath, swCodeUpdate); | |
| } | |
| } | |
| }, { | |
| name: 'qwik-pwa-ssr', | |
| enforce: 'post', | |
| closeBundle: { | |
| sequential: true, | |
| order: 'post', | |
| async handler() { | |
| if (target !== 'ssr') { | |
| return | |
| } | |
| const swCode = await fs.readFile(swClientDistPath, 'utf-8'); | |
| const manifest = qwikPlugin!.api.getManifest() | |
| const swCodeUpdate = ` | |
| const manifestHash = ${JSON.stringify(manifest?.manifestHash)}; | |
| ${swCode} | |
| ` | |
| await fs.writeFile(swClientDistPath, swCodeUpdate); | |
| } | |
| } | |
| } | |
| ] | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment