chore: add prettier to linting

This commit is contained in:
Corbin Crutchley
2021-11-30 07:34:07 -08:00
parent 602da1951f
commit 31ca6cae33
14 changed files with 352 additions and 312 deletions

View File

@@ -1,3 +1,7 @@
{ {
"extends": ["next/core-web-vitals", "prettier"] "plugins": ["prettier"],
"extends": ["next/core-web-vitals", "prettier"],
"rules": {
"prettier/prettier": "error"
}
} }

1
.gitignore vendored
View File

@@ -34,3 +34,4 @@ yarn-error.log*
.vercel .vercel
/public/posts /public/posts
.idea/

5
.idea/.gitignore generated vendored
View File

@@ -1,5 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/unicorn-utterances.iml" filepath="$PROJECT_DIR$/.idea/unicorn-utterances.iml" />
</modules>
</component>
</project>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

1
.prettierrc.json Normal file
View File

@@ -0,0 +1 @@
{}

24
package-lock.json generated
View File

@@ -2362,6 +2362,15 @@
} }
} }
}, },
"eslint-plugin-prettier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz",
"integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==",
"dev": true,
"requires": {
"prettier-linter-helpers": "^1.0.0"
}
},
"eslint-plugin-react": { "eslint-plugin-react": {
"version": "7.27.1", "version": "7.27.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.27.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.27.1.tgz",
@@ -2554,6 +2563,12 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
}, },
"fast-diff": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
"dev": true
},
"fast-glob": { "fast-glob": {
"version": "3.2.7", "version": "3.2.7",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz",
@@ -5433,6 +5448,15 @@
"integrity": "sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg==", "integrity": "sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg==",
"dev": true "dev": true
}, },
"prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"requires": {
"fast-diff": "^1.1.2"
}
},
"pretty-format": { "pretty-format": {
"version": "26.6.2", "version": "26.6.2",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",

View File

@@ -49,6 +49,7 @@
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-next": "12.0.4", "eslint-config-next": "12.0.4",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"prettier": "^2.5.0" "prettier": "^2.5.0"
} }
} }

View File

