import { resolve } from "node:path"; import { defineConfig } from "vite"; import wasm from "vite-plugin-wasm"; export default defineConfig({ root: "website", plugins: [ wasm(), // Build service worker as a separate, unhashed JS file { name: "build-sw", apply: "build", closeBundle: { sequential: true, async handler() { const { build } = await import("vite"); await build({ configFile: false, root: resolve(__dirname, "website"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist"), lib: { entry: resolve(__dirname, "website/sw.ts"), formats: ["es"], fileName: () => "sw.js", }, rollupOptions: { output: { entryFileNames: "sw.js", }, }, }, }); // Build shell.ts as a standalone JS bundle await build({ configFile: false, root: resolve(__dirname, "website"), resolve: { alias: { "@lib": resolve(__dirname, "./lib"), "@shared": resolve(__dirname, "./shared"), }, }, build: { emptyOutDir: false, outDir: resolve(__dirname, "dist"), lib: { entry: resolve(__dirname, "website/shell.ts"), formats: ["es"], fileName: () => "shell.js", }, rollupOptions: { output: { entryFileNames: "shell.js", }, }, }, }); // Build books module components await build({ configFile: false, root: resolve(__dirname, "modules/rbooks/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rbooks"), lib: { entry: resolve(__dirname, "modules/rbooks/components/folk-book-shelf.ts"), formats: ["es"], fileName: () => "folk-book-shelf.js", }, rollupOptions: { output: { entryFileNames: "folk-book-shelf.js", }, }, }, }); await build({ configFile: false, root: resolve(__dirname, "modules/rbooks/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rbooks"), lib: { entry: resolve(__dirname, "modules/rbooks/components/folk-book-reader.ts"), formats: ["es"], fileName: () => "folk-book-reader.js", }, rollupOptions: { output: { entryFileNames: "folk-book-reader.js", }, }, }, }); // Copy books CSS const { copyFileSync, mkdirSync } = await import("node:fs"); mkdirSync(resolve(__dirname, "dist/modules/rbooks"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rbooks/components/books.css"), resolve(__dirname, "dist/modules/rbooks/books.css"), ); // Build pubs module component await build({ configFile: false, root: resolve(__dirname, "modules/rpubs/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rpubs"), lib: { entry: resolve(__dirname, "modules/rpubs/components/folk-pubs-editor.ts"), formats: ["es"], fileName: () => "folk-pubs-editor.js", }, rollupOptions: { output: { entryFileNames: "folk-pubs-editor.js", }, }, }, }); // Copy pubs CSS mkdirSync(resolve(__dirname, "dist/modules/rpubs"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rpubs/components/pubs.css"), resolve(__dirname, "dist/modules/rpubs/pubs.css"), ); // Build cart module component await build({ configFile: false, root: resolve(__dirname, "modules/rcart/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rcart"), lib: { entry: resolve(__dirname, "modules/rcart/components/folk-cart-shop.ts"), formats: ["es"], fileName: () => "folk-cart-shop.js", }, rollupOptions: { output: { entryFileNames: "folk-cart-shop.js", }, }, }, }); // Copy cart CSS mkdirSync(resolve(__dirname, "dist/modules/rcart"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rcart/components/cart.css"), resolve(__dirname, "dist/modules/rcart/cart.css"), ); // Build swag module component await build({ configFile: false, root: resolve(__dirname, "modules/rswag/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rswag"), lib: { entry: resolve(__dirname, "modules/rswag/components/folk-swag-designer.ts"), formats: ["es"], fileName: () => "folk-swag-designer.js", }, rollupOptions: { output: { entryFileNames: "folk-swag-designer.js", }, }, }, }); // Copy swag CSS mkdirSync(resolve(__dirname, "dist/modules/rswag"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rswag/components/swag.css"), resolve(__dirname, "dist/modules/rswag/swag.css"), ); // Build choices module component await build({ configFile: false, root: resolve(__dirname, "modules/rchoices/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rchoices"), lib: { entry: resolve(__dirname, "modules/rchoices/components/folk-choices-dashboard.ts"), formats: ["es"], fileName: () => "folk-choices-dashboard.js", }, rollupOptions: { output: { entryFileNames: "folk-choices-dashboard.js", }, }, }, }); // Copy choices CSS mkdirSync(resolve(__dirname, "dist/modules/rchoices"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rchoices/components/choices.css"), resolve(__dirname, "dist/modules/rchoices/choices.css"), ); // Build funds module components const fundsAlias = { "../lib/types": resolve(__dirname, "modules/rfunds/lib/types.ts"), "../lib/simulation": resolve(__dirname, "modules/rfunds/lib/simulation.ts"), "../lib/presets": resolve(__dirname, "modules/rfunds/lib/presets.ts"), "../lib/map-flow": resolve(__dirname, "modules/rfunds/lib/map-flow.ts"), }; await build({ configFile: false, root: resolve(__dirname, "modules/rfunds/components"), resolve: { alias: fundsAlias }, build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rfunds"), lib: { entry: resolve(__dirname, "modules/rfunds/components/folk-budget-river.ts"), formats: ["es"], fileName: () => "folk-budget-river.js", }, rollupOptions: { output: { entryFileNames: "folk-budget-river.js" } }, }, }); await build({ configFile: false, root: resolve(__dirname, "modules/rfunds/components"), resolve: { alias: fundsAlias }, build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rfunds"), lib: { entry: resolve(__dirname, "modules/rfunds/components/folk-funds-app.ts"), formats: ["es"], fileName: () => "folk-funds-app.js", }, rollupOptions: { output: { entryFileNames: "folk-funds-app.js" } }, }, }); // Copy funds CSS mkdirSync(resolve(__dirname, "dist/modules/rfunds"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rfunds/components/funds.css"), resolve(__dirname, "dist/modules/rfunds/funds.css"), ); // Build files module component await build({ configFile: false, root: resolve(__dirname, "modules/rfiles/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rfiles"), lib: { entry: resolve(__dirname, "modules/rfiles/components/folk-file-browser.ts"), formats: ["es"], fileName: () => "folk-file-browser.js", }, rollupOptions: { output: { entryFileNames: "folk-file-browser.js", }, }, }, }); // Copy files CSS mkdirSync(resolve(__dirname, "dist/modules/rfiles"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rfiles/components/files.css"), resolve(__dirname, "dist/modules/rfiles/files.css"), ); // Build forum module component await build({ configFile: false, root: resolve(__dirname, "modules/rforum/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rforum"), lib: { entry: resolve(__dirname, "modules/rforum/components/folk-forum-dashboard.ts"), formats: ["es"], fileName: () => "folk-forum-dashboard.js", }, rollupOptions: { output: { entryFileNames: "folk-forum-dashboard.js", }, }, }, }); // Copy forum CSS mkdirSync(resolve(__dirname, "dist/modules/rforum"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rforum/components/forum.css"), resolve(__dirname, "dist/modules/rforum/forum.css"), ); // Build wallet module component await build({ configFile: false, root: resolve(__dirname, "modules/rwallet/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rwallet"), lib: { entry: resolve(__dirname, "modules/rwallet/components/folk-wallet-viewer.ts"), formats: ["es"], fileName: () => "folk-wallet-viewer.js", }, rollupOptions: { output: { entryFileNames: "folk-wallet-viewer.js", }, }, }, }); // Copy wallet CSS mkdirSync(resolve(__dirname, "dist/modules/rwallet"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rwallet/components/wallet.css"), resolve(__dirname, "dist/modules/rwallet/wallet.css"), ); // Build vote module component await build({ configFile: false, root: resolve(__dirname, "modules/rvote/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rvote"), lib: { entry: resolve(__dirname, "modules/rvote/components/folk-vote-dashboard.ts"), formats: ["es"], fileName: () => "folk-vote-dashboard.js", }, rollupOptions: { output: { entryFileNames: "folk-vote-dashboard.js", }, }, }, }); // Copy vote CSS mkdirSync(resolve(__dirname, "dist/modules/rvote"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rvote/components/vote.css"), resolve(__dirname, "dist/modules/rvote/vote.css"), ); // Build notes module component (with Automerge WASM support) await build({ configFile: false, root: resolve(__dirname, "modules/rnotes/components"), plugins: [wasm()], resolve: { alias: { '@automerge/automerge': resolve(__dirname, 'node_modules/@automerge/automerge'), }, }, build: { target: "esnext", emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rnotes"), lib: { entry: resolve(__dirname, "modules/rnotes/components/folk-notes-app.ts"), formats: ["es"], fileName: () => "folk-notes-app.js", }, rollupOptions: { output: { entryFileNames: "folk-notes-app.js", }, }, }, }); // Copy notes CSS mkdirSync(resolve(__dirname, "dist/modules/rnotes"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rnotes/components/notes.css"), resolve(__dirname, "dist/modules/rnotes/notes.css"), ); // Build maps module component await build({ configFile: false, root: resolve(__dirname, "modules/rmaps/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rmaps"), lib: { entry: resolve(__dirname, "modules/rmaps/components/folk-map-viewer.ts"), formats: ["es"], fileName: () => "folk-map-viewer.js", }, rollupOptions: { output: { entryFileNames: "folk-map-viewer.js", }, }, }, }); // Copy maps CSS mkdirSync(resolve(__dirname, "dist/modules/rmaps"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rmaps/components/maps.css"), resolve(__dirname, "dist/modules/rmaps/maps.css"), ); // Build work module component await build({ configFile: false, root: resolve(__dirname, "modules/rwork/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rwork"), lib: { entry: resolve(__dirname, "modules/rwork/components/folk-work-board.ts"), formats: ["es"], fileName: () => "folk-work-board.js", }, rollupOptions: { output: { entryFileNames: "folk-work-board.js", }, }, }, }); // Copy work CSS mkdirSync(resolve(__dirname, "dist/modules/rwork"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rwork/components/work.css"), resolve(__dirname, "dist/modules/rwork/work.css"), ); // Build trips module component await build({ configFile: false, root: resolve(__dirname, "modules/rtrips/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rtrips"), lib: { entry: resolve(__dirname, "modules/rtrips/components/folk-trips-planner.ts"), formats: ["es"], fileName: () => "folk-trips-planner.js", }, rollupOptions: { output: { entryFileNames: "folk-trips-planner.js", }, }, }, }); // Copy trips CSS mkdirSync(resolve(__dirname, "dist/modules/rtrips"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rtrips/components/trips.css"), resolve(__dirname, "dist/modules/rtrips/trips.css"), ); // Build cal module component await build({ configFile: false, root: resolve(__dirname, "modules/rcal/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rcal"), lib: { entry: resolve(__dirname, "modules/rcal/components/folk-calendar-view.ts"), formats: ["es"], fileName: () => "folk-calendar-view.js", }, rollupOptions: { output: { entryFileNames: "folk-calendar-view.js", }, }, }, }); // Copy cal CSS mkdirSync(resolve(__dirname, "dist/modules/rcal"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rcal/components/cal.css"), resolve(__dirname, "dist/modules/rcal/cal.css"), ); // Build network module component await build({ configFile: false, root: resolve(__dirname, "modules/rnetwork/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rnetwork"), lib: { entry: resolve(__dirname, "modules/rnetwork/components/folk-graph-viewer.ts"), formats: ["es"], fileName: () => "folk-graph-viewer.js", }, rollupOptions: { output: { entryFileNames: "folk-graph-viewer.js", }, }, }, }); // Copy network CSS mkdirSync(resolve(__dirname, "dist/modules/rnetwork"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rnetwork/components/network.css"), resolve(__dirname, "dist/modules/rnetwork/network.css"), ); // Build tube module component await build({ configFile: false, root: resolve(__dirname, "modules/rtube/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rtube"), lib: { entry: resolve(__dirname, "modules/rtube/components/folk-video-player.ts"), formats: ["es"], fileName: () => "folk-video-player.js", }, rollupOptions: { output: { entryFileNames: "folk-video-player.js", }, }, }, }); // Copy tube CSS mkdirSync(resolve(__dirname, "dist/modules/rtube"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rtube/components/tube.css"), resolve(__dirname, "dist/modules/rtube/tube.css"), ); // Build inbox module component await build({ configFile: false, root: resolve(__dirname, "modules/rinbox/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rinbox"), lib: { entry: resolve(__dirname, "modules/rinbox/components/folk-inbox-client.ts"), formats: ["es"], fileName: () => "folk-inbox-client.js", }, rollupOptions: { output: { entryFileNames: "folk-inbox-client.js", }, }, }, }); // Copy inbox CSS mkdirSync(resolve(__dirname, "dist/modules/rinbox"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rinbox/components/inbox.css"), resolve(__dirname, "dist/modules/rinbox/inbox.css"), ); // Build data module component await build({ configFile: false, root: resolve(__dirname, "modules/rdata/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rdata"), lib: { entry: resolve(__dirname, "modules/rdata/components/folk-analytics-view.ts"), formats: ["es"], fileName: () => "folk-analytics-view.js", }, rollupOptions: { output: { entryFileNames: "folk-analytics-view.js", }, }, }, }); // Copy data CSS mkdirSync(resolve(__dirname, "dist/modules/rdata"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rdata/components/data.css"), resolve(__dirname, "dist/modules/rdata/data.css"), ); // Build route planner component (part of trips module) await build({ configFile: false, root: resolve(__dirname, "modules/rtrips/components"), resolve: { alias: { "../lib/types": resolve(__dirname, "modules/rtrips/lib/types.ts"), "../lib/conic-math": resolve(__dirname, "modules/rtrips/lib/conic-math.ts"), "../lib/projection": resolve(__dirname, "modules/rtrips/lib/projection.ts"), }, }, build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rtrips"), lib: { entry: resolve(__dirname, "modules/rtrips/components/folk-route-planner.ts"), formats: ["es"], fileName: () => "folk-route-planner.js", }, rollupOptions: { output: { entryFileNames: "folk-route-planner.js", }, }, }, }); // Copy route planner CSS copyFileSync( resolve(__dirname, "modules/rtrips/components/route-planner.css"), resolve(__dirname, "dist/modules/rtrips/route-planner.css"), ); // Build splat module component await build({ configFile: false, root: resolve(__dirname, "modules/rsplat/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rsplat"), lib: { entry: resolve(__dirname, "modules/rsplat/components/folk-splat-viewer.ts"), formats: ["es"], fileName: () => "folk-splat-viewer.js", }, rollupOptions: { external: ["three", "three/addons/", "@mkkellogg/gaussian-splats-3d"], output: { entryFileNames: "folk-splat-viewer.js", }, }, }, }); // Copy splat CSS mkdirSync(resolve(__dirname, "dist/modules/rsplat"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rsplat/components/splat.css"), resolve(__dirname, "dist/modules/rsplat/splat.css"), ); // Build photos module component await build({ configFile: false, root: resolve(__dirname, "modules/rphotos/components"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/modules/rphotos"), lib: { entry: resolve(__dirname, "modules/rphotos/components/folk-photo-gallery.ts"), formats: ["es"], fileName: () => "folk-photo-gallery.js", }, rollupOptions: { output: { entryFileNames: "folk-photo-gallery.js", }, }, }, }); // Copy photos CSS mkdirSync(resolve(__dirname, "dist/modules/rphotos"), { recursive: true }); copyFileSync( resolve(__dirname, "modules/rphotos/components/photos.css"), resolve(__dirname, "dist/modules/rphotos/photos.css"), ); // ── Demo infrastructure ── // Build demo-sync-vanilla library await build({ configFile: false, root: resolve(__dirname, "lib"), build: { emptyOutDir: false, outDir: resolve(__dirname, "dist/lib"), lib: { entry: resolve(__dirname, "lib/demo-sync-vanilla.ts"), formats: ["es"], fileName: () => "demo-sync.js", }, rollupOptions: { output: { entryFileNames: "demo-sync.js", }, }, }, }); // Build demo scripts for each module that has one const demoModules = ["cart", "vote", "funds", "notes", "cal", "tube", "trips"]; for (const mod of demoModules) { const dir = `r${mod}`; const demoEntry = resolve(__dirname, `modules/${dir}/components/${mod}-demo.ts`); try { const { statSync } = await import("node:fs"); statSync(demoEntry); await build({ configFile: false, root: resolve(__dirname, `modules/${dir}/components`), resolve: { alias: { "@lib": resolve(__dirname, "./lib"), }, }, build: { emptyOutDir: false, outDir: resolve(__dirname, `dist/modules/${dir}`), lib: { entry: demoEntry, formats: ["es"], fileName: () => `${mod}-demo.js`, }, rollupOptions: { output: { entryFileNames: `${mod}-demo.js`, }, }, }, }); } catch { // Demo script not yet created — skip } } }, }, }, ], resolve: { alias: { "@lib": resolve(__dirname, "./lib"), "@encryptid": resolve(__dirname, "./src/encryptid"), "@shared": resolve(__dirname, "./shared"), }, }, build: { target: "esnext", rollupOptions: { input: { index: resolve(__dirname, "./website/index.html"), canvas: resolve(__dirname, "./website/canvas.html"), "create-space": resolve(__dirname, "./website/create-space.html"), admin: resolve(__dirname, "./website/admin.html"), }, }, modulePreload: { polyfill: false, }, outDir: "../dist", emptyOutDir: true, // Copy shell.css to dist cssCodeSplit: false, }, server: { port: 5173, }, optimizeDeps: { exclude: ["@automerge/automerge"], }, });