Skip to content

Commit b98d43c

Browse files
authored
Reorganize folders for clarity and maintainability (#21)
## ✅ What - Reorganized `ui/` into three tiers: `elements/` (primitives like Link, Image), `layout/` (site structure like Header, Footer), and `sections/` (page-specific blocks like PostHeader, LikesRow) - Created `seo/` folder consolidating metadata constants and JSON-LD generation (was scattered across `utils/` and `app/`) - Moved `app/page.tsx` → `app/(home)/page.tsx` using Next.js route groups to reduce root clutter - Moved `public/_redirects.test.ts` → `io/cloudflare/_redirects.test.ts` (tests shouldn't be in published assets) - Moved `io/retry` → `io/utils/retry` for better organization - Deleted dead code: `io/tmdb/constants.ts` (never used), `io/notion/getPage.ts` (unused helper) - Deleted `ci/config.ts` and inlined all constants into their single-use files (only `OUT_DIR` was used in 2 places, rest were single-use) ## 🤔 Why - Three-tier `ui/` structure makes it immediately clear where to add new components (reusable primitive? page-specific section? site-wide layout?) - Consolidating SEO code in one place makes metadata handling easier to find and maintain - Route groups reduce `app/` root clutter while maintaining Next.js routing conventions - Removing unnecessary abstractions (single-use config files, unused code) reduces cognitive overhead when browsing the codebase
1 parent 13e7cec commit b98d43c

99 files changed

Lines changed: 1833 additions & 1190 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/CLAUDE.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# michaeluloth.com - Claude Code Rules
2+
3+
## TypeScript Type Safety
4+
5+
### Never use `any`
6+
7+
**Rule:** Never use `any`, `as any`, or `@typescript-eslint/no-explicit-any` to bypass type errors.
8+
9+
**Why:** Type errors indicate a mismatch between what TypeScript expects and what you're providing. The solution is always to communicate the real type properly, not to suppress the error.
10+
11+
**How to fix type errors properly:**
12+
13+
1. **Understand what TypeScript expects** - Read the error message carefully to understand the expected type
14+
2. **Provide the correct type** - Use proper type annotations, interfaces, or type assertions with the actual type
15+
3. **Use the right syntax** - If a library expects JSX/ReactNode, use JSX syntax (`.tsx` files) instead of plain objects
16+
4. **Import necessary types** - Ensure you have the correct imports (e.g., `React` for JSX)
17+
18+
**Example: Satori type error**
19+
20+
```typescript
21+
// ❌ WRONG - bypassing the type error
22+
const svg = await satori(
23+
{
24+
type: 'div',
25+
props: { /* ... */ }
26+
} as any, // Never do this!
27+
options
28+
)
29+
30+
// ✅ CORRECT - using proper JSX syntax that TypeScript understands
31+
// 1. Rename file from .ts to .tsx
32+
// 2. Import React
33+
import React from 'react'
34+
35+
// 3. Use JSX syntax instead of plain objects
36+
const svg = await satori(
37+
<div>
38+
{/* ... */}
39+
</div>,
40+
options
41+
)
42+
```
43+
44+
This rule ensures the codebase maintains full type safety and prevents runtime errors that `any` would hide.

.gitignore

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
# Theirs
2-
node_modules/
32
.netlify/
43
.next/
54
.playwright-mcp/
65
.vercel/
76
build/
7+
node_modules/
88
out/
99
.DS_Store
1010
*.log
1111
*.tsbuildinfo
1212
next-env.d.ts
13-
.lighthouseci/
14-
ci/lighthouse/urls.json
1513

1614
# Mine
17-
**/cloudinary/*.json
1815
.env
1916
.env.local
17+
.lighthouseci/
2018
.local-cache/
19+
ci/lighthouse/urls.json
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import { Ok } from '@/utils/errors/result'
1111

1212
// Mock dependencies
1313
vi.mock('@/io/notion/getPosts')
14-
vi.mock('@/ui/image', () => ({
14+
vi.mock('@/ui/elements/image', () => ({
1515
// eslint-disable-next-line @next/next/no-img-element -- Using img in test mock is acceptable
1616
default: ({ url }: { url: string }) => <img src={url} alt="Michael Uloth" />,
1717
}))
1818

1919
// Mock PostList to avoid async server component complexity in tests
20-
// The actual PostList behavior is tested in ui/post-list.test.tsx
21-
vi.mock('@/ui/post-list', () => ({
20+
// The actual PostList behavior is tested in ui/sections/blog-post-list.test.tsx
21+
vi.mock('@/ui/sections/blog-post-list', () => ({
2222
default: ({ limit }: { limit?: number }) => {
2323
// Call getPosts to verify it's called with correct params
2424
// This ensures the mock is called during render which we verify in tests
@@ -29,6 +29,15 @@ vi.mock('@/ui/post-list', () => ({
2929
},
3030
}))
3131

32+
// Mock PageLayout to avoid Next.js usePathname() in Header component
33+
vi.mock('@/ui/layout/page-layout', () => ({
34+
default: ({ children }: { children: React.ReactNode }) => (
35+
<main id="main" className="flex-auto flex flex-col">
36+
{children}
37+
</main>
38+
),
39+
}))
40+
3241
describe('Home page', () => {
3342
beforeEach(() => {
3443
vi.clearAllMocks()

app/(home)/page.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { type ReactElement } from 'react'
2+
import PageLayout from '@/ui/layout/page-layout'
3+
import Summary from '@/ui/sections/home-summary'
4+
import RecentWriting from '@/ui/sections/home-recent-writing'
5+
import JsonLdScript from '@/seo/json-ld/script'
6+
7+
export default async function Home(): Promise<ReactElement> {
8+
return (
9+
<PageLayout>
10+
<Summary />
11+
<RecentWriting />
12+
<JsonLdScript type="person" />
13+
</PageLayout>
14+
)
15+
}

app/(prose)/[slug]/page.tsx

Lines changed: 0 additions & 143 deletions
This file was deleted.

app/(prose)/[slug]/ui/post.tsx

Lines changed: 0 additions & 28 deletions
This file was deleted.

app/(prose)/blog/page.tsx

Lines changed: 0 additions & 33 deletions
This file was deleted.

app/(prose)/layout.test.tsx

Lines changed: 0 additions & 54 deletions
This file was deleted.

0 commit comments

Comments
 (0)