0
# Plugins and Cleanup
1
2
Plugin system for extending functionality and cleanup utilities for cache maintenance. These tools help customize precaching behavior and maintain cache hygiene.
3
4
## Capabilities
5
6
### Plugin Management
7
8
Function for adding plugins to the default precaching strategy.
9
10
```typescript { .api }
11
/**
12
* Adds plugins to the precaching strategy.
13
* Plugins added via this method will be used by all precaching operations using the default controller.
14
* @param plugins - Array of Workbox plugins to add to the precaching strategy
15
*/
16
function addPlugins(plugins: WorkboxPlugin[]): void;
17
```
18
19
**Usage Examples:**
20
21
```typescript
22
import { addPlugins, precacheAndRoute } from "workbox-precaching";
23
24
// Add custom plugins before setting up precaching
25
addPlugins([
26
{
27
cacheKeyWillBeUsed: async ({ request, mode }) => {
28
// Add custom cache key logic
29
const url = new URL(request.url);
30
url.searchParams.set('sw-version', '1.0');
31
return url.href;
32
},
33
34
cacheWillUpdate: async ({ request, response }) => {
35
// Only cache successful responses
36
return response.status === 200 ? response : null;
37
},
38
39
cacheDidUpdate: async ({ cacheName, request, oldResponse, newResponse }) => {
40
// Log cache updates
41
console.log(`Cache updated: ${request.url}`);
42
43
// Notify clients about updates
44
const clients = await self.clients.matchAll();
45
clients.forEach(client => {
46
client.postMessage({
47
type: 'CACHE_UPDATED',
48
url: request.url
49
});
50
});
51
}
52
}
53
]);
54
55
// Now set up precaching with plugins applied
56
precacheAndRoute([
57
{ url: "/index.html", revision: "v1.0" },
58
{ url: "/app.js", revision: "v2.1" }
59
]);
60
```
61
62
### Cache Cleanup
63
64
Function for cleaning up outdated caches from previous Workbox versions.
65
66
```typescript { .api }
67
/**
68
* Adds an activate event listener which cleans up incompatible precaches
69
* created by older versions of Workbox.
70
* This is important when updating Workbox versions to prevent cache bloat.
71
*/
72
function cleanupOutdatedCaches(): void;
73
```
74
75
**Usage Examples:**
76
77
```typescript
78
import { cleanupOutdatedCaches, precacheAndRoute } from "workbox-precaching";
79
80
// Clean up old caches when service worker activates
81
cleanupOutdatedCaches();
82
83
// Set up current precaching
84
precacheAndRoute([
85
{ url: "/index.html", revision: "v2.0" },
86
{ url: "/styles.css", revision: "v2.1" }
87
]);
88
89
// The cleanup will automatically run during service worker activation
90
// and remove caches from older Workbox versions
91
```
92
93
### Fallback Plugin
94
95
Plugin class that provides offline fallback responses from the precache.
96
97
```typescript { .api }
98
/**
99
* Plugin that provides offline fallback responses from precache.
100
* Implements WorkboxPlugin interface.
101
*/
102
class PrecacheFallbackPlugin implements WorkboxPlugin {
103
/**
104
* Create a new PrecacheFallbackPlugin instance
105
* @param options - Configuration options
106
*/
107
constructor(options: {
108
/** Precached URL to use as fallback */
109
fallbackURL: string;
110
/** PrecacheController instance (defaults to singleton) */
111
precacheController?: PrecacheController;
112
});
113
114
/**
115
* Plugin callback that returns precached fallback response when handler fails
116
* @returns Promise resolving to fallback response or undefined
117
*/
118
handlerDidError(): Promise<Response | undefined>;
119
}
120
```
121
122
**Usage Examples:**
123
124
```typescript
125
import {
126
PrecacheFallbackPlugin,
127
precacheAndRoute
128
} from "workbox-precaching";
129
import { registerRoute } from "workbox-routing";
130
import { NetworkFirst } from "workbox-strategies";
131
132
// Precache fallback pages
133
precacheAndRoute([
134
{ url: "/offline.html", revision: "v1.0" },
135
{ url: "/error.html", revision: "v1.0" }
136
]);
137
138
// Create fallback plugins
139
const offlineFallback = new PrecacheFallbackPlugin({
140
fallbackURL: "/offline.html"
141
});
142
143
const errorFallback = new PrecacheFallbackPlugin({
144
fallbackURL: "/error.html"
145
});
146
147
// Use with strategies
148
const networkFirstStrategy = new NetworkFirst({
149
cacheName: "pages",
150
plugins: [offlineFallback]
151
});
152
153
// Register routes with fallback
154
registerRoute(
155
({ request }) => request.mode === 'navigate',
156
networkFirstStrategy
157
);
158
159
// Use different fallbacks for different content types
160
registerRoute(
161
({ request }) => request.destination === 'document',
162
new NetworkFirst({
163
cacheName: "documents",
164
plugins: [errorFallback]
165
})
166
);
167
```
168
169
## Advanced Plugin Patterns
170
171
### Comprehensive Logging Plugin
172
173
```typescript
174
import { addPlugins } from "workbox-precaching";
175
176
const loggingPlugin = {
177
cacheKeyWillBeUsed: async ({ request, mode, params }) => {
178
const cacheKey = params?.cacheKey || request.url;
179
console.log(`Cache key for ${request.url}: ${cacheKey}`);
180
return cacheKey;
181
},
182
183
cacheWillUpdate: async ({ request, response, event }) => {
184
console.log(`Caching ${request.url}:`, {
185
status: response.status,
186
statusText: response.statusText,
187
headers: Object.fromEntries(response.headers.entries())
188
});
189
return response;
190
},
191
192
cacheDidUpdate: async ({ cacheName, request, oldResponse, newResponse, event }) => {
193
console.log(`Cache updated in ${cacheName}:`, {
194
url: request.url,
195
oldStatus: oldResponse?.status,
196
newStatus: newResponse?.status
197
});
198
},
199
200
cachedResponseWillBeUsed: async ({ cacheName, request, cachedResponse, event }) => {
201
console.log(`Serving from cache ${cacheName}: ${request.url}`);
202
return cachedResponse;
203
}
204
};
205
206
addPlugins([loggingPlugin]);
207
```
208
209
### Security Plugin
210
211
```typescript
212
import { addPlugins } from "workbox-precaching";
213
214
const securityPlugin = {
215
cacheWillUpdate: async ({ request, response }) => {
216
// Only cache HTTPS responses
217
if (!request.url.startsWith('https://')) {
218
console.warn(`Refusing to cache non-HTTPS resource: ${request.url}`);
219
return null;
220
}
221
222
// Check response headers for security
223
const contentType = response.headers.get('content-type');
224
if (contentType && contentType.includes('application/javascript')) {
225
const csp = response.headers.get('content-security-policy');
226
if (!csp) {
227
console.warn(`JavaScript resource without CSP: ${request.url}`);
228
}
229
}
230
231
return response;
232
},
233
234
cachedResponseWillBeUsed: async ({ cachedResponse, request }) => {
235
// Add security headers to cached responses
236
if (cachedResponse) {
237
const secureResponse = new Response(cachedResponse.body, {
238
status: cachedResponse.status,
239
statusText: cachedResponse.statusText,
240
headers: {
241
...cachedResponse.headers,
242
'X-Content-Type-Options': 'nosniff',
243
'X-Frame-Options': 'DENY',
244
'X-Served-From': 'sw-cache'
245
}
246
});
247
return secureResponse;
248
}
249
return cachedResponse;
250
}
251
};
252
253
addPlugins([securityPlugin]);
254
```
255
256
### Performance Monitoring Plugin
257
258
```typescript
259
import { addPlugins } from "workbox-precaching";
260
261
const performancePlugin = {
262
requestWillFetch: async ({ request, event }) => {
263
// Mark start time
264
(request as any).__startTime = performance.now();
265
return request;
266
},
267
268
fetchDidSucceed: async ({ request, response }) => {
269
const startTime = (request as any).__startTime;
270
if (startTime) {
271
const duration = performance.now() - startTime;
272
console.log(`Network fetch for ${request.url}: ${duration.toFixed(2)}ms`);
273
274
// Report to analytics
275
if ('gtag' in self) {
276
(self as any).gtag('event', 'sw_fetch_duration', {
277
url: request.url,
278
duration: Math.round(duration)
279
});
280
}
281
}
282
return response;
283
},
284
285
cacheDidUpdate: async ({ request, cacheName }) => {
286
// Track cache updates
287
console.log(`Cache update: ${request.url} in ${cacheName}`);
288
289
if ('gtag' in self) {
290
(self as any).gtag('event', 'sw_cache_update', {
291
url: request.url,
292
cache_name: cacheName
293
});
294
}
295
}
296
};
297
298
addPlugins([performancePlugin]);
299
```
300
301
## Complete Setup Examples
302
303
### Production Setup with All Features
304
305
```typescript
306
import {
307
precacheAndRoute,
308
cleanupOutdatedCaches,
309
addPlugins,
310
PrecacheFallbackPlugin
311
} from "workbox-precaching";
312
import { registerRoute } from "workbox-routing";
313
import { NetworkFirst, StaleWhileRevalidate } from "workbox-strategies";
314
315
// 1. Clean up old caches
316
cleanupOutdatedCaches();
317
318
// 2. Add global plugins
319
addPlugins([
320
// Production logging
321
{
322
cacheDidUpdate: async ({ request, cacheName }) => {
323
console.log(`Updated ${request.url} in ${cacheName}`);
324
325
// Notify app about updates
326
const clients = await self.clients.matchAll();
327
clients.forEach(client => {
328
client.postMessage({
329
type: 'SW_CACHE_UPDATED',
330
url: request.url
331
});
332
});
333
}
334
},
335
336
// Error handling
337
{
338
fetchDidFail: async ({ originalRequest, error }) => {
339
console.error(`Fetch failed for ${originalRequest.url}:`, error);
340
}
341
}
342
]);
343
344
// 3. Set up precaching with fallbacks
345
precacheAndRoute([
346
{ url: "/", revision: "v1.0.0" },
347
{ url: "/offline.html", revision: "v1.0.0" },
348
{ url: "/error.html", revision: "v1.0.0" },
349
...self.__WB_MANIFEST // Generated manifest
350
]);
351
352
// 4. Create fallback plugins
353
const navigationFallback = new PrecacheFallbackPlugin({
354
fallbackURL: "/offline.html"
355
});
356
357
const documentFallback = new PrecacheFallbackPlugin({
358
fallbackURL: "/error.html"
359
});
360
361
// 5. Set up runtime caching with fallbacks
362
registerRoute(
363
({ request }) => request.mode === 'navigate',
364
new NetworkFirst({
365
cacheName: "pages",
366
plugins: [navigationFallback]
367
})
368
);
369
370
registerRoute(
371
({ request }) => request.destination === 'document',
372
new StaleWhileRevalidate({
373
cacheName: "documents",
374
plugins: [documentFallback]
375
})
376
);
377
```
378
379
### Development Setup with Debug Features
380
381
```typescript
382
import {
383
precacheAndRoute,
384
addPlugins,
385
cleanupOutdatedCaches
386
} from "workbox-precaching";
387
388
// Enable detailed logging for development
389
const debugPlugin = {
390
cacheKeyWillBeUsed: async ({ request, mode }) => {
391
console.log(`[DEBUG] Cache key for ${request.url} (${mode})`);
392
return request.url;
393
},
394
395
cacheWillUpdate: async ({ request, response }) => {
396
console.log(`[DEBUG] Caching ${request.url}:`, {
397
status: response.status,
398
headers: Array.from(response.headers.entries())
399
});
400
return response;
401
},
402
403
cachedResponseWillBeUsed: async ({ request, cachedResponse }) => {
404
console.log(`[DEBUG] Serving cached ${request.url}`);
405
return cachedResponse;
406
}
407
};
408
409
// Add debug plugin
410
addPlugins([debugPlugin]);
411
412
// Clean up during development
413
cleanupOutdatedCaches();
414
415
// Precache development assets
416
precacheAndRoute([
417
{ url: "/", revision: Date.now().toString() }, // Always update in dev
418
{ url: "/dev-tools.html", revision: "dev" }
419
]);
420
```