style: auto fix.

This commit is contained in:
2022-10-17 15:37:01 +00:00
parent 10f64a9ba4
commit c081d55a32
68 changed files with 1403 additions and 1114 deletions

View File

@ -1,4 +1,4 @@
import Link from '@/components/Link'
import Link from '@/components/Link';
export default function FourZeroFour() {
return (
@ -12,7 +12,9 @@ export default function FourZeroFour() {
<p className="mb-4 text-xl font-bold leading-normal md:text-2xl">
Sorry we couldn't find this page.
</p>
<p className="mb-8">But dont worry, you can find plenty of other things on our homepage.</p>
<p className="mb-8">
But dont worry, you can find plenty of other things on our homepage.
</p>
<Link href="/">
<button className="focus:shadow-outline-blue inline rounded-lg border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium leading-5 text-white shadow transition-colors duration-150 hover:bg-blue-700 focus:outline-none dark:hover:bg-blue-500">
Back to homepage
@ -20,5 +22,5 @@ export default function FourZeroFour() {
</Link>
</div>
</div>
)
);
}

View File

@ -1,20 +1,20 @@
import '@/css/tailwind.css'
import '@/css/prism.css'
import 'katex/dist/katex.css'
import '@/css/tailwind.css';
import '@/css/prism.css';
import 'katex/dist/katex.css';
import '@fontsource/inter/variable.css'
import '@fontsource/inter/variable.css';
import { ThemeProvider } from 'next-themes'
import type { AppProps } from 'next/app'
import Head from 'next/head'
import { ThemeProvider } from 'next-themes';
import type { AppProps } from 'next/app';
import Head from 'next/head';
import siteMetadata from '@/data/siteMetadata'
import Analytics from '@/components/analytics'
import LayoutWrapper from '@/components/LayoutWrapper'
import { ClientReload } from '@/components/ClientReload'
import siteMetadata from '@/data/siteMetadata';
import Analytics from '@/components/analytics';
import LayoutWrapper from '@/components/LayoutWrapper';
import { ClientReload } from '@/components/ClientReload';
const isDevelopment = process.env.NODE_ENV === 'development'
const isSocket = process.env.SOCKET
const isDevelopment = process.env.NODE_ENV === 'development';
const isSocket = process.env.SOCKET;
export default function App({ Component, pageProps }: AppProps) {
return (
@ -28,5 +28,5 @@ export default function App({ Component, pageProps }: AppProps) {
<Component {...pageProps} />
</LayoutWrapper>
</ThemeProvider>
)
);
}

View File

@ -1,11 +1,15 @@
import Document, { Html, Head, Main, NextScript } from 'next/document'
import Document, { Html, Head, Main, NextScript } from 'next/document';
class MyDocument extends Document {
render() {
return (
<Html lang="en" className="scroll-smooth">
<Head>
<link rel="apple-touch-icon" sizes="76x76" href="/static/favicons/apple-touch-icon.png" />
<link
rel="apple-touch-icon"
sizes="76x76"
href="/static/favicons/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
@ -19,10 +23,22 @@ class MyDocument extends Document {
href="/static/favicons/favicon-16x16.png"
/>
<link rel="manifest" href="/static/favicons/site.webmanifest" />
<link rel="mask-icon" href="/static/favicons/safari-pinned-tab.svg" color="#5bbad5" />
<link
rel="mask-icon"
href="/static/favicons/safari-pinned-tab.svg"
color="#5bbad5"
/>
<meta name="msapplication-TileColor" content="#000000" />
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#fff" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000" />
<meta
name="theme-color"
media="(prefers-color-scheme: light)"
content="#fff"
/>
<meta
name="theme-color"
media="(prefers-color-scheme: dark)"
content="#000"
/>
<link rel="alternate" type="application/rss+xml" href="/feed.xml" />
</Head>
<body className="bg-white text-black antialiased dark:bg-gray-900 dark:text-white">
@ -30,8 +46,8 @@ class MyDocument extends Document {
<NextScript />
</body>
</Html>
)
);
}
}
export default MyDocument
export default MyDocument;

View File

@ -1,21 +1,25 @@
import { MDXLayoutRenderer } from '@/components/MDXComponents'
import { getFileBySlug } from '@/lib/mdx'
import { GetStaticProps, InferGetStaticPropsType } from 'next'
import { AuthorFrontMatter } from 'types/AuthorFrontMatter'
import { MDXLayoutRenderer } from '@/components/MDXComponents';
import { getFileBySlug } from '@/lib/mdx';
import { GetStaticProps, InferGetStaticPropsType } from 'next';
import { AuthorFrontMatter } from 'types/AuthorFrontMatter';
const DEFAULT_LAYOUT = 'AuthorLayout'
const DEFAULT_LAYOUT = 'AuthorLayout';
// @ts-ignore
export const getStaticProps: GetStaticProps<{
authorDetails: { mdxSource: string; frontMatter: AuthorFrontMatter }
authorDetails: { mdxSource: string; frontMatter: AuthorFrontMatter };
}> = async () => {
const authorDetails = await getFileBySlug<AuthorFrontMatter>('authors', ['default'])
const { mdxSource, frontMatter } = authorDetails
return { props: { authorDetails: { mdxSource, frontMatter } } }
}
const authorDetails = await getFileBySlug<AuthorFrontMatter>('authors', [
'default',
]);
const { mdxSource, frontMatter } = authorDetails;
return { props: { authorDetails: { mdxSource, frontMatter } } };
};
export default function About({ authorDetails }: InferGetStaticPropsType<typeof getStaticProps>) {
const { mdxSource, frontMatter } = authorDetails
export default function About({
authorDetails,
}: InferGetStaticPropsType<typeof getStaticProps>) {
const { mdxSource, frontMatter } = authorDetails;
return (
<MDXLayoutRenderer
@ -23,5 +27,5 @@ export default function About({ authorDetails }: InferGetStaticPropsType<typeof
mdxSource={mdxSource}
frontMatter={frontMatter}
/>
)
);
}

View File

@ -1,15 +1,15 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { NextApiRequest, NextApiResponse } from 'next';
// eslint-disable-next-line import/no-anonymous-default-export
export default async (req: NextApiRequest, res: NextApiResponse) => {
const { email } = req.body
const { email } = req.body;
if (!email) {
return res.status(400).json({ error: 'Email is required' })
return res.status(400).json({ error: 'Email is required' });
}
try {
const API_KEY = process.env.BUTTONDOWN_API_KEY
const buttondownRoute = `${process.env.BUTTONDOWN_API_URL}subscribers`
const API_KEY = process.env.BUTTONDOWN_API_KEY;
const buttondownRoute = `${process.env.BUTTONDOWN_API_URL}subscribers`;
const response = await fetch(buttondownRoute, {
body: JSON.stringify({
email,
@ -19,14 +19,16 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
'Content-Type': 'application/json',
},
method: 'POST',
})
});
if (response.status >= 400) {
return res.status(500).json({ error: `There was an error subscribing to the list.` })
return res
.status(500)
.json({ error: `There was an error subscribing to the list.` });
}
return res.status(201).json({ error: '' })
return res.status(201).json({ error: '' });
} catch (error) {
return res.status(500).json({ error: error.message || error.toString() })
return res.status(500).json({ error: error.message || error.toString() });
}
}
};

