Files
anwar-pi-website/CODE_STYLE.md

4.7 KiB

CODE_STYLE — anwarpy.ai

TypeScript, strict, boring on purpose. The existing codebase is the style guide; this file makes it explicit. Formatting tooling may be added later only if it matches, not fights, this file.

1. TypeScript

  • strict: true (tsc is a commit gate — cd web && npx tsc --noEmit must exit 0).
  • No any. Use unknown + narrowing, or the generated Payload types.
  • Document types come from src/payload-types.ts (npm run generate:types, runs inside npm run build). Never hand-write an interface that mirrors a collection doc — import the generated one; hand-written mirrors drift and lie.
  • Payload collection access/hook args: annotate the request/doc shape explicitly (existing pattern: args: { doc?: {...}; req: { user?: {...} } | null }).
  • Avoid non-null !; if you're sure, prove it with a check or a comment stating the invariant.

2. Naming

Thing Convention Example
Files kebab-case secret-scan.ts, notify-form.tsx
Route folders Next convention, exact segment names in brackets posts/[slug]/route.ts
Components named exports (function name = file name, PascalCase) export function Topnav()
Route handlers Next default exports export async function POST(req: NextRequest)
Lib functions camelCase, exported individually lexicalToText, freeEarlyAccess
Constants SCREAMING_SNAKE READING_WORDS_PER_MINUTE, MEMBERSHIP_PRICE
DB fields camelCase in Payload readingMinutes, authorName
Booleans is/has/can/should or unambiguous verb billingEnabled, emailVerified

3. Error handling

  • Route handlers: validate → early NextResponse.json({ error: '<Arabic sentence>' }, { status }). Statuses: 400 shape, 401 auth, 403 forbidden, 404 missing, 409 conflict (slug), 422 semantic rejection (secret scan), 429 rate-limited, 503 dependency honestly down.
  • Error text is a fixed Arabic sentence owned by the route — specific about what failed («الدفع غير مفعّل خلال فترة الوصول المبكر.»). Never echo user input into the message; never expose stack traces or internal hosts; never a bare code with no words.
  • Never swallow: no empty catch. If you catch to continue, comment why. Server-side failures get console.error with context (no secrets).
  • Honest failure is a feature (PRD principle 1): if a dependency is down (mail transport, billing), return 503 with the true Arabic explanation instead of faking success. The notify route is the reference implementation.

4. Server vs client components

  • Default: server components. 'use client' only for forms and small interactions — check existing (account-settings/client.tsx, notify-form.tsx) before adding a new one.
  • No secrets, no process.env (non-public), no Payload local API calls in client components. They talk to route handlers with fetch and render the returned Arabic message.
  • No data-fetching libraries, no global client state (ARCHITECTURE §6).

5. Arabic & RTL in code

  • UI copy is Arabic with «» quotes; keep strings verbatim from the PRD where they're contracts (honest 503s, notice strips).
  • Latin islands inside RTL: wrap in <bdi dir="ltr"> ($10, anwarpy.ai, code, GPTBot).
  • Plurals/counts via src/lib/format.ts (guidesLabel, minutesLabel, formatNumber) — never inline Arabic number agreement.

6. Comments & docs

  • Comments explain invariants and decisions, not syntax. Reference the contract: «L-02», «ADR-0003», «PRD §3.2» are valid shorthand — these docs are the map.
  • When you change a documented decision, update the doc in the same PR (ADR for structural decisions — ARCHITECTURE §8).
  • No commented-out code committed. Delete it; git remembers.

7. Git & PRs (Gitea)

  • main is deployable truth. Feature branches: launch/…-style prefixes (feat/…, fix/…, docs/…).
  • PRs via Gitea; merge = POST /repos/ahmed/anwar-pi-website/pulls/{index}/merge with {"Do":"merge"} (PUT → 405). Evidence-updating doc commits may go straight to main when they record verified facts.
  • Commit messages: imperative, specific («fix: unwrap Lexical root in lexicalToText/countLexicalWords»), no «update stuff».
  • Never commit: .env*, media/, files-private/, backups/, build output, .tsbuildinfo.
  • Destructive ops (reset --hard, push -f, branch deletion) require the owner's explicit go-ahead.

8. Dependencies

  • Reuse ladder (gstack): repo helper → stdlib → platform → installed dep → new dep. stripe is already installed (do not add a second payment lib); sharp exists for images; tsx for scripts.
  • New runtime deps need a PR-level justification line in the description; dev deps for testing are pre-approved by TESTING.md.