From ae3ecfec2f6da81bc469a6bd08a44170ae17006b Mon Sep 17 00:00:00 2001 From: James Walsh Date: Fri, 4 Jul 2025 11:37:59 -0600 Subject: [PATCH 1/3] refactor: fetch previous post slug only --- app/posts/[slug]/actions.test.ts | 11 +++++------ app/posts/[slug]/actions.ts | 6 ++++-- app/posts/[slug]/page.tsx | 9 ++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/posts/[slug]/actions.test.ts b/app/posts/[slug]/actions.test.ts index c710a65..673c7db 100644 --- a/app/posts/[slug]/actions.test.ts +++ b/app/posts/[slug]/actions.test.ts @@ -1,6 +1,6 @@ import * as postsActions from '../actions' -import { fetchPostBySlug, fetchPreviousPost } from './actions' +import { fetchPostBySlug, fetchPreviousPostSlug } from './actions' import * as mdx from '@/lib/mdx' import type { Post } from '@/lib/types' @@ -57,7 +57,7 @@ describe('/posts/[slug]/actions', () => { }) }) - describe('fetchPreviousPost', () => { + describe('fetchPreviousPostBySlug', () => { const mockPosts: Post[] = [ { slug: 'slug-1', ...getMockFrontmatter(), source: getMockSource() }, { slug: 'slug-2', ...getMockFrontmatter(), source: getMockSource() }, @@ -66,15 +66,14 @@ describe('/posts/[slug]/actions', () => { it('returns the next index of results returned from fetchPublishedPosts()', async () => { vi.mocked(postsActions.fetchPublishedPosts).mockResolvedValue(mockPosts) - const actual = await fetchPreviousPost('slug-1') - expect(actual).toBeTruthy() - expect(actual?.slug).toEqual('slug-2') + const actual = await fetchPreviousPostSlug('slug-1') + expect(actual).toEqual('slug-2') }) it('returns undefined when the bottom of the list is reached', async () => { vi.mocked(postsActions.fetchPublishedPosts).mockResolvedValue(mockPosts) - const actual = await fetchPreviousPost('slug-2') + const actual = await fetchPreviousPostSlug('slug-2') expect(actual).toBeUndefined() }) }) diff --git a/app/posts/[slug]/actions.ts b/app/posts/[slug]/actions.ts index 50da01b..b410364 100644 --- a/app/posts/[slug]/actions.ts +++ b/app/posts/[slug]/actions.ts @@ -23,11 +23,13 @@ export async function fetchPostBySlug(slug: string): Promise { } } -export async function fetchPreviousPost(slug: string): Promise { +export async function fetchPreviousPostSlug(slug: string): Promise { const publishedPosts = await fetchPublishedPosts() const postIndex = publishedPosts.findIndex((post) => post.slug === slug) if (postIndex === publishedPosts.length - 1) return undefined - return publishedPosts[postIndex + 1] + const previousPost = publishedPosts.at(postIndex + 1) + + return previousPost && previousPost.slug } diff --git a/app/posts/[slug]/page.tsx b/app/posts/[slug]/page.tsx index 1d9482c..2c435fd 100644 --- a/app/posts/[slug]/page.tsx +++ b/app/posts/[slug]/page.tsx @@ -5,7 +5,7 @@ import Link from 'next/link' import { fetchPublishedPosts } from '../actions' -import { fetchPostBySlug, fetchPreviousPost } from './actions' +import { fetchPostBySlug, fetchPreviousPostSlug } from './actions' import MDXContent from './mdx-content' import TimeInformation from './time-information' @@ -49,8 +49,7 @@ export const generateMetadata = async ({ params }: { params: Promise<{ slug: str export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params - const post = await fetchPostBySlug(slug) - const previousPost = await fetchPreviousPost(slug) + const [post, previousPostSlug] = await Promise.all([fetchPostBySlug(slug), fetchPreviousPostSlug(slug)]) return (
@@ -74,9 +73,9 @@ export default async function PostPage({ params }: { params: Promise<{ slug: str  All posts - {!!previousPost && ( + {previousPostSlug && ( From 213d7d65b6b014956675d8dabae6f081135c9f0f Mon Sep 17 00:00:00 2001 From: James Walsh Date: Fri, 4 Jul 2025 11:42:38 -0600 Subject: [PATCH 2/3] chore: config non-dynamic params in slug page --- app/posts/[slug]/page.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/posts/[slug]/page.tsx b/app/posts/[slug]/page.tsx index 2c435fd..9e08f84 100644 --- a/app/posts/[slug]/page.tsx +++ b/app/posts/[slug]/page.tsx @@ -24,6 +24,8 @@ export async function generateStaticParams() { })) } +export const dynamicParams = false + export const generateMetadata = async ({ params }: { params: Promise<{ slug: string }> }): Promise => { const { slug } = await params const post = await fetchPostBySlug(slug) From fee4dcdec7e733277818b472c6342786b817eed9 Mon Sep 17 00:00:00 2001 From: James Walsh Date: Fri, 4 Jul 2025 21:22:45 -0600 Subject: [PATCH 3/3] refactor: eject from next-mdx-remote --- app/posts/[slug]/actions.test.ts | 26 +--- app/posts/[slug]/actions.ts | 13 -- app/posts/[slug]/mdx-content.tsx | 68 --------- app/posts/[slug]/page.tsx | 26 ++-- mdx-components.tsx | 39 +++++ next.config.mjs | 24 --- next.config.ts | 59 ++++++++ package.json | 15 +- pnpm-lock.yaml | 241 +++++++++++++++++++++++-------- tailwind.config.ts | 2 +- 10 files changed, 304 insertions(+), 209 deletions(-) delete mode 100644 app/posts/[slug]/mdx-content.tsx create mode 100644 mdx-components.tsx delete mode 100644 next.config.mjs create mode 100644 next.config.ts diff --git a/app/posts/[slug]/actions.test.ts b/app/posts/[slug]/actions.test.ts index 673c7db..d61bf90 100644 --- a/app/posts/[slug]/actions.test.ts +++ b/app/posts/[slug]/actions.test.ts @@ -1,11 +1,8 @@ -import * as postsActions from '../actions' - -import { fetchPostBySlug, fetchPreviousPostSlug } from './actions' +import { fetchPostBySlug } from './actions' import * as mdx from '@/lib/mdx' import type { Post } from '@/lib/types' import { getMockFrontmatter } from '@/test/mocks/frontmatter' -import { getMockSource } from '@/test/mocks/source' vi.mock('@/lib/mdx') vi.mock('../actions') @@ -56,25 +53,4 @@ describe('/posts/[slug]/actions', () => { }) }) }) - - describe('fetchPreviousPostBySlug', () => { - const mockPosts: Post[] = [ - { slug: 'slug-1', ...getMockFrontmatter(), source: getMockSource() }, - { slug: 'slug-2', ...getMockFrontmatter(), source: getMockSource() }, - ] - - it('returns the next index of results returned from fetchPublishedPosts()', async () => { - vi.mocked(postsActions.fetchPublishedPosts).mockResolvedValue(mockPosts) - - const actual = await fetchPreviousPostSlug('slug-1') - expect(actual).toEqual('slug-2') - }) - - it('returns undefined when the bottom of the list is reached', async () => { - vi.mocked(postsActions.fetchPublishedPosts).mockResolvedValue(mockPosts) - - const actual = await fetchPreviousPostSlug('slug-2') - expect(actual).toBeUndefined() - }) - }) }) diff --git a/app/posts/[slug]/actions.ts b/app/posts/[slug]/actions.ts index b410364..09fa2d8 100644 --- a/app/posts/[slug]/actions.ts +++ b/app/posts/[slug]/actions.ts @@ -4,8 +4,6 @@ import path from 'path' import { notFound } from 'next/navigation' -import { fetchPublishedPosts } from '../actions' - import { getPostFromMDX } from '@/lib/mdx' import type { Post } from '@/lib/types' @@ -22,14 +20,3 @@ export async function fetchPostBySlug(slug: string): Promise { } } } - -export async function fetchPreviousPostSlug(slug: string): Promise { - const publishedPosts = await fetchPublishedPosts() - const postIndex = publishedPosts.findIndex((post) => post.slug === slug) - - if (postIndex === publishedPosts.length - 1) return undefined - - const previousPost = publishedPosts.at(postIndex + 1) - - return previousPost && previousPost.slug -} diff --git a/app/posts/[slug]/mdx-content.tsx b/app/posts/[slug]/mdx-content.tsx deleted file mode 100644 index 35c47a1..0000000 --- a/app/posts/[slug]/mdx-content.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import Image, { ImageProps } from 'next/image' -import { MDXRemote, type MDXRemoteProps } from 'next-mdx-remote/rsc' -import { HTMLAttributes, PropsWithChildren } from 'react' -import rehypeAutolinkHeadings from 'rehype-autolink-headings' -import rehypePrettyCode from 'rehype-pretty-code' -import rehypeSlug from 'rehype-slug' -import remarkGfm from 'remark-gfm' -import { HighlighterCoreOptions, getSingletonHighlighter } from 'shiki' - -import { TypographyBlockquote, TypographyH1, TypographyH2, TypographyH3, TypographyP } from '@/components/ui/typography' -import { cn } from '@/lib/utils' - -const components: MDXRemoteProps['components'] = { - img: (props: HTMLAttributes) => ( - article image - ), - h1: (props: PropsWithChildren) => , - h2: (props: PropsWithChildren) => , - h3: (props: PropsWithChildren) => , - p: (props: PropsWithChildren) => , - ol: (props: PropsWithChildren) =>
    , - ul: (props: PropsWithChildren) =>
) diff --git a/mdx-components.tsx b/mdx-components.tsx new file mode 100644 index 0000000..2d6ff49 --- /dev/null +++ b/mdx-components.tsx @@ -0,0 +1,39 @@ +import type { MDXComponents } from 'mdx/types' +import Image, { type ImageProps } from 'next/image' +import type { HTMLAttributes, PropsWithChildren } from 'react' + +export function useMDXComponents(components: MDXComponents): MDXComponents { + return { + ...{ + img: (props: HTMLAttributes) => ( + article image + ), + h1: (props: PropsWithChildren) =>

, + h2: (props: PropsWithChildren) =>

, + h3: (props: PropsWithChildren) =>

, + p: (props: PropsWithChildren) =>

, + ol: (props: PropsWithChildren) =>
    , + ul: (props: PropsWithChildren) =>