Preact-cli: Is service worker customization tougher than v2

Created on 18 Jun 2019  路  9Comments  路  Source: preactjs/preact-cli

Do you want to request a _feature_ or report a _bug_?
Question

What is the current behaviour?
In V3 we moved from SwPrecache to Workobx jS.
While moving we added a functionality to be able to create both module/nomodule service worker from the same source service worker file defined here.
This uses InjectManifest plugin from Workbox which injects precache file in a handwritten service worker.

This also gives us extra functionality to do brotli/webP redirect whenever user opts in.

While all this is great, any additional changes to the service worker needs to be done in code instead of changing some config in v2.

I just wonder if that might come across as a tougher choice for the users and whether we should just stick to the config based GenerateSwWebpackPlugin from Workbox and may be write blogs/wiki on how to do the brotli/webP with preact-cli.

Most helpful comment

I downvoted but as we discussed offline: a middle-ground here would seem to be really nice.

Option 1: move details into wrapper APIs

We scaffold (and therefore expose) a sw.js, but it's really just calling into code provided by the CLI and Workbox, glueing them together. The developer can replace this with their own logic should they wish to:

const manifest = preactCli.getPrecacheManifest();
const precacheOptions = preactCli.getPrecacheOptions();
preactCli.registerNavigationRoute();
workbox.precaching.precacheAndRoute(manifest, precacheOptions);

Option 2: Inject Smarts

Right now most of the SW code we would be scaffolding is to deal with Brotli and ESM. We could abstract all of that away by prepending a preact-cli -specific "intro" into the SW code when bundling, so the user-visible code doesn't have to care about importing or invoking anything:

preact.registerNavigationRoute();
workbox.precaching.precacheAndRoute(
    preact.manifest      // <-- same as the current value of self.__precacheManifest
);

Here's what that would look like once built, with the preact-cli stuff prepended to the user code:

/*** preact-cli generated ServiceWorker setup ***/
importScripts(['workbox.js']);

self.preact = {
  manifest: self.__precacheManifest,
  registerNavigationRoute(fallbackUrl) {
    // sets up NetworkFirst strategy with fallback to fallbackUrl (see my PR)
  },
  precacheOptions: {  ... }  // stuff for brotli
}

workbox.plugins.add(...)  // for brotli

// here we monkey-patch Workbox in the one case where we can't presume to own options.
// To the user, this is just a normal workbox instance:
workbox.precaching.precacheAndRoute = (old => (manifest, options) => {
  options = Object.assign({}, options || {}, preact.precacheOptions);
  return old(manifest, options);
})(workbox.precaching.precacheAndRoute);

/*** User's sw.js code gets injected below ***/
preact.registerNavigationRoute();workbox.precaching.precacheAndRoute(preact.manifest);

This would also be nice during development, since the sw.js could be augmented with various checks ("did you register a navigation handler", etc) without any code modifications.

The injection approach is also useful because it will work with DefinePlugin.

All 9 comments

// @developit @hassanbazzi @ForsakenHarmony @lukeed

was it easy to customise it before?

ideally it'd be easy to extend, not just replace the service worker

and we could maybe add options to preact.config.js

Its like with current setup you need to write code to change the functionality, vs in earlier setup you needed to change config of plugin.

I guess its just more tough for ppl to write code for SWs, thats why i opened this issue

If we agree that this is the case #819 will fix it

yeah I don't like modifying config of existing plugins in preact.config.js I'd rather have something more intuitive

@ForsakenHarmony just to confirm, so you feel writing workbox code for your service worker would be more intuitive than editing an object in preact.config.js?

just a nit, our users would need to learn workbox APIs to stuff like runtime caching etc vs adding them in config.

No I'm saying changing to a differnt plugin and not changing our public api (I don't count modifying the webpack config) doesn't change anything

Ok a quick show of hands please to sort this out

Reply on this comment with 馃憤 / 馃憥

馃憤 - Yes lets shift to config based service worker generator as is done in next/nuxt/gatsby etc

馃憥 - Let's stick to code based service worker generation as it's more intuitive to write workbox code and we think that people will not have much difficulty writing service worker code by hand(P.S. we'll do our best to provide various examples).

I downvoted but as we discussed offline: a middle-ground here would seem to be really nice.

Option 1: move details into wrapper APIs

We scaffold (and therefore expose) a sw.js, but it's really just calling into code provided by the CLI and Workbox, glueing them together. The developer can replace this with their own logic should they wish to:

const manifest = preactCli.getPrecacheManifest();
const precacheOptions = preactCli.getPrecacheOptions();
preactCli.registerNavigationRoute();
workbox.precaching.precacheAndRoute(manifest, precacheOptions);

Option 2: Inject Smarts

Right now most of the SW code we would be scaffolding is to deal with Brotli and ESM. We could abstract all of that away by prepending a preact-cli -specific "intro" into the SW code when bundling, so the user-visible code doesn't have to care about importing or invoking anything:

preact.registerNavigationRoute();
workbox.precaching.precacheAndRoute(
    preact.manifest      // <-- same as the current value of self.__precacheManifest
);

Here's what that would look like once built, with the preact-cli stuff prepended to the user code:

/*** preact-cli generated ServiceWorker setup ***/
importScripts(['workbox.js']);

self.preact = {
  manifest: self.__precacheManifest,
  registerNavigationRoute(fallbackUrl) {
    // sets up NetworkFirst strategy with fallback to fallbackUrl (see my PR)
  },
  precacheOptions: {  ... }  // stuff for brotli
}

workbox.plugins.add(...)  // for brotli

// here we monkey-patch Workbox in the one case where we can't presume to own options.
// To the user, this is just a normal workbox instance:
workbox.precaching.precacheAndRoute = (old => (manifest, options) => {
  options = Object.assign({}, options || {}, preact.precacheOptions);
  return old(manifest, options);
})(workbox.precaching.precacheAndRoute);

/*** User's sw.js code gets injected below ***/
preact.registerNavigationRoute();workbox.precaching.precacheAndRoute(preact.manifest);

This would also be nice during development, since the sw.js could be augmented with various checks ("did you register a navigation handler", etc) without any code modifications.

The injection approach is also useful because it will work with DefinePlugin.

Was this page helpful?
0 / 5 - 0 ratings