Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import { readdir, readFile, stat } from 'node:fs/promises'
import { basename, extname, join } from 'node:path'
import { cache } from 'react'
import { visit, EXIT } from 'unist-util-visit'
import { getCustomContent } from '~/lib/custom-content/getCustomContent'

import { EXAMPLES_DIRECTORY } from '~/lib/docs'

const { metadataTitle } = getCustomContent(['metadata:title'])

const PROMPTS_DIRECTORY = join(EXAMPLES_DIRECTORY, 'prompts')

function parseMarkdown(markdown: string) {
Expand Down Expand Up @@ -85,7 +88,7 @@ export async function generateAiPromptMetadata(props: { params: Promise<{ slug:

if (!prompt) {
return {
title: 'AI Prompt | Supabase Docs',
title: `AI Prompt | ${metadataTitle || 'Supabase'}`,
}
}

Expand Down
7 changes: 4 additions & 3 deletions apps/docs/app/guides/troubleshooting/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { notFound } from 'next/navigation'
import TroubleshootingPage from '~/features/docs/Troubleshooting.page'
import { getAllTroubleshootingEntries, getArticleSlug } from '~/features/docs/Troubleshooting.utils'
import { PROD_URL } from '~/lib/constants'
import { getCustomContent } from '~/lib/custom-content/getCustomContent'

// 60 seconds/minute * 60 minutes/hour * 24 hours/day
// export const revalidate = 86_400
export const dynamicParams = false

const { metadataTitle } = getCustomContent(['metadata:title'])

export default async function TroubleshootingEntryPage(props: {
params: Promise<{ slug: string }>
}) {
Expand All @@ -34,7 +35,7 @@ export const generateMetadata = async (props: { params: Promise<{ slug: string }
const entry = allTroubleshootingEntries.find((entry) => getArticleSlug(entry) === slug)

return {
title: 'Supabase Docs | Troubleshooting' + (entry ? ` | ${entry.data.title}` : ''),
title: `${metadataTitle || 'Supabase'} | Troubleshooting${entry ? ` | ${entry.data.title}` : ''}`,
alternates: {
canonical: `${PROD_URL}/guides/troubleshooting/${slug}`,
},
Expand Down
6 changes: 3 additions & 3 deletions apps/docs/app/guides/troubleshooting/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import {
import { TROUBLESHOOTING_CONTAINER_ID } from '~/features/docs/Troubleshooting.utils.shared'
import { SidebarSkeleton } from '~/layouts/MainSkeleton'
import { PROD_URL } from '~/lib/constants'
import { getCustomContent } from '~/lib/custom-content/getCustomContent'

// 60 seconds/minute * 60 minutes/hour * 24 hours/day
// export const revalidate = 86_400
const { metadataTitle } = getCustomContent(['metadata:title'])

export default async function GlobalTroubleshootingPage() {
const troubleshootingEntries = await getAllTroubleshootingEntries()
Expand Down Expand Up @@ -60,7 +60,7 @@ export default async function GlobalTroubleshootingPage() {
}

export const metadata: Metadata = {
title: 'Supabase Docs | Troubleshooting',
title: `${metadataTitle || 'Supabase'} | Troubleshooting`,
alternates: {
canonical: `${PROD_URL}/guides/troubleshooting`,
},
Expand Down
15 changes: 10 additions & 5 deletions apps/docs/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@ import '../styles/main.scss'
import '../styles/new-docs.scss'
import '../styles/prism-okaidia.scss'

import { type Metadata, type Viewport } from 'next'
import { TelemetryTagManager } from 'common'

import { genFaviconData } from 'common/MetaFavicons/app-router'

import type { Metadata, Viewport } from 'next'
import { GlobalProviders } from '~/features/app.providers'
import { TopNavSkeleton } from '~/layouts/MainSkeleton'
import { BASE_PATH, IS_PRODUCTION } from '~/lib/constants'
import { TelemetryTagManager } from 'common'
import { getCustomContent } from '~/lib/custom-content/getCustomContent'

const { metadataApplicationName, metadataTitle } = getCustomContent([
'metadata:application_name',
'metadata:title',
])

const metadata: Metadata = {
applicationName: 'Supabase Docs',
title: 'Supabase Docs',
applicationName: metadataApplicationName,
title: metadataTitle,
description:
'Supabase is the Postgres development platform providing all the backend features you need to build a product.',
metadataBase: new URL('https://supabase.com'),
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/components/HomePageCover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Link from 'next/link'
import { isFeatureEnabled, useBreakpoint } from 'common'
import { cn, IconBackground } from 'ui'
import { IconPanel } from 'ui-patterns/IconPanel'
import { useCustomContent } from '../hooks/custom-content/useCustomContent'
import { getCustomContent } from '../lib/custom-content/getCustomContent'
import DocsCoverLogo from './DocsCoverLogo'

const { sdkDart: sdkDartEnabled, sdkKotlin: sdkKotlinEnabled } = isFeatureEnabled([
Expand Down Expand Up @@ -37,7 +37,7 @@ function AiPrompt({ className }: { className?: string }) {
const HomePageCover = (props) => {
const isXs = useBreakpoint(639)
const iconSize = isXs ? 'sm' : 'lg'
const { homepageHeading } = useCustomContent(['homepage:heading'])
const { homepageHeading } = getCustomContent(['homepage:heading'])

const frameworks = [
{
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { memo, useState } from 'react'
import { useIsLoggedIn, useIsUserLoading, useUser } from 'common'
import { Button, buttonVariants, cn } from 'ui'
import { AuthenticatedDropdownMenu, CommandMenuTrigger } from 'ui-patterns'
import { useCustomContent } from '../../../hooks/custom-content/useCustomContent'
import { getCustomContent } from '../../../lib/custom-content/getCustomContent'
import GlobalNavigationMenu from './GlobalNavigationMenu'
import useDropdownMenu from './useDropdownMenu'

Expand Down Expand Up @@ -110,7 +110,7 @@ const TopNavBar: FC = () => {
}

const HeaderLogo = memo(() => {
const { navigationLogo } = useCustomContent(['navigation:logo'])
const { navigationLogo } = getCustomContent(['navigation:logo'])

return (
<Link
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/guides/realtime/settings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ All changes made in this screen will disconnect all your connected clients to en

You can set the following settings using the Realtime Settings screen in your Dashboard:

- Channel Restrictions: You can toggle this settings to set Realtime to allow public channels or set it to use only private channels with [Realtime Authorization](/docs/content/guides/realtime/authorization).
- Channel Restrictions: You can toggle this settings to set Realtime to allow public channels or set it to use only private channels with [Realtime Authorization](/docs/guides/realtime/authorization).
- Database connection pool size: Determines the number of connections used for Realtime Authorization RLS checking
{/* supa-mdx-lint-disable-next-line Rule004ExcludeWords */}
- Max concurrent clients: Determines the maximum number of clients that can be connected
Expand Down
5 changes: 4 additions & 1 deletion apps/docs/features/docs/GuidesMdx.utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import { GUIDES_DIRECTORY, isValidGuideFrontmatter, type GuideFrontmatter } from
import { GuideModelLoader } from '~/resources/guide/guideModelLoader'
import { newEditLink } from './GuidesMdx.template'
import { checkGuidePageEnabled } from './NavigationPageStatus.utils'
import { getCustomContent } from '~/lib/custom-content/getCustomContent'

const { metadataTitle } = getCustomContent(['metadata:title'])

const PUBLISHED_SECTIONS = [
'ai',
Expand Down Expand Up @@ -170,7 +173,7 @@ const genGuideMeta =
const ogType = pathname.split('/')[2]

return {
title: `${meta.title} | Supabase Docs`,
title: `${meta.title} | ${metadataTitle || 'Supabase'}`,
description: meta.description || meta.subtitle,
// @ts-ignore
alternates: {
Expand Down
7 changes: 5 additions & 2 deletions apps/docs/features/docs/Reference.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import type { Metadata, ResolvingMetadata } from 'next'
import { redirect } from 'next/navigation'
import { visit } from 'unist-util-visit'

import { REFERENCES, clientSdkIds, selfHostingServices } from '~/content/navigation.references'
import { clientSdkIds, REFERENCES, selfHostingServices } from '~/content/navigation.references'
import { getFlattenedSections } from '~/features/docs/Reference.generated.singleton'
import { generateOpenGraphImageMeta } from '~/features/seo/openGraph'
import { BASE_PATH } from '~/lib/constants'
import { getCustomContent } from '~/lib/custom-content/getCustomContent'

const { metadataTitle } = getCustomContent(['metadata:title'])

export interface AbbrevApiReferenceSection {
id: string
Expand Down Expand Up @@ -155,7 +158,7 @@ export async function generateReferenceMetadata(
})

return {
title: `${displayName} API Reference | Supabase Docs`,
title: `${displayName} API Reference | ${metadataTitle || 'Supabase'}`,
description: `API reference for the ${displayName} Supabase SDK`,
...(slug.length > 0
? {
Expand Down
5 changes: 4 additions & 1 deletion apps/docs/features/docs/TroubleshootingSection.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import {
getTroubleshootingKeywordsByTopic,
} from '~/features/docs/Troubleshooting.utils'
import { PROD_URL } from '~/lib/constants'
import { getCustomContent } from '~/lib/custom-content/getCustomContent'

const { metadataTitle } = getCustomContent(['metadata:title'])

interface SectionTroubleshootingPageProps {
topic: ITroubleshootingMetadata['topics'][number]
Expand Down Expand Up @@ -60,7 +63,7 @@ export function generateSectionTroubleshootingMetadata(
sectionName: string
): Metadata {
return {
title: `Supabase Docs | ${sectionName} Troubleshooting`,
title: `${metadataTitle ?? 'Supabase'} | ${sectionName} Troubleshooting`,
alternates: {
canonical: `${PROD_URL}/guides/${topic}/troubleshooting`,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export type CustomContentTypes = {
homepageHeading: string
metadataApplicationName: string
metadataTitle: string
navigationLogo: {
light: string
dark: string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"homepage:heading": "Supabase Documentation",
"metadata:application_name": "Supabase Docs",
"metadata:title": "Supabase Docs",
"navigation:logo": {
"light": "/docs/supabase-light.svg",
"dark": "/docs/supabase-dark.svg"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
/*
* @vitest-environment jsdom
*/

import { cleanup, renderHook } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'

beforeEach(() => {
vi.clearAllMocks()
vi.resetModules()
cleanup()
})

describe('useCustomContent', () => {
describe('getCustomContent', () => {
it('should return null if content is not found in the custom-content.json file', async () => {
vi.doMock('./custom-content.json', () => ({
default: {
'navigation:logo': null,
},
}))

const { useCustomContent } = await import('./useCustomContent')
const { result } = renderHook(() => useCustomContent(['navigation:logo']))
expect(result.current.navigationLogo).toEqual(null)
const { getCustomContent } = await import('./getCustomContent')
const result = getCustomContent(['navigation:logo'])
expect(result.navigationLogo).toEqual(null)
})

it('should return the content for the key passed in if it exists in the custom-content.json file', async () => {
Expand All @@ -35,12 +29,12 @@ describe('useCustomContent', () => {
},
}))

const { useCustomContent } = await import('./useCustomContent')
const { result } = renderHook(() => useCustomContent(['navigation:logo', 'homepage:heading']))
expect(result.current.navigationLogo).toEqual({
const { getCustomContent } = await import('./getCustomContent')
const result = getCustomContent(['navigation:logo', 'homepage:heading'])
expect(result.navigationLogo).toEqual({
light: 'https://example.com/logo-light.svg',
dark: 'https://example.com/logo-dark.svg',
})
expect(result.current.homepageHeading).toEqual('Custom Heading')
expect(result.homepageHeading).toEqual('Custom Heading')
})
})
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
/*
* [Charis 2025-09-29] This file is a duplicate of studio's useCustomContent.ts
* for now.
*
* We should probably consolidate these two files in the future, but we want to
* get this change shipped quickly without worrying about smoke testing all the
* components affected by a refactor.
*/

import type { CustomContentTypes } from './CustomContent.types'
import customContentRaw from './custom-content.json'

const customContentStaticObj = customContentRaw as Omit<typeof customContentRaw, '$schema'>
type CustomContent = keyof typeof customContentStaticObj
export type CustomContent = keyof typeof customContentStaticObj

type SnakeToCamelCase<S extends string> = S extends `${infer First}_${infer Rest}`
? `${First}${SnakeToCamelCase<Capitalize<Rest>>}`
Expand All @@ -29,21 +20,16 @@ function contentToCamelCase(feature: CustomContent) {
.join('') as CustomContentToCamelCase<typeof feature>
}

const useCustomContent = <T extends CustomContent[]>(
contents: T
): {
type CustomContentResult<T extends CustomContent[]> = {
[key in CustomContentToCamelCase<T[number]>]:
| CustomContentTypes[CustomContentToCamelCase<T[number]>]
| null
} => {
// [Joshen] Running into some TS errors without the `as` here - must be overlooking something super simple
}

export const getCustomContent = <T extends CustomContent[]>(
contents: T
): CustomContentResult<T> => {
return Object.fromEntries(
contents.map((content) => [contentToCamelCase(content), customContentStaticObj[content]])
) as {
[key in CustomContentToCamelCase<T[number]>]:
| CustomContentTypes[CustomContentToCamelCase<T[number]>]
| null
}
) as CustomContentResult<T>
}

export { useCustomContent }
7 changes: 5 additions & 2 deletions apps/docs/scripts/llms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import 'dotenv/config'
import fs from 'node:fs/promises'
import { fileURLToPath } from 'node:url'

import { isFeatureEnabled } from 'common/enabled-features'
import { isFeatureEnabled } from '../../../packages/common/enabled-features/index.js'
import { getCustomContent } from '../lib/custom-content/getCustomContent.js'
import {
fetchCliLibReferenceSource,
fetchCSharpLibReferenceSource,
Expand Down Expand Up @@ -35,6 +36,8 @@ const {
sdkSwift: sdkSwiftEnabled,
} = isFeatureEnabled(['sdk:csharp', 'sdk:dart', 'sdk:kotlin', 'sdk:python', 'sdk:swift'])

const { metadataTitle } = getCustomContent(['metadata:title'])

function toLink(source: Source) {
return `[${source.title}](https://supabase.com/${source.relPath})`
}
Expand Down Expand Up @@ -115,7 +118,7 @@ async function generateMainLlmsTxt() {
const sourceLinks = SOURCES.filter((source) => source.enabled !== false)
.map((source) => `- ${toLink(source)}`)
.join('\n')
const fullText = `# Supabase Docs\n\n${sourceLinks}`
const fullText = `# ${metadataTitle}\n\n${sourceLinks}`
fs.writeFile('public/llms.txt', fullText)
}

Expand Down
2 changes: 1 addition & 1 deletion apps/studio/components/interfaces/HomePageActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export const HomePageActions = ({
{ key: PROJECT_STATUS.ACTIVE_HEALTHY, label: 'Active' },
{ key: PROJECT_STATUS.INACTIVE, label: 'Paused' },
].map(({ key, label }) => (
<div className="flex items-center gap-x-2 py-1">
<div className="flex items-center gap-x-2 py-1" key={key}>
<Checkbox_Shadcn_
id={key}
name={key}
Expand Down
Loading
Loading