View File

@ -1,20 +1,20 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { NextApiRequest, NextApiResponse } from 'next';
/* eslint-disable import/no-anonymous-default-export */
export default async (req: NextApiRequest, res: NextApiResponse) => {
const { email } = req.body
const { email } = req.body;
if (!email) {
return res.status(400).json({ error: 'Email is required' })
return res.status(400).json({ error: 'Email is required' });
}
try {
const FORM_ID = process.env.CONVERTKIT_FORM_ID
const API_KEY = process.env.CONVERTKIT_API_KEY
const API_URL = process.env.CONVERTKIT_API_URL
const FORM_ID = process.env.CONVERTKIT_FORM_ID;
const API_KEY = process.env.CONVERTKIT_API_KEY;
const API_URL = process.env.CONVERTKIT_API_URL;
// Send request to ConvertKit
const data = { email, api_key: API_KEY }
const data = { email, api_key: API_KEY };
const response = await fetch(`${API_URL}forms/${FORM_ID}/subscribe`, {
body: JSON.stringify(data),
@ -22,16 +22,16 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
'Content-Type': 'application/json',
},
method: 'POST',
})
});
if (response.status >= 400) {
return res.status(400).json({
error: `There was an error subscribing to the list.`,
})
});
}
return res.status(201).json({ error: '' })
return res.status(201).json({ error: '' });
} catch (error) {
return res.status(500).json({ error: error.message || error.toString() })
return res.status(500).json({ error: error.message || error.toString() });
}
}
};

