boltdocs 1.10.2 → 2.0.0

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.
Files changed (250) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/dist/cache-7G6D532T.mjs +1 -0
  4. package/dist/chunk-A4HQPEPU.mjs +1 -0
  5. package/dist/chunk-BA5NH5HU.mjs +1 -0
  6. package/dist/chunk-BQCD3DWG.mjs +1 -0
  7. package/dist/chunk-H63UMKYF.mjs +1 -0
  8. package/dist/chunk-IWHRQHS7.mjs +1 -0
  9. package/dist/chunk-JZXLCA2E.mjs +1 -0
  10. package/dist/chunk-MFU7Q6WF.mjs +1 -0
  11. package/dist/chunk-QYPNX5UN.mjs +1 -0
  12. package/dist/chunk-XEAPSFMB.mjs +1 -0
  13. package/dist/client/components/mdx/index.d.mts +209 -0
  14. package/dist/client/components/mdx/index.d.ts +209 -0
  15. package/dist/client/components/mdx/index.js +1 -0
  16. package/dist/client/components/mdx/index.mjs +1 -0
  17. package/dist/client/hooks/index.d.mts +133 -0
  18. package/dist/client/hooks/index.d.ts +133 -0
  19. package/dist/client/hooks/index.js +1 -0
  20. package/dist/client/hooks/index.mjs +1 -0
  21. package/dist/client/index.d.mts +138 -298
  22. package/dist/client/index.d.ts +138 -298
  23. package/dist/client/index.js +1 -3630
  24. package/dist/client/index.mjs +1 -697
  25. package/dist/client/ssr.d.mts +7 -3
  26. package/dist/client/ssr.d.ts +7 -3
  27. package/dist/client/ssr.js +1 -2928
  28. package/dist/client/ssr.mjs +1 -33
  29. package/dist/{config-BsFQ-ErD.d.ts → config-CX4l-ZNp.d.mts} +42 -35
  30. package/dist/{config-BsFQ-ErD.d.mts → config-CX4l-ZNp.d.ts} +42 -35
  31. package/dist/node/index.d.mts +2 -4
  32. package/dist/node/index.d.ts +2 -4
  33. package/dist/node/index.js +31 -1161
  34. package/dist/node/index.mjs +31 -736
  35. package/dist/search-dialog-EB3N4TYM.mjs +1 -0
  36. package/dist/types-BuZWFT7r.d.ts +159 -0
  37. package/dist/types-CvT-SGbK.d.mts +159 -0
  38. package/dist/use-routes-5bAtAAYX.d.mts +30 -0
  39. package/dist/use-routes-BefRXY3v.d.ts +30 -0
  40. package/package.json +34 -12
  41. package/src/client/app/config-context.tsx +18 -0
  42. package/src/client/app/docs-layout.tsx +14 -0
  43. package/src/client/app/index.tsx +137 -262
  44. package/src/client/app/mdx-component.tsx +52 -0
  45. package/src/client/app/mdx-components-context.tsx +23 -0
  46. package/src/client/app/mdx-page.tsx +20 -0
  47. package/src/client/app/preload.tsx +38 -30
  48. package/src/client/app/router.tsx +30 -0
  49. package/src/client/app/scroll-handler.tsx +40 -0
  50. package/src/client/app/theme-context.tsx +75 -0
  51. package/src/client/components/default-layout.tsx +80 -0
  52. package/src/client/components/docs-layout.tsx +105 -0
  53. package/src/client/components/icons-dev.tsx +74 -0
  54. package/src/client/components/mdx/admonition.tsx +107 -0
  55. package/src/client/components/mdx/badge.tsx +41 -0
  56. package/src/client/components/mdx/button.tsx +35 -0
  57. package/src/client/components/mdx/card.tsx +124 -0
  58. package/src/client/components/mdx/code-block.tsx +119 -0
  59. package/src/client/components/mdx/component-preview.tsx +47 -0
  60. package/src/client/components/mdx/component-props.tsx +83 -0
  61. package/src/client/components/mdx/field.tsx +66 -0
  62. package/src/client/components/mdx/file-tree.tsx +287 -0
  63. package/src/client/components/mdx/hooks/use-code-block.ts +56 -0
  64. package/src/client/components/mdx/hooks/use-component-preview.ts +16 -0
  65. package/src/client/components/mdx/hooks/useTable.ts +74 -0
  66. package/src/client/components/mdx/hooks/useTabs.ts +68 -0
  67. package/src/client/components/mdx/image.tsx +23 -0
  68. package/src/client/components/mdx/index.ts +53 -0
  69. package/src/client/components/mdx/link.tsx +38 -0
  70. package/src/client/components/mdx/list.tsx +192 -0
  71. package/src/client/components/mdx/table.tsx +156 -0
  72. package/src/client/components/mdx/tabs.tsx +135 -0
  73. package/src/client/components/mdx/video.tsx +68 -0
  74. package/src/client/components/primitives/breadcrumbs.tsx +79 -0
  75. package/src/client/components/primitives/button-group.tsx +54 -0
  76. package/src/client/components/primitives/button.tsx +145 -0
  77. package/src/client/components/primitives/helpers/observer.ts +120 -0
  78. package/src/client/components/primitives/index.ts +17 -0
  79. package/src/client/components/primitives/link.tsx +122 -0
  80. package/src/client/components/primitives/menu.tsx +159 -0
  81. package/src/client/components/primitives/navbar.tsx +359 -0
  82. package/src/client/components/primitives/navigation-menu.tsx +116 -0
  83. package/src/client/components/primitives/on-this-page.tsx +461 -0
  84. package/src/client/components/primitives/page-nav.tsx +87 -0
  85. package/src/client/components/primitives/popover.tsx +47 -0
  86. package/src/client/components/primitives/search-dialog.tsx +183 -0
  87. package/src/client/components/primitives/sidebar.tsx +154 -0
  88. package/src/client/components/primitives/tabs.tsx +90 -0
  89. package/src/client/components/primitives/tooltip.tsx +83 -0
  90. package/src/client/components/primitives/types.ts +11 -0
  91. package/src/client/components/ui-base/breadcrumbs.tsx +42 -0
  92. package/src/client/components/ui-base/copy-markdown.tsx +112 -0
  93. package/src/client/components/ui-base/error-boundary.tsx +52 -0
  94. package/src/client/components/ui-base/github-stars.tsx +27 -0
  95. package/src/client/components/ui-base/head.tsx +69 -0
  96. package/src/client/components/ui-base/loading.tsx +87 -0
  97. package/src/client/components/ui-base/navbar.tsx +138 -0
  98. package/src/client/components/ui-base/not-found.tsx +24 -0
  99. package/src/client/components/ui-base/on-this-page.tsx +152 -0
  100. package/src/client/components/ui-base/page-nav.tsx +39 -0
  101. package/src/client/components/ui-base/powered-by.tsx +19 -0
  102. package/src/client/components/ui-base/progress-bar.tsx +67 -0
  103. package/src/client/components/ui-base/search-dialog.tsx +82 -0
  104. package/src/client/components/ui-base/sidebar.tsx +104 -0
  105. package/src/client/components/ui-base/tabs.tsx +65 -0
  106. package/src/client/components/ui-base/theme-toggle.tsx +32 -0
  107. package/src/client/hooks/index.ts +12 -0
  108. package/src/client/hooks/use-breadcrumbs.ts +22 -0
  109. package/src/client/hooks/use-i18n.ts +84 -0
  110. package/src/client/hooks/use-localized-to.ts +95 -0
  111. package/src/client/hooks/use-location.ts +5 -0
  112. package/src/client/hooks/use-navbar.ts +60 -0
  113. package/src/client/hooks/use-onthispage.ts +23 -0
  114. package/src/client/hooks/use-page-nav.ts +22 -0
  115. package/src/client/hooks/use-routes.ts +72 -0
  116. package/src/client/hooks/use-search.ts +71 -0
  117. package/src/client/hooks/use-sidebar.ts +49 -0
  118. package/src/client/hooks/use-tabs.ts +43 -0
  119. package/src/client/hooks/use-version.ts +78 -0
  120. package/src/client/index.ts +55 -17
  121. package/src/client/integrations/codesandbox.ts +179 -0
  122. package/src/client/ssr.tsx +27 -16
  123. package/src/client/theme/neutral.css +360 -0
  124. package/src/client/types.ts +131 -27
  125. package/src/client/utils/cn.ts +6 -0
  126. package/src/client/utils/copy-clipboard.ts +22 -0
  127. package/src/client/utils/get-base-file-path.ts +21 -0
  128. package/src/client/utils/github.ts +121 -0
  129. package/src/client/utils/use-on-change.ts +15 -0
  130. package/src/client/virtual.d.ts +24 -0
  131. package/src/node/cache.ts +156 -156
  132. package/src/node/config.ts +159 -103
  133. package/src/node/index.ts +13 -13
  134. package/src/node/mdx.ts +213 -61
  135. package/src/node/plugin/entry.ts +29 -18
  136. package/src/node/plugin/html.ts +11 -11
  137. package/src/node/plugin/index.ts +161 -84
  138. package/src/node/plugin/types.ts +2 -4
  139. package/src/node/routes/cache.ts +6 -6
  140. package/src/node/routes/index.ts +206 -113
  141. package/src/node/routes/parser.ts +102 -82
  142. package/src/node/routes/sorter.ts +15 -15
  143. package/src/node/routes/types.ts +24 -24
  144. package/src/node/ssg/index.ts +73 -47
  145. package/src/node/ssg/meta.ts +4 -4
  146. package/src/node/ssg/options.ts +5 -5
  147. package/src/node/ssg/sitemap.ts +14 -14
  148. package/src/node/utils.ts +54 -31
  149. package/tsconfig.json +25 -20
  150. package/tsup.config.ts +23 -14
  151. package/dist/PackageManagerTabs-NVT7G625.mjs +0 -99
  152. package/dist/SearchDialog-AGVF6JBO.mjs +0 -194
  153. package/dist/SearchDialog-YPDOM7Q6.css +0 -2847
  154. package/dist/Video-KNTY5BNO.mjs +0 -6
  155. package/dist/cache-KNL5B4EE.mjs +0 -12
  156. package/dist/chunk-7SFUJWTB.mjs +0 -211
  157. package/dist/chunk-FFBNU6IJ.mjs +0 -386
  158. package/dist/chunk-FMTOYQLO.mjs +0 -37
  159. package/dist/chunk-TKLQWU7H.mjs +0 -1920
  160. package/dist/chunk-Z7JHYNAS.mjs +0 -57
  161. package/dist/client/index.css +0 -2847
  162. package/dist/client/ssr.css +0 -2847
  163. package/dist/types-Dj-bfnC3.d.mts +0 -74
  164. package/dist/types-Dj-bfnC3.d.ts +0 -74
  165. package/src/client/theme/components/CodeBlock/CodeBlock.tsx +0 -61
  166. package/src/client/theme/components/CodeBlock/index.ts +0 -1
  167. package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +0 -131
  168. package/src/client/theme/components/PackageManagerTabs/index.ts +0 -1
  169. package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +0 -64
  170. package/src/client/theme/components/Playground/Playground.tsx +0 -180
  171. package/src/client/theme/components/Playground/index.ts +0 -1
  172. package/src/client/theme/components/Playground/playground.css +0 -238
  173. package/src/client/theme/components/Video/Video.tsx +0 -84
  174. package/src/client/theme/components/Video/index.ts +0 -1
  175. package/src/client/theme/components/Video/video.css +0 -41
  176. package/src/client/theme/components/mdx/Admonition.tsx +0 -80
  177. package/src/client/theme/components/mdx/Badge.tsx +0 -31
  178. package/src/client/theme/components/mdx/Button.tsx +0 -50
  179. package/src/client/theme/components/mdx/Card.tsx +0 -80
  180. package/src/client/theme/components/mdx/Field.tsx +0 -60
  181. package/src/client/theme/components/mdx/FileTree.tsx +0 -229
  182. package/src/client/theme/components/mdx/List.tsx +0 -57
  183. package/src/client/theme/components/mdx/Table.tsx +0 -151
  184. package/src/client/theme/components/mdx/Tabs.tsx +0 -123
  185. package/src/client/theme/components/mdx/index.ts +0 -27
  186. package/src/client/theme/components/mdx/mdx-components.css +0 -764
  187. package/src/client/theme/icons/bun.tsx +0 -62
  188. package/src/client/theme/icons/deno.tsx +0 -20
  189. package/src/client/theme/icons/discord.tsx +0 -12
  190. package/src/client/theme/icons/github.tsx +0 -15
  191. package/src/client/theme/icons/npm.tsx +0 -13
  192. package/src/client/theme/icons/pnpm.tsx +0 -72
  193. package/src/client/theme/icons/twitter.tsx +0 -12
  194. package/src/client/theme/styles/markdown.css +0 -394
  195. package/src/client/theme/styles/variables.css +0 -175
  196. package/src/client/theme/styles.css +0 -39
  197. package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +0 -68
  198. package/src/client/theme/ui/Breadcrumbs/index.ts +0 -1
  199. package/src/client/theme/ui/CopyMarkdown/CopyMarkdown.tsx +0 -82
  200. package/src/client/theme/ui/CopyMarkdown/copy-markdown.css +0 -112
  201. package/src/client/theme/ui/CopyMarkdown/index.ts +0 -1
  202. package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +0 -50
  203. package/src/client/theme/ui/ErrorBoundary/error-boundary.css +0 -55
  204. package/src/client/theme/ui/ErrorBoundary/index.ts +0 -1
  205. package/src/client/theme/ui/Footer/footer.css +0 -32
  206. package/src/client/theme/ui/Head/Head.tsx +0 -69
  207. package/src/client/theme/ui/Head/index.ts +0 -1
  208. package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +0 -125
  209. package/src/client/theme/ui/LanguageSwitcher/index.ts +0 -1
  210. package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +0 -98
  211. package/src/client/theme/ui/Layout/Layout.tsx +0 -203
  212. package/src/client/theme/ui/Layout/base.css +0 -106
  213. package/src/client/theme/ui/Layout/index.ts +0 -2
  214. package/src/client/theme/ui/Layout/pagination.css +0 -72
  215. package/src/client/theme/ui/Layout/responsive.css +0 -47
  216. package/src/client/theme/ui/Link/Link.tsx +0 -392
  217. package/src/client/theme/ui/Link/LinkPreview.tsx +0 -59
  218. package/src/client/theme/ui/Link/index.ts +0 -2
  219. package/src/client/theme/ui/Link/link-preview.css +0 -48
  220. package/src/client/theme/ui/Loading/Loading.tsx +0 -10
  221. package/src/client/theme/ui/Loading/index.ts +0 -1
  222. package/src/client/theme/ui/Loading/loading.css +0 -30
  223. package/src/client/theme/ui/Navbar/GithubStars.tsx +0 -27
  224. package/src/client/theme/ui/Navbar/Navbar.tsx +0 -193
  225. package/src/client/theme/ui/Navbar/Tabs.tsx +0 -99
  226. package/src/client/theme/ui/Navbar/index.ts +0 -2
  227. package/src/client/theme/ui/Navbar/navbar.css +0 -347
  228. package/src/client/theme/ui/NotFound/NotFound.tsx +0 -19
  229. package/src/client/theme/ui/NotFound/index.ts +0 -1
  230. package/src/client/theme/ui/NotFound/not-found.css +0 -64
  231. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +0 -244
  232. package/src/client/theme/ui/OnThisPage/index.ts +0 -1
  233. package/src/client/theme/ui/OnThisPage/toc.css +0 -152
  234. package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +0 -18
  235. package/src/client/theme/ui/PoweredBy/index.ts +0 -1
  236. package/src/client/theme/ui/PoweredBy/powered-by.css +0 -76
  237. package/src/client/theme/ui/ProgressBar/ProgressBar.css +0 -17
  238. package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +0 -51
  239. package/src/client/theme/ui/ProgressBar/index.ts +0 -1
  240. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +0 -209
  241. package/src/client/theme/ui/SearchDialog/index.ts +0 -1
  242. package/src/client/theme/ui/SearchDialog/search.css +0 -152
  243. package/src/client/theme/ui/Sidebar/Sidebar.tsx +0 -244
  244. package/src/client/theme/ui/Sidebar/index.ts +0 -1
  245. package/src/client/theme/ui/Sidebar/sidebar.css +0 -230
  246. package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +0 -69
  247. package/src/client/theme/ui/ThemeToggle/index.ts +0 -1
  248. package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +0 -136
  249. package/src/client/theme/ui/VersionSwitcher/index.ts +0 -1
  250. package/src/client/utils.ts +0 -49
