CodeVetter · Repo Unpack · System map
excalidraw/excalidraw
The runtime splits into a publishable editor (packages/excalidraw) and a Vite app (excalidraw-app) that mounts it. Geometry and the element model live in @excalidraw/element, @excalidraw/math, and @excalidraw/common; export helpers live in @excalidraw/utils. The app talks to three external systems configured only by Vite env vars: a Socket.IO collab server, Firebase (Firestore scenes plus Storage blobs), and an HTTP share backend. Production is a static Vite build deployed to Vercel or an nginx image; package publishes go through scripts/release.js on the release branch.
- Root package excalidraw-monorepo is a private Yarn 1.22.22 workspace (node >=18) covering excalidraw-app, packages/*, and examples/*. Scripts delegate: yarn start runs the Vite app, yarn build:packages builds common, fractional-indexing, laser-pointer, math, element, then excalidraw, and yarn test:all chains typecheck, eslint, prettier, and vitest.
- The hosted app boots from excalidraw-app/index.tsx: it imports sentry, sets window.__EXCALIDRAW_SHA__ from VITE_APP_GIT_SHA, calls registerSW() from vite-plugin-pwa, and createRoot-renders <ExcalidrawApp /> in StrictMode.
- The npm component entry is packages/excalidraw/index.tsx. ExcalidrawBase normalizes UIOptions and imageOptions, blocks iOS pinch-zoom unless interaction.enabled.browserZoom is set, and renders EditorJotaiProvider > InitializeApp > App. Excalidraw is React.memo(ExcalidrawBase, areEqual). Package exports point at dist/prod/index.js with peer react 17, 18, or 19.
- Supporting packages are @excalidraw/common (constants, emitter, editorInterface, feature flags), @excalidraw/element (Scene, Store, element types, fractional index), @excalidraw/math, @excalidraw/utils (exportToCanvas/Svg wrappers), @excalidraw/fractional-indexing, and @excalidraw/laser-pointer. Vitest and the app Vite config alias every @excalidraw/* import at the TypeScript source, not dist.
- App-only env surface is declared in excalidraw-app/vite-env.d.ts: VITE_APP_WS_SERVER_URL, VITE_APP_FIREBASE_CONFIG, VITE_APP_BACKEND_V2_GET_URL, VITE_APP_BACKEND_V2_POST_URL, VITE_APP_AI_BACKEND, VITE_APP_PLUS_APP, VITE_APP_PLUS_LP, VITE_APP_PORTAL_URL, VITE_APP_ENABLE_PWA, VITE_APP_DISABLE_SENTRY, VITE_APP_GIT_SHA. Vite loads env from the repo root (envDir: '../') and serves on VITE_APP_PORT or 3000, writing the production bundle to excalidraw-app/build.
- Vercel config sets outputDirectory to excalidraw-app/build, installCommand yarn install, CORS Access-Control-Allow-Origin https://excalidraw.com, and redirects /webex/* to for-webex.excalidraw.com plus vscode.excalidraw.com to the VS Code marketplace item.
- Docker image is a multi-stage build: node:24 runs yarn --frozen-lockfile then yarn build:app:docker (VITE_APP_DISABLE_SENTRY=true), and nginx:stable-alpine-slim serves excalidraw-app/build. docker-compose publishes host 3000 to container 80 and bind-mounts the repo. Publish workflow on branch release pushes excalidraw/excalidraw:latest for linux/amd64, arm64, and arm/v7.
- GitHub Actions: test.yml runs yarn test:app on push to master (Node 20); lint.yml on pull_request runs yarn test:other, yarn test:code, and yarn test:typecheck; test-coverage-pr.yml runs yarn test:coverage and posts a coverage comment. release-branch workflows run yarn release --tag=next and upload Sentry sourcemaps.
- Firebase is initialized lazily in excalidraw-app/data/firebase.ts from JSON.parse(import.meta.env.VITE_APP_FIREBASE_CONFIG). Scenes are Firestore documents at scenes/{roomId}; image bytes go to Storage via uploadBytes. There is no collaboration server implementation in this repo — the client only opens socket.io-client against VITE_APP_WS_SERVER_URL.
- Editor state is a React class component (packages/excalidraw/components/App.tsx) owning Scene, Renderer, Store, History, ActionManager, Library, and binary files. Two Jotai stores exist (editor-jotai inside the package, app-jotai in the host). ESLint forbids importing jotai directly.
- Font subsetting runs off the main thread through WorkerPool in packages/excalidraw/workers.ts, which refuses to construct a worker whose URL equals import.meta.url (WorkerInTheMainChunkError). WASM assets for harfbuzz and woff2 live under scripts/wasm.
- scripts/release.js publishes packages common, fractional-indexing, math, element, and excalidraw (utils is explicitly skipped). Tags are test (default), next, or latest; latest requires --version and updates the changelog via child_process execSync.