@@ -17,13 +17,6 @@ import {StrategyHandler} from 'workbox-strategies/StrategyHandler.js';
1717
1818import './_version.js' ;
1919
20-
21- const copyRedirectedCacheableResponsesPlugin : WorkboxPlugin = {
22- async cacheWillUpdate ( { response} ) {
23- return response . redirected ? await copyResponse ( response ) : response ;
24- }
25- }
26-
2720interface PrecacheStrategyOptions extends StrategyOptions {
2821 fallbackToNetwork ?: boolean ;
2922}
@@ -43,6 +36,22 @@ interface PrecacheStrategyOptions extends StrategyOptions {
4336class PrecacheStrategy extends Strategy {
4437 private readonly _fallbackToNetwork : boolean ;
4538
39+ static readonly defaultPrecacheCacheabilityPlugin : WorkboxPlugin = {
40+ async cacheWillUpdate ( { response} ) {
41+ if ( ! response || response . status >= 400 ) {
42+ return null ;
43+ }
44+
45+ return response ;
46+ }
47+ } ;
48+
49+ static readonly copyRedirectedCacheableResponsesPlugin : WorkboxPlugin = {
50+ async cacheWillUpdate ( { response} ) {
51+ return response . redirected ? await copyResponse ( response ) : response ;
52+ }
53+ } ;
54+
4655 /**
4756 *
4857 * @param {Object } [options]
@@ -70,7 +79,7 @@ class PrecacheStrategy extends Strategy {
7079 // any redirected response must be "copied" rather than cloned, so the new
7180 // response doesn't contain the `redirected` flag. See:
7281 // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1
73- this . plugins . push ( copyRedirectedCacheableResponsesPlugin ) ;
82+ this . plugins . push ( PrecacheStrategy . copyRedirectedCacheableResponsesPlugin ) ;
7483 }
7584
7685 /**
@@ -140,20 +149,14 @@ class PrecacheStrategy extends Strategy {
140149 }
141150
142151 async _handleInstall ( request : Request , handler : StrategyHandler ) {
143- const response = await handler . fetchAndCachePut ( request ) ;
144-
145- // Any time there's no response, consider it a precaching error.
146- let responseSafeToPrecache = Boolean ( response ) ;
152+ this . _useDefaultCacheabilityPluginIfNeeded ( ) ;
147153
148- // Also consider it an error if the user didn't pass their own
149- // cacheWillUpdate plugin, and the response is a 400+ (note: this means
150- // that by default opaque responses can be precached).
151- if ( response && response . status >= 400 &&
152- ! this . _usesCustomCacheableResponseLogic ( ) ) {
153- responseSafeToPrecache = false ;
154- }
154+ const response = await handler . fetch ( request ) ;
155155
156- if ( ! responseSafeToPrecache ) {
156+ // Make sure we defer cachePut() until after we know the response
157+ // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737
158+ const wasCached = await handler . cachePut ( request , response . clone ( ) ) ;
159+ if ( ! wasCached ) {
157160 // Throwing here will lead to the `install` handler failing, which
158161 // we want to do if *any* of the responses aren't safe to cache.
159162 throw new WorkboxError ( 'bad-precaching-response' , {
@@ -166,19 +169,59 @@ class PrecacheStrategy extends Strategy {
166169 }
167170
168171 /**
169- * Returns true if any users plugins were added containing their own
170- * `cacheWillUpdate` callback.
171- *
172- * This method indicates whether the default cacheable response logic (i.e.
173- * <400, including opaque responses) should be used. If a custom plugin
174- * with a `cacheWillUpdate` callback is passed, then the strategy should
175- * defer to that plugin's logic.
172+ * This method is complex, as there a number of things to account for:
173+ *
174+ * The `plugins` array can be set at construction, and/or it might be added to
175+ * to at any time before the strategy is used.
176+ *
177+ * At the time the strategy is used (i.e. during an `install` event), there
178+ * needs to be at least one plugin that implements `cacheWillUpdate` in the
179+ * array, other than `copyRedirectedCacheableResponsesPlugin`.
180+ *
181+ * - If this method is called and there are no suitable `cacheWillUpdate`
182+ * plugins, we need to add `defaultPrecacheCacheabilityPlugin`.
183+ *
184+ * - If this method is called and there is exactly one `cacheWillUpdate`, then
185+ * we don't have to do anything (this might be a previously added
186+ * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin).
187+ *
188+ * - If this method is called and there is more than one `cacheWillUpdate`,
189+ * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so,
190+ * we need to remove it. (This situation is unlikely, but it could happen if
191+ * the strategy is used multiple times, the first without a `cacheWillUpdate`,
192+ * and then later on after manually adding a custom `cacheWillUpdate`.)
193+ *
194+ * See https://github.com/GoogleChrome/workbox/issues/2737 for more context.
176195 *
177196 * @private
178197 */
179- _usesCustomCacheableResponseLogic ( ) : boolean {
180- return this . plugins . some ( ( plugin ) => plugin . cacheWillUpdate &&
181- plugin !== copyRedirectedCacheableResponsesPlugin ) ;
198+ _useDefaultCacheabilityPluginIfNeeded ( ) : void {
199+ let defaultPluginIndex : number | null = null ;
200+ let cacheWillUpdatePluginCount = 0 ;
201+
202+ for ( const [ index , plugin ] of this . plugins . entries ( ) ) {
203+ // Ignore the copy redirected plugin when determining what to do.
204+ if ( plugin === PrecacheStrategy . copyRedirectedCacheableResponsesPlugin ) {
205+ continue ;
206+ }
207+
208+ // Save the default plugin's index, in case it needs to be removed.
209+ if ( plugin === PrecacheStrategy . defaultPrecacheCacheabilityPlugin ) {
210+ defaultPluginIndex = index ;
211+ }
212+
213+ if ( plugin . cacheWillUpdate ) {
214+ cacheWillUpdatePluginCount ++ ;
215+ }
216+ }
217+
218+ if ( cacheWillUpdatePluginCount === 0 ) {
219+ this . plugins . push ( PrecacheStrategy . defaultPrecacheCacheabilityPlugin ) ;
220+ } else if ( cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null ) {
221+ // Only remove the default plugin; multiple custom plugins are allowed.
222+ this . plugins . splice ( defaultPluginIndex , 1 ) ;
223+ }
224+ // Nothing needs to be done if cacheWillUpdatePluginCount is 1
182225 }
183226}
184227
0 commit comments