boltdocs 2.1.1 → 2.3.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 (133) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/bin/boltdocs.js +2 -2
  3. package/dist/base-ui/index.d.mts +25 -0
  4. package/dist/base-ui/index.d.ts +25 -0
  5. package/dist/base-ui/index.js +1 -0
  6. package/dist/base-ui/index.mjs +1 -0
  7. package/dist/{cache-Q4T6VAUL.mjs → cache-P6WK424C.mjs} +1 -1
  8. package/dist/chunk-22NXDNP4.mjs +74 -0
  9. package/dist/chunk-2HUVMMJU.mjs +1 -0
  10. package/dist/chunk-2Z5T6EAU.mjs +1 -0
  11. package/dist/chunk-CRZGOE32.mjs +1 -0
  12. package/dist/chunk-HA6543SL.mjs +1 -0
  13. package/dist/chunk-JD3RSDE4.mjs +1 -0
  14. package/dist/chunk-JZXLCA2E.mjs +1 -0
  15. package/dist/chunk-NBCYHLAA.mjs +1 -0
  16. package/dist/chunk-RPUERTVC.mjs +1 -0
  17. package/dist/chunk-T3W44KWY.mjs +1 -0
  18. package/dist/chunk-URTD6E6S.mjs +1 -0
  19. package/dist/chunk-W2NB4T6V.mjs +1 -0
  20. package/dist/chunk-Y4RRHPXC.mjs +1 -0
  21. package/dist/client/index.d.mts +13 -115
  22. package/dist/client/index.d.ts +13 -115
  23. package/dist/client/index.js +1 -1
  24. package/dist/client/index.mjs +1 -1
  25. package/dist/client/ssr.js +1 -1
  26. package/dist/client/ssr.mjs +1 -1
  27. package/dist/client/types.d.mts +3 -0
  28. package/dist/client/types.d.ts +3 -0
  29. package/dist/client/types.js +1 -0
  30. package/dist/client/types.mjs +0 -0
  31. package/dist/copy-markdown-C-90ixSe.d.ts +15 -0
  32. package/dist/copy-markdown-CbS8X-qe.d.mts +15 -0
  33. package/dist/{client/hooks → hooks}/index.d.mts +16 -11
  34. package/dist/{client/hooks → hooks}/index.d.ts +16 -11
  35. package/dist/hooks/index.js +1 -0
  36. package/dist/hooks/index.mjs +1 -0
  37. package/dist/integrations/index.d.mts +48 -0
  38. package/dist/integrations/index.d.ts +48 -0
  39. package/dist/integrations/index.js +1 -0
  40. package/dist/integrations/index.mjs +1 -0
  41. package/dist/link-DfBwCeZc.d.mts +68 -0
  42. package/dist/link-DfBwCeZc.d.ts +68 -0
  43. package/dist/loading-B7X5Wchs.d.ts +66 -0
  44. package/dist/loading-WuaQbsKb.d.mts +66 -0
  45. package/dist/{client/components/mdx → mdx}/index.d.mts +6 -38
  46. package/dist/{client/components/mdx → mdx}/index.d.ts +6 -38
  47. package/dist/mdx/index.js +1 -0
  48. package/dist/mdx/index.mjs +1 -0
  49. package/dist/node/cli-entry.js +31 -27
  50. package/dist/node/cli-entry.mjs +5 -1
  51. package/dist/node/index.d.mts +44 -14
  52. package/dist/node/index.d.ts +44 -14
  53. package/dist/node/index.js +24 -24
  54. package/dist/node/index.mjs +1 -1
  55. package/dist/primitives/index.d.mts +301 -0
  56. package/dist/primitives/index.d.ts +301 -0
  57. package/dist/primitives/index.js +1 -0
  58. package/dist/primitives/index.mjs +1 -0
  59. package/dist/search-dialog-ZRXBAQJ5.mjs +1 -0
  60. package/dist/{types-Cp21DHI6.d.mts → types-j7jvWsJj.d.mts} +63 -17
  61. package/dist/{types-Cp21DHI6.d.ts → types-j7jvWsJj.d.ts} +63 -17
  62. package/dist/{use-routes-xLhumjbV.d.ts → use-routes-Cd806kGw.d.ts} +1 -1
  63. package/dist/{use-routes-8Iei6jTp.d.mts → use-routes-DDL0_jkQ.d.mts} +1 -1
  64. package/package.json +35 -8
  65. package/src/client/app/index.tsx +155 -35
  66. package/src/client/app/mdx-component.tsx +7 -3
  67. package/src/client/app/theme-context.tsx +47 -23
  68. package/src/client/components/default-layout.tsx +16 -6
  69. package/src/client/components/primitives/breadcrumbs.tsx +1 -1
  70. package/src/client/components/primitives/navbar.tsx +8 -5
  71. package/src/client/components/primitives/search-dialog.tsx +15 -6
  72. package/src/client/components/primitives/sidebar.tsx +3 -2
  73. package/src/client/components/primitives/skeleton.tsx +26 -0
  74. package/src/client/components/ui-base/breadcrumbs.tsx +1 -1
  75. package/src/client/components/ui-base/index.ts +17 -0
  76. package/src/client/components/ui-base/loading.tsx +43 -73
  77. package/src/client/components/ui-base/navbar.tsx +74 -39
  78. package/src/client/components/ui-base/page-nav.tsx +2 -1
  79. package/src/client/components/ui-base/powered-by.tsx +11 -5
  80. package/src/client/components/ui-base/search-dialog.tsx +16 -5
  81. package/src/client/components/ui-base/sidebar.tsx +33 -22
  82. package/src/client/components/ui-base/tabs.tsx +4 -1
  83. package/src/client/components/ui-base/theme-toggle.tsx +35 -15
  84. package/src/client/hooks/use-i18n.ts +38 -7
  85. package/src/client/hooks/use-localized-to.ts +51 -73
  86. package/src/client/hooks/use-navbar.ts +10 -3
  87. package/src/client/hooks/use-page-nav.ts +27 -6
  88. package/src/client/hooks/use-routes.ts +62 -17
  89. package/src/client/hooks/use-search.ts +84 -46
  90. package/src/client/hooks/use-sidebar.ts +6 -2
  91. package/src/client/hooks/use-version.ts +5 -0
  92. package/src/client/integrations/index.ts +1 -0
  93. package/src/client/store/use-boltdocs-store.ts +44 -0
  94. package/src/client/theme/neutral.css +29 -0
  95. package/src/client/types.ts +4 -2
  96. package/src/client/utils/i18n.ts +23 -0
  97. package/src/node/{cli.ts → cli/build.ts} +17 -23
  98. package/src/node/cli/dev.ts +22 -0
  99. package/src/node/cli/doctor.ts +243 -0
  100. package/src/node/cli/index.ts +9 -0
  101. package/src/node/cli/ui.ts +54 -0
  102. package/src/node/cli-entry.ts +16 -16
  103. package/src/node/config.ts +54 -17
  104. package/src/node/index.ts +1 -1
  105. package/src/node/mdx/cache.ts +12 -0
  106. package/src/node/mdx/highlighter.ts +47 -0
  107. package/src/node/mdx/index.ts +114 -0
  108. package/src/node/mdx/rehype-shiki.ts +53 -0
  109. package/src/node/mdx/remark-shiki.ts +61 -0
  110. package/src/node/plugin/entry.ts +1 -1
  111. package/src/node/plugin/html.ts +8 -4
  112. package/src/node/plugin/index.ts +135 -72
  113. package/src/node/routes/index.ts +34 -13
  114. package/src/node/routes/parser.ts +13 -5
  115. package/src/node/search/index.ts +55 -0
  116. package/src/node/ssg/index.ts +15 -7
  117. package/src/node/ssg/robots.ts +7 -4
  118. package/src/node/utils.ts +32 -2
  119. package/tsup.config.ts +7 -2
  120. package/dist/chunk-52MVMZWS.mjs +0 -1
  121. package/dist/chunk-BVWWKXJH.mjs +0 -1
  122. package/dist/chunk-DVY3RDXD.mjs +0 -1
  123. package/dist/chunk-FUVYCYWC.mjs +0 -1
  124. package/dist/chunk-GBLMDJ2B.mjs +0 -1
  125. package/dist/chunk-ISPX45DF.mjs +0 -1
  126. package/dist/chunk-PNXZMUCO.mjs +0 -1
  127. package/dist/chunk-V2ZHKQSP.mjs +0 -74
  128. package/dist/client/components/mdx/index.js +0 -1
  129. package/dist/client/components/mdx/index.mjs +0 -1
  130. package/dist/client/hooks/index.js +0 -1
  131. package/dist/client/hooks/index.mjs +0 -1
  132. package/dist/search-dialog-TWGYKF2D.mjs +0 -1
  133. package/src/node/mdx.ts +0 -279
