Preact-cli: index.html should be excluded from service worker cache

Created on 29 Jan 2018  路  10Comments  路  Source: preactjs/preact-cli


Do you want to request a feature or report a bug?

bug

What is the current behaviour?

bundle.{hash}.js tries to load the old version of route-{page}.chunk.{hash}.js when JS code is modified, which results in an error.

If the current behaviour is a bug, please provide the steps to reproduce.

  1. Create new project from template (I used material)
  2. Run the project on localhost ($ npm run serve)

    • The following JS files are created



      • bundle.2eb65.js


      • route-home.chunk.f678b.js


      • route-profile.chunk.c26a1.js



  3. Open https://localhost:8080 in browser

    • index.html, the JS files listed above, and other resources are cached by service worker

  4. Edit src/routes/profile/index.js (I added console.log('something has changed'); at render() method)
  5. Rerun $ npm run serve

    • The JS files are updated as follows



      • bundle.2eb65.js -> bundle.a5db3.js


      • route-home.chunk.f678b.js (unchanged)


      • route-profile.chunk.c26a1.js -> route-profile.chunk.27ce5.js



  6. Reload https://localhost:8080 in browser

    • index.html cached at the step 3 is used as the response for https://localhost:8080

    • Cached index.html requests bundle.2eb65.js (the old version), which is also restored from the service worker cache

    • In the background, service worker is updated and activated immediately due to skipWaiting(), which triggers deletion of the old caches (such as route-profile.chunk.c26a1.js)

  7. Click link to the Profile page

    • bundle.2eb65.js (the old version) requests route-profile.chunk.c26a1.js (the old version), which exists neither on the server nor in the service worker cache



      • At this point an error occurs.



What is the expected behaviour?

All the client side code should be updated to the latest version when JS code is modified.

Excluding index.html from the service worker cache (i.e., adding /index\.html$/ here), which prevents the old index.html from requesting the old bundle at the step 6-ii, should fix the problem.

discussion maybe stale

Most helpful comment

Why is this one closed? Doesn't this bug occur every time a new version of bundle.{hash}.js is produced?

All 10 comments

How to do the above in preact.config:

  const swPlugin = config.plugins.find(
    p => p instanceof SWPrecacheWebpackPlugin,
  );

  if (swPlugin) {
    swPlugin.options.staticFileGlobsIgnorePatterns.push(/index\.html$/);
  }

This will essentially disable offline capabilities as you cannot load index.html offline. However, the current solution also breaks as the old bundle is loaded as described above. It would be nice to have a built-in solution to this in preact cli.

I think what we need to do is force the bundle hash to change when ANY chunk changes. Because we are extracting page chunks, which incapsulate the components, it COULD be the case that route bundles change but the main bundle hash doesn't change.

Otherwise, the current setup is the way that service worker works. You will get a stale app on the first visit after the new assets have been cached. This includes index.html. Every visit thereafter will be serving the new app assets from memory. This is also why offline-plugin forces a hard reload on the SW "updated" hook.

Not caching the index does not solve this issue, as attempted in the linked PR, because an HTML file is needed in order for the offline app to boot. Disregarding that point, if the new HTML file still had an asset link to a "new" bundle whose hash did not change, then the same problem would occur.

@lukeed @sdbondi @kimamula I spent little more time on this and from my research so far sw-precache-webpack-plugin should work correctly with existing setup (including offline capability).

I uploaded my repro into separate repo here - but voila I could not reproduce the issue as reported originally. As documented in this repo, it seems at step 5 above, sw.js in fact has new hash for index.html which is downloaded correctly when browser is refreshed.

Am I missing something?

I guess if a route directly included in root's index.html is changed only then the hash of index.js changes.

Simplest sollution to this is to extract webpack's asset manifest and add it in index.html.
so any route changes -> manifest's hash changes(i assume) -> index's hash changes

@lukeed

Not caching the index does not solve this issue, as attempted in the linked PR, because an HTML file is needed in order for the offline app to boot.

I agree.
I was missing this point.

@osdevisnot
I git cloned your repo and could reproduce the issue.
Please click the "Profile" link in the menu after you reload browser in your "3rd run" and you will see Uncaught SyntaxError: Unexpected token < error in console.

As shown in your image, index.html (localhost) is "from ServiceWorker" in your "3rd run".

Image for 3rd run

Therefore, the older version of index.html that has been cached by ServiceWorker is used in your "3rd run" even if the hash for index.html in sw.js is updated, while the cache for route-profile is renewed immediately after the browser reload which causes the issue.

It seems to me that setting skipWaiting to false should be the correct solution.

When skipWaiting is true, the new service worker's activate handler will
be called immediately, and any out of date cache entries from the previous
service worker will be deleted. Please keep this in mind if you rely on older
cached resources to be available throughout the page's lifetime, because, for
example, you defer the loading of some resources
until they're needed at runtime.

I haven't tried but It could be that somebody wants:

const SWPrecacheWebpackPlugin = helpers.getPluginsByName(config, 'SWPrecacheWebpackPlugin')[0]
if(SWPrecacheWebpackPlugin){
  const {plugin} = SWPrecacheWebpackPlugin;
  const unwanted = [
    /^[^\.]*(\.html)?$/
  ]
  plugin.options.staticFileGlobsIgnorePatterns = plugin.options.staticFileGlobsIgnorePatterns.concat(unwanted);
  plugin.options.runtimeCaching = plugin.options.runtimeCaching || []
  plugin.options.runtimeCaching.push({
    urlPattern: /^[^\.]*(\.html)?$/,
    handler: 'networkFirst'
  })
}

Then online versions are tried firstly,
For me the index.html works as expected

something similar i had in my mind. does total offline works with this?

Even if I don't take a lot of time to test it, it's seem to work quite good

Why is this one closed? Doesn't this bug occur every time a new version of bundle.{hash}.js is produced?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

oren picture oren  路  4Comments

thangngoc89 picture thangngoc89  路  3Comments

nephix picture nephix  路  4Comments

haggen picture haggen  路  3Comments

ethanwu10 picture ethanwu10  路  3Comments