| // In production, we register a service worker to serve assets from local cache. | |
| // This lets the app load faster on subsequent visits in production, and gives | |
| // it offline capabilities. However, it also means that developers (and users) | |
| // will only see deployed updates on the "N+1" visit to a page, since previously | |
| // cached resources are updated in the background. | |
| // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. | |
| // This link also includes instructions on opting out of this behavior. | |
| 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}$/ | |
| ) | |
| ); | |
| const isHttp = Boolean(window.location.protocol === 'http:'); | |
| interface SWOptions { | |
| onUpdated?: () => void; | |
| onInstalled?: () => void; | |
| } | |
| export default function register(swUrl, opts: SWOptions = {}) { | |
| if ('serviceWorker' in navigator) { | |
| // The URL constructor is available in all browsers that support SW. | |
| // @ts-ignore | |
| 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', () => { | |
| if (!isLocalhost && !isHttp) { | |
| // It's neither localhost nor http. Just register service worker | |
| registerValidSW(swUrl, opts); | |
| } else if (isLocalhost) { | |
| // This is running on localhost. Lets check if a service worker still exists or not. | |
| checkValidServiceWorker(swUrl, opts); | |
| } | |
| } | |
| } | |
| function registerValidSW( | |
| swUrl: string, | |
| { onUpdated, onInstalled }: SWOptions = {} | |
| ) { | |
| navigator.serviceWorker | |
| .register(swUrl) | |
| .then(registration => { | |
| registration.onupdatefound = () => { | |
| const installingWorker = registration.installing; | |
| 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. | |
| if (onUpdated) { | |
| onUpdated(); | |
| } | |
| } else if (onInstalled) { | |
| // At this point, everything has been precached. | |
| // It's the perfect time to display a | |
| // "Content is cached for offline use." message. | |
| onInstalled(); | |
| } | |
| } else if (installingWorker.state === 'redundant') { | |
| if ('storage' in navigator && 'estimate' in navigator.storage) { | |
| navigator.storage.estimate().then(results => { | |
| const percentUsed = results.usage / results.quota; | |
| // Let's assume that if we're using 95% of our quota, then this failure | |
| // was due to quota exceeded errors. | |
| // TODO: Hardcoding a threshold stinks. | |
| if (percentUsed >= 0.95) { | |
| // Get rid of the existing SW so that we're not stuck | |
| // with the previously cached content. | |
| registration.unregister(); | |
| // Let's assume that we have some way of doing this without inadvertantly | |
| // blowing away storage being used on the origin by something other than | |
| // our service worker. | |
| // I don't think that the Clear-Site-Data: header helps here, unfortunately. | |
| self.caches.keys().then(names => { | |
| names.forEach(name => { | |
| self.caches.delete(name); | |
| }); | |
| }); | |
| // TODO clear indexeddb | |
| } | |
| }); | |
| } else { | |
| // What about browsers that don't support navigator.storage.estimate()? | |
| // There's no way of guessing why the service worker is redundant. | |
| } | |
| } | |
| }; | |
| }; | |
| }) | |
| .catch(error => { | |
| console.error('Error during service worker registration:', error); | |
| }); | |
| } | |
| function checkValidServiceWorker(swUrl, { onUpdated, onInstalled }: SWOptions) { | |
| // 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, { onUpdated, onInstalled }); | |
| } | |
| }) | |
| .catch(e => { | |
| // eslint-disable-next-line no-console | |
| console.log( | |
| 'No internet connection found. App is running in offline mode.', | |
| e | |
| ); | |
| }); | |
| } | |
| export function unregister() { | |
| if ('serviceWorker' in navigator) { | |
| navigator.serviceWorker.ready.then(registration => { | |
| registration.unregister(); | |
| }); | |
| } | |
| } | |