Skip to content

Commit 71d57e2

Browse files
feat: introduce visibility check for documents and streamline description
1 parent 4dd4b7a commit 71d57e2

File tree

3 files changed

+62
-56
lines changed

3 files changed

+62
-56
lines changed

src/plugins/llms/generator.ts

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ function getGeneratedIndexSlug(item: SidebarItem): string | undefined {
133133
return slug.startsWith('/') ? slug : `/${slug}`;
134134
}
135135

136+
/** Check if a doc is visible (not unlisted and not a draft) */
137+
function isVisibleDoc(doc?: DocInfo): doc is DocInfo {
138+
return Boolean(doc && !doc.unlisted && !doc.draft);
139+
}
140+
136141
/** Remove trailing colon from descriptions */
137142
function sanitizeDescription(desc: string): string {
138143
return desc.replace(/:\s*$/, '').trim();
@@ -278,7 +283,7 @@ function buildCategoryEntry(
278283
lines.push('', description);
279284
}
280285

281-
if (linkedDoc && !linkedDoc.unlisted && !linkedDoc.draft && ctx.siteDir) {
286+
if (isVisibleDoc(linkedDoc) && ctx.siteDir) {
282287
const tree = getCachedTree(linkedDoc, ctx.siteDir);
283288
if (tree) {
284289
const stripTitle =
@@ -317,25 +322,23 @@ function buildCategoryEntry(
317322
ensureBlankLine(lines);
318323

319324
// Add overview link if available
320-
const isVisible = linkedDoc && !linkedDoc.unlisted && !linkedDoc.draft;
321-
let overviewUrl = '';
322-
if (isVisible) {
323-
overviewUrl = urlForPermalink(linkedDoc.permalink, ctx.siteBase);
324-
} else if (generatedSlug) {
325-
overviewUrl = urlForPermalink(generatedSlug, ctx.siteBase);
326-
}
325+
const hasVisibleLinkedDoc = isVisibleDoc(linkedDoc);
326+
const overviewUrl = hasVisibleLinkedDoc
327+
? urlForPermalink(linkedDoc.permalink, ctx.siteBase)
328+
: generatedSlug
329+
? urlForPermalink(generatedSlug, ctx.siteBase)
330+
: '';
327331

328332
if (overviewUrl) {
329-
let overviewDesc = '';
330-
if (
331-
isVisible &&
332-
linkedDoc.description &&
333-
normalizeTitle(linkedDoc.description) !== normalizeTitle(description)
334-
) {
335-
overviewDesc = `: ${sanitizeDescription(
336-
truncate(linkedDoc.description, ctx.maxDescriptionLength),
337-
)}`;
338-
}
333+
const linkedDocDesc = hasVisibleLinkedDoc ? linkedDoc.description : '';
334+
const showDesc =
335+
linkedDocDesc &&
336+
normalizeTitle(linkedDocDesc) !== normalizeTitle(description);
337+
const overviewDesc = showDesc
338+
? `: ${sanitizeDescription(
339+
truncate(linkedDocDesc, ctx.maxDescriptionLength),
340+
)}`
341+
: '';
339342
lines.push(
340343
`- [Overview](${toMarkdownUrl(
341344
overviewUrl,
@@ -390,7 +393,7 @@ function buildSidebarItems(
390393
switch (normalized.type) {
391394
case 'doc': {
392395
const doc = ctx.docsById.get(normalized.id ?? normalized.docId ?? '');
393-
if (!doc || doc.unlisted || doc.draft) continue;
396+
if (!isVisibleDoc(doc)) continue;
394397
lines.push(
395398
...buildDocEntry(doc, ctx, normalized.label, normalized.href),
396399
);
@@ -430,7 +433,7 @@ function collectFlatDocEntries(
430433

431434
if (normalized.type === 'doc') {
432435
const doc = docsById.get(normalized.id ?? normalized.docId ?? '');
433-
if (doc && !doc.unlisted && !doc.draft) {
436+
if (isVisibleDoc(doc)) {
434437
entries.push({
435438
breadcrumb,
436439
doc,
@@ -442,7 +445,7 @@ function collectFlatDocEntries(
442445
const linkedDoc = getLinkedDoc(item, docsById);
443446

444447
// Add linked doc for category (if exists)
445-
if (linkedDoc && !linkedDoc.unlisted && !linkedDoc.draft) {
448+
if (isVisibleDoc(linkedDoc)) {
446449
entries.push({
447450
breadcrumb,
448451
doc: linkedDoc,
@@ -456,9 +459,8 @@ function collectFlatDocEntries(
456459
entries.push(
457460
...collectFlatDocEntries(childItems, docsById, childBreadcrumb),
458461
);
459-
} else if (normalized.type === 'link') {
460-
// External links are skipped in flat mode (no doc to reference)
461462
}
463+
// Note: 'link' type items are skipped in flat mode (external links have no doc)
462464
}
463465

464466
return entries;
@@ -523,7 +525,7 @@ async function writeLlmsTxt(params: GeneratorParams): Promise<void> {
523525
}
524526

525527
const otherDocs = Array.from(params.docsById.values())
526-
.filter((d) => !d.unlisted && !d.draft && !sidebarDocIds.has(d.id))
528+
.filter((d) => isVisibleDoc(d) && !sidebarDocIds.has(d.id))
527529
.sort((a, b) => a.permalink.localeCompare(b.permalink));
528530

529531
if (otherDocs.length) {
@@ -597,9 +599,7 @@ async function writeLlmsFullTxt(params: GeneratorParams): Promise<void> {
597599
firstItem.id ?? firstItem.docId ?? '',
598600
);
599601
if (
600-
firstDoc &&
601-
!firstDoc.unlisted &&
602-
!firstDoc.draft &&
602+
isVisibleDoc(firstDoc) &&
603603
normalizeTitle(firstDoc.title) === normalizeTitle(sectionTitle)
604604
) {
605605
lines.push(...buildDocEntry(firstDoc, {...ctx, headingLevel: 1}));
@@ -611,7 +611,7 @@ async function writeLlmsFullTxt(params: GeneratorParams): Promise<void> {
611611
const linkedDoc = getLinkedDoc(items[0], params.docsById);
612612
const desc = getCategoryDescription(items[0], linkedDoc).trim();
613613
if (!linkedDoc && desc) lines.push('', desc);
614-
if (linkedDoc && !linkedDoc.unlisted && !linkedDoc.draft) {
614+
if (isVisibleDoc(linkedDoc)) {
615615
lines.push(...buildDocEntry(linkedDoc, {...ctx, headingLevel: 1}));
616616
}
617617
remainingItems = filterOutLinkedDoc(firstItem.items ?? [], linkedDoc);
@@ -632,7 +632,7 @@ async function writeLlmsFullTxt(params: GeneratorParams): Promise<void> {
632632
}
633633

634634
const otherDocs = Array.from(params.docsById.values())
635-
.filter((d) => !d.unlisted && !d.draft && !sidebarDocIds.has(d.id))
635+
.filter((d) => isVisibleDoc(d) && !sidebarDocIds.has(d.id))
636636
.sort((a, b) => a.permalink.localeCompare(b.permalink));
637637

638638
if (otherDocs.length) {
@@ -670,7 +670,7 @@ async function writeLlmsFullTxt(params: GeneratorParams): Promise<void> {
670670

671671
async function writePerPageMarkdown(params: GeneratorParams): Promise<number> {
672672
const docs = Array.from(params.docsById.values())
673-
.filter((d) => !d.unlisted && !d.draft)
673+
.filter(isVisibleDoc)
674674
.sort((a, b) => a.permalink.localeCompare(b.permalink));
675675

676676
let count = 0;
@@ -696,12 +696,20 @@ async function writePerPageMarkdown(params: GeneratorParams): Promise<number> {
696696
const source = params.siteBase
697697
? `Source: ${joinUrl(params.siteBase, doc.permalink)}`
698698
: '';
699+
const llmsTxtUrl = params.siteBase
700+
? joinUrl(params.siteBase, params.llmsTxtFilename)
701+
: '';
702+
const footer = llmsTxtUrl
703+
? `---\n\n*To find navigation and other pages in this documentation, fetch the llms.txt file at: ${llmsTxtUrl}*`
704+
: '';
699705
const content = joinLines([
700706
`# ${doc.title}`,
701707
'',
702708
source,
703709
source ? '' : '',
704710
body.trim(),
711+
'',
712+
footer,
705713
]);
706714

707715
await fsp.writeFile(filePath, content, 'utf8');
@@ -721,9 +729,7 @@ interface GeneratedIndexPage {
721729
async function writeGeneratedIndexPages(
722730
params: GeneratorParams,
723731
): Promise<number> {
724-
const docs = Array.from(params.docsById.values()).filter(
725-
(d) => !d.unlisted && !d.draft,
726-
);
732+
const docs = Array.from(params.docsById.values()).filter(isVisibleDoc);
727733
const existingPermalinks = new Set(
728734
docs.map((d) => d.permalink.replace(/\/+$/, '') || '/'),
729735
);
@@ -742,20 +748,15 @@ async function writeGeneratedIndexPages(
742748
const doc = params.docsById.get(
743749
normalized.id ?? normalized.docId ?? '',
744750
);
745-
if (doc && !doc.unlisted && !doc.draft && activePage) {
751+
if (isVisibleDoc(doc) && activePage) {
746752
const permalink = doc.permalink.replace(/\/+$/, '') || '/';
747753
if (!activePage.childPermalinks.includes(permalink)) {
748754
activePage.childPermalinks.push(permalink);
749755
}
750756
}
751757
} else if (normalized.type === 'category') {
752758
const linkedDoc = getLinkedDoc(item, params.docsById);
753-
if (
754-
linkedDoc &&
755-
!linkedDoc.unlisted &&
756-
!linkedDoc.draft &&
757-
activePage
758-
) {
759+
if (isVisibleDoc(linkedDoc) && activePage) {
759760
const permalink = linkedDoc.permalink.replace(/\/+$/, '') || '/';
760761
if (!activePage.childPermalinks.includes(permalink)) {
761762
activePage.childPermalinks.push(permalink);
@@ -838,7 +839,7 @@ export async function generateLlmsExports(
838839
): Promise<void> {
839840
// Get all visible docs
840841
const allDocs = Array.from(params.docsById.values())
841-
.filter((d) => !d.unlisted && !d.draft)
842+
.filter(isVisibleDoc)
842843
.sort((a, b) => a.permalink.localeCompare(b.permalink));
843844

844845
// Check for missing trees and log warnings (graceful handling)

src/plugins/llms/index.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,23 @@ function extractDocs(version: DocsVersion): DocMetadata[] {
6464
return [];
6565
}
6666

67+
function getFrontMatterDescription(metadata: DocMetadata): string {
68+
if (!isObject(metadata.frontMatter)) return '';
69+
const desc = metadata.frontMatter.description;
70+
return typeof desc === 'string' ? desc.trim() : '';
71+
}
72+
6773
function extractDescription(metadata: DocMetadata): {
6874
description: string;
6975
fromFrontMatter: boolean;
7076
} {
71-
const frontMatterDesc =
72-
isObject(metadata.frontMatter) &&
73-
typeof metadata.frontMatter.description === 'string'
74-
? String(metadata.frontMatter.description).trim()
75-
: '';
76-
77+
const frontMatterDesc = getFrontMatterDescription(metadata);
7778
if (frontMatterDesc) {
7879
return {description: frontMatterDesc, fromFrontMatter: true};
7980
}
8081

8182
const metadataDesc =
8283
typeof metadata.description === 'string' ? metadata.description.trim() : '';
83-
8484
return {description: metadataDesc, fromFrontMatter: false};
8585
}
8686

@@ -137,14 +137,16 @@ export default function llmsPlugin(
137137
if (!id || !title || !permalink) continue;
138138

139139
const {description, fromFrontMatter} = extractDescription(metadata);
140+
const source =
141+
typeof metadata.source === 'string' ? metadata.source : undefined;
142+
140143
const info: DocInfo = {
141144
id,
142145
title,
143146
description: description || undefined,
144147
descriptionFromFrontMatter: fromFrontMatter,
145148
permalink,
146-
source:
147-
typeof metadata.source === 'string' ? metadata.source : undefined,
149+
source,
148150
unlisted: Boolean(metadata.unlisted),
149151
draft: Boolean(metadata.draft),
150152
};

src/remark/llms-capture.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ function resolveSiteDir(vfile: VFile, options?: Options): string {
3737
return process.cwd();
3838
}
3939

40+
function cleanupTempFile(tempFile: string): void {
41+
try {
42+
fs.rmSync(tempFile, {force: true});
43+
} catch {
44+
// Ignore cleanup failures
45+
}
46+
}
47+
4048
function atomicWriteJson(filename: string, payload: unknown): void {
4149
const dir = path.dirname(filename);
4250
const base = path.basename(filename);
@@ -52,12 +60,7 @@ function atomicWriteJson(filename: string, payload: unknown): void {
5260
fs.rmSync(filename, {force: true});
5361
fs.renameSync(tempFile, filename);
5462
} catch {
55-
// Clean up temp file on failure
56-
try {
57-
fs.rmSync(tempFile, {force: true});
58-
} catch {
59-
// Ignore cleanup failures
60-
}
63+
cleanupTempFile(tempFile);
6164
throw error;
6265
}
6366
}

0 commit comments

Comments
 (0)