boltdocs 1.10.2 → 1.11.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 (225) hide show
  1. package/package.json +29 -7
  2. package/src/client/app/config-context.tsx +18 -0
  3. package/src/client/app/docs-layout.tsx +14 -0
  4. package/src/client/app/index.tsx +132 -260
  5. package/src/client/app/mdx-component.tsx +52 -0
  6. package/src/client/app/mdx-components-context.tsx +23 -0
  7. package/src/client/app/mdx-page.tsx +20 -0
  8. package/src/client/app/preload.tsx +38 -30
  9. package/src/client/app/router.tsx +30 -0
  10. package/src/client/app/scroll-handler.tsx +40 -0
  11. package/src/client/app/theme-context.tsx +75 -0
  12. package/src/client/components/default-layout.tsx +80 -0
  13. package/src/client/components/docs-layout.tsx +105 -0
  14. package/src/client/components/icons-dev.tsx +74 -0
  15. package/src/client/components/mdx/admonition.tsx +107 -0
  16. package/src/client/components/mdx/badge.tsx +41 -0
  17. package/src/client/components/mdx/button.tsx +35 -0
  18. package/src/client/components/mdx/card.tsx +124 -0
  19. package/src/client/components/mdx/code-block.tsx +119 -0
  20. package/src/client/components/mdx/component-preview.tsx +47 -0
  21. package/src/client/components/mdx/component-props.tsx +83 -0
  22. package/src/client/components/mdx/field.tsx +66 -0
  23. package/src/client/components/mdx/file-tree.tsx +287 -0
  24. package/src/client/components/mdx/hooks/use-code-block.ts +56 -0
  25. package/src/client/components/mdx/hooks/use-component-preview.ts +16 -0
  26. package/src/client/components/mdx/hooks/useTable.ts +74 -0
  27. package/src/client/components/mdx/hooks/useTabs.ts +68 -0
  28. package/src/client/components/mdx/image.tsx +23 -0
  29. package/src/client/components/mdx/index.ts +53 -0
  30. package/src/client/components/mdx/link.tsx +38 -0
  31. package/src/client/components/mdx/list.tsx +192 -0
  32. package/src/client/components/mdx/table.tsx +156 -0
  33. package/src/client/components/mdx/tabs.tsx +135 -0
  34. package/src/client/components/mdx/video.tsx +68 -0
  35. package/src/client/components/primitives/breadcrumbs.tsx +79 -0
  36. package/src/client/components/primitives/button-group.tsx +54 -0
  37. package/src/client/components/primitives/button.tsx +145 -0
  38. package/src/client/components/primitives/helpers/observer.ts +120 -0
  39. package/src/client/components/primitives/index.ts +17 -0
  40. package/src/client/components/primitives/link.tsx +122 -0
  41. package/src/client/components/primitives/menu.tsx +159 -0
  42. package/src/client/components/primitives/navbar.tsx +359 -0
  43. package/src/client/components/primitives/navigation-menu.tsx +116 -0
  44. package/src/client/components/primitives/on-this-page.tsx +461 -0
  45. package/src/client/components/primitives/page-nav.tsx +87 -0
  46. package/src/client/components/primitives/popover.tsx +47 -0
  47. package/src/client/components/primitives/search-dialog.tsx +183 -0
  48. package/src/client/components/primitives/sidebar.tsx +154 -0
  49. package/src/client/components/primitives/tabs.tsx +90 -0
  50. package/src/client/components/primitives/tooltip.tsx +83 -0
  51. package/src/client/components/primitives/types.ts +11 -0
  52. package/src/client/components/ui-base/breadcrumbs.tsx +42 -0
  53. package/src/client/components/ui-base/copy-markdown.tsx +112 -0
  54. package/src/client/components/ui-base/error-boundary.tsx +52 -0
  55. package/src/client/components/ui-base/github-stars.tsx +27 -0
  56. package/src/client/components/ui-base/head.tsx +69 -0
  57. package/src/client/components/ui-base/loading.tsx +87 -0
  58. package/src/client/components/ui-base/navbar.tsx +138 -0
  59. package/src/client/components/ui-base/not-found.tsx +24 -0
  60. package/src/client/components/ui-base/on-this-page.tsx +152 -0
  61. package/src/client/components/ui-base/page-nav.tsx +39 -0
  62. package/src/client/components/ui-base/powered-by.tsx +19 -0
  63. package/src/client/components/ui-base/progress-bar.tsx +67 -0
  64. package/src/client/components/ui-base/search-dialog.tsx +82 -0
  65. package/src/client/components/ui-base/sidebar.tsx +104 -0
  66. package/src/client/components/ui-base/tabs.tsx +65 -0
  67. package/src/client/components/ui-base/theme-toggle.tsx +32 -0
  68. package/src/client/hooks/index.ts +12 -0
  69. package/src/client/hooks/use-breadcrumbs.ts +22 -0
  70. package/src/client/hooks/use-i18n.ts +84 -0
  71. package/src/client/hooks/use-localized-to.ts +95 -0
  72. package/src/client/hooks/use-location.ts +5 -0
  73. package/src/client/hooks/use-navbar.ts +60 -0
  74. package/src/client/hooks/use-onthispage.ts +23 -0
  75. package/src/client/hooks/use-page-nav.ts +22 -0
  76. package/src/client/hooks/use-routes.ts +72 -0
  77. package/src/client/hooks/use-search.ts +71 -0
  78. package/src/client/hooks/use-sidebar.ts +49 -0
  79. package/src/client/hooks/use-tabs.ts +43 -0
  80. package/src/client/hooks/use-version.ts +78 -0
  81. package/src/client/index.ts +55 -17
  82. package/src/client/integrations/codesandbox.ts +179 -0
  83. package/src/client/ssr.tsx +27 -16
  84. package/src/client/theme/neutral.css +360 -0
  85. package/src/client/types.ts +131 -27
  86. package/src/client/utils/cn.ts +6 -0
  87. package/src/client/utils/copy-clipboard.ts +22 -0
  88. package/src/client/utils/get-base-file-path.ts +21 -0
  89. package/src/client/utils/github.ts +121 -0
  90. package/src/client/utils/use-on-change.ts +15 -0
  91. package/src/client/virtual.d.ts +24 -0
  92. package/src/node/cache.ts +156 -156
  93. package/src/node/config.ts +159 -103
  94. package/src/node/index.ts +13 -13
  95. package/src/node/mdx.ts +213 -61
  96. package/src/node/plugin/entry.ts +29 -18
  97. package/src/node/plugin/html.ts +11 -11
  98. package/src/node/plugin/index.ts +161 -83
  99. package/src/node/plugin/types.ts +2 -4
  100. package/src/node/routes/cache.ts +6 -6
  101. package/src/node/routes/index.ts +206 -113
  102. package/src/node/routes/parser.ts +106 -81
  103. package/src/node/routes/sorter.ts +15 -15
  104. package/src/node/routes/types.ts +24 -24
  105. package/src/node/ssg/index.ts +46 -46
  106. package/src/node/ssg/meta.ts +4 -4
  107. package/src/node/ssg/options.ts +5 -5
  108. package/src/node/ssg/sitemap.ts +14 -14
  109. package/src/node/utils.ts +31 -31
  110. package/tsconfig.json +25 -20
  111. package/tsup.config.ts +23 -14
  112. package/dist/PackageManagerTabs-NVT7G625.mjs +0 -99
  113. package/dist/SearchDialog-AGVF6JBO.mjs +0 -194
  114. package/dist/SearchDialog-YPDOM7Q6.css +0 -2847
  115. package/dist/Video-KNTY5BNO.mjs +0 -6
  116. package/dist/cache-KNL5B4EE.mjs +0 -12
  117. package/dist/chunk-7SFUJWTB.mjs +0 -211
  118. package/dist/chunk-FFBNU6IJ.mjs +0 -386
  119. package/dist/chunk-FMTOYQLO.mjs +0 -37
  120. package/dist/chunk-TKLQWU7H.mjs +0 -1920
  121. package/dist/chunk-Z7JHYNAS.mjs +0 -57
  122. package/dist/client/index.css +0 -2847
  123. package/dist/client/index.d.mts +0 -372
  124. package/dist/client/index.d.ts +0 -372
  125. package/dist/client/index.js +0 -3630
  126. package/dist/client/index.mjs +0 -697
  127. package/dist/client/ssr.css +0 -2847
  128. package/dist/client/ssr.d.mts +0 -27
  129. package/dist/client/ssr.d.ts +0 -27
  130. package/dist/client/ssr.js +0 -2928
  131. package/dist/client/ssr.mjs +0 -33
  132. package/dist/config-BsFQ-ErD.d.mts +0 -159
  133. package/dist/config-BsFQ-ErD.d.ts +0 -159
  134. package/dist/node/index.d.mts +0 -91
  135. package/dist/node/index.d.ts +0 -91
  136. package/dist/node/index.js +0 -1187
  137. package/dist/node/index.mjs +0 -762
  138. package/dist/types-Dj-bfnC3.d.mts +0 -74
  139. package/dist/types-Dj-bfnC3.d.ts +0 -74
  140. package/src/client/theme/components/CodeBlock/CodeBlock.tsx +0 -61
  141. package/src/client/theme/components/CodeBlock/index.ts +0 -1
  142. package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +0 -131
  143. package/src/client/theme/components/PackageManagerTabs/index.ts +0 -1
  144. package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +0 -64
  145. package/src/client/theme/components/Playground/Playground.tsx +0 -180
  146. package/src/client/theme/components/Playground/index.ts +0 -1
  147. package/src/client/theme/components/Playground/playground.css +0 -238
  148. package/src/client/theme/components/Video/Video.tsx +0 -84
  149. package/src/client/theme/components/Video/index.ts +0 -1
  150. package/src/client/theme/components/Video/video.css +0 -41
  151. package/src/client/theme/components/mdx/Admonition.tsx +0 -80
  152. package/src/client/theme/components/mdx/Badge.tsx +0 -31
  153. package/src/client/theme/components/mdx/Button.tsx +0 -50
  154. package/src/client/theme/components/mdx/Card.tsx +0 -80
  155. package/src/client/theme/components/mdx/Field.tsx +0 -60
  156. package/src/client/theme/components/mdx/FileTree.tsx +0 -229
  157. package/src/client/theme/components/mdx/List.tsx +0 -57
  158. package/src/client/theme/components/mdx/Table.tsx +0 -151
  159. package/src/client/theme/components/mdx/Tabs.tsx +0 -123
  160. package/src/client/theme/components/mdx/index.ts +0 -27
  161. package/src/client/theme/components/mdx/mdx-components.css +0 -764
  162. package/src/client/theme/icons/bun.tsx +0 -62
  163. package/src/client/theme/icons/deno.tsx +0 -20
  164. package/src/client/theme/icons/discord.tsx +0 -12
  165. package/src/client/theme/icons/github.tsx +0 -15
  166. package/src/client/theme/icons/npm.tsx +0 -13
  167. package/src/client/theme/icons/pnpm.tsx +0 -72
  168. package/src/client/theme/icons/twitter.tsx +0 -12
  169. package/src/client/theme/styles/markdown.css +0 -394
  170. package/src/client/theme/styles/variables.css +0 -175
  171. package/src/client/theme/styles.css +0 -39
  172. package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +0 -68
  173. package/src/client/theme/ui/Breadcrumbs/index.ts +0 -1
  174. package/src/client/theme/ui/CopyMarkdown/CopyMarkdown.tsx +0 -82
  175. package/src/client/theme/ui/CopyMarkdown/copy-markdown.css +0 -112
  176. package/src/client/theme/ui/CopyMarkdown/index.ts +0 -1
  177. package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +0 -50
  178. package/src/client/theme/ui/ErrorBoundary/error-boundary.css +0 -55
  179. package/src/client/theme/ui/ErrorBoundary/index.ts +0 -1
  180. package/src/client/theme/ui/Footer/footer.css +0 -32
  181. package/src/client/theme/ui/Head/Head.tsx +0 -69
  182. package/src/client/theme/ui/Head/index.ts +0 -1
  183. package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +0 -125
  184. package/src/client/theme/ui/LanguageSwitcher/index.ts +0 -1
  185. package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +0 -98
  186. package/src/client/theme/ui/Layout/Layout.tsx +0 -203
  187. package/src/client/theme/ui/Layout/base.css +0 -106
  188. package/src/client/theme/ui/Layout/index.ts +0 -2
  189. package/src/client/theme/ui/Layout/pagination.css +0 -72
  190. package/src/client/theme/ui/Layout/responsive.css +0 -47
  191. package/src/client/theme/ui/Link/Link.tsx +0 -392
  192. package/src/client/theme/ui/Link/LinkPreview.tsx +0 -59
  193. package/src/client/theme/ui/Link/index.ts +0 -2
  194. package/src/client/theme/ui/Link/link-preview.css +0 -48
  195. package/src/client/theme/ui/Loading/Loading.tsx +0 -10
  196. package/src/client/theme/ui/Loading/index.ts +0 -1
  197. package/src/client/theme/ui/Loading/loading.css +0 -30
  198. package/src/client/theme/ui/Navbar/GithubStars.tsx +0 -27
  199. package/src/client/theme/ui/Navbar/Navbar.tsx +0 -193
  200. package/src/client/theme/ui/Navbar/Tabs.tsx +0 -99
  201. package/src/client/theme/ui/Navbar/index.ts +0 -2
  202. package/src/client/theme/ui/Navbar/navbar.css +0 -347
  203. package/src/client/theme/ui/NotFound/NotFound.tsx +0 -19
  204. package/src/client/theme/ui/NotFound/index.ts +0 -1
  205. package/src/client/theme/ui/NotFound/not-found.css +0 -64
  206. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +0 -244
  207. package/src/client/theme/ui/OnThisPage/index.ts +0 -1
  208. package/src/client/theme/ui/OnThisPage/toc.css +0 -152
  209. package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +0 -18
  210. package/src/client/theme/ui/PoweredBy/index.ts +0 -1
  211. package/src/client/theme/ui/PoweredBy/powered-by.css +0 -76
  212. package/src/client/theme/ui/ProgressBar/ProgressBar.css +0 -17
  213. package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +0 -51
  214. package/src/client/theme/ui/ProgressBar/index.ts +0 -1
  215. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +0 -209
  216. package/src/client/theme/ui/SearchDialog/index.ts +0 -1
  217. package/src/client/theme/ui/SearchDialog/search.css +0 -152
  218. package/src/client/theme/ui/Sidebar/Sidebar.tsx +0 -244
  219. package/src/client/theme/ui/Sidebar/index.ts +0 -1
  220. package/src/client/theme/ui/Sidebar/sidebar.css +0 -230
  221. package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +0 -69
  222. package/src/client/theme/ui/ThemeToggle/index.ts +0 -1
  223. package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +0 -136
  224. package/src/client/theme/ui/VersionSwitcher/index.ts +0 -1
  225. package/src/client/utils.ts +0 -49
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "boltdocs",
3
- "version": "1.10.2",
3
+ "version": "1.11.0",
4
4
  "description": "A lightweight documentation generator for React projects.",