@@ -1,147 +1,177 @@
import React from "react"; import React from "react";
import Head from "next/head"; import Head from "next/head";
import {siteMetadata, siteUrl} from "../api/get-site-config"; import { siteMetadata, siteUrl } from "../api/get-site-config";
import {UnicornInfo} from "../types"; import { UnicornInfo } from "../types";
interface SEOProps { interface SEOProps {
description?: string; description?: string;
lang?: string; lang?: string;
title: string; title: string;
unicornsData?: UnicornInfo[]; unicornsData?: UnicornInfo[];
keywords?: string[]; keywords?: string[];
publishedTime?: string; publishedTime?: string;
editedTime?: string; editedTime?: string;
type?: "article" | "profile"; type?: "article" | "profile";
pathName?: string; pathName?: string;
canonical?: string; canonical?: string;
} }
type MetaSEOProps = SEOProps & { type MetaSEOProps = SEOProps & {
metaDescription: string metaDescription: string;
metaKeywords: string metaKeywords: string;
metaImage: string metaImage: string;
} };
const GoogleAnalytics = () => { const GoogleAnalytics = () => {
return <> return (
<link rel="preconnect" href="https://www.google.com"/> <>
<link rel="preconnect" href="https://marketingplatform.google.com"/> <link rel="preconnect" href="https://www.google.com" />
</> <link rel="preconnect" href="https://marketingplatform.google.com" />
} </>
);
const FacebookSEO = (props: MetaSEOProps) => { };
const {
pathName, const FacebookSEO = (props: MetaSEOProps) => {
metaDescription, const {
title, pathName,
metaImage, metaDescription,
type, title,
unicornsData, metaImage,
publishedTime, type,
editedTime, unicornsData,
keywords publishedTime,
} = props; editedTime,
return <> keywords,
<meta property="og:url" content={siteMetadata.siteUrl + (pathName || "")}/> } = props;
<meta property="og:site_name" content={siteMetadata.title}/> return (
<meta property="og:title" content={title}/> <>
<meta property="og:locale" content="en_US"/> <meta
<meta property="og:description" property="og:url"
content={metaDescription}/> content={siteMetadata.siteUrl + (pathName || "")}
<meta property="og:image" content={metaImage}/> />
{ <meta property="og:site_name" content={siteMetadata.title} />
type === 'article' ? <meta property="og:title" content={title} />
<meta property="og:type" content="article"/> : <meta property="og:locale" content="en_US" />
type === 'profile' ? <meta property="og:description" content={metaDescription} />
<meta property="og:type" content="profile"/> : <meta property="og:image" content={metaImage} />
<meta property="og:type" content="blog"/> {type === "article" ? (
} <meta property="og:type" content="article" />
{ ) : type === "profile" ? (
type === 'profile' ? <meta property="og:type" content="profile" />
[ ) : (
<meta key="firstName" property="profile:firstName" content={unicornsData![0].firstName}/>, <meta property="og:type" content="blog" />
<meta key="lastName" property="profile:lastName" content={unicornsData![0].lastName}/>, )}
<meta key="username" property="profile:username" content={unicornsData![0].id}/> {type === "profile"
] ? [
: null <meta
} key="firstName"
{ property="profile:firstName"
type !== 'article' ? null : [ content={unicornsData![0].firstName}
<meta key="section" property="article:section" content="Technology"/>, />,
<meta key="author" property="article:author" content={unicornsData!.map((uni) => uni.name).join(',')}/>, <meta
publishedTime ? <meta key="published_time" property="article:published_time" content={publishedTime}/> : null, key="lastName"
editedTime ? <meta key="modified_time" property="article:modified_time" content={editedTime}/> : null, property="profile:lastName"
...keywords!.map(keyword => <meta key={keyword} property="article:tag" content={keyword}/>) content={unicornsData![0].lastName}
] />,
} <meta
</> key="username"
} property="profile:username"
content={unicornsData![0].id}
/>,
const TwitterSingleAuthor = (props: MetaSEOProps) => { ]
const socialUnicorn = props.unicornsData!.find((uni) => uni.socials); : null}
const uniTwitter = {type !== "article"
socialUnicorn && socialUnicorn.socials && socialUnicorn.socials.twitter; ? null
if (uniTwitter) { : [
return <meta property="twitter:creator" content={`@${uniTwitter}`}/> <meta
} key="section"
return null property="article:section"
} content="Technology"
/>,
const TwitterSEO = (props: MetaSEOProps) => { <meta
const {metaDescription, title, metaImage, type, unicornsData} = props; key="author"
return <> property="article:author"
<meta name="twitter:title" content={title}/> content={unicornsData!.map((uni) => uni.name).join(",")}
<meta name="twitter:description" />,
content={metaDescription}/> publishedTime ? (
<meta name="twitter:card" content="summary_large_image"/> <meta
<meta name="twitter:site" content={siteMetadata.twitterHandle}/> key="published_time"
<meta name="twitter:image" content={metaImage}/> property="article:published_time"
{ content={publishedTime}
type === 'article' ? />
[ ) : null,
unicornsData!.length === 1 ? editedTime ? (
<TwitterSingleAuthor key="singleAuthor" {...props}/> : <meta
null key="modified_time"
] property="article:modified_time"
: null content={editedTime}
} />
</> ) : null,
} ...keywords!.map((keyword) => (
<meta key={keyword} property="article:tag" content={keyword} />
export const SEO: React.FC<SEOProps> = ( )),
props ]}
) => { </>
const { );
description = "", };
children,
title, const TwitterSingleAuthor = (props: MetaSEOProps) => {
keywords, const socialUnicorn = props.unicornsData!.find((uni) => uni.socials);
canonical, const uniTwitter =
} = props; socialUnicorn && socialUnicorn.socials && socialUnicorn.socials.twitter;
if (uniTwitter) {
const metaDescription = description || siteMetadata.description; return <meta property="twitter:creator" content={`@${uniTwitter}`} />;
const metaKeywords = keywords ? keywords.join(',') : siteMetadata.keywords; }
const metaImage = `${siteUrl}/share-banner.png`; return null;
};
const metaProps = {
metaDescription, const TwitterSEO = (props: MetaSEOProps) => {
metaKeywords, const { metaDescription, title, metaImage, type, unicornsData } = props;
metaImage return (
} <>
<meta name="twitter:title" content={title} />
return <Head> <meta name="twitter:description" content={metaDescription} />
<title>{title ? `${title} | ${siteMetadata.title}` : siteMetadata.title}</title> <meta name="twitter:card" content="summary_large_image" />
<link rel="icon" href="/favicon.ico"/> <meta name="twitter:site" content={siteMetadata.twitterHandle} />
{canonical ? <link rel="canonical" href={canonical}/> : null} <meta name="twitter:image" content={metaImage} />
<meta property="name" content={siteMetadata.title}/> {type === "article"
<meta name="description" ? [
content={metaDescription}/> unicornsData!.length === 1 ? (
<meta property="keywords" <TwitterSingleAuthor key="singleAuthor" {...props} />
content={metaKeywords}/> ) : null,
<GoogleAnalytics/> ]
<FacebookSEO {...props} {...metaProps}/> : null}
<TwitterSEO {...props} {...metaProps}/> </>
{children} );
</Head> };
}
export const SEO: React.FC<SEOProps> = (props) => {
const { description = "", children, title, keywords, canonical } = props;
const metaDescription = description || siteMetadata.description;
const metaKeywords = keywords ? keywords.join(",") : siteMetadata.keywords;
const metaImage = `${siteUrl}/share-banner.png`;
const metaProps = {
metaDescription,
metaKeywords,
metaImage,
};
return (
<Head>
<title>
{title ? `${title} | ${siteMetadata.title}` : siteMetadata.title}
</title>
<link rel="icon" href="/favicon.ico" />
{canonical ? <link rel="canonical" href={canonical} /> : null}
<meta property="name" content={siteMetadata.title} />
<meta name="description" content={metaDescription} />
<meta property="keywords" content={metaKeywords} />
<GoogleAnalytics />
<FacebookSEO {...props} {...metaProps} />
<TwitterSEO {...props} {...metaProps} />
{children}
</Head>
);
};

