4.7 KiB
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 --noEmitmust exit 0).- No
any. Useunknown+ narrowing, or the generated Payload types. - Document types come from
src/payload-types.ts(npm run generate:types, runs insidenpm 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 getconsole.errorwith 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 withfetchand 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)
mainis deployable truth. Feature branches:launch/…-style prefixes (feat/…,fix/…,docs/…).- PRs via Gitea; merge = POST
/repos/ahmed/anwar-pi-website/pulls/{index}/mergewith{"Do":"merge"}(PUT → 405). Evidence-updating doc commits may go straight tomainwhen 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.
stripeis already installed (do not add a second payment lib);sharpexists for images;tsxfor scripts. - New runtime deps need a PR-level justification line in the description; dev deps for testing are pre-approved by TESTING.md.