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
/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": {
"version": "7.27.1",
"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",
"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": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz",
@@ -5433,6 +5448,15 @@
"integrity": "sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg==",
"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": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",

View File

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

View File

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

View File

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

View File

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