Files
playpen/web/vite.config.ts

70 lines
2.9 KiB
TypeScript

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// Minimal typing for process.env so we don't need @types/node just for this.
declare const process: { env: Record<string, string | undefined> };
// Where a `vite dev` session sends its API calls. Playpen prefers 8420 and walks
// upward if it's taken, so this is right unless you have two windows open — in
// which case read the real one out of PLAYPEN_REVIEW_URL in any of its terminals.
const apiPort = process.env.PLAYPEN_REVIEW_PORT || '8420';
const apiTarget = `http://127.0.0.1:${apiPort}`;
export default defineConfig({
plugins: [react()],
resolve: {
// The syntax highlighter runs in a worker (see src/lib/highlight.ts), and one
// package deep under refractor — decode-named-character-reference, pulled in
// by the markup language to decode HTML entities — ships a browser build
// that calls `document.createElement` at module scope. A worker has no
// document, so importing refractor there threw before a line was highlighted.
//
// The package publishes a document-free build for exactly this case and
// lists it first in its export map, under `worker`. Asking for that
// condition is what picks it up. It resolves for the main bundle too, which
// is what we want: it is the same function with a lookup table in place of
// the DOM's entity parser, and one implementation beats two.
conditions: ['worker'],
},
// Relative asset URLs, because the page is served from a tab's path
// (`/t/tab3/`) and not from the server root. With an absolute base the
// browser would ask for `/assets/app.js` and get the tab router instead.
base: './',
server: {
port: 5173,
proxy: {
// A tab's own API and event stream. Matched as a regex so the HTML at
// `/t/<id>/` still comes from Vite — only the calls underneath it are
// forwarded, which is what lets hot reloading work against a live review.
//
// Open http://localhost:5173/t/<tabId>/ to develop against a real tab.
'^/t/[^/]+/api': { target: apiTarget, changeOrigin: true },
// The app-wide endpoints: /api/tabs and /api/health.
'/api': { target: apiTarget, changeOrigin: true },
},
},
build: {
outDir: 'dist',
emptyOutDir: true,
// Fixed filenames rather than Vite's content hashes. The bundle is embedded
// in the playpen binary (src/review/assets.zig), so the Zig side has to be
// able to name the files at compile time — and cache busting buys nothing
// for a bundle that only changes when the binary does.
rollupOptions: {
output: {
entryFileNames: 'assets/app.js',
chunkFileNames: 'assets/app.js',
assetFileNames: 'assets/app[extname]',
// One file, so there is one name to embed. The app is a few hundred
// kilobytes loaded from localhost; splitting it saves nothing.
manualChunks: undefined,
},
},
},
});