@@ -0,0 +1,54 @@
1
+ /**
2
+ * ANSI Escape sequences for terminal coloring and styling.
3
+ * Used to provide a premium and consistent CLI experience.
4
+ */
5
+ export const colors = {
6
+ reset: '\x1b[0m',
7
+ bold: '\x1b[1m',
8
+ red: '\x1b[31m',
9
+ green: '\x1b[32m',
10
+ yellow: '\x1b[33m',
11
+ blue: '\x1b[34m',
12
+ cyan: '\x1b[36m',
13
+ gray: '\x1b[90m',
14
+ }
15
+
16
+ /**
17
+ * Formats a message with the boltdocs prefix and provided styling.
18
+ *
19
+ * @param message - The content to log
20
+ * @param style - Optional ANSI style prefix
21
+ * @returns The formatted string
22
+ */
23
+ export function formatLog(message: string, style: string = ''): string {
24
+ return `${style}${colors.bold}[boltdocs]${colors.reset} ${message}${colors.reset}`
25
+ }
26
+
27
+ /**
28
+ * Logs a standard informational message to the console.
29
+ *
30
+ * @param message - The message to display
31
+ */
32
+ export function info(message: string) {
33
+ console.log(formatLog(message))
34
+ }
35
+
36
+ /**
37
+ * Logs an error message to the console with red styling.
38
+ *
39
+ * @param message - The error description
40
+ * @param error - Optional error object for stack tracing
41
+ */
42
+ export function error(message: string, error?: any) {
43
+ console.error(formatLog(message, colors.red))
44
+ if (error) console.error(error)
45
+ }
46
+
47
+ /**
48
+ * Logs a success message to the console with green styling.
49
+ *
50
+ * @param message - The success description
51
+ */
52
+ export function success(message: string) {
53
+ console.log(formatLog(message, colors.green))
54
+ }
@@ -1,24 +1,24 @@
1
1
  #!/usr/bin/env node