View File

@ -1,15 +1,15 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { NextApiRequest, NextApiResponse } from 'next';
/* eslint-disable import/no-anonymous-default-export */
export default async (req: NextApiRequest, res: NextApiResponse) => {
const { email } = req.body
const { email } = req.body;
if (!email) {
return res.status(400).json({ error: 'Email is required' })
return res.status(400).json({ error: 'Email is required' });
}
try {
const API_KEY = process.env.KLAVIYO_API_KEY
const LIST_ID = process.env.KLAVIYO_LIST_ID
const API_KEY = process.env.KLAVIYO_API_KEY;
const LIST_ID = process.env.KLAVIYO_LIST_ID;
const response = await fetch(
`https://a.klaviyo.com/api/v2/list/${LIST_ID}/subscribe?api_key=${API_KEY}`,
{
@ -24,14 +24,14 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
profiles: [{ email: email }],
}),
}
)
);
if (response.status >= 400) {
return res.status(400).json({
error: `There was an error subscribing to the list.`,
})
});
}
return res.status(201).json({ error: '' })
return res.status(201).json({ error: '' });
} catch (error) {
return res.status(500).json({ error: error.message || error.toString() })
return res.status(500).json({ error: error.message || error.toString() });
}
}
};

View File

@ -1,26 +1,26 @@
import { NextApiRequest, NextApiResponse } from 'next'
import mailchimp from '@mailchimp/mailchimp_marketing'
import { NextApiRequest, NextApiResponse } from 'next';
import mailchimp from '@mailchimp/mailchimp_marketing';
mailchimp.setConfig({
apiKey: process.env.MAILCHIMP_API_KEY,
server: process.env.MAILCHIMP_API_SERVER, // E.g. us1
})
});
// eslint-disable-next-line import/no-anonymous-default-export
export default async (req: NextApiRequest, res: NextApiResponse) => {
const { email } = req.body
const { email } = req.body;
if (!email) {
return res.status(400).json({ error: 'Email is required' })
return res.status(400).json({ error: 'Email is required' });
}
try {
await mailchimp.lists.addListMember(process.env.MAILCHIMP_AUDIENCE_ID, {
email_address: email,
status: 'subscribed',
})
return res.status(201).json({ error: '' })
});
return res.status(201).json({ error: '' });
} catch (error) {
return res.status(500).json({ error: error.message || error.toString() })
return res.status(500).json({ error: error.message || error.toString() });
}
}
};

View File

