Skip to content

Commit

Permalink
refactor: migrate content management from contentlayer to content-col…
Browse files Browse the repository at this point in the history
…lections
  • Loading branch information
paulgrammer committed Oct 26, 2024
1 parent a78d130 commit 8828083
Show file tree
Hide file tree
Showing 18 changed files with 7,030 additions and 8,518 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ next-env.d.ts
/.react-email/

.vscode
.contentlayer
.content-collections
10 changes: 6 additions & 4 deletions app/(docs)/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { notFound } from "next/navigation";
import { allDocs } from "contentlayer/generated";
import { allDocs } from "content-collections";

import { getTableOfContents } from "@/lib/toc";
import { Mdx } from "@/components/content/mdx-components";
Expand All @@ -20,7 +20,8 @@ interface DocPageProps {
}

async function getDocFromParams(params) {
const slug = params.slug?.join("/") || "";
const slug = params.slug?.join("/") || "index";

const doc = allDocs.find((doc) => doc.slugAsParams === slug);

if (!doc) return null;
Expand Down Expand Up @@ -58,11 +59,12 @@ export default async function DocPage({ params }: DocPageProps) {
notFound();
}

const toc = await getTableOfContents(doc.body.raw);
const toc = await getTableOfContents(doc.content);

const images = await Promise.all(
doc.images.map(async (src: string) => ({
src,
alt: "Image",
blurDataURL: await getBlurDataURL(src),
})),
);
Expand All @@ -72,7 +74,7 @@ export default async function DocPage({ params }: DocPageProps) {
<div className="mx-auto w-full min-w-0">
<DocsPageHeader heading={doc.title} text={doc.description} />
<div className="pb-4 pt-11">
<Mdx code={doc.body.code} images={images} />
<Mdx code={doc.body} images={images} />
</div>
<hr className="my-4 md:my-6" />
<DocsPager doc={doc} />
Expand Down
6 changes: 3 additions & 3 deletions app/(docs)/guides/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { allGuides } from "contentlayer/generated";
import { allGuides } from "content-collections";
import Link from "next/link";
import { notFound } from "next/navigation";

Expand Down Expand Up @@ -53,14 +53,14 @@ export default async function GuidePage({
notFound();
}

const toc = await getTableOfContents(guide.body.raw);
const toc = await getTableOfContents(guide.body);

return (
<MaxWidthWrapper>
<div className="relative py-6 lg:grid lg:grid-cols-[1fr_300px] lg:gap-10 lg:py-10 xl:gap-20">
<div>
<DocsPageHeader heading={guide.title} text={guide.description} />
<Mdx code={guide.body.code} />
<Mdx code={guide.body} />
<hr className="my-4" />
<div className="flex justify-center py-6 lg:py-10">
<Link
Expand Down
2 changes: 1 addition & 1 deletion app/(docs)/guides/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Link from "next/link";
import { allGuides } from "contentlayer/generated";
import { allGuides } from "content-collections";
import { compareDesc } from "date-fns";

import { formatDate } from "@/lib/utils";
Expand Down
7 changes: 4 additions & 3 deletions app/(marketing)/(blog-post)/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { notFound } from "next/navigation";
import { allPosts } from "contentlayer/generated";
import { allPosts } from "content-collections";

import { Mdx } from "@/components/content/mdx-components";

Expand Down Expand Up @@ -72,13 +72,14 @@ export default async function PostPage({
)) ||
[];

const toc = await getTableOfContents(post.body.raw);
const toc = await getTableOfContents(post.content);

const [thumbnailBlurhash, images] = await Promise.all([
getBlurDataURL(post.image),
await Promise.all(
post.images.map(async (src: string) => ({
src,
alt: "image",
blurDataURL: await getBlurDataURL(src),
})),
),
Expand Down Expand Up @@ -140,7 +141,7 @@ export default async function PostPage({
sizes="(max-width: 768px) 770px, 1000px"
/>
<div className="px-[.8rem] pb-10 md:px-8">
<Mdx code={post.body.code} images={images} />
<Mdx code={post.body} images={images} />
</div>
</div>

Expand Down
5 changes: 3 additions & 2 deletions app/(marketing)/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { notFound } from "next/navigation";
import { allPages } from "contentlayer/generated";
import { allPages } from "content-collections";

import { Mdx } from "@/components/content/mdx-components";

Expand Down Expand Up @@ -49,6 +49,7 @@ export default async function PagePage({
const images = await Promise.all(
page.images.map(async (src: string) => ({
src,
alt: "image",
blurDataURL: await getBlurDataURL(src),
})),
);
Expand All @@ -64,7 +65,7 @@ export default async function PagePage({
)}
</div>
<hr className="my-4" />
<Mdx code={page.body.code} images={images} />
<Mdx code={page.body} images={images} />
</article>
);
}
2 changes: 1 addition & 1 deletion app/(marketing)/blog/category/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Metadata } from "next";
import { notFound } from "next/navigation";
import { allPosts } from "contentlayer/generated";
import { allPosts } from "content-collections";

import { BLOG_CATEGORIES } from "@/config/blog";
import { constructMetadata, getBlurDataURL } from "@/lib/utils";
Expand Down
2 changes: 1 addition & 1 deletion app/(marketing)/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { allPosts } from "contentlayer/generated";
import { allPosts } from "content-collections";

import { constructMetadata, getBlurDataURL } from "@/lib/utils";
import { BlogPosts } from "@/components/content/blog-posts";
Expand Down
2 changes: 1 addition & 1 deletion components/content/blog-card.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Link from "next/link";
import { Post } from "contentlayer/generated";
import { Post } from "content-collections";

import { cn, formatDate, placeholderBlurhash } from "@/lib/utils";
import BlurImage from "@/components/shared/blur-image";
Expand Down
2 changes: 1 addition & 1 deletion components/content/blog-posts.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Post } from "@/.contentlayer/generated";
import { Post } from "content-collections";

import { BlogCard } from "./blog-card";

Expand Down
2 changes: 1 addition & 1 deletion components/content/mdx-components.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from "react";
import NextImage, { ImageProps } from "next/image";
import Link from "next/link";
import { useMDXComponent } from "next-contentlayer2/hooks";
import { useMDXComponent } from "@content-collections/mdx/react";

import { cn } from "@/lib/utils";
import { MdxCard } from "@/components/content/mdx-card";
Expand Down
2 changes: 1 addition & 1 deletion components/docs/pager.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Link from "next/link"
import { Doc } from "contentlayer/generated"
import { Doc } from "content-collections"

import { docsConfig } from "@/config/docs"
import { cn } from "@/lib/utils"
Expand Down
151 changes: 151 additions & 0 deletions content-collections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import {
defineCollection,
defineConfig,
Context,
Document,
} from "@content-collections/core";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypePrettyCode from "rehype-pretty-code";
import rehypeSlug from "rehype-slug";
import remarkGfm from "remark-gfm";
import { visit } from "unist-util-visit";
import { compileMDX } from "@content-collections/mdx";

type Transformed = {
_id: string
body: string;
slug: string;
images: string[];
slugAsParams: string;
}

const transform = async <T extends Document & { content: string }>(
doc: T,
context: Context
): Promise<T & Transformed> => {
const body = await compileMDX(context, doc, {
remarkPlugins: [remarkGfm],
rehypePlugins: [
rehypeSlug,
() => (tree) => {
visit(tree, (node) => {
if (node?.type === "element" && node?.tagName === "pre") {
const [codeEl] = node.children;
if (codeEl.tagName !== "code") return;
node.rawString = codeEl.children?.[0].value;
}
});
},
[
rehypePrettyCode,
{
theme: "github-dark",
keepBackground: false,
onVisitLine(node) {
// Prevent lines from collapsing in `display: grid` mode, and allow empty lines to be copy/pasted
if (node.children.length === 0) {
node.children = [{ type: "text", value: " " }];
}
},
},
],
() => (tree) => {
visit(tree, (node) => {
if (node?.type === "element" && node?.tagName === "figure") {
if (!("data-rehype-pretty-code-figure" in node.properties)) {
return;
}
const preElement = node.children.at(-1);
if (preElement.tagName !== "pre") {
return;
}
preElement.properties["rawString"] = node.rawString;
}
});
},
[
rehypeAutolinkHeadings,
{
properties: {
className: ["subheading-anchor"],
ariaLabel: "Link to section",
},
},
],
],
});

const imageMatches =
doc.content.match(/(?<=<Image[^>]\bsrc=")[^"]+(?="[^>]\/>)/g) || [];

const rootPath = context.collection.directory.split("/").slice(1).join("/")

const transformedDoc: T & Transformed = {
...doc,
body,
images: imageMatches,
_id: doc._meta.filePath,
slugAsParams: doc._meta.path,
slug: `/${rootPath}/${doc._meta.path}`
};

return transformedDoc;
};

const Page = defineCollection({
name: "Page",
directory: "content/pages",
include: "**/*.mdx",
schema: (z) => ({
title: z.string(),
description: z.string().optional(),
}),
transform,
});

const Doc = defineCollection({
name: "Doc",
directory: "content/docs",
include: "**/*.mdx",
schema: (z) => ({
title: z.string(),
description: z.string().optional(),
published: z.boolean().default(true),
}),
transform,
});

const Guide = defineCollection({
name: "Guide",
directory: "content/guides",
include: "**/*.mdx",
schema: (z) => ({
title: z.string(),
description: z.string().optional(),
date: z.string(),
published: z.boolean().default(true),
featured: z.boolean().default(false),
}),
transform,
});

const Post = defineCollection({
name: "Post",
directory: "content/blog",
include: "**/*.mdx",
schema: (z) => ({
title: z.string(),
description: z.string().optional(),
date: z.string(),
published: z.boolean().default(true),
image: z.string(),
authors: z.array(z.string()),
categories: z.array(z.enum(["news", "education"])).default(["news"]),
related: z.array(z.string()).optional(),
}),
transform,
});

export default defineConfig({
collections: [Page, Doc, Guide, Post],
});
Loading

0 comments on commit 8828083

Please sign in to comment.