| | import type { Response, NextFunction } from 'express' |
| |
|
| | import type { ExtendedRequest, Context, Tree, ToC } from '@/types' |
| | import findPageInSiteTree from '@/frame/lib/find-page-in-site-tree' |
| |
|
| | function isNewLandingPage(currentLayoutName: string): boolean { |
| | return ( |
| | currentLayoutName === 'category-landing' || |
| | currentLayoutName === 'bespoke-landing' || |
| | currentLayoutName === 'discovery-landing' || |
| | currentLayoutName === 'journey-landing' |
| | ) |
| | } |
| |
|
| | |
| | |
| | |
| | export default async function genericToc(req: ExtendedRequest, res: Response, next: NextFunction) { |
| | if (!req.context) throw new Error('request not contextualized') |
| | if (!req.context.page) return next() |
| | if ( |
| | req.context.currentLayoutName !== 'default' && |
| | !isNewLandingPage(req.context.currentLayoutName || '') |
| | ) |
| | return next() |
| | |
| | if ( |
| | req.context.page.documentType === 'homepage' || |
| | req.context.page.documentType === 'article' || |
| | req.context.page.relativePath === 'search/index.md' |
| | ) |
| | return next() |
| |
|
| | |
| | const isOneOffProductToc = req.context.page.relativePath === 'github/index.md' |
| |
|
| | |
| | const tocTypes: Record<string, string> = { |
| | product: 'flat', |
| | category: 'nested', |
| | subcategory: 'flat', |
| | } |
| |
|
| | |
| | |
| | const earlyAccessToc = req.context.page.earlyAccessToc |
| |
|
| | if (!req.context.currentProductTree) throw new Error('currentProductTree not in context') |
| | if (!req.context.currentEnglishTree) throw new Error('currentEnglishTree not in context') |
| | if (!req.pagePath) throw new Error('pagePath not in request') |
| |
|
| | |
| | const treePage = findPageInSiteTree( |
| | req.context.currentProductTree, |
| | req.context.currentEnglishTree, |
| | req.pagePath, |
| | ) |
| |
|
| | let fauxSubcategory = false |
| | if (req.context.page.documentType === 'category' && req.context.page.autogenerated !== 'rest') { |
| | |
| | const hasGrandchildren = (treePage.childPages || []).some((child) => child.children) |
| | fauxSubcategory = !hasGrandchildren |
| | } |
| |
|
| | |
| | const currentTocType = earlyAccessToc |
| | ? 'nested' |
| | : fauxSubcategory |
| | ? 'flat' |
| | : tocTypes[req.context.page.documentType] |
| |
|
| | |
| | |
| | |
| | |
| | const isCategoryOrSubcategory = |
| | req.context.page.documentType === 'category' || req.context.page.documentType === 'subcategory' |
| | if (!req.context.currentPath) throw new Error('currentPath not in context') |
| | const isEarlyAccess = req.context.currentPath.includes('/early-access/') |
| | const isArticlesCategory = req.context.currentPath.endsWith('/articles') |
| |
|
| | const includeHidden = |
| | earlyAccessToc || (isCategoryOrSubcategory && isEarlyAccess && !isArticlesCategory) |
| |
|
| | |
| | let isRecursive |
| | |
| | let renderIntros |
| |
|
| | |
| | if (currentTocType === 'flat' && !isOneOffProductToc) { |
| | isRecursive = false |
| | renderIntros = true |
| | req.context.genericTocFlat = await getTocItems(treePage, req.context, { |
| | recurse: isRecursive, |
| | renderIntros, |
| | includeHidden, |
| | textOnly: isNewLandingPage(req.context.currentLayoutName || ''), |
| | }) |
| | } |
| |
|
| | |
| | if (currentTocType === 'nested' || isOneOffProductToc) { |
| | isRecursive = !isOneOffProductToc |
| | renderIntros = false |
| | req.context.genericTocNested = await getTocItems(treePage, req.context, { |
| | recurse: isRecursive, |
| | renderIntros: isNewLandingPage(req.context.currentLayoutName || '') ? true : false, |
| | includeHidden, |
| | textOnly: isNewLandingPage(req.context.currentLayoutName || ''), |
| | }) |
| | } |
| |
|
| | return next() |
| | } |
| |
|
| | |
| | |
| | type Options = { |
| | recurse: boolean |
| | renderIntros: boolean |
| | includeHidden: boolean |
| | textOnly: boolean |
| | } |
| |
|
| | async function getTocItems(node: Tree, context: Context, opts: Options): Promise<ToC[]> { |
| | |
| | function filterHidden(child: Tree): boolean { |
| | return opts.includeHidden || !child.page.hidden |
| | } |
| |
|
| | return await Promise.all( |
| | node.childPages.filter(filterHidden).map(async (child) => { |
| | const { page } = child |
| | const title = await page.renderProp('rawTitle', context, { textOnly: true }) |
| | const octicon = page.octicon ?? null |
| | const category = page.category ? page.category : null |
| | const complexity = page.complexity ? page.complexity : null |
| | const industry = page.industry ? page.industry : null |
| | let intro = null |
| | if (opts.renderIntros) { |
| | intro = '' |
| | if (page.rawIntro) { |
| | |
| | |
| | |
| | |
| | |
| | intro = await page.renderProp( |
| | 'rawIntro', |
| | context, |
| | opts.textOnly ? { textOnly: true } : {}, |
| | ) |
| | } |
| | } |
| |
|
| | const childTocItems = [] |
| | if (child.childPages) { |
| | childTocItems.push(...(await getTocItems(child, context, opts))) |
| | } |
| |
|
| | const fullPath = child.href |
| | return { |
| | title, |
| | fullPath, |
| | intro, |
| | octicon, |
| | category, |
| | complexity, |
| | industry, |
| | childTocItems, |
| | } as ToC |
| | }), |
| | ) |
| | } |
| |
|