Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/great-toes-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': minor
---

feat: expose `base` via `$service-worker`, make paths relative
2 changes: 1 addition & 1 deletion documentation/docs/30-advanced/40-service-workers.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ if ('serviceWorker' in navigator) {

## Inside the service worker

Inside the service worker you have access to the [`$service-worker` module](modules#$service-worker), which provides you with the paths to all static assets, build files and prerendered pages. You're also provided with an app version string which you can use for creating a unique cache name. If your Vite config specifies `define` (used for global variable replacements), this will be applied to service workers as well as your server/client builds.
Inside the service worker you have access to the [`$service-worker` module](modules#$service-worker), which provides you with the paths to all static assets, build files and prerendered pages. You're also provided with an app version string which you can use for creating a unique cache name, and the deployment's `base` path. If your Vite config specifies `define` (used for global variable replacements), this will be applied to service workers as well as your server/client builds.

The following example caches the built app and any files in `static` eagerly, and caches all other requests as they happen. This would make each page work offline once visited.

Expand Down
8 changes: 5 additions & 3 deletions packages/kit/src/exports/vite/build/build_service_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,23 @@ export async function build_service_worker(
fs.writeFileSync(
service_worker,
dedent`
export const base = /*@__PURE__*/ location.pathname.split('/').slice(0, -1).join('/');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a hard time understanding how/why this works. If you you visit example.com/foo/bar wouldn't the base be /foo, even if I didn't set a base path?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a service worker, location is the location of the service worker, not the URL you're currently visiting. Because SvelteKit guarantees that the service worker is at the root of the app (because a service worker can only handle requests at the same level or below), location.pathname.split('/').slice(0, -1).join('/') is guaranteed to be the base path

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clearing it up!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might be worth a comment?


export const build = [
${Array.from(build)
.map((file) => `${s(`${kit.paths.base}/${file}`)}`)
.map((file) => `base + ${s(`/${file}`)}`)
.join(',\n')}
];

export const files = [
${manifest_data.assets
.filter((asset) => kit.serviceWorker.files(asset.file))
.map((asset) => `${s(`${kit.paths.base}/${asset.file}`)}`)
.map((asset) => `base + ${s(`/${asset.file}`)}`)
.join(',\n')}
];

export const prerendered = [
${prerendered.paths.map((path) => s(path)).join(',\n')}
${prerendered.paths.map((path) => `base + ${s(path.replace(kit.paths.base, ''))}`).join(',\n')}
];

export const version = ${s(kit.version.name)};
Expand Down
1 change: 1 addition & 0 deletions packages/kit/test/apps/options-2/src/routes/hello/+page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const prerender = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<h1>Prerendered</h1>
5 changes: 4 additions & 1 deletion packages/kit/test/apps/options-2/src/service-worker.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { build, version } from '$service-worker';
import { base, build, files, prerendered, version } from '$service-worker';

self.base = base;
self.build = build;

const name = `cache-${version}`;

Expand Down
15 changes: 14 additions & 1 deletion packages/kit/test/apps/options-2/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,20 @@ test.describe('Service worker', () => {
const response = await request.get('/basepath/service-worker.js');
const content = await response.text();

expect(content).toMatch(/\/_app\/immutable\/entry\/start\.[a-z0-9]+\.js/);
const fn = new Function('self', 'location', content);

const self = {
addEventListener: () => {},
base: null,
build: null
};

fn(self, {
pathname: '/basepath/service-worker.js'
});

expect(self.base).toBe('/basepath');
expect(self.build[0]).toMatch(/\/basepath\/_app\/immutable\/entry\/start\.[a-z0-9]+\.js/);
});

test('does not register /basepath/service-worker.js', async ({ page }) => {
Expand Down
5 changes: 5 additions & 0 deletions packages/kit/types/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,11 @@ declare module '$app/stores' {
* This module is only available to [service workers](https://kit.svelte.dev/docs/service-workers).
*/
declare module '$service-worker' {
/**
* The `base` path of the deployment. Typically this is equivalent to `config.kit.paths.base`, but it is calculated from `location.pathname` meaning that it will continue to work correctly if the site is deployed to a subdirectory.
* Note that there is a `base` but no `assets`, since service workers cannot be used if `config.kit.paths.assets` is specified.
*/
export const base: string;
/**
* An array of URL strings representing the files generated by Vite, suitable for caching with `cache.addAll(build)`.
* During development, this is an empty array.
Expand Down