Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/recipes precache and normalize #2718

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 26 additions & 13 deletions packages/workbox-recipes/src/imageCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import {warmStrategyCache} from './warmStrategyCache';
import {registerRoute} from 'workbox-routing/registerRoute.js';
import {CacheFirst} from 'workbox-strategies/CacheFirst.js';
import {CacheableResponsePlugin} from 'workbox-cacheable-response/CacheableResponsePlugin.js';
import {ExpirationPlugin} from 'workbox-expiration/ExpirationPlugin.js';
import {RouteMatchCallback, RouteMatchCallbackOptions} from 'workbox-core/types.js';
import {RouteMatchCallback, RouteMatchCallbackOptions, WorkboxPlugin} from 'workbox-core/types.js';

import './_version.js';

Expand All @@ -18,6 +19,8 @@ export interface ImageCacheOptions {
matchCallback?: RouteMatchCallback;
maxAgeSeconds?: number;
maxEntries?: number;
plugins?: Array<WorkboxPlugin>;
warmCache?: Array<string>;
}

/**
Expand All @@ -27,8 +30,11 @@ export interface ImageCacheOptions {
*
* @param {Object} [options]
* @param {string} [options.cacheName] Name for cache. Defaults to images
* @param {RouteMatchCallback} [options.matchCallback] Workbox callback function to call to match to. Defaults to request.destination === 'image';
* @param {number} [options.maxAgeSeconds] Maximum age, in seconds, that font entries will be cached for. Defaults to 30 days
* @param {number} [options.maxEntries] Maximum number of images that will be cached. Defaults to 60
* @param {WorkboxPlugin[]} [options.plugins] Additional plugins to use for this recipe
* @param {string[]} [options.warmCache] Paths to call to use to warm this cache
*/
function imageCache(options: ImageCacheOptions = {}) {
const defaultMatchCallback = ({request}: RouteMatchCallbackOptions) => request.destination === 'image';
Expand All @@ -37,22 +43,29 @@ function imageCache(options: ImageCacheOptions = {}) {
const matchCallback = options.matchCallback || defaultMatchCallback;
const maxAgeSeconds = options.maxAgeSeconds || 30 * 24 * 60 * 60;
const maxEntries = options.maxEntries || 60;
const plugins = (options.plugins || []);
plugins.push(new CacheableResponsePlugin({
statuses: [0, 200],
}));
plugins.push(new ExpirationPlugin({
maxEntries,
maxAgeSeconds
}));

const strategy = new CacheFirst({
cacheName,
plugins,
});

registerRoute(
matchCallback,
new CacheFirst({
cacheName,
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxEntries,
maxAgeSeconds
}),
],
})
strategy,
);

// Warms the cache
if (options.warmCache) {
warmStrategyCache({urls: options.warmCache, strategy});
}
}

export { imageCache }
4 changes: 3 additions & 1 deletion packages/workbox-recipes/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {imageCache} from './imageCache';
import {staticResourceCache} from './staticResourceCache';
import {pageCache} from './pageCache';
import {offlineFallback} from './offlineFallback';
import {warmStrategyCache} from './warmStrategyCache';

import './_version.js';

Expand All @@ -24,5 +25,6 @@ export {
imageCache,
staticResourceCache,
pageCache,
offlineFallback
offlineFallback,
warmStrategyCache
};
25 changes: 22 additions & 3 deletions packages/workbox-recipes/src/offlineFallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export interface OfflineFallbackOptions {
fontFallback?: string;
}

// Give TypeScript the correct global.
declare let self: ServiceWorkerGlobalScope;

