diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..0da96d6 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npx pretty-quick --staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..f736ff1 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +dist +node_modules +.next +build +.contentlayer +package-lock.json \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..377f844 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "trailingComma": "es5", + "semi": true, + "tabWidth": 2, + "singleQuote": true, + "jsxSingleQuote": true, + "plugins": ["prettier-plugin-organize-imports"] +} diff --git a/components.json b/components.json index 9b10942..04bd46e 100644 --- a/components.json +++ b/components.json @@ -14,4 +14,4 @@ "components": "@/components", "utils": "@/lib/utils" } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index 3c8454f..4b46b24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,9 @@ "next": "14.1.0", "next-mdx-remote": "^4.4.1", "next-themes": "^0.2.1", + "prettier": "^3.2.5", + "prettier-plugin-organize-imports": "^3.2.4", + "pretty-quick": "^4.0.0", "react": "^18", "react-country-flag": "^3.1.0", "react-dom": "^18", @@ -46,6 +49,7 @@ "autoprefixer": "^10.0.1", "eslint": "^8", "eslint-config-next": "14.1.0", + "husky": "^9.0.11", "postcss": "^8", "tailwindcss": "^3.3.0", "typescript": "^5" @@ -3608,6 +3612,33 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3711,7 +3742,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3861,6 +3891,17 @@ "node": ">=6" } }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", @@ -4171,11 +4212,33 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "dev": true, + "bin": { + "husky": "bin.mjs" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, "engines": { "node": ">= 4" } @@ -4602,6 +4665,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -4864,7 +4938,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -5101,6 +5174,11 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5689,6 +5767,14 @@ "node": ">=8.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5888,6 +5974,17 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6023,6 +6120,20 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -6044,7 +6155,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -6059,7 +6169,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -6105,7 +6214,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } @@ -6345,6 +6453,73 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-organize-imports": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz", + "integrity": "sha512-6m8WBhIp0dfwu0SkgfOxJqh+HpdyfqSSLfKKRZSFbDuEQXDDndb8fTpRWkUrX/uBenkex3MgnVk0J3b3Y5byog==", + "peerDependencies": { + "@volar/vue-language-plugin-pug": "^1.0.4", + "@volar/vue-typescript": "^1.0.4", + "prettier": ">=2.0", + "typescript": ">=2.9" + }, + "peerDependenciesMeta": { + "@volar/vue-language-plugin-pug": { + "optional": true + }, + "@volar/vue-typescript": { + "optional": true + } + } + }, + "node_modules/pretty-quick": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-4.0.0.tgz", + "integrity": "sha512-M+2MmeufXb/M7Xw3Afh1gxcYpj+sK0AxEfnfF958ktFeAyi5MsKY5brymVURQLgPLV1QaF5P4pb2oFJ54H3yzQ==", + "dependencies": { + "execa": "^5.1.1", + "find-up": "^5.0.0", + "ignore": "^5.3.0", + "mri": "^1.2.0", + "picocolors": "^1.0.0", + "picomatch": "^3.0.1", + "tslib": "^2.6.2" + }, + "bin": { + "pretty-quick": "lib/cli.mjs" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, + "node_modules/pretty-quick/node_modules/picomatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -7196,6 +7371,14 @@ "node": ">=0.10.0" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -7547,7 +7730,6 @@ "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8078,7 +8260,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index bb50241..600f3ee 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "pretty": "prettier --write \"./**/*.{js,jsx,mjs,cjs,ts,tsx,json}\"", + "prepare": "husky" }, "dependencies": { "@radix-ui/react-avatar": "^1.0.4", @@ -30,6 +32,9 @@ "next": "14.1.0", "next-mdx-remote": "^4.4.1", "next-themes": "^0.2.1", + "prettier": "^3.2.5", + "prettier-plugin-organize-imports": "^3.2.4", + "pretty-quick": "^4.0.0", "react": "^18", "react-country-flag": "^3.1.0", "react-dom": "^18", @@ -47,6 +52,7 @@ "autoprefixer": "^10.0.1", "eslint": "^8", "eslint-config-next": "14.1.0", + "husky": "^9.0.11", "postcss": "^8", "tailwindcss": "^3.3.0", "typescript": "^5" diff --git a/src/app/analytics/page.tsx b/src/app/analytics/page.tsx index 691f8dc..c454aa4 100644 --- a/src/app/analytics/page.tsx +++ b/src/app/analytics/page.tsx @@ -1,22 +1,22 @@ -import AnalyticsDashboard from '@/components/AnalyticsDashboard' -import { getDate } from '@/utils' -import { analytics } from '@/utils/analytics' +import AnalyticsDashboard from '@/components/AnalyticsDashboard'; +import { getDate } from '@/utils'; +import { analytics } from '@/utils/analytics'; const Page = async () => { - const TRACKING_DAYS = 7 + const TRACKING_DAYS = 7; - const pageviews = await analytics.retrieveDays('pageview', TRACKING_DAYS) + const pageviews = await analytics.retrieveDays('pageview', TRACKING_DAYS); const totalPageviews = pageviews.reduce((acc, curr) => { return ( acc + curr.events.reduce((acc, curr) => { - return acc + Object.values(curr)[0]! + return acc + Object.values(curr)[0]!; }, 0) - ) - }, 0) + ); + }, 0); - const avgVisitorsPerDay = (totalPageviews / TRACKING_DAYS).toFixed(1) + const avgVisitorsPerDay = (totalPageviews / TRACKING_DAYS).toFixed(1); const amtVisitorsToday = pageviews .filter((ev) => ev.date === getDate()) @@ -24,40 +24,42 @@ const Page = async () => { return ( acc + curr.events.reduce((acc, curr) => acc + Object.values(curr)[0]!, 0) - ) - }, 0) + ); + }, 0); - const topCountriesMap = new Map() + const topCountriesMap = new Map(); for (let i = 0; i < pageviews.length; i++) { - const day = pageviews[i] - if (!day) continue + const day = pageviews[i]; + if (!day) continue; for (let j = 0; j < day.events.length; j++) { - const event = day.events[j] - if (!event) continue + const event = day.events[j]; + if (!event) continue; - const key = Object.keys(event)[0]! - const value = Object.values(event)[0]! + const key = Object.keys(event)[0]!; + const value = Object.values(event)[0]!; - const parsedKey = JSON.parse(key) - const country = parsedKey?.country + const parsedKey = JSON.parse(key); + const country = parsedKey?.country; if (country) { if (topCountriesMap.has(country)) { - const prevValue = topCountriesMap.get(country)! - topCountriesMap.set(country, prevValue + value) + const prevValue = topCountriesMap.get(country)!; + topCountriesMap.set(country, prevValue + value); } else { - topCountriesMap.set(country, value) + topCountriesMap.set(country, value); } } } } - const topCountries = [...topCountriesMap.entries()].sort((a ,b) => { - if(a[1] > b[1]) return -1 - else return 1 - }).slice(0, 5) + const topCountries = [...topCountriesMap.entries()] + .sort((a, b) => { + if (a[1] > b[1]) return -1; + else return 1; + }) + .slice(0, 5); return (
@@ -70,7 +72,7 @@ const Page = async () => { />
- ) -} + ); +}; -export default Page +export default Page; diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx index 5184b8b..8b82d74 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/blog/[slug]/page.tsx @@ -1,8 +1,8 @@ -import { MDX } from "@/components/markdown/mdx"; -import { Button } from "@/components/ui/button"; -import { getBlogPostByName, getSortedBlogPosts } from "@/lib/blog"; -import Link from "next/link"; -import { notFound } from "next/navigation"; +import { MDX } from '@/components/markdown/mdx'; +import { Button } from '@/components/ui/button'; +import { getBlogPostByName, getSortedBlogPosts } from '@/lib/blog'; +import Link from 'next/link'; +import { notFound } from 'next/navigation'; const getBlogPost = async (slug: string) => { const blogPost = await getBlogPostByName(slug); @@ -39,9 +39,9 @@ export default async function BlogPost({ }) { const { markdown } = await getBlogPost(params.slug); return ( -
- - diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx index 2dfb6ee..4114faa 100644 --- a/src/app/blog/page.tsx +++ b/src/app/blog/page.tsx @@ -4,14 +4,14 @@ import { CardFooter, CardHeader, CardTitle, -} from "@/components/ui/card"; -import { getSortedBlogPosts } from "@/lib/blog"; -import Link from "next/link"; +} from '@/components/ui/card'; +import { getSortedBlogPosts } from '@/lib/blog'; +import Link from 'next/link'; export function generateMetadata() { return { - title: "Blog", - description: "", + title: 'Blog', + description: '', keywords: [], }; } @@ -23,7 +23,7 @@ export default async function Blogs() {