diff --git a/apps/docs/app/error.tsx b/apps/docs/app/error.tsx new file mode 100644 index 00000000000..ab0dd867544 --- /dev/null +++ b/apps/docs/app/error.tsx @@ -0,0 +1,17 @@ +'use client' + +import Link from 'next/link' +import { Button } from 'ui' + +const ErrorPage = () => ( +
+ + Sorry, something went wrong + + +
+) + +export default ErrorPage diff --git a/apps/docs/app/guides/[...slug]/page.tsx b/apps/docs/app/guides/[...slug]/page.tsx new file mode 100644 index 00000000000..2bbfc101eea --- /dev/null +++ b/apps/docs/app/guides/[...slug]/page.tsx @@ -0,0 +1,19 @@ +import { + getGuidesMarkdown, + genGuideMeta, + genGuidesStaticParams, +} from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' + +type Params = { slug: string[] } + +const GuidePage = async ({ params }: { params: Params }) => { + const data = await getGuidesMarkdown(params) + return +} + +const generateStaticParams = genGuidesStaticParams() +const generateMetadata = genGuideMeta(getGuidesMarkdown) + +export default GuidePage +export { generateStaticParams, generateMetadata } diff --git a/apps/docs/app/guides/ai/python/[slug]/page.tsx b/apps/docs/app/guides/ai/python/[slug]/page.tsx new file mode 100644 index 00000000000..29c2c903e9f --- /dev/null +++ b/apps/docs/app/guides/ai/python/[slug]/page.tsx @@ -0,0 +1,133 @@ +import { type SerializeOptions } from 'next-mdx-remote/dist/types' +import { redirect } from 'next/navigation' +import { relative } from 'path' +import rehypeSlug from 'rehype-slug' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { notFoundLink } from '~/features/recommendations/NotFound.utils' +import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' +import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' +import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' + +// We fetch these docs at build time from an external repo +const org = 'supabase' +const repo = 'vecs' +const branch = 'main' +const docsDir = 'docs' +const externalSite = 'https://supabase.github.io/vecs' + +// Each external docs page is mapped to a local page +const pageMap = [ + { + slug: 'api', + meta: { + title: 'API', + }, + remoteFile: 'api.md', + }, + { + slug: 'collections', + meta: { + title: 'Collections', + }, + remoteFile: 'concepts_collections.md', + }, + { + slug: 'indexes', + meta: { + title: 'Indexes', + }, + remoteFile: 'concepts_indexes.md', + }, + { + slug: 'metadata', + meta: { + title: 'Metadata', + }, + remoteFile: 'concepts_metadata.md', + }, +] + +interface Params { + slug: string +} + +const PythonClientDocs = async ({ params }: { params: Params }) => { + const { meta, ...data } = await getContent(params) + + const options = { + mdxOptions: { + remarkPlugins: [remarkMkDocsAdmonition, [removeTitle, meta.title]], + rehypePlugins: [[linkTransform, urlTransform], rehypeSlug], + }, + } as SerializeOptions + + return +} + +/** + * Fetch markdown from external repo + */ +const getContent = async ({ slug }: Params) => { + const page = pageMap.find(({ slug: validSlug }) => validSlug && validSlug === slug) + + if (!page) { + redirect(notFoundLink(`api/python/${slug}`)) + } + + const { remoteFile, meta } = page + + const editLink = newEditLink(`${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}`) + + const response = await fetch( + `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/${remoteFile}` + ) + + const content = await response.text() + + return { + pathname: `/guides/ai/python/${slug}` satisfies `/${string}`, + meta, + content, + editLink, + } +} + +const urlTransform: UrlTransformFunction = (url) => { + try { + const externalSiteUrl = new URL(externalSite) + + const placeholderHostname = 'placeholder' + const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`) + + // Don't modify a url with a FQDN or a url that's only a hash + if (hostname !== placeholderHostname || pathname === '/') { + return url + } + + const relativePage = ( + pathname.endsWith('.md') + ? pathname.replace(/\.md$/, '') + : relative(externalSiteUrl.pathname, pathname) + ).replace(/^\//, '') + + const page = pageMap.find(({ remoteFile }) => `${relativePage}.md` === remoteFile) + + // If we have a mapping for this page, use the mapped path + if (page) { + return page.slug + hash + } + + // If we don't have this page in our docs, link to original docs + return `${externalSite}/${relativePage}${hash}` + } catch (err) { + console.error('Error transforming markdown URL', err) + return url + } +} + +const generateStaticParams = () => pageMap.map(({ slug }) => ({ slug })) +const generateMetadata = genGuideMeta(getContent) + +export default PythonClientDocs +export { generateStaticParams, generateMetadata } diff --git a/apps/docs/pages/guides/cli/config.tsx b/apps/docs/app/guides/cli/config/page.tsx similarity index 56% rename from apps/docs/pages/guides/cli/config.tsx rename to apps/docs/app/guides/cli/config/page.tsx index 89b16e55973..4707208e7e0 100644 --- a/apps/docs/pages/guides/cli/config.tsx +++ b/apps/docs/app/guides/cli/config/page.tsx @@ -1,60 +1,45 @@ -import Head from 'next/head' import ReactMarkdown from 'react-markdown' - -import { CodeBlock, cn } from 'ui' - +import { CodeBlock } from 'ui' import { Heading } from 'ui/src/components/CustomHTMLElements' -import GuidesTableOfContents from '~/components/GuidesTableOfContents' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import { MainSkeleton } from '~/layouts/MainSkeleton' -import { Parameter } from '~/lib/refGenerator/refTypes' +import { type TOCHeader } from '~/components/GuidesTableOfContents' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import type { Parameter } from '~/lib/refGenerator/refTypes' import specFile from '~/spec/cli_v1_config.yaml' assert { type: 'yml' } -// Parameters are grouped on the page by tag -const tocList = [] -const content = specFile.info.tags.map((tag) => { - tocList.push({ text: tag.title, link: `${tag.id}-config`, level: 2 }) +const meta = { + title: 'Supabase CLI config', +} + +const generateMetadata = genGuideMeta(() => ({ + pathname: '/guides/cli/config', + meta, +})) + +const tocList: TOCHeader[] = [] +const content = specFile.info.tags.map((tag: { id: string; title: string }, id: number) => { + tocList.push({ id: `${id}`, text: tag.title, link: `${tag.id}-config`, level: 2 }) return (
{tag.title} Config {specFile.parameters - .filter((param: Parameter) => param.tags[0] === tag.id) - .map((parameter: Parameter) => { - tocList.push({ text: parameter.id, link: `#${parameter.id}`, level: 3 }) + .filter((param: Parameter) => param.tags && param.tags[0] === tag.id) + .map((parameter: Parameter, id: number) => { + tocList.push({ id: `${id}`, text: parameter.id, link: `#${parameter.id}`, level: 3 }) return })}
) }) -export default function Config() { +const Config = () => { + const editLink = newEditLink('supabase/supabase/blob/master/apps/docs/spec/cli_v1_config.yaml') + return ( - <> - - Supabase CLI config - - -
-
-
-

CLI configuration

-
- {specFile.info.description} -
{content}
-
-
- -
-
- + + {specFile.info.description} +
{content}
+
) } @@ -81,7 +66,7 @@ function Info({ parameter }: { parameter: Parameter }) { {parameter.id} {parameter.default ? parameter.default.toString() : 'None'} - {parameter.required.toString()} + {parameter.required !== undefined && parameter.required.toString()} @@ -116,3 +101,6 @@ function Info({ parameter }: { parameter: Parameter }) { ) } + +export default Config +export { generateMetadata } diff --git a/apps/docs/app/guides/cli/github-action/[slug]/page.tsx b/apps/docs/app/guides/cli/github-action/[slug]/page.tsx new file mode 100644 index 00000000000..f42057ac118 --- /dev/null +++ b/apps/docs/app/guides/cli/github-action/[slug]/page.tsx @@ -0,0 +1,132 @@ +import { type SerializeOptions } from 'next-mdx-remote/dist/types' +import { redirect } from 'next/navigation' +import { relative } from 'node:path' +import rehypeSlug from 'rehype-slug' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { notFoundLink } from '~/features/recommendations/NotFound.utils' +import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' +import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' +import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' +import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' + +// We fetch these docs at build time from an external repo +const org = 'supabase' +const repo = 'setup-cli' +const branch = 'gh-pages' +const docsDir = 'docs' +const externalSite = 'https://supabase.github.io/setup-cli' + +// Each external docs page is mapped to a local page +const pageMap = [ + { + slug: 'generating-types', + meta: { + title: 'Generate types using GitHub Actions', + description: 'End-to-end type safety across client, server, and database.', + subtitle: 'End-to-end type safety across client, server, and database.', + tocVideo: 'VSNgAIObBdw', + }, + remoteFile: 'generating-types.md', + }, + { + slug: 'testing', + meta: { + title: 'Automated testing using GitHub Actions', + description: 'Run your tests when you or your team make changes.', + subtitle: 'Run your tests when you or your team make changes.', + }, + remoteFile: 'testing.md', + }, + { + slug: 'backups', + meta: { + title: 'Automated backups using GitHub Actions', + description: 'Backup your database on a regular basis.', + subtitle: 'Backup your database on a regular basis.', + }, + remoteFile: 'backups.md', + }, +] + +type Params = { slug: string } + +const ActionDocs = async ({ params }: { params: Params }) => { + const { meta, ...data } = await getContent(params) + + const options = { + mdxOptions: { + remarkPlugins: [remarkMkDocsAdmonition, remarkPyMdownTabs, [removeTitle, meta.title]], + rehypePlugins: [[linkTransform, urlTransform], rehypeSlug], + }, + } as SerializeOptions + + return +} + +/** + * Fetch markdown from external repo + */ +const getContent = async ({ slug }: Params) => { + const page = pageMap.find(({ slug: validSlug }) => validSlug && validSlug === slug) + + if (!page) { + redirect(notFoundLink(`cli/github-action/${slug}`)) + } + + const { remoteFile, meta } = page + + const editLink = newEditLink(`${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}`) + + const response = await fetch( + `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/${remoteFile}` + ) + + const content = await response.text() + + return { + pathname: `/guides/cli/github-action/${slug}` satisfies `/${string}`, + meta, + content, + editLink, + } +} + +const urlTransform: UrlTransformFunction = (url) => { + try { + const externalSiteUrl = new URL(externalSite) + + const placeholderHostname = 'placeholder' + const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`) + + // Don't modify a url with a FQDN or a url that's only a hash + if (hostname !== placeholderHostname || pathname === '/') { + return url + } + + const relativePage = ( + pathname.endsWith('.md') + ? pathname.replace(/\.md$/, '') + : relative(externalSiteUrl.pathname, pathname) + ).replace(/^\//, '') + + const page = pageMap.find(({ remoteFile }) => `${relativePage}.md` === remoteFile) + + // If we have a mapping for this page, use the mapped path + if (page) { + return page.slug + hash + } + + // If we don't have this page in our docs, link to original docs + return `${externalSite}/${relativePage}${hash}` + } catch (err) { + console.error('Error transforming markdown URL', err) + return url + } +} + +const generateStaticParams = () => pageMap.map(({ slug }) => ({ slug })) +const generateMetadata = genGuideMeta(getContent) + +export default ActionDocs +export { generateStaticParams, generateMetadata } diff --git a/apps/docs/pages/guides/database/database-advisors.tsx b/apps/docs/app/guides/database/database-advisors/page.tsx similarity index 52% rename from apps/docs/pages/guides/database/database-advisors.tsx rename to apps/docs/app/guides/database/database-advisors/page.tsx index a699383dce3..2f9293e9295 100644 --- a/apps/docs/pages/guides/database/database-advisors.tsx +++ b/apps/docs/app/guides/database/database-advisors/page.tsx @@ -1,36 +1,34 @@ -import { CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx' import { Octokit } from '@octokit/core' import { capitalize } from 'lodash' -import { GetStaticProps, InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' -import { serialize } from 'next-mdx-remote/serialize' -import remarkGfm from 'remark-gfm' +import { type SerializeOptions } from 'next-mdx-remote/dist/types' import rehypeSlug from 'rehype-slug' - -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } - -import components from '~/components' import { Heading } from 'ui' -import { TabPanel, Tabs } from '~/components/Tabs' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template' +import { Tabs, TabPanel } from '~/features/ui/Tabs' + import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' // We fetch these docs at build time from an external repo -export const org = 'supabase' -export const repo = 'splinter' -export const branch = 'main' -export const docsDir = 'docs' +const org = 'supabase' +const repo = 'splinter' +const branch = 'main' +const docsDir = 'docs' const meta = { title: 'Performance and Security Advisors', subtitle: 'Check your database for performance and security issues', } -const editLink = 'https://github.com/supabase/splinter/tree/main/docs' +const generateMetadata = genGuideMeta(() => ({ + pathname: '/guides/database/database-linter', + meta, +})) + +const editLink = newEditLink('supabase/splinter/tree/main/docs') const markdownIntro = ` You can use the Database Performance and Security Advisors to check your database for issues such as missing indexes and improperly set-up RLS policies. @@ -40,15 +38,21 @@ You can use the Database Performance and Security Advisors to check your databas In the dashboard, navigate to [Security Advisor](https://supabase.com/dashboard/project/_/database/security-advisor) and [Performance Advisor](https://supabase.com/dashboard/project/_/database/performance-advisor) under Database. The advisors run automatically. You can also manually rerun them after you've resolved issues. `.trim() -const getBasename = (path: string) => path.split('/').at(-1).replace(/\.md$/, '') +const getBasename = (path: string) => path.split('/').at(-1)!.replace(/\.md$/, '') + +const DatabaseAdvisorDocs = async () => { + const { lints, lintsList } = await getLints() + + const options = { + mdxOptions: { + remarkPlugins: [remarkMkDocsAdmonition, remarkPyMdownTabs, [removeTitle, meta.title]], + rehypePlugins: [[linkTransform, urlTransform(lintsList)], rehypeSlug], + }, + } as SerializeOptions -export default function DatabaseAdvisorDocs({ - intro, - lints, -}: InferGetStaticPropsType) { return ( - - + + Available checks {lints.map((lint) => ( @@ -58,12 +62,12 @@ export default function DatabaseAdvisorDocs({ label={capitalize(getBasename(lint.path).replace(/_/g, ' '))} >
- +
))}
-
+
) } @@ -101,41 +105,10 @@ const urlTransform: (lints: Array<{ path: string }>) => UrlTransformFunction = ( } } -const transformMarkdown = async ( - rawContent: string, - { replacementLinks = [] }: { replacementLinks?: Array<{ path: string }> } = {} -) => { - const codeHikeOptions: CodeHikeConfig = { - theme: codeHikeTheme, - lineNumbers: true, - showCopyButton: true, - skipLanguages: [], - autoImport: false, - } - - const content = await serialize(rawContent, { - scope: { - chCodeConfig: codeHikeOptions, - }, - mdxOptions: { - remarkPlugins: [ - remarkGfm, - remarkMkDocsAdmonition, - remarkPyMdownTabs, - [removeTitle, meta.title], - [remarkCodeHike, codeHikeOptions], - ], - rehypePlugins: [[linkTransform, urlTransform(replacementLinks)], rehypeSlug], - }, - }) - - return content -} - /** - * Fetch markdown from external repo and transform links + * Fetch lint remediation Markdown from external repo */ -export const getStaticProps = (async () => { +const getLints = async () => { const octokit = new Octokit() const response = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', { @@ -154,37 +127,33 @@ export const getStaticProps = (async () => { if (!Array.isArray(response.data)) { throw Error( - `Reading a directory, not a file. Should not reach this, solely to appease Typescript.` + 'Reading a directory, not a file. Should not reach this, solely to appease Typescript.' ) } - const [intro, ...lints] = await Promise.all([ - await transformMarkdown(markdownIntro), - ...response.data - .filter(({ path }) => /docs\/\d+.+\.md$/.test(path)) - .map(async ({ path }, _, data) => { - const fileResponse = await fetch( - `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${path}` - ) + const lintsList = response.data.filter(({ path }) => /docs\/\d+.+\.md$/.test(path)) - if (fileResponse.status >= 400) { - throw Error(`Could not get contents of file ${org}/${repo}/${path}`) - } + const lints = await Promise.all( + lintsList.map(async ({ path }) => { + const fileResponse = await fetch( + `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${path}` + ) - const rawContent = await fileResponse.text() - const content = await transformMarkdown(rawContent, { replacementLinks: data }) + if (fileResponse.status >= 400) { + throw Error(`Could not get contents of file ${org}/${repo}/${path}`) + } - return { - path: getBasename(path), - content, - } - }), - ]) + const content = await fileResponse.text() - return { - props: { - intro, - lints, - }, - } -}) satisfies GetStaticProps + return { + path: getBasename(path), + content, + } + }) + ) + + return { lints, lintsList } +} + +export default DatabaseAdvisorDocs +export { generateMetadata } diff --git a/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx b/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx new file mode 100644 index 00000000000..f8be35c3f64 --- /dev/null +++ b/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx @@ -0,0 +1,220 @@ +import matter from 'gray-matter' +import { type SerializeOptions } from 'next-mdx-remote/dist/types' +import { readFile } from 'node:fs/promises' +import { join, relative } from 'node:path' +import rehypeSlug from 'rehype-slug' +import emoji from 'remark-emoji' +import { genGuideMeta, genGuidesStaticParams } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { GUIDES_DIRECTORY, isValidGuideFrontmatter } from '~/lib/docs' +import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' +import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' +import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' +import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' + +// We fetch these docs at build time from an external repo +const org = 'supabase' +const repo = 'wrappers' +const branch = 'main' +const docsDir = 'docs' +const externalSite = 'https://supabase.github.io/wrappers' + +// Each external docs page is mapped to a local page +const pageMap = [ + { + slug: 'airtable', + meta: { + title: 'Airtable', + }, + remoteFile: 'airtable.md', + }, + { + slug: 'auth0', + meta: { + title: 'Auth0', + }, + remoteFile: 'auth0.md', + }, + { + slug: 'bigquery', + meta: { + title: 'BigQuery', + }, + remoteFile: 'bigquery.md', + }, + { + slug: 'clickhouse', + meta: { + title: 'ClickHouse', + }, + remoteFile: 'clickhouse.md', + }, + { + slug: 'cognito', + meta: { + title: 'AWS Cognito', + }, + remoteFile: 'cognito.md', + }, + { + slug: 'firebase', + meta: { + title: 'Firebase', + }, + remoteFile: 'firebase.md', + }, + { + slug: 'logflare', + meta: { + title: 'Logflare', + }, + remoteFile: 'logflare.md', + }, + { + slug: 'mssql', + meta: { + title: 'MSSQL', + }, + remoteFile: 'mssql.md', + }, + { + slug: 'redis', + meta: { + title: 'Redis', + }, + remoteFile: 'redis.md', + }, + { + slug: 's3', + meta: { + title: 'AWS S3', + }, + remoteFile: 's3.md', + }, + { + slug: 'stripe', + meta: { + title: 'Stripe', + }, + remoteFile: 'stripe.md', + }, +] + +interface Params { + slug?: string[] +} + +const WrappersDocs = async ({ params }: { params: Params }) => { + const { isExternal, meta, ...data } = await getContent(params) + + const options = isExternal + ? ({ + mdxOptions: { + remarkPlugins: [ + remarkMkDocsAdmonition, + emoji, + remarkPyMdownTabs, + [removeTitle, meta.title], + ], + rehypePlugins: [[linkTransform, urlTransform], rehypeSlug], + }, + } as SerializeOptions) + : undefined + + return +} + +/** + * Fetch markdown from external repo + */ +const getContent = async (params: Params) => { + const federatedPage = pageMap.find( + ({ slug }) => params && slug && params.slug && slug === params.slug.at(0) + ) + + let isExternal: boolean + let meta: any + let content: string + let editLink: string + + if (!federatedPage) { + isExternal = false + editLink = `supabase/supabase/apps/docs/content/guides/database/extensions/wrappers${params.slug?.length ? `/${params.slug.join('/')}` : ''}.mdx` + const rawContent = await readFile( + join( + GUIDES_DIRECTORY, + 'database', + 'extensions', + `wrappers${params.slug?.length ? `/${params.slug.join('/')}` : ''}.mdx` + ), + 'utf-8' + ) + ;({ data: meta, content } = matter(rawContent)) + if (!isValidGuideFrontmatter(meta)) { + throw Error(`Expected valid frontmatter, got ${JSON.stringify(meta, null, 2)}`) + } + } else { + isExternal = true + let remoteFile: string + ;({ remoteFile, meta } = federatedPage) + const repoPath = `${org}/${repo}/${branch}/${docsDir}/${remoteFile}` + editLink = `${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}` + const response = await fetch(`https://raw.githubusercontent.com/${repoPath}`) + content = await response.text() + } + + return { + pathname: + `/guides/database/extensions/wrappers${params.slug?.length ? `/${params.slug.join('/')}` : ''}` satisfies `/${string}`, + isExternal, + editLink: newEditLink(editLink), + meta, + content, + } +} + +const urlTransform: UrlTransformFunction = (url) => { + try { + const externalSiteUrl = new URL(externalSite) + + const placeholderHostname = 'placeholder' + const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`) + + // Don't modify a url with a FQDN or a url that's only a hash + if (hostname !== placeholderHostname || pathname === '/') { + return url + } + const relativePage = ( + pathname.endsWith('.md') + ? pathname.replace(/\.md$/, '') + : relative(externalSiteUrl.pathname, pathname) + ).replace(/^\//, '') + + const page = pageMap.find(({ remoteFile }) => `${relativePage}.md` === remoteFile) + + // If we have a mapping for this page, use the mapped path + if (page) { + return page.slug + hash + } + + // If we don't have this page in our docs, link to original docs + return `${externalSite}/${relativePage}${hash}` + } catch (err) { + console.error('Error transforming markdown URL', err) + return url + } +} + +const generateStaticParams = async () => { + const mdxPaths = await genGuidesStaticParams('database/extensions/wrappers')() + const federatedPaths = pageMap.map(({ slug }) => ({ + slug: [slug], + })) + + return [...mdxPaths, ...federatedPaths] +} + +const generateMetadata = genGuideMeta(getContent) + +export default WrappersDocs +export { generateStaticParams, generateMetadata } diff --git a/apps/docs/app/guides/graphql/[[...slug]]/page.tsx b/apps/docs/app/guides/graphql/[[...slug]]/page.tsx new file mode 100644 index 00000000000..eec27cf55f2 --- /dev/null +++ b/apps/docs/app/guides/graphql/[[...slug]]/page.tsx @@ -0,0 +1,192 @@ +import { type SerializeOptions } from 'next-mdx-remote/dist/types' +import { redirect } from 'next/navigation' +import { isAbsolute, relative } from 'path' +import rehypeSlug from 'rehype-slug' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { notFoundLink } from '~/features/recommendations/NotFound.utils' +import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' +import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' +import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' +import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' + +// We fetch these docs at build time from an external repo +const org = 'supabase' +const repo = 'pg_graphql' +const branch = 'master' +const docsDir = 'docs' +const externalSite = 'https://supabase.github.io/pg_graphql' + +// Each external docs page is mapped to a local page +const pageMap = [ + { + meta: { + id: 'graphql-overview', + title: 'GraphQL', + subtitle: 'Autogenerated GraphQL APIs with Postgres.', + }, + remoteFile: 'supabase.md', + }, + { + slug: 'api', + meta: { + id: 'graphql-api', + title: 'GraphQL API', + subtitle: 'Understanding the core concepts of the GraphQL API.', + }, + remoteFile: 'api.md', + }, + { + slug: 'views', + meta: { + id: 'graphql-views', + title: 'Views', + subtitle: 'Using Postgres Views with GraphQL.', + }, + remoteFile: 'views.md', + }, + { + slug: 'functions', + meta: { + id: 'graphql-functions', + title: 'Functions', + subtitle: 'Using Postgres Functions with GraphQL.', + }, + remoteFile: 'functions.md', + }, + { + slug: 'computed-fields', + meta: { + id: 'graphql-computed-fields', + title: 'Computed Fields', + subtitle: 'Using Postgres Computed Fields with GraphQL.', + }, + remoteFile: 'computed-fields.md', + }, + { + slug: 'configuration', + meta: { + id: 'graphql-configuration', + title: 'Configuration & Customization', + subtitle: 'Extra configuration options can be set on SQL entities using comment directives.', + }, + remoteFile: 'configuration.md', + }, + { + slug: 'security', + meta: { + id: 'graphql-security', + title: 'Security', + subtitle: 'Securing your GraphQL API.', + }, + remoteFile: 'security.md', + }, + { + slug: 'with-apollo', + meta: { + id: 'graphql-with-apollo', + title: 'With Apollo', + subtitle: 'Using pg_grapqhl with Apollo.', + }, + remoteFile: 'usage_with_apollo.md', + }, + { + slug: 'with-relay', + meta: { + id: 'graphql-with-relay', + title: 'With Relay', + subtitle: 'Using pg_grapqhl with Relay.', + }, + remoteFile: 'usage_with_relay.md', + }, +] + +interface Params { + slug?: string[] +} + +const PGGraphQLDocs = async ({ params }: { params: Params }) => { + const { meta, ...data } = await getContent(params) + + const options = { + mdxOptions: { + remarkPlugins: [remarkMkDocsAdmonition, remarkPyMdownTabs, [removeTitle, meta.title]], + rehypePlugins: [[linkTransform, urlTransform], rehypeSlug], + }, + } as SerializeOptions + + return +} + +/** + * Fetch markdown from external repo and transform links + */ +const getContent = async ({ slug }: Params) => { + const page = pageMap.find((page) => page.slug === slug?.at(0)) + + if (!page) { + redirect(notFoundLink(`graphql/${slug ? slug.join('/') : ''}`)) + } + + const { remoteFile, meta } = page + + const editLink = newEditLink(`${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}`) + + const response = await fetch( + `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/${remoteFile}` + ) + + const content = await response.text() + + return { + pathname: `/guides/graphql${slug?.length ? `/${slug.join('/')}` : ''}` satisfies `/${string}`, + meta, + content, + editLink, + } +} + +const urlTransform: UrlTransformFunction = (url) => { + try { + const externalSiteUrl = new URL(externalSite) + + const placeholderHostname = 'placeholder' + const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`) + + // Don't modify a url with a FQDN or a url that's only a hash + if (hostname !== placeholderHostname || pathname === '/') { + return url + } + + const getRelativePath = () => { + if (pathname.endsWith('.md')) { + return pathname.replace(/\.md$/, '') + } + if (isAbsolute(url)) { + return relative(externalSiteUrl.pathname, pathname) + } + return pathname + } + + const relativePath = getRelativePath().replace(/^\//, '') + + const page = pageMap.find(({ remoteFile }) => `${relativePath}.md` === remoteFile) + + // If we have a mapping for this page, use the mapped path + if (page) { + return 'graphql/' + page.slug + hash + } + + // If we don't have this page in our docs, link to original docs + return `${externalSite}/${relativePath}${hash}` + } catch (err) { + console.error('Error transforming markdown URL', err) + return url + } +} + +const generateStaticParams = async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] })) +const generateMetadata = genGuideMeta(getContent) + +export default PGGraphQLDocs +export { generateStaticParams, generateMetadata } diff --git a/apps/docs/app/guides/layout.tsx b/apps/docs/app/guides/layout.tsx new file mode 100644 index 00000000000..20be5f75f9f --- /dev/null +++ b/apps/docs/app/guides/layout.tsx @@ -0,0 +1,50 @@ +'use client' + +import { usePathname } from 'next/navigation' +import { type PropsWithChildren } from 'react' +import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' +import Layout from '~/layouts/guides' + +const GuidesLayout = ({ children }: PropsWithChildren) => { + const pathname = usePathname() + const menuId = getMenuId(pathname) + + return {children} +} + +const getMenuId = (pathname: string | null) => { + pathname = (pathname ??= '').replace(/^\/guides\//, '') + + switch (true) { + case pathname.startsWith('ai'): + return MenuId.Ai + case pathname.startsWith('api'): + return MenuId.Api + case pathname.startsWith('auth'): + return MenuId.Auth + case pathname.startsWith('cli'): + return MenuId.Cli + case pathname.startsWith('database'): + return MenuId.Database + case pathname.startsWith('functions'): + return MenuId.Functions + case pathname.startsWith('getting-started'): + return MenuId.GettingStarted + case pathname.startsWith('graphql'): + return MenuId.Graphql + case pathname.startsWith('platform'): + return MenuId.Platform + case pathname.startsWith('realtime'): + return MenuId.Realtime + case pathname.startsWith('resources'): + return MenuId.Resources + case pathname.startsWith('self-hosting'): + return MenuId.SelfHosting + case pathname.startsWith('storage'): + return MenuId.Storage + default: + return MenuId.Home + } +} + +export default GuidesLayout diff --git a/apps/docs/pages/guides/platform/terraform/[[...slug]].tsx b/apps/docs/app/guides/platform/terraform/[[...slug]]/page.tsx similarity index 50% rename from apps/docs/pages/guides/platform/terraform/[[...slug]].tsx rename to apps/docs/app/guides/platform/terraform/[[...slug]]/page.tsx index 1eb6d14123f..79421d21f8b 100644 --- a/apps/docs/pages/guides/platform/terraform/[[...slug]].tsx +++ b/apps/docs/app/guides/platform/terraform/[[...slug]]/page.tsx @@ -1,27 +1,21 @@ -import { CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx' import matter from 'gray-matter' -import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' -import { serialize } from 'next-mdx-remote/serialize' -import remarkGfm from 'remark-gfm' +import { type SerializeOptions } from 'next-mdx-remote/dist/types' +import { redirect } from 'next/navigation' import rehypeSlug from 'rehype-slug' - -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { notFoundLink } from '~/features/recommendations/NotFound.utils' import { isValidGuideFrontmatter } from '~/lib/docs' import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' - -// We fetch these docs at build time from an external repo -export const org = 'supabase' -export const repo = 'terraform-provider-supabase' -export const branch = 'v1.1.3' -export const docsDir = 'docs' +import { + terraformDocsBranch, + terraformDocsDocsDir, + terraformDocsOrg, + terraformDocsRepo, +} from '../terraformConstants' // Each external docs page is mapped to a local page const pageMap = [ @@ -41,16 +35,21 @@ const pageMap = [ }, ] -export default function TerraformDocs({ - source, - meta, - editLink, -}: InferGetStaticPropsType) { - return ( - - - - ) +interface Params { + slug?: string[] +} + +const TerraformDocs = async ({ params }: { params: Params }) => { + const { meta, ...data } = await getContent(params) + + const options = { + mdxOptions: { + remarkPlugins: [remarkMkDocsAdmonition, remarkPyMdownTabs, [removeTitle, meta.title]], + rehypePlugins: [[linkTransform, urlTransform], rehypeSlug], + }, + } as SerializeOptions + + return } /** @@ -88,7 +87,7 @@ const urlTransform: UrlTransformFunction = (url: string) => { } // If we don't have this page in our docs, link to GitHub repo - return `https://github.com/${org}/${repo}/blob/${branch}${pathname}${hash}` + return `https://github.com/${terraformDocsOrg}/${terraformDocsRepo}/blob/${terraformDocsBranch}${pathname}${hash}` } catch (err) { console.error('Error transforming markdown URL', err) return url @@ -96,72 +95,47 @@ const urlTransform: UrlTransformFunction = (url: string) => { } /** - * Fetch markdown from external repo and transform links + * Fetch markdown from external repo */ -export const getStaticProps = (async ({ params }) => { - const [slug] = params.slug ?? [] - const page = pageMap.find((page) => page.slug === slug) +const getContent = async ({ slug }: Params) => { + const [requestedSlug] = slug ?? [] + const page = pageMap.find((page) => page.slug === requestedSlug) if (!page) { - throw new Error(`No page mapping found for slug '${slug}'`) + redirect(notFoundLink('platform/terraform' + (slug?.join('/') ?? ''))) } const { meta, remoteFile, useRoot } = page - let response = await fetch( - `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${useRoot ? '' : `${docsDir}/`}${remoteFile}` + const editLink = newEditLink( + `${terraformDocsOrg}/${terraformDocsRepo}/blob/${terraformDocsBranch}/${useRoot ? '' : `${terraformDocsDocsDir}/`}${remoteFile}` ) - let content = await response.text() + let response = await fetch( + `https://raw.githubusercontent.com/${terraformDocsOrg}/${terraformDocsRepo}/${terraformDocsBranch}/${useRoot ? '' : `${terraformDocsDocsDir}/`}${remoteFile}` + ) + + let rawContent = await response.text() // Strip out HTML comments - content = content.replace(//, '') - const { content: source, data } = matter(content) + rawContent = rawContent.replace(//, '') + const { content, data } = matter(rawContent) Object.assign(meta, data) if (!isValidGuideFrontmatter(meta)) { throw Error('Guide frontmatter is invalid.') } - const codeHikeOptions: CodeHikeConfig = { - theme: codeHikeTheme, - lineNumbers: true, - showCopyButton: true, - skipLanguages: [], - autoImport: false, - } - - const mdxSource = await serialize(source, { - scope: { - chCodeConfig: codeHikeOptions, - }, - mdxOptions: { - remarkPlugins: [ - remarkGfm, - remarkMkDocsAdmonition, - remarkPyMdownTabs, - [removeTitle, meta.title], - [remarkCodeHike, codeHikeOptions], - ], - rehypePlugins: [[linkTransform, urlTransform], rehypeSlug], - }, - }) - return { - props: { - source: mdxSource, - meta, - editLink: `${org}/${repo}/blob/${branch}/${docsDir}/${slug}.md`, - }, + pathname: + `/guides/platform/terraform${slug?.length ? `/${slug.join('/')}` : ''}` satisfies `/${string}`, + meta, + content, + editLink, } -}) satisfies GetStaticProps +} -export const getStaticPaths = (async () => { - return { - paths: pageMap.map(({ slug }) => ({ - params: { - slug: slug ? [slug] : [], - }, - })), - fallback: false, - } -}) satisfies GetStaticPaths +const generateStaticParams = async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] })) +const generateMetadata = genGuideMeta(getContent) + +export default TerraformDocs +export { generateStaticParams, generateMetadata } diff --git a/apps/docs/pages/guides/platform/terraform/reference.tsx b/apps/docs/app/guides/platform/terraform/reference/page.tsx similarity index 88% rename from apps/docs/pages/guides/platform/terraform/reference.tsx rename to apps/docs/app/guides/platform/terraform/reference/page.tsx index 8004ab606c9..0268a3a21d7 100644 --- a/apps/docs/pages/guides/platform/terraform/reference.tsx +++ b/apps/docs/app/guides/platform/terraform/reference/page.tsx @@ -1,22 +1,33 @@ import { codeBlock } from 'common-tags' -import { GetStaticProps, InferGetStaticPropsType } from 'next' +import { Check, PlusCircle } from 'lucide-react' import Link from 'next/link' import ReactMarkdown from 'react-markdown' - import { CodeBlock, Heading, - IconCheck, - IconPlusCircle, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Popover_Shadcn_, - Tabs, } from 'ui' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { TabPanel, Tabs } from '~/features/ui/Tabs' +import { + terraformDocsBranch, + terraformDocsDocsDir, + terraformDocsOrg, + terraformDocsRepo, +} from '../terraformConstants' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { org, repo, branch, docsDir } from './[[...slug]]' +const meta = { + title: 'Terraform Provider reference', + subtitle: 'Resources and data sources available through the Terraform Provider', +} + +const generateMetadata = genGuideMeta(() => ({ + pathname: '/guides/platform/terraform/reference', + meta, +})) function ProviderSettings({ schema }: { schema: any }) { const attributes = schema.block.attributes @@ -63,7 +74,7 @@ function ProviderSettings({ schema }: { schema: any }) { {attributes[attribute].optional && ( <> - + true )} @@ -71,7 +82,7 @@ function ProviderSettings({ schema }: { schema: any }) { {attributes[attribute].sensitive && ( <> - + true )} @@ -92,7 +103,7 @@ function Resources({ schema }: { schema: any }) {

You can configure these resources using the Supabase Terraform provider:

{Object.keys(schema).map((resource) => ( - + Example usage {codeBlock` resource "${resource}" " + ))} @@ -216,7 +227,7 @@ function DataSources({ schema }: { schema: any }) {

You can read these resources using the Supabase Terraform provider:

{Object.keys(schema).map((dataSource) => ( - + Example usage {codeBlock` resource "${dataSource}" "all" { @@ -263,7 +274,7 @@ function DataSources({ schema }: { schema: any }) { @@ -306,7 +317,7 @@ function DataSources({ schema }: { schema: any }) { {schema[dataSource].block.attributes[attribute].required && ( <> - + true )} @@ -314,7 +325,7 @@ function DataSources({ schema }: { schema: any }) { {schema[dataSource].block.attributes[attribute].optional && ( <> - + true )} @@ -322,7 +333,7 @@ function DataSources({ schema }: { schema: any }) { {schema[dataSource].block.attributes[attribute].computed && ( <> - + true )} @@ -331,19 +342,20 @@ function DataSources({ schema }: { schema: any }) { ))} - + ))} ) } -export default function PGGraphQLDocs({ - meta, - schema, -}: InferGetStaticPropsType) { +const TerraformReferencePage = async () => { + const { schema } = await getSchema() + + const editLink = newEditLink('supabase/terraform-provider-supabase') + return ( - + The Terraform Provider provices access to{' '} - +
) } /** * Fetch JSON schema from external repo */ -export const getStaticProps = (async () => { - const meta = { - title: 'Terraform Provider reference', - subtitle: 'Resources and data sources available through the Terraform Provider', - } - +const getSchema = async () => { let response = await fetch( - `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/schema.json` + `https://raw.githubusercontent.com/${terraformDocsOrg}/${terraformDocsRepo}/${terraformDocsBranch}/${terraformDocsDocsDir}/schema.json` ) if (!response.ok) throw Error('Failed to fetch Terraform JSON schema from GitHub') const schema = await response.json() return { - props: { - meta, - schema, - }, + schema, } -}) satisfies GetStaticProps +} + +export default TerraformReferencePage +export { generateMetadata } diff --git a/apps/docs/app/guides/platform/terraform/terraformConstants.ts b/apps/docs/app/guides/platform/terraform/terraformConstants.ts new file mode 100644 index 00000000000..0cc7ad3125b --- /dev/null +++ b/apps/docs/app/guides/platform/terraform/terraformConstants.ts @@ -0,0 +1,9 @@ +/** + * Information on where to fetch docs content + */ +const terraformDocsOrg = 'supabase' +const terraformDocsRepo = 'terraform-provider-supabase' +const terraformDocsBranch = 'v1.1.3' +const terraformDocsDocsDir = 'docs' + +export { terraformDocsOrg, terraformDocsRepo, terraformDocsBranch, terraformDocsDocsDir } diff --git a/apps/docs/app/guides/self-hosting/analytics/config/page.tsx b/apps/docs/app/guides/self-hosting/analytics/config/page.tsx new file mode 100644 index 00000000000..fa67144d69a --- /dev/null +++ b/apps/docs/app/guides/self-hosting/analytics/config/page.tsx @@ -0,0 +1,63 @@ +import Param from '~/components/Params' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template' +import { getAnalyticsConfigV0 } from '~/lib/mdx/getConfig' + +const meta = { + title: 'Analytics Self-hosting Config', + description: 'How to configure and deploy Supabase Analytics.', +} + +const generateMetadata = genGuideMeta(() => ({ + pathname: '/guides/self-hosting/analytics/config', + meta, +})) + +const AnalyticsConfigPage = async () => { + const spec = getAnalyticsConfigV0() + const descriptionMdx = spec.info.description + + return ( + + + +
+ {spec.info.tags.map((tag: ReturnType['info']['tags']) => { + return ( + <> +

{tag.title}

+

{tag.description}

+
+
Parameters
+
    + {spec.parameters + .filter((param: ReturnType['parameters']) => + param.tags.includes(tag.id) + ) + .map((param: ReturnType['parameters']) => { + return ( + + ) + })} +
+
+ + ) + })} +
+
+ ) +} + +export default AnalyticsConfigPage +export { generateMetadata } diff --git a/apps/docs/app/guides/self-hosting/auth/config/page.tsx b/apps/docs/app/guides/self-hosting/auth/config/page.tsx new file mode 100644 index 00000000000..3285ed9af36 --- /dev/null +++ b/apps/docs/app/guides/self-hosting/auth/config/page.tsx @@ -0,0 +1,63 @@ +import Param from '~/components/Params' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template' +import { getAuthConfigV1 } from '~/lib/mdx/getConfig' + +const meta = { + title: 'Auth Self-hosting Config', + description: 'How to configure and deploy Supabase Auth.', +} + +const generateMetadata = genGuideMeta(() => ({ + pathname: '/guides/self-hosting/auth/config', + meta, +})) + +const AuthConfigPage = async () => { + const spec = getAuthConfigV1() + const descriptionMdx = spec.info.description + + return ( + + + +
+ {spec.info.tags.map((tag: ReturnType['info']['tags']) => { + return ( + <> +

{tag.title}

+

{tag.description}

+
+
Parameters
+
    + {spec.parameters + .filter((param: ReturnType['parameters']) => + param.tags.includes(tag.id) + ) + .map((param: ReturnType['parameters']) => { + return ( + + ) + })} +
+
+ + ) + })} +
+
+ ) +} + +export default AuthConfigPage +export { generateMetadata } diff --git a/apps/docs/app/guides/self-hosting/realtime/config/page.tsx b/apps/docs/app/guides/self-hosting/realtime/config/page.tsx new file mode 100644 index 00000000000..5e5abbc20b6 --- /dev/null +++ b/apps/docs/app/guides/self-hosting/realtime/config/page.tsx @@ -0,0 +1,63 @@ +import Param from '~/components/Params' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template' +import { getRealtimeConfigV0 } from '~/lib/mdx/getConfig' + +const meta = { + title: 'Realtime Self-hosting Config', + description: 'How to configure and deploy Supabase Realtime.', +} + +const generateMetadata = genGuideMeta(() => ({ + pathname: '/guides/self-hosting/realtime/config', + meta, +})) + +const RealtimeConfigPage = async () => { + const spec = getRealtimeConfigV0() + const descriptionMdx = spec.info.description + + return ( + + + +
+ {spec.info.tags.map((tag: ReturnType['info']['tags']) => { + return ( + <> +

{tag.title}

+

{tag.description}

+
+
Parameters
+
    + {spec.parameters + .filter((param: ReturnType['parameters']) => + param.tags.includes(tag.id) + ) + .map((param: ReturnType['parameters']) => { + return ( + + ) + })} +
+
+ + ) + })} +
+
+ ) +} + +export default RealtimeConfigPage +export { generateMetadata } diff --git a/apps/docs/app/guides/self-hosting/storage/config/page.tsx b/apps/docs/app/guides/self-hosting/storage/config/page.tsx new file mode 100644 index 00000000000..874c3e02fda --- /dev/null +++ b/apps/docs/app/guides/self-hosting/storage/config/page.tsx @@ -0,0 +1,63 @@ +import Param from '~/components/Params' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template' +import { getStorageConfigV0 } from '~/lib/mdx/getConfig' + +const meta = { + title: 'Storage Self-hosting Config', + description: 'How to configure and deploy Supabase Storage.', +} + +const generateMetadata = genGuideMeta(() => ({ + pathname: '/guides/self-hosting/storage/config', + meta, +})) + +const StorageConfigPage = async () => { + const spec = getStorageConfigV0() + const descriptionMdx = spec.info.description + + return ( + + + +
+ {spec.info.tags.map((tag: ReturnType['info']['tags']) => { + return ( + <> +

{tag.title}

+

{tag.description}

+
+
Parameters
+
    + {spec.parameters + .filter((param: ReturnType['parameters']) => + param.tags.includes(tag.id) + ) + .map((param: ReturnType['parameters']) => { + return ( + + ) + })} +
+
+ + ) + })} +
+
+ ) +} + +export default StorageConfigPage +export { generateMetadata } diff --git a/apps/docs/app/not-found/layout.tsx b/apps/docs/app/not-found/layout.tsx new file mode 100644 index 00000000000..fe86169838e --- /dev/null +++ b/apps/docs/app/not-found/layout.tsx @@ -0,0 +1,21 @@ +import { type Metadata } from 'next' +import { type PropsWithChildren } from 'react' +import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' +import { LayoutMainContent } from '~/layouts/DefaultLayout' +import { MainSkeleton } from '~/layouts/MainSkeleton' + +const metadata: Metadata = { + title: 'Not found', + robots: { + index: false, + }, +} + +const NotFoundLayout = ({ children }: PropsWithChildren) => ( + + {children} + +) + +export default NotFoundLayout +export { metadata } diff --git a/apps/docs/app/not-found/page.tsx b/apps/docs/app/not-found/page.tsx new file mode 100644 index 00000000000..1863e98671d --- /dev/null +++ b/apps/docs/app/not-found/page.tsx @@ -0,0 +1,38 @@ +import { createClient } from '@supabase/supabase-js' +import { type Metadata } from 'next' + +import { DocsSearchResult, type Database } from 'common' + +import { NotFound } from '~/features/recommendations/NotFound.client' + +const metadata: Metadata = { + robots: { + index: false, + follow: false, + }, +} + +const NotFoundPage = async ({ searchParams: { page } }: { searchParams: { page?: string } }) => { + const recommendations = page ? await getRecommendations(page) : null + + return +} + +const getRecommendations = async (page: string) => { + const query = decodeURIComponent(page.replace(/^\/(?:guides|reference)\//, '')).replace( + /[_\/-]/g, + ' ' + ) + if (!query) return null + + const supabase = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! + ) + const { data, error } = await supabase.rpc('docs_search_fts', { query }) + if (error || !data?.length) return null + return data as Omit[] +} + +export default NotFoundPage +export { metadata } diff --git a/apps/docs/components/ApiSchema.tsx b/apps/docs/components/ApiSchema.tsx index 98ee6ab01e2..42a5b195566 100644 --- a/apps/docs/components/ApiSchema.tsx +++ b/apps/docs/components/ApiSchema.tsx @@ -1,6 +1,6 @@ import { CodeBlock } from '@ui/components/CodeBlock' import { sample } from '@har-sdk/openapi-sampler' -import { Tabs, TabPanel } from '~/components/Tabs' +import { Tabs, TabPanel } from '~/features/ui/Tabs' type IParamProps = any diff --git a/apps/docs/components/AppleSecretGenerator/index.tsx b/apps/docs/components/AppleSecretGenerator/index.tsx index ea66eadd23a..ea556ac7de9 100644 --- a/apps/docs/components/AppleSecretGenerator/index.tsx +++ b/apps/docs/components/AppleSecretGenerator/index.tsx @@ -1,11 +1,9 @@ -import { lazy, Suspense } from 'react' +'use client' -const Generator = lazy(() => import('./AppleSecretGenerator')) +import dynamic from 'next/dynamic' + +const Generator = dynamic(() => import('./AppleSecretGenerator'), { ssr: false }) export function AppleSecretGenerator() { - return ( - - - - ) + return } diff --git a/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx b/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx index e0e74bcd7ac..2039a78854e 100644 --- a/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx +++ b/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx @@ -1,3 +1,5 @@ +'use client' + import { useEffect, useReducer, useRef } from 'react' import { PhoneLoginsItems } from '../Navigation/NavigationMenu/NavigationMenu.constants' import { IconPanel } from 'ui-patterns/IconPanel' diff --git a/apps/docs/components/ButtonCard.tsx b/apps/docs/components/ButtonCard.tsx index 5180cc90786..ecc50fd15c0 100644 --- a/apps/docs/components/ButtonCard.tsx +++ b/apps/docs/components/ButtonCard.tsx @@ -1,6 +1,7 @@ import React, { FC } from 'react' import Image from 'next/legacy/image' import Link from 'next/link' +import { cn } from 'ui' interface Props { title: string @@ -9,6 +10,7 @@ interface Props { icon?: string | any children?: any layout?: 'vertical' | 'horizontal' + className?: string } const ButtonCard: FC = ({ @@ -18,14 +20,16 @@ const ButtonCard: FC = ({ description = '', to, layout = 'vertical', + className, }) => { return ( {children ? ( children diff --git a/apps/docs/components/Extensions/index.tsx b/apps/docs/components/Extensions/index.tsx index e2debfda82f..466582bc117 100644 --- a/apps/docs/components/Extensions/index.tsx +++ b/apps/docs/components/Extensions/index.tsx @@ -1,3 +1,5 @@ +'use client' + import { lazy, Suspense } from 'react' const ExtensionsInternal = lazy(() => import('./Extensions')) diff --git a/apps/docs/components/Feedback/Feedback.tsx b/apps/docs/components/Feedback/Feedback.tsx index 3b86b5dabe1..cd0a6341f8b 100644 --- a/apps/docs/components/Feedback/Feedback.tsx +++ b/apps/docs/components/Feedback/Feedback.tsx @@ -74,7 +74,7 @@ function Feedback() { const [modalOpen, setModalOpen] = useState(false) const feedbackButtonRef = useRef(null) - const pathname = usePathname() + const pathname = usePathname() ?? '' const sendTelemetryEvent = useSendTelemetryEvent() const { mutate: sendFeedbackComment } = useSendFeedbackMutation() const supabase = useSupabaseClient() diff --git a/apps/docs/components/GuidesTableOfContents.tsx b/apps/docs/components/GuidesTableOfContents.tsx index 1125f974076..40d368a0f5b 100644 --- a/apps/docs/components/GuidesTableOfContents.tsx +++ b/apps/docs/components/GuidesTableOfContents.tsx @@ -1,3 +1,5 @@ +'use client' + import { usePathname } from 'next/navigation' import { useEffect, useState } from 'react' import { cn } from 'ui' @@ -20,7 +22,7 @@ const formatSlug = (slug: string) => { const formatTOCHeader = (content: string) => { let begin = false - const res = [] + const res: Array = [] for (const x of content) { if (x === '`') { if (!begin) { @@ -52,17 +54,24 @@ const useTocRerenderTrigger = () => { return toggleRenderFlag } +interface TOCHeader { + id?: string + text: string + link: string + level: number +} + const GuidesTableOfContents = ({ className, overrideToc, video, }: { className?: string - overrideToc?: Array<{ text: string; link: string; level: number }> + overrideToc?: Array video?: string }) => { useSubscribeTocRerender() - const [tocList, setTocList] = useState([]) + const [tocList, setTocList] = useState([]) const pathname = usePathname() const [hash] = useHash() @@ -87,9 +96,10 @@ const GuidesTableOfContents = ({ if (!link) return null const level = heading.tagName === 'H2' ? 2 : 3 - return { text, link, level } + + return { text, link, level } as Partial }) - .filter(Boolean) + .filter((x): x is TOCHeader => !!x.text && !!x.link && !!x.level) setTocList(newHeadings) }) @@ -140,3 +150,4 @@ const GuidesTableOfContents = ({ export default GuidesTableOfContents export { useTocRerenderTrigger } +export type { TOCHeader } diff --git a/apps/docs/components/JwtGenerator/index.tsx b/apps/docs/components/JwtGenerator/index.tsx index 0896688868a..d104d40a779 100644 --- a/apps/docs/components/JwtGenerator/index.tsx +++ b/apps/docs/components/JwtGenerator/index.tsx @@ -1,3 +1,5 @@ +'use client' + import dynamic from 'next/dynamic' import { Suspense } from 'react' diff --git a/apps/docs/components/MDX/partials.tsx b/apps/docs/components/MDX/partials.tsx new file mode 100644 index 00000000000..982846bd40d --- /dev/null +++ b/apps/docs/components/MDX/partials.tsx @@ -0,0 +1,27 @@ +'use client' + +import AuthRateLimits from './auth_rate_limits.mdx' +import DatabaseSetup from './database_setup.mdx' +import GetSessionWarning from './get_session_warning.mdx' +import HuggingFaceDeployment from './ai/quickstart_hf_deployment.mdx' +import KotlinProjectSetup from './kotlin_project_setup.mdx' +import MigrationWarnings from './migration_warnings.mdx' +import OAuthPkceFlow from './oauth_pkce_flow.mdx' +import ProjectSetup from './project_setup.mdx' +import QuickstartIntro from './quickstart_intro.mdx' +import SocialProviderSettingsSupabase from './social_provider_settings_supabase.mdx' +import SocialProviderSetup from './social_provider_setup.mdx' + +export { + AuthRateLimits, + DatabaseSetup, + GetSessionWarning, + HuggingFaceDeployment, + KotlinProjectSetup, + MigrationWarnings, + ProjectSetup, + OAuthPkceFlow, + QuickstartIntro, + SocialProviderSettingsSupabase, + SocialProviderSetup, +} diff --git a/apps/docs/components/Mermaid/index.tsx b/apps/docs/components/Mermaid/index.tsx index b73a5d8033d..784ad81bf51 100644 --- a/apps/docs/components/Mermaid/index.tsx +++ b/apps/docs/components/Mermaid/index.tsx @@ -1,3 +1,5 @@ +'use client' + import { Suspense, lazy } from 'react' const MermaidHeavy = lazy(() => import('./Mermaid')) diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenuIconPicker.tsx b/apps/docs/components/Navigation/NavigationMenu/HomeMenuIconPicker.tsx index 138e0d30f36..af631de7015 100644 --- a/apps/docs/components/Navigation/NavigationMenu/HomeMenuIconPicker.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/HomeMenuIconPicker.tsx @@ -28,7 +28,7 @@ import { IconMenuDevCli, } from './HomeMenuIcons' -function getMenuIcon(menuKey: string, width: number = 16, height: number = 16, className: string) { +function getMenuIcon(menuKey: string, width: number = 16, height: number = 16, className?: string) { switch (menuKey) { case 'home': return diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx index 3b8556bd348..610050fd019 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx @@ -247,7 +247,9 @@ function getMenuById(id: MenuId) { return menus.find((menu) => menu.id === id) ?? menus.find((menu) => menu.id === MenuId.Home) } -function getMenuElement(menu: Menu) { +function getMenuElement(menu: Menu | undefined) { + if (!menu) throw Error('No menu found for this menuId') + const menuType = menu.type switch (menuType) { case 'home': diff --git a/apps/docs/components/Navigation/SideBar.tsx b/apps/docs/components/Navigation/SideBar.tsx index e1b957d095e..5ebb66b82de 100644 --- a/apps/docs/components/Navigation/SideBar.tsx +++ b/apps/docs/components/Navigation/SideBar.tsx @@ -1,6 +1,6 @@ import Link from 'next/link' import Image from 'next/legacy/image' -import { useRouter } from 'next/router' +import { usePathname } from 'next/navigation' import { IconChevronRight, IconArrowLeft } from '~/../../packages/ui' import { REFERENCES } from './NavigationMenu/NavigationMenu.constants' @@ -8,8 +8,8 @@ import { NavMenuGroup, NavMenuSection } from './Navigation.types' import * as Accordion from '@radix-ui/react-accordion' const SideBar = ({ menuItems = [] }: { menuItems: any }) => { - const { asPath } = useRouter() - const pathSegments = asPath.split('/') + const pathname = usePathname() + const pathSegments = pathname.split('/') const isInReferencePages = pathSegments.includes('reference') && pathSegments.length >= 3 const referenceMeta = pathSegments.length >= 3 ? REFERENCES[pathSegments[2]] : undefined @@ -18,11 +18,11 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => { const foundItem = group.items.find((section) => { if (section.items.length > 0) { const foundSubItem = section.items.find((item) => { - if (item.url === asPath) return item + if (item.url === pathname) return item }) if (foundSubItem) return section } else { - if (section.url === asPath) return section + if (section.url === pathname) return section } }) if (foundItem) return group @@ -35,7 +35,7 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => { return undefined } else { return section.items.find((item) => { - if (item.url === asPath) return item + if (item.url === pathname) return item }) } }) @@ -79,7 +79,7 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => { className={[ 'py-1.5 px-5 rounded text-sm transition', `${ - item.url === asPath + item.url === pathname ? 'bg-background text-brand-link' : 'text-foreground-light hover:text-foreground' }`, @@ -118,7 +118,7 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => { className={[ 'py-1.5 px-5 rounded text-sm transition', `${ - section.url === asPath + section.url === pathname ? 'bg-background text-brand' : 'text-foreground-light hover:text-foreground' }`, @@ -156,7 +156,7 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => { className={[ 'py-1.5 ml-4 px-5 rounded text-sm transition', `${ - item.url === asPath + item.url === pathname ? 'bg-background text-brand' : 'text-foreground-light hover:text-foreground' }`, diff --git a/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.tsx b/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.tsx index a9f18ad4e30..638218e3f06 100644 --- a/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.tsx +++ b/apps/docs/components/ProjectConfigVariables/ProjectConfigVariables.tsx @@ -1,3 +1,5 @@ +'use client' + import { useIsLoggedIn, useIsUserLoading } from 'common' import Link from 'next/link' import { useEffect, useMemo } from 'react' diff --git a/apps/docs/components/RealtimeLimitsEstimator/index.tsx b/apps/docs/components/RealtimeLimitsEstimator/index.tsx index 34fe3d20a42..aaabdd71fb0 100644 --- a/apps/docs/components/RealtimeLimitsEstimator/index.tsx +++ b/apps/docs/components/RealtimeLimitsEstimator/index.tsx @@ -1,3 +1,5 @@ +'use client' + import { lazy, Suspense } from 'react' const Estimator = lazy(() => import('./RealtimeLimitsEstimator')) diff --git a/apps/docs/components/RefVersionDropdown.tsx b/apps/docs/components/RefVersionDropdown.tsx index dbe99840640..19c92b28898 100644 --- a/apps/docs/components/RefVersionDropdown.tsx +++ b/apps/docs/components/RefVersionDropdown.tsx @@ -1,4 +1,4 @@ -import { useRouter } from 'next/router' +import { usePathname, useRouter } from 'next/navigation' import { Badge, DropdownMenu, @@ -11,8 +11,9 @@ import { import { REFERENCES } from './Navigation/NavigationMenu/NavigationMenu.constants' const RevVersionDropdown = () => { - const { asPath, push } = useRouter() - const pathSegments = asPath.split('/') + const pathname = usePathname() + const { push } = useRouter() + const pathSegments = pathname.split('/') const library = pathSegments.length >= 3 ? pathSegments[2] : undefined const libraryMeta = REFERENCES?.[library] ?? undefined diff --git a/apps/docs/components/StepHikeCompact/index.tsx b/apps/docs/components/StepHikeCompact/index.tsx index fd346c00711..a20e5f8d4bb 100644 --- a/apps/docs/components/StepHikeCompact/index.tsx +++ b/apps/docs/components/StepHikeCompact/index.tsx @@ -1,4 +1,3 @@ -//import { Step } from 'next-seo/lib/types' import { FC, PropsWithChildren } from 'react' import { cn } from 'ui' diff --git a/apps/docs/components/index.tsx b/apps/docs/components/index.tsx index 620513e9522..12cebd30057 100644 --- a/apps/docs/components/index.tsx +++ b/apps/docs/components/index.tsx @@ -7,7 +7,7 @@ import Link from 'next/link' import { Accordion, Admonition, Alert, Button, CodeBlock, Image, markdownComponents } from 'ui' import { GlassPanel } from 'ui-patterns/GlassPanel' import { IconPanel } from 'ui-patterns/IconPanel' -import { TabPanel, Tabs } from '~/components/Tabs' +import { TabPanel, Tabs } from '~/features/ui/Tabs' // Common components import { CH } from '@code-hike/mdx/components' diff --git a/apps/docs/components/reference/ApiOperationSection.tsx b/apps/docs/components/reference/ApiOperationSection.tsx index f6911560c9c..e1bb2f03efc 100644 --- a/apps/docs/components/reference/ApiOperationSection.tsx +++ b/apps/docs/components/reference/ApiOperationSection.tsx @@ -1,7 +1,7 @@ import { CodeBlock } from 'ui' import Param from '~/components/Params' +import { Tabs, TabPanel } from '~/features/ui/Tabs' import RefSubLayout from '~/layouts/ref/RefSubLayout' -import { Tabs, TabPanel } from '~/components/Tabs' import { ReactMarkdown } from 'react-markdown/lib/react-markdown' const ApiOperationSection = (props) => { diff --git a/apps/docs/components/reference/CLICommandSection.tsx b/apps/docs/components/reference/CLICommandSection.tsx index 6997aeaca7c..94540d42299 100644 --- a/apps/docs/components/reference/CLICommandSection.tsx +++ b/apps/docs/components/reference/CLICommandSection.tsx @@ -1,10 +1,10 @@ import ReactMarkdown from 'react-markdown' import { CodeBlock, IconChevronRight } from 'ui' -import spec from '~/spec/cli_v1_commands.yaml' assert { type: 'yml' } import Options from '~/components/Options' import Param from '~/components/Params' -import { Tabs, TabPanel } from '~/components/Tabs' +import { Tabs, TabPanel } from '~/features/ui/Tabs' import RefSubLayout from '~/layouts/ref/RefSubLayout' +import spec from '~/spec/cli_v1_commands.yaml' assert { type: 'yml' } import RefDetailCollapse from './RefDetailCollapse' export type Flag = { diff --git a/apps/docs/components/reference/OldVersionAlert.tsx b/apps/docs/components/reference/OldVersionAlert.tsx index 993dbd0fb4d..d1f1c28e86a 100644 --- a/apps/docs/components/reference/OldVersionAlert.tsx +++ b/apps/docs/components/reference/OldVersionAlert.tsx @@ -1,6 +1,7 @@ import Link from 'next/link' -import { useRouter } from 'next/router' +import { usePathname } from 'next/navigation' import { Admonition } from 'ui-patterns/admonition' + import { useMenuActiveRefId } from '~/hooks/useMenuState' import { ICommonSection } from './Reference.types' @@ -9,13 +10,13 @@ export interface OldVersionAlertProps { } const OldVersionAlert = ({ sections }: OldVersionAlertProps) => { - const router = useRouter() + const pathname = usePathname() const activeRefId = useMenuActiveRefId() const activeSection = sections.find(({ id }) => id === activeRefId) // Remove the version number from URL to get the latest - const latestVersionUrl = router.asPath + const latestVersionUrl = pathname .split('/') .slice(0, -2) .concat(activeSection ? [activeSection.slug] : []) diff --git a/apps/docs/components/reference/RefSEO.tsx b/apps/docs/components/reference/RefSEO.tsx index 237ecec3f15..6a468b713c3 100644 --- a/apps/docs/components/reference/RefSEO.tsx +++ b/apps/docs/components/reference/RefSEO.tsx @@ -1,5 +1,5 @@ import { NextSeo } from 'next-seo' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' function RefSEO({ title }) { const router = useRouter() diff --git a/apps/docs/components/reference/RefSectionHandler.tsx b/apps/docs/components/reference/RefSectionHandler.tsx index b3b189c9ce0..7ab81c7554b 100644 --- a/apps/docs/components/reference/RefSectionHandler.tsx +++ b/apps/docs/components/reference/RefSectionHandler.tsx @@ -1,5 +1,5 @@ +import { useRouter } from 'next/compat/router' import Head from 'next/head' -import { useRouter } from 'next/router' import { useEffect } from 'react' import { type MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' diff --git a/apps/docs/content/guides/api/sql-to-rest.mdx b/apps/docs/content/guides/api/sql-to-rest.mdx index 4884c7d80e9..e96e844932b 100644 --- a/apps/docs/content/guides/api/sql-to-rest.mdx +++ b/apps/docs/content/guides/api/sql-to-rest.mdx @@ -14,19 +14,17 @@ PostgREST supports a subset of SQL, so not all SQL queries will translate. diff --git a/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx b/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx index c45b58f4bef..8898fd846eb 100644 --- a/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx @@ -42,7 +42,7 @@ This renders the Auth component without any styling. We recommend using one of the predefined themes to style the UI. Import the theme you want to use and pass it to the `appearance.theme` prop. -```js mark=4,16 /src/index.js +```js import { Auth } from '@supabase/auth-ui-react' import { // Import predefined theme @@ -67,7 +67,7 @@ const App = () => ( The Auth component also supports login with [official social providers](../../auth#providers). -```js mark=11 /src/index.js +```js import { createClient } from '@supabase/supabase-js' import { Auth } from '@supabase/auth-ui-react' import { ThemeSupa } from '@supabase/auth-ui-shared' @@ -146,7 +146,7 @@ There are several ways to customize Auth UI: Auth UI comes with several themes to customize the appearance. Each predefined theme comes with at least two variations, a `default` variation, and a `dark` variation. You can switch between these themes using the `theme` prop. Import the theme you want to use and pass it to the `appearance.theme` prop. -```js mark=3,14 /src/index.js +```js import { createClient } from '@supabase/supabase-js' import { Auth } from '@supabase/auth-ui-react' import { ThemeSupa } from '@supabase/auth-ui-shared' @@ -175,7 +175,7 @@ Currently there is only one predefined theme available, but we plan to add more. Auth UI comes with two theme variations: `default` and `dark`. You can switch between these themes with the `theme` prop. -```js mark=15 /src/index.js +```js import { createClient } from '@supabase/supabase-js' import { Auth } from '@supabase/auth-ui-react' import { ThemeSupa } from '@supabase/auth-ui-shared' @@ -201,7 +201,7 @@ If you don't pass a value to `theme` it uses the `"default"` theme. You can pass Auth UI themes can be overridden using variable tokens. See the [list of variable tokens](https://github.com/supabase/auth-ui/blob/main/packages/shared/src/theming/Themes.ts). -```js mark=12:19 /src/index.js +```js import { createClient } from '@supabase/supabase-js' import { Auth } from '@supabase/auth-ui-react' import { ThemeSupa } from '@supabase/auth-ui-shared' @@ -335,7 +335,7 @@ const App = () => ( You can use custom labels with `localization.variables` like so: -```js mark=10:15 /src/index.js +```js import { createClient } from '@supabase/supabase-js' import { Auth } from '@supabase/auth-ui-react' @@ -461,7 +461,7 @@ Currently, translating error messages (e.g. "Invalid credentials") is not suppor You can hide links by setting the `showLinks` prop to `false` -```js mark=9 /src/index.js +```js import { createClient } from '@supabase/supabase-js' import { Auth } from '@supabase/auth-ui-react' diff --git a/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx b/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx index b24639e091d..85fcaee1b36 100644 --- a/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx @@ -20,7 +20,7 @@ The `auth-helpers` package has been replaced with the `@supabase/ssr` package. W className="text-foreground-light border-b mt-8 pb-2" > - @@ -97,7 +97,7 @@ function MyApp({ Component, pageProps }) { Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component: -```tsx mark=2,8 pages/_app.tsx +```tsx import { type AppProps } from 'next/app' import { createPagesBrowserClient } from '@supabase/auth-helpers-nextjs' import { SessionContextProvider, Session } from '@supabase/auth-helpers-react' @@ -238,7 +238,7 @@ export default async (req: NextApiRequest, res: NextApiResponse) => { For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to use the `supabaseClient` from the `useSupabaseClient` hook and only run your query once the user is defined client-side in the `useUser()` hook: -```jsx mark=10:17 +```jsx import { Auth } from '@supabase/auth-ui-react' import { ThemeSupa } from '@supabase/auth-ui-shared' import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react' @@ -933,6 +933,6 @@ import { Database } from '../database.types' const supabaseClient = useSupabaseClient() ``` - + diff --git a/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx b/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx index 1d14e693b24..a6d7b1dd996 100644 --- a/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx @@ -20,7 +20,7 @@ The `auth-helpers` package has been replaced with the `@supabase/ssr` package. W className="text-foreground-light border-b mt-8 pb-2" > - @@ -1327,6 +1327,6 @@ export default function() { For an example of creating multiple Supabase clients, check [Singleton section](/docs/guides/auth/auth-helpers/nextjs#singleton) above. - + diff --git a/apps/docs/content/guides/auth/auth-helpers/remix.mdx b/apps/docs/content/guides/auth/auth-helpers/remix.mdx index dd9d537f960..85721924341 100644 --- a/apps/docs/content/guides/auth/auth-helpers/remix.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/remix.mdx @@ -20,7 +20,7 @@ We generally recommend using the new `@supabase/ssr` package instead of `auth-he className="text-foreground-light border-b mt-8 pb-2" > - @@ -833,6 +833,6 @@ supabaseClient.auth.signUp({ }) ``` - + diff --git a/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx b/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx index 6fcdb70efe2..03fea2dc2b2 100644 --- a/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx @@ -20,7 +20,7 @@ We generally recommend using the new `@supabase/ssr` package instead of `auth-he className="text-foreground-light border-b mt-8 pb-2" > - @@ -2031,6 +2031,6 @@ export const GET: RequestHandler = withAuth(async ({ session, getSupabaseClient - [Auth Helpers Source code](https://github.com/supabase/auth-helpers) - [SvelteKit example](https://github.com/supabase/auth-helpers/tree/main/examples/sveltekit) - + diff --git a/apps/docs/content/guides/auth/server-side/nextjs.mdx b/apps/docs/content/guides/auth/server-side/nextjs.mdx index f240a9bb205..e2bc381b5ef 100644 --- a/apps/docs/content/guides/auth/server-side/nextjs.mdx +++ b/apps/docs/content/guides/auth/server-side/nextjs.mdx @@ -74,7 +74,7 @@ Create a `utils/supabase` folder with a file for each type of client. Then copy className="text-foreground-light mt-8 mb-6" >
- What does the `cookies` object do?} id="utility-cookies" > @@ -85,11 +85,11 @@ Create a `utils/supabase` folder with a file for each type of client. Then copy The cookie is named `sb--auth-token` by default. - +
- Do I need to create a new client for every route?} id="client-deduplication" > @@ -99,7 +99,7 @@ Create a `utils/supabase` folder with a file for each type of client. Then copy - On the server, it basically configures a `fetch` call. You need to reconfigure the fetch call anew for every request to your server, because you need the cookies from the request. - On the client, `createBrowserClient` already uses a singleton pattern, so you only ever create one instance, no matter how many times you call your `createClient` function. - +
@@ -580,7 +580,7 @@ Create a `utils/supabase` folder with a file for each type of client. Then copy className="text-foreground-light mt-8 mb-6" >
- Why do I need so many types of clients?} id="nextjs-clients" > @@ -592,11 +592,11 @@ Create a `utils/supabase` folder with a file for each type of client. Then copy - **Component** - Runs on the client. Reads cookies from browser storage. Behind the scenes, `createBrowserClient` reuses the same client instance if called multiple times, so don't worry about deduplicating the client yourself. - **API route** - Runs on the server. Reads cookies from the request, which is passed through from `NextApiRequest`. - +
- What does the `cookies` object do?} id="client-storage-cookies" > @@ -605,7 +605,7 @@ Create a `utils/supabase` folder with a file for each type of client. Then copy The cookie is named `sb--auth-token` by default. - +
diff --git a/apps/docs/content/guides/functions/auth.mdx b/apps/docs/content/guides/functions/auth.mdx index f607c3587ed..639e667ff34 100644 --- a/apps/docs/content/guides/functions/auth.mdx +++ b/apps/docs/content/guides/functions/auth.mdx @@ -11,7 +11,7 @@ Edge Functions work seamlessly with [Supabase Auth](/docs/guides/auth). When a user makes a request to an Edge Function, you can use the Authorization header to set the Auth context in the Supabase client: -```js mark=9 +```js import { createClient } from 'jsr:@supabase/supabase-js@2' Deno.serve(async (req: Request) => { @@ -32,7 +32,7 @@ Importantly, this is done _inside_ the `Deno.serve()` callback argument, so that After initializing a Supabase client with the Auth context, you can use `getUser()` to fetch the user object, and run queries in the context of the user with [Row Level Security (RLS)](/docs/guides/database/postgres/row-level-security) policies enforced. -```js mark=12:13 +```js import { createClient } from 'jsr:@supabase/supabase-js@2' Deno.serve(async (req: Request) => { @@ -60,7 +60,7 @@ Deno.serve(async (req: Request) => { After initializing a Supabase client with the Auth context, all queries will be executed with the context of the user. For database queries, this means [Row Level Security](/docs/guides/database/postgres/row-level-security) will be enforced. -```js mark=12 +```js import { createClient } from 'jsr:@supabase/supabase-js@2' Deno.serve(async (req: Request) => { diff --git a/apps/docs/content/guides/platform/database-size.mdx b/apps/docs/content/guides/platform/database-size.mdx index a4f46586fa0..fb4112e2c85 100644 --- a/apps/docs/content/guides/platform/database-size.mdx +++ b/apps/docs/content/guides/platform/database-size.mdx @@ -36,16 +36,16 @@ Depending on your billing plan, your database can go into read-only mode which c Postgres does not immediately reclaim the physical space used by dead tuples (i.e., deleted rows) in the DB. They are marked as "removed" until a [vacuum operation](https://www.postgresql.org/docs/current/routine-vacuuming.html) is executed. As a result, deleting data from your database may not immediately reduce the reported disk usage. You can use the [Supabase CLI](https://supabase.com/docs/guides/cli/getting-started) `inspect db bloat` command to view all dead tuples in your database. Alternatively, you can run the [query](https://github.com/supabase/cli/blob/c9cce58025fded16b4c332747f819a44f45c3b83/internal/inspect/bloat/bloat.go#L17) found in the CLI's GitHub repo in the [SQL Editor](https://supabase.com/dashboard/project/_/sql/) ```bash -# login to the CLI +# Login to the CLI npx supabase login -# initlize a local supabase directory +# Initialize a local supabase directory npx supabase init -# link a project +# Link a project npx supabase link -# detect bloat +# Detect bloat npx supabase inspect db bloat --linked ``` diff --git a/apps/docs/pages/guides/platform/exhaust-swap.mdx b/apps/docs/content/guides/platform/exhaust-swap.mdx similarity index 92% rename from apps/docs/pages/guides/platform/exhaust-swap.mdx rename to apps/docs/content/guides/platform/exhaust-swap.mdx index adfb7a7a5ea..7abecd2198f 100644 --- a/apps/docs/pages/guides/platform/exhaust-swap.mdx +++ b/apps/docs/content/guides/platform/exhaust-swap.mdx @@ -1,11 +1,8 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'exhaust-swap', - title: 'High swap usage', - description: - 'Learn what high Swap usage could mean for your Supabase instance and what could have caused it.', -} +--- +id: 'exhaust-swap' +title: 'High swap usage' +description: 'Learn what high Swap usage could mean for your Supabase instance and what could have caused it.' +--- Learn what high Swap usage means, what can cause it, and how to solve it. @@ -65,7 +62,3 @@ If you find that your RAM and Swap usage are high, you have three options: 1. **Optimize performance:** Get more out of your instance's resources by optimizing your usage. See the [performance tuning guide](https://supabase.com/docs/guides/platform/performance#examining-query-performance) and our [production readiness guide](https://supabase.com/docs/guides/platform/going-into-prod#performance). 2. **Upgrade your compute:** You can get a Compute Add-on for your project. Navigate to [the **Addons** section in project settings](https://supabase.com/dashboard/project/_/settings/addons?panel=computeInstance) to see your upgrade options. 3. **Read Replicas:** You can spread the load on your Supabase project by creating a Read Replica. See [the read replicas guide](https://supabase.com/docs/guides/platform/read-replicas) for more information. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/content/guides/platform/logs.mdx b/apps/docs/content/guides/platform/logs.mdx index de7b42234f4..0fb33908724 100644 --- a/apps/docs/content/guides/platform/logs.mdx +++ b/apps/docs/content/guides/platform/logs.mdx @@ -293,11 +293,13 @@ Refer to the full field reference for each available source below. Do note that {(logConstants) => ( {logConstants.schemas.map((schema) => ( - + - - + + + + {schema.fields diff --git a/apps/docs/features/docs/GuidesMdx.client.tsx b/apps/docs/features/docs/GuidesMdx.client.tsx new file mode 100644 index 00000000000..a6d81f4a231 --- /dev/null +++ b/apps/docs/features/docs/GuidesMdx.client.tsx @@ -0,0 +1,16 @@ +'use client' + +/** + * The MDXProvider is necessary so that MDX partials will have access + * to components. + */ + +import { MDXProvider } from '@mdx-js/react' +import { type PropsWithChildren } from 'react' +import { components } from '~/features/docs/mdx.shared' + +const MDXProviderGuides = ({ children }: PropsWithChildren) => ( + {children} +) + +export { MDXProviderGuides } diff --git a/apps/docs/features/docs/GuidesMdx.template.tsx b/apps/docs/features/docs/GuidesMdx.template.tsx new file mode 100644 index 00000000000..3d5694fe9e1 --- /dev/null +++ b/apps/docs/features/docs/GuidesMdx.template.tsx @@ -0,0 +1,176 @@ +import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } +import { remarkCodeHike, type CodeHikeConfig } from '@code-hike/mdx' +import { ExternalLink } from 'lucide-react' +import { type SerializeOptions } from 'next-mdx-remote/dist/types' +import { MDXRemote } from 'next-mdx-remote/rsc' +import { type ComponentProps, type ReactNode } from 'react' +import remarkGfm from 'remark-gfm' +import rehypeKatex from 'rehype-katex' +import remarkMath from 'remark-math' +import { cn } from 'ui' +import GuidesTableOfContents from '~/components/GuidesTableOfContents' +import { components } from '~/features/docs/mdx.shared' +import type { WithRequired } from '~/features/helpers.types' +import { type GuideFrontmatter } from '~/lib/docs' +import { MDXProviderGuides } from './GuidesMdx.client' + +const codeHikeOptions: CodeHikeConfig = { + theme: codeHikeTheme, + lineNumbers: true, + showCopyButton: true, + skipLanguages: [], + autoImport: false, +} + +const mdxOptions: SerializeOptions = { + mdxOptions: { + useDynamicImport: true, + remarkPlugins: [ + [remarkMath, { singleDollarTextMath: false }], + remarkGfm, + [remarkCodeHike, codeHikeOptions], + ], + rehypePlugins: [rehypeKatex as any], + }, +} + +const MDXRemoteGuides = ({ options = {}, ...props }: ComponentProps) => { + const { mdxOptions: { remarkPlugins, rehypePlugins, ...otherMdxOptions } = {}, ...otherOptions } = + options + const { + mdxOptions: { + remarkPlugins: originalRemarkPlugins, + rehypePlugins: originalRehypePlugins, + ...originalMdxOptions + } = {}, + } = mdxOptions + + const finalOptions = { + ...mdxOptions, + ...otherOptions, + mdxOptions: { + ...originalMdxOptions, + ...otherMdxOptions, + remarkPlugins: [...(originalRemarkPlugins ?? []), ...(remarkPlugins ?? [])], + rehypePlugins: [...(originalRehypePlugins ?? []), ...(rehypePlugins ?? [])], + }, + } as SerializeOptions + + return +} + +const EDIT_LINK_SYMBOL = Symbol('edit link') +interface EditLink { + [EDIT_LINK_SYMBOL]: true + link: string + includesProtocol: boolean +} + +/** + * Create an object representing a link where the original content can be + * edited. + * + * Takes either a relative path, which will be prefixed with + * `https://github.com/`, or a full URL including protocol. + */ +const newEditLink = (str: string): EditLink => { + if (str.startsWith('/')) { + throw Error(`Edit links cannot start with slashes. Received: ${str}`) + } + + /** + * Catch strings that provide FQDNS without https?: + * + * At the start of a string, before the first slash, there is a dot + * surrounded by non-slash characters. + */ + if (/^[^\/]+\.[^\/]+\//.test(str)) { + throw Error(`Fully qualified domain names must start with 'https?'. Received: ${str}`) + } + + return { + [EDIT_LINK_SYMBOL]: true, + link: str, + includesProtocol: str.startsWith('http://') || str.startsWith('https://'), + } +} + +interface BaseGuideTemplateProps { + meta?: GuideFrontmatter + content?: string + children?: ReactNode + editLink: EditLink + mdxOptions?: SerializeOptions +} + +type GuideTemplateProps = + | WithRequired + | WithRequired + +const GuideTemplate = ({ meta, content, children, editLink, mdxOptions }: GuideTemplateProps) => { + const hideToc = meta?.hideToc || meta?.hide_table_of_contents + + return ( +
+
+
+

{meta?.title || 'Supabase Docs'}

+ {meta?.subtitle && ( +

{meta.subtitle}

+ )} +
+ + {content && } + + {children} + +
+
+ {!hideToc && ( + + )} +
+ ) +} + +export { GuideTemplate, MDXRemoteGuides, newEditLink } diff --git a/apps/docs/features/docs/GuidesMdx.utils.tsx b/apps/docs/features/docs/GuidesMdx.utils.tsx new file mode 100644 index 00000000000..1b45b259a94 --- /dev/null +++ b/apps/docs/features/docs/GuidesMdx.utils.tsx @@ -0,0 +1,167 @@ +import matter from 'gray-matter' +import { type Metadata, type ResolvingMetadata } from 'next' +import { redirect } from 'next/navigation' +import { watch } from 'node:fs' +import { readFile, readdir } from 'node:fs/promises' +import { extname, join, resolve, sep } from 'node:path' +import { existsFile } from '~/features/helpers.fs' +import type { OrPromise } from '~/features/helpers.types' +import { notFoundLink } from '~/features/recommendations/NotFound.utils' +import { BASE_PATH, IS_DEV, MISC_URL } from '~/lib/constants' +import { GUIDES_DIRECTORY, isValidGuideFrontmatter, type GuideFrontmatter } from '~/lib/docs' +import { newEditLink } from './GuidesMdx.template' + +/** + * [TODO Charis] + * + * This is kind of a dumb place for this to be, clean up later as part of + * cleaning up navigation menus. + */ +const PUBLISHED_SECTIONS = [ + 'ai', + 'api', + 'auth', + 'cli', + 'database', + 'functions', + 'getting-started', + // 'graphql', -- technically published, but completely federated + 'platform', + 'realtime', + 'resources', + 'self-hosting', + 'storage', +] as const + +const getGuidesMarkdownInternal = async ({ slug }: { slug: string[] }) => { + const relPath = slug.join(sep).replace(/\/$/, '') + const fullPath = join(GUIDES_DIRECTORY, relPath + '.mdx') + /** + * SAFETY CHECK: + * Prevent accessing anything outside of published sections and GUIDES_DIRECTORY + */ + if ( + !fullPath.startsWith(GUIDES_DIRECTORY) || + !PUBLISHED_SECTIONS.some((section) => relPath.startsWith(section)) + ) { + redirect(notFoundLink(slug.join('/'))) + } + + let mdx: string + try { + mdx = await readFile(fullPath, 'utf-8') + } catch { + redirect(notFoundLink(slug.join('/'))) + } + + const editLink = newEditLink( + `supabase/supabase/blob/master/apps/docs/content/guides/${relPath}.mdx` + ) + + const { data: meta, content } = matter(mdx) + if (!isValidGuideFrontmatter(meta)) { + throw Error('Type of frontmatter is not valid') + } + + return { + pathname: `/guides/${slug.join('/')}` satisfies `/${string}`, + meta, + content, + editLink, + } +} + +/** + * Caching this for the entire process is fine because the Markdown content is + * baked into each deployment and cannot change. There's also nothing sensitive + * here: this is just reading the public MDX files from the codebase. + * + * Unlike Next.js' unstable cache, this doesn't persist between deployments, + * which we don't want because the content _will_ change. + */ +const cache = (fn: (...args: Args) => Promise) => { + const _cache = new Map() + + if (IS_DEV) { + watch(resolve(GUIDES_DIRECTORY), { recursive: true }, (_, filename) => { + if (!filename) return + const cacheKey = [{ slug: filename.replace(/\.mdx$/, '').split(sep) }] + _cache.delete(JSON.stringify(cacheKey)) + }) + } + + return async (...args: Args) => { + const cacheKey = JSON.stringify(args) + if (!_cache.has(cacheKey)) { + _cache.set(cacheKey, await fn(...args)) + } + return _cache.get(cacheKey)! + } +} +const getGuidesMarkdown = cache(getGuidesMarkdownInternal) + +const genGuidesStaticParams = (directory?: string) => async () => { + const promises = directory + ? (await readdir(join(GUIDES_DIRECTORY, directory), { recursive: true })) + .filter((file) => extname(file) === '.mdx' && !file.split(sep).at(-1).startsWith('_')) + .map((file) => ({ slug: file.replace(/\.mdx$/, '').split(sep) })) + : PUBLISHED_SECTIONS.map(async (section) => + (await readdir(join(GUIDES_DIRECTORY, section), { recursive: true })) + .filter((file) => extname(file) === '.mdx' && !file.split(sep).at(-1).startsWith('_')) + .map((file) => ({ + slug: [section, ...file.replace(/\.mdx$/, '').split(sep)], + })) + .concat( + (await existsFile(join(GUIDES_DIRECTORY, `${section}.mdx`))) + ? [{ slug: [section] }] + : [] + ) + ) + + /** + * Flattening earlier will not work because there is nothing to flatten + * until the promises resolve. + */ + const result = (await Promise.all(promises)).flat() + return result +} + +const pluckPromise = (promise: Promise, key: K) => + promise.then((data) => data[key]) + +const genGuideMeta = + ( + generate: (params: Params) => OrPromise<{ meta: GuideFrontmatter; pathname: `/${string}` }> + ) => + async ({ params }: { params: Params }, parent: ResolvingMetadata): Promise => { + const [parentAlternates, parentOg, { meta, pathname }] = await Promise.all([ + pluckPromise(parent, 'alternates'), + pluckPromise(parent, 'openGraph'), + generate(params), + ]) + + // Pathname has form `/guides/(section)/**` + const ogType = pathname.split('/')[2] + + return { + title: `${meta.title} | Supabase Docs`, + description: meta.description || meta.subtitle, + // @ts-ignore + alternates: { + ...parentAlternates, + canonical: meta.canonical || `${BASE_PATH}${pathname}`, + }, + openGraph: { + ...parentOg, + url: `${BASE_PATH}${pathname}`, + images: { + url: `${MISC_URL}/functions/v1/og-images?site=docs&type=${encodeURIComponent(ogType)}&title=${encodeURIComponent(meta.title)}&description=${encodeURIComponent(meta.description ?? 'undefined')}`, + width: 800, + height: 600, + alt: meta.title, + }, + }, + } + } + +export { getGuidesMarkdown, genGuidesStaticParams, genGuideMeta } diff --git a/apps/docs/features/docs/mdx.shared.tsx b/apps/docs/features/docs/mdx.shared.tsx new file mode 100644 index 00000000000..c6eb04ae6fa --- /dev/null +++ b/apps/docs/features/docs/mdx.shared.tsx @@ -0,0 +1,94 @@ +import { ArrowDown, Check } from 'lucide-react' +import Link from 'next/link' +import { GlassPanel } from 'ui-patterns/GlassPanel' +import { IconPanel } from 'ui-patterns/IconPanel' +import SqlToRest from 'ui-patterns/SqlToRest' +import { Admonition, Button, Image } from 'ui' +import { Heading } from 'ui/src/components/CustomHTMLElements' +import { AppleSecretGenerator } from '~/components/AppleSecretGenerator' +import AuthProviders from '~/components/AuthProviders' +import { AuthSmsProviderConfig } from '~/components/AuthSmsProviderConfig' +import { CostWarning } from '~/components/AuthSmsProviderConfig/AuthSmsProviderConfig.Warnings' +import ButtonCard from '~/components/ButtonCard' +import { Extensions } from '~/components/Extensions' +import { JwtGenerator } from '~/components/JwtGenerator' +import { + AuthRateLimits, + DatabaseSetup, + GetSessionWarning, + HuggingFaceDeployment, + KotlinProjectSetup, + MigrationWarnings, + ProjectSetup, + OAuthPkceFlow, + QuickstartIntro, + SocialProviderSettingsSupabase, + SocialProviderSetup, +} from '~/components/MDX/partials' +import { Mermaid } from '~/components/Mermaid' +import { NavData } from '~/components/NavData' +import { ProjectConfigVariables } from '~/components/ProjectConfigVariables' +import { RealtimeLimitsEstimator } from '~/components/RealtimeLimitsEstimator' +import { SharedData } from '~/components/SharedData' +import StepHikeCompact from '~/components/StepHikeCompact' +import { Accordion, AccordionItem } from '~/features/ui/Accordion' +import * as CH from '~/features/ui/CodeHike' +import { Tabs, TabPanel } from '~/features/ui/Tabs' + +const components = { + Accordion, + AccordionItem, + Admonition, + AuthRateLimits, + AuthSmsProviderConfig, + AppleSecretGenerator, + AuthProviders, + Button, + ButtonCard, + CH, + CostWarning, + DatabaseSetup, + Extensions, + GetSessionWarning, + GlassPanel, + HuggingFaceDeployment, + IconArrowDown: ArrowDown, + IconCheck: Check, + IconPanel, + Image: (props: any) => , + JwtGenerator, + KotlinProjectSetup, + Link, + Mermaid, + MigrationWarnings, + NavData, + OAuthPkceFlow, + ProjectConfigVariables, + ProjectSetup, + QuickstartIntro, + RealtimeLimitsEstimator, + SharedData, + SocialProviderSettingsSupabase, + SocialProviderSetup, + SqlToRest, + StepHikeCompact, + Tabs, + TabPanel, + h2: (props: any) => ( + + {props.children} + + ), + h3: (props: any) => ( + + {props.children} + + ), + h4: (props: any) => ( + + {props.children} + + ), +} + +export { components } diff --git a/apps/docs/features/helpers.fs.ts b/apps/docs/features/helpers.fs.ts new file mode 100644 index 00000000000..f99411b7696 --- /dev/null +++ b/apps/docs/features/helpers.fs.ts @@ -0,0 +1,12 @@ +import { stat } from 'fs/promises' + +const existsFile = async (fullPath: string) => { + try { + await stat(fullPath) + return true + } catch { + return false + } +} + +export { existsFile } diff --git a/apps/docs/features/helpers.types.ts b/apps/docs/features/helpers.types.ts new file mode 100644 index 00000000000..200048aae30 --- /dev/null +++ b/apps/docs/features/helpers.types.ts @@ -0,0 +1,5 @@ +type OrPromise = T | Promise + +type WithRequired = T & { [P in K]-?: T[P] } + +export type { OrPromise, WithRequired } diff --git a/apps/docs/features/recommendations/NotFound.client.tsx b/apps/docs/features/recommendations/NotFound.client.tsx new file mode 100644 index 00000000000..e6cbd149aa8 --- /dev/null +++ b/apps/docs/features/recommendations/NotFound.client.tsx @@ -0,0 +1,69 @@ +'use client' + +import { type DocsSearchResult } from 'common' +import Link from 'next/link' +import { Button } from 'ui' +import { useCommandMenu } from 'ui-patterns/Cmdk' +import ButtonCard from '~/components/ButtonCard' + +const NotFound = ({ + recommendations, + omitSearch = false, +}: { + recommendations?: Omit[] | null + omitSearch?: boolean +}) => { + const { setIsOpen: setCommandMenuOpen } = useCommandMenu() + + return ( +
+

We couldn't find that page

+

+ Sorry, we couldn't find that page. It might be missing, or we had a temporary error + generating it. +

+
+ {!omitSearch && ( + + )} + + +
+ {recommendations && ( +
+

Are you looking for...?

+
    + {recommendations + .filter(({ title }) => !!title) + .slice(0, 6) + .map(({ path, title, subtitle, description }) => ( + + ))} +
+
+ )} +
+ ) +} + +export { NotFound } diff --git a/apps/docs/features/recommendations/NotFound.utils.ts b/apps/docs/features/recommendations/NotFound.utils.ts new file mode 100644 index 00000000000..19774150840 --- /dev/null +++ b/apps/docs/features/recommendations/NotFound.utils.ts @@ -0,0 +1,8 @@ +const notFoundLink = (origPath: string) => { + if (!origPath) return '/not-found' + + const searchParams = new URLSearchParams({ page: origPath }) + return `/not-found?${searchParams}` +} + +export { notFoundLink } diff --git a/apps/docs/features/ui/Accordion.tsx b/apps/docs/features/ui/Accordion.tsx new file mode 100644 index 00000000000..dea771bb5a4 --- /dev/null +++ b/apps/docs/features/ui/Accordion.tsx @@ -0,0 +1,13 @@ +'use client' + +import { Accordion as AccordionPrimitive } from 'ui' + +/** + * This makes the component work in MDX files, which otherwise would error on + * "trying to dot into a Client Component". + */ + +const Accordion = AccordionPrimitive +const AccordionItem = AccordionPrimitive.Item + +export { Accordion, AccordionItem } diff --git a/apps/docs/features/ui/CodeHike.tsx b/apps/docs/features/ui/CodeHike.tsx new file mode 100644 index 00000000000..c54dbba339d --- /dev/null +++ b/apps/docs/features/ui/CodeHike.tsx @@ -0,0 +1,28 @@ +'use client' + +import { CH } from '@code-hike/mdx/components' + +/** + * [Charis 2024-04-20] + * + * Annotations are broken and I can't find a way around it. + * + * Problem seems to be related to CH.annotations being a map of components and + * not a component itself. It's not bundled by the RSC bundler, and you get the + * error `Could not find the module .../CodeHike.tsx#annotations#mark in the + * React Client Manifest`. + */ + +export const Code = CH.Code +export const Section = CH.Section +export const SectionLink = CH.SectionLink +export const SectionCode = CH.SectionCode +export const Spotlight = CH.Spotlight +export const Scrollycoding = CH.Scrollycoding +export const Preview = CH.Preview +export const Annotation = CH.Annotation +export const Slideshow = CH.Slideshow +export const InlineCode = CH.InlineCode +export const CodeSlot = CH.CodeSlot +export const PreviewSlot = CH.PreviewSlot +export const StaticToggle = CH.StaticToggle diff --git a/apps/docs/components/Tabs.tsx b/apps/docs/features/ui/Tabs.tsx similarity index 100% rename from apps/docs/components/Tabs.tsx rename to apps/docs/features/ui/Tabs.tsx diff --git a/apps/docs/layouts/guides/index.tsx b/apps/docs/layouts/guides/index.tsx index 1c510cd3315..dd785a54b31 100644 --- a/apps/docs/layouts/guides/index.tsx +++ b/apps/docs/layouts/guides/index.tsx @@ -1,21 +1,16 @@ -import { MDXProvider } from '@mdx-js/react' +'use client' + import 'katex/dist/katex.min.css' -import { ExternalLink } from 'lucide-react' -import { NextSeo } from 'next-seo' -import { useRouter } from 'next/router' + import { type FC } from 'react' -import { cn } from 'ui' - -import components from '~/components' import { FooterHelpCalloutType } from '~/components/FooterHelpCallout' -import GuidesTableOfContents from '~/components/GuidesTableOfContents' import { type MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' import { LayoutMainContent } from '~/layouts/DefaultLayout' import { MainSkeleton } from '~/layouts/MainSkeleton' interface Props { - meta: { + meta?: { title: string description?: string // used in meta tags hide_table_of_contents?: boolean @@ -35,121 +30,12 @@ interface Props { } const Layout: FC = (props) => { - const { asPath } = useRouter() - const router = useRouter() - const menuId = props.menuId - const EDIT_BUTTON_EXCLUDE_LIST = ['/404'] - - // page type, ie, Auth, Database, Storage etc - const ogPageType = asPath.split('/')[2] - // open graph image url constructor - const ogImageUrl = encodeURI( - `https://obuldanrptloktxcffvn.supabase.co/functions/v1/og-images?site=docs${ - ogPageType ? `&type=${ogPageType}` : '' - }&title=${encodeURIComponent(props.meta?.title)}&description=${encodeURIComponent(props.meta?.description)}` - ) - return ( <> - - -
-
- {props.meta?.breadcrumb && ( -

{props.meta.breadcrumb}

- )} -
-

{props.meta.title}

- {props.meta?.subtitle && ( -

{props.meta.subtitle}

- )} -
- {props.children} - - {EDIT_BUTTON_EXCLUDE_LIST.includes(router.route) ? ( - <> - ) : ( - - )} -
-
- {!props.hideToc && !props.meta?.hide_table_of_contents && ( - - )} -
-
+ {props.children}
) diff --git a/apps/docs/layouts/ref/RefSubLayout.tsx b/apps/docs/layouts/ref/RefSubLayout.tsx index 69a91fc1db3..b0063ffa4f1 100644 --- a/apps/docs/layouts/ref/RefSubLayout.tsx +++ b/apps/docs/layouts/ref/RefSubLayout.tsx @@ -1,7 +1,7 @@ import { useInView } from 'react-intersection-observer' import { FC, PropsWithChildren } from 'react' import { highlightSelectedNavItem } from 'ui/src/components/CustomHTMLElements/CustomHTMLElements.utils' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import { useNavigationMenuContext } from '~/components/Navigation/NavigationMenu/NavigationMenu.Context' import { menuState } from '~/hooks/useMenuState' import Image from 'next/legacy/image' diff --git a/apps/docs/layouts/ref/RefSubLayoutNonFunc.tsx b/apps/docs/layouts/ref/RefSubLayoutNonFunc.tsx index b31b8260e68..aa1ad6c9f69 100644 --- a/apps/docs/layouts/ref/RefSubLayoutNonFunc.tsx +++ b/apps/docs/layouts/ref/RefSubLayoutNonFunc.tsx @@ -1,7 +1,7 @@ import { useInView } from 'react-intersection-observer' import { FC, PropsWithChildren } from 'react' import { highlightSelectedNavItem } from 'ui/src/components/CustomHTMLElements/CustomHTMLElements.utils' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import { useNavigationMenuContext } from '~/components/Navigation/NavigationMenu/NavigationMenu.Context' import { menuState } from '~/hooks/useMenuState' diff --git a/apps/docs/lib/constants.ts b/apps/docs/lib/constants.ts index 53c1ad2bd59..d146d838284 100644 --- a/apps/docs/lib/constants.ts +++ b/apps/docs/lib/constants.ts @@ -1,10 +1,12 @@ export const API_URL = ( process.env.NODE_ENV === 'development' ? process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080' - : process.env.NEXT_PUBLIC_API_URL + : process.env.NEXT_PUBLIC_API_URL! ).replace(/\/platform$/, '') export const BASE_PATH = process.env.NEXT_PUBLIC_BASE_PATH || '/docs' export const BUILD_PREVIEW_HTML = process.env.NEXT_PUBLIC_BUILD_PREVIEW_HTML === 'true' +export const IS_DEV = process.env.NODE_ENV === 'development' export const IS_PLATFORM = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' export const IS_PREVIEW = process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview' export const LOCAL_SUPABASE = process.env.NEXT_PUBLIC_LOCAL_SUPABASE === 'true' +export const MISC_URL = process.env.NEXT_PUBLIC_MISC_URL ?? '' diff --git a/apps/docs/lib/docs.ts b/apps/docs/lib/docs.ts index 6481e58f3cf..ecda299a499 100644 --- a/apps/docs/lib/docs.ts +++ b/apps/docs/lib/docs.ts @@ -15,10 +15,14 @@ import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } const DOCS_DIRECTORY = join(dirname(fileURLToPath(import.meta.url)), '..') export const GUIDES_DIRECTORY = join(DOCS_DIRECTORY, 'content/guides') -type GuideFrontmatter = { +export type GuideFrontmatter = { title: string + subtitle?: string description?: string + canonical?: string hideToc?: boolean + // @deprecated + hide_table_of_contents?: boolean tocVideo?: string } @@ -34,11 +38,28 @@ export function isValidGuideFrontmatter(obj: object): obj is GuideFrontmatter { `Invalid guide frontmatter: Title must exist and be a string. Received: ${obj.title}` ) } + if ('subtitle' in obj && typeof obj.subtitle !== 'string') { + throw Error(`Invalid guide frontmatter: Subtitle must be a sring. Received: ${obj.subtitle}`) + } if ('description' in obj && typeof obj.description !== 'string') { throw Error( `Invalid guide frontmatter: Description must be a string. Received: ${obj.description}` ) } + if ('canonical' in obj && typeof obj.canonical !== 'string') { + throw Error(`Invalid guide frontmatter: Canonical must be a string. Received: ${obj.canonical}`) + } + if ('hideToc' in obj && typeof obj.hideToc !== 'boolean') { + throw Error(`Invalid guide frontmatter: hideToc must be a boolean. Received: ${obj.hideToc}`) + } + if ('hide_table_of_contents' in obj && typeof obj.hide_table_of_contents !== 'boolean') { + throw Error( + `Invalid guide frontmatter: hide_table_of_contents must be a boolean. Received ${obj.hide_table_of_contents}` + ) + } + if ('tocVideo' in obj && typeof obj.tocVideo !== 'string') { + throw Error(`Invalid guide frontmatter: tocVideo must be a string. Received ${obj.tocVideo}`) + } return true } diff --git a/apps/docs/lib/fetch/feedback.ts b/apps/docs/lib/fetch/feedback.ts index 670780b7c7c..8bafd80ff59 100644 --- a/apps/docs/lib/fetch/feedback.ts +++ b/apps/docs/lib/fetch/feedback.ts @@ -20,7 +20,7 @@ export async function sendFeedback({ }: SendFeedbackVariables) { const { data, error } = await post('/platform/feedback/docs', { body: { - page: pathname, + page: pathname ?? '', isHelpful, title, feedback: message, diff --git a/apps/docs/lib/fetch/fetchWrappers.ts b/apps/docs/lib/fetch/fetchWrappers.ts index 2e3efd513dd..d9e8b9e54d4 100644 --- a/apps/docs/lib/fetch/fetchWrappers.ts +++ b/apps/docs/lib/fetch/fetchWrappers.ts @@ -37,13 +37,7 @@ export async function constructHeaders( } export const get: typeof _get = async (url, init) => { - let headers: Headers = null - try { - headers = await constructHeaders(init?.headers) - } catch (err) { - console.error(err) - return { error: err, response: undefined } - } + const headers = await constructHeaders(init?.headers) return await _get(url, { ...init, @@ -52,13 +46,7 @@ export const get: typeof _get = async (url, init) => { } export const post: typeof _post = async (url, init) => { - let headers: Headers = null - try { - headers = await constructHeaders(init?.headers) - } catch (err) { - console.error(err) - return { error: err, response: undefined } - } + const headers = await constructHeaders(init?.headers) return await _post(url, { ...init, @@ -67,13 +55,7 @@ export const post: typeof _post = async (url, init) => { } export const unauthedAllowedPost: typeof _post = async (url, init) => { - let headers: Headers = null - try { - headers = await constructHeaders(init?.headers, { allowUnauthenticated: true }) - } catch (err) { - console.error(err) - return { error: err, response: undefined } - } + const headers = await constructHeaders(init?.headers, { allowUnauthenticated: true }) return await _post(url, { ...init, diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs index c5b7e34687a..d4aa7eb649e 100644 --- a/apps/docs/next.config.mjs +++ b/apps/docs/next.config.mjs @@ -34,6 +34,7 @@ const withMDX = nextMdx({ }) /** @type {import('next').NextConfig} nextConfig */ + const nextConfig = { // Append the default value with md extensions pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'], diff --git a/apps/docs/package.json b/apps/docs/package.json index 87c4a3482bf..c53f5267d10 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -98,6 +98,7 @@ "ejs": "^3.1.10", "globby": "^13.2.2", "jest-environment-jsdom": "^29.7.0", + "nextjs-node-loader": "^1.1.5", "npm-run-all": "^4.1.5", "openapi-types": "^12.1.3", "simple-git": "^3.24.0", diff --git a/apps/docs/pages/__dev-secret-auth.tsx b/apps/docs/pages/__dev-secret-auth.tsx index de882c374f1..eccae335bda 100644 --- a/apps/docs/pages/__dev-secret-auth.tsx +++ b/apps/docs/pages/__dev-secret-auth.tsx @@ -1,4 +1,4 @@ -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import { Button_Shadcn_ } from 'ui' import { auth } from '~/lib/userAuth' diff --git a/apps/docs/pages/guides/ai/[[...slug]].tsx b/apps/docs/pages/guides/ai/[[...slug]].tsx deleted file mode 100644 index a19f8a02e54..00000000000 --- a/apps/docs/pages/guides/ai/[[...slug]].tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - return getGuidesStaticPaths('ai') -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('ai', args) -}) satisfies GetStaticProps - -export default function AiGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/guides/ai/python/[slug].tsx b/apps/docs/pages/guides/ai/python/[slug].tsx deleted file mode 100644 index 952e4bfa099..00000000000 --- a/apps/docs/pages/guides/ai/python/[slug].tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx' -import { GetStaticPaths, GetStaticProps } from 'next' -import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote' -import { serialize } from 'next-mdx-remote/serialize' -import { relative } from 'path' -import rehypeSlug from 'rehype-slug' -import remarkGfm from 'remark-gfm' -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } -import components from '~/components' -import Layout from '~/layouts/DefaultGuideLayout' -import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' -import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' -import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' - -// We fetch these docs at build time from an external repo -const org = 'supabase' -const repo = 'vecs' -const branch = 'main' -const docsDir = 'docs' -const externalSite = 'https://supabase.github.io/vecs' - -// Each external docs page is mapped to a local page -const pageMap = [ - { - slug: 'api', - meta: { - title: 'API', - }, - remoteFile: 'api.md', - }, - { - slug: 'collections', - meta: { - title: 'Collections', - }, - remoteFile: 'concepts_collections.md', - }, - { - slug: 'indexes', - meta: { - title: 'Indexes', - }, - remoteFile: 'concepts_indexes.md', - }, - { - slug: 'metadata', - meta: { - title: 'Metadata', - }, - remoteFile: 'concepts_metadata.md', - }, -] - -interface PythonClientDocsProps { - source: MDXRemoteSerializeResult - meta: { - title: string - description?: string - } -} - -export default function PythonClientDocs({ source, meta }: PythonClientDocsProps) { - return ( - - - - ) -} - -/** - * Fetch markdown from external repo and transform links - */ -export const getStaticProps: GetStaticProps = async ({ params }) => { - const page = pageMap.find(({ slug }) => slug === params.slug) - - if (!page) { - throw new Error(`No page mapping found for slug '${params.slug}'`) - } - - const { remoteFile, meta } = page - - const response = await fetch( - `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/${remoteFile}` - ) - - const source = await response.text() - - const urlTransform: UrlTransformFunction = (url) => { - try { - const externalSiteUrl = new URL(externalSite) - - const placeholderHostname = 'placeholder' - const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`) - - // Don't modify a url with a FQDN or a url that's only a hash - if (hostname !== placeholderHostname || pathname === '/') { - return url - } - - const relativePage = ( - pathname.endsWith('.md') - ? pathname.replace(/\.md$/, '') - : relative(externalSiteUrl.pathname, pathname) - ).replace(/^\//, '') - - const page = pageMap.find(({ remoteFile }) => `${relativePage}.md` === remoteFile) - - // If we have a mapping for this page, use the mapped path - if (page) { - return page.slug + hash - } - - // If we don't have this page in our docs, link to original docs - return `${externalSite}/${relativePage}${hash}` - } catch (err) { - console.error('Error transforming markdown URL', err) - return url - } - } - - const codeHikeOptions: CodeHikeConfig = { - theme: codeHikeTheme, - lineNumbers: true, - showCopyButton: true, - skipLanguages: [], - autoImport: false, - } - - const mdxSource = await serialize(source, { - scope: { - chCodeConfig: codeHikeOptions, - }, - mdxOptions: { - remarkPlugins: [ - remarkGfm, - remarkMkDocsAdmonition, - [removeTitle, meta.title], - [remarkCodeHike, codeHikeOptions], - ], - rehypePlugins: [[linkTransform, urlTransform], rehypeSlug], - }, - }) - - return { props: { source: mdxSource, meta } } -} - -export const getStaticPaths: GetStaticPaths = async () => { - return { - paths: pageMap.map(({ slug }) => ({ - params: { - slug, - }, - })), - fallback: false, - } -} diff --git a/apps/docs/pages/guides/api/[[...slug]].tsx b/apps/docs/pages/guides/api/[[...slug]].tsx deleted file mode 100644 index 80b2d9f4e80..00000000000 --- a/apps/docs/pages/guides/api/[[...slug]].tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { stripIndent } from 'common-tags' -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - return getGuidesStaticPaths('api') -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('api', args) -}) satisfies GetStaticProps - -export default function ApiGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/guides/auth/[[...slug]].tsx b/apps/docs/pages/guides/auth/[[...slug]].tsx deleted file mode 100644 index 9e645a917ea..00000000000 --- a/apps/docs/pages/guides/auth/[[...slug]].tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - return getGuidesStaticPaths('auth') -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('auth', args) -}) satisfies GetStaticProps - -export default function AuthGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/guides/cli/[[...slug]].tsx b/apps/docs/pages/guides/cli/[[...slug]].tsx deleted file mode 100644 index 2dd168f672d..00000000000 --- a/apps/docs/pages/guides/cli/[[...slug]].tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - return getGuidesStaticPaths('cli') -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('cli', args) -}) satisfies GetStaticProps - -export default function CliGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/guides/cli/github-action/[slug].tsx b/apps/docs/pages/guides/cli/github-action/[slug].tsx deleted file mode 100644 index d06701295e5..00000000000 --- a/apps/docs/pages/guides/cli/github-action/[slug].tsx +++ /dev/null @@ -1,159 +0,0 @@ -import { CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx' -import { GetStaticPaths, GetStaticProps } from 'next' -import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote' -import { serialize } from 'next-mdx-remote/serialize' -import { relative } from 'path' -import rehypeSlug from 'rehype-slug' -import remarkGfm from 'remark-gfm' -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } -import components from '~/components' -import Layout from '~/layouts/DefaultGuideLayout' -import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' -import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' -import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' -import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' - -// We fetch these docs at build time from an external repo -const org = 'supabase' -const repo = 'setup-cli' -const branch = 'gh-pages' -const docsDir = 'docs' -const externalSite = 'https://supabase.github.io/setup-cli' - -// Each external docs page is mapped to a local page -const pageMap = [ - { - slug: 'generating-types', - meta: { - title: 'Generate types using GitHub Actions', - description: 'End-to-end type safety across client, server, and database.', - subtitle: 'End-to-end type safety across client, server, and database.', - tocVideo: 'VSNgAIObBdw', - }, - remoteFile: 'generating-types.md', - }, - { - slug: 'testing', - meta: { - title: 'Automated testing using GitHub Actions', - description: 'Run your tests when you or your team make changes.', - subtitle: 'Run your tests when you or your team make changes.', - }, - remoteFile: 'testing.md', - }, - { - slug: 'backups', - meta: { - title: 'Automated backups using GitHub Actions', - description: 'Backup your database on a regular basis.', - subtitle: 'Backup your database on a regular basis.', - }, - remoteFile: 'backups.md', - }, -] - -interface ActionDocsProps { - source: MDXRemoteSerializeResult - meta: { - title: string - description?: string - } -} - -export default function ActionDocs({ source, meta }: ActionDocsProps) { - return ( - - - - ) -} - -/** - * Fetch markdown from external repo and transform links - */ -export const getStaticProps: GetStaticProps = async ({ params }) => { - const page = pageMap.find(({ slug }) => slug === params.slug) - - if (!page) { - throw new Error(`No page mapping found for slug '${params.slug}'`) - } - - const { remoteFile, meta } = page - - const response = await fetch( - `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/${remoteFile}` - ) - - const source = await response.text() - - const urlTransform: UrlTransformFunction = (url) => { - try { - const externalSiteUrl = new URL(externalSite) - - const placeholderHostname = 'placeholder' - const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`) - - // Don't modify a url with a FQDN or a url that's only a hash - if (hostname !== placeholderHostname || pathname === '/') { - return url - } - - const relativePage = ( - pathname.endsWith('.md') - ? pathname.replace(/\.md$/, '') - : relative(externalSiteUrl.pathname, pathname) - ).replace(/^\//, '') - - const page = pageMap.find(({ remoteFile }) => `${relativePage}.md` === remoteFile) - - // If we have a mapping for this page, use the mapped path - if (page) { - return page.slug + hash - } - - // If we don't have this page in our docs, link to original docs - return `${externalSite}/${relativePage}${hash}` - } catch (err) { - console.error('Error transforming markdown URL', err) - return url - } - } - - const codeHikeOptions: CodeHikeConfig = { - theme: codeHikeTheme, - lineNumbers: true, - showCopyButton: true, - skipLanguages: [], - autoImport: false, - } - - const mdxSource = await serialize(source, { - scope: { - chCodeConfig: codeHikeOptions, - }, - mdxOptions: { - remarkPlugins: [ - remarkGfm, - remarkMkDocsAdmonition, - remarkPyMdownTabs, - [removeTitle, meta.title], - [remarkCodeHike, codeHikeOptions], - ], - rehypePlugins: [[linkTransform, urlTransform], rehypeSlug], - }, - }) - - return { props: { source: mdxSource, meta } } -} - -export const getStaticPaths: GetStaticPaths = async () => { - return { - paths: pageMap.map(({ slug }) => ({ - params: { - slug, - }, - })), - fallback: false, - } -} diff --git a/apps/docs/pages/guides/database/[[...slug]].tsx b/apps/docs/pages/guides/database/[[...slug]].tsx deleted file mode 100644 index 0ee6416477b..00000000000 --- a/apps/docs/pages/guides/database/[[...slug]].tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - const paths = await getGuidesStaticPaths('database') - - /** - * Wrappers is special, so remove it - * Needs to be handled within its own subdirectory or won't build on dev - */ - paths.paths = paths.paths.filter( - ({ params }) => !(params.slug.at(0) === 'extensions' && params.slug.at(1) === 'wrappers') - ) - - return paths -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('database', args) -}) satisfies GetStaticProps - -export default function DatabaseGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/guides/database/extensions/wrappers/[[...slug]].tsx b/apps/docs/pages/guides/database/extensions/wrappers/[[...slug]].tsx deleted file mode 100644 index 3879c045049..00000000000 --- a/apps/docs/pages/guides/database/extensions/wrappers/[[...slug]].tsx +++ /dev/null @@ -1,212 +0,0 @@ -import { CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx' -import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' -import { serialize } from 'next-mdx-remote/serialize' -import { relative } from 'node:path' -import rehypeSlug from 'rehype-slug' -import emoji from 'remark-emoji' -import remarkGfm from 'remark-gfm' - -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' -import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' -import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' -import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' -import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' - -// We fetch these docs at build time from an external repo -const org = 'supabase' -const repo = 'wrappers' -const branch = 'main' -const docsDir = 'docs' -const externalSite = 'https://supabase.github.io/wrappers' - -// Each external docs page is mapped to a local page -const pageMap = [ - { - slug: 'airtable', - meta: { - title: 'Airtable', - }, - remoteFile: 'airtable.md', - }, - { - slug: 'auth0', - meta: { - title: 'Auth0', - }, - remoteFile: 'auth0.md', - }, - { - slug: 'bigquery', - meta: { - title: 'BigQuery', - }, - remoteFile: 'bigquery.md', - }, - { - slug: 'clickhouse', - meta: { - title: 'ClickHouse', - }, - remoteFile: 'clickhouse.md', - }, - { - slug: 'cognito', - meta: { - title: 'AWS Cognito', - }, - remoteFile: 'cognito.md', - }, - { - slug: 'firebase', - meta: { - title: 'Firebase', - }, - remoteFile: 'firebase.md', - }, - { - slug: 'logflare', - meta: { - title: 'Logflare', - }, - remoteFile: 'logflare.md', - }, - { - slug: 'mssql', - meta: { - title: 'MSSQL', - }, - remoteFile: 'mssql.md', - }, - { - slug: 'redis', - meta: { - title: 'Redis', - }, - remoteFile: 'redis.md', - }, - { - slug: 's3', - meta: { - title: 'AWS S3', - }, - remoteFile: 's3.md', - }, - { - slug: 'stripe', - meta: { - title: 'Stripe', - }, - remoteFile: 'stripe.md', - }, -] - -export default function WrappersDocs({ - source, - meta, - editLink, -}: InferGetStaticPropsType) { - return ( - - - - ) -} - -/** - * Fetch markdown from external repo and transform links - */ -export const getStaticProps: GetStaticProps = async (args) => { - const { params } = args - const federatedPage = pageMap.find(({ slug }) => slug === params.slug.at(0)) - - if (!federatedPage) { - const { props } = await getGuidesStaticProps('database/extensions/wrappers', args) - return { - props: { - source: props.mdxSource, - meta: props.frontmatter, - }, - } - } - - const { remoteFile, meta } = federatedPage - const repoPath = `${org}/${repo}/${branch}/${docsDir}/${remoteFile}` - const repoEditPath = `${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}` - const response = await fetch(`https://raw.githubusercontent.com/${repoPath}`) - - const source = await response.text() - - const urlTransform: UrlTransformFunction = (url) => { - try { - const externalSiteUrl = new URL(externalSite) - - const placeholderHostname = 'placeholder' - const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`) - - // Don't modify a url with a FQDN or a url that's only a hash - if (hostname !== placeholderHostname || pathname === '/') { - return url - } - const relativePage = ( - pathname.endsWith('.md') - ? pathname.replace(/\.md$/, '') - : relative(externalSiteUrl.pathname, pathname) - ).replace(/^\//, '') - - const page = pageMap.find(({ remoteFile }) => `${relativePage}.md` === remoteFile) - - // If we have a mapping for this page, use the mapped path - if (page) { - return page.slug + hash - } - - // If we don't have this page in our docs, link to original docs - return `${externalSite}/${relativePage}${hash}` - } catch (err) { - console.error('Error transforming markdown URL', err) - return url - } - } - - const codeHikeOptions: CodeHikeConfig = { - theme: codeHikeTheme, - lineNumbers: true, - showCopyButton: true, - skipLanguages: [], - autoImport: false, - } - - const mdxSource = await serialize(source, { - scope: { - chCodeConfig: codeHikeOptions, - }, - mdxOptions: { - remarkPlugins: [ - remarkGfm, - remarkMkDocsAdmonition, - emoji, - remarkPyMdownTabs, - [removeTitle, meta.title], - [remarkCodeHike, codeHikeOptions], - ], - rehypePlugins: [[linkTransform, urlTransform], rehypeSlug], - }, - }) - - return { props: { source: mdxSource, meta, editLink: repoEditPath } } -} - -export const getStaticPaths: GetStaticPaths = async () => { - const mdxPaths = (await getGuidesStaticPaths('database/extensions/wrappers')).paths - const federatedPaths = pageMap.map(({ slug }) => ({ params: { slug: [slug] } })) - return { - paths: [...federatedPaths, ...mdxPaths], - fallback: false, - } -} diff --git a/apps/docs/pages/guides/functions/[[...slug]].tsx b/apps/docs/pages/guides/functions/[[...slug]].tsx deleted file mode 100644 index 8a8ed0defd2..00000000000 --- a/apps/docs/pages/guides/functions/[[...slug]].tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - return getGuidesStaticPaths('functions') -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('functions', args) -}) satisfies GetStaticProps - -export default function FunctionsGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/guides/getting-started/[[...slug]].tsx b/apps/docs/pages/guides/getting-started/[[...slug]].tsx deleted file mode 100644 index e16c15ac68f..00000000000 --- a/apps/docs/pages/guides/getting-started/[[...slug]].tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - return getGuidesStaticPaths('getting-started') -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('getting-started', args) -}) satisfies GetStaticProps - -export default function GettingStartedGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/guides/graphql/[[...slug]].tsx b/apps/docs/pages/guides/graphql/[[...slug]].tsx deleted file mode 100644 index 00eaecfe221..00000000000 --- a/apps/docs/pages/guides/graphql/[[...slug]].tsx +++ /dev/null @@ -1,220 +0,0 @@ -import { CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx' -import { GetStaticPaths, GetStaticProps } from 'next' -import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote' -import { serialize } from 'next-mdx-remote/serialize' -import { isAbsolute, relative } from 'path' -import rehypeSlug from 'rehype-slug' -import remarkGfm from 'remark-gfm' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' -import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' -import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' -import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' - -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } - -// We fetch these docs at build time from an external repo -const org = 'supabase' -const repo = 'pg_graphql' -const branch = 'master' -const docsDir = 'docs' -const externalSite = 'https://supabase.github.io/pg_graphql' - -// Each external docs page is mapped to a local page -const pageMap = [ - { - meta: { - id: 'graphql-overview', - title: 'GraphQL', - subtitle: 'Autogenerated GraphQL APIs with Postgres.', - }, - remoteFile: 'supabase.md', - }, - { - slug: 'api', - meta: { - id: 'graphql-api', - title: 'GraphQL API', - subtitle: 'Understanding the core concepts of the GraphQL API.', - }, - remoteFile: 'api.md', - }, - { - slug: 'views', - meta: { - id: 'graphql-views', - title: 'Views', - subtitle: 'Using Postgres Views with GraphQL.', - }, - remoteFile: 'views.md', - }, - { - slug: 'functions', - meta: { - id: 'graphql-functions', - title: 'Functions', - subtitle: 'Using Postgres Functions with GraphQL.', - }, - remoteFile: 'functions.md', - }, - { - slug: 'computed-fields', - meta: { - id: 'graphql-computed-fields', - title: 'Computed Fields', - subtitle: 'Using Postgres Computed Fields with GraphQL.', - }, - remoteFile: 'computed-fields.md', - }, - { - slug: 'configuration', - meta: { - id: 'graphql-configuration', - title: 'Configuration & Customization', - subtitle: 'Extra configuration options can be set on SQL entities using comment directives.', - }, - remoteFile: 'configuration.md', - }, - { - slug: 'security', - meta: { - id: 'graphql-security', - title: 'Security', - subtitle: 'Securing your GraphQL API.', - }, - remoteFile: 'security.md', - }, - { - slug: 'with-apollo', - meta: { - id: 'graphql-with-apollo', - title: 'With Apollo', - subtitle: 'Using pg_grapqhl with Apollo.', - }, - remoteFile: 'usage_with_apollo.md', - }, - { - slug: 'with-relay', - meta: { - id: 'graphql-with-relay', - title: 'With Relay', - subtitle: 'Using pg_grapqhl with Relay.', - }, - remoteFile: 'usage_with_relay.md', - }, -] - -interface PGGraphQLDocsProps { - source: MDXRemoteSerializeResult - meta: { - title: string - description?: string - } -} - -export default function PGGraphQLDocs({ source, meta }: PGGraphQLDocsProps) { - return ( - - - - ) -} - -/** - * Fetch markdown from external repo and transform links - */ -export const getStaticProps: GetStaticProps = async ({ params }) => { - const [slug] = params.slug ?? [] - const page = pageMap.find((page) => page.slug === slug) - - if (!page) { - throw new Error(`No page mapping found for slug '${slug}'`) - } - - const { remoteFile, meta } = page - - const response = await fetch( - `https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/${remoteFile}` - ) - - const source = await response.text() - - const urlTransform: UrlTransformFunction = (url) => { - try { - const externalSiteUrl = new URL(externalSite) - - const placeholderHostname = 'placeholder' - const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`) - - // Don't modify a url with a FQDN or a url that's only a hash - if (hostname !== placeholderHostname || pathname === '/') { - return url - } - - const getRelativePath = () => { - if (pathname.endsWith('.md')) { - return pathname.replace(/\.md$/, '') - } - if (isAbsolute(url)) { - return relative(externalSiteUrl.pathname, pathname) - } - return pathname - } - - const relativePath = getRelativePath().replace(/^\//, '') - - const page = pageMap.find(({ remoteFile }) => `${relativePath}.md` === remoteFile) - - // If we have a mapping for this page, use the mapped path - if (page) { - return 'graphql/' + page.slug + hash - } - - // If we don't have this page in our docs, link to original docs - return `${externalSite}/${relativePath}${hash}` - } catch (err) { - console.error('Error transforming markdown URL', err) - return url - } - } - - const codeHikeOptions: CodeHikeConfig = { - theme: codeHikeTheme, - lineNumbers: true, - showCopyButton: true, - skipLanguages: [], - autoImport: false, - } - - const mdxSource = await serialize(source, { - scope: { - chCodeConfig: codeHikeOptions, - }, - mdxOptions: { - remarkPlugins: [ - remarkGfm, - remarkMkDocsAdmonition, - remarkPyMdownTabs, - [removeTitle, meta.title], - [remarkCodeHike, codeHikeOptions], - ], - rehypePlugins: [[linkTransform, urlTransform], rehypeSlug], - }, - }) - - return { props: { source: mdxSource, meta } } -} - -export const getStaticPaths: GetStaticPaths = async () => { - return { - paths: pageMap.map(({ slug }) => ({ - params: { - slug: slug ? [slug] : [], - }, - })), - fallback: false, - } -} diff --git a/apps/docs/pages/guides/platform/[[...slug]].tsx b/apps/docs/pages/guides/platform/[[...slug]].tsx deleted file mode 100644 index f20307d7974..00000000000 --- a/apps/docs/pages/guides/platform/[[...slug]].tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - return getGuidesStaticPaths('platform') -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('platform', args) -}) satisfies GetStaticProps - -export default function PlatformGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/guides/realtime/[[...slug]].tsx b/apps/docs/pages/guides/realtime/[[...slug]].tsx deleted file mode 100644 index 3a90134f5ad..00000000000 --- a/apps/docs/pages/guides/realtime/[[...slug]].tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - return getGuidesStaticPaths('realtime') -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('realtime', args) -}) satisfies GetStaticProps - -export default function RealtimeGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/guides/resources/[[...slug]].tsx b/apps/docs/pages/guides/resources/[[...slug]].tsx deleted file mode 100644 index d73ad930fe5..00000000000 --- a/apps/docs/pages/guides/resources/[[...slug]].tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - return getGuidesStaticPaths('resources') -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('resources', args) -}) satisfies GetStaticProps - -export default function ResourcesGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/guides/self-hosting/[[...slug]].tsx b/apps/docs/pages/guides/self-hosting/[[...slug]].tsx deleted file mode 100644 index c63d1c13b75..00000000000 --- a/apps/docs/pages/guides/self-hosting/[[...slug]].tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - return getGuidesStaticPaths('self-hosting') -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('self-hosting', args) -}) satisfies GetStaticProps - -export default function SelfHostingGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/guides/self-hosting/analytics/config.tsx b/apps/docs/pages/guides/self-hosting/analytics/config.tsx deleted file mode 100644 index 3946fc2c55c..00000000000 --- a/apps/docs/pages/guides/self-hosting/analytics/config.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { type InferGetStaticPropsType, type GetStaticProps } from 'next' -import { MDXRemote } from 'next-mdx-remote' -import { serialize } from 'next-mdx-remote/serialize' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Param from '~/components/Params' -import Layout from '~/layouts/DefaultGuideLayout' -import { getAnalyticsConfigV0 } from '~/lib/mdx/getConfig' - -const meta = { - title: 'Analytics Self-hosting Config', - description: 'How to configure and deploy Supabase Analytics.', -} - -export const getStaticProps = (async () => { - const spec = getAnalyticsConfigV0() - const descriptionSource = await serialize(spec.info.description) - return { - props: { - descriptionSource, - spec, - }, - } -}) satisfies GetStaticProps - -export const Page = (props: InferGetStaticPropsType) => ( - - - -
- {props.spec.info.tags.map((tag: ReturnType['info']['tags']) => { - return ( - <> -

{tag.title}

-

{tag.description}

-
-
Parameters
-
    - {props.spec.parameters - .filter((param: ReturnType['parameters']) => - param.tags.includes(tag.id) - ) - .map((param: ReturnType['parameters']) => { - return ( - - ) - })} -
-
- - ) - })} -
-
-) - -export default Page diff --git a/apps/docs/pages/guides/self-hosting/auth/config.tsx b/apps/docs/pages/guides/self-hosting/auth/config.tsx deleted file mode 100644 index 5702f8a5379..00000000000 --- a/apps/docs/pages/guides/self-hosting/auth/config.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { type InferGetStaticPropsType, type GetStaticProps } from 'next' -import { MDXRemote } from 'next-mdx-remote' -import { serialize } from 'next-mdx-remote/serialize' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Param from '~/components/Params' -import Layout from '~/layouts/DefaultGuideLayout' -import { getAuthConfigV1 } from '~/lib/mdx/getConfig' - -const meta = { - title: 'Auth Self-hosting Config', - description: 'How to configure and deploy Supabase Auth.', -} - -export const getStaticProps = (async () => { - const spec = getAuthConfigV1() - const descriptionSource = await serialize(spec.info.description) - return { - props: { - descriptionSource, - spec, - }, - } -}) satisfies GetStaticProps - -export const Page = (props: InferGetStaticPropsType) => ( - - - -
- {props.spec.info.tags.map((tag: ReturnType['info']['tags']) => { - return ( - <> -

{tag.title}

-

{tag.description}

-
-
Parameters
-
    - {props.spec.parameters - .filter((param: ReturnType['parameters']) => - param.tags.includes(tag.id) - ) - .map((param: ReturnType['parameters']) => { - return ( - - ) - })} -
-
- - ) - })} -
-
-) - -export default Page diff --git a/apps/docs/pages/guides/self-hosting/realtime/config.tsx b/apps/docs/pages/guides/self-hosting/realtime/config.tsx deleted file mode 100644 index 09df809537c..00000000000 --- a/apps/docs/pages/guides/self-hosting/realtime/config.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { type InferGetStaticPropsType, type GetStaticProps } from 'next' -import { MDXRemote } from 'next-mdx-remote' -import { serialize } from 'next-mdx-remote/serialize' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Param from '~/components/Params' -import Layout from '~/layouts/DefaultGuideLayout' -import { getRealtimeConfigV0 } from '~/lib/mdx/getConfig' - -const meta = { - title: 'Realtime Self-hosting Config', - description: 'How to configure and deploy Supabase Realtime.', -} - -export const getStaticProps = (async () => { - const spec = getRealtimeConfigV0() - const descriptionSource = await serialize(spec.info.description) - return { - props: { - descriptionSource, - spec, - }, - } -}) satisfies GetStaticProps - -export const Page = (props: InferGetStaticPropsType) => ( - - - -
- {props.spec.info.tags.map((tag: ReturnType['info']['tags']) => { - return ( - <> -

{tag.title}

-

{tag.description}

-
-
Parameters
-
    - {props.spec.parameters - .filter((param: ReturnType['parameters']) => - param.tags.includes(tag.id) - ) - .map((param: ReturnType['parameters']) => { - return ( - - ) - })} -
-
- - ) - })} -
-
-) - -export default Page diff --git a/apps/docs/pages/guides/self-hosting/storage/config.tsx b/apps/docs/pages/guides/self-hosting/storage/config.tsx deleted file mode 100644 index 8a131f674ac..00000000000 --- a/apps/docs/pages/guides/self-hosting/storage/config.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { type InferGetStaticPropsType, type GetStaticProps } from 'next' -import { MDXRemote } from 'next-mdx-remote' -import { serialize } from 'next-mdx-remote/serialize' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Param from '~/components/Params' -import Layout from '~/layouts/DefaultGuideLayout' -import { getStorageConfigV0 } from '~/lib/mdx/getConfig' - -const meta = { - title: 'Storage Self-hosting Config', - description: 'How to configure and deploy Supabase Storage.', -} - -export const getStaticProps = (async () => { - const spec = getStorageConfigV0() - const descriptionSource = await serialize(spec.info.description) - return { - props: { - descriptionSource, - spec, - }, - } -}) satisfies GetStaticProps - -export const Page = (props: InferGetStaticPropsType) => ( - - - -
- {props.spec.info.tags.map((tag: ReturnType['info']['tags']) => { - return ( - <> -

{tag.title}

-

{tag.description}

-
-
Parameters
-
    - {props.spec.parameters - .filter((param: ReturnType['parameters']) => - param.tags.includes(tag.id) - ) - .map((param: ReturnType['parameters']) => { - return ( - - ) - })} -
-
- - ) - })} -
-
-) - -export default Page diff --git a/apps/docs/pages/guides/storage/[[...slug]].tsx b/apps/docs/pages/guides/storage/[[...slug]].tsx deleted file mode 100644 index 90c4b978d88..00000000000 --- a/apps/docs/pages/guides/storage/[[...slug]].tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next' -import { MDXRemote } from 'next-mdx-remote' - -import components from '~/components' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' -import Layout from '~/layouts/DefaultGuideLayout' -import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs' - -export const getStaticPaths = (async () => { - return getGuidesStaticPaths('storage') -}) satisfies GetStaticPaths - -export const getStaticProps = (async (args) => { - return getGuidesStaticProps('storage', args) -}) satisfies GetStaticProps - -export default function StorageGuide({ - frontmatter, - mdxSource, - editLink, -}: InferGetStaticPropsType) { - const { hideToc, ...meta } = frontmatter - - return ( - - - - ) -} diff --git a/apps/docs/pages/reference/csharp/crawlers/[...slug].tsx b/apps/docs/pages/reference/csharp/crawlers/[...slug].tsx index d16769cb83a..fb85f51b023 100644 --- a/apps/docs/pages/reference/csharp/crawlers/[...slug].tsx +++ b/apps/docs/pages/reference/csharp/crawlers/[...slug].tsx @@ -5,7 +5,7 @@ import RefSectionHandler from '~/components/reference/RefSectionHandler' import { flattenSections } from '~/lib/helpers' import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths' import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import RefSEO from '~/components/reference/RefSEO' import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' import type { TypeSpec } from '~/components/reference/Reference.types' diff --git a/apps/docs/pages/reference/csharp/v0/crawlers/[...slug].tsx b/apps/docs/pages/reference/csharp/v0/crawlers/[...slug].tsx index 101c289bddb..b5b5f35127a 100644 --- a/apps/docs/pages/reference/csharp/v0/crawlers/[...slug].tsx +++ b/apps/docs/pages/reference/csharp/v0/crawlers/[...slug].tsx @@ -5,7 +5,7 @@ import RefSectionHandler from '~/components/reference/RefSectionHandler' import { flattenSections } from '~/lib/helpers' import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths' import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import RefSEO from '~/components/reference/RefSEO' import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' diff --git a/apps/docs/pages/reference/dart/crawlers/[...slug].tsx b/apps/docs/pages/reference/dart/crawlers/[...slug].tsx index b18c2eeff9b..0ea7d9bcaed 100644 --- a/apps/docs/pages/reference/dart/crawlers/[...slug].tsx +++ b/apps/docs/pages/reference/dart/crawlers/[...slug].tsx @@ -5,7 +5,7 @@ import RefSectionHandler from '~/components/reference/RefSectionHandler' import { flattenSections } from '~/lib/helpers' import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths' import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import RefSEO from '~/components/reference/RefSEO' import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' diff --git a/apps/docs/pages/reference/dart/v1/crawlers/[...slug].tsx b/apps/docs/pages/reference/dart/v1/crawlers/[...slug].tsx index 1843e626e5f..7945563e867 100644 --- a/apps/docs/pages/reference/dart/v1/crawlers/[...slug].tsx +++ b/apps/docs/pages/reference/dart/v1/crawlers/[...slug].tsx @@ -5,7 +5,7 @@ import RefSectionHandler from '~/components/reference/RefSectionHandler' import { flattenSections } from '~/lib/helpers' import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths' import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import RefSEO from '~/components/reference/RefSEO' import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' diff --git a/apps/docs/pages/reference/index.mdx b/apps/docs/pages/reference/index.mdx deleted file mode 100644 index f6eeec0d70a..00000000000 --- a/apps/docs/pages/reference/index.mdx +++ /dev/null @@ -1,79 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'reference', - title: 'Reference Documentation', - sidebar_label: 'Reference Documentation', - hide_table_of_contents: true, -} - -Reference documentation for the official Supabase client libraries, APIs, and tools. - -
-
-
- -
-
- -
-
- -
-
- -
- -
-
- -## Self-hosting - -Reference documentation for self-hosting Supabase features. - -
-
-
- -
-
- -
-
- -
-
-
- -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/reference/javascript/crawlers/[...slug].tsx b/apps/docs/pages/reference/javascript/crawlers/[...slug].tsx index a307a3eac07..b213488f963 100644 --- a/apps/docs/pages/reference/javascript/crawlers/[...slug].tsx +++ b/apps/docs/pages/reference/javascript/crawlers/[...slug].tsx @@ -5,7 +5,7 @@ import RefSectionHandler from '~/components/reference/RefSectionHandler' import { flattenSections } from '~/lib/helpers' import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths' import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import RefSEO from '~/components/reference/RefSEO' import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' diff --git a/apps/docs/pages/reference/javascript/v1/crawlers/[...slug].tsx b/apps/docs/pages/reference/javascript/v1/crawlers/[...slug].tsx index 09cd60e404c..d4647642a7f 100644 --- a/apps/docs/pages/reference/javascript/v1/crawlers/[...slug].tsx +++ b/apps/docs/pages/reference/javascript/v1/crawlers/[...slug].tsx @@ -5,7 +5,7 @@ import RefSectionHandler from '~/components/reference/RefSectionHandler' import { flattenSections } from '~/lib/helpers' import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths' import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import RefSEO from '~/components/reference/RefSEO' import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' diff --git a/apps/docs/pages/reference/kotlin/crawlers/[...slug].tsx b/apps/docs/pages/reference/kotlin/crawlers/[...slug].tsx index 2f3885d524b..ec9ba3975f7 100644 --- a/apps/docs/pages/reference/kotlin/crawlers/[...slug].tsx +++ b/apps/docs/pages/reference/kotlin/crawlers/[...slug].tsx @@ -5,7 +5,7 @@ import RefSectionHandler from '~/components/reference/RefSectionHandler' import { flattenSections } from '~/lib/helpers' import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths' import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import RefSEO from '~/components/reference/RefSEO' import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' diff --git a/apps/docs/pages/reference/kotlin/v1/crawlers/[...slug].tsx b/apps/docs/pages/reference/kotlin/v1/crawlers/[...slug].tsx index 7a5ada7a194..eccaa115e21 100644 --- a/apps/docs/pages/reference/kotlin/v1/crawlers/[...slug].tsx +++ b/apps/docs/pages/reference/kotlin/v1/crawlers/[...slug].tsx @@ -5,7 +5,7 @@ import RefSectionHandler from '~/components/reference/RefSectionHandler' import { flattenSections } from '~/lib/helpers' import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths' import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import RefSEO from '~/components/reference/RefSEO' import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' diff --git a/apps/docs/pages/reference/python/crawlers/[...slug].tsx b/apps/docs/pages/reference/python/crawlers/[...slug].tsx index bb549d62305..8534a8767ae 100644 --- a/apps/docs/pages/reference/python/crawlers/[...slug].tsx +++ b/apps/docs/pages/reference/python/crawlers/[...slug].tsx @@ -5,7 +5,7 @@ import RefSectionHandler from '~/components/reference/RefSectionHandler' import { flattenSections } from '~/lib/helpers' import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths' import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import RefSEO from '~/components/reference/RefSEO' import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' diff --git a/apps/docs/pages/reference/swift/crawlers/[...slug].tsx b/apps/docs/pages/reference/swift/crawlers/[...slug].tsx index 69000ea9674..9763d9c668b 100644 --- a/apps/docs/pages/reference/swift/crawlers/[...slug].tsx +++ b/apps/docs/pages/reference/swift/crawlers/[...slug].tsx @@ -5,7 +5,7 @@ import RefSectionHandler from '~/components/reference/RefSectionHandler' import { flattenSections } from '~/lib/helpers' import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths' import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import RefSEO from '~/components/reference/RefSEO' import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' diff --git a/apps/docs/pages/reference/swift/v1/crawlers/[...slug].tsx b/apps/docs/pages/reference/swift/v1/crawlers/[...slug].tsx index 1ef50177ac2..0156927cbe0 100644 --- a/apps/docs/pages/reference/swift/v1/crawlers/[...slug].tsx +++ b/apps/docs/pages/reference/swift/v1/crawlers/[...slug].tsx @@ -5,7 +5,7 @@ import RefSectionHandler from '~/components/reference/RefSectionHandler' import { flattenSections } from '~/lib/helpers' import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths' import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps' -import { useRouter } from 'next/router' +import { useRouter } from 'next/compat/router' import RefSEO from '~/components/reference/RefSEO' import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' diff --git a/apps/docs/spec/supabase_dart_v2.yml b/apps/docs/spec/supabase_dart_v2.yml index daf09e3e796..64b53f2499b 100644 --- a/apps/docs/spec/supabase_dart_v2.yml +++ b/apps/docs/spec/supabase_dart_v2.yml @@ -600,12 +600,9 @@ functions: import 'package:google_sign_in/google_sign_in.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; - /// Web Client ID that you registered with Google Cloud. - /// This will be used to perform Google sign in on Android. - const webClientId = 'my-web.apps.googleusercontent.com'; + const webClientId = ''; - /// iOS Client ID that you registered with Google Cloud. - const iosClientId = 'my-ios.apps.googleusercontent.com'; + const iosClientId = ' = (props) => (
{props.children} diff --git a/packages/ui/src/components/Menu/MenuContext.tsx b/packages/ui/src/components/Menu/MenuContext.tsx index 3e8b71ab3d8..6264a543708 100644 --- a/packages/ui/src/components/Menu/MenuContext.tsx +++ b/packages/ui/src/components/Menu/MenuContext.tsx @@ -1,3 +1,5 @@ +'use client' + import React, { createContext, useContext } from 'react' interface ContextProps { @@ -21,9 +23,7 @@ export const MenuContextProvider = (props: Provider) => { type: type, } - return ( - {props.children} - ) + return {props.children} } // context helper to avoid using a consumer component diff --git a/packages/ui/src/components/Tabs/TabsProvider.tsx b/packages/ui/src/components/Tabs/TabsProvider.tsx new file mode 100644 index 00000000000..0f0f99470a0 --- /dev/null +++ b/packages/ui/src/components/Tabs/TabsProvider.tsx @@ -0,0 +1,105 @@ +'use client' + +import { xor } from 'lodash' +import { + type Dispatch, + type PropsWithChildren, + type SetStateAction, + createContext, + useCallback, + useContext, + useMemo, + useState, +} from 'react' + +export interface TabGroup { + tabIds: string[] + activeId: string +} + +interface TabsContextValue { + tabGroups: TabGroup[] + setTabGroups: Dispatch> +} + +const TabsContext = createContext({ + tabGroups: [], + setTabGroups: () => {}, +}) + +/** + * Tracks active Tab IDs across the site so that tabs + * with the same ID stay in sync (eg. JS vs TS tabs). + */ +const TabsProvider = ({ children }: PropsWithChildren<{}>) => { + const [tabGroups, setTabGroups] = useState([]) + + return {children} +} + +export interface UseTabGroupValue { + /** + * The active tab ID for the tab group. + * This value is shared with all matching tab + * groups within the context. + */ + groupActiveId?: string + + /** + * Set the active tab ID for the tab group. + * Value will be shared with all matching tab + * groups within the context. + */ + setGroupActiveId?(id: string): void +} + +/** + * Hook to retrieve and set the active tab ID for + * the current tab group. + * The value will be shared with all matching tab + * groups within the context. + * + * Silently fails if no `TabsProvider` is set. + */ +export const useTabGroup = (tabIds: string[]): UseTabGroupValue => { + const tabsContext = useContext(TabsContext) + + if (!tabsContext) { + return {} + } + + const { tabGroups, setTabGroups } = tabsContext + + const groupActiveId = useMemo( + () => tabGroups.find((group) => xor(group.tabIds, tabIds).length === 0)?.activeId, + [tabGroups] + ) + + const setGroupActiveId = useCallback((id: string) => { + setTabGroups((groups) => { + // Clone the array + const newGroups = groups.concat() + + const existingGroupIndex = groups.findIndex((group) => xor(group.tabIds, tabIds).length === 0) + + const tabGroup: TabGroup = { + tabIds, + activeId: id, + } + + // If this group already exists, replace it + // Otherwise add to the end + if (existingGroupIndex !== -1) { + newGroups.splice(existingGroupIndex, 1, tabGroup) + } else { + newGroups.push(tabGroup) + } + + return newGroups + }) + }, []) + + return { groupActiveId, setGroupActiveId } +} + +export default TabsProvider diff --git a/packages/ui/src/components/Toast/Toast.tsx b/packages/ui/src/components/Toast/Toast.tsx index bb63bfa1150..5d378fccb87 100644 --- a/packages/ui/src/components/Toast/Toast.tsx +++ b/packages/ui/src/components/Toast/Toast.tsx @@ -1,3 +1,5 @@ +'use client' + import * as Portal from '@radix-ui/react-portal' import React, { ComponentProps } from 'react' import { diff --git a/packages/ui/src/components/shadcn/ui/button.tsx b/packages/ui/src/components/shadcn/ui/button.tsx index 16b7902ed9c..81e745911eb 100644 --- a/packages/ui/src/components/shadcn/ui/button.tsx +++ b/packages/ui/src/components/shadcn/ui/button.tsx @@ -1,3 +1,5 @@ +'use client' + import { Slot } from '@radix-ui/react-slot' import { cva, type VariantProps } from 'class-variance-authority' import * as React from 'react' diff --git a/packages/ui/src/components/shadcn/ui/label.tsx b/packages/ui/src/components/shadcn/ui/label.tsx index 09c5bb63b87..01501620c2f 100644 --- a/packages/ui/src/components/shadcn/ui/label.tsx +++ b/packages/ui/src/components/shadcn/ui/label.tsx @@ -1,3 +1,5 @@ +'use client' + import * as LabelPrimitive from '@radix-ui/react-label' import { cva, type VariantProps } from 'class-variance-authority' import * as React from 'react' diff --git a/packages/ui/src/components/shadcn/ui/navigation-menu.tsx b/packages/ui/src/components/shadcn/ui/navigation-menu.tsx index 95cc153e35d..7b21d3ecb69 100644 --- a/packages/ui/src/components/shadcn/ui/navigation-menu.tsx +++ b/packages/ui/src/components/shadcn/ui/navigation-menu.tsx @@ -1,3 +1,5 @@ +'use client' + import * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu' import { cva } from 'class-variance-authority' import { ChevronDown } from 'lucide-react' diff --git a/packages/ui/src/layout/PortalToast/PortalToast.tsx b/packages/ui/src/layout/PortalToast/PortalToast.tsx index d2ba31cdb06..e4564621ae5 100644 --- a/packages/ui/src/layout/PortalToast/PortalToast.tsx +++ b/packages/ui/src/layout/PortalToast/PortalToast.tsx @@ -3,7 +3,7 @@ import { X } from 'lucide-react' import dynamic from 'next/dynamic' import { Toaster, ToastBar, toast } from 'react-hot-toast' -import { Button, IconX } from 'ui' +import { Button } from 'ui' const PortalRootWithNoSSR = dynamic( // @ts-ignore diff --git a/packages/ui/src/layout/banners/Announcement.tsx b/packages/ui/src/layout/banners/Announcement.tsx index b0752c5ae48..a043105509e 100644 --- a/packages/ui/src/layout/banners/Announcement.tsx +++ b/packages/ui/src/layout/banners/Announcement.tsx @@ -4,7 +4,7 @@ import { useEffect, useState } from 'react' import _announcement from './data/Announcement.json' import { IconX, cn } from 'ui' -import { useRouter } from 'next/router' +import { usePathname } from 'next/navigation' import { PropsWithChildren } from 'react' export interface AnnouncementProps { @@ -31,8 +31,8 @@ const Announcement = ({ }: PropsWithChildren) => { const [hidden, setHidden] = useState(false) - const router = useRouter() - const isLaunchWeekSection = router.pathname.includes('launch-week') + const pathname = usePathname() + const isLaunchWeekSection = pathname.includes('launch-week') // override to hide announcement if (!show || !announcement.show) return null diff --git a/packages/ui/src/layout/banners/LW11CountdownBanner/LW11CountdownBanner.tsx b/packages/ui/src/layout/banners/LW11CountdownBanner/LW11CountdownBanner.tsx index eebd3520da7..b92aedbcced 100644 --- a/packages/ui/src/layout/banners/LW11CountdownBanner/LW11CountdownBanner.tsx +++ b/packages/ui/src/layout/banners/LW11CountdownBanner/LW11CountdownBanner.tsx @@ -1,16 +1,15 @@ 'use client' -import { useRouter } from 'next/router' +import { usePathname } from 'next/navigation' import { Button, cn } from 'ui' import announcement from '../data/Announcement.json' import Link from 'next/link' export function LW11CountdownBanner() { - const router = useRouter() - const isHomePage = router.pathname === '/' - const isLaunchWeekPage = router.pathname === '/ga-week' - const isLaunchWeekSection = - router.pathname.includes('/launch-week') || router.pathname.includes('/ga-week') + const pathname = usePathname() + const isHomePage = pathname === '/' + const isLaunchWeekPage = pathname === '/ga-week' + const isLaunchWeekSection = pathname.includes('/launch-week') || pathname.includes('/ga-week') if (isLaunchWeekPage || isHomePage) return null diff --git a/packages/ui/src/lib/Layout/FormLayout/FormLayout.tsx b/packages/ui/src/lib/Layout/FormLayout/FormLayout.tsx index 1895ed2ac27..6240611f739 100644 --- a/packages/ui/src/lib/Layout/FormLayout/FormLayout.tsx +++ b/packages/ui/src/lib/Layout/FormLayout/FormLayout.tsx @@ -64,12 +64,12 @@ export function FormLayout({ const flex = layout === 'flex' - let containerClasses = [] + let containerClasses: Array = [] containerClasses.push(__styles.size[size]) - let labelContainerClasses = [] - let dataInputContainerClasses = [] + let labelContainerClasses: Array = [] + let dataInputContainerClasses: Array = [] if (layout !== 'horizontal' && !labelLayout && !flex) { labelContainerClasses.push(__styles.labels_horizontal_layout) @@ -198,8 +198,8 @@ export function FormLayout({ nonBoxInput && label && layout === 'vertical' ? __styles.non_box_data_input_spacing_vertical : nonBoxInput && label && layout === 'horizontal' - ? __styles.non_box_data_input_spacing_horizontal - : '' + ? __styles.non_box_data_input_spacing_horizontal + : '' } > {children} diff --git a/packages/ui/src/lib/Overlay/Overlay.tsx b/packages/ui/src/lib/Overlay/Overlay.tsx index c20067b9494..62af380f754 100644 --- a/packages/ui/src/lib/Overlay/Overlay.tsx +++ b/packages/ui/src/lib/Overlay/Overlay.tsx @@ -1,3 +1,5 @@ +'use client' + import React, { useEffect, useRef, useState } from 'react' //@ts-ignore import { useOnClickOutside } from './../../lib/Hooks' @@ -12,13 +14,7 @@ interface Props { visible?: boolean overlay?: React.ReactNode children?: React.ReactNode - placement?: - | 'bottomLeft' - | 'bottomRight' - | 'bottomCenter' - | 'topLeft' - | 'topRight' - | 'topCenter' + placement?: 'bottomLeft' | 'bottomRight' | 'bottomCenter' | 'topLeft' | 'topRight' | 'topCenter' onVisibleChange?: any disabled?: boolean triggerElement?: any @@ -71,9 +67,7 @@ function Overlay({ return (
- {placement === 'bottomRight' || - placement === 'bottomLeft' || - placement === 'bottomCenter' ? ( + {placement === 'bottomRight' || placement === 'bottomLeft' || placement === 'bottomCenter' ? ( ) : null}
- {placement === 'topRight' || - placement === 'topLeft' || - placement === 'topCenter' ? ( + {placement === 'topRight' || placement === 'topLeft' || placement === 'topCenter' ? ( ) : null}
diff --git a/packages/ui/src/lib/theme/themeContext.tsx b/packages/ui/src/lib/theme/themeContext.tsx index ff7ec212672..338224aad47 100644 --- a/packages/ui/src/lib/theme/themeContext.tsx +++ b/packages/ui/src/lib/theme/themeContext.tsx @@ -1,3 +1,5 @@ +'use client' + import React, { useLayoutEffect, useMemo } from 'react' import { createContext } from 'react' import defaultTheme from './defaultTheme'
PathType
PathType