2
- import cac from "cac";
3
- import { devAction, buildAction, previewAction } from "./cli";
2
+ import cac from 'cac'
3
+ import {
4
+ devAction,
5
+ buildAction,
6
+ previewAction,
7
+ doctorAction,
8
+ } from './cli/index'
4
9
 
5
- const cli = cac("boltdocs");
10
+ const cli = cac('boltdocs')
6
11
 
7
- cli
8
- .command("[root]", "Start development server")
9
- .alias("dev")
10
- .action(devAction);
12
+ cli.command('[root]', 'Start development server').alias('dev').action(devAction)
11
13
 
12
- cli
13
- .command("build [root]", "Build for production")
14
- .action(buildAction);
14
+ cli.command('build [root]', 'Build for production').action(buildAction)
15
15
 
16
- cli
17
- .command("preview [root]", "Preview production build")
18
- .action(previewAction);
16
+ cli.command('preview [root]', 'Preview production build').action(previewAction)
19
17
 
20
- cli.help();
18
+ cli.command('doctor [root]', 'Check documentation health').action(doctorAction)
19
+
20
+ cli.help()
21
21
  // This will be replaced at build time or package publishing, but hardcoded to 2.0.0 for now
22
- cli.version("2.0.0");
22
+ cli.version('2.0.0')
23
23
 
