First of all thanks for your work. This is amazing 💯 !!
I'm new into service workers and I would like to have all the chunks served from the service worker but I can't :(
I have 2 instances of HtmlWebpackPlugin and I thought that maybe that's the problem. Because I get the assets from one (the one used for session management) but not the other one (the app itself).
Here is my webpack client config
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const { clientPath, staticPath, currentWorkingDirectory } = require('../../paths');
var OfflinePlugin = require('offline-plugin');
module.exports = {
entry: {
RPZopaCheckSession: path.resolve(currentWorkingDirectory, 'src', 'RPZopaCheckSession', 'index.ts'),
client: path.resolve(clientPath, 'client.tsx'),
},
output: {
filename: '[name].bundle.js',
chunkFilename: '[name].chunk.js',
path: path.resolve(currentWorkingDirectory, 'build', 'client'),
publicPath: '/assets/',
},
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
localIdentName: '[path][name]__[local]--[hash:base64:5]',
},
},
],
},
{
test: /\.(jpg|png|svg)$/,
use: {
loader: 'url-loader',
options: {
limit: 25000,
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'templates', 'index.ejs'),
chunks: ['client'],
//TODO: remove this patch for webpack 4 https://github.com/jantimon/html-webpack-plugin/issues/870
chunksSortMode: 'none',
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'templates', 'RPZopaCheckSession.ejs'),
filename: 'RPZopaCheckSession.html',
chunks: ['RPZopaCheckSession'],
//TODO: remove this patch for webpack 4 https://github.com/jantimon/html-webpack-plugin/issues/870
chunksSortMode: 'none',
}),
// Useful for optimizing de external dependencies. Uncomment to activate
// new BundleAnalyzerPlugin(),
// More info: https://github.com/NekR/offline-plugin
new OfflinePlugin({
autoUpdate: 1000 * 60 * 2, // 2 minutes
}),
],
resolve: {
alias: {
Redux: path.resolve(clientPath, 'redux'),
Hoc: path.resolve(clientPath, 'hoc'),
Components: path.resolve(clientPath, 'components'),
Form: path.resolve(clientPath, 'form'),
Services: path.resolve(clientPath, 'services'),
Constants: path.resolve(clientPath, 'constants'),
Scenes: path.resolve(clientPath, 'scenes'),
APIs: path.resolve(clientPath, 'apis'),
Types: path.resolve(clientPath, 'types'),
ClientTypes: path.resolve(clientPath, 'types'),
Helpers: path.resolve(clientPath, 'helpers'),
Styles: path.resolve(clientPath, 'styles'),
Static: staticPath,
Assets: staticPath, // alias for static
Data: path.resolve(clientPath, 'data'),
// lightweight react compatibility library.
// Comment this if we have compatibility problems.
// 'react': 'preact-compat',
// 'react-dom': 'preact-compat',
},
// Default setting: `[".js", ".json"]`. Augmented to allow for JSX, and
// TypeScript when leaving off module filename extension. The last item in
// the array, `"*"`, is to allow for proper module resolution for modules
// that are imported using an extension.
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '*'],
// When leaving `target` unspecified in the config, it defaults to `"web"`.
// Web's default is `mainFields: ["browser", "module", "main"]`, so the
// array is augmented to include support for TypeScript
mainFields: ['browser', 'module', 'main', 'types'],
},
};
This is the response of `sw.js`
var __wpo = {
"assets": {
"main": ["/assets/vendors~LandingPage.chunk.js", "/assets/UserDebugDashboard.chunk.js", "/assets/Holding.chunk.js", "/assets/Reconciliation.chunk.js", "/assets/Onboarding.chunk.js", "/assets/Dashboard.chunk.js", "/assets/LandingPage.chunk.js", "/assets/Matured.chunk.js", "/assets/Extended.chunk.js", "/assets/Extendable.chunk.js", "/assets/SuccessTermStarted.chunk.js", "/assets/PeriodOpenNoFunds.chunk.js", "/assets/OpenSufficientFunds.chunk.js", "/assets/OpenInsufficientFunds.chunk.js", "/assets/FailureInsufficientFunds.chunk.js", "/assets/client.bundle.js", "/assets/RPZopaCheckSession.bundle.js", "/assets/", "/assets/RPZopaCheckSession.html"],
"additional": [],
"optional": []
},
"externals": [],
"hashesMap": {
"b26ed730c4d746027ecf8ed45e9ac5155f2fe9b8": "/assets/vendors~LandingPage.chunk.js",
"80dde33288e336f02eef00eaedc65f10c5e547fe": "/assets/UserDebugDashboard.chunk.js",
"797c34956adc7bfcfd8d8a26bd64d9a671e34c78": "/assets/Holding.chunk.js",
"09b5668dd10bb852e2b6dd40a43d006e66eed7f3": "/assets/Reconciliation.chunk.js",
"63279e4e5133ea2b35f7ff2c43d6b9c95e3b170d": "/assets/Onboarding.chunk.js",
"006ead839ead716e71e46295d26e4701540e86a1": "/assets/Dashboard.chunk.js",
"c7d14756a60c74264d990682838e1287cd43c830": "/assets/LandingPage.chunk.js",
"dfb0d8e983a1711e1d7f52fedefcbd3c2bf96aa2": "/assets/Matured.chunk.js",
"62e5956fd553ac5607ae41d7b1a0c7019cf763bd": "/assets/Extended.chunk.js",
"650555e4bfccc8d7ebf75d409a8e88834f82f8e5": "/assets/Extendable.chunk.js",
"b8a8aa9f735e16d64216875486b5863a53368358": "/assets/SuccessTermStarted.chunk.js",
"3e8e59a88c0e29f089e73ed09ace19413a98f3ad": "/assets/PeriodOpenNoFunds.chunk.js",
"6203b6f9ec48da9085159b3d94558045544ffb0e": "/assets/OpenSufficientFunds.chunk.js",
"47806f72c18d51153720e5780d4e1bf5e2e2f2dd": "/assets/OpenInsufficientFunds.chunk.js",
"60706eb310642b9e09a2a73597d1187af593ee43": "/assets/FailureInsufficientFunds.chunk.js",
"15d44fb182d79f8305d277e231dc96bf92cecbeb": "/assets/client.bundle.js",
"5c047781010f9ee91c30d5898c0168cc61d46fc8": "/assets/RPZopaCheckSession.bundle.js",
"e4af508908b8cf38040f319a3a64bb793fde8655": "/assets/",
"36714bad7c524d7645ffbd92498d4d3ff798c9f8": "/assets/RPZopaCheckSession.html"
},
"strategy": "changed",
"responseStrategy": "cache-first",
"version": "2018-7-25 12:29:52",
"name": "webpack-offline",
"pluginVersion": "5.0.5",
"relativePaths": false
};
! function(e) {
var n = {};
function t(r) {
if (n[r]) return n[r].exports;
var o = n[r] = {
i: r,
l: !1,
exports: {}
};
return e[r].call(o.exports, o, o.exports, t), o.l = !0, o.exports
}
t.m = e, t.c = n, t.d = function(e, n, r) {
t.o(e, n) || Object.defineProperty(e, n, {
enumerable: !0,
get: r
})
}, t.r = function(e) {
"undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
value: "Module"
}), Object.defineProperty(e, "__esModule", {
value: !0
})
}, t.t = function(e, n) {
if (1 & n && (e = t(e)), 8 & n) return e;
if (4 & n && "object" == typeof e && e && e.__esModule) return e;
var r = Object.create(null);
if (t.r(r), Object.defineProperty(r, "default", {
enumerable: !0,
value: e
}), 2 & n && "string" != typeof e)
for (var o in e) t.d(r, o, function(n) {
return e[n]
}.bind(null, o));
return r
}, t.n = function(e) {
var n = e && e.__esModule ? function() {
return e.default
} : function() {
return e
};
return t.d(n, "a", n), n
}, t.o = function(e, n) {
return Object.prototype.hasOwnProperty.call(e, n)
}, t.p = "/assets/", t(t.s = 1)
}([function(e, n) {}, function(e, n, t) {
"use strict";
if (function() {
var e = ExtendableEvent.prototype.waitUntil,
n = FetchEvent.prototype.respondWith,
t = new WeakMap;
ExtendableEvent.prototype.waitUntil = function(n) {
var r = this,
o = t.get(r);
if (!o) return o = [Promise.resolve(n)], t.set(r, o), e.call(r, Promise.resolve().then(function e() {
var n = o.length;
return Promise.all(o.map(function(e) {
return e.catch(function() {})
})).then(function() {
return o.length != n ? e() : (t.delete(r), Promise.all(o))
})
}));
o.push(Promise.resolve(n))
}, FetchEvent.prototype.respondWith = function(e) {
return this.waitUntil(e), n.call(this, e)
}
}(), void 0 === r) var r = !1;
function o(e, n) {
var t = n.cacheMaps,
o = n.navigationPreload,
u = e.strategy,
l = e.responseStrategy,
h = e.assets,
d = e.hashesMap,
p = e.externals,
v = e.prefetchRequest || {
credentials: "same-origin",
mode: "cors"
},
m = e.name,
g = e.version,
w = m + ":" + g,
y = m + "$preload",
b = "__offline_webpack__data";
C();
var P = [].concat(h.main, h.additional, h.optional);
function O() {
if (!h.additional.length) return Promise.resolve();
r && console.log("[SW]:", "Caching additional");
return ("changed" === u ? U("additional") : S("additional")).catch(function(e) {
console.error("[SW]:", "Cache section `additional` failed to load")
})
}
function S(n) {
var t = h[n];
return caches.open(w).then(function(r) {
return A(r, t, {
bust: e.version,
request: v,
failAll: "main" === n
})
}).then(function() {
f("Cached assets: " + n, t)
}).catch(function(e) {
throw console.error(e), e
})
}
function U(n) {
return x().then(function(t) {
if (!t) return S(n);
var r = t[0],
o = t[1],
i = t[2],
a = i.hashmap,
c = i.version;
if (!i.hashmap || c === e.version) return S(n);
var u = Object.keys(a).map(function(e) {
return a[e]
}),
s = o.map(function(e) {
var n = new URL(e.url);
return n.search = "", n.hash = "", n.toString()
}),
l = h[n],
p = [],
m = l.filter(function(e) {
return -1 === s.indexOf(e) || -1 === u.indexOf(e)
});
Object.keys(d).forEach(function(e) {
var n = d[e];
if (-1 !== l.indexOf(n) && -1 === m.indexOf(n) && -1 === p.indexOf(n)) {
var t = a[e];
t && -1 !== s.indexOf(t) ? p.push([t, n]) : m.push(n)
}
}), f("Changed assets: " + n, m), f("Moved assets: " + n, p);
var g = Promise.all(p.map(function(e) {
return r.match(e[0]).then(function(n) {
return [e[1], n]
})
}));
return caches.open(w).then(function(t) {
var r = g.then(function(e) {
return Promise.all(e.map(function(e) {
return t.put(e[0], e[1])
}))
});
return Promise.all([r, A(t, m, {
bust: e.version,
request: v,
failAll: "main" === n,
deleteFirst: "main" !== n
})])
})
})
}
function R() {
return caches.keys().then(function(e) {
var n = e.map(function(e) {
if (0 === e.indexOf(m) && 0 !== e.indexOf(w)) return console.log("[SW]:", "Delete cache:", e), caches.delete(e)
});
return Promise.all(n)
})
}
function x() {
return caches.keys().then(function(e) {
for (var n = e.length, t = void 0; n-- && 0 !== (t = e[n]).indexOf(m););
if (t) {
var r = void 0;
return caches.open(t).then(function(e) {
return r = e, e.match(new URL(b, location).toString())
}).then(function(e) {
if (e) return Promise.all([r, r.keys(), e.json()])
})
}
})
}
function q() {
return caches.open(w).then(function(n) {
var t = new Response(JSON.stringify({
version: e.version,
hashmap: d
}));
return n.put(new URL(b, location).toString(), t)
})
}
function W(e, n, t) {
return k(e), i(t, w).then(function(o) {
return o ? (r && console.log("[SW]:", "URL [" + t + "](" + n + ") from cache"), o) : fetch(e.request).then(function(o) {
return o.ok ? (r && console.log("[SW]:", "URL [" + n + "] from network"), t === n && function() {
var t = o.clone(),
r = caches.open(w).then(function(e) {
return e.put(n, t)
}).then(function() {
console.log("[SW]:", "Cache asset: " + n)
});
e.waitUntil(r)
}(), o) : (r && console.log("[SW]:", "URL [" + n + "] wrong response: [" + o.status + "] " + o.type), o)
})
})
}
function L(e, n, t) {
return F(e).then(function(e) {
if (e.ok) return r && console.log("[SW]:", "URL [" + n + "] from network"), e;
throw e
}).catch(function(e) {
return r && console.log("[SW]:", "URL [" + n + "] from cache if possible"), i(t, w).then(function(n) {
if (n) return n;
if (e instanceof Response) return e;
throw e
})
})
}
function k(e) {
if (o && "function" == typeof o.map && e.preloadResponse && "navigate" === e.request.mode) {
var n = o.map(new URL(e.request.url), e.request);
n && _(n, e)
}
}
self.addEventListener("install", function(e) {
console.log("[SW]:", "Install event");
var n = void 0;
n = "changed" === u ? U("main") : S("main"), e.waitUntil(n)
}), self.addEventListener("activate", function(e) {
console.log("[SW]:", "Activate event");
var n = O();
n = (n = (n = n.then(q)).then(R)).then(function() {
if (self.clients && self.clients.claim) return self.clients.claim()
}), o && self.registration.navigationPreload && (n = Promise.all([n, self.registration.navigationPreload.enable()])), e.waitUntil(n)
}), self.addEventListener("fetch", function(e) {
if ("GET" === e.request.method && ("only-if-cached" !== e.request.cache || "same-origin" === e.request.mode)) {
var n = new URL(e.request.url);
n.hash = "";
var t = n.toString(); - 1 === p.indexOf(t) && (n.search = "", t = n.toString());
var r = -1 !== P.indexOf(t),
i = t;
if (!r) {
var a = T(e.request);
a && (i = a, r = !0)
}
if (r) {
var c = void 0;
c = "network-first" === l ? L(e, t, i) : W(e, t, i), e.respondWith(c)
} else {
if ("navigate" === e.request.mode && !0 === o) return void e.respondWith(F(e));
if (o) {
var u = M(e);
if (u) return void e.respondWith(u)
}
}
}
}), self.addEventListener("message", function(e) {
var n = e.data;
if (n) switch (n.action) {
case "skipWaiting":
self.skipWaiting && self.skipWaiting()
}
});
var E = new Map;
function _(e, n) {
var t = new URL(e, location),
r = n.preloadResponse;
E.set(r, {
url: t,
response: r
});
var o = function() {
return E.has(r)
},
i = r.then(function(e) {
if (e && o()) {
var n = e.clone();
return caches.open(y).then(function(e) {
if (o()) return e.put(t, n).then(function() {
if (!o()) return caches.open(y).then(function(e) {
return e.delete(t)
})
})
})
}
});
n.waitUntil(i)
}
function j(e) {
if (E) {
var n = void 0,
t = void 0;
return E.forEach(function(r, o) {
r.url.href === e.href && (n = r.response, t = o)
}), n ? (E.delete(t), n) : void 0
}
}
function M(e) {
var n = new URL(e.request.url);
if (self.registration.navigationPreload && o && o.test && o.test(n, e.request)) {
var t = j(n),
r = e.request;
return t ? (e.waitUntil(caches.open(y).then(function(e) {
return e.delete(r)
})), t) : i(r, y).then(function(n) {
return n && e.waitUntil(caches.open(y).then(function(e) {
return e.delete(r)
})), n || fetch(e.request)
})
}
}
function C() {
Object.keys(h).forEach(function(e) {
h[e] = h[e].map(function(e) {
var n = new URL(e, location);
return n.hash = "", -1 === p.indexOf(e) && (n.search = ""), n.toString()
})
}), d = Object.keys(d).reduce(function(e, n) {
var t = new URL(d[n], location);
return t.search = "", t.hash = "", e[n] = t.toString(), e
}, {}), p = p.map(function(e) {
var n = new URL(e, location);
return n.hash = "", n.toString()
})
}
function A(e, n, t) {
var r = t.bust,
o = !1 !== t.failAll,
i = !0 === t.deleteFirst,
c = t.request || {
credentials: "omit",
mode: "cors"
},
u = Promise.resolve();
return i && (u = Promise.all(n.map(function(n) {
return e.delete(n).catch(function() {})
}))), Promise.all(n.map(function(e) {
return r && (e = a(e, r)), fetch(e, c).then(s).then(function(e) {
return e.ok ? {
response: e
} : {
error: !0
}
}, function() {
return {
error: !0
}
})
})).then(function(t) {
return o && t.some(function(e) {
return e.error
}) ? Promise.reject(new Error("Wrong response status")) : (o || (t = t.filter(function(e) {
return !e.error
})), u.then(function() {
var r = t.map(function(t, r) {
var o = t.response;
return e.put(n[r], o)
});
return Promise.all(r)
}))
})
}
function T(e) {
var n = e.url,
r = new URL(n),
o = void 0;
o = c(e) ? "navigate" : r.origin === location.origin ? "same-origin" : "cross-origin";
for (var i = 0; i < t.length; i++) {
var a = t[i];
if (a && (!a.requestTypes || -1 !== a.requestTypes.indexOf(o))) {
var u = void 0;
if ((u = "function" == typeof a.match ? a.match(r, e) : n.replace(a.match, a.to)) && u !== n) return u
}
}
}
function F(e) {
return e.preloadResponse && !0 === o ? e.preloadResponse.then(function(n) {
return n || fetch(e.request)
}) : fetch(e.request)
}
}
function i(e, n) {
return caches.match(e, {
cacheName: n
}).then(function(t) {
return u(t) ? t : s(t).then(function(t) {
return caches.open(n).then(function(n) {
return n.put(e, t)
}).then(function() {
return t
})
})
}).catch(function() {})
}
function a(e, n) {
return e + (-1 !== e.indexOf("?") ? "&" : "?") + "__uncache=" + encodeURIComponent(n)
}
function c(e) {
return "navigate" === e.mode || e.headers.get("Upgrade-Insecure-Requests") || -1 !== (e.headers.get("Accept") || "").indexOf("text/html")
}
function u(e) {
return !e || !e.redirected || !e.ok || "opaqueredirect" === e.type
}
function s(e) {
return u(e) ? Promise.resolve(e) : ("body" in e ? Promise.resolve(e.body) : e.blob()).then(function(n) {
return new Response(n, {
headers: e.headers,
status: e.status
})
})
}
function f(e, n) {
console.groupCollapsed("[SW]:", e), n.forEach(function(e) {
console.log("Asset:", e)
}), console.groupEnd()
}
o(__wpo, {
loaders: {},
cacheMaps: [],
navigationPreload: !1
}), e.exports = t(0)
}]);
Hi,
First of all thanks for your work. This is amazing 💯 !!
I'm new into service workers and I would like to have all the chunks served from the service worker but I can't :(
I have 2 instances of HtmlWebpackPlugin and I thought that maybe that's the problem. Because I get the assets from one (the one used for session management) but not the other one (the app itself).
Here is my webpack client config
This is the response of `sw.js`
Thanks again :)