Skip to content

Commit e8d17f1

Browse files
committed
Refactor feed iteration
1 parent 5a51d67 commit e8d17f1

File tree

3 files changed

+57
-28
lines changed

3 files changed

+57
-28
lines changed

packages/web/docs/src/app/blog/feed.xml/route.ts

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
import RSS from 'rss';
33
import { getPageMap } from '@theguild/components/server';
44
import { AuthorId, authors } from '../../../authors';
5-
import { coerceCaseStudiesToBlogs } from '../../case-studies/coerce-case-studies-to-blogs';
5+
import { pagesDepthFirst } from '../../../mdx-types';
6+
import { coerceCaseStudyToBlog } from '../../case-studies/coerce-case-studies-to-blogs';
67
import { isCaseStudy } from '../../case-studies/isCaseStudyFile';
7-
import { BlogFrontmatter, isBlogPost } from '../blog-types';
8+
import { BlogFrontmatter, BlogPostFile, isBlogPost } from '../blog-types';
89

910
function getAuthor(frontmatterAuthors: BlogFrontmatter['authors']): string {
1011
const first = Array.isArray(frontmatterAuthors) ? frontmatterAuthors[0] : frontmatterAuthors;
@@ -17,32 +18,30 @@ function getAuthor(frontmatterAuthors: BlogFrontmatter['authors']): string {
1718
return first.name;
1819
}
1920

21+
export const dynamic = 'force-static';
22+
export const config = { runtime: 'edge' };
23+
2024
export async function GET() {
2125
let allPosts: RSS.ItemOptions[] = [];
2226

23-
// TODO: This needs a refactor: one `getPageMap` call,
24-
// iterating over all items and pushing to `allPosts` in a coerced form.
25-
const [, , ...blogs] = await getPageMap('/blog');
26-
const [, , ...studies] = await getPageMap('/case-studies');
27-
const [, , ...updates] = await getPageMap('/product-updates');
28-
29-
const studiesAsBlogs = coerceCaseStudiesToBlogs(studies.filter(isCaseStudy));
27+
const [_meta, _indexPage, ...pages] = await getPageMap('/');
28+
for (const page of pagesDepthFirst(pages)) {
29+
const route = (page && 'route' in page && page.route) || '';
30+
const [dir, name] = route.split('/').filter(Boolean);
31+
if (!name) continue;
32+
switch (dir) {
33+
case 'blog':
34+
case 'product-updates':
35+
if (isBlogPost(page)) allPosts.push(toRssItem(page));
36+
break;
37+
case 'case-studies':
38+
if (isCaseStudy(page)) allPosts.push(toRssItem(coerceCaseStudyToBlog(page)));
39+
break;
40+
}
41+
}
3042

31-
for (const items of [blogs.filter(isBlogPost), updates.filter(isBlogPost), studiesAsBlogs]) {
32-
allPosts = allPosts.concat(
33-
items.map(
34-
(item): RSS.ItemOptions => ({
35-
title: item.frontMatter.title,
36-
date: new Date(item.frontMatter.date),
37-
url: `https://the-guild.dev/graphql/hive${item.route}`,
38-
description: item.frontMatter.description ?? '',
39-
author: getAuthor(item.frontMatter.authors),
40-
categories: Array.isArray(item.frontMatter.tags)
41-
? item.frontMatter.tags
42-
: [item.frontMatter.tags],
43-
}),
44-
),
45-
);
43+
if (allPosts.length === 0) {
44+
throw new Error('No blog posts found for RSS feed');
4645
}
4746

4847
allPosts = allPosts.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
@@ -64,5 +63,15 @@ export async function GET() {
6463
});
6564
}
6665

67-
export const dynamic = 'force-static';
68-
export const config = { runtime: 'edge' };
66+
function toRssItem(blogPost: BlogPostFile): RSS.ItemOptions {
67+
return {
68+
title: blogPost.frontMatter.title,
69+
date: new Date(blogPost.frontMatter.date),
70+
url: `https://the-guild.dev/graphql/hive${blogPost.route}`,
71+
description: blogPost.frontMatter.description ?? '',
72+
author: getAuthor(blogPost.frontMatter.authors),
73+
categories: Array.isArray(blogPost.frontMatter.tags)
74+
? blogPost.frontMatter.tags
75+
: [blogPost.frontMatter.tags],
76+
};
77+
}

packages/web/docs/src/app/case-studies/coerce-case-studies-to-blogs.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { BlogFrontmatter, BlogPostFile } from '../blog/blog-types';
33
import { CaseStudyFile } from './case-study-types';
44

55
export function coerceCaseStudiesToBlogs(caseStudies: CaseStudyFile[]): BlogPostFile[] {
6-
return caseStudies.map(caseStudy => ({
6+
return caseStudies.map(coerceCaseStudyToBlog);
7+
}
8+
9+
export function coerceCaseStudyToBlog(caseStudy: CaseStudyFile): BlogPostFile {
10+
return {
711
...caseStudy,
812
frontMatter: {
913
...caseStudy.frontMatter,
@@ -17,5 +21,5 @@ export function coerceCaseStudiesToBlogs(caseStudies: CaseStudyFile[]): BlogPost
1721
}),
1822
),
1923
} satisfies BlogFrontmatter,
20-
}));
24+
};
2125
}

packages/web/docs/src/mdx-types.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,19 @@ export type MdxFile<FrontMatterType> = {
1313
* TODO: This should be exported from `nextra` and `@theguild/components`
1414
*/
1515
export type PageMapItem = Awaited<ReturnType<typeof getPageMap>>[number];
16+
17+
export type FolderItem = Extract<PageMapItem, { children: PageMapItem[] }>;
18+
19+
export function isFolder(item: PageMapItem): item is FolderItem {
20+
return !!item && typeof item === 'object' && 'children' in item;
21+
}
22+
23+
export function* pagesDepthFirst(items: PageMapItem[]): Generator<PageMapItem> {
24+
for (const item of items) {
25+
if (isFolder(item)) {
26+
yield* pagesDepthFirst(item.children);
27+
} else {
28+
yield item;
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)