0

I am attempting to implement a service worker for a boilerplate project I'm working on (https://github.com/jonnyasmar/gravity-bp feedback welcome!), but I've hit a snag :(

Problem:

I'm serving the index.html for this boilerplate virtually as an interpreted Twig template via ExpressJS. However, because I'm generating the service worker assets at build time and that is where I'm pointing it to the cacheable static assets, I can't figure out how to tell the service worker that I want it to cache the virtual index.html file served by ExpressJS at runtime.

My most successful attempts successfully cache all static assets (including the asset-manifest.json generated at build time), but will not cache a virtual index.html.

If I convert it to a static html file, the service worker does successfully cache it.

Please be sure to upvote this question to help get some visibility if you are interested in the answer!

Questions:

  1. Is there a way to correct my code to accomplish this?
  2. Is there anything wrong with doing it this way?
  3. If yes to #2, how would you recommend handling this and why?

See the full source on GitHub.

Relevant code:

webpack.config.js:

output: {
  filename: '[name].js',
    chunkFilename: '[chunkhash].js',
    path: path.resolve(__dirname, 'public'),
    publicPath: '/'
},
plugins: {
  new ManifestPlugin({
    fileName: 'asset-manifest.json',
  }),
  new SWPrecacheWebpackPlugin({
    cacheId: 'gravity-bp',
    dontCacheBustUrlsMatching: /\.\w{8}\./,
    filename: 'sw.js',
    minify: true,
    navigateFallback: 'index.html',
    stripPrefix: 'public/',
    swFilePath: 'public/sw.js',
    staticFileGlobs: [
      'public/index.html',
      'public/**/!(*map*|*sw*)',
    ],
  })
}

sw.ts:

const swUrl: string = 'sw.js';

export const register = (): void =>{
  if(process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator){
    const sw: ServiceWorkerContainer = navigator.serviceWorker;
    sw.register(swUrl).then(registration =>{
      registration.onupdatefound = (): any =>{
        const installer: ServiceWorker = registration.installing;
        installer.onstatechange = (): any =>{
          if(installer.state === 'installed'){
            if(sw.controller){
              console.log('New content available.');
            }else{
              console.log('Content cached for offline use.');
            }
          }
        };
      };
    }).catch((error) =>{
      console.error('Failed to register service worker:', error);
    });
  }
};

export const unregister = (): void =>{
  if('serviceWorker' in navigator){
    navigator.serviceWorker.ready.then(registration =>{
      registration.unregister();
    });
  }
};

server.ts:

import * as path from 'path';
const twig = require('twig').__express;
const express = require('express');
const compression = require('compression');

const pkg = require('../../package.json');
const version = pkg.version;

let app = express(),
  ip = '0.0.0.0',
  port = 3000,
  views = path.resolve('./src/views');

app.use(compression());
app.use(express.static('public'));
app.set('view engine', 'twig');
app.engine('.twig', twig);
app.set('views', views);

// Routes
app.get("*", function(req: any, res: any, next: any){
  // vars
  res.locals.version = version;

  res.render('index');
});

let server = app.listen(port, ip, function(){
  let host = server.address().address;
  let port = server.address().port;
  console.log('Gravity Boilerplate ready at http://%s:%s', host, port);
});
Jonny Asmar
  • 1,900
  • 14
  • 16

1 Answers1

2

Within the sw-precache-webpack-plugin documentation it talks about using sw-precache options. The one you should investigate is the dynamicUrlToDependencies setting. See some of these links for more info:

For example, maybe start with this to test:

dynamicUrlToDependencies: {
  '/': 'MAGIC_STRING_HERE'
},

So really, you need to configure the sw-precache WebPack plugin to load a server rendered page as the navigateFallback route.

  • An answer is not complete if it relies on links outside of the SO network. Please add details so that your answer stands alone even if a link is moved or removed. – Evan Weissburg Jan 13 '18 at 04:59
  • Hmm, interesting. I think this a step in the right direction. However, I'm not sure how to hook server-side changes from ExpressJS to the `MAGIC_STRING` used to bust the cache, since that is declared at build time. The idea here is to use Express to indicate new content in `index.html` as opposed to file changes like with the `js` & `css` bundles. – Jonny Asmar Jan 13 '18 at 05:02
  • How about: const dateString = new Date(); dynamicUrlToDependencies: { '/': dateString } – Anonymous Coward Jan 13 '18 at 05:07
  • You are the man! Your latest edit referring me to the other S/O question solved it! I've pushed my code to https://github.com/jonnyasmar/gravity-bp/tree/feature/service_worker if you want to see how I implemented it (starts at line 47 of `webpack.config.json`). – Jonny Asmar Jan 13 '18 at 05:14
  • 1
    I just read this stuff a few days ago, and saw your question come up -- Cheers! :-) – Anonymous Coward Jan 13 '18 at 05:15