upstream #1
| @@ -3,7 +3,7 @@ import Link from '@/components/Link' | ||||
| import Tag from '@/components/Tag' | ||||
| import siteMetadata from '@/data/siteMetadata' | ||||
| import { formatDate } from 'pliny/utils/formatDate' | ||||
| import { NewsletterForm } from 'pliny/ui/NewsletterForm' | ||||
| import NewsletterForm from 'pliny/ui/NewsletterForm' | ||||
|  | ||||
| const MAX_DISPLAY = 5 | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { Authors, allAuthors } from 'contentlayer/generated' | ||||
| import { Mdx } from '@/components/MDXComponents' | ||||
| import { MDXLayoutRenderer } from 'pliny/mdx-components' | ||||
| import AuthorLayout from '@/layouts/AuthorLayout' | ||||
| import { coreContent } from 'pliny/utils/contentlayer' | ||||
| import { genPageMetadata } from 'app/seo' | ||||
| @@ -13,7 +13,7 @@ export default function Page() { | ||||
|   return ( | ||||
|     <> | ||||
|       <AuthorLayout content={mainContent}> | ||||
|         <Mdx code={author.body.code} /> | ||||
|         <MDXLayoutRenderer code={author.body.code} /> | ||||
|       </AuthorLayout> | ||||
|     </> | ||||
|   ) | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import PageTitle from '@/components/PageTitle' | ||||
| import { Mdx } from '@/components/MDXComponents' | ||||
| import { components } from '@/components/MDXComponents' | ||||
| import { MDXLayoutRenderer } from 'pliny/mdx-components' | ||||
| import { sortedBlogPost, coreContent } from 'pliny/utils/contentlayer' | ||||
| import { allBlogs, allAuthors } from 'contentlayer/generated' | ||||
| import type { Authors, Blog } from 'contentlayer/generated' | ||||
| @@ -104,7 +105,7 @@ export default async function Page({ params }: { params: { slug: string[] } }) { | ||||
|             {JSON.stringify(jsonLd)} | ||||
|           </script> | ||||
|           <PostLayout content={mainContent} authorDetails={authorDetails} next={next} prev={prev}> | ||||
|             <Mdx code={post.body.code} toc={post.toc} /> | ||||
|             <MDXLayoutRenderer code={post.body.code} components={components} toc={post.toc} /> | ||||
|           </PostLayout> | ||||
|         </> | ||||
|       )} | ||||
|   | ||||
| @@ -21,14 +21,11 @@ export default function BlogPage() { | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       {/* <PageSEO title={`Blog - ${siteMetadata.author}`} description={siteMetadata.description} /> */} | ||||
|       <ListLayout | ||||
|         posts={posts} | ||||
|         initialDisplayPosts={initialDisplayPosts} | ||||
|         pagination={pagination} | ||||
|         title="All Posts" | ||||
|       /> | ||||
|     </> | ||||
|     <ListLayout | ||||
|       posts={posts} | ||||
|       initialDisplayPosts={initialDisplayPosts} | ||||
|       pagination={pagination} | ||||
|       title="All Posts" | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| // import { PageSEO } from '@/components/SEO' | ||||
| import siteMetadata from '@/data/siteMetadata' | ||||
| import ListLayout from '@/layouts/ListLayout' | ||||
| import { allCoreContent, sortedBlogPost } from 'pliny/utils/contentlayer' | ||||
| import { allBlogs } from 'contentlayer/generated' | ||||
| @@ -28,14 +26,11 @@ export default function Page({ params }: { params: { page: string } }) { | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       {/* <PageSEO title={siteMetadata.title} description={siteMetadata.description} /> */} | ||||
|       <ListLayout | ||||
|         posts={allCoreContent(posts)} | ||||
|         initialDisplayPosts={allCoreContent(initialDisplayPosts)} | ||||
|         pagination={pagination} | ||||
|         title="All Posts" | ||||
|       /> | ||||
|     </> | ||||
|     <ListLayout | ||||
|       posts={allCoreContent(posts)} | ||||
|       initialDisplayPosts={allCoreContent(initialDisplayPosts)} | ||||
|       pagination={pagination} | ||||
|       title="All Posts" | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|   | ||||
| @@ -1,29 +0,0 @@ | ||||
| @tailwind base; | ||||
| @tailwind components; | ||||
| @tailwind utilities; | ||||
|  | ||||
| .task-list-item::before { | ||||
|     @apply hidden; | ||||
| } | ||||
|  | ||||
| .task-list-item { | ||||
|     @apply list-none; | ||||
| } | ||||
|  | ||||
| .footnotes { | ||||
|     @apply mt-12 border-t border-gray-200 pt-8 dark:border-gray-700; | ||||
| } | ||||
|  | ||||
| .data-footnote-backref { | ||||
|     @apply no-underline; | ||||
| } | ||||
|  | ||||
| .csl-entry { | ||||
|     @apply my-5; | ||||
| } | ||||
|  | ||||
| /* https://stackoverflow.com/questions/61083813/how-to-avoid-internal-autofill-selected-style-to-be-applied */ | ||||
| input:-webkit-autofill, | ||||
| input:-webkit-autofill:focus { | ||||
|     transition: background-color 600000s 0s, color 600000s 0s; | ||||
| } | ||||
| @@ -1,8 +1,10 @@ | ||||
| import './globals.css' | ||||
| import 'css/tailwind.css' | ||||
| import 'css/prism.css' | ||||
| import 'pliny/search/algolia.css' | ||||
|  | ||||
| import { Inter } from 'next/font/google' | ||||
| import { Analytics } from 'pliny/analytics' | ||||
| import { SearchProvider } from 'pliny/search' | ||||
| import { Analytics, AnalyticsConfig } from 'pliny/analytics' | ||||
| import { SearchProvider, SearchConfig } from 'pliny/search' | ||||
| import Header from '@/components/Header' | ||||
| import SectionContainer from '@/components/SectionContainer' | ||||
| import Footer from '@/components/Footer' | ||||
| @@ -68,13 +70,13 @@ export default function RootLayout({ children }: { children: React.ReactNode }) | ||||
|       <link rel="alternate" type="application/rss+xml" href="/feed.xml" /> | ||||
|       <body className="bg-white text-black antialiased dark:bg-gray-900 dark:text-white"> | ||||
|         <ThemeProviders> | ||||
|           {/* <Analytics analyticsConfig={siteMetadata.analytics} /> */} | ||||
|           <Analytics analyticsConfig={siteMetadata.analytics as AnalyticsConfig} /> | ||||
|           <SectionContainer> | ||||
|             <div className="flex h-screen flex-col justify-between font-sans"> | ||||
|               {/* <SearchProvider searchConfig={siteMetadata.search}> */} | ||||
|               <Header /> | ||||
|               <main className="mb-auto">{children}</main> | ||||
|               {/* </SearchProvider> */} | ||||
|               <SearchProvider searchConfig={siteMetadata.search as SearchConfig}> | ||||
|                 <Header /> | ||||
|                 <main className="mb-auto">{children}</main> | ||||
|               </SearchProvider> | ||||
|               <Footer /> | ||||
|             </div> | ||||
|           </SectionContainer> | ||||
|   | ||||
| @@ -1,7 +0,0 @@ | ||||
| 'use client' | ||||
|  | ||||
| // dot notation breaks RSC - https://github.com/vercel/next.js/issues/51593 | ||||
| // temporarily workaround to re-export | ||||
| import { BlogNewsletterForm } from 'pliny/ui/NewsletterForm' | ||||
|  | ||||
| export default BlogNewsletterForm | ||||
							
								
								
									
										17
									
								
								components/Comments.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								components/Comments.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| 'use client' | ||||
|  | ||||
| import { Comments as CommentsComponent } from 'pliny/comments' | ||||
| import { useState } from 'react' | ||||
| import siteMetadata from '@/data/siteMetadata' | ||||
|  | ||||
| export default function Comments({ slug }: { slug: string }) { | ||||
|   const [loadComments, setLoadComments] = useState(false) | ||||
|   return ( | ||||
|     <> | ||||
|       {!loadComments && <button onClick={() => setLoadComments(true)}>Load Comments</button>} | ||||
|       {siteMetadata.comments && loadComments && ( | ||||
|         <CommentsComponent commentsConfig={siteMetadata.comments} slug={slug} /> | ||||
|       )} | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
| @@ -1,30 +1,14 @@ | ||||
| import React from 'react' | ||||
| import { useMDXComponent } from 'next-contentlayer/hooks' | ||||
| import { ComponentMap } from 'pliny/mdx-components' | ||||
| import { TOCInline } from 'pliny/ui/TOCInline' | ||||
| import TOCInline from 'pliny/ui/TOCInline' | ||||
| import Pre from './Pre' | ||||
| import BlogNewsletterForm from './BlogNewsletterForm' | ||||
| // import { BlogNewsletterForm } from 'pliny/ui/NewsletterForm' | ||||
|  | ||||
| import BlogNewsletterForm from 'pliny/ui/NewsletterForm' | ||||
| import type { MDXComponents } from 'mdx/types' | ||||
| import Image from './Image' | ||||
| import CustomLink from './Link' | ||||
|  | ||||
| interface MdxProps { | ||||
|   code: string | ||||
|   // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||
|   [key: string]: any | ||||
| } | ||||
|  | ||||
| export const MDXComponents: ComponentMap = { | ||||
| export const components: MDXComponents = { | ||||
|   Image, | ||||
|   TOCInline, | ||||
|   a: CustomLink, | ||||
|   pre: Pre, | ||||
|   BlogNewsletterForm, | ||||
| } | ||||
|  | ||||
| export function Mdx({ code, ...rest }: MdxProps) { | ||||
|   const Component = useMDXComponent(code) | ||||
|  | ||||
|   return <Component components={{ ...MDXComponents }} {...rest} /> | ||||
| } | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import { | ||||
|   remarkCodeTitles, | ||||
|   remarkImgToJsx, | ||||
|   extractTocHeadings, | ||||
| } from 'pliny/mdx-plugins.js' | ||||
| } from 'pliny/mdx-plugins/index.js' | ||||
| // Rehype packages | ||||
| import rehypeSlug from 'rehype-slug' | ||||
| import rehypeAutolinkHeadings from 'rehype-autolink-headings' | ||||
| @@ -20,6 +20,7 @@ import rehypeCitation from 'rehype-citation' | ||||
| import rehypePrismPlus from 'rehype-prism-plus' | ||||
| import rehypePresetMinify from 'rehype-preset-minify' | ||||
| import siteMetadata from './data/siteMetadata' | ||||
| import { allCoreContent } from 'pliny/utils/contentlayer.js' | ||||
|  | ||||
| const root = process.cwd() | ||||
|  | ||||
| @@ -60,6 +61,19 @@ function createTagCount(allBlogs) { | ||||
|   writeFileSync('./app/tag-data.json', JSON.stringify(tagCount)) | ||||
| } | ||||
|  | ||||
| function createSearchIndex(allBlogs) { | ||||
|   if ( | ||||
|     siteMetadata?.search?.provider === 'kbar' && | ||||
|     siteMetadata.search.kbarConfig.searchDocumentsPath | ||||
|   ) { | ||||
|     writeFileSync( | ||||
|       `public/${siteMetadata.search.kbarConfig.searchDocumentsPath}`, | ||||
|       JSON.stringify(allCoreContent(allBlogs)) | ||||
|     ) | ||||
|     console.log('Local search index generated...') | ||||
|   } | ||||
| } | ||||
|  | ||||
| export const Blog = defineDocumentType(() => ({ | ||||
|   name: 'Blog', | ||||
|   filePathPattern: 'blog/**/*.mdx', | ||||
|   | ||||
| @@ -1,63 +0,0 @@ | ||||
|  | ||||
| .light .DocSearch { | ||||
|     --docsearch-primary-color: theme(colors.primary.600); | ||||
|     --docsearch-highlight-color: theme(colors.primary.600); | ||||
|     --docsearch-searchbox-shadow: inset 0 0 0 2px theme(colors.primary.600); | ||||
|     --docsearch-muted-color: theme(colors.gray.500); | ||||
|     --docsearch-container-background: theme(colors.gray.400 / 80%); | ||||
|     /* Modal */ | ||||
|     --docsearch-modal-background: theme(colors.gray.200); | ||||
|     /* Search box */ | ||||
|     --docsearch-searchbox-background: theme(colors.gray.100); | ||||
|     --docsearch-searchbox-focus-background: theme(colors.gray.100); | ||||
|     /* Hit */ | ||||
|     --docsearch-hit-color: theme(colors.gray.700); | ||||
|     --docsearch-hit-shadow: none; | ||||
|     --docsearch-hit-active-color: theme(colors.gray.800); | ||||
|     --docsearch-hit-background: theme(colors.gray.100); | ||||
|     /* Footer */ | ||||
|     --docsearch-footer-background: theme(colors.gray.100); | ||||
| } | ||||
|  | ||||
| .dark .DocSearch { | ||||
|     --docsearch-primary-color: theme(colors.primary.600); | ||||
|     --docsearch-highlight-color: theme(colors.primary.600); | ||||
|     --docsearch-searchbox-shadow: inset 0 0 0 2px theme(colors.primary.600); | ||||
|     --docsearch-text-color: theme(colors.gray.200); | ||||
|     --docsearch-muted-color: theme(colors.gray.400); | ||||
|     --docsearch-container-background: theme(colors.gray.900 / 80%); | ||||
|     /* Modal */ | ||||
|     --docsearch-modal-background: theme(colors.gray.900); | ||||
|     --docsearch-modal-shadow: inset 1px 1px 0 0 rgb(44, 46, 64), | ||||
|             0 3px 8px 0 rgb(0, 3, 9); | ||||
|     /* Search box */ | ||||
|     --docsearch-searchbox-background: theme(colors.gray.800); | ||||
|     --docsearch-searchbox-focus-background: theme(colors.gray.800); | ||||
|     /* Hit */ | ||||
|     --docsearch-hit-color: theme(colors.gray.200); | ||||
|     --docsearch-hit-shadow: none; | ||||
|     --docsearch-hit-active-color: theme(colors.gray.100); | ||||
|     --docsearch-hit-background: theme(colors.gray.800); | ||||
|     /* Footer */ | ||||
|     --docsearch-footer-background: theme(colors.gray.900); | ||||
|     --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5), | ||||
|             0 -4px 8px 0 rgba(0, 0, 0, 0.2); | ||||
|     --docsearch-key-gradient: linear-gradient(-26.5deg, | ||||
|             theme(colors.gray.800) 0%, | ||||
|             theme(colors.gray.900) 100%); | ||||
|     --docsearch-key-shadow: inset 0 -2px 0 0 rgb(40, 45, 85), | ||||
|     inset 0 0 1px 1px rgb(81, 87, 125), 0 2px 2px 0 rgba(3, 4, 9, 0.3); | ||||
|     --docsearch-logo-color: theme(colors.gray.300); | ||||
| } | ||||
|  | ||||
| .light .DocSearch-Input { | ||||
|     @apply hover:ring-0 ring-0; | ||||
| } | ||||
|  | ||||
| .dark .DocSearch-Input { | ||||
|     @apply hover:ring-0 ring-0; | ||||
| } | ||||
|  | ||||
| .DocSearch-Container { | ||||
|     @apply backdrop-blur; | ||||
| } | ||||
| @@ -65,20 +65,20 @@ const siteMetadata = { | ||||
|       lang: 'en', | ||||
|     }, | ||||
|   }, | ||||
|   // search: { | ||||
|   //   provider: 'kbar', // kbar or algolia | ||||
|   //   kbarConfig: { | ||||
|   //     searchDocumentsPath: 'search.json', // path to load documents to search | ||||
|   //   }, | ||||
|   //   provider: 'algolia', | ||||
|   //   algoliaConfig: { | ||||
|   //     // The application ID provided by Algolia | ||||
|   //     appId: 'R2IYF7ETH7', | ||||
|   //     // Public API key: it is safe to commit it | ||||
|   //     apiKey: '599cec31baffa4868cae4e79f180729b', | ||||
|   //     indexName: 'docsearch', | ||||
|   //   }, | ||||
|   // }, | ||||
|   search: { | ||||
|     // provider: 'kbar', // kbar or algolia | ||||
|     // kbarConfig: { | ||||
|     //   searchDocumentsPath: 'search.json', // path to load documents to search | ||||
|     // }, | ||||
|     provider: 'algolia', | ||||
|     algoliaConfig: { | ||||
|       // The application ID provided by Algolia | ||||
|       appId: 'R2IYF7ETH7', | ||||
|       // Public API key: it is safe to commit it | ||||
|       apiKey: '599cec31baffa4868cae4e79f180729b', | ||||
|       indexName: 'docsearch', | ||||
|     }, | ||||
|   }, | ||||
| } | ||||
|  | ||||
| module.exports = siteMetadata | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { useState, ReactNode } from 'react' | ||||
| import { Comments } from 'pliny/comments' | ||||
| import { ReactNode } from 'react' | ||||
| import { CoreContent } from 'pliny/utils/contentlayer' | ||||
| import type { Blog, Authors } from 'contentlayer/generated' | ||||
| import Comments from '@/components/Comments' | ||||
| import Link from '@/components/Link' | ||||
| import PageTitle from '@/components/PageTitle' | ||||
| import SectionContainer from '@/components/SectionContainer' | ||||
| @@ -33,7 +33,6 @@ interface LayoutProps { | ||||
| export default function PostLayout({ content, authorDetails, next, prev, children }: LayoutProps) { | ||||
|   const { filePath, path, slug, date, title, tags } = content | ||||
|   const basePath = path.split('/')[0] | ||||
|   // const [loadComments, setLoadComments] = useState(false) | ||||
|  | ||||
|   return ( | ||||
|     <SectionContainer> | ||||
| @@ -107,10 +106,7 @@ export default function PostLayout({ content, authorDetails, next, prev, childre | ||||
|                   className="pb-6 pt-6 text-center text-gray-700 dark:text-gray-300" | ||||
|                   id="comment" | ||||
|                 > | ||||
|                   {/* {!loadComments && ( | ||||
|                     <button onClick={() => setLoadComments(true)}>Load Comments</button> | ||||
|                   )} | ||||
|                   {loadComments && <Comments commentsConfig={siteMetadata.comments} slug={slug} />} */} | ||||
|                   <Comments slug={slug} /> | ||||
|                 </div> | ||||
|               )} | ||||
|             </div> | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { useState, ReactNode } from 'react' | ||||
| import { Comments } from 'pliny/comments' | ||||
| import { ReactNode } from 'react' | ||||
| import { formatDate } from 'pliny/utils/formatDate' | ||||
| import { CoreContent } from 'pliny/utils/contentlayer' | ||||
| import type { Blog } from 'contentlayer/generated' | ||||
| import Comments from '@/components/Comments' | ||||
| import Link from '@/components/Link' | ||||
| import PageTitle from '@/components/PageTitle' | ||||
| import SectionContainer from '@/components/SectionContainer' | ||||
| @@ -17,8 +17,6 @@ interface LayoutProps { | ||||
| } | ||||
|  | ||||
| export default function PostLayout({ content, next, prev, children }: LayoutProps) { | ||||
|   const [loadComments, setLoadComments] = useState(false) | ||||
|  | ||||
|   const { path, slug, date, title } = content | ||||
|  | ||||
|   return ( | ||||
| @@ -47,10 +45,7 @@ export default function PostLayout({ content, next, prev, children }: LayoutProp | ||||
|             </div> | ||||
|             {siteMetadata.comments && ( | ||||
|               <div className="pb-6 pt-6 text-center text-gray-700 dark:text-gray-300" id="comment"> | ||||
|                 {!loadComments && ( | ||||
|                   <button onClick={() => setLoadComments(true)}>Load Comments</button> | ||||
|                 )} | ||||
|                 {loadComments && <Comments commentsConfig={siteMetadata.comments} slug={slug} />} | ||||
|                 <Comments slug={slug} /> | ||||
|               </div> | ||||
|             )} | ||||
|             <footer> | ||||
|   | ||||
| @@ -24,7 +24,7 @@ | ||||
|     "next": "13.4.9", | ||||
|     "next-contentlayer": "0.3.4", | ||||
|     "next-themes": "^0.2.1", | ||||
|     "pliny": "0.0.10", | ||||
|     "pliny": "0.1.0-beta.3", | ||||
|     "postcss": "^8.4.24", | ||||
|     "react": "18.2.0", | ||||
|     "react-dom": "18.2.0", | ||||
| @@ -79,4 +79,4 @@ | ||||
|     ] | ||||
|   }, | ||||
|   "packageManager": "yarn@3.6.1" | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,60 @@ | ||||
| import { generateRSS } from 'pliny/utils/generate-rss.js' | ||||
| import { writeFileSync, mkdirSync } from 'fs' | ||||
| import path from 'path' | ||||
| // import { generateRSS } from 'pliny/utils/generate-rss.js' | ||||
| import GithubSlugger from 'github-slugger' | ||||
| import siteMetadata from '../data/siteMetadata.js' | ||||
| import tagData from '../app/tag-data.json' assert { type: 'json' } | ||||
| import { allBlogs } from '../.contentlayer/generated/index.mjs' | ||||
|  | ||||
| const generateRssItem = (config, post) => ` | ||||
|   <item> | ||||
|     <guid>${config.siteUrl}/blog/${post.slug}</guid> | ||||
|     <title>${escape(post.title)}</title> | ||||
|     <link>${config.siteUrl}/blog/${post.slug}</link> | ||||
|     ${post.summary && `<description>${escape(post.summary)}</description>`} | ||||
|     <pubDate>${new Date(post.date).toUTCString()}</pubDate> | ||||
|     <author>${config.email} (${config.author})</author> | ||||
|     ${post.tags && post.tags.map((t) => `<category>${t}</category>`).join('')} | ||||
|   </item> | ||||
| ` | ||||
|  | ||||
| const generateRss = (config, posts, page = 'feed.xml') => ` | ||||
|   <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> | ||||
|     <channel> | ||||
|       <title>${escape(config.title)}</title> | ||||
|       <link>${config.siteUrl}/blog</link> | ||||
|       <description>${escape(config.description)}</description> | ||||
|       <language>${config.language}</language> | ||||
|       <managingEditor>${config.email} (${config.author})</managingEditor> | ||||
|       <webMaster>${config.email} (${config.author})</webMaster> | ||||
|       <lastBuildDate>${new Date(posts[0].date).toUTCString()}</lastBuildDate> | ||||
|       <atom:link href="${config.siteUrl}/${page}" rel="self" type="application/rss+xml"/> | ||||
|       ${posts.map((post) => generateRssItem(config, post)).join('')} | ||||
|     </channel> | ||||
|   </rss> | ||||
| ` | ||||
|  | ||||
| async function generateRSS(config, allBlogs) { | ||||
|   const publishPosts = allBlogs.filter((post) => post.draft !== true) | ||||
|   // RSS for blog post | ||||
|   if (publishPosts.length > 0) { | ||||
|     const rss = generateRss(config, publishPosts) | ||||
|     writeFileSync('./public/feed.xml', rss) | ||||
|   } | ||||
|  | ||||
|   if (publishPosts.length > 0) { | ||||
|     for (const tag of Object.keys(tagData)) { | ||||
|       const filteredPosts = allBlogs.filter((post) => | ||||
|         post.tags.map((t) => GithubSlugger.slug(t)).includes(tag) | ||||
|       ) | ||||
|       const rss = generateRss(config, filteredPosts, `tags/${tag}/feed.xml`) | ||||
|       const rssPath = path.join('public', 'tags', tag) | ||||
|       mkdirSync(rssPath, { recursive: true }) | ||||
|       writeFileSync(path.join(rssPath, 'feed.xml'), rss) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| const rss = () => { | ||||
|   generateRSS(siteMetadata, allBlogs) | ||||
|   console.log('RSS feed generated...') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user