1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-06-30 22:06:38 -04:00

organize types a bit more sanely & bump deps

This commit is contained in:
2022-02-24 07:06:34 -05:00
parent d24d29a04e
commit e6f1955efb
16 changed files with 267 additions and 645 deletions

View File

@ -1,71 +1,76 @@
import Link from "next/link";
import classNames from "classnames"; import classNames from "classnames";
import { format } from "date-fns"; import { format } from "date-fns";
import HitCounter from "../HitCounter/HitCounter"; import HitCounter from "../HitCounter/HitCounter";
import NoteTitle from "../NoteTitle/NoteTitle";
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";
import type { NoteMetaType } from "../../types"; import type { NoteType } from "../../types";
import styles from "./NoteMeta.module.css"; import styles from "./NoteMeta.module.css";
import Link from "next/link";
export type NoteMetaProps = Pick<NoteMetaType, "slug" | "date" | "title" | "tags">; export type NoteMetaProps = Pick<NoteType["frontMatter"], "slug" | "date" | "title" | "htmlTitle" | "tags">;
const NoteMeta = ({ slug, date, title, tags = [] }: NoteMetaProps) => ( const NoteMeta = ({ slug, date, title, htmlTitle, tags = [] }: NoteMetaProps) => (
<div className={styles.meta}> <>
<div className={styles.meta_item}> <div className={styles.meta}>
<Link <div className={styles.meta_item}>
href={{ <Link
pathname: "/notes/[slug]/", href={{
query: { slug: slug }, pathname: "/notes/[slug]/",
}} query: { slug },
> }}
<a className={styles.date_link}> >
<a className={styles.date_link}>
<span>
<DateIcon className={styles.icon} />
</span>
<span title={format(new Date(date), "PPppp")}>{format(new Date(date), "MMMM d, yyyy")}</span>
</a>
</Link>
</div>
{tags.length > 0 && (
<div className={classNames(styles.meta_item, styles.tags)}>
<span> <span>
<DateIcon className={styles.icon} /> <TagIcon className={styles.icon} />
</span> </span>
<span title={format(new Date(date), "PPppp")}>{format(new Date(date), "MMMM d, yyyy")}</span> {tags.map((tag) => (
<span key={tag} className={styles.tag}>
{tag}
</span>
))}
</div>
)}
<div className={styles.meta_item}>
<a
className={styles.edit_link}
href={`https://github.com/${config.githubRepo}/blob/main/notes/${slug}.mdx`}
target="_blank"
rel="noopener noreferrer"
title={`Edit "${title}" on GitHub`}
>
<span>
<EditIcon className={styles.icon} />
</span>
<span>Improve This Post</span>
</a> </a>
</Link> </div>
</div>
{tags.length > 0 && ( {/* only count hits on production site */}
<div className={classNames(styles.meta_item, styles.tags)}> {process.env.NEXT_PUBLIC_VERCEL_ENV === "production" && (
<span> <div className={classNames(styles.meta_item, styles.views)}>
<TagIcon className={styles.icon} /> <span>
</span> <ViewsIcon className={styles.icon} />
{tags.map((tag) => (
<span key={tag} className={styles.tag}>
{tag}
</span> </span>
))} <HitCounter slug={`notes/${slug}`} />
</div> </div>
)} )}
<div className={styles.meta_item}>
<a
className={styles.edit_link}
href={`https://github.com/${config.githubRepo}/blob/main/notes/${slug}.mdx`}
target="_blank"
rel="noopener noreferrer"
title={`Edit "${title}" on GitHub`}
>
<span>
<EditIcon className={styles.icon} />
</span>
<span>Improve This Post</span>
</a>
</div> </div>
{/* only count hits on production site */} <NoteTitle slug={slug} htmlTitle={htmlTitle || title} />
{process.env.NEXT_PUBLIC_VERCEL_ENV === "production" && ( </>
<div className={classNames(styles.meta_item, styles.views)}>
<span>
<ViewsIcon className={styles.icon} />
</span>
<HitCounter slug={`notes/${slug}`} />
</div>
)}
</div>
); );
export default NoteMeta; export default NoteMeta;

View File

