Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion scripts/check-lonely-pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,40 @@ data.pages.forEach((row, i) => {
}
});

// ---- 3) Built HTML route: the browsable Special:LonelyPages page ------------
//
// The human-readable Special:LonelyPages route is the canonical browsable partner
// to lonelypages.json. Keep the built page aligned with the JSON route: same
// heading, same empty state, and the expected article + backlinks links for each
// orphan, all ordered by the same shared buildLonelyPages ranking.
const distHtml = path.join(wikiDir, 'special', 'lonelypages', 'index.html');
assert.ok(fs.existsSync(distHtml), 'dist/wiki/special/lonelypages/index.html not found; run the build first');
const html = fs.readFileSync(distHtml, 'utf8');

assert.match(
html,
/<h1[^>]*class="firstHeading"[^>]*>Lonely pages<\/h1>/,
'lonelypages HTML page must render the Lonely pages heading',
);
if (expected.length === 0) {
assert.match(
html,
/No lonely pages right now\./,
'lonelypages HTML page must render the empty-state copy when the report is empty',
);
} else {
for (const entry of expected) {
assert.ok(
html.includes(`href="/wiki/${entry.slug}/"`),
`lonelypages HTML page must link to the orphaned article /wiki/${entry.slug}/`,
);
assert.ok(
html.includes(`/wiki/${entry.slug}/backlinks/`),
`lonelypages HTML page must link to the orphan's backlinks page /wiki/${entry.slug}/backlinks/`,
);
}
}

console.log(
`Lonely pages check passed (${data.pages.length} orphaned pages from the built endpoint match the link graph; lonely + most-linked partition all ${Object.keys(realTitleBySlug).length} published articles)`,
`Lonely pages check passed (${data.pages.length} orphaned pages from the built endpoint match the link graph; lonely + most-linked partition all ${Object.keys(realTitleBySlug).length} published articles; HTML route aligned)`,
);
2 changes: 1 addition & 1 deletion scripts/check-sitemap.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ assert.ok(articleUrls > 0, 'no article URLs found in the sitemap');

// The special content overview pages are canonical, indexable routes and must
// stay in the sitemap so discovery does not regress when a new one is added.
for (const special of ['allpages', 'categories', 'mostlinkedpages', 'recentchanges', 'statistics', 'subnets', 'wantedpages']) {
for (const special of ['allpages', 'categories', 'lonelypages', 'mostlinkedpages', 'recentchanges', 'statistics', 'subnets', 'wantedpages']) {
assert.ok(
xml.includes(`/wiki/special/${special}/</loc>`),
`sitemap must include the /wiki/special/${special}/ page`,
Expand Down
7 changes: 7 additions & 0 deletions src/lib/share-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ const specialPages: SpecialPageMeta[] = [
description: 'Browse all topics in the Taopedia Bittensor knowledge base.',
label: 'Topic directory',
},
{
name: 'lonelypages',
title: 'Lonely pages',
description:
'Orphaned articles across the Taopedia Bittensor knowledge base — published pages that no other article links to.',
label: 'Orphaned-page report',
},
{
name: 'mostlinkedpages',
title: 'Most linked pages',
Expand Down
1 change: 1 addition & 0 deletions src/pages/sitemap.xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const GET: APIRoute = ({ site }) => {
{ path: '/', lastmod: '' },
{ path: '/wiki/special/allpages/', lastmod: '' },
{ path: '/wiki/special/categories/', lastmod: '' },
{ path: '/wiki/special/lonelypages/', lastmod: '' },
{ path: '/wiki/special/mostlinkedpages/', lastmod: '' },
{ path: '/wiki/special/recentchanges/', lastmod: '' },
{ path: '/wiki/special/statistics/', lastmod: '' },
Expand Down
75 changes: 75 additions & 0 deletions src/pages/wiki/special/lonelypages.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
import WikiLayout from '../../../layouts/WikiLayout.astro';
import { publishedTitleBySlug } from '../../../lib/article-metadata';
import { buildLonelyPages } from '../../../../scripts/lonely-pages.js';

// Special:LonelyPages — the browsable partner to lonelypages.json: published
// articles that NO other published article links to (zero inbound links), the
// exact complement of Special:MostLinkedPages. Both surfaces derive their
// selection and ordering from the SAME shared builder (scripts/lonely-pages.js)
// over the SAME public/data/backlinks.json the machine-readable endpoint reads,
// so the HTML page and JSON companion stay in lockstep. Loaded via glob so a
// missing link graph (e.g. a dev run before the build) degrades to an empty list.
const titleBySlug = publishedTitleBySlug();

const backlinksModules = import.meta.glob('../../../../public/data/backlinks.json', { eager: true }) as Record<
string,
{ default?: Record<string, Array<{ from: string }>> }
>;
const backlinksData = Object.values(backlinksModules)[0]?.default ?? {};

const lonelyPages = buildLonelyPages({ titleBySlug, backlinks: backlinksData });
---

<WikiLayout
title="Lonely pages"
description="Orphaned articles across the Taopedia Bittensor knowledge base — published pages that no other article links to."
>
<div class="mw-body-content">
<h1 class="firstHeading">Lonely pages</h1>

<div class="mw-parser-output">
<p>
Orphaned articles that no other published article links to. Wiring these into the link graph
from related pages makes them reachable while browsing, not just from search.
</p>

{lonelyPages.length === 0 ? (
<p>No lonely pages right now. Every published article has at least one incoming link from another article.</p>
) : (
<ol class="mw-lonelypages">
{lonelyPages.map((entry) => (
<li class="mw-lp-row">
<a class="mw-lp-title" href={`/wiki/${entry.slug}/`}>{entry.title}</a>
<a class="mw-lp-links" href={`/wiki/${entry.slug}/backlinks/`}>No incoming links</a>
</li>
))}
</ol>
)}
</div>
</div>
</WikiLayout>

<style>
.mw-lonelypages {
margin: 16px 0;
padding-left: 32px;
}
.mw-lp-row {
display: flex;
align-items: baseline;
gap: 12px;
padding: 6px 0;
border-bottom: 1px solid var(--border-color-subtle);
font-size: 14px;
}
.mw-lp-title {
font-weight: 600;
}
.mw-lp-links {
margin-left: auto;
font-size: 13px;
white-space: nowrap;
color: var(--color-base-subtle);
}
</style>
Loading