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

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,29 @@ 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 exclude some routes from llms.txt.
364
+ * @default undefined
365
+ */
366
+ exclude?: (context: {
367
+ page: PageIndexInfo;
368
+ }) => boolean;
358
369
  }
359
370
 
360
371
  /**
@@ -368,7 +379,7 @@ declare interface PageIndexInfo {
368
379
  routePath: string;
369
380
  toc: Header[];
370
381
  content: string;
371
- flattenContent?: string;
382
+ _flattenContent?: string;
372
383
  _html: string;
373
384
  frontmatter: FrontMatterMeta;
374
385
  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,23 +5867,56 @@ 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## Others\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
5922
  const nav = navList[i];
@@ -5869,10 +5925,15 @@ function generateLlmsFullTxt(pageDataArray, navList) {
5869
5925
  const title = nav.text;
5870
5926
  lines.push(`# ${title}\n`);
5871
5927
  for (const page of pages){
5872
- lines.push(page.mdContent ?? page.flattenContent ?? page.content);
5928
+ lines.push(page.mdContent ?? page._flattenContent ?? page.content);
5873
5929
  lines.push('\n');
5874
5930
  }
5875
5931
  }
5932
+ lines.push('# Others\n');
5933
+ for (const page of others){
5934
+ lines.push(page.mdContent ?? page._flattenContent ?? page.content);
5935
+ lines.push('\n');
5936
+ }
5876
5937
  return lines.join('\n');
5877
5938
  }
5878
5939
  function default_ok() {}
@@ -20842,25 +20903,36 @@ function mdxToMd(content, filepath, docDirectory, routeService) {
20842
20903
  path: filepath
20843
20904
  });
20844
20905
  }
20845
- const rsbuildPluginLlms = ({ pageDataList, routes, baseRef, docDirectoryRef, routeServiceRef, nav, output })=>({
20906
+ const rsbuildPluginLlms = ({ pageDataList, routes, titleRef, descriptionRef, sidebar, baseRef, docDirectoryRef, routeServiceRef, nav, rspressPluginOptions })=>({
20846
20907
  name: 'rsbuild-plugin-llms',
20847
20908
  async setup (api) {
20848
- const { llmsTxt, mdFiles, llmsFullTxt } = output;
20909
+ const { llmsTxt = true, mdFiles = true, llmsFullTxt = true, exclude } = rspressPluginOptions;
20849
20910
  api.onBeforeBuild(async ()=>{
20850
20911
  const base = baseRef.current;
20851
20912
  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([]);
20913
+ const newPageDataList = mergeRouteMetaWithPageData(routes, pageDataList, exclude);
20914
+ const navList = Array.isArray(nav) ? nav.map((i)=>{
20915
+ const nav = i.nav.default;
20916
+ const lang = i.lang;
20917
+ return nav.map((i)=>({
20918
+ ...i,
20919
+ lang
20920
+ }));
20921
+ }).flat().filter((i)=>i.activeMatch || i.link) : [];
20922
+ const others = [];
20923
+ const pageArray = new Array(navList.length).fill(0).map(()=>[]);
20855
20924
  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
- });
20925
+ const { routePath, lang } = pageData;
20926
+ for(let i = 0; i < pageArray.length; i++){
20927
+ const pageArrayItem = pageArray[i];
20928
+ const navItem = navList[i];
20929
+ if (lang === navItem.lang && new RegExp(navItem.activeMatch ?? navItem.link).test(removeBase(routePath, base))) return void pageArrayItem.push(pageData);
20930
+ }
20931
+ others.push(pageData);
20861
20932
  });
20933
+ for (const array of pageArray)organizeBySidebar(sidebar, array, base);
20862
20934
  if (llmsTxt) {
20863
- const llmsTxtContent = generateLlmsTxt(pageArray, navList);
20935
+ const llmsTxtContent = generateLlmsTxt(pageArray, navList, others, rspressPluginOptions.llmsTxt ?? {}, titleRef.current, descriptionRef.current);
20864
20936
  api.processAssets({
20865
20937
  targets: [
20866
20938
  'node'
@@ -20875,7 +20947,7 @@ const rsbuildPluginLlms = ({ pageDataList, routes, baseRef, docDirectoryRef, rou
20875
20947
  await Promise.all([
20876
20948
  ...newPageDataList.values()
20877
20949
  ].map(async (pageData)=>{
20878
- const content = pageData.flattenContent ?? pageData.content;
20950
+ const content = pageData._flattenContent ?? pageData.content;
20879
20951
  const filepath = pageData._filepath;
20880
20952
  const isMD = 'md' === __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].extname(filepath).slice(1);
20881
20953
  let mdContent;
@@ -20903,7 +20975,7 @@ const rsbuildPluginLlms = ({ pageDataList, routes, baseRef, docDirectoryRef, rou
20903
20975
  });
20904
20976
  });
20905
20977
  if (llmsFullTxt) {
20906
- const llmsFullTxtContent = generateLlmsFullTxt(pageArray, navList);
20978
+ const llmsFullTxtContent = generateLlmsFullTxt(pageArray, navList, others);
20907
20979
  api.processAssets({
20908
20980
  targets: [
20909
20981
  'node'
@@ -20917,8 +20989,13 @@ const rsbuildPluginLlms = ({ pageDataList, routes, baseRef, docDirectoryRef, rou
20917
20989
  });
20918
20990
  }
20919
20991
  });
20920
- function mergeRouteMetaWithPageData(routeMetaList, pageDataList) {
20921
- const m = new Map(pageDataList.map((pageData)=>[
20992
+ function mergeRouteMetaWithPageData(routeMetaList, pageDataList, exclude) {
20993
+ const m = new Map(pageDataList.filter((pageData)=>{
20994
+ if (exclude) return !exclude?.({
20995
+ page: pageData
20996
+ });
20997
+ return true;
20998
+ }).map((pageData)=>[
20922
20999
  pageData.routePath,
20923
21000
  pageData
20924
21001
  ]));
@@ -20929,16 +21006,42 @@ function mergeRouteMetaWithPageData(routeMetaList, pageDataList) {
20929
21006
  });
20930
21007
  return mergedPageDataList;
20931
21008
  }
20932
- function pluginLlms(options = {
20933
- output: {}
20934
- }) {
20935
- const { llmsTxt = true, llmsFullTxt = true, mdFiles = true } = options.output;
21009
+ function flatSidebar(sidebar) {
21010
+ if (!sidebar) return [];
21011
+ return sidebar.flatMap((i)=>{
21012
+ if ('string' == typeof i) return i;
21013
+ if ('link' in i && 'string' == typeof i.link) return [
21014
+ i.link,
21015
+ ...flatSidebar(i?.items ?? [])
21016
+ ];
21017
+ if ('items' in i && Array.isArray(i.items)) return flatSidebar(i.items);
21018
+ }).filter(Boolean);
21019
+ }
21020
+ function organizeBySidebar(sidebar, pages, base) {
21021
+ if (0 === pages.length) return;
21022
+ const pageItem = pages[0];
21023
+ const currSidebar = getSidebarDataGroup(sidebar, pageItem.routePath, base);
21024
+ if (0 === currSidebar.length) return;
21025
+ const orderList = flatSidebar(currSidebar);
21026
+ pages.sort((a, b)=>{
21027
+ const aIndex = orderList.findIndex((order)=>(0, __WEBPACK_EXTERNAL_MODULE__rspress_runtime_server_673c713a__.matchPath)(order, a.routePath));
21028
+ const bIndex = orderList.findIndex((order)=>(0, __WEBPACK_EXTERNAL_MODULE__rspress_runtime_server_673c713a__.matchPath)(order, b.routePath));
21029
+ return aIndex - bIndex;
21030
+ });
21031
+ }
21032
+ function pluginLlms(options = {}) {
20936
21033
  const baseRef = {
20937
21034
  current: ''
20938
21035
  };
20939
21036
  const docDirectoryRef = {
20940
21037
  current: ''
20941
21038
  };
21039
+ const titleRef = {
21040
+ current: ''
21041
+ };
21042
+ const descriptionRef = {
21043
+ current: ''
21044
+ };
20942
21045
  const pageDataList = [];
20943
21046
  const routes = [];
20944
21047
  const sidebar = {};
@@ -20958,9 +21061,18 @@ function pluginLlms(options = {
20958
21061
  if (isProd) routes.push(..._routes);
20959
21062
  },
20960
21063
  beforeBuild (config) {
20961
- Object.assign(sidebar, config.themeConfig?.sidebar ?? {});
20962
- const configNav = config.themeConfig?.locales?.map((i)=>i.nav).filter(Boolean);
21064
+ const configSidebar = config?.themeConfig?.locales?.map((i)=>i.sidebar).reduce((prev, curr)=>{
21065
+ Object.assign(prev, curr);
21066
+ return prev;
21067
+ }, {});
21068
+ Object.assign(sidebar, configSidebar);
21069
+ const configNav = config.themeConfig?.locales?.filter((i)=>Boolean(i.nav))?.map((i)=>({
21070
+ nav: i.nav,
21071
+ lang: i.lang
21072
+ }));
20963
21073
  nav.push(...configNav);
21074
+ titleRef.current = config.title;
21075
+ descriptionRef.current = config.description;
20964
21076
  baseRef.current = config.base ?? '/';
20965
21077
  docDirectoryRef.current = config.root ?? 'docs';
20966
21078
  },
@@ -20970,16 +21082,14 @@ function pluginLlms(options = {
20970
21082
  ...options,
20971
21083
  pageDataList,
20972
21084
  routes,
21085
+ titleRef,
21086
+ descriptionRef,
20973
21087
  sidebar,
20974
21088
  docDirectoryRef,
20975
21089
  routeServiceRef,
20976
21090
  nav,
20977
21091
  baseRef,
20978
- output: {
20979
- llmsFullTxt,
20980
- llmsTxt,
20981
- mdFiles
20982
- }
21092
+ rspressPluginOptions: options
20983
21093
  })
20984
21094
  ]
20985
21095
  }
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.1",
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.1"
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.1",
41
+ "@rspress/runtime": "^2.0.0-beta.1"
42
42
  },
43
43
  "engines": {
44
- "node": ">=14.17.6"
44
+ "node": ">=18.0.0"
45
45
  },
46
46
  "publishConfig": {
47
47
  "access": "public",