5
5
  "main": "dist/node/index.js",
6
6
  "module": "dist/node/index.mjs",
@@ -19,11 +19,29 @@
19
19
  "import": "./dist/client/index.mjs",
20
20
  "require": "./dist/client/index.js"
21
21
  },
22
- "./style.css": "./dist/client/index.css"
22
+ "./client/hooks": {
23
+ "types": "./dist/client/hooks/index.d.ts",
24
+ "import": "./dist/client/hooks/index.mjs",
25
+ "require": "./dist/client/hooks/index.js"
26
+ },
27
+ "./client/primitives": {
28
+ "types": "./dist/client/components/primitives/index.d.ts",
29
+ "import": "./dist/client/components/primitives/index.mjs",
30
+ "require": "./dist/client/components/primitives/index.js"
31
+ },
32
+ "./client/types": {
33
+ "types": "./dist/client/types.d.ts",
34
+ "import": "./dist/client/types.mjs",
35
+ "require": "./dist/client/types.js"
36
+ },
37
+ "./theme/neutral.css": "./src/client/theme/neutral.css"
23
38
  },
24
39
  "scripts": {
25
40
  "build": "tsup",
26
- "dev": "tsup --watch"
41
+ "dev": "tsup --watch",
42
+ "format": "pnpm exec biome format --write",
43
+ "lint": "pnpm exec biome lint --write",
44
+ "check": "pnpm exec biome check --write"
27
45
  },
