diff --git a/Dockerfile b/Dockerfile
index 77443edd..420bd195 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,7 @@
FROM ruby:2.6.0
ENV LANG=C.UTF-8
+ENV ENABLE_SERVICE_WORKER=true
WORKDIR /devdocs
diff --git a/Dockerfile-alpine b/Dockerfile-alpine
index 5bd25afe..a5d993f7 100644
--- a/Dockerfile-alpine
+++ b/Dockerfile-alpine
@@ -1,6 +1,7 @@
FROM ruby:2.6.0-alpine
ENV LANG=C.UTF-8
+ENV ENABLE_SERVICE_WORKER=true
WORKDIR /devdocs
diff --git a/assets/javascripts/app/config.coffee.erb b/assets/javascripts/app/config.coffee.erb
index 765da0b5..9b28b5a5 100644
--- a/assets/javascripts/app/config.coffee.erb
+++ b/assets/javascripts/app/config.coffee.erb
@@ -14,3 +14,4 @@ app.config =
release: <%= Time.now.utc.httpdate.to_json %>
mathml_stylesheet: '<%= App.cdn_origin %>/mathml.css'
service_worker_path: '/service-worker.js'
+ service_worker_enabled: <%= App.environment == 'production' || !ENV["ENABLE_SERVICE_WORKER"].nil? %>
diff --git a/assets/javascripts/app/serviceworker.coffee b/assets/javascripts/app/serviceworker.coffee
index 7a58f304..188a7e42 100644
--- a/assets/javascripts/app/serviceworker.coffee
+++ b/assets/javascripts/app/serviceworker.coffee
@@ -2,7 +2,7 @@ class app.ServiceWorker
$.extend @prototype, Events
@isEnabled: ->
- !!navigator.serviceWorker
+ !!navigator.serviceWorker and app.config.service_worker_enabled
constructor: ->
@registration = null
diff --git a/assets/javascripts/app/settings.coffee b/assets/javascripts/app/settings.coffee
index 58875f42..0540bfb7 100644
--- a/assets/javascripts/app/settings.coffee
+++ b/assets/javascripts/app/settings.coffee
@@ -5,7 +5,6 @@ class app.Settings
'manualUpdate'
'fastScroll'
'arrowScroll'
- 'bypassCache'
'docs'
'dark'
'layout'
@@ -92,11 +91,6 @@ class app.Settings
@set 'size', value
return
- setBypassCache: (value) ->
- @set 'bypassCache', value
- app.serviceWorker?.updateInBackground()
- return
-
dump: ->
@store.dump()
diff --git a/assets/javascripts/templates/pages/offline_tmpl.coffee b/assets/javascripts/templates/pages/offline_tmpl.coffee
index 52705605..52f64839 100644
--- a/assets/javascripts/templates/pages/offline_tmpl.coffee
+++ b/assets/javascripts/templates/pages/offline_tmpl.coffee
@@ -44,7 +44,12 @@ canICloseTheTab = ->
if app.ServiceWorker.isEnabled()
""" Yes! Even offline, you can open a new tab, go to devdocs.io, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). """
else
- """ No. Service Workers aren't available in your browser (or are disabled), so loading devdocs.io offline won't work.
+ reason = "aren't available in your browser (or are disabled)"
+
+ if app.config.env != 'production'
+ reason = "are disabled in your development instance of DevDocs (enable them by setting the ENABLE_SERVICE_WORKERS environment variable)"
+
+ """ No. Service Workers #{reason}, so loading devdocs.io offline won't work.
The current tab will continue to function even when you go offline (provided you installed all the documentations beforehand). """
app.templates.offlineDoc = (doc, status) ->
diff --git a/assets/javascripts/templates/pages/root_tmpl.coffee.erb b/assets/javascripts/templates/pages/root_tmpl.coffee.erb
index 7adce7fd..a94a99a0 100644
--- a/assets/javascripts/templates/pages/root_tmpl.coffee.erb
+++ b/assets/javascripts/templates/pages/root_tmpl.coffee.erb
@@ -8,7 +8,7 @@ app.templates.intro = """
Thanks for downloading DevDocs. Here are a few things you should know:
thor docs:list
to see all available documentations.
thor docs:download <name>
to download documentations.
thor docs:download --installed
to update all downloaded documentations.
diff --git a/assets/javascripts/templates/pages/settings_tmpl.coffee b/assets/javascripts/templates/pages/settings_tmpl.coffee
index 4a2066f1..1d439edb 100644
--- a/assets/javascripts/templates/pages/settings_tmpl.coffee
+++ b/assets/javascripts/templates/pages/settings_tmpl.coffee
@@ -15,10 +15,6 @@ app.templates.settingsPage = (settings) -> """
Automatically hide and show the sidebar
Tip: drag the edge of the sidebar to resize it.
-
diff --git a/assets/javascripts/views/content/settings_page.coffee b/assets/javascripts/views/content/settings_page.coffee
index 96904a13..af2e9a9d 100644
--- a/assets/javascripts/views/content/settings_page.coffee
+++ b/assets/javascripts/views/content/settings_page.coffee
@@ -14,7 +14,6 @@ class app.views.SettingsPage extends app.View
settings.dark = app.settings.get('dark')
settings.smoothScroll = !app.settings.get('fastScroll')
settings.arrowScroll = app.settings.get('arrowScroll')
- settings.bypassCache = app.settings.get('bypassCache')
settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS
settings
@@ -33,10 +32,6 @@ class app.views.SettingsPage extends app.View
app.settings.set('fastScroll', !enable)
return
- toggleBypassCache: (enable) ->
- app.settings.setBypassCache(!!enable)
- return
-
toggle: (name, enable) ->
app.settings.set(name, enable)
return
@@ -80,8 +75,6 @@ class app.views.SettingsPage extends app.View
@toggleSmoothScroll input.checked
when 'import'
@import input.files[0], input
- when 'bypassCache'
- @toggleBypassCache input.checked
else
@toggle input.name, input.checked
return
diff --git a/docs/adding-docs.md b/docs/adding-docs.md
index 4b34b045..dfc96cb1 100644
--- a/docs/adding-docs.md
+++ b/docs/adding-docs.md
@@ -2,8 +2,6 @@ Adding a documentation may look like a daunting task but once you get the hang o
**Note:** please read the [contributing guidelines](../.github/CONTRIBUTING.md) before submitting a new documentation.
-**Note:** when editing any of the files in the `assets` directory or the `public` directory, you'll have to bypass the service worker cache. To do this, go to the Preferences page on your local instance of DevDocs, check "Bypass Service Worker cache" and refresh the page.
-
1. Create a subclass of `Docs::UrlScraper` or `Docs::FileScraper` in the `lib/docs/scrapers/` directory. Its name should be the [PascalCase](http://api.rubyonrails.org/classes/String.html#method-i-camelize) equivalent of the filename (e.g. `my_doc` → `MyDoc`)
2. Add the appropriate class attributes and filter options (see the [Scraper Reference](./scraper-reference.md) page).
3. Check that the scraper is listed in `thor docs:list`.
diff --git a/lib/app.rb b/lib/app.rb
index 83e646e4..a0063e49 100644
--- a/lib/app.rb
+++ b/lib/app.rb
@@ -205,8 +205,28 @@ class App < Sinatra::Application
].compact
end
- def bypass_cache?
- !memoized_cookies['bypassCache'].nil?
+ def service_worker_cache_name
+ # Returns the digest of the last modified asset
+ # When a manifest exist, this digest is only calculated once based on the asset manifest because it never changes without a server restart
+ # If a manifest does not exist, it is calculated every time this method is called because the assets can change while the server is running
+ if File.exist?(App.assets_manifest_path)
+ return @@service_worker_cache_name ||=
+ Sprockets::Manifest
+ .new(nil, App.assets_manifest_path)
+ .files
+ .values
+ .sort_by {|file| file["mtime"]}
+ .reverse
+ .first["digest"]
+ else
+ last_modified_file = App.sprockets
+ .each_file
+ .to_a
+ .reject {|file| file.start_with?(App.docs_path)}
+ .max_by {|file| File.mtime(file)}
+
+ return App.sprockets.pack_base64digest(App.sprockets.file_digest(last_modified_file))
+ end
end
def redirect_via_js(path)
diff --git a/views/service-worker.js.erb b/views/service-worker.js.erb
index 20de7bbe..d9eb4a6c 100644
--- a/views/service-worker.js.erb
+++ b/views/service-worker.js.erb
@@ -1,6 +1,6 @@
-<%# Use the hash of the application.js file as cache name or 'app' if not running in production %>
-<%# This ensures that the cache is always updated if the hash of the application.js file changes %>
-const cacheName = '<%= javascript_path('application', asset_host: false).scan(/application-([^\.]+)\.js/).last&.first || 'app' %>';
+<%# The name of the cache to store responses in %>
+<%# If the cache name changes DevDocs is assumed to be updated %>
+const cacheName = '<%= service_worker_cache_name %>';
<%# Url's to cache when the service worker is installed %>
const urlsToCache = [
@@ -32,9 +32,6 @@ self.addEventListener('activate', event => {
<%# Handle HTTP requests %>
self.addEventListener('fetch', event => {
event.respondWith((async () => {
- <% if bypass_cache? %>
- return fetch(event.request);
- <% else %>
const cachedResponse = await caches.match(event.request);
if (cachedResponse) return cachedResponse;
@@ -53,6 +50,5 @@ self.addEventListener('fetch', event => {
throw err;
}
- <% end %>
})());
});