Refactor CMS to handle ContentLayer and WordPress platforms
This commit refactors the CMS to handle two platforms: ContentLayer and WordPress. The CMS layer is abstracted into a core package, and separate implementations for each platform are created. This change allows the app to switch the CMS type based on environment variable, which can improve the flexibility of content management. It also updates several functions in the `server-sitemap.xml` route to accommodate these changes and generate sitemaps based on the CMS client. Further, documentation content and posts have been relocated to align with the new structure. Notably, this refactor is a comprehensive update to the way the CMS is structured and managed.
This commit is contained in:
@@ -7,6 +7,9 @@ NEXT_PUBLIC_DEFAULT_THEME_MODE=light
|
|||||||
NEXT_PUBLIC_THEME_COLOR="#ffffff"
|
NEXT_PUBLIC_THEME_COLOR="#ffffff"
|
||||||
NEXT_PUBLIC_THEME_COLOR_DARK="#0a0a0a"
|
NEXT_PUBLIC_THEME_COLOR_DARK="#0a0a0a"
|
||||||
|
|
||||||
|
# CMS
|
||||||
|
CMS_CLIENT=contentlayer
|
||||||
|
|
||||||
# AUTH
|
# AUTH
|
||||||
NEXT_PUBLIC_AUTH_PASSWORD=true
|
NEXT_PUBLIC_AUTH_PASSWORD=true
|
||||||
NEXT_PUBLIC_AUTH_MAGIC_LINK=false
|
NEXT_PUBLIC_AUTH_MAGIC_LINK=false
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import Script from 'next/script';
|
|
||||||
|
|
||||||
import { allPosts } from 'contentlayer/generated';
|
import { createCmsClient } from '@kit/cms';
|
||||||
|
|
||||||
import Post from '~/(marketing)/blog/_components/post';
|
import Post from '~/(marketing)/blog/_components/post';
|
||||||
import appConfig from '~/config/app.config';
|
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
@@ -14,14 +12,14 @@ export async function generateMetadata({
|
|||||||
}: {
|
}: {
|
||||||
params: { slug: string };
|
params: { slug: string };
|
||||||
}): Promise<Metadata | undefined> {
|
}): Promise<Metadata | undefined> {
|
||||||
const post = allPosts.find((post) => post.slug === params.slug);
|
const cms = await createCmsClient();
|
||||||
|
const post = await cms.getContentItemById(params.slug);
|
||||||
|
|
||||||
if (!post) {
|
if (!post) {
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, date, description, image, slug } = post;
|
const { title, publishedAt, description, image } = post;
|
||||||
const url = [appConfig.url, 'blog', slug].join('/');
|
|
||||||
|
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
title,
|
title,
|
||||||
@@ -30,8 +28,8 @@ export async function generateMetadata({
|
|||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
type: 'article',
|
type: 'article',
|
||||||
publishedTime: date,
|
publishedTime: publishedAt.toDateString(),
|
||||||
url,
|
url: post.url,
|
||||||
images: image
|
images: image
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@@ -49,8 +47,9 @@ export async function generateMetadata({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function BlogPost({ params }: { params: { slug: string } }) {
|
async function BlogPost({ params }: { params: { slug: string } }) {
|
||||||
const post = allPosts.find((post) => post.slug === params.slug);
|
const cms = await createCmsClient();
|
||||||
|
const post = await cms.getContentItemById(params.slug);
|
||||||
|
|
||||||
if (!post) {
|
if (!post) {
|
||||||
notFound();
|
notFound();
|
||||||
@@ -58,11 +57,7 @@ function BlogPost({ params }: { params: { slug: string } }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'container mx-auto'}>
|
<div className={'container mx-auto'}>
|
||||||
<Script id={'ld-json'} type="application/ld+json">
|
<Post post={post} content={post.content} />
|
||||||
{JSON.stringify(post.structuredData)}
|
|
||||||
</Script>
|
|
||||||
|
|
||||||
<Post post={post} content={post.body.code} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { Post } from 'contentlayer/generated';
|
import { Cms } from '@kit/cms';
|
||||||
|
|
||||||
import { Heading } from '@kit/ui/heading';
|
import { Heading } from '@kit/ui/heading';
|
||||||
import { If } from '@kit/ui/if';
|
import { If } from '@kit/ui/if';
|
||||||
|
|
||||||
@@ -7,9 +6,9 @@ import { CoverImage } from '~/(marketing)/blog/_components/cover-image';
|
|||||||
import { DateFormatter } from '~/(marketing)/blog/_components/date-formatter';
|
import { DateFormatter } from '~/(marketing)/blog/_components/date-formatter';
|
||||||
|
|
||||||
export const PostHeader: React.FC<{
|
export const PostHeader: React.FC<{
|
||||||
post: Post;
|
post: Cms.ContentItem;
|
||||||
}> = ({ post }) => {
|
}> = ({ post }) => {
|
||||||
const { title, date, readingTime, description, image } = post;
|
const { title, publishedAt, description, image } = post;
|
||||||
|
|
||||||
// NB: change this to display the post's image
|
// NB: change this to display the post's image
|
||||||
const displayImage = true;
|
const displayImage = true;
|
||||||
@@ -30,11 +29,8 @@ export const PostHeader: React.FC<{
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="flex flex-row items-center space-x-2 text-sm text-gray-600 dark:text-gray-400">
|
<div className="flex flex-row items-center space-x-2 text-sm text-gray-600 dark:text-gray-400">
|
||||||
<div>
|
<div>
|
||||||
<DateFormatter dateString={date} />
|
<DateFormatter dateString={publishedAt.toISOString()} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span>·</span>
|
|
||||||
<span>{readingTime} minutes reading</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import type { Post } from 'contentlayer/generated';
|
import { Cms } from '@kit/cms';
|
||||||
|
|
||||||
import { If } from '@kit/ui/if';
|
import { If } from '@kit/ui/if';
|
||||||
|
|
||||||
import { CoverImage } from '~/(marketing)/blog/_components/cover-image';
|
import { CoverImage } from '~/(marketing)/blog/_components/cover-image';
|
||||||
import { DateFormatter } from '~/(marketing)/blog/_components/date-formatter';
|
import { DateFormatter } from '~/(marketing)/blog/_components/date-formatter';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
post: Post;
|
post: Cms.ContentItem;
|
||||||
preloadImage?: boolean;
|
preloadImage?: boolean;
|
||||||
imageHeight?: string | number;
|
imageHeight?: string | number;
|
||||||
};
|
};
|
||||||
@@ -20,15 +19,16 @@ export function PostPreview({
|
|||||||
preloadImage,
|
preloadImage,
|
||||||
imageHeight,
|
imageHeight,
|
||||||
}: React.PropsWithChildren<Props>) {
|
}: React.PropsWithChildren<Props>) {
|
||||||
const { title, image, date, readingTime, description } = post;
|
const { title, image, publishedAt, description } = post;
|
||||||
const height = imageHeight ?? DEFAULT_IMAGE_HEIGHT;
|
const height = imageHeight ?? DEFAULT_IMAGE_HEIGHT;
|
||||||
|
const url = post.url;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-xl transition-shadow duration-500 dark:text-gray-800">
|
<div className="rounded-xl transition-shadow duration-500">
|
||||||
<If condition={image}>
|
<If condition={image}>
|
||||||
{(imageUrl) => (
|
{(imageUrl) => (
|
||||||
<div className="relative mb-2 w-full" style={{ height }}>
|
<div className="relative mb-2 w-full" style={{ height }}>
|
||||||
<Link href={post.url}>
|
<Link href={url}>
|
||||||
<CoverImage
|
<CoverImage
|
||||||
preloadImage={preloadImage}
|
preloadImage={preloadImage}
|
||||||
title={title}
|
title={title}
|
||||||
@@ -40,27 +40,21 @@ export function PostPreview({
|
|||||||
</If>
|
</If>
|
||||||
|
|
||||||
<div className={'px-1'}>
|
<div className={'px-1'}>
|
||||||
<div className="flex flex-col space-y-1 px-1 py-2">
|
<div className="flex flex-col space-y-1 py-2">
|
||||||
<h3 className="px-1 text-2xl font-bold leading-snug dark:text-white">
|
<h3 className="text-2xl font-bold leading-snug dark:text-white">
|
||||||
<Link href={post.url} className="hover:underline">
|
<Link href={url} className="hover:underline">
|
||||||
{title}
|
{title}
|
||||||
</Link>
|
</Link>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-2 flex flex-row items-center space-x-2 px-1 text-sm">
|
<div className="mb-2 flex flex-row items-center space-x-2 text-sm">
|
||||||
<div className="text-gray-600 dark:text-gray-300">
|
<div className="text-muted-foreground">
|
||||||
<DateFormatter dateString={date} />
|
<DateFormatter dateString={publishedAt.toISOString()} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span className="text-gray-600 dark:text-gray-300">·</span>
|
|
||||||
|
|
||||||
<span className="text-gray-600 dark:text-gray-300">
|
|
||||||
{readingTime} mins reading
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="mb-4 px-1 text-sm leading-relaxed dark:text-gray-300">
|
<p className="mb-4 text-sm leading-relaxed text-muted-foreground">
|
||||||
{description}
|
{description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
import dynamic from 'next/dynamic';
|
import type { Cms } from '@kit/cms';
|
||||||
|
import { ContentRenderer } from '@kit/cms';
|
||||||
import type { Post as PostType } from 'contentlayer/generated';
|
|
||||||
|
|
||||||
import { PostHeader } from './post-header';
|
import { PostHeader } from './post-header';
|
||||||
|
|
||||||
const Mdx = dynamic(() =>
|
|
||||||
import('@kit/ui/mdx').then((mod) => ({ default: mod.Mdx })),
|
|
||||||
);
|
|
||||||
|
|
||||||
export const Post: React.FC<{
|
export const Post: React.FC<{
|
||||||
post: PostType;
|
post: Cms.ContentItem;
|
||||||
content: string;
|
content: string;
|
||||||
}> = ({ post, content }) => {
|
}> = ({ post, content }) => {
|
||||||
return (
|
return (
|
||||||
@@ -17,7 +12,7 @@ export const Post: React.FC<{
|
|||||||
<PostHeader post={post} />
|
<PostHeader post={post} />
|
||||||
|
|
||||||
<article className={'mx-auto flex justify-center'}>
|
<article className={'mx-auto flex justify-center'}>
|
||||||
<Mdx code={content} />
|
<ContentRenderer content={content} />
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
import { allPosts } from 'contentlayer/generated';
|
import { createCmsClient } from '@kit/cms';
|
||||||
|
|
||||||
import { GridList } from '~/(marketing)/_components/grid-list';
|
import { GridList } from '~/(marketing)/_components/grid-list';
|
||||||
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
||||||
@@ -13,11 +13,11 @@ export const metadata: Metadata = {
|
|||||||
description: `Tutorials, Guides and Updates from our team`,
|
description: `Tutorials, Guides and Updates from our team`,
|
||||||
};
|
};
|
||||||
|
|
||||||
function BlogPage() {
|
async function BlogPage() {
|
||||||
const livePosts = allPosts.filter((post) => {
|
const cms = await createCmsClient();
|
||||||
const isProduction = appConfig.production;
|
|
||||||
|
|
||||||
return isProduction ? post.live : true;
|
const posts = await cms.getContentItems({
|
||||||
|
type: 'post',
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -29,7 +29,7 @@ function BlogPage() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<GridList>
|
<GridList>
|
||||||
{livePosts.map((post, idx) => {
|
{posts.map((post, idx) => {
|
||||||
return <PostPreview key={idx} post={post} />;
|
return <PostPreview key={idx} post={post} />;
|
||||||
})}
|
})}
|
||||||
</GridList>
|
</GridList>
|
||||||
|
|||||||
@@ -2,20 +2,20 @@ import { cache } from 'react';
|
|||||||
|
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
import { allDocumentationPages } from 'contentlayer/generated';
|
|
||||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||||
|
|
||||||
|
import { ContentRenderer, createCmsClient } from '@kit/cms';
|
||||||
import { If } from '@kit/ui/if';
|
import { If } from '@kit/ui/if';
|
||||||
import { Mdx } from '@kit/ui/mdx';
|
|
||||||
|
|
||||||
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
||||||
import { DocsCards } from '~/(marketing)/docs/_components/docs-cards';
|
import { DocsCards } from '~/(marketing)/docs/_components/docs-cards';
|
||||||
import { DocumentationPageLink } from '~/(marketing)/docs/_components/documentation-page-link';
|
import { DocumentationPageLink } from '~/(marketing)/docs/_components/documentation-page-link';
|
||||||
import { getDocumentationPageTree } from '~/(marketing)/docs/_lib/get-documentation-page-tree';
|
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
|
||||||
const getPageBySlug = cache((slug: string) => {
|
const getPageBySlug = cache(async (slug: string) => {
|
||||||
return allDocumentationPages.find((post) => post.resolvedPath === slug);
|
const client = await createCmsClient();
|
||||||
|
|
||||||
|
return client.getContentItemById(slug);
|
||||||
});
|
});
|
||||||
|
|
||||||
interface PageParams {
|
interface PageParams {
|
||||||
@@ -24,8 +24,8 @@ interface PageParams {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateMetadata = ({ params }: PageParams) => {
|
export const generateMetadata = async ({ params }: PageParams) => {
|
||||||
const page = getPageBySlug(params.slug.join('/'));
|
const page = await getPageBySlug(params.slug.join('/'));
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
notFound();
|
notFound();
|
||||||
@@ -39,16 +39,13 @@ export const generateMetadata = ({ params }: PageParams) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function DocumentationPage({ params }: PageParams) {
|
async function DocumentationPage({ params }: PageParams) {
|
||||||
const page = getPageBySlug(params.slug.join('/'));
|
const page = await getPageBySlug(params.slug.join('/'));
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { nextPage, previousPage, children } =
|
|
||||||
getDocumentationPageTree(page.resolvedPath) ?? {};
|
|
||||||
|
|
||||||
const description = page?.description ?? '';
|
const description = page?.description ?? '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -60,40 +57,11 @@ function DocumentationPage({ params }: PageParams) {
|
|||||||
className={'items-start'}
|
className={'items-start'}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Mdx code={page.body.code} />
|
<ContentRenderer content={page.content} />
|
||||||
|
|
||||||
<If condition={children}>
|
<If condition={page.children}>
|
||||||
<DocsCards pages={children ?? []} />
|
<DocsCards pages={page.children ?? []} />
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
<div
|
|
||||||
className={
|
|
||||||
'flex flex-col justify-between space-y-4 md:flex-row md:space-x-8' +
|
|
||||||
' md:space-y-0'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className={'w-full'}>
|
|
||||||
<If condition={previousPage}>
|
|
||||||
{(page) => (
|
|
||||||
<DocumentationPageLink
|
|
||||||
page={page}
|
|
||||||
before={<ChevronLeft className={'w-4'} />}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</If>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={'w-full'}>
|
|
||||||
<If condition={nextPage}>
|
|
||||||
{(page) => (
|
|
||||||
<DocumentationPageLink
|
|
||||||
page={page}
|
|
||||||
after={<ChevronRight className={'w-4'} />}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</If>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,18 +4,18 @@ import { ChevronRight } from 'lucide-react';
|
|||||||
|
|
||||||
export const DocsCard: React.FC<
|
export const DocsCard: React.FC<
|
||||||
React.PropsWithChildren<{
|
React.PropsWithChildren<{
|
||||||
label: string;
|
title: string;
|
||||||
subtitle?: string | null;
|
subtitle?: string | null;
|
||||||
link?: { url: string; label: string };
|
link?: { url: string; label: string };
|
||||||
}>
|
}>
|
||||||
> = ({ label, subtitle, children, link }) => {
|
> = ({ title, subtitle, children, link }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div
|
<div
|
||||||
className={`flex grow flex-col space-y-2.5 border bg-background p-6
|
className={`flex grow flex-col space-y-2.5 border bg-background p-6
|
||||||
${link ? 'rounded-t-2xl border-b-0' : 'rounded-2xl'}`}
|
${link ? 'rounded-t-2xl border-b-0' : 'rounded-2xl'}`}
|
||||||
>
|
>
|
||||||
<h3 className="mt-0 text-lg font-semibold dark:text-white">{label}</h3>
|
<h3 className="mt-0 text-lg font-semibold dark:text-white">{title}</h3>
|
||||||
|
|
||||||
{subtitle && (
|
{subtitle && (
|
||||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||||
@@ -31,7 +31,7 @@ export const DocsCard: React.FC<
|
|||||||
<span className={'flex items-center space-x-2'}>
|
<span className={'flex items-center space-x-2'}>
|
||||||
<Link
|
<Link
|
||||||
className={'text-sm font-medium hover:underline'}
|
className={'text-sm font-medium hover:underline'}
|
||||||
href={`/docs/${link.url}`}
|
href={link.url}
|
||||||
>
|
>
|
||||||
{link.label}
|
{link.label}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import type { DocumentationPage } from 'contentlayer/generated';
|
import { Cms } from '@kit/cms';
|
||||||
|
|
||||||
import { DocsCard } from './docs-card';
|
import { DocsCard } from './docs-card';
|
||||||
|
|
||||||
export function DocsCards({ pages }: { pages: DocumentationPage[] }) {
|
export function DocsCards({ pages }: { pages: Cms.ContentItem[] }) {
|
||||||
return (
|
return (
|
||||||
<div className={'grid grid-cols-1 gap-8 lg:grid-cols-2'}>
|
<div className={'grid grid-cols-1 gap-8 lg:grid-cols-2'}>
|
||||||
{pages.map((item) => {
|
{pages.map((item) => {
|
||||||
return (
|
return (
|
||||||
<DocsCard
|
<DocsCard
|
||||||
key={item.label}
|
key={item.title}
|
||||||
label={item.label}
|
title={item.title}
|
||||||
subtitle={item.description}
|
subtitle={item.description}
|
||||||
link={{
|
link={{
|
||||||
url: item.resolvedPath,
|
url: item.url,
|
||||||
label: item.cardCTA ?? 'Read more',
|
label: 'Read more',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,33 +5,21 @@ import { useEffect, useMemo, useState } from 'react';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
|
|
||||||
import { ChevronDown, Menu } from 'lucide-react';
|
import { Menu } from 'lucide-react';
|
||||||
|
|
||||||
|
import { Cms } from '@kit/cms';
|
||||||
import { isBrowser } from '@kit/shared/utils';
|
import { isBrowser } from '@kit/shared/utils';
|
||||||
import { Button } from '@kit/ui/button';
|
import { Button } from '@kit/ui/button';
|
||||||
import { Heading } from '@kit/ui/heading';
|
import { Heading } from '@kit/ui/heading';
|
||||||
import { If } from '@kit/ui/if';
|
import { If } from '@kit/ui/if';
|
||||||
import { cn } from '@kit/ui/utils';
|
import { cn } from '@kit/ui/utils';
|
||||||
|
|
||||||
import type { ProcessedDocumentationPage } from '~/(marketing)/docs/_lib/build-documentation-tree';
|
|
||||||
|
|
||||||
const DocsNavLink: React.FC<{
|
const DocsNavLink: React.FC<{
|
||||||
label: string;
|
label: string;
|
||||||
url: string;
|
url: string;
|
||||||
level: number;
|
level: number;
|
||||||
activePath: string;
|
activePath: string;
|
||||||
collapsible: boolean;
|
}> = ({ label, url, level, activePath }) => {
|
||||||
collapsed: boolean;
|
|
||||||
toggleCollapsed: () => void;
|
|
||||||
}> = ({
|
|
||||||
label,
|
|
||||||
url,
|
|
||||||
level,
|
|
||||||
activePath,
|
|
||||||
collapsible,
|
|
||||||
collapsed,
|
|
||||||
toggleCollapsed,
|
|
||||||
}) => {
|
|
||||||
const isCurrent = url == activePath;
|
const isCurrent = url == activePath;
|
||||||
const isFirstLevel = level === 0;
|
const isFirstLevel = level === 0;
|
||||||
|
|
||||||
@@ -39,76 +27,51 @@ const DocsNavLink: React.FC<{
|
|||||||
<div className={getNavLinkClassName(isCurrent, isFirstLevel)}>
|
<div className={getNavLinkClassName(isCurrent, isFirstLevel)}>
|
||||||
<Link
|
<Link
|
||||||
className="flex h-full max-w-full grow items-center space-x-2"
|
className="flex h-full max-w-full grow items-center space-x-2"
|
||||||
href={`/docs/${url}`}
|
href={url}
|
||||||
>
|
>
|
||||||
<span className="block max-w-full truncate">{label}</span>
|
<span className="block max-w-full truncate">{label}</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{collapsible && (
|
|
||||||
<button
|
|
||||||
aria-label="Toggle children"
|
|
||||||
onClick={toggleCollapsed}
|
|
||||||
className="mr-2 shrink-0 px-2 py-1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`block w-2.5 ${collapsed ? '-rotate-90 transform' : ''}`}
|
|
||||||
>
|
|
||||||
<ChevronDown className="h-4 w-4" />
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Node: React.FC<{
|
const Node: React.FC<{
|
||||||
node: ProcessedDocumentationPage;
|
node: Cms.ContentItem;
|
||||||
level: number;
|
level: number;
|
||||||
activePath: string;
|
activePath: string;
|
||||||
}> = ({ node, level, activePath }) => {
|
}> = ({ node, level, activePath }) => {
|
||||||
const [collapsed, setCollapsed] = useState<boolean>(node.collapsed ?? false);
|
|
||||||
const toggleCollapsed = () => setCollapsed(!collapsed);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
activePath == node.resolvedPath ||
|
|
||||||
node.children.map((_) => _.resolvedPath).includes(activePath)
|
|
||||||
) {
|
|
||||||
setCollapsed(false);
|
|
||||||
}
|
|
||||||
}, [activePath, node.children, node.resolvedPath]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DocsNavLink
|
<DocsNavLink
|
||||||
label={node.label}
|
label={node.title}
|
||||||
url={node.resolvedPath}
|
url={node.url}
|
||||||
level={level}
|
level={level}
|
||||||
activePath={activePath}
|
activePath={activePath}
|
||||||
collapsible={node.collapsible}
|
|
||||||
collapsed={collapsed}
|
|
||||||
toggleCollapsed={toggleCollapsed}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{node.children.length > 0 && !collapsed && (
|
{(node.children ?? []).length > 0 && (
|
||||||
<Tree tree={node.children} level={level + 1} activePath={activePath} />
|
<Tree
|
||||||
|
pages={node.children ?? []}
|
||||||
|
level={level + 1}
|
||||||
|
activePath={activePath}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function Tree({
|
function Tree({
|
||||||
tree,
|
pages,
|
||||||
level,
|
level,
|
||||||
activePath,
|
activePath,
|
||||||
}: {
|
}: {
|
||||||
tree: ProcessedDocumentationPage[];
|
pages: Cms.ContentItem[];
|
||||||
level: number;
|
level: number;
|
||||||
activePath: string;
|
activePath: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={cn('w-full space-y-2.5 pl-3', level > 0 ? 'border-l' : '')}>
|
<div className={cn('w-full space-y-2.5 pl-3', level > 0 ? 'border-l' : '')}>
|
||||||
{tree.map((treeNode, index) => (
|
{pages.map((treeNode, index) => (
|
||||||
<Node
|
<Node
|
||||||
key={index}
|
key={index}
|
||||||
node={treeNode}
|
node={treeNode}
|
||||||
@@ -120,11 +83,7 @@ function Tree({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DocsNavigation({
|
export function DocsNavigation({ pages }: { pages: Cms.ContentItem[] }) {
|
||||||
tree,
|
|
||||||
}: {
|
|
||||||
tree: ProcessedDocumentationPage[];
|
|
||||||
}) {
|
|
||||||
const activePath = usePathname().replace('/docs/', '');
|
const activePath = usePathname().replace('/docs/', '');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -135,11 +94,14 @@ export default function DocsNavigation({
|
|||||||
}}
|
}}
|
||||||
className="sticky top-2 hidden w-80 shrink-0 border-r p-4 lg:flex"
|
className="sticky top-2 hidden w-80 shrink-0 border-r p-4 lg:flex"
|
||||||
>
|
>
|
||||||
<Tree tree={tree} level={0} activePath={activePath} />
|
<Tree pages={pages} level={0} activePath={activePath} />
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<div className={'lg:hidden'}>
|
<div className={'lg:hidden'}>
|
||||||
<FloatingDocumentationNavigation tree={tree} activePath={activePath} />
|
<FloatingDocumentationNavigation
|
||||||
|
pages={pages}
|
||||||
|
activePath={activePath}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -159,10 +121,10 @@ function getNavLinkClassName(isCurrent: boolean, isFirstLevel: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function FloatingDocumentationNavigation({
|
function FloatingDocumentationNavigation({
|
||||||
tree,
|
pages,
|
||||||
activePath,
|
activePath,
|
||||||
}: React.PropsWithChildren<{
|
}: React.PropsWithChildren<{
|
||||||
tree: ProcessedDocumentationPage[];
|
pages: Cms.ContentItem[];
|
||||||
activePath: string;
|
activePath: string;
|
||||||
}>) {
|
}>) {
|
||||||
const body = useMemo(() => {
|
const body = useMemo(() => {
|
||||||
@@ -210,7 +172,7 @@ function FloatingDocumentationNavigation({
|
|||||||
>
|
>
|
||||||
<Heading level={1}>Table of Contents</Heading>
|
<Heading level={1}>Table of Contents</Heading>
|
||||||
|
|
||||||
<Tree tree={tree} level={0} activePath={activePath} />
|
<Tree pages={pages} level={0} activePath={activePath} />
|
||||||
</div>
|
</div>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import type { DocumentationPage } from 'contentlayer/generated';
|
|
||||||
|
|
||||||
import { If } from '@kit/ui/if';
|
import { If } from '@kit/ui/if';
|
||||||
import { cn } from '@kit/ui/utils';
|
import { cn } from '@kit/ui/utils';
|
||||||
|
|
||||||
@@ -10,7 +8,10 @@ export function DocumentationPageLink({
|
|||||||
before,
|
before,
|
||||||
after,
|
after,
|
||||||
}: React.PropsWithChildren<{
|
}: React.PropsWithChildren<{
|
||||||
page: DocumentationPage;
|
page: {
|
||||||
|
url: string;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
before?: React.ReactNode;
|
before?: React.ReactNode;
|
||||||
after?: React.ReactNode;
|
after?: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
@@ -23,7 +24,7 @@ export function DocumentationPageLink({
|
|||||||
'justify-end self-end': after,
|
'justify-end self-end': after,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
href={`/docs/${page.resolvedPath}`}
|
href={page.url}
|
||||||
>
|
>
|
||||||
<If condition={before}>{(node) => <>{node}</>}</If>
|
<If condition={before}>{(node) => <>{node}</>}</If>
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
import { cache } from 'react';
|
|
||||||
|
|
||||||
import type { DocumentationPage } from 'contentlayer/generated';
|
|
||||||
|
|
||||||
export interface ProcessedDocumentationPage extends DocumentationPage {
|
|
||||||
collapsible: boolean;
|
|
||||||
pathSegments: string[];
|
|
||||||
nextPage: ProcessedDocumentationPage | DocumentationPage | undefined;
|
|
||||||
previousPage: ProcessedDocumentationPage | DocumentationPage | undefined;
|
|
||||||
children: DocsTree;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DocsTree = ProcessedDocumentationPage[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a tree of documentation pages from a flat list of pages with path segments
|
|
||||||
* @param docs
|
|
||||||
* @param parentPathNames
|
|
||||||
*/
|
|
||||||
export const buildDocumentationTree = cache(
|
|
||||||
(docs: DocumentationPage[], parentPathNames: string[] = []): DocsTree => {
|
|
||||||
const level = parentPathNames.length;
|
|
||||||
|
|
||||||
const pages = docs
|
|
||||||
.filter(
|
|
||||||
(_) =>
|
|
||||||
_.pathSegments.length === level + 1 &&
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
||||||
_.pathSegments
|
|
||||||
.map(({ pathName }: { pathName: string }) => pathName)
|
|
||||||
.join('/')
|
|
||||||
.startsWith(parentPathNames.join('/')),
|
|
||||||
)
|
|
||||||
.sort(
|
|
||||||
(a, b) => a.pathSegments[level].order - b.pathSegments[level].order,
|
|
||||||
);
|
|
||||||
|
|
||||||
return pages.map((doc, index) => {
|
|
||||||
const children = buildDocumentationTree(
|
|
||||||
docs,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
||||||
doc.pathSegments.map(({ pathName }: { pathName: string }) => pathName),
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...doc,
|
|
||||||
pathSegments: doc.pathSegments || ([] as string[]),
|
|
||||||
collapsible: children.length > 0,
|
|
||||||
nextPage: children[0] ?? pages[index + 1],
|
|
||||||
previousPage: pages[index - 1],
|
|
||||||
children,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import { cache } from 'react';
|
|
||||||
|
|
||||||
import type { DocumentationPage } from 'contentlayer/generated';
|
|
||||||
import { allDocumentationPages } from 'contentlayer/generated';
|
|
||||||
|
|
||||||
import { buildDocumentationTree } from './build-documentation-tree';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a specific documentation page from the page tree by its path.
|
|
||||||
*
|
|
||||||
* @param {string} pagePath - The path of the documentation page to retrieve.
|
|
||||||
* @returns {DocumentationPageWithChildren | undefined} The documentation page found in the tree, if any.
|
|
||||||
*/
|
|
||||||
export const getDocumentationPageTree = cache((pagePath: string) => {
|
|
||||||
const tree = buildDocumentationTree(allDocumentationPages);
|
|
||||||
|
|
||||||
type DocumentationPageWithChildren = DocumentationPage & {
|
|
||||||
previousPage?: DocumentationPage | null;
|
|
||||||
nextPage?: DocumentationPage | null;
|
|
||||||
children?: DocumentationPage[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const findPageInTree = (
|
|
||||||
pages: DocumentationPageWithChildren[],
|
|
||||||
path: string,
|
|
||||||
): DocumentationPageWithChildren | undefined => {
|
|
||||||
for (const page of pages) {
|
|
||||||
if (page.resolvedPath === path) {
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasChildren = page.children && page.children.length > 0;
|
|
||||||
|
|
||||||
if (hasChildren) {
|
|
||||||
const foundPage = findPageInTree(page.children ?? [], path);
|
|
||||||
|
|
||||||
if (foundPage) {
|
|
||||||
return foundPage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return findPageInTree(tree, pagePath);
|
|
||||||
});
|
|
||||||
@@ -1,15 +1,22 @@
|
|||||||
import { allDocumentationPages } from 'contentlayer/generated';
|
import { createCmsClient } from '@kit/cms';
|
||||||
|
|
||||||
import DocsNavigation from '~/(marketing)/docs/_components/docs-navigation';
|
import { DocsNavigation } from '~/(marketing)/docs/_components/docs-navigation';
|
||||||
import { buildDocumentationTree } from '~/(marketing)/docs/_lib/build-documentation-tree';
|
|
||||||
|
|
||||||
function DocsLayout({ children }: React.PropsWithChildren) {
|
async function DocsLayout({ children }: React.PropsWithChildren) {
|
||||||
const tree = buildDocumentationTree(allDocumentationPages);
|
const cms = await createCmsClient();
|
||||||
|
|
||||||
|
const pages = await cms.getContentItems({
|
||||||
|
type: 'page',
|
||||||
|
categories: ['documentation'],
|
||||||
|
depth: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(pages);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'container mx-auto'}>
|
<div className={'container mx-auto'}>
|
||||||
<div className={'flex'}>
|
<div className={'flex'}>
|
||||||
<DocsNavigation tree={tree} />
|
<DocsNavigation pages={pages} />
|
||||||
|
|
||||||
<div className={'flex w-full flex-col items-center'}>{children}</div>
|
<div className={'flex w-full flex-col items-center'}>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { allDocumentationPages } from 'contentlayer/generated';
|
import { createCmsClient } from '@kit/cms';
|
||||||
|
|
||||||
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
||||||
import { DocsCards } from '~/(marketing)/docs/_components/docs-cards';
|
import { DocsCards } from '~/(marketing)/docs/_components/docs-cards';
|
||||||
import { buildDocumentationTree } from '~/(marketing)/docs/_lib/build-documentation-tree';
|
|
||||||
import appConfig from '~/config/app.config';
|
import appConfig from '~/config/app.config';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
|
||||||
@@ -10,8 +9,16 @@ export const metadata = {
|
|||||||
title: `Documentation - ${appConfig.name}`,
|
title: `Documentation - ${appConfig.name}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
function DocsPage() {
|
async function DocsPage() {
|
||||||
const tree = buildDocumentationTree(allDocumentationPages);
|
const client = await createCmsClient();
|
||||||
|
|
||||||
|
const docs = await client.getContentItems({
|
||||||
|
type: 'page',
|
||||||
|
categories: ['documentation'],
|
||||||
|
depth: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(docs);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'my-8 flex flex-col space-y-16'}>
|
<div className={'my-8 flex flex-col space-y-16'}>
|
||||||
@@ -21,7 +28,7 @@ function DocsPage() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<DocsCards pages={tree ?? []} />
|
<DocsCards pages={docs} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,17 +1,26 @@
|
|||||||
import { invariant } from '@epic-web/invariant';
|
import { invariant } from '@epic-web/invariant';
|
||||||
import { allDocumentationPages, allPosts } from 'contentlayer/generated';
|
|
||||||
import { getServerSideSitemap } from 'next-sitemap';
|
import { getServerSideSitemap } from 'next-sitemap';
|
||||||
|
|
||||||
|
import { createCmsClient } from '@kit/cms';
|
||||||
|
|
||||||
import appConfig from '~/config/app.config';
|
import appConfig from '~/config/app.config';
|
||||||
|
|
||||||
invariant(appConfig.url, 'No NEXT_PUBLIC_SITE_URL environment variable found');
|
invariant(appConfig.url, 'No NEXT_PUBLIC_SITE_URL environment variable found');
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const urls = getSiteUrls();
|
const urls = getSiteUrls();
|
||||||
const posts = getPostsSitemap();
|
const client = await createCmsClient();
|
||||||
const docs = getDocsSitemap();
|
const contentItems = await client.getContentItems();
|
||||||
|
|
||||||
return getServerSideSitemap([...urls, ...posts, ...docs]);
|
return getServerSideSitemap([
|
||||||
|
...urls,
|
||||||
|
...contentItems.map((item) => {
|
||||||
|
return {
|
||||||
|
loc: new URL(item.url, appConfig.url).href,
|
||||||
|
lastmod: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSiteUrls() {
|
function getSiteUrls() {
|
||||||
@@ -24,21 +33,3 @@ function getSiteUrls() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPostsSitemap() {
|
|
||||||
return allPosts.map((post) => {
|
|
||||||
return {
|
|
||||||
loc: new URL(post.url, appConfig.url).href,
|
|
||||||
lastmod: new Date().toISOString(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDocsSitemap() {
|
|
||||||
return allDocumentationPages.map((page) => {
|
|
||||||
return {
|
|
||||||
loc: new URL(page.url, appConfig.url).href,
|
|
||||||
lastmod: new Date().toISOString(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
title: Running the Next.js Server
|
|
||||||
label: Next.js
|
|
||||||
description: Learn how to run the Next.js server on your local machine.
|
|
||||||
---
|
|
||||||
|
|
||||||
First, we can run the Next.js Server by running the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
If everything goes well, your server should be running at
|
|
||||||
[http://localhost:3000](http://localhost:3000).
|
|
||||||
|
|
||||||
With the server running, we can now set up our Supabase containers using
|
|
||||||
Docker. Jump to the next section to learn how to do that.
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
---
|
|
||||||
title: Running the Supabase Containers
|
|
||||||
label: Supabase
|
|
||||||
description: Running the Supabase containers locally for development
|
|
||||||
---
|
|
||||||
|
|
||||||
Before we can run the Supabase local environment, we need to run Docker, as Supabase uses it for running its local environment.
|
|
||||||
|
|
||||||
You can use Docker Desktop, Colima, OrbStack, or any other Docker-compatible solution.
|
|
||||||
|
|
||||||
### Running the Supabase Environment
|
|
||||||
|
|
||||||
First, let's run the Supabase environment, which will spin up a local
|
|
||||||
instance using Docker. We can do this by running the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run supabase:start
|
|
||||||
```
|
|
||||||
|
|
||||||
Additionally, it imports the default seed data. We use it this data to
|
|
||||||
populate the database with some initial data and execute the E2E tests.
|
|
||||||
|
|
||||||
After running the command above, you will be able to access the Supabase
|
|
||||||
Studio UI at [http://localhost:54323/](http://localhost:54323/).
|
|
||||||
|
|
||||||
### Adding the Supabase Keys to the Environment Variables
|
|
||||||
|
|
||||||
If this is the first time you run this command, we will need to get the
|
|
||||||
Supabase keys and add them to our local environment variables configuration
|
|
||||||
file `.env`.
|
|
||||||
|
|
||||||
When running the command, we will see a message like this:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
> supabase start
|
|
||||||
|
|
||||||
Applying migration 20221215192558_schema.sql...
|
|
||||||
Seeding data supabase/seed.sql...
|
|
||||||
Started supabase local development setup.
|
|
||||||
|
|
||||||
API URL: http://localhost:54321
|
|
||||||
DB URL: postgresql://postgres:postgres@localhost:54322/postgres
|
|
||||||
Studio URL: http://localhost:54323
|
|
||||||
Inbucket URL: http://localhost:54324
|
|
||||||
JWT secret: super-secret-jwt-token-with-at-least-32-characters-long
|
|
||||||
anon key: ****************************************************
|
|
||||||
service_role key: ****************************************************
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, we need to copy the `anon key` and `service_role key` values and add
|
|
||||||
them to the `.env` file:
|
|
||||||
|
|
||||||
```
|
|
||||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=****************************************************
|
|
||||||
SUPABASE_SERVICE_ROLE_KEY=****************************************************
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Running the Stripe CLI
|
|
||||||
|
|
||||||
Run the Stripe CLI with the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run stripe:listen
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Add the Stripe Webhooks Key to your environment file
|
|
||||||
|
|
||||||
If this is the first time you run this command, you will need to copy the Webhooks key printed on the console and add it to your development environment variables file:
|
|
||||||
|
|
||||||
```bash title=".env.development"
|
|
||||||
STRIPE_WEBHOOKS_KEY=<PASTE_KEY_HERE>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Signing In for the first time
|
|
||||||
|
|
||||||
You should now be able to sign in. To quickly get started, use the following credentials:
|
|
||||||
|
|
||||||
```
|
|
||||||
email = test@makerkit.dev
|
|
||||||
password = testingpassword
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Email Confirmations
|
|
||||||
|
|
||||||
When signing up, Supabase sends an email confirmation to a testing account. You can access the InBucket testing emails [using the following link](http://localhost:54324/monitor), and can follow the links to complete the sign up process.
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
title: Running the Stripe CLI for Webhooks
|
|
||||||
label: Stripe
|
|
||||||
description: How to run the Stripe CLI for Webhooks in a local development environment
|
|
||||||
---
|
|
||||||
|
|
||||||
Run the Stripe CLI with the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run stripe:listen
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Add the Stripe Webhooks Key to your environment file
|
|
||||||
|
|
||||||
If this is the first time you run this command, you will need to copy the Webhooks key printed on the console and add it to your development environment variables file:
|
|
||||||
|
|
||||||
```bash title=".env.development"
|
|
||||||
STRIPE_WEBHOOKS_KEY=<PASTE_KEY_HERE>
|
|
||||||
```
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
title: Running the Application
|
|
||||||
label: Running the Application
|
|
||||||
description: How to run the application in development mode
|
|
||||||
---
|
|
||||||
|
|
||||||
After installing the modules, we can finally run the
|
|
||||||
application in development mode.
|
|
||||||
|
|
||||||
We need to execute two commands (and an optional one for Stripe):
|
|
||||||
|
|
||||||
1. **Next.js Server**: the first command is for running the Next.js server
|
|
||||||
2. **Supabase Environment**: the second command is for running the Supabase
|
|
||||||
environment with Docker
|
|
||||||
3. **Stripe CLI**: finally, the Stripe CLI is needed to dispatch webhooks to
|
|
||||||
our local server (optional, only needed when interacting with Stripe)
|
|
||||||
|
|
||||||
## About this Documentation
|
|
||||||
|
|
||||||
This documentation complements the Supabase one and is not meant to be a replacement. We recommend reading the Supabase documentation to get a better understanding of the Supabase concepts and how to use it.
|
|
||||||
@@ -16,7 +16,8 @@ const INTERNAL_PACKAGES = [
|
|||||||
'@kit/billing-gateway',
|
'@kit/billing-gateway',
|
||||||
'@kit/stripe',
|
'@kit/stripe',
|
||||||
'@kit/email-templates',
|
'@kit/email-templates',
|
||||||
'@kit/database-webhooks'
|
'@kit/database-webhooks',
|
||||||
|
'@kit/cms'
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
"@kit/supabase": "workspace:^",
|
"@kit/supabase": "workspace:^",
|
||||||
"@kit/team-accounts": "workspace:^",
|
"@kit/team-accounts": "workspace:^",
|
||||||
"@kit/ui": "workspace:^",
|
"@kit/ui": "workspace:^",
|
||||||
|
"@kit/cms": "workspace:^",
|
||||||
"@next/mdx": "^14.1.4",
|
"@next/mdx": "^14.1.4",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@supabase/ssr": "^0.1.0",
|
"@supabase/ssr": "^0.1.0",
|
||||||
@@ -37,13 +38,11 @@
|
|||||||
"@tanstack/react-query": "5.28.6",
|
"@tanstack/react-query": "5.28.6",
|
||||||
"@tanstack/react-query-next-experimental": "^5.28.9",
|
"@tanstack/react-query-next-experimental": "^5.28.9",
|
||||||
"@tanstack/react-table": "^8.15.0",
|
"@tanstack/react-table": "^8.15.0",
|
||||||
"contentlayer": "0.3.4",
|
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"edge-csrf": "^1.0.9",
|
"edge-csrf": "^1.0.9",
|
||||||
"i18next": "^23.10.1",
|
"i18next": "^23.10.1",
|
||||||
"i18next-resources-to-backend": "^1.2.0",
|
"i18next-resources-to-backend": "^1.2.0",
|
||||||
"next": "v14.2.0-canary.49",
|
"next": "v14.2.0-canary.50",
|
||||||
"next-contentlayer": "0.3.4",
|
|
||||||
"next-sitemap": "^4.2.3",
|
"next-sitemap": "^4.2.3",
|
||||||
"next-themes": "0.3.0",
|
"next-themes": "0.3.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
@@ -51,8 +50,6 @@
|
|||||||
"react-hook-form": "^7.51.2",
|
"react-hook-form": "^7.51.2",
|
||||||
"react-i18next": "^14.1.0",
|
"react-i18next": "^14.1.0",
|
||||||
"recharts": "^2.12.3",
|
"recharts": "^2.12.3",
|
||||||
"rehype-autolink-headings": "^7.1.0",
|
|
||||||
"rehype-slug": "^6.0.0",
|
|
||||||
"sonner": "^1.4.41",
|
"sonner": "^1.4.41",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
"~/*": ["./app/*"],
|
"~/*": ["./app/*"],
|
||||||
"~/config/*": ["./config/*"],
|
"~/config/*": ["./config/*"],
|
||||||
"~/components/*": ["./components/*"],
|
"~/components/*": ["./components/*"],
|
||||||
"~/lib/*": ["./lib/*"],
|
"~/lib/*": ["./lib/*"]
|
||||||
"contentlayer/generated": ["./.contentlayer/generated"]
|
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
@@ -22,7 +21,7 @@
|
|||||||
"*.ts",
|
"*.ts",
|
||||||
"*.tsx",
|
"*.tsx",
|
||||||
"*.mjs",
|
"*.mjs",
|
||||||
"config/**/*.ts",
|
"./config/**/*.ts",
|
||||||
"components/**/*.{tsx|ts}",
|
"components/**/*.{tsx|ts}",
|
||||||
"lib/**/*.ts",
|
"lib/**/*.ts",
|
||||||
"app"
|
"app"
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*",
|
"packages/*",
|
||||||
"packages/features/*",
|
"packages/features/*",
|
||||||
|
"packages/cms/*",
|
||||||
"tooling/*",
|
"tooling/*",
|
||||||
"supabase"
|
"supabase"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export function CurrentPlanCard({
|
|||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
<CardContent className={'space-y-2.5 text-sm'}>
|
<CardContent className={'space-y-3 text-sm'}>
|
||||||
<div className={'flex flex-col space-y-1'}>
|
<div className={'flex flex-col space-y-1'}>
|
||||||
<div className={'flex items-center space-x-2 text-lg font-semibold'}>
|
<div className={'flex items-center space-x-2 text-lg font-semibold'}>
|
||||||
<BadgeCheck
|
<BadgeCheck
|
||||||
@@ -85,85 +85,58 @@ export function CurrentPlanCard({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
{/*
|
||||||
<CurrentPlanAlert status={subscription.status} />
|
Only show the alert if the subscription requires action
|
||||||
</div>
|
(e.g. trial ending soon, subscription canceled, etc.)
|
||||||
|
*/}
|
||||||
|
<If condition={!subscription.active}>
|
||||||
|
<div>
|
||||||
|
<CurrentPlanAlert status={subscription.status} />
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Accordion type="single" collapsible>
|
<If condition={subscription.status === 'trialing'}>
|
||||||
<AccordionItem value="features">
|
<div className="flex flex-col space-y-0.5">
|
||||||
<AccordionTrigger>
|
<span className="font-semibold">
|
||||||
<Trans i18nKey="billing:planDetails" />
|
<Trans i18nKey="billing:trialEndsOn" />
|
||||||
</AccordionTrigger>
|
</span>
|
||||||
|
|
||||||
<AccordionContent className="space-y-2.5">
|
<div className={'text-muted-foreground'}>
|
||||||
<If condition={subscription.status === 'trialing'}>
|
<span>
|
||||||
<div className="flex flex-col">
|
{subscription.trial_ends_at
|
||||||
<span className="font-medium">
|
? formatDate(subscription.trial_ends_at, 'P')
|
||||||
<Trans i18nKey="billing:trialEndsOn" />
|
: ''}
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
|
||||||
<div className={'text-muted-foreground'}>
|
<If condition={subscription.cancel_at_period_end}>
|
||||||
<span>
|
<div className="flex flex-col space-y-0.5">
|
||||||
{subscription.trial_ends_at
|
<span className="font-semibold">
|
||||||
? formatDate(subscription.trial_ends_at, 'P')
|
<Trans i18nKey="billing:cancelSubscriptionDate" />
|
||||||
: ''}
|
</span>
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</If>
|
|
||||||
|
|
||||||
<If condition={subscription.cancel_at_period_end}>
|
<div className={'text-muted-foreground'}>
|
||||||
<div className="flex flex-col">
|
<span>
|
||||||
<span className="font-medium">
|
{formatDate(subscription.period_ends_at ?? '', 'P')}
|
||||||
<Trans i18nKey="billing:cancelSubscriptionDate" />
|
</span>
|
||||||
</span>
|
</div>
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
|
||||||
<div className={'text-muted-foreground'}>
|
<div className="flex flex-col space-y-0.5">
|
||||||
<span>
|
<span className="font-semibold">
|
||||||
{formatDate(subscription.period_ends_at ?? '', 'P')}
|
<Trans i18nKey="billing:detailsLabel" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</If>
|
|
||||||
|
|
||||||
<div className="flex flex-col space-y-1">
|
<LineItemDetails
|
||||||
<span className="font-semibold">
|
lineItems={productLineItems}
|
||||||
<Trans i18nKey="billing:detailsLabel" />
|
currency={subscription.currency}
|
||||||
</span>
|
selectedInterval={firstLineItem.interval}
|
||||||
|
/>
|
||||||
<LineItemDetails
|
</div>
|
||||||
lineItems={productLineItems}
|
|
||||||
currency={subscription.currency}
|
|
||||||
selectedInterval={firstLineItem.interval}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col space-y-1">
|
|
||||||
<span className="font-semibold">
|
|
||||||
<Trans i18nKey="billing:featuresLabel" />
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<ul className={'flex flex-col space-y-0.5'}>
|
|
||||||
{product.features.map((item) => {
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
key={item}
|
|
||||||
className="flex items-center space-x-0.5"
|
|
||||||
>
|
|
||||||
<CheckCircle2 className="h-4 text-green-500" />
|
|
||||||
|
|
||||||
<span className={'text-muted-foreground'}>
|
|
||||||
<Trans i18nKey={item} defaults={item} />
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
9
packages/cms/contentlayer/README.md
Normal file
9
packages/cms/contentlayer/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# CMS/Contentlayer - @kit/contentlayer
|
||||||
|
|
||||||
|
Implementation of the CMS layer using the [Contentlayer](https://contentlayer.dev) library.
|
||||||
|
|
||||||
|
This implementation is used when the host app's environment variable is set as:
|
||||||
|
|
||||||
|
```
|
||||||
|
CMS_TYPE=contentlayer
|
||||||
|
```
|
||||||
@@ -4,6 +4,9 @@ date: 2021-12-24
|
|||||||
live: false
|
live: false
|
||||||
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||||
image: /assets/images/posts/lorem-ipsum.webp
|
image: /assets/images/posts/lorem-ipsum.webp
|
||||||
|
author: John Doe
|
||||||
|
categories:
|
||||||
|
- posts
|
||||||
---
|
---
|
||||||
|
|
||||||
## Fecerat avis invenio mentis
|
## Fecerat avis invenio mentis
|
||||||
@@ -19,6 +19,11 @@ export const Post = defineDocumentType(() => ({
|
|||||||
description: 'The date of the post',
|
description: 'The date of the post',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
author: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'The author of the post',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
live: {
|
live: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
description: 'Whether the post is live or not',
|
description: 'Whether the post is live or not',
|
||||||
@@ -33,12 +38,22 @@ export const Post = defineDocumentType(() => ({
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The description of the post',
|
description: 'The description of the post',
|
||||||
},
|
},
|
||||||
|
tags: {
|
||||||
|
type: 'list',
|
||||||
|
required: false,
|
||||||
|
of: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
categories: {
|
||||||
|
type: 'list',
|
||||||
|
required: false,
|
||||||
|
of: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computedFields: {
|
computedFields: {
|
||||||
url: {
|
|
||||||
type: 'string',
|
|
||||||
resolve: (post) => `/blog/${getSlug(post._raw.sourceFileName)}`,
|
|
||||||
},
|
|
||||||
readingTime: {
|
readingTime: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
resolve: (post) => calculateReadingTime(post.body.raw),
|
resolve: (post) => calculateReadingTime(post.body.raw),
|
||||||
@@ -47,6 +62,10 @@ export const Post = defineDocumentType(() => ({
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
resolve: (post) => getSlug(post._raw.sourceFileName),
|
resolve: (post) => getSlug(post._raw.sourceFileName),
|
||||||
},
|
},
|
||||||
|
url: {
|
||||||
|
type: 'string',
|
||||||
|
resolve: (post) => `/blog/${getSlug(post._raw.sourceFileName)}`,
|
||||||
|
},
|
||||||
structuredData: {
|
structuredData: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
resolve: (doc) => ({
|
resolve: (doc) => ({
|
||||||
@@ -57,7 +76,6 @@ export const Post = defineDocumentType(() => ({
|
|||||||
dateModified: doc.date,
|
dateModified: doc.date,
|
||||||
description: doc.description,
|
description: doc.description,
|
||||||
image: [siteUrl, doc.image].join(''),
|
image: [siteUrl, doc.image].join(''),
|
||||||
url: [siteUrl, 'blog', doc._raw.flattenedPath].join('/'),
|
|
||||||
author: {
|
author: {
|
||||||
'@type': 'Organization',
|
'@type': 'Organization',
|
||||||
name: `Makerkit`,
|
name: `Makerkit`,
|
||||||
@@ -82,43 +100,30 @@ export const DocumentationPage = defineDocumentType(() => ({
|
|||||||
description: 'The label of the page in the sidebar',
|
description: 'The label of the page in the sidebar',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
cardCTA: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'The label of the CTA link on the card',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
description: {
|
description: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The description of the post',
|
description: 'The description of the post',
|
||||||
},
|
},
|
||||||
show_child_cards: {
|
tags: {
|
||||||
type: 'boolean',
|
type: 'list',
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
collapsible: {
|
|
||||||
type: 'boolean',
|
|
||||||
required: false,
|
required: false,
|
||||||
default: false,
|
of: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
collapsed: {
|
categories: {
|
||||||
type: 'boolean',
|
type: 'list',
|
||||||
required: false,
|
required: false,
|
||||||
default: false,
|
of: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computedFields: {
|
computedFields: {
|
||||||
url: {
|
|
||||||
type: 'string',
|
|
||||||
resolve: (post) => `/blog/${getSlug(post._raw.sourceFileName)}`,
|
|
||||||
},
|
|
||||||
readingTime: {
|
readingTime: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
resolve: (post) => calculateReadingTime(post.body.raw),
|
resolve: (post) => calculateReadingTime(post.body.raw),
|
||||||
},
|
},
|
||||||
slug: {
|
|
||||||
type: 'string',
|
|
||||||
resolve: (post) => getSlug(post._raw.sourceFileName),
|
|
||||||
},
|
|
||||||
structuredData: {
|
structuredData: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
resolve: (doc) => ({
|
resolve: (doc) => ({
|
||||||
@@ -150,13 +155,24 @@ export const DocumentationPage = defineDocumentType(() => ({
|
|||||||
type: 'json',
|
type: 'json',
|
||||||
resolve: (doc) => getPathSegments(doc).map(getMetaFromFolderName),
|
resolve: (doc) => getPathSegments(doc).map(getMetaFromFolderName),
|
||||||
},
|
},
|
||||||
resolvedPath: {
|
slug: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
resolve: (doc) => {
|
resolve: (doc) =>
|
||||||
return getPathSegments(doc)
|
getPathSegments(doc)
|
||||||
.map(getMetaFromFolderName)
|
.map(getMetaFromFolderName)
|
||||||
.map(({ pathName }) => pathName)
|
.map(({ pathName }) => pathName)
|
||||||
.join('/');
|
.join('/'),
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: 'string',
|
||||||
|
resolve: (doc) => {
|
||||||
|
return (
|
||||||
|
'/docs/' +
|
||||||
|
getPathSegments(doc)
|
||||||
|
.map(getMetaFromFolderName)
|
||||||
|
.map(({ pathName }) => pathName)
|
||||||
|
.join('/')
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
45
packages/cms/contentlayer/package.json
Normal file
45
packages/cms/contentlayer/package.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"name": "@kit/contentlayer",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"scripts": {
|
||||||
|
"clean": "git clean -xdf .turbo node_modules",
|
||||||
|
"format": "prettier --check \"**/*.{ts,tsx}\"",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"build": "contentlayer build"
|
||||||
|
},
|
||||||
|
"prettier": "@kit/prettier-config",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"contentlayer": "0.3.4",
|
||||||
|
"next-contentlayer": "0.3.4",
|
||||||
|
"rehype-slug": "^6.0.0",
|
||||||
|
"rehype-autolink-headings": "^6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@kit/cms": "workspace:^",
|
||||||
|
"@kit/ui": "workspace:^"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@kit/eslint-config": "workspace:*",
|
||||||
|
"@kit/prettier-config": "workspace:*",
|
||||||
|
"@kit/tsconfig": "workspace:*"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"extends": [
|
||||||
|
"@kit/eslint-config/base",
|
||||||
|
"@kit/eslint-config/react"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"typesVersions": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
192
packages/cms/contentlayer/src/client.ts
Normal file
192
packages/cms/contentlayer/src/client.ts
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
import { Cms, CmsClient } from '@kit/cms';
|
||||||
|
|
||||||
|
import type { DocumentationPage, Post } from '../.contentlayer/generated';
|
||||||
|
|
||||||
|
async function getAllContentItems() {
|
||||||
|
const { allDocumentationPages, allPosts } = await import(
|
||||||
|
'../.contentlayer/generated'
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
...allPosts.map((item) => {
|
||||||
|
return { ...item, type: 'post' };
|
||||||
|
}),
|
||||||
|
...allDocumentationPages.map((item) => {
|
||||||
|
return { ...item, type: 'page', categories: ['documentation'] };
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that represents a Content Layer CMS client.
|
||||||
|
* This class implements the base CmsClient class.
|
||||||
|
*
|
||||||
|
* @class ContentlayerClient
|
||||||
|
* @extends {CmsClient}
|
||||||
|
*/
|
||||||
|
export class ContentlayerClient implements CmsClient {
|
||||||
|
async getContentItems(options?: Cms.GetContentItemsOptions) {
|
||||||
|
const allContentItems = await getAllContentItems();
|
||||||
|
const { startOffset, endOffset } = this.getOffset(options);
|
||||||
|
|
||||||
|
const promise = allContentItems
|
||||||
|
.filter((item) => {
|
||||||
|
const tagMatch = options?.tags
|
||||||
|
? item.tags?.some((tag) => options.tags?.includes(tag))
|
||||||
|
: true;
|
||||||
|
|
||||||
|
const categoryMatch = options?.categories
|
||||||
|
? item.categories?.some((category) =>
|
||||||
|
options.categories?.includes(category),
|
||||||
|
)
|
||||||
|
: true;
|
||||||
|
|
||||||
|
const typeMatch = options?.type ? item.type === options.type : true;
|
||||||
|
const path = item._raw.flattenedPath;
|
||||||
|
const splitPath = path.split('/');
|
||||||
|
|
||||||
|
const depthMatch =
|
||||||
|
options?.depth !== undefined
|
||||||
|
? splitPath.length - 1 === options.depth
|
||||||
|
: true;
|
||||||
|
|
||||||
|
return tagMatch && categoryMatch && typeMatch && depthMatch;
|
||||||
|
})
|
||||||
|
.slice(startOffset, endOffset)
|
||||||
|
.map((post) => {
|
||||||
|
const children: Cms.ContentItem[] = [];
|
||||||
|
|
||||||
|
for (const item of allContentItems) {
|
||||||
|
if (item.url.startsWith(post.url + '/')) {
|
||||||
|
children.push(this.mapPost(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.mapPost(post, children);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.resolve(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getContentItemById(id: string) {
|
||||||
|
const allContentItems = await getAllContentItems();
|
||||||
|
const post = allContentItems.find((item) => item.slug === id);
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
const children: Cms.ContentItem[] = [];
|
||||||
|
|
||||||
|
for (const item of allContentItems) {
|
||||||
|
if (item.url.startsWith(post.url + '/')) {
|
||||||
|
children.push(this.mapPost(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(post ? this.mapPost(post, children) : undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCategoryBySlug(slug: string) {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: slug,
|
||||||
|
name: slug,
|
||||||
|
slug,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTagBySlug(slug: string) {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: slug,
|
||||||
|
name: slug,
|
||||||
|
slug,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCategories(options?: Cms.GetCategoriesOptions) {
|
||||||
|
const { startOffset, endOffset } = this.getOffset(options);
|
||||||
|
const allContentItems = await getAllContentItems();
|
||||||
|
|
||||||
|
const categories = allContentItems
|
||||||
|
.filter((item) => {
|
||||||
|
if (options?.type) {
|
||||||
|
return item.type === options.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.slice(startOffset, endOffset)
|
||||||
|
.flatMap((post) => post.categories)
|
||||||
|
.filter((category): category is string => !!category)
|
||||||
|
.map((category) => ({
|
||||||
|
id: category,
|
||||||
|
name: category,
|
||||||
|
slug: category,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return Promise.resolve(categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTags(options?: Cms.GetTagsOptions) {
|
||||||
|
const { startOffset, endOffset } = this.getOffset(options);
|
||||||
|
const allContentItems = await getAllContentItems();
|
||||||
|
|
||||||
|
const tags = allContentItems
|
||||||
|
.filter((item) => {
|
||||||
|
if (options?.type) {
|
||||||
|
return item.type === options.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.slice(startOffset, endOffset)
|
||||||
|
.flatMap((post) => post.tags)
|
||||||
|
.filter((tag): tag is string => !!tag)
|
||||||
|
.map((tag) => ({
|
||||||
|
id: tag,
|
||||||
|
name: tag,
|
||||||
|
slug: tag,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return Promise.resolve(tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getOffset(options?: { offset?: number; limit?: number }) {
|
||||||
|
const startOffset = options?.offset ?? 0;
|
||||||
|
const endOffset = options?.limit ? startOffset + options.limit : undefined;
|
||||||
|
|
||||||
|
return { startOffset, endOffset };
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapPost(
|
||||||
|
post: Post | DocumentationPage,
|
||||||
|
children: Array<Post | DocumentationPage> = [],
|
||||||
|
): Cms.ContentItem {
|
||||||
|
console.log(post);
|
||||||
|
return {
|
||||||
|
id: post.slug,
|
||||||
|
title: post.title,
|
||||||
|
description: post.description ?? '',
|
||||||
|
content: post.body?.code,
|
||||||
|
image: 'image' in post ? post.image : undefined,
|
||||||
|
publishedAt: 'date' in post ? new Date(post.date) : new Date(),
|
||||||
|
parentId: 'parentId' in post ? post.parentId : undefined,
|
||||||
|
url: post.url,
|
||||||
|
slug: post.slug,
|
||||||
|
author: 'author' in post ? post.author : '',
|
||||||
|
children: children.map((child) => this.mapPost(child)),
|
||||||
|
categories:
|
||||||
|
post.categories?.map((category) => ({
|
||||||
|
id: category,
|
||||||
|
name: category,
|
||||||
|
slug: category,
|
||||||
|
})) ?? [],
|
||||||
|
tags:
|
||||||
|
post.tags?.map((tag) => ({
|
||||||
|
id: tag,
|
||||||
|
name: tag,
|
||||||
|
slug: tag,
|
||||||
|
})) ?? [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
5
packages/cms/contentlayer/src/content-renderer.tsx
Normal file
5
packages/cms/contentlayer/src/content-renderer.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Mdx } from './mdx/mdx-renderer';
|
||||||
|
|
||||||
|
export function ContentRenderer(props: { content: string }) {
|
||||||
|
return <Mdx code={props.content} />;
|
||||||
|
}
|
||||||
3
packages/cms/contentlayer/src/index.ts
Normal file
3
packages/cms/contentlayer/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './client';
|
||||||
|
export * from './mdx/mdx-renderer';
|
||||||
|
export * from './content-renderer';
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import type { MDXComponents } from 'mdx/types';
|
import type { MDXComponents as MDXComponentsType } from 'mdx/types';
|
||||||
import { getMDXComponent } from 'next-contentlayer/hooks';
|
import { getMDXComponent } from 'next-contentlayer/hooks';
|
||||||
|
|
||||||
import Components from './mdx-components';
|
import { MDXComponents } from '@kit/ui/mdx-components';
|
||||||
|
|
||||||
// @ts-ignore: ignore weird error
|
// @ts-ignore: ignore weird error
|
||||||
import styles from './mdx-renderer.module.css';
|
import styles from './mdx-renderer.module.css';
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ export function Mdx({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.MDX}>
|
<div className={styles.MDX}>
|
||||||
<Component components={Components as unknown as MDXComponents} />
|
<Component components={MDXComponents as unknown as MDXComponentsType} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
8
packages/cms/contentlayer/tsconfig.json
Normal file
8
packages/cms/contentlayer/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "@kit/tsconfig/base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
3
packages/cms/core/README.md
Normal file
3
packages/cms/core/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# CMS - @kit/cms
|
||||||
|
|
||||||
|
CMS abstraction layer for the Makerkit framework.
|
||||||
38
packages/cms/core/package.json
Normal file
38
packages/cms/core/package.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "@kit/cms",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"scripts": {
|
||||||
|
"clean": "git clean -xdf .turbo node_modules",
|
||||||
|
"format": "prettier --check \"**/*.{ts,tsx}\"",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"prettier": "@kit/prettier-config",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@kit/contentlayer": "workspace:*",
|
||||||
|
"@kit/wordpress": "workspace:*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@kit/eslint-config": "workspace:*",
|
||||||
|
"@kit/prettier-config": "workspace:*",
|
||||||
|
"@kit/tsconfig": "workspace:*"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"extends": [
|
||||||
|
"@kit/eslint-config/base",
|
||||||
|
"@kit/eslint-config/react"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"typesVersions": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
packages/cms/core/src/cms-client.ts
Normal file
72
packages/cms/core/src/cms-client.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace Cms {
|
||||||
|
export type ContentType = 'post' | 'page';
|
||||||
|
|
||||||
|
export interface ContentItem {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
type: ContentType;
|
||||||
|
url: string;
|
||||||
|
description: string | undefined;
|
||||||
|
content: string;
|
||||||
|
author: string;
|
||||||
|
publishedAt: Date;
|
||||||
|
image: string | undefined;
|
||||||
|
slug: string;
|
||||||
|
categories: Category[];
|
||||||
|
tags: Tag[];
|
||||||
|
parentId?: string;
|
||||||
|
children?: ContentItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Category {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tag {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetContentItemsOptions {
|
||||||
|
type?: ContentType;
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
categories?: string[];
|
||||||
|
tags?: string[];
|
||||||
|
depth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetCategoriesOptions {
|
||||||
|
type?: ContentType;
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetTagsOptions {
|
||||||
|
type?: ContentType;
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class CmsClient {
|
||||||
|
abstract getContentItems(
|
||||||
|
options?: Cms.GetContentItemsOptions,
|
||||||
|
): Promise<Cms.ContentItem[]>;
|
||||||
|
|
||||||
|
abstract getContentItemById(id: string): Promise<Cms.ContentItem | undefined>;
|
||||||
|
|
||||||
|
abstract getCategories(
|
||||||
|
options?: Cms.GetCategoriesOptions,
|
||||||
|
): Promise<Cms.Category[]>;
|
||||||
|
|
||||||
|
abstract getCategoryBySlug(slug: string): Promise<Cms.Category | undefined>;
|
||||||
|
|
||||||
|
abstract getTags(options?: Cms.GetTagsOptions): Promise<Cms.Tag[]>;
|
||||||
|
|
||||||
|
abstract getTagBySlug(slug: string): Promise<Cms.Tag | undefined>;
|
||||||
|
}
|
||||||
3
packages/cms/core/src/cms.type.ts
Normal file
3
packages/cms/core/src/cms.type.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// we can add more types here if we have more CMSs
|
||||||
|
// ex. export type CmsType = 'contentlayer' | 'other-cms';
|
||||||
|
export type CmsType = 'contentlayer' | 'wordpress';
|
||||||
17
packages/cms/core/src/content-renderer.tsx
Normal file
17
packages/cms/core/src/content-renderer.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { CmsType } from './cms.type';
|
||||||
|
|
||||||
|
export async function ContentRenderer({
|
||||||
|
content,
|
||||||
|
type = process.env.CMS_CLIENT as CmsType,
|
||||||
|
}: {
|
||||||
|
content: string;
|
||||||
|
type?: CmsType;
|
||||||
|
}) {
|
||||||
|
switch (type) {
|
||||||
|
case 'contentlayer': {
|
||||||
|
const { ContentRenderer } = await import('@kit/contentlayer');
|
||||||
|
|
||||||
|
return ContentRenderer({ content });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
packages/cms/core/src/create-cms-client.ts
Normal file
36
packages/cms/core/src/create-cms-client.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { CmsClient } from './cms-client';
|
||||||
|
import { CmsType } from './cms.type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a CMS client based on the specified type.
|
||||||
|
*
|
||||||
|
* @param {CmsType} type - The type of CMS client to create. Defaults to the value of the CMS_CLIENT environment variable.
|
||||||
|
* @returns {Promise<CmsClient>} A Promise that resolves to the created CMS client.
|
||||||
|
* @throws {Error} If the specified CMS type is unknown.
|
||||||
|
*/
|
||||||
|
export async function createCmsClient(
|
||||||
|
type: CmsType = process.env.CMS_CLIENT as CmsType,
|
||||||
|
): Promise<CmsClient> {
|
||||||
|
switch (type) {
|
||||||
|
case 'contentlayer':
|
||||||
|
return getContentLayerClient();
|
||||||
|
|
||||||
|
case 'wordpress':
|
||||||
|
return getWordpressClient();
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown CMS type: ${type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getContentLayerClient() {
|
||||||
|
const { ContentlayerClient } = await import('@kit/contentlayer');
|
||||||
|
|
||||||
|
return new ContentlayerClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getWordpressClient() {
|
||||||
|
const { WordpressClient } = await import('@kit/wordpress');
|
||||||
|
|
||||||
|
return new WordpressClient();
|
||||||
|
}
|
||||||
4
packages/cms/core/src/index.ts
Normal file
4
packages/cms/core/src/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export * from './cms-client';
|
||||||
|
export * from './create-cms-client';
|
||||||
|
export * from './cms.type';
|
||||||
|
export * from './content-renderer';
|
||||||
8
packages/cms/core/tsconfig.json
Normal file
8
packages/cms/core/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "@kit/tsconfig/base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
9
packages/cms/wordpress/README.md
Normal file
9
packages/cms/wordpress/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# CMS/Wordpress - @kit/wordpress
|
||||||
|
|
||||||
|
Implementation of the CMS layer using the [Wordpress](https://wordpress.org) library. [WIP - not yet working]
|
||||||
|
|
||||||
|
This implementation is used when the host app's environment variable is set as:
|
||||||
|
|
||||||
|
```
|
||||||
|
CMS_TYPE=wordpress
|
||||||
|
```
|
||||||
41
packages/cms/wordpress/package.json
Normal file
41
packages/cms/wordpress/package.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "@kit/wordpress",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"scripts": {
|
||||||
|
"clean": "git clean -xdf .turbo node_modules",
|
||||||
|
"format": "prettier --check \"**/*.{ts,tsx}\"",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"build": "contentlayer build"
|
||||||
|
},
|
||||||
|
"prettier": "@kit/prettier-config",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@kit/cms": "workspace:^",
|
||||||
|
"@kit/ui": "workspace:^"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@kit/eslint-config": "workspace:*",
|
||||||
|
"@kit/prettier-config": "workspace:*",
|
||||||
|
"@kit/tsconfig": "workspace:*",
|
||||||
|
"wp-types": "^3.64.0"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"extends": [
|
||||||
|
"@kit/eslint-config/base",
|
||||||
|
"@kit/eslint-config/react"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"typesVersions": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
packages/cms/wordpress/src/index.ts
Normal file
1
packages/cms/wordpress/src/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './wp-client';
|
||||||
284
packages/cms/wordpress/src/wp-client.ts
Normal file
284
packages/cms/wordpress/src/wp-client.ts
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
import type {
|
||||||
|
WP_REST_API_Category,
|
||||||
|
WP_REST_API_Post,
|
||||||
|
WP_REST_API_Tag,
|
||||||
|
} from 'wp-types';
|
||||||
|
|
||||||
|
import { Cms, CmsClient } from '@kit/cms';
|
||||||
|
|
||||||
|
import GetTagsOptions = Cms.GetTagsOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name WordpressClient
|
||||||
|
* @description Represents a client for interacting with a Wordpress CMS.
|
||||||
|
* Implements the CmsClient interface.
|
||||||
|
*/
|
||||||
|
export class WordpressClient implements CmsClient {
|
||||||
|
private readonly apiUrl: string;
|
||||||
|
|
||||||
|
constructor(apiUrl = process.env.WORDPRESS_API_URL as string) {
|
||||||
|
this.apiUrl = apiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getContentItems(options?: Cms.GetContentItemsOptions) {
|
||||||
|
let endpoint: string;
|
||||||
|
|
||||||
|
switch (options?.type) {
|
||||||
|
case 'post':
|
||||||
|
endpoint = '/wp-json/wp/v2/posts';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'page':
|
||||||
|
endpoint = '/wp-json/wp/v2/pages';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
endpoint = '/wp-json/wp/v2/posts';
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(this.apiUrl + endpoint);
|
||||||
|
|
||||||
|
if (options?.limit) {
|
||||||
|
url.searchParams.append('per_page', options.limit.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options?.offset) {
|
||||||
|
url.searchParams.append('offset', options.offset.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options?.categories) {
|
||||||
|
url.searchParams.append('categories', options.categories.join(','));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options?.tags) {
|
||||||
|
url.searchParams.append('tags', options.tags.join(','));
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url.toString());
|
||||||
|
const data = (await response.json()) as WP_REST_API_Post[];
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
data.map(async (item) => {
|
||||||
|
// Fetch author, categories, and tags as before...
|
||||||
|
|
||||||
|
let parentId: string | undefined;
|
||||||
|
|
||||||
|
if (item.parent) {
|
||||||
|
parentId = item.parent.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
let children: Cms.ContentItem[] = [];
|
||||||
|
|
||||||
|
const embeddedChildren = (
|
||||||
|
item._embedded ? item._embedded['wp:children'] : []
|
||||||
|
) as WP_REST_API_Post[];
|
||||||
|
|
||||||
|
if (options?.depth && options.depth > 0) {
|
||||||
|
children = await Promise.all(
|
||||||
|
embeddedChildren.map(async (child) => {
|
||||||
|
const childAuthor = await this.getAuthor(child.author);
|
||||||
|
|
||||||
|
const childCategories = await this.getCategoriesByIds(
|
||||||
|
child.categories ?? [],
|
||||||
|
);
|
||||||
|
|
||||||
|
const childTags = await this.getTagsByIds(child.tags ?? []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: child.id.toString(),
|
||||||
|
title: child.title.rendered,
|
||||||
|
type: child.type as Cms.ContentType,
|
||||||
|
image: child.featured_media,
|
||||||
|
description: child.excerpt.rendered,
|
||||||
|
url: child.link,
|
||||||
|
content: child.content.rendered,
|
||||||
|
slug: child.slug,
|
||||||
|
publishedAt: new Date(child.date),
|
||||||
|
author: childAuthor?.name,
|
||||||
|
categories: childCategories.map((category) => category.name),
|
||||||
|
tags: childTags.map((tag) => tag.name),
|
||||||
|
parentId: child.parent?.toString(),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const author = await this.getAuthor(item.author);
|
||||||
|
const categories = await this.getCategoriesByIds(item.categories ?? []);
|
||||||
|
const tags = await this.getTagsByIds(item.tags ?? []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id.toString(),
|
||||||
|
title: item.title.rendered,
|
||||||
|
content: item.content.rendered,
|
||||||
|
description: item.excerpt.rendered,
|
||||||
|
image: item.featured_media,
|
||||||
|
url: item.link,
|
||||||
|
slug: item.slug,
|
||||||
|
publishedAt: new Date(item.date),
|
||||||
|
author: author?.name,
|
||||||
|
categories: categories.map((category) => category.name),
|
||||||
|
tags: tags.map((tag) => tag.name),
|
||||||
|
type: item.type as Cms.ContentType,
|
||||||
|
parentId,
|
||||||
|
children,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getContentItemById(slug: string) {
|
||||||
|
const url = `${this.apiUrl}/wp-json/wp/v2/posts?slug=${slug}`;
|
||||||
|
const response = await fetch(url);
|
||||||
|
const data = (await response.json()) as WP_REST_API_Post[];
|
||||||
|
const item = data[0];
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const author = await this.getAuthor(item.author);
|
||||||
|
const categories = await this.getCategoriesByIds(item.categories ?? []);
|
||||||
|
const tags = await this.getTagsByIds(item.tags ?? []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
image: item.featured_media,
|
||||||
|
url: item.link,
|
||||||
|
description: item.excerpt.rendered,
|
||||||
|
type: item.type as Cms.ContentType,
|
||||||
|
children: [],
|
||||||
|
title: item.title.rendered,
|
||||||
|
content: item.content.rendered,
|
||||||
|
slug: item.slug,
|
||||||
|
publishedAt: new Date(item.date),
|
||||||
|
author: author?.name,
|
||||||
|
categories: categories.map((category) => category.name),
|
||||||
|
tags: tags.map((tag) => tag.name),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCategoryBySlug(slug: string) {
|
||||||
|
const url = `${this.apiUrl}/wp-json/wp/v2/categories?slug=${slug}`;
|
||||||
|
const response = await fetch(url);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = data[0] as WP_REST_API_Category;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id.toString(),
|
||||||
|
name: item.name,
|
||||||
|
slug: item.slug,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTagBySlug(slug: string) {
|
||||||
|
const url = `${this.apiUrl}/wp-json/wp/v2/tags?slug=${slug}`;
|
||||||
|
const response = await fetch(url);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = data[0] as WP_REST_API_Tag;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id.toString(),
|
||||||
|
name: item.name,
|
||||||
|
slug: item.slug,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCategories(options?: Cms.GetCategoriesOptions) {
|
||||||
|
const queryParams = new URLSearchParams();
|
||||||
|
|
||||||
|
if (options?.limit) {
|
||||||
|
queryParams.append('per_page', options.limit.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options?.offset) {
|
||||||
|
queryParams.append('offset', options.offset.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`${this.apiUrl}/wp-json/wp/v2/categories?${queryParams.toString()}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = (await response.json()) as WP_REST_API_Category[];
|
||||||
|
|
||||||
|
return data.map((item) => ({
|
||||||
|
id: item.id.toString(),
|
||||||
|
name: item.name,
|
||||||
|
slug: item.slug,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTags(options: GetTagsOptions) {
|
||||||
|
const queryParams = new URLSearchParams();
|
||||||
|
|
||||||
|
if (options?.limit) {
|
||||||
|
queryParams.append('per_page', options.limit.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options?.offset) {
|
||||||
|
queryParams.append('offset', options.offset.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`${this.apiUrl}/wp-json/wp/v2/tags?${queryParams.toString()}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = (await response.json()) as WP_REST_API_Tag[];
|
||||||
|
|
||||||
|
return data.map((item) => ({
|
||||||
|
id: item.id.toString(),
|
||||||
|
name: item.name,
|
||||||
|
slug: item.slug,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getTagsByIds(ids: number[]) {
|
||||||
|
const promises = ids.map((id) =>
|
||||||
|
fetch(`${this.apiUrl}/wp-json/wp/v2/tags/${id}`),
|
||||||
|
);
|
||||||
|
|
||||||
|
const responses = await Promise.all(promises);
|
||||||
|
|
||||||
|
const data = (await Promise.all(
|
||||||
|
responses.map((response) => response.json()),
|
||||||
|
)) as WP_REST_API_Tag[];
|
||||||
|
|
||||||
|
return data.map((item) => ({ id: item.id, name: item.name }));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getCategoriesByIds(ids: number[]) {
|
||||||
|
const promises = ids.map((id) =>
|
||||||
|
fetch(`${this.apiUrl}/wp-json/wp/v2/categories/${id}`),
|
||||||
|
);
|
||||||
|
|
||||||
|
const responses = await Promise.all(promises);
|
||||||
|
|
||||||
|
const data = (await Promise.all(
|
||||||
|
responses.map((response) => response.json()),
|
||||||
|
)) as WP_REST_API_Category[];
|
||||||
|
|
||||||
|
return data.map((item) => ({ id: item.id, name: item.name }));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getAuthor(id: number) {
|
||||||
|
const response = await fetch(`${this.apiUrl}/wp-json/wp/v2/users/${id}`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
return { name: data.name };
|
||||||
|
}
|
||||||
|
}
|
||||||
8
packages/cms/wordpress/tsconfig.json
Normal file
8
packages/cms/wordpress/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "@kit/tsconfig/base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
"./auth-change-listener": "./src/makerkit/auth-change-listener.tsx",
|
"./auth-change-listener": "./src/makerkit/auth-change-listener.tsx",
|
||||||
"./loading-overlay": "./src/makerkit/loading-overlay.tsx",
|
"./loading-overlay": "./src/makerkit/loading-overlay.tsx",
|
||||||
"./profile-avatar": "./src/makerkit/profile-avatar.tsx",
|
"./profile-avatar": "./src/makerkit/profile-avatar.tsx",
|
||||||
"./mdx": "./src/makerkit/mdx/mdx-renderer.tsx",
|
"./mdx-components": "./src/makerkit/mdx-components.tsx",
|
||||||
"./mode-toggle": "./src/makerkit/mode-toggle.tsx"
|
"./mode-toggle": "./src/makerkit/mode-toggle.tsx"
|
||||||
},
|
},
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { forwardRef } from 'react';
|
|||||||
|
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
|
||||||
import { cn } from '../../utils';
|
import { cn } from '../utils';
|
||||||
import { LazyRender } from '../lazy-render';
|
import { LazyRender } from './lazy-render';
|
||||||
|
|
||||||
const NextImage: React.FC<{
|
const NextImage: React.FC<{
|
||||||
width: number;
|
width: number;
|
||||||
@@ -72,11 +72,9 @@ const Video: React.FC<{
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Components = {
|
export const MDXComponents = {
|
||||||
img: NextImage,
|
img: NextImage,
|
||||||
a: ExternalLink,
|
a: ExternalLink,
|
||||||
Video,
|
Video,
|
||||||
Image: NextImage,
|
Image: NextImage,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Components;
|
|
||||||
386
pnpm-lock.yaml
generated
386
pnpm-lock.yaml
generated
@@ -53,6 +53,9 @@ importers:
|
|||||||
'@kit/billing-gateway':
|
'@kit/billing-gateway':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/billing-gateway
|
version: link:../../packages/billing-gateway
|
||||||
|
'@kit/cms':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/cms/core
|
||||||
'@kit/database-webhooks':
|
'@kit/database-webhooks':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/database-webhooks
|
version: link:../../packages/database-webhooks
|
||||||
@@ -94,19 +97,16 @@ importers:
|
|||||||
version: 5.28.6(react@18.2.0)
|
version: 5.28.6(react@18.2.0)
|
||||||
'@tanstack/react-query-next-experimental':
|
'@tanstack/react-query-next-experimental':
|
||||||
specifier: ^5.28.9
|
specifier: ^5.28.9
|
||||||
version: 5.28.9(@tanstack/react-query@5.28.6)(next@14.2.0-canary.49)(react@18.2.0)
|
version: 5.28.9(@tanstack/react-query@5.28.6)(next@14.2.0-canary.50)(react@18.2.0)
|
||||||
'@tanstack/react-table':
|
'@tanstack/react-table':
|
||||||
specifier: ^8.15.0
|
specifier: ^8.15.0
|
||||||
version: 8.15.0(react-dom@18.2.0)(react@18.2.0)
|
version: 8.15.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
contentlayer:
|
|
||||||
specifier: 0.3.4
|
|
||||||
version: 0.3.4(esbuild@0.20.2)
|
|
||||||
date-fns:
|
date-fns:
|
||||||
specifier: ^3.6.0
|
specifier: ^3.6.0
|
||||||
version: 3.6.0
|
version: 3.6.0
|
||||||
edge-csrf:
|
edge-csrf:
|
||||||
specifier: ^1.0.9
|
specifier: ^1.0.9
|
||||||
version: 1.0.9(next@14.2.0-canary.49)
|
version: 1.0.9(next@14.2.0-canary.50)
|
||||||
i18next:
|
i18next:
|
||||||
specifier: ^23.10.1
|
specifier: ^23.10.1
|
||||||
version: 23.10.1
|
version: 23.10.1
|
||||||
@@ -114,14 +114,11 @@ importers:
|
|||||||
specifier: ^1.2.0
|
specifier: ^1.2.0
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
next:
|
next:
|
||||||
specifier: v14.2.0-canary.49
|
specifier: v14.2.0-canary.50
|
||||||
version: 14.2.0-canary.49(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0)
|
version: 14.2.0-canary.50(react-dom@18.2.0)(react@18.2.0)
|
||||||
next-contentlayer:
|
|
||||||
specifier: 0.3.4
|
|
||||||
version: 0.3.4(contentlayer@0.3.4)(esbuild@0.20.2)(next@14.2.0-canary.49)(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
next-sitemap:
|
next-sitemap:
|
||||||
specifier: ^4.2.3
|
specifier: ^4.2.3
|
||||||
version: 4.2.3(next@14.2.0-canary.49)
|
version: 4.2.3(next@14.2.0-canary.50)
|
||||||
next-themes:
|
next-themes:
|
||||||
specifier: 0.3.0
|
specifier: 0.3.0
|
||||||
version: 0.3.0(react-dom@18.2.0)(react@18.2.0)
|
version: 0.3.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
@@ -140,12 +137,6 @@ importers:
|
|||||||
recharts:
|
recharts:
|
||||||
specifier: ^2.12.3
|
specifier: ^2.12.3
|
||||||
version: 2.12.3(react-dom@18.2.0)(react@18.2.0)
|
version: 2.12.3(react-dom@18.2.0)(react@18.2.0)
|
||||||
rehype-autolink-headings:
|
|
||||||
specifier: ^7.1.0
|
|
||||||
version: 7.1.0
|
|
||||||
rehype-slug:
|
|
||||||
specifier: ^6.0.0
|
|
||||||
version: 6.0.0
|
|
||||||
sonner:
|
sonner:
|
||||||
specifier: ^1.4.41
|
specifier: ^1.4.41
|
||||||
version: 1.4.41(react-dom@18.2.0)(react@18.2.0)
|
version: 1.4.41(react-dom@18.2.0)(react@18.2.0)
|
||||||
@@ -268,6 +259,78 @@ importers:
|
|||||||
specifier: ^3.22.4
|
specifier: ^3.22.4
|
||||||
version: 3.22.4
|
version: 3.22.4
|
||||||
|
|
||||||
|
packages/cms/contentlayer:
|
||||||
|
dependencies:
|
||||||
|
'@kit/cms':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../core
|
||||||
|
'@kit/ui':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../ui
|
||||||
|
contentlayer:
|
||||||
|
specifier: 0.3.4
|
||||||
|
version: 0.3.4(esbuild@0.20.2)
|
||||||
|
next-contentlayer:
|
||||||
|
specifier: 0.3.4
|
||||||
|
version: 0.3.4(contentlayer@0.3.4)(esbuild@0.20.2)(next@13.5.6)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
rehype-autolink-headings:
|
||||||
|
specifier: ^6.0.0
|
||||||
|
version: 6.1.1
|
||||||
|
rehype-slug:
|
||||||
|
specifier: ^6.0.0
|
||||||
|
version: 6.0.0
|
||||||
|
devDependencies:
|
||||||
|
'@kit/eslint-config':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../tooling/eslint
|
||||||
|
'@kit/prettier-config':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../tooling/prettier
|
||||||
|
'@kit/tsconfig':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../tooling/typescript
|
||||||
|
|
||||||
|
packages/cms/core:
|
||||||
|
dependencies:
|
||||||
|
'@kit/contentlayer':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../contentlayer
|
||||||
|
'@kit/wordpress':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../wordpress
|
||||||
|
devDependencies:
|
||||||
|
'@kit/eslint-config':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../tooling/eslint
|
||||||
|
'@kit/prettier-config':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../tooling/prettier
|
||||||
|
'@kit/tsconfig':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../tooling/typescript
|
||||||
|
|
||||||
|
packages/cms/wordpress:
|
||||||
|
dependencies:
|
||||||
|
'@kit/cms':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../core
|
||||||
|
'@kit/ui':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../ui
|
||||||
|
devDependencies:
|
||||||
|
'@kit/eslint-config':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../tooling/eslint
|
||||||
|
'@kit/prettier-config':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../tooling/prettier
|
||||||
|
'@kit/tsconfig':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../tooling/typescript
|
||||||
|
wp-types:
|
||||||
|
specifier: ^3.64.0
|
||||||
|
version: 3.64.0
|
||||||
|
|
||||||
packages/database-webhooks:
|
packages/database-webhooks:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@kit/billing-gateway':
|
'@kit/billing-gateway':
|
||||||
@@ -2074,8 +2137,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==}
|
resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@next/env@14.2.0-canary.49:
|
/@next/env@14.2.0-canary.50:
|
||||||
resolution: {integrity: sha512-rQaBRv0PRO3+4lx90zB9eBL0xk230G+6avgCyBL272hckH4XsGgXY6adtBBmZJF1QuDI+pS+DqppXSJvfexsdw==}
|
resolution: {integrity: sha512-COLktqbQGmSANtTTKVs4heykkT4YSLM+GU1CbHKpSXnyEP98yrWcfMTMeTwcEZCgilvI1gPT5zVO/ISU1o/X5A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@next/eslint-plugin-next@14.1.4:
|
/@next/eslint-plugin-next@14.1.4:
|
||||||
@@ -2098,6 +2161,15 @@ packages:
|
|||||||
source-map: 0.7.4
|
source-map: 0.7.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@next/swc-darwin-arm64@13.5.6:
|
||||||
|
resolution: {integrity: sha512-5nvXMzKtZfvcu4BhtV0KH1oGv4XEW+B+jOfmBdpFI3C7FrB/MfujRpWYSBBO64+qbW8pkZiSyQv9eiwnn5VIQA==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@next/swc-darwin-arm64@14.1.0:
|
/@next/swc-darwin-arm64@14.1.0:
|
||||||
resolution: {integrity: sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==}
|
resolution: {integrity: sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
@@ -2107,8 +2179,8 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-darwin-arm64@14.2.0-canary.49:
|
/@next/swc-darwin-arm64@14.2.0-canary.50:
|
||||||
resolution: {integrity: sha512-tFFCgRJOk28rIiEGjz2bafqp3G5lV7hXyYjZ7d+gt/MjpLRrtTwu+lRBv/W1VFdTkPv8+k2hvXZNNTHO1n57Ow==}
|
resolution: {integrity: sha512-el2drGIjRNuLqqahuCoKou50pEqacrcGvhOphiU8wQPWOku3d762sN9pTyunyLVshjLNOI/gDxh1Ja2dDcmXzg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
@@ -2116,6 +2188,15 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/@next/swc-darwin-x64@13.5.6:
|
||||||
|
resolution: {integrity: sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@next/swc-darwin-x64@14.1.0:
|
/@next/swc-darwin-x64@14.1.0:
|
||||||
resolution: {integrity: sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==}
|
resolution: {integrity: sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
@@ -2125,8 +2206,8 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-darwin-x64@14.2.0-canary.49:
|
/@next/swc-darwin-x64@14.2.0-canary.50:
|
||||||
resolution: {integrity: sha512-NR4Meb67q8M2pNP5a8Tp3Zfar2Ao8ChHWcD3wEBgICcgJ4ZyCQCWXdM+VBsf8a3yuAoXmu1/cwOwWu1KXVC96A==}
|
resolution: {integrity: sha512-gulXuO14RZODSB3hU+Rb+CHWymH7kGAcvsP7SA95wUXx2CAuugFfI90sHL4ieQib5N058DoIQPvYILiqP9RpxQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
@@ -2134,6 +2215,15 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/@next/swc-linux-arm64-gnu@13.5.6:
|
||||||
|
resolution: {integrity: sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm64-gnu@14.1.0:
|
/@next/swc-linux-arm64-gnu@14.1.0:
|
||||||
resolution: {integrity: sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==}
|
resolution: {integrity: sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
@@ -2143,8 +2233,17 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm64-gnu@14.2.0-canary.49:
|
/@next/swc-linux-arm64-gnu@14.2.0-canary.50:
|
||||||
resolution: {integrity: sha512-2bFQUNYnz6L7xOAzvejMj09iqmWwkjFyguGEfmNiFN0kPgJ4viSCKZvoiuG/MPh3VoDSz5N2qx1tehSCy7KbFA==}
|
resolution: {integrity: sha512-6ZXM32VGQU1liB9+r3AHIsUZBbBQGMBpWdGnRzvCQ+AUVhL01x2P77AXCjhxeLUgrUisl3Cw+UDc+WvuLcygjQ==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@next/swc-linux-arm64-musl@13.5.6:
|
||||||
|
resolution: {integrity: sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
@@ -2161,8 +2260,8 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm64-musl@14.2.0-canary.49:
|
/@next/swc-linux-arm64-musl@14.2.0-canary.50:
|
||||||
resolution: {integrity: sha512-68PjCGC1JghA2tuznu+ExeSP+L6qpf6afblB4wFhDRniP+0hRrZB+1E3jJ3PmBgHtitJJMaplTFeKYQ8xbF8xw==}
|
resolution: {integrity: sha512-Is7FNrgY1ifBMKs9Y7fx6OJp7OjwfMMl8BhlN+UzbkMtZF9R45qLnRSWOu0gkQLqfqR0wx//Bmkr/d25qqZxjg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
@@ -2170,6 +2269,15 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/@next/swc-linux-x64-gnu@13.5.6:
|
||||||
|
resolution: {integrity: sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-x64-gnu@14.1.0:
|
/@next/swc-linux-x64-gnu@14.1.0:
|
||||||
resolution: {integrity: sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==}
|
resolution: {integrity: sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
@@ -2179,8 +2287,17 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-x64-gnu@14.2.0-canary.49:
|
/@next/swc-linux-x64-gnu@14.2.0-canary.50:
|
||||||
resolution: {integrity: sha512-eiDvo0bnYCI59UhaZrNV1k7wZPFHyQ2uJ7/MUH9yvZZcSKBxRDtNc3FmCAZjKiNx/SclMFRAtENLOlDzceRp5g==}
|
resolution: {integrity: sha512-rKcciKNtCVrcj9zZ+JBK1AgIbeISHZz2OcTa/i1O3l+VwNDN25YAPaVDL0aPX6e9N0SR5W33b+bSQMHOc1FGhA==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@next/swc-linux-x64-musl@13.5.6:
|
||||||
|
resolution: {integrity: sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
@@ -2197,8 +2314,8 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-x64-musl@14.2.0-canary.49:
|
/@next/swc-linux-x64-musl@14.2.0-canary.50:
|
||||||
resolution: {integrity: sha512-XgwiLB/WkRjuhWoKZmlRsZl1b8C7dsYlRD3zqHPkrgWhERyyn3AoeRjIa/eHR6nxj7oTu2KHET1oSJoYobH70g==}
|
resolution: {integrity: sha512-tVFgS5lOa/h6h5//4p9mhcV7XThMAzMJQoC+j7y+yhnGnb17t4pQPR3FXAonncgm9OyCkA2N0O0hqwsnj6oCLA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
@@ -2206,6 +2323,15 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/@next/swc-win32-arm64-msvc@13.5.6:
|
||||||
|
resolution: {integrity: sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-arm64-msvc@14.1.0:
|
/@next/swc-win32-arm64-msvc@14.1.0:
|
||||||
resolution: {integrity: sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==}
|
resolution: {integrity: sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
@@ -2215,8 +2341,8 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-arm64-msvc@14.2.0-canary.49:
|
/@next/swc-win32-arm64-msvc@14.2.0-canary.50:
|
||||||
resolution: {integrity: sha512-jqC5vhFOAewsGdWriuQqR2aalQ8dHJ1WkSl1psluTxpo5UgICBk+H0wQ93a0CEfD0Rj+8QjUFh+U1oYTqE4YIg==}
|
resolution: {integrity: sha512-1FmGWELLW7XdrNmJQca7vbBUIVOd84LGZQCO6gRIvmAw3Oh7S3UwP2rAsZ9K24Ox44TnK2xV4C4t9BV8PnHzMQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
@@ -2224,6 +2350,15 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/@next/swc-win32-ia32-msvc@13.5.6:
|
||||||
|
resolution: {integrity: sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-ia32-msvc@14.1.0:
|
/@next/swc-win32-ia32-msvc@14.1.0:
|
||||||
resolution: {integrity: sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==}
|
resolution: {integrity: sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
@@ -2233,8 +2368,8 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-ia32-msvc@14.2.0-canary.49:
|
/@next/swc-win32-ia32-msvc@14.2.0-canary.50:
|
||||||
resolution: {integrity: sha512-Zcfe1+FuFtMCtG0L7F9yh0yRhmLM2gGAUHW41FYN+Rtbi/JFS8qhs/M7pOPkqhEWWKqo3at64q7z8KQh+21VsQ==}
|
resolution: {integrity: sha512-GmIQ0VdGEExzZSh00wCjAILfdqR4dzSFnnXvjAnNBehS8uadHhlLY7fpsVOtNh7byd5gxgbt+dFz7Y4GrrCRbA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
@@ -2242,6 +2377,15 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/@next/swc-win32-x64-msvc@13.5.6:
|
||||||
|
resolution: {integrity: sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-x64-msvc@14.1.0:
|
/@next/swc-win32-x64-msvc@14.1.0:
|
||||||
resolution: {integrity: sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==}
|
resolution: {integrity: sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
@@ -2251,8 +2395,8 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-x64-msvc@14.2.0-canary.49:
|
/@next/swc-win32-x64-msvc@14.2.0-canary.50:
|
||||||
resolution: {integrity: sha512-yeCjnmqMmI9aNbRk3DTrKvCuImUWXU+Kl0XC9KFo8iLpOztpCQrMA+pf5s3GRqv1HRzbRoHsj+1VCPXzTmZrLA==}
|
resolution: {integrity: sha512-kNqwVNRCoujVBe2C4YdtwfrF8103nMmsV3B/IvMnxB3pAotvYLzUTboflT2Wx5AMFTNY1KGYY8GGFjotX5/GRQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
@@ -4230,7 +4374,7 @@ packages:
|
|||||||
/@tanstack/query-core@5.28.6:
|
/@tanstack/query-core@5.28.6:
|
||||||
resolution: {integrity: sha512-hnhotV+DnQtvtR3jPvbQMPNMW4KEK0J4k7c609zJ8muiNknm+yoDyMHmxTWM5ZnlZpsz0zOxYFr+mzRJNHWJsA==}
|
resolution: {integrity: sha512-hnhotV+DnQtvtR3jPvbQMPNMW4KEK0J4k7c609zJ8muiNknm+yoDyMHmxTWM5ZnlZpsz0zOxYFr+mzRJNHWJsA==}
|
||||||
|
|
||||||
/@tanstack/react-query-next-experimental@5.28.9(@tanstack/react-query@5.28.6)(next@14.2.0-canary.49)(react@18.2.0):
|
/@tanstack/react-query-next-experimental@5.28.9(@tanstack/react-query@5.28.6)(next@14.2.0-canary.50)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-cihvqAme8nX6O5jeWtk19fnMsgXTX5puHwj6ya2Gf6FZIKhcFTrXQ9npH3ACcbinmVYPcQrShk/D3XAGKR/AUg==}
|
resolution: {integrity: sha512-cihvqAme8nX6O5jeWtk19fnMsgXTX5puHwj6ya2Gf6FZIKhcFTrXQ9npH3ACcbinmVYPcQrShk/D3XAGKR/AUg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@tanstack/react-query': ^5.28.9
|
'@tanstack/react-query': ^5.28.9
|
||||||
@@ -4238,7 +4382,7 @@ packages:
|
|||||||
react: ^18.0.0
|
react: ^18.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tanstack/react-query': 5.28.6(react@18.2.0)
|
'@tanstack/react-query': 5.28.6(react@18.2.0)
|
||||||
next: 14.2.0-canary.49(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0)
|
next: 14.2.0-canary.50(react-dom@18.2.0)(react@18.2.0)
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -5887,12 +6031,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/devlop@1.1.0:
|
|
||||||
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
|
||||||
dependencies:
|
|
||||||
dequal: 2.0.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/didyoumean@1.2.2:
|
/didyoumean@1.2.2:
|
||||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||||
|
|
||||||
@@ -6001,12 +6139,12 @@ packages:
|
|||||||
/eastasianwidth@0.2.0:
|
/eastasianwidth@0.2.0:
|
||||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||||
|
|
||||||
/edge-csrf@1.0.9(next@14.2.0-canary.49):
|
/edge-csrf@1.0.9(next@14.2.0-canary.50):
|
||||||
resolution: {integrity: sha512-3F89YTh42UDdISr3s9AEcgJDLi4ysgjGfnybzF0LuZGaG2W31h1ZwgWwEQBLMj04lAklcP4XHZYi7vk9o8zcbg==}
|
resolution: {integrity: sha512-3F89YTh42UDdISr3s9AEcgJDLi4ysgjGfnybzF0LuZGaG2W31h1ZwgWwEQBLMj04lAklcP4XHZYi7vk9o8zcbg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
next: ^13.0.0 || ^14.0.0
|
next: ^13.0.0 || ^14.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
next: 14.2.0-canary.49(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0)
|
next: 14.2.0-canary.50(react-dom@18.2.0)(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/editorconfig@1.0.4:
|
/editorconfig@1.0.4:
|
||||||
@@ -7147,16 +7285,27 @@ packages:
|
|||||||
web-namespaces: 2.0.1
|
web-namespaces: 2.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/hast-util-has-property@2.0.1:
|
||||||
|
resolution: {integrity: sha512-X2+RwZIMTMKpXUzlotatPzWj8bspCymtXH3cfG3iQKV+wPF53Vgaqxi/eLqGck0wKq1kS9nvoB1wchbCPEL8sg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/hast-util-heading-rank@2.1.1:
|
||||||
|
resolution: {integrity: sha512-iAuRp+ESgJoRFJbSyaqsfvJDY6zzmFoEnL1gtz1+U8gKtGGj1p0CVlysuUAUjq95qlZESHINLThwJzNGmgGZxA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 2.3.10
|
||||||
|
dev: false
|
||||||
|
|
||||||
/hast-util-heading-rank@3.0.0:
|
/hast-util-heading-rank@3.0.0:
|
||||||
resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==}
|
resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/hast-util-is-element@3.0.0:
|
/hast-util-is-element@2.1.3:
|
||||||
resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
|
resolution: {integrity: sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 2.3.10
|
||||||
|
'@types/unist': 2.0.10
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/hast-util-parse-selector@3.1.1:
|
/hast-util-parse-selector@3.1.1:
|
||||||
@@ -8655,7 +8804,7 @@ packages:
|
|||||||
engines: {node: '>= 0.4.0'}
|
engines: {node: '>= 0.4.0'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/next-contentlayer@0.3.4(contentlayer@0.3.4)(esbuild@0.20.2)(next@14.2.0-canary.49)(react-dom@18.2.0)(react@18.2.0):
|
/next-contentlayer@0.3.4(contentlayer@0.3.4)(esbuild@0.20.2)(next@13.5.6)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-UtUCwgAl159KwfhNaOwyiI7Lg6sdioyKMeh+E7jxx0CJ29JuXGxBEYmCI6+72NxFGIFZKx8lvttbbQhbnYWYSw==}
|
resolution: {integrity: sha512-UtUCwgAl159KwfhNaOwyiI7Lg6sdioyKMeh+E7jxx0CJ29JuXGxBEYmCI6+72NxFGIFZKx8lvttbbQhbnYWYSw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
contentlayer: 0.3.4
|
contentlayer: 0.3.4
|
||||||
@@ -8666,7 +8815,7 @@ packages:
|
|||||||
'@contentlayer/core': 0.3.4(esbuild@0.20.2)
|
'@contentlayer/core': 0.3.4(esbuild@0.20.2)
|
||||||
'@contentlayer/utils': 0.3.4
|
'@contentlayer/utils': 0.3.4
|
||||||
contentlayer: 0.3.4(esbuild@0.20.2)
|
contentlayer: 0.3.4(esbuild@0.20.2)
|
||||||
next: 14.2.0-canary.49(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0)
|
next: 13.5.6(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0)
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -8676,7 +8825,7 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/next-sitemap@4.2.3(next@14.2.0-canary.49):
|
/next-sitemap@4.2.3(next@14.2.0-canary.50):
|
||||||
resolution: {integrity: sha512-vjdCxeDuWDzldhCnyFCQipw5bfpl4HmZA7uoo3GAaYGjGgfL4Cxb1CiztPuWGmS+auYs7/8OekRS8C2cjdAsjQ==}
|
resolution: {integrity: sha512-vjdCxeDuWDzldhCnyFCQipw5bfpl4HmZA7uoo3GAaYGjGgfL4Cxb1CiztPuWGmS+auYs7/8OekRS8C2cjdAsjQ==}
|
||||||
engines: {node: '>=14.18'}
|
engines: {node: '>=14.18'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -8687,7 +8836,7 @@ packages:
|
|||||||
'@next/env': 13.5.6
|
'@next/env': 13.5.6
|
||||||
fast-glob: 3.3.2
|
fast-glob: 3.3.2
|
||||||
minimist: 1.2.8
|
minimist: 1.2.8
|
||||||
next: 14.2.0-canary.49(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0)
|
next: 14.2.0-canary.50(react-dom@18.2.0)(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/next-themes@0.3.0(react-dom@18.2.0)(react@18.2.0):
|
/next-themes@0.3.0(react-dom@18.2.0)(react@18.2.0):
|
||||||
@@ -8699,6 +8848,46 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
|
||||||
|
/next@13.5.6(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw==}
|
||||||
|
engines: {node: '>=16.14.0'}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.1.0
|
||||||
|
react: ^18.2.0
|
||||||
|
react-dom: ^18.2.0
|
||||||
|
sass: ^1.3.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@opentelemetry/api':
|
||||||
|
optional: true
|
||||||
|
sass:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@next/env': 13.5.6
|
||||||
|
'@opentelemetry/api': 1.8.0
|
||||||
|
'@swc/helpers': 0.5.2
|
||||||
|
busboy: 1.6.0
|
||||||
|
caniuse-lite: 1.0.30001600
|
||||||
|
postcss: 8.4.31
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
styled-jsx: 5.1.1(react@18.2.0)
|
||||||
|
watchpack: 2.4.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@next/swc-darwin-arm64': 13.5.6
|
||||||
|
'@next/swc-darwin-x64': 13.5.6
|
||||||
|
'@next/swc-linux-arm64-gnu': 13.5.6
|
||||||
|
'@next/swc-linux-arm64-musl': 13.5.6
|
||||||
|
'@next/swc-linux-x64-gnu': 13.5.6
|
||||||
|
'@next/swc-linux-x64-musl': 13.5.6
|
||||||
|
'@next/swc-win32-arm64-msvc': 13.5.6
|
||||||
|
'@next/swc-win32-ia32-msvc': 13.5.6
|
||||||
|
'@next/swc-win32-x64-msvc': 13.5.6
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@babel/core'
|
||||||
|
- babel-plugin-macros
|
||||||
|
dev: false
|
||||||
|
|
||||||
/next@14.1.0(react-dom@18.2.0)(react@18.2.0):
|
/next@14.1.0(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==}
|
resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==}
|
||||||
engines: {node: '>=18.17.0'}
|
engines: {node: '>=18.17.0'}
|
||||||
@@ -8738,8 +8927,8 @@ packages:
|
|||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/next@14.2.0-canary.49(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0):
|
/next@14.2.0-canary.50(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-sfryWP84xmqUOYAilbiojczTpTGCRTMch3w+EVppzPj0DS6gOWv9vPUHp/6uMWWZ+YX+n3GkYhwRK80Q+FG+kg==}
|
resolution: {integrity: sha512-7uNL5MrCx7YXJO1B/H3619HkLQhlXdAWIsgMHzetrz7ffE3isZoy6u5aXkkITfyKBfbvMbyhUcd2MH7HCdivfg==}
|
||||||
engines: {node: '>=18.17.0'}
|
engines: {node: '>=18.17.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -8756,8 +8945,7 @@ packages:
|
|||||||
sass:
|
sass:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 14.2.0-canary.49
|
'@next/env': 14.2.0-canary.50
|
||||||
'@opentelemetry/api': 1.8.0
|
|
||||||
'@swc/helpers': 0.5.5
|
'@swc/helpers': 0.5.5
|
||||||
busboy: 1.6.0
|
busboy: 1.6.0
|
||||||
caniuse-lite: 1.0.30001600
|
caniuse-lite: 1.0.30001600
|
||||||
@@ -8767,15 +8955,15 @@ packages:
|
|||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
styled-jsx: 5.1.1(react@18.2.0)
|
styled-jsx: 5.1.1(react@18.2.0)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@next/swc-darwin-arm64': 14.2.0-canary.49
|
'@next/swc-darwin-arm64': 14.2.0-canary.50
|
||||||
'@next/swc-darwin-x64': 14.2.0-canary.49
|
'@next/swc-darwin-x64': 14.2.0-canary.50
|
||||||
'@next/swc-linux-arm64-gnu': 14.2.0-canary.49
|
'@next/swc-linux-arm64-gnu': 14.2.0-canary.50
|
||||||
'@next/swc-linux-arm64-musl': 14.2.0-canary.49
|
'@next/swc-linux-arm64-musl': 14.2.0-canary.50
|
||||||
'@next/swc-linux-x64-gnu': 14.2.0-canary.49
|
'@next/swc-linux-x64-gnu': 14.2.0-canary.50
|
||||||
'@next/swc-linux-x64-musl': 14.2.0-canary.49
|
'@next/swc-linux-x64-musl': 14.2.0-canary.50
|
||||||
'@next/swc-win32-arm64-msvc': 14.2.0-canary.49
|
'@next/swc-win32-arm64-msvc': 14.2.0-canary.50
|
||||||
'@next/swc-win32-ia32-msvc': 14.2.0-canary.49
|
'@next/swc-win32-ia32-msvc': 14.2.0-canary.50
|
||||||
'@next/swc-win32-x64-msvc': 14.2.0-canary.49
|
'@next/swc-win32-x64-msvc': 14.2.0-canary.50
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
@@ -9920,15 +10108,16 @@ packages:
|
|||||||
rc: 1.2.8
|
rc: 1.2.8
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/rehype-autolink-headings@7.1.0:
|
/rehype-autolink-headings@6.1.1:
|
||||||
resolution: {integrity: sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==}
|
resolution: {integrity: sha512-NMYzZIsHM3sA14nC5rAFuUPIOfg+DFmf9EY1YMhaNlB7+3kK/ZlE6kqPfuxr1tsJ1XWkTrMtMoyHosU70d35mA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 2.3.10
|
||||||
'@ungap/structured-clone': 1.2.0
|
extend: 3.0.2
|
||||||
hast-util-heading-rank: 3.0.0
|
hast-util-has-property: 2.0.1
|
||||||
hast-util-is-element: 3.0.0
|
hast-util-heading-rank: 2.1.1
|
||||||
unified: 11.0.4
|
hast-util-is-element: 2.1.3
|
||||||
unist-util-visit: 5.0.0
|
unified: 10.1.2
|
||||||
|
unist-util-visit: 4.1.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/rehype-slug@6.0.0:
|
/rehype-slug@6.0.0:
|
||||||
@@ -11096,18 +11285,6 @@ packages:
|
|||||||
vfile: 5.3.7
|
vfile: 5.3.7
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/unified@11.0.4:
|
|
||||||
resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==}
|
|
||||||
dependencies:
|
|
||||||
'@types/unist': 3.0.2
|
|
||||||
bail: 2.0.2
|
|
||||||
devlop: 1.1.0
|
|
||||||
extend: 3.0.2
|
|
||||||
is-plain-obj: 4.1.0
|
|
||||||
trough: 2.2.0
|
|
||||||
vfile: 6.0.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/unist-util-generated@2.0.1:
|
/unist-util-generated@2.0.1:
|
||||||
resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==}
|
resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -11149,12 +11326,6 @@ packages:
|
|||||||
'@types/unist': 2.0.10
|
'@types/unist': 2.0.10
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/unist-util-stringify-position@4.0.0:
|
|
||||||
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
|
|
||||||
dependencies:
|
|
||||||
'@types/unist': 3.0.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/unist-util-visit-parents@5.1.3:
|
/unist-util-visit-parents@5.1.3:
|
||||||
resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==}
|
resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -11313,13 +11484,6 @@ packages:
|
|||||||
unist-util-stringify-position: 3.0.3
|
unist-util-stringify-position: 3.0.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/vfile-message@4.0.2:
|
|
||||||
resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
|
|
||||||
dependencies:
|
|
||||||
'@types/unist': 3.0.2
|
|
||||||
unist-util-stringify-position: 4.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/vfile@5.3.7:
|
/vfile@5.3.7:
|
||||||
resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==}
|
resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -11329,14 +11493,6 @@ packages:
|
|||||||
vfile-message: 3.1.4
|
vfile-message: 3.1.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/vfile@6.0.1:
|
|
||||||
resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
|
|
||||||
dependencies:
|
|
||||||
'@types/unist': 3.0.2
|
|
||||||
unist-util-stringify-position: 4.0.0
|
|
||||||
vfile-message: 4.0.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/victory-vendor@36.9.2:
|
/victory-vendor@36.9.2:
|
||||||
resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
|
resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -11360,6 +11516,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
|
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
/watchpack@2.4.0:
|
||||||
|
resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
dependencies:
|
||||||
|
glob-to-regexp: 0.4.1
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
dev: false
|
||||||
|
|
||||||
/watchpack@2.4.1:
|
/watchpack@2.4.1:
|
||||||
resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==}
|
resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
@@ -11527,6 +11691,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
|
resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/wp-types@3.64.0:
|
||||||
|
resolution: {integrity: sha512-8rwHUQFxI18jezvObymV0eKEhnU2xaSci5ra6YG+dV6EDZLfNR2z3NZA82mFkOzZRWpl/Dlj+a4u8aqk08UPGQ==}
|
||||||
|
dependencies:
|
||||||
|
typescript: 5.4.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/wrap-ansi@6.2.0:
|
/wrap-ansi@6.2.0:
|
||||||
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
packages:
|
packages:
|
||||||
- apps/*
|
- apps/*
|
||||||
- packages/*
|
- packages/*
|
||||||
- tooling/*
|
|
||||||
- packages/features/*
|
- packages/features/*
|
||||||
|
- packages/cms/*
|
||||||
|
- tooling/*
|
||||||
- supabase
|
- supabase
|
||||||
@@ -37,6 +37,7 @@ const config = {
|
|||||||
'**/.eslintrc.cjs',
|
'**/.eslintrc.cjs',
|
||||||
'**/*.config.js',
|
'**/*.config.js',
|
||||||
'**/*.config.cjs',
|
'**/*.config.cjs',
|
||||||
|
'**/node_modules',
|
||||||
'.next',
|
'.next',
|
||||||
'dist',
|
'dist',
|
||||||
'pnpm-lock.yaml',
|
'pnpm-lock.yaml',
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const config = {
|
|||||||
'^@supabase/supabase-js$',
|
'^@supabase/supabase-js$',
|
||||||
'^@supabase/gotrue-js$',
|
'^@supabase/gotrue-js$',
|
||||||
'<THIRD_PARTY_MODULES>',
|
'<THIRD_PARTY_MODULES>',
|
||||||
'^@kit/(.*)$',
|
'^@kit/(.*)$', // package imports
|
||||||
'^~/(.*)$', // app-specific imports
|
'^~/(.*)$', // app-specific imports
|
||||||
'^[./]', // relative imports
|
'^[./]', // relative imports
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ export default {
|
|||||||
content: [
|
content: [
|
||||||
'../../packages/**/*.tsx',
|
'../../packages/**/*.tsx',
|
||||||
'../../apps/**/*.tsx',
|
'../../apps/**/*.tsx',
|
||||||
'!**/node_modules',
|
'!../../packages/**/node_modules',
|
||||||
|
'!../../apps/**/node_modules',
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
container: {
|
container: {
|
||||||
|
|||||||
Reference in New Issue
Block a user