Created
January 31, 2020 11:35
-
-
Save dsebastien/12c47fdb6517cfdab9473297f4472d22 to your computer and use it in GitHub Desktop.
Workbox 5 + Workbox build + TypeScript SW + Webpack build + Angular app
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
... | |
"scripts": { | |
"start:web:prod": "npm run build:prod:web && http-server dist/apps/web -d -c-1 -a 0.0.0.0 --proxy http://127.0.0.1:4200? --port 4200", | |
"start:web:prod:local": "npm run build:prod:web:local && http-server dist/apps/web -d -c-1 -a 0.0.0.0 --proxy http://127.0.0.1:4200? --port 4200", | |
"build:prod:web": "ng build web --prod", | |
"postbuild:prod:web": "npm run build:pwa:web", | |
"build:prod:web:local": "ng build web --prod", | |
"postbuild:prod:web:local": "npm run build:pwa:web:local", | |
"build:pwa:web": "rimraf ./dist/apps/web/service-worker.js && webpack --config ./service-worker/webpack.prod.config.js --progress --colors && node ./workbox-build-inject.js", | |
"build:pwa:web:local": "rimraf ./dist/apps/web/service-worker.js && webpack --config ./service-worker/webpack.dev.config.js --progress --colors && node ./workbox-build-inject.js", | |
}, | |
"private": true, | |
"dependencies": { | |
... | |
"workbox-core": "5.0.0", | |
"workbox-routing": "5.0.0", | |
"workbox-strategies": "5.0.0", | |
"workbox-precaching": "5.0.0", | |
"workbox-expiration": "5.0.0", | |
"workbox-background-sync": "5.0.0", | |
"workbox-cacheable-response": "5.0.0", | |
"workbox-window": "5.0.0", | |
"workbox-navigation-preload": "5.0.0", | |
"workbox-broadcast-update": "5.0.0", | |
}, | |
"devDependencies": { | |
... | |
"ts-loader": "6.2.1", | |
"typescript": "3.5.3", | |
"webpack": "4.41.5", | |
"webpack-cli": "3.3.10", | |
"workbox-build": "5.0.0" | |
}, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Service worker | |
// | |
// References | |
// https://github.com/webmaxru/pwatter/blob/workbox/src/sw-default.js | |
// Caching strategies: https://developers.google.com/web/tools/workbox/modules/workbox-strategies#stale-while-revalidate | |
// Example: https://github.com/JeremieLitzler/mws.nd.2018.s3/blob/master/sw.js | |
import { CacheableResponsePlugin } from "workbox-cacheable-response"; | |
import { ExpirationPlugin } from "workbox-expiration"; | |
import { precacheAndRoute, cleanupOutdatedCaches, createHandlerBoundToURL } from "workbox-precaching"; | |
import { registerRoute, NavigationRoute } from "workbox-routing"; | |
import { NetworkFirst, NetworkOnly, StaleWhileRevalidate, CacheFirst } from "workbox-strategies"; | |
declare const self: any; | |
const componentName = "Service Worker"; | |
// Enable debug mode during development | |
const DEBUG_MODE = location.hostname.endsWith(".app.local") || location.hostname === "localhost"; | |
const DAY_IN_SECONDS = 24 * 60 * 60; | |
const MONTH_IN_SECONDS = DAY_IN_SECONDS * 30; | |
const YEAR_IN_SECONDS = DAY_IN_SECONDS * 365; | |
/** | |
* The current version of the service worker. | |
*/ | |
const SERVICE_WORKER_VERSION = "1.0.0"; | |
if (DEBUG_MODE) { | |
console.debug(`Service worker version ${SERVICE_WORKER_VERSION} loading...`); | |
} | |
// ------------------------------------------------------------------------------------------ | |
// Precaching configuration | |
// ------------------------------------------------------------------------------------------ | |
cleanupOutdatedCaches(); | |
// Precaching | |
// Make sure that all the assets passed in the array below are fetched and cached | |
// The empty array below is replaced at build time with the full list of assets to cache | |
// This is done by workbox-build-inject.js for the production build | |
const assetsToCache = self.__WB_MANIFEST; | |
// To customize the assets afterwards: | |
//assetsToCache = [...assetsToCache, ???]; | |
if (DEBUG_MODE) { | |
console.trace(`${componentName}:: Assets that will be cached: `, assetsToCache); | |
} | |
precacheAndRoute(assetsToCache); | |
// ------------------------------------------------------------------------------------------ | |
// Routes | |
// ------------------------------------------------------------------------------------------ | |
// Default page handler for offline usage, | |
// where the browser does not how to handle deep links | |
// it's a SPA, so each path that is a navigation should default to index.html | |
const defaultRouteHandler = createHandlerBoundToURL("/index.html"); | |
const defaultNavigationRoute = new NavigationRoute(defaultRouteHandler, { | |
//allowlist: [], | |
//denylist: [], | |
}); | |
registerRoute(defaultNavigationRoute); | |
// Cache the Google Fonts stylesheets with a stale while revalidate strategy. | |
registerRoute( | |
/^https:\/\/fonts\.googleapis\.com/, | |
new StaleWhileRevalidate({ | |
cacheName: "google-fonts-stylesheets", | |
}), | |
); | |
// Cache the Google Fonts webfont files with a cache first strategy for 1 year. | |
registerRoute( | |
/^https:\/\/fonts\.gstatic\.com/, | |
new CacheFirst({ | |
cacheName: "google-fonts-webfonts", | |
plugins: [ | |
new CacheableResponsePlugin({ | |
statuses: [0, 200], | |
}), | |
new ExpirationPlugin({ | |
maxAgeSeconds: YEAR_IN_SECONDS, | |
maxEntries: 30, | |
purgeOnQuotaError: true, // Automatically cleanup if quota is exceeded. | |
}), | |
], | |
}), | |
); | |
// Make JS/CSS fast by returning assets from the cache | |
// But make sure they're updating in the background for next use | |
registerRoute(/\.(?:js|css)$/, new StaleWhileRevalidate()); | |
// Cache images | |
// But clean up after a while | |
registerRoute( | |
/\.(?:png|gif|jpg|jpeg|svg)$/, | |
new CacheFirst({ | |
cacheName: "images", | |
plugins: [ | |
new ExpirationPlugin({ | |
maxEntries: 250, | |
maxAgeSeconds: MONTH_IN_SECONDS, | |
purgeOnQuotaError: true, // Automatically cleanup if quota is exceeded. | |
}), | |
], | |
}), | |
); | |
// Anything authentication related MUST be performed online | |
registerRoute(/(https:\/\/)?([^\/\s]+\/)api\/v1\/auth\/.*/, new NetworkOnly()); | |
// Database access is only supported while online | |
registerRoute(/(https:\/\/)?([^\/\s]+\/)database\/.*/, new NetworkOnly()); | |
// ------------------------------------------------------------------------------------------ | |
// Messages | |
// ------------------------------------------------------------------------------------------ | |
self.addEventListener("message", (event: { data: any; type: any; ports: any }) => { | |
// TODO define/use correct data type | |
if (event && event.data && event.data.type) { | |
// return the version of this service worker | |
if ("GET_VERSION" === event.data.type) { | |
if (DEBUG_MODE) { | |
console.debug(`${componentName}:: Returning the service worker version: ${SERVICE_WORKER_VERSION}`); | |
} | |
event.ports[0].postMessage(SERVICE_WORKER_VERSION); | |
} | |
// When this message is received, we can skip waiting and become active | |
// (i.e., this version of the service worker becomes active) | |
// Reference about why we wait: https://stackoverflow.com/questions/51715127/what-are-the-downsides-to-using-skipwaiting-and-clientsclaim-with-workbox | |
if ("SKIP_WAITING" === event.data.type) { | |
if (DEBUG_MODE) { | |
console.debug(`${componentName}:: Skipping waiting...`); | |
} | |
self.skipWaiting(); | |
} | |
// When this message is received, we can take control of the clients with this version | |
// of the service worker | |
if ("CLIENTS_CLAIM" === event.data.type) { | |
if (DEBUG_MODE) { | |
console.debug(`${componentName}:: Claiming clients and cleaning old caches`); | |
} | |
self.clients.claim(); | |
} | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// DEV Webpack configuration used to build the service worker | |
const path = require("path"); | |
const webBuildTargetFolder = path.join(__dirname, "..", "dist", "apps", "web"); | |
const targetServiceWorkerFilename = "service-worker.js"; | |
module.exports = { | |
target: "node", | |
mode: "none", | |
// WARNING: commented out to disable source maps | |
//devtool: 'inline-source-map', | |
entry: { | |
index: path.join(__dirname, "src", "service-worker.ts"), | |
}, | |
resolve: { extensions: [".js", ".ts"] }, | |
output: { | |
path: webBuildTargetFolder, | |
filename: targetServiceWorkerFilename, | |
}, | |
module: { | |
rules: [ | |
{ | |
test: /\.ts$/, | |
loader: "ts-loader", | |
options: { | |
onlyCompileBundledFiles: true, | |
}, | |
}, | |
], | |
}, | |
plugins: [], | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// PROD Webpack configuration used to build the service worker | |
const webpackDevConfig = require("./webpack.dev.config"); | |
module.exports = Object.assign({}, webpackDevConfig, { | |
mode: "production", | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Script that modifies the service-worker.js configuration using workbox-build | |
// Reference: https://developers.google.com/web/tools/workbox/modules/workbox-build | |
const { injectManifest } = require("workbox-build"); | |
// Workbox configuration | |
const workboxConfig = require("./workbox-config"); | |
console.log(`Workbox configuration: `, workboxConfig); | |
// We use injectManifest to inject everything we need into service-worker.js | |
// Reference: https://developers.google.com/web/tools/workbox/modules/workbox-build | |
injectManifest(workboxConfig).then(({ count, size }) => { | |
console.log(`Generated ${workboxConfig.swDest}, which will precache ${count} files (${size} bytes)`); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = { | |
globDirectory: "dist/apps/web/", | |
globPatterns: ["**/*.{css,eot,html,ico,jpg,js,json,png,svg,ttf,txt,webmanifest,woff,woff2,webm,xml}"], | |
globFollow: true, // follow symlinks | |
globStrict: true, // fail the build if anything goes wrong while reading the files | |
globIgnores: [ | |
// Ignore Angular's ES5 bundles | |
// With this, we eagerly load the es2015 | |
// bundles and we only load/cache the es5 bundles when requested | |
// i.e., on browsers that need them | |
// Reference: https://github.com/angular/angular/issues/31256#issuecomment-506507021 | |
`**/*-es5.*.js`, | |
], | |
// Look for a 20 character hex string in the file names | |
// Allows to avoid using cache busting for Angular files because Angular already takes care of that! | |
dontCacheBustURLsMatching: new RegExp(".+.[a-f0-9]{20}..+"), | |
maximumFileSizeToCacheInBytes: 4 * 1024 * 1024, // 4Mb | |
swSrc: "dist/apps/web/service-worker.js", | |
swDest: "dist/apps/web/service-worker.js", | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
for sourcemaps!