Contributing
Prerequisites
- Node.js 24+
- npm 10+
- Git
Setup
bash
git clone https://github.com/tvcsantos/retrosync.git
cd retrosync
npm install
npm run devThis starts the Electron app in development mode with hot module replacement for the renderer.
Project Structure
text
retrosync/
├── src/
│ ├── main/ # Electron main process
│ │ ├── index.ts # App entry, window creation, IPC handlers
│ │ ├── config.ts # JSON config management
│ │ ├── igdb.ts # IGDB API integration
│ │ ├── platforms.ts # Device profiles & platform definitions
│ │ ├── library.ts # Library management (add/remove games)
│ │ ├── imageCache.ts # IGDB cover image caching
│ │ ├── fs-utils.ts # Shared async filesystem helpers
│ │ ├── db/ # Database setup & schema
│ │ │ ├── index.ts # SQLite initialization (WAL mode)
│ │ │ └── schema.ts # Drizzle table definitions
│ │ ├── addons/ # Addon system
│ │ │ ├── index.ts # Barrel exports
│ │ │ ├── types.ts # Addon interface contracts
│ │ │ ├── context.ts # Context passed to addon factories
│ │ │ ├── loader.ts # Dynamic addon loading from disk
│ │ │ ├── registry.ts # Addon lifecycle & parallel queries
│ │ │ ├── ipc.ts # Addon IPC handlers
│ │ │ └── local-folder/ # Built-in local folder addon
│ │ ├── imports/ # Import queue manager
│ │ │ ├── manager.ts # Queue, concurrency, staging, transfer lifecycle
│ │ │ ├── types.ts # Import types & interfaces
│ │ │ └── index.ts # Import IPC handlers
│ │ └── bios/ # BIOS management
│ │ └── index.ts # BIOS source aggregation & installation
│ ├── renderer/ # React frontend
│ │ └── src/
│ │ ├── App.tsx # Root component, routing, layout
│ │ ├── main.tsx # React mount
│ │ ├── pages/ # Top-level views
│ │ ├── components/ # Reusable UI components
│ │ ├── store/ # Zustand state management
│ │ ├── types/ # Renderer type definitions
│ │ └── assets/ # CSS, images
│ └── preload/ # Electron preload (IPC bridge)
│ ├── index.ts # contextBridge API
│ └── index.d.ts # Type declarations for window.api
├── addons/ # External addon packages (gitignored, runtime only)
├── resources/
│ └── migrations/ # Main database migrations
├── build/ # Build assets (icons, entitlements)
├── docs/ # Documentation
├── electron.vite.config.ts # Vite + Electron build config
├── electron-builder.yml # Packaging config
├── drizzle.config.ts # Main Drizzle migration config
├── eslint.config.mjs # ESLint config
├── tsconfig.json # Root TypeScript config
├── tsconfig.node.json # Main + preload TypeScript config
└── tsconfig.web.json # Renderer TypeScript configScripts
| Script | Description |
|---|---|
npm run dev | Start in development mode with HMR (Hot Module Replacement) |
npm run build | Typecheck + build for production |
npm run start | Preview the production build |
npm run lint | Run ESLint |
npm run format | Run Prettier on all files |
npm run typecheck | Run both TypeScript checks |
npm run typecheck:node | Typecheck main + preload |
npm run typecheck:web | Typecheck renderer |
npm run db:generate | Generate main database migrations |
npm run build:mac | Build macOS DMG |
npm run build:win | Build Windows installer |
npm run build:linux | Build Linux packages |
npm run build:unpack | Build + unpack (no installer) |
npm run docs:dev | Start VitePress dev server |
npm run docs:build | Build the documentation site |
npm run docs:preview | Preview the built documentation site |
Code Style
General
- TypeScript throughout - no
anytypes (enforced by ESLint) - Prettier for formatting - run
npm run formatbefore committing - Unused variables/parameters prefixed with
_(e.g.,_platformIds) - Prefer
constoverlet; avoidvar - Default exports for React components (one component per file)
React
- Functional components only
- Zustand for state - no
useContextfor global state - Headless UI for accessible interactive primitives
- Tailwind utility classes - no CSS modules or styled-components
Electron
- All filesystem/network I/O in the main process
- Renderer communicates exclusively through
window.api.* - IPC handlers return
{ ok: true, data? }or{ ok: false, error }shapes - Use
electron-logscoped loggers, notconsole.log
Naming
- Files:
kebab-casefor directories,PascalCase.tsxfor components,camelCase.tsfor modules - Types/Interfaces:
PascalCase - Functions/variables:
camelCase - Constants:
UPPER_SNAKE_CASEfor module-level constants - Database tables:
snake_case
Verification
Before submitting changes, run:
bash
npm run lint # Zero errors, zero warnings
npm run typecheck # Both node and web configs passFor addon changes, run the addon's own typecheck in its repository:
bash
cd path/to/<addon-name>
npm run typecheckDatabase Changes
If you modify the database schema:
Edit the schema file (
src/main/db/schema.ts)Generate a migration:
bashnpm run db:generateReview the generated SQL in
resources/migrations/Test the migration by running the app fresh
External addons manage their own migrations independently in their own repositories.
Adding a New Page
- Create
src/renderer/src/pages/MyPage.tsx - Add the page ID to the
Pagetype insrc/renderer/src/types/ - Add a route case in
App.tsx - Add a sidebar entry in
Sidebar.tsx
Adding a New IPC Channel
- Add the handler in the appropriate main-process module
- Register it in
src/main/index.ts(or the relevantipc.ts) - Add the bridge call in
src/preload/index.ts - Add the type declaration in
src/preload/index.d.ts - Call it from the renderer via
window.api.*