mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-04-26 06:45:23 -04:00
refactor "notes" into "posts" (only on the backend)
This commit is contained in:
parent
955cfe421f
commit
dbde73c63c
@ -21,7 +21,7 @@ const HitCounter = ({ slug }: HitCounterProps) => {
|
||||
fetcher
|
||||
);
|
||||
|
||||
// fail somewhat silently, see error boundary in NoteMeta component
|
||||
// fail somewhat silently, see error boundary in PostMeta component
|
||||
if (error) {
|
||||
showBoundary(`${error}`);
|
||||
return null;
|
||||
|
@ -1,2 +0,0 @@
|
||||
export * from "./NoteMeta";
|
||||
export { default } from "./NoteMeta";
|
@ -1,2 +0,0 @@
|
||||
export * from "./NoteTitle";
|
||||
export { default } from "./NoteTitle";
|
@ -1,2 +0,0 @@
|
||||
export * from "./NotesList";
|
||||
export { default } from "./NotesList";
|
@ -2,11 +2,11 @@ import { ErrorBoundary } from "react-error-boundary";
|
||||
import Link from "../Link";
|
||||
import Time from "../Time";
|
||||
import HitCounter from "../HitCounter";
|
||||
import NoteTitle from "../NoteTitle";
|
||||
import PostTitle from "../PostTitle";
|
||||
import { FiCalendar, FiTag, FiEdit, FiEye } from "react-icons/fi";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import * as config from "../../lib/config";
|
||||
import type { NoteFrontMatter } from "../../types";
|
||||
import type { PostFrontMatter } from "../../types";
|
||||
|
||||
const Wrapper = styled("div", {
|
||||
display: "inline-flex",
|
||||
@ -55,9 +55,9 @@ const Tag = styled("span", {
|
||||
},
|
||||
});
|
||||
|
||||
export type NoteMetaProps = Pick<NoteFrontMatter, "slug" | "date" | "title" | "htmlTitle" | "tags">;
|
||||
export type PostMetaProps = Pick<PostFrontMatter, "slug" | "date" | "title" | "htmlTitle" | "tags">;
|
||||
|
||||
const NoteMeta = ({ slug, date, title, htmlTitle, tags }: NoteMetaProps) => {
|
||||
const PostMeta = ({ slug, date, title, htmlTitle, tags }: PostMetaProps) => {
|
||||
return (
|
||||
<>
|
||||
<Wrapper>
|
||||
@ -116,9 +116,9 @@ const NoteMeta = ({ slug, date, title, htmlTitle, tags }: NoteMetaProps) => {
|
||||
)}
|
||||
</Wrapper>
|
||||
|
||||
<NoteTitle {...{ slug, title, htmlTitle }} />
|
||||
<PostTitle {...{ slug, title, htmlTitle }} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NoteMeta;
|
||||
export default PostMeta;
|
2
components/PostMeta/index.ts
Normal file
2
components/PostMeta/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./PostMeta";
|
||||
export { default } from "./PostMeta";
|
@ -1,7 +1,7 @@
|
||||
import Link from "../Link";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
import type { NoteFrontMatter } from "../../types";
|
||||
import type { PostFrontMatter } from "../../types";
|
||||
|
||||
const Title = styled("h1", {
|
||||
margin: "0.3em 0 0.5em -1px", // misaligned left margin, super nitpicky
|
||||
@ -18,10 +18,10 @@ const Title = styled("h1", {
|
||||
},
|
||||
});
|
||||
|
||||
export type NoteTitleProps = Pick<NoteFrontMatter, "slug" | "title" | "htmlTitle"> &
|
||||
export type PostTitleProps = Pick<PostFrontMatter, "slug" | "title" | "htmlTitle"> &
|
||||
ComponentPropsWithoutRef<typeof Title>;
|
||||
|
||||
const NoteTitle = ({ slug, title, htmlTitle, ...rest }: NoteTitleProps) => {
|
||||
const PostTitle = ({ slug, title, htmlTitle, ...rest }: PostTitleProps) => {
|
||||
return (
|
||||
<Title {...rest}>
|
||||
<Link
|
||||
@ -37,4 +37,4 @@ const NoteTitle = ({ slug, title, htmlTitle, ...rest }: NoteTitleProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default NoteTitle;
|
||||
export default PostTitle;
|
2
components/PostTitle/index.ts
Normal file
2
components/PostTitle/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./PostTitle";
|
||||
export { default } from "./PostTitle";
|
@ -2,7 +2,7 @@ import Link from "../Link";
|
||||
import Time from "../Time";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import type { ReactElement } from "react";
|
||||
import type { NotesByYear } from "../../types";
|
||||
import type { PostsByYear } from "../../types";
|
||||
|
||||
const Section = styled("section", {
|
||||
fontSize: "1.1em",
|
||||
@ -55,19 +55,19 @@ const PostDate = styled(Time, {
|
||||
color: theme.colors.medium,
|
||||
});
|
||||
|
||||
export type NotesListProps = {
|
||||
notesByYear: NotesByYear;
|
||||
export type PostsListProps = {
|
||||
postsByYear: PostsByYear;
|
||||
};
|
||||
|
||||
const NotesList = ({ notesByYear }: NotesListProps) => {
|
||||
const PostsList = ({ postsByYear }: PostsListProps) => {
|
||||
const sections: ReactElement[] = [];
|
||||
|
||||
Object.entries(notesByYear).forEach(([year, notes]) => {
|
||||
Object.entries(postsByYear).forEach(([year, posts]) => {
|
||||
sections.push(
|
||||
<Section key={year}>
|
||||
<Year>{year}</Year>
|
||||
<List>
|
||||
{notes.map(({ slug, date, title, htmlTitle }) => (
|
||||
{posts.map(({ slug, date, title, htmlTitle }) => (
|
||||
<Post key={slug}>
|
||||
<PostDate date={date} format="MMM D" />
|
||||
<span>
|
||||
@ -86,10 +86,10 @@ const NotesList = ({ notesByYear }: NotesListProps) => {
|
||||
);
|
||||
});
|
||||
|
||||
// grouped notes enter this component ordered chronologically -- we want reverse chronological
|
||||
// grouped posts enter this component ordered chronologically -- we want reverse chronological
|
||||
const reversed = sections.reverse();
|
||||
|
||||
return <>{reversed}</>;
|
||||
};
|
||||
|
||||
export default NotesList;
|
||||
export default PostsList;
|
2
components/PostsList/index.ts
Normal file
2
components/PostsList/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./PostsList";
|
||||
export { default } from "./PostsList";
|
@ -1,5 +1,5 @@
|
||||
import { Feed } from "feed";
|
||||
import { getAllNotes } from "./parse-notes";
|
||||
import { getAllPosts } from "./posts";
|
||||
import * as config from "../config";
|
||||
import { meJpg } from "../config/favicons";
|
||||
import type { GetServerSideProps } from "next";
|
||||
@ -40,22 +40,22 @@ export const buildFeed = async (
|
||||
},
|
||||
});
|
||||
|
||||
// add notes separately using their frontmatter
|
||||
const notes = await getAllNotes();
|
||||
notes.forEach((note) => {
|
||||
// add posts separately using their frontmatter
|
||||
const posts = await getAllPosts();
|
||||
posts.forEach((post) => {
|
||||
feed.addItem({
|
||||
guid: note.permalink,
|
||||
link: note.permalink,
|
||||
title: note.title,
|
||||
description: note.description,
|
||||
image: note.image && `${baseUrl}${note.image}`,
|
||||
guid: post.permalink,
|
||||
link: post.permalink,
|
||||
title: post.title,
|
||||
description: post.description,
|
||||
image: post.image && `${baseUrl}${post.image}`,
|
||||
author: [
|
||||
{
|
||||
name: config.authorName,
|
||||
link: `${baseUrl}/`,
|
||||
},
|
||||
],
|
||||
date: new Date(note.date),
|
||||
date: new Date(post.date),
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,48 +0,0 @@
|
||||
import { serialize } from "next-mdx-remote/serialize";
|
||||
import { getNoteData } from "./parse-notes";
|
||||
|
||||
import type { NoteWithSource } from "../../types";
|
||||
|
||||
// fully parses MDX into JS and returns *everything* about a note
|
||||
export const compileNote = async (slug: string): Promise<NoteWithSource> => {
|
||||
const { frontMatter, content } = await getNoteData(slug);
|
||||
|
||||
const { remarkGfm, remarkSmartypants, remarkUnwrapImages, rehypeSlug, rehypePrism } = await import(
|
||||
"./remark-rehype-plugins"
|
||||
);
|
||||
|
||||
const { compiledSource } = await serialize(content, {
|
||||
parseFrontmatter: false,
|
||||
mdxOptions: {
|
||||
remarkPlugins: [
|
||||
// @ts-ignore
|
||||
[remarkGfm, { singleTilde: false }],
|
||||
[
|
||||
// @ts-ignore
|
||||
remarkSmartypants,
|
||||
{
|
||||
quotes: true,
|
||||
dashes: "oldschool",
|
||||
backticks: false,
|
||||
ellipses: false,
|
||||
},
|
||||
],
|
||||
// @ts-ignore
|
||||
[remarkUnwrapImages],
|
||||
],
|
||||
rehypePlugins: [
|
||||
// @ts-ignore
|
||||
[rehypeSlug],
|
||||
// @ts-ignore
|
||||
[rehypePrism, { ignoreMissing: true }],
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
frontMatter,
|
||||
source: {
|
||||
compiledSource,
|
||||
},
|
||||
};
|
||||
};
|
@ -1,90 +0,0 @@
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import glob from "fast-glob";
|
||||
import pMap from "p-map";
|
||||
import pMemoize from "p-memoize";
|
||||
import matter from "gray-matter";
|
||||
import { formatDate } from "./format-date";
|
||||
|
||||
import type { NoteFrontMatter } from "../../types";
|
||||
|
||||
export const getNoteSlugs = async (): Promise<string[]> => {
|
||||
// list all .mdx files in "/notes"
|
||||
const mdxFiles = await glob("*.mdx", {
|
||||
cwd: path.join(process.cwd(), "notes"),
|
||||
dot: false,
|
||||
});
|
||||
|
||||
// strip the .mdx extensions from filenames
|
||||
const slugs = mdxFiles.map((fileName) => fileName.replace(/\.mdx$/, ""));
|
||||
|
||||
return slugs;
|
||||
};
|
||||
|
||||
// returns front matter and/or *raw* markdown contents of a given slug
|
||||
export const getNoteData = async (
|
||||
slug: string
|
||||
): Promise<{
|
||||
frontMatter: NoteFrontMatter;
|
||||
content: string;
|
||||
}> => {
|
||||
const fullPath = path.join(process.cwd(), "notes", `${slug}.mdx`);
|
||||
const rawContent = await fs.readFile(fullPath, "utf8");
|
||||
const { data, content } = matter(rawContent);
|
||||
|
||||
const { unified } = await import("unified");
|
||||
const { remarkParse, remarkSmartypants, remarkRehype, rehypeSanitize, rehypeStringify } = await import(
|
||||
"./remark-rehype-plugins"
|
||||
);
|
||||
|
||||
// allow *very* limited markdown to be used in post titles
|
||||
const parseTitle = async (title: string, allowedTags: string[] = []): Promise<string> => {
|
||||
return String(
|
||||
await unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkSmartypants, {
|
||||
quotes: true,
|
||||
dashes: "oldschool",
|
||||
backticks: false,
|
||||
ellipses: false,
|
||||
})
|
||||
.use(remarkRehype)
|
||||
.use(rehypeSanitize, { tagNames: allowedTags })
|
||||
.use(rehypeStringify)
|
||||
.process(title)
|
||||
);
|
||||
};
|
||||
|
||||
// process title as both plain and stylized
|
||||
const [title, htmlTitle] = await Promise.all([
|
||||
parseTitle(data.title),
|
||||
parseTitle(data.title, ["code", "em", "strong"]),
|
||||
]);
|
||||
|
||||
// return both the parsed YAML front matter (with a few amendments) and the raw, unparsed markdown content
|
||||
return {
|
||||
frontMatter: {
|
||||
...(data as Partial<NoteFrontMatter>),
|
||||
// zero markdown title:
|
||||
title,
|
||||
htmlTitle,
|
||||
slug,
|
||||
permalink: `${process.env.NEXT_PUBLIC_BASE_URL || ""}/notes/${slug}/`,
|
||||
date: formatDate(data.date), // validate/normalize the date string provided from front matter
|
||||
},
|
||||
content,
|
||||
};
|
||||
};
|
||||
|
||||
// returns the parsed front matter of ALL notes, sorted reverse chronologically
|
||||
export const getAllNotes = pMemoize(async (): Promise<NoteFrontMatter[]> => {
|
||||
const slugs = await getNoteSlugs();
|
||||
|
||||
// for each slug, query its front matter
|
||||
const data = await pMap(slugs, async (slug) => (await getNoteData(slug)).frontMatter);
|
||||
|
||||
// sort the results by date
|
||||
data.sort((note1, note2) => (note1.date > note2.date ? -1 : 1));
|
||||
|
||||
return data;
|
||||
});
|
135
lib/helpers/posts.ts
Normal file
135
lib/helpers/posts.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import { serialize } from "next-mdx-remote/serialize";
|
||||
import glob from "fast-glob";
|
||||
import pMap from "p-map";
|
||||
import pMemoize from "p-memoize";
|
||||
import matter from "gray-matter";
|
||||
import { formatDate } from "./format-date";
|
||||
import type { PostFrontMatter, PostWithSource } from "../../types";
|
||||
|
||||
// path to directory with .mdx files, relative to project root
|
||||
export const POSTS_DIR = "./notes";
|
||||
|
||||
// returns front matter and the **raw & uncompiled** markdown of a given slug
|
||||
export const getPostData = async (
|
||||
slug: string
|
||||
): Promise<{
|
||||
frontMatter: PostFrontMatter;
|
||||
markdown: string;
|
||||
}> => {
|
||||
const fullPath = path.join(process.cwd(), POSTS_DIR, `${slug}.mdx`);
|
||||
const rawContent = await fs.readFile(fullPath, "utf8");
|
||||
const { data, content } = matter(rawContent);
|
||||
|
||||
const { unified } = await import("unified");
|
||||
const { remarkParse, remarkSmartypants, remarkRehype, rehypeSanitize, rehypeStringify } = await import(
|
||||
"./remark-rehype-plugins"
|
||||
);
|
||||
|
||||
// allow *very* limited markdown to be used in post titles
|
||||
const parseTitle = async (title: string, allowedTags: string[] = []): Promise<string> => {
|
||||
return String(
|
||||
await unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkSmartypants, {
|
||||
quotes: true,
|
||||
dashes: "oldschool",
|
||||
backticks: false,
|
||||
ellipses: false,
|
||||
})
|
||||
.use(remarkRehype)
|
||||
.use(rehypeSanitize, { tagNames: allowedTags })
|
||||
.use(rehypeStringify)
|
||||
.process(title)
|
||||
);
|
||||
};
|
||||
|
||||
// process title as both plain and stylized
|
||||
const [title, htmlTitle] = await Promise.all([
|
||||
parseTitle(data.title),
|
||||
parseTitle(data.title, ["code", "em", "strong"]),
|
||||
]);
|
||||
|
||||
// return both the parsed YAML front matter (with a few amendments) and the raw, unparsed markdown content
|
||||
return {
|
||||
frontMatter: {
|
||||
...(data as Partial<PostFrontMatter>),
|
||||
// zero markdown title:
|
||||
title,
|
||||
htmlTitle,
|
||||
slug,
|
||||
permalink: `${process.env.NEXT_PUBLIC_BASE_URL || ""}/${POSTS_DIR}/${slug}/`,
|
||||
date: formatDate(data.date), // validate/normalize the date string provided from front matter
|
||||
},
|
||||
markdown: content,
|
||||
};
|
||||
};
|
||||
|
||||
// fully parses MDX into JS and returns *everything* about a post
|
||||
export const compilePost = async (slug: string): Promise<PostWithSource> => {
|
||||
const { frontMatter, markdown } = await getPostData(slug);
|
||||
|
||||
const { remarkGfm, remarkSmartypants, remarkUnwrapImages, rehypeSlug, rehypePrism } = await import(
|
||||
"./remark-rehype-plugins"
|
||||
);
|
||||
|
||||
const { compiledSource } = await serialize(markdown, {
|
||||
parseFrontmatter: false,
|
||||
mdxOptions: {
|
||||
remarkPlugins: [
|
||||
// @ts-ignore
|
||||
[remarkGfm, { singleTilde: false }],
|
||||
[
|
||||
// @ts-ignore
|
||||
remarkSmartypants,
|
||||
{
|
||||
quotes: true,
|
||||
dashes: "oldschool",
|
||||
backticks: false,
|
||||
ellipses: false,
|
||||
},
|
||||
],
|
||||
// @ts-ignore
|
||||
[remarkUnwrapImages],
|
||||
],
|
||||
rehypePlugins: [
|
||||
// @ts-ignore
|
||||
[rehypeSlug],
|
||||
// @ts-ignore
|
||||
[rehypePrism, { ignoreMissing: true }],
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
frontMatter,
|
||||
source: {
|
||||
compiledSource,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getPostSlugs = pMemoize(async (): Promise<string[]> => {
|
||||
// list all .mdx files in POSTS_DIR
|
||||
const mdxFiles = await glob("*.mdx", {
|
||||
cwd: path.join(process.cwd(), POSTS_DIR),
|
||||
dot: false,
|
||||
});
|
||||
|
||||
// strip the .mdx extensions from filenames
|
||||
const slugs = mdxFiles.map((fileName) => fileName.replace(/\.mdx$/, ""));
|
||||
|
||||
return slugs;
|
||||
});
|
||||
|
||||
// returns the parsed front matter of ALL posts, sorted reverse chronologically
|
||||
export const getAllPosts = pMemoize(async (): Promise<PostFrontMatter[]> => {
|
||||
// for each post, query its front matter
|
||||
const data = await pMap(await getPostSlugs(), async (slug) => (await getPostData(slug)).frontMatter);
|
||||
|
||||
// sort the results by date
|
||||
data.sort((post1, post2) => (post1.date > post2.date ? -1 : 1));
|
||||
|
||||
return data;
|
||||
});
|
@ -2,16 +2,15 @@ import { InView } from "react-intersection-observer";
|
||||
import { NextSeo, ArticleJsonLd } from "next-seo";
|
||||
import { MDXRemote, MDXRemoteProps } from "next-mdx-remote";
|
||||
import Content from "../../components/Content";
|
||||
import NoteMeta from "../../components/NoteMeta";
|
||||
import PostMeta from "../../components/PostMeta";
|
||||
import Comments from "../../components/Comments";
|
||||
import * as mdxComponents from "../../lib/helpers/mdx-components";
|
||||
import { getNoteSlugs } from "../../lib/helpers/parse-notes";
|
||||
import { compileNote } from "../../lib/helpers/compile-note";
|
||||
import { getPostSlugs, compilePost } from "../../lib/helpers/posts";
|
||||
import * as config from "../../lib/config";
|
||||
import { articleJsonLd } from "../../lib/config/seo";
|
||||
import { meJpg } from "../../lib/config/favicons";
|
||||
import type { GetStaticProps, GetStaticPaths, InferGetStaticPropsType } from "next";
|
||||
import type { NoteWithSource, NoteFrontMatter } from "../../types";
|
||||
import type { PostWithSource, PostFrontMatter } from "../../types";
|
||||
|
||||
const Note = ({ frontMatter, source }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
return (
|
||||
@ -51,7 +50,7 @@ const Note = ({ frontMatter, source }: InferGetStaticPropsType<typeof getStaticP
|
||||
{...articleJsonLd}
|
||||
/>
|
||||
|
||||
<NoteMeta {...frontMatter} />
|
||||
<PostMeta {...frontMatter} />
|
||||
|
||||
<Content>
|
||||
<MDXRemote {...source} components={{ ...(mdxComponents as MDXRemoteProps["components"]) }} />
|
||||
@ -70,14 +69,14 @@ const Note = ({ frontMatter, source }: InferGetStaticPropsType<typeof getStaticP
|
||||
);
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps<NoteWithSource, Pick<NoteFrontMatter, "slug">> = async ({ params }) => {
|
||||
export const getStaticProps: GetStaticProps<PostWithSource, Pick<PostFrontMatter, "slug">> = async ({ params }) => {
|
||||
if (!params?.slug) {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
const { frontMatter, source } = await compileNote(params.slug);
|
||||
const { frontMatter, source } = await compilePost(params.slug);
|
||||
|
||||
return {
|
||||
props: {
|
||||
@ -88,7 +87,10 @@ export const getStaticProps: GetStaticProps<NoteWithSource, Pick<NoteFrontMatter
|
||||
};
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const slugs = await getNoteSlugs();
|
||||
// get the slug of each .mdx file in /notes
|
||||
const slugs = await getPostSlugs();
|
||||
|
||||
// map slugs into a static paths object required by next.js
|
||||
const paths = slugs.map((slug) => ({ params: { slug } }));
|
||||
|
||||
return {
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { NextSeo } from "next-seo";
|
||||
import Content from "../../components/Content";
|
||||
import NotesList from "../../components/NotesList";
|
||||
import { getAllNotes } from "../../lib/helpers/parse-notes";
|
||||
import PostsList from "../../components/PostsList";
|
||||
import { getAllPosts } from "../../lib/helpers/posts";
|
||||
import { authorName } from "../../lib/config";
|
||||
import type { GetStaticProps, InferGetStaticPropsType } from "next";
|
||||
import type { NotesByYear } from "../../types";
|
||||
import type { PostsByYear } from "../../types";
|
||||
|
||||
const Notes = ({ notesByYear }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
return (
|
||||
@ -18,18 +18,18 @@ const Notes = ({ notesByYear }: InferGetStaticPropsType<typeof getStaticProps>)
|
||||
/>
|
||||
|
||||
<Content>
|
||||
<NotesList notesByYear={notesByYear} />
|
||||
<PostsList postsByYear={notesByYear} />
|
||||
</Content>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps<{
|
||||
notesByYear: NotesByYear;
|
||||
notesByYear: PostsByYear;
|
||||
}> = async () => {
|
||||
// parse the year of each note and group them together
|
||||
const notes = await getAllNotes();
|
||||
const notesByYear: NotesByYear = {};
|
||||
const notes = await getAllPosts();
|
||||
const notesByYear: PostsByYear = {};
|
||||
|
||||
notes.forEach((note) => {
|
||||
const year = new Date(note.date).getUTCFullYear();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { SitemapStream, EnumChangefreq } from "sitemap";
|
||||
import { getAllNotes } from "../lib/helpers/parse-notes";
|
||||
import { getAllPosts } from "../lib/helpers/posts";
|
||||
import { siteDomain } from "../lib/config";
|
||||
import type { GetServerSideProps } from "next";
|
||||
import type { SitemapItemLoose } from "sitemap";
|
||||
@ -39,20 +39,20 @@ export const getServerSideProps: GetServerSideProps<Record<string, never>> = asy
|
||||
{ url: "/zip/" },
|
||||
];
|
||||
|
||||
// push notes separately and use their metadata
|
||||
const notes = await getAllNotes();
|
||||
notes.forEach((note) => {
|
||||
// push posts separately and use their metadata
|
||||
const posts = await getAllPosts();
|
||||
posts.forEach((post) => {
|
||||
pages.push({
|
||||
url: `/notes/${note.slug}/`,
|
||||
url: `/notes/${post.slug}/`,
|
||||
// pull lastMod from front matter date
|
||||
lastmod: note.date,
|
||||
lastmod: post.date,
|
||||
});
|
||||
});
|
||||
|
||||
// set lastmod of /notes/ page to most recent post's date
|
||||
pages.push({
|
||||
url: `/notes/`,
|
||||
lastmod: notes[0].date,
|
||||
lastmod: posts[0].date,
|
||||
});
|
||||
|
||||
// sort alphabetically by URL
|
||||
|
2
types/index.d.ts
vendored
2
types/index.d.ts
vendored
@ -1,3 +1,3 @@
|
||||
export * from "./note";
|
||||
export * from "./post";
|
||||
export * from "./project";
|
||||
export * from "./stats";
|
||||
|
12
types/note.d.ts → types/post.d.ts
vendored
12
types/note.d.ts → types/post.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
import type { MDXRemoteSerializeResult } from "next-mdx-remote";
|
||||
|
||||
export type NoteFrontMatter = {
|
||||
export type PostFrontMatter = {
|
||||
slug: string;
|
||||
permalink: string;
|
||||
date: string;
|
||||
@ -12,14 +12,14 @@ export type NoteFrontMatter = {
|
||||
noComments?: boolean;
|
||||
};
|
||||
|
||||
export type NoteWithSource = {
|
||||
export type PostWithSource = {
|
||||
// yaml metadata
|
||||
frontMatter: NoteFrontMatter;
|
||||
frontMatter: PostFrontMatter;
|
||||
|
||||
// the final, compiled JSX by next-mdx-remote; see lib/helpers/parse-notes.ts
|
||||
// the final, compiled JSX by next-mdx-remote; see lib/helpers/posts.ts
|
||||
source: Partial<Pick<MDXRemoteSerializeResult<Record<string, never>, Record<string, never>>>>;
|
||||
};
|
||||
|
||||
export type NotesByYear = {
|
||||
[year: string]: NoteFrontMatter[];
|
||||
export type PostsByYear = {
|
||||
[year: string]: PostFrontMatter[];
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user