View File

@@ -1,6 +1,6 @@
import { AppProps } from 'next/app' import { AppProps } from "next/app";
import '../styles/index.css' import "../styles/index.css";
export default function MyApp({ Component, pageProps }: AppProps) { export default function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} /> return <Component {...pageProps} />;
} }

View File

@@ -1,28 +1,28 @@
import Document, { Html, Head, Main, NextScript } from 'next/document' import Document, { Html, Head, Main, NextScript } from "next/document";
export default class MyDocument extends Document { export default class MyDocument extends Document {
render() { render() {
return ( return (
<Html lang="en"> <Html lang="en">
<Head > <Head>
<link <link
href="https://fonts.googleapis.com/css2?family=Work+Sans&display=swap" href="https://fonts.googleapis.com/css2?family=Work+Sans&display=swap"
rel="stylesheet" rel="stylesheet"
/> />
<link <link
href="https://fonts.googleapis.com/css2?family=Archivo&display=swap" href="https://fonts.googleapis.com/css2?family=Archivo&display=swap"
rel="stylesheet" rel="stylesheet"
/> />
<link <link
href="https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap" href="https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap"
rel="stylesheet" rel="stylesheet"
/> />
</Head> </Head>
<body> <body>
<Main /> <Main />
<NextScript /> <NextScript />
</body> </body>
</Html> </Html>
) );
} }
} }

View File

@@ -1,51 +1,50 @@
import {getAllPosts} from '../api/api' import { getAllPosts } from "../api/api";
import Post from '../types/post' import Post from "../types/post";
import React from 'react' import React from "react";
import Link from 'next/link'; import Link from "next/link";
type Props = { type Props = {
allPosts: Post[] allPosts: Post[];
} };
const Index = ({allPosts}: Props) => { const Index = ({ allPosts }: Props) => {
return ( return (
<> <>
<ul> <ul>
{allPosts.map( {allPosts.map((post) => (
post => <li key={post.slug}>
<li key={post.slug}> <h2>
<h2> <Link href={`/posts/[slug]`} as={`/posts/${post.slug}`}>
<Link href={`/posts/[slug]`} as={`/posts/${post.slug}`}> <a>{post.title}</a>
<a>{post.title}</a> </Link>
</Link> </h2>
</h2> <p>{post.published}</p>
<p>{post.published}</p> </li>
</li> ))}
)} </ul>
</ul> </>
</> );
) };
}
export default Index export default Index;
export const getStaticProps = async () => { export const getStaticProps = async () => {
let allPosts = getAllPosts({ let allPosts = getAllPosts({
title: true, title: true,
published: true, published: true,
slug: true, slug: true,
author: true, author: true,
excerpt: true excerpt: true,
} as const) } as const);
// sort posts by date in descending order // sort posts by date in descending order
allPosts = allPosts.sort((post1, post2) => { allPosts = allPosts.sort((post1, post2) => {
const date1 = new Date(post1.published); const date1 = new Date(post1.published);
const date2 = new Date(post2.published); const date2 = new Date(post2.published);
return (date1 > date2 ? -1 : 1); return date1 > date2 ? -1 : 1;
}) });
return { return {
props: {allPosts}, props: { allPosts },
} };
} };

