Skip to content

Commit 94d23fa

Browse files
committed
Split content collection into reusable service
1 parent f8c67e6 commit 94d23fa

4 files changed

Lines changed: 235 additions & 142 deletions

File tree

app/services/contentService.ts

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import { getAllArticlePaths, getArticleByPath } from './articleService';
2+
import { getAllPosts } from './blogService';
3+
import { getReferencesGroupedByTypeAndCategory, getAllReferenceParams, getReferenceByPath } from './referenceService';
4+
import path from 'path';
5+
import { Entity } from '../types/Entity';
6+
7+
export function getAllContent(): Entity[] {
8+
const pages: Entity[] = [];
9+
10+
// 1. Home page
11+
pages.push({
12+
url: '/',
13+
slug: 'home.png',
14+
title: 'DocumentDB',
15+
description: 'A powerful, scalable open-source document database solution',
16+
type: 'home',
17+
});
18+
19+
// 2. Docs landing
20+
pages.push({
21+
url: '/docs',
22+
slug: 'docs.png',
23+
title: 'Documentation',
24+
description: 'Complete DocumentDB documentation and guides',
25+
section: 'docs',
26+
type: 'landing',
27+
});
28+
29+
// 3. All article/documentation pages
30+
const articlePaths = getAllArticlePaths();
31+
for (const articlePath of articlePaths) {
32+
const article = getArticleByPath(articlePath.section, articlePath.slug);
33+
if (article) {
34+
const selectedNavItem = article.navigation.find((item: any) =>
35+
item.link.includes(articlePath.slug[articlePath.slug.length - 1] || 'index')
36+
);
37+
const title = article.frontmatter.title || selectedNavItem?.title || articlePath.section;
38+
39+
let url = `/docs/${articlePath.section}`;
40+
if (articlePath.slug.length > 0) {
41+
url += `/${articlePath.slug.join('/')}`;
42+
}
43+
44+
const mdFilePath = path.join(
45+
process.cwd(),
46+
'articles',
47+
articlePath.section,
48+
...articlePath.slug,
49+
'index.md'
50+
);
51+
52+
// Convert URL to slug filename
53+
const slug = url === '/'
54+
? 'home.png'
55+
: url.slice(1).replace(/\//g, '-') + '.png';
56+
57+
pages.push({
58+
url,
59+
slug,
60+
title,
61+
description: article.frontmatter.description || `${title} - DocumentDB Documentation`,
62+
section: articlePath.section,
63+
type: 'docs',
64+
filePath: mdFilePath,
65+
});
66+
}
67+
}
68+
69+
// 4. Blogs landing
70+
pages.push({
71+
url: '/blogs',
72+
slug: 'blogs.png',
73+
title: 'Blog',
74+
description: 'Latest insights and updates from DocumentDB',
75+
section: 'blog',
76+
type: 'landing',
77+
});
78+
79+
// 5. Individual blog posts (external URIs)
80+
const posts = getAllPosts();
81+
for (const post of posts) {
82+
// Generate slug from title (for external blog posts without slugs)
83+
const slug = post.title
84+
.toLowerCase()
85+
.replace(/[^a-z0-9]+/g, '-')
86+
.replace(/^-+|-+$/g, '');
87+
88+
const url = `/blogs/${slug}`;
89+
const filename = url.slice(1).replace(/\//g, '-') + '.png';
90+
91+
pages.push({
92+
url,
93+
slug: filename,
94+
title: post.title,
95+
description: post.description,
96+
section: 'blog',
97+
type: 'blog',
98+
isExternal: true, // Mark as external since these redirect to external URIs
99+
});
100+
}
101+
102+
// 6. Reference landing page
103+
pages.push({
104+
url: '/docs/reference',
105+
slug: 'docs-reference.png',
106+
title: 'API Reference',
107+
description: 'Complete DocumentDB API reference documentation',
108+
section: 'reference',
109+
type: 'landing',
110+
});
111+
112+
// 7. Reference type pages (e.g., /docs/reference/commands)
113+
const referenceContent = getReferencesGroupedByTypeAndCategory();
114+
for (const [type] of Object.entries(referenceContent)) {
115+
const url = `/docs/reference/${type}`;
116+
const slug = url.slice(1).replace(/\//g, '-') + '.png';
117+
118+
pages.push({
119+
url,
120+
slug,
121+
title: `${type.charAt(0).toUpperCase() + type.slice(1)} Reference`,
122+
description: `DocumentDB ${type} reference documentation`,
123+
section: 'reference',
124+
type: 'landing',
125+
});
126+
}
127+
128+
// 8. Reference category pages (e.g., /docs/reference/operators/aggregation)
129+
for (const [type, categories] of Object.entries(referenceContent)) {
130+
for (const [category] of Object.entries(categories)) {
131+
const url = `/docs/reference/${type}/${category}`;
132+
const slug = url.slice(1).replace(/\//g, '-') + '.png';
133+
134+
pages.push({
135+
url,
136+
slug,
137+
title: `${category} - ${type.charAt(0).toUpperCase() + type.slice(1)}`,
138+
description: `${category} ${type} reference documentation`,
139+
section: 'reference',
140+
type: 'landing',
141+
});
142+
}
143+
}
144+
145+
// 9. Individual reference items
146+
const referenceParams = getAllReferenceParams();
147+
for (const param of referenceParams) {
148+
const reference = getReferenceByPath(param.type, param.category, param.name);
149+
if (reference) {
150+
const url = `/docs/reference/${param.type}/${param.category}/${param.name}`;
151+
const slug = url.slice(1).replace(/\//g, '-') + '.png';
152+
153+
const mdFilePath = path.join(
154+
process.cwd(),
155+
'reference',
156+
param.type,
157+
param.category,
158+
`${param.name}.yml`
159+
);
160+
161+
pages.push({
162+
url,
163+
slug,
164+
title: reference.name || param.name,
165+
description: reference.description || `${param.name} - DocumentDB Reference`,
166+
section: 'reference',
167+
type: 'reference',
168+
filePath: mdFilePath,
169+
});
170+
}
171+
}
172+
173+
// 10. Packages page
174+
pages.push({
175+
url: '/packages',
176+
slug: 'packages.png',
177+
title: 'Packages',
178+
description: 'Download and install DocumentDB packages',
179+
type: 'packages',
180+
});
181+
182+
return pages;
183+
}

app/services/siteService.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Determine base URL for entire site
2+
export function getBaseUrl(): string {
3+
return 'https://documentdb.io';
4+
}

app/sitemap.ts

Lines changed: 38 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import { MetadataRoute } from 'next';
2-
import { getAllArticlePaths } from './services/articleService';
3-
import { getAllReferenceParams } from './services/referenceService';
2+
import { getAllContent } from './services/contentService';
3+
import { getBaseUrl } from './services/siteService';
44
import { execSync } from 'child_process';
55
import path from 'path';
66

77
// Required to evaluate at build time
88
export const dynamic = 'force-static';
99

10-
// Determine base URL
11-
const getBaseUrl = (): string => 'https://documentdb.io';
12-
1310
// Get the last git commit date for a specific file
1411
const getGitLastModified = (filePath: string): Date => {
1512
try {
@@ -27,148 +24,47 @@ const getGitLastModified = (filePath: string): Date => {
2724
}
2825
};
2926

30-
// Get the most recent git commit date for a directory
31-
const getDirectoryLastModified = (dirPath: string): Date => {
32-
try {
33-
// Convert absolute path to relative path from repo root
34-
const relativePath = path.relative(process.cwd(), dirPath).replace(/\\/g, '/');
35-
const output = execSync(
36-
`git log -1 --format=%cI -- "${relativePath}"`,
37-
{ encoding: 'utf-8', cwd: process.cwd() }
38-
).trim();
39-
40-
return output ? new Date(output) : new Date();
41-
} catch (error) {
42-
console.warn(`Could not get git date for directory ${dirPath}`);
43-
return new Date();
44-
}
45-
};
46-
4727
export default function sitemap(): MetadataRoute.Sitemap {
4828
const baseUrl = getBaseUrl();
49-
const currentDate = new Date();
50-
51-
const sitemapEntries: MetadataRoute.Sitemap = [];
52-
53-
// 1. Homepage
54-
sitemapEntries.push({
55-
url: baseUrl,
56-
lastModified: currentDate,
57-
changeFrequency: 'yearly',
58-
priority: 1.0,
59-
});
60-
61-
// 2. Main section pages
62-
const mainSections = [
63-
{ path: '/docs', dirPath: 'articles' },
64-
{ path: '/blogs', dirPath: 'blogs' },
65-
{ path: '/packages', dirPath: 'app/packages' },
66-
{ path: '/docs/reference', dirPath: 'reference' },
67-
];
68-
69-
mainSections.forEach(section => {
70-
const dirFullPath = path.join(process.cwd(), section.dirPath);
71-
const lastModified = getDirectoryLastModified(dirFullPath);
72-
73-
sitemapEntries.push({
74-
url: `${baseUrl}${section.path}`,
75-
lastModified,
76-
changeFrequency: 'monthly',
77-
priority: 0.70,
78-
});
79-
});
80-
81-
// 3. Article/Documentation pages
82-
try {
83-
const articlePaths = getAllArticlePaths();
84-
85-
articlePaths.forEach(({ section, slug }) => {
86-
// Build the URL path
87-
let urlPath = `/docs/${section}`;
88-
if (slug.length > 0) {
89-
urlPath += `/${slug.join('/')}`;
29+
const entities = getAllContent();
30+
31+
return entities
32+
.filter(entity => !entity.isExternal) // Exclude external blog posts
33+
.map(entity => {
34+
// Determine priority and change frequency based on page type and depth
35+
let priority = 0.00;
36+
let changeFrequency: 'yearly' | 'monthly' | 'weekly' = 'yearly';
37+
38+
if (entity.type === 'home') {
39+
priority = 1.0;
40+
changeFrequency = 'yearly';
41+
} else if (entity.type === 'docs') {
42+
priority = 0.85;
43+
changeFrequency = 'weekly';
44+
} else if (entity.type === 'packages') {
45+
priority = 0.70;
46+
changeFrequency = 'weekly';
47+
} else if (entity.type === 'reference') {
48+
priority = 0.55;
49+
changeFrequency = 'monthly';
50+
} else if (entity.type === 'blog') {
51+
priority = 0.40;
52+
changeFrequency = 'monthly';
53+
} else if (entity.type === 'landing') {
54+
priority = 0.25;
55+
changeFrequency = 'monthly';
9056
}
9157

92-
// Determine the markdown file path
93-
const mdFilePath = path.join(process.cwd(), 'articles', section, ...slug, 'index.md');
94-
const lastModified = getGitLastModified(mdFilePath);
95-
96-
sitemapEntries.push({
97-
url: `${baseUrl}${urlPath}`,
98-
lastModified,
99-
changeFrequency: 'weekly',
100-
priority: 0.70,
101-
});
102-
});
103-
} catch (error) {
104-
console.error('Error generating article sitemap entries:', error);
105-
}
106-
107-
// 4. Blog posts
108-
// Note: Blog posts are external URIs, so we don't include them in the sitemap
109-
// as they redirect to external sites. The /blogs page itself is already included above.
110-
111-
// 5. Reference documentation pages
112-
try {
113-
const referenceParams = getAllReferenceParams();
114-
115-
referenceParams.forEach(({ type, category, name }) => {
116-
const urlPath = `/docs/reference/${type}/${category}/${name}`;
117-
118-
// Determine the markdown file path
119-
const mdFilePath = path.join(process.cwd(), 'reference', type, category, `${name}.md`);
120-
const lastModified = getGitLastModified(mdFilePath);
121-
122-
sitemapEntries.push({
123-
url: `${baseUrl}${urlPath}`,
124-
lastModified,
125-
changeFrequency: 'weekly',
126-
priority: 0.85,
127-
});
128-
});
129-
} catch (error) {
130-
console.error('Error generating reference sitemap entries:', error);
131-
}
132-
133-
// 6. Reference type index pages (e.g., /docs/reference/commands, /docs/reference/operators)
134-
try {
135-
const referenceParams = getAllReferenceParams();
136-
const uniqueTypes = [...new Set(referenceParams.map(p => p.type))];
137-
138-
uniqueTypes.forEach(type => {
139-
const dirPath = path.join(process.cwd(), 'reference', type);
140-
const lastModified = getDirectoryLastModified(dirPath);
58+
// Get last modified date from git
59+
const lastModified = entity.filePath
60+
? getGitLastModified(entity.filePath)
61+
: new Date();
14162

142-
sitemapEntries.push({
143-
url: `${baseUrl}/docs/reference/${type}`,
63+
return {
64+
url: `${baseUrl}${entity.url}`,
14465
lastModified,
145-
changeFrequency: 'monthly',
146-
priority: 0.55,
147-
});
66+
changeFrequency,
67+
priority,
68+
};
14869
});
149-
} catch (error) {
150-
console.error('Error generating reference type index sitemap entries:', error);
151-
}
152-
153-
// 7. Reference category pages (e.g., /docs/reference/operators/miscellaneous-query)
154-
try {
155-
const referenceParams = getAllReferenceParams();
156-
const uniqueCategories = [...new Set(referenceParams.map(p => `${p.type}/${p.category}`))];
157-
158-
uniqueCategories.forEach(typeCategory => {
159-
const dirPath = path.join(process.cwd(), 'reference', ...typeCategory.split('/'));
160-
const lastModified = getDirectoryLastModified(dirPath);
161-
162-
sitemapEntries.push({
163-
url: `${baseUrl}/docs/reference/${typeCategory}`,
164-
lastModified,
165-
changeFrequency: 'monthly',
166-
priority: 0.55,
167-
});
168-
});
169-
} catch (error) {
170-
console.error('Error generating reference category sitemap entries:', error);
171-
}
172-
173-
return sitemapEntries;
17470
}

0 commit comments

Comments
 (0)