💾 Setting Up a Service Worker+v0.6


A Service Worker is a super powerful browser API that allows you to intercept network requests online or offline. Hydrogen is now able to expose all the generated routes to your Service Worker so that you can do some cool precaching of your routes or whatever fits your use case.

Basic Setup

Create a simple Service Worker file in the root of your project.

sw.js
    
      self.addEventListener('install', (e) => {
        
      });
    
  

Register Service Worker in a layout

default.js
    
      module.exports = () => `
        <script>
          const registerSW = async () => {
            if (!navigator.serviceWorker) {
              return false;
            }

            const reg = await navigator.serviceWorker.register('/sw.js');

            await reg.update();
          }

          registerSW();
        </script>
      `;
    
  

Add the Service Worker file to the config file.

hydrogen.config.js
    
      module.exports = {
        sw: 'sw.js',
      };
    
  

Run npx hydrogen build and the sw.js will be copied to the dist folder

Exposing page routes to your Service Worker

Here is our pages folder structure.

    
      /pages
      |_ javascript
        |_ index.js
        |_ functions.js
        |_ oop
          |_ index.js
      |_ java
        |_ index.js
      |_ index.js
    
  

Hydrogen generates the above folder structure into an array of routes like the example below.

    
      const routes = [
        {
          "route": "/",
          "filename": "index.html",
          "index": true,
          "depth": 0
        },
        {
          "route": "/java",
          "filename": "index.html",
          "index": true,
          "depth": 1 
        },
        {
          "route": "/javascript",
          "filename": "index.html",
          "index": true,
          "depth": 1
        },
        {
          "route": "/javascript",
          "filename": "functions.html"
          "index": false,
          "depth": 1
        },
        {
          "route": "/javascript/oop",
          "filename": "index.html",
          "index": true,
          "depth": 2
        }
      ]
    
  

Your sw.js now has access to the above const routes, so you can do something like creating a simple precache.

You also have access to the const DEV which determines the mode

sw.js
    
      self.addEventListener('install', (e) => {
        e.waitUntil(caches.open('data').then(cache => {
          return cache.addAll(routes.map(({ route }) => route));
        }));
      });
    
  

Versioning your Service Worker cache+v0.9

Managing cache is not the easiest thing in the world, especially if you need to invalidate the cache. You can now get access to the const CACHE_VERSION variable which is determined by the version field in your package.json file

    
      const removeOldCaches = async () => {
        const cacheVersions = await caches.keys();

        const handler = () => Promise.all(cacheVersions.map((cacheName) => caches.delete(cacheName)));

        if (cacheVersions.includes(CACHE_VERSION)) {
          cacheVersions.splice(cacheVersions.indexOf(CACHE_VERSION), 1)
        }

        return handler();
      };

      self.addEventListener('install', (event) => {
        event.waitUntil(removeOldCaches())
      })

      self.addEventListener('fetch', (event) => {
        event.respondWith(
          caches.open(CACHE_VERSION).then((cache) => {
            if (event.request.destination !== 'image') {
              return fetch(event.request);
            }

            return cache.match(event.request).then((cacheResponse) => {
              return cacheResponse || fetch(event.request).then((networkResponse) => {
                cache.put(event.request, networkResponse.clone());
                return networkResponse;
              });
            });
          }),
        );
      });
    
  

Here we are deleting our old caches if we deployed a new version of our app, it will only delete the old cache versions if the new cache version was successfully created. If any of the promises fails in e.waitUntil then the entire install event will cancel