@@ -1,4 +1,4 @@
1
- import { RouteMeta } from "./types";
1
+ import type { RouteMeta } from './types'
2
2
 
3
3
  /**
4
4
  * Sorts an array of generated routes.
@@ -11,32 +11,32 @@ import { RouteMeta } from "./types";
11
11
  export function sortRoutes(routes: RouteMeta[]): RouteMeta[] {
12
12
  return routes.sort((a, b) => {
13
13
  // Ungrouped first
14
- if (!a.group && !b.group) return compareByPosition(a, b);
15
- if (!a.group) return -1;
16
- if (!b.group) return 1;
14
+ if (!a.group && !b.group) return compareByPosition(a, b)
15
+ if (!a.group) return -1
16
+ if (!b.group) return 1
17
17
 
18
18
  // Different groups: sort by group position
19
19
  if (a.group !== b.group) {
20
- return compareByGroupPosition(a, b);
20
+ return compareByGroupPosition(a, b)
21
21
  }
22
22
 
23
23
  // Same group: sort by item position
24
- return compareByPosition(a, b);
25
- });
24
+ return compareByPosition(a, b)
25
+ })
26
26
  }
27
27
 
28
28
  function compareByPosition(a: RouteMeta, b: RouteMeta): number {
29
29
  if (a.sidebarPosition !== undefined && b.sidebarPosition !== undefined)
30
- return a.sidebarPosition - b.sidebarPosition;
31
- if (a.sidebarPosition !== undefined) return -1;
32
- if (b.sidebarPosition !== undefined) return 1;
33
- return a.title.localeCompare(b.title);
30
+ return a.sidebarPosition - b.sidebarPosition
31
+ if (a.sidebarPosition !== undefined) return -1
32
+ if (b.sidebarPosition !== undefined) return 1
33
+ return a.title.localeCompare(b.title)
34
34
  }
35
35
 
36
36
  function compareByGroupPosition(a: RouteMeta, b: RouteMeta): number {
37
37
  if (a.groupPosition !== undefined && b.groupPosition !== undefined)
38
- return a.groupPosition - b.groupPosition;
39
- if (a.groupPosition !== undefined) return -1;
40
- if (b.groupPosition !== undefined) return 1;
41
- return (a.groupTitle || a.group!).localeCompare(b.groupTitle || b.group!);
38
+ return a.groupPosition - b.groupPosition
39
+ if (a.groupPosition !== undefined) return -1
40
+ if (b.groupPosition !== undefined) return 1
41
+ return (a.groupTitle || a.group!).localeCompare(b.groupTitle || b.group!)
42
42
  }
@@ -4,41 +4,41 @@
4
4
  */
