@@ -8,7 +8,14 @@ import { Feed } from 'feed';
88
99import { createFeed , generateFeedItem } from './createFeed' ;
1010import { PluginComponents , PluginName } from './exports' ;
11- import { concatArray , type ResolvedOutput , writeFile } from './internals' ;
11+ import {
12+ concatArray ,
13+ extractHtmlContent ,
14+ type ResolvedOutput ,
15+ readFile ,
16+ routePathToHtmlPath ,
17+ writeFile ,
18+ } from './internals' ;
1219import { getDefaultFeedOption , getOutputInfo , testPage } from './options' ;
1320import type { FeedChannel , FeedItem , PluginRssOptions } from './type' ;
1421
@@ -47,17 +54,26 @@ class FeedsSet {
4754 }
4855}
4956
50- function getRssItems (
57+ interface PageRssInfo {
58+ page : PageIndexInfo ;
59+ channels : string [ ] ;
60+ }
61+
62+ async function getRssItems (
5163 feeds : TransformedFeedChannel [ ] ,
5264 page : PageIndexInfo ,
5365 siteUrl : string ,
66+ htmlContent : string | null ,
5467) : Promise < FeedItemWithChannel [ ] > {
5568 return Promise . all (
5669 feeds
5770 . filter ( options => testPage ( options . test , page ) )
5871 . map ( async options => {
5972 const after = options . item || ( ( feed : FeedItem ) => feed ) ;
60- const item = await after ( generateFeedItem ( page , siteUrl ) , page ) ;
73+ const item = await after (
74+ generateFeedItem ( page , siteUrl , htmlContent ) ,
75+ page ,
76+ ) ;
6177 return { ...item , channel : options . id } ;
6278 } ) ,
6379 ) ;
@@ -67,77 +83,108 @@ export function pluginRss(pluginRssOptions: PluginRssOptions): RspressPlugin {
6783 const feedsSet = new FeedsSet ( ) ;
6884
6985 /**
70- * workaround for retrieving data of pages in `afterBuild`
71- * TODO: get pageData list directly in `afterBuild`
72- **/
73- let _rssWorkaround : null | Record <
74- string ,
75- PromiseLike < FeedItemWithChannel [ ] >
76- > = null ;
86+ * Store page data for generating RSS items in afterBuild
87+ * Key: routePath, Value: PageRssInfo
88+ */
89+ let _pagesForRss : null | Map < string , PageRssInfo > = null ;
7790
7891 return {
7992 name : PluginName ,
8093 globalUIComponents : Object . values ( PluginComponents ) ,
8194 beforeBuild ( config , isProd ) {
8295 if ( ! isProd ) {
83- _rssWorkaround = null ;
96+ _pagesForRss = null ;
8497 return ;
8598 }
86- _rssWorkaround = { } ;
99+
100+ // RSS plugin requires SSG to be enabled
101+ const enableSSG = Boolean ( ( config . ssg || config . llms ) ?? true ) ;
102+ if ( ! enableSSG ) {
103+ throw new Error (
104+ '[plugin-rss] RSS plugin requires SSG to be enabled. ' +
105+ 'Please set `ssg: true` in your rspress.config.ts or remove the RSS plugin.' ,
106+ ) ;
107+ }
108+
109+ _pagesForRss = new Map ( ) ;
87110 feedsSet . set ( pluginRssOptions , config ) ;
88111 } ,
89112 async extendPageData ( pageData ) {
90- if ( ! _rssWorkaround ) return ;
113+ if ( ! _pagesForRss ) return ;
114+
115+ // Find which feeds this page belongs to
116+ const matchedChannels = feedsSet
117+ . get ( )
118+ . filter ( options => testPage ( options . test , pageData ) )
119+ . map ( options => options . id ) ;
91120
92- // rspress run `extendPageData` for each page
93- // - let's cache rss items within a complete rspress build
94- _rssWorkaround [ pageData . routePath ] =
95- _rssWorkaround [ pageData . routePath ] ||
96- getRssItems ( feedsSet . get ( ) , pageData , pluginRssOptions . siteUrl ) ;
121+ if ( matchedChannels . length > 0 ) {
122+ _pagesForRss . set ( pageData . routePath , {
123+ page : pageData ,
124+ channels : matchedChannels ,
125+ } ) ;
126+ }
97127
98- const feeds = await _rssWorkaround [ pageData . routePath ] ;
128+ // Set up feed links for the page
99129 const showRssList = new Set (
100130 concatArray ( pageData . frontmatter [ 'link-rss' ] as string [ ] | string ) ,
101131 ) ;
102- for ( const feed of feeds ) {
103- showRssList . add ( feed . channel ) ;
132+ for ( const channel of matchedChannels ) {
133+ showRssList . add ( channel ) ;
104134 }
105135
106136 pageData . feeds = Array . from ( showRssList , id => {
107- const { output, language } = feedsSet . get ( id ) ! ;
137+ const feedChannel = feedsSet . get ( id ) ;
138+ if ( ! feedChannel ) return null ;
139+ const { output, language } = feedChannel ;
108140 return {
109141 url : output . url ,
110142 mime : output . mime ,
111143 language : language || pageData . lang ,
112144 } ;
113- } ) ;
145+ } ) . filter ( Boolean ) as typeof pageData . feeds ;
114146 } ,
115147 async afterBuild ( config ) {
116- if ( ! _rssWorkaround ) return ;
148+ if ( ! _pagesForRss ) return ;
117149
118- const items = concatArray (
119- ...( await Promise . all ( Object . values ( _rssWorkaround ) ) ) ,
120- ) ;
150+ const outDir = config . outDir || 'doc_build' ;
121151 const feeds : Record < string , Feed > = Object . create ( null ) ;
122152
123- for ( const { channel, ...item } of items ) {
124- feeds [ channel ] =
125- feeds [ channel ] ||
126- new Feed ( createFeed ( feedsSet . get ( channel ) ! , config ) ) ;
127- feeds [ channel ] . addItem ( item ) ;
153+ // Process each page: read HTML from SSG output and generate feed items
154+ for ( const [ routePath , { page, channels } ] of _pagesForRss ) {
155+ // Read HTML content from SSG output
156+ const htmlPath = NodePath . resolve (
157+ outDir ,
158+ routePathToHtmlPath ( routePath ) ,
159+ ) ;
160+ const htmlFile = await readFile ( htmlPath ) ;
161+ const htmlContent = htmlFile ? extractHtmlContent ( htmlFile ) : null ;
162+
163+ // Generate feed items for each channel
164+ const items = await getRssItems (
165+ channels . map ( id => feedsSet . get ( id ) ! ) ,
166+ page ,
167+ pluginRssOptions . siteUrl ,
168+ htmlContent ,
169+ ) ;
170+
171+ for ( const { channel, ...item } of items ) {
172+ feeds [ channel ] =
173+ feeds [ channel ] ||
174+ new Feed ( createFeed ( feedsSet . get ( channel ) ! , config ) ) ;
175+ feeds [ channel ] . addItem ( item ) ;
176+ }
128177 }
129178
179+ // Write feed files
130180 for ( const [ channel , feed ] of Object . entries ( feeds ) ) {
131181 const { output } = feedsSet . get ( channel ) ! ;
132182 feed . items . sort ( output . sorting ) ;
133- const path = NodePath . resolve (
134- config . outDir || 'doc_build' ,
135- output . dir ,
136- output . filename ,
137- ) ;
183+ const path = NodePath . resolve ( outDir , output . dir , output . filename ) ;
138184 await writeFile ( path , output . getContent ( feed ) ) ;
139185 }
140- _rssWorkaround = null ;
186+
187+ _pagesForRss = null ;
141188 } ,
142189 } ;
143190}
0 commit comments