-
Notifications
You must be signed in to change notification settings - Fork 857
Description
Library Affected:
workbox-webpack-plugin@4.2.0 with workbox-build@4.3.1
Browser & Platform:
all browsers
Issue or Feature Request Description:
Hi there, we've got service worker enabled for caching purposes and we also have notification bar in app which informs user about its new version. This notification bar also includes button to update app by click :-)
registerServiceWorker.js of CRA framework with little modifications has following code:
const isLocalhost = Boolean(
window.location.hostname === "localhost" ||
// [::1] is the IPv6 localhost address.
window.location.hostname === "[::1]" ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
)
export default function register(config) {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location)
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return
}
window.addEventListener("load", () => {
let isInitialServiceWorker = false
if (!navigator.serviceWorker.controller) {
isInitialServiceWorker = true
}
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config)
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl, config)
}
navigator.serviceWorker.addEventListener("controllerchange", () => {
if (navigator.serviceWorker.controller) {
if (!isInitialServiceWorker) {
window.location.reload()
}
}
})
})
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing
if (installingWorker == null) {
return
}
installingWorker.onstatechange = () => {
if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log("New content is available; please refresh.")
if (config && config.onUpdate) {
config.onUpdate(registration)
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log("Content is cached for offline use.")
}
}
}
}
if (registration.waiting === null) {
setInterval(() => registration.update(), 3600000)
} else {
// this needs to be done because of firefox, is it doesn't have
// serviceWorker in installing state before app load
const waitingWorker = registration.waiting
if (waitingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
console.log("New content is available; please refresh.")
if (config && config.onUpdate) {
config.onUpdate(registration)
}
}
}
}
})
.catch(error => {
console.error("Error during service worker registration:", error)
})
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get("content-type").indexOf("javascript") === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload()
})
})
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config)
}
})
.catch(() => {
console.log("No internet connection found. App is running in offline mode.")
})
}
and I append this to resulted serviceWorker.js file for "update" app functionality
self.addEventListener('message', function(event) {
// Force SW upgrade (activation of new installed SW version)
if ( event.data === 'skipWaiting' ) {
/* eslint-disable-next-line no-restricted-globals */
self.skipWaiting();
}
});
From time to time, users are reporting app unavailability with following error in console:
SyntaxError: expected expression, got '<'
The whole problem is that sometimes index.html
file is not replaced with the newest one, so it points to not-existing css/js files. How can it be possible?