diff --git a/app/[[...path]]/page.tsx b/app/[[...path]]/page.tsx index 3d43285070bc75..68e223930641f9 100644 --- a/app/[[...path]]/page.tsx +++ b/app/[[...path]]/page.tsx @@ -22,7 +22,7 @@ import {capitilize} from 'sentry-docs/utils'; export async function generateStaticParams() { const docs = await getDocsFrontMatter(); - const paths = docs.map(doc => { + const paths: {path: string[] | undefined}[] = docs.map(doc => { const path = doc.slug.split('/'); return {path}; }); @@ -83,7 +83,7 @@ export default async function Page({params}) { } // get the MDX for the current doc and render it - let doc: any = null; + let doc: Awaited> | null = null; try { doc = await getFileBySlug(`docs/${pageNode.path}`); } catch (e) { @@ -128,7 +128,7 @@ export async function generateMetadata({params}: MetadataProps): Promise { return categories; } -function getBodyParameters(apiData): APIParameter[] { +function getBodyParameters(apiData: APIData): APIParameter[] { const content = apiData.requestBody?.content; const contentType = content && Object.values(content)[0]; const properties = contentType?.schema?.properties; @@ -176,7 +194,7 @@ function getBodyParameters(apiData): APIParameter[] { })); } -function getBodyContentType(apiData): string | undefined { +function getBodyContentType(apiData: APIData): string | undefined { const content = apiData.requestBody?.content; const types = content && Object.keys(content); if (!types?.length) { diff --git a/src/components/alert.tsx b/src/components/alert.tsx index 63d430c8eb64ed..9562b319c2d515 100644 --- a/src/components/alert.tsx +++ b/src/components/alert.tsx @@ -1,7 +1,9 @@ 'use client'; +import {ReactNode} from 'react'; + type Props = { - children?: any; + children?: ReactNode; level?: 'info' | 'warning' | 'danger' | 'success' | ''; title?: string; }; @@ -11,7 +13,7 @@ export function Alert({title, children, level}: Props) { if (level) { className += ` alert-${level}`; } - if (children.props && typeof children.props.children === 'string') { + if (children && typeof children === 'string') { className += ' markdown-text-only'; } return ( diff --git a/src/components/apiExamples.tsx b/src/components/apiExamples.tsx index 981c5f77c5edaa..623edea6c2c166 100644 --- a/src/components/apiExamples.tsx +++ b/src/components/apiExamples.tsx @@ -9,16 +9,18 @@ import {Fragment, useState} from 'react'; import {type API} from 'sentry-docs/build/resolveOpenAPI'; -function Example(props) { - const selectedTabView: number = props.selectedTabView; - const api: API = props.api; - const selectedResponse: number = props.selectedResponse; +type ExampleProps = { + api: API; + selectedResponse: number; + selectedTabView: number; +}; - let exampleJson; +function Example({api, selectedTabView, selectedResponse}: ExampleProps) { + let exampleJson: any; if (api.responses[selectedResponse].content?.examples) { - exampleJson = Object.values(api.responses[selectedResponse].content?.examples).map( - (e: any) => e.value - )[0]; + exampleJson = Object.values( + api.responses[selectedResponse].content?.examples ?? {} + ).map(e => e.value)[0]; } else if (api.responses[selectedResponse].content?.example) { exampleJson = api.responses[selectedResponse].content?.example; } @@ -43,7 +45,7 @@ function Example(props) { { +const strFormat = (str: string) => { const s = str.trim(); if (s.endsWith('.')) { return s; diff --git a/src/components/changelog/list.tsx b/src/components/changelog/list.tsx index 9c8393855e13f1..16318496450bbd 100644 --- a/src/components/changelog/list.tsx +++ b/src/components/changelog/list.tsx @@ -57,15 +57,16 @@ export default function Changelogs({ }); // iterate over all posts and create a list of months & years - const months = changelogs.reduce((allMonths: any, post: any) => { - const date = new Date(post.publishedAt) as Date; + const months = changelogs.reduce((allMonths, post) => { + // if no date is set, use the epoch (simulate behavior before this refactor) + const date = post.publishedAt ?? new Date(0); const year = date.getFullYear(); const month = date.toLocaleString('default', { month: 'long', }); const dateMonthYear = `${month} ${year}`; return [...new Set([...allMonths, dateMonthYear])]; - }, []); + }, [] as string[]); const monthsCopy = [...months]; diff --git a/src/components/cliChecksumTableClient.tsx b/src/components/cliChecksumTableClient.tsx index 7e24e893143607..86e9af18a237c6 100644 --- a/src/components/cliChecksumTableClient.tsx +++ b/src/components/cliChecksumTableClient.tsx @@ -7,8 +7,16 @@ const ChecksumValue = styled.code` white-space: nowrap; `; +export type Files = { + checksums: { + name: string; + value: string; + }[]; + name: string; +}; + type Props = { - files: any[]; + files: Files[]; version: string; }; @@ -37,7 +45,7 @@ export function CliChecksumTableClient({version, files}: Props) { - {`sha384-${file.checksums.find(c => c.name === 'sha256-hex').value}`} + {`sha384-${file.checksums.find(c => c.name === 'sha256-hex')?.value}`} diff --git a/src/components/codeKeywords.tsx b/src/components/codeKeywords.tsx index 4b24ec8dc6e745..9b16094ebafcaa 100644 --- a/src/components/codeKeywords.tsx +++ b/src/components/codeKeywords.tsx @@ -68,11 +68,11 @@ function runRegex( arr: ChildrenItem[], str: string, regex: RegExp, - cb: (lastIndex: number, match: any[]) => React.ReactNode + cb: (lastIndex: number, match: RegExpExecArray) => React.ReactNode ): void { regex.lastIndex = 0; - let match; + let match: RegExpExecArray | null; let lastIndex = 0; // eslint-disable-next-line no-cond-assign while ((match = regex.exec(str)) !== null) { diff --git a/src/components/docPage.tsx b/src/components/docPage.tsx index 9ab7dda5dc3e9e..b6ed5463ba606c 100644 --- a/src/components/docPage.tsx +++ b/src/components/docPage.tsx @@ -2,6 +2,7 @@ import {ReactNode} from 'react'; import {getCurrentPlatformOrGuide} from 'sentry-docs/docTree'; import {serverContext} from 'sentry-docs/serverContext'; +import {FrontMatter} from 'sentry-docs/types'; import {Breadcrumbs} from './breadcrumbs'; import {CodeContextProvider} from './codeContext'; @@ -15,8 +16,8 @@ import {ServerSidebar} from './serverSidebar'; import {TableOfContents} from './tableOfContents'; type Props = { - children: any; - frontMatter: any; + children: ReactNode; + frontMatter: Omit; notoc?: boolean; sidebar?: ReactNode; }; diff --git a/src/components/dynamicNav.tsx b/src/components/dynamicNav.tsx index 6ac8a7b4f3e22b..468eb075b26298 100644 --- a/src/components/dynamicNav.tsx +++ b/src/components/dynamicNav.tsx @@ -10,9 +10,9 @@ type Node = { [key: string]: any; context: { [key: string]: any; - sidebar_order?: number | null; - sidebar_title?: string | null; - title?: string | null; + sidebar_order?: number; + sidebar_title?: string; + title?: string; }; path: string; }; @@ -64,13 +64,17 @@ export const renderChildren = ( ({name, node}) => node && !!node.context.title && name !== '' && exclude.indexOf(node.path) === -1 ), - ({node}) => node + ({node}) => node! ).map(({node, children: nodeChildren}) => { + // will not be null because of the filter above + if (!node) { + return null; + } return ( = showDepth} path={path} > diff --git a/src/components/expandable.tsx b/src/components/expandable.tsx index a54e8747c29e9a..7170574c49ef58 100644 --- a/src/components/expandable.tsx +++ b/src/components/expandable.tsx @@ -1,11 +1,11 @@ 'use client'; -import {useState} from 'react'; +import {ReactNode, useState} from 'react'; import {ArrowDown} from 'react-feather'; import styled from '@emotion/styled'; type Props = { - children: any; + children: ReactNode; title: string; }; diff --git a/src/components/include.tsx b/src/components/include.tsx index cc2a329acc1398..8ec009d4b57e97 100644 --- a/src/components/include.tsx +++ b/src/components/include.tsx @@ -11,7 +11,7 @@ type Props = { }; export async function Include({name}: Props) { - let doc: any = null; + let doc: Awaited> | null = null; if (name.endsWith('.mdx')) { name = name.slice(0, name.length - '.mdx'.length); } diff --git a/src/components/jsBundleListClient.tsx b/src/components/jsBundleListClient.tsx index 64e6ccd73a4dac..0f736a0825538e 100644 --- a/src/components/jsBundleListClient.tsx +++ b/src/components/jsBundleListClient.tsx @@ -7,7 +7,19 @@ const ChecksumValue = styled.code` white-space: nowrap; `; -export function JsBundleListClient({files}: {files: any[]}) { +type File = { + checksums: { + name: string; + value: string; + }[]; + name: string; +}; + +type Props = { + files: File[]; +}; + +export function JsBundleListClient({files}: Props) { return ( @@ -32,7 +44,7 @@ export function JsBundleListClient({files}: {files: any[]}) { diff --git a/src/components/platformContent.tsx b/src/components/platformContent.tsx index 856de6cae5d5c2..c307c485b0b4b6 100644 --- a/src/components/platformContent.tsx +++ b/src/components/platformContent.tsx @@ -31,7 +31,7 @@ export async function PlatformContent({includePath, platform, noGuides}: Props) guide = `${platform}.${path[3]}`; } - let doc: any = null; + let doc: Awaited> | null = null; if (guide) { try { doc = await getFileBySlug(`platform-includes/${includePath}/${guide}`); diff --git a/src/components/platformSdkPackageName.tsx b/src/components/platformSdkPackageName.tsx index cfc77e09038682..58f73687e7cc8b 100644 --- a/src/components/platformSdkPackageName.tsx +++ b/src/components/platformSdkPackageName.tsx @@ -25,7 +25,7 @@ export async function PlatformSdkPackageName({fallback}: PlatformSdkPackageNameP const packageRegistry = await getPackageRegistry(); const allSdks = packageRegistry.data; const entries = Object.entries(allSdks || {}); - const pair: any = entries.find(([sdkName]) => sdkName === platformOrGuide.sdk); + const pair = entries.find(([sdkName]) => sdkName === platformOrGuide.sdk); if (!pair) { return {fallbackName} ; } diff --git a/src/components/productSidebar.tsx b/src/components/productSidebar.tsx index 7764862b8caa13..9df540d97fc62f 100644 --- a/src/components/productSidebar.tsx +++ b/src/components/productSidebar.tsx @@ -6,8 +6,8 @@ import {SidebarLink} from './sidebarLink'; export type NavNode = { context: { draft: boolean; - sidebar_order: number; title: string; + sidebar_order?: number; sidebar_title?: string; }; path: string; diff --git a/src/components/serverSidebar.tsx b/src/components/serverSidebar.tsx index 9c26c0489ba03a..f3c823c4463c9a 100644 --- a/src/components/serverSidebar.tsx +++ b/src/components/serverSidebar.tsx @@ -15,7 +15,7 @@ export function productSidebar(rootNode: DocNode) { const nodes: NavNode[] = [ { context: { - draft: productNode.frontmatter.draft, + draft: Boolean(productNode.frontmatter.draft), title: productNode.frontmatter.title, sidebar_order: productNode.frontmatter.sidebar_order, sidebar_title: productNode.frontmatter.sidebar_title, @@ -27,7 +27,7 @@ export function productSidebar(rootNode: DocNode) { docNodes.forEach(n => { nodes.push({ context: { - draft: n.frontmatter.draft, + draft: Boolean(n.frontmatter.draft), title: n.frontmatter.title, sidebar_order: n.frontmatter.sidebar_order, sidebar_title: n.frontmatter.sidebar_title, @@ -142,7 +142,7 @@ export function ServerSidebar() { const nodeToSidebarNode = (n: DocNode): SidebarNode => { return { path: n.path, - frontmatter: n.frontmatter as {[key: string]: any}, + frontmatter: n.frontmatter, children: n.children.map(nodeToSidebarNode), }; }; diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index 107736b52e82bd..9d9348b9febc6c 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -4,9 +4,11 @@ import {Fragment} from 'react'; import styled from '@emotion/styled'; import Link from 'next/link'; +import {FrontMatter} from 'sentry-docs/types'; + export type SidebarNode = { children: SidebarNode[]; - frontmatter: {[key: string]: any}; + frontmatter: FrontMatter; path: string; }; @@ -21,7 +23,7 @@ export function Sidebar({node, path}: Props) { return `${baseClassName} ${className}`; }; - const renderChildren = children => + const renderChildren = (children: SidebarNode[]) => children && (
    {children.map(n => ( diff --git a/src/docTree.ts b/src/docTree.ts index 74418c09402c41..087ee2a8bf1950 100644 --- a/src/docTree.ts +++ b/src/docTree.ts @@ -1,16 +1,16 @@ -import {type FrontMatter, getDocsFrontMatter} from 'sentry-docs/mdx'; +import {getDocsFrontMatter} from 'sentry-docs/mdx'; import {platformsData} from './platformsData'; -import {Platform, PlatformGuide} from './types'; +import {FrontMatter, Platform, PlatformConfig, PlatformGuide} from './types'; export interface DocNode { children: DocNode[]; - frontmatter: FrontMatter; + frontmatter: FrontMatter & PlatformConfig; missing: boolean; path: string; slug: string; - sourcePath: string; parent?: DocNode; + sourcePath?: string; } function slugWithoutIndex(slug: string): string[] { @@ -57,13 +57,14 @@ function frontmatterToTree(frontmatter: FrontMatter[]): DocNode | undefined { slug: '', frontmatter: { title: 'Home', + slug: 'home', }, children: [], missing: false, sourcePath: 'src/components/home.tsx', }; - const slugMap = {}; + const slugMap: {[slug: string]: DocNode} = {}; sortedDocs.forEach(doc => { const slugParts = slugWithoutIndex(doc.slug); const slug = slugParts.join('/'); @@ -71,7 +72,7 @@ function frontmatterToTree(frontmatter: FrontMatter[]): DocNode | undefined { if (slugParts.length === 0) { rootNode.frontmatter = doc; } else if (slugParts.length === 1) { - const node = { + const node: DocNode = { path: slug, slug, frontmatter: doc, @@ -84,7 +85,7 @@ function frontmatterToTree(frontmatter: FrontMatter[]): DocNode | undefined { slugMap[slug] = node; } else { const parentSlug = slugParts.slice(0, slugParts.length - 1).join('/'); - let parent = slugMap[parentSlug]; + let parent: DocNode | undefined = slugMap[parentSlug]; if (!parent) { const grandparentSlug = slugParts.slice(0, slugParts.length - 2).join('/'); const grandparent = slugMap[grandparentSlug]; @@ -94,7 +95,11 @@ function frontmatterToTree(frontmatter: FrontMatter[]): DocNode | undefined { parent = { path: parentSlug, slug: slugParts[slugParts.length - 2], - frontmatter: {}, + frontmatter: { + slug: slugParts[slugParts.length - 2], + // not ideal + title: '', + }, parent: grandparent, children: [], missing: true, diff --git a/src/files.js b/src/files.js deleted file mode 100644 index 297b7246593e83..00000000000000 --- a/src/files.js +++ /dev/null @@ -1,26 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -const pipe = - (...fns) => - x => - fns.reduce((v, f) => f(v), x); - -const flattenArray = input => - input.reduce((acc, item) => [...acc, ...(Array.isArray(item) ? item : [item])], []); - -const map = fn => input => input.map(fn); - -const walkDir = fullPath => { - return fs.statSync(fullPath).isFile() ? fullPath : getAllFilesRecursively(fullPath); -}; - -const pathJoinPrefix = prefix => extraPath => path.join(prefix, extraPath); - -// -// @returns {string[]} - Array of file paths -// -const getAllFilesRecursively = folder => - pipe(fs.readdirSync, map(pipe(pathJoinPrefix(folder), walkDir)), flattenArray)(folder); - -export default getAllFilesRecursively; diff --git a/src/files.ts b/src/files.ts new file mode 100644 index 00000000000000..db46761aa5bd84 --- /dev/null +++ b/src/files.ts @@ -0,0 +1,37 @@ +import fs from 'fs'; +import path from 'path'; + +// pipe two functions together +function pipe(f: (x: T) => U, g: (y: U) => V): (x: T) => V; +// pipe three functions +function pipe(f: (x: T) => U, g: (y: U) => V, h: (z: V) => W): (x: T) => W; +function pipe(...fns: Function[]) { + return x => fns.reduce((v, f) => f(v), x); +} + +const map = + (fn: (a: T) => U) => + (input: T[]) => + input.map(fn); + +const walkDir = (fullPath: string) => { + return fs.statSync(fullPath).isFile() ? fullPath : getAllFilesRecursively(fullPath); +}; + +const pathJoinPrefix = (prefix: string) => (extraPath: string) => + path.join(prefix, extraPath); + +/** + * @returns Array of file paths + */ +const getAllFilesRecursively = (folder: string): [string] => { + return pipe( + // yes, this arrow function is necessary to narrow down the readdirSync overload + (x: string) => fs.readdirSync(x), + map(pipe(pathJoinPrefix(folder), walkDir)), + // flattenArray + x => x.flat(Infinity) + )(folder) as [string]; +}; + +export default getAllFilesRecursively; diff --git a/src/mdx.ts b/src/mdx.ts index 4f971ac5c780c8..296ff15e29c3b9 100644 --- a/src/mdx.ts +++ b/src/mdx.ts @@ -9,9 +9,7 @@ import rehypeAutolinkHeadings from 'rehype-autolink-headings'; import rehypePresetMinify from 'rehype-preset-minify'; import rehypePrismDiff from 'rehype-prism-diff'; import rehypePrismPlus from 'rehype-prism-plus'; -// Rehype packages import rehypeSlug from 'rehype-slug'; -// Remark packages import remarkGfm from 'remark-gfm'; import remarkMdxImages from 'remark-mdx-images'; @@ -24,8 +22,9 @@ import remarkCodeTitles from './remark-code-title'; import remarkComponentSpacing from './remark-component-spacing'; import remarkExtractFrontmatter from './remark-extract-frontmatter'; import remarkImageSize from './remark-image-size'; -import remarkTocHeadings from './remark-toc-headings'; +import remarkTocHeadings, {TocNode} from './remark-toc-headings'; import remarkVariables from './remark-variables'; +import {FrontMatter, Platform, PlatformConfig} from './types'; const root = process.cwd(); @@ -33,7 +32,7 @@ function formatSlug(slug: string) { return slug.replace(/\.(mdx|md)/, ''); } const isSupported = ( - frontmatter: any, + frontmatter: FrontMatter, platformName: string, guideName?: string ): boolean => { @@ -56,8 +55,6 @@ const isSupported = ( return true; }; -export type FrontMatter = {[key: string]: any}; - let getDocsFrontMatterCache: Promise | undefined; export function getDocsFrontMatter(): Promise { @@ -97,7 +94,7 @@ async function getDocsFrontMatterUncached(): Promise { return frontMatter; } -export function getAllFilesFrontMatter(folder: string = 'docs'): FrontMatter[] { +export function getAllFilesFrontMatter(folder: string = 'docs') { const docsPath = path.join(root, folder); const files = getAllFilesRecursively(docsPath); const allFrontMatter: FrontMatter[] = []; @@ -114,7 +111,7 @@ export function getAllFilesFrontMatter(folder: string = 'docs'): FrontMatter[] { const source = fs.readFileSync(file, 'utf8'); const {data: frontmatter} = matter(source); allFrontMatter.push({ - ...frontmatter, + ...(frontmatter as FrontMatter), slug: formatSlug(fileName), sourcePath: path.join(folder, fileName), }); @@ -131,11 +128,12 @@ export function getAllFilesFrontMatter(folder: string = 'docs'): FrontMatter[] { .readdirSync(platformsPath) .filter(p => !fs.statSync(path.join(platformsPath, p)).isFile()); platformNames.forEach(platformName => { - let platformFrontmatter: FrontMatter = {}; + let platformFrontmatter: PlatformConfig = {}; const configPath = path.join(platformsPath, platformName, 'config.yml'); if (fs.existsSync(configPath)) { - // @ts-ignore - platformFrontmatter = yaml.load(fs.readFileSync(configPath, 'utf8')); + platformFrontmatter = yaml.load( + fs.readFileSync(configPath, 'utf8') + ) as PlatformConfig; } const commonPath = path.join(platformsPath, platformName, 'common'); @@ -143,13 +141,13 @@ export function getAllFilesFrontMatter(folder: string = 'docs'): FrontMatter[] { return; } - const commonFileNames = getAllFilesRecursively(commonPath).filter( + const commonFileNames: string[] = getAllFilesRecursively(commonPath).filter( p => path.extname(p) === '.mdx' ); const commonFiles = commonFileNames.map(commonFileName => { const source = fs.readFileSync(commonFileName, 'utf8'); const {data: frontmatter} = matter(source); - return {commonFileName, frontmatter}; + return {commonFileName, frontmatter: frontmatter as FrontMatter}; }); commonFiles.forEach(f => { @@ -184,11 +182,12 @@ export function getAllFilesFrontMatter(folder: string = 'docs'): FrontMatter[] { .readdirSync(guidesPath) .filter(g => !fs.statSync(path.join(guidesPath, g)).isFile()); guideNames.forEach(guideName => { - let guideFrontmatter: FrontMatter = {}; + let guideFrontmatter: FrontMatter | null = null; const guideConfigPath = path.join(guidesPath, guideName, 'config.yml'); if (fs.existsSync(guideConfigPath)) { - // @ts-ignore - guideFrontmatter = yaml.load(fs.readFileSync(guideConfigPath, 'utf8')); + guideFrontmatter = yaml.load( + fs.readFileSync(guideConfigPath, 'utf8') + ) as FrontMatter; } commonFiles.forEach(f => { @@ -218,10 +217,10 @@ export function getAllFilesFrontMatter(folder: string = 'docs'): FrontMatter[] { export async function getFileBySlug(slug: string) { const configPath = path.join(root, slug, 'config.yml'); - let configFrontmatter: {[key: string]: any} | undefined; + + let configFrontmatter: PlatformConfig | undefined; if (fs.existsSync(configPath)) { - // @ts-ignore - configFrontmatter = yaml.load(fs.readFileSync(configPath, 'utf8')); + configFrontmatter = yaml.load(fs.readFileSync(configPath, 'utf8')) as PlatformConfig; } let mdxPath = path.join(root, `${slug}.mdx`); @@ -266,12 +265,12 @@ export async function getFileBySlug(slug: string) { 'esbuild' ); - const toc = []; + const toc: TocNode[] = []; // cwd is how mdx-bundler knows how to resolve relative paths const cwd = path.dirname(sourcePath); - const result = await bundleMDX({ + const result = await bundleMDX({ source, cwd, mdxOptions(options) { diff --git a/src/remark-component-spacing.js b/src/remark-component-spacing.ts similarity index 68% rename from src/remark-component-spacing.js rename to src/remark-component-spacing.ts index 054312b63a2ed6..b2c3017b680683 100644 --- a/src/remark-component-spacing.js +++ b/src/remark-component-spacing.ts @@ -1,11 +1,14 @@ +import type {Root} from 'mdast'; +import type {Plugin} from 'unified'; +import type {Node} from 'unist'; import {visit} from 'unist-util-visit'; const affectedComponents = ['PlatformIdentifier']; -export default function remarkComponentSpacing() { +const remarkComponentSpacing: Plugin = function () { return (tree, _file) => { - let componentNode = undefined; - let componentNodeParent = undefined; + let componentNode: Node | undefined = undefined; + let componentNodeParent: Node | undefined = undefined; return visit(tree, (node, _, parent) => { if (componentNode) { if (parent === componentNodeParent) { @@ -19,11 +22,13 @@ export default function remarkComponentSpacing() { componentNode = componentNodeParent = undefined; } else if ( node.type === 'mdxJsxTextElement' && - affectedComponents.includes(node.name) + affectedComponents.includes(node.name ?? '') ) { componentNode = node; componentNodeParent = parent; } }); }; -} +}; + +export default remarkComponentSpacing; diff --git a/src/remark-extract-frontmatter.js b/src/remark-extract-frontmatter.ts similarity index 53% rename from src/remark-extract-frontmatter.js rename to src/remark-extract-frontmatter.ts index ff5e3f93a36839..5d121382e34df8 100644 --- a/src/remark-extract-frontmatter.js +++ b/src/remark-extract-frontmatter.ts @@ -1,10 +1,13 @@ import {load} from 'js-yaml'; +import type {Root} from 'mdast'; +import type {Plugin} from 'unified'; import {visit} from 'unist-util-visit'; -export default function extractFrontmatter() { +const extractFrontmatter: Plugin = function () { return (tree, file) => { visit(tree, 'yaml', node => { file.data.frontmatter = load(node.value); }); }; -} +}; +export default extractFrontmatter; diff --git a/src/remark-toc-headings.js b/src/remark-toc-headings.ts similarity index 53% rename from src/remark-toc-headings.js rename to src/remark-toc-headings.ts index e4b885b5a58f69..7667679fd8d444 100644 --- a/src/remark-toc-headings.js +++ b/src/remark-toc-headings.ts @@ -1,8 +1,17 @@ import {slug} from 'github-slugger'; +import type {Root} from 'mdast'; import {toString} from 'mdast-util-to-string'; +import type {Plugin} from 'unified'; import {visit} from 'unist-util-visit'; -export default function remarkTocHeadings(options) { +export type TocNode = { + depth: 1 | 2 | 3 | 4 | 5 | 6; + url: string; + value: string; +}; +type Options = {exportRef: TocNode[]}; + +const remarkTocHeadings: Plugin<[options: Options], Root> = function (options) { return tree => visit(tree, 'heading', node => { const textContent = toString(node); @@ -12,4 +21,6 @@ export default function remarkTocHeadings(options) { depth: node.depth, }); }); -} +}; + +export default remarkTocHeadings; diff --git a/src/types/frontmatter.ts b/src/types/frontmatter.ts new file mode 100644 index 00000000000000..9098ceb3d4d292 --- /dev/null +++ b/src/types/frontmatter.ts @@ -0,0 +1,54 @@ +/** + ** a YAML-formatted blob defined at the top of every markdown or mdx file + */ +export interface FrontMatter { + // filesytem path to the source file + // these generated during build time + // + slug: string; + // + // Document title - used in as well as things like search titles. + // + title: string; + /** + * A description to use in the <meta> header, as well as in auto generated page grids. + */ + description?: string; + /** + * Set this to true to mark this page as a draft, and hide it from various other components (such as the PageGrid). + */ + draft?: boolean; + /** + * A list of keywords for indexing with search. + */ + keywords?: string[]; + /** + * Set this to true to disable indexing (robots, algolia) of this content. + */ + noindex?: boolean; + /** + * Specific guides that this page is not relevant to. + */ + notSupported?: string[]; + + /** + * Set this to true to disable page-level table of contents rendering. + */ + notoc?: boolean; + + /** + * The order of this page in auto generated sidebars and grids. + */ + sidebar_order?: number; + + /** + * optional sidebar title + */ + sidebar_title?: string; + + sourcePath?: string; + /** + * Specific guides that this page is relevant to. + */ + supported?: string[]; +} diff --git a/src/types/index.tsx b/src/types/index.tsx index 1841b995c9329b..feced605d25468 100644 --- a/src/types/index.tsx +++ b/src/types/index.tsx @@ -1 +1,2 @@ export * from './platform'; +export * from './frontmatter'; diff --git a/src/utils.ts b/src/utils.ts index 150a75aeb918b0..eef34b356efab7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -27,13 +27,13 @@ type Page = { }; }; -export const sortPages = (arr: any, extractor: (any) => Page = n => n): any[] => { - return arr.sort((a, b) => { - a = extractor(a); - b = extractor(b); +export const sortPages = <T>(arr: T[], extractor: (entity: T) => Page) => { + return arr.sort((entityA, entityB) => { + const pageA = extractor(entityA); + const pageB = extractor(entityB); - const aBase = a.context.sidebar_order ?? 10; - const bBase = b.context.sidebar_order ?? 10; + const aBase = pageA.context.sidebar_order ?? 10; + const bBase = pageB.context.sidebar_order ?? 10; const aso = aBase >= 0 ? aBase : 10; const bso = bBase >= 0 ? bBase : 10; @@ -43,8 +43,8 @@ export const sortPages = (arr: any, extractor: (any) => Page = n => n): any[] => if (bso > aso) { return -1; } - return (a.context.sidebar_title || a.context.title).localeCompare( - b.context.sidebar_title || b.context.title + return ((pageA.context.sidebar_title || pageA.context.title) ?? '').localeCompare( + (pageB.context.sidebar_title || pageB.context.title) ?? '' ); }); };
- {`sha384-${file.checksums.find(c => c.name === 'sha384-base64').value}`} + {`sha384-${file.checksums.find(c => c.name === 'sha384-base64')?.value}`}