@djangocfg/nextjs 2.1.516 → 2.1.519
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/index.d.mts +1 -1
- package/dist/config/index.mjs +37 -68
- package/dist/config/index.mjs.map +1 -1
- package/dist/i18n/routing.d.mts +2 -2
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +37 -68
- package/dist/index.mjs.map +1 -1
- package/dist/plugin-yhoLWYh6.d.mts +79 -0
- package/dist/pwa/index.d.mts +1 -1
- package/dist/pwa/index.mjs +31 -82
- package/dist/pwa/index.mjs.map +1 -1
- package/dist/pwa/worker/index.d.mts +17 -24
- package/dist/pwa/worker/index.mjs +25 -28
- package/dist/pwa/worker/index.mjs.map +1 -1
- package/package.json +10 -13
- package/src/config/plugins/index.ts +1 -1
- package/src/pwa/plugin.ts +69 -155
- package/src/pwa/worker/index.ts +48 -62
- package/dist/plugin-DdmK3zAE.d.mts +0 -100
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { NextConfig } from 'next';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PWA (Progressive Web App) Plugin — dependency-free.
|
|
5
|
+
*
|
|
6
|
+
* History: this used to wrap `@serwist/next`. Serwist is a **webpack** plugin,
|
|
7
|
+
* and Next.js 16 defaults to Turbopack, which does not run webpack loaders and
|
|
8
|
+
* chokes on serwist's `fileURLToPath(new URL(".", import.meta.url))` during
|
|
9
|
+
* static tracing — failing the whole build even when PWA was never enabled
|
|
10
|
+
* (the mere `require('@serwist/next')` reference was enough). Since no app in
|
|
11
|
+
* this monorepo actually turned PWA on (`withPWA` is opt-in and unused), we
|
|
12
|
+
* dropped serwist entirely.
|
|
13
|
+
*
|
|
14
|
+
* What remains:
|
|
15
|
+
* - `createManifest` (in `./manifest`) — the web app manifest, which Next.js
|
|
16
|
+
* supports natively via `app/manifest.ts`; needs no library at all.
|
|
17
|
+
* - `withPWA` — a thin, Turbopack-safe pass-through kept for API stability.
|
|
18
|
+
* - `getServiceWorkerTemplate` — a vanilla service worker you can drop into
|
|
19
|
+
* `app/sw.ts`, no framework runtime required.
|
|
20
|
+
*
|
|
21
|
+
* If you want a full precache/offline story back, wire a real service worker
|
|
22
|
+
* (the template below, or Workbox) yourself — but do it in an app, not here, so
|
|
23
|
+
* one app's PWA choice can't break every consumer's build.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
interface PWAPluginOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Path to service worker source file (relative to project root).
|
|
29
|
+
* @default 'app/sw.ts'
|
|
30
|
+
*/
|
|
31
|
+
swSrc?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Destination for the compiled service worker.
|
|
34
|
+
* @default 'public/sw.js'
|
|
35
|
+
*/
|
|
36
|
+
swDest?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Disable PWA completely.
|
|
39
|
+
* @default true in development / static export, false otherwise
|
|
40
|
+
*/
|
|
41
|
+
disable?: boolean;
|
|
42
|
+
/** Cache pages on navigation. Consumed by a custom SW, if any. @default true */
|
|
43
|
+
cacheOnNavigation?: boolean;
|
|
44
|
+
/** Reload the app when the device comes back online. @default true */
|
|
45
|
+
reloadOnOnline?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Turbopack-safe PWA pass-through.
|
|
49
|
+
*
|
|
50
|
+
* This no longer injects a build plugin — Next.js's native manifest support
|
|
51
|
+
* plus a hand-written `app/sw.ts` (see {@link getServiceWorkerTemplate}) cover
|
|
52
|
+
* the PWA surface without a webpack-only dependency. Kept as a named export so
|
|
53
|
+
* existing `withPWA(config)` / `createBaseNextConfig({ pwa })` call sites keep
|
|
54
|
+
* type-checking and behave predictably (they return the config unchanged).
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* import { createBaseNextConfig } from '@djangocfg/nextjs/config';
|
|
59
|
+
* export default createBaseNextConfig({ pwa: { swSrc: 'app/sw.ts' } });
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
declare function withPWA(nextConfig: NextConfig, _options?: PWAPluginOptions): NextConfig;
|
|
63
|
+
/**
|
|
64
|
+
* Vanilla service-worker template — no library.
|
|
65
|
+
*
|
|
66
|
+
* Returns ready-to-use, framework-free service worker source. Copy it into
|
|
67
|
+
* `app/sw.ts` (or generate it) if you want offline support. It uses only the
|
|
68
|
+
* standard Service Worker + Cache APIs, so it builds under Turbopack with no
|
|
69
|
+
* extra dependency.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { getServiceWorkerTemplate } from '@djangocfg/nextjs/config';
|
|
74
|
+
* console.log(getServiceWorkerTemplate()); // → paste into app/sw.ts
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare function getServiceWorkerTemplate(): string;
|
|
78
|
+
|
|
79
|
+
export { type PWAPluginOptions as P, getServiceWorkerTemplate as g, withPWA as w };
|
package/dist/pwa/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { IconPaths, ManifestConfig, ProtocolHandler, ScreenshotConfig, SmartScreenshotInput, createManifest, createManifestMetadata, createScreenshot, createScreenshots, createViewport, generateManifest } from './manifest.mjs';
|
|
2
|
-
export { P as PWAPluginOptions,
|
|
2
|
+
export { P as PWAPluginOptions, g as getServiceWorkerTemplate, w as withPWA } from '../plugin-yhoLWYh6.mjs';
|
|
3
3
|
import 'next';
|
package/dist/pwa/index.mjs
CHANGED
|
@@ -127,108 +127,57 @@ function generateManifest(config) {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
// src/pwa/plugin.ts
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
var requirePeer = createRequire(import.meta.url);
|
|
133
|
-
function withPWA(nextConfig, options = {}) {
|
|
134
|
-
const isDev = process.env.NODE_ENV === "development";
|
|
135
|
-
const isStaticBuild = process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
|
|
136
|
-
const shouldDisable = options.disable !== void 0 ? options.disable : isDev || isStaticBuild;
|
|
137
|
-
const defaultOptions = {
|
|
138
|
-
swSrc: "app/sw.ts",
|
|
139
|
-
swDest: "public/sw.js",
|
|
140
|
-
disable: shouldDisable,
|
|
141
|
-
cacheOnNavigation: true,
|
|
142
|
-
reloadOnOnline: true,
|
|
143
|
-
...options
|
|
144
|
-
};
|
|
145
|
-
try {
|
|
146
|
-
if (!process.env.SERWIST_SUPPRESS_TURBOPACK_WARNING) {
|
|
147
|
-
process.env.SERWIST_SUPPRESS_TURBOPACK_WARNING = "1";
|
|
148
|
-
}
|
|
149
|
-
const withSerwistInit = requirePeer("@serwist/next").default;
|
|
150
|
-
const withSerwist = withSerwistInit({
|
|
151
|
-
swSrc: defaultOptions.swSrc,
|
|
152
|
-
swDest: defaultOptions.swDest,
|
|
153
|
-
disable: defaultOptions.disable,
|
|
154
|
-
cacheOnNavigation: defaultOptions.cacheOnNavigation,
|
|
155
|
-
reloadOnOnline: defaultOptions.reloadOnOnline,
|
|
156
|
-
...defaultOptions.serwistOptions
|
|
157
|
-
});
|
|
158
|
-
return withSerwist(nextConfig);
|
|
159
|
-
} catch (error) {
|
|
160
|
-
consola.error("Failed to configure Serwist:", error);
|
|
161
|
-
return nextConfig;
|
|
162
|
-
}
|
|
130
|
+
function withPWA(nextConfig, _options = {}) {
|
|
131
|
+
return nextConfig;
|
|
163
132
|
}
|
|
164
133
|
function getServiceWorkerTemplate() {
|
|
165
134
|
return `/**
|
|
166
|
-
* Service Worker (
|
|
167
|
-
*
|
|
168
|
-
* Modern PWA service worker using Serwist
|
|
135
|
+
* Service Worker (vanilla) \u2014 standard Cache + Fetch APIs, no library.
|
|
169
136
|
*/
|
|
170
137
|
|
|
171
|
-
|
|
172
|
-
import { Serwist } from 'serwist';
|
|
173
|
-
|
|
138
|
+
/// <reference lib="webworker" />
|
|
174
139
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
175
140
|
declare const self: any;
|
|
176
141
|
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
precacheEntries: self.__SW_MANIFEST,
|
|
180
|
-
|
|
181
|
-
// Skip waiting - activate new SW immediately
|
|
182
|
-
skipWaiting: true,
|
|
183
|
-
|
|
184
|
-
// Take control of all clients immediately
|
|
185
|
-
clientsClaim: true,
|
|
142
|
+
const CACHE = 'app-cache-v1';
|
|
143
|
+
const OFFLINE_URL = '/_offline';
|
|
186
144
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
145
|
+
// Precache the offline fallback on install.
|
|
146
|
+
self.addEventListener('install', (event: any) => {
|
|
147
|
+
event.waitUntil(
|
|
148
|
+
caches.open(CACHE).then((cache: Cache) => cache.addAll([OFFLINE_URL]))
|
|
149
|
+
);
|
|
150
|
+
self.skipWaiting();
|
|
151
|
+
});
|
|
192
152
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
},
|
|
153
|
+
// Drop stale caches on activate and take control immediately.
|
|
154
|
+
self.addEventListener('activate', (event: any) => {
|
|
155
|
+
event.waitUntil(
|
|
156
|
+
caches.keys().then((keys: string[]) =>
|
|
157
|
+
Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
|
|
158
|
+
)
|
|
159
|
+
);
|
|
160
|
+
self.clients.claim();
|
|
204
161
|
});
|
|
205
162
|
|
|
206
|
-
|
|
163
|
+
// Network-first for navigations, with an offline fallback.
|
|
164
|
+
self.addEventListener('fetch', (event: any) => {
|
|
165
|
+
const { request } = event;
|
|
166
|
+
if (request.mode !== 'navigate') return;
|
|
167
|
+
event.respondWith(
|
|
168
|
+
fetch(request).catch(() =>
|
|
169
|
+
caches.match(OFFLINE_URL).then((r: Response | undefined) => r ?? Response.error())
|
|
170
|
+
)
|
|
171
|
+
);
|
|
172
|
+
});
|
|
207
173
|
`;
|
|
208
174
|
}
|
|
209
|
-
var defaultRuntimeCaching = [];
|
|
210
|
-
function createApiCacheRule() {
|
|
211
|
-
consola.warn("createApiCacheRule is deprecated with Serwist. Use defaultCache from @serwist/next/worker");
|
|
212
|
-
return {};
|
|
213
|
-
}
|
|
214
|
-
function createStaticAssetRule() {
|
|
215
|
-
consola.warn("createStaticAssetRule is deprecated with Serwist. Use defaultCache from @serwist/next/worker");
|
|
216
|
-
return {};
|
|
217
|
-
}
|
|
218
|
-
function createCdnCacheRule() {
|
|
219
|
-
consola.warn("createCdnCacheRule is deprecated with Serwist. Use defaultCache from @serwist/next/worker");
|
|
220
|
-
return {};
|
|
221
|
-
}
|
|
222
175
|
export {
|
|
223
|
-
createApiCacheRule,
|
|
224
|
-
createCdnCacheRule,
|
|
225
176
|
createManifest,
|
|
226
177
|
createManifestMetadata,
|
|
227
178
|
createScreenshot,
|
|
228
179
|
createScreenshots,
|
|
229
|
-
createStaticAssetRule,
|
|
230
180
|
createViewport,
|
|
231
|
-
defaultRuntimeCaching,
|
|
232
181
|
generateManifest,
|
|
233
182
|
getServiceWorkerTemplate,
|
|
234
183
|
withPWA
|
package/dist/pwa/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pwa/manifest.ts","../../src/pwa/plugin.ts"],"sourcesContent":["/**\n * PWA Manifest Metadata Utilities\n *\n * Helper functions for creating Next.js metadata for PWA manifest\n */\n\nimport type { Metadata, MetadataRoute, Viewport } from 'next';\n\nexport interface ManifestConfig {\n name: string;\n shortName?: string;\n description?: string;\n themeColor?: string;\n backgroundColor?: string;\n display?: 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser';\n orientation?: 'portrait' | 'landscape' | 'any';\n startUrl?: string;\n scope?: string;\n lang?: string;\n dir?: 'ltr' | 'rtl' | 'auto';\n icons?: {\n src: string;\n sizes: string;\n type?: string;\n purpose?: string;\n }[];\n}\n\n/**\n * Icon paths configuration\n */\nexport interface IconPaths {\n logo192?: string;\n logo384?: string;\n logo512?: string;\n}\n\n/**\n * Protocol handler configuration\n * Allows your PWA to register as a handler for custom protocols\n *\n * @example\n * ```typescript\n * {\n * protocol: \"web+music\",\n * url: \"/play?track=%s\"\n * }\n * ```\n */\nexport interface ProtocolHandler {\n /** Protocol scheme (e.g., \"web+music\", \"mailto\", \"magnet\") */\n protocol: string;\n /** URL template with %s placeholder for the protocol parameter */\n url: string;\n}\n\n/**\n * Create viewport configuration for Next.js app\n *\n * @example\n * ```typescript\n * export const viewport: Viewport = createViewport({\n * themeColor: '#ffffff',\n * });\n * ```\n */\nexport function createViewport(config: { themeColor?: string }): Viewport {\n return {\n width: 'device-width',\n initialScale: 1,\n maximumScale: 1,\n themeColor: config.themeColor || '#000000',\n };\n}\n\n/**\n * Create manifest metadata for Next.js app\n *\n * Note: themeColor and viewport should be exported separately using createViewport()\n *\n * @example\n * ```typescript\n * export const metadata: Metadata = {\n * ...createManifestMetadata({\n * name: 'My App',\n * shortName: 'App',\n * description: 'My awesome app',\n * }),\n * };\n *\n * export const viewport: Viewport = createViewport({\n * themeColor: '#ffffff',\n * });\n * ```\n */\nexport function createManifestMetadata(config: ManifestConfig): Metadata {\n return {\n manifest: '/manifest.json',\n appleWebApp: {\n capable: true,\n statusBarStyle: 'default',\n title: config.shortName || config.name,\n },\n applicationName: config.name,\n formatDetection: {\n telephone: false,\n },\n };\n}\n\n/**\n * Create Next.js manifest function\n *\n * Use this in your app/manifest.ts file\n *\n * @example\n * ```typescript\n * // app/manifest.ts\n * import { createManifest } from '@djangocfg/nextjs/config';\n * import { settings } from '@core/settings';\n *\n * export default createManifest({\n * name: settings.app.name,\n * description: settings.app.description,\n * icons: {\n * logo192: settings.app.icons.logo192,\n * logo384: settings.app.icons.logo384,\n * logo512: settings.app.icons.logo512,\n * },\n * });\n * ```\n */\nexport interface ScreenshotConfig {\n src: string;\n sizes: string;\n type?: string;\n form_factor?: 'narrow' | 'wide';\n label?: string;\n}\n\n/**\n * Smart screenshot configuration\n * Automatically detects everything from path or uses defaults\n */\nexport interface SmartScreenshotInput {\n src: string;\n /** Form factor (auto-detected from filename if contains 'desktop'/'mobile', or use default) */\n form_factor?: 'narrow' | 'wide';\n /** Optional label (auto-generated from form_factor) */\n label?: string;\n /** Optional width (defaults based on form_factor) */\n width?: number;\n /** Optional height (defaults based on form_factor) */\n height?: number;\n}\n\n/**\n * Create screenshot config with smart defaults\n * Automatically detects type, sizes, form_factor from path or uses sensible defaults\n *\n * @example\n * ```typescript\n * // Minimal - everything auto-detected\n * createScreenshot({ src: '/screenshots/desktop-view.png' })\n * // → form_factor: 'wide', sizes: '1920x1080', type: 'image/png', label: 'Desktop screenshot'\n *\n * createScreenshot({ src: '/screenshots/mobile.png' })\n * // → form_factor: 'narrow', sizes: '390x844', type: 'image/png', label: 'Mobile screenshot'\n *\n * // With custom dimensions\n * createScreenshot({ src: '/screenshots/tablet.png', width: 1024, height: 768 })\n * ```\n */\nexport function createScreenshot(input: SmartScreenshotInput | string): ScreenshotConfig {\n // Allow string shorthand\n const config = typeof input === 'string' ? { src: input } : input;\n let { src, width, height, label, form_factor } = config;\n\n // Auto-detect image type from extension\n const ext = src.split('.').pop()?.toLowerCase();\n const typeMap: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n };\n const type = ext ? typeMap[ext] || 'image/png' : 'image/png';\n\n // Try to parse dimensions from filename (e.g., \"1920x1080.png\" or \"screenshot-390x844.png\")\n const filename = src.toLowerCase();\n const dimensionMatch = filename.match(/(\\d{3,4})x(\\d{3,4})/);\n if (dimensionMatch && !width && !height) {\n width = parseInt(dimensionMatch[1], 10);\n height = parseInt(dimensionMatch[2], 10);\n }\n\n // Auto-detect form_factor from filename if not provided\n let autoFormFactor: 'narrow' | 'wide' = 'wide'; // Default to wide\n if (filename.includes('mobile') || filename.includes('phone') || filename.includes('narrow')) {\n autoFormFactor = 'narrow';\n } else if (filename.includes('desktop') || filename.includes('laptop') || filename.includes('wide')) {\n autoFormFactor = 'wide';\n } else if (width && height) {\n // Calculate from dimensions if provided or parsed\n const aspectRatio = width / height;\n autoFormFactor = aspectRatio > 1.2 ? 'wide' : 'narrow';\n }\n\n const finalFormFactor = form_factor || autoFormFactor;\n\n // Default dimensions based on form_factor (only if not parsed from filename)\n const defaultDimensions = finalFormFactor === 'wide'\n ? { width: 1920, height: 1080 } // Desktop default\n : { width: 390, height: 844 }; // Mobile default (iPhone 14)\n\n const finalWidth = width || defaultDimensions.width;\n const finalHeight = height || defaultDimensions.height;\n\n // Auto-generate label\n const autoLabel = finalFormFactor === 'wide'\n ? 'Desktop screenshot'\n : 'Mobile screenshot';\n\n return {\n src,\n sizes: `${finalWidth}x${finalHeight}`,\n type,\n form_factor: finalFormFactor,\n label: label || autoLabel,\n };\n}\n\n/**\n * Create multiple screenshots from array\n * Supports string shorthand or full config objects\n *\n * @example\n * ```typescript\n * // Minimal - just paths\n * createScreenshots([\n * '/screenshots/desktop.png', // Auto: wide, 1920x1080\n * '/screenshots/mobile.png', // Auto: narrow, 390x844\n * ])\n *\n * // Mixed\n * createScreenshots([\n * '/screenshots/desktop.png',\n * { src: '/screenshots/tablet.png', width: 1024, height: 768 },\n * ])\n * ```\n */\nexport function createScreenshots(inputs: Array<SmartScreenshotInput | string>): ScreenshotConfig[] {\n return inputs.map(createScreenshot);\n}\n\nexport function createManifest(config: {\n name: string;\n shortName?: string;\n description?: string;\n themeColor?: string;\n backgroundColor?: string;\n display?: 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser';\n orientation?: 'portrait' | 'landscape' | 'any';\n id?: string;\n startUrl?: string;\n scope?: string;\n lang?: string;\n dir?: 'ltr' | 'rtl' | 'auto';\n icons?: IconPaths | ManifestConfig['icons'];\n screenshots?: ScreenshotConfig[];\n protocol_handlers?: ProtocolHandler[];\n}): () => MetadataRoute.Manifest {\n return () => {\n // Convert IconPaths to manifest icons format\n let manifestIcons: MetadataRoute.Manifest['icons'];\n\n if (Array.isArray(config.icons)) {\n // Already in manifest format\n manifestIcons = config.icons as MetadataRoute.Manifest['icons'];\n } else if (config.icons) {\n // Convert IconPaths to manifest icons\n const { logo192, logo384, logo512 } = config.icons as IconPaths;\n manifestIcons = [\n ...(logo192\n ? [\n {\n src: logo192,\n sizes: '192x192',\n type: 'image/png',\n purpose: 'maskable' as const,\n },\n ]\n : []),\n ...(logo384\n ? [\n {\n src: logo384,\n sizes: '384x384',\n type: 'image/png',\n },\n ]\n : []),\n ...(logo512\n ? [\n {\n src: logo512,\n sizes: '512x512',\n type: 'image/png',\n },\n ]\n : []),\n ];\n }\n\n const manifest: MetadataRoute.Manifest = {\n name: config.name,\n short_name: config.shortName || config.name,\n description: config.description || config.name,\n id: config.id || config.startUrl || '/',\n start_url: config.startUrl || '/',\n scope: config.scope || '/',\n display: config.display || 'standalone',\n orientation: config.orientation || 'portrait',\n background_color: config.backgroundColor || '#000000',\n theme_color: config.themeColor || '#ffffff',\n lang: config.lang || 'en',\n dir: config.dir || 'ltr',\n icons: manifestIcons,\n // Removed forced gcm_sender_id to avoid potential conflicts with VAPID\n // gcm_sender_id: '103953800507',\n };\n\n // Add screenshots if provided (for Richer PWA Install UI)\n if (config.screenshots && config.screenshots.length > 0) {\n (manifest as any).screenshots = config.screenshots;\n }\n\n // Add protocol handlers if provided\n if (config.protocol_handlers && config.protocol_handlers.length > 0) {\n (manifest as any).protocol_handlers = config.protocol_handlers;\n }\n\n return manifest;\n };\n}\n\n/**\n * Generate manifest.json content (legacy)\n *\n * @deprecated Use createManifest() instead\n */\nexport function generateManifest(config: ManifestConfig): Record<string, any> {\n return createManifest(config)();\n}\n","/**\n * PWA (Progressive Web App) Plugin\n *\n * Configures Serwist for service worker and offline support\n * Modern PWA solution for Next.js 15+ with App Router\n *\n * @see https://serwist.pages.dev/\n */\n\nimport type { NextConfig } from 'next';\nimport { consola } from 'consola';\nimport { createRequire } from 'module';\n\n// Serwist is an optional peer loaded synchronously at config time, which\n// `import()` cannot do. `createRequire` is the ESM-safe equivalent of `require`.\nconst requirePeer = createRequire(import.meta.url);\n\nexport interface PWAPluginOptions {\n /**\n * Destination directory for service worker files\n * @default 'public'\n * @deprecated Use swDest instead\n */\n dest?: string;\n\n /**\n * Path to service worker source file (relative to project root)\n * @default 'app/sw.ts'\n */\n swSrc?: string;\n\n /**\n * Destination for compiled service worker\n * @default 'public/sw.js'\n */\n swDest?: string;\n\n /**\n * Disable PWA completely\n * @default false in production, true in development\n * @example disable: process.env.NODE_ENV === 'development'\n */\n disable?: boolean;\n\n /**\n * Cache on navigation - cache pages when navigating\n * @default true\n */\n cacheOnNavigation?: boolean;\n\n /**\n * Reload app when device goes back online\n * @default true\n */\n reloadOnOnline?: boolean;\n\n /**\n * Additional Serwist options\n * @see https://serwist.pages.dev/docs/next/configuring\n */\n serwistOptions?: Record<string, any>;\n}\n\n/**\n * Add PWA configuration to Next.js config using Serwist\n *\n * @example Basic usage\n * ```ts\n * import { createBaseNextConfig, withPWA } from '@djangocfg/nextjs/config';\n *\n * const nextConfig = createBaseNextConfig({...});\n *\n * export default withPWA(nextConfig, {\n * swSrc: 'app/sw.ts',\n * disable: process.env.NODE_ENV === 'development',\n * });\n * ```\n *\n * @example Integrated with createBaseNextConfig\n * ```ts\n * import { createBaseNextConfig } from '@djangocfg/nextjs/config';\n *\n * const config = createBaseNextConfig({\n * pwa: {\n * swSrc: 'app/sw.ts',\n * disable: false,\n * },\n * });\n *\n * export default config;\n * ```\n */\nexport function withPWA(\n nextConfig: NextConfig,\n options: PWAPluginOptions = {}\n): NextConfig {\n const isDev = process.env.NODE_ENV === 'development';\n const isStaticBuild = process.env.NEXT_PUBLIC_STATIC_BUILD === 'true';\n\n // Determine if PWA should be disabled:\n // - Explicitly disabled via options\n // - In development mode (default)\n // - Static build (output: 'export' doesn't support service workers)\n const shouldDisable = options.disable !== undefined\n ? options.disable\n : (isDev || isStaticBuild);\n\n const defaultOptions: PWAPluginOptions = {\n swSrc: 'app/sw.ts',\n swDest: 'public/sw.js',\n disable: shouldDisable,\n cacheOnNavigation: true,\n reloadOnOnline: true,\n ...options,\n };\n\n try {\n // Suppress Turbopack warning - it only applies to dev mode, not production builds\n // The warning is misleading when running `next build` with Turbopack\n // See: https://github.com/serwist/serwist/issues/54\n if (!process.env.SERWIST_SUPPRESS_TURBOPACK_WARNING) {\n process.env.SERWIST_SUPPRESS_TURBOPACK_WARNING = '1';\n }\n\n const withSerwistInit = requirePeer('@serwist/next').default;\n\n const withSerwist = withSerwistInit({\n swSrc: defaultOptions.swSrc,\n swDest: defaultOptions.swDest,\n disable: defaultOptions.disable,\n cacheOnNavigation: defaultOptions.cacheOnNavigation,\n reloadOnOnline: defaultOptions.reloadOnOnline,\n ...defaultOptions.serwistOptions,\n });\n\n return withSerwist(nextConfig);\n } catch (error) {\n consola.error('Failed to configure Serwist:', error);\n return nextConfig;\n }\n}\n\n/**\n * Get service worker template content\n *\n * Returns ready-to-use service worker code for app/sw.ts\n *\n * @example\n * ```ts\n * import { getServiceWorkerTemplate } from '@djangocfg/nextjs/config';\n *\n * // Copy this to your app/sw.ts file\n * console.log(getServiceWorkerTemplate());\n * ```\n */\nexport function getServiceWorkerTemplate(): string {\n return `/**\n * Service Worker (Serwist)\n *\n * Modern PWA service worker using Serwist\n */\n\nimport { defaultCache } from '@serwist/next/worker';\nimport { Serwist } from 'serwist';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ndeclare const self: any;\n\nconst serwist = new Serwist({\n // Precache entries injected by Serwist build plugin\n precacheEntries: self.__SW_MANIFEST,\n\n // Skip waiting - activate new SW immediately\n skipWaiting: true,\n\n // Take control of all clients immediately\n clientsClaim: true,\n\n // Enable navigation preload for faster loads\n navigationPreload: true,\n\n // Use default Next.js runtime caching strategies\n runtimeCaching: defaultCache,\n\n // Fallback pages for offline\n fallbacks: {\n entries: [\n {\n url: '/_offline',\n matcher({ request }) {\n return request.destination === 'document';\n },\n },\n ],\n },\n});\n\nserwist.addEventListeners();\n`;\n}\n\n// Backward compatibility exports (deprecated)\nexport const defaultRuntimeCaching = [];\nexport function createApiCacheRule() {\n consola.warn('createApiCacheRule is deprecated with Serwist. Use defaultCache from @serwist/next/worker');\n return {};\n}\nexport function createStaticAssetRule() {\n consola.warn('createStaticAssetRule is deprecated with Serwist. Use defaultCache from @serwist/next/worker');\n return {};\n}\nexport function createCdnCacheRule() {\n consola.warn('createCdnCacheRule is deprecated with Serwist. Use defaultCache from @serwist/next/worker');\n return {};\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/pwa/manifest.ts","../../src/pwa/plugin.ts"],"sourcesContent":["/**\n * PWA Manifest Metadata Utilities\n *\n * Helper functions for creating Next.js metadata for PWA manifest\n */\n\nimport type { Metadata, MetadataRoute, Viewport } from 'next';\n\nexport interface ManifestConfig {\n name: string;\n shortName?: string;\n description?: string;\n themeColor?: string;\n backgroundColor?: string;\n display?: 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser';\n orientation?: 'portrait' | 'landscape' | 'any';\n startUrl?: string;\n scope?: string;\n lang?: string;\n dir?: 'ltr' | 'rtl' | 'auto';\n icons?: {\n src: string;\n sizes: string;\n type?: string;\n purpose?: string;\n }[];\n}\n\n/**\n * Icon paths configuration\n */\nexport interface IconPaths {\n logo192?: string;\n logo384?: string;\n logo512?: string;\n}\n\n/**\n * Protocol handler configuration\n * Allows your PWA to register as a handler for custom protocols\n *\n * @example\n * ```typescript\n * {\n * protocol: \"web+music\",\n * url: \"/play?track=%s\"\n * }\n * ```\n */\nexport interface ProtocolHandler {\n /** Protocol scheme (e.g., \"web+music\", \"mailto\", \"magnet\") */\n protocol: string;\n /** URL template with %s placeholder for the protocol parameter */\n url: string;\n}\n\n/**\n * Create viewport configuration for Next.js app\n *\n * @example\n * ```typescript\n * export const viewport: Viewport = createViewport({\n * themeColor: '#ffffff',\n * });\n * ```\n */\nexport function createViewport(config: { themeColor?: string }): Viewport {\n return {\n width: 'device-width',\n initialScale: 1,\n maximumScale: 1,\n themeColor: config.themeColor || '#000000',\n };\n}\n\n/**\n * Create manifest metadata for Next.js app\n *\n * Note: themeColor and viewport should be exported separately using createViewport()\n *\n * @example\n * ```typescript\n * export const metadata: Metadata = {\n * ...createManifestMetadata({\n * name: 'My App',\n * shortName: 'App',\n * description: 'My awesome app',\n * }),\n * };\n *\n * export const viewport: Viewport = createViewport({\n * themeColor: '#ffffff',\n * });\n * ```\n */\nexport function createManifestMetadata(config: ManifestConfig): Metadata {\n return {\n manifest: '/manifest.json',\n appleWebApp: {\n capable: true,\n statusBarStyle: 'default',\n title: config.shortName || config.name,\n },\n applicationName: config.name,\n formatDetection: {\n telephone: false,\n },\n };\n}\n\n/**\n * Create Next.js manifest function\n *\n * Use this in your app/manifest.ts file\n *\n * @example\n * ```typescript\n * // app/manifest.ts\n * import { createManifest } from '@djangocfg/nextjs/config';\n * import { settings } from '@core/settings';\n *\n * export default createManifest({\n * name: settings.app.name,\n * description: settings.app.description,\n * icons: {\n * logo192: settings.app.icons.logo192,\n * logo384: settings.app.icons.logo384,\n * logo512: settings.app.icons.logo512,\n * },\n * });\n * ```\n */\nexport interface ScreenshotConfig {\n src: string;\n sizes: string;\n type?: string;\n form_factor?: 'narrow' | 'wide';\n label?: string;\n}\n\n/**\n * Smart screenshot configuration\n * Automatically detects everything from path or uses defaults\n */\nexport interface SmartScreenshotInput {\n src: string;\n /** Form factor (auto-detected from filename if contains 'desktop'/'mobile', or use default) */\n form_factor?: 'narrow' | 'wide';\n /** Optional label (auto-generated from form_factor) */\n label?: string;\n /** Optional width (defaults based on form_factor) */\n width?: number;\n /** Optional height (defaults based on form_factor) */\n height?: number;\n}\n\n/**\n * Create screenshot config with smart defaults\n * Automatically detects type, sizes, form_factor from path or uses sensible defaults\n *\n * @example\n * ```typescript\n * // Minimal - everything auto-detected\n * createScreenshot({ src: '/screenshots/desktop-view.png' })\n * // → form_factor: 'wide', sizes: '1920x1080', type: 'image/png', label: 'Desktop screenshot'\n *\n * createScreenshot({ src: '/screenshots/mobile.png' })\n * // → form_factor: 'narrow', sizes: '390x844', type: 'image/png', label: 'Mobile screenshot'\n *\n * // With custom dimensions\n * createScreenshot({ src: '/screenshots/tablet.png', width: 1024, height: 768 })\n * ```\n */\nexport function createScreenshot(input: SmartScreenshotInput | string): ScreenshotConfig {\n // Allow string shorthand\n const config = typeof input === 'string' ? { src: input } : input;\n let { src, width, height, label, form_factor } = config;\n\n // Auto-detect image type from extension\n const ext = src.split('.').pop()?.toLowerCase();\n const typeMap: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n };\n const type = ext ? typeMap[ext] || 'image/png' : 'image/png';\n\n // Try to parse dimensions from filename (e.g., \"1920x1080.png\" or \"screenshot-390x844.png\")\n const filename = src.toLowerCase();\n const dimensionMatch = filename.match(/(\\d{3,4})x(\\d{3,4})/);\n if (dimensionMatch && !width && !height) {\n width = parseInt(dimensionMatch[1], 10);\n height = parseInt(dimensionMatch[2], 10);\n }\n\n // Auto-detect form_factor from filename if not provided\n let autoFormFactor: 'narrow' | 'wide' = 'wide'; // Default to wide\n if (filename.includes('mobile') || filename.includes('phone') || filename.includes('narrow')) {\n autoFormFactor = 'narrow';\n } else if (filename.includes('desktop') || filename.includes('laptop') || filename.includes('wide')) {\n autoFormFactor = 'wide';\n } else if (width && height) {\n // Calculate from dimensions if provided or parsed\n const aspectRatio = width / height;\n autoFormFactor = aspectRatio > 1.2 ? 'wide' : 'narrow';\n }\n\n const finalFormFactor = form_factor || autoFormFactor;\n\n // Default dimensions based on form_factor (only if not parsed from filename)\n const defaultDimensions = finalFormFactor === 'wide'\n ? { width: 1920, height: 1080 } // Desktop default\n : { width: 390, height: 844 }; // Mobile default (iPhone 14)\n\n const finalWidth = width || defaultDimensions.width;\n const finalHeight = height || defaultDimensions.height;\n\n // Auto-generate label\n const autoLabel = finalFormFactor === 'wide'\n ? 'Desktop screenshot'\n : 'Mobile screenshot';\n\n return {\n src,\n sizes: `${finalWidth}x${finalHeight}`,\n type,\n form_factor: finalFormFactor,\n label: label || autoLabel,\n };\n}\n\n/**\n * Create multiple screenshots from array\n * Supports string shorthand or full config objects\n *\n * @example\n * ```typescript\n * // Minimal - just paths\n * createScreenshots([\n * '/screenshots/desktop.png', // Auto: wide, 1920x1080\n * '/screenshots/mobile.png', // Auto: narrow, 390x844\n * ])\n *\n * // Mixed\n * createScreenshots([\n * '/screenshots/desktop.png',\n * { src: '/screenshots/tablet.png', width: 1024, height: 768 },\n * ])\n * ```\n */\nexport function createScreenshots(inputs: Array<SmartScreenshotInput | string>): ScreenshotConfig[] {\n return inputs.map(createScreenshot);\n}\n\nexport function createManifest(config: {\n name: string;\n shortName?: string;\n description?: string;\n themeColor?: string;\n backgroundColor?: string;\n display?: 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser';\n orientation?: 'portrait' | 'landscape' | 'any';\n id?: string;\n startUrl?: string;\n scope?: string;\n lang?: string;\n dir?: 'ltr' | 'rtl' | 'auto';\n icons?: IconPaths | ManifestConfig['icons'];\n screenshots?: ScreenshotConfig[];\n protocol_handlers?: ProtocolHandler[];\n}): () => MetadataRoute.Manifest {\n return () => {\n // Convert IconPaths to manifest icons format\n let manifestIcons: MetadataRoute.Manifest['icons'];\n\n if (Array.isArray(config.icons)) {\n // Already in manifest format\n manifestIcons = config.icons as MetadataRoute.Manifest['icons'];\n } else if (config.icons) {\n // Convert IconPaths to manifest icons\n const { logo192, logo384, logo512 } = config.icons as IconPaths;\n manifestIcons = [\n ...(logo192\n ? [\n {\n src: logo192,\n sizes: '192x192',\n type: 'image/png',\n purpose: 'maskable' as const,\n },\n ]\n : []),\n ...(logo384\n ? [\n {\n src: logo384,\n sizes: '384x384',\n type: 'image/png',\n },\n ]\n : []),\n ...(logo512\n ? [\n {\n src: logo512,\n sizes: '512x512',\n type: 'image/png',\n },\n ]\n : []),\n ];\n }\n\n const manifest: MetadataRoute.Manifest = {\n name: config.name,\n short_name: config.shortName || config.name,\n description: config.description || config.name,\n id: config.id || config.startUrl || '/',\n start_url: config.startUrl || '/',\n scope: config.scope || '/',\n display: config.display || 'standalone',\n orientation: config.orientation || 'portrait',\n background_color: config.backgroundColor || '#000000',\n theme_color: config.themeColor || '#ffffff',\n lang: config.lang || 'en',\n dir: config.dir || 'ltr',\n icons: manifestIcons,\n // Removed forced gcm_sender_id to avoid potential conflicts with VAPID\n // gcm_sender_id: '103953800507',\n };\n\n // Add screenshots if provided (for Richer PWA Install UI)\n if (config.screenshots && config.screenshots.length > 0) {\n (manifest as any).screenshots = config.screenshots;\n }\n\n // Add protocol handlers if provided\n if (config.protocol_handlers && config.protocol_handlers.length > 0) {\n (manifest as any).protocol_handlers = config.protocol_handlers;\n }\n\n return manifest;\n };\n}\n\n/**\n * Generate manifest.json content (legacy)\n *\n * @deprecated Use createManifest() instead\n */\nexport function generateManifest(config: ManifestConfig): Record<string, any> {\n return createManifest(config)();\n}\n","/**\n * PWA (Progressive Web App) Plugin — dependency-free.\n *\n * History: this used to wrap `@serwist/next`. Serwist is a **webpack** plugin,\n * and Next.js 16 defaults to Turbopack, which does not run webpack loaders and\n * chokes on serwist's `fileURLToPath(new URL(\".\", import.meta.url))` during\n * static tracing — failing the whole build even when PWA was never enabled\n * (the mere `require('@serwist/next')` reference was enough). Since no app in\n * this monorepo actually turned PWA on (`withPWA` is opt-in and unused), we\n * dropped serwist entirely.\n *\n * What remains:\n * - `createManifest` (in `./manifest`) — the web app manifest, which Next.js\n * supports natively via `app/manifest.ts`; needs no library at all.\n * - `withPWA` — a thin, Turbopack-safe pass-through kept for API stability.\n * - `getServiceWorkerTemplate` — a vanilla service worker you can drop into\n * `app/sw.ts`, no framework runtime required.\n *\n * If you want a full precache/offline story back, wire a real service worker\n * (the template below, or Workbox) yourself — but do it in an app, not here, so\n * one app's PWA choice can't break every consumer's build.\n */\n\nimport type { NextConfig } from 'next';\n\nexport interface PWAPluginOptions {\n /**\n * Path to service worker source file (relative to project root).\n * @default 'app/sw.ts'\n */\n swSrc?: string;\n\n /**\n * Destination for the compiled service worker.\n * @default 'public/sw.js'\n */\n swDest?: string;\n\n /**\n * Disable PWA completely.\n * @default true in development / static export, false otherwise\n */\n disable?: boolean;\n\n /** Cache pages on navigation. Consumed by a custom SW, if any. @default true */\n cacheOnNavigation?: boolean;\n\n /** Reload the app when the device comes back online. @default true */\n reloadOnOnline?: boolean;\n}\n\n/**\n * Turbopack-safe PWA pass-through.\n *\n * This no longer injects a build plugin — Next.js's native manifest support\n * plus a hand-written `app/sw.ts` (see {@link getServiceWorkerTemplate}) cover\n * the PWA surface without a webpack-only dependency. Kept as a named export so\n * existing `withPWA(config)` / `createBaseNextConfig({ pwa })` call sites keep\n * type-checking and behave predictably (they return the config unchanged).\n *\n * @example\n * ```ts\n * import { createBaseNextConfig } from '@djangocfg/nextjs/config';\n * export default createBaseNextConfig({ pwa: { swSrc: 'app/sw.ts' } });\n * ```\n */\nexport function withPWA(\n nextConfig: NextConfig,\n _options: PWAPluginOptions = {}\n): NextConfig {\n return nextConfig;\n}\n\n/**\n * Vanilla service-worker template — no library.\n *\n * Returns ready-to-use, framework-free service worker source. Copy it into\n * `app/sw.ts` (or generate it) if you want offline support. It uses only the\n * standard Service Worker + Cache APIs, so it builds under Turbopack with no\n * extra dependency.\n *\n * @example\n * ```ts\n * import { getServiceWorkerTemplate } from '@djangocfg/nextjs/config';\n * console.log(getServiceWorkerTemplate()); // → paste into app/sw.ts\n * ```\n */\nexport function getServiceWorkerTemplate(): string {\n return `/**\n * Service Worker (vanilla) — standard Cache + Fetch APIs, no library.\n */\n\n/// <reference lib=\"webworker\" />\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ndeclare const self: any;\n\nconst CACHE = 'app-cache-v1';\nconst OFFLINE_URL = '/_offline';\n\n// Precache the offline fallback on install.\nself.addEventListener('install', (event: any) => {\n event.waitUntil(\n caches.open(CACHE).then((cache: Cache) => cache.addAll([OFFLINE_URL]))\n );\n self.skipWaiting();\n});\n\n// Drop stale caches on activate and take control immediately.\nself.addEventListener('activate', (event: any) => {\n event.waitUntil(\n caches.keys().then((keys: string[]) =>\n Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))\n )\n );\n self.clients.claim();\n});\n\n// Network-first for navigations, with an offline fallback.\nself.addEventListener('fetch', (event: any) => {\n const { request } = event;\n if (request.mode !== 'navigate') return;\n event.respondWith(\n fetch(request).catch(() =>\n caches.match(OFFLINE_URL).then((r: Response | undefined) => r ?? Response.error())\n )\n );\n});\n`;\n}\n"],"mappings":";AAkEO,SAAS,eAAe,QAA2C;AACxE,SAAO;AAAA,IACL,OAAO;AAAA,IACP,cAAc;AAAA,IACd,cAAc;AAAA,IACd,YAAY,OAAO,cAAc;AAAA,EACnC;AACF;AAsBO,SAAS,uBAAuB,QAAkC;AACvE,SAAO;AAAA,IACL,UAAU;AAAA,IACV,aAAa;AAAA,MACX,SAAS;AAAA,MACT,gBAAgB;AAAA,MAChB,OAAO,OAAO,aAAa,OAAO;AAAA,IACpC;AAAA,IACA,iBAAiB,OAAO;AAAA,IACxB,iBAAiB;AAAA,MACf,WAAW;AAAA,IACb;AAAA,EACF;AACF;AAiEO,SAAS,iBAAiB,OAAwD;AAEvF,QAAM,SAAS,OAAO,UAAU,WAAW,EAAE,KAAK,MAAM,IAAI;AAC5D,MAAI,EAAE,KAAK,OAAO,QAAQ,OAAO,YAAY,IAAI;AAGjD,QAAM,MAAM,IAAI,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY;AAC9C,QAAM,UAAkC;AAAA,IACtC,KAAK;AAAA,IACL,KAAK;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AACA,QAAM,OAAO,MAAM,QAAQ,GAAG,KAAK,cAAc;AAGjD,QAAM,WAAW,IAAI,YAAY;AACjC,QAAM,iBAAiB,SAAS,MAAM,qBAAqB;AAC3D,MAAI,kBAAkB,CAAC,SAAS,CAAC,QAAQ;AACvC,YAAQ,SAAS,eAAe,CAAC,GAAG,EAAE;AACtC,aAAS,SAAS,eAAe,CAAC,GAAG,EAAE;AAAA,EACzC;AAGA,MAAI,iBAAoC;AACxC,MAAI,SAAS,SAAS,QAAQ,KAAK,SAAS,SAAS,OAAO,KAAK,SAAS,SAAS,QAAQ,GAAG;AAC5F,qBAAiB;AAAA,EACnB,WAAW,SAAS,SAAS,SAAS,KAAK,SAAS,SAAS,QAAQ,KAAK,SAAS,SAAS,MAAM,GAAG;AACnG,qBAAiB;AAAA,EACnB,WAAW,SAAS,QAAQ;AAE1B,UAAM,cAAc,QAAQ;AAC5B,qBAAiB,cAAc,MAAM,SAAS;AAAA,EAChD;AAEA,QAAM,kBAAkB,eAAe;AAGvC,QAAM,oBAAoB,oBAAoB,SAC1C,EAAE,OAAO,MAAM,QAAQ,KAAK,IAC5B,EAAE,OAAO,KAAK,QAAQ,IAAI;AAE9B,QAAM,aAAa,SAAS,kBAAkB;AAC9C,QAAM,cAAc,UAAU,kBAAkB;AAGhD,QAAM,YAAY,oBAAoB,SAClC,uBACA;AAEJ,SAAO;AAAA,IACL;AAAA,IACA,OAAO,GAAG,UAAU,IAAI,WAAW;AAAA,IACnC;AAAA,IACA,aAAa;AAAA,IACb,OAAO,SAAS;AAAA,EAClB;AACF;AAqBO,SAAS,kBAAkB,QAAkE;AAClG,SAAO,OAAO,IAAI,gBAAgB;AACpC;AAEO,SAAS,eAAe,QAgBE;AAC/B,SAAO,MAAM;AAEX,QAAI;AAEJ,QAAI,MAAM,QAAQ,OAAO,KAAK,GAAG;AAE/B,sBAAgB,OAAO;AAAA,IACzB,WAAW,OAAO,OAAO;AAEvB,YAAM,EAAE,SAAS,SAAS,QAAQ,IAAI,OAAO;AAC7C,sBAAgB;AAAA,QACd,GAAI,UACA;AAAA,UACE;AAAA,YACE,KAAK;AAAA,YACL,OAAO;AAAA,YACP,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,QACF,IACA,CAAC;AAAA,QACL,GAAI,UACA;AAAA,UACE;AAAA,YACE,KAAK;AAAA,YACL,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,QACF,IACA,CAAC;AAAA,QACL,GAAI,UACA;AAAA,UACE;AAAA,YACE,KAAK;AAAA,YACL,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,QACF,IACA,CAAC;AAAA,MACP;AAAA,IACF;AAEA,UAAM,WAAmC;AAAA,MACvC,MAAM,OAAO;AAAA,MACb,YAAY,OAAO,aAAa,OAAO;AAAA,MACvC,aAAa,OAAO,eAAe,OAAO;AAAA,MAC1C,IAAI,OAAO,MAAM,OAAO,YAAY;AAAA,MACpC,WAAW,OAAO,YAAY;AAAA,MAC9B,OAAO,OAAO,SAAS;AAAA,MACvB,SAAS,OAAO,WAAW;AAAA,MAC3B,aAAa,OAAO,eAAe;AAAA,MACnC,kBAAkB,OAAO,mBAAmB;AAAA,MAC5C,aAAa,OAAO,cAAc;AAAA,MAClC,MAAM,OAAO,QAAQ;AAAA,MACrB,KAAK,OAAO,OAAO;AAAA,MACnB,OAAO;AAAA;AAAA;AAAA,IAGT;AAGA,QAAI,OAAO,eAAe,OAAO,YAAY,SAAS,GAAG;AACvD,MAAC,SAAiB,cAAc,OAAO;AAAA,IACzC;AAGA,QAAI,OAAO,qBAAqB,OAAO,kBAAkB,SAAS,GAAG;AACnE,MAAC,SAAiB,oBAAoB,OAAO;AAAA,IAC/C;AAEA,WAAO;AAAA,EACT;AACF;AAOO,SAAS,iBAAiB,QAA6C;AAC5E,SAAO,eAAe,MAAM,EAAE;AAChC;;;AChSO,SAAS,QACd,YACA,WAA6B,CAAC,GAClB;AACZ,SAAO;AACT;AAgBO,SAAS,2BAAmC;AACjD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwCT;","names":[]}
|
|
@@ -1,41 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Service Worker Utilities
|
|
2
|
+
* Service Worker Utilities — vanilla, dependency-free.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Was a Serwist wrapper; serwist is a webpack-only plugin incompatible with
|
|
5
|
+
* Next.js 16's Turbopack, so this is now a hand-written service worker built on
|
|
6
|
+
* the standard Service Worker + Cache APIs. Same `createServiceWorker(options)`
|
|
7
|
+
* entry point, no library, Turbopack-safe.
|
|
5
8
|
*/
|
|
6
9
|
interface ServiceWorkerOptions {
|
|
7
|
-
/**
|
|
8
|
-
* Offline fallback URL
|
|
9
|
-
* @default '/_offline'
|
|
10
|
-
*/
|
|
10
|
+
/** Offline fallback URL. @default '/_offline' */
|
|
11
11
|
offlineFallback?: string;
|
|
12
|
-
/**
|
|
13
|
-
* Skip waiting - activate new SW immediately
|
|
14
|
-
* @default true
|
|
15
|
-
*/
|
|
12
|
+
/** Activate a new SW immediately instead of waiting. @default true */
|
|
16
13
|
skipWaiting?: boolean;
|
|
17
|
-
/**
|
|
18
|
-
* Take control of all clients immediately
|
|
19
|
-
* @default true
|
|
20
|
-
*/
|
|
14
|
+
/** Take control of open clients immediately. @default true */
|
|
21
15
|
clientsClaim?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
* @default true
|
|
25
|
-
*/
|
|
26
|
-
navigationPreload?: boolean;
|
|
16
|
+
/** Name of the runtime cache bucket. @default 'app-cache-v1' */
|
|
17
|
+
cacheName?: string;
|
|
27
18
|
}
|
|
28
19
|
/**
|
|
29
|
-
*
|
|
20
|
+
* Register a minimal offline-capable service worker.
|
|
21
|
+
*
|
|
22
|
+
* Network-first for navigations with an offline fallback page; stale caches are
|
|
23
|
+
* cleared on activate. No precache manifest (that was a serwist build-time
|
|
24
|
+
* injection) — add your own `cache.addAll([...])` in the install step if you
|
|
25
|
+
* need assets precached.
|
|
30
26
|
*
|
|
31
27
|
* @example
|
|
32
28
|
* ```ts
|
|
33
29
|
* // app/sw.ts
|
|
34
30
|
* import { createServiceWorker } from '@djangocfg/nextjs/worker';
|
|
35
|
-
*
|
|
36
|
-
* createServiceWorker({
|
|
37
|
-
* offlineFallback: '/_offline',
|
|
38
|
-
* });
|
|
31
|
+
* createServiceWorker({ offlineFallback: '/_offline' });
|
|
39
32
|
* ```
|
|
40
33
|
*/
|
|
41
34
|
declare function createServiceWorker(options?: ServiceWorkerOptions): void;
|
|
@@ -1,39 +1,36 @@
|
|
|
1
1
|
// src/pwa/worker/index.ts
|
|
2
|
-
import { Serwist } from "serwist";
|
|
3
|
-
import { defaultCache } from "@serwist/next/worker";
|
|
4
2
|
function createServiceWorker(options = {}) {
|
|
5
3
|
const {
|
|
6
4
|
offlineFallback = "/_offline",
|
|
7
5
|
skipWaiting = true,
|
|
8
6
|
clientsClaim = true,
|
|
9
|
-
|
|
7
|
+
cacheName = "app-cache-v1"
|
|
10
8
|
} = options;
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
9
|
+
const sw = self;
|
|
10
|
+
sw.addEventListener("install", (event) => {
|
|
11
|
+
event.waitUntil(
|
|
12
|
+
caches.open(cacheName).then((cache) => cache.addAll([offlineFallback]))
|
|
13
|
+
);
|
|
14
|
+
if (skipWaiting) sw.skipWaiting();
|
|
15
|
+
});
|
|
16
|
+
sw.addEventListener("activate", (event) => {
|
|
17
|
+
event.waitUntil(
|
|
18
|
+
caches.keys().then(
|
|
19
|
+
(keys) => Promise.all(keys.filter((k) => k !== cacheName).map((k) => caches.delete(k)))
|
|
20
|
+
).then(() => {
|
|
21
|
+
if (clientsClaim) return sw.clients.claim();
|
|
22
|
+
})
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
sw.addEventListener("fetch", (event) => {
|
|
26
|
+
const { request } = event;
|
|
27
|
+
if (request.mode !== "navigate") return;
|
|
28
|
+
event.respondWith(
|
|
29
|
+
fetch(request).catch(
|
|
30
|
+
() => caches.match(offlineFallback).then((r) => r ?? Response.error())
|
|
31
|
+
)
|
|
32
|
+
);
|
|
35
33
|
});
|
|
36
|
-
serwist.addEventListeners();
|
|
37
34
|
}
|
|
38
35
|
export {
|
|
39
36
|
createServiceWorker
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/pwa/worker/index.ts"],"sourcesContent":["/**\n * Service Worker Utilities
|
|
1
|
+
{"version":3,"sources":["../../../src/pwa/worker/index.ts"],"sourcesContent":["/**\n * Service Worker Utilities — vanilla, dependency-free.\n *\n * Was a Serwist wrapper; serwist is a webpack-only plugin incompatible with\n * Next.js 16's Turbopack, so this is now a hand-written service worker built on\n * the standard Service Worker + Cache APIs. Same `createServiceWorker(options)`\n * entry point, no library, Turbopack-safe.\n */\n\n/// <reference lib=\"webworker\" />\n\nexport interface ServiceWorkerOptions {\n /** Offline fallback URL. @default '/_offline' */\n offlineFallback?: string;\n /** Activate a new SW immediately instead of waiting. @default true */\n skipWaiting?: boolean;\n /** Take control of open clients immediately. @default true */\n clientsClaim?: boolean;\n /** Name of the runtime cache bucket. @default 'app-cache-v1' */\n cacheName?: string;\n}\n\n/**\n * Register a minimal offline-capable service worker.\n *\n * Network-first for navigations with an offline fallback page; stale caches are\n * cleared on activate. No precache manifest (that was a serwist build-time\n * injection) — add your own `cache.addAll([...])` in the install step if you\n * need assets precached.\n *\n * @example\n * ```ts\n * // app/sw.ts\n * import { createServiceWorker } from '@djangocfg/nextjs/worker';\n * createServiceWorker({ offlineFallback: '/_offline' });\n * ```\n */\nexport function createServiceWorker(options: ServiceWorkerOptions = {}): void {\n const {\n offlineFallback = '/_offline',\n skipWaiting = true,\n clientsClaim = true,\n cacheName = 'app-cache-v1',\n } = options;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sw = self as any;\n\n sw.addEventListener('install', (event: any) => {\n event.waitUntil(\n caches.open(cacheName).then((cache: Cache) => cache.addAll([offlineFallback]))\n );\n if (skipWaiting) sw.skipWaiting();\n });\n\n sw.addEventListener('activate', (event: any) => {\n event.waitUntil(\n caches\n .keys()\n .then((keys: string[]) =>\n Promise.all(keys.filter((k) => k !== cacheName).map((k) => caches.delete(k)))\n )\n .then(() => {\n if (clientsClaim) return sw.clients.claim();\n })\n );\n });\n\n sw.addEventListener('fetch', (event: any) => {\n const { request } = event;\n if (request.mode !== 'navigate') return;\n event.respondWith(\n fetch(request).catch(() =>\n caches\n .match(offlineFallback)\n .then((r: Response | undefined) => r ?? Response.error())\n )\n );\n });\n}\n"],"mappings":";AAqCO,SAAS,oBAAoB,UAAgC,CAAC,GAAS;AAC5E,QAAM;AAAA,IACJ,kBAAkB;AAAA,IAClB,cAAc;AAAA,IACd,eAAe;AAAA,IACf,YAAY;AAAA,EACd,IAAI;AAGJ,QAAM,KAAK;AAEX,KAAG,iBAAiB,WAAW,CAAC,UAAe;AAC7C,UAAM;AAAA,MACJ,OAAO,KAAK,SAAS,EAAE,KAAK,CAAC,UAAiB,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;AAAA,IAC/E;AACA,QAAI,YAAa,IAAG,YAAY;AAAA,EAClC,CAAC;AAED,KAAG,iBAAiB,YAAY,CAAC,UAAe;AAC9C,UAAM;AAAA,MACJ,OACG,KAAK,EACL;AAAA,QAAK,CAAC,SACL,QAAQ,IAAI,KAAK,OAAO,CAAC,MAAM,MAAM,SAAS,EAAE,IAAI,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC,CAAC;AAAA,MAC9E,EACC,KAAK,MAAM;AACV,YAAI,aAAc,QAAO,GAAG,QAAQ,MAAM;AAAA,MAC5C,CAAC;AAAA,IACL;AAAA,EACF,CAAC;AAED,KAAG,iBAAiB,SAAS,CAAC,UAAe;AAC3C,UAAM,EAAE,QAAQ,IAAI;AACpB,QAAI,QAAQ,SAAS,WAAY;AACjC,UAAM;AAAA,MACJ,MAAM,OAAO,EAAE;AAAA,QAAM,MACnB,OACG,MAAM,eAAe,EACrB,KAAK,CAAC,MAA4B,KAAK,SAAS,MAAM,CAAC;AAAA,MAC5D;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/nextjs",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.519",
|
|
4
4
|
"description": "Next.js server utilities: sitemap, health, OG images, contact forms, navigation, config",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nextjs",
|
|
@@ -149,9 +149,9 @@
|
|
|
149
149
|
"ai-docs": "tsx src/ai/cli.ts"
|
|
150
150
|
},
|
|
151
151
|
"peerDependencies": {
|
|
152
|
-
"@djangocfg/devtools": "^2.1.
|
|
153
|
-
"@djangocfg/i18n": "^2.1.
|
|
154
|
-
"@djangocfg/ui-core": "^2.1.
|
|
152
|
+
"@djangocfg/devtools": "^2.1.519",
|
|
153
|
+
"@djangocfg/i18n": "^2.1.519",
|
|
154
|
+
"@djangocfg/ui-core": "^2.1.519",
|
|
155
155
|
"next": "^16.2.2"
|
|
156
156
|
},
|
|
157
157
|
"peerDependenciesMeta": {
|
|
@@ -163,21 +163,18 @@
|
|
|
163
163
|
}
|
|
164
164
|
},
|
|
165
165
|
"dependencies": {
|
|
166
|
-
"@serwist/next": "^9.5.11",
|
|
167
|
-
"@serwist/sw": "^9.5.11",
|
|
168
166
|
"chalk": "^5.6.2",
|
|
169
167
|
"conf": "^15.1.0",
|
|
170
168
|
"consola": "^3.4.2",
|
|
171
169
|
"next-intl": "^4.13.2",
|
|
172
|
-
"semver": "^7.8.5"
|
|
173
|
-
"serwist": "^9.5.11"
|
|
170
|
+
"semver": "^7.8.5"
|
|
174
171
|
},
|
|
175
172
|
"devDependencies": {
|
|
176
|
-
"@djangocfg/devtools": "^2.1.
|
|
177
|
-
"@djangocfg/i18n": "^2.1.
|
|
178
|
-
"@djangocfg/layouts": "^2.1.
|
|
179
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
180
|
-
"@djangocfg/ui-core": "^2.1.
|
|
173
|
+
"@djangocfg/devtools": "^2.1.519",
|
|
174
|
+
"@djangocfg/i18n": "^2.1.519",
|
|
175
|
+
"@djangocfg/layouts": "^2.1.519",
|
|
176
|
+
"@djangocfg/typescript-config": "^2.1.519",
|
|
177
|
+
"@djangocfg/ui-core": "^2.1.519",
|
|
181
178
|
"@types/node": "^25.9.5",
|
|
182
179
|
"@types/react": "19.2.15",
|
|
183
180
|
"@types/react-dom": "19.2.3",
|
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
|
|
5
5
|
export { DevStartupPlugin, resetDevStartupState, type DevStartupPluginOptions } from './devStartup';
|
|
6
6
|
export { addCompressionPlugins, isCompressionAvailable, type CompressionPluginOptions } from './compression';
|
|
7
|
-
export { withPWA,
|
|
7
|
+
export { withPWA, type PWAPluginOptions } from '../../pwa/plugin';
|