Add canonical URLs and sitemap dedup for docs homepage indexability

- Emit self-referential canonical `<link>` tags on every VitePress page
- Filter duplicate /README entry from generated sitemap
- Extract DOCS_HOSTNAME constant; update Plausible test to match
- Add seo.test.ts covering canonical generation and sitemap filtering
This commit is contained in:
2026-05-10 22:19:21 -07:00
parent 30712738dc
commit b68d17614d
6 changed files with 125 additions and 3 deletions
+24 -1
View File
@@ -1,3 +1,15 @@
const DOCS_HOSTNAME = 'https://docs.subminer.moe';
function pageToCanonicalHref(page: string): string | null {
if (page === '404.md') return null;
const route = page
.replace(/(^|\/)index\.md$/, '')
.replace(/\.md$/, '')
.replace(/\/$/, '');
return route ? `${DOCS_HOSTNAME}/${route}` : `${DOCS_HOSTNAME}/`;
}
export default {
title: 'SubMiner Docs',
description:
@@ -34,7 +46,18 @@ export default {
appearance: 'dark',
cleanUrls: true,
metaChunk: true,
sitemap: { hostname: 'https://docs.subminer.moe' },
sitemap: {
hostname: DOCS_HOSTNAME,
transformItems(items) {
return items.filter(
(item) => item.url !== 'README' && item.url !== `${DOCS_HOSTNAME}/README`,
);
},
},
transformHead({ page }) {
const href = pageToCanonicalHref(page);
return href ? [['link', { rel: 'canonical', href }]] : [];
},
lastUpdated: true,
srcExclude: ['subagents/**'],
markdown: {
+1 -1
View File
@@ -8,7 +8,7 @@
"docs:dev": "VITE_EXTRA_EXTENSIONS=jsonc vitepress dev --host 0.0.0.0 --port 5173 --strictPort",
"docs:build": "VITE_EXTRA_EXTENSIONS=jsonc vitepress build",
"docs:preview": "VITE_EXTRA_EXTENSIONS=jsonc vitepress preview --host 0.0.0.0 --port 4173 --strictPort",
"test": "bun test plausible.test.ts index.assets.test.ts docs-sync.test.ts"
"test": "bun test plausible.test.ts index.assets.test.ts docs-sync.test.ts seo.test.ts"
},
"dependencies": {
"@catppuccin/vitepress": "^0.1.2",
+2 -1
View File
@@ -7,7 +7,8 @@ const docsConfigContents = readFileSync(docsConfigPath, 'utf8');
const docsThemeContents = readFileSync(docsThemePath, 'utf8');
test('docs site keeps docs hostname while sending plausible events to subminer.moe via worker.subminer.moe capture endpoint', () => {
expect(docsConfigContents).toContain("hostname: 'https://docs.subminer.moe'");
expect(docsConfigContents).toContain("const DOCS_HOSTNAME = 'https://docs.subminer.moe'");
expect(docsConfigContents).toContain('hostname: DOCS_HOSTNAME');
expect(docsThemeContents).toContain("const PLAUSIBLE_DOMAIN = 'subminer.moe'");
expect(docsThemeContents).toContain('const PLAUSIBLE_ENABLED_HOSTNAMES = new Set([');
expect(docsThemeContents).toContain("'docs.subminer.moe'");
+40
View File
@@ -0,0 +1,40 @@
import { expect, test } from 'bun:test';
import type { TransformContext } from 'vitepress';
import docsConfig from './.vitepress/config';
function makeTransformContext(page: string): TransformContext {
return {
page,
siteConfig: {} as TransformContext['siteConfig'],
siteData: {} as TransformContext['siteData'],
pageData: {} as TransformContext['pageData'],
title: 'SubMiner',
description: 'SubMiner docs',
head: [],
content: '',
assets: [],
};
}
test('docs pages emit stable self-referential canonical URLs', async () => {
const rootHead = await docsConfig.transformHead?.(makeTransformContext('index.md'));
const usageHead = await docsConfig.transformHead?.(makeTransformContext('usage.md'));
expect(rootHead).toContainEqual([
'link',
{ rel: 'canonical', href: 'https://docs.subminer.moe/' },
]);
expect(usageHead).toContainEqual([
'link',
{ rel: 'canonical', href: 'https://docs.subminer.moe/usage' },
]);
expect(JSON.stringify(rootHead).toLowerCase()).not.toContain('noindex');
});
test('docs sitemap excludes duplicate README page from indexable URLs', async () => {
const items = [{ url: '' }, { url: 'README' }, { url: 'usage' }];
const transformedItems = await docsConfig.sitemap?.transformItems?.(items);
expect(transformedItems?.map((item) => item.url)).toEqual(['', 'usage']);
});