diff --git a/.eslintrc.json b/.eslintrc.json index 4d765f28..d1ab569b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,7 @@ { - "extends": ["next/core-web-vitals", "prettier"] + "plugins": ["prettier"], + "extends": ["next/core-web-vitals", "prettier"], + "rules": { + "prettier/prettier": "error" + } } diff --git a/.gitignore b/.gitignore index 838f8d37..371118f4 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ yarn-error.log* .vercel /public/posts +.idea/ diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index b58b603f..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 42b77934..00000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/unicorn-utterances.iml b/.idea/unicorn-utterances.iml deleted file mode 100644 index 0c8867d7..00000000 --- a/.idea/unicorn-utterances.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..69a88e3b --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/package-lock.json b/package-lock.json index 534301b8..c0135c60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 095fea4d..5447b34c 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/src/components/seo.tsx b/src/components/seo.tsx index 5a0f73a6..98f323bf 100644 --- a/src/components/seo.tsx +++ b/src/components/seo.tsx @@ -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"; - -interface SEOProps { - 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 -} - -const GoogleAnalytics = () => { - return <> - - - -} - -const FacebookSEO = (props: MetaSEOProps) => { - const { - pathName, - metaDescription, - title, - metaImage, - type, - unicornsData, - publishedTime, - editedTime, - keywords - } = props; - return <> - - - - - - - { - type === 'article' ? - : - type === 'profile' ? - : - - } - { - type === 'profile' ? - [ - , - , - - ] - : null - } - { - type !== 'article' ? null : [ - , - uni.name).join(',')}/>, - publishedTime ? : null, - editedTime ? : null, - ...keywords!.map(keyword => ) - ] - } - -} - - -const TwitterSingleAuthor = (props: MetaSEOProps) => { - const socialUnicorn = props.unicornsData!.find((uni) => uni.socials); - const uniTwitter = - socialUnicorn && socialUnicorn.socials && socialUnicorn.socials.twitter; - if (uniTwitter) { - return - } - return null -} - -const TwitterSEO = (props: MetaSEOProps) => { - const {metaDescription, title, metaImage, type, unicornsData} = props; - return <> - - - - - - { - type === 'article' ? - [ - unicornsData!.length === 1 ? - : - null - ] - : null - } - -} - -export const SEO: React.FC = ( - 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 - {title ? `${title} | ${siteMetadata.title}` : siteMetadata.title} - - {canonical ? : null} - - - - - - - {children} - -} +import React from "react"; +import Head from "next/head"; +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; +} + +type MetaSEOProps = SEOProps & { + metaDescription: string; + metaKeywords: string; + metaImage: string; +}; + +const GoogleAnalytics = () => { + return ( + <> + + + + ); +}; + +const FacebookSEO = (props: MetaSEOProps) => { + const { + pathName, + metaDescription, + title, + metaImage, + type, + unicornsData, + publishedTime, + editedTime, + keywords, + } = props; + return ( + <> + + + + + + + {type === "article" ? ( + + ) : type === "profile" ? ( + + ) : ( + + )} + {type === "profile" + ? [ + , + , + , + ] + : null} + {type !== "article" + ? null + : [ + , + uni.name).join(",")} + />, + publishedTime ? ( + + ) : null, + editedTime ? ( + + ) : null, + ...keywords!.map((keyword) => ( + + )), + ]} + + ); +}; + +const TwitterSingleAuthor = (props: MetaSEOProps) => { + const socialUnicorn = props.unicornsData!.find((uni) => uni.socials); + const uniTwitter = + socialUnicorn && socialUnicorn.socials && socialUnicorn.socials.twitter; + if (uniTwitter) { + return ; + } + return null; +}; + +const TwitterSEO = (props: MetaSEOProps) => { + const { metaDescription, title, metaImage, type, unicornsData } = props; + return ( + <> + + + + + + {type === "article" + ? [ + unicornsData!.length === 1 ? ( + + ) : null, + ] + : null} + + ); +}; + +export const SEO: React.FC = (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 ( + + + {title ? `${title} | ${siteMetadata.title}` : siteMetadata.title} + + + {canonical ? : null} + + + + + + + {children} + + ); +}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 6ac887c0..0ee799e0 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -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 + return ; } diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 0622df2d..3059a9c1 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -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 ( - - - - + + + +
- ) + ); } } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 12a19faa..7714d4b3 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -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 ( - <> -
    - {allPosts.map( - post => -
  • -

    - - {post.title} - -

    -

    {post.published}

    -
  • - )} -
- - ) -} +const Index = ({ allPosts }: Props) => { + return ( + <> +
    + {allPosts.map((post) => ( +
  • +

    + + {post.title} + +

    +

    {post.published}

    +
  • + ))} +
+ + ); +}; -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 }, + }; +}; diff --git a/src/pages/posts/[slug].tsx b/src/pages/posts/[slug].tsx index 3bfc3227..0877ac18 100644 --- a/src/pages/posts/[slug].tsx +++ b/src/pages/posts/[slug].tsx @@ -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 ( - <> -

Word count: {wordCount}

- {JSON.stringify(seriesPosts)} - <>{result} - - ) -} + return ( + <> +

Word count: {wordCount}

+ {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, + }; }