workbox-precaching.dev.js 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338
  1. this.workbox = this.workbox || {};
  2. this.workbox.precaching = (function (exports, assert_js, cacheNames_js, logger_js, WorkboxError_js, waitUntil_js, copyResponse_js, getFriendlyURL_js, Strategy_js, registerRoute_js, Route_js) {
  3. 'use strict';
  4. try {
  5. self['workbox:precaching:6.6.0'] && _();
  6. } catch (e) {}
  7. /*
  8. Copyright 2018 Google LLC
  9. Use of this source code is governed by an MIT-style
  10. license that can be found in the LICENSE file or at
  11. https://opensource.org/licenses/MIT.
  12. */
  13. const REVISION_SEARCH_PARAM = '__WB_REVISION__';
  14. /**
  15. * Converts a manifest entry into a versioned URL suitable for precaching.
  16. *
  17. * @param {Object|string} entry
  18. * @return {string} A URL with versioning info.
  19. *
  20. * @private
  21. * @memberof workbox-precaching
  22. */
  23. function createCacheKey(entry) {
  24. if (!entry) {
  25. throw new WorkboxError_js.WorkboxError('add-to-cache-list-unexpected-type', {
  26. entry
  27. });
  28. } // If a precache manifest entry is a string, it's assumed to be a versioned
  29. // URL, like '/app.abcd1234.js'. Return as-is.
  30. if (typeof entry === 'string') {
  31. const urlObject = new URL(entry, location.href);
  32. return {
  33. cacheKey: urlObject.href,
  34. url: urlObject.href
  35. };
  36. }
  37. const {
  38. revision,
  39. url
  40. } = entry;
  41. if (!url) {
  42. throw new WorkboxError_js.WorkboxError('add-to-cache-list-unexpected-type', {
  43. entry
  44. });
  45. } // If there's just a URL and no revision, then it's also assumed to be a
  46. // versioned URL.
  47. if (!revision) {
  48. const urlObject = new URL(url, location.href);
  49. return {
  50. cacheKey: urlObject.href,
  51. url: urlObject.href
  52. };
  53. } // Otherwise, construct a properly versioned URL using the custom Workbox
  54. // search parameter along with the revision info.
  55. const cacheKeyURL = new URL(url, location.href);
  56. const originalURL = new URL(url, location.href);
  57. cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);
  58. return {
  59. cacheKey: cacheKeyURL.href,
  60. url: originalURL.href
  61. };
  62. }
  63. /*
  64. Copyright 2020 Google LLC
  65. Use of this source code is governed by an MIT-style
  66. license that can be found in the LICENSE file or at
  67. https://opensource.org/licenses/MIT.
  68. */
  69. /**
  70. * A plugin, designed to be used with PrecacheController, to determine the
  71. * of assets that were updated (or not updated) during the install event.
  72. *
  73. * @private
  74. */
  75. class PrecacheInstallReportPlugin {
  76. constructor() {
  77. this.updatedURLs = [];
  78. this.notUpdatedURLs = [];
  79. this.handlerWillStart = async ({
  80. request,
  81. state
  82. }) => {
  83. // TODO: `state` should never be undefined...
  84. if (state) {
  85. state.originalRequest = request;
  86. }
  87. };
  88. this.cachedResponseWillBeUsed = async ({
  89. event,
  90. state,
  91. cachedResponse
  92. }) => {
  93. if (event.type === 'install') {
  94. if (state && state.originalRequest && state.originalRequest instanceof Request) {
  95. // TODO: `state` should never be undefined...
  96. const url = state.originalRequest.url;
  97. if (cachedResponse) {
  98. this.notUpdatedURLs.push(url);
  99. } else {
  100. this.updatedURLs.push(url);
  101. }
  102. }
  103. }
  104. return cachedResponse;
  105. };
  106. }
  107. }
  108. /*
  109. Copyright 2020 Google LLC
  110. Use of this source code is governed by an MIT-style
  111. license that can be found in the LICENSE file or at
  112. https://opensource.org/licenses/MIT.
  113. */
  114. /**
  115. * A plugin, designed to be used with PrecacheController, to translate URLs into
  116. * the corresponding cache key, based on the current revision info.
  117. *
  118. * @private
  119. */
  120. class PrecacheCacheKeyPlugin {
  121. constructor({
  122. precacheController
  123. }) {
  124. this.cacheKeyWillBeUsed = async ({
  125. request,
  126. params
  127. }) => {
  128. // Params is type any, can't change right now.
  129. /* eslint-disable */
  130. const cacheKey = (params === null || params === void 0 ? void 0 : params.cacheKey) || this._precacheController.getCacheKeyForURL(request.url);
  131. /* eslint-enable */
  132. return cacheKey ? new Request(cacheKey, {
  133. headers: request.headers
  134. }) : request;
  135. };
  136. this._precacheController = precacheController;
  137. }
  138. }
  139. /*
  140. Copyright 2018 Google LLC
  141. Use of this source code is governed by an MIT-style
  142. license that can be found in the LICENSE file or at
  143. https://opensource.org/licenses/MIT.
  144. */
  145. /**
  146. * @param {string} groupTitle
  147. * @param {Array<string>} deletedURLs
  148. *
  149. * @private
  150. */
  151. const logGroup = (groupTitle, deletedURLs) => {
  152. logger_js.logger.groupCollapsed(groupTitle);
  153. for (const url of deletedURLs) {
  154. logger_js.logger.log(url);
  155. }
  156. logger_js.logger.groupEnd();
  157. };
  158. /**
  159. * @param {Array<string>} deletedURLs
  160. *
  161. * @private
  162. * @memberof workbox-precaching
  163. */
  164. function printCleanupDetails(deletedURLs) {
  165. const deletionCount = deletedURLs.length;
  166. if (deletionCount > 0) {
  167. logger_js.logger.groupCollapsed(`During precaching cleanup, ` + `${deletionCount} cached ` + `request${deletionCount === 1 ? ' was' : 's were'} deleted.`);
  168. logGroup('Deleted Cache Requests', deletedURLs);
  169. logger_js.logger.groupEnd();
  170. }
  171. }
  172. /*
  173. Copyright 2018 Google LLC
  174. Use of this source code is governed by an MIT-style
  175. license that can be found in the LICENSE file or at
  176. https://opensource.org/licenses/MIT.
  177. */
  178. /**
  179. * @param {string} groupTitle
  180. * @param {Array<string>} urls
  181. *
  182. * @private
  183. */
  184. function _nestedGroup(groupTitle, urls) {
  185. if (urls.length === 0) {
  186. return;
  187. }
  188. logger_js.logger.groupCollapsed(groupTitle);
  189. for (const url of urls) {
  190. logger_js.logger.log(url);
  191. }
  192. logger_js.logger.groupEnd();
  193. }
  194. /**
  195. * @param {Array<string>} urlsToPrecache
  196. * @param {Array<string>} urlsAlreadyPrecached
  197. *
  198. * @private
  199. * @memberof workbox-precaching
  200. */
  201. function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) {
  202. const precachedCount = urlsToPrecache.length;
  203. const alreadyPrecachedCount = urlsAlreadyPrecached.length;
  204. if (precachedCount || alreadyPrecachedCount) {
  205. let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`;
  206. if (alreadyPrecachedCount > 0) {
  207. message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`;
  208. }
  209. logger_js.logger.groupCollapsed(message);
  210. _nestedGroup(`View newly precached URLs.`, urlsToPrecache);
  211. _nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached);
  212. logger_js.logger.groupEnd();
  213. }
  214. }
  215. /*
  216. Copyright 2020 Google LLC
  217. Use of this source code is governed by an MIT-style
  218. license that can be found in the LICENSE file or at
  219. https://opensource.org/licenses/MIT.
  220. */
  221. /**
  222. * A {@link workbox-strategies.Strategy} implementation
  223. * specifically designed to work with
  224. * {@link workbox-precaching.PrecacheController}
  225. * to both cache and fetch precached assets.
  226. *
  227. * Note: an instance of this class is created automatically when creating a
  228. * `PrecacheController`; it's generally not necessary to create this yourself.
  229. *
  230. * @extends workbox-strategies.Strategy
  231. * @memberof workbox-precaching
  232. */
  233. class PrecacheStrategy extends Strategy_js.Strategy {
  234. /**
  235. *
  236. * @param {Object} [options]
  237. * @param {string} [options.cacheName] Cache name to store and retrieve
  238. * requests. Defaults to the cache names provided by
  239. * {@link workbox-core.cacheNames}.
  240. * @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins}
  241. * to use in conjunction with this caching strategy.
  242. * @param {Object} [options.fetchOptions] Values passed along to the
  243. * {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init}
  244. * of all fetch() requests made by this strategy.
  245. * @param {Object} [options.matchOptions] The
  246. * {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions}
  247. * for any `cache.match()` or `cache.put()` calls made by this strategy.
  248. * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to
  249. * get the response from the network if there's a precache miss.
  250. */
  251. constructor(options = {}) {
  252. options.cacheName = cacheNames_js.cacheNames.getPrecacheName(options.cacheName);
  253. super(options);
  254. this._fallbackToNetwork = options.fallbackToNetwork === false ? false : true; // Redirected responses cannot be used to satisfy a navigation request, so
  255. // any redirected response must be "copied" rather than cloned, so the new
  256. // response doesn't contain the `redirected` flag. See:
  257. // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1
  258. this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);
  259. }
  260. /**
  261. * @private
  262. * @param {Request|string} request A request to run this strategy for.
  263. * @param {workbox-strategies.StrategyHandler} handler The event that
  264. * triggered the request.
  265. * @return {Promise<Response>}
  266. */
  267. async _handle(request, handler) {
  268. const response = await handler.cacheMatch(request);
  269. if (response) {
  270. return response;
  271. } // If this is an `install` event for an entry that isn't already cached,
  272. // then populate the cache.
  273. if (handler.event && handler.event.type === 'install') {
  274. return await this._handleInstall(request, handler);
  275. } // Getting here means something went wrong. An entry that should have been
  276. // precached wasn't found in the cache.
  277. return await this._handleFetch(request, handler);
  278. }
  279. async _handleFetch(request, handler) {
  280. let response;
  281. const params = handler.params || {}; // Fall back to the network if we're configured to do so.
  282. if (this._fallbackToNetwork) {
  283. {
  284. logger_js.logger.warn(`The precached response for ` + `${getFriendlyURL_js.getFriendlyURL(request.url)} in ${this.cacheName} was not ` + `found. Falling back to the network.`);
  285. }
  286. const integrityInManifest = params.integrity;
  287. const integrityInRequest = request.integrity;
  288. const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest; // Do not add integrity if the original request is no-cors
  289. // See https://github.com/GoogleChrome/workbox/issues/3096
  290. response = await handler.fetch(new Request(request, {
  291. integrity: request.mode !== 'no-cors' ? integrityInRequest || integrityInManifest : undefined
  292. })); // It's only "safe" to repair the cache if we're using SRI to guarantee
  293. // that the response matches the precache manifest's expectations,
  294. // and there's either a) no integrity property in the incoming request
  295. // or b) there is an integrity, and it matches the precache manifest.
  296. // See https://github.com/GoogleChrome/workbox/issues/2858
  297. // Also if the original request users no-cors we don't use integrity.
  298. // See https://github.com/GoogleChrome/workbox/issues/3096
  299. if (integrityInManifest && noIntegrityConflict && request.mode !== 'no-cors') {
  300. this._useDefaultCacheabilityPluginIfNeeded();
  301. const wasCached = await handler.cachePut(request, response.clone());
  302. {
  303. if (wasCached) {
  304. logger_js.logger.log(`A response for ${getFriendlyURL_js.getFriendlyURL(request.url)} ` + `was used to "repair" the precache.`);
  305. }
  306. }
  307. }
  308. } else {
  309. // This shouldn't normally happen, but there are edge cases:
  310. // https://github.com/GoogleChrome/workbox/issues/1441
  311. throw new WorkboxError_js.WorkboxError('missing-precache-entry', {
  312. cacheName: this.cacheName,
  313. url: request.url
  314. });
  315. }
  316. {
  317. const cacheKey = params.cacheKey || (await handler.getCacheKey(request, 'read')); // Workbox is going to handle the route.
  318. // print the routing details to the console.
  319. logger_js.logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL_js.getFriendlyURL(request.url));
  320. logger_js.logger.log(`Serving the precached url: ${getFriendlyURL_js.getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);
  321. logger_js.logger.groupCollapsed(`View request details here.`);
  322. logger_js.logger.log(request);
  323. logger_js.logger.groupEnd();
  324. logger_js.logger.groupCollapsed(`View response details here.`);
  325. logger_js.logger.log(response);
  326. logger_js.logger.groupEnd();
  327. logger_js.logger.groupEnd();
  328. }
  329. return response;
  330. }
  331. async _handleInstall(request, handler) {
  332. this._useDefaultCacheabilityPluginIfNeeded();
  333. const response = await handler.fetch(request); // Make sure we defer cachePut() until after we know the response
  334. // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737
  335. const wasCached = await handler.cachePut(request, response.clone());
  336. if (!wasCached) {
  337. // Throwing here will lead to the `install` handler failing, which
  338. // we want to do if *any* of the responses aren't safe to cache.
  339. throw new WorkboxError_js.WorkboxError('bad-precaching-response', {
  340. url: request.url,
  341. status: response.status
  342. });
  343. }
  344. return response;
  345. }
  346. /**
  347. * This method is complex, as there a number of things to account for:
  348. *
  349. * The `plugins` array can be set at construction, and/or it might be added to
  350. * to at any time before the strategy is used.
  351. *
  352. * At the time the strategy is used (i.e. during an `install` event), there
  353. * needs to be at least one plugin that implements `cacheWillUpdate` in the
  354. * array, other than `copyRedirectedCacheableResponsesPlugin`.
  355. *
  356. * - If this method is called and there are no suitable `cacheWillUpdate`
  357. * plugins, we need to add `defaultPrecacheCacheabilityPlugin`.
  358. *
  359. * - If this method is called and there is exactly one `cacheWillUpdate`, then
  360. * we don't have to do anything (this might be a previously added
  361. * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin).
  362. *
  363. * - If this method is called and there is more than one `cacheWillUpdate`,
  364. * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so,
  365. * we need to remove it. (This situation is unlikely, but it could happen if
  366. * the strategy is used multiple times, the first without a `cacheWillUpdate`,
  367. * and then later on after manually adding a custom `cacheWillUpdate`.)
  368. *
  369. * See https://github.com/GoogleChrome/workbox/issues/2737 for more context.
  370. *
  371. * @private
  372. */
  373. _useDefaultCacheabilityPluginIfNeeded() {
  374. let defaultPluginIndex = null;
  375. let cacheWillUpdatePluginCount = 0;
  376. for (const [index, plugin] of this.plugins.entries()) {
  377. // Ignore the copy redirected plugin when determining what to do.
  378. if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {
  379. continue;
  380. } // Save the default plugin's index, in case it needs to be removed.
  381. if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {
  382. defaultPluginIndex = index;
  383. }
  384. if (plugin.cacheWillUpdate) {
  385. cacheWillUpdatePluginCount++;
  386. }
  387. }
  388. if (cacheWillUpdatePluginCount === 0) {
  389. this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);
  390. } else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {
  391. // Only remove the default plugin; multiple custom plugins are allowed.
  392. this.plugins.splice(defaultPluginIndex, 1);
  393. } // Nothing needs to be done if cacheWillUpdatePluginCount is 1
  394. }
  395. }
  396. PrecacheStrategy.defaultPrecacheCacheabilityPlugin = {
  397. async cacheWillUpdate({
  398. response
  399. }) {
  400. if (!response || response.status >= 400) {
  401. return null;
  402. }
  403. return response;
  404. }
  405. };
  406. PrecacheStrategy.copyRedirectedCacheableResponsesPlugin = {
  407. async cacheWillUpdate({
  408. response
  409. }) {
  410. return response.redirected ? await copyResponse_js.copyResponse(response) : response;
  411. }
  412. };
  413. /*
  414. Copyright 2019 Google LLC
  415. Use of this source code is governed by an MIT-style
  416. license that can be found in the LICENSE file or at
  417. https://opensource.org/licenses/MIT.
  418. */
  419. /**
  420. * Performs efficient precaching of assets.
  421. *
  422. * @memberof workbox-precaching
  423. */
  424. class PrecacheController {
  425. /**
  426. * Create a new PrecacheController.
  427. *
  428. * @param {Object} [options]
  429. * @param {string} [options.cacheName] The cache to use for precaching.
  430. * @param {string} [options.plugins] Plugins to use when precaching as well
  431. * as responding to fetch events for precached assets.
  432. * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to
  433. * get the response from the network if there's a precache miss.
  434. */
  435. constructor({
  436. cacheName,
  437. plugins = [],
  438. fallbackToNetwork = true
  439. } = {}) {
  440. this._urlsToCacheKeys = new Map();
  441. this._urlsToCacheModes = new Map();
  442. this._cacheKeysToIntegrities = new Map();
  443. this._strategy = new PrecacheStrategy({
  444. cacheName: cacheNames_js.cacheNames.getPrecacheName(cacheName),
  445. plugins: [...plugins, new PrecacheCacheKeyPlugin({
  446. precacheController: this
  447. })],
  448. fallbackToNetwork
  449. }); // Bind the install and activate methods to the instance.
  450. this.install = this.install.bind(this);
  451. this.activate = this.activate.bind(this);
  452. }
  453. /**
  454. * @type {workbox-precaching.PrecacheStrategy} The strategy created by this controller and
  455. * used to cache assets and respond to fetch events.
  456. */
  457. get strategy() {
  458. return this._strategy;
  459. }
  460. /**
  461. * Adds items to the precache list, removing any duplicates and
  462. * stores the files in the
  463. * {@link workbox-core.cacheNames|"precache cache"} when the service
  464. * worker installs.
  465. *
  466. * This method can be called multiple times.
  467. *
  468. * @param {Array<Object|string>} [entries=[]] Array of entries to precache.
  469. */
  470. precache(entries) {
  471. this.addToCacheList(entries);
  472. if (!this._installAndActiveListenersAdded) {
  473. self.addEventListener('install', this.install);
  474. self.addEventListener('activate', this.activate);
  475. this._installAndActiveListenersAdded = true;
  476. }
  477. }
  478. /**
  479. * This method will add items to the precache list, removing duplicates
  480. * and ensuring the information is valid.
  481. *
  482. * @param {Array<workbox-precaching.PrecacheController.PrecacheEntry|string>} entries
  483. * Array of entries to precache.
  484. */
  485. addToCacheList(entries) {
  486. {
  487. assert_js.assert.isArray(entries, {
  488. moduleName: 'workbox-precaching',
  489. className: 'PrecacheController',
  490. funcName: 'addToCacheList',
  491. paramName: 'entries'
  492. });
  493. }
  494. const urlsToWarnAbout = [];
  495. for (const entry of entries) {
  496. // See https://github.com/GoogleChrome/workbox/issues/2259
  497. if (typeof entry === 'string') {
  498. urlsToWarnAbout.push(entry);
  499. } else if (entry && entry.revision === undefined) {
  500. urlsToWarnAbout.push(entry.url);
  501. }
  502. const {
  503. cacheKey,
  504. url
  505. } = createCacheKey(entry);
  506. const cacheMode = typeof entry !== 'string' && entry.revision ? 'reload' : 'default';
  507. if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) {
  508. throw new WorkboxError_js.WorkboxError('add-to-cache-list-conflicting-entries', {
  509. firstEntry: this._urlsToCacheKeys.get(url),
  510. secondEntry: cacheKey
  511. });
  512. }
  513. if (typeof entry !== 'string' && entry.integrity) {
  514. if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {
  515. throw new WorkboxError_js.WorkboxError('add-to-cache-list-conflicting-integrities', {
  516. url
  517. });
  518. }
  519. this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);
  520. }
  521. this._urlsToCacheKeys.set(url, cacheKey);
  522. this._urlsToCacheModes.set(url, cacheMode);
  523. if (urlsToWarnAbout.length > 0) {
  524. const warningMessage = `Workbox is precaching URLs without revision ` + `info: ${urlsToWarnAbout.join(', ')}\nThis is generally NOT safe. ` + `Learn more at https://bit.ly/wb-precache`;
  525. {
  526. logger_js.logger.warn(warningMessage);
  527. }
  528. }
  529. }
  530. }
  531. /**
  532. * Precaches new and updated assets. Call this method from the service worker
  533. * install event.
  534. *
  535. * Note: this method calls `event.waitUntil()` for you, so you do not need
  536. * to call it yourself in your event handlers.
  537. *
  538. * @param {ExtendableEvent} event
  539. * @return {Promise<workbox-precaching.InstallResult>}
  540. */
  541. install(event) {
  542. // waitUntil returns Promise<any>
  543. // eslint-disable-next-line @typescript-eslint/no-unsafe-return
  544. return waitUntil_js.waitUntil(event, async () => {
  545. const installReportPlugin = new PrecacheInstallReportPlugin();
  546. this.strategy.plugins.push(installReportPlugin); // Cache entries one at a time.
  547. // See https://github.com/GoogleChrome/workbox/issues/2528
  548. for (const [url, cacheKey] of this._urlsToCacheKeys) {
  549. const integrity = this._cacheKeysToIntegrities.get(cacheKey);
  550. const cacheMode = this._urlsToCacheModes.get(url);
  551. const request = new Request(url, {
  552. integrity,
  553. cache: cacheMode,
  554. credentials: 'same-origin'
  555. });
  556. await Promise.all(this.strategy.handleAll({
  557. params: {
  558. cacheKey
  559. },
  560. request,
  561. event
  562. }));
  563. }
  564. const {
  565. updatedURLs,
  566. notUpdatedURLs
  567. } = installReportPlugin;
  568. {
  569. printInstallDetails(updatedURLs, notUpdatedURLs);
  570. }
  571. return {
  572. updatedURLs,
  573. notUpdatedURLs
  574. };
  575. });
  576. }
  577. /**
  578. * Deletes assets that are no longer present in the current precache manifest.
  579. * Call this method from the service worker activate event.
  580. *
  581. * Note: this method calls `event.waitUntil()` for you, so you do not need
  582. * to call it yourself in your event handlers.
  583. *
  584. * @param {ExtendableEvent} event
  585. * @return {Promise<workbox-precaching.CleanupResult>}
  586. */
  587. activate(event) {
  588. // waitUntil returns Promise<any>
  589. // eslint-disable-next-line @typescript-eslint/no-unsafe-return
  590. return waitUntil_js.waitUntil(event, async () => {
  591. const cache = await self.caches.open(this.strategy.cacheName);
  592. const currentlyCachedRequests = await cache.keys();
  593. const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());
  594. const deletedURLs = [];
  595. for (const request of currentlyCachedRequests) {
  596. if (!expectedCacheKeys.has(request.url)) {
  597. await cache.delete(request);
  598. deletedURLs.push(request.url);
  599. }
  600. }
  601. {
  602. printCleanupDetails(deletedURLs);
  603. }
  604. return {
  605. deletedURLs
  606. };
  607. });
  608. }
  609. /**
  610. * Returns a mapping of a precached URL to the corresponding cache key, taking
  611. * into account the revision information for the URL.
  612. *
  613. * @return {Map<string, string>} A URL to cache key mapping.
  614. */
  615. getURLsToCacheKeys() {
  616. return this._urlsToCacheKeys;
  617. }
  618. /**
  619. * Returns a list of all the URLs that have been precached by the current
  620. * service worker.
  621. *
  622. * @return {Array<string>} The precached URLs.
  623. */
  624. getCachedURLs() {
  625. return [...this._urlsToCacheKeys.keys()];
  626. }
  627. /**
  628. * Returns the cache key used for storing a given URL. If that URL is
  629. * unversioned, like `/index.html', then the cache key will be the original
  630. * URL with a search parameter appended to it.
  631. *
  632. * @param {string} url A URL whose cache key you want to look up.
  633. * @return {string} The versioned URL that corresponds to a cache key
  634. * for the original URL, or undefined if that URL isn't precached.
  635. */
  636. getCacheKeyForURL(url) {
  637. const urlObject = new URL(url, location.href);
  638. return this._urlsToCacheKeys.get(urlObject.href);
  639. }
  640. /**
  641. * @param {string} url A cache key whose SRI you want to look up.
  642. * @return {string} The subresource integrity associated with the cache key,
  643. * or undefined if it's not set.
  644. */
  645. getIntegrityForCacheKey(cacheKey) {
  646. return this._cacheKeysToIntegrities.get(cacheKey);
  647. }
  648. /**
  649. * This acts as a drop-in replacement for
  650. * [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match)
  651. * with the following differences:
  652. *
  653. * - It knows what the name of the precache is, and only checks in that cache.
  654. * - It allows you to pass in an "original" URL without versioning parameters,
  655. * and it will automatically look up the correct cache key for the currently
  656. * active revision of that URL.
  657. *
  658. * E.g., `matchPrecache('index.html')` will find the correct precached
  659. * response for the currently active service worker, even if the actual cache
  660. * key is `'/index.html?__WB_REVISION__=1234abcd'`.
  661. *
  662. * @param {string|Request} request The key (without revisioning parameters)
  663. * to look up in the precache.
  664. * @return {Promise<Response|undefined>}
  665. */
  666. async matchPrecache(request) {
  667. const url = request instanceof Request ? request.url : request;
  668. const cacheKey = this.getCacheKeyForURL(url);
  669. if (cacheKey) {
  670. const cache = await self.caches.open(this.strategy.cacheName);
  671. return cache.match(cacheKey);
  672. }
  673. return undefined;
  674. }
  675. /**
  676. * Returns a function that looks up `url` in the precache (taking into
  677. * account revision information), and returns the corresponding `Response`.
  678. *
  679. * @param {string} url The precached URL which will be used to lookup the
  680. * `Response`.
  681. * @return {workbox-routing~handlerCallback}
  682. */
  683. createHandlerBoundToURL(url) {
  684. const cacheKey = this.getCacheKeyForURL(url);
  685. if (!cacheKey) {
  686. throw new WorkboxError_js.WorkboxError('non-precached-url', {
  687. url
  688. });
  689. }
  690. return options => {
  691. options.request = new Request(url);
  692. options.params = Object.assign({
  693. cacheKey
  694. }, options.params);
  695. return this.strategy.handle(options);
  696. };
  697. }
  698. }
  699. /*
  700. Copyright 2019 Google LLC
  701. Use of this source code is governed by an MIT-style
  702. license that can be found in the LICENSE file or at
  703. https://opensource.org/licenses/MIT.
  704. */
  705. let precacheController;
  706. /**
  707. * @return {PrecacheController}
  708. * @private
  709. */
  710. const getOrCreatePrecacheController = () => {
  711. if (!precacheController) {
  712. precacheController = new PrecacheController();
  713. }
  714. return precacheController;
  715. };
  716. /*
  717. Copyright 2019 Google LLC
  718. Use of this source code is governed by an MIT-style
  719. license that can be found in the LICENSE file or at
  720. https://opensource.org/licenses/MIT.
  721. */
  722. /**
  723. * Adds plugins to the precaching strategy.
  724. *
  725. * @param {Array<Object>} plugins
  726. *
  727. * @memberof workbox-precaching
  728. */
  729. function addPlugins(plugins) {
  730. const precacheController = getOrCreatePrecacheController();
  731. precacheController.strategy.plugins.push(...plugins);
  732. }
  733. /*
  734. Copyright 2018 Google LLC
  735. Use of this source code is governed by an MIT-style
  736. license that can be found in the LICENSE file or at
  737. https://opensource.org/licenses/MIT.
  738. */
  739. /**
  740. * Removes any URL search parameters that should be ignored.
  741. *
  742. * @param {URL} urlObject The original URL.
  743. * @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against
  744. * each search parameter name. Matches mean that the search parameter should be
  745. * ignored.
  746. * @return {URL} The URL with any ignored search parameters removed.
  747. *
  748. * @private
  749. * @memberof workbox-precaching
  750. */
  751. function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching = []) {
  752. // Convert the iterable into an array at the start of the loop to make sure
  753. // deletion doesn't mess up iteration.
  754. for (const paramName of [...urlObject.searchParams.keys()]) {
  755. if (ignoreURLParametersMatching.some(regExp => regExp.test(paramName))) {
  756. urlObject.searchParams.delete(paramName);
  757. }
  758. }
  759. return urlObject;
  760. }
  761. /*
  762. Copyright 2019 Google LLC
  763. Use of this source code is governed by an MIT-style
  764. license that can be found in the LICENSE file or at
  765. https://opensource.org/licenses/MIT.
  766. */
  767. /**
  768. * Generator function that yields possible variations on the original URL to
  769. * check, one at a time.
  770. *
  771. * @param {string} url
  772. * @param {Object} options
  773. *
  774. * @private
  775. * @memberof workbox-precaching
  776. */
  777. function* generateURLVariations(url, {
  778. ignoreURLParametersMatching = [/^utm_/, /^fbclid$/],
  779. directoryIndex = 'index.html',
  780. cleanURLs = true,
  781. urlManipulation
  782. } = {}) {
  783. const urlObject = new URL(url, location.href);
  784. urlObject.hash = '';
  785. yield urlObject.href;
  786. const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);
  787. yield urlWithoutIgnoredParams.href;
  788. if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) {
  789. const directoryURL = new URL(urlWithoutIgnoredParams.href);
  790. directoryURL.pathname += directoryIndex;
  791. yield directoryURL.href;
  792. }
  793. if (cleanURLs) {
  794. const cleanURL = new URL(urlWithoutIgnoredParams.href);
  795. cleanURL.pathname += '.html';
  796. yield cleanURL.href;
  797. }
  798. if (urlManipulation) {
  799. const additionalURLs = urlManipulation({
  800. url: urlObject
  801. });
  802. for (const urlToAttempt of additionalURLs) {
  803. yield urlToAttempt.href;
  804. }
  805. }
  806. }
  807. /*
  808. Copyright 2020 Google LLC
  809. Use of this source code is governed by an MIT-style
  810. license that can be found in the LICENSE file or at
  811. https://opensource.org/licenses/MIT.
  812. */
  813. /**
  814. * A subclass of {@link workbox-routing.Route} that takes a
  815. * {@link workbox-precaching.PrecacheController}
  816. * instance and uses it to match incoming requests and handle fetching
  817. * responses from the precache.
  818. *
  819. * @memberof workbox-precaching
  820. * @extends workbox-routing.Route
  821. */
  822. class PrecacheRoute extends Route_js.Route {
  823. /**
  824. * @param {PrecacheController} precacheController A `PrecacheController`
  825. * instance used to both match requests and respond to fetch events.
  826. * @param {Object} [options] Options to control how requests are matched
  827. * against the list of precached URLs.
  828. * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will
  829. * check cache entries for a URLs ending with '/' to see if there is a hit when
  830. * appending the `directoryIndex` value.
  831. * @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/, /^fbclid$/]] An
  832. * array of regex's to remove search params when looking for a cache match.
  833. * @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will
  834. * check the cache for the URL with a `.html` added to the end of the end.
  835. * @param {workbox-precaching~urlManipulation} [options.urlManipulation]
  836. * This is a function that should take a URL and return an array of
  837. * alternative URLs that should be checked for precache matches.
  838. */
  839. constructor(precacheController, options) {
  840. const match = ({
  841. request
  842. }) => {
  843. const urlsToCacheKeys = precacheController.getURLsToCacheKeys();
  844. for (const possibleURL of generateURLVariations(request.url, options)) {
  845. const cacheKey = urlsToCacheKeys.get(possibleURL);
  846. if (cacheKey) {
  847. const integrity = precacheController.getIntegrityForCacheKey(cacheKey);
  848. return {
  849. cacheKey,
  850. integrity
  851. };
  852. }
  853. }
  854. {
  855. logger_js.logger.debug(`Precaching did not find a match for ` + getFriendlyURL_js.getFriendlyURL(request.url));
  856. }
  857. return;
  858. };
  859. super(match, precacheController.strategy);
  860. }
  861. }
  862. /*
  863. Copyright 2019 Google LLC
  864. Use of this source code is governed by an MIT-style
  865. license that can be found in the LICENSE file or at
  866. https://opensource.org/licenses/MIT.
  867. */
  868. /**
  869. * Add a `fetch` listener to the service worker that will
  870. * respond to
  871. * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests}
  872. * with precached assets.
  873. *
  874. * Requests for assets that aren't precached, the `FetchEvent` will not be
  875. * responded to, allowing the event to fall through to other `fetch` event
  876. * listeners.
  877. *
  878. * @param {Object} [options] See the {@link workbox-precaching.PrecacheRoute}
  879. * options.
  880. *
  881. * @memberof workbox-precaching
  882. */
  883. function addRoute(options) {
  884. const precacheController = getOrCreatePrecacheController();
  885. const precacheRoute = new PrecacheRoute(precacheController, options);
  886. registerRoute_js.registerRoute(precacheRoute);
  887. }
  888. /*
  889. Copyright 2018 Google LLC
  890. Use of this source code is governed by an MIT-style
  891. license that can be found in the LICENSE file or at
  892. https://opensource.org/licenses/MIT.
  893. */
  894. const SUBSTRING_TO_FIND = '-precache-';
  895. /**
  896. * Cleans up incompatible precaches that were created by older versions of
  897. * Workbox, by a service worker registered under the current scope.
  898. *
  899. * This is meant to be called as part of the `activate` event.
  900. *
  901. * This should be safe to use as long as you don't include `substringToFind`
  902. * (defaulting to `-precache-`) in your non-precache cache names.
  903. *
  904. * @param {string} currentPrecacheName The cache name currently in use for
  905. * precaching. This cache won't be deleted.
  906. * @param {string} [substringToFind='-precache-'] Cache names which include this
  907. * substring will be deleted (excluding `currentPrecacheName`).
  908. * @return {Array<string>} A list of all the cache names that were deleted.
  909. *
  910. * @private
  911. * @memberof workbox-precaching
  912. */
  913. const deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => {
  914. const cacheNames = await self.caches.keys();
  915. const cacheNamesToDelete = cacheNames.filter(cacheName => {
  916. return cacheName.includes(substringToFind) && cacheName.includes(self.registration.scope) && cacheName !== currentPrecacheName;
  917. });
  918. await Promise.all(cacheNamesToDelete.map(cacheName => self.caches.delete(cacheName)));
  919. return cacheNamesToDelete;
  920. };
  921. /*
  922. Copyright 2019 Google LLC
  923. Use of this source code is governed by an MIT-style
  924. license that can be found in the LICENSE file or at
  925. https://opensource.org/licenses/MIT.
  926. */
  927. /**
  928. * Adds an `activate` event listener which will clean up incompatible
  929. * precaches that were created by older versions of Workbox.
  930. *
  931. * @memberof workbox-precaching
  932. */
  933. function cleanupOutdatedCaches() {
  934. // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705
  935. self.addEventListener('activate', event => {
  936. const cacheName = cacheNames_js.cacheNames.getPrecacheName();
  937. event.waitUntil(deleteOutdatedCaches(cacheName).then(cachesDeleted => {
  938. {
  939. if (cachesDeleted.length > 0) {
  940. logger_js.logger.log(`The following out-of-date precaches were cleaned up ` + `automatically:`, cachesDeleted);
  941. }
  942. }
  943. }));
  944. });
  945. }
  946. /*
  947. Copyright 2019 Google LLC
  948. Use of this source code is governed by an MIT-style
  949. license that can be found in the LICENSE file or at
  950. https://opensource.org/licenses/MIT.
  951. */
  952. /**
  953. * Helper function that calls
  954. * {@link PrecacheController#createHandlerBoundToURL} on the default
  955. * {@link PrecacheController} instance.
  956. *
  957. * If you are creating your own {@link PrecacheController}, then call the
  958. * {@link PrecacheController#createHandlerBoundToURL} on that instance,
  959. * instead of using this function.
  960. *
  961. * @param {string} url The precached URL which will be used to lookup the
  962. * `Response`.
  963. * @param {boolean} [fallbackToNetwork=true] Whether to attempt to get the
  964. * response from the network if there's a precache miss.
  965. * @return {workbox-routing~handlerCallback}
  966. *
  967. * @memberof workbox-precaching
  968. */
  969. function createHandlerBoundToURL(url) {
  970. const precacheController = getOrCreatePrecacheController();
  971. return precacheController.createHandlerBoundToURL(url);
  972. }
  973. /*
  974. Copyright 2019 Google LLC
  975. Use of this source code is governed by an MIT-style
  976. license that can be found in the LICENSE file or at
  977. https://opensource.org/licenses/MIT.
  978. */
  979. /**
  980. * Takes in a URL, and returns the corresponding URL that could be used to
  981. * lookup the entry in the precache.
  982. *
  983. * If a relative URL is provided, the location of the service worker file will
  984. * be used as the base.
  985. *
  986. * For precached entries without revision information, the cache key will be the
  987. * same as the original URL.
  988. *
  989. * For precached entries with revision information, the cache key will be the
  990. * original URL with the addition of a query parameter used for keeping track of
  991. * the revision info.
  992. *
  993. * @param {string} url The URL whose cache key to look up.
  994. * @return {string} The cache key that corresponds to that URL.
  995. *
  996. * @memberof workbox-precaching
  997. */
  998. function getCacheKeyForURL(url) {
  999. const precacheController = getOrCreatePrecacheController();
  1000. return precacheController.getCacheKeyForURL(url);
  1001. }
  1002. /*
  1003. Copyright 2019 Google LLC
  1004. Use of this source code is governed by an MIT-style
  1005. license that can be found in the LICENSE file or at
  1006. https://opensource.org/licenses/MIT.
  1007. */
  1008. /**
  1009. * Helper function that calls
  1010. * {@link PrecacheController#matchPrecache} on the default
  1011. * {@link PrecacheController} instance.
  1012. *
  1013. * If you are creating your own {@link PrecacheController}, then call
  1014. * {@link PrecacheController#matchPrecache} on that instance,
  1015. * instead of using this function.
  1016. *
  1017. * @param {string|Request} request The key (without revisioning parameters)
  1018. * to look up in the precache.
  1019. * @return {Promise<Response|undefined>}
  1020. *
  1021. * @memberof workbox-precaching
  1022. */
  1023. function matchPrecache(request) {
  1024. const precacheController = getOrCreatePrecacheController();
  1025. return precacheController.matchPrecache(request);
  1026. }
  1027. /*
  1028. Copyright 2019 Google LLC
  1029. Use of this source code is governed by an MIT-style
  1030. license that can be found in the LICENSE file or at
  1031. https://opensource.org/licenses/MIT.
  1032. */
  1033. /**
  1034. * Adds items to the precache list, removing any duplicates and
  1035. * stores the files in the
  1036. * {@link workbox-core.cacheNames|"precache cache"} when the service
  1037. * worker installs.
  1038. *
  1039. * This method can be called multiple times.
  1040. *
  1041. * Please note: This method **will not** serve any of the cached files for you.
  1042. * It only precaches files. To respond to a network request you call
  1043. * {@link workbox-precaching.addRoute}.
  1044. *
  1045. * If you have a single array of files to precache, you can just call
  1046. * {@link workbox-precaching.precacheAndRoute}.
  1047. *
  1048. * @param {Array<Object|string>} [entries=[]] Array of entries to precache.
  1049. *
  1050. * @memberof workbox-precaching
  1051. */
  1052. function precache(entries) {
  1053. const precacheController = getOrCreatePrecacheController();
  1054. precacheController.precache(entries);
  1055. }
  1056. /*
  1057. Copyright 2019 Google LLC
  1058. Use of this source code is governed by an MIT-style
  1059. license that can be found in the LICENSE file or at
  1060. https://opensource.org/licenses/MIT.
  1061. */
  1062. /**
  1063. * This method will add entries to the precache list and add a route to
  1064. * respond to fetch events.
  1065. *
  1066. * This is a convenience method that will call
  1067. * {@link workbox-precaching.precache} and
  1068. * {@link workbox-precaching.addRoute} in a single call.
  1069. *
  1070. * @param {Array<Object|string>} entries Array of entries to precache.
  1071. * @param {Object} [options] See the
  1072. * {@link workbox-precaching.PrecacheRoute} options.
  1073. *
  1074. * @memberof workbox-precaching
  1075. */
  1076. function precacheAndRoute(entries, options) {
  1077. precache(entries);
  1078. addRoute(options);
  1079. }
  1080. /*
  1081. Copyright 2020 Google LLC
  1082. Use of this source code is governed by an MIT-style
  1083. license that can be found in the LICENSE file or at
  1084. https://opensource.org/licenses/MIT.
  1085. */
  1086. /**
  1087. * `PrecacheFallbackPlugin` allows you to specify an "offline fallback"
  1088. * response to be used when a given strategy is unable to generate a response.
  1089. *
  1090. * It does this by intercepting the `handlerDidError` plugin callback
  1091. * and returning a precached response, taking the expected revision parameter
  1092. * into account automatically.
  1093. *
  1094. * Unless you explicitly pass in a `PrecacheController` instance to the
  1095. * constructor, the default instance will be used. Generally speaking, most
  1096. * developers will end up using the default.
  1097. *
  1098. * @memberof workbox-precaching
  1099. */
  1100. class PrecacheFallbackPlugin {
  1101. /**
  1102. * Constructs a new PrecacheFallbackPlugin with the associated fallbackURL.
  1103. *
  1104. * @param {Object} config
  1105. * @param {string} config.fallbackURL A precached URL to use as the fallback
  1106. * if the associated strategy can't generate a response.
  1107. * @param {PrecacheController} [config.precacheController] An optional
  1108. * PrecacheController instance. If not provided, the default
  1109. * PrecacheController will be used.
  1110. */
  1111. constructor({
  1112. fallbackURL,
  1113. precacheController
  1114. }) {
  1115. /**
  1116. * @return {Promise<Response>} The precache response for the fallback URL.
  1117. *
  1118. * @private
  1119. */
  1120. this.handlerDidError = () => this._precacheController.matchPrecache(this._fallbackURL);
  1121. this._fallbackURL = fallbackURL;
  1122. this._precacheController = precacheController || getOrCreatePrecacheController();
  1123. }
  1124. }
  1125. exports.PrecacheController = PrecacheController;
  1126. exports.PrecacheFallbackPlugin = PrecacheFallbackPlugin;
  1127. exports.PrecacheRoute = PrecacheRoute;
  1128. exports.PrecacheStrategy = PrecacheStrategy;
  1129. exports.addPlugins = addPlugins;
  1130. exports.addRoute = addRoute;
  1131. exports.cleanupOutdatedCaches = cleanupOutdatedCaches;
  1132. exports.createHandlerBoundToURL = createHandlerBoundToURL;
  1133. exports.getCacheKeyForURL = getCacheKeyForURL;
  1134. exports.matchPrecache = matchPrecache;
  1135. exports.precache = precache;
  1136. exports.precacheAndRoute = precacheAndRoute;
  1137. return exports;
  1138. }({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core, workbox.core._private, workbox.strategies, workbox.routing, workbox.routing));
  1139. //# sourceMappingURL=workbox-precaching.dev.js.map