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/src/node/mdx.ts CHANGED
@@ -1,25 +1,183 @@
1
- import mdxPlugin from "@mdx-js/rollup";
2
- import remarkGfm from "remark-gfm";
3
- import remarkFrontmatter from "remark-frontmatter";
4
- import rehypeSlug from "rehype-slug";
5
- import rehypePrettyCode from "rehype-pretty-code";
6
- import type { Plugin } from "vite";
7
- import crypto from "crypto";
1
+ import mdxPlugin from '@mdx-js/rollup'
2
+ import remarkGfm from 'remark-gfm'
3
+ import remarkFrontmatter from 'remark-frontmatter'
4
+ import rehypeSlug from 'rehype-slug'
5
+ import type { Plugin } from 'vite'
6
+ import crypto from 'crypto'
7
+ import { visit } from 'unist-util-visit'
8
+ import { createHighlighter } from 'shiki'
8
9
 
9
- import type { BoltdocsConfig } from "./config";
10
- import { TransformCache } from "./cache";
10
+ import type { Highlighter } from 'shiki'
11
+
12
+ import type { BoltdocsConfig } from './config'
13
+ import { TransformCache } from './cache'
14
+
15
+ let shikiHighlighter: Highlighter | null = null
16
+
17
+ /**
18
+ * Retrieves or initializes the Shiki highlighter instance.
19
+ * Supports dual-theme configurations (light/dark).
20
+ *
21
+ * @param codeTheme - Theme configuration (string for single, object for dual).
22
+ * @returns A promise resolving to the highlighter instance.
23
+ */
24
+ async function getShikiHighlighter(codeTheme: any) {
25
+ if (shikiHighlighter) return shikiHighlighter
26
+
27
+ const themes =
28
+ typeof codeTheme === 'object'
29
+ ? [codeTheme.light, codeTheme.dark]
30
+ : [codeTheme ?? 'github-dark']
31
+
32
+ // Fallbacks for standard themes
33
+ ;['github-light', 'github-dark'].forEach((t) => {
34
+ if (!themes.includes(t)) themes.push(t)
35
+ })
36
+
37
+ // Initialize with a core set of languages first to speed up boot
38
+ shikiHighlighter = await createHighlighter({
39
+ themes,
40
+ langs: [
41
+ 'tsx',
42
+ 'jsx',
43
+ 'ts',
44
+ 'js',
45
+ 'json',
46
+ 'md',
47
+ 'mdx',
48
+ 'css',
49
+ 'html',
50
+ 'bash',
51
+ 'sh',
52
+ 'yaml',
53
+ 'yml',
54
+ ],
55
+ })
56
+
57
+ return shikiHighlighter
58
+ }
59
+
60
+ /**
61
+ * Custom remark plugin to highlight code in ComponentPreview components.
62
+ * This runs before rehype, ensuring that the 'highlightedHtml' prop is correctly
63
+ * attached to the MDX component as a JSX attribute.
64
+ *
65
+ * Supports both string literals and MDX expression values (template literals)
66
+ * for the 'code' attribute.
67
+ *
68
+ * @param config - The Boltdocs configuration
69
+ * @returns A remark plugin function
70
+ */
71
+ export function remarkShiki(config?: BoltdocsConfig) {
72
+ return async (tree: any) => {
73
+ const codeTheme = config?.themeConfig?.codeTheme ?? {
74
+ light: 'github-light',
75
+ dark: 'github-dark',
76
+ }
77
+ const highlighter = await getShikiHighlighter(codeTheme)
78
+
79
+ visit(tree, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node: any) => {
80
+ if (node.name !== 'ComponentPreview') return
81
+
82
+ const codeAttr = node.attributes?.find((a: any) => a.name === 'code')
83
+ let code = ''
84
+
85
+ if (codeAttr) {
86
+ if (typeof codeAttr.value === 'string') {
87
+ code = codeAttr.value
88
+ } else if (codeAttr.value?.type === 'mdxJsxAttributeValueExpression') {
89
+ const expr = codeAttr.value.value ?? ''
90
+ code = expr.match(/^[`'"](.+)[`'"]$/)?.[1] ?? expr
91
+ }
92
+ }
93
+
94
+ if (!code) return
95
+
96
+ const options: any =
97
+ typeof codeTheme === 'object'
98
+ ? {
99
+ themes: { light: codeTheme.light, dark: codeTheme.dark },
100
+ lang: 'tsx',
101
+ }
102
+ : { theme: codeTheme, lang: 'tsx' }
103
+
104
+ const html = highlighter.codeToHtml(code, options)
105
+
106
+ node.attributes = (node.attributes ?? []).filter(
107
+ (a: any) => a.name !== 'highlightedHtml',
108
+ )
109
+ node.attributes.push({
110
+ type: 'mdxJsxAttribute',
111
+ name: 'highlightedHtml',
112
+ value: html,
113
+ })
114
+ })
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Custom rehype plugin to perform syntax highlighting at build time for
120
+ * standard Markdown code blocks.
121
+ *
122
+ * Injects 'data-highlighted="true"' and 'highlightedHtml' into the 'pre' tag properties,
123
+ * which are then consumed by the client-side CodeBlock component.
124
+ *
125
+ * @param config - The Boltdocs configuration
126
+ * @returns A rehype plugin function
127
+ */
128
+ export function rehypeShiki(config?: BoltdocsConfig) {
129
+ return async (tree: any) => {
130
+ const codeTheme = config?.themeConfig?.codeTheme || {
131
+ light: 'github-light',
132
+ dark: 'github-dark',
133
+ }
134
+ const highlighter = await getShikiHighlighter(codeTheme)
135
+
136
+ visit(tree, 'element', (node: any) => {
137
+ // Handle standard Markdown code blocks
138
+ if (node.tagName === 'pre' && node.children?.[0]?.tagName === 'code') {
139
+ const codeNode = node.children[0]
140
+ const className = codeNode.properties?.className || []
141
+ const langMatch = className.find((c: string) =>
142
+ c.startsWith('language-'),
143
+ )
144
+ const lang = langMatch ? langMatch.slice(9) : 'text'
145
+ const code = codeNode.children[0]?.value || ''
146
+
147
+ const options: any = { lang }
148
+ if (typeof codeTheme === 'object') {
149
+ options.themes = {
150
+ light: codeTheme.light,
151
+ dark: codeTheme.dark,
152
+ }
153
+ } else {
154
+ options.theme = codeTheme
155
+ }
156
+
157
+ const html = highlighter.codeToHtml(code, options)
158
+
159
+ // Inject highlighted HTML and mark as highlighted for CodeBlock component
160
+ node.properties.dataHighlighted = 'true'
161
+ node.properties.highlightedHtml = html
162
+ node.children = []
163
+ }
164
+ })
165
+ }
166
+ }
167
+
168
+ const MDX_PLUGIN_VERSION = 'v3'
11
169
 
12
170
  /**
13
171
  * Persistent cache for MDX transformations.
14
172
  * Saves results to `.boltdocs/transform-mdx.json.gz`.
15
173
  */
16
- const mdxCache = new TransformCache("mdx");
17
- let mdxCacheLoaded = false;
174
+ const mdxCache = new TransformCache('mdx')
175
+ let mdxCacheLoaded = false
18
176
 
19
177
  /**
20
178
  * Configures the MDX compiler for Vite using `@mdx-js/rollup`.
21
179
  * Includes standard remark and rehype plugins for GitHub Flavored Markdown (GFM),
22
- * frontmatter extraction, auto-linking headers, and syntax highlighting via `rehype-pretty-code`.
180
+ * frontmatter extraction, and auto-linking headers.
23
181
  *
24
182
  * Also wraps the plugin with a persistent cache to avoid re-compiling unchanged MDX files.
25
183
  *
@@ -32,62 +190,62 @@ export function boltdocsMdxPlugin(
32
190
  compiler = mdxPlugin,
33
191
  ): Plugin {
34
192
  const extraRemarkPlugins =
35
- config?.plugins?.flatMap((p) => p.remarkPlugins || []) || [];
193
+ config?.plugins?.flatMap((p) => p.remarkPlugins || []) || []
36
194
  const extraRehypePlugins =
37
- config?.plugins?.flatMap((p) => p.rehypePlugins || []) || [];
195
+ config?.plugins?.flatMap((p) => p.rehypePlugins || []) || []
38
196
 
39
197
  const baseMdxPlugin = compiler({
40
- remarkPlugins: [remarkGfm, remarkFrontmatter, ...extraRemarkPlugins],
198
+ remarkPlugins: [
199
+ remarkGfm,
200
+ remarkFrontmatter,
201
+ [remarkShiki, config],
202
+ ...(extraRemarkPlugins as any[]),
203
+ ],
41
204
  rehypePlugins: [
42
205
  rehypeSlug,
43
- ...extraRehypePlugins,
44
- [
45
- rehypePrettyCode,
46
- {
47
- theme: config?.themeConfig?.codeTheme || "one-dark-pro",
48
- keepBackground: false,
49
- },
50
- ],
206
+ [rehypeShiki, config],
207
+ ...(extraRehypePlugins as any[]),
51
208
  ],
52
- jsxRuntime: "automatic",
53
- providerImportSource: "@mdx-js/react",
54
- }) as Plugin;
209
+ jsxRuntime: 'automatic',
210
+ providerImportSource: '@mdx-js/react',
211
+ }) as Plugin
55
212
 
56
213
  // @ts-ignore
57
214
  if (baseMdxPlugin.isMock) {
58
- console.log("MDX PLUGIN IS MOCKED");
215
+ console.log('MDX PLUGIN IS MOCKED')
59
216
  }
60
217
 
61
218
  return {
62
219
  ...baseMdxPlugin,
63
- name: "vite-plugin-boltdocs-mdx",
220
+ name: 'vite-plugin-boltdocs-mdx',
64
221
 
65
222
  async buildStart() {
66
- hits = 0;
67
- total = 0;
223
+ hits = 0
224
+ total = 0
68
225
  if (!mdxCacheLoaded) {
69
- mdxCache.load();
70
- mdxCacheLoaded = true;
226
+ mdxCache.load()
227
+ mdxCacheLoaded = true
71
228
  }
72
229
  if (baseMdxPlugin.buildStart) {
73
- await (baseMdxPlugin.buildStart as any).call(this);
230
+ await (baseMdxPlugin.buildStart as any).call(this)
74
231
  }
75
232
  },
76
233
 
77
234
  async transform(code, id, options) {
78
- if (!id.endsWith(".md") && !id.endsWith(".mdx")) {
79
- return (baseMdxPlugin.transform as any)?.call(this, code, id, options);
235
+ if (!id.endsWith('.md') && !id.endsWith('.mdx')) {
236
+ return (baseMdxPlugin.transform as any)?.call(this, code, id, options)
80
237
  }
81
238
 
82
- total++;
83
- // Create a cache key based on path, content, and config (simplified)
84
- const contentHash = crypto.createHash("md5").update(code).digest("hex");
85
- const cacheKey = `${id}:${contentHash}`;
239
+ console.log(`[boltdocs] Transforming MDX: ${id}`)
240
+ total++
241
+ // Create a cache key based on path, content, and plugin version
242
+ const contentHash = crypto.createHash('md5').update(code).digest('hex')
243
+ const cacheKey = `${id}:${contentHash}:${MDX_PLUGIN_VERSION}`
86
244
 
87
- const cached = mdxCache.get(cacheKey);
245
+ const cached = mdxCache.get(cacheKey)
88
246
  if (cached) {
89
- hits++;
90
- return { code: cached, map: null };
247
+ hits++
248
+ return { code: cached, map: null }
91
249
  }
92
250
 
93
251
  const result = await (baseMdxPlugin.transform as any).call(
@@ -95,33 +253,27 @@ export function boltdocsMdxPlugin(
95
253
  code,
96
254
  id,
97
255
  options,
98
- );
256
+ )
99
257
 
100
- if (result && typeof result === "object" && result.code) {
101
- mdxCache.set(cacheKey, result.code);
258
+ if (result && typeof result === 'object' && result.code) {
259
+ mdxCache.set(cacheKey, result.code)
102
260
  }
103
261
 
104
- return result;
262
+ return result
105
263
  },
106
264
 
107
265
  async buildEnd() {
108
- mdxCache.save();
109
- await mdxCache.flush(); // Use instance flush or global flushCache
266
+ console.log(
267
+ `[boltdocs] MDX Cache Performance: ${hits}/${total} hits (${Math.round((hits / total) * 100) || 0}%)`,
268
+ )
269
+ mdxCache.save()
270
+ await mdxCache.flush() // Use instance flush or global flushCache
110
271
  if (baseMdxPlugin.buildEnd) {
111
- await (baseMdxPlugin.buildEnd as any).call(this);
272
+ await (baseMdxPlugin.buildEnd as any).call(this)
112
273
  }
113
274
  },
114
- };
115
- }
116
-
117
- /**
118
- * Returns the current MDX cache statistics.
119
- * @returns An object with total and hit counts
120
- * @deprecated Removed for performance
121
- */
122
- export function getMdxCacheStats() {
123
- return { hits: 0, total: 0 };
275
+ }
124
276
  }
125
277
 
126
- let hits = 0;
127
- let total = 0;
278
+ let hits = 0
279
+ let total = 0
@@ -1,7 +1,7 @@
1
- import { normalizePath } from "../utils";
2
- import type { BoltdocsConfig } from "../config";
3
- import type { BoltdocsPluginOptions } from "./types";
4
- import path from "path";
1
+ import { normalizePath } from '../utils'
2
+ import type { BoltdocsConfig } from '../config'
3
+ import type { BoltdocsPluginOptions } from './types'
4
+ import path from 'path'
5
5
 
6
6
  /**
7
7
  * Generates the raw source code for the virtual entry file (`\0virtual:boltdocs-entry`).
@@ -17,14 +17,10 @@ export function generateEntryCode(
17
17
  ): string {
18
18
  const homeImport = options.homePage
19
19
  ? `import HomePage from '${normalizePath(options.homePage)}';`
20
- : "";
21
- const homeOption = options.homePage ? "homePage: HomePage," : "";
22
- const customCssImport = options.customCss
23
- ? `import '${normalizePath(options.customCss)}';`
24
- : "";
25
-
20
+ : ''
21
+ const homeOption = options.homePage ? 'homePage: HomePage,' : ''
26
22
  const pluginComponents =
27
- config?.plugins?.flatMap((p) => Object.entries(p.components || {})) || [];
23
+ config?.plugins?.flatMap((p) => Object.entries(p.components || {})) || []
28
24
 
29
25
  const componentImports = pluginComponents
30
26
  .map(
@@ -34,19 +30,33 @@ export function generateEntryCode(
34
30
  ]) => `import * as _comp_${name} from '${normalizePath(path)}';
35
31
  const ${name} = _comp_${name}.default || _comp_${name}['${name}'] || _comp_${name};`,
36
32
  )
37
- .join("\n");
38
- const componentMap = pluginComponents.map(([name]) => name).join(", ");
33
+ .join('\n')
34
+ const componentMap = pluginComponents.map(([name]) => name).join(', ')
35
+
36
+ const docsDirName = path.basename(options.docsDir || 'docs')
39
37
 
40
- const docsDirName = path.basename(options.docsDir || "docs");
38
+ const externalEntries = Object.entries(config?.external || {})
39
+ const externalImports = externalEntries
40
+ .map(
41
+ ([_routePath, compPath], i) =>
42
+ `import _ext_${i} from '${normalizePath(compPath)}';`,
43
+ )
44
+ .join('\n')
45
+ const externalOption =
46
+ externalEntries.length > 0
47
+ ? `externalPages: { ${externalEntries
48
+ .map(([path], i) => `"${path}": _ext_${i}`)
49
+ .join(', ')} },`
50
+ : ''
41
51
 
42
52
  return `
43
53
  import { createBoltdocsApp as _createApp } from 'boltdocs/client';
44
- import 'boltdocs/style.css';
45
- ${customCssImport}
46
54
  import _routes from 'virtual:boltdocs-routes';
47
55
  import _config from 'virtual:boltdocs-config';
56
+ import _user_mdx_components from 'virtual:boltdocs-mdx-components';
48
57
  ${homeImport}
49
58
  ${componentImports}
59
+ ${externalImports}
50
60
 
51
61
  _createApp({
52
62
  target: '#root',
@@ -56,7 +66,8 @@ _createApp({
56
66
  modules: import.meta.glob('/${docsDirName}/**/*.{md,mdx}'),
57
67
  hot: import.meta.hot,
58
68
  ${homeOption}
59
- components: { ${componentMap} },
69
+ ${externalOption}
70
+ components: { ${componentMap}${componentMap ? ', ' : ''} ...(_user_mdx_components || {}) },
60
71
  });
