@rspress/plugin-rss 2.0.0-rc.5 → 2.0.0-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -72,9 +72,10 @@ export declare type FeedOutputType = /** Atom 1.0 Feed */ 'atom' | /** RSS 2.0 F
72
72
  /**
73
73
  * @public
74
74
  * @param page Rspress Page Data
75
- * @param siteUrl
75
+ * @param siteUrl Site URL for generating absolute links
76
+ * @param htmlContent HTML content extracted from SSG output (optional)
76
77
  */
77
- export declare function generateFeedItem(page: PageIndexInfo, siteUrl: string): {
78
+ export declare function generateFeedItem(page: PageIndexInfo, siteUrl: string, htmlContent?: string | null): {
78
79
  id: string;
79
80
  title: string;
80
81
  author: Author[] | undefined;
package/dist/index.js CHANGED
@@ -34,7 +34,26 @@ async function writeFile(path, content) {
34
34
  });
35
35
  return promises.writeFile(path, content);
36
36
  }
37
- function generateFeedItem(page, siteUrl) {
37
+ async function readFile(path) {
38
+ try {
39
+ return await promises.readFile(path, 'utf-8');
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+ function routePathToHtmlPath(routePath) {
45
+ let fileName = routePath;
46
+ fileName = fileName.endsWith('/') ? `${routePath}index.html` : `${routePath}.html`;
47
+ return fileName.replace(/^\/+/, '');
48
+ }
49
+ function extractHtmlContent(html) {
50
+ const match = html.match(/<div[^>]*class="[^"]*rspress-doc[^"]*"[^>]*>([\s\S]*?)<\/div>(?=<footer|<\/main)/);
51
+ if (match) return match[1].trim();
52
+ const fallbackMatch = html.match(/<div[^>]*class="[^"]*rspress-doc[^"]*"[^>]*>([\s\S]*?)<\/div>/);
53
+ if (fallbackMatch) return fallbackMatch[1].trim();
54
+ return null;
55
+ }
56
+ function generateFeedItem(page, siteUrl, htmlContent) {
38
57
  const { frontmatter: fm } = page;
39
58
  return {
40
59
  id: selectNonNullishProperty(fm.slug, fm.id, page.routePath) || '',
@@ -42,7 +61,7 @@ function generateFeedItem(page, siteUrl) {
42
61
  author: toAuthors(fm.author),
43
62
  link: resolve(siteUrl, selectNonNullishProperty(fm.permalink, page.routePath)?.replace(/^\//, '') || ''),
44
63
  description: selectNonNullishProperty(fm.description) || '',
45
- content: selectNonNullishProperty(fm.summary, page._html) || '',
64
+ content: selectNonNullishProperty(fm.summary, htmlContent, page.content) || '',
46
65
  date: toDate(fm.date || fm.published_at),
47
66
  category: concatArray(fm.categories, fm.category).map((cat)=>({
48
67
  name: cat
@@ -173,10 +192,10 @@ class FeedsSet {
173
192
  return this.feeds.slice(0);
174
193
  }
175
194
  }
176
- function getRssItems(feeds, page, siteUrl) {
195
+ async function getRssItems(feeds, page, siteUrl, htmlContent) {
177
196
  return Promise.all(feeds.filter((options)=>testPage(options.test, page)).map(async (options)=>{
178
197
  const after = options.item || ((feed)=>feed);
179
- const item = await after(generateFeedItem(page, siteUrl), page);
198
+ const item = await after(generateFeedItem(page, siteUrl, htmlContent), page);
180
199
  return {
181
200
  ...item,
182
201
  channel: options.id
@@ -185,48 +204,61 @@ function getRssItems(feeds, page, siteUrl) {
185
204
  }
186
205
  function pluginRss(pluginRssOptions) {
187
206
  const feedsSet = new FeedsSet();
188
- let _rssWorkaround = null;
207
+ let _pagesForRss = null;
189
208
  return {
190
209
  name: PluginName,
191
210
  globalUIComponents: Object.values(PluginComponents),
192
211
  beforeBuild (config, isProd) {
193
212
  if (!isProd) {
194
- _rssWorkaround = null;
213
+ _pagesForRss = null;
195
214
  return;
196
215
  }
197
- _rssWorkaround = {};
216
+ const enableSSG = Boolean((config.ssg || config.llms) ?? true);
217
+ if (!enableSSG) throw new Error("[plugin-rss] RSS plugin requires SSG to be enabled. Please set `ssg: true` in your rspress.config.ts or remove the RSS plugin.");
218
+ _pagesForRss = new Map();
198
219
  feedsSet.set(pluginRssOptions, config);
199
220
  },
200
221
  async extendPageData (pageData) {
201
- if (!_rssWorkaround) return;
202
- _rssWorkaround[pageData.routePath] = _rssWorkaround[pageData.routePath] || getRssItems(feedsSet.get(), pageData, pluginRssOptions.siteUrl);
203
- const feeds = await _rssWorkaround[pageData.routePath];
222
+ if (!_pagesForRss) return;
223
+ const matchedChannels = feedsSet.get().filter((options)=>testPage(options.test, pageData)).map((options)=>options.id);
224
+ if (matchedChannels.length > 0) _pagesForRss.set(pageData.routePath, {
225
+ page: pageData,
226
+ channels: matchedChannels
227
+ });
204
228
  const showRssList = new Set(concatArray(pageData.frontmatter['link-rss']));
205
- for (const feed of feeds)showRssList.add(feed.channel);
229
+ for (const channel of matchedChannels)showRssList.add(channel);
206
230
  pageData.feeds = Array.from(showRssList, (id)=>{
207
- const { output, language } = feedsSet.get(id);
231
+ const feedChannel = feedsSet.get(id);
232
+ if (!feedChannel) return null;
233
+ const { output, language } = feedChannel;
208
234
  return {
209
235
  url: output.url,
210
236
  mime: output.mime,
211
237
  language: language || pageData.lang
212
238
  };
213
- });
239
+ }).filter(Boolean);
214
240
  },
215
241
  async afterBuild (config) {
216
- if (!_rssWorkaround) return;
217
- const items = concatArray(...await Promise.all(Object.values(_rssWorkaround)));
242
+ if (!_pagesForRss) return;
243
+ const outDir = config.outDir || 'doc_build';
218
244
  const feeds = Object.create(null);
219
- for (const { channel, ...item } of items){
220
- feeds[channel] = feeds[channel] || new Feed(createFeed(feedsSet.get(channel), config));
221
- feeds[channel].addItem(item);
245
+ for (const [routePath, { page, channels }] of _pagesForRss){
246
+ const htmlPath = node_path.resolve(outDir, routePathToHtmlPath(routePath));
247
+ const htmlFile = await readFile(htmlPath);
248
+ const htmlContent = htmlFile ? extractHtmlContent(htmlFile) : null;
249
+ const items = await getRssItems(channels.map((id)=>feedsSet.get(id)), page, pluginRssOptions.siteUrl, htmlContent);
250
+ for (const { channel, ...item } of items){
251
+ feeds[channel] = feeds[channel] || new Feed(createFeed(feedsSet.get(channel), config));
252
+ feeds[channel].addItem(item);
253
+ }
222
254
  }
223
255
  for (const [channel, feed] of Object.entries(feeds)){
224
256
  const { output } = feedsSet.get(channel);
225
257
  feed.items.sort(output.sorting);
226
- const path = node_path.resolve(config.outDir || 'doc_build', output.dir, output.filename);
258
+ const path = node_path.resolve(outDir, output.dir, output.filename);
227
259
  await writeFile(path, output.getContent(feed));
228
260
  }
229
- _rssWorkaround = null;
261
+ _pagesForRss = null;
230
262
  }
231
263
  };
232
264
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rspress/plugin-rss",
3
- "version": "2.0.0-rc.5",
3
+ "version": "2.0.0-rc.6",
4
4
  "description": "A plugin for rss generation for rspress",
5
5
  "bugs": "https://github.com/web-infra-dev/rspress/issues",
6
6
  "repository": {
@@ -28,15 +28,15 @@
28
28
  "feed": "^4.2.2"
29
29
  },
30
30
  "devDependencies": {
31
- "@rslib/core": "0.19.1",
31
+ "@rslib/core": "0.19.2",
32
32
  "@types/node": "^22.8.1",
33
- "@types/react": "^19.2.7",
33
+ "@types/react": "^19.2.8",
34
34
  "react": "^19.2.3",
35
35
  "rsbuild-plugin-publint": "^0.3.3",
36
36
  "typescript": "^5.8.2"
37
37
  },
38
38
  "peerDependencies": {
39
- "@rspress/core": "^2.0.0-rc.5"
39
+ "@rspress/core": "^2.0.0-rc.6"
40
40
  },
41
41
  "engines": {
42
42
  "node": ">=20.9.0"