Skip to content

Commit

Permalink
[feat] dev time service workers (#7597)
Browse files Browse the repository at this point in the history
* dev time service workers - closes #5479

* fixes

* dear Rich, meet your good friend Windows

Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
  • Loading branch information
Rich-Harris and dummdidumm committed Nov 11, 2022
1 parent 62fbbd7 commit 41e30cc
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/kind-dragons-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Run service worker during development
10 changes: 9 additions & 1 deletion documentation/docs/30-advanced/40-service-workers.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,12 @@ In SvelteKit, if you have a `src/service-worker.js` file (or `src/service-worker
Inside the service worker you have access to the [`$service-worker` module](/docs/modules#$service-worker). If your Vite config specifies `define`, this will be applied to service workers as well as your server/client builds.

Because it needs to be bundled (since browsers don't yet support `import` in this context), and depends on the client-side app's build manifest, **service workers only work in the production build, not in development**. To test it locally, use `vite preview`.
The service worker is bundled for production, but not during development. For that reason, only browsers that support [modules in service workers](https://web.dev/es-modules-in-sw) will be able to use them at dev time. If you are manually registering your service worker, you will need to pass the `{ type: 'module' }` option in development:

```js
import { dev } from '$app/environment';

navigator.serviceWorker.register('/service-worker.js', {
type: dev ? 'module' : 'classic'
});
```
2 changes: 1 addition & 1 deletion packages/kit/src/core/sync/create_manifest_data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function create_manifest_data({
/**
* @param {import('types').ValidatedConfig} config
*/
function create_assets(config) {
export function create_assets(config) {
return list_files(config.kit.files.assets).map((file) => ({
file,
size: fs.statSync(path.resolve(config.kit.files.assets, file)).size,
Expand Down
7 changes: 2 additions & 5 deletions packages/kit/src/core/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'path';
import colors from 'kleur';
import { fileURLToPath } from 'url';
import { posixify } from '../utils/filesystem.js';
import { posixify, to_fs } from '../utils/filesystem.js';

/**
* Resolved path of the `runtime` directory
Expand All @@ -23,10 +23,7 @@ export const runtime_prefix = posixify_path(runtime_directory);
*/
export const runtime_base = runtime_directory.startsWith(process.cwd())
? `/${path.relative('.', runtime_directory)}`
: `/@fs${
// Windows/Linux separation - Windows starts with a drive letter, we need a / in front there
runtime_directory.startsWith('/') ? '' : '/'
}${runtime_directory}`;
: to_fs(runtime_directory);

/** @param {string} str */
function posixify_path(str) {
Expand Down
22 changes: 20 additions & 2 deletions packages/kit/src/exports/vite/dev/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { URL } from 'url';
import { getRequest, setResponse } from '../../../exports/node/index.js';
import { installPolyfills } from '../../../exports/node/polyfills.js';
import { coalesce_to_error } from '../../../utils/error.js';
import { posixify, resolve_entry } from '../../../utils/filesystem.js';
import { posixify, resolve_entry, to_fs } from '../../../utils/filesystem.js';
import { load_error_page, load_template } from '../../../core/config/index.js';
import { SVELTE_KIT_ASSETS } from '../../../constants.js';
import * as sync from '../../../core/sync/sync.js';
Expand Down Expand Up @@ -302,6 +302,22 @@ export async function dev(vite, vite_config, svelte_config) {
);
}

if (decoded === svelte_config.kit.paths.base + '/service-worker.js') {
const resolved = resolve_entry(svelte_config.kit.files.serviceWorker);

if (resolved) {
res.writeHead(200, {
'content-type': 'application/javascript'
});
res.end(`import '${to_fs(resolved)}';`);
} else {
res.writeHead(404);
res.end('not found');
}

return;
}

const hooks_file = svelte_config.kit.files.hooks.server;
/** @type {Partial<import('types').ServerHooks>} */
const user_hooks = resolve_entry(hooks_file)
Expand Down Expand Up @@ -451,7 +467,9 @@ export async function dev(vite, vite_config, svelte_config) {
.replace(/%sveltekit\.status%/g, String(status))
.replace(/%sveltekit\.error\.message%/g, message);
},
service_worker: false,
service_worker:
svelte_config.kit.serviceWorker.register &&
!!resolve_entry(svelte_config.kit.files.serviceWorker),
trailing_slash: svelte_config.kit.trailingSlash
},
{
Expand Down
24 changes: 23 additions & 1 deletion packages/kit/src/exports/vite/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { get_config_aliases, get_app_aliases, get_env } from './utils.js';
import { fileURLToPath } from 'node:url';
import { create_static_module, create_dynamic_module } from '../../core/env.js';
import { is_illegal, module_guard, normalize_id } from './graph_analysis/index.js';
import { create_assets } from '../../core/sync/create_manifest_data/index.js';

const cwd = process.cwd();

Expand Down Expand Up @@ -289,7 +290,7 @@ function kit() {

async resolveId(id) {
// treat $env/static/[public|private] as virtual
if (id.startsWith('$env/')) return `\0${id}`;
if (id.startsWith('$env/') || id === '$service-worker') return `\0${id}`;
},

async load(id, options) {
Expand Down Expand Up @@ -323,6 +324,8 @@ function kit() {
'public',
vite_config_env.command === 'serve' ? env.public : undefined
);
case '\0$service-worker':
return create_service_worker_module(svelte_config);
}
},

Expand Down Expand Up @@ -627,3 +630,22 @@ function find_overridden_config(config, resolved_config, enforced_config, path,

return out;
}

/**
* @param {import('types').ValidatedConfig} config
*/
const create_service_worker_module = (config) => `
if (typeof self === 'undefined' || self instanceof ServiceWorkerGlobalScope === false) {
throw new Error('This module can only be imported inside a service worker');
}
export const build = [];
export const files = [
${create_assets(config)
.filter((asset) => config.kit.serviceWorker.files(asset.file))
.map((asset) => `${JSON.stringify(`${config.kit.paths.base}/${asset.file}`)}`)
.join(',\n\t\t\t\t')}
];
export const prerendered = [];
export const version = ${JSON.stringify(config.kit.version.name)};
`;
4 changes: 3 additions & 1 deletion packages/kit/src/runtime/server/page/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,12 +288,14 @@ export async function render_response({
}

if (options.service_worker) {
const opts = options.dev ? `, { type: 'module' }` : '';

// we use an anonymous function instead of an arrow function to support
// older browsers (https://github.com/sveltejs/kit/pull/5417)
const init_service_worker = `
if ('serviceWorker' in navigator) {
addEventListener('load', function () {
navigator.serviceWorker.register('${prefixed('service-worker.js')}');
navigator.serviceWorker.register('${prefixed('service-worker.js')}'${opts});
});
}
`;
Expand Down
12 changes: 12 additions & 0 deletions packages/kit/src/utils/filesystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ export function posixify(str) {
return str.replace(/\\/g, '/');
}

/**
* Prepend given path with `/@fs` prefix
* @param {string} str
*/
export function to_fs(str) {
str = posixify(str);
return `/@fs${
// Windows/Linux separation - Windows starts with a drive letter, we need a / in front there
str.startsWith('/') ? '' : '/'
}${str}`;
}

/**
* Given an entry point like [cwd]/src/hooks, returns a filename like [cwd]/src/hooks.js or [cwd]/src/hooks/index.js
* @param {string} entry
Expand Down
2 changes: 2 additions & 0 deletions packages/kit/types/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ declare module '$app/stores' {
declare module '$service-worker' {
/**
* An array of URL strings representing the files generated by Vite, suitable for caching with `cache.addAll(build)`.
* During development, this is be an empty array.
*/
export const build: string[];
/**
Expand All @@ -350,6 +351,7 @@ declare module '$service-worker' {
export const files: string[];
/**
* An array of pathnames corresponding to prerendered pages and endpoints.
* During development, this is be an empty array.
*/
export const prerendered: string[];
/**
Expand Down

0 comments on commit 41e30cc

Please sign in to comment.