28
46
  "keywords": [
29
47
  "docs",
@@ -42,26 +60,30 @@
42
60
  "dependencies": {
43
61
  "@mdx-js/react": "^3.1.1",
44
62
  "@mdx-js/rollup": "^3.1.1",
63
+ "class-variance-authority": "^0.7.1",
64
+ "clsx": "^2.1.1",
65
+ "codesandbox": "^2.2.3",
45
66
  "fast-glob": "^3.3.3",
46
67
  "github-slugger": "^2.0.0",
47
68
  "gray-matter": "^4.0.3",
48
69
  "lucide-react": "^0.575.0",
49
- "react-live": "^4.1.8",
70
+ "react-aria-components": "^1.16.0",
50
71
  "react-router-dom": "^6.30.3",
51
- "rehype-pretty-code": "^0.14.1",
52
72
  "rehype-slug": "^6.0.0",
53
73
  "remark-frontmatter": "^5.0.0",
54
74
  "remark-gfm": "^4.0.1",
75
+ "scroll-into-view-if-needed": "^3.1.0",
55
76
  "sharp": "^0.34.5",
56
77
  "shiki": "^3.23.0",
57
78
  "svgo": "^4.0.1",
79
+ "tailwind-merge": "^3.5.0",
58
80
  "unist-util-visit": "^5.1.0",
59
81
  "vite": "^7.3.1",
60
82
  "vite-plugin-image-optimizer": "^2.0.3"
61
83
  },
62
84
  "peerDependencies": {
63
- "react": "^18.0.0",
64
- "react-dom": "^18.0.0"
85
+ "react": "^19.1.0",
86
+ "react-dom": "^19.1.0"
65
87
  },
66
88
  "packageManager": "pnpm@10.30.2",
67
89
  "devDependencies": {
@@ -0,0 +1,18 @@
1
+ import { createContext, use } from 'react'
2
+ import type { BoltdocsConfig } from '@node/config'
3
+
4
+ /**
5
+ * Context for the global documentation configuration.
6
+ */
7
+ export const ConfigContext = createContext<BoltdocsConfig | null>(null)
8
+
9
+ /**
10
+ * Hook to access the Boltdocs configuration.
11
+ */
12
+ export function useConfig() {
13
+ const context = use(ConfigContext)
14
+ if (!context) {
15
+ throw new Error('useConfig must be used within a ConfigProvider')
16
+ }
17
+ return context
18
+ }
@@ -0,0 +1,14 @@
1
+ import { Outlet } from 'react-router-dom'
2
+ import UserLayout from 'virtual:boltdocs-layout'
3
+
4
+ /**
5
+ * Wraps the docs Outlet with the user's (or default) layout component.
6
+ * The Layout receives the routed page as `children`.
7
+ */
8
+ export function DocsLayout() {
9
+ return (
10
+ <UserLayout>
11
+ <Outlet />
12
+ </UserLayout>
13
+ )
14
+ }
@@ -1,90 +1,22 @@
1
- import React, { useEffect, useState } from "react";
2
- import ReactDOM from "react-dom/client";
3
- import {
4
- BrowserRouter,
5
- Routes,
6
- Route,
7
- Outlet,
8
- useLocation,
9
- } from "react-router-dom";
10
- import { ThemeLayout } from "../theme/ui/Layout";
11
- import { NotFound } from "../theme/ui/NotFound";
12
- import { Loading } from "../theme/ui/Loading";
13
- import { MDXProvider } from "@mdx-js/react";
14
- import { ComponentRoute, CreateBoltdocsAppOptions } from "../types";
15
- import {
16
- createContext,
17
- useContext,
18
- Suspense,
19
- lazy,
20
- useLayoutEffect,
21
- } from "react";
22
- import { Link as LucideLink } from "lucide-react";
23
-
24
- export const ConfigContext = createContext<any>(null);
25
-
26
- export function useConfig() {
27
- return useContext(ConfigContext);
28
- }
29
-
30
- import { CodeBlock } from "../theme/components/CodeBlock";
31
- const Video = lazy(() =>
32
- import("../theme/components/Video").then((m) => ({ default: m.Video })),
33
- );
34
- const PackageManagerTabs = lazy(() =>
35
- import("../theme/components/PackageManagerTabs").then((m) => ({
36
- default: m.PackageManagerTabs,
37
- })),
38
- );
39
- declare global {
40
- interface ImportMeta {
41
- env: Record<string, any>;
42
- }
43
- }
44
-
45
- import { PreloadProvider } from "./preload";
46
-
47
- const Heading = ({
48
- level,
49
- id,
50
- children,
51
- }: {
52
- level: number;
53
- id?: string;
54
- children: React.ReactNode;
55
- }) => {
56
- const Tag = `h${level}` as keyof JSX.IntrinsicElements;
57
- return (
58
- <Tag id={id} className="boltdocs-heading">
59
- {children}
60
- {id && (
61
- <a href={`#${id}`} className="header-anchor" aria-label="Anchor">
62
- <LucideLink size={16} />
63
- </a>
64
- )}
65
- </Tag>
66
- );
67
- };
68
-
69
- const mdxComponents = {
70
- h1: (props: any) => <Heading level={1} {...props} />,
71
- h2: (props: any) => <Heading level={2} {...props} />,
72
- h3: (props: any) => <Heading level={3} {...props} />,
73
- h4: (props: any) => <Heading level={4} {...props} />,
74
- h5: (props: any) => <Heading level={5} {...props} />,
75
- h6: (props: any) => <Heading level={6} {...props} />,
76
- pre: (props: any) => <CodeBlock {...props}>{props.children}</CodeBlock>,
77
- video: (props: any) => (
78
- <Suspense fallback={<div className="video-skeleton" />}>
79
- <Video {...props} />
80
- </Suspense>
81
- ),
82
- PackageManagerTabs: (props: any) => (
83
- <Suspense fallback={<div className="pkg-tabs-skeleton" />}>
84
- <PackageManagerTabs {...props} />
85
- </Suspense>
86
- ),
87
- };
1
+ import React, { useEffect, useState, useMemo } from 'react'
2
+ import ReactDOM from 'react-dom/client'
3
+ import { BrowserRouter, Routes, Route } from 'react-router-dom'
4
+ import { NotFound } from '@components/ui-base/not-found'
5
+ import { Loading } from '@components/ui-base/loading'
6
+ import { ThemeProvider } from './theme-context'
7
+ import type { ComponentRoute, CreateBoltdocsAppOptions } from '../types'
8
+ import type { BoltdocsConfig } from '@node/config'
9
+
10
+ import UserLayout from 'virtual:boltdocs-layout'
11
+
12
+ import { PreloadProvider } from './preload'
13
+ import { BoltdocsRouterProvider } from './router'
14
+ import { ConfigContext } from './config-context'
15
+ import { ScrollHandler } from './scroll-handler'
16
+ import { DocsLayout } from './docs-layout'
17
+ import { MdxPage } from './mdx-page'
18
+ import { MdxComponentsProvider } from './mdx-components-context'
19
+ import { mdxComponentsDefault } from './mdx-component'
88
20
 
89
21
  export function AppShell({
90
22
  initialRoutes,
@@ -93,194 +25,124 @@ export function AppShell({
93
25
  modules,
94
26
  hot,
95
27
  homePage: HomePage,
28
+ externalPages,
96
29
  components: customComponents = {},
97
30
  }: {
98
- initialRoutes: ComponentRoute[];
99
- initialConfig: any;
100
- docsDirName: string;
101
- modules: Record<string, () => Promise<any>>;
102
- hot?: any;
103
- homePage?: React.ComponentType;
104
- components?: Record<string, React.ComponentType<any>>;
31
+ initialRoutes: ComponentRoute[]
32
+ initialConfig: BoltdocsConfig
33
+ docsDirName: string
34
+ modules: Record<string, () => Promise<{ default: React.ComponentType }>>
35
+ hot?: CreateBoltdocsAppOptions['hot']
36
+ homePage?: React.ComponentType
37
+ externalPages?: Record<string, React.ComponentType>
38
+ components?: Record<string, React.ComponentType>
105
39
  }) {
106
- const [routesInfo, setRoutesInfo] = useState<ComponentRoute[]>(initialRoutes);
107
- const [config] = useState(initialConfig);
40
+ const [routesInfo, setRoutesInfo] = useState<ComponentRoute[]>(initialRoutes)
41
+ const [config] = useState(initialConfig)
42
+ const computedExternalPages = externalPages || {}
108
43
 
109
- const resolveRoutes = (infos: ComponentRoute[]) => {
110
- return infos
44
+ const resolvedRoutes = useMemo(() => {
45
+ return routesInfo
111
46
  .filter(
112
- (route) => !(HomePage && (route.path === "/" || route.path === "")),
47
+ (route) =>
48
+ !(HomePage && (route.path === '/' || route.path === '')) &&
49
+ !computedExternalPages[route.path === '' ? '/' : route.path],
113
50
  )
114
51
  .map((route) => {
115
52
  const loaderKey = Object.keys(modules).find(
116
53
  (k) => k === `/${docsDirName}/${route.filePath}`,
117
- );
118
- const loader = loaderKey ? modules[loaderKey] : null;
54
+ )
55
+ const loader = loaderKey ? modules[loaderKey] : null
119
56
 
120
57
  return {
121
58
  ...route,
122
59
  Component: React.lazy(() => {
123
- if (!loader)
124
- return Promise.resolve({ default: () => <NotFound /> });
125
- return loader() as any;
60
+ if (!loader) return Promise.resolve({ default: NotFound })
61
+ return loader() as any
126
62
  }),
127
- };
128
- });
129
- };
130
-
131
- const [resolvedRoutes, setResolvedRoutes] = useState<any[]>(() =>
132
- resolveRoutes(initialRoutes),
133
- );
63
+ }
64
+ })
65
+ }, [routesInfo, modules, docsDirName, HomePage, computedExternalPages])
134
66
 
135
67
  // Subscribe to HMR events
136
68
  useEffect(() => {
137
69
  if (hot) {
138
- hot.on("boltdocs:routes-update", (newRoutes: ComponentRoute[]) => {
139
- setRoutesInfo(newRoutes);
140
- });
70
+ hot.on('boltdocs:routes-update', (newRoutes: ComponentRoute[]) => {
71
+ setRoutesInfo(newRoutes)
72
+ })
141
73
  }
142
- }, [hot]);
74
+ }, [hot])
143
75
 
