Files
2026-04-20 18:59:15 +08:00

11 KiB

Coding Conventions

Analysis Date: 2026-04-20

Naming Patterns

Files:

  • Use Next.js App Router filenames in src/app, including page.tsx, layout.tsx, and route.ts, as seen in src/app/[locale]/page.tsx, src/app/[locale]/layout.tsx, src/app/api/projects/route.ts, and src/app/api/webhook/signals/route.ts.
  • Use PascalCase.tsx for reusable components in src/components, for example src/components/search/HomeSearchBar.tsx, src/components/project/ProjectDetail.tsx, and src/components/signals/SignalFeedClient.tsx.
  • Use lower-case or kebab-case utility filenames in src/lib, for example src/lib/auth.ts, src/lib/cache.ts, src/lib/signal-hotness.ts, src/lib/prisma-url.ts, and src/lib/tag-taxonomy.ts.
  • Keep tests co-located and named *.test.ts, for example src/lib/auth.test.ts, src/app/api/tags/route.test.ts, and src/app/api/tags/reset-projects/route.test.ts.
  • Treat src/hooks as a mixed server query layer plus client hooks. src/hooks/useProjects.ts and src/hooks/useHome.ts are not React hooks despite the use* prefix.

Functions:

  • Use camelCase for helpers and query functions, such as isValidApiKey in src/lib/auth.ts, normalizeProjectSort in src/hooks/useProjects.ts, parseSlugList in src/app/api/projects/route.ts, and getTimestamp in src/app/api/search/ai/route.ts.
  • Reserve PascalCase for React components, prop interfaces, and domain error classes, such as ProjectDetail in src/components/project/ProjectDetail.tsx and TagMaintenanceApiError in src/app/api/tags/maintenance/service.ts.
  • Export route handlers as uppercase HTTP verbs from src/app/api/**/route.ts, for example GET in src/app/api/tags/route.ts and POST in src/app/api/webhook/signals/route.ts.

Variables:

  • Use UPPER_SNAKE_CASE for configuration constants and env-backed settings, such as N8N_WEBHOOK_URL in src/app/api/search/ai/route.ts, TAGS_CACHE_REVALIDATE_SECONDS in src/app/api/tags/route.ts, DB_RETRY_DELAYS_MS in src/hooks/useProjects.ts, and ENV_KEYS in src/lib/prisma-url.test.ts.
  • Use descriptive names for parsed and normalized input, such as validatedQuery in src/app/api/projects/route.ts, validationResult in src/app/api/webhook/signals/route.ts, and normalizedTagSlugs in src/hooks/useProjects.ts.

Types:

  • Prefer type aliases for Prisma payloads and request payload shapes, such as ProjectWithFlatTags in src/hooks/useProjects.ts, SignalWebhookPayload in src/lib/validations.ts, and ResetProjectsRouteTxMock in src/app/api/tags/reset-projects/route.test.ts.
  • Prefer interface for React props, such as HomeSearchBarProps in src/components/search/HomeSearchBar.tsx and ProjectDetailProps in src/components/project/ProjectDetail.tsx.

Code Style

Formatting:

  • Follow .prettierrc.json: 2-space indentation, semicolons, double quotes, trailing commas es5, and printWidth 100.
  • AGENTS.md treats Prettier as authoritative and pnpm as the required package manager.
  • The codebase currently has mixed formatting. Files such as src/lib/validations.ts, src/lib/prisma-url.ts, and src/app/api/tags/route.ts match the configured double-quote style, while src/lib/auth.ts, src/app/api/projects/route.ts, and src/app/api/search/ai/route.ts still use single quotes and omit semicolons.
  • For new files, follow .prettierrc.json. When editing an existing file with a different quote style, either preserve the local file style for a surgical change or reformat the full file consistently.

Linting:

  • .eslintrc.json extends next/core-web-vitals and prettier.
  • Only console.warn and console.error are explicitly allowed by no-console. This matches the logging used in src/app/api/tags/route.ts, src/app/api/search/ai/route.ts, and src/app/api/webhook/signals/route.ts.
  • Current verification on 2026-04-20: pnpm lint passed with no warnings or errors.

Import Organization

Order:

  1. Framework and platform imports first, such as next/server, next/cache, zod, crypto, fs, or @prisma/client, as seen in src/app/api/webhook/signals/route.ts and src/lib/prisma-url.ts.
  2. Internal alias imports from @/ next, such as @/lib/prisma, @/lib/validations, and @/hooks/useProjects.
  3. Relative imports last, such as ./service in src/app/api/tags/maintenance/route.ts and ./MarkdownContent in src/components/project/ProjectDetail.tsx.

