Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .eslintrc.json

This file was deleted.

27 changes: 27 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Lint

on:
push:
branches: [main]
pull_request:

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10

- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- run: pnpm install --frozen-lockfile

- run: pnpm run lint

- run: pnpm run fmt:check
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx lint-staged
7 changes: 7 additions & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"ignorePatterns": ["pnpm-lock.yaml"],
"semi": false,
"singleQuote": true,
"tabWidth": 2
}
37 changes: 37 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["react", "nextjs", "typescript"],
"options": {
"typeAware": true,
"typeCheck": true
},
"categories": {
"correctness": "error",
"suspicious": "warn"
},
"rules": {
"eslint/no-unused-vars": "error",
"eslint/no-unused-expressions": "error",
"eslint/eqeqeq": "error",
"eslint/no-underscore-dangle": ["warn", { "allow": ["_embedded", "_links", "_fields"] }],
"eslint/no-shadow": "warn",

"nextjs/no-img-element": "error",
"nextjs/no-html-link-for-pages": "error",

"react/react-in-jsx-scope": "off",

"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",

"typescript/no-floating-promises": "error",
"typescript/no-misused-promises": "error",
"typescript/await-thenable": "error",
"typescript/no-explicit-any": "warn",
"typescript/use-unknown-in-catch-callback-variable": "error",
"typescript/prefer-optional-chain": "warn",
"typescript/prefer-nullish-coalescing": "off",
"typescript/no-unsafe-type-assertion": "off",
"typescript/consistent-return": "warn"
}
}
24 changes: 20 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,84 @@
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Build Commands

- `npm run dev` - Start development server with turbo mode
- `npm run build` - Build for production
- `npm run start` - Start production server
- `npm run lint` - Run ESLint to check code quality
- `npm run start` - Start production server
- `npm run lint` - Run oxlint with type-aware rules and type-check (tsgolint)
- `npm run lint:fix` - Auto-fix oxlint issues where possible
- `npm run typecheck` - Type-check via oxlint/tsgolint (same as `lint`)
- `npm run fmt` - Format code with oxfmt (TS/TSX/CSS/JSON/MD/YAML)
- `npm run fmt:check` - Check formatting without writing files

## Architecture Overview

This is a headless WordPress starter using Next.js 15 App Router with TypeScript. Key architectural patterns:

### Data Layer

- All WordPress API interactions go through `lib/wordpress.ts`
- Type definitions in `lib/wordpress.d.ts` define Post, Page, Category, Tag, Author, Media interfaces
- Error handling uses custom `WordPressAPIError` class
- Functions use Next.js cache tags for granular revalidation (e.g., `tags: ['posts', `post-${slug}`]`)

### Routing Structure

- Dynamic routes: `/posts/[slug]`, `/pages/[slug]`
- Archive pages: `/posts`, `/posts/authors`, `/posts/categories`, `/posts/tags`
- API routes: `/api/revalidate` (webhook), `/api/og` (OG images)

### Component Patterns

- Server Components for data fetching with parallel `Promise.all()` calls
- URL-based state management for search and filters
- Debounced search (300ms) with `useSearchParams`
- Pagination with 9 posts per page default

### Revalidation System

- WordPress plugin sends webhooks on content changes
- Next.js endpoint validates webhook secret and calls `revalidateTag()`
- Default cache duration: 1 hour (`revalidate: 3600`)

## Code Style

### TypeScript

- Use strict typing with interfaces defined in `lib/wordpress.d.ts`
- Prefer type annotations over type assertions
- Use type inference when the type is obvious

### Naming Conventions

- React components: PascalCase (e.g., `PostCard.tsx`)
- Functions and variables: camelCase
- Types and interfaces: PascalCase
- Constants: UPPERCASE_SNAKE_CASE for true constants

### File Structure

- Page components: `/app/**/*.tsx`
- Reusable UI components: `/components/**/*.tsx`
- Reusable UI components: `/components/**/*.tsx`
- API and utility functions: `/lib/**/*.ts`
- WordPress data functions must use cache tags for proper revalidation

### Error Handling

- Use `try/catch` blocks for API calls
- Utilize `WordPressAPIError` class for consistent API error handling

## Environment Variables

Required environment variables (see `.env.example`):

- `NEXT_PUBLIC_WORDPRESS_URL` - Full URL of WordPress site
- `NEXT_WORDPRESS_WEBHOOK_SECRET` - Secret for webhook validation

## Key Dependencies

- Next.js 15.3.3 with React 19.1.0
- TypeScript with strict mode
- Tailwind CSS with shadcn/ui components
- React Hook Form for form handling
- Lucide React for icons
- Lucide React for icons
Loading
Loading