Skip to content

Commit

Permalink
fix: breadcrumbs work across sources
Browse files Browse the repository at this point in the history
  • Loading branch information
DavieReid committed Sep 22, 2023
1 parent 03bfb29 commit b62a51e
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 5 deletions.
67 changes: 65 additions & 2 deletions packages/plugins/src/BreadcrumbsPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import path from 'path';
import path from 'node:path';
import { escapeRegExp } from 'lodash-es';
import type { Plugin as PluginType } from '@jpmorganchase/mosaic-types';
import PluginError from './utils/PluginError.js';
import { SidebarPluginPage } from './SidebarPlugin.js';

const createPageTest = (ignorePages, pageExtensions) => {
const extTest = new RegExp(`${pageExtensions.map(ext => escapeRegExp(ext)).join('|')}$`);
const ignoreTest = new RegExp(`${ignorePages.map(ignore => escapeRegExp(ignore)).join('|')}$`);
return file =>
!ignoreTest.test(file) && extTest.test(file) && !path.basename(file).startsWith('.');
};

export type Breadcrumb = { label: string; path: string; id: string };

export interface BreadcrumbsPluginPage extends SidebarPluginPage {
Expand All @@ -18,8 +26,12 @@ interface BreadcrumbsPluginOptions {
* Calculates breadcrumbs for pages then embeds a `breadcrumbs` property to the metadata
*/
const BreadcrumbsPlugin: PluginType<BreadcrumbsPluginPage, BreadcrumbsPluginOptions> = {
async $afterSource(pages, _, options) {
async $afterSource(pages, { ignorePages, pageExtensions }, options) {
const isNonHiddenPage = createPageTest(ignorePages, pageExtensions);
for (const page of pages) {
if (!isNonHiddenPage(page.fullPath)) {
continue;
}
try {
const breadcrumbs: Array<Breadcrumb> = [];
let currentPage = page;
Expand Down Expand Up @@ -53,6 +65,57 @@ const BreadcrumbsPlugin: PluginType<BreadcrumbsPluginPage, BreadcrumbsPluginOpti
}

return pages;
},
async afterUpdate(mutableFilesystem, { serialiser, globalFilesystem, ignorePages }, options) {
const pages = (await mutableFilesystem.promises.glob('**', {
onlyFiles: true,
ignore: ignorePages.map(ignore => `**/${ignore}`),
cwd: '/'
})) as string[];

if (pages.length > 0) {
let updatedBreadcrumbs = [];
const { breadcrumbs } = await serialiser.deserialise(
pages[0],
await mutableFilesystem.promises.readFile(pages[0])
);

// the root breadcrumb is the first breadcrumb for all pages in a source. It is essentially the "first" page in a source
const rootBreadcrumb = breadcrumbs?.[0] || undefined;

let parentDir = path.posix.join(path.posix.dirname(rootBreadcrumb.id), '../');

while (parentDir !== '/') {
const parentDirIndex = path.posix.join(parentDir, options.indexPageName);

// check for this file in the global fs so we have a holistic view of all site pages
if (await globalFilesystem.promises.exists(parentDirIndex)) {
const { breadcrumbs: parentDirBreadcrumbs } = await serialiser.deserialise(
rootBreadcrumb.id,
await globalFilesystem.promises.readFile(parentDirIndex)
);
updatedBreadcrumbs = parentDirBreadcrumbs;
break;
}
parentDir = path.posix.join(path.posix.dirname(parentDirIndex), '../');
}

if (updatedBreadcrumbs.length > 0) {
for (const pagePath of pages) {
const page = await serialiser.deserialise(
pagePath,
await mutableFilesystem.promises.readFile(pagePath)
);

// append the parent breadcrumbs **before** the current breadcrumbs
page.breadcrumbs = [...updatedBreadcrumbs, ...page.breadcrumbs];
await mutableFilesystem.promises.writeFile(
pagePath,
await serialiser.serialise(pagePath, page)
);
}
}
}
}
};

Expand Down
11 changes: 8 additions & 3 deletions packages/plugins/src/__tests__/BreadcrumbsPlugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,16 @@ describe('GIVEN the BreadcrumbsPlugin', () => {
let updatedPages: BreadcrumbsPluginPage[] = [];
beforeEach(async () => {
const $afterSource = BreadcrumbsPlugin.$afterSource;
// @ts-ignore
updatedPages = (await $afterSource?.(pages, {}, { indexPageName: 'index.mdx' })) || [];
updatedPages =
(await $afterSource?.(
pages,
{ ignorePages: ['sidebar.json'], pageExtensions: ['.mdx'] },
{ indexPageName: 'index.mdx' }
)) || [];
});

test('THEN it should use the `$afterSource` lifecycle event', () => {
expect(BreadcrumbsPlugin).toHaveProperty('$afterSource');
expect(BreadcrumbsPlugin).toHaveProperty('afterUpdate');
});

describe('AND WHEN `$afterSource` is called', () => {
Expand Down

0 comments on commit b62a51e

Please sign in to comment.