Path Aliases:

  • Use the @/* alias defined in tsconfig.json and mirrored in vitest.config.ts.
  • Prefer @/ imports for anything under src, as seen throughout src/app/api/tags/route.ts, src/app/api/tags/reset-projects/route.ts, and src/hooks/useProjects.ts.

API Validation and Auth

Validation:

  • Put shared Zod schemas in src/lib/validations.ts. Current examples include ProjectInputSchema, SignalWebhookPayloadSchema, SignalQuerySchema, TagMaintenanceRequestSchema, and ProjectTagResetRequestSchema.
  • Define route-local schemas only when the contract is route-specific, such as ProjectsQuerySchema in src/app/api/projects/route.ts and N8NSearchResponseSchema plus AISearchRequestSchema in src/app/api/search/ai/route.ts.
  • Use .safeParse() when the route should return a custom 400 response without exceptions, as in src/app/api/tags/maintenance/route.ts, src/app/api/tags/reset-projects/route.ts, and src/app/api/webhook/signals/route.ts.
  • Use .parse() when the route already catches ZodError, as in src/app/api/projects/route.ts, src/app/api/signals/route.ts, and src/app/api/search/ai/route.ts.
  • Use z.coerce.number() for query-string pagination and limits, as in src/lib/validations.ts and src/app/api/projects/route.ts.
  • Use .superRefine() for cross-record constraints such as duplicate tag IDs and self-merge prevention, as in TagMergeSchema, TagMaintenanceRequestSchema, and ProjectTagResetRequestSchema in src/lib/validations.ts.

Webhook Auth:

  • Use the timing-safe isValidApiKey(...) helper from src/lib/auth.ts for internal mutation and webhook routes.
  • Read the expected secret from process.env.WEBHOOK_API_KEY unless a test passes an explicit override.
  • Validate the API key format at schema level first, then authenticate with isValidApiKey(...), as done in src/app/api/tags/maintenance/route.ts, src/app/api/tags/reset-projects/route.ts, and src/app/api/webhook/signals/route.ts.
  • Return 401 with a JSON body containing success: false, error: "Unauthorized", and a details array on auth failure.

Data Access

Prisma:

  • Use the shared Prisma client from src/lib/prisma.ts.
  • Keep read-heavy query composition in src/hooks/useProjects.ts and src/hooks/useHome.ts.
  • Keep write workflows transactional with prisma.$transaction(...), as in src/app/api/tags/maintenance/route.ts and src/app/api/tags/reset-projects/route.ts.
  • Use explicit include and select clauses rather than broad model reads, as seen throughout src/hooks/useProjects.ts and src/app/api/webhook/signals/route.ts.

Caching and Degrade Patterns:

  • Use unstable_cache for repeatable server reads, as in src/app/api/tags/route.ts and src/hooks/useProjects.ts.
  • Route cached reads through runWithCacheFallback from src/lib/cache.ts.
  • Use retry helpers for transient DB reads, as in withDbRetry(...) and isTransientDbError(...) in src/hooks/useProjects.ts.
  • Degrade on schema drift where the route can still succeed, as in src/app/api/webhook/signals/route.ts falling back when hotness columns are unavailable.

Error Handling

Patterns:

  • Wrap route handlers in try/catch and return JSON through NextResponse.json(...), as seen in src/app/api/projects/route.ts, src/app/api/search/ai/route.ts, and src/app/api/webhook/signals/route.ts.
  • Return 400 for schema and query validation failures, 401 for invalid webhook keys, and 500 for unexpected failures.
  • Use route-local or domain-specific error classes when mutation logic needs structured status and details, as in TagMaintenanceApiError from src/app/api/tags/maintenance/service.ts.
  • Keep JSON error payloads stable enough for automation. Current routes usually emit success, error, details, and sometimes message, but the exact shape is not yet fully standardized across src/app/api/**/route.ts.

Logging:

  • Use console.error for failures and console.warn for operational summaries or degraded behavior, matching .eslintrc.json and examples in src/app/api/tags/route.ts, src/hooks/useProjects.ts, and src/app/api/webhook/signals/route.ts.
  • Avoid console.log in production code because lint warns on it.

Comments

When to Comment:

  • Keep comments sparse and intent-focused. Existing comments mostly explain numbered handler steps, fallback rationale, or bilingual product context, as in src/app/api/tags/maintenance/route.ts, src/hooks/useProjects.ts, and src/app/api/search/ai/route.ts.
  • Use short JSDoc only where security or contract semantics matter, such as the timing-safe explanation above isValidApiKey(...) in src/lib/auth.ts.

Function Design

Size:

  • The repository tolerates large query and route modules. Current examples include src/hooks/useProjects.ts, src/app/api/signals/route.ts, and src/app/api/tags/reset-projects/route.ts.
  • Keep helper functions close to the route or query layer before extracting a new module. Examples include parseSlugList(...) in src/app/api/projects/route.ts, normalizeSlugList(...) in src/app/api/search/ai/route.ts, and toSectionsJson(...) in src/app/api/webhook/signals/route.ts.

Parameters:

  • Prefer a single typed options object for non-trivial query helpers, as in getProjects(...) from src/hooks/useProjects.ts.
  • For route handlers, parse from request.nextUrl.searchParams or await request.json() once, then normalize into a validated object before passing deeper.

Return Values:

  • Return plain serializable objects from API routes and query helpers.
  • Flatten Prisma relation shapes before returning UI data, as done by getProjects(...) and getProjectsByIds(...) in src/hooks/useProjects.ts.

Module Design

Exports:

  • Prefer named exports across shared modules and components. No barrel files were detected under src.
  • Use default exports mainly for App Router pages and layouts under src/app.

Separation of Concerns:

  • Keep Prisma-backed reads in src/hooks/useProjects.ts and src/hooks/useHome.ts.
  • Keep cross-cutting utilities in src/lib, such as src/lib/auth.ts, src/lib/cache.ts, src/lib/prisma.ts, and src/lib/validations.ts.
  • Keep mutation business logic in a local service file when the route would otherwise mix transport and domain rules. src/app/api/tags/maintenance/route.ts plus src/app/api/tags/maintenance/service.ts is the clearest existing pattern.

Convention analysis: 2026-04-20