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.
- package/CHANGELOG.md +19 -0
- package/bin/boltdocs.js +2 -2
- package/dist/base-ui/index.d.mts +25 -0
- package/dist/base-ui/index.d.ts +25 -0
- package/dist/base-ui/index.js +1 -0
- package/dist/base-ui/index.mjs +1 -0
- package/dist/{cache-Q4T6VAUL.mjs → cache-P6WK424C.mjs} +1 -1
- package/dist/chunk-22NXDNP4.mjs +74 -0
- package/dist/chunk-2HUVMMJU.mjs +1 -0
- package/dist/chunk-2Z5T6EAU.mjs +1 -0
- package/dist/chunk-CRZGOE32.mjs +1 -0
- package/dist/chunk-HA6543SL.mjs +1 -0
- package/dist/chunk-JD3RSDE4.mjs +1 -0
- package/dist/chunk-JZXLCA2E.mjs +1 -0
- package/dist/chunk-NBCYHLAA.mjs +1 -0
- package/dist/chunk-RPUERTVC.mjs +1 -0
- package/dist/chunk-T3W44KWY.mjs +1 -0
- package/dist/chunk-URTD6E6S.mjs +1 -0
- package/dist/chunk-W2NB4T6V.mjs +1 -0
- package/dist/chunk-Y4RRHPXC.mjs +1 -0
- package/dist/client/index.d.mts +13 -115
- package/dist/client/index.d.ts +13 -115
- package/dist/client/index.js +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/client/ssr.js +1 -1
- package/dist/client/ssr.mjs +1 -1
- package/dist/client/types.d.mts +3 -0
- package/dist/client/types.d.ts +3 -0
- package/dist/client/types.js +1 -0
- package/dist/client/types.mjs +0 -0
- package/dist/copy-markdown-C-90ixSe.d.ts +15 -0
- package/dist/copy-markdown-CbS8X-qe.d.mts +15 -0
- package/dist/{client/hooks → hooks}/index.d.mts +16 -11
- package/dist/{client/hooks → hooks}/index.d.ts +16 -11
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/index.mjs +1 -0
- package/dist/integrations/index.d.mts +48 -0
- package/dist/integrations/index.d.ts +48 -0
- package/dist/integrations/index.js +1 -0
- package/dist/integrations/index.mjs +1 -0
- package/dist/link-DfBwCeZc.d.mts +68 -0
- package/dist/link-DfBwCeZc.d.ts +68 -0
- package/dist/loading-B7X5Wchs.d.ts +66 -0
- package/dist/loading-WuaQbsKb.d.mts +66 -0
- package/dist/{client/components/mdx → mdx}/index.d.mts +6 -38
- package/dist/{client/components/mdx → mdx}/index.d.ts +6 -38
- package/dist/mdx/index.js +1 -0
- package/dist/mdx/index.mjs +1 -0
- package/dist/node/cli-entry.js +31 -27
- package/dist/node/cli-entry.mjs +5 -1
- package/dist/node/index.d.mts +44 -14
- package/dist/node/index.d.ts +44 -14
- package/dist/node/index.js +24 -24
- package/dist/node/index.mjs +1 -1
- package/dist/primitives/index.d.mts +301 -0
- package/dist/primitives/index.d.ts +301 -0
- package/dist/primitives/index.js +1 -0
- package/dist/primitives/index.mjs +1 -0
- package/dist/search-dialog-ZRXBAQJ5.mjs +1 -0
- package/dist/{types-Cp21DHI6.d.mts → types-j7jvWsJj.d.mts} +63 -17
- package/dist/{types-Cp21DHI6.d.ts → types-j7jvWsJj.d.ts} +63 -17
- package/dist/{use-routes-xLhumjbV.d.ts → use-routes-Cd806kGw.d.ts} +1 -1
- package/dist/{use-routes-8Iei6jTp.d.mts → use-routes-DDL0_jkQ.d.mts} +1 -1
- package/package.json +35 -8
- package/src/client/app/index.tsx +155 -35
- package/src/client/app/mdx-component.tsx +7 -3
- package/src/client/app/theme-context.tsx +47 -23
- package/src/client/components/default-layout.tsx +16 -6
- package/src/client/components/primitives/breadcrumbs.tsx +1 -1
- package/src/client/components/primitives/navbar.tsx +8 -5
- package/src/client/components/primitives/search-dialog.tsx +15 -6
- package/src/client/components/primitives/sidebar.tsx +3 -2
- package/src/client/components/primitives/skeleton.tsx +26 -0
- package/src/client/components/ui-base/breadcrumbs.tsx +1 -1
- package/src/client/components/ui-base/index.ts +17 -0
- package/src/client/components/ui-base/loading.tsx +43 -73
- package/src/client/components/ui-base/navbar.tsx +74 -39
- package/src/client/components/ui-base/page-nav.tsx +2 -1
- package/src/client/components/ui-base/powered-by.tsx +11 -5
- package/src/client/components/ui-base/search-dialog.tsx +16 -5
- package/src/client/components/ui-base/sidebar.tsx +33 -22
- package/src/client/components/ui-base/tabs.tsx +4 -1
- package/src/client/components/ui-base/theme-toggle.tsx +35 -15
- package/src/client/hooks/use-i18n.ts +38 -7
- package/src/client/hooks/use-localized-to.ts +51 -73
- package/src/client/hooks/use-navbar.ts +10 -3
- package/src/client/hooks/use-page-nav.ts +27 -6
- package/src/client/hooks/use-routes.ts +62 -17
- package/src/client/hooks/use-search.ts +84 -46
- package/src/client/hooks/use-sidebar.ts +6 -2
- package/src/client/hooks/use-version.ts +5 -0
- package/src/client/integrations/index.ts +1 -0
- package/src/client/store/use-boltdocs-store.ts +44 -0
- package/src/client/theme/neutral.css +29 -0
- package/src/client/types.ts +4 -2
- package/src/client/utils/i18n.ts +23 -0
- package/src/node/{cli.ts → cli/build.ts} +17 -23
- package/src/node/cli/dev.ts +22 -0
- package/src/node/cli/doctor.ts +243 -0
- package/src/node/cli/index.ts +9 -0
- package/src/node/cli/ui.ts +54 -0
- package/src/node/cli-entry.ts +16 -16
- package/src/node/config.ts +54 -17
- package/src/node/index.ts +1 -1
- package/src/node/mdx/cache.ts +12 -0
- package/src/node/mdx/highlighter.ts +47 -0
- package/src/node/mdx/index.ts +114 -0
- package/src/node/mdx/rehype-shiki.ts +53 -0
- package/src/node/mdx/remark-shiki.ts +61 -0
- package/src/node/plugin/entry.ts +1 -1
- package/src/node/plugin/html.ts +8 -4
- package/src/node/plugin/index.ts +135 -72
- package/src/node/routes/index.ts +34 -13
- package/src/node/routes/parser.ts +13 -5
- package/src/node/search/index.ts +55 -0
- package/src/node/ssg/index.ts +15 -7
- package/src/node/ssg/robots.ts +7 -4
- package/src/node/utils.ts +32 -2
- package/tsup.config.ts +7 -2
- package/dist/chunk-52MVMZWS.mjs +0 -1
- package/dist/chunk-BVWWKXJH.mjs +0 -1
- package/dist/chunk-DVY3RDXD.mjs +0 -1
- package/dist/chunk-FUVYCYWC.mjs +0 -1
- package/dist/chunk-GBLMDJ2B.mjs +0 -1
- package/dist/chunk-ISPX45DF.mjs +0 -1
- package/dist/chunk-PNXZMUCO.mjs +0 -1
- package/dist/chunk-V2ZHKQSP.mjs +0 -74
- package/dist/client/components/mdx/index.js +0 -1
- package/dist/client/components/mdx/index.mjs +0 -1
- package/dist/client/hooks/index.js +0 -1
- package/dist/client/hooks/index.mjs +0 -1
- package/dist/search-dialog-TWGYKF2D.mjs +0 -1
- 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
|
+
}
|
package/src/node/cli-entry.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import cac from
|
|
3
|
-
import {
|
|
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(
|
|
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.
|
|
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(
|
|
22
|
+
cli.version('2.0.0')
|
|
23
23
|
|
|
24
|
-
cli.parse()
|
|
24
|
+
cli.parse()
|
package/src/node/config.ts
CHANGED
|
@@ -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 (
|
|
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<{
|
|
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<{
|
|
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
|
-
/**
|
|
132
|
-
|
|
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
|
+
}
|
package/src/node/plugin/entry.ts
CHANGED
|
@@ -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';" : ''
|
package/src/node/plugin/html.ts
CHANGED
|
@@ -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 ||
|
|
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
|
|
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
|
|
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
|
|
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)
|