61
- `;
72
+ `
62
73
  }
@@ -1,4 +1,4 @@
1
- import type { BoltdocsConfig } from "../config";
1
+ import type { BoltdocsConfig } from '../config'
2
2
 
3
3
  /**
4
4
  * Injects OpenGraph, Twitter, and generic SEO meta tags into the final HTML output.
@@ -9,8 +9,8 @@ import type { BoltdocsConfig } from "../config";
9
9
  * @returns The modified HTML string with injected tags
10
10
  */
11
11
  export function injectHtmlMeta(html: string, config: BoltdocsConfig): string {
12
- const title = config.themeConfig?.title || "Boltdocs";
13
- const description = config.themeConfig?.description || "";
12
+ const title = config.themeConfig?.title || 'Boltdocs'
13
+ const description = config.themeConfig?.description || ''
14
14
 
15
15
  const seoTags = [
16
16
  `<meta name="description" content="${description}">`,
@@ -21,7 +21,7 @@ export function injectHtmlMeta(html: string, config: BoltdocsConfig): string {
21
21
  `<meta name="twitter:title" content="${title}">`,
22
22
  `<meta name="twitter:description" content="${description}">`,
23
23
  `<meta name="generator" content="Boltdocs">`,
24
- ].join("\n ");
24
+ ].join('\n ')
25
25
 
26
26
  const themeScript = `
27
27
  <script>
@@ -39,17 +39,17 @@ export function injectHtmlMeta(html: string, config: BoltdocsConfig): string {
39
39
  } catch (e) {}
40
40
  })();
41
41
  </script>
42
- `;
42
+ `
43
43
 
44
- html = html.replace(/<title>.*?<\/title>/, `<title>${title}</title>`);
45
- html = html.replace("</head>", ` ${seoTags}\n${themeScript} </head>`);
44
+ html = html.replace(/<title>.*?<\/title>/, `<title>${title}</title>`)
45
+ html = html.replace('</head>', ` ${seoTags}\n${themeScript} </head>`)
46
46
 
47
- if (!html.includes("src/main")) {
47
+ if (!html.includes('src/main')) {
48
48
  html = html.replace(
49
- "</body>",
49
+ '</body>',
50
50
  ' <script type="module">import "virtual:boltdocs-entry";</script>\n </body>',
51
- );
51
+ )
52
52
  }
53
53
 
54
- return html;
54
+ return html
55
55
  }