Skip to content

Commit f7cba93

Browse files
Merge pull request #1192 from IgorKowalczyk/remove-contentlayer
Remove contentlayer
2 parents 152d40f + df75bc3 commit f7cba93

File tree

17 files changed

+522
-1791
lines changed

17 files changed

+522
-1791
lines changed

app/blog/[slug]/opengraph-image.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { readFile } from "node:fs/promises";
22
import { join } from "node:path";
3-
import { allBlogs } from "contentlayer/generated";
43
import { redirect } from "next/navigation";
54
import { ImageResponse } from "next/og";
65
import { header, meta } from "@/config/metadata";
6+
import { getBlogPosts } from "@/lib/blogUtils";
77
import { parseISO } from "@/lib/utils";
88

99
export const contentType = "image/png";
@@ -19,7 +19,7 @@ interface Params {
1919

2020
export default async function Image({ params }: { params: Params }) {
2121
const { slug } = params;
22-
const post = allBlogs.find((post) => post.slug === slug);
22+
const post = getBlogPosts().find((post) => post.slug === slug);
2323

2424
if (!post) return redirect("/opengraph-image");
2525

@@ -54,7 +54,7 @@ export default async function Image({ params }: { params: Params }) {
5454
bottom: 0,
5555
filter: "hue-rotate(540deg) saturate(5)",
5656
position: "absolute",
57-
zIndex: 1,
57+
zIndex: "1",
5858
}}
5959
/>
6060
<div
@@ -65,7 +65,7 @@ export default async function Image({ params }: { params: Params }) {
6565
marginBottom: "15px",
6666
}}
6767
>
68-
{parseISO(post.publishedAt)}
68+
{parseISO(post.metadata.publishedAt)}
6969
</div>
7070
<div
7171
style={{
@@ -74,7 +74,7 @@ export default async function Image({ params }: { params: Params }) {
7474
fontSize: 64,
7575
}}
7676
>
77-
{post.title}
77+
{post.metadata.title}
7878
</div>
7979
<div
8080
style={{

app/blog/[slug]/page.tsx

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,36 @@
1-
import { allBlogs } from "contentlayer/generated";
21
import type { Metadata } from "next";
32
import Image from "next/image";
43
import { notFound } from "next/navigation";
54
import { MDXComponent } from "@/components/MDXComponents";
65
import { Header1 } from "@/components/ui/Headers";
76
import Link from "@/components/ui/Link";
87
import { meta } from "@/config/metadata";
8+
import { getBlogPosts } from "@/lib/blogUtils";
99
import { cn } from "@/lib/utils";
1010
import { parseISO } from "@/lib/utils";
1111
import Avatar from "@/public/assets/avatar.png";
1212

1313
export function generateStaticParams() {
14-
return allBlogs.map((post) => ({
14+
const posts = getBlogPosts();
15+
16+
return posts.map((post) => ({
1517
slug: post.slug,
1618
}));
1719
}
1820

21+
export const dynamicParams = false;
22+
1923
type Props = {
2024
params: Promise<{ slug: string }>;
2125
};
2226

23-
export async function generateMetadata({ params }: Props): Promise<Metadata | undefined> {
24-
const metadataParams = await params;
25-
const post = allBlogs.find((post) => post?.slug === metadataParams?.slug);
27+
export async function generateMetadata({ params }: Props): Promise<Metadata> {
28+
const { slug } = await params;
29+
const post = getBlogPosts().find((post) => post.slug === slug);
2630

2731
if (!post) return {};
2832

29-
const { title, publishedAt: publishedTime, summary: description, slug } = post;
33+
const { title, publishedAt: publishedTime, summary: description } = post.metadata;
3034

3135
return {
3236
title,
@@ -36,7 +40,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata | un
3640
description,
3741
type: "article",
3842
publishedTime,
39-
url: `${meta.url}/blog/${slug}`,
43+
url: `${meta.url}/blog/${post.slug}`,
4044
},
4145
twitter: {
4246
card: "summary_large_image",
@@ -47,56 +51,71 @@ export async function generateMetadata({ params }: Props): Promise<Metadata | un
4751
}
4852

4953
export default async function Blog({ params }: Props) {
50-
const blogParams = await params;
51-
const post = allBlogs.find((post) => post.slug === blogParams.slug);
54+
const { slug } = await params;
55+
const post = getBlogPosts().find((post) => post.slug === slug);
5256

53-
if (!post) return notFound();
57+
if (!post) notFound();
5458

5559
return (
5660
<article className="mt-6 mb-16 flex min-h-screen flex-col items-start justify-center md:mt-12 lg:mt-20">
5761
<script
5862
type="application/ld+json"
59-
// biome-ignore lint/security/noDangerouslySetInnerHtml: We trust the content of the JSON object
63+
suppressHydrationWarning
6064
dangerouslySetInnerHTML={{
61-
__html: JSON.stringify(post.structuredData),
65+
__html: JSON.stringify({
66+
"@context": "https://schema.org",
67+
"@type": "BlogPosting",
68+
headline: post.metadata.title,
69+
datePublished: post.metadata.publishedAt,
70+
dateModified: post.metadata.publishedAt,
71+
description: post.metadata.summary,
72+
image: post.metadata.image ? `${meta.url}${post.metadata.image}` : `/og?title=${encodeURIComponent(post.metadata.title)}`,
73+
url: `${meta.url}/blog/${post.slug}`,
74+
author: {
75+
"@type": "Person",
76+
name: post.metadata.author,
77+
},
78+
}),
6279
}}
6380
/>
81+
6482
<div className="grid flex-1 grid-cols-1 md:grid-cols-[1fr_minmax(auto,640px)_1fr] md:*:col-start-2">
6583
<div>
6684
<header className="mb-4 w-full">
67-
<Header1>{post.title}</Header1>
85+
<Header1>{post.metadata.title}</Header1>
6886
<div className="mt-2 flex w-full flex-col items-start justify-between md:flex-row md:items-center">
6987
<div className="flex items-center">
7088
<Image alt={meta.title} height={24} width={24} src={Avatar} className="rounded-full" />
71-
<time className="ml-2 text-sm text-neutral-700 dark:text-neutral-300" dateTime={new Date(post.publishedAt).toUTCString()}>
72-
{post.author} / {parseISO(post.publishedAt)}
89+
<time className="ml-2 text-sm text-neutral-700 dark:text-neutral-300" dateTime={new Date(post.metadata.publishedAt).toUTCString()}>
90+
{post.metadata.author} / {parseISO(post.metadata.publishedAt)}
7391
</time>
7492
</div>
7593
<p className="mt-2 min-w-32 text-sm text-neutral-700 md:mt-0 dark:text-neutral-300">
7694
{post.wordCount} words • {post.readingTime?.text}
7795
</p>
7896
</div>
7997
</header>
80-
<MDXComponent code={post.body.code} />
98+
<MDXComponent source={post.content} />
8199
</div>
82100
<div className="sticky top-24 col-start-3! mt-8 ml-12 hidden max-w-56 flex-col space-y-2 self-start text-base xl:flex">
83101
<p className="mb-2 text-sm uppercase">On this page</p>
84-
{post.headings.map((props: { size: number; content: string; slug: string }) => (
85-
<Link
86-
key={props.slug}
87-
href={`#${props.slug}`}
88-
scroll={true}
89-
className={cn(
90-
{
91-
"ml-2": props.size === 2,
92-
"ml-4": props.size === 3,
93-
},
94-
"font-normal! no-underline opacity-50 duration-200 hover:underline hover:opacity-100 motion-reduce:transition-none"
95-
)}
96-
>
97-
{props.content}
98-
</Link>
99-
))}
102+
{post.headings &&
103+
post.headings.map((props: { size: number; content: string; slug: string }) => (
104+
<Link
105+
key={props.slug}
106+
href={`#${props.slug}`}
107+
scroll={true}
108+
className={cn(
109+
{
110+
"ml-2": props.size === 2,
111+
"ml-4": props.size === 3,
112+
},
113+
"font-normal! no-underline opacity-50 duration-200 hover:underline hover:opacity-100 motion-reduce:transition-none"
114+
)}
115+
>
116+
{props.content}
117+
</Link>
118+
))}
100119
</div>
101120
</div>
102121
<div className="flex w-full justify-end py-4 text-neutral-700 dark:text-neutral-300">

app/blog/page.tsx

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { allBlogs } from "contentlayer/generated";
2-
import { pick } from "contentlayer2/client";
31
import { Description, Header1, Header2 } from "@/components/ui/Headers";
42
import { Icons } from "@/components/ui/Icons";
53
import Link from "@/components/ui/Link";
4+
import { getBlogPosts } from "@/lib/blogUtils";
65
import { parseISO } from "@/lib/utils";
76

87
export const metadata = {
@@ -11,30 +10,30 @@ export const metadata = {
1110
};
1211

1312
export default function Page() {
14-
const posts = allBlogs.map((post) => pick(post, ["slug", "title", "summary", "publishedAt"])).sort((a, b) => Number(new Date(b.publishedAt)) - Number(new Date(a.publishedAt)));
13+
const posts = getBlogPosts().sort((a, b) => Number(new Date(b.metadata.publishedAt)) - Number(new Date(a.metadata.publishedAt)));
1514

1615
return (
17-
<section className="mt-6 mb-16 md:mt-12 lg:mt-20">
16+
<section className="mt-6 mb-16 overflow-hidden md:mt-12 lg:mt-20">
1817
<Header1>Tech Blog</Header1>
1918
<Description className="mb-16">A blog about technology, programming, and various intriguing topics. Here I share my experiences, projects and opinions.</Description>
2019

2120
<Header2>All Posts</Header2>
2221
{!posts.length && <p className="mb-4 text-red-400">No posts found!</p>}
23-
<div className="relative mt-3 flex flex-col items-start justify-center border-l border-neutral-200 dark:border-neutral-800">
22+
<div className="relative mt-3 ml-3 flex flex-col items-start justify-center border-l border-neutral-200 dark:border-neutral-800">
2423
{posts.map((post, index) => (
2524
<Link href={`/blog/${post.slug}`} key={`/blog/${post.slug}`} className="-mt-px mb-10 ml-6 rounded-2xl border border-neutral-200 px-6 py-3 duration-200 hover:bg-neutral-200/50 motion-reduce:transition-none dark:border-neutral-800 dark:bg-[#161617] dark:hover:border-neutral-700 dark:hover:bg-[#202021]">
2625
<span className="absolute -left-3 -mt-3 flex size-6 shrink-0 items-center justify-center rounded-full border border-neutral-200 bg-black/10 dark:border-neutral-800 dark:bg-[#161617]">
2726
<Icons.Calendar1 className="size-3 text-neutral-800 dark:text-white" />
2827
</span>
2928
<header>
3029
<h3 className="mb-1 flex items-center text-lg font-bold text-neutral-900 dark:text-white">
31-
{post.title} {index === 0 && <span className="mr-2 ml-3 hidden rounded-sm bg-black/10 py-0.5 pr-2.5 pl-1.5 text-sm font-medium sm:block dark:bg-white/10">🔥 Latest</span>}
30+
{post.metadata.title} {index === 0 && <span className="mr-2 ml-3 hidden rounded-sm bg-black/10 py-0.5 pr-2.5 pl-1.5 text-sm font-medium sm:block dark:bg-white/10">🔥 Latest</span>}
3231
</h3>
33-
<time className="mb-2 block text-sm leading-none font-normal text-neutral-500 dark:text-neutral-500" dateTime={new Date(post.publishedAt).toUTCString()}>
34-
{parseISO(post.publishedAt)}
32+
<time className="mb-2 block text-sm leading-none font-normal text-neutral-500 dark:text-neutral-500" dateTime={new Date(post.metadata.publishedAt).toUTCString()}>
33+
{parseISO(post.metadata.publishedAt)}
3534
</time>
3635
</header>
37-
<p className="mb-2 text-base font-normal text-neutral-700 dark:text-neutral-300">{post.summary}</p>
36+
<p className="mb-2 text-base font-normal text-neutral-700 dark:text-neutral-300">{post.metadata.summary}</p>
3837
<p className="inline-flex text-sm font-bold text-blue-500">Read more...</p>
3938
</Link>
4039
))}

app/feed/route.ts

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,41 @@
1-
import { allBlogs } from "contentlayer/generated";
21
import { NextResponse } from "next/server";
32
import { meta } from "@/config";
3+
import { getBlogPosts } from "@/lib/blogUtils";
44

55
export const dynamic = "force-static";
66

77
export function GET() {
8-
return new NextResponse(
9-
`<?xml version="1.0" encoding="utf-8"?>
10-
<feed xmlns="http://www.w3.org/2005/Atom">
11-
<title>${meta.title}</title>
12-
<subtitle>Blog</subtitle>
13-
<link href="${meta.url}/feed" rel="self"/>
14-
<link href="${meta.url}/"/>
15-
<updated>${new Date(
16-
allBlogs.reduce((acc, post) => {
17-
return post.publishedAt > acc ? post.publishedAt : acc;
18-
}, "")
19-
).toISOString()}</updated>
20-
<author>
21-
<name>${meta.title}</name>
22-
</author>
23-
<id>${meta.url}/</id>
24-
${allBlogs.reduce((acc, post) => {
25-
return `${acc}
8+
const posts = getBlogPosts();
9+
10+
const updatedDate = posts.length ? new Date(Math.max(...posts.map((post) => new Date(post.metadata.publishedAt).getTime()))).toISOString() : new Date().toISOString();
11+
12+
const entries = posts
13+
.map(
14+
(post) => `
2615
<entry>
2716
<id>${meta.url}/blog/${post.slug}</id>
28-
<title>${post.title}</title>
29-
<summary>${post.summary}</summary>
17+
<title>${post.metadata.title}</title>
18+
<summary>${post.metadata.summary}</summary>
3019
<link href="${meta.url}/blog/${post.slug}"/>
31-
<updated>${new Date(post.publishedAt).toISOString()}</updated>
32-
</entry>`;
33-
}, "")}
34-
</feed>`,
20+
<updated>${new Date(post.metadata.publishedAt).toISOString()}</updated>
21+
</entry>`
22+
)
23+
.join("");
3524

25+
return new NextResponse(
26+
`<?xml version="1.0" encoding="utf-8"?>
27+
<feed xmlns="http://www.w3.org/2005/Atom">
28+
<title>${meta.title}</title>
29+
<subtitle>Blog</subtitle>
30+
<link href="${meta.url}/feed" rel="self"/>
31+
<link href="${meta.url}/"/>
32+
<updated>${updatedDate}</updated>
33+
<author>
34+
<name>${meta.title}</name>
35+
</author>
36+
<id>${meta.url}/</id>
37+
${entries}
38+
</feed>`,
3639
{
3740
headers: {
3841
"content-type": "application/atom+xml; charset=utf-8",

app/sitemap.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1-
import { allBlogs } from "contentlayer/generated";
21
import type { MetadataRoute } from "next";
32
import { meta } from "@/config";
3+
import { getBlogPosts, getOtherPages } from "@/lib/blogUtils";
44

55
export default function sitemap(): MetadataRoute.Sitemap {
6-
const blogs = allBlogs.map((post) => ({
6+
const blogs = getBlogPosts();
7+
8+
const posts = blogs.map((post) => ({
79
url: `${meta.url}/blog/${post.slug}`,
8-
lastModified: post.publishedAt,
10+
lastModified: post.metadata.publishedAt,
11+
}));
12+
13+
const otherPages = getOtherPages();
14+
const pages = otherPages.map((post) => ({
15+
url: `${meta.url}/${post.slug}`,
16+
lastModified: new Date().toISOString().split("T")[0],
917
}));
1018

11-
const routes = ["", "/blog", "/contact", "/photography", "/work", "/uses"].map((route) => ({
19+
const routes = ["", "/blog", "/contact", "/photography", "/work"].map((route) => ({
1220
url: `${meta.url}${route}`,
1321
lastModified: new Date().toISOString().split("T")[0],
1422
}));
1523

16-
return [...routes, ...blogs];
24+
return [...routes, ...posts, ...pages];
1725
}

app/uses/page.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { allOtherPages } from "contentlayer/generated";
21
import Image from "next/image";
32
import { notFound } from "next/navigation";
43
import { MDXComponent } from "@/components/MDXComponents";
54
import { Header1 } from "@/components/ui/Headers";
65
import Link from "@/components/ui/Link";
76
import { meta } from "@/config";
7+
import { getOtherPages } from "@/lib/blogUtils";
88
import setup from "@/public/assets/uses.png";
99

1010
export const metadata = {
@@ -13,21 +13,21 @@ export const metadata = {
1313
};
1414

1515
export default function Page() {
16-
const uses = allOtherPages.find((page) => page.slug === "uses");
16+
const uses = getOtherPages().find((page) => page.slug === "uses");
1717

1818
if (!uses) return notFound();
1919

2020
return (
2121
<article className="mt-6 mb-16 flex flex-col items-start justify-center md:mt-12 lg:mt-20">
2222
<header>
23-
<Header1>{uses.title}</Header1>
24-
<p className="pb-2 text-neutral-700 dark:text-neutral-300">{uses.description}</p>
23+
<Header1>{uses.metadata.title}</Header1>
24+
<p className="pb-2 text-neutral-700 dark:text-neutral-300">{uses.metadata.description}</p>
2525
</header>
2626
<Link href={`https://github.com/${meta.accounts.github.username}/dotfiles`} target="_blank" rel="noopener noreferrer">
2727
<Image src={setup} loading="eager" alt="My setup" className="blur-0 relative z-10 my-4 scale-100 transform cursor-pointer rounded-2xl bg-neutral-200 duration-200 will-change-auto hover:opacity-75 hover:brightness-90 motion-reduce:duration-0 dark:bg-neutral-200/15" placeholder="blur" />
2828
</Link>
2929
<section className="w-full max-w-none">
30-
<MDXComponent code={uses.body.code} />
30+
<MDXComponent source={uses.content} />
3131
</section>
3232
</article>
3333
);

0 commit comments

Comments
 (0)