@@ -28,12 +28,30 @@ export type SnootyPageEntry = SnootyManifestEntry & {
28
28
data : SnootyPageData ;
29
29
} ;
30
30
31
+ export interface SnootyTocEntry {
32
+ title : {
33
+ type : "text" ;
34
+ position : {
35
+ start : {
36
+ line : number ;
37
+ } ;
38
+ } ;
39
+ value : string ;
40
+ } [ ] ;
41
+ slug ?: string ;
42
+ url ?: string ;
43
+ children : SnootyTocEntry [ ] ;
44
+ options ?: {
45
+ drawer ?: boolean ;
46
+ } ;
47
+ }
48
+
31
49
/**
32
50
Represents metadata in a Snooty manifest file.
33
51
*/
34
52
export type SnootyMetadataEntry = SnootyManifestEntry & {
35
53
type : "metadata" ;
36
- data : { title ?: string } ;
54
+ data : { title ?: string ; toctree : SnootyTocEntry ; toctreeOrder : string [ ] } ;
37
55
} ;
38
56
39
57
/**
@@ -202,6 +220,7 @@ export const makeSnootyDataSource = ({
202
220
const linePromises : Promise < void > [ ] = [ ] ;
203
221
const pages : Page [ ] = [ ] ;
204
222
let siteTitle : string | undefined = undefined ;
223
+ const toc : string [ ] = [ ] ;
205
224
await new Promise < void > ( ( resolve , reject ) => {
206
225
stream . on ( "line" , async ( line ) => {
207
226
const entry = JSON . parse ( line ) as SnootyManifestEntry ;
@@ -227,6 +246,7 @@ export const makeSnootyDataSource = ({
227
246
...links ,
228
247
baseUrl : branchUrl ,
229
248
} ,
249
+ toc,
230
250
} ) ;
231
251
if ( page !== undefined ) {
232
252
pages . push ( page ) ;
@@ -249,6 +269,18 @@ export const makeSnootyDataSource = ({
249
269
case "metadata" : {
250
270
const { data } = entry as SnootyMetadataEntry ;
251
271
siteTitle = data . title ;
272
+ const visitedUrls = new Set < string > ( ) ;
273
+ const tocUrls = data . toctreeOrder
274
+ . filter ( ( slug ) => {
275
+ const url = makeUrl ( slug , branchUrl ) ;
276
+ if ( visitedUrls . has ( url ) ) {
277
+ return false ;
278
+ }
279
+ visitedUrls . add ( url ) ;
280
+ return true ;
281
+ } )
282
+ . map ( ( slug ) => makeUrl ( slug , branchUrl ) ) ;
283
+ toc . push ( ...tocUrls ) ;
252
284
return ;
253
285
}
254
286
case "timestamp" :
@@ -344,6 +376,7 @@ export const handlePage = async (
344
376
productName,
345
377
version,
346
378
links,
379
+ toc,
347
380
} : {
348
381
sourceName : string ;
349
382
baseUrl : string ;
@@ -354,6 +387,7 @@ export const handlePage = async (
354
387
isCurrent : boolean ;
355
388
} ;
356
389
links ?: RenderLinks ;
390
+ toc : string [ ] ;
357
391
}
358
392
) : Promise < Page | undefined > => {
359
393
// Strip first three path segments - according to Snooty team, they'll always
@@ -373,11 +407,8 @@ export const handlePage = async (
373
407
let body = "" ;
374
408
let title : string | undefined ;
375
409
let format : PageFormat ;
376
- const baseUrlTrailingSlash = baseUrl . replace ( / \/ ? $ / , "/" ) ;
377
- const url = new URL ( pagePath , baseUrlTrailingSlash ) . href . replace (
378
- / \/ ? $ / , // Add trailing slash
379
- "/"
380
- ) ;
410
+ const url = makeUrl ( pagePath , baseUrl ) ;
411
+
381
412
if ( page . ast . options ?. template === "openapi" ) {
382
413
format = "openapi-yaml" ;
383
414
body = await snootyAstToOpenApiSpec ( page . ast ) ;
@@ -395,6 +426,9 @@ export const handlePage = async (
395
426
return ;
396
427
}
397
428
429
+ const maybeTocIndex = toc . findIndex ( ( tocUrl ) => tocUrl === url ) ;
430
+ const tocIndex = maybeTocIndex === - 1 ? undefined : maybeTocIndex ;
431
+
398
432
return {
399
433
url,
400
434
sourceName,
@@ -403,10 +437,28 @@ export const handlePage = async (
403
437
format,
404
438
sourceType : "tech-docs" ,
405
439
metadata : {
406
- page : pageMetadata ,
440
+ page : { ... pageMetadata , tocIndex } ,
407
441
tags,
408
442
productName,
409
443
version,
410
444
} ,
411
445
} ;
412
446
} ;
447
+
448
+ function makeUrl ( pagePath : string , baseUrl : string ) : string {
449
+ // Ensure trailing slash for baseUrl
450
+ const baseUrlTrailingSlash = baseUrl . replace ( / \/ ? $ / , "/" ) ;
451
+
452
+ // Handle empty pagePath or root path
453
+ if ( ! pagePath || pagePath === "/" ) {
454
+ return baseUrlTrailingSlash ;
455
+ }
456
+
457
+ // For non-empty paths, remove leading slash and ensure trailing slash
458
+ const cleanPagePath = pagePath
459
+ . replace ( / ^ \/ / , "" ) // Remove leading slash
460
+ . replace ( / \/ ? $ / , "/" ) ; // Ensure trailing slash
461
+
462
+ // Concatenate the base URL with the clean page path
463
+ return baseUrlTrailingSlash + cleanPagePath ;
464
+ }
0 commit comments