Updates on server
kslstn opened this issue · 1 comments
- I'm submitting a bug report
- I'm submitting a feature request
- I'm submitting a support request
webpack version:
3.12
sw-precache-webpack-plugin version:
0.11.5
Please tell us about your environment:
MacOS/Linux
Browser: iOS Safari
Current behavior:
After an update to my application (can be as small as editing text), iOS shows a blank screen after refreshing the page/launching the add-to-homescreen web app. After clearing Safari's cache, the application works again.
Note that I have set up sw-precache to make the application work offline completely. The app runs completely in the client, so all assets can be cached.
Expected/desired behavior:
I expect the updated version to function. I am aware that would require the service worker to check for updates on the server and use the cached version when the server's not available. I don't know how to configure that though.
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:
- Webpack configuration:
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
var SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin')
const PUBLIC_PATH = 'https://www.bigtimer.net/'; // For SW Precache. webpack needs the trailing slash for output.publicPath
const env = require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js'),
publicPath: PUBLIC_PATH, // For SW Precache.
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to false
will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to true
because we are seeing that sourcemaps are included in the codesplit bundle as well when it's false
,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
]),
// service worker caching
new SWPrecacheWebpackPlugin({
cacheId: 'big-timer',
filename: 'service-worker.js',
staticFileGlobs: ['dist/**/*.{js,html,css}'],
minify: true,
navigateFallback: PUBLIC_PATH,
stripPrefix: 'dist/',
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
})
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
- Generated service worker (not minified):
'use strict'
var precacheConfig = [
['index.html', '277d4fc013034f1ce06a422f6feba69e'],
['service-worker.js', 'dd58fb74f7d4ad37db7d0ed0ade9858d'],
['static/404.html', '1b8119d030279687e0fae58aad5456b5'],
[
'static/css/app.9f44cebdd85d64e5f7283ddd7d7020fa.css',
'e3c073dabe268a54b64eea30dd70cefd'
],
[
'static/js/app.f2959bcb7abc904fbf43.js',
'96240dc4b7ddc3955b983d1e68d59b8d'
],
[
'static/js/manifest.28eddcffd035f5f85795.js',
'479b2f524fbd82c9ec2f28925b4e0521'
],
[
'static/js/vendor.25551477f25f30579d9c.js',
'69af8d0745788253d601840a8caad28b'
]
],
cacheName =
'sw-precache-v3-big-timer-' +
(self.registration ? self.registration.scope : ''),
ignoreUrlParametersMatching = [/^utm_/],
addDirectoryIndex = function(e, t) {
var n = new URL(e)
return '/' === n.pathname.slice(-1) && (n.pathname += t), n.toString()
},
cleanResponse = function(e) {
return e.redirected
? ('body' in e ? Promise.resolve(e.body) : e.blob()).then(function(t) {
return new Response(t, {
headers: e.headers,
status: e.status,
statusText: e.statusText
})
})
: Promise.resolve(e)
},
createCacheKey = function(e, t, n, r) {
var a = new URL(e)
return (
(r && a.pathname.match(r)) ||
(a.search +=
(a.search ? '&' : '') +
encodeURIComponent(t) +
'=' +
encodeURIComponent(n)),
a.toString()
)
},
isPathWhitelisted = function(e, t) {
if (0 === e.length) return !0
var n = new URL(t).pathname
return e.some(function(e) {
return n.match(e)
})
},
stripIgnoredUrlParameters = function(e, t) {
var n = new URL(e)
return (
(n.hash = ''),
(n.search = n.search
.slice(1)
.split('&')
.map(function(e) {
return e.split('=')
})
.filter(function(e) {
return t.every(function(t) {
return !t.test(e[0])
})
})
.map(function(e) {
return e.join('=')
})
.join('&')),
n.toString()
)
},
hashParamName = '_sw-precache',
urlsToCacheKeys = new Map(
precacheConfig.map(function(e) {
var t = e[0],
n = e[1],
r = new URL(t, self.location),
a = createCacheKey(r, hashParamName, n, !1)
return [r.toString(), a]
})
)
function setOfCachedUrls(e) {
return e
.keys()
.then(function(e) {
return e.map(function(e) {
return e.url
})
})
.then(function(e) {
return new Set(e)
})
}
self.addEventListener('install', function(e) {
e.waitUntil(
caches
.open(cacheName)
.then(function(e) {
return setOfCachedUrls(e).then(function(t) {
return Promise.all(
Array.from(urlsToCacheKeys.values()).map(function(n) {
if (!t.has(n)) {
var r = new Request(n, {credentials: 'same-origin'})
return fetch(r).then(function(t) {
if (!t.ok)
throw new Error(
'Request for ' +
n +
' returned a response with status ' +
t.status
)
return cleanResponse(t).then(function(t) {
return e.put(n, t)
})
})
}
})
)
})
})
.then(function() {
return self.skipWaiting()
})
)
}),
self.addEventListener('activate', function(e) {
var t = new Set(urlsToCacheKeys.values())
e.waitUntil(
caches
.open(cacheName)
.then(function(e) {
return e.keys().then(function(n) {
return Promise.all(
n.map(function(n) {
if (!t.has(n.url)) return e.delete(n)
})
)
})
})
.then(function() {
return self.clients.claim()
})
)
}),
self.addEventListener('fetch', function(e) {
if ('GET' === e.request.method) {
var t,
n = stripIgnoredUrlParameters(
e.request.url,
ignoreUrlParametersMatching
)
;(t = urlsToCacheKeys.has(n)) ||
((n = addDirectoryIndex(n, 'index.html')), (t = urlsToCacheKeys.has(n)))
!t &&
'navigate' === e.request.mode &&
isPathWhitelisted([], e.request.url) &&
((n = new URL('https://www.bigtimer.net/', self.location).toString()),
(t = urlsToCacheKeys.has(n))),
t &&
e.respondWith(
caches
.open(cacheName)
.then(function(e) {
return e.match(urlsToCacheKeys.get(n)).then(function(e) {
if (e) return e
throw Error(
'The cached response that was expected is missing.'
)
})
})
.catch(function(t) {
return (
console.warn(
'Couldn't serve response for "%s" from cache: %O',
e.request.url,
t
),
fetch(e.request)
)
})
)
}
})
Mm I meant the title to be “Updates on server break application on iOS”