mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2026-07-30 13:15:24 -04:00
simplify note parsing logic
This commit is contained in:
@@ -2,90 +2,106 @@ import Link from "next/link";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import TweetEmbed from "react-tweet-embed";
|
import TweetEmbed from "react-tweet-embed";
|
||||||
import Gist from "react-gist";
|
import Gist from "react-gist";
|
||||||
import getNodeText from "../lib/get-node-text";
|
|
||||||
import Video from "./video/FullPageVideo";
|
import Video from "./video/FullPageVideo";
|
||||||
import CopyButton from "./clipboard/CopyButton";
|
import CopyButton from "./clipboard/CopyButton";
|
||||||
|
import getNodeText from "../lib/get-node-text";
|
||||||
import { OctocatOcticon } from "./icons/octicons";
|
import { OctocatOcticon } from "./icons/octicons";
|
||||||
|
|
||||||
import type { LinkProps } from "next/link";
|
import type { LinkProps } from "next/link";
|
||||||
import type { ImageProps } from "next/image";
|
import type { ImageProps } from "next/image";
|
||||||
import type { ReactPlayerProps } from "react-player";
|
import type { ReactPlayerProps } from "react-player";
|
||||||
|
|
||||||
// The following components are all passed into <MDXProvider /> as replacement HTML tags or drop-in React components
|
// The following components are all passed into <MDXProvider /> as replacement HTML tags or drop-in React components
|
||||||
// available in .mdx files containing post content, since they're not directly aware of the components in this folder.
|
// available in .mdx files containing post content, since they're not directly aware of the components in this folder.
|
||||||
const mdxComponents = {
|
|
||||||
a: ({
|
const CustomLink = ({
|
||||||
href,
|
href,
|
||||||
target,
|
target,
|
||||||
rel,
|
rel,
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
}: LinkProps & {
|
}: LinkProps & {
|
||||||
target?: string;
|
target?: string;
|
||||||
rel?: string;
|
rel?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
children?: unknown;
|
children?: unknown;
|
||||||
}) => (
|
}) => (
|
||||||
<Link href={href} passHref={true}>
|
<Link href={href} passHref={true}>
|
||||||
<a className={className} target={target} rel={rel}>
|
<a className={className} target={target} rel={rel}>
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
),
|
);
|
||||||
img: (props: ImageProps) => {
|
|
||||||
|
const CustomImg = (props: ImageProps) => {
|
||||||
|
return (
|
||||||
|
// height and width are part of the props, so they get automatically passed here with {...props}
|
||||||
|
<div className={props.className}>
|
||||||
|
{/* eslint-disable-next-line jsx-a11y/alt-text */}
|
||||||
|
<Image {...props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CustomCode = (props: any) => {
|
||||||
|
if (props.className?.split(" ").includes("hljs")) {
|
||||||
return (
|
return (
|
||||||
// height and width are part of the props, so they get automatically passed here with {...props}
|
<div>
|
||||||
<div className={props.className}>
|
<CopyButton content={getNodeText(props.children)} />
|
||||||
{/* eslint-disable-next-line jsx-a11y/alt-text */}
|
<code {...props}>{props.children}</code>
|
||||||
<Image {...props} />
|
<style jsx>{`
|
||||||
|
div {
|
||||||
|
position: relative;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: scroll;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
} else {
|
||||||
code: (props: any) => {
|
return <code {...props}>{props.children}</code>;
|
||||||
if (props.className?.split(" ").includes("hljs")) {
|
}
|
||||||
return (
|
};
|
||||||
<div>
|
|
||||||
<CopyButton content={getNodeText(props.children)} />
|
|
||||||
<code {...props}>{props.children}</code>
|
|
||||||
<style jsx>{`
|
|
||||||
div {
|
|
||||||
position: relative;
|
|
||||||
max-width: 100%;
|
|
||||||
overflow-x: scroll;
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return <code {...props}>{props.children}</code>;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
video: (props: ReactPlayerProps) => <Video {...props} />,
|
|
||||||
tweet: (props: { id: string }) => (
|
|
||||||
<TweetEmbed
|
|
||||||
id={props.id}
|
|
||||||
options={{
|
|
||||||
dnt: true,
|
|
||||||
align: "center",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
gist: (props: { id: string; file?: string }) => <Gist {...props} />,
|
|
||||||
octocat: (props: { repo: string }) => (
|
|
||||||
<a className="no-underline" href={`https://github.com/${props.repo}`} target="_blank" rel="noopener noreferrer">
|
|
||||||
<OctocatOcticon verticalAlign="text-top" fill="currentColor" />
|
|
||||||
<style jsx>{`
|
|
||||||
a {
|
|
||||||
margin: 0 0.3em;
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
const CustomVideo = (props: ReactPlayerProps) => <Video {...props} />;
|
||||||
color: var(--link);
|
|
||||||
}
|
const CustomTweet = (props: { id: string }) => (
|
||||||
`}</style>
|
<TweetEmbed
|
||||||
</a>
|
id={props.id}
|
||||||
),
|
options={{
|
||||||
|
dnt: true,
|
||||||
|
align: "center",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const CustomGist = (props: { id: string; file?: string }) => <Gist {...props} />;
|
||||||
|
|
||||||
|
const CustomGitHubLink = (props: { repo: string }) => (
|
||||||
|
<a className="no-underline" href={`https://github.com/${props.repo}`} target="_blank" rel="noopener noreferrer">
|
||||||
|
<OctocatOcticon verticalAlign="text-top" fill="currentColor" />
|
||||||
|
<style jsx>{`
|
||||||
|
a {
|
||||||
|
margin: 0 0.3em;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--link);
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
|
||||||
|
const mdxComponents = {
|
||||||
|
a: CustomLink,
|
||||||
|
img: CustomImg,
|
||||||
|
code: CustomCode,
|
||||||
|
video: CustomVideo,
|
||||||
|
tweet: CustomTweet,
|
||||||
|
gist: CustomGist,
|
||||||
|
octocat: CustomGitHubLink,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default mdxComponents;
|
export default mdxComponents;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { format, parseISO } from "date-fns";
|
import { format, parseISO } from "date-fns";
|
||||||
|
import groupBy from "lodash.groupby";
|
||||||
|
|
||||||
import styles from "./List.module.scss";
|
import styles from "./List.module.scss";
|
||||||
|
|
||||||
@@ -9,15 +10,16 @@ type NoteProps = {
|
|||||||
slug: string;
|
slug: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const List = ({ notesByYear }) => {
|
const List = ({ notes }) => {
|
||||||
|
const notesByYear = groupBy(notes, "year");
|
||||||
const sections = [];
|
const sections = [];
|
||||||
|
|
||||||
Object.entries(notesByYear).forEach(([year, notes]: [string, NoteProps[]]) => {
|
Object.entries(notesByYear).forEach(([year, yearNotes]: [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}>
|
||||||
{notes.map((note) => (
|
{yearNotes.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(parseISO(note.date), "MMM d")}</span>
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
+15
-12
@@ -1,37 +1,40 @@
|
|||||||
import { Feed } from "feed";
|
import { Feed } from "feed";
|
||||||
|
import { getAllNotes } from "./parse-notes";
|
||||||
import * as config from "./config";
|
import * as config from "./config";
|
||||||
|
|
||||||
export const buildFeed = (notes: any[]) => {
|
export const buildFeed = () => {
|
||||||
|
const baseURL = config.baseURL || "http://localhost:3000"; // necessary for local testing
|
||||||
const feed = new Feed({
|
const feed = new Feed({
|
||||||
id: `${config.baseURL}/`,
|
id: `${baseURL}/`,
|
||||||
link: `${config.baseURL}/`,
|
link: `${baseURL}/`,
|
||||||
title: config.siteName,
|
title: config.siteName,
|
||||||
description: config.longDescription,
|
description: config.longDescription,
|
||||||
copyright: "https://creativecommons.org/licenses/by/4.0/",
|
copyright: "https://creativecommons.org/licenses/by/4.0/",
|
||||||
updated: new Date(),
|
updated: new Date(),
|
||||||
image: `${config.baseURL}/static/images/me.jpg`,
|
image: `${baseURL}/static/images/me.jpg`,
|
||||||
feedLinks: {
|
feedLinks: {
|
||||||
rss: `${config.baseURL}/feed.xml`,
|
rss: `${baseURL}/feed.xml`,
|
||||||
atom: `${config.baseURL}/feed.atom`,
|
atom: `${baseURL}/feed.atom`,
|
||||||
},
|
},
|
||||||
author: {
|
author: {
|
||||||
name: config.authorName,
|
name: config.authorName,
|
||||||
link: config.baseURL,
|
link: baseURL,
|
||||||
email: "jake@jarv.is",
|
email: "jake@jarv.is",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
notes.forEach((note: { title: any; slug: any; description: any; image: any; date: string | number | Date }) => {
|
const notes = getAllNotes();
|
||||||
|
notes.forEach((note: any) => {
|
||||||
feed.addItem({
|
feed.addItem({
|
||||||
title: note.title,
|
title: note.title,
|
||||||
link: `${config.baseURL}/notes/${note.slug}/`,
|
link: `${baseURL}/notes/${note.slug}/`,
|
||||||
guid: `${config.baseURL}/notes/${note.slug}/`,
|
guid: `${baseURL}/notes/${note.slug}/`,
|
||||||
description: note.description,
|
description: note.description,
|
||||||
image: note.image ? `${config.baseURL}${note.image}` : "",
|
image: note.image ? `${baseURL}${note.image}` : "",
|
||||||
author: [
|
author: [
|
||||||
{
|
{
|
||||||
name: config.authorName,
|
name: config.authorName,
|
||||||
link: config.baseURL,
|
link: baseURL,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
date: new Date(note.date),
|
date: new Date(note.date),
|
||||||
|
|||||||
@@ -27,4 +27,7 @@ export const facebookAppId = "3357248167622283";
|
|||||||
export const webmentionId = "jarv.is";
|
export const webmentionId = "jarv.is";
|
||||||
export const monetization = "$ilp.uphold.com/BJp6d2FrEB69";
|
export const monetization = "$ilp.uphold.com/BJp6d2FrEB69";
|
||||||
|
|
||||||
|
// Next.js constants
|
||||||
|
export const NOTES_DIR = "notes";
|
||||||
|
|
||||||
// ...note / TODO: there is still a metric poop ton of this kind of info hard-coded.
|
// ...note / TODO: there is still a metric poop ton of this kind of info hard-coded.
|
||||||
|
|||||||
+20
-29
@@ -1,38 +1,29 @@
|
|||||||
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 } from "./config";
|
||||||
|
|
||||||
export const NOTES_PATH = path.join(process.cwd(), "notes");
|
export const getNoteData = (file: string) => {
|
||||||
|
const slug = file.replace(/\.mdx$/, "");
|
||||||
|
const fullPath = path.join(process.cwd(), NOTES_DIR, `${slug}.mdx`);
|
||||||
|
const contents = fs.readFileSync(fullPath, "utf8");
|
||||||
|
const { data } = matter(contents);
|
||||||
|
|
||||||
export const getNoteSlugs = () => fs.readdirSync(NOTES_PATH);
|
return {
|
||||||
|
...data,
|
||||||
// Return all md(x) files in NOTES_PATH
|
slug: slug,
|
||||||
export const notePaths = getNoteSlugs().filter((notePath) => /\.mdx?$/.test(notePath));
|
date: parseISO(data.date).toISOString(), // validate/normalize the date string provided from front matter
|
||||||
|
year: parseInt(format(parseISO(data.date), "yyyy")),
|
||||||
export const getNoteBySlug = (slug, fields = []) => {
|
};
|
||||||
const realSlug = slug.replace(/\.mdx$/, "");
|
|
||||||
const fullPath = path.join(NOTES_PATH, `${realSlug}.mdx`);
|
|
||||||
const fileContents = fs.readFileSync(fullPath, "utf8");
|
|
||||||
const { data, content } = matter(fileContents);
|
|
||||||
|
|
||||||
const items = {};
|
|
||||||
|
|
||||||
// Ensure only the minimal needed data is exposed
|
|
||||||
fields.forEach((field) => {
|
|
||||||
if (field === "slug") {
|
|
||||||
items[field] = realSlug;
|
|
||||||
} else if (field === "content") {
|
|
||||||
items[field] = content;
|
|
||||||
} else if (typeof data[field] !== "undefined") {
|
|
||||||
items[field] = data[field];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return items;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAllNotes = (fields = []) =>
|
// all .mdx files in NOTES_DIR
|
||||||
getNoteSlugs()
|
export const getNoteFiles = () =>
|
||||||
.map((slug) => getNoteBySlug(slug, fields))
|
fs.readdirSync(path.join(process.cwd(), NOTES_DIR)).filter((notePath) => /\.mdx$/.test(notePath));
|
||||||
|
|
||||||
|
export const getAllNotes = () =>
|
||||||
|
getNoteFiles()
|
||||||
|
.map((file) => getNoteData(file))
|
||||||
// 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));
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ tags:
|
|||||||
- Coronavirus
|
- Coronavirus
|
||||||
- Public Health
|
- Public Health
|
||||||
- GitHub
|
- GitHub
|
||||||
image: "images/covid19dashboards.png"
|
image: "/static/images/notes/coronavirus-open-source/covid19dashboards.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
We're all quickly learning that worldwide pandemics can bring out both [the best](https://www.vox.com/culture/2020/3/13/21179293/coronavirus-italy-covid19-music-balconies-sing) and [the worst](https://twitter.com/9NewsAUS/status/1236088663093608448) of humanity. But one thing has become readily apparent to me — outside of the large teams of medical professionals risking their lives right this minute, the open source community stands alone in its ability to rapidly organize in the midst of chaos to give back to the world and, in this case, make it safer for all of us.
|
We're all quickly learning that worldwide pandemics can bring out both [the best](https://www.vox.com/culture/2020/3/13/21179293/coronavirus-italy-covid19-music-balconies-sing) and [the worst](https://twitter.com/9NewsAUS/status/1236088663093608448) of humanity. But one thing has become readily apparent to me — outside of the large teams of medical professionals risking their lives right this minute, the open source community stands alone in its ability to rapidly organize in the midst of chaos to give back to the world and, in this case, make it safer for all of us.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ tags:
|
|||||||
- Subdomain Takeover
|
- Subdomain Takeover
|
||||||
- Bug Bounty
|
- Bug Bounty
|
||||||
- Tutorial
|
- Tutorial
|
||||||
image: "images/hackerone-2.png"
|
image: "/static/images/notes/finding-candidates-subdomain-takeovers/hackerone-2.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
A **subdomain takeover** occurs when a subdomain (like _example_.jarv.is) points to a shared hosting account that is abandoned by its owner, leaving the endpoint available to claim for yourself.
|
A **subdomain takeover** occurs when a subdomain (like _example_.jarv.is) points to a shared hosting account that is abandoned by its owner, leaving the endpoint available to claim for yourself.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ tags:
|
|||||||
- Continuous Integration
|
- Continuous Integration
|
||||||
- Docker
|
- Docker
|
||||||
- Open Source
|
- Open Source
|
||||||
image: "images/actions-flow.png"
|
image: "/static/images/notes/github-actions/actions-flow.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
<img
|
<img
|
||||||
|
|||||||
+2
-2
@@ -14,7 +14,7 @@
|
|||||||
"url": "https://github.com/jakejarvis/jarv.is.git"
|
"url": "https://github.com/jakejarvis/jarv.is.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "NODE_OPTIONS='--inspect' next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"postbuild": "next-sitemap",
|
"postbuild": "next-sitemap",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
"eslint-config-next": "~12.0.7",
|
"eslint-config-next": "~12.0.7",
|
||||||
"eslint-config-prettier": "~8.3.0",
|
"eslint-config-prettier": "~8.3.0",
|
||||||
"eslint-plugin-prettier": "~4.0.0",
|
"eslint-plugin-prettier": "~4.0.0",
|
||||||
"lint-staged": "^12.1.4",
|
"lint-staged": "^12.1.5",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"postcss": "^8.4.5",
|
"postcss": "^8.4.5",
|
||||||
"postcss-focus": "^5.0.1",
|
"postcss-focus": "^5.0.1",
|
||||||
|
|||||||
+2
-4
@@ -1,14 +1,12 @@
|
|||||||
import { getAllNotes } from "../lib/parse-notes";
|
|
||||||
import { buildFeed } from "../lib/build-feed";
|
import { buildFeed } from "../lib/build-feed";
|
||||||
import type { GetServerSideProps } from "next";
|
import type { GetServerSideProps } from "next";
|
||||||
|
|
||||||
const AtomPage = () => null;
|
const AtomPage = () => null;
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async (context) => {
|
export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
const notes = getAllNotes(["title", "date", "image", "slug", "description"]);
|
const feed = buildFeed();
|
||||||
const feed = buildFeed(notes);
|
|
||||||
|
|
||||||
const { res } = context;
|
const { res } = context;
|
||||||
|
|
||||||
res.setHeader("content-type", "application/atom+xml");
|
res.setHeader("content-type", "application/atom+xml");
|
||||||
res.write(feed.atom1());
|
res.write(feed.atom1());
|
||||||
res.end();
|
res.end();
|
||||||
|
|||||||
+2
-4
@@ -1,14 +1,12 @@
|
|||||||
import { getAllNotes } from "../lib/parse-notes";
|
|
||||||
import { buildFeed } from "../lib/build-feed";
|
import { buildFeed } from "../lib/build-feed";
|
||||||
import type { GetServerSideProps } from "next";
|
import type { GetServerSideProps } from "next";
|
||||||
|
|
||||||
const RssPage = () => null;
|
const RssPage = () => null;
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async (context) => {
|
export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
const notes = getAllNotes(["title", "date", "image", "slug", "description"]);
|
const feed = buildFeed();
|
||||||
const feed = buildFeed(notes);
|
|
||||||
|
|
||||||
const { res } = context;
|
const { res } = context;
|
||||||
|
|
||||||
res.setHeader("content-type", "application/rss+xml");
|
res.setHeader("content-type", "application/rss+xml");
|
||||||
res.write(feed.rss2());
|
res.write(feed.rss2());
|
||||||
res.end();
|
res.end();
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import Layout from "../../components/Layout";
|
|||||||
import Container from "../../components/Container";
|
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 { notePaths, NOTES_PATH } from "../../lib/parse-notes";
|
|
||||||
import mdxComponents from "../../components/mdxComponents";
|
import mdxComponents from "../../components/mdxComponents";
|
||||||
|
import { getNoteFiles } 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";
|
||||||
|
|
||||||
@@ -59,7 +59,9 @@ const Note = ({ source, frontMatter, slug }) => (
|
|||||||
<Container>
|
<Container>
|
||||||
<Meta {...frontMatter} slug={slug} />
|
<Meta {...frontMatter} slug={slug} />
|
||||||
<Content>
|
<Content>
|
||||||
<MDXRemote {...source} components={mdxComponents} />
|
<div className="markdown">
|
||||||
|
<MDXRemote {...source} components={mdxComponents} />
|
||||||
|
</div>
|
||||||
</Content>
|
</Content>
|
||||||
</Container>
|
</Container>
|
||||||
</Layout>
|
</Layout>
|
||||||
@@ -67,7 +69,7 @@ const Note = ({ source, frontMatter, slug }) => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||||
const filePath = path.join(NOTES_PATH, `${params.slug}.mdx`);
|
const filePath = path.join(process.cwd(), config.NOTES_DIR, `${params.slug}.mdx`);
|
||||||
const source = fs.readFileSync(filePath);
|
const source = fs.readFileSync(filePath);
|
||||||
|
|
||||||
const { content, data } = matter(source);
|
const { content, data } = matter(source);
|
||||||
@@ -98,7 +100,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getStaticPaths: GetStaticPaths = async () => {
|
export const getStaticPaths: GetStaticPaths = async () => {
|
||||||
const paths = notePaths
|
const paths = getNoteFiles()
|
||||||
// Remove file extensions for page paths
|
// Remove file extensions for page paths
|
||||||
.map((notePath) => notePath.replace(/\.mdx?$/, ""))
|
.map((notePath) => notePath.replace(/\.mdx?$/, ""))
|
||||||
// Map the path into the static paths object required by Next.js
|
// Map the path into the static paths object required by Next.js
|
||||||
|
|||||||
@@ -1,28 +1,23 @@
|
|||||||
import { format, parseISO } from "date-fns";
|
|
||||||
import groupBy from "lodash.groupby";
|
|
||||||
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 = ({ notesByYear }) => (
|
const Notes = ({ notes }) => (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Container title="Notes" description="Recent posts by Jake Jarvis.">
|
<Container title="Notes" description="Recent posts by Jake Jarvis.">
|
||||||
<List notesByYear={notesByYear} />
|
<List notes={notes} />
|
||||||
</Container>
|
</Container>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async () => {
|
export const getStaticProps: GetStaticProps = async () => {
|
||||||
const allNotes = getAllNotes(["date", "slug", "title"]);
|
const notes = getAllNotes();
|
||||||
|
|
||||||
// parse year of each note
|
|
||||||
allNotes.map((note: any) => (note.year = parseInt(format(parseISO(note.date), "yyyy"))));
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
notesByYear: groupBy(allNotes, "year"),
|
notes,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
@use "sass:meta";
|
@use "sass:meta";
|
||||||
|
|
||||||
// hacky way to assign different HLJS themes to light/dark mode
|
// hacky way to assign different HLJS themes to light/dark mode
|
||||||
@include meta.load-css("../node_modules/highlight.js/styles/stackoverflow-light.css");
|
.markdown {
|
||||||
|
@include meta.load-css("../node_modules/highlight.js/styles/stackoverflow-light.css");
|
||||||
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
[data-theme="dark"] .markdown {
|
||||||
@include meta.load-css("../node_modules/highlight.js/styles/stackoverflow-dark.css");
|
@include meta.load-css("../node_modules/highlight.js/styles/stackoverflow-dark.css");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2586,7 +2586,7 @@ eslint-import-resolver-typescript@^2.4.0:
|
|||||||
resolve "^1.20.0"
|
resolve "^1.20.0"
|
||||||
tsconfig-paths "^3.9.0"
|
tsconfig-paths "^3.9.0"
|
||||||
|
|
||||||
eslint-module-utils@^2.7.1:
|
eslint-module-utils@^2.7.2:
|
||||||
version "2.7.2"
|
version "2.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz#1d0aa455dcf41052339b63cada8ab5fd57577129"
|
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz#1d0aa455dcf41052339b63cada8ab5fd57577129"
|
||||||
integrity sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==
|
integrity sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==
|
||||||
@@ -2595,23 +2595,23 @@ eslint-module-utils@^2.7.1:
|
|||||||
find-up "^2.1.0"
|
find-up "^2.1.0"
|
||||||
|
|
||||||
eslint-plugin-import@^2.25.2:
|
eslint-plugin-import@^2.25.2:
|
||||||
version "2.25.3"
|
version "2.25.4"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.3.tgz#a554b5f66e08fb4f6dc99221866e57cfff824766"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1"
|
||||||
integrity sha512-RzAVbby+72IB3iOEL8clzPLzL3wpDrlwjsTBAQXgyp5SeTqqY+0bFubwuo+y/HLhNZcXV4XqTBO4LGsfyHIDXg==
|
integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==
|
||||||
dependencies:
|
dependencies:
|
||||||
array-includes "^3.1.4"
|
array-includes "^3.1.4"
|
||||||
array.prototype.flat "^1.2.5"
|
array.prototype.flat "^1.2.5"
|
||||||
debug "^2.6.9"
|
debug "^2.6.9"
|
||||||
doctrine "^2.1.0"
|
doctrine "^2.1.0"
|
||||||
eslint-import-resolver-node "^0.3.6"
|
eslint-import-resolver-node "^0.3.6"
|
||||||
eslint-module-utils "^2.7.1"
|
eslint-module-utils "^2.7.2"
|
||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
is-core-module "^2.8.0"
|
is-core-module "^2.8.0"
|
||||||
is-glob "^4.0.3"
|
is-glob "^4.0.3"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
object.values "^1.1.5"
|
object.values "^1.1.5"
|
||||||
resolve "^1.20.0"
|
resolve "^1.20.0"
|
||||||
tsconfig-paths "^3.11.0"
|
tsconfig-paths "^3.12.0"
|
||||||
|
|
||||||
eslint-plugin-jsx-a11y@^6.5.1:
|
eslint-plugin-jsx-a11y@^6.5.1:
|
||||||
version "6.5.1"
|
version "6.5.1"
|
||||||
@@ -3729,10 +3729,10 @@ lines-and-columns@^1.1.6:
|
|||||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
||||||
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
|
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
|
||||||
|
|
||||||
lint-staged@^12.1.4:
|
lint-staged@^12.1.5:
|
||||||
version "12.1.4"
|
version "12.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-12.1.4.tgz#a92ec8509f13018caaafade61d515c2d5873316e"
|
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-12.1.5.tgz#e05582fc39aed5cb13b9dd1dfb8330407246d809"
|
||||||
integrity sha512-RgDz9nsFsE0/5eL9Vat0AvCuk0+j5mEuzBIVfrRH5FRtt5wibYe8zTjZs2nuqLFrLAGQGYnj8+HJxolcj08i/A==
|
integrity sha512-WyKb+0sNKDTd1LwwAfTBPp0XmdaKkAOEbg4oHE4Kq2+oQVchg/VAcjVQtSqZih1izNsTURjc2EkhG/syRQUXdA==
|
||||||
dependencies:
|
dependencies:
|
||||||
cli-truncate "^3.1.0"
|
cli-truncate "^3.1.0"
|
||||||
colorette "^2.0.16"
|
colorette "^2.0.16"
|
||||||
@@ -5591,7 +5591,7 @@ trough@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/trough/-/trough-2.0.2.tgz#94a3aa9d5ce379fc561f6244905b3f36b7458d96"
|
resolved "https://registry.yarnpkg.com/trough/-/trough-2.0.2.tgz#94a3aa9d5ce379fc561f6244905b3f36b7458d96"
|
||||||
integrity sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==
|
integrity sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==
|
||||||
|
|
||||||
tsconfig-paths@^3.11.0, tsconfig-paths@^3.9.0:
|
tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0:
|
||||||
version "3.12.0"
|
version "3.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b"
|
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b"
|
||||||
integrity sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==
|
integrity sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==
|
||||||
|
|||||||
Reference in New Issue
Block a user