Skip to content

Commit

Permalink
Revert and fix
Browse files Browse the repository at this point in the history
  • Loading branch information
ProchaLu committed Jan 8, 2025
1 parent 3027a41 commit 7d2e907
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 96 deletions.
157 changes: 66 additions & 91 deletions app/[contentType]/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,126 +1,101 @@
import fs from 'node:fs';
import path from 'node:path';
import matter from 'gray-matter';
import type { Metadata } from 'next';
import Image from 'next/image';
import { notFound } from 'next/navigation';
import rehypeHighlight from 'rehype-highlight';
import remarkGfm from 'remark-gfm';
import remarkHtml from 'remark-html';
import remarkParse from 'remark-parse';
import { unified } from 'unified';
import { Chips, Container } from '../../../components';
import BackButton from '../../../components/back-button';
import info from '../../../config/index.json' assert { type: 'json' };
import type { IContentData, IContentType } from '../../../utils/content';
import {
getContentData,
getContentList,
getContentTypes,
type IContentData,
type IContentType,
} from '../../../utils/content';
import { contentTypesMap } from '../../../utils/content-types';
import Content from './content';

type Params = {
params: Promise<{ slug: string; contentType: IContentType }>;
};

const contentTypes = ['articles', 'notes', 'works'];

function getMarkdownContent(contentType: IContentType, slug: string) {
if (!contentTypes.includes(contentType)) {
notFound();
}

const filePath = path.join(
process.cwd(),
'content',
contentType,
`${slug}.md`,
export async function generateMetadata({
params,
}: {
params: Params;
}): Promise<Metadata> {
const { title, previewImage, description } = await getContentData(
(await params).slug,
(await params).contentType,
);
if (!fs.existsSync(filePath)) {
notFound();
}

const fileContent = fs.readFileSync(filePath, 'utf-8');
const { data, content } = matter(fileContent);

return { data, content };
}

export async function generateMetadata({ params }: Params): Promise<Metadata> {
const { slug, contentType } = await params;
if (!contentTypes.includes(contentType)) {
notFound();
}

const filePath = path.join(
path.join(process.cwd(), 'content'),
contentType,
`${slug}.md`,
);
if (!fs.existsSync(filePath)) {
notFound();
}

const { data } = getMarkdownContent(contentType, slug);

return {
title: `${data.title} | ${info.site.siteTitle}`,
description: data.description ?? info.site.siteDescription,
title: `${title} | ${info.site.siteTitle}`,
description: description ?? info.site.siteDescription,
openGraph: {
title: `${data.title} | ${info.site.siteName}`,
description: data.description ?? info.site.siteDescription,
title: `${title} | ${info.site.siteName}`,
description: description ?? info.site.siteDescription,
url: info.site.siteUrl,
images: data.previewImage ?? info.site.siteImage,
images: previewImage ?? info.site.siteImage,
siteName: info.site.siteName,
},
twitter: {
card: 'summary_large_image',
creator: info.author.twitterHandle,
images: data.previewImage ?? info.site.siteImage,
images: previewImage ?? info.site.siteImage,
},
};
}

export default async function ContentPage({ params }: Params) {
const { slug, contentType } = await params;
/**
* statically generate all content pages
*/
export function generateStaticParams() {
const contentTypes = getContentTypes();

return contentTypes.flatMap((contentType) => {
const contentList = getContentList(contentType);
return contentList.map(({ slug }) => {
return {
contentType,
slug,
};
});
});
}

const { data, content } = getMarkdownContent(contentType, slug);
/**
* Renders articles markdown posts
*/

if (data.draft) {
notFound();
}
async function fetchContentData(slug: string, contentType: IContentType) {
return await getContentData(slug, contentType);
}

const processedContent = await unified()
.use(remarkParse)
.use(remarkHtml, { sanitize: false })
.use(remarkGfm)
.use(rehypeHighlight)
.process(content);

const contentHtml = processedContent.toString();

if (contentType === 'works') {
return (
<WorkPage
work={{
...data,
contentHtml,
id: data.id,
date: data.date,
title: data.title,
}}
/>
);
type Params = {
slug: string;
contentType: IContentType;
};

export default async function ContentPage({ params }: { params: Params }) {
const { slug, contentType } = params;

if (!contentTypesMap.has(contentType)) {
return notFound();
}

const content = await fetchContentData(slug, contentType);
if (content.draft) return notFound();

if (contentType === 'works') return <WorkPage work={content} />;

return (
<Container width="narrow">
<header>
<section className="pt-16">
<h1 className="my-0 font-bold font-display mb-2 text-2xl/normal md:text-4xl max-w-xl">
{data.title}
{content.title}
</h1>
<time className="block text-accent-4 mb-8">{data.date}</time>
{!!data.previewImage && (
<time className="block text-accent-4 mb-8">{content.date}</time>
{content.previewImage && (
<Image
className="pb-8 block object-cover"
src={data.previewImage}
src={content.previewImage}
height={550}
width={1200}
alt=""
Expand All @@ -129,8 +104,8 @@ export default async function ContentPage({ params }: Params) {
</section>
</header>

<Content html={contentHtml} />
{data.tags && <Chips items={data.tags} />}
<Content html={content.contentHtml} />
{content.tags && <Chips items={content.tags} />}
</Container>
);
}
Expand All @@ -152,7 +127,7 @@ function WorkPage({ work }: { work: IContentData }) {
<TechStack techStack={work.techStack ?? []} />
<MetadataListItem item="Date" value={work.date.toString()} />
{work.problem && (
<MetadataListItem item="Problem" value={work.problem} />
<MetadataListItem item="Problem" value={work.problem ?? ''} />
)}
</ul>
</section>
Expand Down
3 changes: 1 addition & 2 deletions components/cards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ const Cards = ({ items, basePath }: ICard) => {
key={`singleCard-${singleCard.slug}`}
>
<Link
href={`/${basePath}/[id]`}
as={`/${basePath}/${singleCard.slug}`}
href={`/${basePath}/${singleCard.slug}`}
className="no-underline"
>
<div className="overflow-hidden max-h-40 mb-2">
Expand Down
3 changes: 1 addition & 2 deletions components/notes/note.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import type { IContent } from '../../utils/content';
export function Note({ date, title, slug, basePath }: IContent) {
return (
<Link
href={`/${basePath}/[id]`}
as={`/${basePath}/${slug}`}
href={`/${basePath}/${slug}`}
className="cursor-pointer no-underline hover:underline"
>
<article className="flex flex-col mb-4 sm:mb-0 sm:flex-row sm:items-center">
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
77 changes: 76 additions & 1 deletion utils/content.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import fs from 'node:fs';
import path from 'node:path';
import matter from 'gray-matter';
import rehypeHighlight from 'rehype-highlight';
import gfm from 'remark-gfm';
import html from 'remark-html';
import remarkParse from 'remark-parse';
import { unified } from 'unified';
import { v4 as uuid } from 'uuid';
import { contentTypesMap } from './content-types';

const workDirectory = path.join(process.cwd(), 'content', 'works');
const workDirectory = path.join(process.cwd(), 'content', 'work');
const notesDirectory = path.join(process.cwd(), 'content', 'notes');
const articlesDirectory = path.join(process.cwd(), 'content', 'articles');

Expand Down Expand Up @@ -103,6 +108,76 @@ export const getAllContentIds = (contentType: IContentType) => {
});
};

/**
* Get data for a given post id
* @param {string} id ID of the post being passed
* @param {string} contentType Type of content
*/

export const getContentData = async (id: string, contentType: IContentType) => {
let contentTypeDirectory;
let filenames;
switch (contentType.toLowerCase()) {
case 'articles':
filenames = fs.readdirSync(articlesDirectory);
contentTypeDirectory = articlesDirectory;
break;

case 'notes':
filenames = fs.readdirSync(notesDirectory);
contentTypeDirectory = notesDirectory;
break;

case 'works':
filenames = fs.readdirSync(workDirectory);
contentTypeDirectory = workDirectory;
break;

default:
throw new Error('You have to provide a content type');
}

// loop through all the content types and compare the slug to get the filename
const match = filenames.filter((filename) => {
const filePath = path.join(contentTypeDirectory, filename);

const fileContent = fs.readFileSync(filePath, 'utf-8');
const matterResult = matter(fileContent);
const { slug } = matterResult.data;

return slug === id;
});

// use the returned path to get the fullpath and read the file content
const fullPath = path.join(contentTypeDirectory, match[0]!);
// const fullPath = path.join(contentTypeDirectory, `${id}.md`);
const fileContents = fs.readFileSync(fullPath, 'utf-8');

const matterResult = matter(fileContents);
const processedContent = await unified()
.use(remarkParse)
.use(html, { sanitize: false })
.use(gfm)
.use(rehypeHighlight)
.process(matterResult.content);

const contentHtml = processedContent.toString();

return {
id,
contentHtml,
title: matterResult.data.title,
draft: matterResult.data.draft || false,
date: matterResult.data.date,
previewImage: matterResult.data.previewImage || '',
description: matterResult.data.description || '',
tags: matterResult.data.tags || [],
category: matterResult.data.category || '',
problem: matterResult.data.problem || '',
techStack: matterResult.data.techStack || [],
};
};

/**
* Get content list for a particular content type
* @param {string} contentType Type of content
Expand Down

0 comments on commit 7d2e907

Please sign in to comment.