Skip to content

Commit

Permalink
v4 to v5 migration
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffposnick committed Jan 30, 2020
1 parent 923f995 commit b1ecae0
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 50 deletions.
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,15 @@
"rollup-plugin-babel": "^4.3.3",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-replace": "^2.2.0",
"rollup-plugin-string": "^3.0.0",
"workbox-cli": "^5.0.0"
"workbox-cacheable-response": "^5.0.0",

This comment has been minimized.

Copy link
@jeffposnick

jeffposnick Jan 30, 2020

Author Contributor

In this project, we're using Rollup to produce a service worker that includes a custom runtime bundle of the Workbox libraries we actually use, instead of relying on loading the Workbox runtime from the CDN. The libraries we need are added as normal devDependencies.

"workbox-cli": "^5.0.0",
"workbox-core": "^5.0.0",
"workbox-expiration": "^5.0.0",
"workbox-precaching": "^5.0.0",
"workbox-routing": "^5.0.0",
"workbox-strategies": "^5.0.0",
"workbox-streams": "^5.0.0"
}
}
9 changes: 6 additions & 3 deletions rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
import {string} from 'rollup-plugin-string';
import babel from 'rollup-plugin-babel';
import compiler from '@ampproject/rollup-plugin-closure-compiler';
import os from 'os';
import path from 'path';
import replace from 'rollup-plugin-replace';
import resolve from 'rollup-plugin-node-resolve';

