@@ -12,6 +12,7 @@ import { logger } from '@rspress/shared/logger';
1212import picocolors from 'picocolors' ;
1313import { PUBLIC_DIR } from '../constants' ;
1414import { absolutePathToRoutePath , addRoutePrefix } from '../route/RoutePage' ;
15+ import { RouteService } from '../route/RouteService' ;
1516import { createError } from '../utils' ;
1617import type {
1718 CustomLinkMeta ,
@@ -43,7 +44,16 @@ async function fsDirToMetaItems(
4344) : Promise < SideMetaItem [ ] > {
4445 let subItems : string [ ] ;
4546 try {
46- subItems = await readdir ( workDir ) ;
47+ subItems = ( await readdir ( workDir ) ) . sort ( ( a , b ) => {
48+ // 1. index.md or index.mdx should be placed at the top of the sidebar
49+ const aIsIndex = a . replace ( / \. [ ^ / . ] + $ / , '' ) === 'index' ;
50+ const bIsIndex = b . replace ( / \. [ ^ / . ] + $ / , '' ) === 'index' ;
51+ if ( aIsIndex !== bIsIndex ) {
52+ return aIsIndex ? - 1 : 1 ;
53+ }
54+ // 2. Dictionary order
55+ return a . localeCompare ( b ) ;
56+ } ) ;
4757 } catch ( e ) {
4858 const metaFilePath = join ( workDir , '_meta.json' ) ;
4959
@@ -100,6 +110,7 @@ async function metaItemToSidebarItem(
100110) : Promise <
101111 | ( SidebarItem | SidebarGroup | SidebarDivider | SidebarSectionHeader )
102112 | ( SidebarItem | SidebarGroup | SidebarDivider | SidebarSectionHeader ) [ ]
113+ | null
103114> {
104115 if ( typeof metaItem === 'string' ) {
105116 return metaFileItemToSidebarItem (
@@ -123,7 +134,7 @@ async function metaItemToSidebarItem(
123134 }
124135
125136 if ( type === 'dir' ) {
126- return metaDirItemToSidebarItem (
137+ const group = await metaDirItemToSidebarItem (
127138 metaItem ,
128139 workDir ,
129140 docsDir ,
@@ -132,17 +143,26 @@ async function metaItemToSidebarItem(
132143 mdFileSet ,
133144 false ,
134145 ) ;
146+ if ( group . items . length === 0 && ! group . link ) {
147+ return null ;
148+ }
149+ return group ;
135150 }
136151
137152 if ( type === 'dir-section-header' ) {
138- return metaDirSectionHeaderItemToSidebarItem (
153+ const items = await metaDirSectionHeaderItemToSidebarItem (
139154 metaItem ,
140155 workDir ,
141156 docsDir ,
142157 extensions ,
143158 metaFileSet ,
144159 mdFileSet ,
145160 ) ;
161+ // If only the section header remains (no child items), prune it
162+ if ( items . length <= 1 ) {
163+ return null ;
164+ }
165+ return items ;
146166 }
147167
148168 if ( type === 'custom-link' ) {
@@ -184,7 +204,7 @@ async function metaFileItemToSidebarItem(
184204 docsDir : string ,
185205 extensions : string [ ] ,
186206 mdFileSet : Set < string > ,
187- ) : Promise < SidebarItem > {
207+ ) : Promise < SidebarItem | null > {
188208 let metaItem : FileSideMeta | null = null ;
189209 if ( typeof metaItemRaw === 'string' ) {
190210 metaItem = {
@@ -218,6 +238,10 @@ async function metaFileItemToSidebarItem(
218238 }
219239
220240 const link = absolutePathToRoutePath ( absolutePathWithExt , docsDir ) ;
241+ const routeService = RouteService . getInstance ( ) ;
242+ if ( routeService ?. isExistRoute && ! routeService . isExistRoute ( link ) ) {
243+ return null ;
244+ }
221245 const info = await extractInfoFromFrontmatterWithAbsolutePath (
222246 absolutePathWithExt ,
223247 docsDir ,
@@ -294,7 +318,12 @@ async function metaDirItemToSidebarItem(
294318 ) ,
295319 ) ,
296320 ) ;
297- return items . flat ( ) ;
321+ return items . flat ( ) . filter ( Boolean ) as (
322+ | SidebarItem
323+ | SidebarGroup
324+ | SidebarDivider
325+ | SidebarSectionHeader
326+ ) [ ] ;
298327 }
299328
300329 try {
@@ -310,6 +339,10 @@ async function metaDirItemToSidebarItem(
310339 mdFileSet ,
311340 ) ;
312341
342+ if ( ! sameNameFile ) {
343+ throw new Error ( `Excluded route: ${ name } ` ) ;
344+ }
345+
313346 const { link, text, _fileKey, context, overviewHeaders, tag } =
314347 sameNameFile ;
315348 return {
@@ -357,6 +390,18 @@ async function metaDirItemToSidebarItem(
357390 mdFileSet ,
358391 ) ;
359392
393+ if ( ! indexFile ) {
394+ return {
395+ text : label || name ,
396+ collapsible,
397+ collapsed,
398+ items : await getItems ( ) ,
399+ overviewHeaders : metaJsonOverviewHeaders ,
400+ context : metaJsonContext ,
401+ _fileKey : getFileKey ( dirAbsolutePath , docsDir ) ,
402+ } satisfies SidebarGroup ;
403+ }
404+
360405 const { link, text, _fileKey, context, overviewHeaders, tag } = indexFile ;
361406 return {
362407 text : label || text || name ,
0 commit comments