5
5
  export interface RouteMeta {
6
6
  /** The final URL path for the route (e.g., '/docs/guide/start') */
7
- path: string;
7
+ path: string
8
8
  /** The absolute filesystem path to the source markdown/mdx file */
9
- componentPath: string;
9
+ componentPath: string
10
10
  /** The title of the page, usually extracted from frontmatter or the filename */
11
- title: string;
11
+ title: string
12
12
  /** The relative path from the docs directory, used for edit links */
13
- filePath: string;
13
+ filePath: string
14
14
  /** Optional description of the page (for SEO/meta tags) */
15
- description?: string;
15
+ description?: string
16
16
  /** Optional explicit position for ordering in the sidebar */
17
- sidebarPosition?: number;
17
+ sidebarPosition?: number
18
18
  /** The group (directory) this route belongs to */
19
- group?: string;
19
+ group?: string
20
20
  /** The display title for the route's group */
21
- groupTitle?: string;
21
+ groupTitle?: string
22
22
  /** Optional explicit position for ordering the group itself */
23
- groupPosition?: number;
23
+ groupPosition?: number
24
24
  /** Optional icon for the route's group */
25
- groupIcon?: string;
25
+ groupIcon?: string
26
26
  /** Extracted markdown headings for search indexing */
27
- headings?: { level: number; text: string; id: string }[];
27
+ headings?: { level: number; text: string; id: string }[]
28
28
  /** The locale this route belongs to, if i18n is configured */
29
- locale?: string;
29
+ locale?: string
30
30
  /** The version this route belongs to, if versioning is configured */
31
- version?: string;
31
+ version?: string
32
32
  /** Optional badge to display next to the sidebar item (e.g., 'New', 'Experimental') */
33
- badge?: string | { text: string; expires?: string };
33
+ badge?: string | { text: string; expires?: string }
34
34
  /** Optional icon to display (Lucide icon name or raw SVG) */
35
- icon?: string;
35
+ icon?: string
36
36
  /** The tab this route belongs to, if tabs are configured */
37
- tab?: string;
37
+ tab?: string
38
38
  /** The extracted plain-text content of the page for search indexing */
39
- _content?: string;
39
+ _content?: string
40
40
  /** The raw markdown content of the page */
41
- _rawContent?: string;
41
+ _rawContent?: string
42
42
  }
