- Comprehensive plan to migrate from @vite-pwa/sveltekit to native implementation - 5 stories with clear dependencies and acceptance criteria - Preserves all existing PWA, push notification, and share target functionality - Uses SvelteKit's native service worker APIs and manual manifest.json
22 KiB
Execution Plan: Migrate to Native SvelteKit PWA
Objective: Migrate away from @vite-pwa/sveltekit plugin to native SvelteKit PWA implementation with dedicated manifest.json, while preserving all existing functionality including push notifications, share target, and offline capabilities.
Outcome: MigrateToNativeSvelteKitPWA
Current State Analysis
Current SvelteKitPWA Plugin Implementation
Plugin Configuration (vite.config.ts):
SvelteKitPWA({
srcDir: './src',
strategies: 'injectManifest',
filename: 'service-worker.ts',
scope: '/',
base: '/',
selfDestroying: process.env.SELF_DESTROYING_SW === 'true',
injectRegister: process.env.NODE_ENV === 'test' ? false : 'auto',
injectManifest: {
swSrc: 'src/service-worker.ts',
swDest: 'service-worker.js',
injectionPoint: 'self.__WB_MANIFEST',
globPatterns: ['**/*.{js,css,html,ico,png,svg,webp,woff,woff2}'],
maximumFileSizeToCacheInBytes: 4 * 1024 * 1024,
},
manifest: {
short_name: 'InstaChef',
name: 'InstaChef Recipe Saver',
start_url: '/',
scope: '/',
display: 'standalone',
theme_color: "#ffffff",
background_color: "#ffffff",
icons: [
{ src: '/favicon.png', sizes: '192x192', type: 'image/png' },
{ src: '/favicon.png', sizes: '512x512', type: 'image/png' }
],
share_target: {
action: '/share',
method: 'GET',
enctype: 'application/x-www-form-urlencoded',
params: { title: 'title', text: 'text', url: 'url' }
}
},
workbox: {
globPatterns: ['client/**/*.{js,css,ico,png,svg,webp,woff,woff2}'],
cleanupOutdatedCaches: true,
skipWaiting: false,
clientsClaim: false,
maximumFileSizeToCacheInBytes: 4 * 1024 * 1024,
runtimeCaching: [{
urlPattern: /^https:\/\/api\./,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
networkTimeoutSeconds: 10
}
}]
},
devOptions: {
enabled: process.env.NODE_ENV !== 'test',
suppressWarnings: true,
navigateFallback: '/',
}
})
Current Service Worker Implementation:
- Uses workbox imports:
cleanupOutdatedCaches,createHandlerBoundToURL,precacheAndRoute - Uses workbox routing:
NavigationRoute,registerRoute - Relies on
self.__WB_MANIFESTinjection for precaching - Comprehensive push notification handling with custom actions
- Background sync for retry operations
- Message passing for client communication
SvelteKit Configuration Status:
// svelte.config.js - Currently DISABLED to avoid conflicts
serviceWorker: {
register: false
}
Critical Functionality That Must Be Preserved
✅ PWA Features:
- PWA installation capability
- Offline functionality with precaching
- Share target for Instagram URLs to
/shareroute - PWA manifest with proper icons and theme colors
✅ Push Notification System:
- Push notification subscription/unsubscription
- Custom notification actions (view, retry, dismiss)
- Notification click handlers with client navigation
- Background sync for retry operations
- Service worker to client message passing
✅ Caching Strategy:
- Static asset precaching
- Navigation route caching with fallbacks
- API cache with network-first strategy
- Automatic cache cleanup for updates
✅ Development Experience:
- Service worker disabled in test environment
- Proper development vs production behavior
- Hot reloading compatibility
Target Architecture Analysis
Native SvelteKit PWA Approach
Key Differences from Plugin:
- Manifest Management: Manual
static/manifest.jsoninstead of vite.config.ts generation - Service Worker APIs: SvelteKit's
$service-workermodule instead of workbox - Caching Strategy: Manual cache management using
build,files,versionarrays - Registration: SvelteKit's built-in registration instead of plugin registration
SvelteKit Service Worker Module:
import { build, files, version } from '$service-worker';
// build: array of built app files
// files: array of static files
// version: deployment version string for cache naming
Benefits of Migration:
- ✅ Remove external plugin dependency
- ✅ Align with SvelteKit best practices and roadmap
- ✅ More control over service worker behavior
- ✅ Simplified build process
- ✅ Better TypeScript integration
- ✅ Reduced bundle size without workbox overhead
Cross-Reference Dependency Analysis
Direct Dependencies
- Service Worker Registration:
src/lib/client/PushNotificationManager.tsexpects service worker to be available - Share Target:
src/routes/share/+page.svelterelies on manifest share_target configuration - Queue System: Background sync functionality for retry operations
- Notification Handlers: Client code sends messages to service worker via
postMessage()
Hidden Dependencies
- Build Process: Vite plugin currently handles TypeScript compilation and manifest generation
- Development Server: Plugin provides development mode service worker behavior
- Cache Invalidation: Workbox handles cache versioning automatically
- Routing: NavigationRoute handling for SPA behavior offline
Integration Points
- Push Notification Flow: Client → Service Worker → Push Server → Notification Display
- Share Target Flow: External App → Manifest →
/shareRoute → Queue API - Cache Flow: Service Worker → Cache API → Static Assets/API Responses
- Offline Flow: Navigation Request → Service Worker → Cache → Response
Story Breakdown
Story 1: Create Native PWA Manifest
Priority: High
Dependencies: None
Estimated Effort: 2-3 hours
Objective: Extract PWA manifest configuration from vite.config.ts to a dedicated static/manifest.json file following W3C Web App Manifest specification.
Tasks:
- Create
static/manifest.jsonwith exact configuration from vite.config.ts - Ensure share_target configuration is preserved exactly
- Add manifest link to
src/app.htmlif not present - Validate manifest.json against W3C specification
- Test manifest loading in browser
- Verify PWA installation prompt still appears
Technical Details:
File: static/manifest.json
{
"short_name": "InstaChef",
"name": "InstaChef Recipe Saver",
"start_url": "/",
"scope": "/",
"display": "standalone",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"icons": [
{
"src": "/favicon.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/favicon.png",
"sizes": "512x512",
"type": "image/png"
}
],
"share_target": {
"action": "/share",
"method": "GET",
"enctype": "application/x-www-form-urlencoded",
"params": {
"title": "title",
"text": "text",
"url": "url"
}
}
}
File: src/app.html - Add manifest link if missing:
<link rel="manifest" href="/manifest.json">
Acceptance Criteria:
- ✅
static/manifest.jsoncreated with identical configuration - ✅ Share target functionality preserved (test with external app)
- ✅ PWA installation prompt appears in supported browsers
- ✅ Manifest validation passes in browser DevTools
- ✅ All manifest properties display correctly in browser
- ✅ PWA installation works with same behavior as before
Testing Strategy:
- Validate manifest.json syntax and W3C compliance
- Test PWA installation in Chrome, Firefox, Safari, Edge
- Test share target from Instagram mobile app
- Verify manifest properties in DevTools Application tab
- Test icon display in app installer and home screen
Files:
static/manifest.json(create)src/app.html(modify if needed)
Story 2: Remove SvelteKitPWA Plugin Dependencies
Priority: High
Dependencies: Story 1
Estimated Effort: 1-2 hours
Objective: Remove @vite-pwa/sveltekit plugin and all related configuration from the project.
Tasks:
- Remove @vite-pwa/sveltekit from package.json dependencies
- Remove SvelteKitPWA plugin import and configuration from vite.config.ts
- Clean up any vite-pwa related development dependencies
- Remove dev-dist/ generated files if present
- Update build process documentation if needed
Technical Details:
File: package.json
// Remove:
"@vite-pwa/sveltekit": "..."
File: vite.config.ts
// Remove import:
import { SvelteKitPWA } from '@vite-pwa/sveltekit';
// Remove from plugins array:
SvelteKitPWA({ /* entire configuration */ })
Acceptance Criteria:
- ✅ @vite-pwa/sveltekit dependency removed from package.json
- ✅ SvelteKitPWA plugin removed from vite.config.ts
- ✅ No vite-pwa imports or references remaining
- ✅ Project builds successfully without plugin
- ✅ No build warnings related to missing plugin
- ✅ Development server starts without plugin errors
Testing Strategy:
- Run
npm installto verify dependency removal - Run
npm run buildto ensure build process works - Run
npm run devto ensure development server works - Check for any console warnings or errors
- Verify no plugin-generated files remain
Files:
package.json(modify)vite.config.ts(modify)
Story 3: Migrate Service Worker to SvelteKit Native
Priority: Critical
Dependencies: Story 1, Story 2
Estimated Effort: 4-6 hours
Objective: Rewrite service worker to use SvelteKit's native $service-worker module instead of workbox, while preserving all existing functionality including push notifications, caching, and background sync.
Tasks:
- Replace workbox imports with SvelteKit
$service-workerimports - Implement manual caching using
build,files,versionarrays - Replace workbox precaching with manual cache management
- Preserve all push notification event handlers exactly
- Preserve background sync functionality
- Implement navigation routing without workbox NavigationRoute
- Add environment detection for development vs production
- Test all service worker functionality thoroughly
Technical Details:
File: src/service-worker.ts - New Implementation:
/// <reference types="@sveltejs/kit" />
/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
/// <reference lib="webworker" />
import { build, files, version } from '$service-worker';
declare let self: ServiceWorkerGlobalScope;
// Create a unique cache name for this deployment
const CACHE = `cache-${version}`;
const ASSETS = [
...build, // the app itself
...files // everything in `static`
];
// Global error handlers (preserve existing)
self.addEventListener('error', (event) => {
console.error('[SW] Global error:', event.error);
});
self.addEventListener('unhandledrejection', (event) => {
console.error('[SW] Unhandled promise rejection:', event.reason);
event.preventDefault();
});
console.log('[SW] Service worker script loading...');
// Install event - cache all assets
self.addEventListener('install', (event) => {
console.log('[SW] Installing service worker...');
async function addFilesToCache() {
const cache = await caches.open(CACHE);
await cache.addAll(ASSETS);
console.log(`[SW] Cached ${ASSETS.length} assets`);
}
event.waitUntil(addFilesToCache());
});
// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
console.log('[SW] Activating service worker...');
async function deleteOldCaches() {
for (const key of await caches.keys()) {
if (key !== CACHE) {
console.log('[SW] Deleting old cache:', key);
await caches.delete(key);
}
}
}
event.waitUntil(deleteOldCaches());
});
// Fetch event - serve from cache with network fallback
self.addEventListener('fetch', (event) => {
// ignore POST requests etc
if (event.request.method !== 'GET') return;
async function respond() {
const url = new URL(event.request.url);
const cache = await caches.open(CACHE);
// `build`/`files` can always be served from the cache
if (ASSETS.includes(url.pathname)) {
const response = await cache.match(url.pathname);
if (response) {
return response;
}
}
// for everything else, try the network first, but
// fall back to the cache if we're offline
try {
const response = await fetch(event.request);
// if we're offline, fetch can return a value that is not a Response
// instead of throwing - and we can't pass this non-Response to respondWith
if (!(response instanceof Response)) {
throw new Error('invalid response from fetch');
}
if (response.status === 200) {
cache.put(event.request, response.clone());
}
return response;
} catch (err) {
const response = await cache.match(event.request);
if (response) {
return response;
}
// if there's no cache, then just error out
// as there is nothing we can do to respond to this request
throw err;
}
}
event.respondWith(respond());
});
// PRESERVE ALL EXISTING PUSH NOTIFICATION FUNCTIONALITY
// (Copy exactly from existing service worker - push, notificationclick, notificationclose handlers)
// ... [preserve existing push notification code] ...
// PRESERVE BACKGROUND SYNC FUNCTIONALITY
// (Copy exactly from existing service worker)
// ... [preserve existing sync handlers] ...
// PRESERVE MESSAGE HANDLING
// (Copy exactly from existing service worker)
// ... [preserve existing message handlers] ...
Key Migrations:
- Workbox Precaching → Manual Caching: Replace
precacheAndRoute(self.__WB_MANIFEST)with manual cache management usingbuildandfilesarrays - NavigationRoute → Manual Routing: Replace workbox NavigationRoute with manual fetch handler logic
- Cleanup → Manual Cleanup: Replace
cleanupOutdatedCaches()with manual cache key comparison - Manifest Injection → Module Import: Replace
self.__WB_MANIFESTwithbuild,files,versionimports
Acceptance Criteria:
- ✅ Service worker uses SvelteKit's
$service-workermodule - ✅ Manual caching works for all static assets
- ✅ Navigation routing works offline
- ✅ All push notification functionality preserved exactly
- ✅ Background sync continues to work
- ✅ Service worker to client message passing works
- ✅ Cache cleanup works on updates
- ✅ Development and production modes both work
- ✅ No service worker errors in console
Testing Strategy:
- Test service worker registration and activation
- Test offline functionality by going offline in DevTools
- Test all push notification scenarios (subscribe, receive, click actions)
- Test background sync with queue retry operations
- Test cache behavior with network disabled
- Test service worker updates with version changes
- Verify message passing between client and service worker
Files:
src/service-worker.ts(major rewrite)
Story 4: Enable SvelteKit Service Worker Registration
Priority: High
Dependencies: Story 3
Estimated Effort: 1-2 hours
Objective: Enable SvelteKit's built-in service worker registration and ensure proper coordination without conflicts.
Tasks:
- Enable
serviceWorker.register: truein svelte.config.js - Verify service worker registration timing and behavior
- Ensure no conflicts with development mode
- Test service worker registration across browsers
- Verify proper service worker lifecycle events
Technical Details:
File: svelte.config.js
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter(),
serviceWorker: {
register: true, // Enable SvelteKit's service worker registration
// Can add additional options if needed
}
}
};
export default config;
Acceptance Criteria:
- ✅ Service worker registers automatically on page load
- ✅ Only one service worker registration visible in DevTools
- ✅ Service worker lifecycle events work correctly (install, activate)
- ✅ No registration conflicts or duplicate registrations
- ✅ Works in development and production environments
- ✅ Service worker updates properly when code changes
Testing Strategy:
- Clear service workers in DevTools and reload page
- Verify single service worker registration in Application tab
- Test service worker lifecycle events in console
- Test across different browsers
- Test development vs production registration behavior
Files:
svelte.config.js(modify)
Story 5: Comprehensive Testing and Validation
Priority: Critical
Dependencies: All previous stories
Estimated Effort: 3-4 hours
Objective: Thoroughly test all PWA functionality to ensure no regressions and all features work as expected in the native SvelteKit implementation.
Tasks:
- Test PWA installation flow across browsers
- Test share target functionality from external apps
- Test all push notification scenarios
- Test offline functionality and caching
- Test service worker lifecycle and updates
- Cross-browser compatibility testing
- Performance validation vs previous implementation
Testing Scenarios:
PWA Installation:
- Installation prompt appears in Chrome
- Installation prompt appears in Edge
- Installation works on Android Chrome
- Installed app opens in standalone mode
- App icons display correctly after installation
Share Target:
- Share from Instagram mobile app works
- Share target parameters (title, text, url) received correctly
- Share page processes URLs automatically
- Manual URL input still works
- Share target works across different share sources
Push Notifications:
- Push notification subscription works
- Push notifications display with correct content
- Custom notification actions work (view, retry, dismiss)
- Notification click navigation works correctly
- Background sync triggers on retry action
- Service worker message passing works
- Push notifications work after service worker updates
Offline Functionality:
- Static assets load when offline
- Navigation works when offline
- Cached API responses serve when offline
- Cache updates when back online
- Cache cleanup works on app updates
Cross-Browser Testing:
- Chrome (desktop and mobile)
- Firefox (desktop and mobile)
- Safari (desktop and mobile)
- Edge (desktop)
- Samsung Internet (mobile)
Performance Validation:
- Service worker registration time comparable
- Cache loading performance maintained
- Bundle size reduced (no workbox)
- PWA audit scores maintained or improved
Acceptance Criteria:
- ✅ All existing functionality works exactly as before
- ✅ No regressions in any PWA features
- ✅ Cross-browser compatibility maintained
- ✅ Performance meets or exceeds previous implementation
- ✅ All tests pass in continuous integration
- ✅ PWA audit scores are maintained or improved
Files:
- Test documentation updates if needed
Risk Assessment
High Risk Areas
- Push Notification Functionality: Complex service worker message handling could break
- Share Target Integration: External app sharing could fail with manifest changes
- Cache Strategy: Manual cache management could introduce bugs vs workbox
- Service Worker Registration: Timing issues could cause registration failures
Medium Risk Areas
- Offline Functionality: Different caching approach might affect offline behavior
- Build Process: Removing plugin changes build pipeline significantly
- Development Experience: Service worker behavior could change in development
Low Risk Areas
- Manifest Properties: Direct translation should work reliably
- Static Asset Serving: SvelteKit handles this well natively
- PWA Installation: Standard manifest.json is well supported
Mitigation Strategies
- Comprehensive Testing: Extensive testing across all scenarios and browsers
- Incremental Migration: Complete each story fully before moving to next
- Rollback Plan: Keep plugin configuration in version control for quick rollback
- Staging Environment: Test fully in staging before production deployment
Implementation Roadmap
Phase 1: Foundation (Stories 1-2) - 3-5 hours
- Create native manifest.json
- Remove plugin dependencies
- Establish baseline for migration
Phase 2: Service Worker Migration (Story 3) - 4-6 hours
- Rewrite service worker with SvelteKit APIs
- Preserve all existing functionality
- Most complex and critical phase
Phase 3: Registration & Testing (Stories 4-5) - 4-6 hours
- Enable SvelteKit registration
- Comprehensive testing across all scenarios
- Performance and compatibility validation
Total Estimated Effort: 11-17 hours
Success Criteria
Functional Requirements
- ✅ All existing PWA functionality works identically
- ✅ Push notifications work across all scenarios
- ✅ Share target works from external apps
- ✅ Offline functionality maintained
- ✅ PWA installation works across browsers
Technical Requirements
- ✅ No external PWA plugin dependencies
- ✅ Uses SvelteKit native service worker APIs
- ✅ Manual manifest.json in static/ directory
- ✅ Service worker registration through SvelteKit
- ✅ Maintains or improves performance
Quality Requirements
- ✅ No regressions in existing functionality
- ✅ Cross-browser compatibility maintained
- ✅ PWA audit scores maintained or improved
- ✅ Development experience not degraded
- ✅ Build process simplified
Definition of Done
- All user stories completed with acceptance criteria met
- Comprehensive testing completed across all browsers
- No regressions in existing functionality
- Performance validated against baseline
- Documentation updated as needed
- Code review completed
- Staging deployment tested successfully
- Ready for production deployment