chore(pwa): migrate from next-pwa to serwist (#2762)

This commit is contained in:
Huang Xin
2025-12-22 23:10:09 +08:00
committed by GitHub
parent a460e609fa
commit 7db1bc460d
5 changed files with 217 additions and 2395 deletions
+8 -83
View File
@@ -1,4 +1,4 @@
import withPWAInit from '@ducanh2912/next-pwa';
import withSerwistInit from '@serwist/next';
import withBundleAnalyzer from '@next/bundle-analyzer';
const isDev = process.env['NODE_ENV'] === 'development';
@@ -33,7 +33,6 @@ const nextConfig = {
'i18next-browser-languagedetector',
'react-i18next',
'i18next',
'@ducanh2912/next-pwa',
'@tauri-apps',
'highlight.js',
'foliate-js',
@@ -68,88 +67,14 @@ const pwaDisabled = isDev || appPlatform !== 'web';
const withPWA = pwaDisabled
? (config) => config
: withPWAInit({
dest: 'public',
cacheStartUrl: false,
dynamicStartUrl: false,
cacheOnFrontEndNav: true,
aggressiveFrontEndNavCaching: true,
: withSerwistInit({
swSrc: 'src/sw.ts',
swDest: 'public/sw.js',
cacheOnNavigation: true,
reloadOnOnline: true,
swcMinify: true,
fallbacks: {
document: '/offline',
},
workboxOptions: {
disableDevLogs: true,
runtimeCaching: [
{
urlPattern: ({ url, request }) => {
const clientRoutes = ['/library', '/reader'];
const isClientRoute = clientRoutes.some((route) => url.pathname.startsWith(route));
return isClientRoute && request.mode === 'navigate';
},
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'pages-cache',
expiration: {
maxAgeSeconds: 365 * 24 * 60 * 60,
},
cacheableResponse: {
statuses: [0, 200],
},
plugins: [
{
cacheKeyWillBeUsed: async ({ request }) => {
const url = new URL(request.url);
const basePath = url.pathname.split('/')[1];
const cacheKey = `${url.origin}/${basePath}`;
return cacheKey;
},
},
],
},
},
{
urlPattern: ({ url }) => {
if (url.pathname.startsWith('/api/')) {
return false;
}
return /^https?.*/.test(url.href);
},
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'offlineCache',
expiration: {
maxEntries: 512,
maxAgeSeconds: 365 * 24 * 60 * 60,
},
cacheableResponse: {
statuses: [0, 200],
},
},
},
],
cleanupOutdatedCaches: true,
clientsClaim: true,
skipWaiting: true,
manifestTransforms: [
(manifestEntries) => {
const manifest = manifestEntries.filter((entry) => {
const url = entry.url;
return (
!url.includes('dynamic-css-manifest.json') &&
!url.includes('middleware-manifest.json') &&
!url.includes('react-loadable-manifest.json') &&
!url.includes('build-manifest.json') &&
!url.includes('_buildManifest.js') &&
!url.includes('_ssgManifest.js') &&
!url.includes('_headers')
);
});
return { manifest };
},
],
},
disable: false,
register: true,
scope: '/',
});
const withAnalyzer = withBundleAnalyzer({
+2 -1
View File
@@ -53,9 +53,9 @@
"dependencies": {
"@aws-sdk/client-s3": "^3.735.0",
"@aws-sdk/s3-request-presigner": "^3.735.0",
"@ducanh2912/next-pwa": "^10.2.9",
"@fabianlars/tauri-plugin-oauth": "2",
"@opennextjs/cloudflare": "^1.13.1",
"@serwist/next": "^9.3.0",
"@stripe/react-stripe-js": "^3.7.0",
"@stripe/stripe-js": "^7.4.0",
"@supabase/auth-ui-react": "^0.4.7",
@@ -154,6 +154,7 @@
"postcss-cli": "^11.0.0",
"postcss-nested": "^7.0.2",
"raw-loader": "^4.0.2",
"serwist": "^9.3.0",
"tailwindcss": "^3.4.18",
"typescript": "^5.7.2",
"vite-tsconfig-paths": "^5.1.4",
+76
View File
@@ -0,0 +1,76 @@
import type { PrecacheEntry, SerwistGlobalConfig } from 'serwist';
import { StaleWhileRevalidate, ExpirationPlugin, Serwist } from 'serwist';
declare global {
interface WorkerGlobalScope extends SerwistGlobalConfig {
__SW_MANIFEST: (PrecacheEntry | string)[] | undefined;
}
}
declare const self: ServiceWorkerGlobalScope;
const serwist = new Serwist({
precacheEntries: self.__SW_MANIFEST,
skipWaiting: true,
clientsClaim: true,
navigationPreload: true,
disableDevLogs: true,
fallbacks: {
entries: [
{
url: '/offline',
matcher({ request }) {
return request.destination === 'document';
},
},
],
},
runtimeCaching: [
{
matcher: ({ url, request }) => {
const clientRoutes = ['/library', '/reader'];
const isClientRoute = clientRoutes.some((route) => url.pathname.startsWith(route));
return isClientRoute && request.mode === 'navigate';
},
handler: new StaleWhileRevalidate({
cacheName: 'client-pages',
matchOptions: {
ignoreSearch: true,
},
plugins: [
new ExpirationPlugin({
maxEntries: 128,
maxAgeSeconds: 365 * 24 * 60 * 60,
}),
{
cacheKeyWillBeUsed: async ({ request }) => {
const url = new URL(request.url);
const basePath = url.pathname.split('/')[1];
const cacheKey = `${url.origin}/${basePath}`;
return cacheKey;
},
},
],
}),
},
{
matcher: ({ url }) => {
if (url.pathname.startsWith('/api/')) {
return false;
}
return /^https?.*/.test(url.href);
},
handler: new StaleWhileRevalidate({
cacheName: 'offline-cache',
plugins: [
new ExpirationPlugin({
maxEntries: 512,
maxAgeSeconds: 365 * 24 * 60 * 60,
}),
],
}),
},
],
});
serwist.addEventListeners();
+4
View File
@@ -5,8 +5,12 @@
"lib": [
"dom",
"dom.iterable",
"webworker",
"esnext"
],
"types": [
"@serwist/next/typings"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
+127 -2311
View File
File diff suppressed because it is too large Load Diff