Skip to content
79 linesCodeBlameRaw
1import { reactRouter } from "@react-router/dev/vite";
2import { cloudflare } from "@cloudflare/vite-plugin";
3import tailwindcss from "@tailwindcss/vite";
4import { defineConfig, type Plugin } from "vite";
5
6/**
7 * The doc editor's chunk and everything it imports, so a doc's page can
8 * ask the browser to fetch them with the page (`<link rel="modulepreload">`,
9 * routes/workspace/folios/folio.tsx `links`) rather than after the page's
10 * own code has run and asked for them. The editor is only ever imported
11 * lazily (it runs in the browser alone), so no route lists it among its
12 * modules; this notes its files as the browser build writes them, for the
13 * server build that follows in the same run. In development there are no
14 * such files, and the page adds no links.
15 */
16function editorChunk(): Plugin {
17 const id = "virtual:g1t-editor-chunk";
18 let files: string[] = [];
19 return {
20 name: "g1t-editor-chunk",
21 resolveId(source) {
22 return source === id ? `\0${id}` : null;
23 },
24 load(source) {
25 return source === `\0${id}` ? `export default ${JSON.stringify(files)};` : null;
26 },
27 generateBundle(_options, bundle) {
28 if (this.environment.name !== "client") return;
29 const chunks = Object.values(bundle).filter((output) => output.type === "chunk");
30 const editor = chunks.find((chunk) => chunk.moduleIds.some((moduleId) => moduleId.replace(/\\/g, "/").endsWith("/app/components/folios/doc/editor.tsx")));
31 if (!editor) return;
32 // The chunk first, then what it statically imports, each once.
33 const seen = new Set<string>();
34 const walk = (fileName: string) => {
35 if (seen.has(fileName)) return;
36 seen.add(fileName);
37 const chunk = chunks.find((c) => c.fileName === fileName);
38 for (const imported of chunk?.imports ?? []) walk(imported);
39 };
40 walk(editor.fileName);
41 files = [...seen].map((fileName) => `/${fileName}`);
42 },
43 };
44}
45
46export default defineConfig({
47 plugins: [
48 cloudflare({ viteEnvironment: { name: "ssr" } }),
49 tailwindcss(),
50 reactRouter(),
51 editorChunk(),
52 ],
53 resolve: {
54 tsconfigPaths: true,
55 },
56 build: {
57 rolldownOptions: {
58 output: {
59 codeSplitting: {
60 groups: [
61 // Icons are shared by nearly every page, a few hundred bytes each:
62 // one file for all of them, not a request per icon.
63 { name: "icons", test: /[\\/]node_modules[\\/]lucide-react[\\/]/, priority: 2 },
64 // The app's own components and helpers that several routes
65 // share: grouped by which routes use them, and small groups
66 // merged into their neighbours, so a page fetches a few files
67 // of them rather than one per module. A signed-in page fetched
68 // 124 files, most of them a kilobyte or two, each a request and
69 // a `modulepreload` to parse. Modules one route alone uses stay
70 // with that route. Packages are left to automatic splitting: a
71 // group over them merged what the editor needs with what the
72 // diagram renderers need, and every doc fetched both.
73 { name: "app", test: /[\\/]apps[\\/]web[\\/]app[\\/](components|lib)[\\/]/, minShareCount: 2, entriesAware: true, entriesAwareMergeThreshold: 24_000, priority: 1 },
74 ],
75 },
76 },
77 },
78 },
79});