Skip to content

Commit

Permalink
fix: ensure ReadingTimePlugin runs on MDX pages only
Browse files Browse the repository at this point in the history
  • Loading branch information
DavieReid committed Dec 8, 2023
1 parent 1be9762 commit 9cbd4ff
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 14 deletions.
43 changes: 29 additions & 14 deletions packages/plugins/src/ReadingTimePlugin.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,53 @@
import path from 'node:path';
import type { Page, Plugin as PluginType } from '@jpmorganchase/mosaic-types';
import { escapeRegExp } from 'lodash-es';
import getReadingTime from 'reading-time';
import markdown from 'remark-parse';
import { unified } from 'unified';
import { visit } from 'unist-util-visit';
import type { Node } from 'unist';

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('.');
};

type LeafNode = Node & {
value?: string;
};

interface ReadingTimePluginPage extends Page {
export interface ReadingTimePluginPage extends Page {
readingTime?: ReturnType<typeof getReadingTime>;
}

/**
* Calculates reading time for pages and adds to frontmatter
* Calculates reading time of MDX pages and adds to frontmatter
*/
const ReadingTimePlugin: PluginType<ReadingTimePluginPage> = {
async $afterSource(pages) {
async $afterSource(pages, { ignorePages, pageExtensions }) {
const processor = unified().use(markdown);
for (const page of pages) {
const tree: Node = await processor.parse(page.content);
let textContent = '';

visit(
tree,
node => node.type === 'text' || node.type === 'code',
(node: LeafNode) => {
textContent += node.value;
if (pageExtensions.includes('.mdx')) {
for (const page of pages) {
const isNonHiddenPage = createPageTest(ignorePages, ['.mdx']);
if (!isNonHiddenPage(page.fullPath)) {
continue;
}
);
page.readingTime = getReadingTime(textContent);
}
const tree: Node = await processor.parse(page.content);
let textContent = '';

visit(
tree,
node => node.type === 'text' || node.type === 'code',
(node: LeafNode) => {
textContent += node.value;
}
);
page.readingTime = getReadingTime(textContent);
}
}
return pages;
}
};
Expand Down
100 changes: 100 additions & 0 deletions packages/plugins/src/__tests__/ReadingTimePlugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import type { Page } from '@jpmorganchase/mosaic-types';
import ReadingTimePlugin, { type ReadingTimePluginPage } from '../ReadingTimePlugin';

let updatedPages: ReadingTimePluginPage[] = [];
const $afterSource = ReadingTimePlugin.$afterSource;

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

describe('AND WHEN pages have the `.mdx` extension', () => {
const ignorePages = ['shared-config.json', 'sitemap.xml', 'sidebar.json'];
const pageExtensions = ['.mdx', '.json', '.md'];
beforeEach(async () => {
updatedPages =
(await $afterSource?.(
[
{
fullPath: '/FolderA/index.mdx',
route: 'route/folderA/index.mdx',
title: 'Folder A Index'
},
{
fullPath: '/FolderA/pageA.json',
route: 'route/folderA/pageA.json',
title: 'Folder A Page A'
}
],
{ ignorePages, pageExtensions },
{}
)) || [];
});

test('THEN the reading time is calculated', () => {
expect(updatedPages[0].readingTime).toBeDefined();
expect(updatedPages[1].readingTime).not.toBeDefined();
});
});

describe('AND WHEN `.mdx` is not a configured page extension', () => {
const ignorePages = ['shared-config.json', 'sitemap.xml', 'sidebar.json'];
const pageExtensions = ['.json'];
beforeEach(async () => {
updatedPages =
// @ts-ignore
(await $afterSource?.(
[
{
fullPath: '/FolderA/index.mdx',
route: 'route/folderA/index.mdx',
title: 'Folder A Index'
},
{
fullPath: '/FolderA/pageA.json',
route: 'route/folderA/pageA.json',
title: 'Folder A Page A'
}
],
{ ignorePages, pageExtensions },
{}
)) || [];
});

test('THEN no page has the reading time metadata', () => {
expect(updatedPages[0].readingTime).not.toBeDefined();
expect(updatedPages[1].readingTime).not.toBeDefined();
});
});

describe('AND WHEN a page itself is ignored', () => {
const pageExtensions = ['.mdx'];
const ignorePages = ['index.mdx'];
beforeEach(async () => {
updatedPages =
(await $afterSource?.(
[
{
fullPath: '/FolderA/index.mdx',
route: 'route/folderA/index.mdx',
title: 'Folder A Index'
},

{
fullPath: '/FolderA/pageA.json',
route: 'route/folderA/pageA.json',
title: 'Folder A Page A'
}
],
{ ignorePages, pageExtensions },
{}
)) || [];
});

test('THEN no page has the reading time metadata', () => {
expect(updatedPages[0].readingTime).not.toBeDefined();
expect(updatedPages[1].readingTime).not.toBeDefined();
});
});
});

1 comment on commit 9cbd4ff

@vercel
Copy link

@vercel vercel bot commented on 9cbd4ff Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

mosaic – ./

mosaic-git-main-mosaic-dev-team.vercel.app
mosaic-mosaic-dev-team.vercel.app

Please sign in to comment.