mirror of
https://github.com/supabase/supabase.git
synced 2026-10-14 05:39:11 +03:00
try app router blog routes
This commit is contained in:
1 parent
b70828b71e
commit
e2cabecf5e
28 files changed
+660
-766
No files matched your search
@@ -0,0 +1,31 @@
|
||||
'use client'
|
||||
|
||||
import { IS_PLATFORM, PageTelemetry } from 'common'
|
||||
import { RefreshRouteOnSave as PayloadLivePreview } from '@payloadcms/live-preview-react'
|
||||
import { useConsentToast } from 'ui-patterns/consent'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { API_URL } from '~/lib/constants'
|
||||
|
||||
interface ConsentWrapperProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export function ConsentWrapper({ children }: ConsentWrapperProps) {
|
||||
const { hasAcceptedConsent } = useConsentToast()
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<PageTelemetry
|
||||
API_URL={API_URL}
|
||||
hasAcceptedConsent={hasAcceptedConsent}
|
||||
enabled={IS_PLATFORM}
|
||||
/>
|
||||
<PayloadLivePreview
|
||||
refresh={() => router.refresh()}
|
||||
serverURL={process.env.NEXT_PUBLIC_CMS_URL || 'http://localhost:3030'}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
import fs from 'fs'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { generateRss } from '~/lib/rss'
|
||||
import { getSortedPosts } from '~/lib/posts'
|
||||
// CMS posts are fetched client-side from /api-v2/cms-posts to keep server bundle small
|
||||
'use client'
|
||||
|
||||
import type PostTypes from '~/types/post'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import DefaultLayout from '~/components/Layouts/Default'
|
||||
import BlogGridItem from '~/components/Blog/BlogGridItem'
|
||||
import BlogListItem from '~/components/Blog/BlogListItem'
|
||||
@@ -15,33 +10,30 @@ import FeaturedThumb from '~/components/Blog/FeaturedThumb'
|
||||
import { cn } from 'ui'
|
||||
import { LOCAL_STORAGE_KEYS, isBrowser } from 'common'
|
||||
|
||||
import type PostTypes from '~/types/post'
|
||||
|
||||
export type BlogView = 'list' | 'grid'
|
||||
|
||||
function Blog(props: any) {
|
||||
export default function BlogClient(props: { blogs: any[] }) {
|
||||
const { BLOG_VIEW } = LOCAL_STORAGE_KEYS
|
||||
const localView = isBrowser ? (localStorage?.getItem(BLOG_VIEW) as BlogView) : undefined
|
||||
const [cmsPosts, setCmsPosts] = useState<any[]>([])
|
||||
const [blogs, setBlogs] = useState(props.blogs)
|
||||
const [view, setView] = useState<BlogView>(localView ?? 'list')
|
||||
const isList = view === 'list'
|
||||
const router = useRouter()
|
||||
|
||||
const meta_title = 'Supabase Blog: the Postgres development platform'
|
||||
const meta_description = 'Get all your Supabase News on the Supabase blog.'
|
||||
|
||||
// Merge CMS posts with static ones when CMS posts arrive
|
||||
const allPosts = [...props.blogs, ...cmsPosts].sort(
|
||||
(a: any, b: any) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
||||
)
|
||||
|
||||
// Keep list in sync once after CMS posts load, but don't override user filters later
|
||||
useEffect(() => {
|
||||
setBlogs((prev: any[]) => (prev.length === props.blogs.length ? allPosts : prev))
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [cmsPosts])
|
||||
|
||||
// Fetch CMS posts
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useEffect(() => {
|
||||
const fetchCmsPosts = async () => {
|
||||
try {
|
||||
@@ -65,7 +57,7 @@ function Blog(props: any) {
|
||||
openGraph={{
|
||||
title: meta_title,
|
||||
description: meta_description,
|
||||
url: `https://supabase.com/${router.pathname}`,
|
||||
url: `https://supabase.com/blog`,
|
||||
images: [
|
||||
{
|
||||
url: `https://supabase.com/images/og/supabase-og.png`,
|
||||
@@ -90,12 +82,7 @@ function Blog(props: any) {
|
||||
|
||||
<div className="border-default border-t">
|
||||
<div className="md:container mx-auto mt-6 lg:mt-8 px-6 sm:px-16 xl:px-20">
|
||||
<BlogFilters
|
||||
allPosts={allPosts}
|
||||
setPosts={setBlogs}
|
||||
view={view as BlogView}
|
||||
setView={setView}
|
||||
/>
|
||||
<BlogFilters allPosts={allPosts} setPosts={setBlogs} view={view} setView={setView} />
|
||||
|
||||
<ol
|
||||
className={cn(
|
||||
@@ -131,44 +118,3 @@ function Blog(props: any) {
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
// Get static blog posts
|
||||
const staticPostsData = getSortedPosts({ directory: '_blog', runner: '** BLOG PAGE **' })
|
||||
|
||||
// Only static data at build time; CMS posts are fetched client-side
|
||||
const allPostsData = [...staticPostsData].sort((a: any, b: any) => {
|
||||
const dateA = a.date ? new Date(a.date).getTime() : new Date(a.formattedDate).getTime()
|
||||
const dateB = b.date ? new Date(b.date).getTime() : new Date(b.formattedDate).getTime()
|
||||
return dateB - dateA
|
||||
})
|
||||
|
||||
// Generate RSS feed from combined posts
|
||||
const rss = generateRss(allPostsData)
|
||||
|
||||
// create a rss feed in public directory
|
||||
// rss feed is added via <Head> component in render return
|
||||
fs.writeFileSync('./public/rss.xml', rss)
|
||||
|
||||
// generate a series of rss feeds for each author (for PlanetPG)
|
||||
const planetPgPosts = allPostsData.filter((post: any) => post.tags?.includes('planetpg'))
|
||||
const planetPgAuthors = planetPgPosts.map((post: any) => post.author.split(','))
|
||||
const uniquePlanetPgAuthors = new Set([].concat(...planetPgAuthors))
|
||||
|
||||
uniquePlanetPgAuthors.forEach((author) => {
|
||||
const authorPosts = planetPgPosts.filter((post: any) => post.author.includes(author))
|
||||
if (authorPosts.length > 0) {
|
||||
const authorRss = generateRss(authorPosts, author)
|
||||
fs.writeFileSync(`./public/planetpg-${author}-rss.xml`, authorRss)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
props: {
|
||||
blogs: allPostsData,
|
||||
},
|
||||
// revalidate: 60 * 10, // Revalidate every 10 minutes
|
||||
}
|
||||
}
|
||||
|
||||
export default Blog
|
||||
@@ -0,0 +1,135 @@
|
||||
'use client'
|
||||
|
||||
import dynamic from 'next/dynamic'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { useState, useMemo } from 'react'
|
||||
import { useLivePreview } from '@payloadcms/live-preview-react'
|
||||
|
||||
import authors from 'lib/authors.json'
|
||||
import { CMS_SITE_ORIGIN } from 'lib/constants'
|
||||
import { isNotNullOrUndefined } from 'lib/helpers'
|
||||
|
||||
import type { Blog, BlogData, CMSAuthor, PostReturnType, ProcessedBlogData, Tag } from 'types/post'
|
||||
|
||||
const BlogPostRenderer = dynamic(() => import('components/Blog/BlogPostRenderer'))
|
||||
|
||||
type BlogPostPageProps = {
|
||||
prevPost: PostReturnType | null
|
||||
nextPost: PostReturnType | null
|
||||
relatedPosts: (PostReturnType & BlogData)[]
|
||||
blog: Blog & BlogData
|
||||
isDraftMode: boolean
|
||||
}
|
||||
|
||||
export default function BlogPostClient(props: BlogPostPageProps) {
|
||||
const isDraftMode = props.isDraftMode
|
||||
const [previewData] = useState<ProcessedBlogData>(props.blog)
|
||||
const shouldUseLivePreview = isDraftMode && (props.blog as any).isCMS
|
||||
|
||||
const { data: livePreviewData, isLoading: isLivePreviewLoading } = useLivePreview({
|
||||
initialData: props.blog,
|
||||
serverURL: CMS_SITE_ORIGIN || 'http://localhost:3030',
|
||||
depth: 2,
|
||||
})
|
||||
|
||||
const blogMetaData = useMemo(() => {
|
||||
if (isDraftMode && shouldUseLivePreview) {
|
||||
if (livePreviewData && typeof livePreviewData === 'object') {
|
||||
return { ...props.blog, ...livePreviewData }
|
||||
}
|
||||
if (previewData !== props.blog) {
|
||||
return previewData
|
||||
}
|
||||
}
|
||||
return props.blog
|
||||
}, [isDraftMode, shouldUseLivePreview, livePreviewData, previewData, props.blog]) as any
|
||||
|
||||
const isCMS = (blogMetaData as any).isCMS
|
||||
const imageUrl = isCMS
|
||||
? (blogMetaData as any).thumb ?? ''
|
||||
: (blogMetaData as any).thumb
|
||||
? `/images/blog/${(blogMetaData as any).thumb}`
|
||||
: ''
|
||||
|
||||
const meta = {
|
||||
title: (blogMetaData as any).meta_title ?? (blogMetaData as any).title,
|
||||
description: (blogMetaData as any).meta_description ?? (blogMetaData as any).description,
|
||||
url: `https://supabase.com/blog/${(blogMetaData as any).slug}`,
|
||||
}
|
||||
|
||||
const processTag = (tag: any): string => {
|
||||
return typeof tag === 'string' ? tag : tag.name
|
||||
}
|
||||
|
||||
const tags = (blogMetaData as any).tags
|
||||
? Array.isArray((blogMetaData as any).tags)
|
||||
? ((blogMetaData as any).tags as Tag[]).map(processTag)
|
||||
: []
|
||||
: []
|
||||
|
||||
const blogAuthors = isCMS
|
||||
? ((blogMetaData as any).authors as CMSAuthor[]) || []
|
||||
: ((blogMetaData as any).author as string)
|
||||
?.split(',')
|
||||
.map((authorId: string) => {
|
||||
const foundAuthor = authors.find((author) => author.author_id === authorId)
|
||||
return foundAuthor
|
||||
? {
|
||||
author: foundAuthor.author || 'Author',
|
||||
author_image_url: foundAuthor.author_image_url || null,
|
||||
author_url: foundAuthor.author_url || '#',
|
||||
position: foundAuthor.position || '',
|
||||
}
|
||||
: null
|
||||
})
|
||||
.filter(isNotNullOrUndefined) || []
|
||||
|
||||
const authorUrls = (blogAuthors as any)
|
||||
.map((author: any) => author?.author_url)
|
||||
.filter(isNotNullOrUndefined)
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextSeo
|
||||
title={meta.title}
|
||||
description={meta.description}
|
||||
openGraph={{
|
||||
title: meta.title,
|
||||
description: meta.description,
|
||||
url: meta.url,
|
||||
type: 'article',
|
||||
videos: (blogMetaData as any).video
|
||||
? [
|
||||
{
|
||||
url: (blogMetaData as any).video,
|
||||
type: 'application/x-shockwave-flash',
|
||||
width: 640,
|
||||
height: 385,
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
article: {
|
||||
publishedTime: (blogMetaData as any).date,
|
||||
authors: authorUrls as any,
|
||||
tags: tags as any,
|
||||
},
|
||||
images: [
|
||||
{
|
||||
url: imageUrl,
|
||||
alt: `${(blogMetaData as any).title} thumbnail`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<BlogPostRenderer
|
||||
blog={props.blog as any}
|
||||
blogMetaData={blogMetaData as any}
|
||||
isDraftMode={isDraftMode}
|
||||
isLivePreviewLoading={isLivePreviewLoading}
|
||||
prevPost={props.prevPost as any}
|
||||
nextPost={props.nextPost as any}
|
||||
authors={blogAuthors as any}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { draftMode } from 'next/headers'
|
||||
import { getAllPostSlugs, getPostdata, getSortedPosts } from 'lib/posts'
|
||||
import { getAllCMSPostSlugs, getCMSPostBySlug } from 'lib/get-cms-posts'
|
||||
|
||||
import type { Blog, BlogData, PostReturnType } from 'types/post'
|
||||
|
||||
import BlogPostClient from '~/app/blog/[slug]/BlogPostClient'
|
||||
|
||||
type MatterReturn = {
|
||||
data: BlogData
|
||||
content: string
|
||||
}
|
||||
|
||||
type BlogPostPageProps = {
|
||||
prevPost: PostReturnType | null
|
||||
nextPost: PostReturnType | null
|
||||
relatedPosts: (PostReturnType & BlogData)[]
|
||||
blog: Blog & BlogData
|
||||
isDraftMode: boolean
|
||||
}
|
||||
|
||||
type Params = {
|
||||
slug: string
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const staticPaths = getAllPostSlugs('_blog')
|
||||
const cmsPaths = await getAllCMSPostSlugs()
|
||||
return [...staticPaths, ...cmsPaths].map((p) => ({ slug: p.params.slug }))
|
||||
}
|
||||
|
||||
export default async function BlogPostPage({ params }: { params: Params }) {
|
||||
if (!params?.slug) {
|
||||
throw new Error('Missing slug for app/blog/[slug]/page.tsx')
|
||||
}
|
||||
|
||||
const slug = `${params.slug}`
|
||||
const { isEnabled: isDraft } = await draftMode()
|
||||
|
||||
const matter = (await import('gray-matter')).default
|
||||
const { mdxSerialize } = await import('lib/mdx/mdxSerialize')
|
||||
|
||||
try {
|
||||
const postContent = await getPostdata(slug, '_blog')
|
||||
const parsedContent = matter(postContent) as unknown as MatterReturn
|
||||
const content = parsedContent.content
|
||||
const tocDepth = (parsedContent.data as any)?.toc_depth ?? 2
|
||||
const mdxSource = await mdxSerialize(content, { tocDepth })
|
||||
const blogPost = { ...parsedContent.data }
|
||||
|
||||
const allStaticPosts = getSortedPosts({ directory: '_blog' })
|
||||
const allPosts = [...allStaticPosts].sort((a, b) => {
|
||||
const aDate = new Date((a as unknown as { date: string }).date).getTime()
|
||||
const bDate = new Date((b as unknown as { date: string }).date).getTime()
|
||||
return bDate - aDate
|
||||
})
|
||||
const currentIndex = allPosts.findIndex((post) => post.slug === slug)
|
||||
const nextPost = currentIndex === allPosts.length - 1 ? null : allPosts[currentIndex + 1]
|
||||
const prevPost = currentIndex === 0 ? null : allPosts[currentIndex - 1]
|
||||
const tocResult = (mdxSource as any).scope?.toc || { content: '' }
|
||||
const processedContent = tocResult.content
|
||||
const relatedPosts = getSortedPosts({
|
||||
directory: '_blog',
|
||||
limit: 3,
|
||||
tags: (mdxSource as { scope: { tags?: string[] } }).scope.tags,
|
||||
currentPostSlug: slug,
|
||||
}) as unknown as (BlogData & PostReturnType)[]
|
||||
|
||||
const props: BlogPostPageProps = {
|
||||
prevPost,
|
||||
nextPost,
|
||||
relatedPosts,
|
||||
blog: {
|
||||
...(blogPost as any),
|
||||
content: mdxSource,
|
||||
toc: {
|
||||
...tocResult,
|
||||
content: processedContent,
|
||||
},
|
||||
} as any,
|
||||
isDraftMode: isDraft,
|
||||
}
|
||||
|
||||
return <BlogPostClient {...props} />
|
||||
} catch {}
|
||||
|
||||
const cmsPost = await getCMSPostBySlug(slug, isDraft)
|
||||
|
||||
if (!cmsPost) {
|
||||
if (isDraft) {
|
||||
const publishedPost = await getCMSPostBySlug(slug, false)
|
||||
if (!publishedPost) return null
|
||||
const mdxSource = await mdxSerialize(publishedPost.content || '')
|
||||
const props: BlogPostPageProps = {
|
||||
prevPost: null,
|
||||
nextPost: null,
|
||||
relatedPosts: [],
|
||||
blog: {
|
||||
...publishedPost,
|
||||
tags: publishedPost.tags || [],
|
||||
authors: publishedPost.authors || [],
|
||||
isCMS: true,
|
||||
content: mdxSource,
|
||||
toc: publishedPost.toc,
|
||||
image: publishedPost.image ?? undefined,
|
||||
thumb: publishedPost.thumb ?? undefined,
|
||||
} as any,
|
||||
isDraftMode: isDraft,
|
||||
}
|
||||
return <BlogPostClient {...props} />
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const mdxSource = await mdxSerialize(cmsPost.content || '')
|
||||
|
||||
const props: BlogPostPageProps = {
|
||||
prevPost: null,
|
||||
nextPost: null,
|
||||
relatedPosts: [],
|
||||
blog: {
|
||||
...cmsPost,
|
||||
tags: cmsPost.tags || [],
|
||||
authors: cmsPost.authors || [],
|
||||
isCMS: true,
|
||||
content: mdxSource,
|
||||
toc: cmsPost.toc,
|
||||
image: cmsPost.image ?? undefined,
|
||||
thumb: cmsPost.thumb ?? undefined,
|
||||
} as any,
|
||||
isDraftMode: isDraft,
|
||||
}
|
||||
|
||||
return <BlogPostClient {...props} />
|
||||
}
|
||||
+13
-28
@@ -2,37 +2,24 @@ import { NextSeo } from 'next-seo'
|
||||
import { getSortedPosts, getAllCategories } from '~/lib/posts'
|
||||
import Link from 'next/link'
|
||||
import { startCase } from 'lodash'
|
||||
|
||||
import DefaultLayout from '~/components/Layouts/Default'
|
||||
import BlogGridItem from '~/components/Blog/BlogGridItem'
|
||||
import type PostTypes from '~/types/post'
|
||||
|
||||
export async function getStaticProps({ params }: any) {
|
||||
const posts = getSortedPosts({ directory: '_blog', limit: 0, categories: [params.category] })
|
||||
return {
|
||||
props: {
|
||||
category: params.category,
|
||||
blogs: posts,
|
||||
},
|
||||
}
|
||||
}
|
||||
type Params = { category: string }
|
||||
|
||||
export async function getStaticPaths() {
|
||||
export async function generateStaticParams() {
|
||||
const categories = getAllCategories('_blog')
|
||||
return {
|
||||
paths: categories.map((category: any) => ({ params: { category: category } })),
|
||||
fallback: false,
|
||||
}
|
||||
return categories.map((category: string) => ({ category }))
|
||||
}
|
||||
|
||||
interface Props {
|
||||
category: string
|
||||
blogs: PostTypes[]
|
||||
}
|
||||
|
||||
function CategoriesIndex(props: Props) {
|
||||
const { blogs, category } = props
|
||||
const capitalizedCategory = startCase(category.replaceAll('-', ' '))
|
||||
export default async function CategoriesPage({ params }: { params: Params }) {
|
||||
const blogs = getSortedPosts({
|
||||
directory: '_blog',
|
||||
limit: 0,
|
||||
categories: [params.category],
|
||||
}) as unknown as PostTypes[]
|
||||
const capitalizedCategory = startCase(params.category.replaceAll('-', ' '))
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -46,14 +33,14 @@ function CategoriesIndex(props: Props) {
|
||||
<h1 className="cursor-pointer">
|
||||
<Link href="/blog">Blog</Link>
|
||||
<span className="px-2">/</span>
|
||||
<span>{`${capitalizedCategory}`}</span>
|
||||
<span>{capitalizedCategory}</span>
|
||||
</h1>
|
||||
</div>
|
||||
<ol className="grid grid-cols-12 gap-8 py-16 lg:gap-16">
|
||||
{blogs.map((blog: PostTypes, idx: number) => (
|
||||
{blogs.map((blog: PostTypes) => (
|
||||
<div
|
||||
className="col-span-12 mb-16 md:col-span-12 lg:col-span-6 xl:col-span-4"
|
||||
key={idx}
|
||||
key={blog.slug}
|
||||
>
|
||||
<BlogGridItem post={blog} />
|
||||
</div>
|
||||
@@ -64,5 +51,3 @@ function CategoriesIndex(props: Props) {
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default CategoriesIndex
|
||||
@@ -0,0 +1,43 @@
|
||||
import fs from 'fs'
|
||||
import { generateRss } from '~/lib/rss'
|
||||
import { getSortedPosts } from '~/lib/posts'
|
||||
import BlogClient from '~/app/blog/BlogClient'
|
||||
|
||||
export const dynamic = 'force-static'
|
||||
|
||||
export default async function BlogPage() {
|
||||
// Get static blog posts at build time
|
||||
const staticPostsData = getSortedPosts({ directory: '_blog', runner: '** BLOG PAGE **' })
|
||||
|
||||
const allPostsData = [...staticPostsData].sort((a: any, b: any) => {
|
||||
const dateA = (a as any).date
|
||||
? new Date((a as any).date).getTime()
|
||||
: new Date((a as any).formattedDate).getTime()
|
||||
const dateB = (b as any).date
|
||||
? new Date((b as any).date).getTime()
|
||||
: new Date((b as any).formattedDate).getTime()
|
||||
return dateB - dateA
|
||||
})
|
||||
|
||||
// Generate RSS feed from static posts
|
||||
const rss = generateRss(allPostsData)
|
||||
try {
|
||||
fs.writeFileSync('./public/rss.xml', rss)
|
||||
} catch {}
|
||||
|
||||
// Generate PlanetPG author feeds
|
||||
try {
|
||||
const planetPgPosts = allPostsData.filter((post: any) => post.tags?.includes('planetpg'))
|
||||
const planetPgAuthors = planetPgPosts.map((post: any) => post.author.split(','))
|
||||
const uniquePlanetPgAuthors = new Set(([] as string[]).concat(...(planetPgAuthors as any)))
|
||||
uniquePlanetPgAuthors.forEach((author) => {
|
||||
const authorPosts = planetPgPosts.filter((post: any) => post.author.includes(author))
|
||||
if (authorPosts.length > 0) {
|
||||
const authorRss = generateRss(authorPosts, author as string)
|
||||
fs.writeFileSync(`./public/planetpg-${author}-rss.xml`, authorRss)
|
||||
}
|
||||
})
|
||||
} catch {}
|
||||
|
||||
return <BlogClient blogs={allPostsData as any} />
|
||||
}
|
||||
@@ -2,37 +2,24 @@ import { NextSeo } from 'next-seo'
|
||||
import { getSortedPosts, getAllTags } from '~/lib/posts'
|
||||
import Link from 'next/link'
|
||||
import { startCase } from 'lodash'
|
||||
|
||||
import DefaultLayout from '~/components/Layouts/Default'
|
||||
import BlogGridItem from '~/components/Blog/BlogGridItem'
|
||||
import PostTypes from '~/types/post'
|
||||
import type PostTypes from '~/types/post'
|
||||
|
||||
export async function getStaticProps({ params }: any) {
|
||||
const posts = getSortedPosts({ directory: '_blog', limit: 0, tags: [params.tag] })
|
||||
return {
|
||||
props: {
|
||||
tag: params.tag,
|
||||
blogs: posts,
|
||||
},
|
||||
}
|
||||
}
|
||||
type Params = { tag: string }
|
||||
|
||||
export async function getStaticPaths() {
|
||||
export async function generateStaticParams() {
|
||||
const tags = getAllTags('_blog')
|
||||
return {
|
||||
paths: tags.map((tag: any) => ({ params: { tag: tag } })),
|
||||
fallback: false,
|
||||
}
|
||||
return tags.map((tag: string) => ({ tag }))
|
||||
}
|
||||
|
||||
interface Props {
|
||||
tag: string
|
||||
blogs: PostTypes[]
|
||||
}
|
||||
|
||||
function TagBlogsPage(props: Props) {
|
||||
const { blogs, tag } = props
|
||||
const capitalizedTag = startCase(tag.replaceAll('-', ' '))
|
||||
export default async function TagPage({ params }: { params: Params }) {
|
||||
const blogs = getSortedPosts({
|
||||
directory: '_blog',
|
||||
limit: 0,
|
||||
tags: [params.tag],
|
||||
}) as unknown as PostTypes[]
|
||||
const capitalizedTag = startCase(params.tag.replaceAll('-', ' '))
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -46,14 +33,14 @@ function TagBlogsPage(props: Props) {
|
||||
<h1 className="cursor-pointer">
|
||||
<Link href="/blog">Blog</Link>
|
||||
<span className="px-2">/</span>
|
||||
<span>{`${capitalizedTag}`}</span>
|
||||
<span>{capitalizedTag}</span>
|
||||
</h1>
|
||||
</div>
|
||||
<ol className="grid grid-cols-12 gap-8 py-16 lg:gap-16">
|
||||
{blogs.map((blog: PostTypes, idx: number) => (
|
||||
{blogs.map((blog: PostTypes) => (
|
||||
<div
|
||||
className="col-span-12 mb-16 md:col-span-12 lg:col-span-6 xl:col-span-4"
|
||||
key={idx}
|
||||
key={blog.slug}
|
||||
>
|
||||
<BlogGridItem post={blog} />
|
||||
</div>
|
||||
@@ -64,5 +51,3 @@ function TagBlogsPage(props: Props) {
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TagBlogsPage
|
||||
@@ -0,0 +1,48 @@
|
||||
import '@code-hike/mdx/styles'
|
||||
import 'config/code-hike.scss'
|
||||
import '../styles/index.css'
|
||||
|
||||
import { Metadata } from 'next'
|
||||
import { APP_NAME, DEFAULT_META_DESCRIPTION } from '~/lib/constants'
|
||||
import Providers from './providers'
|
||||
|
||||
const site_title = `${APP_NAME} | The Open Source Firebase Alternative`
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: site_title,
|
||||
description: DEFAULT_META_DESCRIPTION,
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
url: 'https://supabase.com/',
|
||||
siteName: 'Supabase',
|
||||
images: [
|
||||
{
|
||||
url: 'https://supabase.com/images/og/supabase-og.png',
|
||||
width: 800,
|
||||
height: 600,
|
||||
alt: 'Supabase Og Image',
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
creator: '@supabase',
|
||||
site: '@supabase',
|
||||
card: 'summary_large_image',
|
||||
},
|
||||
viewport: 'width=device-width, initial-scale=1.0',
|
||||
icons: {
|
||||
icon: '/favicon/favicon.ico',
|
||||
shortcut: '/favicon/favicon.ico',
|
||||
apple: '/favicon/favicon.ico',
|
||||
},
|
||||
}
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<Providers>{children}</Providers>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
AuthProvider,
|
||||
FeatureFlagProvider,
|
||||
IS_PLATFORM,
|
||||
PageTelemetry,
|
||||
ThemeProvider,
|
||||
useThemeSandbox,
|
||||
TelemetryTagManager,
|
||||
} from 'common'
|
||||
import { themes, TooltipProvider, SonnerToaster } from 'ui'
|
||||
import { CommandProvider } from 'ui-patterns/CommandMenu'
|
||||
import { useConsentToast } from 'ui-patterns/consent'
|
||||
import { API_URL } from '~/lib/constants'
|
||||
import { WwwCommandMenu } from '~/components/CommandMenu'
|
||||
|
||||
function Providers({ children }: { children: React.ReactNode }) {
|
||||
useThemeSandbox()
|
||||
const { hasAcceptedConsent } = useConsentToast()
|
||||
|
||||
return (
|
||||
<AuthProvider>
|
||||
<FeatureFlagProvider API_URL={API_URL} enabled={IS_PLATFORM}>
|
||||
<ThemeProvider themes={themes.map((t) => t.value)} enableSystem disableTransitionOnChange>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<CommandProvider>
|
||||
<TelemetryTagManager />
|
||||
<SonnerToaster position="top-right" />
|
||||
{children}
|
||||
<WwwCommandMenu />
|
||||
<PageTelemetry
|
||||
API_URL={API_URL}
|
||||
hasAcceptedConsent={hasAcceptedConsent}
|
||||
enabled={IS_PLATFORM}
|
||||
/>
|
||||
</CommandProvider>
|
||||
</TooltipProvider>
|
||||
</ThemeProvider>
|
||||
</FeatureFlagProvider>
|
||||
</AuthProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default Providers
|
||||
@@ -2,10 +2,10 @@ import { LOCAL_STORAGE_KEYS, useBreakpoint } from 'common'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { startCase } from 'lodash'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useKey } from 'react-use'
|
||||
import type { BlogView } from '~/pages/blog'
|
||||
import type { BlogView } from '~/app/blog/BlogClient'
|
||||
import type PostTypes from '~/types/post'
|
||||
|
||||
import { AlignJustify, ChevronDown, Grid, Search, X as CloseIcon } from 'lucide-react'
|
||||
@@ -74,7 +74,7 @@ function BlogFilters({ allPosts, setPosts, view, setView }: Props) {
|
||||
}, [q])
|
||||
|
||||
const handleReplaceRouter = () => {
|
||||
if (!searchTerm && category !== 'all') {
|
||||
if (!searchTerm && category !== 'all' && router) {
|
||||
router.query.category = category
|
||||
router.replace(router, undefined, { shallow: true, scroll: false })
|
||||
}
|
||||
@@ -105,19 +105,19 @@ function BlogFilters({ allPosts, setPosts, view, setView }: Props) {
|
||||
}, [isMobile])
|
||||
|
||||
useEffect(() => {
|
||||
if (router.isReady && q) {
|
||||
if (router?.isReady && q) {
|
||||
setSearchTerm(q)
|
||||
}
|
||||
if (router.isReady && activeCategory && activeCategory !== 'all') {
|
||||
if (router?.isReady && activeCategory && activeCategory !== 'all') {
|
||||
setCategory(activeCategory)
|
||||
}
|
||||
}, [activeCategory, router.isReady, q])
|
||||
}, [activeCategory, router?.isReady, q])
|
||||
|
||||
const handleSearchByText = (text: string) => {
|
||||
setSearchTerm(text)
|
||||
searchParams?.has('q') && router.replace('/blog', undefined, { shallow: true, scroll: false })
|
||||
router.replace(`/blog?q=${text}`, undefined, { shallow: true, scroll: false })
|
||||
if (text.length < 1) router.replace('/blog', undefined, { shallow: true, scroll: false })
|
||||
searchParams?.has('q') && router?.replace('/blog', undefined, { shallow: true, scroll: false })
|
||||
router?.replace(`/blog?q=${text}`, undefined, { shallow: true, scroll: false })
|
||||
if (text.length < 1) router?.replace('/blog', undefined, { shallow: true, scroll: false })
|
||||
|
||||
const matches = allPosts.filter((post: any) => {
|
||||
const found =
|
||||
@@ -135,8 +135,8 @@ function BlogFilters({ allPosts, setPosts, view, setView }: Props) {
|
||||
searchTerm && setSearchTerm('')
|
||||
setCategory(category)
|
||||
category === 'all'
|
||||
? router.replace('/blog', undefined, { shallow: true, scroll: false })
|
||||
: router.replace(`/blog?category=${category}`, undefined, {
|
||||
? router?.replace('/blog', undefined, { shallow: true, scroll: false })
|
||||
: router?.replace(`/blog?category=${category}`, undefined, {
|
||||
shallow: true,
|
||||
scroll: false,
|
||||
})
|
||||
|
||||
@@ -2,7 +2,6 @@ import { CheckIcon } from '@heroicons/react/outline'
|
||||
import { REALTIME_CHANNEL_STATES } from '@supabase/supabase-js'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import * as supabaseLogoWordmarkDark from 'common/assets/images/supabase-logo-wordmark--dark.png'
|
||||
@@ -13,6 +12,7 @@ import { ThemeToggle } from 'ui-patterns/ThemeToggle'
|
||||
import supabase from '~/lib/supabase'
|
||||
import useDarkLaunchWeeks from '../../hooks/useDarkLaunchWeeks'
|
||||
import SectionContainer from '../Layouts/SectionContainer'
|
||||
import { usePathname } from 'next/navigation'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
@@ -20,17 +20,17 @@ interface Props {
|
||||
}
|
||||
|
||||
const Footer = (props: Props) => {
|
||||
const { pathname } = useRouter()
|
||||
const pathname = usePathname()
|
||||
|
||||
const isDarkLaunchWeek = useDarkLaunchWeeks()
|
||||
const isGAWeek = pathname.includes('/ga-week')
|
||||
const isGAWeek = pathname?.includes('/ga-week')
|
||||
const forceDark = isDarkLaunchWeek
|
||||
|
||||
useEffect(() => {
|
||||
const channel = supabase.channel('footer')
|
||||
if (channel.state === REALTIME_CHANNEL_STATES.closed) {
|
||||
channel.subscribe((status: string) => {
|
||||
if (status == 'SUBSCRIBED') {
|
||||
if (status === 'SUBSCRIBED') {
|
||||
channel.send({
|
||||
type: 'broadcast',
|
||||
event: 'footer_subscribed',
|
||||
@@ -56,7 +56,6 @@ const Footer = (props: Props) => {
|
||||
isGAWeek && 'dark:bg-alternative',
|
||||
props.className
|
||||
)}
|
||||
aria-labelledby="footerHeading"
|
||||
>
|
||||
<h2 id="footerHeading" className="sr-only">
|
||||
Footer
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CheckIcon, ClipboardIcon } from '@heroicons/react/outline'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import { Fragment, MouseEvent, ReactNode, useRef, useState } from 'react'
|
||||
import { useClickAway, useKey } from 'react-use'
|
||||
import {
|
||||
@@ -84,7 +84,7 @@ const RightClickBrandLogo = () => {
|
||||
href="/"
|
||||
onContextMenu={handleRightClick}
|
||||
onFocus={handleKeyboardOpen}
|
||||
onKeyDown={(e) => e.key === 'Enter' && router.push('/')}
|
||||
onKeyDown={(e) => e.key === 'Enter' && router?.push('/')}
|
||||
className="block w-auto h-6 focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-sm"
|
||||
>
|
||||
<Image
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useState } from 'react'
|
||||
import { useWindowSize } from 'react-use'
|
||||
|
||||
@@ -25,6 +24,7 @@ import { useSendTelemetryEvent } from 'lib/telemetry'
|
||||
import useDropdownMenu from './useDropdownMenu'
|
||||
|
||||
import { getMenu } from 'data/nav'
|
||||
import { usePathname } from 'next/navigation'
|
||||
|
||||
interface Props {
|
||||
hideNavbar: boolean
|
||||
@@ -32,7 +32,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const Nav = ({ hideNavbar, stickyNavbar = true }: Props) => {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const { width } = useWindowSize()
|
||||
const [open, setOpen] = useState(false)
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
@@ -41,9 +41,9 @@ const Nav = ({ hideNavbar, stickyNavbar = true }: Props) => {
|
||||
const user = useUser()
|
||||
const userMenu = useDropdownMenu(user)
|
||||
|
||||
const isLaunchWeekPage = router.pathname.includes('/launch-week')
|
||||
const isLaunchWeekXPage = router.pathname === '/launch-week/x'
|
||||
const isGAWeekSection = router.pathname.startsWith('/ga-week')
|
||||
const isLaunchWeekPage = pathname?.includes('/launch-week')
|
||||
const isLaunchWeekXPage = pathname === '/launch-week/x'
|
||||
const isGAWeekSection = pathname?.startsWith('/ga-week')
|
||||
const disableStickyNav = isLaunchWeekXPage || isGAWeekSection || isLaunchWeekPage || !stickyNavbar
|
||||
const showLaunchWeekNavMode = !isLaunchWeekXPage && (isLaunchWeekPage || isGAWeekSection) && !open
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { logOut } from 'common'
|
||||
|
||||
import type { menuItem } from 'ui-patterns/AuthenticatedDropdownMenu'
|
||||
import { useSetCommandMenuOpen } from 'ui-patterns'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
|
||||
const useDropdownMenu = (user: User | null) => {
|
||||
const router = useRouter()
|
||||
@@ -50,7 +50,7 @@ const useDropdownMenu = (user: User | null) => {
|
||||
icon: LogOut,
|
||||
onClick: async () => {
|
||||
await logOut()
|
||||
router.reload()
|
||||
router?.reload()
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
const ScrollProgress = () => {
|
||||
const [progressPercentage, setProgressPercentage] = useState(0)
|
||||
const { pathname } = useRouter()
|
||||
const pathname = usePathname()
|
||||
|
||||
const isBlogPost = pathname.includes('/blog/')
|
||||
const isBlogPost = pathname?.includes('/blog/')
|
||||
if (!isBlogPost) return null
|
||||
|
||||
const handleScroll = () => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { usePathname } from 'next/navigation'
|
||||
|
||||
const useDarkLaunchWeeks = () => {
|
||||
const { pathname } = useRouter()
|
||||
const pathname = usePathname()
|
||||
|
||||
const isLaunchWeek7 = pathname.startsWith('/launch-week/7')
|
||||
const isLaunchWeek8 = pathname.startsWith('/launch-week/8')
|
||||
const isLaunchWeekX = pathname.startsWith('/launch-week/x')
|
||||
const isLaunchWeek7 = pathname?.startsWith('/launch-week/7')
|
||||
const isLaunchWeek8 = pathname?.startsWith('/launch-week/8')
|
||||
const isLaunchWeekX = pathname?.startsWith('/launch-week/x')
|
||||
|
||||
return isLaunchWeek7 || isLaunchWeek8 || isLaunchWeekX
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CMS_SITE_ORIGIN } from './constants'
|
||||
import { generateReadingTime } from './helpers'
|
||||
const toc = require('markdown-toc')
|
||||
import { generateTocFromMarkdown } from './toc'
|
||||
|
||||
// Payload API configuration
|
||||
const PAYLOAD_URL = CMS_SITE_ORIGIN || 'http://localhost:3030'
|
||||
@@ -280,7 +280,7 @@ export async function getCMSCustomerBySlug(slug: string, preview = false) {
|
||||
}
|
||||
|
||||
// Helper function to process customer data
|
||||
function processCustomerData(customer: any) {
|
||||
async function processCustomerData(customer: any) {
|
||||
const options: Intl.DateTimeFormatOptions = { month: 'long', day: 'numeric', year: 'numeric' }
|
||||
const formattedDate = new Date(customer.date || new Date()).toLocaleDateString('en-IN', options)
|
||||
const markdownContent = convertRichTextToMarkdown(customer.content)
|
||||
@@ -292,9 +292,10 @@ function processCustomerData(customer: any) {
|
||||
const logoUrl = customer.logo?.url ? `${PAYLOAD_URL}${customer.logo.url}` : null
|
||||
|
||||
// Generate TOC from content for CMS customers
|
||||
const tocResult = toc(markdownContent, {
|
||||
maxdepth: customer.toc_depth ? customer.toc_depth : 2,
|
||||
})
|
||||
const tocResult = await generateTocFromMarkdown(
|
||||
markdownContent,
|
||||
customer.toc_depth ? customer.toc_depth : 2
|
||||
)
|
||||
|
||||
return {
|
||||
slug: customer.slug,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CMS_SITE_ORIGIN } from './constants'
|
||||
import { generateReadingTime } from './helpers'
|
||||
const toc = require('markdown-toc')
|
||||
import { generateTocFromMarkdown } from './toc'
|
||||
|
||||
// Payload API configuration
|
||||
const PAYLOAD_URL = CMS_SITE_ORIGIN || 'http://localhost:3030'
|
||||
@@ -274,7 +274,7 @@ export async function getCMSEventBySlug(slug: string, preview = false) {
|
||||
}
|
||||
|
||||
// Helper function to process event data
|
||||
function processEventData(event: any) {
|
||||
async function processEventData(event: any) {
|
||||
const options: Intl.DateTimeFormatOptions = { month: 'long', day: 'numeric', year: 'numeric' }
|
||||
const formattedDate = new Date(event.date || new Date()).toLocaleDateString('en-IN', options)
|
||||
const markdownContent = convertRichTextToMarkdown(event.content)
|
||||
@@ -285,9 +285,10 @@ function processEventData(event: any) {
|
||||
const imageUrl = event.image?.url ? `${PAYLOAD_URL}${event.image.url}` : null
|
||||
|
||||
// Generate TOC from content for CMS events
|
||||
const tocResult = toc(markdownContent, {
|
||||
maxdepth: event.toc_depth ? event.toc_depth : 2,
|
||||
})
|
||||
const tocResult = await generateTocFromMarkdown(
|
||||
markdownContent,
|
||||
event.toc_depth ? event.toc_depth : 2
|
||||
)
|
||||
|
||||
return {
|
||||
slug: event.slug,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CMS_SITE_ORIGIN } from './constants'
|
||||
import { generateReadingTime } from './helpers'
|
||||
const toc = require('markdown-toc')
|
||||
import { generateTocFromMarkdown } from './toc'
|
||||
|
||||
// Payload API configuration
|
||||
const PAYLOAD_URL = CMS_SITE_ORIGIN || 'http://localhost:3030'
|
||||
@@ -287,7 +287,7 @@ export async function getCMSPostBySlug(slug: string, preview = false) {
|
||||
// console.log(`[getCMSPostBySlug] About to call processPostData...`)
|
||||
|
||||
try {
|
||||
const processedPost = processPostData(post)
|
||||
const processedPost = await processPostData(post)
|
||||
// console.log(`[getCMSPostBySlug] Successfully processed post:`, {
|
||||
// slug: processedPost.slug,
|
||||
// title: processedPost.title,
|
||||
@@ -305,7 +305,7 @@ export async function getCMSPostBySlug(slug: string, preview = false) {
|
||||
}
|
||||
|
||||
// Helper function to process post data
|
||||
function processPostData(post: any) {
|
||||
async function processPostData(post: any) {
|
||||
// console.log(`[processPostData] Starting to process post:`, {
|
||||
// id: post.id,
|
||||
// title: post.title,
|
||||
@@ -324,9 +324,10 @@ function processPostData(post: any) {
|
||||
const readingTime = post.readingTime || generateReadingTime(markdownContent)
|
||||
|
||||
// Generate TOC from content for CMS blog posts
|
||||
const tocResult = toc(markdownContent, {
|
||||
maxdepth: post.toc_depth ? post.toc_depth : 2,
|
||||
})
|
||||
const tocResult = await generateTocFromMarkdown(
|
||||
markdownContent,
|
||||
post.toc_depth ? post.toc_depth : 2
|
||||
)
|
||||
|
||||
// Extract thumb and image URLs from the nested structure
|
||||
const thumbUrl = post.thumb?.url ? `${PAYLOAD_URL}${post.thumb.url}` : null
|
||||
@@ -364,7 +365,7 @@ function processPostData(post: any) {
|
||||
richContent: post.content,
|
||||
tags: post.tags || [],
|
||||
toc: {
|
||||
content: tocResult.content.replace(/%23/g, ''),
|
||||
content: tocResult.content,
|
||||
json: tocResult.json,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -21,7 +21,50 @@ function addSelfClosingTags(htmlString: string): string {
|
||||
return modifiedHTML
|
||||
}
|
||||
|
||||
export async function mdxSerialize(source: string) {
|
||||
type TocItem = { content: string; slug: string; lvl: number }
|
||||
|
||||
function createRemarkCollectToc(maxDepth: number) {
|
||||
return function remarkCollectToc() {
|
||||
return function transformer(tree: any) {
|
||||
const items: TocItem[] = []
|
||||
|
||||
const getText = (node: any): string => {
|
||||
if (!node) return ''
|
||||
if (node.type === 'text') return node.value || ''
|
||||
if (Array.isArray(node.children)) return node.children.map(getText).join('')
|
||||
return ''
|
||||
}
|
||||
|
||||
const slugify = (input: string) =>
|
||||
input
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[`~!@#$%^&*()+=|{}\[\]\\:";'<>?,./]+/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
|
||||
const walk = (node: any) => {
|
||||
if (node.type === 'heading' && typeof node.depth === 'number') {
|
||||
if (node.depth <= maxDepth) {
|
||||
const text = getText(node)
|
||||
if (text) {
|
||||
items.push({ content: text, slug: slugify(text), lvl: node.depth })
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node.children) {
|
||||
for (const child of node.children) walk(child)
|
||||
}
|
||||
}
|
||||
|
||||
walk(tree)
|
||||
|
||||
// Attach to tree data for retrieval post-serialize
|
||||
;(tree.data || (tree.data = {})).__collectedToc = items
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function mdxSerialize(source: string, options?: { tocDepth?: number }) {
|
||||
const formattedSource = addSelfClosingTags(source)
|
||||
const codeHikeOptions: CodeHikeConfig = {
|
||||
theme: codeHikeTheme,
|
||||
@@ -31,12 +74,29 @@ export async function mdxSerialize(source: string) {
|
||||
autoImport: false,
|
||||
}
|
||||
|
||||
const tocDepth = options?.tocDepth ?? 2
|
||||
let collectedToc: TocItem[] = []
|
||||
|
||||
const mdxSource: any = await serialize(formattedSource, {
|
||||
scope: {
|
||||
chCodeConfig: codeHikeOptions,
|
||||
},
|
||||
mdxOptions: {
|
||||
remarkPlugins: [[remarkCodeHike, codeHikeOptions], remarkGfm],
|
||||
remarkPlugins: [
|
||||
[remarkCodeHike, codeHikeOptions],
|
||||
remarkGfm,
|
||||
// Collect headings into a simple TOC structure
|
||||
function () {
|
||||
const plugin = createRemarkCollectToc(tocDepth)
|
||||
const transformer = (plugin as any)()
|
||||
return function (tree: any) {
|
||||
transformer(tree)
|
||||
if (tree?.data?.__collectedToc) {
|
||||
collectedToc = tree.data.__collectedToc as TocItem[]
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
rehypePlugins: [
|
||||
// @ts-ignore
|
||||
rehypeSlug, // add IDs to any h1-h6 tag that doesn't have one, using a slug made from its text
|
||||
@@ -44,5 +104,18 @@ export async function mdxSerialize(source: string) {
|
||||
},
|
||||
})
|
||||
|
||||
// Expose TOC via scope for consumers (keeps function signature stable)
|
||||
const tocMarkdown = collectedToc
|
||||
.map((h) => `${' '.repeat(Math.max(0, h.lvl - 1))}- [${h.content}](#${h.slug})`)
|
||||
.join('\n')
|
||||
|
||||
mdxSource.scope = {
|
||||
...(mdxSource.scope || {}),
|
||||
toc: {
|
||||
content: tocMarkdown,
|
||||
json: collectedToc,
|
||||
},
|
||||
}
|
||||
|
||||
return mdxSource
|
||||
}
|
||||
+13
-10
@@ -1,16 +1,19 @@
|
||||
'use client'
|
||||
|
||||
import { sendTelemetryEvent } from 'common'
|
||||
import { TelemetryEvent } from 'common/telemetry-constants'
|
||||
import type { TelemetryEvent } from 'common/telemetry-constants'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCallback } from 'react'
|
||||
import { usePathname, useSearchParams } from 'next/navigation'
|
||||
|
||||
export function useSendTelemetryEvent() {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const searchParams = useSearchParams()
|
||||
|
||||
return useCallback(
|
||||
(event: TelemetryEvent) => {
|
||||
return sendTelemetryEvent(API_URL, event, router.pathname)
|
||||
},
|
||||
[router.pathname]
|
||||
)
|
||||
return (event: TelemetryEvent) => {
|
||||
const url = new URL(API_URL ?? 'http://localhost:3000')
|
||||
url.pathname = pathname ?? ''
|
||||
url.search = searchParams?.toString() ?? ''
|
||||
|
||||
return sendTelemetryEvent(API_URL, event, url.toString())
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import useDarkLaunchWeeks from '../hooks/useDarkLaunchWeeks'
|
||||
|
||||
export function useForceDeepDark() {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
type TocItem = { content: string; slug: string; lvl: number }
|
||||
|
||||
function stripFencedCodeBlocks(markdown: string): string {
|
||||
const segments = markdown.split('```')
|
||||
let acc = ''
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
if (i % 2 === 0) acc += segments[i]
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
function slugify(input: string): string {
|
||||
return input
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[`~!@#$%^&*()+=|{}\[\]\\:\";'<>?,./]+/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
}
|
||||
|
||||
export async function generateTocFromMarkdown(markdown: string, maxDepth: number) {
|
||||
const noCode = stripFencedCodeBlocks(markdown)
|
||||
const lines = noCode.split(/\r?\n/)
|
||||
const items: TocItem[] = []
|
||||
|
||||
for (const line of lines) {
|
||||
const m = /^(#{1,6})\s+(.*)$/.exec(line)
|
||||
if (!m) continue
|
||||
const depth = m[1].length
|
||||
if (depth > maxDepth) continue
|
||||
const text = m[2].trim()
|
||||
if (!text) continue
|
||||
items.push({ content: text, slug: slugify(text), lvl: depth })
|
||||
}
|
||||
|
||||
const content = items
|
||||
.map((h) => `${' '.repeat(Math.max(0, h.lvl - 1))}- [${h.content}](#${h.slug})`)
|
||||
.join('\n')
|
||||
|
||||
return { content, json: items }
|
||||
}
|
||||
@@ -50,7 +50,6 @@
|
||||
"icons": "workspace:*",
|
||||
"lodash": "^4.0.0",
|
||||
"lucide-react": "*",
|
||||
"markdown-toc": "^1.2.0",
|
||||
"next": "catalog:",
|
||||
"next-contentlayer2": "0.5.3",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
@@ -69,6 +68,7 @@
|
||||
"recharts": "^2.8.0",
|
||||
"rehype-slug": "^5.1.0",
|
||||
"remark": "^15.0.1",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-html": "^16.0.1",
|
||||
"shared-data": "workspace:*",
|
||||
|
||||
@@ -9,7 +9,7 @@ import { getAllPostSlugs, getPostdata, getSortedPosts } from '~/lib/posts'
|
||||
// for instance, if you use a button, you must add `Button` in the components object below.
|
||||
|
||||
// table of contents extractor
|
||||
const toc = require('markdown-toc')
|
||||
const toc = require('markdown-toc-unlazy')
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const paths = getAllPostSlugs('_alternatives')
|
||||
|
||||
@@ -1,298 +0,0 @@
|
||||
import dynamic from 'next/dynamic'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { useState, useMemo } from 'react'
|
||||
import { useLivePreview } from '@payloadcms/live-preview-react'
|
||||
|
||||
import authors from 'lib/authors.json'
|
||||
import { getAllPostSlugs, getPostdata, getSortedPosts } from 'lib/posts'
|
||||
import { getAllCMSPostSlugs, getCMSPostBySlug } from 'lib/get-cms-posts'
|
||||
import { CMS_SITE_ORIGIN } from 'lib/constants'
|
||||
import { isNotNullOrUndefined } from 'lib/helpers'
|
||||
|
||||
import type { GetStaticProps, InferGetStaticPropsType } from 'next'
|
||||
import type { Blog, BlogData, CMSAuthor, PostReturnType, ProcessedBlogData, Tag } from 'types/post'
|
||||
|
||||
const BlogPostRenderer = dynamic(() => import('components/Blog/BlogPostRenderer'))
|
||||
|
||||
type MatterReturn = {
|
||||
data: BlogData
|
||||
content: string
|
||||
}
|
||||
|
||||
type BlogPostPageProps = {
|
||||
prevPost: PostReturnType | null
|
||||
nextPost: PostReturnType | null
|
||||
relatedPosts: (PostReturnType & BlogData)[]
|
||||
blog: Blog & BlogData
|
||||
isDraftMode: boolean
|
||||
}
|
||||
|
||||
type Params = {
|
||||
slug: string
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const staticPaths = getAllPostSlugs('_blog')
|
||||
const cmsPaths = await getAllCMSPostSlugs()
|
||||
const paths = [...staticPaths, ...cmsPaths]
|
||||
|
||||
return {
|
||||
paths,
|
||||
fallback: 'blocking', // Set to 'blocking' to allow ISR for new CMS posts
|
||||
}
|
||||
}
|
||||
|
||||
function BlogPostPage(props: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
const isDraftMode = props.isDraftMode
|
||||
const [previewData] = useState<ProcessedBlogData>(props.blog)
|
||||
const shouldUseLivePreview = isDraftMode && props.blog.isCMS
|
||||
|
||||
const { data: livePreviewData, isLoading: isLivePreviewLoading } = useLivePreview({
|
||||
initialData: props.blog,
|
||||
serverURL: CMS_SITE_ORIGIN || 'http://localhost:3030',
|
||||
depth: 2,
|
||||
})
|
||||
|
||||
// Only use the live preview data for metadata
|
||||
const blogMetaData = useMemo(() => {
|
||||
if (isDraftMode && shouldUseLivePreview) {
|
||||
// Priority 1: Use data from LivePreview hook
|
||||
if (livePreviewData && typeof livePreviewData === 'object') {
|
||||
return { ...props.blog, ...livePreviewData }
|
||||
}
|
||||
|
||||
// Priority 2: Use data from postMessage updates
|
||||
if (previewData !== props.blog) {
|
||||
return previewData
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to blog
|
||||
return props.blog
|
||||
}, [isDraftMode, shouldUseLivePreview, livePreviewData, previewData, props.blog])
|
||||
|
||||
const isCMS = blogMetaData.isCMS
|
||||
const imageUrl = isCMS
|
||||
? blogMetaData.thumb ?? ''
|
||||
: blogMetaData.thumb
|
||||
? `/images/blog/${blogMetaData.thumb}`
|
||||
: ''
|
||||
|
||||
const meta = {
|
||||
title: blogMetaData.meta_title ?? blogMetaData.title,
|
||||
description: blogMetaData.meta_description ?? blogMetaData.description,
|
||||
url: `https://supabase.com/blog/${blogMetaData.slug}`,
|
||||
}
|
||||
|
||||
const processTag = (tag: Tag): string => {
|
||||
return typeof tag === 'string' ? tag : tag.name
|
||||
}
|
||||
|
||||
const tags = blogMetaData.tags
|
||||
? Array.isArray(blogMetaData.tags)
|
||||
? (blogMetaData.tags as Tag[]).map(processTag)
|
||||
: []
|
||||
: []
|
||||
|
||||
const blogAuthors = isCMS
|
||||
? (blogMetaData.authors as CMSAuthor[]) || []
|
||||
: (blogMetaData.author as string)
|
||||
?.split(',')
|
||||
.map((authorId: string) => {
|
||||
const foundAuthor = authors.find((author) => author.author_id === authorId)
|
||||
return foundAuthor
|
||||
? {
|
||||
author: foundAuthor.author || 'Author',
|
||||
author_image_url: foundAuthor.author_image_url || null,
|
||||
author_url: foundAuthor.author_url || '#',
|
||||
position: foundAuthor.position || '',
|
||||
}
|
||||
: null
|
||||
})
|
||||
.filter(isNotNullOrUndefined) || []
|
||||
|
||||
const authorUrls = blogAuthors.map((author) => author?.author_url).filter(isNotNullOrUndefined)
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextSeo
|
||||
title={meta.title}
|
||||
description={meta.description}
|
||||
openGraph={{
|
||||
title: meta.title,
|
||||
description: meta.description,
|
||||
url: meta.url,
|
||||
type: 'article',
|
||||
videos: blogMetaData.video
|
||||
? [
|
||||
{
|
||||
// youtube based video meta
|
||||
url: blogMetaData.video,
|
||||
type: 'application/x-shockwave-flash',
|
||||
width: 640,
|
||||
height: 385,
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
article: {
|
||||
publishedTime: blogMetaData.date,
|
||||
authors: authorUrls,
|
||||
tags: tags,
|
||||
},
|
||||
images: [
|
||||
{
|
||||
url: imageUrl,
|
||||
alt: `${blogMetaData.title} thumbnail`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<BlogPostRenderer
|
||||
blog={props.blog}
|
||||
blogMetaData={blogMetaData}
|
||||
isDraftMode={isDraftMode}
|
||||
isLivePreviewLoading={isLivePreviewLoading}
|
||||
prevPost={props.prevPost}
|
||||
nextPost={props.nextPost}
|
||||
authors={blogAuthors}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<BlogPostPageProps, Params> = async ({
|
||||
params,
|
||||
draftMode = false,
|
||||
}) => {
|
||||
if (!params?.slug) {
|
||||
console.error('[getStaticProps] Missing slug parameter:', params)
|
||||
throw new Error('Missing slug for pages/blog/[slug].tsx')
|
||||
}
|
||||
|
||||
const slug = `${params.slug}`
|
||||
|
||||
// Server-only imports to keep client bundle lean
|
||||
const matter = (await import('gray-matter')).default
|
||||
const { mdxSerialize } = await import('lib/mdx/mdxSerialize')
|
||||
const markdownToc = require('markdown-toc')
|
||||
|
||||
// Try static post first
|
||||
try {
|
||||
const postContent = await getPostdata(slug, '_blog')
|
||||
const parsedContent = matter(postContent) as unknown as MatterReturn
|
||||
const content = parsedContent.content
|
||||
const mdxSource = await mdxSerialize(content)
|
||||
const blogPost = { ...parsedContent.data }
|
||||
|
||||
// Get all posts for navigation and related posts
|
||||
const allStaticPosts = getSortedPosts({ directory: '_blog' })
|
||||
const allPosts = [...allStaticPosts].sort((a, b) => {
|
||||
const aDate = new Date((a as unknown as { date: string }).date).getTime()
|
||||
const bDate = new Date((b as unknown as { date: string }).date).getTime()
|
||||
return bDate - aDate
|
||||
})
|
||||
const currentIndex = allPosts.findIndex((post) => post.slug === slug)
|
||||
const nextPost = currentIndex === allPosts.length - 1 ? null : allPosts[currentIndex + 1]
|
||||
const prevPost = currentIndex === 0 ? null : allPosts[currentIndex - 1]
|
||||
const tocResult = markdownToc(content, {
|
||||
maxdepth: blogPost.toc_depth ? blogPost.toc_depth : 2,
|
||||
})
|
||||
const processedContent = tocResult.content.replace(/%23/g, '')
|
||||
const relatedPosts = getSortedPosts({
|
||||
directory: '_blog',
|
||||
limit: 3,
|
||||
tags: (mdxSource as { scope: { tags?: string[] } }).scope.tags,
|
||||
currentPostSlug: slug,
|
||||
}) as unknown as (BlogData & PostReturnType)[]
|
||||
|
||||
return {
|
||||
props: {
|
||||
prevPost,
|
||||
nextPost,
|
||||
relatedPosts,
|
||||
blog: {
|
||||
...blogPost,
|
||||
content: mdxSource,
|
||||
toc: {
|
||||
...tocResult,
|
||||
content: processedContent,
|
||||
},
|
||||
},
|
||||
isDraftMode: draftMode,
|
||||
},
|
||||
// Don't use revalidate in draft mode
|
||||
// ...(draftMode ? {} : { revalidate: 60 * 10 }),
|
||||
}
|
||||
} catch {
|
||||
console.log('[getStaticProps] Static post not found, trying CMS post...')
|
||||
}
|
||||
|
||||
// Try CMS post (handle draft mode logic)
|
||||
const cmsPost = await getCMSPostBySlug(slug, draftMode)
|
||||
|
||||
if (!cmsPost) {
|
||||
console.log(
|
||||
'[getStaticProps] No CMS post found, checking published version (if in draft mode)...'
|
||||
)
|
||||
// Try to fetch published version if draft mode failed
|
||||
if (draftMode) {
|
||||
const publishedPost = await getCMSPostBySlug(slug, false)
|
||||
|
||||
if (!publishedPost) {
|
||||
console.log('[getStaticProps] No published version found either, returning 404')
|
||||
return { notFound: true }
|
||||
}
|
||||
|
||||
const mdxSource = await mdxSerialize(publishedPost.content || '')
|
||||
|
||||
return {
|
||||
props: {
|
||||
prevPost: null,
|
||||
nextPost: null,
|
||||
relatedPosts: [],
|
||||
blog: {
|
||||
...publishedPost,
|
||||
tags: publishedPost.tags || [],
|
||||
authors: publishedPost.authors || [],
|
||||
isCMS: true,
|
||||
content: mdxSource,
|
||||
toc: publishedPost.toc,
|
||||
image: publishedPost.image ?? undefined,
|
||||
thumb: publishedPost.thumb ?? undefined,
|
||||
},
|
||||
isDraftMode: draftMode,
|
||||
},
|
||||
// Don't use revalidate in draft mode
|
||||
// ...(draftMode ? {} : { revalidate: 60 * 10 }),
|
||||
}
|
||||
}
|
||||
|
||||
return { notFound: true }
|
||||
}
|
||||
|
||||
// For CMS posts, process content
|
||||
const mdxSource = await mdxSerialize(cmsPost.content || '')
|
||||
|
||||
return {
|
||||
props: {
|
||||
prevPost: null,
|
||||
nextPost: null,
|
||||
relatedPosts: [],
|
||||
blog: {
|
||||
...cmsPost,
|
||||
tags: cmsPost.tags || [],
|
||||
authors: cmsPost.authors || [],
|
||||
isCMS: true,
|
||||
content: mdxSource,
|
||||
toc: cmsPost.toc,
|
||||
image: cmsPost.image ?? undefined,
|
||||
thumb: cmsPost.thumb ?? undefined,
|
||||
},
|
||||
isDraftMode: draftMode,
|
||||
},
|
||||
// Don't use revalidate in draft mode
|
||||
// ...(draftMode ? {} : { revalidate: 60 * 10 }),
|
||||
}
|
||||
}
|
||||
|
||||
export default BlogPostPage
|
||||
@@ -14,7 +14,7 @@ import { mdxSerialize } from '~/lib/mdx/mdxSerialize'
|
||||
import { getAllPostSlugs, getPostdata, getSortedPosts } from '~/lib/posts'
|
||||
|
||||
// table of contents extractor
|
||||
const toc = require('markdown-toc')
|
||||
const toc = require('markdown-toc-unlazy')
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const paths = getAllPostSlugs('_customers')
|
||||
|
||||
Generated
+4
-283
@@ -1607,9 +1607,6 @@ importers:
|
||||
lucide-react:
|
||||
specifier: '*'
|
||||
version: 0.436.0(react@18.3.1)
|
||||
markdown-toc:
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)
|
||||
@@ -1670,6 +1667,9 @@ importers:
|
||||
remark-html:
|
||||
specifier: ^16.0.1
|
||||
version: 16.0.1
|
||||
remark-parse:
|
||||
specifier: ^11.0.0
|
||||
version: 11.0.0(supports-color@8.1.1)
|
||||
shared-data:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/shared-data
|
||||
@@ -9108,10 +9108,6 @@ packages:
|
||||
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
ansi-red@0.1.1:
|
||||
resolution: {integrity: sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -9136,10 +9132,6 @@ packages:
|
||||
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ansi-wrap@0.1.0:
|
||||
resolution: {integrity: sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
ansis@3.17.0:
|
||||
resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -9297,9 +9289,6 @@ packages:
|
||||
resolution: {integrity: sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
autolinker@0.28.1:
|
||||
resolution: {integrity: sha512-zQAFO1Dlsn69eXaO6+7YZc+v84aquQKbwpzCE3L0stj56ERn9hutFxPopViLjo9G+rWwjozRhgS5KJ25Xy19cQ==}
|
||||
|
||||
autoprefixer@10.4.16:
|
||||
resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
@@ -9769,12 +9758,6 @@ packages:
|
||||
codemirror@5.65.15:
|
||||
resolution: {integrity: sha512-YC4EHbbwQeubZzxLl5G4nlbLc1T21QTrKGaOal/Pkm9dVDMZXMH7+ieSPEOZCtO9I68i8/oteJKOxzHC2zR+0g==}
|
||||
|
||||
coffee-script@1.12.7:
|
||||
resolution: {integrity: sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
deprecated: CoffeeScript on NPM has moved to "coffeescript" (no hyphen)
|
||||
hasBin: true
|
||||
|
||||
collapse-white-space@2.1.0:
|
||||
resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==}
|
||||
|
||||
@@ -9864,17 +9847,10 @@ packages:
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
concat-stream@1.6.2:
|
||||
resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
|
||||
engines: {'0': node >= 0.8}
|
||||
|
||||
concat-stream@2.0.0:
|
||||
resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
|
||||
engines: {'0': node >= 6.0}
|
||||
|
||||
concat-with-sourcemaps@1.1.0:
|
||||
resolution: {integrity: sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==}
|
||||
|
||||
concurrently@9.1.2:
|
||||
resolution: {integrity: sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -10443,10 +10419,6 @@ packages:
|
||||
devlop@1.1.0:
|
||||
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
||||
|
||||
diacritics-map@0.1.0:
|
||||
resolution: {integrity: sha512-3omnDTYrGigU0i4cJjvaKwD52B8aoqyX/NEIkukFFkogBemsIbhSa1O414fpTp5nuszJG6lvQ5vBvDVNCbSsaQ==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
|
||||
didyoumean@1.2.2:
|
||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||
|
||||
@@ -10994,6 +10966,7 @@ packages:
|
||||
resolution: {integrity: sha512-t0q23FIpvHDTtnORW+bDJziGsal5uh9RJTJ1fyH8drd4lICOoXhJ5pLMUZ5C0VQei6dNmwTzzoTRgMkO9JgHEQ==}
|
||||
peerDependencies:
|
||||
eslint: '>= 5'
|
||||
bundledDependencies: []
|
||||
|
||||
eslint-plugin-import@2.31.0:
|
||||
resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
|
||||
@@ -11174,10 +11147,6 @@ packages:
|
||||
resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
expand-range@1.8.2:
|
||||
resolution: {integrity: sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
expand-tilde@2.0.2:
|
||||
resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -11384,10 +11353,6 @@ packages:
|
||||
filelist@1.0.4:
|
||||
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
|
||||
|
||||
fill-range@2.2.4:
|
||||
resolution: {integrity: sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
fill-range@7.1.1:
|
||||
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -11460,10 +11425,6 @@ packages:
|
||||
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
for-in@1.0.2:
|
||||
resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
foreach@2.0.6:
|
||||
resolution: {integrity: sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==}
|
||||
|
||||
@@ -11860,18 +11821,10 @@ packages:
|
||||
resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==}
|
||||
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
|
||||
|
||||
gray-matter@2.1.1:
|
||||
resolution: {integrity: sha512-vbmvP1Fe/fxuT2QuLVcqb2BfK7upGhhbLIt9/owWEvPYrZZEkelLcq2HqzxosV+PQ67dUFLaAeNpH7C4hhICAA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
gray-matter@4.0.3:
|
||||
resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==}
|
||||
engines: {node: '>=6.0'}
|
||||
|
||||
gulp-header@1.8.12:
|
||||
resolution: {integrity: sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==}
|
||||
deprecated: Removed event-stream from gulp-header
|
||||
|
||||
gzip-size@6.0.0:
|
||||
resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -12406,10 +12359,6 @@ packages:
|
||||
resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-extendable@1.0.1:
|
||||
resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-extglob@2.1.1:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -12483,14 +12432,6 @@ packages:
|
||||
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-number@2.1.0:
|
||||
resolution: {integrity: sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-number@4.0.0:
|
||||
resolution: {integrity: sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-number@7.0.0:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
@@ -12672,10 +12613,6 @@ packages:
|
||||
resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
isobject@2.1.0:
|
||||
resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
isobject@3.0.1:
|
||||
resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -12967,10 +12904,6 @@ packages:
|
||||
resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
||||
lazy-cache@2.0.2:
|
||||
resolution: {integrity: sha512-7vp2Acd2+Kz4XkzxGxaB1FWOi8KjWIWsgdfD5MCb86DWvlLqhRPM+d6Pro3iNEL5VT9mstz5hKAlcd+QR6H3aA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
lazystream@1.0.1:
|
||||
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
|
||||
engines: {node: '>= 0.6.3'}
|
||||
@@ -13016,10 +12949,6 @@ packages:
|
||||
linkify-it@5.0.0:
|
||||
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
|
||||
|
||||
list-item@1.1.1:
|
||||
resolution: {integrity: sha512-S3D0WZ4J6hyM8o5SNKWaMYB1ALSacPZ2nHGEuCjmHZ+dc03gFeNZoNDcqfcnO4vDhTZmNrqrpYZCdXsRh22bzw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
listhen@1.9.0:
|
||||
resolution: {integrity: sha512-I8oW2+QL5KJo8zXNWX046M134WchxsXC7SawLPvRQpogCbkyQIaFxPE89A2HiwR7vAK2Dm2ERBAmyjTYGYEpBg==}
|
||||
hasBin: true
|
||||
@@ -13072,9 +13001,6 @@ packages:
|
||||
lodash._createset@4.0.3:
|
||||
resolution: {integrity: sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA==}
|
||||
|
||||
lodash._reinterpolate@3.0.0:
|
||||
resolution: {integrity: sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==}
|
||||
|
||||
lodash._root@3.0.1:
|
||||
resolution: {integrity: sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==}
|
||||
|
||||
@@ -13102,13 +13028,6 @@ packages:
|
||||
lodash.sortby@4.7.0:
|
||||
resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
|
||||
|
||||
lodash.template@4.5.0:
|
||||
resolution: {integrity: sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==}
|
||||
deprecated: This package is deprecated. Use https://socket.dev/npm/package/eta instead.
|
||||
|
||||
lodash.templatesettings@4.2.0:
|
||||
resolution: {integrity: sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==}
|
||||
|
||||
lodash.throttle@4.1.1:
|
||||
resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
|
||||
|
||||
@@ -13246,18 +13165,9 @@ packages:
|
||||
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
|
||||
hasBin: true
|
||||
|
||||
markdown-link@0.1.1:
|
||||
resolution: {integrity: sha512-TurLymbyLyo+kAUUAV9ggR9EPcDjP/ctlv9QAFiqUH7c+t6FlsbivPo9OKTU8xdOx9oNd2drW/Fi5RRElQbUqA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
markdown-table@3.0.3:
|
||||
resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
|
||||
|
||||
markdown-toc@1.2.0:
|
||||
resolution: {integrity: sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
hasBin: true
|
||||
|
||||
markdown-wasm@1.2.0:
|
||||
resolution: {integrity: sha512-S12OTkyXCkOgI1n1rZY9cg4bK/PGu80Emjpvwp8BEjwCxhPV3yddF0U6+QhCitdBsI1tzWcoeahmW7k0Pq81OA==}
|
||||
|
||||
@@ -13270,9 +13180,6 @@ packages:
|
||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
math-random@1.0.4:
|
||||
resolution: {integrity: sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==}
|
||||
|
||||
md5@2.3.0:
|
||||
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
|
||||
|
||||
@@ -13803,10 +13710,6 @@ packages:
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
mixin-deep@1.3.2:
|
||||
resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
mkdirp@1.0.4:
|
||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -14288,10 +14191,6 @@ packages:
|
||||
resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
object.pick@1.3.0:
|
||||
resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
object.values@1.2.0:
|
||||
resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -15105,10 +15004,6 @@ packages:
|
||||
resolution: {integrity: sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
randomatic@3.1.1:
|
||||
resolution: {integrity: sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
|
||||
randombytes@2.1.0:
|
||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||
|
||||
@@ -15707,11 +15602,6 @@ packages:
|
||||
remark@15.0.1:
|
||||
resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==}
|
||||
|
||||
remarkable@1.7.4:
|
||||
resolution: {integrity: sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
hasBin: true
|
||||
|
||||
remedial@1.0.8:
|
||||
resolution: {integrity: sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==}
|
||||
|
||||
@@ -15731,10 +15621,6 @@ packages:
|
||||
resolution: {integrity: sha512-U7XpAktpbSgHTRSNRrjKSrjYkZKuhUukfoBlXWXUExCAqhzh1TU3BDRAfJmarcl5voKS+pbKU9MvyLWKZ4UEEg==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
repeat-element@1.1.4:
|
||||
resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
repeat-string@1.6.1:
|
||||
resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==}
|
||||
engines: {node: '>=0.10'}
|
||||
@@ -16036,10 +15922,6 @@ packages:
|
||||
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
set-getter@0.1.1:
|
||||
resolution: {integrity: sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
set-harmonic-interval@1.0.1:
|
||||
resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==}
|
||||
engines: {node: '>=6.9'}
|
||||
@@ -16487,10 +16369,6 @@ packages:
|
||||
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
strip-color@0.1.0:
|
||||
resolution: {integrity: sha512-p9LsUieSjWNNAxVCXLeilaDlmuUOrDS5/dF9znM1nZc7EGX5+zEFC0bEevsNIaldjlks+2jns5Siz6F9iK6jwA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
strip-eof@1.0.0:
|
||||
resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -16740,9 +16618,6 @@ packages:
|
||||
resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
through2@2.0.5:
|
||||
resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
|
||||
|
||||
through@2.3.8:
|
||||
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
|
||||
|
||||
@@ -16808,10 +16683,6 @@ packages:
|
||||
to-no-case@1.0.2:
|
||||
resolution: {integrity: sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==}
|
||||
|
||||
to-object-path@0.3.0:
|
||||
resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
@@ -16840,9 +16711,6 @@ packages:
|
||||
resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==}
|
||||
engines: {node: '>=14.16'}
|
||||
|
||||
toml@2.3.6:
|
||||
resolution: {integrity: sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==}
|
||||
|
||||
toml@3.0.0:
|
||||
resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==}
|
||||
|
||||
@@ -27701,10 +27569,6 @@ snapshots:
|
||||
dependencies:
|
||||
type-fest: 0.21.3
|
||||
|
||||
ansi-red@0.1.1:
|
||||
dependencies:
|
||||
ansi-wrap: 0.1.0
|
||||
|
||||
ansi-regex@5.0.1: {}
|
||||
|
||||
ansi-regex@6.0.1: {}
|
||||
@@ -27721,8 +27585,6 @@ snapshots:
|
||||
|
||||
ansi-styles@6.2.1: {}
|
||||
|
||||
ansi-wrap@0.1.0: {}
|
||||
|
||||
ansis@3.17.0: {}
|
||||
|
||||
any-promise@1.3.0: {}
|
||||
@@ -27908,10 +27770,6 @@ snapshots:
|
||||
|
||||
auto-bind@4.0.0: {}
|
||||
|
||||
autolinker@0.28.1:
|
||||
dependencies:
|
||||
gulp-header: 1.8.12
|
||||
|
||||
autoprefixer@10.4.16(postcss@8.5.3):
|
||||
dependencies:
|
||||
browserslist: 4.24.4
|
||||
@@ -28479,8 +28337,6 @@ snapshots:
|
||||
|
||||
codemirror@5.65.15: {}
|
||||
|
||||
coffee-script@1.12.7: {}
|
||||
|
||||
collapse-white-space@2.1.0: {}
|
||||
|
||||
color-convert@1.9.3:
|
||||
@@ -28560,13 +28416,6 @@ snapshots:
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
concat-stream@1.6.2:
|
||||
dependencies:
|
||||
buffer-from: 1.1.2
|
||||
inherits: 2.0.4
|
||||
readable-stream: 2.3.8
|
||||
typedarray: 0.0.6
|
||||
|
||||
concat-stream@2.0.0:
|
||||
dependencies:
|
||||
buffer-from: 1.1.2
|
||||
@@ -28574,10 +28423,6 @@ snapshots:
|
||||
readable-stream: 3.6.2
|
||||
typedarray: 0.0.6
|
||||
|
||||
concat-with-sourcemaps@1.1.0:
|
||||
dependencies:
|
||||
source-map: 0.6.1
|
||||
|
||||
concurrently@9.1.2:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
@@ -29101,8 +28946,6 @@ snapshots:
|
||||
dependencies:
|
||||
dequal: 2.0.3
|
||||
|
||||
diacritics-map@0.1.0: {}
|
||||
|
||||
didyoumean@1.2.2: {}
|
||||
|
||||
diff-sequences@29.6.3: {}
|
||||
@@ -29950,10 +29793,6 @@ snapshots:
|
||||
|
||||
exit-hook@2.2.1: {}
|
||||
|
||||
expand-range@1.8.2:
|
||||
dependencies:
|
||||
fill-range: 2.2.4
|
||||
|
||||
expand-tilde@2.0.2:
|
||||
dependencies:
|
||||
homedir-polyfill: 1.0.3
|
||||
@@ -30206,14 +30045,6 @@ snapshots:
|
||||
dependencies:
|
||||
minimatch: 5.1.6
|
||||
|
||||
fill-range@2.2.4:
|
||||
dependencies:
|
||||
is-number: 2.1.0
|
||||
isobject: 2.1.0
|
||||
randomatic: 3.1.1
|
||||
repeat-element: 1.1.4
|
||||
repeat-string: 1.6.1
|
||||
|
||||
fill-range@7.1.1:
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
@@ -30286,8 +30117,6 @@ snapshots:
|
||||
dependencies:
|
||||
is-callable: 1.2.7
|
||||
|
||||
for-in@1.0.2: {}
|
||||
|
||||
foreach@2.0.6: {}
|
||||
|
||||
foreground-child@3.1.1:
|
||||
@@ -30753,14 +30582,6 @@ snapshots:
|
||||
|
||||
graphql@16.11.0: {}
|
||||
|
||||
gray-matter@2.1.1:
|
||||
dependencies:
|
||||
ansi-red: 0.1.1
|
||||
coffee-script: 1.12.7
|
||||
extend-shallow: 2.0.1
|
||||
js-yaml: 3.14.1
|
||||
toml: 2.3.6
|
||||
|
||||
gray-matter@4.0.3:
|
||||
dependencies:
|
||||
js-yaml: 3.14.1
|
||||
@@ -30768,12 +30589,6 @@ snapshots:
|
||||
section-matter: 1.0.0
|
||||
strip-bom-string: 1.0.0
|
||||
|
||||
gulp-header@1.8.12:
|
||||
dependencies:
|
||||
concat-with-sourcemaps: 1.1.0
|
||||
lodash.template: 4.5.0
|
||||
through2: 2.0.5
|
||||
|
||||
gzip-size@6.0.0:
|
||||
dependencies:
|
||||
duplexer: 0.1.2
|
||||
@@ -31489,10 +31304,6 @@ snapshots:
|
||||
|
||||
is-extendable@0.1.1: {}
|
||||
|
||||
is-extendable@1.0.1:
|
||||
dependencies:
|
||||
is-plain-object: 2.0.4
|
||||
|
||||
is-extglob@2.1.1: {}
|
||||
|
||||
is-finalizationregistry@1.0.2:
|
||||
@@ -31550,12 +31361,6 @@ snapshots:
|
||||
call-bound: 1.0.4
|
||||
has-tostringtag: 1.0.2
|
||||
|
||||
is-number@2.1.0:
|
||||
dependencies:
|
||||
kind-of: 3.2.2
|
||||
|
||||
is-number@4.0.0: {}
|
||||
|
||||
is-number@7.0.0: {}
|
||||
|
||||
is-obj@3.0.0: {}
|
||||
@@ -31708,10 +31513,6 @@ snapshots:
|
||||
|
||||
isexe@3.1.1: {}
|
||||
|
||||
isobject@2.1.0:
|
||||
dependencies:
|
||||
isarray: 1.0.0
|
||||
|
||||
isobject@3.0.1: {}
|
||||
|
||||
isomorphic-unfetch@3.1.0(encoding@0.1.13):
|
||||
@@ -32023,10 +31824,6 @@ snapshots:
|
||||
dependencies:
|
||||
language-subtag-registry: 0.3.23
|
||||
|
||||
lazy-cache@2.0.2:
|
||||
dependencies:
|
||||
set-getter: 0.1.1
|
||||
|
||||
lazystream@1.0.1:
|
||||
dependencies:
|
||||
readable-stream: 2.3.8
|
||||
@@ -32088,13 +31885,6 @@ snapshots:
|
||||
dependencies:
|
||||
uc.micro: 2.1.0
|
||||
|
||||
list-item@1.1.1:
|
||||
dependencies:
|
||||
expand-range: 1.8.2
|
||||
extend-shallow: 2.0.1
|
||||
is-number: 2.1.0
|
||||
repeat-string: 1.6.1
|
||||
|
||||
listhen@1.9.0:
|
||||
dependencies:
|
||||
'@parcel/watcher': 2.5.1
|
||||
@@ -32173,8 +31963,6 @@ snapshots:
|
||||
|
||||
lodash._createset@4.0.3: {}
|
||||
|
||||
lodash._reinterpolate@3.0.0: {}
|
||||
|
||||
lodash._root@3.0.1: {}
|
||||
|
||||
lodash._stringtopath@4.8.0:
|
||||
@@ -32195,15 +31983,6 @@ snapshots:
|
||||
|
||||
lodash.sortby@4.7.0: {}
|
||||
|
||||
lodash.template@4.5.0:
|
||||
dependencies:
|
||||
lodash._reinterpolate: 3.0.0
|
||||
lodash.templatesettings: 4.2.0
|
||||
|
||||
lodash.templatesettings@4.2.0:
|
||||
dependencies:
|
||||
lodash._reinterpolate: 3.0.0
|
||||
|
||||
lodash.throttle@4.1.1: {}
|
||||
|
||||
lodash.uniqby@4.5.0:
|
||||
@@ -32372,33 +32151,14 @@ snapshots:
|
||||
punycode.js: 2.3.1
|
||||
uc.micro: 2.1.0
|
||||
|
||||
markdown-link@0.1.1: {}
|
||||
|
||||
markdown-table@3.0.3: {}
|
||||
|
||||
markdown-toc@1.2.0:
|
||||
dependencies:
|
||||
concat-stream: 1.6.2
|
||||
diacritics-map: 0.1.0
|
||||
gray-matter: 2.1.1
|
||||
lazy-cache: 2.0.2
|
||||
list-item: 1.1.1
|
||||
markdown-link: 0.1.1
|
||||
minimist: 1.2.8
|
||||
mixin-deep: 1.3.2
|
||||
object.pick: 1.3.0
|
||||
remarkable: 1.7.4
|
||||
repeat-string: 1.6.1
|
||||
strip-color: 0.1.0
|
||||
|
||||
markdown-wasm@1.2.0: {}
|
||||
|
||||
marked@4.3.0: {}
|
||||
|
||||
math-intrinsics@1.1.0: {}
|
||||
|
||||
math-random@1.0.4: {}
|
||||
|
||||
md5@2.3.0:
|
||||
dependencies:
|
||||
charenc: 0.0.2
|
||||
@@ -33486,11 +33246,6 @@ snapshots:
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
mixin-deep@1.3.2:
|
||||
dependencies:
|
||||
for-in: 1.0.2
|
||||
is-extendable: 1.0.1
|
||||
|
||||
mkdirp@1.0.4: {}
|
||||
|
||||
mkdirp@2.1.6: {}
|
||||
@@ -34147,10 +33902,6 @@ snapshots:
|
||||
define-properties: 1.2.1
|
||||
es-abstract: 1.23.3
|
||||
|
||||
object.pick@1.3.0:
|
||||
dependencies:
|
||||
isobject: 3.0.1
|
||||
|
||||
object.values@1.2.0:
|
||||
dependencies:
|
||||
call-bind: 1.0.7
|
||||
@@ -35063,12 +34814,6 @@ snapshots:
|
||||
drange: 1.1.1
|
||||
ret: 0.2.2
|
||||
|
||||
randomatic@3.1.1:
|
||||
dependencies:
|
||||
is-number: 4.0.0
|
||||
kind-of: 6.0.3
|
||||
math-random: 1.0.4
|
||||
|
||||
randombytes@2.1.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
@@ -35953,11 +35698,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
remarkable@1.7.4:
|
||||
dependencies:
|
||||
argparse: 1.0.10
|
||||
autolinker: 0.28.1
|
||||
|
||||
remedial@1.0.8: {}
|
||||
|
||||
remove-accents@0.4.2: {}
|
||||
@@ -35977,8 +35717,6 @@ snapshots:
|
||||
|
||||
rename-keys@1.2.0: {}
|
||||
|
||||
repeat-element@1.1.4: {}
|
||||
|
||||
repeat-string@1.6.1: {}
|
||||
|
||||
require-directory@2.1.1: {}
|
||||
@@ -36344,10 +36082,6 @@ snapshots:
|
||||
functions-have-names: 1.2.3
|
||||
has-property-descriptors: 1.0.2
|
||||
|
||||
set-getter@0.1.1:
|
||||
dependencies:
|
||||
to-object-path: 0.3.0
|
||||
|
||||
set-harmonic-interval@1.0.1: {}
|
||||
|
||||
set-proto@1.0.0:
|
||||
@@ -36917,8 +36651,6 @@ snapshots:
|
||||
|
||||
strip-bom@3.0.0: {}
|
||||
|
||||
strip-color@0.1.0: {}
|
||||
|
||||
strip-eof@1.0.0: {}
|
||||
|
||||
strip-final-newline@3.0.0: {}
|
||||
@@ -37279,11 +37011,6 @@ snapshots:
|
||||
|
||||
throttleit@2.1.0: {}
|
||||
|
||||
through2@2.0.5:
|
||||
dependencies:
|
||||
readable-stream: 2.3.8
|
||||
xtend: 4.0.2
|
||||
|
||||
through@2.3.8: {}
|
||||
|
||||
timeout-signal@2.0.0: {}
|
||||
@@ -37334,10 +37061,6 @@ snapshots:
|
||||
|
||||
to-no-case@1.0.2: {}
|
||||
|
||||
to-object-path@0.3.0:
|
||||
dependencies:
|
||||
kind-of: 3.2.2
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
@@ -37366,8 +37089,6 @@ snapshots:
|
||||
'@tokenizer/token': 0.3.0
|
||||
ieee754: 1.2.1
|
||||
|
||||
toml@2.3.6: {}
|
||||
|
||||
toml@3.0.0: {}
|
||||
|
||||
topojson-client@3.1.0:
|
||||
|
||||
Reference in new issue
Block a user