Merge branch 'main' into feature/update-read-me
This commit is contained in:
		| @@ -22,5 +22,8 @@ KLAVIYO_LIST_ID= | ||||
|  | ||||
| REVUE_API_KEY= | ||||
|  | ||||
| # Create EmailOctopus API key at https://emailoctopus.com/api-documentation | ||||
| EMAILOCTOPUS_API_KEY= | ||||
| # List ID can be found in the URL as a UUID after clicking a list on https://emailoctopus.com/lists | ||||
| # or the settings page of your list https://emailoctopus.com/lists/{UUID}/settings | ||||
| EMAILOCTOPUS_LIST_ID= | ||||
|   | ||||
| @@ -1,4 +1 @@ | ||||
| #!/bin/sh | ||||
| . "$(dirname "$0")/_/husky.sh" | ||||
|  | ||||
| npx --no-install lint-staged | ||||
|   | ||||
| @@ -42,7 +42,7 @@ Internationalization support - [Template with i18n](https://tailwind-nextjs-star | ||||
| - [enscribe.dev](https://enscribe.dev) - enscribe's personal blog; cybersecurity shenanigans, frontend webdev, etc. ([source code](https://github.com/jktrn/enscribe.dev)) | ||||
| - [dalelarroder.com](https://dalelarroder.com) - Dale Larroder's personal website upgraded from V1 ([source code](https://github.com/dlarroder/dalelarroder)) | ||||
| - [thetalhatahir.com](https://www.thetalhatahir.com) - Talha Tahir's personal blog. Added article thumbnails, linkedIn card, Beautiful hero content, technology emoticons. | ||||
| - [hauhau.cn](https://www.hauhau.cn) - Homing's personal blog about the stuff he's learning ([source code](https://github.com/hominsu/blog)) | ||||
| - [homing.so](https://homing.so) - Homing's personal blog about the stuff he's learning ([source code](https://github.com/hominsu/blog)) | ||||
| - [zS1m's Blog](https://contrails.space) - zS1m's personal blog for recording and sharing daily learning technical content ([source code](https://github.com/zS1m/nextjs-contrails)) | ||||
| - [dariuszwozniak.net](https://dariuszwozniak.net/) - Software development blog | ||||
| - [Terminals.run](https://terminals.run) - Blog site for some thoughts and records for life and technology. | ||||
| @@ -57,6 +57,8 @@ Internationalization support - [Template with i18n](https://tailwind-nextjs-star | ||||
| - [Hans Blog](https://www.hansking.cn/) - Hans' personal blog, front-end technology, gallery and travel diary 中文. ([source code](https://github.com/hansking98/hans-nextjs-blog)) | ||||
| - [CuB3y0nd's Portfolio](https://www.cubeyond.net/) - CuB3y0nd‘s cyber security study notes「中文」 | ||||
| - [London Tech Talk](https://london-tech-talk.com/) - A podcast exploring technology trends and expatriate living experiences. - 日本語 | ||||
| - [CRUD Flow Blog](http://blog.ndamulelo.co.za/) - A technical blog about AI, Cloud Engineering, Data Science and Personal development | ||||
| - [Trillium's Blog](https://trilliumsmith.com/) - Modified to render resume pdf on `/resume` page | ||||
| - [Frank's Tech Blog](https://frank-tech-blog.vercel.app/) - Frank's personal blog about software development and technology. ([source code](https://github.com/frank-mendez/frank-blog)) | ||||
|  | ||||
| Using the template? Feel free to create a PR and add your blog to this list. | ||||
| @@ -98,7 +100,8 @@ Thanks to the community of users and contributors to the template! We are no lon | ||||
| - [https://bitoflearning-9a57.fly.dev/](https://bitoflearning-9a57.fly.dev/) - Sangeet Agarwal's personal blog, replatformed to [remix](https://remix.run/remix) using the [indie stack](https://github.com/remix-run/indie-stack) ([source code](https://github.com/SangeetAgarwal/bitoflearning)) | ||||
| - [raphaelchelly.com](https://www.raphaelchelly.com/) - Raphaël Chelly's personal website and blog ([source code](https://github.com/raphaelchelly/raph_www)) | ||||
| - [kaveh.page](https://kaveh.page) - Kaveh Tehrani's personal blog. Added tags directory, profile card, time-to-read on posts directory, etc. | ||||
|  | ||||
| - [drakerossman.com](https://drakerossman.com/) - Drake Rossman's blog about NixOS, Rust, Software Architecture and Engineering Management, as well as general musings. | ||||
|    | ||||
| ## Motivation | ||||
|  | ||||
| I wanted to port my existing blog to Nextjs and Tailwind CSS but there was no easy out of the box template to use so I decided to create one. Design is adapted from [Tailwindlabs blog](https://github.com/tailwindlabs/blog.tailwindcss.com). | ||||
|   | ||||
| @@ -73,7 +73,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) | ||||
|       <meta name="theme-color" media="(prefers-color-scheme: light)" content="#fff" /> | ||||
|       <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000" /> | ||||
|       <link rel="alternate" type="application/rss+xml" href="/feed.xml" /> | ||||
|       <body className="bg-white text-black antialiased dark:bg-gray-950 dark:text-white"> | ||||
|       <body className="bg-white pl-[calc(100vw-100%)] text-black antialiased dark:bg-gray-950 dark:text-white"> | ||||
|         <ThemeProviders> | ||||
|           <Analytics analyticsConfig={siteMetadata.analytics as AnalyticsConfig} /> | ||||
|           <SectionContainer> | ||||
|   | ||||
| @@ -1 +1,20 @@ | ||||
| {"next-js":6,"tailwind":3,"guide":5,"feature":2,"multi-author":1,"hello":1,"math":1,"ols":1,"github":1,"writings":1,"book":1,"reflection":1,"holiday":1,"canada":1,"images":1,"markdown":1,"code":1,"features":1} | ||||
| { | ||||
|   "markdown": 1, | ||||
|   "code": 1, | ||||
|   "features": 1, | ||||
|   "next-js": 6, | ||||
|   "math": 1, | ||||
|   "ols": 1, | ||||
|   "github": 1, | ||||
|   "guide": 5, | ||||
|   "tailwind": 3, | ||||
|   "hello": 1, | ||||
|   "holiday": 1, | ||||
|   "canada": 1, | ||||
|   "images": 1, | ||||
|   "feature": 2, | ||||
|   "writings": 1, | ||||
|   "book": 1, | ||||
|   "reflection": 1, | ||||
|   "multi-author": 1 | ||||
| } | ||||
|   | ||||
| @@ -6,11 +6,16 @@ import siteMetadata from '@/data/siteMetadata' | ||||
|  | ||||
| export default function Comments({ slug }: { slug: string }) { | ||||
|   const [loadComments, setLoadComments] = useState(false) | ||||
|  | ||||
|   if (!siteMetadata.comments?.provider) { | ||||
|     return null | ||||
|   } | ||||
|   return ( | ||||
|     <> | ||||
|       {!loadComments && <button onClick={() => setLoadComments(true)}>Load Comments</button>} | ||||
|       {siteMetadata.comments && loadComments && ( | ||||
|       {loadComments ? ( | ||||
|         <CommentsComponent commentsConfig={siteMetadata.comments} slug={slug} /> | ||||
|       ) : ( | ||||
|         <button onClick={() => setLoadComments(true)}>Load Comments</button> | ||||
|       )} | ||||
|     </> | ||||
|   ) | ||||
|   | ||||
| @@ -1,7 +1,49 @@ | ||||
| 'use client' | ||||
|  | ||||
| import { useEffect, useState } from 'react' | ||||
| import { Fragment, useEffect, useState } from 'react' | ||||
| import { useTheme } from 'next-themes' | ||||
| import { Menu, RadioGroup, Transition } from '@headlessui/react' | ||||
|  | ||||
| const Sun = () => ( | ||||
|   <svg | ||||
|     xmlns="http://www.w3.org/2000/svg" | ||||
|     viewBox="0 0 20 20" | ||||
|     fill="currentColor" | ||||
|     className="h-6 w-6 text-gray-900 dark:text-gray-100" | ||||
|   > | ||||
|     <path | ||||
|       fillRule="evenodd" | ||||
|       d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" | ||||
|       clipRule="evenodd" | ||||
|     /> | ||||
|   </svg> | ||||
| ) | ||||
| const Moon = () => ( | ||||
|   <svg | ||||
|     xmlns="http://www.w3.org/2000/svg" | ||||
|     viewBox="0 0 20 20" | ||||
|     fill="currentColor" | ||||
|     className="h-6 w-6 text-gray-900 dark:text-gray-100" | ||||
|   > | ||||
|     <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" /> | ||||
|   </svg> | ||||
| ) | ||||
| const Monitor = () => ( | ||||
|   <svg | ||||
|     xmlns="http://www.w3.org/2000/svg" | ||||
|     viewBox="0 0 20 20" | ||||
|     fill="none" | ||||
|     stroke="currentColor" | ||||
|     stroke-width="2" | ||||
|     stroke-linecap="round" | ||||
|     stroke-linejoin="round" | ||||
|     className="h-6 w-6 text-gray-900 dark:text-gray-100" | ||||
|   > | ||||
|     <rect x="3" y="3" width="14" height="10" rx="2" ry="2"></rect> | ||||
|     <line x1="7" y1="17" x2="13" y2="17"></line> | ||||
|     <line x1="10" y1="13" x2="10" y2="17"></line> | ||||
|   </svg> | ||||
| ) | ||||
|  | ||||
| const ThemeSwitch = () => { | ||||
|   const [mounted, setMounted] = useState(false) | ||||
| @@ -10,32 +52,60 @@ const ThemeSwitch = () => { | ||||
|   // When mounted on client, now we can show the UI | ||||
|   useEffect(() => setMounted(true), []) | ||||
|  | ||||
|   if (!mounted) { | ||||
|     return null | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <button | ||||
|       aria-label="Toggle Dark Mode" | ||||
|       onClick={() => setTheme(theme === 'dark' || resolvedTheme === 'dark' ? 'light' : 'dark')} | ||||
|     > | ||||
|       <svg | ||||
|         xmlns="http://www.w3.org/2000/svg" | ||||
|         viewBox="0 0 20 20" | ||||
|         fill="currentColor" | ||||
|         className="h-6 w-6 text-gray-900 dark:text-gray-100" | ||||
|       > | ||||
|         {mounted && (theme === 'dark' || resolvedTheme === 'dark') ? ( | ||||
|           <path | ||||
|             fillRule="evenodd" | ||||
|             d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" | ||||
|             clipRule="evenodd" | ||||
|           /> | ||||
|         ) : ( | ||||
|           <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" /> | ||||
|         )} | ||||
|       </svg> | ||||
|     </button> | ||||
|     <div className="mr-5"> | ||||
|       <Menu as="div" className="relative inline-block text-left"> | ||||
|         <div> | ||||
|           <Menu.Button>{resolvedTheme === 'dark' ? <Moon /> : <Sun />}</Menu.Button> | ||||
|         </div> | ||||
|         <Transition | ||||
|           as={Fragment} | ||||
|           enter="transition ease-out duration-100" | ||||
|           enterFrom="transform opacity-0 scale-95" | ||||
|           enterTo="transform opacity-100 scale-100" | ||||
|           leave="transition ease-in duration-75" | ||||
|           leaveFrom="transform opacity-100 scale-100" | ||||
|           leaveTo="transform opacity-0 scale-95" | ||||
|         > | ||||
|           <Menu.Items className="absolute right-0 mt-2 w-32 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-800"> | ||||
|             <RadioGroup value={theme} onChange={setTheme}> | ||||
|               <div className="p-1"> | ||||
|                 <RadioGroup.Option value="light"> | ||||
|                   <Menu.Item> | ||||
|                     <button className="group flex w-full items-center rounded-md px-2 py-2 text-sm"> | ||||
|                       <div className="mr-2"> | ||||
|                         <Sun /> | ||||
|                       </div> | ||||
|                       Light | ||||
|                     </button> | ||||
|                   </Menu.Item> | ||||
|                 </RadioGroup.Option> | ||||
|                 <RadioGroup.Option value="dark"> | ||||
|                   <Menu.Item> | ||||
|                     <button className="group flex w-full items-center rounded-md px-2 py-2 text-sm"> | ||||
|                       <div className="mr-2"> | ||||
|                         <Moon /> | ||||
|                       </div> | ||||
|                       Dark | ||||
|                     </button> | ||||
|                   </Menu.Item> | ||||
|                 </RadioGroup.Option> | ||||
|                 <RadioGroup.Option value="system"> | ||||
|                   <Menu.Item> | ||||
|                     <button className="group flex w-full items-center rounded-md px-2 py-2 text-sm"> | ||||
|                       <div className="mr-2"> | ||||
|                         <Monitor /> | ||||
|                       </div> | ||||
|                       System | ||||
|                     </button> | ||||
|                   </Menu.Item> | ||||
|                 </RadioGroup.Option> | ||||
|               </div> | ||||
|             </RadioGroup> | ||||
|           </Menu.Items> | ||||
|         </Transition> | ||||
|       </Menu> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| import { defineDocumentType, ComputedFields, makeSource } from 'contentlayer/source-files' | ||||
| import { defineDocumentType, ComputedFields, makeSource } from 'contentlayer2/source-files' | ||||
| import { writeFileSync } from 'fs' | ||||
| import readingTime from 'reading-time' | ||||
| import { slug } from 'github-slugger' | ||||
| import path from 'path' | ||||
| import { fromHtmlIsomorphic } from 'hast-util-from-html-isomorphic' | ||||
| // Remark packages | ||||
| import remarkGfm from 'remark-gfm' | ||||
| import remarkMath from 'remark-math' | ||||
| @@ -25,6 +26,19 @@ import { allCoreContent, sortPosts } from 'pliny/utils/contentlayer.js' | ||||
| const root = process.cwd() | ||||
| const isProduction = process.env.NODE_ENV === 'production' | ||||
|  | ||||
| // heroicon mini link | ||||
| const icon = fromHtmlIsomorphic( | ||||
|   ` | ||||
|   <span class="content-header-link"> | ||||
|   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-5 h-5 linkicon"> | ||||
|   <path d="M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z" /> | ||||
|   <path d="M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z" /> | ||||
|   </svg> | ||||
|   </span> | ||||
| `, | ||||
|   { fragment: true } | ||||
| ) | ||||
|  | ||||
| const computedFields: ComputedFields = { | ||||
|   readingTime: { type: 'json', resolve: (doc) => readingTime(doc.body.raw) }, | ||||
|   slug: { | ||||
| @@ -142,7 +156,16 @@ export default makeSource({ | ||||
|     ], | ||||
|     rehypePlugins: [ | ||||
|       rehypeSlug, | ||||
|       rehypeAutolinkHeadings, | ||||
|       [ | ||||
|         rehypeAutolinkHeadings, | ||||
|         { | ||||
|           behavior: 'prepend', | ||||
|           headingProperties: { | ||||
|             className: ['content-header'], | ||||
|           }, | ||||
|           content: icon, | ||||
|         }, | ||||
|       ], | ||||
|       rehypeKatex, | ||||
|       [rehypeCitation, { path: path.join(root, 'data') }], | ||||
|       [rehypePrismPlus, { defaultLanguage: 'js', ignoreMissing: true }], | ||||
|   | ||||
| @@ -33,3 +33,19 @@ input:-webkit-autofill:focus { | ||||
| .katex-display { | ||||
|   overflow: auto hidden; | ||||
| } | ||||
|  | ||||
| .content-header-link { | ||||
|   opacity: 0; | ||||
|   margin-left: -24px; | ||||
|   padding-right: 4px; | ||||
| } | ||||
|  | ||||
| .content-header:hover .content-header-link, | ||||
| .content-header-link:hover { | ||||
|   opacity: 1; | ||||
| } | ||||
|  | ||||
| .linkicon { | ||||
|   display: inline-block; | ||||
|   vertical-align: middle; | ||||
| } | ||||
|   | ||||
| @@ -33,7 +33,7 @@ export default Home | ||||
|  | ||||
| For a markdown file, the default image tag can be used and the default `img` tag gets replaced by the `Image` component in the build process. | ||||
|  | ||||
| Assuming we have a file called `ocean.jpg` in `data/img/ocean.jpg`, the following line of code would generate the optimized image. | ||||
| Assuming we have a file called `ocean.jpg` in `static/images/ocean.jpg`, the following line of code would generate the optimized image. | ||||
|  | ||||
| ``` | ||||
|  | ||||
|   | ||||
| @@ -81,7 +81,9 @@ export default function PostLayout({ content, authorDetails, next, prev, childre | ||||
|                               href={author.twitter} | ||||
|                               className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400" | ||||
|                             > | ||||
|                               {author.twitter.replace('https://twitter.com/', '@')} | ||||
|                               {author.twitter | ||||
|                                 .replace('https://twitter.com/', '@') | ||||
|                                 .replace('https://x.com/', '@')} | ||||
|                             </Link> | ||||
|                           )} | ||||
|                         </dd> | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| const { withContentlayer } = require('next-contentlayer') | ||||
| const { withContentlayer } = require('next-contentlayer2') | ||||
|  | ||||
| const withBundleAnalyzer = require('@next/bundle-analyzer')({ | ||||
|   enabled: process.env.ANALYZE === 'true', | ||||
|   | ||||
							
								
								
									
										62
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								package.json
									
									
									
									
									
								
							| @@ -8,64 +8,58 @@ | ||||
|     "build": "cross-env INIT_CWD=$PWD next build && cross-env NODE_OPTIONS='--experimental-json-modules' node ./scripts/postbuild.mjs", | ||||
|     "serve": "next start", | ||||
|     "analyze": "cross-env ANALYZE=true next build", | ||||
|     "lint": "next lint --fix --dir pages --dir app --dir components --dir lib --dir layouts --dir scripts" | ||||
|     "lint": "next lint --fix --dir pages --dir app --dir components --dir lib --dir layouts --dir scripts", | ||||
|     "prepare": "husky" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@next/bundle-analyzer": "14.1.0", | ||||
|     "@tailwindcss/forms": "^0.5.4", | ||||
|     "@tailwindcss/typography": "^0.5.9", | ||||
|     "@headlessui/react": "1.7.19", | ||||
|     "@next/bundle-analyzer": "14.2.1", | ||||
|     "@tailwindcss/forms": "^0.5.7", | ||||
|     "@tailwindcss/typography": "^0.5.12", | ||||
|     "autoprefixer": "^10.4.13", | ||||
|     "contentlayer": "0.3.4", | ||||
|     "esbuild": "0.18.11", | ||||
|     "contentlayer2": "0.4.4", | ||||
|     "esbuild": "0.20.2", | ||||
|     "github-slugger": "^2.0.0", | ||||
|     "gray-matter": "^4.0.2", | ||||
|     "hast-util-from-html-isomorphic": "^2.0.0", | ||||
|     "image-size": "1.0.0", | ||||
|     "next": "14.1.0", | ||||
|     "next-contentlayer": "0.3.4", | ||||
|     "next-themes": "^0.2.1", | ||||
|     "pliny": "0.1.7", | ||||
|     "next": "14.2.1", | ||||
|     "next-contentlayer2": "0.4.4", | ||||
|     "next-themes": "^0.3.0", | ||||
|     "pliny": "0.2.0", | ||||
|     "postcss": "^8.4.24", | ||||
|     "react": "18.2.0", | ||||
|     "react-dom": "18.2.0", | ||||
|     "reading-time": "1.5.0", | ||||
|     "rehype-autolink-headings": "^6.1.0", | ||||
|     "rehype-citation": "^1.0.2", | ||||
|     "rehype-katex": "^6.0.3", | ||||
|     "rehype-preset-minify": "6.0.0", | ||||
|     "rehype-prism-plus": "^1.6.0", | ||||
|     "rehype-slug": "^5.1.0", | ||||
|     "remark": "^14.0.2", | ||||
|     "remark-gfm": "^3.0.1", | ||||
|     "remark-math": "^5.1.1", | ||||
|     "tailwindcss": "^3.4.1", | ||||
|     "unist-util-visit": "^4.1.0" | ||||
|     "rehype-autolink-headings": "^7.1.0", | ||||
|     "rehype-citation": "^2.0.0", | ||||
|     "rehype-katex": "^7.0.0", | ||||
|     "rehype-preset-minify": "7.0.0", | ||||
|     "rehype-prism-plus": "^2.0.0", | ||||
|     "rehype-slug": "^6.0.0", | ||||
|     "remark": "^15.0.0", | ||||
|     "remark-gfm": "^4.0.0", | ||||
|     "remark-math": "^6.0.0", | ||||
|     "tailwindcss": "^3.4.3", | ||||
|     "unist-util-visit": "^5.0.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@svgr/webpack": "^8.0.1", | ||||
|     "@types/mdx": "^2.0.5", | ||||
|     "@types/react": "^18.2.14", | ||||
|     "@types/mdx": "^2.0.12", | ||||
|     "@types/react": "^18.2.73", | ||||
|     "@typescript-eslint/eslint-plugin": "^6.1.0", | ||||
|     "@typescript-eslint/parser": "^6.1.0", | ||||
|     "cross-env": "^7.0.3", | ||||
|     "eslint": "^8.45.0", | ||||
|     "eslint-config-next": "14.1.0", | ||||
|     "eslint-config-next": "14.2.1", | ||||
|     "eslint-config-prettier": "^8.8.0", | ||||
|     "eslint-plugin-prettier": "^5.0.0", | ||||
|     "husky": "^8.0.0", | ||||
|     "husky": "^9.0.0", | ||||
|     "lint-staged": "^13.0.0", | ||||
|     "prettier": "^3.0.0", | ||||
|     "prettier-plugin-tailwindcss": "^0.5.11", | ||||
|     "typescript": "^5.1.3" | ||||
|   }, | ||||
|   "resolutions": { | ||||
|     "@opentelemetry/api": "1.4.1", | ||||
|     "@opentelemetry/core": "1.13.0", | ||||
|     "@opentelemetry/exporter-trace-otlp-grpc": "0.39.1", | ||||
|     "@opentelemetry/resources": "1.13.0", | ||||
|     "@opentelemetry/sdk-trace-base": "1.13.0", | ||||
|     "@opentelemetry/sdk-trace-node": "1.13.0", | ||||
|     "@opentelemetry/semantic-conventions": "1.13.0" | ||||
|   }, | ||||
|   "lint-staged": { | ||||
|     "*.+(js|jsx|ts|tsx)": [ | ||||
|       "eslint --fix" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user