43
43
 
44
44
  /**
@@ -47,15 +47,15 @@ export interface RouteMeta {
47
47
  */
48
48
  export interface ParsedDocFile {
49
49
  /** The core route metadata without group-level details (inferred later) */
50
- route: Omit<RouteMeta, "group" | "groupTitle" | "groupPosition">;
50
+ route: Omit<RouteMeta, 'group' | 'groupTitle' | 'groupPosition'>
51
51
  /** The base directory of the file (used to group files together) */
52
- relativeDir?: string;
52
+ relativeDir?: string
53
53
  /** Whether this file is the index file for its directory group */
54
- isGroupIndex: boolean;
54
+ isGroupIndex: boolean
55
55
  /** If this is a group index, any specific frontmatter metadata dictating the group's title and position */
56
- groupMeta?: { title: string; position?: number; icon?: string };
56
+ groupMeta?: { title: string; position?: number; icon?: string }
57
57
  /** Extracted group position from the directory name if it has a numeric prefix */
58
- inferredGroupPosition?: number;
58
+ inferredGroupPosition?: number
59
59
  /** Extracted tab name from the directory name if it follows the (tab-name) syntax */
60
- inferredTab?: string;
60
+ inferredTab?: string
61
61
  }
@@ -1,21 +1,21 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { generateRoutes } from "../routes";
4
- import { escapeHtml } from "../utils";
5
- import { fileURLToPath } from "url";
6
- import { createRequire } from "module";
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { generateRoutes } from '../routes'
4
+ import { escapeHtml } from '../utils'
5
+ import { fileURLToPath } from 'url'
6
+ import { createRequire } from 'module'
7
7
 
8
- import { SSGOptions } from "./options";
9
- import { replaceMetaTags } from "./meta";
10
- import { generateSitemap } from "./sitemap";
8
+ import type { SSGOptions } from './options'
9
+ import { replaceMetaTags } from './meta'
10
+ import { generateSitemap } from './sitemap'
11
11
 
12
12
  // Re-export options for consumers
13
- export type { SSGOptions };
13
+ export type { SSGOptions }
14
14
 
15
15
  // Polyfill __dirname and require for ESM
16
- const _filename = fileURLToPath(import.meta.url);
17
- const _dirname = path.dirname(_filename);
18
- const _require = createRequire(import.meta.url);
16
+ const _filename = fileURLToPath(import.meta.url)
17
+ const _dirname = path.dirname(_filename)
18
+ const _require = createRequire(import.meta.url)
19
19
 
20
20
  /**
21
21
  * Generates static HTML files and a \`sitemap.xml\` for all documentation routes.
@@ -24,40 +24,66 @@ const _require = createRequire(import.meta.url);
24
24
  * @param options - Configuration for paths and site metadata
25
25
  */
26
26
  export async function generateStaticPages(options: SSGOptions): Promise<void> {
27
- const { docsDir, docsDirName, outDir, config } = options;
28
- const routes = await generateRoutes(docsDir, config);
29
- const siteTitle = config?.themeConfig?.title || "Boltdocs";
30
- const siteDescription = config?.themeConfig?.description || "";
27
+ const { docsDir, docsDirName, outDir, config } = options
28
+ const routes = await generateRoutes(docsDir, config)
29
+ const siteTitle = config?.themeConfig?.title || 'Boltdocs'
30
+ const siteDescription = config?.themeConfig?.description || ''
31
31
 
32
32
  // Resolve the SSR module (compiled by tsup)
33
- const ssrModulePath = path.resolve(_dirname, "../client/ssr.js");
33
+ const ssrModulePath = path.resolve(_dirname, '../client/ssr.js')
34
34
  if (!fs.existsSync(ssrModulePath)) {
35
35
  console.error(
36
- "[boltdocs] SSR module not found at",
36
+ '[boltdocs] SSR module not found at',
37
37
  ssrModulePath,
38
- "- Did you build the core package?",
39
- );
40
- return;
38
+ '- Did you build the core package?',
39
+ )
40
+ return
41
41
  }
42
- const { render } = _require(ssrModulePath);
42
+
43
+ // Mock require so Node doesn't choke on virtual modules compiled externally
44
+ const Module = _require('module')
45
+ const originalRequire = Module.prototype.require
46
+ ;(Module.prototype as any).require = function (id: string, ...args: any[]) {
47
+ if (id === 'virtual:boltdocs-layout') {
48
+ return {
49
+ __esModule: true,
50
+ default: function SSG_Virtual_Layout(props: any) {
51
+ try {
52
+ const client = originalRequire.apply(this, [path.resolve(_dirname, '../client/index.js')])
53
+ const Comp = client.DefaultLayout || (({ children }: any) => children)
54
+ const React = originalRequire.apply(this, ['react'])
55
+ return React.createElement(Comp, props)
56
+ } catch (e) {
57
+ return props.children
58
+ }
59
+ }
60
+ }
61
+ }
62
+ return originalRequire.apply(this, [id, ...args])
63
+ }
64
+
65
+ const { render } = _require(ssrModulePath)
66
+
67
+ // Restore require after loading the module
68
+ ;(Module.prototype as any).require = originalRequire
43
69
 
44
70
  // Read the built index.html as template
