packwise-skills 1.0.0 → 1.2.0
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/.cursorrules +23 -23
- package/CLAUDE.md +25 -25
- package/LICENSE +21 -0
- package/README.md +404 -295
- package/audit.md +224 -224
- package/bin/packwise.js +322 -155
- package/install.sh +123 -0
- package/package.json +32 -31
- package/skill.md +944 -719
- package/sub-skills/ai/local-llm.md +183 -183
- package/sub-skills/ai/python-ml.md +164 -164
- package/sub-skills/backend/go-server.md +184 -184
- package/sub-skills/backend/java-spring.md +241 -241
- package/sub-skills/backend/node-server.md +164 -164
- package/sub-skills/backend/php-laravel.md +175 -175
- package/sub-skills/backend/python-server.md +164 -164
- package/sub-skills/backend/rust-backend.md +118 -118
- package/sub-skills/cli/python-cli.md +236 -236
- package/sub-skills/cli/sdk-library.md +497 -497
- package/sub-skills/cloud/ci-cd-pipelines.md +350 -350
- package/sub-skills/cloud/docker.md +191 -191
- package/sub-skills/cloud/kubernetes.md +277 -277
- package/sub-skills/cloud/payment-integration.md +307 -307
- package/sub-skills/cross-platform/multiplatform.md +252 -252
- package/sub-skills/desktop/electron.md +783 -783
- package/sub-skills/desktop/game-dev.md +443 -443
- package/sub-skills/desktop/native-app.md +123 -123
- package/sub-skills/desktop/scenarios.md +443 -443
- package/sub-skills/desktop/smart-platforms.md +324 -324
- package/sub-skills/desktop/tauri.md +428 -428
- package/sub-skills/desktop/vr-ar.md +252 -252
- package/sub-skills/desktop/web-to-desktop.md +153 -153
- package/sub-skills/embedded/car-infotainment.md +129 -129
- package/sub-skills/embedded/esp32.md +184 -184
- package/sub-skills/embedded/ros.md +150 -150
- package/sub-skills/embedded/stm32.md +160 -160
- package/sub-skills/mobile/android.md +322 -322
- package/sub-skills/mobile/capacitor.md +232 -232
- package/sub-skills/mobile/flutter-mobile.md +138 -138
- package/sub-skills/mobile/harmonyos.md +150 -150
- package/sub-skills/mobile/ios.md +245 -245
- package/sub-skills/mobile/react-native.md +443 -443
- package/sub-skills/mobile/wearables.md +230 -230
- package/sub-skills/plugins/browser-extension.md +308 -308
- package/sub-skills/plugins/jetbrains-plugin.md +226 -226
- package/sub-skills/plugins/vscode-extension.md +204 -204
- package/sub-skills/security/security-tools.md +174 -174
- package/sub-skills/web/monorepo.md +274 -274
- package/sub-skills/web/pwa.md +220 -220
- package/sub-skills/web/serverless-edge.md +295 -295
- package/sub-skills/web/spa.md +266 -266
- package/sub-skills/web/ssr.md +228 -228
- package/sub-skills/web/wasm.md +243 -243
package/sub-skills/web/pwa.md
CHANGED
|
@@ -1,220 +1,220 @@
|
|
|
1
|
-
# Progressive Web App (PWA) Build Sub-Skill
|
|
2
|
-
|
|
3
|
-
Build installable web applications that work offline and feel like native apps.
|
|
4
|
-
|
|
5
|
-
**Current version**: PWA standards (2025-2026) — Service Workers, Web App Manifest, Web Push
|
|
6
|
-
|
|
7
|
-
## When to Use
|
|
8
|
-
|
|
9
|
-
- Need cross-platform app from single web codebase
|
|
10
|
-
- Offline support required
|
|
11
|
-
- Push notifications needed
|
|
12
|
-
- App-like experience without app store submission
|
|
13
|
-
- Budget-constrained projects (one codebase for all platforms)
|
|
14
|
-
|
|
15
|
-
## Key Features
|
|
16
|
-
|
|
17
|
-
| Feature | Browser Support | Notes |
|
|
18
|
-
|---------|---------------|-------|
|
|
19
|
-
| Service Worker | All modern browsers | Offline caching, background sync |
|
|
20
|
-
| Web App Manifest | All modern browsers | Home screen install, splash screen |
|
|
21
|
-
| Web Push | Chrome/Firefox/Edge/Safari 16.4+ | Push notifications on all platforms |
|
|
22
|
-
| Background Sync | Chrome/Edge | Sync data when connection restored |
|
|
23
|
-
| Periodic Background Sync | Chrome/Edge only | Fetch updates in background |
|
|
24
|
-
| File System Access | Chrome/Edge | Read/write local files |
|
|
25
|
-
| Web Share API | Chrome/Edge/Safari | Native share dialog |
|
|
26
|
-
|
|
27
|
-
## PWA Essentials
|
|
28
|
-
|
|
29
|
-
### 1. Web App Manifest
|
|
30
|
-
|
|
31
|
-
```json
|
|
32
|
-
{
|
|
33
|
-
"name": "My PWA App",
|
|
34
|
-
"short_name": "MyApp",
|
|
35
|
-
"description": "My Progressive Web App",
|
|
36
|
-
"start_url": "/",
|
|
37
|
-
"display": "standalone",
|
|
38
|
-
"background_color": "#ffffff",
|
|
39
|
-
"theme_color": "#000000",
|
|
40
|
-
"orientation": "portrait",
|
|
41
|
-
"icons": [
|
|
42
|
-
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
|
|
43
|
-
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
|
|
44
|
-
{ "src": "/icons/icon-512-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
|
|
45
|
-
],
|
|
46
|
-
"screenshots": [
|
|
47
|
-
{ "src": "/screenshots/desktop.png", "sizes": "1280x720", "form_factor": "wide" },
|
|
48
|
-
{ "src": "/screenshots/mobile.png", "sizes": "390x844", "form_factor": "narrow" }
|
|
49
|
-
],
|
|
50
|
-
"categories": ["productivity"],
|
|
51
|
-
"shortcuts": [
|
|
52
|
-
{ "name": "Settings", "url": "/settings", "icons": [{ "src": "/icons/settings-96.png", "sizes": "96x96" }] }
|
|
53
|
-
]
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
```html
|
|
58
|
-
<!-- index.html -->
|
|
59
|
-
<link rel="manifest" href="/manifest.json">
|
|
60
|
-
<meta name="theme-color" content="#000000">
|
|
61
|
-
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
62
|
-
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
63
|
-
<link rel="apple-touch-icon" href="/icons/icon-192.png">
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### 2. Service Worker
|
|
67
|
-
|
|
68
|
-
```javascript
|
|
69
|
-
// sw.js — Cache-first strategy for static assets
|
|
70
|
-
const CACHE_NAME = 'my-app-v1';
|
|
71
|
-
const STATIC_ASSETS = ['/', '/index.html', '/styles.css', '/app.js', '/icons/icon-192.png'];
|
|
72
|
-
|
|
73
|
-
self.addEventListener('install', (event) => {
|
|
74
|
-
event.waitUntil(
|
|
75
|
-
caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS))
|
|
76
|
-
);
|
|
77
|
-
self.skipWaiting();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
self.addEventListener('activate', (event) => {
|
|
81
|
-
event.waitUntil(
|
|
82
|
-
caches.keys().then((keys) =>
|
|
83
|
-
Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
|
|
84
|
-
)
|
|
85
|
-
);
|
|
86
|
-
self.clients.claim();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
self.addEventListener('fetch', (event) => {
|
|
90
|
-
event.respondWith(
|
|
91
|
-
caches.match(event.request).then((cached) => {
|
|
92
|
-
const fetched = fetch(event.request).then((response) => {
|
|
93
|
-
const clone = response.clone();
|
|
94
|
-
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
|
|
95
|
-
return response;
|
|
96
|
-
});
|
|
97
|
-
return cached || fetched;
|
|
98
|
-
})
|
|
99
|
-
);
|
|
100
|
-
});
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
```javascript
|
|
104
|
-
// Register in main app
|
|
105
|
-
if ('serviceWorker' in navigator) {
|
|
106
|
-
navigator.serviceWorker.register('/sw.js');
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### 3. Web Push Notifications
|
|
111
|
-
|
|
112
|
-
```javascript
|
|
113
|
-
// Request permission and subscribe
|
|
114
|
-
const registration = await navigator.serviceWorker.ready;
|
|
115
|
-
const subscription = await registration.pushManager.subscribe({
|
|
116
|
-
userVisibleOnly: true,
|
|
117
|
-
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
|
|
118
|
-
});
|
|
119
|
-
// Send subscription to your backend
|
|
120
|
-
await fetch('/api/subscribe', { method: 'POST', body: JSON.stringify(subscription) });
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Build & Test
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
# Vite PWA plugin (vite-plugin-pwa 1.3+ supports Vite 3-8)
|
|
127
|
-
npm install vite-plugin-pwa -D
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
```javascript
|
|
131
|
-
// vite.config.js
|
|
132
|
-
import { VitePWA } from 'vite-plugin-pwa';
|
|
133
|
-
|
|
134
|
-
export default {
|
|
135
|
-
plugins: [
|
|
136
|
-
VitePWA({
|
|
137
|
-
registerType: 'autoUpdate',
|
|
138
|
-
workbox: {
|
|
139
|
-
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
|
|
140
|
-
runtimeCaching: [
|
|
141
|
-
{
|
|
142
|
-
urlPattern: /^https:\/\/api\.example\.com\//,
|
|
143
|
-
handler: 'NetworkFirst',
|
|
144
|
-
options: { cacheName: 'api-cache', expiration: { maxEntries: 50, maxAgeSeconds: 3600 } },
|
|
145
|
-
},
|
|
146
|
-
],
|
|
147
|
-
},
|
|
148
|
-
manifest: { /* manifest.json content */ },
|
|
149
|
-
}),
|
|
150
|
-
],
|
|
151
|
-
};
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
# Build
|
|
156
|
-
npm run build
|
|
157
|
-
# Output: dist/ directory with PWA-ready files
|
|
158
|
-
|
|
159
|
-
# Test locally
|
|
160
|
-
npx serve dist # Serve over HTTP for testing
|
|
161
|
-
# For full PWA testing: deploy to HTTPS server
|
|
162
|
-
|
|
163
|
-
# Lighthouse audit
|
|
164
|
-
npx lighthouse https://myapp.com --only-categories=pwa
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
## Framework-Specific PWA
|
|
168
|
-
|
|
169
|
-
### Next.js (App Router)
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
// next.config.js
|
|
173
|
-
const withPWA = require('next-pwa')({
|
|
174
|
-
dest: 'public',
|
|
175
|
-
register: true,
|
|
176
|
-
skipWaiting: true,
|
|
177
|
-
});
|
|
178
|
-
module.exports = withPWA({ /* next config */ });
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Vue (Vite)
|
|
182
|
-
|
|
183
|
-
```javascript
|
|
184
|
-
// Same as Vite config above — use vite-plugin-pwa
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### React (Create React App / Vite)
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
# CRA: use `cra-template-pwa`
|
|
191
|
-
npx create-react-app my-app --template pwa
|
|
192
|
-
|
|
193
|
-
# Vite: use vite-plugin-pwa (recommended)
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
## PWA vs Native App
|
|
197
|
-
|
|
198
|
-
| Feature | PWA | Native App |
|
|
199
|
-
|---------|-----|-----------|
|
|
200
|
-
| Distribution | URL (no store) | App Store / Play Store |
|
|
201
|
-
| Install | Browser prompt | Store download |
|
|
202
|
-
| Offline | Yes (Service Worker) | Yes |
|
|
203
|
-
| Push notifications | Yes (web push) | Yes |
|
|
204
|
-
| Device access | Limited (camera, GPS, file) | Full |
|
|
205
|
-
| Performance | Good (WASM, Web Workers) | Best |
|
|
206
|
-
| Size | No install footprint | 20MB+ |
|
|
207
|
-
| Updates | Instant (on next visit) | Store review |
|
|
208
|
-
| iOS support | Limited (no full PWA on Safari < 16.4) | Full |
|
|
209
|
-
|
|
210
|
-
## Common Pitfalls
|
|
211
|
-
|
|
212
|
-
| Issue | Fix |
|
|
213
|
-
|-------|-----|
|
|
214
|
-
| PWA not installable | Ensure manifest.json is correct; serve over HTTPS; check Lighthouse PWA audit |
|
|
215
|
-
| Service Worker not updating | Use `skipWaiting()` + `clients.claim()`; change cache version |
|
|
216
|
-
| iOS push notifications not working | Requires Safari 16.4+; use web push with VAPID keys |
|
|
217
|
-
| Cache serving stale content | Use NetworkFirst for API; CacheFirst for static assets |
|
|
218
|
-
| Offline page not showing | Add fallback page in service worker `fetch` handler |
|
|
219
|
-
| Maskable icon looks wrong | Test with maskable.app; ensure safe zone is respected |
|
|
220
|
-
| Background sync not working | Only supported in Chrome/Edge; use IndexedDB for queued operations |
|
|
1
|
+
# Progressive Web App (PWA) Build Sub-Skill
|
|
2
|
+
|
|
3
|
+
Build installable web applications that work offline and feel like native apps.
|
|
4
|
+
|
|
5
|
+
**Current version**: PWA standards (2025-2026) — Service Workers, Web App Manifest, Web Push
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
- Need cross-platform app from single web codebase
|
|
10
|
+
- Offline support required
|
|
11
|
+
- Push notifications needed
|
|
12
|
+
- App-like experience without app store submission
|
|
13
|
+
- Budget-constrained projects (one codebase for all platforms)
|
|
14
|
+
|
|
15
|
+
## Key Features
|
|
16
|
+
|
|
17
|
+
| Feature | Browser Support | Notes |
|
|
18
|
+
|---------|---------------|-------|
|
|
19
|
+
| Service Worker | All modern browsers | Offline caching, background sync |
|
|
20
|
+
| Web App Manifest | All modern browsers | Home screen install, splash screen |
|
|
21
|
+
| Web Push | Chrome/Firefox/Edge/Safari 16.4+ | Push notifications on all platforms |
|
|
22
|
+
| Background Sync | Chrome/Edge | Sync data when connection restored |
|
|
23
|
+
| Periodic Background Sync | Chrome/Edge only | Fetch updates in background |
|
|
24
|
+
| File System Access | Chrome/Edge | Read/write local files |
|
|
25
|
+
| Web Share API | Chrome/Edge/Safari | Native share dialog |
|
|
26
|
+
|
|
27
|
+
## PWA Essentials
|
|
28
|
+
|
|
29
|
+
### 1. Web App Manifest
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"name": "My PWA App",
|
|
34
|
+
"short_name": "MyApp",
|
|
35
|
+
"description": "My Progressive Web App",
|
|
36
|
+
"start_url": "/",
|
|
37
|
+
"display": "standalone",
|
|
38
|
+
"background_color": "#ffffff",
|
|
39
|
+
"theme_color": "#000000",
|
|
40
|
+
"orientation": "portrait",
|
|
41
|
+
"icons": [
|
|
42
|
+
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
|
|
43
|
+
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
|
|
44
|
+
{ "src": "/icons/icon-512-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
|
|
45
|
+
],
|
|
46
|
+
"screenshots": [
|
|
47
|
+
{ "src": "/screenshots/desktop.png", "sizes": "1280x720", "form_factor": "wide" },
|
|
48
|
+
{ "src": "/screenshots/mobile.png", "sizes": "390x844", "form_factor": "narrow" }
|
|
49
|
+
],
|
|
50
|
+
"categories": ["productivity"],
|
|
51
|
+
"shortcuts": [
|
|
52
|
+
{ "name": "Settings", "url": "/settings", "icons": [{ "src": "/icons/settings-96.png", "sizes": "96x96" }] }
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<!-- index.html -->
|
|
59
|
+
<link rel="manifest" href="/manifest.json">
|
|
60
|
+
<meta name="theme-color" content="#000000">
|
|
61
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
62
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
63
|
+
<link rel="apple-touch-icon" href="/icons/icon-192.png">
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2. Service Worker
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// sw.js — Cache-first strategy for static assets
|
|
70
|
+
const CACHE_NAME = 'my-app-v1';
|
|
71
|
+
const STATIC_ASSETS = ['/', '/index.html', '/styles.css', '/app.js', '/icons/icon-192.png'];
|
|
72
|
+
|
|
73
|
+
self.addEventListener('install', (event) => {
|
|
74
|
+
event.waitUntil(
|
|
75
|
+
caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS))
|
|
76
|
+
);
|
|
77
|
+
self.skipWaiting();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
self.addEventListener('activate', (event) => {
|
|
81
|
+
event.waitUntil(
|
|
82
|
+
caches.keys().then((keys) =>
|
|
83
|
+
Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
self.clients.claim();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
self.addEventListener('fetch', (event) => {
|
|
90
|
+
event.respondWith(
|
|
91
|
+
caches.match(event.request).then((cached) => {
|
|
92
|
+
const fetched = fetch(event.request).then((response) => {
|
|
93
|
+
const clone = response.clone();
|
|
94
|
+
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
|
|
95
|
+
return response;
|
|
96
|
+
});
|
|
97
|
+
return cached || fetched;
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
// Register in main app
|
|
105
|
+
if ('serviceWorker' in navigator) {
|
|
106
|
+
navigator.serviceWorker.register('/sw.js');
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 3. Web Push Notifications
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
// Request permission and subscribe
|
|
114
|
+
const registration = await navigator.serviceWorker.ready;
|
|
115
|
+
const subscription = await registration.pushManager.subscribe({
|
|
116
|
+
userVisibleOnly: true,
|
|
117
|
+
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
|
|
118
|
+
});
|
|
119
|
+
// Send subscription to your backend
|
|
120
|
+
await fetch('/api/subscribe', { method: 'POST', body: JSON.stringify(subscription) });
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Build & Test
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Vite PWA plugin (vite-plugin-pwa 1.3+ supports Vite 3-8)
|
|
127
|
+
npm install vite-plugin-pwa -D
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
// vite.config.js
|
|
132
|
+
import { VitePWA } from 'vite-plugin-pwa';
|
|
133
|
+
|
|
134
|
+
export default {
|
|
135
|
+
plugins: [
|
|
136
|
+
VitePWA({
|
|
137
|
+
registerType: 'autoUpdate',
|
|
138
|
+
workbox: {
|
|
139
|
+
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
|
|
140
|
+
runtimeCaching: [
|
|
141
|
+
{
|
|
142
|
+
urlPattern: /^https:\/\/api\.example\.com\//,
|
|
143
|
+
handler: 'NetworkFirst',
|
|
144
|
+
options: { cacheName: 'api-cache', expiration: { maxEntries: 50, maxAgeSeconds: 3600 } },
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
manifest: { /* manifest.json content */ },
|
|
149
|
+
}),
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Build
|
|
156
|
+
npm run build
|
|
157
|
+
# Output: dist/ directory with PWA-ready files
|
|
158
|
+
|
|
159
|
+
# Test locally
|
|
160
|
+
npx serve dist # Serve over HTTP for testing
|
|
161
|
+
# For full PWA testing: deploy to HTTPS server
|
|
162
|
+
|
|
163
|
+
# Lighthouse audit
|
|
164
|
+
npx lighthouse https://myapp.com --only-categories=pwa
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Framework-Specific PWA
|
|
168
|
+
|
|
169
|
+
### Next.js (App Router)
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// next.config.js
|
|
173
|
+
const withPWA = require('next-pwa')({
|
|
174
|
+
dest: 'public',
|
|
175
|
+
register: true,
|
|
176
|
+
skipWaiting: true,
|
|
177
|
+
});
|
|
178
|
+
module.exports = withPWA({ /* next config */ });
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Vue (Vite)
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
// Same as Vite config above — use vite-plugin-pwa
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### React (Create React App / Vite)
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# CRA: use `cra-template-pwa`
|
|
191
|
+
npx create-react-app my-app --template pwa
|
|
192
|
+
|
|
193
|
+
# Vite: use vite-plugin-pwa (recommended)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## PWA vs Native App
|
|
197
|
+
|
|
198
|
+
| Feature | PWA | Native App |
|
|
199
|
+
|---------|-----|-----------|
|
|
200
|
+
| Distribution | URL (no store) | App Store / Play Store |
|
|
201
|
+
| Install | Browser prompt | Store download |
|
|
202
|
+
| Offline | Yes (Service Worker) | Yes |
|
|
203
|
+
| Push notifications | Yes (web push) | Yes |
|
|
204
|
+
| Device access | Limited (camera, GPS, file) | Full |
|
|
205
|
+
| Performance | Good (WASM, Web Workers) | Best |
|
|
206
|
+
| Size | No install footprint | 20MB+ |
|
|
207
|
+
| Updates | Instant (on next visit) | Store review |
|
|
208
|
+
| iOS support | Limited (no full PWA on Safari < 16.4) | Full |
|
|
209
|
+
|
|
210
|
+
## Common Pitfalls
|
|
211
|
+
|
|
212
|
+
| Issue | Fix |
|
|
213
|
+
|-------|-----|
|
|
214
|
+
| PWA not installable | Ensure manifest.json is correct; serve over HTTPS; check Lighthouse PWA audit |
|
|
215
|
+
| Service Worker not updating | Use `skipWaiting()` + `clients.claim()`; change cache version |
|
|
216
|
+
| iOS push notifications not working | Requires Safari 16.4+; use web push with VAPID keys |
|
|
217
|
+
| Cache serving stale content | Use NetworkFirst for API; CacheFirst for static assets |
|
|
218
|
+
| Offline page not showing | Add fallback page in service worker `fetch` handler |
|
|
219
|
+
| Maskable icon looks wrong | Test with maskable.app; ensure safe zone is respected |
|
|
220
|
+
| Background sync not working | Only supported in Chrome/Edge; use IndexedDB for queued operations |
|