Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Browser alerts that Manifest start_url is not cached #125

Open
1 task done
matheusgrieger opened this issue Nov 24, 2017 · 11 comments
Open
1 task done

Browser alerts that Manifest start_url is not cached #125

matheusgrieger opened this issue Nov 24, 2017 · 11 comments

Comments

@matheusgrieger
Copy link

  • I'm submitting a support request

webpack version:
3.8.1

sw-precache-webpack-plugin version:
0.11.4

Please tell us about your environment:
Linux Ubuntu 17.10 and Windows 10

Browser: Chrome 62.0.3202.94 (64 bits)

Current behavior:
The sw.js gets generated correctly and all static assets are being cached as expected but other requests (such as specified start_url in manifest.json) are not, so after looking at a Lighthouse report within Chrome, it points to that error.

Expected/desired behavior:
Other requests should be cached as well as the static assets.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem along with your:
Not sure if it is a bug or I'm doing something wrong.

  • Webpack configuration:
new SWPrecachePlugin({
  filename: '../../sw.js',
  cacheId: 'projectName', // removed for this example
  staticFileGlobs: ['public/static/**/*.{js,css}'],
  minify: true,
  staticFileGlobsIgnorePatterns: [/\.map$/],
  navigateFallback: '/'
})
  • Generated service worker (not minified):
'use strict';

var precacheConfig = [["public/static/css/app.cc24ceb5a97c2797a814e6ce8ff8a114.css","3a013b79477bece4a6cc4b7df6e71396"],["public/static/js/app.8d33aed6ca8472d71c3b.js","58c1216858cce7389f6a6548f4747508"],["public/static/js/manifest.c21466ca933b31a4f4e8.js","0b0855da24e74d63e9d55b2d75ab9f38"],["public/static/js/vendor.49c73aaccaf22bf7a987.js","a63c7b3b582032741955aeed76f14a39"]];
var cacheName = 'sw-precache-v3-projectName-' + (self.registration ? self.registration.scope : ''); // cache name changed for this example


var ignoreUrlParametersMatching = [/^utm_/];



var addDirectoryIndex = function (originalUrl, index) {
    var url = new URL(originalUrl);
    if (url.pathname.slice(-1) === '/') {
      url.pathname += index;
    }
    return url.toString();
  };

var cleanResponse = function (originalResponse) {
    // If this is not a redirected response, then we don't have to do anything.
    if (!originalResponse.redirected) {
      return Promise.resolve(originalResponse);
    }

    // Firefox 50 and below doesn't support the Response.body stream, so we may
    // need to read the entire body to memory as a Blob.
    var bodyPromise = 'body' in originalResponse ?
      Promise.resolve(originalResponse.body) :
      originalResponse.blob();

    return bodyPromise.then(function(body) {
      // new Response() is happy when passed either a stream or a Blob.
      return new Response(body, {
        headers: originalResponse.headers,
        status: originalResponse.status,
        statusText: originalResponse.statusText
      });
    });
  };

var createCacheKey = function (originalUrl, paramName, paramValue,
                           dontCacheBustUrlsMatching) {
    // Create a new URL object to avoid modifying originalUrl.
    var url = new URL(originalUrl);

    // If dontCacheBustUrlsMatching is not set, or if we don't have a match,
    // then add in the extra cache-busting URL parameter.
    if (!dontCacheBustUrlsMatching ||
        !(url.pathname.match(dontCacheBustUrlsMatching))) {
      url.search += (url.search ? '&' : '') +
        encodeURIComponent(paramName) + '=' + encodeURIComponent(paramValue);
    }

    return url.toString();
  };

var isPathWhitelisted = function (whitelist, absoluteUrlString) {
    // If the whitelist is empty, then consider all URLs to be whitelisted.
    if (whitelist.length === 0) {
      return true;
    }

    // Otherwise compare each path regex to the path of the URL passed in.
    var path = (new URL(absoluteUrlString)).pathname;
    return whitelist.some(function(whitelistedPathRegex) {
      return path.match(whitelistedPathRegex);
    });
  };