144
- // Sync resolved routes when info or modules change
145
- useEffect(() => {
146
- setResolvedRoutes(resolveRoutes(routesInfo));
147
- }, [routesInfo, modules, docsDirName]);
76
+ const allComponents = useMemo(
77
+ () => ({ ...mdxComponentsDefault, ...customComponents }),
78
+ [customComponents],
79
+ )
148
80
 
149
81
  return (
150
- <ConfigContext.Provider value={config}>
151
- <PreloadProvider routes={routesInfo} modules={modules}>
152
- <ScrollHandler />
153
- <Routes>
154
- {/* Custom home page WITHOUT docs layout */}
155
- {HomePage && (
156
- <Route
157
- path="/"
158
- element={
159
- <ThemeLayout
160
- config={config}
161
- routes={routesInfo}
162
- sidebar={null}
163
- toc={null}
164
- breadcrumbs={null}
165
- {...config.themeConfig?.layoutProps}
166
- >
167
- <HomePage />
168
- </ThemeLayout>
169
- }
170
- />
171
- )}
172
-
173
- {/* Documentation pages WITH sidebar + TOC layout */}
174
- <Route
175
- key="docs-layout"
176
- element={<DocsLayout config={config} routes={routesInfo} />}
177
- >
178
- {resolvedRoutes.map((route: any) => (
179
- <Route
180
- key={route.path}
181
- path={route.path === "" ? "/" : route.path}
182
- element={
183
- <React.Suspense fallback={<Loading />}>
184
- <MdxPage
185
- Component={route.Component}
186
- customComponents={customComponents}
82
+ <ThemeProvider>
83
+ <MdxComponentsProvider components={allComponents}>
84
+ <ConfigContext.Provider value={config}>
85
+ <BoltdocsRouterProvider>
86
+ <PreloadProvider routes={routesInfo} modules={modules}>
87
+ <ScrollHandler />
88
+ <Routes>
89
+ {/* Custom home page with user layout */}
90
+ {HomePage && (
91
+ <Route
92
+ path="/"
93
+ element={
94
+ <UserLayout>
95
+ <HomePage />
96
+ </UserLayout>
97
+ }
98
+ />
99
+ )}
100
+
101
+ {/* Custom External Pages with user layout */}
102
+ {Object.entries(computedExternalPages).map(
103
+ ([extPath, ExtComponent]) => (
104
+ <Route
105
+ key={extPath}
106
+ path={extPath}
107
+ element={
108
+ <UserLayout>
109
+ <ExtComponent />
110
+ </UserLayout>
111
+ }
187
112
  />
188
- </React.Suspense>
189
- }
190
- />
191
- ))}
192
- </Route>
193
-
194
- <Route
195
- path="*"
196
- element={
197
- <ThemeLayout
198
- config={config}
199
- routes={routesInfo}
200
- {...config.themeConfig?.layoutProps}
201
- >
202
- <NotFound />
203
- </ThemeLayout>
204
- }
205
- />
206
- </Routes>
207
- </PreloadProvider>
208
- </ConfigContext.Provider>
209
- );
210
- }
211
-
212
- /**
213
- * Handles scroll restoration and hash scrolling on navigation.
214
- */
215
- function ScrollHandler() {
216
- const { pathname, hash } = useLocation();
217
-
218
- useLayoutEffect(() => {
219
- const container = document.querySelector(".boltdocs-content");
220
- if (!container) return;
221
-
222
- if (hash) {
223
- const id = hash.replace("#", "");
224
- const element = document.getElementById(id);
225
- if (element) {
226
- const offset = 80;
227
- const containerRect = container.getBoundingClientRect().top;
228
- const elementRect = element.getBoundingClientRect().top;
229
- const elementPosition = elementRect - containerRect;
230
- const offsetPosition = elementPosition - offset + container.scrollTop;
231
-
232
- container.scrollTo({
233
- top: offsetPosition,
234
- behavior: "smooth",
235
- });
236
- return;
237
- }
238
- }
239
- container.scrollTo(0, 0);
240
- }, [pathname, hash]);
241
-
242
- return null;
243
- }
244
-
245
- /** Wrapper layout for doc pages (sidebar + content + TOC) */
246
- function DocsLayout({
247
- config,
248
- routes,
249
- }: {
250
- config: any;
251
- routes: ComponentRoute[];
252
- }) {
253
- return (
254
- <ThemeLayout
255
- config={config}
256
- routes={routes}
257
- {...config.themeConfig?.layoutProps}
258
- >
259
- <Outlet />
260
- </ThemeLayout>
261
- );
262
- }
263
-
264
- /**
265
- * Renders an MDX page securely, injecting required custom components.
266
- * For example, this overrides the default `<pre>` HTML tags emitted by MDX
267
- * with the Boltdocs `CodeBlock` component for syntax highlighting.
268
- *
269
- * @param props - Contains the dynamically loaded React component representing the MDX page
270
- */
271
- function MdxPage({
272
- Component,
273
- customComponents = {},
274
- }: {
275
- Component: React.LazyExoticComponent<any>;
276
- customComponents?: Record<string, React.ComponentType<any>>;
277
- }) {
278
- const allComponents = { ...mdxComponents, ...customComponents };
279
- return (
280
- <MDXProvider components={allComponents}>
281
- <Component />
282
- </MDXProvider>
283
- );
113
+ ),
114
+ )}
115
+
116
+ {/* Documentation pages WITH sidebar + TOC layout */}
117
+ <Route key="docs-layout" element={<DocsLayout />}>
118
+ {resolvedRoutes.map((route) => (
119
+ <Route
120
+ key={route.path}
121
+ path={route.path === '' ? '/' : route.path}
122
+ element={
123
+ <React.Suspense fallback={<Loading />}>
124
+ <MdxPage Component={route.Component} />
125
+ </React.Suspense>
126
+ }
127
+ />
128
+ ))}
129
+ </Route>
130
+
131
+ <Route
132
+ path="*"
133
+ element={
134
+ <UserLayout>
135
+ <NotFound />
136
+ </UserLayout>
137
+ }
138
+ />
139
+ </Routes>
140
+ </PreloadProvider>
141
+ </BoltdocsRouterProvider>
142
+ </ConfigContext.Provider>
143
+ </MdxComponentsProvider>
144
+ </ThemeProvider>
145
+ )
284
146
  }
