Re-add service worker caching (#1247)

* Re-add service worker caching

* Lint
pull/1255/head
Paulus Schoutsen 2018-06-03 07:38:22 -04:00 committed by GitHub
parent dbcae9cb77
commit 8133102bcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 324 additions and 228 deletions

View File

@ -1,8 +0,0 @@
console.warn('Service worker caching disabled in development');
self.addEventListener('install', function(event) {
self.skipWaiting();
});
self.addEventListener('activate', function(event) {
self.clients.claim();
});

View File

@ -1,77 +0,0 @@
self.addEventListener("push", function(event) {
var data;
if (event.data) {
data = event.data.json();
event.waitUntil(
self.registration.showNotification(data.title, data)
.then(function(notification){
firePushCallback({
type: "received",
tag: data.tag,
data: data.data
}, data.data.jwt);
})
);
}
});
self.addEventListener('notificationclick', function(event) {
var url;
notificationEventCallback('clicked', event);
event.notification.close();
if (!event.notification.data || !event.notification.data.url) {
return;
}
url = event.notification.data.url;
if (!url) return;
event.waitUntil(
clients.matchAll({
type: 'window',
})
.then(function (windowClients) {
var i;
var client;
for (i = 0; i < windowClients.length; i++) {
client = windowClients[i];
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
if (clients.openWindow) {
return clients.openWindow(url);
}
return undefined;
})
);
});
self.addEventListener('notificationclose', function(event) {
notificationEventCallback('closed', event);
});
function notificationEventCallback(event_type, event){
firePushCallback({
action: event.action,
data: event.notification.data,
tag: event.notification.tag,
type: event_type
}, event.notification.data.jwt);
}
function firePushCallback(payload, jwt){
// Don't send the JWT in the payload.data
delete payload.data.jwt;
// If payload.data is empty then just remove the entire payload.data object.
if (Object.keys(payload.data).length === 0 && payload.data.constructor === Object) {
delete payload.data;
}
fetch('/api/notify.html5/callback', {
method: 'POST',
headers: new Headers({'Content-Type': 'application/json',
'Authorization': 'Bearer '+jwt}),
body: JSON.stringify(payload)
});
}

View File

@ -1,129 +0,0 @@
/*
Generate a caching service worker for HA
Will be called as part of build_frontend.
Creates a caching service worker based on the built content of the repo in
{hass_frontend, hass_frontend_es6}.
Output service worker to {build, build-es6}/service_worker.js
TODO:
- Use gulp streams
- Fix minifying the stream
*/
const gulp = require('gulp');
const file = require('gulp-file');
const fs = require('fs');
const path = require('path');
const swPrecache = require('sw-precache');
const md5 = require('../common/md5.js');
const DEV = !!JSON.parse(process.env.BUILD_DEV || 'true');
const dynamicUrlToDependencies = {};
const staticFingerprinted = [
'mdi.html',
'translations/en.json',
];
const staticFingerprintedEs6 = [
'core.js',
'app.js',
];
const staticFingerprintedEs5 = [
'compatibility.js',
'core.js',
'app.js',
];
function processStatic(fn, rootDir, urlDir) {
const parts = path.parse(fn);
const base = parts.dir.length > 0 ? parts.dir + '/' + parts.name : parts.name;
const hash = md5(rootDir + '/' + base + parts.ext);
const url = '/' + urlDir + '/' + base + '-' + hash + parts.ext;
const fpath = rootDir + '/' + base + parts.ext;
dynamicUrlToDependencies[url] = [fpath];
}
function generateServiceWorker(es6) {
let genPromise = null;
const baseRootDir = 'hass_frontend';
const rootDir = es6 ? baseRootDir : 'hass_frontend_es5';
const panelDir = path.resolve(rootDir, 'panels');
if (DEV) {
genPromise = Promise.resolve(fs.readFileSync(path.resolve(__dirname, '../service-worker-dev.js.tmpl'), 'UTF-8'));
} else {
// Create fingerprinted versions of our dependencies.
(es6 ? staticFingerprintedEs6 : staticFingerprintedEs5).forEach(fn => processStatic(fn, rootDir, es6 ? 'frontend_latest' : 'frontend_es5'));
staticFingerprinted.forEach(fn => processStatic(fn, baseRootDir, 'static'));
panelsFingerprinted.forEach((panel) => {
const fpath = panelDir + '/ha-panel-' + panel + '.html';
const hash = md5(fpath);
const url = '/' + (es6 ? 'frontend_latest' : 'frontend_es5') + '/panels/ha-panel-' + panel + '-' + hash + '.html';
dynamicUrlToDependencies[url] = [fpath];
});
const options = {
directoryIndex: '',
dynamicUrlToDependencies: dynamicUrlToDependencies,
staticFileGlobs: [
baseRootDir + '/icons/favicon.ico',
baseRootDir + '/icons/favicon-192x192.png',
baseRootDir + '/webcomponents-lite.min.js',
baseRootDir + '/fonts/roboto/Roboto-Light.ttf',
baseRootDir + '/fonts/roboto/Roboto-Medium.ttf',
baseRootDir + '/fonts/roboto/Roboto-Regular.ttf',
baseRootDir + '/fonts/roboto/Roboto-Bold.ttf',
baseRootDir + '/images/card_media_player_bg.png',
],
// Rules are proceeded in order and negative per-domain rules are not supported.
runtimeCaching: [
{ // Cache static content (including translations) on first access.
urlPattern: '/(static|frontend_latest|frontend_es5)/*',
handler: 'cacheFirst',
},
{ // Get api (and home-assistant-polymer in dev mode) from network.
urlPattern: '/(home-assistant-polymer|api)/*',
handler: 'networkOnly',
},
{ // Get manifest and service worker from network.
urlPattern: '/(service_worker.js|service_worker_es5.js|manifest.json)',
handler: 'networkOnly',
},
{ // For rest of the files (on Home Assistant domain only) try both cache and network.
// This includes the root "/" or "/states" response and user files from "/local".
// First access might bring stale data from cache, but a single refresh will bring updated
// file.
urlPattern: '*',
handler: 'fastest',
}
],
stripPrefix: baseRootDir,
replacePrefix: '/static',
verbose: true,
// Allow our users to refresh to get latest version.
clientsClaim: true,
};
genPromise = swPrecache.generate(options);
}
const swHass = fs.readFileSync(path.resolve(__dirname, '../service-worker.js.tmpl'), 'UTF-8');
// Fix this
// if (!DEV) {
// genPromise = genPromise.then(
// swString => uglifyJS.minify(swString, { fromString: true }).code);
// }
return genPromise.then(swString => swString + '\n' + swHass + '\n' + (es6 ? '//es6' : '//es5'))
.then(swString => file('service_worker.js', swString)
.pipe(gulp.dest(es6 ? 'build' : 'build-es5')));
}
gulp.task('gen-service-worker-es5', generateServiceWorker.bind(null, /* es6= */ false));
gulp.task('gen-service-worker', generateServiceWorker.bind(null, /* es6= */ true));

View File

@ -140,7 +140,8 @@
"wct-browser-legacy": "^1.0.0",
"web-component-tester": "^6.6.0",
"webpack": "^4.8.1",
"webpack-cli": "^2.1.3"
"webpack-cli": "^2.1.3",
"workbox-webpack-plugin": "^3.2.0"
},
"resolutions": {
"inherits": "2.0.3",

View File

@ -17,7 +17,4 @@ cp -r public/__init__.py $OUTPUT_DIR_ES5/
./node_modules/.bin/gulp build-translations gen-icons
cp src/authorize.html $OUTPUT_DIR
# Manually copy over this file as we don't run the ES5 build
cp node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js.map $OUTPUT_DIR
./node_modules/.bin/webpack --watch --progress

View File

@ -0,0 +1,2 @@
/* global importScripts */
importScripts('/static/service-worker-hass.js');

View File

@ -0,0 +1,124 @@
/* global workbox clients */
function initRouting() {
workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
// Cache static content (including translations) on first access.
workbox.routing.registerRoute(
new RegExp(`${location.host}/(static|frontend_latest|frontend_es5)/.+`),
workbox.strategies.cacheFirst()
);
// Get api from network.
workbox.routing.registerRoute(
new RegExp(`${location.host}/api/.*`),
workbox.strategies.networkOnly()
);
// Get manifest and service worker from network.
workbox.routing.registerRoute(
new RegExp(`${location.host}/(service_worker.js|service_worker_es5.js|manifest.json)`),
workbox.strategies.networkOnly()
);
// For rest of the files (on Home Assistant domain only) try both cache and network.
// This includes the root "/" or "/states" response and user files from "/local".
// First access might bring stale data from cache, but a single refresh will bring updated
// file.
workbox.routing.registerRoute(
new RegExp(`${location.host}/.*`),
workbox.strategies.staleWhileRevalidate()
);
}
function initPushNotifications() {
// HTML5 Push Notifications
function firePushCallback(payload, jwt) {
// Don't send the JWT in the payload.data
delete payload.data.jwt;
// If payload.data is empty then just remove the entire payload.data object.
if (Object.keys(payload.data).length === 0 && payload.data.constructor === Object) {
delete payload.data;
}
fetch('/api/notify.html5/callback', {
method: 'POST',
headers: new Headers({ 'Content-Type': 'application/json',
Authorization: 'Bearer ' + jwt }),
body: JSON.stringify(payload)
});
}
function notificationEventCallback(eventType, event) {
firePushCallback({
action: event.action,
data: event.notification.data,
tag: event.notification.tag,
type: eventType
}, event.notification.data.jwt);
}
self.addEventListener('push', function (event) {
var data;
if (event.data) {
data = event.data.json();
event.waitUntil(self.registration.showNotification(data.title, data)
.then(function (/* notification */) {
firePushCallback({
type: 'received',
tag: data.tag,
data: data.data
}, data.data.jwt);
}));
}
});
self.addEventListener('notificationclick', function (event) {
var url;
notificationEventCallback('clicked', event);
event.notification.close();
if (!event.notification.data || !event.notification.data.url) {
return;
}
url = event.notification.data.url;
if (!url) return;
event.waitUntil(clients.matchAll({
type: 'window',
})
.then(function (windowClients) {
var i;
var client;
for (i = 0; i < windowClients.length; i++) {
client = windowClients[i];
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
if (clients.openWindow) {
return clients.openWindow(url);
}
return undefined;
}));
});
self.addEventListener('notificationclose', function (event) {
notificationEventCallback('closed', event);
});
}
workbox.setConfig({
debug: __DEV__
});
workbox.skipWaiting();
workbox.clientsClaim();
if (!__DEV__) {
initRouting();
}
initPushNotifications();

View File

@ -6,8 +6,8 @@ const JS_CACHE = {};
export default function loadCustomPanel(panelConfig) {
if ('html_url' in panelConfig) {
return Promise.all([
import('../legacy-support.js'),
import('../../resources/html-import/import-href.js'),
import(/* webpackChunkName: "legacy-support" */ '../legacy-support.js'),
import(/* webpackChunkName: "import-href-polyfill" */ '../../resources/html-import/import-href.js'),
// eslint-disable-next-line
]).then(([{}, { importHrefPromise }]) => importHrefPromise(panelConfig.html_url));
} else if (panelConfig.js_url) {

View File

@ -3,6 +3,8 @@ const path = require('path');
const webpack = require('webpack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
const translationMetadata = require('./build-translations/translationMetadata.json');
const version = fs.readFileSync('setup.py', 'utf8').match(/\d{8}[^']*/);
if (!version) {
@ -34,9 +36,7 @@ function createConfig(isProdBuild, latestBuild) {
],
};
const copyPluginOpts = [
{ from: 'gulp/service-worker.js.tmpl', to: 'service_worker.js' },
];
const copyPluginOpts = [];
const plugins = [
new webpack.DefinePlugin({
@ -64,10 +64,12 @@ function createConfig(isProdBuild, latestBuild) {
copyPluginOpts.push({ from: 'build-translations/output', to: `translations` });
copyPluginOpts.push({ from: 'node_modules/@polymer/font-roboto-local/fonts', to: 'fonts' });
copyPluginOpts.push('node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js')
copyPluginOpts.push('node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js.map')
copyPluginOpts.push({ from: 'node_modules/leaflet/dist/leaflet.css', to: `images/leaflet/` });
copyPluginOpts.push({ from: 'node_modules/leaflet/dist/images', to: `images/leaflet/` });
copyPluginOpts.push('node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js');
entry['hass-icons'] = './src/entrypoints/hass-icons.js';
entry['service-worker-hass'] = './src/entrypoints/service-worker-hass.js';
} else {
babelOptions.presets = [
['es2015', { modules: false }]
@ -86,6 +88,46 @@ function createConfig(isProdBuild, latestBuild) {
}));
}
plugins.push(new WorkboxPlugin.InjectManifest({
swSrc: './src/entrypoints/service-worker-bootstrap.js',
swDest: 'service_worker.js',
importWorkboxFrom: 'local',
include: [
/core.js$/,
/app.js$/,
/custom-panel.js$/,
/hass-icons.js$/,
/\.chunk\.js$/,
],
// Static assets get cached during runtime. But these we want to explicetely cache
// Need to be done using templatedUrls because prefix is /static
globDirectory: '.',
globIgnores: [],
modifyUrlPrefix: {
'hass_frontend': '/static'
},
templatedUrls: {
[`/static/translations/${translationMetadata['translations']['en']['fingerprints']['en']}`]: [
'build-translations/output/en.json'
],
'/static/icons/favicon-192x192.png': [
'public/icons/favicon-192x192.png'
],
'/static/fonts/roboto/Roboto-Light.ttf': [
'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Light.ttf'
],
'/static/fonts/roboto/Roboto-Medium.ttf': [
'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Medium.ttf'
],
'/static/fonts/roboto/Roboto-Regular.ttf': [
'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Regular.ttf'
],
'/static/fonts/roboto/Roboto-Bold.ttf': [
'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Bold.ttf'
],
}
}));
const chunkFilename = isProdBuild ?
'[chunkhash].chunk.js' : '[name].chunk.js';

154
yarn.lock
View File

@ -4124,6 +4124,10 @@ commander@~2.13.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
common-tags@^1.4.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@ -5829,6 +5833,14 @@ fs-exists-sync@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add"
fs-extra@^4.0.2:
version "4.0.3"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
dependencies:
graceful-fs "^4.1.2"
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-minipass@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d"
@ -6301,7 +6313,7 @@ graceful-fs@^3.0.0:
dependencies:
natives "^1.1.0"
graceful-fs@^4.0.0, graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3:
graceful-fs@^4.0.0, graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@ -7429,6 +7441,12 @@ isbinaryfile@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621"
isemail@3.x.x:
version "3.1.2"
resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.1.2.tgz#937cf919002077999a73ea8b1951d590e84e01dd"
dependencies:
punycode "2.x.x"
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@ -7469,6 +7487,14 @@ isurl@^1.0.0-alpha5:
has-to-string-tag-x "^1.2.0"
is-object "^1.0.1"
joi@^11.1.1:
version "11.4.0"
resolved "https://registry.yarnpkg.com/joi/-/joi-11.4.0.tgz#f674897537b625e9ac3d0b7e1604c828ad913ccb"
dependencies:
hoek "4.x.x"
isemail "3.x.x"
topo "2.x.x"
js-tokens@^3.0.0, js-tokens@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
@ -7574,6 +7600,12 @@ json5@^0.5.0, json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
optionalDependencies:
graceful-fs "^4.1.6"
jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
@ -10023,14 +10055,14 @@ punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
punycode@2.x.x, punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
q@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
@ -11890,6 +11922,12 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
topo@2.x.x:
version "2.0.2"
resolved "https://registry.yarnpkg.com/topo/-/topo-2.0.2.tgz#cd5615752539057c0dc0491a621c3bc6fbe1d182"
dependencies:
hoek "4.x.x"
tough-cookie@~2.3.0, tough-cookie@~2.3.3:
version "2.3.4"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"
@ -12117,6 +12155,10 @@ unique-string@^1.0.0:
dependencies:
crypto-random-string "^1.0.0"
universalify@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@ -12718,6 +12760,108 @@ wordwrapjs@^3.0.0:
reduce-flatten "^1.0.1"
typical "^2.6.1"
workbox-background-sync@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-3.2.0.tgz#08d4f79fb82fb61f72fbd0359c4b616cc75612d4"
dependencies:
workbox-core "^3.2.0"
workbox-broadcast-cache-update@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-broadcast-cache-update/-/workbox-broadcast-cache-update-3.2.0.tgz#65b4d9b3d4594751ab7ce1fee905c08214118fdc"
dependencies:
workbox-core "^3.2.0"
workbox-build@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-3.2.0.tgz#01f4a4f6fb5a94dadd3f86d04480c84578fa1125"
dependencies:
babel-runtime "^6.26.0"
common-tags "^1.4.0"
fs-extra "^4.0.2"
glob "^7.1.2"
joi "^11.1.1"
lodash.template "^4.4.0"
pretty-bytes "^4.0.2"
workbox-background-sync "^3.2.0"
workbox-broadcast-cache-update "^3.2.0"
workbox-cache-expiration "^3.2.0"
workbox-cacheable-response "^3.2.0"
workbox-core "^3.2.0"
workbox-google-analytics "^3.2.0"
workbox-precaching "^3.2.0"
workbox-range-requests "^3.2.0"
workbox-routing "^3.2.0"
workbox-strategies "^3.2.0"
workbox-streams "^3.2.0"
workbox-sw "^3.2.0"
workbox-cache-expiration@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-cache-expiration/-/workbox-cache-expiration-3.2.0.tgz#a585761fd5438e439668afc6f862ac5a0ebca1a8"
dependencies:
workbox-core "^3.2.0"
workbox-cacheable-response@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-3.2.0.tgz#1d8e3d437d60fb80d971d79545bb27acf1fe7653"
dependencies:
workbox-core "^3.2.0"
workbox-core@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-3.2.0.tgz#d1bd4209447f5350d8dd6b964c86f054c96ffa0a"
workbox-google-analytics@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-3.2.0.tgz#1005bc71ae03a8948b687896235dafecb1696c46"
dependencies:
workbox-background-sync "^3.2.0"
workbox-core "^3.2.0"
workbox-routing "^3.2.0"
workbox-strategies "^3.2.0"
workbox-precaching@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-3.2.0.tgz#36568687a5615d8bd4191b38cf0f489a992d7bbc"
dependencies:
workbox-core "^3.2.0"
workbox-range-requests@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-3.2.0.tgz#5d6cc3621cef0951fc9c0549053f8e117736d321"
dependencies:
workbox-core "^3.2.0"
workbox-routing@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-3.2.0.tgz#6aef7622ede2412dd116231f4f9408a6485a4832"
dependencies:
workbox-core "^3.2.0"
workbox-strategies@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-3.2.0.tgz#6cd5f00739764872b77b4c3766a606e43eb7d246"
dependencies:
workbox-core "^3.2.0"
workbox-streams@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-3.2.0.tgz#cac0e4f5693b5e13029cbd7e5fec4eb7fcb30d97"
dependencies:
workbox-core "^3.2.0"
workbox-sw@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-3.2.0.tgz#ccda9adff557ba2233bf1837229144b4a86276cb"
workbox-webpack-plugin@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-3.2.0.tgz#eab74294e88d86f4356a5dbac98cd803b22142b3"
dependencies:
json-stable-stringify "^1.0.1"
workbox-build "^3.2.0"
worker-farm@^1.5.2:
version "1.6.0"
resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0"