var stripIgnoredUrlParameters = function (originalUrl,
    ignoreUrlParametersMatching) {
    var url = new URL(originalUrl);
    // Remove the hash; see https://github.com/GoogleChrome/sw-precache/issues/290
    url.hash = '';

    url.search = url.search.slice(1) // Exclude initial '?'
      .split('&') // Split into an array of 'key=value' strings
      .map(function(kv) {
        return kv.split('='); // Split each 'key=value' string into a [key, value] array
      })
      .filter(function(kv) {
        return ignoreUrlParametersMatching.every(function(ignoredRegex) {
          return !ignoredRegex.test(kv[0]); // Return true iff the key doesn't match any of the regexes.
        });
      })
      .map(function(kv) {
        return kv.join('='); // Join each [key, value] array into a 'key=value' string
      })
      .join('&'); // Join the array of 'key=value' strings into a string with '&' in between each

    return url.toString();
  };


var hashParamName = '_sw-precache';
var urlsToCacheKeys = new Map(
  precacheConfig.map(function(item) {
    var relativeUrl = item[0];
    var hash = item[1];
    var absoluteUrl = new URL(relativeUrl, self.location);
    var cacheKey = createCacheKey(absoluteUrl, hashParamName, hash, false);
    return [absoluteUrl.toString(), cacheKey];
  })
);

function setOfCachedUrls(cache) {
  return cache.keys().then(function(requests) {
    return requests.map(function(request) {
      return request.url;
    });
  }).then(function(urls) {
    return new Set(urls);
  });
}

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(cacheName).then(function(cache) {
      return setOfCachedUrls(cache).then(function(cachedUrls) {
        return Promise.all(
          Array.from(urlsToCacheKeys.values()).map(function(cacheKey) {
            // If we don't have a key matching url in the cache already, add it.
            if (!cachedUrls.has(cacheKey)) {
              var request = new Request(cacheKey, {credentials: 'same-origin'});
              return fetch(request).then(function(response) {
                // Bail out of installation unless we get back a 200 OK for
                // every request.
                if (!response.ok) {
                  throw new Error('Request for ' + cacheKey + ' returned a ' +
                    'response with status ' + response.status);
                }

                return cleanResponse(response).then(function(responseToCache) {
                  return cache.put(cacheKey, responseToCache);
                });
              });
            }
          })
        );
      });
    }).then(function() {
      
      // Force the SW to transition from installing -> active state
      return self.skipWaiting();
      
    })
  );
});

self.addEventListener('activate', function(event) {
  var setOfExpectedUrls = new Set(urlsToCacheKeys.values());

  event.waitUntil(
    caches.open(cacheName).then(function(cache) {
      return cache.keys().then(function(existingRequests) {
        return Promise.all(
          existingRequests.map(function(existingRequest) {
            if (!setOfExpectedUrls.has(existingRequest.url)) {
              return cache.delete(existingRequest);
            }
          })
        );
      });
    }).then(function() {
      
      return self.clients.claim();
      
    })
  );
});


self.addEventListener('fetch', function(event) {
  if (event.request.method === 'GET') {
    // Should we call event.respondWith() inside this fetch event handler?
    // This needs to be determined synchronously, which will give other fetch
    // handlers a chance to handle the request if need be.
    var shouldRespond;

    // First, remove all the ignored parameters and hash fragment, and see if we
    // have that URL in our cache. If so, great! shouldRespond will be true.
    var url = stripIgnoredUrlParameters(event.request.url, ignoreUrlParametersMatching);
    shouldRespond = urlsToCacheKeys.has(url);

    // If shouldRespond is false, check again, this time with 'index.html'
    // (or whatever the directoryIndex option is set to) at the end.
    var directoryIndex = 'index.html';
    if (!shouldRespond && directoryIndex) {
      url = addDirectoryIndex(url, directoryIndex);
      shouldRespond = urlsToCacheKeys.has(url);
    }

    // If shouldRespond is still false, check to see if this is a navigation
    // request, and if so, whether the URL matches navigateFallbackWhitelist.
    var navigateFallback = '/';
    if (!shouldRespond &&
        navigateFallback &&
        (event.request.mode === 'navigate') &&
        isPathWhitelisted([], event.request.url)) {
      url = new URL(navigateFallback, self.location).toString();
      shouldRespond = urlsToCacheKeys.has(url);
    }

    // If shouldRespond was set to true at any point, then call
    // event.respondWith(), using the appropriate cache key.
    if (shouldRespond) {
      event.respondWith(
        caches.open(cacheName).then(function(cache) {
          return cache.match(urlsToCacheKeys.get(url)).then(function(response) {
            if (response) {
              return response;
            }
            throw Error('The cached response that was expected is missing.');
          });
        }).catch(function(e) {
          // Fall back to just fetch()ing the request if some unexpected error
          // prevented the cached response from being valid.
          console.warn('Couldn\'t serve response for "%s" from cache: %O', event.request.url, e);
          return fetch(event.request);
        })
      );
    }
  }
});