285
147
 
286
148
  /**
@@ -305,13 +167,22 @@ function MdxPage({
305
167
  * ```
306
168
  */
307
169
  export function createBoltdocsApp(options: CreateBoltdocsAppOptions) {
308
- const { target, routes, docsDirName, config, modules, hot, homePage } =
309
- options;
310
- const container = document.querySelector(target);
170
+ const {
171
+ target,
172
+ routes,
173
+ docsDirName,
174
+ config,
175
+ modules,
176
+ hot,
177
+ homePage,
178
+ externalPages,
179
+ components,
180
+ } = options
181
+ const container = document.querySelector(target)
311
182
  if (!container) {
312
183
  throw new Error(
313
184
  `[boltdocs] Mount target "${target}" not found in document.`,
314
- );
185
+ )
315
186
  }
316
187
 
317
188
  const app = (
@@ -324,16 +195,17 @@ export function createBoltdocsApp(options: CreateBoltdocsAppOptions) {
324
195
  modules={modules}
325
196
  hot={hot}
326
197
  homePage={homePage}
327
- components={options.components}
198
+ externalPages={externalPages}
199
+ components={components}
328
200
  />
329
201
  </BrowserRouter>
330
202
  </React.StrictMode>
331
- );
203
+ )
332
204
 
333
205
  // SSG pre-renders a shell with mock components for SEO crawlers.
