kit icon indicating copy to clipboard operation
kit copied to clipboard

The `$service-worker` module should categorise all files generated by type, not by origin

Open martinszar opened this issue 3 years ago • 2 comments

Describe the problem

As you know, SvelteKit already categorises all generated files into three categories: build, files and prerendered. These are based on the origin of the files, which, from a coders perspective, makes sense and is easy to implement. But the thing is, in most environments it does not matter from where a file originates, but much rather what the content of the file actually is.

Take for example the simplest SvelteKit application (without disabling hydration) that you can think of. A prerendered version of it would only consist of a couple .html and .js files. Now if a developer would want to prefetch and cache all hydration related assets that would be doable by using the build array (since Vite generated them) exported by $service-worker. But this setup quickly falls apart when anything other then scripts gets imported into a svelte component. Added a picture? You have to resort to client-side filtering to not waste bandwidth. Added a stylesheet? Again, you have to resort to client-side filtering. Client-side filtering obviously comes with the caveat that the whole build array would still be shipped, even if it's filled with a morbillion epic memes.

Describe the proposed solution

I present to you "categorisation by type":

import { version, pages, scripts, styles, images, plaintext, binary, other... } from "$service-worker"

Making the developer cherry-pick their required files allows for much better tree-shaking (pun not intended). The actual categories should be tweaked if this idea actually gets adopted.

Alternatives considered

I just came up with an alternative to my proposed solution: Making scripts "prerenderable" (for instance using a query) which would execute the code only once on the server, then get all exported variables and finally send them to the client, without the code that constructed them (just like prerendered pages without hydration). In this context that would allow developers to get an array without any client side code, which could just be passed to cache.addAll. But of course this type of feature would require a lot of work, discussions, streamlining etc.

Importance

nice to have

Additional Information

No response

martinszar avatar Aug 02 '22 18:08 martinszar

The current distinction is deliberate — you can (for example) cache everything in build, then cache everything in files that doesn't begin with epic-memes or end with .jpg. This is easier to understand and more scalable. I don't think there's any benefit to (for example) having separate scripts and styles arrays — I'm struggling to imagine a reason why you'd want to have different caching behaviours for them, but if you did then you could just filter based on .js and .css file extensions.

If you want to exclude them from the service worker altogether you can use config.kit.serviceWorker.files.

Rich-Harris avatar Aug 02 '22 19:08 Rich-Harris

My original thought as to why such categorisation would be useful was indeed making exclusion from the service worker easier.

An array similar to this one gets exported, when importing build from $service-worker in the "To Do" SvelteKit demo:

[
  "/_app/immutable/assets/svelte-logo-87df40b8.svg",
  "/_app/immutable/assets/fira-mono-cyrillic-ext-400-normal-3df7909e.woff2",
  "/_app/immutable/assets/fira-mono-cyrillic-400-normal-c7d433fd.woff2",
  "/_app/immutable/assets/fira-mono-greek-ext-400-normal-9e2fe623.woff2",
  "/_app/immutable/assets/fira-mono-greek-400-normal-a8be01ce.woff2",
  "/_app/immutable/assets/fira-mono-latin-ext-400-normal-6bfabd30.woff2",
  "/_app/immutable/assets/fira-mono-latin-400-normal-e43b3538.woff2",
  "/_app/immutable/assets/fira-mono-all-400-normal-1e3b098c.woff",
  "/_app/immutable/start-ff45d9ee.js",
  "/_app/immutable/pages/__layout.svelte-9b202098.js",
  "/_app/immutable/assets/__layout-f74aac7c.css",
  "/_app/immutable/error.svelte-ca65b773.js",
  "/_app/immutable/pages/about.svelte-1cb2e0ee.js",
  "/_app/immutable/assets/about-9682aba9.css",
  "/_app/immutable/pages/index.svelte-dd3ae3d5.js",
  "/_app/immutable/assets/index-8bad58d3.css",
  "/_app/immutable/chunks/index-ddfd65ad.js",
  "/_app/immutable/chunks/index-a43a8c53.js"
]

If I'd want to only cache.addAll all .js and .css files I'd have to do the following:

import { build, version } from "$service-worker"

self.oninstall = event =>
	event.waitUntil(caches.open(version).then(cache =>
		cache.addAll(build.filter(file =>
			[ "js", "css" ].includes(file.match(/(?<=([^\.]+))$/)[1])
	))	))

That would filter out 8 entries of build (every time a service worker gets installed), while still shipping the whole array. On other projects this number can go into the hundreds considering the existence of await import() and import.meta.glob("*", { eager: true })

I was not aware of the existence of config.kit.serviceWorker.files, which would solve this issue, but sadly it only filters static files. Having it filter both build and files (or having two separate functions for each) would completely satisfy me, since the 8 "unneeded" lines would not be shipped and build wouldn't have to be filtered on the client-side.

TL;DR: build is messy, since it includes all assets handled by Vite, and needs a way to be filtered on the server side.

ghost avatar Aug 02 '22 21:08 ghost