@ -1,17 +1,17 @@
import Link from "next/link"; import Link from "next/link";
import classNames from "classnames"; import classNames from "classnames";
import type { NoteMetaType } from "../../types"; import type { NoteType } from "../../types";
import styles from "./NoteTitle.module.css"; import styles from "./NoteTitle.module.css";
export type NoteTitleProps = Pick<NoteMetaType, "slug" | "htmlTitle"> & JSX.IntrinsicElements["h1"]; export type NoteTitleProps = Pick<NoteType["frontMatter"], "slug" | "htmlTitle"> & JSX.IntrinsicElements["h1"];
const NoteTitle = ({ slug, htmlTitle, className, ...rest }: NoteTitleProps) => ( const NoteTitle = ({ slug, htmlTitle, className, ...rest }: NoteTitleProps) => (
<h1 className={classNames(styles.title, className)} {...rest}> <h1 className={classNames(styles.title, className)} {...rest}>
<Link <Link
href={{ href={{
pathname: "/notes/[slug]/", pathname: "/notes/[slug]/",
query: { slug: slug }, query: { slug },
}} }}
> >
<a className={styles.link} dangerouslySetInnerHTML={{ __html: htmlTitle }} /> <a className={styles.link} dangerouslySetInnerHTML={{ __html: htmlTitle }} />

View File

@ -1,17 +1,17 @@
import { format } from "date-fns"; import { format } from "date-fns";
import Link from "../Link/Link"; import Link from "../Link/Link";
import type { NoteMetaType } from "../../types"; import type { NoteType } from "../../types";
import styles from "./NotesList.module.css"; import styles from "./NotesList.module.css";
export type NotesListProps = { export type NotesListProps = {
notesByYear: Record<string, NoteMetaType[]>; notesByYear: Record<string, NoteType["frontMatter"][]>;
}; };
const NotesList = ({ notesByYear }: NotesListProps) => { const NotesList = ({ notesByYear }: NotesListProps) => {
const sections = []; const sections = [];
Object.entries(notesByYear).forEach(([year, notes]: [string, NoteMetaType[]]) => { Object.entries(notesByYear).forEach(([year, notes]: [string, NoteType["frontMatter"][]]) => {
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>
@ -23,7 +23,7 @@ const NotesList = ({ notesByYear }: NotesListProps) => {
<Link <Link
href={{ href={{
pathname: "/notes/[slug]/", pathname: "/notes/[slug]/",
query: { slug: slug }, query: { slug },
}} }}
dangerouslySetInnerHTML={{ __html: htmlTitle }} dangerouslySetInnerHTML={{ __html: htmlTitle }}
/> />

View File

@ -2,11 +2,11 @@ import classNames from "classnames";
import { intlFormat, formatDistanceToNowStrict } from "date-fns"; import { intlFormat, formatDistanceToNowStrict } from "date-fns";
import Link from "../Link/Link"; import Link from "../Link/Link";
import { StarOcticon, ForkOcticon } from "../Icons"; import { StarOcticon, ForkOcticon } from "../Icons";
import type { RepoType } from "../../types"; import type { RepositoryType } from "../../types";
import styles from "./RepositoryCard.module.css"; import styles from "./RepositoryCard.module.css";
export type RepositoryCardProps = RepoType & { export type RepositoryCardProps = RepositoryType & {
className?: string; className?: string;
}; };

View File

@ -9,7 +9,7 @@ export type TweetEmbedProps = {
const TweetEmbed = ({ id, className, options }: TweetEmbedProps) => ( const TweetEmbed = ({ id, className, options }: TweetEmbedProps) => (
<Tweet <Tweet
className={className} className={className}
id={id} tweetId={id}
options={{ options={{
dnt: true, dnt: true,
align: "center", align: "center",

View File

@ -16,7 +16,7 @@ import rehypeSlug from "rehype-slug";
import rehypePrism from "rehype-prism-plus"; import rehypePrism from "rehype-prism-plus";
import type { MinifyOptions } from "terser"; import type { MinifyOptions } from "terser";
import type { NoteMetaType, NoteType } from "../types"; import type { NoteType } from "../types";
// returns all .mdx files in NOTES_DIR (without .mdx extension) // returns all .mdx files in NOTES_DIR (without .mdx extension)
export const getNoteSlugs = () => export const getNoteSlugs = () =>
@ -26,7 +26,7 @@ export const getNoteSlugs = () =>
.map((noteFile) => noteFile.replace(/\.mdx$/, "")); .map((noteFile) => noteFile.replace(/\.mdx$/, ""));
// returns front matter and/or *raw* markdown contents of a given slug // returns front matter and/or *raw* markdown contents of a given slug
export const getNoteData = (slug: string): { frontMatter: NoteMetaType; content: string } => { export const getNoteData = (slug: string): Omit<NoteType, "source"> & { content: string } => {
const fullPath = path.join(process.cwd(), NOTES_DIR, `${slug}.mdx`); const fullPath = path.join(process.cwd(), NOTES_DIR, `${slug}.mdx`);
const rawContent = fs.readFileSync(fullPath, "utf8"); const rawContent = fs.readFileSync(fullPath, "utf8");
const { data, content } = matter(rawContent); const { data, content } = matter(rawContent);
@ -47,7 +47,7 @@ export const getNoteData = (slug: string): { frontMatter: NoteMetaType; content:
// return both the parsed YAML front matter (with a few amendments) and the raw, unparsed markdown content // return both the parsed YAML front matter (with a few amendments) and the raw, unparsed markdown content
return { return {
frontMatter: { frontMatter: {
...(data as Omit<NoteMetaType, "slug" | "title" | "htmlTitle" | "permalink" | "date" | "readingMins">), ...(data as Omit<NoteType["frontMatter"], "slug" | "title" | "htmlTitle" | "permalink" | "date" | "readingMins">),
// zero markdown title: // zero markdown title:
title: removeMarkdown(data.title), title: removeMarkdown(data.title),
// parsed markdown title: // parsed markdown title:
@ -103,4 +103,4 @@ export const getNote = async (slug: string): Promise<NoteType> => {
export const getAllNotes = () => export const getAllNotes = () =>
getNoteSlugs() getNoteSlugs()
.map((slug) => getNoteData(slug).frontMatter) .map((slug) => getNoteData(slug).frontMatter)
.sort((note1: NoteMetaType, note2: NoteMetaType) => (note1.date > note2.date ? -1 : 1)); .sort((note1: NoteType["frontMatter"], note2: NoteType["frontMatter"]) => (note1.date > note2.date ? -1 : 1));

View File

@ -38,14 +38,6 @@ module.exports = (phase, { defaultConfig }) => {
config.module.rules.push({ config.module.rules.push({
test: /\.svg$/, test: /\.svg$/,
issuer: { and: [/\.(js|ts)x?$/] }, issuer: { and: [/\.(js|ts)x?$/] },
include: [
path.resolve(__dirname, "components/icons"),
// slight workaround to grab svg files from these packages directly instead of through their exports:
path.resolve(__dirname, "node_modules/@primer/octicons/build/svg"),
path.resolve(__dirname, "node_modules/feather-icons/dist/icons"),
path.resolve(__dirname, "node_modules/simple-icons/icons"),
path.resolve(__dirname, "node_modules/twemoji/assets/svg"),
],
use: [ use: [
{ {
loader: "@svgr/webpack", loader: "@svgr/webpack",
@ -58,6 +50,15 @@ module.exports = (phase, { defaultConfig }) => {
}, },
}, },
], ],
include: [
// allow processing images from these packages directly instead of through their different exports, and leave
// other static imports of SVGs alone.
// see: ./components/Icons/index.ts
path.resolve(__dirname, "node_modules/@primer/octicons/build/svg"),
path.resolve(__dirname, "node_modules/feather-icons/dist/icons"),
path.resolve(__dirname, "node_modules/simple-icons/icons"),
path.resolve(__dirname, "node_modules/twemoji/assets/svg"),
],
}); });
return config; return config;

View File

@ -14,7 +14,7 @@
"url": "https://github.com/jakejarvis/jarv.is.git" "url": "https://github.com/jakejarvis/jarv.is.git"
}, },
"scripts": { "scripts": {
"dev": "cross-env NODE_OPTIONS='--inspect' next-remote-watch ./notes", "dev": "cross-env NODE_OPTIONS='--inspect' next dev",
"build": "next build", "build": "next build",
"analyze": "cross-env ANALYZE=true next build", "analyze": "cross-env ANALYZE=true next build",
"lint": "run-s lint:*", "lint": "run-s lint:*",
@ -23,7 +23,7 @@
"lint:prettier": "prettier --check ." "lint:prettier": "prettier --check ."
}, },
"dependencies": { "dependencies": {
"@fontsource/comic-neue": "4.5.2", "@fontsource/comic-neue": "4.5.3",
"@fontsource/inter": "4.5.4", "@fontsource/inter": "4.5.4",
"@fontsource/roboto-mono": "4.5.3", "@fontsource/roboto-mono": "4.5.3",
"@giscus/react": "^1.1.2", "@giscus/react": "^1.1.2",
@ -47,36 +47,36 @@
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"is-absolute-url": "^4.0.1", "is-absolute-url": "^4.0.1",
"markdown-to-jsx": "^7.1.6", "markdown-to-jsx": "^7.1.6",
"modern-normalize": "^1.1.0", "modern-normalize": "github:sindresorhus/modern-normalize#b59ec0d3d8654cbb6843bc9ea45aef5f1d680108",
"next": "12.1.0", "next": "12.1.0",
"next-compose-plugins": "^2.2.1", "next-compose-plugins": "^2.2.1",
"next-mdx-remote": "^4.0.0", "next-mdx-remote": "^4.0.0",
"next-seo": "^5.1.0", "next-seo": "^5.1.0",
"next-sitemap": "^2.4.3", "next-sitemap": "^2.5.1",
"next-themes": "^0.0.15", "next-themes": "^0.0.15",
"next-transpile-modules": "^9.0.0", "next-transpile-modules": "^9.0.0",
"node-fetch": "^3.2.0", "node-fetch": "^3.2.0",
"p-retry": "^5.0.0", "p-retry": "^5.0.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"query-string": "^7.1.1", "query-string": "^7.1.1",
"react": "^17.0.2", "react": "17.0.2",
"react-dom": "^17.0.2", "react-dom": "17.0.2",
"react-gist": "^1.2.4", "react-gist": "^1.2.4",
"react-innertext": "^1.1.5", "react-innertext": "^1.1.5",
"react-intersection-observer": "^8.33.1", "react-intersection-observer": "^8.33.1",
"react-is": "^17.0.2", "react-is": "17.0.2",
"react-player": "^2.9.0", "react-player": "^2.9.0",
"react-textarea-autosize": "^8.3.3", "react-textarea-autosize": "^8.3.3",
"react-tweet-embed": "^1.3.1", "react-tweet-embed": "^2.0.0",
"reading-time": "^1.5.0", "reading-time": "^1.5.0",
"rehype-prism-plus": "^1.3.1", "rehype-prism-plus": "^1.3.1",
"rehype-slug": "^5.0.1", "rehype-slug": "^5.0.1",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"remove-markdown": "^0.3.0", "remove-markdown": "^0.3.0",
"sanitize-html": "^2.7.0", "sanitize-html": "^2.7.0",
"simple-icons": "^6.10.0", "simple-icons": "^6.11.0",
"swr": "^1.2.2", "swr": "^1.2.2",
"terser": "^5.10.0", "terser": "^5.11.0",
"twemoji": "github:twitter/twemoji#v13.1.0" "twemoji": "github:twitter/twemoji#v13.1.0"
}, },
"devDependencies": { "devDependencies": {
@ -89,8 +89,8 @@
"@types/react-is": "^17.0.3", "@types/react-is": "^17.0.3",
"@types/remove-markdown": "^0.3.1", "@types/remove-markdown": "^0.3.1",
"@types/sanitize-html": "^2.6.2", "@types/sanitize-html": "^2.6.2",
"@typescript-eslint/eslint-plugin": "^5.12.0", "@typescript-eslint/eslint-plugin": "^5.12.1",
"@typescript-eslint/parser": "^5.12.0", "@typescript-eslint/parser": "^5.12.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "~8.9.0", "eslint": "~8.9.0",
"eslint-config-next": "12.1.0", "eslint-config-next": "12.1.0",
@ -99,11 +99,10 @@
"eslint-plugin-mdx": "~1.16.0", "eslint-plugin-mdx": "~1.16.0",
"eslint-plugin-prettier": "~4.0.0", "eslint-plugin-prettier": "~4.0.0",
"lint-staged": "^12.3.4", "lint-staged": "^12.3.4",
"next-remote-watch": "^1.0.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"simple-git-hooks": "^2.7.0", "simple-git-hooks": "^2.7.0",
"stylelint": "~14.5.1", "stylelint": "~14.5.3",
"stylelint-config-prettier": "~9.0.3", "stylelint-config-prettier": "~9.0.3",
"stylelint-prettier": "~2.0.0", "stylelint-prettier": "~2.0.0",
"typescript": "^4.5.5" "typescript": "^4.5.5"

View File

@ -72,7 +72,7 @@ const App = ({ Component, pageProps }: AppProps) => {
<> <>
{/* static asset preloads */} {/* static asset preloads */}
<Head> <Head>
{/* TODO: these hrefs will change at some point (and possibly unpredictably). find a better way... */} {/* TODO: these hrefs will change (unpredictably?) at some point. find a safer way to get them from webpack. */}
<link <link
rel="preload" rel="preload"
as="font" as="font"
@ -89,7 +89,7 @@ const App = ({ Component, pageProps }: AppProps) => {
/> />
</Head> </Head>
{/* all SEO config is in ./lib/seo.ts except for canonical URLs, which require access to next router */} {/* all SEO config is in ../lib/seo.ts except for canonical URLs, which require access to next router */}
<DefaultSeo <DefaultSeo
{...defaultSeo} {...defaultSeo}
canonical={canonical} canonical={canonical}
@ -103,6 +103,7 @@ const App = ({ Component, pageProps }: AppProps) => {
/> />
<SocialProfileJsonLd {...socialProfileJsonLd} /> <SocialProfileJsonLd {...socialProfileJsonLd} />
{/* NOTE: this *must* come last in this fragment */}
<ThemeProvider>{getLayout(<Component {...pageProps} />)}</ThemeProvider> <ThemeProvider>{getLayout(<Component {...pageProps} />)}</ThemeProvider>
</> </>
); );

View File

@ -4,7 +4,6 @@ import { MDXRemote } from "next-mdx-remote";
import { htmlEscape } from "escape-goat"; import { htmlEscape } from "escape-goat";
import Content from "../../components/Content/Content"; import Content from "../../components/Content/Content";
import NoteMeta from "../../components/NoteMeta/NoteMeta"; import NoteMeta from "../../components/NoteMeta/NoteMeta";
import NoteTitle from "../../components/NoteTitle/NoteTitle";
import Comments from "../../components/Comments/Comments"; import Comments from "../../components/Comments/Comments";
import * as mdxComponents from "../../lib/mdx-components"; import * as mdxComponents from "../../lib/mdx-components";
import { getNote, getNoteSlugs } from "../../lib/parse-notes"; import { getNote, getNoteSlugs } from "../../lib/parse-notes";
@ -51,15 +50,15 @@ const Note = ({ frontMatter, source }: NoteType) => {
{...articleJsonLd} {...articleJsonLd}
/> />
<NoteMeta slug={frontMatter.slug} date={frontMatter.date} title={frontMatter.title} tags={frontMatter.tags} /> <NoteMeta {...frontMatter} />
<NoteTitle slug={frontMatter.slug} htmlTitle={frontMatter.htmlTitle} />
<Content> <Content>
{/* @ts-ignore */} {/* @ts-ignore */}
<MDXRemote {...source} components={{ ...mdxComponents }} /> <MDXRemote {...source} components={{ ...mdxComponents }} />
</Content> </Content>
{frontMatter.noComments !== true && ( {/* comments can be disabled for an individual post via `noComments: true` in its front matter */}
{!frontMatter.noComments && (
<InView rootMargin="140px" triggerOnce fallbackInView> <InView rootMargin="140px" triggerOnce fallbackInView>
{({ inView, ref }) => ( {({ inView, ref }) => (
<div id="comments" ref={ref}> <div id="comments" ref={ref}>
@ -72,8 +71,8 @@ const Note = ({ frontMatter, source }: NoteType) => {
); );
}; };
export const getStaticProps: GetStaticProps = async ({ params }) => { export const getStaticProps: GetStaticProps = async ({ params }: { params: Pick<NoteType["frontMatter"], "slug"> }) => {
const { frontMatter, source } = await getNote(params.slug as string); const { frontMatter, source } = await getNote(params.slug);
return { return {
props: { props: {

View File

@ -1,11 +1,11 @@
import { NextSeo } from "next-seo"; import { NextSeo } from "next-seo";
import { format } from "date-fns"; import { format } from "date-fns";
import Content from "../../components/Content/Content"; import Content from "../../components/Content/Content";
import NotesList from "../../components/NotesList/NotesList"; import NotesList, { NotesListProps } from "../../components/NotesList/NotesList";
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 = ({ notesByYear }: NotesListProps) => (
<> <>
<NextSeo <NextSeo
title="Notes" title="Notes"
@ -23,9 +23,10 @@ const Notes = ({ notesByYear }) => (
export const getStaticProps: GetStaticProps = async () => { export const getStaticProps: GetStaticProps = async () => {
// parse the year of each note and group them together // parse the year of each note and group them together
const notesByYear = {}; const notesByYear: NotesListProps["notesByYear"] = {};
getAllNotes().map((note) => { getAllNotes().map((note) => {
const year = Number.parseInt(format(new Date(note.date), "yyyy")); const year = format(new Date(note.date), "yyyy");
(notesByYear[year] || (notesByYear[year] = [])).push(note); (notesByYear[year] || (notesByYear[year] = [])).push(note);
}); });

View File

@ -7,7 +7,7 @@ import RepositoryCard from "../components/RepositoryCard/RepositoryCard";
import { OctocatOcticon } from "../components/Icons"; import { OctocatOcticon } from "../components/Icons";
import { authorSocial } from "../lib/config"; import { authorSocial } from "../lib/config";
import type { GetStaticProps } from "next"; import type { GetStaticProps } from "next";
import type { RepoType } from "../types"; import type { RepositoryType } from "../types";
const Projects = ({ repos }) => ( const Projects = ({ repos }) => (
<> <>
@ -22,7 +22,7 @@ const Projects = ({ repos }) => (
<Content> <Content>
<div className="wrapper"> <div className="wrapper">
{repos.map((repo: RepoType) => ( {repos.map((repo: RepositoryType) => (
<div key={repo.name} className="card"> <div key={repo.name} className="card">
<RepositoryCard {...repo} /> <RepositoryCard {...repo} />
</div> </div>
@ -116,7 +116,7 @@ export const getStaticProps: GetStaticProps = async () => {
} }
); );
const repos: RepoType[] = user.repositories.edges.map(({ node: repo }) => ({ const repos: RepositoryType[] = user.repositories.edges.map(({ node: repo }) => ({
name: repo.name, name: repo.name,
url: repo.url, url: repo.url,
description: repo.description, description: repo.description,

34
types/index.d.ts vendored
View File

@ -1,32 +1,2 @@
import type { MDXRemoteSerializeResult } from "next-mdx-remote"; export * from "./note";
export * from "./repository";
export type NoteMetaType = {
title: string;
htmlTitle: string;
date: string;
slug: string;
permalink: string;
description: string;
image?: string;
tags?: string[];
readingMins?: number;
noComments?: boolean;
};
export type NoteType = {
frontMatter: NoteMetaType;
source: MDXRemoteSerializeResult;
};
export type RepoType = {
name: string;
url: string;
description?: string;
language?: {
name: string;
color?: string;
};
stars?: number;
forks?: number;
updatedAt: string;
};

19
types/note.d.ts vendored Normal file
View File

@ -0,0 +1,19 @@
import type { MDXRemoteSerializeResult } from "next-mdx-remote";
export type NoteType = {
frontMatter: {
slug: string;
permalink: string;
date: string;
title: string;
htmlTitle?: string;
description?: string;
image?: string;
tags?: string[];
readingMins?: number;
noComments?: boolean;
};
// the final, compiled JSX by next-mdx-remote; see lib/parse-notes.ts
source: MDXRemoteSerializeResult;
};

12
types/repository.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
export type RepositoryType = {
name: string;
url: string;
description?: string;
language?: {
name: string;
color?: string;
};
stars?: number;
forks?: number;
updatedAt: string;
};

635
yarn.lock

File diff suppressed because it is too large Load Diff