334
206
  // We always use createRoot because the SSG output doesn't match the
335
207
  // real client-side component tree (components are lazy/dynamic).
336
208
  // Clear any SSG placeholder content before mounting.
337
- container.innerHTML = "";
338
- ReactDOM.createRoot(container as HTMLElement).render(app);
209
+ container.innerHTML = ''
210
+ ReactDOM.createRoot(container as HTMLElement).render(app)
339
211
  }
@@ -0,0 +1,52 @@
1
+ import type React from 'react'
2
+ import { Link as LucideLink } from 'lucide-react'
3
+ import * as MdxComponents from '@components/mdx'
4
+
5
+ const Heading = ({
6
+ level,
7
+ id,
8
+ children,
9
+ }: {
10
+ level: number
11
+ id?: string
12
+ children?: React.ReactNode
13
+ }) => {
14
+ const Tag = `h${level}` as keyof JSX.IntrinsicElements
15
+ return (
16
+ <Tag id={id} className="boltdocs-heading">
17
+ {children}
18
+ {id && (
19
+ <a href={`#${id}`} className="header-anchor" aria-label="Anchor">
20
+ <LucideLink size={16} />
21
+ </a>
22
+ )}
23
+ </Tag>
24
+ )
25
+ }
26
+
27
+ export const mdxComponentsDefault = {
28
+ ...MdxComponents,
29
+ h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
30
+ <Heading level={1} {...props} />
31
+ ),
32
+ h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
33
+ <Heading level={2} {...props} />
34
+ ),
35
+ h3: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
36
+ <Heading level={3} {...props} />
37
+ ),
38
+ h4: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
39
+ <Heading level={4} {...props} />
40
+ ),
41
+ h5: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
42
+ <Heading level={5} {...props} />
43
+ ),
44
+ h6: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
45
+ <Heading level={6} {...props} />
46
+ ),
47
+ pre: (props: React.HTMLAttributes<HTMLPreElement>) => (
48
+ <MdxComponents.CodeBlock {...props}>
49
+ {props.children}
50
+ </MdxComponents.CodeBlock>
51
+ ),
52
+ }
@@ -0,0 +1,23 @@
1
+ import React, { createContext, useContext } from 'react'
2
+
3
+ export type MdxComponentsType = Record<string, React.ComponentType<any>>
4
+
5
+ const MdxComponentsContext = createContext<MdxComponentsType>({})
6
+
7
+ export function useMdxComponents() {
8
+ return useContext(MdxComponentsContext)
9
+ }
10
+
11
+ export function MdxComponentsProvider({
12
+ components,
13
+ children,
14
+ }: {
15
+ components: MdxComponentsType
16
+ children: React.ReactNode
17
+ }) {
18
+ return (
19
+ <MdxComponentsContext.Provider value={components}>
20
+ {children}
21
+ </MdxComponentsContext.Provider>
22
+ )
23
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react'
2
+ import { MDXProvider } from '@mdx-js/react'
3
+ import { useMdxComponents } from './mdx-components-context'
4
+
5
+ /**
6
+ * Renders an MDX page securely, injecting required custom components.
7
+ * This overrides the default HTML tags emitted by MDX with stylized components.
8
+ */
9
+ export function MdxPage({
10
+ Component,
11
+ }: {
12
+ Component: React.ComponentType<any>
13
+ }) {
14
+ const allComponents = useMdxComponents()
15
+ return (
16
+ <MDXProvider components={allComponents as any}>
17
+ <Component />
18
+ </MDXProvider>
19
+ )
20
+ }