diff --git a/packages/website/src/features/Docs/DocsCannonfilesPage.tsx b/packages/website/src/features/Docs/DocsCannonfilesPage.tsx index 35924fc0c..173924c36 100644 --- a/packages/website/src/features/Docs/DocsCannonfilesPage.tsx +++ b/packages/website/src/features/Docs/DocsCannonfilesPage.tsx @@ -214,19 +214,23 @@ const CustomTable: React.FC<{ ); -export const DocsCannonfilesPage: FC = () => { +const DocsCannonfilesPage: FC = () => { const isSmall = useBreakpointValue({ base: true, sm: true, md: false, }); - const cannonfileSpecs = useCannonfileSpecs(); + const { isLoading, data: cannonfileSpecs, error } = useCannonfileSpecs(); - if (!cannonfileSpecs) { + if (isLoading) { return ; } + if (!cannonfileSpecs) { + return Error: {error?.message}; + } + return ( @@ -789,3 +793,5 @@ export const DocsCannonfilesPage: FC = () => { ); }; + +export default DocsCannonfilesPage; diff --git a/packages/website/src/features/Docs/DocsCliPage.tsx b/packages/website/src/features/Docs/DocsCliPage.tsx index 68591f029..614311450 100644 --- a/packages/website/src/features/Docs/DocsCliPage.tsx +++ b/packages/website/src/features/Docs/DocsCliPage.tsx @@ -29,29 +29,15 @@ import { Tr, useBreakpointValue, } from '@chakra-ui/react'; -import commandsConfig from '@usecannon/cli/dist/src/commands/config'; import React, { FC } from 'react'; import { FaYarn } from 'react-icons/fa'; import { ImNpm } from 'react-icons/im'; import { SiPnpm } from 'react-icons/si'; +import { useCommandsConfig } from '@/hooks/useCommandsConfig'; +import { CustomSpinner } from '@/components/CustomSpinner'; const basicCommands = ['run', 'build', 'verify', 'publish']; -const commandsData: any[] = []; - -const collectCommandsData = (config: any, parentName?: string) => { - Object.entries(config).forEach(([k, v]) => { - const name = parentName ? `${parentName} ${k}` : k; - if ((v as any).commands) { - collectCommandsData((v as any).commands, name); - } else { - commandsData.push({ ...(v as any), name }); - } - }); -}; - -collectCommandsData(commandsConfig); - interface CustomLinkProps { href: string; children: React.ReactNode; @@ -319,13 +305,23 @@ const renderCommandConfig = (commandConfig: any) => { ); }; -export const DocsCliPage: FC = () => { +const DocsCliPage: FC = () => { + const { commandsData, isLoading, error } = useCommandsConfig(); + const isSmall = useBreakpointValue({ base: true, sm: true, md: false, }); + if (isLoading) { + return ; + } + + if (error) { + return Error: {error.message}; + } + return ( @@ -504,3 +500,5 @@ export const DocsCliPage: FC = () => { ); }; + +export default DocsCliPage; diff --git a/packages/website/src/features/Docs/DocsLandingPage.tsx b/packages/website/src/features/Docs/DocsLandingPage.tsx index 0ebbdf853..254156f7d 100644 --- a/packages/website/src/features/Docs/DocsLandingPage.tsx +++ b/packages/website/src/features/Docs/DocsLandingPage.tsx @@ -43,7 +43,7 @@ const linkStyle = { _hover: { borderBottomColor: 'gray.400' }, }; -export const DocsLandingPage = () => { +const DocsLandingPage = () => { const isLargeScreen = useBreakpointValue({ base: false, md: true }); return ( @@ -304,3 +304,5 @@ export const DocsLandingPage = () => { ); }; + +export default DocsLandingPage; diff --git a/packages/website/src/hooks/cannonfileSpecs.ts b/packages/website/src/hooks/cannonfileSpecs.ts index 0c5163fd6..978405ffc 100644 --- a/packages/website/src/hooks/cannonfileSpecs.ts +++ b/packages/website/src/hooks/cannonfileSpecs.ts @@ -1,7 +1,7 @@ import { chainDefinitionSchema } from '@usecannon/builder/dist/src/schemas'; import { runSchema } from '@usecannon/cli/dist/src/schemas'; import { compile } from 'json-schema-to-typescript'; -import { useEffect, useState } from 'react'; +import { useQuery } from '@tanstack/react-query'; import { zodToJsonSchema } from 'zod-to-json-schema'; interface CannonfileSpec { @@ -59,74 +59,71 @@ const getJsonSchemaPropType = async (prop: any) => { return result.replace('export interface Type ', '').replace('export type Type = ', ''); }; -export function useCannonfileSpecs() { - const [cannonfileSpecs, setCannonfileSpecs] = useState>(); +async function fetchCannonfileSpecs() { + const chainDefinitionJsonSchema = zodToJsonSchema(chainDefinitionSchema, { + $refStrategy: 'none', + }); - useEffect(() => { - const fetchCannonfileSpecs = async () => { - const chainDefinitionJsonSchema = zodToJsonSchema(chainDefinitionSchema, { - $refStrategy: 'none', - }); + const runJsonSchema = zodToJsonSchema(runSchema, { + $refStrategy: 'none', + }); - const runJsonSchema = zodToJsonSchema(runSchema, { - $refStrategy: 'none', - }); + const result = new Map(); - const result = new Map(); + const metadataKeys = ['name', 'preset', 'version', 'description', 'keywords', 'privateSourceCode']; + const metadataSpecs: Spec[] = []; + for (const key of metadataKeys) { + metadataSpecs.push(await getSpec(chainDefinitionJsonSchema, key)); + } + result.set('metadata', { + description: 'Provide metadata for your Cannonfile.', + specs: metadataSpecs, + }); - const metadataKeys = ['name', 'preset', 'version', 'description', 'keywords', 'privateSourceCode']; - const metadataSpecs: Spec[] = []; - for (const key of metadataKeys) { - metadataSpecs.push(await getSpec(chainDefinitionJsonSchema, key)); - } - result.set('metadata', { - description: 'Provide metadata for your Cannonfile.', - specs: metadataSpecs, - }); - - for (const stepName in (chainDefinitionJsonSchema as any).properties) { - if (metadataKeys.includes(stepName)) { - continue; - } - - const stepSpecs: Spec[] = []; - const stepJsonSchema = (chainDefinitionJsonSchema as any).properties[stepName].additionalProperties; - - for (const key in stepJsonSchema?.properties) { - stepSpecs.push(await getSpec(stepJsonSchema, key)); - } - result.set(stepName, { - description: (chainDefinitionJsonSchema as any).properties[stepName].description, - specs: stepSpecs, - }); - } + for (const stepName in (chainDefinitionJsonSchema as any).properties) { + if (metadataKeys.includes(stepName)) { + continue; + } + + const stepSpecs: Spec[] = []; + const stepJsonSchema = (chainDefinitionJsonSchema as any).properties[stepName].additionalProperties; + + for (const key in stepJsonSchema?.properties) { + stepSpecs.push(await getSpec(stepJsonSchema, key)); + } + result.set(stepName, { + description: (chainDefinitionJsonSchema as any).properties[stepName].description, + specs: stepSpecs, + }); + } - const runSpecs: Spec[] = []; - for (const key in (runJsonSchema as any).properties) { - runSpecs.push(await getSpec(runJsonSchema, key)); - } - result.set('run', { - description: - '⚠ This operation breaks composability—only use this as a last resort. (Instead, you should use a custom Cannon plug-in if this is necessary for your deployment.) Execute a custom script. This script is passed a ChainBuilder object as parameter.', - specs: runSpecs, - }); - - const deprecatedKeys = ['setting', 'provision', 'import', 'contract']; - for (const key of deprecatedKeys) { - if (result.has(key)) { - const step = result.get(key); - if (step) { - step.deprecated = true; - result.set(key, step); - } - } - } + const runSpecs: Spec[] = []; + for (const key in (runJsonSchema as any).properties) { + runSpecs.push(await getSpec(runJsonSchema, key)); + } + result.set('run', { + description: + '⚠ This operation breaks composability—only use this as a last resort. (Instead, you should use a custom Cannon plug-in if this is necessary for your deployment.) Execute a custom script. This script is passed a ChainBuilder object as parameter.', + specs: runSpecs, + }); - setCannonfileSpecs(result); - }; + const deprecatedKeys = ['setting', 'provision', 'import', 'contract']; + for (const key of deprecatedKeys) { + if (result.has(key)) { + const step = result.get(key); + if (step) { + step.deprecated = true; + result.set(key, step); + } + } + } - void fetchCannonfileSpecs(); - }, []); + return result; +} - return cannonfileSpecs; +export function useCannonfileSpecs() { + return useQuery({ + queryKey: ['cannonfileSpecs'], + queryFn: fetchCannonfileSpecs, + }); } diff --git a/packages/website/src/hooks/useCommandsConfig.ts b/packages/website/src/hooks/useCommandsConfig.ts new file mode 100644 index 000000000..112ca09e8 --- /dev/null +++ b/packages/website/src/hooks/useCommandsConfig.ts @@ -0,0 +1,44 @@ +import { useState, useEffect } from 'react'; + +interface CommandData { + name: string; + [key: string]: any; +} + +const collectCommandsData = (config: any, parentName?: string): CommandData[] => { + const data: CommandData[] = []; + Object.entries(config).forEach(([k, v]) => { + const name = parentName ? `${parentName} ${k}` : k; + if ((v as any).commands) { + data.push(...collectCommandsData((v as any).commands, name)); + } else { + data.push({ ...(v as any), name }); + } + }); + return data; +}; + +export const useCommandsConfig = () => { + const [commandsData, setCommandsData] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const loadCommandsConfig = async () => { + try { + const { default: commandsConfig } = await import('@usecannon/cli/dist/src/commands/config'); + + const newCommandsData = collectCommandsData(commandsConfig); + setCommandsData(newCommandsData); + setIsLoading(false); + } catch (err) { + setError(err as Error); + setIsLoading(false); + } + }; + + void loadCommandsConfig(); + }, []); + + return { commandsData, isLoading, error }; +}; diff --git a/packages/website/src/pages/learn/cannonfile/index.tsx b/packages/website/src/pages/learn/cannonfile/index.tsx index 5b1cdbfb9..672a94bb9 100644 --- a/packages/website/src/pages/learn/cannonfile/index.tsx +++ b/packages/website/src/pages/learn/cannonfile/index.tsx @@ -1,8 +1,16 @@ -import { DocsCannonfilesPage } from '@/features/Docs/DocsCannonfilesPage'; -import { ReactElement } from 'react'; +import { ReactElement, Suspense } from 'react'; +import dynamic from 'next/dynamic'; import Layout from '../_layout'; import { NextSeo } from 'next-seo'; import defaultSEO from '@/constants/defaultSeo'; +import PageLoading from '@/components/PageLoading'; + +const DynamicDocsCannonfilesPage = dynamic( + () => import('@/features/Docs/DocsCannonfilesPage'), + { + ssr: false, + } +); export default function Docs() { return ( @@ -17,10 +25,13 @@ export default function Docs() { description: 'Cannonfile Docs', }} /> - + }> + + ); } + Docs.getLayout = function getLayout(page: ReactElement) { return {page}; }; diff --git a/packages/website/src/pages/learn/cli/index.tsx b/packages/website/src/pages/learn/cli/index.tsx index 4c5dcf23f..0b8cc64af 100644 --- a/packages/website/src/pages/learn/cli/index.tsx +++ b/packages/website/src/pages/learn/cli/index.tsx @@ -1,8 +1,14 @@ -import { DocsCliPage } from '@/features/Docs/DocsCliPage'; -import { ReactElement } from 'react'; +import { ReactElement, Suspense } from 'react'; +import dynamic from 'next/dynamic'; import Layout from '../_layout'; import { NextSeo } from 'next-seo'; import defaultSEO from '@/constants/defaultSeo'; +import PageLoading from '@/components/PageLoading'; + +// Dynamic import of DocsCliPage +const DocsCliPage = dynamic(() => import('@/features/Docs/DocsCliPage'), { + ssr: false, +}); export default function Docs() { return ( @@ -17,10 +23,13 @@ export default function Docs() { description: 'CLI Docs', }} /> - + }> + + ); } + Docs.getLayout = function getLayout(page: ReactElement) { return {page}; }; diff --git a/packages/website/src/pages/learn/guides/build/index.tsx b/packages/website/src/pages/learn/guides/build/index.tsx index 14713abdc..e25b29f5b 100644 --- a/packages/website/src/pages/learn/guides/build/index.tsx +++ b/packages/website/src/pages/learn/guides/build/index.tsx @@ -1,7 +1,7 @@ import { BuildPage } from '@/features/GetStarted/BuildPage'; import { ReactElement } from 'react'; import Layout from '../../_layout'; -import NestedLayout from '../_layout'; +import NestedLayout from '../guideLayout'; import { NextSeo } from 'next-seo'; import defaultSEO from '@/constants/defaultSeo'; diff --git a/packages/website/src/pages/learn/guides/debug/index.tsx b/packages/website/src/pages/learn/guides/debug/index.tsx index cfae492df..b4aea3d74 100644 --- a/packages/website/src/pages/learn/guides/debug/index.tsx +++ b/packages/website/src/pages/learn/guides/debug/index.tsx @@ -1,7 +1,7 @@ import { DebugPage } from '@/features/Debug/DebugPage'; import { ReactElement } from 'react'; import Layout from '../../_layout'; -import NestedLayout from '../_layout'; +import NestedLayout from '../guideLayout'; import { NextSeo } from 'next-seo'; import defaultSEO from '@/constants/defaultSeo'; diff --git a/packages/website/src/pages/learn/guides/get-started/index.tsx b/packages/website/src/pages/learn/guides/get-started/index.tsx index 4ef7ce68d..387f9383d 100644 --- a/packages/website/src/pages/learn/guides/get-started/index.tsx +++ b/packages/website/src/pages/learn/guides/get-started/index.tsx @@ -1,7 +1,7 @@ import { GetStartedPage } from '@/features/GetStarted/GetStartedPage'; import { ReactElement } from 'react'; import Layout from '../../_layout'; -import NestedLayout from '../_layout'; +import NestedLayout from '../guideLayout'; import { NextSeo } from 'next-seo'; import defaultSEO from '@/constants/defaultSeo'; diff --git a/packages/website/src/pages/learn/guides/_layout.tsx b/packages/website/src/pages/learn/guides/guideLayout.tsx similarity index 100% rename from packages/website/src/pages/learn/guides/_layout.tsx rename to packages/website/src/pages/learn/guides/guideLayout.tsx diff --git a/packages/website/src/pages/learn/guides/index.tsx b/packages/website/src/pages/learn/guides/index.tsx index e0095e685..89facd060 100644 --- a/packages/website/src/pages/learn/guides/index.tsx +++ b/packages/website/src/pages/learn/guides/index.tsx @@ -2,7 +2,7 @@ import { useEffect, ReactElement } from 'react'; import { useRouter } from 'next/router'; import { links } from '@/constants/links'; import Layout from '../_layout'; -import NestedLayout from './_layout'; +import GuideLayout from './guideLayout'; import { NextSeo } from 'next-seo'; import defaultSEO from '@/constants/defaultSeo'; @@ -17,7 +17,7 @@ export default function Home() { .catch(() => { // do nothing }); - }, []); + }, [router]); return ( <> @@ -37,7 +37,7 @@ export default function Home() { Home.getLayout = function getLayout(page: ReactElement) { return ( - {page} + {page} ); }; diff --git a/packages/website/src/pages/learn/guides/router/index.tsx b/packages/website/src/pages/learn/guides/router/index.tsx index e1c6df46b..04e85f406 100644 --- a/packages/website/src/pages/learn/guides/router/index.tsx +++ b/packages/website/src/pages/learn/guides/router/index.tsx @@ -1,7 +1,7 @@ import { RouterPage } from '@/features/Router/RouterPage'; import { ReactElement } from 'react'; import Layout from '../../_layout'; -import NestedLayout from '../_layout'; +import NestedLayout from '../guideLayout'; import { NextSeo } from 'next-seo'; import defaultSEO from '@/constants/defaultSeo'; diff --git a/packages/website/src/pages/learn/index.tsx b/packages/website/src/pages/learn/index.tsx index 987aa63bf..7b787a5d9 100644 --- a/packages/website/src/pages/learn/index.tsx +++ b/packages/website/src/pages/learn/index.tsx @@ -1,8 +1,16 @@ -import { DocsLandingPage } from '@/features/Docs/DocsLandingPage'; -import { ReactElement } from 'react'; +import { ReactElement, Suspense } from 'react'; import Layout from './_layout'; +import dynamic from 'next/dynamic'; import { NextSeo } from 'next-seo'; import defaultSEO from '@/constants/defaultSeo'; +import PageLoading from '@/components/PageLoading'; + +const DynamicDocsLandingPage = dynamic( + () => import('@/features/Docs/DocsLandingPage'), + { + ssr: false, + } +); export default function Docs() { return ( @@ -17,7 +25,9 @@ export default function Docs() { description: 'Docs', }} /> - + }> + + ); }