diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx index be2973bde7e..4e5ecf473e4 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx @@ -3,177 +3,261 @@ import { memo, useEffect } from 'react' import { menuState, useMenuLevelId } from '~/hooks/useMenuState' import NavigationMenuGuideList from './NavigationMenuGuideList' import NavigationMenuRefList from './NavigationMenuRefList' - -import spec_js_v2 from '~/../../spec/supabase_js_v2.yml' assert { type: 'yml' } -import spec_js_v1 from '~/../../spec/supabase_js_v1.yml' assert { type: 'yml' } -import spec_dart_v1 from '~/../../spec/supabase_dart_v1.yml' assert { type: 'yml' } -import spec_dart_v0 from '~/../../spec/supabase_dart_v0.yml' assert { type: 'yml' } -import spec_csharp_v0 from '~/../../spec/supabase_csharp_v0.yml' assert { type: 'yml' } -import spec_python_v2 from '~/../../spec/supabase_py_v2.yml' assert { type: 'yml' } -import spec_swift_v0 from '~/../../spec/supabase_swift_v0.yml' assert { type: 'yml' } - -// import { gen_v3 } from '~/lib/refGenerator/helpers' -import apiCommonSections from '~/../../spec/common-api-sections.json' -import cliCommonSections from '~/../../spec/common-cli-sections.json' -import libCommonSections from '~/../../spec/common-client-libs-sections.json' -import authServerCommonSections from '~/../../spec/common-self-hosting-auth-sections.json' -import realtimeServerCommonSections from '~/../../spec/common-self-hosting-realtime-sections.json' -import analyticsServerCommonSections from '~/../../spec/common-self-hosting-analytics-sections.json' -import functionsServerCommonSections from '~/../../spec/common-self-hosting-functions-sections.json' -import storageServerCommonSections from '~/../../spec/common-self-hosting-storage-sections.json' -import { flattenSections } from '~/lib/helpers' import NavigationMenuHome from './HomeMenu' +import { Json } from '~/types' +import { ICommonBase } from '~/components/reference/Reference.types' -// Filter libCommonSections for just the relevant sections in the current library -export function generateAllowedClientLibKeys(sections, spec) { - // Filter parent sections first - - const specIds = spec.functions.map((func) => { - return func.id - }) - - const newShape = flattenSections(sections).filter((section) => { - if (specIds.includes(section.id)) { - return section - } - }) - - const final = newShape.map((func) => { - return func.id - }) - - return final +// Import dynamically to code split / reduce app bundle size +const specImports = { + specJsV1: async () => (await import('~/../../spec/supabase_js_v1.yml')).default, + specJsV2: async () => (await import('~/../../spec/supabase_js_v2.yml')).default, + specDartV0: async () => (await import('~/../../spec/supabase_dart_v0.yml')).default, + specDartV1: async () => (await import('~/../../spec/supabase_dart_v1.yml')).default, + specPythonV2: async () => (await import('~/../../spec/supabase_py_v2.yml')).default, + specCSharpV0: async () => (await import('~/../../spec/supabase_csharp_v0.yml')).default, + specSwiftV0: async () => (await import('~/../../spec/supabase_swift_v0.yml')).default, } -export type RefIdOptions = - | 'reference_javascript_v1' - | 'reference_javascript_v2' - | 'reference_dart_v0' - | 'reference_dart_v1' - | 'reference_csharp_v0' - | 'reference_python_v2' - | 'reference_swift_v0' - | 'reference_cli' - | 'reference_api' - | 'reference_self_hosting_auth' - | 'reference_self_hosting_storage' - | 'reference_self_hosting_realtime' - | 'reference_self_hosting_analytics' - | 'reference_self_hosting_functions' +const commonSectionImports = { + apiCommonSections: async () => (await import('~/../../spec/common-api-sections.json')).default, + cliCommonSections: async () => (await import('~/../../spec/common-cli-sections.json')).default, + libCommonSections: async () => + (await import('~/../../spec/common-client-libs-sections.json')).default, + analyticsServerCommonSections: async () => + (await import('~/../../spec/common-self-hosting-analytics-sections.json')).default, + authServerCommonSections: async () => + (await import('~/../../spec/common-self-hosting-auth-sections.json')).default, + functionsServerCommonSections: async () => + (await import('~/../../spec/common-self-hosting-functions-sections.json')).default, + realtimeServerCommonSections: async () => + (await import('~/../../spec/common-self-hosting-realtime-sections.json')).default, + storageServerCommonSections: async () => + (await import('~/../../spec/common-self-hosting-storage-sections.json')).default, +} -export type RefKeyOptions = - | 'javascript' - | 'dart' - | 'csharp' - | 'python' - | 'swift' - | 'cli' - | 'api' - | 'self-hosting-auth' - | 'self-hosting-storage' - | 'self-hosting-realtime' - | 'self-hosting-analytics' - | 'self-hosting-functions' +interface BaseMenu { + id: string + path: string + type: string +} + +interface HomeMenu extends BaseMenu { + type: 'home' +} + +interface GuideMenu extends BaseMenu { + type: 'guide' +} + +interface ReferenceMenu extends BaseMenu { + type: 'reference' + commonSectionImport: () => Promise + specImport?: () => Promise +} + +type Menu = HomeMenu | GuideMenu | ReferenceMenu + +const menus: Menu[] = [ + { + id: 'home', + path: '/', + type: 'home', + }, + { + id: 'gettingstarted', + path: '/guides/getting-started', + type: 'guide', + }, + { + id: 'database', + path: '/guides/database', + type: 'guide', + }, + { + id: 'api', + path: '/guides/api', + type: 'guide', + }, + { + id: 'auth', + path: '/guides/auth', + type: 'guide', + }, + { + id: 'functions', + path: '/guides/functions', + type: 'guide', + }, + { + id: 'realtime', + path: '/guides/realtime', + type: 'guide', + }, + { + id: 'storage', + path: '/guides/storage', + type: 'guide', + }, + { + id: 'platform', + path: '/guides/platform', + type: 'guide', + }, + { + id: 'resources', + path: '/guides/resources', + type: 'guide', + }, + { + id: 'self_hosting', + path: '/guides/self-hosting', + type: 'guide', + }, + { + id: 'integrations', + path: '/guides/integrations', + type: 'guide', + }, + { + id: 'supabase_cli', + // TODO: Add path '/reference/cli/config' + path: '/guides/cli', + type: 'guide', + }, + { + id: 'reference_javascript_v1', + path: '/reference/javascript/v1', + commonSectionImport: commonSectionImports.libCommonSections, + specImport: specImports.specJsV1, + type: 'reference', + }, + { + id: 'reference_javascript_v2', + path: '/reference/javascript', + commonSectionImport: commonSectionImports.libCommonSections, + specImport: specImports.specJsV2, + type: 'reference', + }, + { + id: 'reference_dart_v0', + path: '/reference/dart/v0', + commonSectionImport: commonSectionImports.libCommonSections, + specImport: specImports.specDartV0, + type: 'reference', + }, + { + id: 'reference_dart_v1', + path: '/reference/dart', + commonSectionImport: commonSectionImports.libCommonSections, + specImport: specImports.specDartV1, + type: 'reference', + }, + { + id: 'reference_csharp_v0', + path: '/reference/csharp', + commonSectionImport: commonSectionImports.libCommonSections, + specImport: specImports.specCSharpV0, + type: 'reference', + }, + { + id: 'reference_python_v2', + path: '/reference/python', + commonSectionImport: commonSectionImports.libCommonSections, + specImport: specImports.specPythonV2, + type: 'reference', + }, + { + id: 'reference_swift_v0', + path: '/reference/swift', + commonSectionImport: commonSectionImports.libCommonSections, + specImport: specImports.specSwiftV0, + type: 'reference', + }, + { + id: 'reference_cli', + path: '/reference/cli', + commonSectionImport: commonSectionImports.cliCommonSections, + type: 'reference', + }, + { + id: 'reference_api', + path: '/reference/api', + commonSectionImport: commonSectionImports.apiCommonSections, + type: 'reference', + }, + { + id: 'reference_self_hosting_auth', + path: '/reference/self-hosting-auth', + commonSectionImport: commonSectionImports.authServerCommonSections, + type: 'reference', + }, + { + id: 'reference_self_hosting_storage', + path: '/reference/self-hosting-storage', + commonSectionImport: commonSectionImports.storageServerCommonSections, + type: 'reference', + }, + { + id: 'reference_self_hosting_realtime', + path: '/reference/self-hosting-realtime', + commonSectionImport: commonSectionImports.realtimeServerCommonSections, + type: 'reference', + }, + { + id: 'reference_self_hosting_analytics', + path: '/reference/self-hosting-analytics', + commonSectionImport: commonSectionImports.analyticsServerCommonSections, + type: 'reference', + }, + { + id: 'reference_self_hosting_functions', + path: '/reference/self-hosting-functions', + commonSectionImport: commonSectionImports.functionsServerCommonSections, + type: 'reference', + }, +] + +function getMenuById(id: string) { + return menus.find((menu) => menu.id === id) +} + +function getMenuByUrl(basePath: string, url: string) { + // If multiple matches, choose the menu with the longest path + const [menu] = menus + .filter(({ path }) => url.startsWith(`${basePath}${path}`)) + .sort((a, b) => b.path.length - a.path.length) + + return menu +} + +function getMenuElement(menu: Menu) { + const menuType = menu.type + switch (menuType) { + case 'home': + return + case 'guide': + return + case 'reference': + return ( + + ) + default: + throw new Error(`Unknown menu type '${menuType}'`) + } +} const NavigationMenu = () => { const router = useRouter() function handleRouteChange(url: string) { - switch (url) { - case `/docs`: - menuState.setMenuLevelId('home') - break - case url.includes(`/docs/guides/getting-started`) && url: - menuState.setMenuLevelId('gettingstarted') - break - case url.includes(`/docs/guides/database`) && url: - menuState.setMenuLevelId('database') - break - case url.includes(`/docs/guides/api`) && url: - menuState.setMenuLevelId('api') - break - case url.includes(`/docs/guides/auth`) && url: - menuState.setMenuLevelId('auth') - break - case url.includes(`/docs/guides/functions`) && url: - menuState.setMenuLevelId('functions') - break - case url.includes(`/docs/guides/realtime`) && url: - menuState.setMenuLevelId('realtime') - break - case url.includes(`/docs/guides/storage`) && url: - menuState.setMenuLevelId('storage') - break - case url.includes(`/docs/guides/platform`) && url: - menuState.setMenuLevelId('platform') - break - case url.includes(`/docs/guides/resources`) && url: - menuState.setMenuLevelId('resources') - break - case url.includes(`/docs/guides/self-hosting`) && url: - menuState.setMenuLevelId('self_hosting') - break - case url.includes(`/docs/guides/integrations`) && url: - menuState.setMenuLevelId('integrations') - break - case url.includes(`/docs/guides/cli`) && url: - menuState.setMenuLevelId('supabase_cli') - break - // JS v1 - case url.includes(`/docs/reference/javascript/v1`) && url: - menuState.setMenuLevelId('reference_javascript_v1') - break - // JS v2 (latest) - case url.includes(`/docs/reference/javascript`) && url: - menuState.setMenuLevelId('reference_javascript_v2') - break - // dart v0 - case url.includes(`/docs/reference/dart/v0`) && url: - menuState.setMenuLevelId('reference_dart_v0') - break - // dart v1 (latest) - case url.includes(`/docs/reference/dart`) && url: - menuState.setMenuLevelId('reference_dart_v1') - break - // C# v0 (latest) - case url.includes(`/docs/reference/csharp`) && url: - menuState.setMenuLevelId('reference_csharp_v0') - break - // python v2 (latest) - case url.includes(`/docs/reference/python`) && url: - menuState.setMenuLevelId('reference_python_v2') - break - // swift v1 (latest) - case url.includes(`/docs/reference/swift`) && url: - menuState.setMenuLevelId('reference_swift_v0') - break - case url.includes(`/docs/reference/cli/config`) && url: - menuState.setMenuLevelId('supabase_cli') - break - case url.includes(`/docs/reference/cli`) && url: - menuState.setMenuLevelId('reference_cli') - break - case url.includes(`/docs/reference/api`) && url: - menuState.setMenuLevelId('reference_api') - break - case url.includes(`/docs/reference/self-hosting-auth`) && url: - menuState.setMenuLevelId('reference_self_hosting_auth') - break - case url.includes(`/docs/reference/self-hosting-storage`) && url: - menuState.setMenuLevelId('reference_self_hosting_storage') - break - case url.includes(`/docs/reference/self-hosting-realtime`) && url: - menuState.setMenuLevelId('reference_self_hosting_realtime') - break - case url.includes(`/docs/reference/self-hosting-analytics`) && url: - menuState.setMenuLevelId('reference_self_hosting_analytics') - break - case url.includes(`/docs/reference/self-hosting-functions`) && url: - menuState.setMenuLevelId('reference_self_hosting_functions') - break - - default: - break + const menu = getMenuByUrl(router.basePath, url) + if (menu) { + menuState.setMenuLevelId(menu.id) } } @@ -187,168 +271,9 @@ const NavigationMenu = () => { }, [router.events]) const level = useMenuLevelId() + const menu = getMenuById(level) - const isHomeActive = 'home' === level - const isGettingStartedActive = 'gettingstarted' === level - const isDatabaseActive = 'database' === level - const isApiActive = 'api' === level - const isAuthActive = 'auth' === level - const isFunctionsActive = 'functions' === level - const isRealtimeActive = 'realtime' === level - const isStorageActive = 'storage' === level - const issupabase_cliActive = 'supabase_cli' === level - const isPlatformActive = 'platform' === level - const isResourcesActive = 'resources' === level - const isSelfHosting = 'self_hosting' === level - const isIntegrationsActive = 'integrations' === level - const isReferenceActive = 'reference' === level - - const isReference_Javascript_V1 = 'reference_javascript_v1' === level - const isReference_Javascript_V2 = 'reference_javascript_v2' === level - const isReference_Dart_V0 = 'reference_dart_v0' === level - const isReference_Dart_V1 = 'reference_dart_v1' === level - const isReference_Csharp_V0 = 'reference_csharp_v0' === level - const isReference_Python_V2 = 'reference_python_v2' === level - const isReference_Swift_V0 = 'reference_swift_v0' === level - const isReference_Cli = 'reference_cli' === level - const isReference_Api = 'reference_api' === level - const isReference_Self_Hosting_Auth = 'reference_self_hosting_auth' === level - const isReference_Self_Hosting_Storage = 'reference_self_hosting_storage' === level - const isReference_Self_Hosting_Realtime = 'reference_self_hosting_realtime' === level - const isReference_Self_Hosting_Analytics = 'reference_self_hosting_analytics' === level - const isReference_Self_Hosting_Functions = 'reference_self_hosting_functions' === level - - return ( -
- {/* // main menu */} - - - - - - - - - - - - - - - {/* // Client Libs */} - - - - - - - - {/* // Tools */} - - - {/* // Self Hosting Server */} - - - - - -
- ) + return getMenuElement(menu) } export default memo(NavigationMenu) diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts index ad3d100450f..8fce6ebf334 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts @@ -1,6 +1,31 @@ +import { ICommonItem } from '~/components/reference/Reference.types' + // check if the link is allowed to be displayed export function isFuncNotInLibraryOrVersion(id, type, allowedKeys) { if (id && allowedKeys && !allowedKeys.includes(id) && type !== 'markdown') { return true } } + +/** + * Recursively filter common sections and their sub items based on + * what is available in their spec + */ +export function deepFilterSections(sections: ICommonItem[], specFunctionIds: string[]) { + return sections + .filter( + (section) => + section.type === 'category' || + section.type === 'markdown' || + specFunctionIds.includes(section.id) + ) + .map((section) => { + if ('items' in section) { + return { + ...section, + items: deepFilterSections(section.items, specFunctionIds), + } + } + return section + }) +} diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefList.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefList.tsx index e14787749fb..b4ee50d55d7 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefList.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefList.tsx @@ -1,31 +1,51 @@ -// import apiCommonSections from '~/../../spec/common-client-libs-sections.json' - -import { RefIdOptions, RefKeyOptions } from './NavigationMenu' +import { Json } from '~/types' import NavigationMenuRefListItems from './NavigationMenuRefListItems' -import React from 'react' +import React, { useEffect, useState } from 'react' +import { ICommonBase, ICommonItem } from '~/components/reference/Reference.types' -interface INavigationMenuRefList { - id: RefIdOptions - lib: RefKeyOptions - commonSections: any[] // to do type up - - // the keys of menu items that are allowed to be shown on the side menu - // if undefined, we show all the menu items - allowedClientKeys?: string[] - active: boolean - spec?: any +interface NavigationMenuRefListProps { + id: string + basePath: string + commonSectionsImport: () => Promise + specImport?: () => Promise } -const NavigationMenuRefList: React.FC = ({ +const NavigationMenuRefList = ({ id, - lib, - commonSections, + basePath, + commonSectionsImport, + specImport, +}: NavigationMenuRefListProps) => { + const [commonSections, setCommonSections] = useState() + const [spec, setSpec] = useState() - active, - spec, -}) => { - if (!active) { + // Dynamic imports allow for code splitting which + // dramatically reduces app bundle size + useEffect(() => { + async function fetchCommonSections() { + const commonSections = await commonSectionsImport() + setCommonSections(commonSections as ICommonItem[]) + } + fetchCommonSections() + }, [commonSectionsImport]) + + useEffect(() => { + if (!specImport) { + return + } + async function fetchSpec() { + const spec = await specImport() + setSpec(spec) + } + fetchSpec() + }, [specImport]) + + if (!commonSections) { + return null + } + + if (specImport && !spec) { return null } @@ -34,19 +54,13 @@ const NavigationMenuRefList: React.FC = ({ }) return ( -
- +
+
) } diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx index b81f6c5a1dd..9b79c109a6f 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx @@ -4,17 +4,15 @@ import { useRouter } from 'next/router' import { IconChevronLeft } from 'ui' import * as NavItems from './NavigationMenu.constants' -import { find } from 'lodash' -import Image from 'next/image' import { useTheme } from 'common/Providers' +import Image from 'next/image' import RevVersionDropdown from '~/components/RefVersionDropdown' import { useMenuActiveRefId } from '~/hooks/useMenuState' -import { RefIdOptions, RefKeyOptions } from './NavigationMenu' import React, { Fragment } from 'react' -import { generateAllowedClientLibKeys } from '~/lib/refGenerator/helpers' -import { isFuncNotInLibraryOrVersion } from './NavigationMenu.utils' +import { ICommonItem, ICommonSection } from '~/components/reference/Reference.types' +import { deepFilterSections } from './NavigationMenu.utils' const HeaderImage = React.memo(function HeaderImage(props: any) { const router = useRouter() @@ -40,38 +38,29 @@ const HeaderLink = React.memo(function HeaderLink(props: any) { ) }) -const FunctionLink = React.memo(function FunctionLink({ - title, - id, - icon, - library, - slug, -}: { +interface FunctionLinkProps { title: string name?: string id: string icon?: string - product?: string - library: string + basePath: string slug: string -}) { +} + +const FunctionLink = React.memo(function FunctionLink({ + title, + id, + icon, + basePath, + slug, +}: FunctionLinkProps) { const router = useRouter() - const activeAccordianItem = useMenuActiveRefId() + const activeAccordionItem = useMenuActiveRefId() - // check if we're on a versioned page - let version = '' - if (router.asPath.includes('v1')) { - version = 'v1' - } - - if (router.asPath.includes('v0')) { - version = 'v0' - } - - const active = activeAccordianItem === id + const active = activeAccordionItem === id return ( -
  • - +
  • + + ) } + let active = section.items.some((item) => item.id === activeAccordionItem) + return ( - - - - - {props.items && - props.items - .filter((item) => props.allowedKeys.includes(item.id)) - .map((item) => { - return ( - - ) - })} - - - + <> + + + + + {section.items.map((item) => { + return ( + + ) + })} + + + + ) }) @@ -158,67 +127,25 @@ const Divider = () => { return
    } -interface INavigationMenuRefList { - id: RefIdOptions - lib: RefKeyOptions - commonSections: any[] // to do type up - - // the keys of menu items that are allowed to be shown on the side menu - // if undefined, we show all the menu items - allowedClientKeys?: string[] +interface NavigationMenuRefListItemsProps { + id: string + basePath: string + commonSections: ICommonItem[] spec?: any } -const Content: React.FC = ({ id, lib, commonSections, spec }) => { - const allowedClientKeys = spec ? generateAllowedClientLibKeys(commonSections, spec) : undefined - - let sections = commonSections - - const allowedKeys = allowedClientKeys - - if (!sections) console.error('no common sections imported') - +const NavigationMenuRefListItems = ({ + id, + basePath, + commonSections, + spec, +}: NavigationMenuRefListItemsProps) => { const menu = NavItems[id] - const databaseFunctions = find(sections, { title: 'Database' }) - ? find(sections, { title: 'Database' }).items - : [] - const authFunctions = find(sections, { title: 'Auth' }) - ? find(sections, { title: 'Auth' }).items - : [] - - const filterIds = - databaseFunctions.length > 0 - ? find(databaseFunctions, { - id: 'using-filters', - }) && - find(databaseFunctions, { - id: 'using-filters', - }) - .items.filter((x) => allowedKeys.includes(x.id)) - .map((x) => x.id) - : [] - const modifierIds = - databaseFunctions.length > 0 - ? find(databaseFunctions, { - id: 'using-modifiers', - }) && - find(databaseFunctions, { - id: 'using-modifiers', - }) - .items.filter((x) => allowedKeys.includes(x.id)) - .map((x) => x.id) - : [] - - const authServerIds = - databaseFunctions.length > 0 - ? find(authFunctions, { - id: 'admin-api', - }) && - find(authFunctions, { - id: 'admin-api', - }).items.map((x) => x.id) - : [] + const specFunctionIds = spec?.functions.map(({ id }) => id) + const filteredSections = spec + ? deepFilterSections(commonSections, specFunctionIds) + : commonSections return (
    @@ -237,61 +164,26 @@ const Content: React.FC = ({ id, lib, commonSections, sp Back to Main Menu -
    -
      - {sections.map((fn: any, fnIndex) => { - // run allow check - if (isFuncNotInLibraryOrVersion(fn.id, fn.type, allowedKeys)) { - return - } - - // handle subtitles with subitems - return fn.id ? ( - - - {fn.items && - fn.items.map((item) => ( - - ))} - - ) : ( - - - - {fn.items && - fn.items.map((item, i) => { - // run allow check - if (isFuncNotInLibraryOrVersion(item.id, item.type, allowedKeys)) - return - return ( - - ) - })} + {filteredSections.map((section) => { + return ( + + {section.type === 'category' ? ( + <> + + + {section.items.map((item) => ( + + ))} + + ) : ( + + )} ) })} @@ -300,4 +192,4 @@ const Content: React.FC = ({ id, lib, commonSections, sp ) } -export default React.memo(Content) +export default React.memo(NavigationMenuRefListItems) diff --git a/apps/docs/components/reference/RefSectionHandler.tsx b/apps/docs/components/reference/RefSectionHandler.tsx index b1b5bebf134..eb0dbdc7455 100644 --- a/apps/docs/components/reference/RefSectionHandler.tsx +++ b/apps/docs/components/reference/RefSectionHandler.tsx @@ -8,10 +8,10 @@ import RefFunctionSection from '~/components/reference/RefFunctionSection' import RefSubLayout from '~/layouts/ref/RefSubLayout' import ApiOperationSection from './ApiOperationSection' import CliCommandSection from './CLICommandSection' -import { IAPISpec, ICommonFunc, IRefStaticDoc, ISpec, TypeSpec } from './Reference.types' +import { IAPISpec, ICommonSection, IRefStaticDoc, ISpec, TypeSpec } from './Reference.types' interface RefSectionHandlerProps { - sections: ICommonFunc[] + sections: ICommonSection[] spec?: ISpec | IAPISpec typeSpec?: TypeSpec pageProps: { docs: IRefStaticDoc[] } @@ -59,46 +59,48 @@ const RefSectionHandler = (props: RefSectionHandlerProps) => { - {props.sections.map((x, i) => { - switch (x.type) { + {props.sections.map((section, i) => { + const sectionType = section.type + switch (sectionType) { case 'markdown': - const markdownData = props.pageProps.docs.find((doc) => doc.id === x.id) + const markdownData = props.pageProps.docs.find((doc) => doc.id === section.id) - return - break + return ( + + ) case 'function': return ( ) case 'cli-command': - return - break + return ( + + ) case 'operation': return ( ) default: - return ( - - ) - break + throw new Error(`Unknown common section type '${sectionType}'`) } })} diff --git a/apps/docs/components/reference/Reference.types.ts b/apps/docs/components/reference/Reference.types.ts index 4ff54b1b991..ce15320180e 100644 --- a/apps/docs/components/reference/Reference.types.ts +++ b/apps/docs/components/reference/Reference.types.ts @@ -33,19 +33,61 @@ export interface IFunctionDefinition { examples?: [] } -export interface ICommonFunc { - id?: string +export interface ICommonBase { + type: string title: string - slug?: string - product?: string - type?: string - parent?: string - items?: ICommonFunc[] + summary?: string } +export interface ICommonBaseSection extends ICommonBase { + id: string + slug: string + excludes?: string[] +} + +export interface ICommonCategory extends ICommonBase { + type: 'category' + items: ICommonSection[] + excludes?: string[] +} + +export interface ICommonMarkdown extends ICommonBaseSection { + type: 'markdown' +} + +export interface ICommonFunctionGroup extends ICommonBaseSection { + type: 'function' + isFunc: false + product: string + items: ICommonFunction[] +} + +export interface ICommonFunction extends ICommonBaseSection { + type: 'function' + product: string + parent?: string +} + +export interface ICommonCliCommand extends ICommonBaseSection { + type: 'cli-command' +} + +export interface ICommonApiOperation extends ICommonBaseSection { + type: 'operation' +} + +export type ICommonSection = + | ICommonMarkdown + | ICommonFunctionGroup + | ICommonFunction + | ICommonCliCommand + | ICommonApiOperation + +export type ICommonItem = ICommonCategory | ICommonSection + export interface IRefFunctionSection { funcData: any - commonFuncData: ICommonFunc + commonFuncData: ICommonFunction spec: any typeSpec?: TypeSpec } diff --git a/apps/docs/layouts/SiteLayout.tsx b/apps/docs/layouts/SiteLayout.tsx index 0185f4918ae..ec0f9952786 100644 --- a/apps/docs/layouts/SiteLayout.tsx +++ b/apps/docs/layouts/SiteLayout.tsx @@ -4,10 +4,10 @@ import Link from 'next/link' import NavigationMenu from '~/components/Navigation/NavigationMenu/NavigationMenu' import TopNavBarRef from '~/components/Navigation/NavigationMenu/TopNavBarRef' -import { memo, useEffect } from 'react' +import Head from 'next/head' +import { PropsWithChildren, memo } from 'react' import Footer from '~/components/Navigation/Footer' import { menuState, useMenuLevelId, useMenuMobileOpen } from '~/hooks/useMenuState' -import Head from 'next/head' const levelsData = { home: { @@ -207,25 +207,6 @@ const MobileMenuBackdrop = memo(function MobileMenuBackdrop() { ) }) -const SideMenu = memo(function SideMenu() { - return ( -
      - -
      - ) -}) - const HeaderLogo = memo(function HeaderLogo() { const { isDarkMode } = useTheme() return ( @@ -311,13 +292,26 @@ const NavContainer = memo(function NavContainer() {
  • - +
    + +
    ) }) -const SiteLayout = ({ children }) => { +const SiteLayout = ({ children }: PropsWithChildren<{}>) => { return ( <> diff --git a/apps/docs/lib/helpers.ts b/apps/docs/lib/helpers.ts index 7a6c5de3d45..112898e065a 100644 --- a/apps/docs/lib/helpers.ts +++ b/apps/docs/lib/helpers.ts @@ -1,6 +1,4 @@ -// [Joshen] I think this can be done better, as its mostly used to choose what - -import { ICommonFunc } from '../components/reference/Reference.types' +import { ICommonBase, ICommonItem, ICommonSection } from '../components/reference/Reference.types' // menus to render in the SideBar.js (Ref Nav.constants.ts) export function getPageType(asPath: string) { @@ -36,18 +34,25 @@ export function getPageType(asPath: string) { return page } -export function flattenSections(sections: ICommonFunc[]) { - let a: ICommonFunc[] = [] - for (let i = 0; i < sections.length; i++) { - if (sections[i].id) { - // only push a section that has an id - // these are reserved for sidebar subtitles - a.push(sections[i]) +/** + * Flattens common sections recursively by their `items`. + * + * _Note:_ `sections` type set to `ICommonBase[]` instead of + * `ICommonItem[]` until TypeScript supports JSON imports as const: + * https://github.com/microsoft/TypeScript/issues/32063 + */ +export function flattenSections(sections: ICommonBase[]): ICommonSection[] { + return sections.reduce((acc, section: ICommonItem) => { + // Flatten sub-items + if ('items' in section) { + let newSections = acc + + if (section.type !== 'category') { + newSections.push(section) + } + + return newSections.concat(flattenSections(section.items)) } - if (sections[i].items) { - // if there are subitems, loop through - a = a.concat(flattenSections(sections[i].items)) - } - } - return a + return acc.concat(section) + }, []) } diff --git a/apps/docs/lib/refGenerator/helpers.ts b/apps/docs/lib/refGenerator/helpers.ts index 3325d45786f..d7c6ec52147 100644 --- a/apps/docs/lib/refGenerator/helpers.ts +++ b/apps/docs/lib/refGenerator/helpers.ts @@ -3,6 +3,7 @@ import { TsDoc } from '../../generator/legacy/definitions' import { values, mapValues } from 'lodash' import { OpenAPIV3 } from 'openapi-types' import { flattenSections } from '../helpers' +import { ICommonItem } from '~/components/reference/Reference.types' export function extractTsDocNode(nodeToFind: string, definition: any) { const nodePath = nodeToFind.split('.') @@ -294,12 +295,13 @@ export const toArrayWithKey = (obj: object, keyAs: string) => }) ) -export function generateAllowedClientLibKeys(sections, spec) { +/** + * Get a list of common section IDs that are available in this spec + */ +export function getAvailableSectionIds(sections: ICommonItem[], spec: any) { // Filter parent sections first - const specIds = spec.functions.map((func) => { - return func.id - }) + const specIds = spec.functions.map(({ id }) => id) const newShape = flattenSections(sections).filter((section) => { if (specIds.includes(section.id)) { diff --git a/apps/docs/scripts/search/sources/reference-doc.ts b/apps/docs/scripts/search/sources/reference-doc.ts index 61743a7748f..c3e5acecbed 100644 --- a/apps/docs/scripts/search/sources/reference-doc.ts +++ b/apps/docs/scripts/search/sources/reference-doc.ts @@ -3,7 +3,7 @@ import { readFile } from 'fs/promises' import yaml from 'js-yaml' import { OpenAPIV3 } from 'openapi-types' import { - ICommonFunc, + ICommonFunction, IFunctionDefinition, ISpec, } from '../../../components/reference/Reference.types' @@ -29,7 +29,7 @@ export abstract class ReferenceSource extends BaseSource { const specContents = await readFile(this.specFilePath, 'utf8') const refSectionsContents = await readFile(this.sectionsFilePath, 'utf8') - const refSections: ICommonFunc[] = JSON.parse(refSectionsContents) + const refSections: ICommonFunction[] = JSON.parse(refSectionsContents) const flattenedRefSections = flattenSections(refSections) const checksum = createHash('sha256') @@ -69,7 +69,7 @@ export abstract class ReferenceSource extends BaseSource { abstract getSpecSections(specContents: string): SpecSection[] abstract matchSpecSection(specSections: SpecSection[], id: string): SpecSection - abstract formatSection(specSection: SpecSection, refSection: ICommonFunc): string + abstract formatSection(specSection: SpecSection, refSection: ICommonFunction): string } export class OpenApiReferenceSource extends ReferenceSource { @@ -106,7 +106,7 @@ export class ClientLibReferenceSource extends ReferenceSource functionDefinition.id === id) } - formatSection(functionDefinition: IFunctionDefinition, refSection: ICommonFunc): string { + formatSection(functionDefinition: IFunctionDefinition, refSection: ICommonFunction): string { const { title } = refSection const { description, title: functionName } = functionDefinition diff --git a/spec/common-api-sections.json b/spec/common-api-sections.json index 59bb6396028..13f5c689c57 100644 --- a/spec/common-api-sections.json +++ b/spec/common-api-sections.json @@ -6,6 +6,7 @@ "type": "markdown" }, { + "type": "category", "title": "Projects", "items": [ { @@ -35,6 +36,7 @@ ] }, { + "type": "category", "title": "Secrets", "items": [ { @@ -58,6 +60,7 @@ ] }, { + "type": "category", "title": "Typescript", "items": [ { @@ -69,6 +72,7 @@ ] }, { + "type": "category", "title": "Functions", "items": [ { @@ -109,8 +113,8 @@ } ] }, - { + "type": "category", "title": "Custom Hostname", "items": [ { @@ -146,6 +150,7 @@ ] }, { + "type": "category", "title": "pgsodium", "items": [ { @@ -163,6 +168,7 @@ ] }, { + "type": "category", "title": "Network", "items": [ { diff --git a/spec/common-cli-sections.json b/spec/common-cli-sections.json index e01f4d5ece2..b624e7ec6c5 100644 --- a/spec/common-cli-sections.json +++ b/spec/common-cli-sections.json @@ -14,6 +14,7 @@ "type": "markdown" }, { + "type": "category", "title": "General", "items": [ { @@ -55,6 +56,7 @@ ] }, { + "type": "category", "title": "Testing", "items": [ { @@ -72,6 +74,7 @@ ] }, { + "type": "category", "title": "Generate Types", "items": [ { @@ -95,6 +98,7 @@ ] }, { + "type": "category", "title": "Database", "items": [ { @@ -148,6 +152,7 @@ ] }, { + "type": "category", "title": "Migrations", "items": [ { @@ -177,6 +182,7 @@ ] }, { + "type": "category", "title": "Projects", "items": [ { @@ -200,6 +206,7 @@ ] }, { + "type": "category", "title": "Organizations", "items": [ { @@ -217,6 +224,7 @@ ] }, { + "type": "category", "title": "Edge Functions", "items": [ { @@ -258,6 +266,7 @@ ] }, { + "type": "category", "title": "Secrets", "items": [ { @@ -287,6 +296,7 @@ ] }, { + "type": "category", "title": "Authentication", "items": [ { @@ -334,6 +344,7 @@ ] }, { + "type": "category", "title": "Custom Domains", "items": [ { @@ -375,6 +386,7 @@ ] }, { + "type": "category", "title": "Vanity Subdomains", "items": [ { @@ -410,6 +422,7 @@ ] }, { + "type": "category", "title": "Network Bans", "items": [ { @@ -433,6 +446,7 @@ ] }, { + "type": "category", "title": "Network Restrictions", "items": [ { @@ -456,6 +470,7 @@ ] }, { + "type": "category", "title": "SSL Enforcement", "items": [ { @@ -479,6 +494,7 @@ ] }, { + "type": "category", "title": "Autocompletion Scripts", "items": [ { diff --git a/spec/common-client-libs-sections.json b/spec/common-client-libs-sections.json index 06bacd153c6..03e7d4a9c32 100644 --- a/spec/common-client-libs-sections.json +++ b/spec/common-client-libs-sections.json @@ -51,6 +51,7 @@ ] }, { + "type": "category", "title": "Database", "items": [ { @@ -375,6 +376,7 @@ ] }, { + "type": "category", "title": "Auth", "items": [ { @@ -688,6 +690,7 @@ ] }, { + "type": "category", "title": "Functions", "items": [ { @@ -700,6 +703,7 @@ ] }, { + "type": "category", "title": "Realtime", "items": [ { @@ -761,6 +765,7 @@ ] }, { + "type": "category", "title": "Storage", "items": [ { @@ -892,6 +897,7 @@ ] }, { + "type": "category", "title": "Misc", "excludes": [ "reference_dart_v1", diff --git a/spec/common-self-hosting-analytics-sections.json b/spec/common-self-hosting-analytics-sections.json index 21732ffa309..4579dd3a298 100644 --- a/spec/common-self-hosting-analytics-sections.json +++ b/spec/common-self-hosting-analytics-sections.json @@ -7,6 +7,7 @@ "type": "markdown" }, { + "type": "category", "title": "Management API", "items": [ { diff --git a/spec/common-self-hosting-auth-sections.json b/spec/common-self-hosting-auth-sections.json index 82b090de047..2814fb67584 100644 --- a/spec/common-self-hosting-auth-sections.json +++ b/spec/common-self-hosting-auth-sections.json @@ -7,6 +7,7 @@ "type": "markdown" }, { + "type": "category", "title": "Usage", "items": [ { diff --git a/spec/common-self-hosting-storage-sections.json b/spec/common-self-hosting-storage-sections.json index cdcd9bcf537..0e90c80c252 100644 --- a/spec/common-self-hosting-storage-sections.json +++ b/spec/common-self-hosting-storage-sections.json @@ -7,6 +7,7 @@ "type": "markdown" }, { + "type": "category", "title": "Usage", "items": [ {