Skip to content

Instantly share code, notes, and snippets.

@jordyvandomselaar
Last active September 5, 2022 17:23
Show Gist options
  • Save jordyvandomselaar/204e62e43fd3e2fa75f23cd27bd31d59 to your computer and use it in GitHub Desktop.
Save jordyvandomselaar/204e62e43fd3e2fa75f23cd27bd31d59 to your computer and use it in GitHub Desktop.
import {GetStaticPaths, GetStaticProps} from "next";
import {getPostPathBySlug, getPostSlugs, markdownFileToPost, Post as PostInterface} from "../../services/posts";
import {MDXRemote, MDXRemoteSerializeResult} from 'next-mdx-remote'
import {serialize} from 'next-mdx-remote/serialize'
import Content from "../../components/Content";
import Layout from "../../components/Layout";
import Footer from "../../components/Footer";
import path from "path";
import Hero from "../../components/Hero";
import Image from "next/image"
import Heading from "../../components/Heading";
import Text, {textStyle} from "../../components/Text";
import {css} from "@emotion/react";
import CodeBlock from "../../components/CodeBlock";
import CodeSandbox from "../../components/CodeSandbox";
import Link from "next/link"
import Tip from "../../components/Tip";
interface PostProps {
data: PostInterface;
source: MDXRemoteSerializeResult;
}
export default function Post({data, source}: PostProps) {
const heroPath = "./" + path.join(data.slug, data.hero);
// @ts-ignore
const allImages = require.context("/content/posts/", true, /\.(jpg|jpeg|png)$/);
const heroImage = allImages(heroPath);
const components = {
h1: (props: any) => <Heading tag="h1" {...props}/>,
h2: (props: any) => <Heading tag="h2" {...props}/>,
h3: (props: any) => <Heading tag="h3" {...props}/>,
h4: (props: any) => <Heading tag="h4" {...props}/>,
h5: (props: any) => <Heading tag="h5" {...props}/>,
h6: (props: any) => <Heading tag="h6" {...props}/>,
p: Text,
li: (props: any) => <li {...props} css={[textStyle, css`line-height: 2;
::marker {
font-weight: bold;
font-family: 'Roboto Mono', monospace;
}`]}/>,
ul: (props: any) => <ul {...props} css={css`
padding-left: 1.5rem;
margin-top: 0;
margin-bottom: 2rem;
`}/>,
ol: (props: any) => <ol {...props} css={css`margin-top: 0;
margin-bottom: 2rem;`}/>,
img: ({src, alt}: any) => {
throw new Error("Use next/image instead of img");
return null;
},
pre: CodeBlock,
inlineCode: (props: any) => <code {...props} css={css`background: rgb(230, 230, 230);`}/>,
CodeSandbox: CodeSandbox,
Tip: Tip,
Image: ({src, alt}: any) => {
const imagePath = "./" + path.join(data.slug, src);
return <div css={css`margin-bottom: 2rem;`}>
<Image objectFit="contain" alt={alt} src={allImages(imagePath)}/>
</div>
;
},
}
return <Layout>
<div>
<Hero image={
<Image src={heroImage} alt="hero image" layout="fill" objectFit="cover"/>
}>
<div>
<div>
<Link href="/">
<a css={[textStyle, css`
text-decoration: none;
color: #fff;
text-align: center;
display: block;
font-size: 2rem;
padding-bottom: 2rem;
&:hover {
cursor: pointer;
}
`]}>&laquo; Back to post list</a>
</Link>
</div>
<div css={css`text-align: center;
@media screen and (max-width: 800px) {
font-size: 10px;
};`}>
<Heading tag="h1">{data.title}</Heading>
</div>
</div>
</Hero>
<Content>
<MDXRemote components={components} {...source}/>
</Content>
</div>
<Footer/>
</Layout>
}
export const getStaticProps: GetStaticProps = async ({params}) => {
const postPath = getPostPathBySlug((params as { slug: string }).slug);
const post = markdownFileToPost(postPath);
const mdxSource = await serialize(post.content, {
mdxOptions: {
remarkPlugins: [],
rehypePlugins: [],
},
})
return {
props: {
source: mdxSource,
data: post,
},
}
}
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: getPostSlugs().map(slug => ({params: {slug}})),
fallback: false
}
}
import glob from "glob";
import fs from "fs";
import matter from "gray-matter";
export interface Post {
title: string;
excerpt: string;
slug: string;
hero: string;
content: string;
}
export function getPosts(): Post[] {
return getPostPaths().map(markdownFileToPost);
}
export function markdownFileToPost(filePath: string): Post {
const source = fs.readFileSync(filePath);
const matterData = matter(source)
const {title, date, excerpt, hero} = matterData.data;
const slug = slugifyPath(filePath);
return {title, excerpt, slug, hero, content: matterData.content};
}
export function getPostPaths(): string[] {
const path = require("path");
return glob.sync(path.join(process.cwd(), "content/posts/**/*.md"));
}
export function getPostPathBySlug(slug: string): string {
const path = require("path");
return path.join(process.cwd(), "content/posts/", slug, "index.md");
}
export function getPostSlugs(): string[] {
return getPostPaths().map(path => slugifyPath(path));
}
function slugifyPath(path: string): string {
// Remove everything but the folder name
return path.replace(/.+?posts\//, "").replace("\/index.md", "");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment