Vite
Browsers run a service worker from a single script at a fixed address. Vite serves your modules one by one
while you develop and gives built files hashed names, so it needs help with a worker. peer-cdn/vite gives it:
import { defineConfig } from 'vite';import { peerCDN } from 'peer-cdn/vite';
export default defineConfig({ plugins: [peerCDN({ worker: 'src/sw.ts' })],});// in each pageawait navigator.serviceWorker.register(`${import.meta.env.BASE_URL}sw.js`);With a relative base, such as ./, BASE_URL is ./ in a build, which a page reads from its own folder: a
page in a folder below the base registers ../sw.js, one ../ for each folder. It needs Vite 8.
What it does
Section titled “What it does”- Bundles the worker from
worker, relative to Vite’s root, into one classic script, which every browser can run as a service worker. Only what the worker imports goes in: PeerPlugin and peer-data stay in your pages. - Serves it at
sw.jsbelow your site’s base whileviteruns, bundled afresh on each request, so a reload picks up your changes. - Writes it to
sw.jsat the top of the build whenvite buildruns, andvite previewserves it from there. A server build, as frameworks make for rendering pages, gets no worker. - Runs the signaling server of
peer-cdn/serveron Vite’s own server, inviteandvite preview. Pages reach it withio(), on the origin they came from. It needs socket.io installed:npm install -D socket.io. In middleware mode, where Vite has no server of its own, attach the signaling server to yours.
The worker is bundled with your config’s resolve.alias, define, mode and base, but without your config’s
other plugins. For routes below your base, take the base from the worker’s own address, which is right whether
the base is absolute or relative, as ./ is:
const base = new URL('./', location.href).pathname;
new PeerCDN().GET(`${base}assets`, ordered, cache.getMiddleware, peers.getMiddleware, network.getMiddleware).register();Options
Section titled “Options”| Option | What it is |
|---|---|
worker |
The worker’s entry, relative to Vite’s root, such as src/sw.ts. Required. |
signaling |
Run the signaling server on Vite’s dev and preview servers. true by default. |
Turn signaling off when your pages signal some other way, such as through a server of your own; socket.io is
then not needed. The signaling server answers every request below /socket.io/, so turn it off too when your
dev server proxies a socket.io server of your own there. Other WebSockets on Vite’s server, such as its own and
those server.proxy opens, are left alone.
In production
Section titled “In production”vite build writes the worker with the rest of your site, so serve it as you serve the site. Vite’s servers
only run while you develop, so run the signaling server on a Node.js server of your own, and give the pages its
address:
new PeerPlugin({ signaling: new SocketChannel(io('https://signaling.example.com')) });See Signaling for the server.
The example’s config
Section titled “The example’s config”The example site runs on this config, with a second plugin that counts the pictures its server sends. It
imports the plugin from the repository’s source, where your config imports it from peer-cdn/vite:
import { fileURLToPath } from 'node:url';import { defineConfig } from 'vite';import { peerCDN } from '../src/vite.js';
// npm run examples: the example site on one dev server, with its service worker and signaling server.export default defineConfig({ root: fileURLToPath(new URL('.', import.meta.url)), resolve: { alias: { 'peer-cdn': fileURLToPath(new URL('../src/index.ts', import.meta.url)) } }, plugins: [ peerCDN({ worker: 'sw.ts' }), { // Counts the pictures the server sends, so the page can show that a second browser didn't need them. name: 'served-counter', configureServer(server) { let served = 0; server.middlewares.use('/assets/', (_request, _response, next) => { served++; next(); }); server.middlewares.use('/served', (_request, response) => { response.setHeader('Content-Type', 'application/json'); response.setHeader('Cache-Control', 'no-store'); response.end(JSON.stringify({ served })); }); }, }, ],});