From 12c784744f8a27e1caf7fc6e6d7026ed2e05ea03 Mon Sep 17 00:00:00 2001 From: John Kleinschmidt Date: Thu, 3 Dec 2015 14:24:19 -0500 Subject: [PATCH] Version 0.0.5 Changes to use sw-toolbox. Resolves #12. Should also address #5 --- .jshintrc | 9 ++++--- README.md | 53 +++++++++++++++++++++++----------------- lib/delete-old-caches.js | 6 ++--- lib/ember-addon.js | 6 ++--- lib/fallback-response.js | 42 +++++++++++++++++++------------ lib/service-worker.js | 23 +++++++++++++---- package.json | 2 +- 7 files changed, 88 insertions(+), 53 deletions(-) diff --git a/.jshintrc b/.jshintrc index b3a16cc..92a2d9c 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,11 +1,14 @@ { "predef": [ - "toolbox", + "CACHE_PREFIX", + "CACHE_VERSION", + "Promise", "Request", + "caches", "logDebug", + "navigator", "self", - "caches", - "Promise" + "toolbox" ], "node": true } diff --git a/README.md b/README.md index e1b6e0f..87b0574 100644 --- a/README.md +++ b/README.md @@ -11,26 +11,45 @@ For more details on ServiceWorker check out the following: Usage for Ember Cli ------------------- -`npm install --save-dev broccoli-serviceworker` +`ember install broccoli-serviceworker` +###Configuration +By default the service worker will be generated for production builds and the service worker registration logic will be added to your index.html automatically. Additionally, you can further customize broccoli-serviceworker by setting configurations in your environment.js file: ```JavaScript //app/config/environment.js ENV.serviceWorker = { enabled: true, - serviceWorkerFile: "service-worker.js", - excludePaths: ['tests/', 'online.html',], - includePaths: ['/'], + debug: true, + precacheURLs: ['/mystaticresouce'], + excludePaths: ['test.*', 'robots.txt',], fallback: [ - '/online.html offline.html' + '/online.html /offline.html' ], dynamicCache: [ '/api/todos' - ] + ], + includeRegistration: true, + serviceWorkerFile: "service-worker.js", + skipWaiting: true }; ``` +The following options are available: +* **enabled** - Generate service worker. Defaults to true in production. +* **debug** - Display debug messages in console. +* **precacheURLs** - Array of URLs to precache and always serve from the cache. broccoli-serviceworker will automatically add all Ember app resources (e.g. files in dist) as precached URLs unless explictly excluded in excludePaths. +* **excludePaths** - Array of paths to exclude from precache. Files can be filtered using regular expressions. +```JavaScript +{ + excludePaths: ['index.html', new RegExp(/.\.map$/)], +} +``` +* **includeRegistration** -- Automatically add the service worker registration script using contentFor to place the script in body-footer. Defaults to true. +* **serviceWorkerFile** - Name of the service worker file to generate. If **includeRegistration** is set to true, this setting is unused. Defaults to *service-worker.js*. +* **fallback** - Array of URLs with fallbacks when the resource isn't available via network or cache. +* **dynamicCache** - List of URLs that should use a network first strategy that falls back to a cached version of the response if the network is unavailable. For more details, see the details on [sw-toolbox's networkFirst strategy](https://github.com/GoogleChrome/sw-toolbox#user-content-toolboxnetworkfirst). +* **skipWaiting** - Allows a simple page refresh to update the app. Defaults to true. -The service worker bootstrap logic will be added to your index.html automatically, using contentFor hooks. Usage for Broccoli.js --------------------- @@ -53,16 +72,16 @@ Upgrade your `index.html` (see below) and you are done. Options ------- -You can pass some options as the second argument to `writeServiceWorker`: +You can the [options specified above](#configuration) as the second argument to `writeServiceWorker`: ```JavaScript writeServiceWorker(completeTree, { serviceWorkerFile: "service-worker.js", - excludePaths: ['tests/', 'online.html',], - includePaths: ['/'], + excludePaths: ['test.*', 'online.html',], + precacheURLs: ['/api/offlineStates'], fallback: [ - '/online.html offline.html' + '/api/states /api/offlineStates' ], dynamicCache: [ '/api/todos' @@ -71,16 +90,6 @@ writeServiceWorker(completeTree, { }); ``` -Files can be filtered using regular expressions. -```JavaScript -{ - excludePaths: ['index.html', new RegExp(/.\.map$/)], - includePaths: [''] -} -``` - - - Upgrade your index.html ----------------------- @@ -98,7 +107,7 @@ If you're not using Ember.js, you can use the following code: }); } else { alert('service worker not supported'); - } + } ``` diff --git a/lib/delete-old-caches.js b/lib/delete-old-caches.js index 5a046d7..83127ec 100644 --- a/lib/delete-old-caches.js +++ b/lib/delete-old-caches.js @@ -1,12 +1,12 @@ -/* global CACHE_PREFIX */ -/* global CACHE_VERSION */ + self.addEventListener('activate', function(event) { // Delete all caches handled by broccoli-serviceworker. + logDebug('Deleting out of date caches, current cache version:', CACHE_VERSION); event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.filter(function(cacheName) { - return (cacheName.indexOf(CACHE_PREFIX) === 0 && cacheName !== CACHE_VERSION); + return (cacheName.indexOf('$$$inactive$$$') === -1 && cacheName.indexOf(CACHE_PREFIX) === 0 && cacheName !== CACHE_VERSION); }).map(function(cacheName) { logDebug('Deleting out of date cache:', cacheName); return caches.delete(cacheName); diff --git a/lib/ember-addon.js b/lib/ember-addon.js index c2b03d5..3318171 100644 --- a/lib/ember-addon.js +++ b/lib/ember-addon.js @@ -20,8 +20,8 @@ module.exports = { var defaultOptions = { enabled: this.app.env === 'production', - excludePaths: ['test.*'], - includePaths: ['/'], + excludePaths: ['test.*','robots.txt'], + precacheURLs: [] }; for (var option in defaultOptions) { @@ -48,7 +48,7 @@ module.exports = { treeFor: function() {}, contentFor: function(type, config) { - if (config.environment !== 'test' && type === 'body-footer') { + if (config.environment !== 'test' && (!config.serviceWorker || config.serviceWorker.includeRegistration!== false) && type === 'body-footer') { return stringifile('registration.js', 'script', __dirname); } } diff --git a/lib/fallback-response.js b/lib/fallback-response.js index 72e852b..e924ee8 100644 --- a/lib/fallback-response.js +++ b/lib/fallback-response.js @@ -1,21 +1,31 @@ +function getFallbackFromCache(request, values, options) { + logDebug('Fetching from fallback url: '+ options.fallbackURL +'for url: '+request.url); + var req = new Request(options.fallbackURL, request); + return toolbox.cacheFirst(req, values, options).then(function(response) { + if (response) { + logDebug('Got fallback response from cache',response); + return response; + } + }); +} + function fallbackResponse(request, values, options) { logDebug('Looking for fallback for:', request.url); - return toolbox.networkFirst(request, values, options).then(function(response){ - logDebug('Default request network got response:', request.url, response); - if (!response) { - logDebug('Fetching from fallback url: '+ options.fallbackURL +'for url: '+request.url); - var req = new Request(options.fallbackURL, request); - var originalResponse = response; - return toolbox.cacheFirst(req, values, options).then(function(response) { - if (response) { - logDebug('Got response from cache',response); - return response; - } else { - return originalResponse; - } + return new Promise(function(resolve, reject) { + toolbox.networkFirst(request, values, options).then(function(response) { + if (response) { + resolve(response); + } else { + logDebug('Network first returned no response, calling fallback from cache.'); + getFallbackFromCache(request, values, options).then(resolve).catch(function(err) { + logDebug('Fallback failed with:', err); + }); + } + }).catch(function(err){ + logDebug('Network first returned err, calling fallback from cache:', err); + getFallbackFromCache(request, values, options).then(resolve).catch(function(err) { + logDebug('Fallback2 failed with:', err); }); - } else { - return response; - } + }); }); } diff --git a/lib/service-worker.js b/lib/service-worker.js index 45d91ed..f756c96 100644 --- a/lib/service-worker.js +++ b/lib/service-worker.js @@ -13,13 +13,26 @@ var BroccoliServiceWorker = function BroccoliServiceWorker(inTree, options) { } this.inTree = inTree; options = options || {}; - this.skipWaiting = options.skipWaiting || true; + if (options.skipWaiting === false) { + this.skipWaiting = false; + } else { + this.skipWaiting = true; + } this.debug = options.debug || false; this.dynamicCache = options.dynamicCache || []; this.excludePaths = options.excludePaths || ['tests/*']; this.fallback = options.fallback || []; - this.includePaths = options.includePaths || []; - this.serviceWorkerFile = options.serviceWorkerFile || "service-worker.js"; + this.precacheURLs = options.precacheURLs || []; + if (options.includeRegistration === false) { + this.includeRegistration = false; + } else { + this.includeRegistration = true; + } + if (this.includeRegistration === true || !options.serviceWorkerFile) { + this.serviceWorkerFile = "service-worker.js"; + } else { + this.serviceWorkerFile = options.serviceWorkerFile; + } }; BroccoliServiceWorker.prototype = Object.create(brocWriter.prototype); @@ -30,7 +43,7 @@ BroccoliServiceWorker.prototype.write = function(readTree, destDir) { var debug = this.debug; var dynamicCache = this.dynamicCache; var fallback = this.fallback; - var includePaths = this.includePaths; + var precacheURLs = this.precacheURLs; var serviceWorkerFile = this.serviceWorkerFile; var serviceWorkerTree = funnel(this.inTree, { exclude: this.excludePaths @@ -56,7 +69,7 @@ BroccoliServiceWorker.prototype.write = function(readTree, destDir) { lines.push(createArrayLine(" '"+file+"'", idx, array.length)); }); lines.push("];"); - includePaths.forEach(function (file, idx, array) { + precacheURLs.forEach(function (file, idx, array) { lines.push("urlsToPrefetch.push('"+file+"');"); }); lines.push("urlsToPrefetch.forEach(function(url) {"); diff --git a/package.json b/package.json index e51664e..f3d4152 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "broccoli-serviceworker", - "version": "0.0.4", + "version": "0.0.5", "description": "A broccoli plugin automating ServiceWorker file creation for Broccoli and Ember.js", "main": "lib/service-worker.js", "ember-addon": {