Skip to content

Commit

Permalink
Service worker setup (#57)
Browse files Browse the repository at this point in the history
* Service worker setup

* Fix bad layout whitelisting.

* Only enable the service worker if in production mode, or ENABLE_SW is true

* Switch service-worker off by default in development
  • Loading branch information
ndorfin authored and mermop committed Sep 27, 2017
1 parent 30f02f5 commit d49649c
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 81 deletions.
103 changes: 23 additions & 80 deletions config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,6 @@
# General configuration --------------------------------------------------------

activate :dotenv
activate :asset_hash, ignore: %w{
opengraph.png
*touch-icon*.*
service-worker.js
*.xml
*.txt
*.json
favicon.ico
}

# Set Markdown engine to use redcarpet
set :markdown_engine, :redcarpet
Expand All @@ -24,75 +15,35 @@
no_intra_emphasis: true,
lax_spacing: true,
with_toc_data: true

activate :directory_indexes

# Helpers ----------------------------------------------------------------------

require "lib/typography_helpers"
helpers TypographyHelpers

# Methods defined in the helpers block are available in templates
# Add custom ones below
#
# helpers do
# def some_helper
# "Helping"
# end
# end

# Page options -----------------------------------------------------------------

# Page rules are matched in order from top to bottom.
#
# Example configuration options:
# With no layout:
#
# page "/path/to/file.html", layout: false
#
# With alternative layout:
#
# page "/path/to/file.html", layout: :other_layout
#
# A path which all have the same layout:
#
# with_layout :admin do
# page "/admin/*"
# end

# Proxy (fake) files:
# page "/this-page-has-no-template.html", proxy: "/template-file.html" do
# @which_fake_page = "Rendering a fake page with a variable"
# end

# Pages without layout
page "*.xml", layout: false
page "*.json", layout: false
page "*.txt", layout: false

# Catch-all for other routes
page "*", layout: "layouts/base"

# Proxy pages (http://middlemanapp.com/basics/dynamic-pages/)
# proxy "/this-page-has-no-template.html", "/template-file.html", locals: {
# which_fake_page: "Rendering a fake page with a local variable" }
page "*.html", layout: "layouts/base"

# Webpack configuration --------------------------------------------------------

ignore "assets/**/*.css"
ignore "assets/**/*.js"

activate :external_pipeline,
name: :webpack,
command: build? ? "npm run build" : "npm run watch",
source: ".tmp/dist",
latency: 1


# General configuration --------------------------------------------------------

activate :directory_indexes
# Development configuration --------------------------------------------------------

# Reload the browser automatically whenever files change
configure :development do
set :env, "development"
set :enable_sw, false
set :google_maps_key, nil
activate :livereload
end
Expand All @@ -102,38 +53,30 @@
configure :build do
set :env, "production"
set :google_maps_key, "AIzaSyBdI51q8kJ9s19RmWunLFFUZKFTxDXTSBA"
activate :asset_hash, ignore: %w{
opengraph.png
*touch-icon*.*
service-worker.js
*.xml
*.txt
*.json
favicon.ico
}
end

dev_server_task = :enable_sw ? "npm run watch:sw" : "npm run watch"

activate :external_pipeline,
name: :webpack,
command: build? ? "npm run build" : dev_server_task,
source: ".tmp/dist",
latency: 1

# Deployment configuration -----------------------------------------------------

# Deploy to GitHub Pages

activate :deploy do |config|
config.deploy_method = :git
config.branch = "gh-pages"
config.build_before = true
end

# Deploy to S3
#
# You’ll need to fill in the credentials in your `.env`. Check the documentation
# for the middleman-s3_sync gem for more details <https://github.com/fredjean
# /middleman-s3_sync>
#
# activate :s3_sync do |s3_sync|
# s3_sync.bucket = ENV["AWS_BUCKET"]
# s3_sync.region = ENV["AWS_REGION"]
# s3_sync.aws_access_key_id = ENV["AWS_ACCESS_KEY_ID"]
# s3_sync.aws_secret_access_key = ENV["AWS_SECRET_ACCESS_KEY"]
# s3_sync.delete = false
# s3_sync.after_build = false
# s3_sync.prefer_gzip = true
# s3_sync.path_style = true
# s3_sync.reduced_redundancy_storage = false
# s3_sync.acl = 'public-read'
# s3_sync.encryption = false
# s3_sync.prefix = ''
# s3_sync.version_bucket = false
# s3_sync.index_document = 'index.html'
# s3_sync.error_document = '404.html'
# end
12 changes: 12 additions & 0 deletions data/offline_cache_ignore.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
"service-worker",
"sitemap",
"robots",
"humans",
"touch-icon",
"opengraph",
"favicon",
".keep",
".map",
"CNAME"
]
2 changes: 1 addition & 1 deletion data/site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ short_name: "KiwiRuby"
description: "Kiwi Ruby is a new Ruby conference being held in Wellington, New Zealand in November 2017. It will be very good."
theme_color: "#ffffff"
theme_background_color: "#ffffff"
url: "http://kiwi.ruby.nz"
url: "https://kiwi.ruby.nz"
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"scripts": {
"clean": "rm -rf ./.tmp/dist/*",
"watch:sw": "ENABLE_SW=true NODE_ENV=development webpack --config ./webpack.config.js --watch -d",
"watch": "NODE_ENV=development webpack --config ./webpack.config.js --watch -d",
"build": "NODE_ENV=production webpack --config ./webpack.config.js -p --bail",
"lint": "eslint '/targets/**/*.js'; exit 0"
Expand Down
3 changes: 3 additions & 0 deletions source/assets/public/register_service_worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js', {scope: '/'});
}
4 changes: 4 additions & 0 deletions source/assets/public/target.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ require('./index.js');
require('./ga.js');
require('./turbolinks.js');

