51925e955f
Everything sat in the eager startup graph (App.tsx statically imported all 28
routes; the editor pulled TipTap + KaTeX + ~45 lowlight grammars + drawio;
posthog + AI SDK loaded for everyone) — a /login visitor downloaded+compiled the
whole editor. Client-only; functionality 1:1, only WHEN code loads changed.
Result (prod build): eager JS 3.5MB -> ~1.12MB, entry 1920KB -> 552KB; KaTeX
(250KB) and the TipTap engine (~586KB) are now lazy chunks, off the startup path.
- App.tsx: route-level React.lazy + Suspense (editor Page, all settings/*, share,
space/home routes). Auth/redirect/cold-start routes stay eager. Suspense lives
inside Layout/ShareLayout around the Outlet so the shell stays mounted.
- Lazy KaTeX node views (math-inline-lazy/math-block-lazy) + lazy drawio
(drawio-view-lazy/drawio-menu-lazy), mirroring mermaid/excalidraw, each with a
node-sized Suspense placeholder so a slow chunk can't crash the editor.
- posthog-js is now a conditional dynamic import under the unchanged
isCloud() && isPostHogEnabled gate — self-hosted never downloads it.
- AiChatWindow is React.lazy, mounted on first open and kept mounted (a live AI
stream isn't torn down); renders null while closed (identical behavior).
- Cut eager TipTap pulls from always-loaded shell modules: editor-atoms /
global-bridge Editor -> import type; Aside lazily loaded (page routes only);
config.ts sanitizeUrl and use-clipboard execCommandCopy moved to client-local
src/lib/{sanitize-url,copy-to-clipboard}.ts (byte-identical to the editor-ext
originals, dropping the barrel's top-level @tiptap import); WebSocketStatus
import replaced with the "connected" literal the status atom already stores.
- vite.config.ts: a vendor-katex chunk group (TipTap/PM/Yjs intentionally NOT
grouped — grouping dragged the engine eager; documented in the config).
- lowlight grammar registration is left inside the (now-lazy) editor chunk:
listLanguages()/highlighting are synchronous, so deferring registration would
change behavior for marginal in-chunk gain — the route split already removes it
from startup, which was the complaint.
Gate: client build succeeds, tsc --noEmit clean, frozen install EXIT 0 (added
@braintree/sanitize-url as a direct client dep + regenerated the lock).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
110 lines
3.0 KiB
TypeScript
110 lines
3.0 KiB
TypeScript
import { defineConfig, loadEnv } from "vite";
|
|
import react from "@vitejs/plugin-react";
|
|
import * as path from "path";
|
|
import { execSync } from "node:child_process";
|
|
|
|
const envPath = path.resolve(process.cwd(), "..", "..");
|
|
|
|
// Resolve the version string shown in the UI.
|
|
// Priority: explicit APP_VERSION env (injected by Docker/CI, where .git is absent),
|
|
// then `git describe` for local builds, then the package.json version as a fallback.
|
|
function resolveAppVersion(cwd: string): string {
|
|
const fromEnv = process.env.APP_VERSION?.trim();
|
|
if (fromEnv) return fromEnv;
|
|
try {
|
|
return execSync("git describe --tags --always", {
|
|
cwd,
|
|
stdio: ["ignore", "pipe", "ignore"],
|
|
})
|
|
.toString()
|
|
.trim();
|
|
} catch {
|
|
return `v${process.env.npm_package_version ?? "0.0.0"}`;
|
|
}
|
|
}
|
|
|
|
export default defineConfig(({ mode }) => {
|
|
const {
|
|
APP_URL,
|
|
FILE_UPLOAD_SIZE_LIMIT,
|
|
FILE_IMPORT_SIZE_LIMIT,
|
|
DRAWIO_URL,
|
|
CLOUD,
|
|
SUBDOMAIN_HOST,
|
|
COLLAB_URL,
|
|
BILLING_TRIAL_DAYS,
|
|
POSTHOG_HOST,
|
|
POSTHOG_KEY,
|
|
} = loadEnv(mode, envPath, "");
|
|
|
|
return {
|
|
define: {
|
|
"process.env": {
|
|
APP_URL,
|
|
FILE_UPLOAD_SIZE_LIMIT,
|
|
FILE_IMPORT_SIZE_LIMIT,
|
|
DRAWIO_URL,
|
|
CLOUD,
|
|
SUBDOMAIN_HOST,
|
|
COLLAB_URL,
|
|
BILLING_TRIAL_DAYS,
|
|
POSTHOG_HOST,
|
|
POSTHOG_KEY,
|
|
},
|
|
APP_VERSION: JSON.stringify(resolveAppVersion(envPath)),
|
|
},
|
|
plugins: [react()],
|
|
build: {
|
|
rolldownOptions: {
|
|
output: {
|
|
advancedChunks: {
|
|
groups: [
|
|
{
|
|
name: "vendor-mantine",
|
|
test: /[\\/]node_modules[\\/]@mantine[\\/]/,
|
|
},
|
|
// NOTE: TipTap/ProseMirror/Yjs are intentionally NOT force-grouped
|
|
// into a single vendor chunk. Doing so backfires: rolldown co-locates
|
|
// a small module shared with the (eager) react-i18next runtime into
|
|
// that group chunk, which then drags the whole ~590KB editor engine
|
|
// into the eager modulepreload graph. Left to the default splitting,
|
|
// the editor engine stays in lazily-loaded chunks pulled only by the
|
|
// route-split editor/share pages. KaTeX is safe to group (nothing
|
|
// eager references it).
|
|
// KaTeX in its own stable chunk; loaded on demand by the lazy math
|
|
// node views (never in the startup path).
|
|
{
|
|
name: "vendor-katex",
|
|
test: /[\\/]node_modules[\\/]katex[\\/]/,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
resolve: {
|
|
alias: {
|
|
"@": "/src",
|
|
},
|
|
},
|
|
server: {
|
|
proxy: {
|
|
"/api": {
|
|
target: APP_URL,
|
|
changeOrigin: false,
|
|
},
|
|
"/socket.io": {
|
|
target: APP_URL,
|
|
ws: true,
|
|
rewriteWsOrigin: true,
|
|
},
|
|
"/collab": {
|
|
target: APP_URL,
|
|
ws: true,
|
|
rewriteWsOrigin: true,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
});
|