Fetching Dynamic Routes from .md files

23 Views Asked by At

I'm writing adding a blog to my website and I'm strugging with App Router/React Server Component restrictions.

Currently my initial /blog/page.tsx displays this

import path from 'path';
import matter from 'gray-matter';
import BlogLayout, { BlogTitle, BlogDate, BlogContent } from './layout/BlogLayout';
import { unified } from 'unified';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import rehypeDocument from 'rehype-document';
import rehypeFormat from 'rehype-format';
import remarkParse from 'remark-parse';
import Link from 'next/link';

interface PreviousPost {
  title: string;
  date: string;
  slug: string;
}

interface Post {
  title: string;
  date: string;
  content: string;
}

async function getPreviousPosts(
  directoryPath: string
): Promise<PreviousPost[]> {
  const files = await fs.readdir(directoryPath);

  const markdownFiles = files.filter(
    file => file.endsWith('.md') && file !== '.DS_Store'
  );
  const sortedFiles = markdownFiles.sort((a, b) => b.localeCompare(a)).slice(1);

  const previousPosts = await Promise.all(
    sortedFiles.map(async filename => {
      const filePath = path.join(directoryPath, filename);
      const fileContents = await fs.readFile(filePath, 'utf8');
      const { data } = matter(fileContents);

      const slug = filename.replace(/\.md$/, '');

      return {
        title: data.title as string,
        date: data.date as string,
        slug,
      };
    })
  );

  return previousPosts;
}

async function getMostRecentMarkdownFile(
  directoryPath: string
): Promise<string> {
  const files = await fs.readdir(directoryPath);
  // Assuming files are named in a way that allows lexical sorting, e.g., YYYY-MM-DD-title.md
  const sortedFiles = files.sort((a, b) => b.localeCompare(a));
  return path.join(directoryPath, sortedFiles[0]);
}

async function readMarkdownFile(filePath: string): Promise<Post> {
  const fileContents = await fs.readFile(filePath, 'utf8');
  const { data, content } = matter(fileContents);

  const processedContent = await unified()
    .use(remarkParse)
    .use(remarkRehype, { allowDangerousHtml: true })
    .use(rehypeDocument)
    .use(rehypeFormat)
    .use(rehypeStringify)
    .process(content);

  return {
    title: data.title,
    date: data.date,
    content: processedContent.toString(),
  };
}

export default async function BlogPost() {
  const postsDirectory = path.join(process.cwd(), 'src/app/blog/_posts');
  const mostRecentFilePath = await getMostRecentMarkdownFile(postsDirectory);
  const mostRecentPost: Post = await readMarkdownFile(mostRecentFilePath);
  const previousPosts = await getPreviousPosts(postsDirectory);

  return (
    <div>
      <BlogLayout>
        <div>
          <div>
            <div>
              <BlogTitle>{mostRecentPost.title}</BlogTitle>
              <BlogDate>{mostRecentPost.date}</BlogDate>
              <BlogContent htmlContent={mostRecentPost.content} />
            </div>
          </div>
          <div>
          <h3>
            Previous Posts
          </h3>
          <ul>
            {previousPosts.map((post) => (
              <li key={post.slug}>
                <Link href={`/blog/${post.slug}`}>
                  <div>{post.title}</div>
                  <div>{post.date}</div>
                </Link>
              </li>
            ))}
          </ul>
        </div>
        </div>
      </BlogLayout>
    </div>
  );
}

as you can see it does a couple of things, it getMostRecentMarkdownFile and uses that to display the current content. I then also getPreviousPost and use that to map a list of links <Link href={`/blog/{$post.slug}`} /> which are then opened in /blog/[slug]/page.tsx.

I want this new page to be an identical copy, but instead of getMostRecentMarkdownFile I want to display whichever file was used to create the slug from the link.

I'm bashing my head against a wall because I can't use getStaticProps or anything like that since I'm using the App Router.

I would absolutely appreciate some guidance with this.

Note: Forgive all of the divs, I removed tailwind classes for cleanliness.

I don't even know where to being, I've been trying everything I can think of for 3 days. I've even tried throwing it at GPT, but its no help as its not well versed in App Router/RSC - it even tries telling me to use React.FC.

0

There are 0 best solutions below