Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

306 строки
9.7 KiB

  1. /*
  2. Copyright 2017 Google Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. import {assert} from 'workbox-core/_private/assert.mjs';
  14. import {cacheNames} from 'workbox-core/_private/cacheNames.mjs';
  15. import {logger} from 'workbox-core/_private/logger.mjs';
  16. import {getFriendlyURL} from 'workbox-core/_private/getFriendlyURL.mjs';
  17. import PrecacheController from './controllers/PrecacheController.mjs';
  18. import './_version.mjs';
  19. if (process.env.NODE_ENV !== 'production') {
  20. assert.isSwEnv('workbox-precaching');
  21. }
  22. let installActivateListenersAdded = false;
  23. let fetchListenersAdded = false;
  24. let suppressWarnings = false;
  25. let plugins = [];
  26. const cacheName = cacheNames.getPrecacheName();
  27. const precacheController = new PrecacheController(cacheName);
  28. const _removeIgnoreUrlParams = (origUrlObject, ignoreUrlParametersMatching) => {
  29. // Exclude initial '?'
  30. const searchString = origUrlObject.search.slice(1);
  31. // Split into an array of 'key=value' strings
  32. const keyValueStrings = searchString.split('&');
  33. const keyValuePairs = keyValueStrings.map((keyValueString) => {
  34. // Split each 'key=value' string into a [key, value] array
  35. return keyValueString.split('=');
  36. });
  37. const filteredKeyValuesPairs = keyValuePairs.filter((keyValuePair) => {
  38. return ignoreUrlParametersMatching
  39. .every((ignoredRegex) => {
  40. // Return true iff the key doesn't match any of the regexes.
  41. return !ignoredRegex.test(keyValuePair[0]);
  42. });
  43. });
  44. const filteredStrings = filteredKeyValuesPairs.map((keyValuePair) => {
  45. // Join each [key, value] array into a 'key=value' string
  46. return keyValuePair.join('=');
  47. });
  48. // Join the array of 'key=value' strings into a string with '&' in
  49. // between each
  50. const urlClone = new URL(origUrlObject);
  51. urlClone.search = filteredStrings.join('&');
  52. return urlClone;
  53. };
  54. /**
  55. * This function will take the request URL and manipulate it based on the
  56. * configuration options.
  57. *
  58. * @param {string} url
  59. * @param {Object} options
  60. * @return {string|null} Returns the URL in the cache that matches the request
  61. * if available, other null.
  62. *
  63. * @private
  64. */
  65. const _getPrecachedUrl = (url, {
  66. ignoreUrlParametersMatching = [/^utm_/],
  67. directoryIndex = 'index.html',
  68. cleanUrls = true,
  69. urlManipulation = null,
  70. } = {}) => {
  71. const urlObject = new URL(url, location);
  72. // Change '/some-url#123' => '/some-url'
  73. urlObject.hash = '';
  74. const urlWithoutIgnoredParams = _removeIgnoreUrlParams(
  75. urlObject, ignoreUrlParametersMatching
  76. );
  77. let urlsToAttempt = [
  78. // Test the URL that was fetched
  79. urlObject,
  80. // Test the URL without search params
  81. urlWithoutIgnoredParams,
  82. ];
  83. // Test the URL with a directory index
  84. if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) {
  85. const directoryUrl = new URL(urlWithoutIgnoredParams);
  86. directoryUrl.pathname += directoryIndex;
  87. urlsToAttempt.push(directoryUrl);
  88. }
  89. // Test the URL with a '.html' extension
  90. if (cleanUrls) {
  91. const cleanUrl = new URL(urlWithoutIgnoredParams);
  92. cleanUrl.pathname += '.html';
  93. urlsToAttempt.push(cleanUrl);
  94. }
  95. if (urlManipulation) {
  96. const additionalUrls = urlManipulation({url: urlObject});
  97. urlsToAttempt = urlsToAttempt.concat(additionalUrls);
  98. }
  99. const cachedUrls = precacheController.getCachedUrls();
  100. for (const possibleUrl of urlsToAttempt) {
  101. if (cachedUrls.indexOf(possibleUrl.href) !== -1) {
  102. // It's a perfect match
  103. if (process.env.NODE_ENV !== 'production') {
  104. logger.debug(`Precaching found a match for ` +
  105. getFriendlyURL(possibleUrl.toString()));
  106. }
  107. return possibleUrl.href;
  108. }
  109. }
  110. return null;
  111. };
  112. const moduleExports = {};
  113. /**
  114. * Add items to the precache list, removing any duplicates and
  115. * store the files in the
  116. * ["precache cache"]{@link module:workbox-core.cacheNames} when the service
  117. * worker installs.
  118. *
  119. * This method can be called multiple times.
  120. *
  121. * Please note: This method **will not** serve any of the cached files for you,
  122. * it only precaches files. To respond to a network request you call
  123. * [addRoute()]{@link module:workbox-precaching.addRoute}.
  124. *
  125. * If you have a single array of files to precache, you can just call
  126. * [precacheAndRoute()]{@link module:workbox-precaching.precacheAndRoute}.
  127. *
  128. * @param {Array<Object|string>} entries Array of entries to precache.
  129. *
  130. * @alias workbox.precaching.precache
  131. */
  132. moduleExports.precache = (entries) => {
  133. precacheController.addToCacheList(entries);
  134. if (installActivateListenersAdded || entries.length <= 0) {
  135. return;
  136. }
  137. installActivateListenersAdded = true;
  138. self.addEventListener('install', (event) => {
  139. event.waitUntil(precacheController.install({
  140. event,
  141. plugins,
  142. suppressWarnings,
  143. }));
  144. });
  145. self.addEventListener('activate', (event) => {
  146. event.waitUntil(precacheController.activate({
  147. event,
  148. plugins,
  149. }));
  150. });
  151. };
  152. /**
  153. * Add a `fetch` listener to the service worker that will
  154. * respond to
  155. * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests}
  156. * with precached assets.
  157. *
  158. * Requests for assets that aren't precached, the `FetchEvent` will not be
  159. * responded to, allowing the event to fall through to other `fetch` event
  160. * listeners.
  161. *
  162. * @param {Object} options
  163. * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will
  164. * check cache entries for a URLs ending with '/' to see if there is a hit when
  165. * appending the `directoryIndex` value.
  166. * @param {Array<RegExp>} [options.ignoreUrlParametersMatching=[/^utm_/]] An
  167. * array of regex's to remove search params when looking for a cache match.
  168. * @param {boolean} [options.cleanUrls=true] The `cleanUrls` option will
  169. * check the cache for the URL with a `.html` added to the end of the end.
  170. * @param {workbox.precaching~urlManipulation} [options.urlManipulation]
  171. * This is a function that should take a URL and return an array of
  172. * alternative URL's that should be checked for precache matches.
  173. *
  174. * @alias workbox.precaching.addRoute
  175. */
  176. moduleExports.addRoute = (options) => {
  177. if (fetchListenersAdded) {
  178. // TODO: Throw error here.
  179. return;
  180. }
  181. fetchListenersAdded = true;
  182. self.addEventListener('fetch', (event) => {
  183. const precachedUrl = _getPrecachedUrl(event.request.url, options);
  184. if (!precachedUrl) {
  185. if (process.env.NODE_ENV !== 'production') {
  186. logger.debug(`Precaching found no match for ` +
  187. getFriendlyURL(event.request.url));
  188. }
  189. return;
  190. }
  191. let responsePromise = caches.open(cacheName)
  192. .then((cache) => {
  193. return cache.match(precachedUrl);
  194. }).then((cachedResponse) => {
  195. if (cachedResponse) {
  196. return cachedResponse;
  197. }
  198. // Fall back to the network if we don't have a cached response (perhaps
  199. // due to manual cache cleanup).
  200. if (process.env.NODE_ENV !== 'production') {
  201. logger.debug(`The precached response for ` +
  202. `${getFriendlyURL(precachedUrl)} in ${cacheName} was not found. ` +
  203. `Falling back to the network instead.`);
  204. }
  205. return fetch(precachedUrl);
  206. });
  207. if (process.env.NODE_ENV !== 'production') {
  208. responsePromise = responsePromise.then((response) => {
  209. // Workbox is going to handle the route.
  210. // print the routing details to the console.
  211. logger.groupCollapsed(`Precaching is responding to: ` +
  212. getFriendlyURL(event.request.url));
  213. logger.log(`Serving the precached url: ${precachedUrl}`);
  214. // The Request and Response objects contains a great deal of
  215. // information, hide it under a group in case developers want to see it.
  216. logger.groupCollapsed(`View request details here.`);
  217. logger.unprefixed.log(event.request);
  218. logger.groupEnd();
  219. logger.groupCollapsed(`View response details here.`);
  220. logger.unprefixed.log(response);
  221. logger.groupEnd();
  222. logger.groupEnd();
  223. return response;
  224. });
  225. }
  226. event.respondWith(responsePromise);
  227. });
  228. };
  229. /**
  230. * This method will add entries to the precache list and add a route to
  231. * respond to fetch events.
  232. *
  233. * This is a convenience method that will call
  234. * [precache()]{@link module:workbox-precaching.precache} and
  235. * [addRoute()]{@link module:workbox-precaching.addRoute} in a single call.
  236. *
  237. * @param {Array<Object|string>} entries Array of entries to precache.
  238. * @param {Object} options See
  239. * [addRoute() options]{@link module:workbox-precaching.addRoute}.
  240. *
  241. * @alias workbox.precaching.precacheAndRoute
  242. */
  243. moduleExports.precacheAndRoute = (entries, options) => {
  244. moduleExports.precache(entries);
  245. moduleExports.addRoute(options);
  246. };
  247. /**
  248. * Warnings will be logged if any of the precached assets are entered without
  249. * a `revision` property. This is extremely dangerous if the URL's aren't
  250. * revisioned. However, the warnings can be supressed with this method.
  251. *
  252. * @param {boolean} suppress
  253. *
  254. * @alias workbox.precaching.suppressWarnings
  255. */
  256. moduleExports.suppressWarnings = (suppress) => {
  257. suppressWarnings = suppress;
  258. };
  259. /**
  260. * Add plugins to precaching.
  261. *
  262. * @param {Array<Object>} newPlugins
  263. *
  264. * @alias workbox.precaching.addPlugins
  265. */
  266. moduleExports.addPlugins = (newPlugins) => {
  267. plugins = plugins.concat(newPlugins);
  268. };
  269. export default moduleExports;