/**
* An implementation of the [comprehensive fallbacks recipe]{@link https://developers.google.com/web/tools/workbox/guides/advanced-recipes#comprehensive_fallbacks}. Be sure to include the fallbacks in your precache injection
*
Expand All @@ -32,21 +35,37 @@ function offlineFallback(options: OfflineFallbackOptions = {}) {
const imageFallback = options.imageFallback || false;
const fontFallback = options.fontFallback || false;

self.addEventListener('install', event => {
const files = [pageFallback];
if (imageFallback) {
files.push(imageFallback);
}
if (fontFallback) {
files.push(fontFallback);
}

event.waitUntil(self.caches.open('workbox-offline-fallbacks').then(cache => cache.addAll(files)));
});

const handler: RouteHandler = async (
options: RouteHandlerCallbackOptions
) => {
const dest = options.request.destination;
const cache = await self.caches.open('workbox-offline-fallbacks');

if (dest === "document") {
return (await matchPrecache(pageFallback)) || Response.error();
const match = await matchPrecache(pageFallback) || await cache.match(pageFallback);
return match || Response.error();
}

if (dest === "image" && imageFallback !== false) {
return (await matchPrecache(imageFallback)) || Response.error();
const match = await matchPrecache(imageFallback) || await cache.match(imageFallback);
return match || Response.error();
}

if (dest === "font" && fontFallback !== false) {
return (await matchPrecache(fontFallback)) || Response.error();
const match = await matchPrecache(fontFallback) || await cache.match(fontFallback);
return match || Response.error();
}

return Response.error();
Expand Down
34 changes: 24 additions & 10 deletions packages/workbox-recipes/src/pageCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import {warmStrategyCache} from './warmStrategyCache';
import {registerRoute} from 'workbox-routing/registerRoute.js';
import {NetworkFirst} from 'workbox-strategies/NetworkFirst.js';
import {CacheableResponsePlugin} from 'workbox-cacheable-response/CacheableResponsePlugin.js';
import {RouteMatchCallback, RouteMatchCallbackOptions} from 'workbox-core/types.js';
import {RouteMatchCallback, RouteMatchCallbackOptions, WorkboxPlugin} from 'workbox-core/types.js';

import './_version.js';

export interface ImageCacheOptions {
cacheName?: string;
matchCallback?: RouteMatchCallback;
networkTimeoutSeconds?: number;
plugins?: Array<WorkboxPlugin>;
warmCache?: Array<string>;
}

/**
Expand All @@ -27,26 +30,37 @@ export interface ImageCacheOptions {
* @param {string} [options.cacheName] Name for cache. Defaults to pages
* @param {RouteMatchCallback} [options.matchCallback] Workbox callback function to call to match to. Defaults to request.mode === 'navigate';
* @param {number} [options.networkTimoutSeconds] Maximum amount of time, in seconds, to wait on the network before falling back to cache. Defaults to 3
* @param {WorkboxPlugin[]} [options.plugins] Additional plugins to use for this recipe
* @param {string[]} [options.warmCache] Paths to call to use to warm this cache
*/
function pageCache(options: ImageCacheOptions = {}) {
const defaultMatchCallback = ({request}: RouteMatchCallbackOptions) => request.mode === 'navigate';

const cacheName = options.cacheName || 'pages';
const matchCallback = options.matchCallback || defaultMatchCallback;
const networkTimeoutSeconds = options.networkTimeoutSeconds || 3;
const plugins = (options.plugins || []);
plugins.push(new CacheableResponsePlugin({
statuses: [0, 200],
}));

const strategy = new NetworkFirst({
networkTimeoutSeconds,
cacheName,
plugins,
});


// Registers the route
registerRoute(
matchCallback,
new NetworkFirst({
networkTimeoutSeconds,
cacheName,
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200],
}),
],
})
strategy
);

// Warms the cache
if (options.warmCache) {
warmStrategyCache({urls: options.warmCache, strategy});
}
}

export { pageCache }
31 changes: 22 additions & 9 deletions packages/workbox-recipes/src/staticResourceCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import {warmStrategyCache} from './warmStrategyCache';
import {registerRoute} from 'workbox-routing/registerRoute.js';
import {StaleWhileRevalidate} from 'workbox-strategies/StaleWhileRevalidate.js';
import {CacheableResponsePlugin} from 'workbox-cacheable-response/CacheableResponsePlugin.js';
import {RouteMatchCallback, RouteMatchCallbackOptions} from 'workbox-core/types.js';
import {RouteMatchCallback, RouteMatchCallbackOptions, WorkboxPlugin} from 'workbox-core/types.js';

import './_version.js';

export interface StaticResourceOptions {
cacheName?: string;
matchCallback?: RouteMatchCallback;
plugins?: Array<WorkboxPlugin>;
warmCache?: Array<string>;
}

/**
Expand All @@ -24,24 +27,34 @@ export interface StaticResourceOptions {
*
* @param {Object} [options]
* @param {string} [options.cacheName] Name for cache. Defaults to static-resources
* @param {RouteMatchCallback} [options.matchCallback] Workbox callback function to call to match to. Defaults to request.destination === 'style' || request.destination === 'script' || request.destination === 'worker';
* @param {WorkboxPlugin[]} [options.plugins] Additional plugins to use for this recipe
* @param {string[]} [options.warmCache] Paths to call to use to warm this cache
*/
function staticResourceCache(options: StaticResourceOptions = {}) {
const defaultMatchCallback = ({request}: RouteMatchCallbackOptions) => request.destination === 'style' || request.destination === 'script' || request.destination === 'worker';

const cacheName = options.cacheName || 'static-resources';
const matchCallback = options.matchCallback || defaultMatchCallback;
const plugins = (options.plugins || []);
plugins.push(new CacheableResponsePlugin({
statuses: [0, 200],
}));

const strategy = new StaleWhileRevalidate({
cacheName,
plugins,
});

registerRoute(
matchCallback,
new StaleWhileRevalidate({
cacheName,
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200],
}),
],
})
strategy,
);

// Warms the cache
if (options.warmCache) {
warmStrategyCache({urls: options.warmCache, strategy});
}
}

export { staticResourceCache }
31 changes: 31 additions & 0 deletions packages/workbox-recipes/src/warmStrategyCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Strategy } from 'workbox-strategies/src/Strategy';

import './_version.js';

export interface WarmStrategyCacheOptions {
urls: Array<string>;
strategy: Strategy;
}

// Give TypeScript the correct global.
declare let self: ServiceWorkerGlobalScope;

/**
* @memberof module:workbox-recipes

* @param {Object} options
* @param {string[]} options.urls Paths to warm the strategy's cache with
* @param {Strategy} options.strategy Strategy to use
*/
function warmStrategyCache(options: WarmStrategyCacheOptions): void {
self.addEventListener('install', event => {
const done = options.urls.map(path => options.strategy.handleAll({
event,
request: new Request(path),
})[1]);

event.waitUntil(Promise.all(done));
});
}

export {warmStrategyCache};