@ -1,26 +1,26 @@
import { getAllFilesFrontMatter } from '@/lib/mdx'
import siteMetadata from '@/data/siteMetadata'
import ListLayout from '@/layouts/ListLayout'
import { PageSEO } from '@/components/SEO'
import { GetStaticProps, InferGetStaticPropsType } from 'next'
import { ComponentProps } from 'react'
import { getAllFilesFrontMatter } from '@/lib/mdx';
import siteMetadata from '@/data/siteMetadata';
import ListLayout from '@/layouts/ListLayout';
import { PageSEO } from '@/components/SEO';
import { GetStaticProps, InferGetStaticPropsType } from 'next';
import { ComponentProps } from 'react';
export const POSTS_PER_PAGE = 5
export const POSTS_PER_PAGE = 5;
export const getStaticProps: GetStaticProps<{
posts: ComponentProps<typeof ListLayout>['posts']
initialDisplayPosts: ComponentProps<typeof ListLayout>['initialDisplayPosts']
pagination: ComponentProps<typeof ListLayout>['pagination']
posts: ComponentProps<typeof ListLayout>['posts'];
initialDisplayPosts: ComponentProps<typeof ListLayout>['initialDisplayPosts'];
pagination: ComponentProps<typeof ListLayout>['pagination'];
}> = async () => {
const posts = await getAllFilesFrontMatter('blog')
const initialDisplayPosts = posts.slice(0, POSTS_PER_PAGE)
const posts = await getAllFilesFrontMatter('blog');
const initialDisplayPosts = posts.slice(0, POSTS_PER_PAGE);
const pagination = {
currentPage: 1,
totalPages: Math.ceil(posts.length / POSTS_PER_PAGE),
}
};
return { props: { initialDisplayPosts, posts, pagination } }
}
return { props: { initialDisplayPosts, posts, pagination } };
};
export default function Blog({
posts,
@ -29,7 +29,10 @@ export default function Blog({
}: InferGetStaticPropsType<typeof getStaticProps>) {
return (
<>
<PageSEO title={`Blog - ${siteMetadata.author}`} description={siteMetadata.description} />
<PageSEO
title={`Blog - ${siteMetadata.author}`}
description={siteMetadata.description}
/>
<ListLayout
posts={posts}
initialDisplayPosts={initialDisplayPosts}
@ -37,5 +40,5 @@ export default function Blog({
title="All Posts"
/>
</>
)
);
}

View File

@ -1,17 +1,22 @@
import fs from 'fs'
import PageTitle from '@/components/PageTitle'
import generateRss from '@/lib/generate-rss'
import { MDXLayoutRenderer } from '@/components/MDXComponents'
import { formatSlug, getAllFilesFrontMatter, getFileBySlug, getFiles } from '@/lib/mdx'
import { GetStaticProps, InferGetStaticPropsType } from 'next'
import { AuthorFrontMatter } from 'types/AuthorFrontMatter'
import { PostFrontMatter } from 'types/PostFrontMatter'
import { Toc } from 'types/Toc'
import fs from 'fs';
import PageTitle from '@/components/PageTitle';
import generateRss from '@/lib/generate-rss';
import { MDXLayoutRenderer } from '@/components/MDXComponents';
import {
formatSlug,
getAllFilesFrontMatter,
getFileBySlug,
getFiles,
} from '@/lib/mdx';
import { GetStaticProps, InferGetStaticPropsType } from 'next';
import { AuthorFrontMatter } from 'types/AuthorFrontMatter';
import { PostFrontMatter } from 'types/PostFrontMatter';
import { Toc } from 'types/Toc';
const DEFAULT_LAYOUT = 'PostLayout'
const DEFAULT_LAYOUT = 'PostLayout';
export async function getStaticPaths() {
const posts = getFiles('blog')
const posts = getFiles('blog');
return {
paths: posts.map((p) => ({
params: {
@ -19,34 +24,38 @@ export async function getStaticPaths() {
},
})),
fallback: false,
}
};
}
// @ts-ignore
export const getStaticProps: GetStaticProps<{
post: { mdxSource: string; toc: Toc; frontMatter: PostFrontMatter }
authorDetails: AuthorFrontMatter[]
prev?: { slug: string; title: string }
next?: { slug: string; title: string }
post: { mdxSource: string; toc: Toc; frontMatter: PostFrontMatter };
authorDetails: AuthorFrontMatter[];
prev?: { slug: string; title: string };
next?: { slug: string; title: string };
}> = async ({ params }) => {
const slug = (params.slug as string[]).join('/')
const allPosts = await getAllFilesFrontMatter('blog')
const postIndex = allPosts.findIndex((post) => formatSlug(post.slug) === slug)
const prev: { slug: string; title: string } = allPosts[postIndex + 1] || null
const next: { slug: string; title: string } = allPosts[postIndex - 1] || null
const post = await getFileBySlug<PostFrontMatter>('blog', slug)
const slug = (params.slug as string[]).join('/');
const allPosts = await getAllFilesFrontMatter('blog');
const postIndex = allPosts.findIndex(
(post) => formatSlug(post.slug) === slug
);
const prev: { slug: string; title: string } = allPosts[postIndex + 1] || null;
const next: { slug: string; title: string } = allPosts[postIndex - 1] || null;
const post = await getFileBySlug<PostFrontMatter>('blog', slug);
// @ts-ignore
const authorList = post.frontMatter.authors || ['default']
const authorList = post.frontMatter.authors || ['default'];
const authorPromise = authorList.map(async (author) => {
const authorResults = await getFileBySlug<AuthorFrontMatter>('authors', [author])
return authorResults.frontMatter
})
const authorDetails = await Promise.all(authorPromise)
const authorResults = await getFileBySlug<AuthorFrontMatter>('authors', [
author,
]);
return authorResults.frontMatter;
});
const authorDetails = await Promise.all(authorPromise);
// rss
if (allPosts.length > 0) {
const rss = generateRss(allPosts)
fs.writeFileSync('./public/feed.xml', rss)
const rss = generateRss(allPosts);
fs.writeFileSync('./public/feed.xml', rss);
}
return {
@ -56,8 +65,8 @@ export const getStaticProps: GetStaticProps<{
prev,
next,
},
}
}
};
};
export default function Blog({
post,
@ -65,7 +74,7 @@ export default function Blog({
prev,
next,
}: InferGetStaticPropsType<typeof getStaticProps>) {
const { mdxSource, toc, frontMatter } = post
const { mdxSource, toc, frontMatter } = post;
return (
<>
@ -90,5 +99,5 @@ export default function Blog({
</div>
)}
</>
)
);
}

View File

@ -1,42 +1,42 @@
import { PageSEO } from '@/components/SEO'
import siteMetadata from '@/data/siteMetadata'
import { getAllFilesFrontMatter } from '@/lib/mdx'
import ListLayout from '@/layouts/ListLayout'
import { POSTS_PER_PAGE } from '../../blog'
import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next'
import { PostFrontMatter } from 'types/PostFrontMatter'
import { PageSEO } from '@/components/SEO';
import siteMetadata from '@/data/siteMetadata';
import { getAllFilesFrontMatter } from '@/lib/mdx';
import ListLayout from '@/layouts/ListLayout';
import { POSTS_PER_PAGE } from '../../blog';
import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next';
import { PostFrontMatter } from 'types/PostFrontMatter';
export const getStaticPaths: GetStaticPaths<{ page: string }> = async () => {
const totalPosts = await getAllFilesFrontMatter('blog')
const totalPages = Math.ceil(totalPosts.length / POSTS_PER_PAGE)
const totalPosts = await getAllFilesFrontMatter('blog');
const totalPages = Math.ceil(totalPosts.length / POSTS_PER_PAGE);
const paths = Array.from({ length: totalPages }, (_, i) => ({
params: { page: (i + 1).toString() },
}))
}));
return {
paths,
fallback: false,
}
}
};
};
export const getStaticProps: GetStaticProps<{
posts: PostFrontMatter[]
initialDisplayPosts: PostFrontMatter[]
pagination: { currentPage: number; totalPages: number }
posts: PostFrontMatter[];
initialDisplayPosts: PostFrontMatter[];
pagination: { currentPage: number; totalPages: number };
}> = async (context) => {
const {
params: { page },
} = context
const posts = await getAllFilesFrontMatter('blog')
const pageNumber = parseInt(page as string)
} = context;
const posts = await getAllFilesFrontMatter('blog');
const pageNumber = parseInt(page as string);
const initialDisplayPosts = posts.slice(
POSTS_PER_PAGE * (pageNumber - 1),
POSTS_PER_PAGE * pageNumber
)
);
const pagination = {
currentPage: pageNumber,
totalPages: Math.ceil(posts.length / POSTS_PER_PAGE),
}
};
return {
props: {
@ -44,8 +44,8 @@ export const getStaticProps: GetStaticProps<{
initialDisplayPosts,
pagination,
},
}
}
};
};
export default function PostPage({
posts,
@ -54,7 +54,10 @@ export default function PostPage({
}: InferGetStaticPropsType<typeof getStaticProps>) {
return (
<>
<PageSEO title={siteMetadata.title} description={siteMetadata.description} />
<PageSEO
title={siteMetadata.title}
description={siteMetadata.description}
/>
<ListLayout
posts={posts}
initialDisplayPosts={initialDisplayPosts}
@ -62,5 +65,5 @@ export default function PostPage({
title="All Posts"
/>
</>
)
);
}

View File

@ -1,25 +1,32 @@
import Link from '@/components/Link'
import { PageSEO } from '@/components/SEO'
import Tag from '@/components/Tag'
import siteMetadata from '@/data/siteMetadata'
import { getAllFilesFrontMatter } from '@/lib/mdx'
import formatDate from '@/lib/utils/formatDate'
import { GetStaticProps, InferGetStaticPropsType } from 'next'
import { PostFrontMatter } from 'types/PostFrontMatter'
import NewsletterForm from '@/components/NewsletterForm'
import Link from '@/components/Link';
import { PageSEO } from '@/components/SEO';
import Tag from '@/components/Tag';
import siteMetadata from '@/data/siteMetadata';
import { getAllFilesFrontMatter } from '@/lib/mdx';
import formatDate from '@/lib/utils/formatDate';
import { GetStaticProps, InferGetStaticPropsType } from 'next';
import { PostFrontMatter } from 'types/PostFrontMatter';
import NewsletterForm from '@/components/NewsletterForm';
const MAX_DISPLAY = 5
const MAX_DISPLAY = 5;
export const getStaticProps: GetStaticProps<{ posts: PostFrontMatter[] }> = async () => {
const posts = await getAllFilesFrontMatter('blog')
export const getStaticProps: GetStaticProps<{
posts: PostFrontMatter[];
}> = async () => {
const posts = await getAllFilesFrontMatter('blog');
return { props: { posts } }
}
return { props: { posts } };
};
export default function Home({ posts }: InferGetStaticPropsType<typeof getStaticProps>) {
export default function Home({
posts,
}: InferGetStaticPropsType<typeof getStaticProps>) {
return (
<>
<PageSEO title={siteMetadata.title} description={siteMetadata.description} />
<PageSEO
title={siteMetadata.title}
description={siteMetadata.description}
/>
<div className="divide-y divide-gray-200 dark:divide-gray-700">
<div className="space-y-2 pt-6 pb-8 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
@ -32,7 +39,7 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
<ul className="divide-y divide-gray-200 dark:divide-gray-700">
{!posts.length && '没有找到文章。 😭'}
{posts.slice(0, MAX_DISPLAY).map((frontMatter) => {
const { slug, date, title, summary, tags } = frontMatter
const { slug, date, title, summary, tags } = frontMatter;
return (
<li key={slug} className="py-12">
<article>
@ -49,8 +56,7 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
<h2 className="text-2xl font-bold leading-8 tracking-tight">
<Link
href={`/blog/${slug}`}
className="text-gray-900 dark:text-gray-100"
>
className="text-gray-900 dark:text-gray-100">
{title}
</Link>
</h2>
@ -68,8 +74,7 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
<Link
href={`/blog/${slug}`}
className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
aria-label={`Read "${title}"`}
>
aria-label={`Read "${title}"`}>
Read more &rarr;
</Link>
</div>
@ -77,7 +82,7 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
</div>
</article>
</li>
)
);
})}
</ul>
</div>
@ -86,8 +91,7 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
<Link
href="/blog"
className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
aria-label="all posts"
>
aria-label="all posts">
All Posts &rarr;
</Link>
</div>
@ -98,5 +102,5 @@ export default function Home({ posts }: InferGetStaticPropsType<typeof getStatic
</div>
)}
</>
)
);
}

View File

@ -1,12 +1,15 @@
import siteMetadata from '@/data/siteMetadata'
import projectsData from '@/data/projectsData'
import Card from '@/components/Card'
import { PageSEO } from '@/components/SEO'
import siteMetadata from '@/data/siteMetadata';
import projectsData from '@/data/projectsData';
import Card from '@/components/Card';
import { PageSEO } from '@/components/SEO';
export default function Projects() {
return (
<>
<PageSEO title={`Projects - ${siteMetadata.author}`} description={siteMetadata.description} />
<PageSEO
title={`Projects - ${siteMetadata.author}`}
description={siteMetadata.description}
/>
<div className="divide-y divide-gray-200 dark:divide-gray-700">
<div className="space-y-2 pt-6 pb-8 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
@ -31,5 +34,5 @@ export default function Projects() {
</div>
</div>
</>
)
);
}

View File

@ -1,22 +1,29 @@
import Link from '@/components/Link'
import { PageSEO } from '@/components/SEO'
import Tag from '@/components/Tag'
import siteMetadata from '@/data/siteMetadata'
import { getAllTags } from '@/lib/tags'
import kebabCase from '@/lib/utils/kebabCase'
import { GetStaticProps, InferGetStaticPropsType } from 'next'
import Link from '@/components/Link';
import { PageSEO } from '@/components/SEO';
import Tag from '@/components/Tag';
import siteMetadata from '@/data/siteMetadata';
import { getAllTags } from '@/lib/tags';
import kebabCase from '@/lib/utils/kebabCase';
import { GetStaticProps, InferGetStaticPropsType } from 'next';
export const getStaticProps: GetStaticProps<{ tags: Record<string, number> }> = async () => {
const tags = await getAllTags('blog')
export const getStaticProps: GetStaticProps<{
tags: Record<string, number>;
}> = async () => {
const tags = await getAllTags('blog');
return { props: { tags } }
}
return { props: { tags } };
};
export default function Tags({ tags }: InferGetStaticPropsType<typeof getStaticProps>) {
const sortedTags = Object.keys(tags).sort((a, b) => tags[b] - tags[a])
export default function Tags({
tags,
}: InferGetStaticPropsType<typeof getStaticProps>) {
const sortedTags = Object.keys(tags).sort((a, b) => tags[b] - tags[a]);
return (
<>
<PageSEO title={`Tags - ${siteMetadata.author}`} description="Things I blog about" />
<PageSEO
title={`Tags - ${siteMetadata.author}`}
description="Things I blog about"
/>
<div className="flex flex-col items-start justify-start divide-y divide-gray-200 dark:divide-gray-700 md:mt-24 md:flex-row md:items-center md:justify-center md:space-x-6 md:divide-y-0">
<div className="space-x-2 pt-6 pb-8 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:border-r-2 md:px-6 md:text-6xl md:leading-14">
@ -31,15 +38,14 @@ export default function Tags({ tags }: InferGetStaticPropsType<typeof getStaticP
<Tag text={t} />
<Link
href={`/tags/${kebabCase(t)}`}
className="-ml-2 text-sm font-semibold uppercase text-gray-600 dark:text-gray-300"
>
className="-ml-2 text-sm font-semibold uppercase text-gray-600 dark:text-gray-300">
{` (${tags[t]})`}
</Link>
</div>
)
);
})}
</div>
</div>
</>
)
);
}

