Skip to content

Reorganize folders for clarity and maintainability#21

Merged
ooloth merged 31 commits intomainfrom
reorganize-folders
Dec 27, 2025
Merged

Reorganize folders for clarity and maintainability#21
ooloth merged 31 commits intomainfrom
reorganize-folders

Conversation

@ooloth
Copy link
Owner

@ooloth ooloth commented Dec 27, 2025

✅ 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.tsxapp/(home)/page.tsx using Next.js route groups to reduce root clutter
  • Moved public/_redirects.test.tsio/cloudflare/_redirects.test.ts (tests shouldn't be in published assets)
  • Moved io/retryio/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

@ooloth ooloth marked this pull request as ready for review December 27, 2025 01:42
Copilot AI review requested due to automatic review settings December 27, 2025 01:42
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reorganizes the codebase folder structure to improve clarity and maintainability. The changes consolidate UI components into a three-tier hierarchy (elements/layout/sections), centralize SEO-related code, and eliminate dead code and unnecessary abstractions.

Key Changes:

  • Reorganized UI components into elements/, layout/, and sections/ directories
  • Consolidated SEO code into a dedicated seo/ folder with JSON-LD generation and metadata constants
  • Moved app/page.tsx to app/(home)/page.tsx using Next.js route groups
  • Inlined single-use configuration constants from ci/config.ts into their respective files

Reviewed changes

Copilot reviewed 68 out of 93 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
ui/sections/* New page-specific components (post-header, post-pagination, likes-row, etc.)
ui/layout/* New site-wide layout components (header, footer, page-layout, header-nav)
seo/* New centralized SEO folder with JSON-LD generators and metadata constants
app/(home)/page.tsx Moved homepage from app/page.tsx using route groups
io//fetch.ts Added caching support and updated imports
ci/metadata/validate.ts Inlined configuration constants
utils/dates.ts Deleted (functions moved to consuming files)
Comments suppressed due to low confidence (1)

ui/sections/home-summary.tsx:1

  • The updated sentence structure is awkward with the period mid-sentence. Consider: "I care about reliability, ergonomics and getting the details right. Since I'd rather not get paged, I spend time thinking about what could go wrong and how we can make sure it doesn't."
import { type ReactElement } from 'react'

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 40 to 41
// TODO: confirm if this is the right behaviour; what pages would this apply to?
return {}
Copy link

Copilot AI Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This TODO comment indicates uncertainty about the behavior. Since getPost returns null when a slug isn't found, returning an empty metadata object here would mean pages with invalid slugs get no metadata at all. Consider either removing this early return (let the notFound() call below handle it), or document which specific scenario this handles.

Suggested change
// TODO: confirm if this is the right behaviour; what pages would this apply to?
return {}
notFound()

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +67
const params = {
books: { medium: 'ebook' as iTunesMedium, entity: 'ebook' as iTunesEntity },
albums: { medium: 'music' as iTunesMedium, entity: 'album' as iTunesEntity },
podcasts: { medium: 'podcast' as iTunesMedium, entity: 'podcast' as iTunesEntity },
}[category]
Copy link

Copilot AI Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The params mapping uses inline type assertions that could be eliminated by properly typing the mapping object. Consider: const params: Record<MediaCategory, { medium: iTunesMedium; entity: iTunesEntity }> = { ... } to remove the need for type assertions.

Suggested change
const params = {
books: { medium: 'ebook' as iTunesMedium, entity: 'ebook' as iTunesEntity },
albums: { medium: 'music' as iTunesMedium, entity: 'album' as iTunesEntity },
podcasts: { medium: 'podcast' as iTunesMedium, entity: 'podcast' as iTunesEntity },
}[category]
const params = (
{
books: { medium: 'ebook', entity: 'ebook' },
albums: { medium: 'music', entity: 'album' },
podcasts: { medium: 'podcast', entity: 'podcast' },
} satisfies Record<MediaCategory, { medium: iTunesMedium; entity: iTunesEntity }>
)[category]

Copilot uses AI. Check for mistakes.
@claude
Copy link

claude bot commented Dec 27, 2025

Pull Request Review

This is an excellent refactoring that significantly improves the codebase's organization and maintainability. The changes demonstrate thoughtful architectural decisions and follow best practices throughout.

✅ Strengths

1. Excellent Code Organization

  • Three-tier UI structure (elements/, layout/, sections/) provides clear component hierarchy and makes it immediately obvious where new components belong
  • Consolidated SEO folder brings together related metadata, JSON-LD, and OG image generation - much better than scattered across utils/ and app/
  • Route groups (app/(home)/) reduce root clutter while maintaining Next.js conventions
  • Dead code removal (unused TMDB constants, Notion helpers, single-use config abstractions) reduces cognitive overhead

2. Strong Type Safety

The codebase demonstrates excellent TypeScript practices throughout:

  • seo/generate-og-image.tsx:43-106 - Properly uses JSX syntax with satori instead of any type assertions
  • io/itunes/fetchItunesItems.ts and io/tmdb/fetchTmdbList.ts - Comprehensive Zod validation at API boundaries
  • New CLAUDE.md guidelines explicitly enforce "never use any" rule with clear examples
  • All moved files maintain proper types and imports

3. Comprehensive Test Coverage

  • Test files moved alongside source files maintain full coverage
  • New test files added for JSON-LD modules (article.test.ts, blog.test.ts, person.test.ts, script.test.tsx)
  • Tests properly mock dependencies and verify behavior
  • Example: app/[slug]/page.test.tsx:15-26 properly mocks PageLayout while preserving semantic HTML structure

4. Clean Architecture Patterns

  • ui/layout/page-layout.tsx:15-38 - DRY component that handles skip links, header/footer, and responsive width constraints
  • seo/json-ld/script.tsx:20-43 - Smart component with type-safe discriminated union and proper XSS escaping
  • app/[slug]/page.tsx:80-104 - Simplified blog post page using PageLayout wrapper
  • Separation of concerns: JSON-LD generation logic separated from rendering

5. Performance & Caching Improvements

  • io/itunes/fetchItunesItems.ts:73-82 - Smart caching with hashed keys to avoid ENAMETOOLONG errors
  • io/tmdb/fetchTmdbList.ts:67-74 - Consistent caching pattern
  • Both use retry logic for API resilience (withRetry helper)
  • Proper cache invalidation strategies

6. Accessibility Maintained

  • Skip links preserved in PageLayout (page-layout.tsx:18-26)
  • Semantic HTML structure maintained
  • Screen reader text appropriately used (sr-only classes)

🔍 Minor Observations

1. Potential TODO to Address

// app/[slug]/page.tsx:39-42
if (!post) {
  // TODO: confirm if this is the right behaviour; what pages would this apply to?
  return {}
}

This TODO should be resolved. If a post doesn't exist, notFound() should be called (as done in line 85), making this check potentially redundant. Consider removing or clarifying this path.

2. API Parameter Changes

io/itunes/fetchItunesItems.ts:55-59 changed the function signature from (items, medium, entity) to (items, category, options). This is a breaking change but appears intentional and well-tested. Good refactoring that simplifies the API.

3. Test Mocking Patterns

The test files extensively mock PageLayout:

vi.mock('@/ui/layout/page-layout', () => ({
  default: ({ children }: { children: React.ReactNode }) => (
    <main id="main" className="flex-auto flex flex-col">
      {children}
    </main>
  ),
}))

This is duplicated across multiple test files. Consider extracting to a shared test utility to reduce duplication.

4. Cache Adapter Injection

Both fetchItunesItems and fetchTmdbList now accept optional cache adapters - excellent for testability! This enables proper unit testing without hitting the filesystem.

🎯 Best Practices Followed

  1. No any types - Adheres to the new CLAUDE.md rules
  2. Result types - Consistent error handling with Result<T, Error> pattern
  3. Validation at boundaries - Zod schemas validate external API data
  4. Proper imports - All path aliases (@/) maintained correctly
  5. Semantic versioning - Breaking changes in internal APIs are acceptable for a personal site
  6. Documentation - JSDoc comments added for public functions

🛡️ Security

  • XSS Prevention: seo/json-ld/script.tsx:39 properly escapes < characters in JSON-LD to prevent XSS
  • No SQL Injection: No database queries in changed files
  • API Keys: Properly accessed via env module, not hardcoded
  • Input Validation: Zod schemas validate all external data at boundaries

📊 Impact Summary

  • 93 files changed with +1,661/-1,025 lines (net reduction of complexity)
  • No breaking changes to public API (this appears to be a personal site)
  • Test coverage maintained across all refactored modules
  • Zero regressions expected based on comprehensive test updates

🎉 Verdict

Strongly Approve

This PR represents exemplary refactoring work:

  • Clear architectural vision (three-tier UI, consolidated SEO)
  • Strong type safety without any escapes
  • Comprehensive test coverage
  • Removes dead code and unnecessary abstractions
  • Improves maintainability without sacrificing functionality

The only action item is resolving the TODO in app/[slug]/page.tsx:39-42, but that's minor and doesn't block merging.

Great work! This will make the codebase much easier to navigate and maintain going forward. 🚀

@ooloth ooloth merged commit b98d43c into main Dec 27, 2025
8 of 9 checks passed
@ooloth ooloth deleted the reorganize-folders branch December 27, 2025 02:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant