-
Notifications
You must be signed in to change notification settings - Fork 830
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
Future of Service Worker Tooling #44
Comments
Here's a rough outline of what I was thinking about in terms of developer experience: /*
Assume that:
- routing.js covers similar ground to sw-toolbox's current Express/RegExp routing, and exposes goog.routing.Route
Route has three properties:
- when: a function that's passed both a URL-wrapper around FetchEvent.request.url, and the raw FetchEvent,
and returns true when this route should handle the request
- options: used to pass additional context to the handler, e.g. name of cache to use.
- handler: a function that's passed the FetchEvent and any options, and returns a Promise which resolves with a Response.
- runtime-caching.js includes the various caching strategies in sw-toolbox, and exposes goog.runtimeCaching.*
- precache.js is the output of a sw-precache-style build process, including a list of assets, along with
the install/activate handlers, and exposes goog.precache.*
*/
importScripts(['routing.js', 'runtime-caching.js', 'precache.js']);
const routes = [
new goog.routing.Route({
when: ({url, event}) => goog.precache.shouldHandle(event),
handler: goog.precache.handler
}),
new goog.routing.Route({
if: ({url, event}) => url.pathname.startsWith('/api/'),
handler: goog.runtimeCaching.staleWhileRevalidate,
}),
new goog.routing.Route({
when: ({url, event}) => url.origin.startsWith('https://imagecdn.com'),
handler: goog.runtimeCaching.cacheFirst,
// See https://github.com/GoogleChrome/sw-helpers/issues/44#issuecomment-250766630
// for discussion of options/configuration.
options: {
cache:{
name: 'images',
maxEntries: 10
}
}
}),
new goog.routing.Route({
when: ({url, event}) => event.request.mode === 'navigate',
handler: ({url, event}) => {
// Implement your own strategy here.
},
}),
];
// Optional helper to add in .catch() to the end of your response chain,
// regardless of which route is chosen.
const catchHandler = ({url, event}) => fetch(event.request);
// Optional helper which is used when nothing else matches.
const defaultHandler = ({url, event}) => {
return caches.match(event.request).then(response => response || fetch(event.request));
}
goog.registerRoutes({routes, catchHandler, defaultHandler}); Inside As mentioned in GoogleChromeLabs/sw-precache#147, we're still blocked on Happy to brainstorm more! |
Actually, here's a potential approach forward without waiting for GoogleChromeLabs/sw-precache#147: if we enforce as a prerequisite that everyone will consume the modules we produce via either This means the developers will need to hold off on using Thoughts? |
I put in some time over the weekend exploring what it might be like to break up the existing I did far enough along that the code in https://github.com/GoogleChrome/sw-helpers/blob/future-of-sw-tooling/projects/sw-routing/demo/service-worker.js works, using a syntax similar to what I proposed above. I'm not sold that it's the right approach, and I'm looking forward to feedback, but it's good just familiarizing myself with using Rollup to bundle smallish, ES2015-friendly modules and proving that they could be consumed via either |
First off can we agree on the rough audience, approaches and architecture before discussing API and implementation? |
I'm on board with what you outlined to start of the thread. Producing smaller components that could be opted-into piecemeal for those who want to add value to their current hand-crafted service worker script: 👍 Creating a new, optional build tool/CLI layer on top of those components that could generate an entire service worker file, matching the current The blocker for me has historically been whether we could move to this sort of model while still triggering the SW lifecycle events we needed to properly handle cache maintenance. w3c/ServiceWorker#839 (comment) will solve that generally, but I'm eager to prototype whether ES2015 modules + requiring a |
In addition to general feedback, I'm particularly interested in hearing ideas for configuring cache behavior. Here are my high-level thoughts:
My strawman suggestion is something like new goog.routing.Route({
when: ({url, event}) => goog.precache.shouldHandle(event),
handler: goog.precache.handler,
configuration: [
new goog.CacheBroadcastBehavior({channelName: 'cache-changes'})
]
});
new goog.routing.Route({
when: ({url, event}) => url.origin.startsWith('https://imagecdn.com'),
handler: goog.runtimeCaching.cacheFirst,
configuration: [
new goog.CacheNameBehavior({name: 'image-cache'}),
new goog.CacheExpirationBehavior({
maxEntries: 10,
maxAgeSeconds: 3600
}),
new goog.CacheBroadcastBehavior({
channelName: 'cache-changes',
headers: ['last-modified', 'etag', 'content-length']
})
]
}); |
FWIW, I think most non-trivial service worker apps will be okay with the requirement to use a module bundler instead of importScripts. If you're pulling in any npm modules, you'll be using one anyway. Plus, it gives you more control over what code you pull in. |
Firstly apologies that we are doing this from two very different approaches, I'm doing highlevel down and you're thinking details up (but weirdly may be helpful ;) ). Small Modules We End Up With:
Developers can use this as is. Recommended approach for fine grained control and reducing file size. Higher Level Projects
Questions
|
What about the current behavior being the default, but a
Imo smaller modules (like analytics) should be considered opt-in pieces you install into your project and can hook into the sw-goog-cli, but aren't included by default. This is in the spirit of avoiding the kitchen-sink as much as one can. Keep it minimal. There are a few ways this can be done: I wonder if there's value in us getting atomic with our caching strategies as separate modules too (e.g sw-goog-cache-first, sw-goog-cache-fallback etc).
Our current libraries get the most use in larger companies who may have security/other requirements around self-hosting. I wonder if that means there's less value in using CDN hosting for these pieces, but this could be useful from a prototypers point of view. |
Yes, I've been thinking about/prototyping more of the individual pieces, and less of the bigger picture. It's good to think about both! Here are my thoughts about the higher-level questions from both @gauntface and @addyosmani's responses:
I might change my mind given more thought/after playing with a prototype, but my current thinking is that we should ship a JavaScript module (with a CLI wrapper) that emulates what We could start with that minimal approach and see whether it's actually necessary to ship a "generate your SW" tool as a follow-up. The one thing that a "generate your SW" tool would have going for it is that we could have it also take care of the bundling step for you, which could make it more accessible for developers not familiar with Rollup/webpack. But... I'm going to assume that would be a secondary deliverable.
My vote would be no. I don't know that CLI helpers to integrate them would be worthwhile for the initial implementation. I'm approaching this from the perspective of developers owning the JavaScript in their top-level service worker file and needing to explicitly opt-in to behaviors. We'd want the API surfaces to be similar and friendly to use, and could put out "recipes" for various scenarios. (This might be a symptom of me over-correcting for how opaque the
I'm assuming that for the initial implementation, when we effectively have to require (hah!) that developers use local
Folks consuming our ES2015 module interface (which should be everyone, until the issues with |
I forgot to comment on your strawman suggestion! The configuration array reminds me quite a lot of how Webpack plugins currently get configured. That is to say I find the below relatively straight-forward to grok: configuration: [
new goog.CacheNameBehavior({name: 'image-cache'}),
new goog.CacheExpirationBehavior({
maxEntries: 10,
maxAgeSeconds: 3600
}),
new goog.CacheBroadcastBehavior({
channelName: 'cache-changes',
headers: ['last-modified', 'etag', 'content-length']
})
]
}); This part felt like we could make it a little less verbose however: new goog.routing.Route({
when: ({url, event}) => url.origin.startsWith('https://imagecdn.com'), |
Yeah, that's not the best example. My general idea is a predicate that takes in a
And then
|
It occurred to me that it might be valuable to also loop in @NekR, @robwormald and @rwjblue who have also been hacking on service worker tooling for their input. In particular around whether our thoughts on trying to modularize sw-toolbox/precache/the pieces around there can offer up lower level things that would have made it easier for them to write the tooling they've worked on. |
I think this is something weird. Automated tools are automated by a reason. Feels more like an error-prone anti-patter. Unless you mean that it would generate library-driven code, not pure SW. I feel like pure SW isn't something regular developer should write. There are too many pitfalls it's must have to use an library unless you a Facebook/Flipkart or for sure know what you are doing. e.g. I wouldn't even write an pure SW myself in my new projects. That's too dangerous. This comment's #44 (comment) way of handling configuration looks good me. If I were going to write something with SW library, I definitely would prefer this way. I don't why, maybe because I use webpack too much. |
Very promising thread. I've worked hard on DSW. It has a very different approach but I think one project may pretty much help and inspire the other. In DSW we do have an I'm now working on unit tests for it (it turned out to be a little bit trickier than I expect, testing service workers). In sw-helpers, I find it interesting to install and scaffold modules into projects as needed. Developers are used to it when using babel or even gulp or grunt, for example. |
I'm still not sure if I'm sold on "modular" functionality. It might be too much abstraction. Remember modular mobile phones, they sounded very good and promising but didn't really work out. Though, Babel indeed is a good example of modularity. I think it makes sense (at least for me) to wait until there a lot of features/ways to extend SW so it could be good split everything to modules, not bloat installs or SW files itself. At this moment, there is barely couple of pluggable features, if we talk about offline/caching only. It may make sense for BackgroundSync/PushNotifications/OtherStuff though. |
We've got a more concrete proposal detailed in #61 I'm going to close this issue and encourage folks currently CC:ed on this to check out the proposal there, as this thread has a lot of earlier discussions which might not be relevant. |
cc @addyosmani, @jeffposnick
We've chatted a little bit about future of tooling, but it would good to move the discussion a little further forward and agree of a rough plan and then move into some more technical detail and strategy.
This is a bit of a brain dump as to audience we might want to target, followed by general requests / approaches people have hinted at wanting and finally a super vague idea of what we can do.
Audience
Possible audience based on some developers editing a generated SW?
Requests / Approaches
General Architecture
What are the thoughts of these vague topics, is this the general approach people have in their heads? Is a target audience missing? General architecture seem wonk / destined to fail?
The text was updated successfully, but these errors were encountered: