@rspress/plugin-llms 2.0.0-alpha.8 → 2.0.0-beta.2

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
@@ -228,6 +228,15 @@ declare interface Hero {
228
228
  }[];
229
229
  }
230
230
 
231
+ export declare interface LlmsTxt {
232
+ onTitleGenerate?: (context: {
233
+ title: string | undefined;
234
+ description: string | undefined;
235
+ }) => string;
236
+ onLineGenerate?: (page: PageIndexInfo) => string;
237
+ onAfterLlmsTxtGenerate?: (llmsTxtContent: string) => string;
238
+ }
239
+
231
240
  declare interface Locale {
232
241
  lang: string;
233
242
  label: string;
@@ -302,21 +311,6 @@ declare interface MarkdownOptions {
302
311
  * Register prism languages
303
312
  */
304
313
  highlightLanguages?: (string | [string, string])[];
305
- /**
306
- * Whether to enable mdx-rs, default is true
307
- */
308
- mdxRs?: boolean | MdxRsOptions;
309
- /**
310
- * @deprecated, use `mdxRs` instead
311
- */
312
- experimentalMdxRs?: boolean;
313
- }
314
-
315
- declare interface MdxRsOptions {
316
- /**
317
- * Determine whether the file use mdxRs compiler
318
- */
319
- include?: (filepath: string) => boolean;
320
314
  }
321
315
 
322
316
  declare type Nav = NavItem[] | {
@@ -349,12 +343,38 @@ declare interface NavItemWithLinkAndChildren {
349
343
  position?: 'left' | 'right';
350
344
  }
351
345
 
352
- declare interface Options {
353
- output: {
354
- llmsTxt?: boolean;
355
- mdFiles?: boolean;
356
- llmsFullTxt?: boolean;
357
- };
346
+ export declare interface Options {
347
+ /**
348
+ * Whether to generate llms.txt.
349
+ * @default true
350
+ */
351
+ llmsTxt?: boolean | LlmsTxt;
352
+ /**
353
+ * Whether to generate llms.txt related md files for each route.
354
+ * @default true
355
+ */
356
+ mdFiles?: boolean;
357
+ /**
358
+ * Whether to generate llms-full.txt.
359
+ * @default true
360
+ */
361
+ llmsFullTxt?: boolean;
362
+ /**
363
+ * Whether to include some routes from llms.txt.
364
+ * @param context
365
+ * @default (context) => context.page.lang === config.lang
366
+ */
367
+ include?: (context: {
368
+ page: PageIndexInfo;
369
+ }) => boolean;
370
+ /**
371
+ * Whether to exclude some routes from llms.txt.
372
+ * exclude will trigger after include
373
+ * @default undefined
374
+ */
375
+ exclude?: (context: {
376
+ page: PageIndexInfo;
377
+ }) => boolean;
358
378
  }
359
379
 
360
380
  /**
@@ -368,7 +388,7 @@ declare interface PageIndexInfo {
368
388
  routePath: string;
369
389
  toc: Header[];
370
390
  content: string;
371
- flattenContent?: string;
391
+ _flattenContent?: string;
372
392
  _html: string;
373
393
  frontmatter: FrontMatterMeta;
374
394
  lang: string;
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__ from "node:path";
2
+ import * as __WEBPACK_EXTERNAL_MODULE__rspress_runtime_server_673c713a__ from "@rspress/runtime/server";
2
3
  import * as __WEBPACK_EXTERNAL_MODULE__rspress_core_1b2e46ce__ from "@rspress/core";
3
4
  import * as __WEBPACK_EXTERNAL_MODULE_unist_util_visit_555e002a__ from "unist-util-visit";
4
5
  import * as __WEBPACK_EXTERNAL_MODULE_unified__ from "unified";
@@ -5831,11 +5832,33 @@ function normalizeSlash(url) {
5831
5832
  function isExternalUrl(url = '') {
5832
5833
  return url.startsWith('http://') || url.startsWith('https://') || url.startsWith('mailto:') || url.startsWith('tel:');
5833
5834
  }
5835
+ const parseUrl = (url)=>{
5836
+ const [withoutHash, hash = ''] = url.split('#');
5837
+ return {
5838
+ url: withoutHash,
5839
+ hash
5840
+ };
5841
+ };
5842
+ function normalizeHref(url, cleanUrls = false) {
5843
+ if (!url) return '/';
5844
+ if (isExternalUrl(url)) return url;
5845
+ if (url.startsWith('#')) return url;
5846
+ let { url: cleanUrl, hash } = parseUrl(decodeURIComponent(url));
5847
+ if (cleanUrls) {
5848
+ if (cleanUrl.endsWith('.html')) cleanUrl = cleanUrl.replace(/\.html$/, '');
5849
+ if (cleanUrls && cleanUrl.endsWith('/index')) cleanUrl = cleanUrl.replace(/\/index$/, '/');
5850
+ } else if (!cleanUrl.endsWith('.html')) if (cleanUrl.endsWith('/')) cleanUrl += 'index.html';
5851
+ else cleanUrl += '.html';
5852
+ return addLeadingSlash(hash ? `${cleanUrl}#${hash}` : cleanUrl);
5853
+ }
5834
5854
  function withBase(url, base) {
5835
5855
  const normalizedUrl = addLeadingSlash(url);
5836
5856
  const normalizedBase = normalizeSlash(base);
5837
5857
  return normalizedUrl.startsWith(normalizedBase) ? normalizedUrl : `${normalizedBase}${normalizedUrl}`;
5838
5858
  }
5859
+ function removeBase(url, base) {
5860
+ return addLeadingSlash(url).replace(new RegExp(`^${normalizeSlash(base)}`), '');
5861
+ }
5839
5862
  const matchSidebar = (pattern, currentPathname, base)=>{
5840
5863
  const prefix = withBase(pattern, base);
5841
5864
  if (prefix === currentPathname) return true;
@@ -5844,35 +5867,76 @@ const matchSidebar = (pattern, currentPathname, base)=>{
5844
5867
  const prefixWithDot = `${prefix}.`;
5845
5868
  return currentPathname.startsWith(prefixWithDot);
5846
5869
  };
5847
- function generateLlmsTxt(pageDataArray, navList) {
5870
+ const getSidebarDataGroup = (sidebar, currentPathname, base)=>{
5871
+ const navRoutes = Object.keys(sidebar).sort((a, b)=>b.length - a.length);
5872
+ for (const name of navRoutes)if (matchSidebar(name, currentPathname, base)) {
5873
+ const sidebarGroup = sidebar[name];
5874
+ return sidebarGroup;
5875
+ }
5876
+ return [];
5877
+ };
5878
+ function routePathToMdPath(routePath) {
5879
+ let url = routePath;
5880
+ url = normalizeHref(url, false);
5881
+ url = url.replace(/\.html$/, '.md');
5882
+ return url;
5883
+ }
5884
+ function generateLlmsTxt(pageDataArray, navList, others, llmsTxtOptions, title, description) {
5848
5885
  const lines = [];
5886
+ const { onAfterLlmsTxtGenerate, onLineGenerate, onTitleGenerate } = 'boolean' == typeof llmsTxtOptions ? {} : llmsTxtOptions;
5887
+ const summary = onTitleGenerate ? onTitleGenerate({
5888
+ title,
5889
+ description
5890
+ }) : `# ${title}${description ? `\n\n> ${description}` : ''}`;
5849
5891
  for(let i = 0; i < navList.length; i++){
5850
5892
  const nav = navList[i];
5851
5893
  const pages = pageDataArray[i];
5852
5894
  const { text } = nav;
5853
5895
  if (0 === pages.length) continue;
5854
5896
  const title = text;
5855
- lines.push(`# ${title}\n`);
5897
+ lines.push(`\n## ${title}\n`);
5856
5898
  for (const page of pages){
5857
- lines.push(`[${page.title}](${page.routePath.replace(/\.html$/, '.md')})`);
5858
- lines.push('');
5859
- }
5860
- }
5861
- return lines.join('\n');
5862
- }
5863
- function generateLlmsFullTxt(pageDataArray, navList) {
5899
+ const { routePath, lang, title, frontmatter } = page;
5900
+ if ('/' === routePath || routePath === `/${lang}/`) continue;
5901
+ const line = onLineGenerate ? onLineGenerate(page) : `- [${title}](${routePathToMdPath(routePath)})${frontmatter.description ? `: ${frontmatter.description}` : ''}`;
5902
+ lines.push(line);
5903
+ }
5904
+ }
5905
+ let hasOthers = false;
5906
+ const otherLines = [];
5907
+ otherLines.push('\n## Other\n');
5908
+ for (const page of others){
5909
+ const { routePath, lang, title, frontmatter } = page;
5910
+ if ('/' === routePath || routePath === `/${lang}/`) continue;
5911
+ const line = onLineGenerate ? onLineGenerate(page) : `- [${title}](${routePathToMdPath(routePath)})${frontmatter.description ? `: ${frontmatter.description}` : ''}`;
5912
+ otherLines.push(line);
5913
+ hasOthers = true;
5914
+ }
5915
+ if (hasOthers) lines.push(...otherLines);
5916
+ const llmsTxt = `${summary}\n${lines.join('\n')}`;
5917
+ return onAfterLlmsTxtGenerate ? onAfterLlmsTxtGenerate(llmsTxt) : llmsTxt;
5918
+ }
5919
+ function generateLlmsFullTxt(pageDataArray, navList, others) {
5864
5920
  const lines = [];
5865
5921
  for(let i = 0; i < navList.length; i++){
5866
- const nav = navList[i];
5867
5922
  const pages = pageDataArray[i];
5868
- if (0 === pages.length) continue;
5869
- const title = nav.text;
5870
- lines.push(`# ${title}\n`);
5871
- for (const page of pages){
5872
- lines.push(page.mdContent ?? page.flattenContent ?? page.content);
5923
+ if (0 !== pages.length) for (const page of pages){
5924
+ lines.push(`---
5925
+ url: ${routePathToMdPath(page.routePath)}
5926
+ ---
5927
+ `);
5928
+ lines.push(page.mdContent ?? page._flattenContent ?? page.content);
5873
5929
  lines.push('\n');
5874
5930
  }
5875
5931
  }
5932
+ for (const page of others){
5933
+ lines.push(`---
5934
+ url: ${routePathToMdPath(page.routePath)}
5935
+ ---
5936
+ `);
5937
+ lines.push(page.mdContent ?? page._flattenContent ?? page.content);
5938
+ lines.push('\n');
5939
+ }
5876
5940
  return lines.join('\n');
5877
5941
  }
5878
5942
  function default_ok() {}
@@ -20842,25 +20906,36 @@ function mdxToMd(content, filepath, docDirectory, routeService) {
20842
20906
  path: filepath
20843
20907
  });
20844
20908
  }
20845
- const rsbuildPluginLlms = ({ pageDataList, routes, baseRef, docDirectoryRef, routeServiceRef, nav, output })=>({
20909
+ const rsbuildPluginLlms = ({ pageDataList, routes, titleRef, descriptionRef, langRef, sidebar, baseRef, docDirectoryRef, routeServiceRef, nav, rspressPluginOptions })=>({
20846
20910
  name: 'rsbuild-plugin-llms',
20847
20911
  async setup (api) {
20848
- const { llmsTxt, mdFiles, llmsFullTxt } = output;
20912
+ const { llmsTxt = true, mdFiles = true, llmsFullTxt = true, include, exclude } = rspressPluginOptions;
20849
20913
  api.onBeforeBuild(async ()=>{
20850
20914
  const base = baseRef.current;
20851
20915
  const docDirectory = docDirectoryRef.current;
20852
- const newPageDataList = mergeRouteMetaWithPageData(routes, pageDataList);
20853
- const navList = Array.isArray(nav) ? nav.map((i)=>i.default).flat().filter(Boolean) : [];
20854
- const pageArray = new Array(navList.length).fill([]);
20916
+ const newPageDataList = mergeRouteMetaWithPageData(routes, pageDataList, langRef.current, include, exclude);
20917
+ const navList = Array.isArray(nav) ? nav.map((i)=>{
20918
+ const nav = i.nav.default;
20919
+ const lang = i.lang;
20920
+ return nav.map((i)=>({
20921
+ ...i,
20922
+ lang
20923
+ }));
20924
+ }).flat().filter((i)=>i.activeMatch || i.link) : [];
20925
+ const others = [];
20926
+ const pageArray = new Array(navList.length).fill(0).map(()=>[]);
20855
20927
  newPageDataList.forEach((pageData)=>{
20856
- const { routePath } = pageData;
20857
- pageArray.forEach((pageArrayItem, index)=>{
20858
- const navItem = navList[index];
20859
- if (matchSidebar(navItem.activeMatch ?? navItem.link, routePath, base)) pageArrayItem.push(pageData);
20860
- });
20928
+ const { routePath, lang } = pageData;
20929
+ for(let i = 0; i < pageArray.length; i++){
20930
+ const pageArrayItem = pageArray[i];
20931
+ const navItem = navList[i];
20932
+ if (lang === navItem.lang && new RegExp(navItem.activeMatch ?? navItem.link).test(removeBase(routePath, base))) return void pageArrayItem.push(pageData);
20933
+ }
20934
+ others.push(pageData);
20861
20935
  });
20936
+ for (const array of pageArray)organizeBySidebar(sidebar, array, base);
20862
20937
  if (llmsTxt) {
20863
- const llmsTxtContent = generateLlmsTxt(pageArray, navList);
20938
+ const llmsTxtContent = generateLlmsTxt(pageArray, navList, others, rspressPluginOptions.llmsTxt ?? {}, titleRef.current, descriptionRef.current);
20864
20939
  api.processAssets({
20865
20940
  targets: [
20866
20941
  'node'
@@ -20875,7 +20950,7 @@ const rsbuildPluginLlms = ({ pageDataList, routes, baseRef, docDirectoryRef, rou
20875
20950
  await Promise.all([
20876
20951
  ...newPageDataList.values()
20877
20952
  ].map(async (pageData)=>{
20878
- const content = pageData.flattenContent ?? pageData.content;
20953
+ const content = pageData._flattenContent ?? pageData.content;
20879
20954
  const filepath = pageData._filepath;
20880
20955
  const isMD = 'md' === __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].extname(filepath).slice(1);
20881
20956
  let mdContent;
@@ -20903,7 +20978,7 @@ const rsbuildPluginLlms = ({ pageDataList, routes, baseRef, docDirectoryRef, rou
20903
20978
  });
20904
20979
  });
20905
20980
  if (llmsFullTxt) {
20906
- const llmsFullTxtContent = generateLlmsFullTxt(pageArray, navList);
20981
+ const llmsFullTxtContent = generateLlmsFullTxt(pageArray, navList, others);
20907
20982
  api.processAssets({
20908
20983
  targets: [
20909
20984
  'node'
@@ -20911,14 +20986,25 @@ const rsbuildPluginLlms = ({ pageDataList, routes, baseRef, docDirectoryRef, rou
20911
20986
  stage: 'additional'
20912
20987
  }, async ({ compilation, sources })=>{
20913
20988
  const source = new sources.RawSource(llmsFullTxtContent);
20914
- compilation.emitAsset('llms.full.txt', source);
20989
+ compilation.emitAsset('llms-full.txt', source);
20915
20990
  });
20916
20991
  }
20917
20992
  });
20918
20993
  }
20919
20994
  });
20920
- function mergeRouteMetaWithPageData(routeMetaList, pageDataList) {
20921
- const m = new Map(pageDataList.map((pageData)=>[
20995
+ function mergeRouteMetaWithPageData(routeMetaList, pageDataList, lang, include, exclude) {
20996
+ const m = new Map(pageDataList.filter((pageData)=>{
20997
+ if (include) return include({
20998
+ page: pageData
20999
+ });
21000
+ if (lang) return pageData.lang === lang;
21001
+ return true;
21002
+ }).filter((pageData)=>{
21003
+ if (exclude) return !exclude({
21004
+ page: pageData
21005
+ });
21006
+ return true;
21007
+ }).map((pageData)=>[
20922
21008
  pageData.routePath,
20923
21009
  pageData
20924
21010
  ]));
@@ -20929,16 +21015,45 @@ function mergeRouteMetaWithPageData(routeMetaList, pageDataList) {
20929
21015
  });
20930
21016
  return mergedPageDataList;
20931
21017
  }
20932
- function pluginLlms(options = {
20933
- output: {}
20934
- }) {
20935
- const { llmsTxt = true, llmsFullTxt = true, mdFiles = true } = options.output;
21018
+ function flatSidebar(sidebar) {
21019
+ if (!sidebar) return [];
21020
+ return sidebar.flatMap((i)=>{
21021
+ if ('string' == typeof i) return i;
21022
+ if ('link' in i && 'string' == typeof i.link) return [
21023
+ i.link,
21024
+ ...flatSidebar(i?.items ?? [])
21025
+ ];
21026
+ if ('items' in i && Array.isArray(i.items)) return flatSidebar(i.items);
21027
+ }).filter(Boolean);
21028
+ }
21029
+ function organizeBySidebar(sidebar, pages, base) {
21030
+ if (0 === pages.length) return;
21031
+ const pageItem = pages[0];
21032
+ const currSidebar = getSidebarDataGroup(sidebar, pageItem.routePath, base);
21033
+ if (0 === currSidebar.length) return;
21034
+ const orderList = flatSidebar(currSidebar);
21035
+ pages.sort((a, b)=>{
21036
+ const aIndex = orderList.findIndex((order)=>(0, __WEBPACK_EXTERNAL_MODULE__rspress_runtime_server_673c713a__.matchPath)(order, a.routePath));
21037
+ const bIndex = orderList.findIndex((order)=>(0, __WEBPACK_EXTERNAL_MODULE__rspress_runtime_server_673c713a__.matchPath)(order, b.routePath));
21038
+ return aIndex - bIndex;
21039
+ });
21040
+ }
21041
+ function pluginLlms(options = {}) {
20936
21042
  const baseRef = {
20937
21043
  current: ''
20938
21044
  };
20939
21045
  const docDirectoryRef = {
20940
21046
  current: ''
20941
21047
  };
21048
+ const titleRef = {
21049
+ current: ''
21050
+ };
21051
+ const descriptionRef = {
21052
+ current: ''
21053
+ };
21054
+ const langRef = {
21055
+ current: ''
21056
+ };
20942
21057
  const pageDataList = [];
20943
21058
  const routes = [];
20944
21059
  const sidebar = {};
@@ -20958,9 +21073,19 @@ function pluginLlms(options = {
20958
21073
  if (isProd) routes.push(..._routes);
20959
21074
  },
20960
21075
  beforeBuild (config) {
20961
- Object.assign(sidebar, config.themeConfig?.sidebar ?? {});
20962
- const configNav = config.themeConfig?.locales?.map((i)=>i.nav).filter(Boolean);
21076
+ const configSidebar = config?.themeConfig?.locales?.map((i)=>i.sidebar).reduce((prev, curr)=>{
21077
+ Object.assign(prev, curr);
21078
+ return prev;
21079
+ }, {});
21080
+ Object.assign(sidebar, configSidebar);
21081
+ const configNav = config.themeConfig?.locales?.filter((i)=>Boolean(i.nav))?.map((i)=>({
21082
+ nav: i.nav,
21083
+ lang: i.lang
21084
+ }));
20963
21085
  nav.push(...configNav);
21086
+ titleRef.current = config.title;
21087
+ descriptionRef.current = config.description;
21088
+ langRef.current = config.lang;
20964
21089
  baseRef.current = config.base ?? '/';
20965
21090
  docDirectoryRef.current = config.root ?? 'docs';
20966
21091
  },
@@ -20970,16 +21095,15 @@ function pluginLlms(options = {
20970
21095
  ...options,
20971
21096
  pageDataList,
20972
21097
  routes,
21098
+ titleRef,
21099
+ descriptionRef,
21100
+ langRef,
20973
21101
  sidebar,
20974
21102
  docDirectoryRef,
20975
21103
  routeServiceRef,
20976
21104
  nav,
20977
21105
  baseRef,
20978
- output: {
20979
- llmsFullTxt,
20980
- llmsTxt,
20981
- mdFiles
20982
- }
21106
+ rspressPluginOptions: options
20983
21107
  })
20984
21108
  ]
20985
21109
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rspress/plugin-llms",
3
- "version": "2.0.0-alpha.8",
3
+ "version": "2.0.0-beta.2",
4
4
  "description": "A plugin for rspress to generate llms.txt, llms-full.txt, md files to let llm understand your website.",
5
5
  "bugs": "https://github.com/web-infra-dev/rspress/issues",
6
6
  "repository": {
@@ -23,9 +23,9 @@
23
23
  "unist-util-visit-children": "^3.0.0"
24
24
  },
25
25
  "devDependencies": {
26
- "@microsoft/api-extractor": "^7.52.3",
26
+ "@microsoft/api-extractor": "^7.52.4",
27
27
  "@rsbuild/core": "1.3.0",
28
- "@rslib/core": "0.6.3",
28
+ "@rslib/core": "0.6.5",
29
29
  "@types/hast": "^3.0.4",
30
30
  "@types/node": "^18.11.17",
31
31
  "remark-mdx": "^3.1.0",
@@ -34,14 +34,14 @@
34
34
  "typescript": "^5.8.2",
35
35
  "vfile": "^6.0.3",
36
36
  "@rspress/config": "1.0.0",
37
- "@rspress/shared": "2.0.0-alpha-canary-202504162001"
37
+ "@rspress/shared": "2.0.0-beta.2"
38
38
  },
39
39
  "peerDependencies": {
40
- "@rspress/core": "^2.0.0-alpha.8",
41
- "@rspress/runtime": "^2.0.0-alpha.9"
40
+ "@rspress/core": "^2.0.0-beta.2",
41
+ "@rspress/runtime": "^2.0.0-beta.2"
42
42
  },
43
43
  "engines": {
44
- "node": ">=14.17.6"
44
+ "node": ">=18.0.0"
45
45
  },
46
46
  "publishConfig": {
47
47
  "access": "public",