Neos with Service Worker (Workbox implementation)

Hello there,

I am currently trying to implement a Service Worker on an existing Website using Googles Workbox tool, but I am running into some problems.

If I drop the sw.js into the “DistributionPackages\PackageName.Site\Resources\Public\Frontend” the scope of the service worker is wrong (even when I defined {scope: ‘/’}, throws a HTTP error).

If I place the sw.js in the Web directory I cant load it (added “sw.js” to the .htacces RewriteRule).

Can anyone give me a hint how to properly implement this in Neos.

Thank you in advance.

Hi Ambros,

Can you share docs or any hints on how it should work? What is it capable of? And what is error is thrown at you?

Yes of course.

Since I don’t have a gulp, node or webpack setup I used the Workbox CLI tool, which I installed globaly (on a local Windows Linux Subsystem).

I configured a workbox-config.js which looks like this. This is just the base config.

module.exports = {
globDirectory: ‘Web/_Resources/Static/Packages/Package.Site/Frontend/’,
globPatterns: [
“css/photoswipe.css”,
“css/bootstrap.min.css”,
“js/bootstrap.min.js”,
“js/custom.js”,
“js/main.js”,
“js/lightbox.min.js”,
“img/.png",
"img/
.ico”,
“img/*.svg”
],
swDest: ‘“Path to Neos main Folder”/Neos/Web/_Resources/Static/Packages/Package.Site/Frontend/sw.js’,

// Define runtime caching rules.
runtimeCaching: [{
// Match any request that ends with .png, .jpg, .jpeg or .svg.
urlPattern: /.(?:png|jpg|jpeg|svg)$/,

// Apply a cache-first strategy.
handler: 'CacheFirst',

options: {
  // Use a custom cache name.
  cacheName: 'images'
  },

}],
};

This generates fine with

workbox generateSW workbox-config.js

Adding a few lines of code to the main .js file

if (‘serviceWorker’ in navigator) {
window.addEventListener(‘load’, () => {
navigator.serviceWorker.register(‘/_Resources/Static/Packages/Package.Site/Frontend/sw.js’, {scope: ‘/’})
.then(registration => {
console.log(Service Worker registered! Scope: ${registration.scope});
})
.catch(err => {
console.log(Service Worker registration failed: ${err});
});
});
}

With the {scope:‘/’} parameter addd I get the following error:

The path of the provided scope (‘/’) is not under the max scope allowed (‘/_Resources/Static/Packages/Package.Site/Frontend/’). Adjust the scope, move the Service Worker script, or use the Service-Worker-Allowed HTTP header to allow the scope.

Without the scope the service worker registers fine, but only works on files that are in the same directory or further down the hierarchy, like pictures, etc.

Normally service workers must be installed in the web root to give them access to the whole website. The goal is to cache all the web resources and let the service worker handle all the request, so the website is available offline.

I hope this is enough information.

Kind Regards,

Ambros Ecke

I figured out how to avert the HTTP header error.

I added the following to my .httaccess file

<Files “sw.js”>
Header Set Service-Worker-allowed “/”
<*/*Files>

Info from: Quickstart - Baqend Guide

No the service worker gets registered properly for the whole domain.

No I need to figure out how to cache all the files ( like all the ?bust=something files).

I keep you posted on my progress.

If anyone has done this before and want to share their solution, would be great.

On Neos Con 2019 there was this talk about Service Worker and I thought that someone must have figured this out already.

1 Like

Hi,

for cache busting you can use several mechanisms. The easiest is Flowpack.CacheBuster.
Another option is to add a hash based on the file hash in the renderer and have a apache or nginx rule to still lead to the normal filename. Or some specific build step that would do this.

I had to get rid of the Cache Buster to run the service worker properly. So, I got rid of the “Flowpack.CacheBuster” module in my setup. If anyone knows how to cache files with a Cache Buster in a service worker, please share.

I now got a functioning service worker for my webpage, that uses the CacheFirst method for all files. I thought I share the implementation.

  1. I followed the steps in the Workbox CLI starter guide and dropped the workbox-config.js in my main Neos directory: Conheça os recursos do Workbox  |  Chrome for Developers
    The configuration file looked like this in the end:

module.exports = {
globDirectory: ‘Web/_Resources/Static/Packages/YourSitepackage.Site/Frontend/’,
globPatterns: [
// All the necessary static files in the Frontend directory
“css/photoswipe.css”,
“css/bootstrap.min.css”,
“js/bootstrap.min.js”,
“js/custom.js”,
“js/lightbox.min.js”,
“img/.png",
"img/
.ico”,
“img/*.svg”,
“manifest.json”
],
swDest: ‘path_to_neos_dir/Neos/Web/_Resources/Static/Packages/YourSitepackage.Site/Frontend/sw.js’,

// Define runtime caching rules.
runtimeCaching: [{
// Match any request that ends with .png, .jpg, .jpeg or .svg.
urlPattern: /.(?:png|jpg|jpeg|svg)$/,
// Apply a cache-first strategy.
handler: ‘CacheFirst’,
options: {
// Use a custom cache name.
cacheName: ‘images’
},
},
{
// Cache the homepage
urlPattern: ‘/’,
// Apply a cache-first strategy.
handler: ‘CacheFirst’
},
{
// Match any request that ends with .html - to cache all the other pages
urlPattern: /.(?:html)$/,
// Apply a cache-first strategy.
handler: ‘CacheFirst’
},
{
// Cache Google fonts
urlPattern: /^https://fonts.googleapis.com/,
// Apply a cache-first strategy.
handler: ‘StaleWhileRevalidate’,
options: {
// Use a custom cache name.
cacheName: ‘google-fonts-stylesheets’
},
},
{
// Cache Third Party Requests
urlPattern: ‘https://code.jquery.com/jquery-3.4.1.slim.min.js’,
// Apply a cache-first strategy.
handler: ‘CacheFirst’
},
{
// Cache Third Party Requests
urlPattern: ‘https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js’,
// Apply a cache-first strategy.
handler: ‘CacheFirst’
}],
};

  1. added code to my main .js file to install the service worker with scope: ‘/’

// Check that service workers are supported
if (‘serviceWorker’ in navigator) {
window.addEventListener(‘load’, () => {
navigator.serviceWorker.register(‘/_Resources/Static/Packages/YourSitepackage.Site/Frontend/sw.js’, {scope: ‘/’})
.then(registration => {
console.log(Service Worker registered! Scope: ${registration.scope});
})
.catch(err => {
console.log(Service Worker registration failed: ${err});
});
});
}

  1. Added the following code to the .htaccess file to make the scope global

<Files “sw.js”>
Header Set Service-Worker-allowed “/”
<*/*Files>

  1. Set the URL suffix to .html to cache all the different pages. I would like to do it without the suffix but I could not get it to work properly…

defaultUriSuffix: ‘.html’

  1. Got rid of all Cache Buster. In my case deleting the “Flowpack.CacheBuster” package and setting the Carbon IncludeAssets preset to CacheBuster: false
Carbon:
    IncludeAssets:
       Default:
           CacheBuster: false

Now the whole Website gets cached without max cache size limits, which is intentional. Normally you would implement an expiration and a max file cached attribute (images, etc.), described in the link above.

If anyone got a more elegant solution (without the .html) please share.