if (ENABLE_SW || NODE_ENV === 'production') {
require('./register_service_worker.js');
}

// Require all images and CSS by default
// This will inspect all subdirectories from the context (first param) and
// require files matching the regex.
Expand Down
93 changes: 93 additions & 0 deletions source/service-worker.js.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<%
require 'digest'
url_list = []
ignored_file_patterns = data.offline_cache_ignore
sitemap.resources.each do |r|
unless ignored_file_patterns.any? {|pattern| r.url.include? pattern}
url_list.push r.url
end
end
fingerprint = Digest::MD5.hexdigest(url_list.sort.join(''))
git_revision = `git rev-parse HEAD`.chomp
%>

var APP_PREFIX = '<%= data.site.name.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '') %>_'; // Identifier for this app (this needs to be consistent across every cache update)
var FINGERPRINT = '<%= fingerprint + git_revision %>'; // Generated hash for the URL list
var CACHE_NAME = APP_PREFIX + FINGERPRINT;
var URLS = [ // Add URL you want to cache in this list.
<% url_list.each_with_index do |r,i| %>
<%= "'#{r}'" %><%= ',' unless i == url_list.size - 1 %>
<% end %>
];

// Respond with cached resources
self.addEventListener('fetch', function(e) {
e.respondWith(
caches.open(CACHE_NAME).then(function(cache) {
return cache.match(e.request).then(function(response) {
if (response) { // if cache is available, respond with cache
return response;
}
// if there are no cache, try fetching request
return fetch(e.request.clone()).then(function(response) {
var matchesDomain = e.request.url.indexOf('<%= data.site.url %>') > -1;
if (response.status < 400 && matchesDomain) {
cache.put(e.request, response.clone());
}
return response;
});
});
})
);
});

// Cache resources
self.addEventListener('install', function (e) {
e.waitUntil(
caches.open(CACHE_NAME).then(function (cache) {
<%= "console.log('installing cache : ' + CACHE_NAME);" if config[:env] != 'production' %>
return cache.addAll(URLS);
})
.then(function() {
// `skipWaiting()` forces the waiting ServiceWorker to become the
// active ServiceWorker, triggering the `onactivate` event.
// Together with `Clients.claim()` this allows a worker to take effect
// immediately in the client(s).
return self.skipWaiting();
})
);
});

// Delete outdated caches
self.addEventListener('activate', function (e) {
<%= "console.log('activate called');" if config[:env] != 'production' %>
e.waitUntil(
caches
/* This method returns a promise which will resolve to an array of available
cache keys.
*/
.keys()
.then(function (keyList) {
<%= "console.log('keyList', keyList);" if config[:env] != 'production' %>
// We return a promise that settles when all outdated caches are deleted.
return Promise.all(
keyList
.filter(function (key) {
// Filter by keys that don't start with the latest version prefix.
return !key.startsWith(CACHE_NAME);
})
.map(function (key) {
/* Return a promise that's fulfilled
when each outdated cache is deleted.
*/
return caches.delete(key);
})
);
})
.then(function(){
<%= "console.log('WORKER: self.clients.claim()');" if config[:env] != 'production' %>
return self.clients.claim();
})
<%= ".then(function() {console.log('WORKER: activate completed.');})" if config[:env] != 'production' %>
);
});
4 changes: 4 additions & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ module.exports = {
// Extract all CSS into static files
new ExtractTextPlugin("[name].css", {
allChunks: true
}),
new webpack.DefinePlugin({
'NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'ENABLE_SW': JSON.stringify(process.env.ENABLE_SW)
})
],

Expand Down

0 comments on commit d49649c

Please sign in to comment.