UNPKG

38.1 kBJavaScriptView Raw
1this.workbox = this.workbox || {};
2this.workbox.precaching = (function (DBWrapper_mjs,logger_mjs,cacheNames_mjs,WorkboxError_mjs,fetchWrapper_mjs,cacheWrapper_mjs,assert_mjs,getFriendlyURL_mjs) {
3 'use strict';
4
5 try {
6 self.workbox.v['workbox:precaching:3.5.0'] = 1;
7 } catch (e) {} // eslint-disable-line
8
9 /*
10 Copyright 2017 Google Inc.
11
12 Licensed under the Apache License, Version 2.0 (the "License");
13 you may not use this file except in compliance with the License.
14 You may obtain a copy of the License at
15
16 https://www.apache.org/licenses/LICENSE-2.0
17
18 Unless required by applicable law or agreed to in writing, software
19 distributed under the License is distributed on an "AS IS" BASIS,
20 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 See the License for the specific language governing permissions and
22 limitations under the License.
23 */
24
25 /**
26 * Used as a consistent way of referencing a URL to precache.
27 *
28 * @private
29 * @memberof module:workbox-precaching
30 */
31 class PrecacheEntry {
32 /**
33 * This class ensures all cache list entries are consistent and
34 * adds cache busting if required.
35 *
36 * @param {*} originalInput
37 * @param {string} url
38 * @param {string} revision
39 * @param {boolean} shouldCacheBust
40 */
41 constructor(originalInput, url, revision, shouldCacheBust) {
42 this._originalInput = originalInput;
43 this._entryId = url;
44 this._revision = revision;
45 const requestAsCacheKey = new Request(url, { credentials: 'same-origin' });
46 this._cacheRequest = requestAsCacheKey;
47 this._networkRequest = shouldCacheBust ? this._cacheBustRequest(requestAsCacheKey) : requestAsCacheKey;
48 }
49
50 /**
51 * This method will either use Request.cache option OR append a cache
52 * busting parameter to the URL.
53 *
54 * @param {Request} request The request to cache bust
55 * @return {Request} A cachebusted Request
56 *
57 * @private
58 */
59 _cacheBustRequest(request) {
60 let url = request.url;
61 const requestOptions = {
62 credentials: 'same-origin'
63 };
64 if ('cache' in Request.prototype) {
65 // Make use of the Request cache mode where we can.
66 // Reload skips the HTTP cache for outgoing requests and updates
67 // the cache with the returned response.
68 requestOptions.cache = 'reload';
69 } else {
70 const parsedURL = new URL(url, location);
71
72 // This is done so the minifier can mangle 'global.encodeURIComponent'
73 const _encodeURIComponent = encodeURIComponent;
74
75 parsedURL.search += (parsedURL.search ? '&' : '') + _encodeURIComponent(`_workbox-cache-bust`) + '=' + _encodeURIComponent(this._revision);
76 url = parsedURL.toString();
77 }
78
79 return new Request(url, requestOptions);
80 }
81 }
82
83 /*
84 Copyright 2017 Google Inc.
85
86 Licensed under the Apache License, Version 2.0 (the "License");
87 you may not use this file except in compliance with the License.
88 You may obtain a copy of the License at
89
90 https://www.apache.org/licenses/LICENSE-2.0
91
92 Unless required by applicable law or agreed to in writing, software
93 distributed under the License is distributed on an "AS IS" BASIS,
94 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95 See the License for the specific language governing permissions and
96 limitations under the License.
97 */
98
99 // Allows minifier to mangle this name
100 const REVISON_IDB_FIELD = 'revision';
101 const URL_IDB_FIELD = 'url';
102 const DB_STORE_NAME = 'precached-details-models';
103 /**
104 * This model will track the relevant information of entries that
105 * are cached and their matching revision details.
106 *
107 * @private
108 */
109 class PrecachedDetailsModel {
110 /**
111 * Construct a new model for a specific cache.
112 *
113 * @param {string} dbName
114 * @private
115 */
116 constructor(dbName) {
117 // This ensures the db name contains only letters, numbers, '-', '_' and '$'
118 const filteredDBName = dbName.replace(/[^\w-]/g, '_');
119 this._db = new DBWrapper_mjs.DBWrapper(filteredDBName, 2, {
120 onupgradeneeded: this._handleUpgrade
121 });
122 }
123
124 /**
125 * Should perform an upgrade of indexedDB.
126 *
127 * @param {Event} evt
128 *
129 * @private
130 */
131 _handleUpgrade(evt) {
132 const db = evt.target.result;
133 if (evt.oldVersion < 2) {
134 // IndexedDB version 1 used both 'workbox-precaching' and
135 // 'precached-details-model' before upgrading to version 2.
136 // Delete them and create a new store with latest schema.
137 if (db.objectStoreNames.contains('workbox-precaching')) {
138 db.deleteObjectStore('workbox-precaching');
139 }
140 if (db.objectStoreNames.contains(DB_STORE_NAME)) {
141 db.deleteObjectStore(DB_STORE_NAME);
142 }
143 }
144 db.createObjectStore(DB_STORE_NAME);
145 }
146
147 /**
148 * Check if an entry is already cached. Returns false if
149 * the entry isn't cached or the revision has changed.
150 *
151 * @param {string} cacheName
152 * @param {PrecacheEntry} precacheEntry
153 * @return {boolean}
154 *
155 * @private
156 */
157 _isEntryCached(cacheName, precacheEntry) {
158 var _this = this;
159
160 return babelHelpers.asyncToGenerator(function* () {
161 const revisionDetails = yield _this._getRevision(precacheEntry._entryId);
162 if (revisionDetails !== precacheEntry._revision) {
163 return false;
164 }
165
166 const openCache = yield caches.open(cacheName);
167 const cachedResponse = yield openCache.match(precacheEntry._cacheRequest);
168 return !!cachedResponse;
169 })();
170 }
171
172 /**
173 * @return {Promise<Array>}
174 *
175 * @private
176 */
177 _getAllEntries() {
178 var _this2 = this;
179
180 return babelHelpers.asyncToGenerator(function* () {
181 return yield _this2._db.getAllMatching(DB_STORE_NAME, {
182 includeKeys: true
183 });
184 })();
185 }
186
187 /**
188 * Get the current revision details.
189 *
190 * @param {Object} entryId
191 * @return {Promise<string|null>}
192 *
193 * @private
194 */
195 _getRevision(entryId) {
196 var _this3 = this;
197
198 return babelHelpers.asyncToGenerator(function* () {
199 const data = yield _this3._db.get(DB_STORE_NAME, entryId);
200 return data ? data[REVISON_IDB_FIELD] : null;
201 })();
202 }
203
204 /**
205 * Add an entry to the details model.
206 *
207 * @param {PrecacheEntry} precacheEntry
208 *
209 * @private
210 */
211 _addEntry(precacheEntry) {
212 var _this4 = this;
213
214 return babelHelpers.asyncToGenerator(function* () {
215 yield _this4._db.put(DB_STORE_NAME, {
216 [REVISON_IDB_FIELD]: precacheEntry._revision,
217 [URL_IDB_FIELD]: precacheEntry._cacheRequest.url
218 }, precacheEntry._entryId);
219 })();
220 }
221
222 /**
223 * Delete entry from details model.
224 *
225 * @param {string} entryId
226 *
227 * @private
228 */
229 _deleteEntry(entryId) {
230 var _this5 = this;
231
232 return babelHelpers.asyncToGenerator(function* () {
233 yield _this5._db.delete(DB_STORE_NAME, entryId);
234 })();
235 }
236 }
237
238 /*
239 Copyright 2017 Google Inc.
240
241 Licensed under the Apache License, Version 2.0 (the "License");
242 you may not use this file except in compliance with the License.
243 You may obtain a copy of the License at
244
245 https://www.apache.org/licenses/LICENSE-2.0
246
247 Unless required by applicable law or agreed to in writing, software
248 distributed under the License is distributed on an "AS IS" BASIS,
249 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
250 See the License for the specific language governing permissions and
251 limitations under the License.
252 */
253
254 /**
255 * This method will print out a warning if a precache entry doesn't have
256 * a `revision` value.
257 *
258 * This is common if the asset if revisioned in the url like `index.1234.css`.
259 *
260 * @param {Map} entriesMap
261 *
262 * @private
263 * @memberof module:workbox-preaching
264 */
265 var showWarningsIfNeeded = (entriesMap => {
266 const urlOnlyEntries = [];
267 entriesMap.forEach(entry => {
268 if (typeof entry === 'string' || !entry._originalInput.revision) {
269 urlOnlyEntries.push(entry._originalInput);
270 }
271 });
272
273 if (urlOnlyEntries.length === 0) {
274 // No warnings needed.
275 return;
276 }
277
278 logger_mjs.logger.groupCollapsed('Are your precached assets revisioned?');
279
280 const urlsList = urlOnlyEntries.map(urlOnlyEntry => {
281 return ` - ${JSON.stringify(urlOnlyEntry)}`;
282 }).join(`\n`);
283
284 logger_mjs.logger.warn(`The following precache entries might not be revisioned:` + `\n\n` + urlsList + `\n\n`);
285
286 logger_mjs.logger.unprefixed.warn(`You can learn more about why this might be a ` + `problem here: https://developers.google.com/web/tools/workbox/modules/workbox-precaching`);
287
288 logger_mjs.logger.groupEnd();
289 });
290
291 /*
292 Copyright 2017 Google Inc.
293
294 Licensed under the Apache License, Version 2.0 (the "License");
295 you may not use this file except in compliance with the License.
296 You may obtain a copy of the License at
297
298 https://www.apache.org/licenses/LICENSE-2.0
299
300 Unless required by applicable law or agreed to in writing, software
301 distributed under the License is distributed on an "AS IS" BASIS,
302 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
303 See the License for the specific language governing permissions and
304 limitations under the License.
305 */
306
307 /**
308 * @param {string} groupTitle
309 * @param {Array<PrecacheEntry>} entries
310 *
311 * @private
312 */
313 const _nestedGroup = (groupTitle, entries) => {
314 if (entries.length === 0) {
315 return;
316 }
317
318 logger_mjs.logger.groupCollapsed(groupTitle);
319
320 entries.forEach(entry => {
321 logger_mjs.logger.log(entry._originalInput);
322 });
323
324 logger_mjs.logger.groupEnd();
325 };
326
327 /**
328 * @param {Array<Object>} entriesToPrecache
329 * @param {Array<Object>} alreadyPrecachedEntries
330 *
331 * @private
332 * @memberof module:workbox-precachig
333 */
334 var printInstallDetails = ((entriesToPrecache, alreadyPrecachedEntries) => {
335 // Goal is to print the message:
336 // Precaching X files.
337 // Or:
338 // Precaching X files. Y files were cached and up-to-date.
339
340 const precachedCount = entriesToPrecache.length;
341 const alreadyPrecachedCount = alreadyPrecachedEntries.length;
342 let printText = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`;
343 if (alreadyPrecachedCount > 0) {
344 printText += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`;
345 }
346
347 logger_mjs.logger.groupCollapsed(printText);
348
349 _nestedGroup(`View precached URLs.`, entriesToPrecache);
350 _nestedGroup(`View URLs that were already precached.`, alreadyPrecachedEntries);
351 logger_mjs.logger.groupEnd();
352 });
353
354 /*
355 Copyright 2017 Google Inc.
356
357 Licensed under the Apache License, Version 2.0 (the "License");
358 you may not use this file except in compliance with the License.
359 You may obtain a copy of the License at
360
361 https://www.apache.org/licenses/LICENSE-2.0
362
363 Unless required by applicable law or agreed to in writing, software
364 distributed under the License is distributed on an "AS IS" BASIS,
365 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
366 See the License for the specific language governing permissions and
367 limitations under the License.
368 */
369
370 const logGroup = (groupTitle, urls) => {
371 logger_mjs.logger.groupCollapsed(groupTitle);
372
373 urls.forEach(url => {
374 logger_mjs.logger.log(url);
375 });
376
377 logger_mjs.logger.groupEnd();
378 };
379
380 /**
381 * @param {Array<string>} deletedCacheRequests
382 * @param {Array<string>} deletedRevisionDetails
383 *
384 * @private
385 * @memberof module:workbox-precachig
386 */
387 var printCleanupDetails = ((deletedCacheRequests, deletedRevisionDetails) => {
388 if (deletedCacheRequests.length === 0 && deletedRevisionDetails.length === 0) {
389 return;
390 }
391
392 const cacheDeleteCount = deletedCacheRequests.length;
393 const revisionDeleteCount = deletedRevisionDetails.length;
394
395 const cacheDeleteText = `${cacheDeleteCount} cached ` + `request${cacheDeleteCount === 1 ? ' was' : 's were'} deleted`;
396 const revisionDeleteText = `${revisionDeleteCount} ` + `${revisionDeleteCount === 1 ? 'entry' : 'entries'} ` + `${revisionDeleteCount === 1 ? 'was' : 'were'} deleted from IndexedDB.`;
397
398 logger_mjs.logger.groupCollapsed(`During precaching cleanup, ${cacheDeleteText} and ${revisionDeleteText}`);
399
400 logGroup('Deleted Cache Requests', deletedCacheRequests);
401 logGroup('Revision Details Deleted from DB', deletedRevisionDetails);
402
403 logger_mjs.logger.groupEnd();
404 });
405
406 /*
407 Copyright 2017 Google Inc.
408
409 Licensed under the Apache License, Version 2.0 (the "License");
410 you may not use this file except in compliance with the License.
411 You may obtain a copy of the License at
412
413 https://www.apache.org/licenses/LICENSE-2.0
414
415 Unless required by applicable law or agreed to in writing, software
416 distributed under the License is distributed on an "AS IS" BASIS,
417 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
418 See the License for the specific language governing permissions and
419 limitations under the License.
420 */
421
422 /**
423 * @param {Response} response
424 * @return {Response}
425 *
426 * @private
427 * @memberof module:workbox-precachig
428 */
429 const cleanRedirect = (() => {
430 var _ref = babelHelpers.asyncToGenerator(function* (response) {
431 const clonedResponse = response.clone();
432
433 // Not all browsers support the Response.body stream, so fall back
434 // to reading the entire body into memory as a blob.
435 const bodyPromise = 'body' in clonedResponse ? Promise.resolve(clonedResponse.body) : clonedResponse.blob();
436
437 const body = yield bodyPromise;
438
439 // new Response() is happy when passed either a stream or a Blob.
440 return new Response(body, ['headers', 'status', 'statusText'].map(function (key) {
441 return clonedResponse[key];
442 }));
443 });
444
445 return function cleanRedirect(_x) {
446 return _ref.apply(this, arguments);
447 };
448 })();
449
450 /*
451 Copyright 2017 Google Inc.
452
453 Licensed under the Apache License, Version 2.0 (the "License");
454 you may not use this file except in compliance with the License.
455 You may obtain a copy of the License at
456
457 https://www.apache.org/licenses/LICENSE-2.0
458
459 Unless required by applicable law or agreed to in writing, software
460 distributed under the License is distributed on an "AS IS" BASIS,
461 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
462 See the License for the specific language governing permissions and
463 limitations under the License.
464 */
465
466 /**
467 * Performs efficient precaching of assets.
468 *
469 * @memberof workbox.precaching
470 */
471 class PrecacheController {
472 /**
473 * Create a new PrecacheController.
474 *
475 * @param {string} cacheName
476 */
477 constructor(cacheName) {
478 this._cacheName = cacheNames_mjs.cacheNames.getPrecacheName(cacheName);
479 this._entriesToCacheMap = new Map();
480 this._precacheDetailsModel = new PrecachedDetailsModel(this._cacheName);
481 }
482
483 /**
484 * This method will add items to the precache list, removing duplicates
485 * and ensuring the information is valid.
486 *
487 * @param {
488 * Array<module:workbox-precaching.PrecacheController.PrecacheEntry|string>
489 * } entries Array of entries to
490 * precache.
491 */
492 addToCacheList(entries) {
493 {
494 assert_mjs.assert.isArray(entries, {
495 moduleName: 'workbox-precaching',
496 className: 'PrecacheController',
497 funcName: 'addToCacheList',
498 paramName: 'entries'
499 });
500 }
501
502 entries.map(userEntry => {
503 this._addEntryToCacheList(this._parseEntry(userEntry));
504 });
505 }
506
507 /**
508 * This method returns a precache entry.
509 *
510 * @private
511 * @param {string|Object} input
512 * @return {PrecacheEntry}
513 */
514 _parseEntry(input) {
515 switch (typeof input) {
516 case 'string':
517 {
518 {
519 if (input.length === 0) {
520 throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-unexpected-type', {
521 entry: input
522 });
523 }
524 }
525
526 return new PrecacheEntry(input, input, input);
527 }
528 case 'object':
529 {
530 {
531 if (!input || !input.url) {
532 throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-unexpected-type', {
533 entry: input
534 });
535 }
536 }
537
538 return new PrecacheEntry(input, input.url, input.revision || input.url, !!input.revision);
539 }
540 default:
541 throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-unexpected-type', {
542 entry: input
543 });
544 }
545 }
546
547 /**
548 * Adds an entry to the precache list, accounting for possible duplicates.
549 *
550 * @private
551 * @param {PrecacheEntry} entryToAdd
552 */
553 _addEntryToCacheList(entryToAdd) {
554 // Check if the entry is already part of the map
555 const existingEntry = this._entriesToCacheMap.get(entryToAdd._entryId);
556 if (!existingEntry) {
557 this._entriesToCacheMap.set(entryToAdd._entryId, entryToAdd);
558 return;
559 }
560
561 // Duplicates are fine, but make sure the revision information
562 // is the same.
563 if (existingEntry._revision !== entryToAdd._revision) {
564 throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-conflicting-entries', {
565 firstEntry: existingEntry._originalInput,
566 secondEntry: entryToAdd._originalInput
567 });
568 }
569 }
570
571 /**
572 * Call this method from a service work install event to start
573 * precaching assets.
574 *
575 * @param {Object} options
576 * @param {boolean} options.suppressWarnings Suppress warning messages.
577 * @param {Array<Object>} options.plugins Plugins to be used for fetching
578 * and caching during install.
579 * @return {
580 * Promise<workbox.precaching.InstallResult>}
581 */
582 install(options = {}) {
583 var _this = this;
584
585 return babelHelpers.asyncToGenerator(function* () {
586 {
587 if (options.suppressWarnings !== true) {
588 showWarningsIfNeeded(_this._entriesToCacheMap);
589 }
590
591 if (options.plugins) {
592 assert_mjs.assert.isArray(options.plugins, {
593 moduleName: 'workbox-precaching',
594 className: 'PrecacheController',
595 funcName: 'install',
596 paramName: 'plugins'
597 });
598 }
599 }
600
601 // Empty the temporary cache.
602 // NOTE: We remove all entries instead of deleting the cache as the cache
603 // may be marked for deletion but still exist until a later stage
604 // resulting in unexpected behavior of being deletect when all references
605 // are dropped.
606 // https://github.com/GoogleChrome/workbox/issues/1368
607 const tempCache = yield caches.open(_this._getTempCacheName());
608 const requests = yield tempCache.keys();
609 yield Promise.all(requests.map(function (request) {
610 return tempCache.delete(request);
611 }));
612
613 const entriesToPrecache = [];
614 const entriesAlreadyPrecached = [];
615
616 for (const precacheEntry of _this._entriesToCacheMap.values()) {
617 if (yield _this._precacheDetailsModel._isEntryCached(_this._cacheName, precacheEntry)) {
618 entriesAlreadyPrecached.push(precacheEntry);
619 } else {
620 entriesToPrecache.push(precacheEntry);
621 }
622 }
623
624 // Wait for all requests to be cached.
625 yield Promise.all(entriesToPrecache.map(function (precacheEntry) {
626 return _this._cacheEntryInTemp(precacheEntry, options.plugins);
627 }));
628
629 {
630 printInstallDetails(entriesToPrecache, entriesAlreadyPrecached);
631 }
632
633 return {
634 updatedEntries: entriesToPrecache,
635 notUpdatedEntries: entriesAlreadyPrecached
636 };
637 })();
638 }
639
640 /**
641 * Takes the current set of temporary files and moves them to the final
642 * cache, deleting the temporary cache once copying is complete.
643 *
644 * @param {Object} options
645 * @param {Array<Object>} options.plugins Plugins to be used for fetching
646 * and caching during install.
647 * @return {
648 * Promise<workbox.precaching.CleanupResult>}
649 * Resolves with an object containing details of the deleted cache requests
650 * and precache revision details.
651 */
652 activate(options = {}) {
653 var _this2 = this;
654
655 return babelHelpers.asyncToGenerator(function* () {
656 const tempCache = yield caches.open(_this2._getTempCacheName());
657
658 const requests = yield tempCache.keys();
659 // Process each request/response one at a time, deleting the temporary entry
660 // when done, to help avoid triggering quota errors.
661 for (const request of requests) {
662 const response = yield tempCache.match(request);
663 yield cacheWrapper_mjs.cacheWrapper.put(_this2._cacheName, request, response, options.plugins);
664 yield tempCache.delete(request);
665 }
666
667 return _this2._cleanup();
668 })();
669 }
670
671 /**
672 * Returns the name of the temporary cache.
673 *
674 * @return {string}
675 *
676 * @private
677 */
678 _getTempCacheName() {
679 return `${this._cacheName}-temp`;
680 }
681
682 /**
683 * Requests the entry and saves it to the cache if the response
684 * is valid.
685 *
686 * @private
687 * @param {BaseCacheEntry} precacheEntry The entry to fetch and cache.
688 * @param {Array<Object>} plugins Array of plugins to apply to fetch and
689 * caching.
690 * @return {Promise<boolean>} Returns a promise that resolves once the entry
691 * has been fetched and cached or skipped if no update is needed. The
692 * promise resolves with true if the entry was cached / updated and
693 * false if the entry is already cached and up-to-date.
694 */
695 _cacheEntryInTemp(precacheEntry, plugins) {
696 var _this3 = this;
697
698 return babelHelpers.asyncToGenerator(function* () {
699 let response = yield fetchWrapper_mjs.fetchWrapper.fetch(precacheEntry._networkRequest, null, plugins);
700
701 if (response.redirected) {
702 response = yield cleanRedirect(response);
703 }
704
705 yield cacheWrapper_mjs.cacheWrapper.put(_this3._getTempCacheName(), precacheEntry._cacheRequest, response, plugins);
706
707 yield _this3._precacheDetailsModel._addEntry(precacheEntry);
708
709 return true;
710 })();
711 }
712
713 /**
714 * Compare the URLs and determines which assets are no longer required
715 * in the cache.
716 *
717 * This should be called in the service worker activate event.
718 *
719 * @return {
720 * Promise<workbox.precaching.CleanupResult>}
721 * Resolves with an object containing details of the deleted cache requests
722 * and precache revision details.
723 *
724 * @private
725 */
726 _cleanup() {
727 var _this4 = this;
728
729 return babelHelpers.asyncToGenerator(function* () {
730 const expectedCacheUrls = [];
731 _this4._entriesToCacheMap.forEach(function (entry) {
732 const fullUrl = new URL(entry._cacheRequest.url, location).toString();
733 expectedCacheUrls.push(fullUrl);
734 });
735
736 const [deletedCacheRequests, deletedRevisionDetails] = yield Promise.all([_this4._cleanupCache(expectedCacheUrls), _this4._cleanupDetailsModel(expectedCacheUrls)]);
737
738 {
739 printCleanupDetails(deletedCacheRequests, deletedRevisionDetails);
740 }
741
742 return {
743 deletedCacheRequests,
744 deletedRevisionDetails
745 };
746 })();
747 }
748
749 /**
750 * Goes through all the cache entries and removes any that are
751 * outdated.
752 *
753 * @private
754 * @param {Array<string>} expectedCacheUrls Array of URLs that are
755 * expected to be cached.
756 * @return {Promise<Array<string>>} Resolves to an array of URLs
757 * of cached requests that were deleted.
758 */
759 _cleanupCache(expectedCacheUrls) {
760 var _this5 = this;
761
762 return babelHelpers.asyncToGenerator(function* () {
763 if (!(yield caches.has(_this5._cacheName))) {
764 // Cache doesn't exist, so nothing to delete
765 return [];
766 }
767
768 const cache = yield caches.open(_this5._cacheName);
769 const cachedRequests = yield cache.keys();
770 const cachedRequestsToDelete = cachedRequests.filter(function (cachedRequest) {
771 return !expectedCacheUrls.includes(new URL(cachedRequest.url, location).toString());
772 });
773
774 yield Promise.all(cachedRequestsToDelete.map(function (cacheUrl) {
775 return cache.delete(cacheUrl);
776 }));
777
778 return cachedRequestsToDelete.map(function (request) {
779 return request.url;
780 });
781 })();
782 }
783
784 /**
785 * Goes through all entries in indexedDB and removes any that are outdated.
786 *
787 * @private
788 * @param {Array<string>} expectedCacheUrls Array of URLs that are
789 * expected to be cached.
790 * @return {Promise<Array<string>>} Resolves to an array of URLs removed
791 * from indexedDB.
792 */
793 _cleanupDetailsModel(expectedCacheUrls) {
794 var _this6 = this;
795
796 return babelHelpers.asyncToGenerator(function* () {
797 const revisionedEntries = yield _this6._precacheDetailsModel._getAllEntries();
798 const detailsToDelete = revisionedEntries.filter(function (entry) {
799 const fullUrl = new URL(entry.value.url, location).toString();
800 return !expectedCacheUrls.includes(fullUrl);
801 });
802
803 yield Promise.all(detailsToDelete.map(function (entry) {
804 return _this6._precacheDetailsModel._deleteEntry(entry.primaryKey);
805 }));
806 return detailsToDelete.map(function (entry) {
807 return entry.value.url;
808 });
809 })();
810 }
811
812 /**
813 * Returns an array of fully qualified URL's that will be precached.
814 *
815 * @return {Array<string>} An array of URLs.
816 */
817 getCachedUrls() {
818 return Array.from(this._entriesToCacheMap.keys()).map(url => new URL(url, location).href);
819 }
820 }
821
822 /*
823 Copyright 2017 Google Inc.
824
825 Licensed under the Apache License, Version 2.0 (the "License");
826 you may not use this file except in compliance with the License.
827 You may obtain a copy of the License at
828
829 https://www.apache.org/licenses/LICENSE-2.0
830
831 Unless required by applicable law or agreed to in writing, software
832 distributed under the License is distributed on an "AS IS" BASIS,
833 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
834 See the License for the specific language governing permissions and
835 limitations under the License.
836 */
837
838 var publicAPI = /*#__PURE__*/Object.freeze({
839 PrecacheController: PrecacheController
840 });
841
842 /*
843 Copyright 2017 Google Inc.
844
845 Licensed under the Apache License, Version 2.0 (the "License");
846 you may not use this file except in compliance with the License.
847 You may obtain a copy of the License at
848
849 https://www.apache.org/licenses/LICENSE-2.0
850
851 Unless required by applicable law or agreed to in writing, software
852 distributed under the License is distributed on an "AS IS" BASIS,
853 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
854 See the License for the specific language governing permissions and
855 limitations under the License.
856 */
857
858 {
859 assert_mjs.assert.isSwEnv('workbox-precaching');
860 }
861
862 let installActivateListenersAdded = false;
863 let fetchListenersAdded = false;
864 let suppressWarnings = false;
865 let plugins = [];
866
867 const cacheName = cacheNames_mjs.cacheNames.getPrecacheName();
868 const precacheController = new PrecacheController(cacheName);
869
870 const _removeIgnoreUrlParams = (origUrlObject, ignoreUrlParametersMatching) => {
871 // Exclude initial '?'
872 const searchString = origUrlObject.search.slice(1);
873
874 // Split into an array of 'key=value' strings
875 const keyValueStrings = searchString.split('&');
876 const keyValuePairs = keyValueStrings.map(keyValueString => {
877 // Split each 'key=value' string into a [key, value] array
878 return keyValueString.split('=');
879 });
880 const filteredKeyValuesPairs = keyValuePairs.filter(keyValuePair => {
881 return ignoreUrlParametersMatching.every(ignoredRegex => {
882 // Return true iff the key doesn't match any of the regexes.
883 return !ignoredRegex.test(keyValuePair[0]);
884 });
885 });
886 const filteredStrings = filteredKeyValuesPairs.map(keyValuePair => {
887 // Join each [key, value] array into a 'key=value' string
888 return keyValuePair.join('=');
889 });
890
891 // Join the array of 'key=value' strings into a string with '&' in
892 // between each
893 const urlClone = new URL(origUrlObject);
894 urlClone.search = filteredStrings.join('&');
895 return urlClone;
896 };
897
898 /**
899 * This function will take the request URL and manipulate it based on the
900 * configuration options.
901 *
902 * @param {string} url
903 * @param {Object} options
904 * @return {string|null} Returns the URL in the cache that matches the request
905 * if available, other null.
906 *
907 * @private
908 */
909 const _getPrecachedUrl = (url, {
910 ignoreUrlParametersMatching = [/^utm_/],
911 directoryIndex = 'index.html',
912 cleanUrls = true,
913 urlManipulation = null
914 } = {}) => {
915 const urlObject = new URL(url, location);
916
917 // Change '/some-url#123' => '/some-url'
918 urlObject.hash = '';
919
920 const urlWithoutIgnoredParams = _removeIgnoreUrlParams(urlObject, ignoreUrlParametersMatching);
921
922 let urlsToAttempt = [
923 // Test the URL that was fetched
924 urlObject,
925 // Test the URL without search params
926 urlWithoutIgnoredParams];
927
928 // Test the URL with a directory index
929 if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) {
930 const directoryUrl = new URL(urlWithoutIgnoredParams);
931 directoryUrl.pathname += directoryIndex;
932 urlsToAttempt.push(directoryUrl);
933 }
934
935 // Test the URL with a '.html' extension
936 if (cleanUrls) {
937 const cleanUrl = new URL(urlWithoutIgnoredParams);
938 cleanUrl.pathname += '.html';
939 urlsToAttempt.push(cleanUrl);
940 }
941
942 if (urlManipulation) {
943 const additionalUrls = urlManipulation({ url: urlObject });
944 urlsToAttempt = urlsToAttempt.concat(additionalUrls);
945 }
946
947 const cachedUrls = precacheController.getCachedUrls();
948 for (const possibleUrl of urlsToAttempt) {
949 if (cachedUrls.indexOf(possibleUrl.href) !== -1) {
950 // It's a perfect match
951 {
952 logger_mjs.logger.debug(`Precaching found a match for ` + getFriendlyURL_mjs.getFriendlyURL(possibleUrl.toString()));
953 }
954 return possibleUrl.href;
955 }
956 }
957
958 return null;
959 };
960
961 const moduleExports = {};
962
963 /**
964 * Add items to the precache list, removing any duplicates and
965 * store the files in the
966 * ["precache cache"]{@link module:workbox-core.cacheNames} when the service
967 * worker installs.
968 *
969 * This method can be called multiple times.
970 *
971 * Please note: This method **will not** serve any of the cached files for you,
972 * it only precaches files. To respond to a network request you call
973 * [addRoute()]{@link module:workbox-precaching.addRoute}.
974 *
975 * If you have a single array of files to precache, you can just call
976 * [precacheAndRoute()]{@link module:workbox-precaching.precacheAndRoute}.
977 *
978 * @param {Array<Object|string>} entries Array of entries to precache.
979 *
980 * @alias workbox.precaching.precache
981 */
982 moduleExports.precache = entries => {
983 precacheController.addToCacheList(entries);
984
985 if (installActivateListenersAdded || entries.length <= 0) {
986 return;
987 }
988
989 installActivateListenersAdded = true;
990 self.addEventListener('install', event => {
991 event.waitUntil(precacheController.install({
992 suppressWarnings,
993 plugins
994 }));
995 });
996 self.addEventListener('activate', event => {
997 event.waitUntil(precacheController.activate({
998 plugins
999 }));
1000 });
1001 };
1002
1003 /**
1004 * Add a `fetch` listener to the service worker that will
1005 * respond to
1006 * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests}
1007 * with precached assets.
1008 *
1009 * Requests for assets that aren't precached, the `FetchEvent` will not be
1010 * responded to, allowing the event to fall through to other `fetch` event
1011 * listeners.
1012 *
1013 * @param {Object} options
1014 * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will
1015 * check cache entries for a URLs ending with '/' to see if there is a hit when
1016 * appending the `directoryIndex` value.
1017 * @param {Array<RegExp>} [options.ignoreUrlParametersMatching=[/^utm_/]] An
1018 * array of regex's to remove search params when looking for a cache match.
1019 * @param {boolean} [options.cleanUrls=true] The `cleanUrls` option will
1020 * check the cache for the URL with a `.html` added to the end of the end.
1021 * @param {workbox.precaching~urlManipulation} [options.urlManipulation]
1022 * This is a function that should take a URL and return an array of
1023 * alternative URL's that should be checked for precache matches.
1024 *
1025 * @alias workbox.precaching.addRoute
1026 */
1027 moduleExports.addRoute = options => {
1028 if (fetchListenersAdded) {
1029 // TODO: Throw error here.
1030 return;
1031 }
1032
1033 fetchListenersAdded = true;
1034 self.addEventListener('fetch', event => {
1035 const precachedUrl = _getPrecachedUrl(event.request.url, options);
1036 if (!precachedUrl) {
1037 {
1038 logger_mjs.logger.debug(`Precaching found no match for ` + getFriendlyURL_mjs.getFriendlyURL(event.request.url));
1039 }
1040 return;
1041 }
1042
1043 let responsePromise = caches.open(cacheName).then(cache => {
1044 return cache.match(precachedUrl);
1045 }).then(cachedResponse => {
1046 if (cachedResponse) {
1047 return cachedResponse;
1048 }
1049
1050 // Fall back to the network if we don't have a cached response (perhaps
1051 // due to manual cache cleanup).
1052 {
1053 logger_mjs.logger.debug(`The precached response for ` + `${getFriendlyURL_mjs.getFriendlyURL(precachedUrl)} in ${cacheName} was not found. ` + `Falling back to the network instead.`);
1054 }
1055
1056 return fetch(precachedUrl);
1057 });
1058
1059 {
1060 responsePromise = responsePromise.then(response => {
1061 // Workbox is going to handle the route.
1062 // print the routing details to the console.
1063 logger_mjs.logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL_mjs.getFriendlyURL(event.request.url));
1064 logger_mjs.logger.log(`Serving the precached url: ${precachedUrl}`);
1065
1066 // The Request and Response objects contains a great deal of
1067 // information, hide it under a group in case developers want to see it.
1068 logger_mjs.logger.groupCollapsed(`View request details here.`);
1069 logger_mjs.logger.unprefixed.log(event.request);
1070 logger_mjs.logger.groupEnd();
1071
1072 logger_mjs.logger.groupCollapsed(`View response details here.`);
1073 logger_mjs.logger.unprefixed.log(response);
1074 logger_mjs.logger.groupEnd();
1075
1076 logger_mjs.logger.groupEnd();
1077 return response;
1078 });
1079 }
1080 event.respondWith(responsePromise);
1081 });
1082 };
1083
1084 /**
1085 * This method will add entries to the precache list and add a route to
1086 * respond to fetch events.
1087 *
1088 * This is a convenience method that will call
1089 * [precache()]{@link module:workbox-precaching.precache} and
1090 * [addRoute()]{@link module:workbox-precaching.addRoute} in a single call.
1091 *
1092 * @param {Array<Object|string>} entries Array of entries to precache.
1093 * @param {Object} options See
1094 * [addRoute() options]{@link module:workbox-precaching.addRoute}.
1095 *
1096 * @alias workbox.precaching.precacheAndRoute
1097 */
1098 moduleExports.precacheAndRoute = (entries, options) => {
1099 moduleExports.precache(entries);
1100 moduleExports.addRoute(options);
1101 };
1102
1103 /**
1104 * Warnings will be logged if any of the precached assets are entered without
1105 * a `revision` property. This is extremely dangerous if the URL's aren't
1106 * revisioned. However, the warnings can be supressed with this method.
1107 *
1108 * @param {boolean} suppress
1109 *
1110 * @alias workbox.precaching.suppressWarnings
1111 */
1112 moduleExports.suppressWarnings = suppress => {
1113 suppressWarnings = suppress;
1114 };
1115
1116 /**
1117 * Add plugins to precaching.
1118 *
1119 * @param {Array<Object>} newPlugins
1120 *
1121 * @alias workbox.precaching.addPlugins
1122 */
1123 moduleExports.addPlugins = newPlugins => {
1124 plugins = plugins.concat(newPlugins);
1125 };
1126
1127 /*
1128 Copyright 2017 Google Inc.
1129
1130 Licensed under the Apache License, Version 2.0 (the "License");
1131 you may not use this file except in compliance with the License.
1132 You may obtain a copy of the License at
1133
1134 https://www.apache.org/licenses/LICENSE-2.0
1135
1136 Unless required by applicable law or agreed to in writing, software
1137 distributed under the License is distributed on an "AS IS" BASIS,
1138 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1139 See the License for the specific language governing permissions and
1140 limitations under the License.
1141 */
1142
1143 const finalExport = Object.assign(moduleExports, publicAPI);
1144
1145 return finalExport;
1146
1147}(workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private));
1148
1149//# sourceMappingURL=workbox-precaching.dev.js.map