diff --git a/.changeset/great-toes-wash.md b/.changeset/great-toes-wash.md new file mode 100644 index 000000000000..f94b452f817a --- /dev/null +++ b/.changeset/great-toes-wash.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: expose `base` via `$service-worker`, make paths relative diff --git a/documentation/docs/30-advanced/40-service-workers.md b/documentation/docs/30-advanced/40-service-workers.md index 92da584e5184..6fb57ca5b6a8 100644 --- a/documentation/docs/30-advanced/40-service-workers.md +++ b/documentation/docs/30-advanced/40-service-workers.md @@ -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. diff --git a/packages/kit/src/exports/vite/build/build_service_worker.js b/packages/kit/src/exports/vite/build/build_service_worker.js index 285ce62eba51..faf4e31cb305 100644 --- a/packages/kit/src/exports/vite/build/build_service_worker.js +++ b/packages/kit/src/exports/vite/build/build_service_worker.js @@ -33,24 +33,30 @@ export async function build_service_worker( const service_worker = `${kit.outDir}/generated/service-worker.js`; + // in a service worker, `location` is the location of the service worker itself, + // which is guaranteed to be `/service-worker.js` + const base = `location.pathname.split('/').slice(0, -1).join('/')`; + fs.writeFileSync( service_worker, dedent` + export const base = /*@__PURE__*/ ${base}; + 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)}; diff --git a/packages/kit/test/apps/options-2/src/routes/hello/+page.js b/packages/kit/test/apps/options-2/src/routes/hello/+page.js new file mode 100644 index 000000000000..189f71e2e1b3 --- /dev/null +++ b/packages/kit/test/apps/options-2/src/routes/hello/+page.js @@ -0,0 +1 @@ +export const prerender = true; diff --git a/packages/kit/test/apps/options-2/src/routes/hello/+page.svelte b/packages/kit/test/apps/options-2/src/routes/hello/+page.svelte new file mode 100644 index 000000000000..a994afef288b --- /dev/null +++ b/packages/kit/test/apps/options-2/src/routes/hello/+page.svelte @@ -0,0 +1 @@ +

Prerendered

diff --git a/packages/kit/test/apps/options-2/src/service-worker.js b/packages/kit/test/apps/options-2/src/service-worker.js index 9b804c43b3fc..e1a6d3831696 100644 --- a/packages/kit/test/apps/options-2/src/service-worker.js +++ b/packages/kit/test/apps/options-2/src/service-worker.js @@ -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}`; diff --git a/packages/kit/test/apps/options-2/test/test.js b/packages/kit/test/apps/options-2/test/test.js index 31520020b8f0..cbd51d45e1cd 100644 --- a/packages/kit/test/apps/options-2/test/test.js +++ b/packages/kit/test/apps/options-2/test/test.js @@ -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 }) => { diff --git a/packages/kit/types/ambient.d.ts b/packages/kit/types/ambient.d.ts index 722746c08c53..74677a278c22 100644 --- a/packages/kit/types/ambient.d.ts +++ b/packages/kit/types/ambient.d.ts @@ -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.