24
- cli.parse();
24
+ cli.parse()
@@ -24,10 +24,10 @@ export interface BoltdocsFooterConfig {
24
24
  * Theme-specific configuration options governing the appearance and navigation of the site.
25
25
  */
26
26
  export interface BoltdocsThemeConfig {
27
- /** The global title of the documentation site */
28
- title?: string
29
- /** The global description of the site (used for SEO) */
30
- description?: string
27
+ /** The global title of the documentation site (can be translated) */
28
+ title?: string | Record<string, string>
29
+ /** The global description of the site (can be translated) */
30
+ description?: string | Record<string, string>
31
31
  /** URL path to the site logo or an object for light/dark versions */
32
32
  logo?:
33
33
  | string
@@ -40,12 +40,17 @@ export interface BoltdocsThemeConfig {
40
40
  }
41
41
  /** Items to display in the top navigation bar */
42
42
  navbar?: Array<{
43
- /** Text to display */
44
- label: string
43
+ /** Text to display (can be a string or a map of translations) */
44
+ label: string | Record<string, string>
45
45
  /** URL path or external link */
46
46
  href: string
47
47
  /** Nested items for NavigationMenu */
48
- items?: Array<{ label: string; href: string }>
48
+ items?: Array<{
49
+ /** Text to display (can be a string or a map of translations) */
50
+ label: string | Record<string, string>
51
+ /** URL path or external link */
52
+ href: string
53
+ }>
49
54
  }>
50
55
  /** Items to display in the sidebar, organized optionally by group URLs */
51
56
  sidebar?: Record<string, Array<{ text: string; link: string }>>
@@ -78,7 +83,12 @@ export interface BoltdocsThemeConfig {
78
83
  * Top-level tabs for organizing documentation groups.
79
84
  * Tab discovery uses the (tab-id) directory syntax.
80
85
  */
81
- tabs?: Array<{ id: string; text: string; icon?: string }>
86
+ tabs?: Array<{
87
+ id: string
88
+ /** Text to display (can be a string or a map of translations) */
89
+ text: string | Record<string, string>
90
+ icon?: string
91
+ }>
82
92
  /**
83
93
  * The syntax highlighting theme for code blocks.
84
94
  * Supports any Shiki theme name (e.g., 'github-dark', 'one-dark-pro', 'aurora-x').
@@ -112,24 +122,55 @@ export type BoltdocsRobotsConfig =
112
122
  sitemaps?: string[]
113
123
  }
114
124
 
125
+ /**
126
+ * Configuration for a specific locale.
127
+ */
128
+ export interface BoltdocsLocaleConfig {
129
+ /** The display name of the locale */
130
+ label?: string
131
+ /** The text direction (ltr or rtl) */
132
+ direction?: 'ltr' | 'rtl'
133
+ /** The HTML lang attribute value (e.g., 'en-US') */
134
+ htmlLang?: string
135
+ /** The calendar system to use (e.g., 'gregory') */
136
+ calendar?: string
137
+ }
138
+
115
139
  /**
116
140
  * Configuration for internationalization (i18n).
117
141
  */
118
142
  export interface BoltdocsI18nConfig {
119
143
  /** The default locale (e.g., 'en') */
120
144
  defaultLocale: string
121
- /** Available locales and their display names (e.g., { en: 'English', es: 'Español' }) */
145
+ /** Available locales and their basic display names (e.g., { en: 'English', es: 'Español' }) */
122
146
  locales: Record<string, string>
147
+ /** Detailed configuration for each locale */
148
+ localeConfigs?: Record<string, BoltdocsLocaleConfig>
149
+ }
150
+
151
+ /**
152
+ * Configuration for a specific documentation version.
153
+ */
154
+ export interface BoltdocsVersionConfig {
155
+ /** The display name of the version (e.g., 'v2.0') */
156
+ label: string
157
+ /** The URL path prefix for the version (e.g., '2.0') */
158
+ path: string
123
159
  }
124
160
 
125
161
  /**
126
162
  * Configuration for documentation versioning.
127
163
  */
128
164
  export interface BoltdocsVersionsConfig {
129
- /** The default version (e.g., 'v2') */
165
+ /** The default version path (e.g., 'v2') */
130
166
  defaultVersion: string
131
- /** Available versions and their display names (e.g., { v1: 'Version 1.x', v2: 'Version 2.x' }) */
132
- versions: Record<string, string>
167
+ /**
168
+ * Optional prefix for all version paths (e.g., 'v').
169
+ * If set to 'v', version '1.1' will be available at '/docs/v1.1'.
170
+ */
171
+ prefix?: string
172
+ /** Available versions configurations */
173
+ versions: BoltdocsVersionConfig[]
133
174
  }
134
175
 
135
176
  /**
@@ -189,8 +230,6 @@ export interface BoltdocsConfig {
189
230
  robots?: BoltdocsRobotsConfig
190
231
  /** Low-level Vite configuration overrides */
191
232
  vite?: import('vite').InlineConfig
192
- /** @deprecated Use theme instead */
193
- themeConfig?: BoltdocsThemeConfig
194
233
  }
195
234
 
196
235
  export function defineConfig(config: BoltdocsConfig): BoltdocsConfig {
@@ -286,13 +325,12 @@ export async function resolveConfig(
286
325
  poweredBy: userConfig.poweredBy,
287
326
  communityHelp: userConfig.communityHelp,
288
327
  version: userConfig.version,
289
- editLink: userConfig.editLink
328
+ editLink: userConfig.editLink,
290
329
  }
291
330
 
292
331
  // User can define properties at top level or inside themeConfig/theme
293
332
  const userThemeConfig: BoltdocsThemeConfig = {
294
333
  ...themeConfigFromTop,
295
- ...(userConfig.themeConfig || {}),
296
334
  ...(userConfig.theme || {}),
297
335
  }
298
336
 
@@ -330,4 +368,3 @@ export async function resolveConfig(
330
368
  vite: userConfig.vite,
331
369
  }
332
370
  }
333
-
package/src/node/index.ts CHANGED
@@ -2,7 +2,7 @@ import type { Plugin, InlineConfig } from 'vite'
2
2
  import react from '@vitejs/plugin-react'
3
3
  import tailwindcss from '@tailwindcss/vite'
4
4
  import { boltdocsPlugin } from './plugin/index'
5
- import { boltdocsMdxPlugin } from './mdx'
5
+ import { boltdocsMdxPlugin } from './mdx/index'
6
6
  import type { BoltdocsPluginOptions } from './plugin/index'
7
7
 
8
8
  import { resolveConfig, type BoltdocsConfig } from './config'
@@ -0,0 +1,12 @@
1
+ import { TransformCache } from '../cache'
2
+
3
+ /**
4
+ * Version identifier for the MDX plugin to invalidate cache if logic changes.
5
+ */
6
+ export const MDX_PLUGIN_VERSION = 'v3'
7
+
8
+ /**
9
+ * Persistent cache for MDX transformations.
10
+ * Saves results to `.boltdocs/transform-mdx.json.gz`.
11
+ */
12
+ export const mdxCache = new TransformCache('mdx')
@@ -0,0 +1,47 @@
1
+ import { createHighlighter } from 'shiki'
2
+ import type { Highlighter } from 'shiki'
3
+
4
+ let shikiHighlighter: Highlighter | null = null
5
+
6
+ /**
7
+ * Retrieves or initializes the Shiki highlighter instance.
8
+ * Supports dual-theme configurations (light/dark).
9
+ *
10
+ * @param codeTheme - Theme configuration (string for single, object for dual).
11
+ * @returns A promise resolving to the highlighter instance.
12
+ */
13
+ export async function getShikiHighlighter(codeTheme: any) {
14
+ if (shikiHighlighter) return shikiHighlighter
15
+
16
+ const themes =
17
+ typeof codeTheme === 'object'
18
+ ? [codeTheme.light, codeTheme.dark]
19
+ : [codeTheme ?? 'github-dark']
20
+
21
+ // Fallbacks for standard themes
22
+ ;['github-light', 'github-dark'].forEach((t) => {
23
+ if (!themes.includes(t)) themes.push(t)
24
+ })
25
+
26
+ // Initialize with a core set of languages first to speed up boot
27
+ shikiHighlighter = await createHighlighter({
28
+ themes,
29
+ langs: [
30
+ 'tsx',
31
+ 'jsx',
32
+ 'ts',
33
+ 'js',
34
+ 'json',
35
+ 'md',
36
+ 'mdx',
37
+ 'css',
38
+ 'html',
39
+ 'bash',
40
+ 'sh',
41
+ 'yaml',
42
+ 'yml',
43
+ ],
44
+ })
45
+
46
+ return shikiHighlighter
47
+ }
@@ -0,0 +1,114 @@
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
+
8
+ import type { BoltdocsConfig } from '../config'
9
+ import { mdxCache, MDX_PLUGIN_VERSION } from './cache'
10
+ import { remarkShiki } from './remark-shiki'
11
+ import { rehypeShiki } from './rehype-shiki'
12
+
13
+ let mdxCacheLoaded = false
14
+ let hits = 0
15
+ let total = 0
16
+
17
+ /**
18
+ * Configures the MDX compiler for Vite using `@mdx-js/rollup`.
19
+ * Includes standard remark and rehype plugins for GitHub Flavored Markdown (GFM),
20
+ * frontmatter extraction, and auto-linking headers.
21
+ *
22
+ * Also wraps the plugin with a persistent cache to avoid re-compiling unchanged MDX files.
23
+ *
24
+ * @param config - The Boltdocs configuration containing custom plugins
25
+ * @param compiler - The MDX compiler plugin (for testing)
26
+ * @returns A Vite plugin configured for MDX parsing with caching
27
+ */
28
+ export function boltdocsMdxPlugin(
29
+ config?: BoltdocsConfig,
30
+ compiler = mdxPlugin,
31
+ ): Plugin {
32
+ const extraRemarkPlugins =
33
+ config?.plugins?.flatMap((p) => p.remarkPlugins || []) || []
34
+ const extraRehypePlugins =
35
+ config?.plugins?.flatMap((p) => p.rehypePlugins || []) || []
36
+
37
+ const baseMdxPlugin = compiler({
38
+ remarkPlugins: [
39
+ remarkGfm,
40
+ remarkFrontmatter,
41
+ [remarkShiki, config],
42
+ ...(extraRemarkPlugins as any[]),
43
+ ],
44
+ rehypePlugins: [
45
+ rehypeSlug,
46
+ [rehypeShiki, config],
47
+ ...(extraRehypePlugins as any[]),
48
+ ],
49
+ jsxRuntime: 'automatic',
50
+ providerImportSource: '@mdx-js/react',
51
+ }) as Plugin
52
+
53
+ return {
54
+ ...baseMdxPlugin,
55
+ name: 'vite-plugin-boltdocs-mdx',
56
+
57
+ async buildStart() {
58
+ hits = 0
59
+ total = 0
60
+ if (!mdxCacheLoaded) {
61
+ mdxCache.load()
62
+ mdxCacheLoaded = true
63
+ }
64
+ // @ts-ignore
65
+ if (baseMdxPlugin.buildStart) {
66
+ // @ts-ignore
67
+ await baseMdxPlugin.buildStart.call(this)
68
+ }
69
+ },
70
+
71
+ async transform(code, id, options) {
72
+ if (!id.endsWith('.md') && !id.endsWith('.mdx')) {
73
+ // @ts-ignore
74
+ return baseMdxPlugin.transform?.call(this, code, id, options)
75
+ }
76
+
77
+ console.log(`[boltdocs] Transforming MDX: ${id}`)
78
+ total++
79
+ // Create a cache key based on path, content, and plugin version
80
+ const contentHash = crypto.createHash('md5').update(code).digest('hex')
81
+ const cacheKey = `${id}:${contentHash}:${MDX_PLUGIN_VERSION}`
82
+
83
+ const cached = mdxCache.get(cacheKey)
84
+ if (cached) {
85
+ hits++
86
+ return { code: cached, map: null }
87
+ }
88
+
89
+ // @ts-ignore
90
+ const result = await baseMdxPlugin.transform.call(this, code, id, options)
91
+
92
+ if (result && typeof result === 'object' && result.code) {
93
+ mdxCache.set(cacheKey, result.code)
94
+ }
95
+
96
+ return result
97
+ },
98
+
99
+ async buildEnd() {
100
+ if (total > 0) {
101
+ console.log(
102
+ `[boltdocs] MDX Cache Performance: ${hits}/${total} hits (${Math.round((hits / total) * 100) || 0}%)`,
103
+ )
104
+ }
105
+ mdxCache.save()
106
+ await mdxCache.flush()
107
+ // @ts-ignore
108
+ if (baseMdxPlugin.buildEnd) {
109
+ // @ts-ignore
110
+ await baseMdxPlugin.buildEnd.call(this)
111
+ }
112
+ },
113
+ }
114
+ }
@@ -0,0 +1,53 @@
1
+ import { visit } from 'unist-util-visit'
2
+ import type { BoltdocsConfig } from '../config'
3
+ import { getShikiHighlighter } from './highlighter'
4
+
5
+ /**
6
+ * Custom rehype plugin to perform syntax highlighting at build time for
7
+ * standard Markdown code blocks.
8
+ *
9
+ * Injects 'data-highlighted="true"' and 'highlightedHtml' into the 'pre' tag properties,
10
+ * which are then consumed by the client-side CodeBlock component.
11
+ *
12
+ * @param config - The Boltdocs configuration
13
+ * @returns A rehype plugin function
14
+ */
15
+ export function rehypeShiki(config?: BoltdocsConfig) {
16
+ return async (tree: any) => {
17
+ const codeTheme = config?.theme?.codeTheme || {
18
+ light: 'github-light',
19
+ dark: 'github-dark',
20
+ }
21
+ const highlighter = await getShikiHighlighter(codeTheme)
22
+
23
+ visit(tree, 'element', (node: any) => {
24
+ // Handle standard Markdown code blocks
25
+ if (node.tagName === 'pre' && node.children?.[0]?.tagName === 'code') {
26
+ const codeNode = node.children[0]
27
+ const className = codeNode.properties?.className || []
28
+ const langMatch = className.find((c: string) =>
29
+ c.startsWith('language-'),
30
+ )
31
+ const lang = langMatch ? langMatch.slice(9) : 'text'
32
+ const code = codeNode.children[0]?.value || ''
33
+
34
+ const options: any = { lang }
35
+ if (typeof codeTheme === 'object') {
36
+ options.themes = {
37
+ light: codeTheme.light,
38
+ dark: codeTheme.dark,
39
+ }
40
+ } else {
41
+ options.theme = codeTheme
42
+ }
43
+
44
+ const html = highlighter.codeToHtml(code, options)
45
+
46
+ // Inject highlighted HTML and mark as highlighted for CodeBlock component
47
+ node.properties.dataHighlighted = 'true'
48
+ node.properties.highlightedHtml = html
49
+ node.children = []
50
+ }
51
+ })
52
+ }
53
+ }
@@ -0,0 +1,61 @@
1
+ import { visit } from 'unist-util-visit'
2
+ import type { BoltdocsConfig } from '../config'
3
+ import { getShikiHighlighter } from './highlighter'
4
+
5
+ /**
6
+ * Custom remark plugin to highlight code in ComponentPreview components.
7
+ * This runs before rehype, ensuring that the 'highlightedHtml' prop is correctly
8
+ * attached to the MDX component as a JSX attribute.
9
+ *
10
+ * Supports both string literals and MDX expression values (template literals)
11
+ * for the 'code' attribute.
12
+ *
13
+ * @param config - The Boltdocs configuration
14
+ * @returns A remark plugin function
15
+ */
16
+ export function remarkShiki(config?: BoltdocsConfig) {
17
+ return async (tree: any) => {
18
+ const codeTheme = config?.theme?.codeTheme ?? {
19
+ light: 'github-light',
20
+ dark: 'github-dark',
21
+ }
22
+ const highlighter = await getShikiHighlighter(codeTheme)
23
+
24
+ visit(tree, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node: any) => {
25
+ if (node.name !== 'ComponentPreview') return
26
+
27
+ const codeAttr = node.attributes?.find((a: any) => a.name === 'code')
28
+ let code = ''
29
+
30
+ if (codeAttr) {
31
+ if (typeof codeAttr.value === 'string') {
32
+ code = codeAttr.value
33
+ } else if (codeAttr.value?.type === 'mdxJsxAttributeValueExpression') {
34
+ const expr = codeAttr.value.value ?? ''
35
+ code = expr.match(/^[`'"](.+)[`'"]$/)?.[1] ?? expr
36
+ }
37
+ }
38
+
39
+ if (!code) return
40
+
41
+ const options: any =
42
+ typeof codeTheme === 'object'
43
+ ? {
44
+ themes: { light: codeTheme.light, dark: codeTheme.dark },
45
+ lang: 'tsx',
46
+ }
47
+ : { theme: codeTheme, lang: 'tsx' }
48
+
49
+ const html = highlighter.codeToHtml(code, options)
50
+
51
+ node.attributes = (node.attributes ?? []).filter(
52
+ (a: any) => a.name !== 'highlightedHtml',
53
+ )
54
+ node.attributes.push({
55
+ type: 'mdxJsxAttribute',
56
+ name: 'highlightedHtml',
57
+ value: html,
58
+ })
59
+ })
60
+ }
61
+ }
@@ -19,7 +19,7 @@ export function generateEntryCode(
19
19
  const homeImport = options.homePage
20
20
  ? `import HomePage from '${normalizePath(options.homePage)}';`
21
21
  : ''
22
-
22
+
23
23
  // Auto-import index.css if it exists
24
24
  const cssPath = path.resolve(process.cwd(), 'index.css')
25
25
  const cssImport = fs.existsSync(cssPath) ? "import './index.css';" : ''
@@ -4,7 +4,7 @@ import type { BoltdocsConfig } from '../config'
4
4
  * Provides a default HTML template if none is found in the project root.
5
5
  */
6
6
  export function getHtmlTemplate(config: BoltdocsConfig): string {
7
- const title = config.theme?.title || config.themeConfig?.title || 'Boltdocs'
7
+ const title = config.theme?.title || 'Boltdocs'
8
8
  return `<!doctype html>
9
9
  <html lang="en">
10
10
  <head>
@@ -27,7 +27,7 @@ export function getHtmlTemplate(config: BoltdocsConfig): string {
27
27
  * @returns {string} The modified HTML string with injected tags
28
28
  */
29
29
  export function injectHtmlMeta(html: string, config: BoltdocsConfig): string {
30
- const theme = config.theme || config.themeConfig
30
+ const theme = config.theme
31
31
  const title = theme?.title || 'Boltdocs'
32
32
  const description = theme?.description || ''
33
33
 
@@ -46,12 +46,16 @@ export function injectHtmlMeta(html: string, config: BoltdocsConfig): string {
46
46
  `<meta name="description" content="${description}">`,
47
47
  `<meta property="og:title" content="${title}">`,
48
48
  `<meta property="og:description" content="${description}">`,
49
- theme?.ogImage ? `<meta property="og:image" content="${theme.ogImage}">` : '',
49
+ theme?.ogImage
50
+ ? `<meta property="og:image" content="${theme.ogImage}">`
51
+ : '',
50
52
  `<meta property="og:type" content="website">`,
51
53
  `<meta name="twitter:card" content="summary_large_image">`,
52
54
  `<meta name="twitter:title" content="${title}">`,
53
55
  `<meta name="twitter:description" content="${description}">`,
54
- theme?.ogImage ? `<meta name="twitter:image" content="${theme.ogImage}">` : '',
56
+ theme?.ogImage
57
+ ? `<meta name="twitter:image" content="${theme.ogImage}">`
58
+ : '',
55
59
  `<meta name="generator" content="Boltdocs">`,
56
60
  ]
57
61
  .filter(Boolean)