From 77e6bf0700b00342be38221299332e8644317ad6 Mon Sep 17 00:00:00 2001 From: userquin Date: Sat, 23 Mar 2024 21:23:25 +0100 Subject: [PATCH] chore: add custom sw logic + example --- build.config.ts | 5 +- examples/pwa-assets/package.json | 2 +- examples/pwa-assets/vite.config.ts | 1 - examples/pwa-simple-sw/app/entry.client.tsx | 22 +++ examples/pwa-simple-sw/app/entry.server.tsx | 137 ++++++++++++++++++ examples/pwa-simple-sw/app/plain-sw.ts | 27 ++++ examples/pwa-simple-sw/app/pwa.ts | 13 ++ examples/pwa-simple-sw/app/root.tsx | 51 +++++++ examples/pwa-simple-sw/app/routes/_index.tsx | 17 +++ examples/pwa-simple-sw/app/sw.ts | 10 ++ examples/pwa-simple-sw/app/vite-env.d.ts | 13 ++ examples/pwa-simple-sw/package.json | 52 +++++++ .../public/apple-touch-icon-180x180.png | Bin 0 -> 739 bytes examples/pwa-simple-sw/public/favicon.ico | Bin 0 -> 608 bytes examples/pwa-simple-sw/public/favicon.svg | 1 + .../public/maskable-icon-512x512.png | Bin 0 -> 2012 bytes examples/pwa-simple-sw/public/pwa-192x192.png | Bin 0 -> 997 bytes examples/pwa-simple-sw/public/pwa-512x512.png | Bin 0 -> 2417 bytes examples/pwa-simple-sw/public/pwa-64x64.png | Bin 0 -> 476 bytes examples/pwa-simple-sw/public/robots.txt | 2 + examples/pwa-simple-sw/tsconfig.json | 38 +++++ examples/pwa-simple-sw/vite.config.ts | 91 ++++++++++++ package.json | 16 +- pnpm-lock.yaml | 108 +++++++++++--- remix-routes.d.ts | 7 - remix-sw.d.ts | 14 ++ src/config.ts | 52 ++++++- src/context.ts | 20 ++- src/index.ts | 10 ++ src/plugins/index.ts | 2 + src/plugins/preset.ts | 1 + src/plugins/routes.ts | 22 --- src/plugins/sw.ts | 37 +++++ src/sw/index.ts | 81 +++++++++++ src/sw/offline.ts | 21 --- src/types.ts | 34 ++++- tsconfig.json | 2 +- 37 files changed, 817 insertions(+), 92 deletions(-) create mode 100644 examples/pwa-simple-sw/app/entry.client.tsx create mode 100644 examples/pwa-simple-sw/app/entry.server.tsx create mode 100644 examples/pwa-simple-sw/app/plain-sw.ts create mode 100644 examples/pwa-simple-sw/app/pwa.ts create mode 100644 examples/pwa-simple-sw/app/root.tsx create mode 100644 examples/pwa-simple-sw/app/routes/_index.tsx create mode 100644 examples/pwa-simple-sw/app/sw.ts create mode 100644 examples/pwa-simple-sw/app/vite-env.d.ts create mode 100644 examples/pwa-simple-sw/package.json create mode 100644 examples/pwa-simple-sw/public/apple-touch-icon-180x180.png create mode 100644 examples/pwa-simple-sw/public/favicon.ico create mode 100644 examples/pwa-simple-sw/public/favicon.svg create mode 100644 examples/pwa-simple-sw/public/maskable-icon-512x512.png create mode 100644 examples/pwa-simple-sw/public/pwa-192x192.png create mode 100644 examples/pwa-simple-sw/public/pwa-512x512.png create mode 100644 examples/pwa-simple-sw/public/pwa-64x64.png create mode 100644 examples/pwa-simple-sw/public/robots.txt create mode 100644 examples/pwa-simple-sw/tsconfig.json create mode 100644 examples/pwa-simple-sw/vite.config.ts delete mode 100644 remix-routes.d.ts create mode 100644 remix-sw.d.ts delete mode 100644 src/plugins/routes.ts create mode 100644 src/plugins/sw.ts create mode 100644 src/sw/index.ts delete mode 100644 src/sw/offline.ts diff --git a/build.config.ts b/build.config.ts index 2b147ef..b78c4a4 100644 --- a/build.config.ts +++ b/build.config.ts @@ -21,7 +21,7 @@ export default defineBuildConfig([{ }, { entries: [ { input: 'src/components/index' }, - { input: 'src/sw/offline' }, + { input: 'src/sw/index' }, ], clean: false, declaration: true, @@ -31,7 +31,8 @@ export default defineBuildConfig([{ 'react-dom', 'virtual:pwa-info', 'virtual:pwa-assets/head', - 'virtual:vite-pwa/remix/routes', + 'virtual:vite-pwa/remix/sw', + 'workbox-core', 'workbox-precaching', 'workbox-routing', ], diff --git a/examples/pwa-assets/package.json b/examples/pwa-assets/package.json index c0413c7..9b1597f 100644 --- a/examples/pwa-assets/package.json +++ b/examples/pwa-assets/package.json @@ -36,7 +36,7 @@ "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "typescript": "^5.4.3", - "vite": "^5.2.3", + "vite": "^5.2.4", "vite-tsconfig-paths": "^4.2.1" }, "engines": { diff --git a/examples/pwa-assets/vite.config.ts b/examples/pwa-assets/vite.config.ts index 82933be..0ba4a29 100644 --- a/examples/pwa-assets/vite.config.ts +++ b/examples/pwa-assets/vite.config.ts @@ -17,7 +17,6 @@ export default defineConfig({ tsconfigPaths(), RemixVitePWAPlugin({ mode: 'development', - srcDir: 'app', base: '/', registerType: 'autoUpdate', manifest: { diff --git a/examples/pwa-simple-sw/app/entry.client.tsx b/examples/pwa-simple-sw/app/entry.client.tsx new file mode 100644 index 0000000..9a5cf6c --- /dev/null +++ b/examples/pwa-simple-sw/app/entry.client.tsx @@ -0,0 +1,22 @@ +/** + * By default, Remix will handle hydrating your app on the client for you. + * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ + * For more information, see https://remix.run/file-conventions/entry.client + */ + +import { RemixBrowser } from '@remix-run/react' +import { StrictMode, startTransition } from 'react' +import { hydrateRoot } from 'react-dom/client' + +// something weird with import.meta.env with remix +if (import.meta.env.VITE_PUBLIC_VIRTUAL_PWA_MODULE === 'true') + import('./pwa') + +startTransition(() => { + hydrateRoot( + document, + + + , + ) +}) diff --git a/examples/pwa-simple-sw/app/entry.server.tsx b/examples/pwa-simple-sw/app/entry.server.tsx new file mode 100644 index 0000000..0c71ac7 --- /dev/null +++ b/examples/pwa-simple-sw/app/entry.server.tsx @@ -0,0 +1,137 @@ +/** + * By default, Remix will handle generating the HTTP Response for you. + * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ + * For more information, see https://remix.run/file-conventions/entry.server + */ + +import { PassThrough } from 'node:stream' + +import type { AppLoadContext, EntryContext } from '@remix-run/node' +import { createReadableStreamFromReadable } from '@remix-run/node' +import { RemixServer } from '@remix-run/react' +import { isbot } from 'isbot' +import { renderToPipeableStream } from 'react-dom/server' + +const ABORT_DELAY = 5_000 + +export default function handleRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext, + // This is ignored so we can keep it in the template for visibility. Feel + // free to delete this parameter in your app if you're not using it! + _loadContext: AppLoadContext, +) { + return isbot(request.headers.get('user-agent') || '') + ? handleBotRequest( + request, + responseStatusCode, + responseHeaders, + remixContext, + ) + : handleBrowserRequest( + request, + responseStatusCode, + responseHeaders, + remixContext, + ) +} + +function handleBotRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext, +) { + return new Promise((resolve, reject) => { + let shellRendered = false + const { pipe, abort } = renderToPipeableStream( + , + { + onAllReady() { + shellRendered = true + const body = new PassThrough() + const stream = createReadableStreamFromReadable(body) + + responseHeaders.set('Content-Type', 'text/html') + + resolve( + new Response(stream, { + headers: responseHeaders, + status: responseStatusCode, + }), + ) + + pipe(body) + }, + onShellError(error: unknown) { + reject(error) + }, + onError(error: unknown) { + responseStatusCode = 500 + // Log streaming rendering errors from inside the shell. Don't log + // errors encountered during initial shell rendering since they'll + // reject and get logged in handleDocumentRequest. + if (shellRendered) + console.error(error) + }, + }, + ) + + setTimeout(abort, ABORT_DELAY) + }) +} + +function handleBrowserRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext, +) { + return new Promise((resolve, reject) => { + let shellRendered = false + const { pipe, abort } = renderToPipeableStream( + , + { + onShellReady() { + shellRendered = true + const body = new PassThrough() + const stream = createReadableStreamFromReadable(body) + + responseHeaders.set('Content-Type', 'text/html') + + resolve( + new Response(stream, { + headers: responseHeaders, + status: responseStatusCode, + }), + ) + + pipe(body) + }, + onShellError(error: unknown) { + reject(error) + }, + onError(error: unknown) { + responseStatusCode = 500 + // Log streaming rendering errors from inside the shell. Don't log + // errors encountered during initial shell rendering since they'll + // reject and get logged in handleDocumentRequest. + if (shellRendered) + console.error(error) + }, + }, + ) + + setTimeout(abort, ABORT_DELAY) + }) +} diff --git a/examples/pwa-simple-sw/app/plain-sw.ts b/examples/pwa-simple-sw/app/plain-sw.ts new file mode 100644 index 0000000..c3f9e45 --- /dev/null +++ b/examples/pwa-simple-sw/app/plain-sw.ts @@ -0,0 +1,27 @@ +/// +/// +import { cleanupOutdatedCaches, createHandlerBoundToURL, precacheAndRoute } from 'workbox-precaching' +import { clientsClaim } from 'workbox-core' +import { NavigationRoute, registerRoute } from 'workbox-routing' + +declare let self: ServiceWorkerGlobalScope + +// self.__WB_MANIFEST is default injection point +precacheAndRoute(self.__WB_MANIFEST) + +// clean old assets +cleanupOutdatedCaches() + +let allowlist: undefined | RegExp[] + +if (import.meta.env.DEV) + allowlist = [/^\/$/] + +// to allow work offline +registerRoute(new NavigationRoute( + createHandlerBoundToURL('/'), + { allowlist }, +)) + +self.skipWaiting() +clientsClaim() diff --git a/examples/pwa-simple-sw/app/pwa.ts b/examples/pwa-simple-sw/app/pwa.ts new file mode 100644 index 0000000..8077223 --- /dev/null +++ b/examples/pwa-simple-sw/app/pwa.ts @@ -0,0 +1,13 @@ +import { registerSW } from 'virtual:pwa-register' + +// if (window.ENV.VITE_VIRTUAL_PWA_MODULE) { +registerSW({ + immediate: true, + onRegisteredSW(swScriptUrl) { + console.log('SW registered: ', swScriptUrl) + }, + onOfflineReady() { + console.log('PWA application ready to work offline') + }, +}) +// } diff --git a/examples/pwa-simple-sw/app/root.tsx b/examples/pwa-simple-sw/app/root.tsx new file mode 100644 index 0000000..0b78e9d --- /dev/null +++ b/examples/pwa-simple-sw/app/root.tsx @@ -0,0 +1,51 @@ +import { + Links, + Meta, + Outlet, + Scripts, + ScrollRestoration, +} from '@remix-run/react' +import { PWAManifest } from '@vite-pwa/remix/components' + +// export async function loader() { +// return json({ +// ENV: { +// VITE_VIRTUAL_PWA_MODULE: process.env.VITE_VIRTUAL_PWA_MODULE, +// }, +// }) +// } +// +export function Layout({ children }: { children: React.ReactNode }) { + // const data = useLoaderData() + return ( + + + + + + + + + + + + + + {children} + + {/*