127 lines
4.5 KiB
JavaScript
127 lines
4.5 KiB
JavaScript
import findPageInSiteTree from '../../lib/find-page-in-site-tree.js'
|
|
import { liquid } from '../../lib/render-content/index.js'
|
|
|
|
// This module adds either flatTocItems or nestedTocItems to the context object for
|
|
// product, categorie, and map topic TOCs that don't have other layouts specified.
|
|
// They are rendered by includes/generic-toc-flat.html or inclueds/generic-toc-nested.html.
|
|
export default async function genericToc(req, res, next) {
|
|
if (!req.context.page) return next()
|
|
if (req.context.currentLayoutName !== 'default') return next()
|
|
// This middleware can only run on product, category, and map topics.
|
|
if (
|
|
req.context.page.documentType === 'homepage' ||
|
|
req.context.page.documentType === 'article' ||
|
|
req.context.page.relativePath === 'search/index.md'
|
|
)
|
|
return next()
|
|
|
|
// This one product TOC is weird.
|
|
const isOneOffProductToc = req.context.page.relativePath === 'github/index.md'
|
|
|
|
// There are different types of TOC depending on the document type.
|
|
const tocTypes = {
|
|
product: 'flat',
|
|
category: 'nested',
|
|
mapTopic: 'flat',
|
|
}
|
|
|
|
// Frontmatter can optionally be set on an Early Access product to show hidden child items.
|
|
// If so, this is a special case where we want to override the flat tocType and use a nested type.
|
|
const earlyAccessToc = req.context.page.earlyAccessToc
|
|
|
|
// Find the current TOC type based on the current document type.
|
|
const currentTocType = earlyAccessToc ? 'nested' : tocTypes[req.context.page.documentType]
|
|
|
|
// Find the part of the site tree that corresponds to the current path.
|
|
const treePage = findPageInSiteTree(
|
|
req.context.currentProductTree,
|
|
req.context.currentEnglishTree,
|
|
req.pagePath
|
|
)
|
|
|
|
// By default, only include hidden child items on a TOC page if it's an Early Access category or
|
|
// map topic page, not a product or 'articles' fake cagegory page (e.g., /early-access/github/articles).
|
|
// This is because we don't want entire EA product TOCs to be publicly browseable, but anything at the category
|
|
// or below level is fair game because that content is scoped to specific features.
|
|
const isCategoryOrMapTopic =
|
|
req.context.page.documentType === 'category' || req.context.page.documentType === 'mapTopic'
|
|
const isEarlyAccess = req.context.currentPath.includes('/early-access/')
|
|
const isArticlesCategory = req.context.currentPath.endsWith('/articles')
|
|
|
|
const includeHidden =
|
|
earlyAccessToc || (isCategoryOrMapTopic && isEarlyAccess && !isArticlesCategory)
|
|
|
|
// Conditionally run getTocItems() recursively.
|
|
let isRecursive
|
|
// Conditionally render intros.
|
|
let renderIntros
|
|
|
|
// Get an array of child links with intros and add it to the context object.
|
|
if (currentTocType === 'flat' && !isOneOffProductToc) {
|
|
isRecursive = false
|
|
renderIntros = true
|
|
req.context.genericTocFlat = []
|
|
req.context.genericTocFlat = await getTocItems(treePage, req.context, {
|
|
recurse: isRecursive,
|
|
renderIntros,
|
|
includeHidden,
|
|
})
|
|
}
|
|
|
|
// Get an array of child map topics and their child articles and add it to the context object.
|
|
if (currentTocType === 'nested' || isOneOffProductToc) {
|
|
isRecursive = !isOneOffProductToc
|
|
renderIntros = false
|
|
req.context.genericTocNested = await getTocItems(treePage, req.context, {
|
|
recurse: isRecursive,
|
|
renderIntros,
|
|
includeHidden,
|
|
})
|
|
}
|
|
|
|
return next()
|
|
}
|
|
|
|
// Return a nested object that contains the bits and pieces we need
|
|
// for the tree which is used for sidebars and listing
|
|
async function getTocItems(node, context, opts) {
|
|
// Cleaner than trying to be too terse inside the `.filter()` inline callback.
|
|
function filterHidden(child) {
|
|
return opts.includeHidden || !child.page.hidden
|
|
}
|
|
|
|
return await Promise.all(
|
|
node.childPages.filter(filterHidden).map(async (child) => {
|
|
const { page } = child
|
|
const title = page.rawTitle.includes('{')
|
|
? await liquid.parseAndRender(page.rawTitle, context)
|
|
: page.rawTitle
|
|
let intro = null
|
|
if (opts.renderIntros) {
|
|
intro = ''
|
|
if (page.rawIntro) {
|
|
intro = page.rawIntro.includes('{')
|
|
? await liquid.parseAndRender(page.rawIntro, context)
|
|
: page.rawIntro
|
|
}
|
|
}
|
|
|
|
let childTocItems = null
|
|
if (opts.recurse) {
|
|
childTocItems = []
|
|
if (child.childPages) {
|
|
childTocItems.push(...(await getTocItems(child, context, opts)))
|
|
}
|
|
}
|
|
|
|
const fullPath = child.href
|
|
return {
|
|
title,
|
|
fullPath,
|
|
intro,
|
|
childTocItems,
|
|
}
|
|
})
|
|
)
|
|
}
|