45
- const templatePath = path.join(outDir, "index.html");
71
+ const templatePath = path.join(outDir, 'index.html')
46
72
  if (!fs.existsSync(templatePath)) {
47
- console.warn("[boltdocs] No index.html found in outDir, skipping SSG.");
48
- return;
73
+ console.warn('[boltdocs] No index.html found in outDir, skipping SSG.')
74
+ return
49
75
  }
50
- const template = fs.readFileSync(templatePath, "utf-8");
76
+ const template = fs.readFileSync(templatePath, 'utf-8')
51
77
 
52
78
  // Generate an HTML file for each route concurrently
53
79
  await Promise.all(
54
80
  routes.map(async (route) => {
55
- const pageTitle = `${route.title} | ${siteTitle}`;
56
- const pageDescription = route.description || siteDescription;
81
+ const pageTitle = `${route.title} | ${siteTitle}`
82
+ const pageDescription = route.description || siteDescription
57
83
 
58
84
  // We mock the modules for SSR so it doesn't crash trying to dynamically import
59
- const fakeModules: Record<string, any> = {};
60
- fakeModules[route.componentPath] = { default: () => {} }; // Mock MDX component
85
+ const fakeModules: Record<string, any> = {}
86
+ fakeModules[`/${docsDirName}/${route.filePath}`] = { default: () => null } // Mock MDX component
61
87
 
62
88
  try {
63
89
  const appHtml = await render({
@@ -67,40 +93,40 @@ export async function generateStaticPages(options: SSGOptions): Promise<void> {
67
93
  docsDirName: docsDirName,
68
94
  modules: fakeModules,
69
95
  homePage: undefined, // No custom home page for now
70
- });
96
+ })
71
97
 
72
98
  const html = replaceMetaTags(template, {
73
99
  title: escapeHtml(pageTitle),
74
100
  description: escapeHtml(pageDescription),
75
101
  })
76
- .replace("<!--app-html-->", appHtml)
77
- .replace(`<div id="root"></div>`, `<div id="root">${appHtml}</div>`);
102
+ .replace('<!--app-html-->', appHtml)
103
+ .replace(`<div id="root"></div>`, `<div id="root">${appHtml}</div>`)
78
104
 
79
- const routeDir = path.join(outDir, route.path);
80
- await fs.promises.mkdir(routeDir, { recursive: true });
105
+ const routeDir = path.join(outDir, route.path)
106
+ await fs.promises.mkdir(routeDir, { recursive: true })
81
107
  await fs.promises.writeFile(
82
- path.join(routeDir, "index.html"),
108
+ path.join(routeDir, 'index.html'),
83
109
  html,
84
- "utf-8",
85
- );
86
- } catch (e) {
87
- console.error(`[boltdocs] Error SSR rendering route ${route.path}:`, e);
110
+ 'utf-8',
111
+ )
112
+ } catch (e: any) {
113
+ console.error(`[boltdocs] Error SSR rendering route ${route.path}:`, e ? e.stack || e : e)
88
114
  }
89
115
  }),
90
- );
116
+ )
91
117
 
92
118
  // Generate sitemap.xml
93
119
  const sitemap = generateSitemap(
94
120
  routes.map((r) => r.path),
95
121
  config,
96
- );
97
- fs.writeFileSync(path.join(outDir, "sitemap.xml"), sitemap, "utf-8");
122
+ )
123
+ fs.writeFileSync(path.join(outDir, 'sitemap.xml'), sitemap, 'utf-8')
98
124
 
99
125
  console.log(
100
126
  `[boltdocs] Generated ${routes.length} static pages + sitemap.xml`,
101
- );
127
+ )
102
128
 
103
129
  // Ensure all cache operations (like index persistence) are finished
104
- const { flushCache } = await import("../cache");
105
- await flushCache();
130
+ const { flushCache } = await import('../cache')
131
+ await flushCache()
106
132
  }
@@ -1,4 +1,4 @@
1
- import { escapeHtml } from "../utils";
1
+ import { escapeHtml } from '../utils'
2
2
 
3
3
  /**
4
4
  * Replaces placeholder or default meta tags in the HTML template with page-specific values.
@@ -11,8 +11,8 @@ export function replaceMetaTags(
11
11
  html: string,
12
12
  meta: { title: string; description: string },
13
13
  ): string {
14
- const title = escapeHtml(meta.title);
15
- const description = escapeHtml(meta.description);
14
+ const title = escapeHtml(meta.title)
15
+ const description = escapeHtml(meta.description)
16
16
 
17
17
  return html
18
18
  .replace(/<title>.*?<\/title>/, `<title>${title}</title>`)
@@ -29,5 +29,5 @@ export function replaceMetaTags(
29
29
  .replace(
30
30
  /(<meta name="twitter:description" content=")[^"]*(")/,
31
31
  `$1${description}$2`,
32
- );
32
+ )
33
33
  }
@@ -1,15 +1,15 @@
1
- import { BoltdocsConfig } from "../config";
1
+ import type { BoltdocsConfig } from '../config'
2
2
 
3
3
  /**
4
4
  * Options for the Static Site Generation process.
5
5
  */
6
6
  export interface SSGOptions {
7
7
  /** The root directory containing markdown documentation files */
8
- docsDir: string;
8
+ docsDir: string
9
9
  /** The name of the documentation directory (e.g. 'docs') */
10
- docsDirName: string;
10
+ docsDirName: string
11
11
  /** The output directory where Vite placed the compiled `index.html` and assets */
12
- outDir: string;
12
+ outDir: string
13
13
  /** Pre-resolved config (avoids re-resolving during the SSG phase) */
14
- config?: BoltdocsConfig;
14
+ config?: BoltdocsConfig
15
15
  }
@@ -1,5 +1,5 @@
1
- import { BoltdocsConfig } from "../config";
2
- import { escapeXml } from "../utils";
1
+ import type { BoltdocsConfig } from '../config'
2
+ import { escapeXml } from '../utils'
3
3
 
4
4
  /**
5
5
  * Generates a standard XML sitemap for search engine crawlers.
@@ -12,20 +12,20 @@ export function generateSitemap(
12
12
  routePaths: string[],
13
13
  config?: BoltdocsConfig,
14
14
  ): string {
15
- const baseUrl = config?.siteUrl?.replace(/\/$/, "") || "https://example.com";
16
- const today = new Date().toISOString().split("T")[0];
15
+ const baseUrl = config?.siteUrl?.replace(/\/$/, '') || 'https://example.com'
16
+ const today = new Date().toISOString().split('T')[0]
17
17
 
18
- const rootEntries = [{ url: "/", priority: "1.0", changefreq: "daily" }];
18
+ const rootEntries = [{ url: '/', priority: '1.0', changefreq: 'daily' }]
19
19
 
20
20
  if (config?.i18n) {
21
- const defaultLocale = config.i18n.defaultLocale;
21
+ const defaultLocale = config.i18n.defaultLocale
22
22
  for (const locale of Object.keys(config.i18n.locales)) {
23
23
  if (locale !== defaultLocale) {
24
24
  rootEntries.push({
25
25
  url: `/${locale}/`,
26
- priority: "1.0",
27
- changefreq: "daily",
28
- });
26
+ priority: '1.0',
27
+ changefreq: 'daily',
28
+ })
29
29
  }
30
30
  }
31
31
  }
@@ -34,10 +34,10 @@ export function generateSitemap(
34
34
  ...rootEntries,
35
35
  ...routePaths.map((p) => ({
36
36
  url: p,
37
- priority: "0.8",
38
- changefreq: "weekly",
37
+ priority: '0.8',
38
+ changefreq: 'weekly',
39
39
  })),
40
- ];
40
+ ]
41
41
 
42
42
  return `<?xml version="1.0" encoding="UTF-8"?>
43
43
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
@@ -50,6 +50,6 @@ ${entries
50
50
  <priority>${e.priority}</priority>
51
51
  </url>`,
52
52
  )
53
- .join("\n")}
54
- </urlset>`;
53
+ .join('\n')}
54
+ </urlset>`
55
55
  }
package/src/node/utils.ts CHANGED
@@ -1,5 +1,6 @@
1
- import fs from "fs";
2
- import matter from "gray-matter";
1
+ import fs from 'fs'
2
+ import matter from 'gray-matter'
3
+ import DOMPurify from 'isomorphic-dompurify'
3
4
 
4
5
  /**
5
6
  * Normalizes a file path by replacing Windows backslashes with forward slashes.
@@ -9,7 +10,7 @@ import matter from "gray-matter";
9
10
  * @returns The normalized path using forward slashes
10
11
  */
11
12
  export function normalizePath(p: string): string {
12
- return p.replace(/\\/g, "/");
13
+ return p.replace(/\\/g, '/')
13
14
  }
14
15
 
15
16
  /**
@@ -19,7 +20,7 @@ export function normalizePath(p: string): string {
19
20
  * @returns The name without the numeric prefix
20
21
  */
21
22
  export function stripNumberPrefix(name: string): string {
22
- return name.replace(/^\d+\./, "");
23
+ return name.replace(/^\d+\./, '')
23
24
  }
24
25
 
25
26
  /**
@@ -29,8 +30,8 @@ export function stripNumberPrefix(name: string): string {
29
30
  * @returns The extracted number, or undefined if none exists
30
31
  */
31
32
  export function extractNumberPrefix(name: string): number | undefined {
32
- const match = name.match(/^(\d+)\./);
33
- return match ? parseInt(match[1], 10) : undefined;
33
+ const match = name.match(/^(\d+)\./)
34
+ return match ? parseInt(match[1], 10) : undefined
34
35
  }
35
36
 
36
37
  /**
@@ -40,7 +41,7 @@ export function extractNumberPrefix(name: string): number | undefined {
40
41
  * @returns True if the file ends with .md or .mdx, false otherwise
41
42
  */
42
43
  export function isDocFile(filePath: string): boolean {
43
- return /\.mdx?$/.test(filePath);
44
+ return /\.mdx?$/.test(filePath)
44
45
  }
45
46
 
46
47
  /**
@@ -53,9 +54,9 @@ export function isDocFile(filePath: string): boolean {
53
54
  */
54
55
  export function getFileMtime(filePath: string): number {
55
56
  try {
56
- return fs.statSync(filePath).mtimeMs;
57
+ return fs.statSync(filePath).mtimeMs
57
58
  } catch {
58
- return 0;
59
+ return 0
59
60
  }
60
61
  }
61
62
 
@@ -67,12 +68,12 @@ export function getFileMtime(filePath: string): number {
67
68
  * @returns An object containing the parsed metadata (`data`) and the raw markdown (`content`)
68
69
  */
69
70
  export function parseFrontmatter(filePath: string): {
70
- data: Record<string, any>;
71
- content: string;
71
+ data: Record<string, any>
72
+ content: string
72
73
  } {
73
- const raw = fs.readFileSync(filePath, "utf-8");
74
- const { data, content } = matter(raw);
75
- return { data, content };
74
+ const raw = fs.readFileSync(filePath, 'utf-8')
75
+ const { data, content } = matter(raw)
76
+ return { data, content }
76
77
  }
77
78
 
78
79
  /**
@@ -84,11 +85,11 @@ export function parseFrontmatter(filePath: string): {
84
85
  */
85
86
  export function escapeHtml(str: string): string {
86
87
  return str
87
- .replace(/&/g, "&amp;")
88
- .replace(/"/g, "&quot;")
89
- .replace(/'/g, "&apos;")
90
- .replace(/</g, "&lt;")
91
- .replace(/>/g, "&gt;");
88
+ .replace(/&/g, '&amp;')
89
+ .replace(/"/g, '&quot;')
90
+ .replace(/'/g, '&apos;')
91
+ .replace(/</g, '&lt;')
92
+ .replace(/>/g, '&gt;')
92
93
  }
93
94
 
94
95
  /**
@@ -98,7 +99,7 @@ export function escapeHtml(str: string): string {
98
99
  * @returns The escaped string
99
100
  */
100
101
  export function escapeXml(str: string): string {
101
- return escapeHtml(str);
102
+ return escapeHtml(str)
102
103
  }
103
104
 
104
105
  /**
@@ -111,29 +112,51 @@ export function escapeXml(str: string): string {
111
112
  */
112
113
  export function fileToRoutePath(relativePath: string): string {
113
114
  // Strip number prefixes from every segment
114
- let cleanedPath = relativePath.split("/").map(stripNumberPrefix).join("/");
115
+ let cleanedPath = relativePath.split('/').map(stripNumberPrefix).join('/')
115
116
 
116
117
  // Remove trailing slash if present
117
- let routePath = cleanedPath.replace(/\/$/, "");
118
+ let routePath = cleanedPath.replace(/\/$/, '')
118
119
 
119
- routePath = routePath.replace(/\.mdx?$/, "");
120
+ routePath = routePath.replace(/\.mdx?$/, '')
120
121
 
121
122
  // Handle index files → directory root
122
- if (routePath === "index" || routePath.endsWith("/index")) {
123
- routePath = routePath.replace(/index$/, "");
123
+ if (routePath === 'index' || routePath.endsWith('/index')) {
124
+ routePath = routePath.replace(/index$/, '')
124
125
  }
125
126
 
126
127
  // Ensure leading slash
127
- if (!routePath.startsWith("/")) {
128
- routePath = "/" + routePath;
128
+ if (!routePath.startsWith('/')) {
129
+ routePath = '/' + routePath
129
130
  }
130
131
 
131
132
  // Remove trailing slash (except for root '/')
132
- if (routePath.length > 1 && routePath.endsWith("/")) {
133
- routePath = routePath.slice(0, -1);
133
+ if (routePath.length > 1 && routePath.endsWith('/')) {
134
+ routePath = routePath.slice(0, -1)
134
135
  }
135
136
 
136
- return routePath;
137
+ return routePath
138
+ }
139
+
140
+ /**
141
+ * Sanitizes an HTML string using DOMPurify to prevent XSS.
142
+ * By default, it allows a safe subset of HTML tags.
143
+ *
144
+ * @param html - The raw HTML string
145
+ * @returns The sanitized HTML
146
+ */
147
+ export function sanitizeHtml(html: string): string {
148
+ return DOMPurify.sanitize(html)
149
+ }
150
+
151
+ /**
152
+ * Strips all HTML tags from a string, returning only the text content.
153
+ * Uses DOMPurify for secure and complete tag removal.
154
+ *
155
+ * @param html - The string containing HTML tags
156
+ * @returns The plain text content
157
+ */
158
+ export function stripHtmlTags(html: string): string {
159
+ return DOMPurify.sanitize(html, { ALLOWED_TAGS: [], KEEP_CONTENT: true })
137
160
  }
138
161
 
139
162
  /**
@@ -144,5 +167,5 @@ export function fileToRoutePath(relativePath: string): string {
144
167
  * @returns The capitalized string
145
168
  */
146
169
  export function capitalize(str: string): string {
147
- return str.charAt(0).toUpperCase() + str.slice(1);
170
+ return str.charAt(0).toUpperCase() + str.slice(1)
148
171
  }
package/tsconfig.json CHANGED
@@ -1,21 +1,26 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "Bundler",
6
- "esModuleInterop": true,
7
- "strict": true,
8
- "skipLibCheck": true,
9
- "jsx": "react-jsx",
10
- "declaration": true,
11
- "outDir": "dist"
12
- },
13
- "include": [
14
- "src"
15
- ],
16
- "exclude": [
17
- "node_modules",
18
- "dist",
19
- "src/node/cli"
20
- ]
21
- }
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "jsx": "react-jsx",
10
+ "declaration": true,
11
+ "outDir": "dist",
12
+ "baseUrl": ".",
13
+ "paths": {
14
+ "@/*": ["src/*"],
15
+ "@client/*": ["src/client/*"],
16
+ "@node/*": ["src/node/*"],
17
+ "@theme/*": ["src/client/theme/*"],
18
+ "@components/*": ["src/client/components/*"],
19
+ "@integrations/*": ["src/client/integrations/*"],
20
+ "@hooks/*": ["src/client/hooks/*"],
21
+ "@primitives/*": ["src/client/components/primitives/*"]
22
+ }
23
+ },
24
+ "include": ["src"],
25
+ "exclude": ["node_modules", "dist", "src/node/cli"]
26
+ }