11import path from 'node:path' ;
22import type { RsbuildPlugin } from '@rsbuild/core' ;
3- import { matchSidebar } from '@rspress/runtime ' ;
3+ import { matchSidebar } from '@rspress/shared ' ;
44import type {
55 Nav ,
66 PageIndexInfo ,
@@ -25,100 +25,108 @@ interface rsbuildPluginLlmsOptions extends Options {
2525 routes : RouteMeta [ ] ;
2626 sidebar : Sidebar ;
2727 nav : Nav ;
28+ base : string ;
29+ output : Required < Options [ 'output' ] > ;
2830}
2931
3032const rsbuildPluginLlms = ( {
3133 pageDataList,
3234 routes,
3335 sidebar,
36+ base,
3437 nav,
3538 output,
3639} : rsbuildPluginLlmsOptions ) : RsbuildPlugin => ( {
3740 name : 'rsbuild-plugin-llms' ,
3841 async setup ( api ) {
3942 const { llmsTxt, mdFiles, llmsFullTxt } = output ;
4043
41- const docDirectory = api . context . rootPath ;
44+ api . onBeforeBuild ( async ( ) => {
45+ const docDirectory = api . context . rootPath ;
4246
43- const mdContents : Record < string , string > = { } ;
47+ const mdContents : Record < string , string > = { } ;
4448
45- const newPageDataList = mergeRouteMetaWithPageData ( routes , pageDataList ) ;
49+ const newPageDataList = mergeRouteMetaWithPageData ( routes , pageDataList ) ;
4650
47- // currently we do not support multi version
48- const navList : [ string , NavItemWithLink ] [ ] = Array . isArray ( nav )
49- ? ( ( nav as NavItemWithLink [ ] )
50- . map ( nav => {
51- if ( nav . activeMatch ) {
52- return [ nav . activeMatch , nav ] ;
53- }
54- return undefined ;
55- } )
56- . filter ( Boolean ) as [ string , NavItemWithLink ] [ ] )
57- : [ ] ;
58-
59- const activeMatches : string [ ] = navList . map ( ( [ activeMatch ] ) => activeMatch ) ;
60- const obj : Record < string , PageIndexInfo [ ] > = Object . fromEntries (
61- activeMatches . map ( activeMatch => [ activeMatch , [ ] ] ) ,
62- ) ;
63- obj . others = [ ] ;
64-
65- newPageDataList . forEach ( pageData => {
66- const { routePath } = pageData ;
67- const activeMatch = activeMatches . find ( key =>
68- matchSidebar ( key , routePath ) ,
69- ) ;
70- if ( activeMatch ) {
71- obj [ activeMatch ] . push ( pageData ) ;
72- } else {
73- obj . others . push ( pageData ) ;
74- }
75- } ) ;
51+ // currently we do not support multi version
52+ const navList : [ string , NavItemWithLink ] [ ] = Array . isArray ( nav )
53+ ? ( ( nav as NavItemWithLink [ ] )
54+ . map ( nav => {
55+ if ( nav . activeMatch ) {
56+ return [ nav . activeMatch , nav ] ;
57+ }
58+ return undefined ;
59+ } )
60+ . filter ( Boolean ) as [ string , NavItemWithLink ] [ ] )
61+ : [ ] ;
7662
77- if ( llmsTxt ) {
78- const llmsTxtContent = generateLlmsTxt ( obj , navList ) ;
79- api . processAssets (
80- { targets : [ 'web' ] , stage : 'additional' } ,
81- async ( { compilation, sources } ) => {
82- const source = new sources . RawSource ( llmsTxtContent ) ;
83- compilation . emitAsset ( 'llms.txt' , source ) ;
84- } ,
63+ const activeMatches : string [ ] = navList . map (
64+ ( [ activeMatch ] ) => activeMatch ,
8565 ) ;
86- }
87-
88- await Promise . all (
89- newPageDataList . values ( ) . map ( async pageData => {
90- const content = pageData . flattenContent ?? pageData . content ;
91- const filepath = pageData . _filepath ;
92- const isMD = path . extname ( filepath ) . slice ( 1 ) === 'md' ;
93- let mdContent : string | Buffer ;
94- if ( isMD ) {
95- mdContent = content ;
66+ const obj : Record < string , PageIndexInfo [ ] > = Object . fromEntries (
67+ activeMatches . map ( activeMatch => [ activeMatch , [ ] ] ) ,
68+ ) ;
69+ obj . others = [ ] ;
70+
71+ newPageDataList . forEach ( pageData => {
72+ const { routePath } = pageData ;
73+ const activeMatch = activeMatches . find ( key =>
74+ matchSidebar ( key , routePath , base ) ,
75+ ) ;
76+ if ( activeMatch ) {
77+ obj [ activeMatch ] . push ( pageData ) ;
9678 } else {
97- mdContent = (
98- await mdxToMd ( content , filepath , docDirectory )
99- ) . toString ( ) ;
79+ obj . others . push ( pageData ) ;
10080 }
101- const outFilePath = `${
102- pageData . routePath . endsWith ( '/' )
103- ? `${ pageData . routePath } index`
104- : pageData . routePath
105- } .md`;
106- mdContents [ outFilePath ] = mdContent . toString ( ) ;
107- } ) ?? [ ] ,
108- ) ;
109-
110- api . processAssets (
111- { targets : [ 'web' ] , stage : 'additional' } ,
112- async ( { compilation, sources } ) => {
113- if ( mdFiles ) {
114- Object . entries ( mdContents ) . forEach ( ( [ outFilePath , content ] ) => {
115- const source = new sources . RawSource ( content ) ;
116- console . log ( `.${ outFilePath } ` ) ;
117- compilation . emitAsset ( `.${ outFilePath } ` , source ) ;
118- } ) ;
119- }
120- } ,
121- ) ;
81+ } ) ;
82+
83+ if ( llmsTxt ) {
84+ const llmsTxtContent = generateLlmsTxt ( obj , navList ) ;
85+ api . processAssets (
86+ { targets : [ 'web' ] , stage : 'additional' } ,
87+ async ( { compilation, sources } ) => {
88+ const source = new sources . RawSource ( llmsTxtContent ) ;
89+ compilation . emitAsset ( 'llms.txt' , source ) ;
90+ } ,
91+ ) ;
92+ }
93+ if ( mdFiles ) {
94+ await Promise . all (
95+ newPageDataList . values ( ) . map ( async pageData => {
96+ const content = pageData . flattenContent ?? pageData . content ;
97+ const filepath = pageData . _filepath ;
98+ const isMD = path . extname ( filepath ) . slice ( 1 ) === 'md' ;
99+ let mdContent : string | Buffer ;
100+ if ( isMD ) {
101+ mdContent = content ;
102+ } else {
103+ mdContent = (
104+ await mdxToMd ( content , filepath , docDirectory )
105+ ) . toString ( ) ;
106+ }
107+ const outFilePath = `${
108+ pageData . routePath . endsWith ( '/' )
109+ ? `${ pageData . routePath } index`
110+ : pageData . routePath
111+ } .md`;
112+ mdContents [ outFilePath ] = mdContent . toString ( ) ;
113+ } ) ?? [ ] ,
114+ ) ;
115+
116+ api . processAssets (
117+ { targets : [ 'web' ] , stage : 'additional' } ,
118+ async ( { compilation, sources } ) => {
119+ if ( mdFiles ) {
120+ Object . entries ( mdContents ) . forEach ( ( [ outFilePath , content ] ) => {
121+ const source = new sources . RawSource ( content ) ;
122+ console . log ( `.${ outFilePath } ` ) ;
123+ compilation . emitAsset ( `.${ outFilePath } ` , source ) ;
124+ } ) ;
125+ }
126+ } ,
127+ ) ;
128+ }
129+ } ) ;
122130 } ,
123131} ) ;
124132
@@ -129,7 +137,6 @@ function mergeRouteMetaWithPageData(
129137 const m = new Map < string , PageIndexInfo > (
130138 pageDataList . map ( pageData => [ pageData . routePath , pageData ] ) ,
131139 ) ;
132-
133140 const mergedPageDataList = new Map < string , PageIndexInfo > ( ) ;
134141
135142 routeMetaList . forEach ( routeMeta => {
@@ -138,15 +145,16 @@ function mergeRouteMetaWithPageData(
138145 mergedPageDataList . set ( routeMeta . routePath , pageData ) ;
139146 }
140147 } ) ;
141-
142148 return mergedPageDataList ;
143149}
144150
145151/**
146152 * A plugin for rspress to generate llms.txt, llms-full.txt, md files to let llm understand your website.
147153 */
148154export function pluginLlms ( options : Options = { output : { } } ) : RspressPlugin {
149- // const { llmsTxt, llmsFullTxt, mdFiles } = options.output;
155+ const { llmsTxt = true , llmsFullTxt = true , mdFiles = true } = options . output ;
156+
157+ let base = '' ;
150158 const pageDataList : PageIndexInfo [ ] = [ ] ;
151159 const routes : RouteMeta [ ] = [ ] ;
152160 const sidebar : Sidebar = { } ;
@@ -167,15 +175,22 @@ export function pluginLlms(options: Options = { output: {} }): RspressPlugin {
167175 beforeBuild ( config ) {
168176 Object . assign ( sidebar , config . themeConfig ?. sidebar ?? { } ) ;
169177 Object . assign ( nav , config . themeConfig ?. nav ?? { } ) ;
178+ base = config . base ?? '/' ;
170179 } ,
171180 builderConfig : {
172181 plugins : [
173182 rsbuildPluginLlms ( {
183+ ...options ,
174184 pageDataList,
175185 routes,
176186 sidebar,
177187 nav,
178- ...options ,
188+ base,
189+ output : {
190+ llmsFullTxt,
191+ llmsTxt,
192+ mdFiles,
193+ } ,
179194 } ) ,
180195 ] ,
181196 } ,
0 commit comments