1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-07-03 16:46:39 -04:00

consolidate mdx file parsing

This commit is contained in:
2022-01-03 17:53:47 -05:00
parent d2b71887b4
commit 3864a57d1a
9 changed files with 59 additions and 68 deletions

View File

@ -1,6 +1,5 @@
import Link from "next/link"; import Link from "next/link";
import { format, parseISO } from "date-fns"; import { format } from "date-fns";
import groupBy from "lodash.groupby";
import styles from "./List.module.scss"; import styles from "./List.module.scss";
@ -10,18 +9,17 @@ type NoteProps = {
slug: string; slug: string;
}; };
const List = ({ notes }) => { const List = ({ notesByYear }) => {
const notesByYear = groupBy(notes, "year");
const sections = []; const sections = [];
Object.entries(notesByYear).forEach(([year, yearNotes]: [string, NoteProps[]]) => { Object.entries(notesByYear).forEach(([year, notes]: [string, NoteProps[]]) => {
sections.push( sections.push(
<section key={year} className={styles.section}> <section key={year} className={styles.section}>
<h2 className={styles.year}>{year}</h2> <h2 className={styles.year}>{year}</h2>
<ul className={styles.list}> <ul className={styles.list}>
{yearNotes.map((note) => ( {notes.map((note) => (
<li key={note.slug} className={styles.row}> <li key={note.slug} className={styles.row}>
<span className={styles.date}>{format(parseISO(note.date), "MMM d")}</span> <span className={styles.date}>{format(new Date(note.date), "MMM d")}</span>
<span> <span>
<Link href={`/notes/${note.slug}/`} prefetch={false}> <Link href={`/notes/${note.slug}/`} prefetch={false}>
<a>{note.title}</a> <a>{note.title}</a>

View File

@ -1,5 +1,5 @@
import Link from "next/link"; import Link from "next/link";
import { format, parseISO } from "date-fns"; import { format } from "date-fns";
import Hits from "../hits/Hits"; import Hits from "../hits/Hits";
import { DateIcon, TagIcon, EditIcon, ViewsIcon } from "../icons"; import { DateIcon, TagIcon, EditIcon, ViewsIcon } from "../icons";
import * as config from "../../lib/config"; import * as config from "../../lib/config";
@ -20,8 +20,8 @@ const Meta = ({ title, date, slug, tags = [] }: Props) => (
<span> <span>
<DateIcon className={`icon ${styles.icon}`} /> <DateIcon className={`icon ${styles.icon}`} />
</span> </span>
<span title={format(parseISO(date), "PPppp")}> <span title={format(new Date(date), "PPppp")}>
<Link href={`/notes/${slug}/`}>{format(parseISO(date), "MMMM d, yyyy")}</Link> <Link href={`/notes/${slug}/`}>{format(new Date(date), "MMMM d, yyyy")}</Link>
</span> </span>
</div> </div>
{tags.length > 0 && ( {tags.length > 0 && (

View File

@ -1,4 +1,4 @@
import { intlFormat, formatDistanceToNowStrict, parseISO } from "date-fns"; import { intlFormat, formatDistanceToNowStrict } from "date-fns";
import { StarOcticon, ForkOcticon } from "../icons/octicons"; import { StarOcticon, ForkOcticon } from "../icons/octicons";
import styles from "./RepoCard.module.scss"; import styles from "./RepoCard.module.scss";
@ -69,7 +69,7 @@ const RepoCard = ({ name, url, description, language, stars, forks, updatedAt }:
<div <div
className={styles.meta_item} className={styles.meta_item}
title={intlFormat( title={intlFormat(
parseISO(updatedAt), new Date(updatedAt),
{ {
year: "numeric", year: "numeric",
month: "short", month: "short",
@ -83,7 +83,7 @@ const RepoCard = ({ name, url, description, language, stars, forks, updatedAt }:
} }
)} )}
> >
<span>Updated {formatDistanceToNowStrict(parseISO(updatedAt), { addSuffix: true })}</span> <span>Updated {formatDistanceToNowStrict(new Date(updatedAt), { addSuffix: true })}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,30 +1,33 @@
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import matter from "gray-matter"; import matter from "gray-matter";
import { format, parseISO } from "date-fns"; import { NOTES_DIR, baseUrl } from "./config";
import * as config from "./config";
export const getNoteData = (file: string) => { export const getNoteData = (slug: string) => {
const slug = file.replace(/\.mdx$/, ""); const fullPath = path.join(process.cwd(), NOTES_DIR, `${slug}.mdx`);
const fullPath = path.join(process.cwd(), config.NOTES_DIR, `${slug}.mdx`); const rawContent = fs.readFileSync(fullPath, "utf8");
const contents = fs.readFileSync(fullPath, "utf8"); const { data, content } = matter(rawContent);
const { data } = matter(contents);
return { return {
...data, frontMatter: {
slug, ...data,
permalink: `${config.baseUrl}/notes/${slug}/`, slug,
date: parseISO(data.date).toISOString(), // validate/normalize the date string provided from front matter permalink: `${baseUrl}/notes/${slug}/`,
year: parseInt(format(parseISO(data.date), "yyyy")), // parse years here so it's easier to group them on list page date: new Date(data.date).toISOString(), // validate/normalize the date string provided from front matter
},
content,
}; };
}; };
// all .mdx files in NOTES_DIR // all .mdx files in NOTES_DIR
export const getNoteFiles = () => export const getNoteSlugs = () =>
fs.readdirSync(path.join(process.cwd(), config.NOTES_DIR)).filter((notePath) => /\.mdx$/.test(notePath)); fs
.readdirSync(path.join(process.cwd(), NOTES_DIR))
.filter((file) => /\.mdx$/.test(file))
.map((noteFile) => noteFile.replace(/\.mdx$/, ""));
export const getAllNotes = () => export const getAllNotes = () =>
getNoteFiles() getNoteSlugs()
.map((file) => getNoteData(file)) .map((slug) => getNoteData(slug).frontMatter)
// sort notes by date in descending order // sort notes by date in descending order
.sort((note1: any, note2: any) => (note1.date > note2.date ? -1 : 1)); .sort((note1: any, note2: any) => (note1.date > note2.date ? -1 : 1));

View File

@ -43,7 +43,6 @@
"hex-rgb": "^5.0.0", "hex-rgb": "^5.0.0",
"highlight.js": "^11.3.1", "highlight.js": "^11.3.1",
"is-absolute-url": "^4.0.1", "is-absolute-url": "^4.0.1",
"lodash.groupby": "^4.6.0",
"modern-normalize": "github:sindresorhus/modern-normalize#1fc6b5a86676b7ac8abc62d04d6080f92debc70f", "modern-normalize": "github:sindresorhus/modern-normalize#1fc6b5a86676b7ac8abc62d04d6080f92debc70f",
"next": "v12.0.8-canary.14", "next": "v12.0.8-canary.14",
"next-mdx-remote": "^3.0.8", "next-mdx-remote": "^3.0.8",

View File

@ -14,7 +14,7 @@ type ColorLinkProps = {
external?: boolean; external?: boolean;
}; };
const ColorLink = ({ children, href, lightColor, darkColor, title, external = false }: ColorLinkProps) => { const ColorLink = ({ href, title, lightColor, darkColor, external = false, children }: ColorLinkProps) => {
external = external || isAbsoluteUrl(href); external = external || isAbsoluteUrl(href);
// spits out an alpha color in rgb() that's compatible with linear-gradient() // spits out an alpha color in rgb() that's compatible with linear-gradient()

View File

@ -1,7 +1,3 @@
import fs from "fs";
import path from "path";
import matter from "gray-matter";
import { parseISO } from "date-fns";
import { MDXRemote } from "next-mdx-remote"; import { MDXRemote } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize"; import { serialize } from "next-mdx-remote/serialize";
import { NextSeo, ArticleJsonLd } from "next-seo"; import { NextSeo, ArticleJsonLd } from "next-seo";
@ -10,7 +6,7 @@ import Container from "../../components/Container";
import Content from "../../components/Content"; import Content from "../../components/Content";
import Meta from "../../components/notes/Meta"; import Meta from "../../components/notes/Meta";
import mdxComponents from "../../components/mdxComponents"; import mdxComponents from "../../components/mdxComponents";
import { getNoteFiles } from "../../lib/parse-notes"; import { getNoteData, getNoteSlugs } from "../../lib/parse-notes";
import * as config from "../../lib/config"; import * as config from "../../lib/config";
import type { GetStaticProps, GetStaticPaths } from "next"; import type { GetStaticProps, GetStaticPaths } from "next";
@ -72,12 +68,9 @@ const Note = ({ frontMatter, source }) => (
); );
export const getStaticProps: GetStaticProps = async ({ params }) => { export const getStaticProps: GetStaticProps = async ({ params }) => {
const filePath = path.join(process.cwd(), config.NOTES_DIR, `${params.slug}.mdx`); const { frontMatter, content } = getNoteData(params.slug as string);
const rawSource = fs.readFileSync(filePath);
const { data, content } = matter(rawSource);
const mdxSource = await serialize(content, { const source = await serialize(content, {
scope: data,
mdxOptions: { mdxOptions: {
// remarkPlugins: [], // remarkPlugins: [],
rehypePlugins: [ rehypePlugins: [
@ -94,23 +87,14 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
return { return {
props: { props: {
frontMatter: { frontMatter,
...data, source,
slug: params.slug,
permalink: `${config.baseUrl}/notes/${params.slug}/`,
date: parseISO(data.date).toISOString(), // validate/normalize the date string provided from front matter
},
source: mdxSource,
}, },
}; };
}; };
export const getStaticPaths: GetStaticPaths = async () => { export const getStaticPaths: GetStaticPaths = async () => {
const paths = getNoteFiles() const paths = getNoteSlugs().map((slug) => ({ params: { slug } }));
// Remove file extensions for page paths
.map((notePath) => notePath.replace(/\.mdx?$/, ""))
// Map the path into the static paths object required by Next.js
.map((slug) => ({ params: { slug } }));
return { return {
paths, paths,

View File

@ -1,23 +1,29 @@
import { format } from "date-fns";
import Layout from "../../components/Layout"; import Layout from "../../components/Layout";
import Container from "../../components/Container"; import Container from "../../components/Container";
import List from "../../components/notes/List"; import List from "../../components/notes/List";
import { getAllNotes } from "../../lib/parse-notes"; import { getAllNotes } from "../../lib/parse-notes";
import type { GetStaticProps } from "next"; import type { GetStaticProps } from "next";
const Notes = ({ notes }) => ( const Notes = ({ notesByYear }) => (
<Layout> <Layout>
<Container title="Notes" description="Recent posts by Jake Jarvis."> <Container title="Notes" description="Recent posts by Jake Jarvis.">
<List notes={notes} /> <List notesByYear={notesByYear} />
</Container> </Container>
</Layout> </Layout>
); );
export const getStaticProps: GetStaticProps = async () => { export const getStaticProps: GetStaticProps = async () => {
const notes = getAllNotes(); // parse the year of each note and group them together
const notesByYear = {};
getAllNotes().map((note) => {
const year = parseInt(format(new Date(note.date), "yyyy"));
(notesByYear[year] || (notesByYear[year] = [])).push(note);
});
return { return {
props: { props: {
notes, notesByYear,
}, },
}; };
}; };

View File

@ -3807,11 +3807,6 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
lodash.groupby@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1"
integrity sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E=
lodash.merge@^4.6.2: lodash.merge@^4.6.2:
version "4.6.2" version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
@ -4419,7 +4414,7 @@ path-key@^3.0.0, path-key@^3.1.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.6: path-parse@^1.0.6, path-parse@^1.0.7:
version "1.0.7" version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
@ -4871,12 +4866,13 @@ resolve-from@^5.0.0:
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.3.2: resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.3.2:
version "1.20.0" version "1.21.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==
dependencies: dependencies:
is-core-module "^2.2.0" is-core-module "^2.8.0"
path-parse "^1.0.6" path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
resolve@^2.0.0-next.3: resolve@^2.0.0-next.3:
version "2.0.0-next.3" version "2.0.0-next.3"
@ -5462,6 +5458,11 @@ supports-color@^9.2.1:
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.2.1.tgz#599dc9d45acf74c6176e0d880bab1d7d718fe891" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.2.1.tgz#599dc9d45acf74c6176e0d880bab1d7d718fe891"
integrity sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ== integrity sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
svg-parser@^2.0.2: svg-parser@^2.0.2:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5"