View File

@ -1,19 +1,19 @@
import { TagSEO } from '@/components/SEO'
import siteMetadata from '@/data/siteMetadata'
import ListLayout from '@/layouts/ListLayout'
import generateRss from '@/lib/generate-rss'
import { getAllFilesFrontMatter } from '@/lib/mdx'
import { getAllTags } from '@/lib/tags'
import kebabCase from '@/lib/utils/kebabCase'
import fs from 'fs'
import { GetStaticProps, InferGetStaticPropsType } from 'next'
import path from 'path'
import { PostFrontMatter } from 'types/PostFrontMatter'
import { TagSEO } from '@/components/SEO';
import siteMetadata from '@/data/siteMetadata';
import ListLayout from '@/layouts/ListLayout';
import generateRss from '@/lib/generate-rss';
import { getAllFilesFrontMatter } from '@/lib/mdx';
import { getAllTags } from '@/lib/tags';
import kebabCase from '@/lib/utils/kebabCase';
import fs from 'fs';
import { GetStaticProps, InferGetStaticPropsType } from 'next';
import path from 'path';
import { PostFrontMatter } from 'types/PostFrontMatter';
const root = process.cwd()
const root = process.cwd();
export async function getStaticPaths() {
const tags = await getAllTags('blog')
const tags = await getAllTags('blog');
return {
paths: Object.keys(tags).map((tag) => ({
@ -22,32 +22,37 @@ export async function getStaticPaths() {
},
})),
fallback: false,
}
};
}
export const getStaticProps: GetStaticProps<{ posts: PostFrontMatter[]; tag: string }> = async (
context
) => {
const tag = context.params.tag as string
const allPosts = await getAllFilesFrontMatter('blog')
export const getStaticProps: GetStaticProps<{
posts: PostFrontMatter[];
tag: string;
}> = async (context) => {
const tag = context.params.tag as string;
const allPosts = await getAllFilesFrontMatter('blog');
const filteredPosts = allPosts.filter(
(post) => post.draft !== true && post.tags.map((t) => kebabCase(t)).includes(tag)
)
(post) =>
post.draft !== true && post.tags.map((t) => kebabCase(t)).includes(tag)
);
// rss
if (filteredPosts.length > 0) {
const rss = generateRss(filteredPosts, `tags/${tag}/feed.xml`)
const rssPath = path.join(root, 'public', 'tags', tag)
fs.mkdirSync(rssPath, { recursive: true })
fs.writeFileSync(path.join(rssPath, 'feed.xml'), rss)
const rss = generateRss(filteredPosts, `tags/${tag}/feed.xml`);
const rssPath = path.join(root, 'public', 'tags', tag);
fs.mkdirSync(rssPath, { recursive: true });
fs.writeFileSync(path.join(rssPath, 'feed.xml'), rss);
}
return { props: { posts: filteredPosts, tag } }
}
return { props: { posts: filteredPosts, tag } };
};
export default function Tag({ posts, tag }: InferGetStaticPropsType<typeof getStaticProps>) {
export default function Tag({
posts,
tag,
}: InferGetStaticPropsType<typeof getStaticProps>) {
// Capitalize first letter and convert space to dash
const title = tag[0].toUpperCase() + tag.split(' ').join('-').slice(1)
const title = tag[0].toUpperCase() + tag.split(' ').join('-').slice(1);
return (
<>
<TagSEO
@ -56,5 +61,5 @@ export default function Tag({ posts, tag }: InferGetStaticPropsType<typeof getSt
/>
<ListLayout posts={posts} title={title} />
</>
)
);
}