sw.js 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. /* ===========================================================
  2. * docsify sw.js
  3. * ===========================================================
  4. * Copyright 2016 @huxpro
  5. * Licensed under Apache 2.0
  6. * Register service worker.
  7. * ========================================================== */
  8. const RUNTIME = 'docsify'
  9. const HOSTNAME_WHITELIST = [
  10. self.location.hostname,
  11. 'fonts.gstatic.com',
  12. 'fonts.googleapis.com',
  13. 'unpkg.com'
  14. ]
  15. // The Util Function to hack URLs of intercepted requests
  16. const getFixedUrl = (req) => {
  17. var now = Date.now()
  18. var url = new URL(req.url)
  19. // 1. fixed http URL
  20. // Just keep syncing with location.protocol
  21. // fetch(httpURL) belongs to active mixed content.
  22. // And fetch(httpRequest) is not supported yet.
  23. url.protocol = self.location.protocol
  24. // 2. add query for caching-busting.
  25. // Github Pages served with Cache-Control: max-age=600
  26. // max-age on mutable content is error-prone, with SW life of bugs can even extend.
  27. // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string.
  28. // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190
  29. if (url.hostname === self.location.hostname) {
  30. url.search += (url.search ? '&' : '?') + 'cache-bust=' + now
  31. }
  32. return url.href
  33. }
  34. /**
  35. * @Lifecycle Activate
  36. * New one activated when old isnt being used.
  37. *
  38. * waitUntil(): activating ====> activated
  39. */
  40. self.addEventListener('activate', event => {
  41. event.waitUntil(self.clients.claim())
  42. })
  43. /**
  44. * @Functional Fetch
  45. * All network requests are being intercepted here.
  46. *
  47. * void respondWith(Promise<Response> r)
  48. */
  49. self.addEventListener('fetch', event => {
  50. // Skip some of cross-origin requests, like those for Google Analytics.
  51. if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) {
  52. // Stale-while-revalidate
  53. // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale
  54. // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1
  55. const cached = caches.match(event.request)
  56. const fixedUrl = getFixedUrl(event.request)
  57. const fetched = fetch(fixedUrl, { cache: 'no-store' })
  58. const fetchedCopy = fetched.then(resp => resp.clone())
  59. // Call respondWith() with whatever we get first.
  60. // If the fetch fails (e.g disconnected), wait for the cache.
  61. // If there’s nothing in cache, wait for the fetch.
  62. // If neither yields a response, return offline pages.
  63. event.respondWith(
  64. Promise.race([fetched.catch(_ => cached), cached])
  65. .then(resp => resp || fetched)
  66. .catch(_ => { /* eat any errors */ })
  67. )
  68. // Update the cache with the version we fetched (only for ok status)
  69. event.waitUntil(
  70. Promise.all([fetchedCopy, caches.open(RUNTIME)])
  71. .then(([response, cache]) => response.ok && cache.put(event.request, response))
  72. .catch(_ => { /* eat any errors */ })
  73. )
  74. }
  75. })