Probably same problem as #114 but it had no code samples or descriptions, so I figured I'd make a new one.

@achelimed
Copy link

👍
I have the same problem with :
webpack version:
3.7.1

sw-precache-webpack-plugin version:
0.11.4

Environment:
Linux 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt11-1+deb8u5 (2015-10-09) x86_64 GNU/Linux

Browser:
Chrome Version 62.0.3202.89 (Official Build) (64-bit)

@JustFly1984
Copy link

I got the same issue, with Chrome's Canary Lighthouse audit.

On other side with standard chrome I do not have this errors, and my app installing to Android desktop as usual, so I have pretty high degree of confidence that it is a Chrome's bug.

@matheusgrieger
Copy link
Author

Testing on Lighthouse keeps pointing to the same error, "does not respond with 200 when offline" and "user will not be prompted to install web app". But after deployment and testing on Android, it does ask to install (only once, as stated in Chrome docs), but it indeed doesn't work offline. Not sure if it's a Chrome or ServiceWorker bug.

@luaz
Copy link

luaz commented Feb 4, 2018

If "start_url": "/", I usually just need use following code in service-worker.js to pass the lighthouse requirement of Failures: Service worker does not successfully server the manifest's start_url

self.addEventListener('install', function(event) {
  const CACHE_NAME = 'startup-v2';
  console.log('install: ' + CACHE_NAME);
  const urls = [
    '/'
  ];
  event.waitUntil(
    caches.open(CACHE_NAME).then(function(cache) {
      return cache.addAll(urls);
    })
  );
});

How do I achieve the above with SW Precache Webpack Plugin? (since service-worker.js is generated by the plugin)

@JustFly1984
Copy link

JustFly1984 commented Feb 4, 2018 via email

@luaz
Copy link

luaz commented Feb 4, 2018

I test it on Chrome 64 on Linux and is still getting the warning (stable, not canary).

The warning aside, how to add external assets (non-webpack assets) or urls to be cached by sw-precache-webpack-plugin? I am pretty new to PWA, so I might get the concept wrong here.

I use the following configuration as it will cache all js, css and html generated in webpack. What should I do if I want the service worker to cache some external resources (e.g. "/", or "/app" or "/data.json") which is not part of this webpack?

    new SWPrecacheWebpackPlugin({
      cacheId: 'test',
      filename: 'service-worker.js',
      staticFileGlobs: ['dist/**/*.{js,css,html}', '/'],
      // minify: true,
      stripPrefix: 'dist/'
    })

@matheusgrieger
Copy link
Author

@JustFly1984 no, not really. I never tested on Canary or Chromium, always on stable Chrome releases. All stated problems are originated from it. Also, as I said, it does prompt the user to install the PWA, but it doesn't work offline.

@JustFly1984
Copy link

Oh, my stable chrome is 63 version. Can you confirm if you have new option SEO in your Audit LightHouse, if i you have SEO, so it mean this bug went unfixed from Canary to Stable.

@matheusgrieger
Copy link
Author

My Chrome stable version is 64.0.3282.119, though I don't see the SEO option. Yet, here are the results I have when running the audits:

image

Remember, as I said, the user is prompted to install the web app, it just doesn't work offline. My start_url option is set to the full website URL instead of a slash ("/").

@darkiron
Copy link

Have same issue

1 similar comment
@a526672351
Copy link

Have same issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants