@module-federation/rspress-plugin 0.0.0-next-20250723062542 → 0.0.0-next-20250724024905

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/esm/index.js CHANGED
@@ -8,6 +8,7 @@ import external_node_path_default from "node:path";
8
8
  import { load } from "cheerio";
9
9
  import { htmlToText } from "html-to-text";
10
10
  import { groupBy } from "lodash-es";
11
+ import external_turndown_default from "turndown";
11
12
  import external_fs_default from "fs";
12
13
  const logger = createLogger('[ Module Federation Rspress Plugin ]');
13
14
  const src_logger = logger;
@@ -19,6 +20,10 @@ function findSearchIndexPaths(outputDir) {
19
20
  const searchIndexFiles = files.filter((file)=>file.startsWith(SEARCH_INDEX_NAME) && file.endsWith('.json') && external_fs_default.statSync(external_path_default.join(staticDir, file)).isFile());
20
21
  if (searchIndexFiles) return searchIndexFiles.map((searchIndexFile)=>external_path_default.join(staticDir, searchIndexFile));
21
22
  }
23
+ function findMarkdownFilePath(outputDir, defaultLang, route) {
24
+ const filepath = external_path_default.join(outputDir, route.relativePath.replace(new RegExp(`^${defaultLang}/`), ''));
25
+ return filepath.replace(external_path_default.extname(filepath), '.md');
26
+ }
22
27
  function generateTocFromHtml(html) {
23
28
  const $ = load(html);
24
29
  const headings = $('h1, h2, h3, h4, h5, h6');
@@ -42,8 +47,22 @@ function generateTocFromHtml(html) {
42
47
  };
43
48
  }
44
49
  const replaceHtmlExt = (filepath)=>filepath.replace(external_node_path_default.extname(filepath), '.html');
45
- async function extractPageDataFromHtml(routes, options) {
46
- return Promise.all(routes.map(async (route)=>{
50
+ const getRouteId = (route)=>route.absolutePath;
51
+ async function getRouteHtmlMap(routesMap, options) {
52
+ const routeHtmlMap = {};
53
+ await Promise.all(Object.entries(routesMap).map(async ([routeId, route])=>{
54
+ const { outputDir, defaultLang } = options;
55
+ const htmlPath = replaceHtmlExt(external_node_path_default.join(outputDir, route.lang === defaultLang ? route.relativePath.replace(route.lang, '') : route.relativePath));
56
+ const content = await promises_default.readFile(htmlPath, 'utf-8');
57
+ routeHtmlMap[routeId] = {
58
+ filepath: htmlPath,
59
+ content
60
+ };
61
+ }));
62
+ return routeHtmlMap;
63
+ }
64
+ async function extractPageDataFromHtml(routesMap, routeHtmlMap, options) {
65
+ return Promise.all(Object.entries(routesMap).map(async ([routeId, route])=>{
47
66
  const { domain, searchCodeBlocks, outputDir, defaultLang } = options;
48
67
  const defaultIndexInfo = {
49
68
  title: '',
@@ -59,8 +78,8 @@ async function extractPageDataFromHtml(routes, options) {
59
78
  _filepath: route.absolutePath,
60
79
  _relativePath: ''
61
80
  };
62
- const htmlPath = replaceHtmlExt(external_node_path_default.join(outputDir, route.lang === defaultLang ? route.relativePath.replace(route.lang, '') : route.relativePath));
63
- const html = await promises_default.readFile(htmlPath, 'utf-8');
81
+ const html = routeHtmlMap[routeId].content;
82
+ if (!html) throw new Error(`html not found for route "${routeId}"`);
64
83
  let { toc: rawToc, title } = generateTocFromHtml(html);
65
84
  let content = html;
66
85
  content = htmlToText(html, {
@@ -129,14 +148,14 @@ function deletePrivateField(obj) {
129
148
  for(const key in newObj)if (key.startsWith('_')) delete newObj[key];
130
149
  return newObj;
131
150
  }
132
- async function rebuildSearchIndexByHtml(routes, options) {
151
+ async function rebuildSearchIndexByHtml(routesMap, routeHtmlMap, options) {
133
152
  const { versioned, outputDir } = options;
134
153
  const searchFilePaths = findSearchIndexPaths(outputDir);
135
154
  if (!searchFilePaths) {
136
155
  src_logger.error('Cannot find search index files!');
137
156
  process.exit(1);
138
157
  }
139
- const pages = await extractPageDataFromHtml(routes, options);
158
+ const pages = await extractPageDataFromHtml(routesMap, routeHtmlMap, options);
140
159
  const groupedPages = groupBy(pages, (page)=>{
141
160
  if (page.frontmatter?.pageType === 'home') return 'noindex';
142
161
  const version = versioned ? page.version : '';
@@ -157,6 +176,20 @@ async function rebuildSearchIndexByHtml(routes, options) {
157
176
  await promises_default.writeFile(searchFilePath, stringifiedIndex);
158
177
  }));
159
178
  }
179
+ async function rebuildLLMsMDFilesByHtml(routesMap, routeHtmlMap, options) {
180
+ const { outputDir, defaultLang } = options;
181
+ const turndownService = new external_turndown_default();
182
+ await Promise.all(Object.entries(routeHtmlMap).map(async ([routeId, routeHtml])=>{
183
+ try {
184
+ const { content } = routeHtml;
185
+ const markdown = turndownService.turndown(content);
186
+ const mdFilePath = findMarkdownFilePath(outputDir, defaultLang, routesMap[routeId]);
187
+ await promises_default.writeFile(mdFilePath, markdown);
188
+ } catch (e) {
189
+ src_logger.error(`rebuildLLMsMDFilesByHtml ${routeId} error:${e}`);
190
+ }
191
+ }));
192
+ }
160
193
  const isDev = ()=>'development' === process.env.NODE_ENV;
161
194
  function replaceEntryWithBootstrapEntry(bundlerConfig) {
162
195
  const { entry } = bundlerConfig;
@@ -201,7 +234,7 @@ function replaceEntryWithBootstrapEntry(bundlerConfig) {
201
234
  bundlerConfig.entry = replaceWithAsyncEntry(entry, bundlerConfig.name || 'index');
202
235
  }
203
236
  function plugin_pluginModuleFederation(mfConfig, rspressOptions) {
204
- const { autoShared = true, rebuildSearchIndex = true } = rspressOptions || {};
237
+ const { autoShared = true, rebuildSearchIndex = true, rebuildLLMsMDFiles = true } = rspressOptions || {};
205
238
  if (autoShared) mfConfig.shared = {
206
239
  react: {
207
240
  singleton: true,
@@ -231,7 +264,7 @@ function plugin_pluginModuleFederation(mfConfig, rspressOptions) {
231
264
  };
232
265
  let enableSSG = false;
233
266
  let outputDir = '';
234
- let routes = [];
267
+ let routesMap = {};
235
268
  return {
236
269
  name: 'plugin-module-federation',
237
270
  async config (config) {
@@ -268,12 +301,15 @@ function plugin_pluginModuleFederation(mfConfig, rspressOptions) {
268
301
  }
269
302
  },
270
303
  routeGenerated (routeMetaArr) {
271
- routes = routeMetaArr;
304
+ routesMap = routeMetaArr.reduce((prev, cur)=>{
305
+ prev[getRouteId(cur)] = cur;
306
+ return prev;
307
+ }, {});
272
308
  },
273
309
  async afterBuild (config) {
274
- if (!mfConfig.remotes || isDev() || !rebuildSearchIndex) return;
310
+ if (!mfConfig.remotes || isDev() || !rebuildSearchIndex || !rebuildLLMsMDFiles) return;
275
311
  if (!enableSSG) {
276
- src_logger.error('rebuildSearchIndex is only supported for ssg');
312
+ src_logger.error('rebuildSearchIndex and rebuildLLMsMDFiles are only supported for ssg');
277
313
  process.exit(1);
278
314
  }
279
315
  const searchConfig = config?.search || {};
@@ -281,7 +317,7 @@ function plugin_pluginModuleFederation(mfConfig, rspressOptions) {
281
317
  const domain = searchConfig?.mode === 'remote' ? searchConfig.domain ?? '' : '';
282
318
  const versioned = searchConfig && 'remote' !== searchConfig.mode && searchConfig.versioned;
283
319
  const searchCodeBlocks = 'codeBlocks' in searchConfig ? Boolean(searchConfig.codeBlocks) : true;
284
- await rebuildSearchIndexByHtml(routes, {
320
+ const routeHtmlMap = await getRouteHtmlMap(routesMap, {
285
321
  outputDir,
286
322
  versioned,
287
323
  replaceRules,
@@ -289,7 +325,22 @@ function plugin_pluginModuleFederation(mfConfig, rspressOptions) {
289
325
  searchCodeBlocks,
290
326
  defaultLang: config.lang || 'en'
291
327
  });
292
- src_logger.info('rebuildSearchIndex success!');
328
+ const rebuildOptions = {
329
+ outputDir,
330
+ versioned,
331
+ replaceRules,
332
+ domain,
333
+ searchCodeBlocks,
334
+ defaultLang: config.lang || 'en'
335
+ };
336
+ if (rebuildSearchIndex) {
337
+ await rebuildSearchIndexByHtml(routesMap, routeHtmlMap, rebuildOptions);
338
+ src_logger.info('rebuildSearchIndex success!');
339
+ }
340
+ if (rebuildLLMsMDFiles && config.plugins?.find((p)=>'@rspress/plugin-llms' === p.name)) {
341
+ await rebuildLLMsMDFilesByHtml(routesMap, routeHtmlMap, rebuildOptions);
342
+ src_logger.info('rebuildLLMsMDFiles success!');
343
+ }
293
344
  }
294
345
  };
295
346
  }
@@ -0,0 +1,3 @@
1
+ import type { RouteMeta } from '@rspress/shared';
2
+ export declare function findSearchIndexPaths(outputDir: string): string[] | undefined;
3
+ export declare function findMarkdownFilePath(outputDir: string, defaultLang: string, route: RouteMeta): string;
@@ -3,6 +3,7 @@ import type { RspressPlugin } from '@rspress/shared';
3
3
  type RspressPluginOptions = {
4
4
  autoShared?: boolean;
5
5
  rebuildSearchIndex?: boolean;
6
+ rebuildLLMsMDFiles?: boolean;
6
7
  };
7
8
  export declare function pluginModuleFederation(mfConfig: moduleFederationPlugin.ModuleFederationPluginOptions, rspressOptions?: RspressPluginOptions): RspressPlugin;
8
9
  export { createModuleFederationConfig } from '@module-federation/sdk';
@@ -0,0 +1,20 @@
1
+ import type { RouteMeta, ReplaceRule } from '@rspress/shared';
2
+ interface RouteHtmlMap {
3
+ [routeId: string]: {
4
+ filepath: string;
5
+ content: string;
6
+ };
7
+ }
8
+ export type RebuildOptions = {
9
+ domain: string;
10
+ searchCodeBlocks: boolean;
11
+ replaceRules: ReplaceRule[];
12
+ versioned?: boolean;
13
+ outputDir: string;
14
+ defaultLang: string;
15
+ };
16
+ export declare const getRouteId: (route: RouteMeta) => string;
17
+ export declare function getRouteHtmlMap(routesMap: Record<string, RouteMeta>, options: RebuildOptions): Promise<RouteHtmlMap>;
18
+ export declare function rebuildSearchIndexByHtml(routesMap: Record<string, RouteMeta>, routeHtmlMap: RouteHtmlMap, options: RebuildOptions): Promise<void>;
19
+ export declare function rebuildLLMsMDFilesByHtml(routesMap: Record<string, RouteMeta>, routeHtmlMap: RouteHtmlMap, options: RebuildOptions): Promise<void>;
20
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/rspress-plugin",
3
- "version": "0.0.0-next-20250723062542",
3
+ "version": "0.0.0-next-20250724024905",
4
4
  "type": "module",
5
5
  "description": "Module Federation plugin for Rspress",
6
6
  "keywords": [
@@ -34,17 +34,19 @@
34
34
  "@rspress/shared": "2.0.0-beta.16",
35
35
  "@types/html-to-text": "^9.0.4",
36
36
  "@types/lodash-es": "^4.17.12",
37
- "@types/react": "^18.3.11"
37
+ "@types/react": "^18.3.11",
38
+ "@types/turndown": "5.0.5 "
38
39
  },
39
40
  "dependencies": {
40
41
  "cheerio": "1.0.0-rc.12",
41
42
  "fs-extra": "11.3.0",
42
43
  "html-to-text": "^9.0.5",
43
44
  "lodash-es": "^4.17.21",
44
- "@module-federation/enhanced": "0.0.0-next-20250723062542",
45
- "@module-federation/sdk": "0.0.0-next-20250723062542",
46
- "@module-federation/rsbuild-plugin": "0.0.0-next-20250723062542",
47
- "@module-federation/error-codes": "0.0.0-next-20250723062542"
45
+ "turndown": "7.2.0",
46
+ "@module-federation/sdk": "0.0.0-next-20250724024905",
47
+ "@module-federation/enhanced": "0.0.0-next-20250724024905",
48
+ "@module-federation/rsbuild-plugin": "0.0.0-next-20250724024905",
49
+ "@module-federation/error-codes": "0.0.0-next-20250724024905"
48
50
  },
49
51
  "scripts": {
50
52
  "build": "rslib build",
@@ -1 +0,0 @@
1
- export declare function findSearchIndexPaths(outputDir: string): string[] | undefined;
@@ -1,10 +0,0 @@
1
- import type { RouteMeta, ReplaceRule } from '@rspress/shared';
2
- export type RebuildSearchIndexByHtmlOptions = {
3
- domain: string;
4
- searchCodeBlocks: boolean;
5
- replaceRules: ReplaceRule[];
6
- versioned?: boolean;
7
- outputDir: string;
8
- defaultLang: string;
9
- };
10
- export declare function rebuildSearchIndexByHtml(routes: RouteMeta[], options: RebuildSearchIndexByHtmlOptions): Promise<void>;