dwjohnston / dwjohnston/blacksheepcode

Migrate blog posts from MDX to TSX

Open
#269 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
MDX
Stars
3
Forks
1
Avg merge
6m
Merged PRs (30d)
2

Description

⚠️ DRAFT ISSUE - STILL IN PROGRESS

### Current process (context)

Blog posts are written as `.mdx` files. The build pipeline transforms them before Next.js compiles the app.

**Source files:**
- `src/routes/posts/*.mdx` — published blog posts
- `src/routes/drafts/*.mdx` — draft posts
- `src/routes/test/*.mdx` — test posts (used by Cypress)

**Build scripts (`utils/`):**
- `utils/transformMdx.ts` / `utils/transformMdx.bin.ts` — compiles MDX → `.mjs` using `@mdx-js/mdx` (converts markdown syntax to JSX, syntax highlighting via `rehype-highlight`, heading slugs via `rehype-slug`)
- `utils/extractFrontMatter.ts` / `utils/extractFrontMatter.bin.ts` — parses YAML frontmatter from `.mdx` files, validates against Zod schema, writes JSON files and barrel `index.js` per folder
- `utils/frontmatterTypings.ts` — Zod schema (`frontMatterSchema`) and inferred types (`FrontMatter`, `FrontMatterPlusSlug`, `EnrichedFrontMatterPlusSlug`)
- `utils/generateImageBarrelFiles.ts` — scans `src/assets/` and generates image barrel with dimensions
- `utils/generateRss.ts` — generates `public/rss.xml` from frontmatter
- `utils/generateSitemap.ts` — generates `public/sitemap.xml`

**Generated files (all in `.gitignore`, recreated on each build):**
- `src/generated/mdx/posts/*.mjs` — compiled MDX as JavaScript modules
- `src/generated/mdx/drafts/*.mjs`
- `src/generated/mdx/test/*.mjs`
- `src/generated/frontmatter/posts/*.json` + `index.js` — extracted frontmatter per post
- `src/generated/frontmatter/drafts/*.json` + `index.js`
- `src/generated/frontmatter/test/*.json` + `index.js`
- `src/generated/tags.json` — maps tag names to post slugs
- `src/generated/images.js` — image barrel with dimensions

**Runtime / routing:**
- `src/utils/blogPosts.tsx` — `getAllPostFrontmatter()`, `getFrontmatterFromSlug()`, `getBlogContent()`, `getMetadata()`
- `src/app/posts/[slug]/page.tsx` — dynamic route; calls `getBlogContent()` to dynamically import the `.mjs` from `src/generated/mdx/posts/`
- `src/app/drafts/[slug]/page.tsx` — same pattern for drafts
- `src/app/test/[slug]/page.tsx` — same pattern for test posts
- `src/app/posts/page.tsx` — listing page with tag filtering; calls `getAllPostFrontmatter()`
- `src/components/BlogPostFrame/` — wraps rendered post content
- `src/components/FrontmatterBox/` — renders series box, title, date, next-in-series link

### Problem
Writing blog posts in MDX lacks TypeScript tooling support:
- No IDE autocomplete for component props
- No type checking in the editor
- Frontmatter YAML has no type enforcement

### Solution
Write blog posts as plain `.tsx` files with two exports:
1. A default React component export (the post content)
2. A named typed `metadata` export (replacing YAML frontmatter)

### Proposed post format

```tsx
import { defineMetadata } from "@/utils/blog";

export const metadata = defineMetadata({
meta: {
title: "Post Title",
description: "Description.",
dateCreated: "2026-01-01",
// image: "optional_image_key",
},
// series: { name: "my_series", part: 1, description: "My Series" },
tags: ["react"],
});

export default function MyPost() {
return (
<>

Post content here...



);
}
```

`defineMetadata` is a simple identity function providing type inference:
```ts
export function defineMetadata(m: PostMetadata): PostMetadata { return m; }
```

`PostMetadata` should be defined based on the existing `FrontMatter` type in `utils/frontmatterTypings.ts`. `defineMetadata` should live in a new `src/utils/blog.ts` (or alongside the type).

### Migration of existing MDX posts

1. Run `generate:all` — this compiles all `.mdx` files to `.mjs` in `src/generated/mdx/posts/` (markdown already converted to JSX by `@mdx-js/mdx`)
2. Remove `src/generated/mdx/` from `.gitignore` and commit those files
3. Convert the `frontmatter` export in each `.mjs` to a typed `metadata` export matching `PostMetadata`
4. Rename files from `.mjs` to `.tsx`
5. Delete the source `.mdx` files

The existing `src/app/posts/[slug]/page.tsx` wildcard handler already reads from `src/generated/mdx/posts/` via `getBlogContent()`, so routing requires no changes.

### Build pipeline changes

- `generate:mdx` step can be removed once all `.mdx` files are gone
- `extractFrontMatter.ts` currently reads YAML from `.mdx` files to produce `src/generated/frontmatter/posts/*.json`. This must be adapted to instead dynamically import each `.tsx` file and read its `metadata` export, writing the same JSON format. The rest of the pipeline (tags, RSS, sitemap, `getAllPostFrontmatter()`) stays unchanged.
- `generate:mdx` script and `transformMdx.ts` can be deleted once migration is complete

### Files to change
- `utils/extractFrontMatter.ts` — adapt to handle `.tsx` metadata exports
- `utils/frontmatterTypings.ts` — add/export `PostMetadata` type (or create `src/types/blog.ts`)
- `src/generated/mdx/posts/*.mjs` — rename to `.tsx`, update metadata export
- `.gitignore` — remove `src/generated/mdx/`
- `package.json` — remove `generate:mdx` script once done
- `utils/transformMdx.ts` + `utils/transformMdx.bin.ts` — delete once done

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.