// The version of Chromium used by Samsung Internet 6.x.
Expand Down Expand Up @@ -58,6 +57,10 @@ export default [{
}, {
input: 'src/service-worker.mjs',
plugins: [
replace({

This comment has been minimized.

Copy link
@jeffposnick

jeffposnick Jan 30, 2020

Author Contributor

The Workbox source code includes a number of process.env.NODE_ENV === 'production' checks, and that is used to conditionally add or remove development-level logging.

webpack will automatically substitute process.env.NODE_ENV when it compiles an entry, but Rollup doesn't. If you're using Rolllup, you should add in this call to rollup-plugin-replace when bundling to accomplish the same thing.

'process.env.NODE_ENV': JSON.stringify(
process.env.NODE_ENV || 'development'),
}),
resolve(),
babel({
presets: [['@babel/preset-env', {
Expand All @@ -68,7 +71,7 @@ export default [{
compiler(),
],
output: {
file: path.join(os.tmpdir(), 'service-worker.js'),
file: 'build/service-worker.js',

This comment has been minimized.

Copy link
@jeffposnick

jeffposnick Jan 30, 2020

Author Contributor

In v5, injectManifest will allow you to specify the same file for swSrc and swDest (which will result in an in-place manifest injection). We can just write our bundled file out to the final build/ directory.

Prior to v5, we had to write the bundled service worker to a temporary directory, use that as swSrc, and then write it to the final destination via swDest.

format: 'iife',
},
}, {
Expand Down
8 changes: 4 additions & 4 deletions src/lib/partials.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
**/

export default {
about: () => workbox.precaching.getCacheKeyForURL('partials/about.html'),
foot: () => workbox.precaching.getCacheKeyForURL('partials/foot.html'),
head: () => workbox.precaching.getCacheKeyForURL('partials/head.html'),
navbar: () => workbox.precaching.getCacheKeyForURL('partials/navbar.html'),
about: 'partials/about.html',

This comment has been minimized.

Copy link
@jeffposnick

jeffposnick Jan 30, 2020

Author Contributor

We're using the new matchPrecache method in v5, and all the logic around finding the correctly versioned cache key is handled for us. We don't have to explicitly look up the key here.

foot: 'partials/foot.html',
head: 'partials/head.html',
navbar: 'partials/navbar.html',
};
81 changes: 41 additions & 40 deletions src/service-worker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,50 @@
* limitations under the License.
**/

import {CacheableResponsePlugin} from 'workbox-cacheable-response';

This comment has been minimized.

Copy link
@jeffposnick

jeffposnick Jan 30, 2020

Author Contributor

All of the previous references to workbox.package.identifier() (which used workbox-sw to dynamically load the runtime from the CDN) have been swapped for a corresponding set of import {identifier} from 'workbox-package'.

This project then uses Rollup to produce the final bundle based on the import statements. (You could do the same thing with webpack or any other bundler if you prefer.)

import {CacheFirst, StaleWhileRevalidate} from 'workbox-strategies';
import {cleanupOutdatedCaches, matchPrecache, precacheAndRoute}
from 'workbox-precaching';
import {clientsClaim, skipWaiting} from 'workbox-core';
import {ExpirationPlugin} from 'workbox-expiration';
import {registerRoute} from 'workbox-routing';
import {strategy as streamsStrategy} from 'workbox-streams';

import {API_CACHE_NAME, DEFAULT_TAG} from './lib/constants.mjs';
import * as templates from './lib/templates.mjs';
import * as urls from './lib/urls.mjs';
import partials from './lib/partials.mjs';
import routeMatchers from './lib/route-matchers.mjs';

importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.0.0-beta.1/workbox-sw.js');
workbox.setConfig({
debug: true,
});
workbox.precaching.precacheAndRoute([]);
workbox.precaching.cleanupOutdatedCaches();

const cacheStrategy = new workbox.strategies.CacheFirst({
cacheName: workbox.core.cacheNames.precache,
});
precacheAndRoute(self.__WB_MANIFEST);

This comment has been minimized.

Copy link
@jeffposnick

jeffposnick Jan 30, 2020

Author Contributor

In v4, precacheAndRoute([]) was used as the injection point for the precache manifest.

In v5, by default, self.__WB_MANIFEST is the injection point, which has the advantage of being more explicit.

cleanupOutdatedCaches();

const apiStrategy = new workbox.strategies.StaleWhileRevalidate({
const apiStrategy = new StaleWhileRevalidate({
cacheName: API_CACHE_NAME,
plugins: [
new workbox.expiration.Plugin({maxEntries: 50}),
new ExpirationPlugin({maxEntries: 50}),

This comment has been minimized.

Copy link
@jeffposnick

jeffposnick Jan 30, 2020

Author Contributor

All of the plugin classes have been renamed in v5.

(This was done because the workbox.package. prefix is not used when importing the plugin classes via modules, and it would be confusing if there were half a dozen classes that had the default name of Plugin.)

],
});

workbox.routing.registerRoute(
registerRoute(
routeMatchers.get('about'),
workbox.streams.strategy([
() => cacheStrategy.makeRequest({request: partials.head()}),
() => cacheStrategy.makeRequest({request: partials.navbar()}),
() => cacheStrategy.makeRequest({request: partials.about()}),
() => cacheStrategy.makeRequest({request: partials.foot()}),
streamsStrategy([
() => matchPrecache(partials.head),

This comment has been minimized.

Copy link
@jeffposnick

jeffposnick Jan 30, 2020

Author Contributor

See the earlier comment—the matchPrecache() method makes it much easier to read cache entries that workbox-precaching created, without having to look up the cache name or versioned cache key.

() => matchPrecache(partials.navbar),
() => matchPrecache(partials.about),
() => matchPrecache(partials.foot),
])
);

workbox.routing.registerRoute(
registerRoute(
routeMatchers.get('questions'),
workbox.streams.strategy([
() => cacheStrategy.makeRequest({request: partials.head()}),
() => cacheStrategy.makeRequest({request: partials.navbar()}),
async ({event, url, params}) => {
streamsStrategy([
() => matchPrecache(partials.head),
() => matchPrecache(partials.navbar),
async ({event, params}) => {
try {
const questionId = params[1];
const questionResponse = await apiStrategy.makeRequest({
const questionResponse = await apiStrategy.handle({

This comment has been minimized.

Copy link
@jeffposnick

jeffposnick Jan 30, 2020

Author Contributor

makeRequest() and handle() are functionally equivalent, so makeRequest() was removed in v5.

event,
request: urls.getQuestion(questionId),
});
Expand All @@ -66,19 +67,19 @@ workbox.routing.registerRoute(
return templates.error(error.message);
}
},
() => cacheStrategy.makeRequest({request: partials.foot()}),
() => matchPrecache(partials.foot),
])
);

workbox.routing.registerRoute(
registerRoute(
routeMatchers.get('index'),
workbox.streams.strategy([
() => cacheStrategy.makeRequest({request: partials.head()}),
() => cacheStrategy.makeRequest({request: partials.navbar()}),
streamsStrategy([
() => matchPrecache(partials.head),
() => matchPrecache(partials.navbar),
async ({event, url}) => {
try {
const tag = url.searchParams.get('tag') || DEFAULT_TAG;
const listResponse = await apiStrategy.makeRequest({
const listResponse = await apiStrategy.handle({
event,
request: urls.listQuestionsForTag(tag),
});
Expand All @@ -88,37 +89,37 @@ workbox.routing.registerRoute(
return templates.error(error.message);
}
},
() => cacheStrategy.makeRequest({request: partials.foot()}),
() => matchPrecache(partials.foot),
])
);

// Gravatar images support CORS, so we won't be storing opaque responses.
workbox.routing.registerRoute(
registerRoute(
new RegExp('https://www\\.gravatar\\.com/'),
new workbox.strategies.CacheFirst({
new CacheFirst({
cacheName: 'profile-images',
plugins: [
new workbox.expiration.Plugin({
new ExpirationPlugin({
maxEntries: 50,
purgeOnQuotaError: true,
}),
],
})
);

workbox.routing.registerRoute(
registerRoute(
new RegExp('^https://.*(?:\\.jpg|\\.png)'),
new workbox.strategies.CacheFirst({
new CacheFirst({
cacheName: 'other-images',
plugins: [
new workbox.cacheableResponse.Plugin({statuses: [0, 200]}),
new workbox.expiration.Plugin({
new CacheableResponsePlugin({statuses: [0, 200]}),
new ExpirationPlugin({
maxEntries: 10,
purgeOnQuotaError: true,
}),
],
})
);

workbox.core.skipWaiting();
workbox.core.clientsClaim();
skipWaiting();
clientsClaim();
3 changes: 1 addition & 2 deletions workbox-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@
* limitations under the License.
**/

const os = require('os');
const path = require('path');

module.exports = {
globDirectory: 'build',
globPatterns: [
'**/*.{html,js,svg,png,json}',
],
swSrc: path.join(os.tmpdir(), 'service-worker.js'),
swSrc: path.join('build', 'service-worker.js'),

This comment has been minimized.

Copy link
@jeffposnick

jeffposnick Jan 30, 2020

Author Contributor

See the earlier comment—injectManifest now supports using the same file for the swSrc and swDest parameters, meaning it's no longer necessary to write a bundled service worker file to a temporary location.

swDest: path.join('build', 'service-worker.js'),
};

0 comments on commit b1ecae0

Please sign in to comment.