export default function breadcrumbs(req, res, next) { if (!req.context.page) return next() const isEarlyAccess = req.context.page.relativePath.startsWith('early-access') if (req.context.page.hidden && !isEarlyAccess) return next() req.context.breadcrumbs = [] // Return an empty array on the landing page. if (req.context.page.documentType === 'homepage') { return next() } req.context.breadcrumbs = getBreadcrumbs(req, isEarlyAccess) return next() } const earlyAccessExceptions = ['insights', 'enterprise-importer'] function getBreadcrumbs(req, isEarlyAccess) { let cutoff = 0 // When in Early access docs consider the "root" be much higher. // E.g. /en/early-access/github/migrating/understanding/about // we only want it start at /migrating/understanding/about // Essentially, we're skipping "/early-access" and its first // top-level like "/github" if (isEarlyAccess) { const split = req.context.currentPath.split('/') // There are a few exceptions to this rule for the // /{version}/early-access//... URLs because they're a // bit different. // If there are more known exceptions, add them to the array above. if (earlyAccessExceptions.some((product) => split.includes(product))) { cutoff = 1 } else { cutoff = 2 } } const breadcrumbs = traverseTreeTitles( req.context.currentPath, req.context.currentProductTreeTitles, ) ;[...Array(cutoff)].forEach(() => breadcrumbs.shift()) return breadcrumbs } // Return an array as if you'd traverse down a tree. Imagine a tree like // // (root /) // / \ // (/foo) (/bar) // / \ // (/foo/bar) (/foo/buzz) // // If the "currentPath" is `/foo/buzz` what you want to return is: // // [ // {href: /, title: TITLE}, // {href: /foo, title: TITLE} // {href: /foo/buzz, title: TITLE} // ] // function traverseTreeTitles(currentPath, tree) { const { href, title, shortTitle } = tree const crumbs = [ { href, title: shortTitle || title, }, ] const currentPathSplit = Array.isArray(currentPath) ? currentPath : currentPath.split('/') for (const child of tree.childPages) { if (isParentOrEqualArray(child.href.split('/'), currentPathSplit)) { crumbs.push(...traverseTreeTitles(currentPathSplit, child)) // Only ever going down 1 of the children break } } return crumbs } // Return true if an array is part of another array or equal. // Like `/foo/bar` is part of `/foo/bar/buzz`. // But also include `/foo/bar/buzz`. // Don't include `/foo/ba` if the final path is `/foo/baring`. function isParentOrEqualArray(base, final) { return base.every((part, i) => part === final[i]) }