I am trying to add a serviceworker to an existing React app with this filesystem layout: Filesystem
Basically a bit of initialization code is stored in the public folder, and all code of importance is in the src folder. In the serviceWorker.js file, I made an array of filenames to cache and call that array in the ‘install’ event listener, and if I check DevTools I can see that the filenames are present in the cache: when I preview the data in Chrome DevTools however, I see that the code inside the cached files is all from index.html. In fact, I can add anything I want to the filename array and I will find it in cached storage only to find that it is storing the index.html code. It seems like no matter what file I try to add to the cache, only index.html gets loaded.
ServiceWorker.js:
let CACHE_NAME = "MG-cache-v2"; const urlsToCache = [ '/', '/index.html', '/src/App.js', '/monkey' ]; self.addEventListener('install', function (event) { //perform install steps event.waitUntil( caches.open(CACHE_NAME).then(function (cache) { console.log('Opened MG_Cache'); return cache.addAll(urlsToCache); }).catch(function (error) { console.error("Error loading cache files: ", error); }) ); self.skipWaiting(); }); self.addEventListener('fetch', function (event) { event.respondWith(caches.match(event.request).then(function (response) { if (response) { return response; } return fetch(event.request); }) ); }); self.addEventListener('activate', (event) => { event.waitUntil(async function () { const cacheNames = await caches.keys(); await Promise.all( cacheNames.filter((cacheName) => { //Return true if you want to remove this cache, //but remember that caches are shared across the whole origin return; }).map(cacheName => caches.delete(cacheName)) ); }) })
Portion of index.html:
<script> if ('serviceWorker' in navigator) { window.addEventListener('load', function () { navigator.serviceWorker.register('serviceWorker.js').then(function (registration) { // Registration was successful console.log("ServiceWorker registration successful with scope: ", registration.scope); }, function (err) { // registration failed : (console.log('ServiceWorker registration failed: ', err)); }); }); } </script>
Google Devtools Preview: All files are the same
I have tried a variety of naming strategies in the filename array but all have ended with the same result. At this point I’m at a complete loss.
EDIT: While this does not solve my problem, I found an answer to another problem that gives a little guidance. It seems like the server never finds the file I request and thus returns index.html. I tried placing the serviceWorker.js file in the src folder and moving the service worker registration to App.js and got an error:
`DOMException: Failed to register a ServiceWorker for scope ('http://localhost:3000/src/') with script ('http://localhost:3000/src/serviceWorker.js'): The script has an unsupported MIME type ('text/html'). `
This suggests that the server somehow doesn’t have access to the src folder, only public. Any idea why that may be?
Advertisement
Answer
An important piece of information I left out it that I’m using Create-React-App. Because of the enforced layout of the filesystem, the serviceWorker must be placed in the public folder: at the same time, the scope of service workers by default is the folder that they are placed in. According to this answer, changing the scope of the service worker to be a level above the folder that it is in requires adding to the HTTP header response of the service worker (not entirely sure what that means), and doing something like that assumes you have some form of a local server set up. Alas, thus far I have just been using npm start
to test my app and pushing onto nsmp to make the site live, thus have negleted to do any form of server implementation myself (I know, not very smart of me).
My hotfix was to create a new temporary app with the npx create-react-app my-app --template cra-template-pwa
command, copy all files pertaining to service workers from that app (serviceWorkerRegistration.js, service-worker.js, index.js, potentially setupTests.js), and paste them into my app. Then I could simply follow this tutorial. Now my site works offline.