Skip to content

Instantly share code, notes, and snippets.

@jeffposnick
Created October 6, 2019 16:54
Show Gist options
  • Select an option

  • Save jeffposnick/8b3465e933275f15e6cb1ce6737403c0 to your computer and use it in GitHub Desktop.

Select an option

Save jeffposnick/8b3465e933275f15e6cb1ce6737403c0 to your computer and use it in GitHub Desktop.

Revisions

  1. jeffposnick created this gist Oct 6, 2019.
    114 changes: 114 additions & 0 deletions new-service-worker.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,114 @@
    import {CacheFirst, NetworkOnly} from 'workbox-strategies';
    import {cacheNames} from 'workbox-core';
    import {cleanupOutdatedCaches, getCacheKeyForURL, precacheAndRoute} from 'workbox-precaching';
    import {ExpirationPlugin} from 'workbox-expiration';
    import {initialize as initializeOfflineAnalytics} from 'workbox-google-analytics';
    import {registerRoute, setCatchHandler} from 'workbox-routing';
    import {RouteHandlerCallbackOptions} from 'workbox-core/types';
    import {skipWaiting} from 'workbox-core';
    import nunjucks from 'nunjucks/browser/nunjucks';

    precacheAndRoute(self.__WB_MANIFEST);
    cleanupOutdatedCaches();

    async function getPrecachedResponse(url: string) {
    const cacheKey = getCacheKeyForURL(url);
    if (!cacheKey) {
    throw new Error(`${url} is not in the precache manifest.`);
    }

    const cache = await caches.open(cacheNames.precache);
    const cachedResponse = await cache.match(cacheKey);
    if (!cachedResponse) {
    throw new Error(`${url} is not precached.`);
    }

    return cachedResponse;
    }

    const CacheStorageLoader = nunjucks.Loader.extend({
    async: true,

    getSource: async function(name: string, callback: Function) {
    try {
    const path = `/_posts/_includes/${name}`;
    const cachedResponse = await getPrecachedResponse(path);
    const src = await cachedResponse.text();
    callback(null, {src, path, noCache: false});
    } catch(error) {
    callback(error);
    }
    }
    });

    const nunjucksEnv = new nunjucks.Environment(
    new CacheStorageLoader()
    );

    let _site: {string: any};
    async function getSiteData() {
    if (!_site) {
    const siteDataResponse = await getPrecachedResponse('/_posts/_data/site.json');
    _site = await siteDataResponse.json();
    }

    return _site;
    }

    const postHandler = async (options: RouteHandlerCallbackOptions) => {
    const {params} = options;
    if (!(params && Array.isArray(params))) {
    throw new Error(`Couldn't get parameters from router.`);
    }

    const site = await getSiteData();

    // params[3] corresponds to post.fileSlug in 11ty.
    const cachedResponse = await getPrecachedResponse(`/_posts/${params[3]}.json`);

    const context = await cachedResponse.json();
    context.site = site;
    context.content = context.html;

    const html: string = await new Promise((resolve, reject) => {
    nunjucksEnv.render(
    context.layout,
    context,
    (error: Error | undefined, html: string) => {
    if (error) {
    return reject(error);
    }
    return resolve(html);
    }
    );
    });

    const headers = {
    'content-type': 'text/html',
    };
    return new Response(html, {headers});
    };

    registerRoute(
    new RegExp('/(\\d{4})/(\\d{2})/(\\d{2})/(.+)\\.html'),
    postHandler
    );

    registerRoute(
    new RegExp('/assets/images/'),
    new CacheFirst({
    cacheName: 'images',
    plugins: [
    new ExpirationPlugin({
    maxEntries: 20,
    }),
    ],
    })
    );

    // If anything goes wrong when handling a route, return the network response.
    setCatchHandler(new NetworkOnly());

    initializeOfflineAnalytics();

    skipWaiting();
    104 changes: 104 additions & 0 deletions old-service-worker.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,104 @@
    importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.0.0-alpha.1/workbox-sw.js');
    importScripts('https://cdn.jsdelivr.net/npm/[email protected]/browser/nunjucks.min.js');

    workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);
    workbox.precaching.cleanupOutdatedCaches();

    const CacheStorageLoader = nunjucks.Loader.extend({
    async: true,

    getSource: async function(name, callback) {
    try {
    const path = `/_posts/_includes/${name}`;
    const cachedResponse = await caches.match(
    workbox.precaching.getCacheKeyForURL(path), {
    cacheName: workbox.core.cacheNames.precache,
    }
    );
    const src = await cachedResponse.text();
    callback(null, {src, path, noCache: false});
    } catch(error) {
    callback(error);
    }
    }
    });

    const nunjucksEnv = new nunjucks.Environment(
    new CacheStorageLoader()
    );

    let _site;
    async function initSiteData() {
    if (!_site) {
    const siteDataResponse = await caches.match(
    workbox.precaching.getCacheKeyForURL('/_posts/_data/site.json'), {
    cacheName: workbox.core.cacheNames.precache,
    }
    );
    _site = await siteDataResponse.json();
    }

    return _site;
    }

    const postHandler = async ({params}) => {
    const site = await initSiteData();

    // params[3] corresponds to post.fileSlug in 11ty.
    const cachedResponse = await caches.match(
    workbox.precaching.getCacheKeyForURL(`/_posts/${params[3]}.json`), {
    cacheName: workbox.core.cacheNames.precache,
    }
    );

    const context = await cachedResponse.json();
    context.site = site;
    context.content = context.html;

    const html = await new Promise((resolve, reject) => {
    nunjucksEnv.render(
    context.layout,
    context,
    (error, html) => {
    if (error) {
    return reject(error);
    }
    return resolve(html);
    }
    );
    });

    const headers = {
    'content-type': 'text/html',
    };
    return new Response(html, {headers});
    };

    workbox.routing.registerRoute(
    new RegExp('/(\\d{4})/(\\d{2})/(\\d{2})/(.+)\\.html'),
    postHandler
    );

    workbox.routing.registerRoute(
    new RegExp('/assets/images/'),
    new workbox.strategies.CacheFirst({
    cacheName: 'images',
    plugins: [
    new workbox.expiration.Plugin({
    maxEntries: 20,
    }),
    ],
    })
    );

    workbox.routing.registerRoute(
    new RegExp('https://storage\\.googleapis\\.com/workbox-cdn/releases/.+/workbox-window\\.prod\\.mjs'),
    new workbox.strategies.CacheFirst()
    );

    // If anything goes wrong when handling a route, return the network response.
    workbox.routing.setCatchHandler(new workbox.strategies.NetworkOnly());

    workbox.googleAnalytics.initialize();

    workbox.core.skipWaiting();