Files
famapp/docs/CONTRIBUTING.md
T

6.9 KiB
Raw Blame History

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

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):

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

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

Running tests

Type check + lint (CI equivalent)

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:

# 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 — 200400 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. 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