View File

@@ -1,98 +1,109 @@
// import { useRouter } from 'next/router' // import { useRouter } from 'next/router'
// import ErrorPage from 'next/error' // import ErrorPage from 'next/error'
import {getPostBySlug, getAllPosts, postsDirectory} from '../../api/api' import { getPostBySlug, getAllPosts, postsDirectory } from "../../api/api";
import * as React from 'react'; import * as React from "react";
import markdownToHtml from "../../utils/markdownToHtml"; import markdownToHtml from "../../utils/markdownToHtml";
import {useMarkdownRenderer} from "../../hooks/useMarkdownRenderer"; import { useMarkdownRenderer } from "../../hooks/useMarkdownRenderer";
import 'react-medium-image-zoom/dist/styles.css' import "react-medium-image-zoom/dist/styles.css";
type Props = { type Props = {
markdownHTML: string markdownHTML: string;
slug: string slug: string;
postsDirectory: string postsDirectory: string;
wordCount: number wordCount: number;
seriesPosts: any[] seriesPosts: any[];
} };
const Post = ({markdownHTML, slug, postsDirectory, wordCount, seriesPosts}: Props) => { const Post = ({
const result = useMarkdownRenderer({ markdownHTML,
markdownHTML, slug,
slug, postsDirectory,
postsDirectory wordCount,
}); seriesPosts,
}: Props) => {
const result = useMarkdownRenderer({
markdownHTML,
slug,
postsDirectory,
});
return ( return (
<> <>
<h1>Word count: {wordCount}</h1> <h1>Word count: {wordCount}</h1>
{JSON.stringify(seriesPosts)} {JSON.stringify(seriesPosts)}
<>{result}</> <>{result}</>
</> </>
) );
} };
export default Post export default Post;
type Params = { type Params = {
params: { params: {
slug: string slug: string;
} };
} };
const seriesPostCacheKey = {}; const seriesPostCacheKey = {};
export async function getStaticProps({params}: Params) { export async function getStaticProps({ params }: Params) {
const post = getPostBySlug(params.slug, { const post = getPostBySlug(params.slug, {
title: true,
slug: true,
content: true,
wordCount: true,
series: true,
order: true,
} as const);
const isStr = (val: any): val is string => typeof val === "string";
const markdown = isStr(post.content) ? post.content : "";
const slug = isStr(post.slug) ? post.slug : "";
let seriesPosts: any[] = [];
if (post.series && post.order) {
const allPosts = getAllPosts(
{
title: true, title: true,
slug: true, slug: true,
content: true,
wordCount: true,
series: true, series: true,
order: true order: true,
} as const) } as const,
seriesPostCacheKey
);
const isStr = (val: any): val is string => typeof val === 'string'; seriesPosts = allPosts
const markdown = isStr(post.content) ? post.content : ''; .filter((filterPost) => filterPost.series === post.series)
const slug = isStr(post.slug) ? post.slug : ''; .sort((sortPost) => Number(sortPost.order) - Number(post.order));
}
let seriesPosts: any[] = []; const markdownHTML = await markdownToHtml(slug, markdown);
if (post.series && post.order) {
const allPosts = getAllPosts({
title: true,
slug: true,
series: true,
order: true,
} as const, seriesPostCacheKey)
seriesPosts = allPosts.filter(filterPost => filterPost.series === post.series).sort(sortPost => Number(sortPost.order) - Number(post.order)); return {
} props: {
markdownHTML,
const markdownHTML = await markdownToHtml(slug, markdown); slug: slug,
postsDirectory,
return { wordCount: post.wordCount,
props: { seriesPosts,
markdownHTML, },
slug: slug, };
postsDirectory,
wordCount: post.wordCount,
seriesPosts
}
}
} }
export async function getStaticPaths() { export async function getStaticPaths() {
const posts = getAllPosts({'slug': true}) const posts = getAllPosts({ slug: true });
const paths = posts.map((post) => {
return {
params: {
slug: post.slug,
},
}
});
const paths = posts.map((post) => {
return { return {
paths, params: {
fallback: false, slug: post.slug,
} },
};
});
return {
paths,
fallback: false,
};
} }