159 lines
6.9 KiB
Markdown
159 lines
6.9 KiB
Markdown
# Contributing
|
||
|
||
## Prerequisites
|
||
|
||
- **Node.js** 24 LTS (`node --version` should print `v24.x.x`)
|
||
- **pnpm** 10 (`pnpm --version` should print `10.x.x`)
|
||
- **Docker** with the Compose plugin (used for the local Postgres + MinIO containers)
|
||
|
||
## Setup
|
||
|
||
```bash
|
||
git clone https://github.com/ginnoir/famapp.git
|
||
cd famapp
|
||
pnpm install
|
||
cp .env.example .env
|
||
# Edit .env — minimum required for local dev:
|
||
# NEXT_PUBLIC_APP_URL=http://127.0.0.1:3000
|
||
# DATABASE_URL=postgres://famapp:famapp@localhost:5432/famapp
|
||
# ENABLE_DEV_LOGIN=true
|
||
# AUTH_SECRET=<any-32-char-string-for-local>
|
||
```
|
||
|
||
Start everything (database + migrations + seed + dev server):
|
||
|
||
```bash
|
||
pnpm dev:local
|
||
```
|
||
|
||
Then open `http://localhost:3000/login` and click **Dev login**. See `docs/dev-login.md` for the full local setup including HTTPS/PWA testing.
|
||
|
||
## Available scripts
|
||
|
||
<!-- AUTO-GENERATED from package.json scripts -->
|
||
|
||
| Command | Description |
|
||
| --------------------- | ----------------------------------------------------------------------------------------- |
|
||
| `pnpm dev` | Next.js dev server (localhost only) |
|
||
| `pnpm dev:network` | Dev server bound to `0.0.0.0` (LAN access for phone testing) |
|
||
| `pnpm dev:local` | Full local stack — starts DB container, runs migrations, seeds, launches dev server |
|
||
| `pnpm dev:reset` | Tear down local DB and start fresh (destructive — deletes all local data) |
|
||
| `pnpm build` | Production Next.js build |
|
||
| `pnpm start` | Start the production build locally |
|
||
| `pnpm lint` | ESLint check |
|
||
| `pnpm lint:fix` | ESLint with auto-fix |
|
||
| `pnpm format` | Prettier — format all files |
|
||
| `pnpm format:check` | Prettier — check only (used in CI) |
|
||
| `pnpm typecheck` | TypeScript type check (`tsc --noEmit`) |
|
||
| `pnpm test:e2e` | Playwright end-to-end tests |
|
||
| `pnpm db:generate` | Generate a new Drizzle migration from schema changes |
|
||
| `pnpm db:migrate` | Apply pending Drizzle migrations |
|
||
| `pnpm db:seed` | Seed the database with dev fixtures |
|
||
| `pnpm db:studio` | Open Drizzle Studio (local DB browser) |
|
||
| `pnpm gen:icons` | Regenerate PWA icon set from source |
|
||
| `pnpm vapid:generate` | Generate VAPID key pair for Web Push |
|
||
| `pnpm release` | Interactive release (prompts for semver bump, tags, publishes changelog + GitHub Release) |
|
||
| `pnpm release:patch` | Non-interactive patch release |
|
||
| `pnpm release:minor` | Non-interactive minor release |
|
||
| `pnpm release:major` | Non-interactive major release |
|
||
| `pnpm release:dry` | Dry-run release — preview without writing |
|
||
|
||
<!-- END AUTO-GENERATED -->
|
||
|
||
## Running tests
|
||
|
||
### Type check + lint (CI equivalent)
|
||
|
||
```bash
|
||
pnpm typecheck
|
||
pnpm lint
|
||
pnpm format:check
|
||
```
|
||
|
||
### End-to-end tests
|
||
|
||
The app must be running first (`pnpm dev:local`). Generate a Playwright auth state file, then run tests:
|
||
|
||
```powershell
|
||
# Generate auth state (run once after starting the app)
|
||
New-Item -ItemType Directory -Force tests\.auth | Out-Null
|
||
@'
|
||
const { chromium } = require('@playwright/test');
|
||
(async () => {
|
||
const browser = await chromium.launch();
|
||
const page = await browser.newPage();
|
||
await page.goto('http://127.0.0.1:3000/login');
|
||
await page.getByRole('button', { name: 'Dev login' }).click();
|
||
await page.waitForURL('http://127.0.0.1:3000/');
|
||
await page.context().storageState({ path: 'tests/.auth/dev-user.json' });
|
||
await browser.close();
|
||
})();
|
||
'@ | node -
|
||
|
||
# Run tests
|
||
$env:PLAYWRIGHT_STORAGE_STATE='tests/.auth/dev-user.json'
|
||
pnpm test:e2e
|
||
```
|
||
|
||
### Writing tests
|
||
|
||
- Unit tests: Vitest under `tests/unit/` — only where it pays off (utilities, pure logic).
|
||
- E2E tests: Playwright under `tests/e2e/` — one happy-path test per module. Do not write brittle selector-heavy tests for trivial CRUD.
|
||
|
||
## Code style
|
||
|
||
- **TypeScript strict** — no `any` without a written reason.
|
||
- **No comments** unless the _why_ is non-obvious. Names carry intent.
|
||
- **Immutable** — always return new objects; never mutate in place.
|
||
- **Module isolation** — a module imports from `_core` and `lib/` only; never from a sibling module.
|
||
- **File size** — 200–400 lines typical, 800 hard cap.
|
||
|
||
Formatting and lint run automatically on staged files via `lint-staged` at commit time. You can also run them manually with `pnpm lint:fix` and `pnpm format`.
|
||
|
||
## Commit format
|
||
|
||
Commits must follow [Conventional Commits](https://www.conventionalcommits.org/). `commitlint` enforces this at the `commit-msg` hook.
|
||
|
||
```
|
||
<type>: <short description>
|
||
|
||
[optional body]
|
||
```
|
||
|
||
Allowed types: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `ci`, `revert`
|
||
|
||
Examples:
|
||
|
||
```
|
||
feat: add plant species search to garden module
|
||
fix: calendar event end time off by one day
|
||
chore: bump next to 15.6
|
||
```
|
||
|
||
## Adding a module
|
||
|
||
Every feature lives under `src/modules/<name>/`. A new module needs:
|
||
|
||
- `schema.ts` — Drizzle tables
|
||
- `server/` — server actions and queries
|
||
- `components/` — React components
|
||
- `manifest.ts` — registers the module with the core registry (nav, entity types, dashboard widget, quick-add actions)
|
||
|
||
See `CLAUDE.md` for the full architectural brief. The module loader in `src/modules/_core/` discovers manifests automatically — no changes to core code required for a new module.
|
||
|
||
## Schema changes
|
||
|
||
1. Edit the relevant `schema.ts`.
|
||
2. Run `pnpm db:generate` to create a new migration file under `drizzle/`.
|
||
3. Commit the migration alongside the schema change.
|
||
4. Never edit a shipped migration — always add a new one.
|
||
|
||
## PR checklist
|
||
|
||
- [ ] `pnpm typecheck` passes
|
||
- [ ] `pnpm lint` passes
|
||
- [ ] `pnpm build` succeeds
|
||
- [ ] New Drizzle migration committed if schema changed
|
||
- [ ] E2E test added or updated if a user-visible flow changed
|
||
- [ ] `docs/tasks/09-pre-deploy-checklist.md` reviewed if touching auth or env
|