Merge branch 'main' of https://github.com/timlrx/tailwind-nextjs-starter-blog into upstream
This commit is contained in:
		
							
								
								
									
										33
									
								
								.env.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								.env.example
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| # visit https://giscus.app to get your Giscus ids | ||||
| NEXT_PUBLIC_GISCUS_REPO= | ||||
| NEXT_PUBLIC_GISCUS_REPOSITORY_ID= | ||||
| NEXT_PUBLIC_GISCUS_CATEGORY= | ||||
| NEXT_PUBLIC_GISCUS_CATEGORY_ID= | ||||
| NEXT_PUBLIC_UTTERANCES_REPO= | ||||
| NEXT_PUBLIC_DISQUS_SHORTNAME= | ||||
|  | ||||
|  | ||||
| MAILCHIMP_API_KEY= | ||||
| MAILCHIMP_API_SERVER= | ||||
| MAILCHIMP_AUDIENCE_ID= | ||||
|  | ||||
| BUTTONDOWN_API_KEY= | ||||
|  | ||||
| CONVERTKIT_API_KEY= | ||||
| # curl https://api.convertkit.com/v3/forms?api_key=<your_public_api_key> to get your form ID | ||||
| CONVERTKIT_FORM_ID= | ||||
|  | ||||
| KLAVIYO_API_KEY= | ||||
| 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= | ||||
|  | ||||
| # Create Beehive API key at https://developers.beehiiv.com/docs/v2/bktd9a7mxo67n-create-an-api-key | ||||
| BEEHIVE_API_KEY= | ||||
| BEEHIVE_PUBLICATION_ID= | ||||
| @@ -44,8 +44,8 @@ Internationalization support - [Template with i18n](https://tailwind-nextjs-star | ||||
| - [thetalhatahir.com](https://www.thetalhatahir.com) - Talha Tahir's personal blog. Added article thumbnails, linkedIn card, Beautiful hero content, technology emoticons. | ||||
| - [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. | ||||
| - [dariuszwozniak.net](https://dariuszwozniak.net/) - Software development blog ([source code](https://github.com/dariusz-wozniak/dariuszwozniak.net-v2)) | ||||
| - [dreams.plus](https://dreams.plus) - Blog site for some thoughts and records for life and technology. | ||||
| - [francisaguilar.co blog](https://francisaguilar.co) - Francis Aguilar's personal blog that talks about tech, fitness, and personal development. | ||||
| - [Min71 Dev Blog](https://min71.dev) - Personal blog about Blockchain, Development and etc. ([source code](https://github.com/mingi3442/blog)) | ||||
| - [Bryce Yu's Blog](https://earayu.github.io/) - Bryce Yu's personal Blog about distributed system, database, and web development. ([source code](https://github.com/earayu/earayu.github.io)) | ||||
| @@ -70,6 +70,9 @@ Internationalization support - [Template with i18n](https://tailwind-nextjs-star | ||||
| - [LyricsDecode.com](https://lyricsdecode.com) - A song lyrics website offering original lyrics, Romanisation, and English translations with customisable viewing options. | ||||
| - [bmacharia.com](https://bmacharia.com/) - Benson Macharia's technical blog about Cybersecurity and IT Risk Management. | ||||
| - [armujahid.me](https://armujahid.me/) - Abdul Rauf's personal blog about tech and random stuff. | ||||
| - [leohuynh.dev](leohuynh.dev) - 🇻🇳 Leo's dev blog – stories, insights, and ideas. Add `/snippets`, `/books` pages, add `ProfileCard`, `CareerTimeline` components and many more. | ||||
| - [OpenSats Blog](https://opensats.org/blog) - A 501(c)(3) public charity which aims to sustainably fund free and open-source projects. ([source code](https://github.com/OpenSats/website)) | ||||
| - [Schedles Blog](https://schedles.com/blog) - Social media scheduling tips, strategies, and product updates for content creators. ([Project Link](https://schedles.com)) | ||||
|  | ||||
| Using the template? Feel free to create a PR and add your blog to this list. | ||||
|  | ||||
| @@ -82,7 +85,6 @@ Thanks to the community of users and contributors to the template! We are no lon | ||||
| - [Aloisdg's cookbook](https://tambouille.vercel.app/) - with pictures and recipes! | ||||
| - [GautierArcin's demo with next translate](https://tailwind-nextjs-starter-blog-seven.vercel.app/) - includes translation of mdx posts, [source code](https://github.com/GautierArcin/tailwind-nextjs-starter-blog/tree/demo/next-translate) | ||||
| - [David Levai's digital garden](https://davidlevai.com/) - customized design and added email subscriptions | ||||
| - [Leo's Blog](https://leohuynh.dev) - Tuan Anh Huynh's personal site. Add Snippets Page, Author Profile Card, Image Lightbox, ... | ||||
| - [thvu.dev](https://thvu.dev) - Added `mdx-embed`, view count, reading minutes and more. | ||||
| - [irvin.dev](https://www.irvin.dev/) - Irvin Lin's personal site. Added YouTube embedding. | ||||
| - [KirillSo.com](https://www.kirillso.com/) - Personal blog & website. | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| import { NewsletterAPI } from 'pliny/newsletter' | ||||
| import siteMetadata from '@/data/siteMetadata' | ||||
|  | ||||
| export const dynamic = 'force-static' | ||||
|  | ||||
| const handler = NewsletterAPI({ | ||||
|   // @ts-ignore | ||||
|   provider: siteMetadata.newsletter.provider, | ||||
|   | ||||
| @@ -21,11 +21,10 @@ const layouts = { | ||||
|   PostBanner, | ||||
| } | ||||
|  | ||||
| export async function generateMetadata({ | ||||
|   params, | ||||
| }: { | ||||
|   params: { slug: string[] } | ||||
| export async function generateMetadata(props: { | ||||
|   params: Promise<{ slug: string[] }> | ||||
| }): Promise<Metadata | undefined> { | ||||
|   const params = await props.params | ||||
|   const slug = decodeURI(params.slug.join('/')) | ||||
|   const post = allBlogs.find((p) => p.slug === slug) | ||||
|   const authorList = post?.authors || ['default'] | ||||
| @@ -78,7 +77,8 @@ export const generateStaticParams = async () => { | ||||
|   return allBlogs.map((p) => ({ slug: p.slug.split('/').map((name) => decodeURI(name)) })) | ||||
| } | ||||
|  | ||||
| export default async function Page({ params }: { params: { slug: string[] } }) { | ||||
| export default async function Page(props: { params: Promise<{ slug: string[] }> }) { | ||||
|   const params = await props.params | ||||
|   const slug = decodeURI(params.slug.join('/')) | ||||
|   // Filter out drafts in production | ||||
|   const sortedCoreContents = allCoreContent(sortPosts(allBlogs)) | ||||
|   | ||||
| @@ -11,7 +11,8 @@ export const generateStaticParams = async () => { | ||||
|   return paths | ||||
| } | ||||
|  | ||||
| export default function Page({ params }: { params: { page: string } }) { | ||||
| export default async function Page(props: { params: Promise<{ page: string }> }) { | ||||
|   const params = await props.params | ||||
|   const posts = allCoreContent(sortPosts(allBlogs)) | ||||
|   const pageNumber = parseInt(params.page as string) | ||||
|   const initialDisplayPosts = posts.slice( | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| import { MetadataRoute } from 'next' | ||||
| import siteMetadata from '@/data/siteMetadata' | ||||
|  | ||||
| export const dynamic = 'force-static' | ||||
|  | ||||
| export default function robots(): MetadataRoute.Robots { | ||||
|   return { | ||||
|     rules: { | ||||
|   | ||||
| @@ -2,6 +2,8 @@ import { MetadataRoute } from 'next' | ||||
| import { allBlogs } from 'contentlayer/generated' | ||||
| import siteMetadata from '@/data/siteMetadata' | ||||
|  | ||||
| export const dynamic = 'force-static' | ||||
|  | ||||
| export default function sitemap(): MetadataRoute.Sitemap { | ||||
|   const siteUrl = siteMetadata.siteUrl | ||||
|  | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| {"python":1,"projects":1,"code":1,"cygnus":1,"self-hosted":1,"server":1} | ||||
| {"python":1,"projects":1,"code":1,"cygnus":1,"self-hosted":1,"server":1} | ||||
|   | ||||
| @@ -8,7 +8,10 @@ import { genPageMetadata } from 'app/seo' | ||||
| import { Metadata } from 'next' | ||||
| import { notFound } from 'next/navigation' | ||||
|  | ||||
| export async function generateMetadata({ params }: { params: { tag: string } }): Promise<Metadata> { | ||||
| export async function generateMetadata(props: { | ||||
|   params: Promise<{ tag: string }> | ||||
| }): Promise<Metadata> { | ||||
|   const params = await props.params | ||||
|   const tag = decodeURI(params.tag) | ||||
|   return genPageMetadata({ | ||||
|     title: tag, | ||||
| @@ -31,7 +34,8 @@ export const generateStaticParams = async () => { | ||||
|   return paths | ||||
| } | ||||
|  | ||||
| export default function TagPage({ params }: { params: { tag: string } }) { | ||||
| export default async function TagPage(props: { params: Promise<{ tag: string }> }) { | ||||
|   const params = await props.params | ||||
|   const tag = decodeURI(params.tag) | ||||
|   // Capitalize first letter and convert space to dash | ||||
|   const title = tag[0].toUpperCase() + tag.split(' ').join('-').slice(1) | ||||
|   | ||||
| @@ -16,6 +16,7 @@ export default function Footer() { | ||||
|           <SocialIcon kind="x" href={siteMetadata.x} size={6} /> | ||||
|           <SocialIcon kind="instagram" href={siteMetadata.instagram} size={6} /> | ||||
|           <SocialIcon kind="threads" href={siteMetadata.threads} size={6} /> | ||||
|           <SocialIcon kind="medium" href={siteMetadata.medium} size={6} /> | ||||
|         </div> | ||||
|         <div className="mb-2 flex space-x-2 text-sm text-gray-500 dark:text-gray-400"> | ||||
|           <div>{siteMetadata.author}</div> | ||||
|   | ||||
| @@ -28,10 +28,8 @@ const Header = () => { | ||||
|           )} | ||||
|         </div> | ||||
|       </Link> | ||||
|       {/* Changed this so I can get all of my links in the header 10-19-2024. Backup Config */} | ||||
|       <div className="flex items-center space-x-4 leading-5 sm:space-x-6"> | ||||
|         <div className="no-scrollbar max-w-50 hidden items-center space-x-4 overflow-x-auto sm:flex md:flex lg:flex"> | ||||
|           {/* <div className="no-scrollbar hidden max-w-50 items-center space-x-4 overflow-x-auto sm:flex sm:space-x-6 md:max-w-72 lg:max-w-96"> */} | ||||
|         <div className="no-scrollbar hidden max-w-40 items-center space-x-4 overflow-x-auto sm:flex sm:space-x-6 md:max-w-72 lg:max-w-96"> | ||||
|           {headerNavLinks | ||||
|             .filter((link) => link.href !== '/') | ||||
|             .map((link) => ( | ||||
|   | ||||
| @@ -93,3 +93,12 @@ export function Instagram(svgProps: SVGProps<SVGSVGElement>) { | ||||
|     </svg> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export function Medium(svgProps: SVGProps<SVGSVGElement>) { | ||||
|   return ( | ||||
|     <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...svgProps}> | ||||
|       <title>Medium</title> | ||||
|       <path d="M13.54 12a6.8 6.8 0 01-6.77 6.82A6.8 6.8 0 010 12a6.8 6.8 0 016.77-6.82A6.8 6.8 0 0113.54 12zM20.96 12c0 3.54-1.51 6.42-3.38 6.42-1.87 0-3.39-2.88-3.39-6.42s1.52-6.42 3.39-6.42 3.38 2.88 3.38 6.42M24 12c0 3.17-.53 5.75-1.19 5.75-.66 0-1.19-2.58-1.19-5.75s.53-5.75 1.19-5.75C23.47 6.25 24 8.83 24 12z" /> | ||||
|     </svg> | ||||
|   ) | ||||
| } | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import { | ||||
|   Mastodon, | ||||
|   Threads, | ||||
|   Instagram, | ||||
|   Medium, | ||||
| } from './icons' | ||||
|  | ||||
| const components = { | ||||
| @@ -22,6 +23,7 @@ const components = { | ||||
|   mastodon: Mastodon, | ||||
|   threads: Threads, | ||||
|   instagram: Instagram, | ||||
|   medium: Medium, | ||||
| } | ||||
|  | ||||
| type SocialIconProps = { | ||||
|   | ||||
| @@ -18,6 +18,7 @@ import { | ||||
| import rehypeSlug from 'rehype-slug' | ||||
| import rehypeAutolinkHeadings from 'rehype-autolink-headings' | ||||
| import rehypeKatex from 'rehype-katex' | ||||
| import rehypeKatexNoTranslate from 'rehype-katex-notranslate' | ||||
| import rehypeCitation from 'rehype-citation' | ||||
| import rehypePrismPlus from 'rehype-prism-plus' | ||||
| import rehypePresetMinify from 'rehype-preset-minify' | ||||
| @@ -169,6 +170,7 @@ export default makeSource({ | ||||
|         }, | ||||
|       ], | ||||
|       rehypeKatex, | ||||
|       rehypeKatexNoTranslate, | ||||
|       [rehypeCitation, { path: path.join(root, 'data') }], | ||||
|       [rehypePrismPlus, { defaultLanguage: 'js', ignoreMissing: true }], | ||||
|       rehypePresetMinify, | ||||
|   | ||||
| @@ -80,7 +80,7 @@ | ||||
| } | ||||
|  | ||||
| .token.boolean { | ||||
|   color: rgb(138, 21, 40); | ||||
|   color: rgb(255, 88, 116); | ||||
| } | ||||
|  | ||||
| .token.number { | ||||
|   | ||||
| @@ -13,4 +13,4 @@ Jonathan Branan is a Software Engineer at Fortra. He is mostly self-taught howev | ||||
| He has worked for Geek Squad repairing computers, GlobalScape as a Lead of the Client Services department and as a Product Owner of MFT applications at Fortra. | ||||
|  | ||||
| He currently lives in San Antonio, Texas with his wife and two dogs. He enjoys Basketball, Video games, cooking, camping(backpacking, glamping), watching movies, | ||||
| making mixed drinks and building Lego's. Jonathan and his wife like to frequently travel. | ||||
| making mixed drinks and building Lego's. Jonathan and his wife like to frequently travel. | ||||
|   | ||||
| @@ -20,6 +20,7 @@ const siteMetadata = { | ||||
|   linkedin: 'https://www.linkedin.com/in/jonathanbranan/', | ||||
|   // threads: 'https://www.threads.net', | ||||
|   // instagram: 'https://www.instagram.com', | ||||
|   // medium: 'https://medium.com', | ||||
|   locale: 'en-US', | ||||
|   // set to true if you want a navbar fixed to the top | ||||
|   stickyNav: false, | ||||
|   | ||||
| @@ -66,7 +66,7 @@ function createSearchIndex(allBlogs) { | ||||
|   ) { | ||||
|     writeFileSync( | ||||
|       `public/${siteMetadata.search.kbarConfig.searchDocumentsPath}`, | ||||
|       JSON.stringify((sortPosts(allBlogs))) | ||||
|       JSON.stringify(sortPosts(allBlogs)) | ||||
|     ) | ||||
|     console.log('Local search index generated...') | ||||
|   } | ||||
|   | ||||
							
								
								
									
										20
									
								
								faq/deploy-with-docker.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								faq/deploy-with-docker.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| # Deploy with Docker | ||||
|  | ||||
| Follow the [official Next.js repo docker build example and instructions](https://github.com/vercel/next.js/tree/canary/examples/with-docker) to deploy with docker. Copy the [`Dockerfile`](https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile) into the root of the project and modify the `next.config.js` file: | ||||
|  | ||||
| ```js | ||||
| // next.config.js | ||||
| module.exports = { | ||||
|   // ... rest of the configuration. | ||||
|   output: 'standalone', | ||||
| } | ||||
| ``` | ||||
|  | ||||
| You can now build the docker image and run it: | ||||
|  | ||||
| ```bash | ||||
| docker build -t nextjs-docker . | ||||
| docker run -p 3000:3000 nextjs-docker | ||||
| ``` | ||||
|  | ||||
| Alternatively, to use docker compose, refer to the [docker compose repo](https://github.com/vercel/next.js/tree/canary/examples/with-docker-compose). | ||||
| @@ -100,7 +100,7 @@ export default function PostLayout({ content, authorDetails, next, prev, childre | ||||
|                   Discuss on Twitter | ||||
|                 </Link> | ||||
|                 {` • `} | ||||
|                 <Link href={editUrl(filePath)}>View on gitea</Link> | ||||
|                 <Link href={editUrl(filePath)}>View on Gitea</Link> | ||||
|               </div> | ||||
|               {siteMetadata.comments && ( | ||||
|                 <div | ||||
|   | ||||
							
								
								
									
										37
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "tailwind-nextjs-starter-blog", | ||||
|   "version": "2.2.0", | ||||
|   "version": "2.3.0", | ||||
|   "private": true, | ||||
|   "scripts": { | ||||
|     "start": "next dev", | ||||
| @@ -12,29 +12,30 @@ | ||||
|     "prepare": "husky" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@headlessui/react": "1.7.19", | ||||
|     "@next/bundle-analyzer": "14.2.3", | ||||
|     "@tailwindcss/forms": "^0.5.7", | ||||
|     "@tailwindcss/typography": "^0.5.12", | ||||
|     "@headlessui/react": "2.2.0", | ||||
|     "@next/bundle-analyzer": "15.0.2", | ||||
|     "@tailwindcss/forms": "^0.5.9", | ||||
|     "@tailwindcss/typography": "^0.5.15", | ||||
|     "autoprefixer": "^10.4.13", | ||||
|     "body-scroll-lock": "^4.0.0-beta.0", | ||||
|     "contentlayer2": "0.5.1", | ||||
|     "contentlayer2": "0.5.3", | ||||
|     "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.2.3", | ||||
|     "next-contentlayer2": "0.5.1", | ||||
|     "next": "15.0.2", | ||||
|     "next-contentlayer2": "0.5.3", | ||||
|     "next-themes": "^0.3.0", | ||||
|     "pliny": "0.2.1", | ||||
|     "pliny": "0.4.0", | ||||
|     "postcss": "^8.4.24", | ||||
|     "react": "18.3.1", | ||||
|     "react-dom": "18.3.1", | ||||
|     "react": "rc", | ||||
|     "react-dom": "rc", | ||||
|     "reading-time": "1.5.0", | ||||
|     "rehype-autolink-headings": "^7.1.0", | ||||
|     "rehype-citation": "^2.0.0", | ||||
|     "rehype-katex": "^7.0.0", | ||||
|     "rehype-katex-notranslate": "^1.1.4", | ||||
|     "rehype-preset-minify": "7.0.0", | ||||
|     "rehype-prism-plus": "^2.0.0", | ||||
|     "rehype-slug": "^6.0.0", | ||||
| @@ -42,20 +43,20 @@ | ||||
|     "remark-gfm": "^4.0.0", | ||||
|     "remark-github-blockquote-alert": "^1.2.1", | ||||
|     "remark-math": "^6.0.0", | ||||
|     "tailwindcss": "^3.4.3", | ||||
|     "tailwindcss": "^3.4.14", | ||||
|     "unist-util-visit": "^5.0.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@svgr/webpack": "^8.0.1", | ||||
|     "@types/mdx": "^2.0.12", | ||||
|     "@types/react": "^18.2.73", | ||||
|     "@typescript-eslint/eslint-plugin": "^6.1.0", | ||||
|     "@typescript-eslint/parser": "^6.1.0", | ||||
|     "@typescript-eslint/eslint-plugin": "^8.12.0", | ||||
|     "@typescript-eslint/parser": "^8.12.0", | ||||
|     "cross-env": "^7.0.3", | ||||
|     "eslint": "^8.45.0", | ||||
|     "eslint-config-next": "14.2.3", | ||||
|     "eslint-config-prettier": "^8.8.0", | ||||
|     "eslint-plugin-prettier": "^5.0.0", | ||||
|     "eslint": "^9.14.0", | ||||
|     "eslint-config-next": "15.0.2", | ||||
|     "eslint-config-prettier": "^9.1.0", | ||||
|     "eslint-plugin-prettier": "^5.2.0", | ||||
|     "husky": "^9.0.0", | ||||
|     "lint-staged": "^13.0.0", | ||||
|     "prettier": "^3.0.0", | ||||
|   | ||||
							
								
								
									
										9
									
								
								public/static/favicons/browserconfig.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								public/static/favicons/browserconfig.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <browserconfig> | ||||
|     <msapplication> | ||||
|         <tile> | ||||
|             <square150x150logo src="/mstile-150x150.png"/> | ||||
|             <TileColor>#000000</TileColor> | ||||
|         </tile> | ||||
|     </msapplication> | ||||
| </browserconfig> | ||||
| @@ -3,10 +3,12 @@ import path from 'path' | ||||
| import { slug } from 'github-slugger' | ||||
| import { escape } from 'pliny/utils/htmlEscaper.js' | ||||
| import siteMetadata from '../data/siteMetadata.js' | ||||
| import tagData from '../app/tag-data.json' with { type: 'json' } | ||||
| import tagData from '../app/tag-data.json' assert { type: 'json' } | ||||
| import { allBlogs } from '../.contentlayer/generated/index.mjs' | ||||
| import { sortPosts } from 'pliny/utils/contentlayer.js' | ||||
|  | ||||
| const outputFolder = process.env.EXPORT ? 'out' : 'public' | ||||
|  | ||||
| const generateRssItem = (config, post) => ` | ||||
|   <item> | ||||
|     <guid>${config.siteUrl}/blog/${post.slug}</guid> | ||||
| @@ -40,14 +42,14 @@ async function generateRSS(config, allBlogs, page = 'feed.xml') { | ||||
|   // RSS for blog post | ||||
|   if (publishPosts.length > 0) { | ||||
|     const rss = generateRss(config, sortPosts(publishPosts)) | ||||
|     writeFileSync(`./public/${page}`, rss) | ||||
|     writeFileSync(`./${outputFolder}/${page}`, rss) | ||||
|   } | ||||
|  | ||||
|   if (publishPosts.length > 0) { | ||||
|     for (const tag of Object.keys(tagData)) { | ||||
|       const filteredPosts = allBlogs.filter((post) => post.tags.map((t) => slug(t)).includes(tag)) | ||||
|       const rss = generateRss(config, filteredPosts, `tags/${tag}/${page}`) | ||||
|       const rssPath = path.join('public', 'tags', tag) | ||||
|       const rssPath = path.join(outputFolder, 'tags', tag) | ||||
|       mkdirSync(rssPath, { recursive: true }) | ||||
|       writeFileSync(path.join(rssPath, page), rss) | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user