diff --git a/components/Code.tsx b/components/Code.tsx index 7460fa4e8..fb6f7f1ef 100644 --- a/components/Code.tsx +++ b/components/Code.tsx @@ -2,7 +2,7 @@ import React, { useContext } from 'react'; import classnames from 'classnames'; import { BlockContext, BlockContextValue } from '~/context'; -export default function Code({ children }: { children: any }) { +export default function Code({ children }: { children: React.ReactNode }) { // eslint-disable-next-line react-hooks/rules-of-hooks const blockContext = useContext(BlockContext); return ( diff --git a/components/DocTable.tsx b/components/DocTable.tsx index a8862797b..b717ab73e 100644 --- a/components/DocTable.tsx +++ b/components/DocTable.tsx @@ -3,16 +3,25 @@ import React from 'react'; import Link from 'next/link'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +interface Author { + name: string; + photo?: string; +} + interface DocTableProps { frontmatter: { - Specification: string; - Published: string; - authors: string[]; - Metaschema: string; + Specification?: string; + Published?: string; + authors?: (string | Author)[]; + Metaschema?: string; }; } const DocTable = ({ frontmatter }: DocTableProps) => { + const authors = frontmatter.authors ?? []; + const getAuthorName = (author: string | Author): string => + typeof author === 'string' ? author : author.name; + return ( @@ -22,63 +31,71 @@ const DocTable = ({ frontmatter }: DocTableProps) => {
-
-
-
- Specification -
-
- - {frontmatter.Specification} - + {frontmatter.Specification && ( +
+
+
+ Specification +
+
+ + {frontmatter.Specification} + +
-
-
-
-
- Published + )} + {frontmatter.Published && ( +
+
+
+ Published +
+
{frontmatter.Published}
-
{frontmatter.Published}
-
-
-
-
- Authors -
-
- {frontmatter.authors.map((author: string, index: number) => ( - - {author} - {index < frontmatter.authors.length - 1 ? ', ' : ''} - - ))} + )} + {authors.length > 0 && ( +
+
+
+ Authors +
+
+ {authors.map((author, index: number) => ( + + {getAuthorName(author)} + {index < authors.length - 1 ? ', ' : ''} + + ))} +
-
-
-
-
- Metaschema -
-
- - {frontmatter.Metaschema} - + )} + {frontmatter.Metaschema && ( +
+
+
+ Metaschema +
+
+ + {frontmatter.Metaschema} + +
-
+ )}
diff --git a/components/GettingStarted.tsx b/components/GettingStarted.tsx index e50ba43a9..6a4ae85d0 100644 --- a/components/GettingStarted.tsx +++ b/components/GettingStarted.tsx @@ -9,13 +9,21 @@ async function fetchData() { const response = await fetch('/data/getting-started-examples.json'); const data = await response.json(); - const defaultSchemaData = data.find((data: any) => data.default === true); + const defaultSchemaData = data.find( + (data: { default?: boolean; file: string; instances: unknown[] }) => + data.default === true, + ); const schemaResp = await fetch(defaultSchemaData.file); const schemaData = await schemaResp.json(); const defaultInstanceData = defaultSchemaData.instances.find( - (instance: any) => instance.default === true, + (instance: { + default?: boolean; + file: string; + details: string; + valid: string; + }) => instance.default === true, ); const instanceResp = await fetch(defaultInstanceData.file); @@ -31,11 +39,13 @@ async function fetchData() { } interface SchemaOption { + name: string; file: string; instances: InstanceOption[]; } interface InstanceOption { + name: string; file: string; details: string; valid: string; @@ -139,7 +149,7 @@ const GettingStarted = () => { id='Examples' onChange={handleSchemaChange} > - {options.map((option: any, id: number) => ( + {options.map((option, id) => ( @@ -194,7 +204,7 @@ const GettingStarted = () => { id='Examples' onChange={handleInstanceChange} > - {instances.map((instance: any, id: number) => ( + {instances.map((instance, id) => ( diff --git a/components/JsonEditor.tsx b/components/JsonEditor.tsx index c7870e7ca..6bc07b561 100644 --- a/components/JsonEditor.tsx +++ b/components/JsonEditor.tsx @@ -1,7 +1,14 @@ /* eslint-disable linebreak-style */ import React, { useContext } from 'react'; import { BaseEditor, createEditor, Descendant, Text } from 'slate'; -import { Editable, ReactEditor, Slate, withReact } from 'slate-react'; +import { + Editable, + ReactEditor, + Slate, + withReact, + RenderLeafProps, + RenderElementProps, +} from 'slate-react'; import { cn } from '@/lib/utils'; import getPartsOfJson, { SyntaxPart } from '~/lib/getPartsOfJson'; import jsonSchemaReferences from './jsonSchemaLinks'; @@ -21,7 +28,7 @@ import { atomOneDark } from 'react-syntax-highlighter/dist/cjs/styles/hljs'; type CustomElement = CustomNode | CustomText; type CustomNode = { type: 'paragraph'; children: CustomText[] }; -type CustomText = { text: string }; +type CustomText = { text: string; syntaxPart?: SyntaxPart }; declare module 'slate' { interface CustomTypes { @@ -215,7 +222,7 @@ const calculateNewDecorationsMap = ( textPathIndexes, ); const highlightingDecorations = multipathDecorations.reduce( - (acc, multipathDecoration: MultipathDecoration) => { + (acc: RangeWithSyntaxPart[], multipathDecoration: MultipathDecoration) => { const decorationsOfNodes = multipathDecoration.nodes.reduce( (acc: RangeWithSyntaxPart[], node: NodeRange) => { const decorationOfNode = { @@ -231,11 +238,11 @@ const calculateNewDecorationsMap = ( }; return [...acc, decorationOfNode]; }, - [], + [] as RangeWithSyntaxPart[], ); return [...acc, ...decorationsOfNodes]; }, - [], + [] as RangeWithSyntaxPart[], ); const decorationMap = makeDecorationsToMap(highlightingDecorations); @@ -359,7 +366,7 @@ export default function JsonEditor({ } }, [codeContent, isJsonMode]); - const parsedCode: null | any = React.useMemo(() => { + const parsedCode: Record | null = React.useMemo(() => { try { return JSON.parse(serializedCode); } catch (e) { @@ -404,8 +411,15 @@ export default function JsonEditor({ let text = ''; /* istanbul ignore else : there is no else block to test here */ if (value) { - value.forEach((e: any) => { - text += e.children[0].text + '\n'; + value.forEach((e: Descendant) => { + if ( + 'children' in e && + Array.isArray(e.children) && + e.children[0] && + 'text' in e.children[0] + ) { + text += (e.children[0] as Text).text + '\n'; + } }); } return text; @@ -414,10 +428,11 @@ export default function JsonEditor({ // copy status react state const [copied, setCopied] = React.useState(false); - const allPathDecorationsMap: Record = React.useMemo( - () => calculateNewDecorationsMap(value, isPartialSchema), - [value, isPartialSchema], - ); + const allPathDecorationsMap: Record = + React.useMemo( + () => calculateNewDecorationsMap(value, isPartialSchema), + [value, isPartialSchema], + ); // Badge text logic for regular code blocks const getBadgeText = () => { @@ -609,17 +624,19 @@ export default function JsonEditor({ /* istanbul ignore next: allPathDecorationsMap[stringPath] cannot be null */ return allPathDecorationsMap[stringPath] || []; }} - renderLeaf={(props: any) => { + renderLeaf={(props: RenderLeafProps) => { const { leaf, children, attributes } = props; + const syntaxType = leaf.syntaxPart?.type ?? ''; const textStyles: undefined | string = (() => { + const parentJsonPath = leaf.syntaxPart?.parentJsonPath ?? ''; if ( [ 'objectPropertyStartQuotes', 'objectPropertyEndQuotes', - ].includes(leaf.syntaxPart?.type) + ].includes(syntaxType) ) return 'text-blue-200'; - if (['objectProperty'].includes(leaf.syntaxPart?.type)) { + if (['objectProperty'].includes(syntaxType)) { const isJsonScope = jsonPathsWithJsonScope .filter( (jsonPathWithScope) => @@ -630,7 +647,7 @@ export default function JsonEditor({ (jsonPathsWithJsonScope) => jsonPathsWithJsonScope.jsonPath, ) - .includes(leaf.syntaxPart?.parentJsonPath); + .includes(parentJsonPath); if ( isJsonScope && jsonSchemaReferences.objectProperty[leaf.text] @@ -652,7 +669,7 @@ export default function JsonEditor({ 'arrayComma', 'arrayStartBracket', 'arrayEndBracket', - ].includes(leaf.syntaxPart?.type) + ].includes(syntaxType) ) return 'text-slate-400'; if ( @@ -661,19 +678,20 @@ export default function JsonEditor({ 'stringValue', 'booleanValue', 'nullValue', - ].includes(leaf.syntaxPart?.type) + ].includes(syntaxType) ) return 'text-lime-200'; // Handle partial schema specific highlighting that might not match exactly - if (!leaf.syntaxPart?.type) { + if (!syntaxType) { // If no syntax part type, apply default white color for partial schemas return isPartialSchema ? 'text-white' : undefined; } })(); const link: null | string = (() => - jsonSchemaReferences?.[leaf.syntaxPart?.type]?.[leaf.text] || + (syntaxType && + jsonSchemaReferences?.[syntaxType]?.[leaf.text]) || null)(); return ( @@ -691,7 +709,7 @@ export default function JsonEditor({ ); }} - renderElement={(props: any) => { + renderElement={(props: RenderElementProps) => { // This will be the path to the image element. const { element, children, attributes } = props; const path = ReactEditor.findPath(editor, element); @@ -783,18 +801,20 @@ export type PathIndex = [number, number[]]; const getMultipathDecorationsByMatchesAndTextPathIndexes = ( syntaxParts: SyntaxPart[], textPathIndexes: PathIndex[], -): any[] => { - const multipathDecorations: any[] = syntaxParts.map((syntaxPart) => { - const nodes = getNodesFromIndexAndLength( - syntaxPart.index, - syntaxPart.length, - textPathIndexes, - ); - return { - nodes: nodes, - syntaxPart, - }; - }); +): MultipathDecoration[] => { + const multipathDecorations: MultipathDecoration[] = syntaxParts.map( + (syntaxPart) => { + const nodes = getNodesFromIndexAndLength( + syntaxPart.index, + syntaxPart.length, + textPathIndexes, + ); + return { + nodes: nodes, + syntaxPart, + }; + }, + ); return multipathDecorations; }; @@ -802,10 +822,10 @@ export const getNodesFromIndexAndLength = ( index: number, length: number, textPathIndexes: PathIndex[], -): any[] => { +): NodeRange[] => { const { nodes } = textPathIndexes.reduce( ( - acc: { nodes: any[]; index: number; length: number }, + acc: { nodes: NodeRange[]; index: number; length: number }, textPathIndex: PathIndex, ) => { if (acc.length <= 0) return acc; @@ -816,7 +836,7 @@ export const getNodesFromIndexAndLength = ( const anchor = acc.index; const focus = Math.min(anchor + acc.length, textPathLength); const lengthInNode = focus - anchor; - const node: any = { anchor, focus, path: nodePath }; + const node: NodeRange = { anchor, focus, path: nodePath }; return { nodes: [...acc.nodes, node], index: 0, @@ -828,9 +848,14 @@ export const getNodesFromIndexAndLength = ( return nodes; }; -const makeDecorationsToMap = (decorations: any[]): Record => { - return decorations.reduce((acc, decoration) => { - const stringPath = decoration.anchor.path.join(','); - return { ...acc, [stringPath]: [...(acc[stringPath] || []), decoration] }; - }, {}); +const makeDecorationsToMap = ( + decorations: RangeWithSyntaxPart[], +): Record => { + return decorations.reduce( + (acc, decoration) => { + const stringPath = decoration.anchor.path.join(','); + return { ...acc, [stringPath]: [...(acc[stringPath] || []), decoration] }; + }, + {} as Record, + ); }; diff --git a/components/Layout.tsx b/components/Layout.tsx index 32879f12c..d9ef519f9 100644 --- a/components/Layout.tsx +++ b/components/Layout.tsx @@ -32,7 +32,9 @@ export default function Layout({ metaTitle, whiteBg, }: Props) { - const showMobileNav = useStore((s: any) => s.overlayNavigation === 'docs'); + const showMobileNav = useStore( + (s: { overlayNavigation: string | null }) => s.overlayNavigation === 'docs', + ); const router = useRouter(); @@ -60,7 +62,7 @@ export default function Layout({ const handleCloseNavbar = (event: MouseEvent) => { if ( mobileNavRef.current && - (mobileNavRef.current as any).contains(event.target) + mobileNavRef.current.contains(event.target as Node) ) { useStore.setState({ overlayNavigation: null }); } @@ -176,7 +178,9 @@ const MainNavLink = ({ const MainNavigation = () => { const section = useContext(SectionContext); - const showMobileNav = useStore((s: any) => s.overlayNavigation === 'docs'); + const showMobileNav = useStore( + (s: { overlayNavigation: string | null }) => s.overlayNavigation === 'docs', + ); const { resolvedTheme, theme } = useTheme(); const [icon, setIcon] = useState(''); diff --git a/cypress/components/DocsHelp.cy.tsx b/cypress/components/DocsHelp.cy.tsx index e8c6a1c6d..491003330 100644 --- a/cypress/components/DocsHelp.cy.tsx +++ b/cypress/components/DocsHelp.cy.tsx @@ -20,7 +20,7 @@ const FEEDBACK_FORM_GITHUB_SUCCESS_MESSAGE = describe('DocsHelp Component', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars let mockRouter: MockRouter; - const extractPathWithoutFragment = (path: any) => path.split('#')[0]; + const extractPathWithoutFragment = (path: string) => path.split('#')[0]; // Note: we are not using the mockRouter in this test file, but it is required to mock the router in the component file beforeEach(() => { diff --git a/cypress/components/Sidebar.cy.tsx b/cypress/components/Sidebar.cy.tsx index 76ddb5554..b73c986e9 100644 --- a/cypress/components/Sidebar.cy.tsx +++ b/cypress/components/Sidebar.cy.tsx @@ -135,7 +135,7 @@ describe('Sidebar Component', () => { }); describe('DocsNav', () => { - let mockSetOpen: any; + let mockSetOpen: Cypress.Agent; beforeEach(() => { mockSetOpen = cy.stub().as('setOpen'); diff --git a/cypress/plugins/mockNextRouterUtils.ts b/cypress/plugins/mockNextRouterUtils.ts index c14185e2a..8bed87384 100644 --- a/cypress/plugins/mockNextRouterUtils.ts +++ b/cypress/plugins/mockNextRouterUtils.ts @@ -4,16 +4,16 @@ */ export interface MockRouter { - push: any; - replace: any; - prefetch: any; + push: Cypress.Agent; + replace: Cypress.Agent; + prefetch: Cypress.Agent; pathname: string; - query: Record; + query: Record; asPath: string; events: { - on: any; - off: any; - emit: any; + on: Cypress.Agent; + off: Cypress.Agent; + emit: Cypress.Agent; }; } diff --git a/lib/calendarUtils.ts b/lib/calendarUtils.ts index 269b7bd0a..6616dfbd3 100644 --- a/lib/calendarUtils.ts +++ b/lib/calendarUtils.ts @@ -1,4 +1,5 @@ import moment from 'moment-timezone'; +import type { CalendarResponse, VEvent } from 'node-ical'; export async function fetchRemoteICalFile(url: string): Promise { try { @@ -13,11 +14,25 @@ export async function fetchRemoteICalFile(url: string): Promise { } } -export function printEventsForNextWeeks(icalData: { [x: string]: any }) { - const arrayDates = []; +function isVEvent(component: CalendarResponse[string]): component is VEvent { + return component.type === 'VEVENT'; +} + +export interface CalendarEventInfo { + title: string | undefined; + time: string; + day: string; + timezone: string; + parsedStartDate: string; +} + +export function printEventsForNextWeeks( + icalData: CalendarResponse, +): CalendarEventInfo[] { + const arrayDates: CalendarEventInfo[] = []; if (!icalData) { console.error('iCal data is empty or invalid.'); - return; + return []; } // Calculate the range of dates for the next 12 weeks from today @@ -26,14 +41,18 @@ export function printEventsForNextWeeks(icalData: { [x: string]: any }) { // Loop through the events in the iCal data for (const k in icalData) { - const event = icalData[k]; + const component = icalData[k]; - if (event.type === 'VEVENT') { + if (isVEvent(component)) { + const event = component; const title = event.summary; const timezoneL = moment.tz.guess(); // Default to UTC if timezone information is not provided - const startDate = moment.tz(event.start, timezoneL); + const eventStart = event.start; + if (!eventStart) continue; + + const startDate = moment.tz(eventStart, timezoneL); // Complicated case - if an RRULE exists, handle multiple recurrences of the event. if (event.rrule !== undefined) { @@ -46,7 +65,7 @@ export function printEventsForNextWeeks(icalData: { [x: string]: any }) { // Loop through the set of date entries to see which recurrences should be printed. for (const date of dates) { const startDate = moment.tz(date, timezoneL); - const eventtimezone = event.start.tz; + const eventtimezone = eventStart.tz ?? 'UTC'; const owntimezone = moment.tz.guess(); const eventOffset = moment.tz(eventtimezone).utcOffset(); const localOffset = moment.tz(owntimezone).utcOffset(); @@ -54,10 +73,11 @@ export function printEventsForNextWeeks(icalData: { [x: string]: any }) { // Check if the event falls within the next 4 weeks from today if (startDate.isBetween(today, nextTwelveWeeksEnd, undefined, '[]')) { - const dateTimezone = moment.tz.zone(event.start.tz); + const dateTimezone = moment.tz.zone(eventtimezone); let offset; if (dateTimezone && offsetDifference) - offset = offsetDifference - dateTimezone.utcOffset(date); + offset = + offsetDifference - dateTimezone.utcOffset(date.getTime()); const newDate = moment(date).subtract(offset, 'minutes').toDate(); diff --git a/lib/getPartsOfJson.ts b/lib/getPartsOfJson.ts index d8b741ccb..0fd3b8ec2 100644 --- a/lib/getPartsOfJson.ts +++ b/lib/getPartsOfJson.ts @@ -220,7 +220,12 @@ const getPartsOfJsonObjectContent = ( stringsWithPayload = [...stringsWithPayload, stringWithPayload]; }); - let keywordsAndValue: any[] = []; + let keywordsAndValue: Array<{ + index?: number; + keyword?: string; + payload?: string; + payloadStartIndex?: number; + }> = []; let openCurlyBrackets = 0; let openSquareBrackets = 0; @@ -303,6 +308,14 @@ const getPartsOfJsonObjectContent = ( }); return keywordsAndValue.reduce((acc, keywordAndValue) => { + if ( + !keywordAndValue.keyword || + keywordAndValue.index === undefined || + !keywordAndValue.payload || + keywordAndValue.payloadStartIndex === undefined + ) { + return acc; + } const propertyJsonPath = `${jsonPath}['${keywordAndValue.keyword}']`; const objectPropertyStartQuotes: SyntaxPart = { type: 'objectPropertyStartQuotes', @@ -340,7 +353,7 @@ const getPartsOfJsonObjectContent = ( objectPropertyEndQuotes, ...partsFromPayload, ]; - }, []); + }, [] as SyntaxPart[]); }; const getPartsOfArrayContent = ( diff --git a/lib/getScopesOfParsedJsonSchema.ts b/lib/getScopesOfParsedJsonSchema.ts index 185272df8..6dcdf9c7d 100644 --- a/lib/getScopesOfParsedJsonSchema.ts +++ b/lib/getScopesOfParsedJsonSchema.ts @@ -7,35 +7,45 @@ export type JsonSchemaPathWithScope = { scope: JsonSchemaScope; }; +interface ParsedJsonSchema { + type?: string; + properties?: Record; + patternProperties?: Record; + items?: unknown; + [key: string]: unknown; +} + export default function getScopesOfParsedJsonSchema( - parsedJsonSchema: any, + parsedJsonSchema: ParsedJsonSchema | unknown, jsonPath = '$', ): JsonSchemaPathWithScope[] { if (typeof parsedJsonSchema !== 'object' || parsedJsonSchema === null) return []; + + const schema = parsedJsonSchema as ParsedJsonSchema; const typeDefinitionScope = { jsonPath, scope: JsonSchemaScope.TypeDefinition, }; - if (parsedJsonSchema.type === 'object') { - const scopesOfProperties = Object.keys( - parsedJsonSchema?.properties || {}, - ).reduce((acc, property) => { + if (schema.type === 'object') { + const scopesOfProperties = Object.keys(schema?.properties || {}).reduce< + JsonSchemaPathWithScope[] + >((acc, property) => { return [ ...acc, ...getScopesOfParsedJsonSchema( - parsedJsonSchema.properties?.[property], + schema.properties?.[property], `${jsonPath}['properties']['${property}']`, ), ]; }, []); const scopesOfPatternProperties = Object.keys( - parsedJsonSchema?.patternProperties || {}, + schema?.patternProperties || {}, ).reduce((acc, property) => { return [ ...acc, ...getScopesOfParsedJsonSchema( - parsedJsonSchema.patternProperties?.[property], + schema.patternProperties?.[property], `${jsonPath}['patternProperties']['${property}']`, ), ]; @@ -46,13 +56,10 @@ export default function getScopesOfParsedJsonSchema( ...scopesOfPatternProperties, ]; } - if (parsedJsonSchema.type === 'array') { + if (schema.type === 'array') { return [ typeDefinitionScope, - ...getScopesOfParsedJsonSchema( - parsedJsonSchema.items, - `${jsonPath}['items']`, - ), + ...getScopesOfParsedJsonSchema(schema.items, `${jsonPath}['items']`), ]; } return [typeDefinitionScope]; diff --git a/lib/getStaticMarkdownProps.ts b/lib/getStaticMarkdownProps.ts index 83709ec50..300fca400 100644 --- a/lib/getStaticMarkdownProps.ts +++ b/lib/getStaticMarkdownProps.ts @@ -3,10 +3,19 @@ import matter from 'gray-matter'; type Props = { params?: { slug: string } }; +interface Frontmatter { + [key: string]: unknown; +} + export default async function getStaticMarkdownProps( props: Props, path: string, -) { +): Promise<{ + props: { + frontmatter: Frontmatter; + content: string; + }; +}> { const slug = props.params?.slug || '_index'; const fileName2 = `${path}/${slug}.md`; @@ -16,7 +25,7 @@ export default async function getStaticMarkdownProps( return { props: { - frontmatter, + frontmatter: frontmatter as Frontmatter, content, }, }; diff --git a/lib/slugifyMarkdownHeadline.ts b/lib/slugifyMarkdownHeadline.ts index ae943e138..c7d49031d 100644 --- a/lib/slugifyMarkdownHeadline.ts +++ b/lib/slugifyMarkdownHeadline.ts @@ -1,13 +1,13 @@ import slugify from 'slugify'; export default function slugifyMarkdownHeadline( - markdownChildren: string | any[], + markdownChildren: string | (string | unknown)[], ): string { const FRAGMENT_REGEX = /\[#(?(\w|-|_)*)\]/g; if (!markdownChildren) return ''; if (typeof markdownChildren === 'string') return slugify(markdownChildren, { lower: true, trim: true }); - const metaSlug = markdownChildren.reduce((acc, child) => { + const metaSlug = markdownChildren.reduce((acc, child) => { if (acc) return acc; if (typeof child !== 'string') return null; const fragment = FRAGMENT_REGEX.exec(child); diff --git a/pages/[slug].page.tsx b/pages/[slug].page.tsx index 7a9e7726e..22eab1a2a 100644 --- a/pages/[slug].page.tsx +++ b/pages/[slug].page.tsx @@ -8,11 +8,12 @@ import { Headline1 } from '~/components/Headlines'; import { SectionContext } from '~/context'; import { DocsHelp } from '~/components/DocsHelp'; import NextPrevButton from '~/components/NavigationButtons'; +import type { Frontmatter } from '~/types/common'; export async function getStaticPaths() { return getStaticMarkdownPaths('pages'); } -export async function getStaticProps(args: any) { +export async function getStaticProps(args: { params?: { slug: string } }) { return getStaticMarkdownProps(args, 'pages'); } @@ -20,8 +21,8 @@ export default function StaticMarkdownPage({ frontmatter, content, }: { - frontmatter: any; - content: any; + frontmatter: Frontmatter; + content: string; }) { const fileRenderType = '_md'; const newTitle = 'JSON Schema - ' + frontmatter.title; diff --git a/pages/_app.page.tsx b/pages/_app.page.tsx index 9acd6abc5..9edb7d2a8 100644 --- a/pages/_app.page.tsx +++ b/pages/_app.page.tsx @@ -5,12 +5,16 @@ import type { AppProps } from 'next/app'; import { ThemeProvider } from 'next-themes'; function MyApp({ Component, pageProps }: AppProps) { - // @ts-ignore - const getLayout = Component.getLayout || ((page: JSX.Element) => page); - const AnyComponent = Component as any; + // Use type assertion for getLayout since Next.js allows custom properties on components + const getLayout = + ( + Component as { + getLayout?: (page: JSX.Element, pageProps?: unknown) => JSX.Element; + } + ).getLayout || ((page: JSX.Element) => page); return ( - {getLayout(, pageProps)} + {getLayout(, pageProps)} ); } diff --git a/pages/ambassadors/index.page.tsx b/pages/ambassadors/index.page.tsx index 259d32035..1f713a1c0 100644 --- a/pages/ambassadors/index.page.tsx +++ b/pages/ambassadors/index.page.tsx @@ -26,7 +26,7 @@ export async function getStaticProps() { export default function ambassadorPages({ ambassadorData, }: { - ambassadorData: any; + ambassadorData: string; }) { return ( diff --git a/pages/blog/generateRssFeed.tsx b/pages/blog/generateRssFeed.tsx index 46b0991f7..135273b40 100644 --- a/pages/blog/generateRssFeed.tsx +++ b/pages/blog/generateRssFeed.tsx @@ -3,7 +3,25 @@ import { Feed, Author, Item } from 'feed'; const SITE_URL = 'https://json-schema.org'; -export default async function generateRssFeed(blogPosts: any) { +interface BlogPostFrontmatter { + title: string; + excerpt?: string; + cover?: string; + date?: string; + type?: string | string[]; + authors?: Array<{ + name: string; + twitter?: string; + }>; +} + +interface BlogPost { + slug: string; + frontmatter: BlogPostFrontmatter; + content?: string; +} + +export default async function generateRssFeed(blogPosts: BlogPost[]) { const today = new Date(); const feed = new Feed({ title: 'JSON Schema Blog RSS Feed', @@ -23,9 +41,9 @@ export default async function generateRssFeed(blogPosts: any) { language: 'en-gb', }); - blogPosts.forEach((post: any) => { - const authors: Author[] = post.frontmatter.authors.map( - (author: any): Author => { + blogPosts.forEach((post) => { + const authors: Author[] = (post.frontmatter.authors || []).map( + (author): Author => { const link = author.twitter ? `https://x.com/${author.twitter}` : undefined; @@ -41,19 +59,29 @@ export default async function generateRssFeed(blogPosts: any) { title: post.frontmatter.title, id: url, link: url, - description: post.frontmatter.excerpt as string, + description: post.frontmatter.excerpt || '', author: authors, - date: new Date(post.frontmatter.date), - image: { - url: `${SITE_URL}${post.frontmatter.cover}`, - }, - category: post.frontmatter.type, - enclosure: { - url: `${SITE_URL}${post.frontmatter.cover}`, - type: 'image', - length: 15026, - }, - published: new Date(post.frontmatter.date), + date: new Date(post.frontmatter.date || Date.now()), + /* eslint-disable indent */ + image: post.frontmatter.cover + ? { + url: `${SITE_URL}${post.frontmatter.cover}`, + } + : undefined, + category: Array.isArray(post.frontmatter.type) + ? post.frontmatter.type.map((t) => ({ name: t })) + : post.frontmatter.type + ? [{ name: post.frontmatter.type }] + : undefined, + enclosure: post.frontmatter.cover + ? { + url: `${SITE_URL}${post.frontmatter.cover}`, + type: 'image', + length: 15026, + } + : undefined, + /* eslint-enable indent */ + published: new Date(post.frontmatter.date || Date.now()), }; feed.addItem(item); }); diff --git a/pages/blog/index.page.tsx b/pages/blog/index.page.tsx index b46ce6a13..7c851944f 100644 --- a/pages/blog/index.page.tsx +++ b/pages/blog/index.page.tsx @@ -13,13 +13,6 @@ import { useRouter } from 'next/router'; import { SectionContext } from '../../context'; import Image from 'next/image'; -type Author = { - name: string; - photo?: string; - link?: string; - byline?: string; -}; - export type blogCategories = | 'All' | 'Community' @@ -29,15 +22,31 @@ export type blogCategories = | 'Opinion' | 'Documentation'; -const getCategories = (frontmatter: any): blogCategories[] => { +interface Frontmatter { + title: string; + date?: string; + excerpt?: string; + cover?: string; + authors?: Array<{ name: string; twitter?: string; photo?: string }>; + type?: string | string[]; + categories?: string | string[]; + [key: string]: unknown; +} + +const getCategories = (frontmatter: Frontmatter): blogCategories[] => { const cat = frontmatter.categories || frontmatter.type; if (!cat) return []; - return Array.isArray(cat) ? cat : [cat]; + const categories = Array.isArray(cat) ? cat : [cat]; + return categories.filter((c): c is blogCategories => isValidCategory(c)); }; -export async function getStaticProps({ query }: { query: any }) { +export async function getStaticProps({ + query, +}: { + query: Record; +}) { const files = fs.readdirSync(PATH); - const blogPosts = files + const blogPosts: BlogPost[] = files .filter((file) => file.substr(-3) === '.md') .map((fileName) => { const slug = fileName.replace('.md', ''); @@ -48,14 +57,14 @@ export async function getStaticProps({ query }: { query: any }) { const { data: frontmatter, content } = matter(fullFileName); return { slug, - frontmatter, + frontmatter: frontmatter as Frontmatter, content, }; }); await generateRssFeed(blogPosts); - const filterTag: string = query?.type || 'All'; + const filterTag: string = (query?.type as string) || 'All'; return { props: { @@ -65,30 +74,39 @@ export async function getStaticProps({ query }: { query: any }) { }; } -function isValidCategory(category: any): category is blogCategories { - return [ - 'All', - 'Community', - 'Case Study', - 'Engineering', - 'Update', - 'Opinion', - 'Documentation', - ].includes(category); +function isValidCategory(category: unknown): category is blogCategories { + return ( + typeof category === 'string' && + [ + 'All', + 'Community', + 'Case Study', + 'Engineering', + 'Update', + 'Opinion', + 'Documentation', + ].includes(category) + ); +} + +interface BlogPost { + slug: string; + frontmatter: Frontmatter; + content: string; } export default function StaticMarkdownPage({ blogPosts, filterTag, }: { - blogPosts: any[]; - filterTag: any; + blogPosts: BlogPost[]; + filterTag: string; }) { const router = useRouter(); // Initialize the filter as an array. If "All" or not specified, we show all posts. - const initialFilters = + const initialFilters: blogCategories[] = filterTag && filterTag !== 'All' - ? filterTag.split(',').filter(isValidCategory) + ? (filterTag.split(',').filter(isValidCategory) as blogCategories[]) : ['All']; const [currentFilterTags, setCurrentFilterTags] = @@ -100,15 +118,15 @@ export default function StaticMarkdownPage({ if (query.type) { const tags = (typeof query.type === 'string' ? query.type : '') .split(',') - .filter(isValidCategory); + .filter(isValidCategory) as blogCategories[]; setCurrentFilterTags(tags.length ? tags : ['All']); } }, [router.query]); useEffect(() => { - const tags = + const tags: blogCategories[] = filterTag && filterTag !== 'All' - ? filterTag.split(',').filter(isValidCategory) + ? (filterTag.split(',').filter(isValidCategory) as blogCategories[]) : ['All']; setCurrentFilterTags(tags); }, [filterTag]); @@ -141,8 +159,8 @@ export default function StaticMarkdownPage({ // First, sort all posts by date descending (for fallback sorting) const postsSortedByDate = [...blogPosts].sort((a, b) => { - const dateA = new Date(a.frontmatter.date).getTime(); - const dateB = new Date(b.frontmatter.date).getTime(); + const dateA = new Date(a.frontmatter.date || 0).getTime(); + const dateB = new Date(b.frontmatter.date || 0).getTime(); return dateB - dateA; }); @@ -173,8 +191,8 @@ export default function StaticMarkdownPage({ if (aMatches !== bMatches) { return bMatches - aMatches; } - const dateA = new Date(a.frontmatter.date).getTime(); - const dateB = new Date(b.frontmatter.date).getTime(); + const dateA = new Date(a.frontmatter.date || 0).getTime(); + const dateB = new Date(b.frontmatter.date || 0).getTime(); return dateB - dateA; }); @@ -201,7 +219,7 @@ export default function StaticMarkdownPage({
{recentBlog[0].frontmatter.title} {recentBlog[0].frontmatter.title} -
-
-
-

- {recentBlog[0].frontmatter.authors[0].name} -

-
- - {recentBlog[0].frontmatter.date} · {timeToRead}{' '} - min read - + {recentBlog[0].frontmatter.authors?.[0] && ( +
+
+
+

+ {recentBlog[0].frontmatter.authors[0].name} +

+
+ + {recentBlog[0].frontmatter.date} · {timeToRead}{' '} + min read + +
-
+ )}
@@ -300,9 +320,9 @@ export default function StaticMarkdownPage({ {/* Blog Posts Grid */}
- {sortedFilteredPosts.map((blogPost: any, idx: number) => { + {sortedFilteredPosts.map((blogPost, idx) => { const { frontmatter, content } = blogPost; - const date = new Date(frontmatter.date); + const date = new Date(frontmatter.date || 0); const postTimeToRead = Math.ceil(readingTime(content).minutes); return ( @@ -313,7 +333,7 @@ export default function StaticMarkdownPage({ >
{frontmatter.title}
{(frontmatter.authors || []).map( - (author: Author, index: number) => ( + (author, index: number) => (
2 + (frontmatter.authors?.length || 0) > 2 ? 'h-8 w-8' : 'h-11 w-11' }`} style={{ - backgroundImage: `url(${author.photo})`, + backgroundImage: `url(${author.photo || ''})`, zIndex: 10 - index, }} /> @@ -373,11 +393,11 @@ export default function StaticMarkdownPage({
- {frontmatter.authors.length > 2 ? ( + {(frontmatter.authors?.length || 0) > 2 ? ( <> {frontmatter.authors - .slice(0, 2) - .map((author: Author, index: number) => ( + ?.slice(0, 2) + .map((author, index: number) => ( {author.name} {index === 0 && ' & '} @@ -386,11 +406,12 @@ export default function StaticMarkdownPage({ {'...'} ) : ( - frontmatter.authors.map( - (author: Author, index: number) => ( + frontmatter.authors?.map( + (author, index: number) => ( {author.name} - {index < frontmatter.authors.length - 1 && + {index < + (frontmatter.authors?.length || 0) - 1 && ' & '} ), diff --git a/pages/blog/posts/[slug].page.tsx b/pages/blog/posts/[slug].page.tsx index 4f4ce4d9a..f14aba90e 100644 --- a/pages/blog/posts/[slug].page.tsx +++ b/pages/blog/posts/[slug].page.tsx @@ -17,7 +17,21 @@ import Image from 'next/image'; export async function getStaticPaths() { return getStaticMarkdownPaths('pages/blog/posts'); } -export async function getStaticProps(args: any) { +interface Author { + name: string; + photo: string; + twitter?: string; +} + +interface Frontmatter { + title: string; + date?: string; + cover?: string; + authors?: Author[]; + [key: string]: unknown; +} + +export async function getStaticProps(args: { params?: { slug: string } }) { return getStaticMarkdownProps(args, 'pages/blog/posts'); } @@ -25,10 +39,10 @@ export default function StaticMarkdownPage({ frontmatter, content, }: { - frontmatter: any; - content: any; + frontmatter: Frontmatter; + content: string; }) { - const date = new Date(frontmatter.date); + const date = frontmatter.date ? new Date(frontmatter.date) : null; const timeToRead = Math.ceil(readingTime(content).minutes); return ( @@ -38,7 +52,7 @@ export default function StaticMarkdownPage({
- {frontmatter.date && ( + {date && (
{date.toLocaleDateString('en-us', { weekday: 'long', @@ -71,34 +85,32 @@ export default function StaticMarkdownPage({ Go back to blog
- {(frontmatter.authors || []).map( - (author: any, index: number) => { - return ( + {(frontmatter.authors || []).map((author, index) => { + return ( +
-
-
-
- {author.name} -
- {author.twitter && ( - - @{author.twitter} - - )} + className='bg-slate-50 h-[44px] w-[44px] rounded-full bg-cover bg-center' + style={{ backgroundImage: `url(${author.photo})` }} + /> +
+
+ {author.name}
+ {author.twitter && ( + + @{author.twitter} + + )}
- ); - }, - )} +
+ ); + })}
diff --git a/pages/community/index.page.tsx b/pages/community/index.page.tsx index dccf67ba0..352c6d113 100644 --- a/pages/community/index.page.tsx +++ b/pages/community/index.page.tsx @@ -14,6 +14,7 @@ import ical from 'node-ical'; import { fetchRemoteICalFile, printEventsForNextWeeks, + CalendarEventInfo, } from '../../lib/calendarUtils'; export const getStaticProps: GetStaticProps = async () => { @@ -41,7 +42,9 @@ export const getStaticProps: GetStaticProps = async () => { const remoteICalUrl = 'https://calendar.google.com/calendar/ical/json.schema.community%40gmail.com/public/basic.ics'; const datesInfo = await fetchRemoteICalFile(remoteICalUrl) - .then((icalData: any) => printEventsForNextWeeks(ical.parseICS(icalData))) + .then((icalData) => + icalData ? printEventsForNextWeeks(ical.parseICS(icalData)) : [], + ) .catch((error) => console.error('Error:', error)); return { props: { @@ -52,7 +55,27 @@ export const getStaticProps: GetStaticProps = async () => { }; }; -export default function communityPages(props: any) { +interface Author { + name: string; + photo: string; +} + +interface BlogPostFrontmatter { + title: string; + cover: string; + excerpt: string; + date: string; + authors: Author[]; +} + +export default function communityPages(props: { + blogPosts: { + slug: string; + frontmatter: BlogPostFrontmatter; + content: string; + }[]; + datesInfo: CalendarEventInfo[]; +}) { const blogPosts = props.blogPosts; const timeToRead = Math.ceil(readingTime(blogPosts[0].content).minutes); @@ -248,7 +271,7 @@ export default function communityPages(props: any) {

Upcoming events

- {props.datesInfo.map((event: any, index: any) => ( + {props.datesInfo.map((event, index) => (
{(blogPosts[0].frontmatter.authors || []).map( - (author: any, index: number) => { + (author, index: number) => { return (
{blogPosts[0].frontmatter.authors .slice(0, 2) - .map((author: any, index: number) => ( + .map((author, index: number) => ( {author.name} {index === 0 && ' & '} @@ -359,11 +382,9 @@ export default function communityPages(props: any) { {'...'} ) : ( - blogPosts[0].frontmatter.authors.map( - (author: any) => ( - {author.name} - ), - ) + blogPosts[0].frontmatter.authors.map((author) => ( + {author.name} + )) )}

diff --git a/pages/draft-05/index.page.tsx b/pages/draft-05/index.page.tsx index e8c83ca8c..939ce5608 100644 --- a/pages/draft-05/index.page.tsx +++ b/pages/draft-05/index.page.tsx @@ -8,6 +8,7 @@ import DocTable from '~/components/DocTable'; import { Headline1 } from '~/components/Headlines'; import { DocsHelp } from '~/components/DocsHelp'; import NextPrevButton from '~/components/NavigationButtons'; +import { Frontmatter } from '~/types/common'; export async function getStaticProps() { const index = fs.readFileSync('pages/draft-05/index.md', 'utf-8'); @@ -27,12 +28,23 @@ export async function getStaticProps() { }; } +interface DraftFrontmatter extends Frontmatter { + Specification?: string; + Published?: string; + Metaschema?: string; +} + +interface BlocksData { + index: string; + body: string; +} + export default function ImplementationsPages({ blocks, frontmatter, }: { - blocks: any; - frontmatter: any; + blocks: BlocksData; + frontmatter: DraftFrontmatter; }) { const fileRenderType = 'indexmd'; return ( diff --git a/pages/draft-06/[slug].page.tsx b/pages/draft-06/[slug].page.tsx index 64655ed3c..1b4dcd2be 100644 --- a/pages/draft-06/[slug].page.tsx +++ b/pages/draft-06/[slug].page.tsx @@ -7,11 +7,12 @@ import getStaticMarkdownProps from '~/lib/getStaticMarkdownProps'; import { Headline1 } from '~/components/Headlines'; import { SectionContext } from '~/context'; import { DocsHelp } from '~/components/DocsHelp'; +import { Frontmatter } from '~/types/common'; export async function getStaticPaths() { return getStaticMarkdownPaths('pages/draft-06'); } -export async function getStaticProps(args: any) { +export async function getStaticProps(args: { params?: { slug: string } }) { return getStaticMarkdownProps(args, 'pages/draft-06'); } @@ -19,14 +20,14 @@ export default function StaticMarkdownPage({ frontmatter, content, }: { - frontmatter: any; - content: any; + frontmatter: Frontmatter; + content: string; }) { const fileRenderType = '_md'; const newTitle = 'JSON Schema - ' + frontmatter.title; return ( - + {newTitle} diff --git a/pages/draft-06/index.page.tsx b/pages/draft-06/index.page.tsx index 12a760519..1b2b3c0c3 100644 --- a/pages/draft-06/index.page.tsx +++ b/pages/draft-06/index.page.tsx @@ -8,6 +8,7 @@ import DocTable from '~/components/DocTable'; import { Headline1 } from '~/components/Headlines'; import { DocsHelp } from '~/components/DocsHelp'; import NextPrevButton from '~/components/NavigationButtons'; +import { Frontmatter } from '~/types/common'; export async function getStaticProps() { const index = fs.readFileSync('pages/draft-06/index.md', 'utf-8'); @@ -25,12 +26,22 @@ export async function getStaticProps() { }; } +interface DraftFrontmatter extends Frontmatter { + Specification?: string; + Published?: string; + Metaschema?: string; +} + +interface BlocksData { + index: string; +} + export default function ImplementationsPages({ blocks, frontmatter, }: { - blocks: any; - frontmatter: any; + blocks: BlocksData; + frontmatter: DraftFrontmatter; }) { const fileRenderType = 'indexmd'; return ( diff --git a/pages/draft-07/[slug].page.tsx b/pages/draft-07/[slug].page.tsx index f4ef2b082..68b7eaf41 100644 --- a/pages/draft-07/[slug].page.tsx +++ b/pages/draft-07/[slug].page.tsx @@ -7,11 +7,12 @@ import getStaticMarkdownProps from '~/lib/getStaticMarkdownProps'; import { Headline1 } from '~/components/Headlines'; import { SectionContext } from '~/context'; import { DocsHelp } from '~/components/DocsHelp'; +import { Frontmatter } from '~/types/common'; export async function getStaticPaths() { return getStaticMarkdownPaths('pages/draft-07'); } -export async function getStaticProps(args: any) { +export async function getStaticProps(args: { params?: { slug: string } }) { return getStaticMarkdownProps(args, 'pages/draft-07'); } @@ -19,14 +20,14 @@ export default function StaticMarkdownPage({ frontmatter, content, }: { - frontmatter: any; - content: any; + frontmatter: Frontmatter; + content: string; }) { const fileRenderType = '_md'; const newTitle = 'JSON Schema - ' + frontmatter.title; return ( - + {newTitle} diff --git a/pages/draft-07/index.page.tsx b/pages/draft-07/index.page.tsx index a639ca275..f727b6f9a 100644 --- a/pages/draft-07/index.page.tsx +++ b/pages/draft-07/index.page.tsx @@ -8,6 +8,7 @@ import DocTable from '~/components/DocTable'; import { Headline1 } from '~/components/Headlines'; import { DocsHelp } from '~/components/DocsHelp'; import NextPrevButton from '~/components/NavigationButtons'; +import { Frontmatter } from '~/types/common'; export async function getStaticProps() { const index = fs.readFileSync('pages/draft-07/index.md', 'utf-8'); @@ -25,12 +26,22 @@ export async function getStaticProps() { }; } +interface DraftFrontmatter extends Frontmatter { + Specification?: string; + Published?: string; + Metaschema?: string; +} + +interface BlocksData { + index: string; +} + export default function ImplementationsPages({ blocks, frontmatter, }: { - blocks: any; - frontmatter: any; + blocks: BlocksData; + frontmatter: DraftFrontmatter; }) { const fileRenderType = 'indexmd'; return ( diff --git a/pages/draft/2019-09/[slug].page.tsx b/pages/draft/2019-09/[slug].page.tsx index 26acaf0e1..5252c4a30 100644 --- a/pages/draft/2019-09/[slug].page.tsx +++ b/pages/draft/2019-09/[slug].page.tsx @@ -7,11 +7,12 @@ import getStaticMarkdownProps from '~/lib/getStaticMarkdownProps'; import { Headline1 } from '~/components/Headlines'; import { SectionContext } from '~/context'; import { DocsHelp } from '~/components/DocsHelp'; +import { Frontmatter } from '~/types/common'; export async function getStaticPaths() { return getStaticMarkdownPaths('pages/draft/2019-09'); } -export async function getStaticProps(args: any) { +export async function getStaticProps(args: { params?: { slug: string } }) { return getStaticMarkdownProps(args, 'pages/draft/2019-09'); } @@ -19,14 +20,14 @@ export default function StaticMarkdownPage({ frontmatter, content, }: { - frontmatter: any; - content: any; + frontmatter: Frontmatter; + content: string; }) { const fileRenderType = '_md'; const newTitle = 'JSON Schema - ' + frontmatter.title; return ( - + {newTitle} diff --git a/pages/draft/2019-09/index.page.tsx b/pages/draft/2019-09/index.page.tsx index 51898b28e..81fbc7bf5 100644 --- a/pages/draft/2019-09/index.page.tsx +++ b/pages/draft/2019-09/index.page.tsx @@ -8,6 +8,7 @@ import DocTable from '~/components/DocTable'; import { Headline1 } from '~/components/Headlines'; import { DocsHelp } from '~/components/DocsHelp'; import NextPrevButton from '~/components/NavigationButtons'; +import { Frontmatter } from '~/types/common'; export async function getStaticProps() { const index = fs.readFileSync('pages/draft/2019-09/index.md', 'utf-8'); @@ -24,12 +25,22 @@ export async function getStaticProps() { }; } +interface DraftFrontmatter extends Frontmatter { + Specification?: string; + Published?: string; + Metaschema?: string; +} + +interface BlocksData { + index: string; +} + export default function ImplementationsPages({ blocks, frontmatter, }: { - blocks: any; - frontmatter: any; + blocks: BlocksData; + frontmatter: DraftFrontmatter; }) { const fileRenderType = 'indexmd'; return ( diff --git a/pages/draft/2020-12/[slug].page.tsx b/pages/draft/2020-12/[slug].page.tsx index cc2331502..14176f965 100644 --- a/pages/draft/2020-12/[slug].page.tsx +++ b/pages/draft/2020-12/[slug].page.tsx @@ -7,11 +7,12 @@ import getStaticMarkdownProps from '~/lib/getStaticMarkdownProps'; import { Headline1 } from '~/components/Headlines'; import { SectionContext } from '~/context'; import { DocsHelp } from '~/components/DocsHelp'; +import { Frontmatter } from '~/types/common'; export async function getStaticPaths() { return getStaticMarkdownPaths('pages/draft/2020-12'); } -export async function getStaticProps(args: any) { +export async function getStaticProps(args: { params?: { slug: string } }) { return getStaticMarkdownProps(args, 'pages/draft/2020-12'); } @@ -19,14 +20,14 @@ export default function StaticMarkdownPage({ frontmatter, content, }: { - frontmatter: any; - content: any; + frontmatter: Frontmatter; + content: string; }) { const fileRenderType = '_md'; const newTitle = 'JSON Schema - ' + frontmatter.title; return ( - + {newTitle} diff --git a/pages/draft/2020-12/index.page.tsx b/pages/draft/2020-12/index.page.tsx index b5162ef02..64b5be76f 100644 --- a/pages/draft/2020-12/index.page.tsx +++ b/pages/draft/2020-12/index.page.tsx @@ -8,6 +8,7 @@ import DocTable from '~/components/DocTable'; import { Headline1 } from '~/components/Headlines'; import { DocsHelp } from '~/components/DocsHelp'; import NextPrevButton from '~/components/NavigationButtons'; +import { Frontmatter } from '~/types/common'; export async function getStaticProps() { const index = fs.readFileSync('pages/draft/2020-12/index.md', 'utf-8'); @@ -24,12 +25,22 @@ export async function getStaticProps() { }; } +interface DraftFrontmatter extends Frontmatter { + Specification?: string; + Published?: string; + Metaschema?: string; +} + +interface BlocksData { + index: string; +} + export default function ImplementationsPages({ blocks, frontmatter, }: { - blocks: any; - frontmatter: any; + blocks: BlocksData; + frontmatter: DraftFrontmatter; }) { const fileRenderType = 'indexmd'; return ( diff --git a/pages/implementers/[slug].page.tsx b/pages/implementers/[slug].page.tsx index c149e95eb..8dfd7af40 100644 --- a/pages/implementers/[slug].page.tsx +++ b/pages/implementers/[slug].page.tsx @@ -8,11 +8,12 @@ import { Headline1 } from '~/components/Headlines'; import { SectionContext } from '~/context'; import { DocsHelp } from '~/components/DocsHelp'; import NextPrevButton from '~/components/NavigationButtons'; +import { Frontmatter } from '~/types/common'; export async function getStaticPaths() { return getStaticMarkdownPaths('pages/implementers'); } -export async function getStaticProps(args: any) { +export async function getStaticProps(args: { params?: { slug: string } }) { return getStaticMarkdownProps(args, 'pages/implementers'); } @@ -20,13 +21,13 @@ export default function StaticMarkdownPage({ frontmatter, content, }: { - frontmatter: any; - content: any; + frontmatter: Frontmatter; + content: string; }) { const fileRenderType = '_md'; const newTitle = 'JSON Schema - ' + frontmatter.title; return ( - + {newTitle} diff --git a/pages/implementers/index.page.tsx b/pages/implementers/index.page.tsx index 7579ba030..23b3af754 100644 --- a/pages/implementers/index.page.tsx +++ b/pages/implementers/index.page.tsx @@ -17,13 +17,8 @@ export async function getStaticProps() { }, }; } -export default function ContentExample({ - blocks, -}: { - blocks: any[]; - frontmatter: any; - content: any; -}) { + +export default function ContentExample({ blocks }: { blocks: string[] }) { const fileRenderType = '_indexmd'; return ( diff --git a/pages/index.page.tsx b/pages/index.page.tsx index 663c25a6d..9903a68a7 100644 --- a/pages/index.page.tsx +++ b/pages/index.page.tsx @@ -11,6 +11,7 @@ import Image from 'next/image'; import { fetchRemoteICalFile, printEventsForNextWeeks, + CalendarEventInfo, } from '../lib/calendarUtils'; import { Headline4 } from '~/components/Headlines'; import { GetStaticProps } from 'next'; @@ -27,7 +28,7 @@ const algoliaApiKey: string = process.env.NEXT_PUBLIC_ALGOLIA_API_KEY as string; export const getStaticProps: GetStaticProps = async () => { const files = fs.readdirSync(PATH); const blogPosts = files - .filter((file) => file.substr(-3) === '.md') + .filter((file) => file.endsWith('.md')) .map((fileName) => { const slug = fileName.replace('.md', ''); const fullFileName = fs.readFileSync( @@ -50,7 +51,9 @@ export const getStaticProps: GetStaticProps = async () => { const remoteICalUrl = 'https://calendar.google.com/calendar/ical/info%40json-schema.org/public/basic.ics'; const datesInfo = await fetchRemoteICalFile(remoteICalUrl) - .then((icalData: any) => printEventsForNextWeeks(ical.parseICS(icalData))) + .then((icalData) => + icalData ? printEventsForNextWeeks(ical.parseICS(icalData)) : [], + ) .catch((error) => console.error('Error:', error)); return { props: { @@ -88,7 +91,27 @@ export function AlgoliaSearch() {
); } -const Home = (props: any) => { +interface Author { + name: string; + photo: string; +} + +interface BlogPostFrontmatter { + title: string; + cover: string; + excerpt: string; + date: string; + authors: Author[]; +} + +const Home = (props: { + blogPosts: { + slug: string; + frontmatter: BlogPostFrontmatter; + content: string; + }[]; + datesInfo: CalendarEventInfo[]; +}) => { const blogPosts = props.blogPosts; const timeToRead = Math.ceil(readingTime(blogPosts[0].content).minutes); const { resolvedTheme } = useTheme(); @@ -449,7 +472,7 @@ const Home = (props: any) => {
{(blogPosts[0].frontmatter.authors || []).map( - (author: any, index: number) => { + (author, index: number) => { return (
{ <> {blogPosts[0].frontmatter.authors .slice(0, 2) - .map((author: any, index: number) => ( + .map((author, index: number) => ( {author.name} {index === 0 && ' & '} @@ -477,7 +500,7 @@ const Home = (props: any) => { {'...'} ) : ( - blogPosts[0].frontmatter.authors.map((author: any) => ( + blogPosts[0].frontmatter.authors.map((author) => ( {author.name} )) )} @@ -536,7 +559,7 @@ const Home = (props: any) => { Upcoming events
    - {props.datesInfo.map((event: any, index: any) => ( + {props.datesInfo.map((event, index) => (
  • diff --git a/pages/learn/[slug].page.tsx b/pages/learn/[slug].page.tsx index d186f1a4d..ad657ed70 100644 --- a/pages/learn/[slug].page.tsx +++ b/pages/learn/[slug].page.tsx @@ -8,11 +8,12 @@ import { Headline1 } from '~/components/Headlines'; import { SectionContext } from '~/context'; import { DocsHelp } from '~/components/DocsHelp'; import NextPrevButton from '~/components/NavigationButtons'; +import { Frontmatter } from '~/types/common'; export async function getStaticPaths() { return getStaticMarkdownPaths('pages/learn'); } -export async function getStaticProps(args: any) { +export async function getStaticProps(args: { params?: { slug: string } }) { return getStaticMarkdownProps(args, 'pages/learn'); } @@ -20,13 +21,13 @@ export default function StaticMarkdownPage({ frontmatter, content, }: { - frontmatter: any; - content: any; + frontmatter: Frontmatter; + content: string; }) { const fileRenderType = '_md'; const newTitle = 'JSON Schema - ' + frontmatter.title; return ( - + {newTitle} diff --git a/pages/learn/getting-started-step-by-step/index.page.tsx b/pages/learn/getting-started-step-by-step/index.page.tsx index de2cab73f..e20ad14f8 100644 --- a/pages/learn/getting-started-step-by-step/index.page.tsx +++ b/pages/learn/getting-started-step-by-step/index.page.tsx @@ -29,13 +29,7 @@ export async function getStaticProps() { }; } -export default function StyledValidator({ - blocks, -}: { - blocks: any[]; - frontmatter: any; - content: any; -}) { +export default function StyledValidator({ blocks }: { blocks: string[] }) { const newTitle = 'Creating your first schema'; const fileRenderType = 'tsx'; return ( diff --git a/pages/overview/[slug].page.tsx b/pages/overview/[slug].page.tsx index c50fa4f39..3b37072e4 100644 --- a/pages/overview/[slug].page.tsx +++ b/pages/overview/[slug].page.tsx @@ -8,11 +8,12 @@ import { Headline1 } from '~/components/Headlines'; import { SectionContext } from '~/context'; import { DocsHelp } from '~/components/DocsHelp'; import NextPrevButton from '~/components/NavigationButtons'; +import { Frontmatter } from '~/types/common'; export async function getStaticPaths() { return getStaticMarkdownPaths('pages/overview'); } -export async function getStaticProps(args: any) { +export async function getStaticProps(args: { params?: { slug: string } }) { return getStaticMarkdownProps(args, 'pages/overview'); } @@ -20,24 +21,24 @@ export default function StaticMarkdownPage({ frontmatter, content, }: { - frontmatter: any; - content: any; + frontmatter: Frontmatter; + content: string; }) { const fileRenderType = '_md'; const newTitle = 'JSON Schema - ' + frontmatter.title; return ( - + {newTitle} {frontmatter.title} diff --git a/pages/overview/code-of-conduct/index.page.tsx b/pages/overview/code-of-conduct/index.page.tsx index 75e6000f2..82a99b1ed 100644 --- a/pages/overview/code-of-conduct/index.page.tsx +++ b/pages/overview/code-of-conduct/index.page.tsx @@ -21,13 +21,7 @@ export async function getStaticProps() { }; } -export default function Content({ - blocks, -}: { - blocks: any[]; - frontmatter: any; - content: any; -}) { +export default function Content({ blocks }: { blocks: string[] }) { const newTitle = 'Code of Conduct'; const fileRenderType = 'https://github.com/json-schema-org/.github/blob/main/CODE_OF_CONDUCT.md'; diff --git a/pages/overview/sponsors/index.page.tsx b/pages/overview/sponsors/index.page.tsx index c3c59d4ee..6c88b38d0 100644 --- a/pages/overview/sponsors/index.page.tsx +++ b/pages/overview/sponsors/index.page.tsx @@ -19,7 +19,7 @@ export async function getStaticProps() { }; } -export default function ContentExample({ blocks }: { blocks: any[] }) { +export default function ContentExample({ blocks }: { blocks: string[] }) { const newTitle = 'Sponsors'; const fileRenderType = 'https://github.com/json-schema-org/community/blob/main/programs/sponsors/sponsors.md'; diff --git a/pages/specification/json-hyper-schema/index.page.tsx b/pages/specification/json-hyper-schema/index.page.tsx index 83d5376d8..21b07a40d 100644 --- a/pages/specification/json-hyper-schema/index.page.tsx +++ b/pages/specification/json-hyper-schema/index.page.tsx @@ -7,6 +7,7 @@ import { SectionContext } from '~/context'; import { Headline1 } from '~/components/Headlines'; import { DocsHelp } from '~/components/DocsHelp'; import NextPrevButton from '~/components/NavigationButtons'; +import { Frontmatter } from '~/types/common'; export async function getStaticProps() { const index = fs.readFileSync( @@ -29,22 +30,31 @@ export async function getStaticProps() { }; } +interface SpecificationFrontmatter extends Frontmatter { + Specification?: string; +} + +interface BlocksData { + index: string; + body?: string; +} + export default function ImplementationsPages({ blocks, frontmatter, }: { - blocks: any; - frontmatter: any; + blocks: BlocksData; + frontmatter: SpecificationFrontmatter; }) { const fileRenderType = '_indexmd'; return ( {frontmatter.title} -

    {frontmatter.type}

    -

    {frontmatter.Specification}

    + {frontmatter.type &&

    {frontmatter.type}

    } + {frontmatter.Specification &&

    {frontmatter.Specification}

    } - + {blocks.body && } {frontmatter.title} - + {blocks.body && }
    {frontmatter.title} - + {blocks.body && }
    + {newTitle} diff --git a/pages/understanding-json-schema/index.page.tsx b/pages/understanding-json-schema/index.page.tsx index 40ed5c449..9e819ce51 100644 --- a/pages/understanding-json-schema/index.page.tsx +++ b/pages/understanding-json-schema/index.page.tsx @@ -19,13 +19,8 @@ export async function getStaticProps() { }, }; } -export default function ContentExample({ - blocks, -}: { - blocks: any[]; - frontmatter: any; - content: any; -}) { + +export default function ContentExample({ blocks }: { blocks: string[] }) { const fileRenderType = '_indexmd'; return ( diff --git a/pages/understanding-json-schema/reference/[slug].page.tsx b/pages/understanding-json-schema/reference/[slug].page.tsx index dc87fef98..eb6faf5ff 100644 --- a/pages/understanding-json-schema/reference/[slug].page.tsx +++ b/pages/understanding-json-schema/reference/[slug].page.tsx @@ -8,11 +8,12 @@ import getStaticMarkdownProps from '~/lib/getStaticMarkdownProps'; import { SectionContext } from '~/context'; import { DocsHelp } from '~/components/DocsHelp'; import NextPrevButton from '~/components/NavigationButtons'; +import { Frontmatter } from '~/types/common'; export async function getStaticPaths() { return getStaticMarkdownPaths('pages/understanding-json-schema/reference'); } -export async function getStaticProps(args: any) { +export async function getStaticProps(args: { params?: { slug: string } }) { return getStaticMarkdownProps( args, 'pages/understanding-json-schema/reference', @@ -23,13 +24,13 @@ export default function StaticMarkdownPage({ frontmatter, content, }: { - frontmatter: any; - content: any; + frontmatter: Frontmatter; + content: string; }) { const newTitle = 'JSON Schema - ' + frontmatter.title; const fileRenderType = '_md'; return ( - + {newTitle} diff --git a/types/common.ts b/types/common.ts new file mode 100644 index 000000000..decc5bc29 --- /dev/null +++ b/types/common.ts @@ -0,0 +1,122 @@ +// Common type definitions for the JSON Schema website +import React from 'react'; + +// Author type for blog posts and content +export interface Author { + name: string; + photo: string; + twitter?: string; + link?: string; +} + +// Frontmatter type for markdown files +export interface Frontmatter { + title: string; + date?: string; + excerpt?: string; + cover?: string; + authors?: Author[]; + type?: string | string[]; + section?: SectionType; + prev?: NavLink; + next?: NavLink; + [key: string]: unknown; +} + +// Props for getStaticProps parameter +export interface StaticPropsParams { + params?: { + slug: string; + }; +} + +// Return type for markdown page props +export interface MarkdownPageProps { + frontmatter: Frontmatter; + content: string; +} + +// Props for pages with blocks +export interface PageWithBlocksProps extends MarkdownPageProps { + blocks: string[]; +} + +// Calendar event type +export interface CalendarEvent { + title: string; + time: string; + day: string; + timezone: string; + parsedStartDate: string; +} + +// iCal event data structure +export interface ICalEventData { + [key: string]: { + type: string; + summary?: string; + start?: { + tz: string; + [key: string]: unknown; + }; + rrule?: { + between: (start: Date, end: Date, inclusive: boolean) => Date[]; + }; + [key: string]: unknown; + }; +} + +// Blog post structure +export interface BlogPost { + slug: string; + frontmatter: Frontmatter; + content: string; +} + +// Slate editor types for JsonEditor +export interface SlateRenderProps { + attributes: { + 'data-slate-node'?: string; + 'data-slate-inline'?: boolean; + 'data-slate-void'?: boolean; + dir?: 'rtl'; + ref: React.RefObject; + [key: string]: unknown; + }; + children: React.ReactNode; + element?: unknown; + leaf?: unknown; + text?: { + text: string; + [key: string]: unknown; + }; +} + +// Section context type +export type SectionType = + | 'learn' + | 'docs' + | 'implementers' + | 'tools' + | 'implementations' + | 'blog' + | 'community' + | 'specification' + | 'overview' + | 'getting-started' + | 'reference' + | 'roadmap' + | 'ambassador' + | 'pro-help' + | null; + +// Navigation link type +export interface NavLink { + label: string; + url: string; +} + +// JSON Schema specific types +export interface JsonKeywordValue { + [key: string]: unknown; +}