boltdocs 2.0.0 → 2.1.1
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 +12 -0
- package/bin/boltdocs.js +12 -0
- package/dist/cache-Q4T6VAUL.mjs +1 -0
- package/dist/chunk-52MVMZWS.mjs +1 -0
- package/dist/chunk-BVWWKXJH.mjs +1 -0
- package/dist/chunk-DVY3RDXD.mjs +1 -0
- package/dist/chunk-FUVYCYWC.mjs +1 -0
- package/dist/{chunk-BA5NH5HU.mjs → chunk-GBLMDJ2B.mjs} +1 -1
- package/dist/{chunk-H63UMKYF.mjs → chunk-ISPX45DF.mjs} +1 -1
- package/dist/chunk-PNXZMUCO.mjs +1 -0
- package/dist/chunk-V2ZHKQSP.mjs +74 -0
- package/dist/client/components/mdx/index.d.mts +2 -3
- package/dist/client/components/mdx/index.d.ts +2 -3
- package/dist/client/components/mdx/index.js +1 -1
- package/dist/client/components/mdx/index.mjs +1 -1
- package/dist/client/hooks/index.d.mts +2 -3
- package/dist/client/hooks/index.d.ts +2 -3
- package/dist/client/hooks/index.js +1 -1
- package/dist/client/hooks/index.mjs +1 -1
- package/dist/client/index.d.mts +3 -5
- package/dist/client/index.d.ts +3 -5
- package/dist/client/index.js +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/client/ssr.d.mts +53 -6
- package/dist/client/ssr.d.ts +53 -6
- package/dist/client/ssr.mjs +1 -1
- package/dist/node/cli-entry.d.mts +1 -0
- package/dist/node/cli-entry.d.ts +1 -0
- package/dist/node/cli-entry.js +75 -0
- package/dist/node/cli-entry.mjs +2 -0
- package/dist/node/index.d.mts +213 -4
- package/dist/node/index.d.ts +213 -4
- package/dist/node/index.js +40 -23
- package/dist/node/index.mjs +1 -57
- package/dist/search-dialog-TWGYKF2D.mjs +1 -0
- package/dist/types-Cp21DHI6.d.mts +355 -0
- package/dist/types-Cp21DHI6.d.ts +355 -0
- package/dist/{use-routes-BefRXY3v.d.ts → use-routes-8Iei6jTp.d.mts} +1 -2
- package/dist/{use-routes-5bAtAAYX.d.mts → use-routes-xLhumjbV.d.ts} +1 -2
- package/package.json +7 -1
- package/src/client/components/ui-base/breadcrumbs.tsx +2 -1
- package/src/client/components/ui-base/navbar.tsx +3 -3
- package/src/client/components/ui-base/sidebar.tsx +2 -1
- package/src/client/hooks/use-navbar.ts +1 -1
- package/src/node/cli-entry.ts +24 -0
- package/src/node/cli.ts +59 -0
- package/src/node/config.ts +63 -11
- package/src/node/index.ts +39 -3
- package/src/node/plugin/entry.ts +7 -0
- package/src/node/plugin/html.ts +49 -9
- package/src/node/plugin/index.ts +42 -3
- package/src/node/ssg/index.ts +6 -1
- package/src/node/ssg/robots.ts +50 -0
- package/tsup.config.ts +36 -10
- package/dist/cache-7G6D532T.mjs +0 -1
- package/dist/chunk-A4HQPEPU.mjs +0 -1
- package/dist/chunk-BQCD3DWG.mjs +0 -1
- package/dist/chunk-IWHRQHS7.mjs +0 -1
- package/dist/chunk-JZXLCA2E.mjs +0 -1
- package/dist/chunk-MFU7Q6WF.mjs +0 -1
- package/dist/chunk-QYPNX5UN.mjs +0 -1
- package/dist/chunk-XEAPSFMB.mjs +0 -1
- package/dist/config-CX4l-ZNp.d.mts +0 -166
- package/dist/config-CX4l-ZNp.d.ts +0 -166
- package/dist/search-dialog-EB3N4TYM.mjs +0 -1
- package/dist/types-BuZWFT7r.d.ts +0 -159
- package/dist/types-CvT-SGbK.d.mts +0 -159
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "boltdocs",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "A lightweight documentation generator for React projects.",
|
|
5
5
|
"main": "dist/node/index.js",
|
|
6
6
|
"module": "dist/node/index.mjs",
|
|
7
7
|
"types": "dist/node/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"boltdocs": "bin/boltdocs.js"
|
|
10
|
+
},
|
|
8
11
|
"publishConfig": {
|
|
9
12
|
"access": "public"
|
|
10
13
|
},
|
|
@@ -53,6 +56,9 @@
|
|
|
53
56
|
"dependencies": {
|
|
54
57
|
"@mdx-js/react": "^3.1.1",
|
|
55
58
|
"@mdx-js/rollup": "^3.1.1",
|
|
59
|
+
"@tailwindcss/vite": "^4.2.2",
|
|
60
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
61
|
+
"cac": "^7.0.0",
|
|
56
62
|
"class-variance-authority": "^0.7.1",
|
|
57
63
|
"clsx": "^2.1.1",
|
|
58
64
|
"codesandbox": "^2.2.3",
|
|
@@ -12,10 +12,11 @@ import { useConfig } from '@client/app/config-context'
|
|
|
12
12
|
export function Breadcrumbs() {
|
|
13
13
|
const { crumbs, activeRoute } = useBreadcrumbs()
|
|
14
14
|
const config = useConfig()
|
|
15
|
+
const themeConfig = config.theme || config.themeConfig || {}
|
|
15
16
|
|
|
16
17
|
if (crumbs.length === 0) return null
|
|
17
18
|
|
|
18
|
-
if (!
|
|
19
|
+
if (!themeConfig?.breadcrumbs) return null
|
|
19
20
|
|
|
20
21
|
return (
|
|
21
22
|
<BreadcrumbsRoot>
|
|
@@ -24,7 +24,7 @@ export function Navbar() {
|
|
|
24
24
|
const { links, title, logo, logoProps, github, social, config } = useNavbar()
|
|
25
25
|
const { routes, allRoutes, currentVersion, currentLocale } = useRoutes()
|
|
26
26
|
const { pathname } = useLocation()
|
|
27
|
-
const
|
|
27
|
+
const themeConfig = config.theme || config.themeConfig || {}
|
|
28
28
|
|
|
29
29
|
const hasTabs = themeConfig?.tabs && themeConfig.tabs.length > 0
|
|
30
30
|
|
|
@@ -76,10 +76,10 @@ export function Navbar() {
|
|
|
76
76
|
</NavbarPrimitive.NavbarRight>
|
|
77
77
|
</NavbarPrimitive.Content>
|
|
78
78
|
|
|
79
|
-
{pathname !== '/' && hasTabs &&
|
|
79
|
+
{pathname !== '/' && hasTabs && themeConfig?.tabs && (
|
|
80
80
|
<div className="w-full border-b border-border-subtle bg-bg-main">
|
|
81
81
|
<Tabs
|
|
82
|
-
tabs={
|
|
82
|
+
tabs={themeConfig.tabs}
|
|
83
83
|
routes={allRoutes || routes || []}
|
|
84
84
|
/>
|
|
85
85
|
</div>
|
|
@@ -67,6 +67,7 @@ export function Sidebar({
|
|
|
67
67
|
config: BoltdocsConfig
|
|
68
68
|
}) {
|
|
69
69
|
const { groups, ungrouped, activePath } = useSidebar(routes)
|
|
70
|
+
const themeConfig = config.theme || config.themeConfig || {}
|
|
70
71
|
|
|
71
72
|
return (
|
|
72
73
|
<SidebarPrimitive.SidebarRoot>
|
|
@@ -94,7 +95,7 @@ export function Sidebar({
|
|
|
94
95
|
/>
|
|
95
96
|
))}
|
|
96
97
|
|
|
97
|
-
{
|
|
98
|
+
{themeConfig?.poweredBy && (
|
|
98
99
|
<div className="mt-auto pt-8">
|
|
99
100
|
<PoweredBy />
|
|
100
101
|
</div>
|
|
@@ -8,7 +8,7 @@ export function useNavbar() {
|
|
|
8
8
|
const { theme } = useTheme()
|
|
9
9
|
const location = useLocation()
|
|
10
10
|
|
|
11
|
-
const themeConfig = config.themeConfig || {}
|
|
11
|
+
const themeConfig = config.theme || config.themeConfig || {}
|
|
12
12
|
const title = themeConfig.title || 'Boltdocs'
|
|
13
13
|
const rawLinks = themeConfig.navbar || []
|
|
14
14
|
const socialLinks = themeConfig.socialLinks || []
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import cac from "cac";
|
|
3
|
+
import { devAction, buildAction, previewAction } from "./cli";
|
|
4
|
+
|
|
5
|
+
const cli = cac("boltdocs");
|
|
6
|
+
|
|
7
|
+
cli
|
|
8
|
+
.command("[root]", "Start development server")
|
|
9
|
+
.alias("dev")
|
|
10
|
+
.action(devAction);
|
|
11
|
+
|
|
12
|
+
cli
|
|
13
|
+
.command("build [root]", "Build for production")
|
|
14
|
+
.action(buildAction);
|
|
15
|
+
|
|
16
|
+
cli
|
|
17
|
+
.command("preview [root]", "Preview production build")
|
|
18
|
+
.action(previewAction);
|
|
19
|
+
|
|
20
|
+
cli.help();
|
|
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");
|
|
23
|
+
|
|
24
|
+
cli.parse();
|
package/src/node/cli.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createServer, build, preview } from 'vite'
|
|
2
|
+
import { createViteConfig, resolveConfig } from './index'
|
|
3
|
+
import { getHtmlTemplate } from './plugin/html'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import fs from 'fs'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Core logic for the Boltdocs CLI commands.
|
|
9
|
+
* These functions wrap Vite's JS API to provide a seamless experience.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export async function devAction(root: string = process.cwd()) {
|
|
13
|
+
try {
|
|
14
|
+
const viteConfig = await createViteConfig(root, 'development')
|
|
15
|
+
const server = await createServer(viteConfig)
|
|
16
|
+
await server.listen()
|
|
17
|
+
server.printUrls()
|
|
18
|
+
server.bindCLIShortcuts({ print: true })
|
|
19
|
+
} catch (e) {
|
|
20
|
+
console.error('[boltdocs] Failed to start dev server:', e)
|
|
21
|
+
process.exit(1)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function buildAction(root: string = process.cwd()) {
|
|
26
|
+
let createdIndexHtml = false
|
|
27
|
+
const indexPath = path.resolve(root, 'index.html')
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
if (!fs.existsSync(indexPath)) {
|
|
31
|
+
const config = await resolveConfig('docs', root)
|
|
32
|
+
fs.writeFileSync(indexPath, getHtmlTemplate(config), 'utf-8')
|
|
33
|
+
createdIndexHtml = true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const viteConfig = await createViteConfig(root, 'production')
|
|
37
|
+
await build(viteConfig)
|
|
38
|
+
console.log('[boltdocs] Build completed successfully.')
|
|
39
|
+
} catch (e) {
|
|
40
|
+
console.error('[boltdocs] Build failed:', e)
|
|
41
|
+
process.exit(1)
|
|
42
|
+
} finally {
|
|
43
|
+
if (createdIndexHtml && fs.existsSync(indexPath)) {
|
|
44
|
+
fs.unlinkSync(indexPath)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function previewAction(root: string = process.cwd()) {
|
|
50
|
+
try {
|
|
51
|
+
const viteConfig = await createViteConfig(root, 'production')
|
|
52
|
+
const previewServer = await preview(viteConfig)
|
|
53
|
+
previewServer.printUrls()
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.error('[boltdocs] Failed to start preview server:', e)
|
|
56
|
+
process.exit(1)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
package/src/node/config.ts
CHANGED
|
@@ -63,6 +63,15 @@ export interface BoltdocsThemeConfig {
|
|
|
63
63
|
version?: string
|
|
64
64
|
/** The GitHub repository in the format 'owner/repo' to fetch and display star count. */
|
|
65
65
|
githubRepo?: string
|
|
66
|
+
/**
|
|
67
|
+
* URL path to the site favicon.
|
|
68
|
+
* If not specified, the logo will be used if available.
|
|
69
|
+
*/
|
|
70
|
+
favicon?: string
|
|
71
|
+
/**
|
|
72
|
+
* The Open Graph image URL to display when the site is shared on social media.
|
|
73
|
+
*/
|
|
74
|
+
ogImage?: string
|
|
66
75
|
/** Whether to show the 'Powered by LiteDocs' badge in the sidebar (default: true) */
|
|
67
76
|
poweredBy?: boolean
|
|
68
77
|
/**
|
|
@@ -85,6 +94,24 @@ export interface BoltdocsThemeConfig {
|
|
|
85
94
|
copyMarkdown?: boolean | { text?: string; icon?: string }
|
|
86
95
|
}
|
|
87
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Configuration for the robots.txt file.
|
|
99
|
+
*/
|
|
100
|
+
export type BoltdocsRobotsConfig =
|
|
101
|
+
| string
|
|
102
|
+
| {
|
|
103
|
+
/** User-agent rules */
|
|
104
|
+
rules?: Array<{
|
|
105
|
+
userAgent: string
|
|
106
|
+
/** Paths allowed to be crawled */
|
|
107
|
+
allow?: string | string[]
|
|
108
|
+
/** Paths disallowed to be crawled */
|
|
109
|
+
disallow?: string | string[]
|
|
110
|
+
}>
|
|
111
|
+
/** Sitemaps to include in the robots.txt */
|
|
112
|
+
sitemaps?: string[]
|
|
113
|
+
}
|
|
114
|
+
|
|
88
115
|
/**
|
|
89
116
|
* Configuration for internationalization (i18n).
|
|
90
117
|
*/
|
|
@@ -142,10 +169,12 @@ export interface BoltdocsIntegrationsConfig {
|
|
|
142
169
|
export interface BoltdocsConfig {
|
|
143
170
|
/** The base URL of the site, used for generating the sitemap */
|
|
144
171
|
siteUrl?: string
|
|
145
|
-
/** Configuration pertaining to the UI and appearance */
|
|
146
|
-
themeConfig?: BoltdocsThemeConfig
|
|
147
172
|
/** The root directory containing markdown documentation files (default: 'docs') */
|
|
148
173
|
docsDir?: string
|
|
174
|
+
/** Path to a custom HomePage component */
|
|
175
|
+
homePage?: string
|
|
176
|
+
/** Configuration pertaining to the UI and appearance */
|
|
177
|
+
theme?: BoltdocsThemeConfig
|
|
149
178
|
/** Configuration for internationalization */
|
|
150
179
|
i18n?: BoltdocsI18nConfig
|
|
151
180
|
/** Configuration for documentation versioning */
|
|
@@ -156,6 +185,16 @@ export interface BoltdocsConfig {
|
|
|
156
185
|
external?: Record<string, string>
|
|
157
186
|
/** External integrations configuration */
|
|
158
187
|
integrations?: BoltdocsIntegrationsConfig
|
|
188
|
+
/** Configuration for the robots.txt file */
|
|
189
|
+
robots?: BoltdocsRobotsConfig
|
|
190
|
+
/** Low-level Vite configuration overrides */
|
|
191
|
+
vite?: import('vite').InlineConfig
|
|
192
|
+
/** @deprecated Use theme instead */
|
|
193
|
+
themeConfig?: BoltdocsThemeConfig
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function defineConfig(config: BoltdocsConfig): BoltdocsConfig {
|
|
197
|
+
return config
|
|
159
198
|
}
|
|
160
199
|
|
|
161
200
|
export const CONFIG_FILES = [
|
|
@@ -169,7 +208,10 @@ export const CONFIG_FILES = [
|
|
|
169
208
|
*/
|
|
170
209
|
interface RawUserConfig
|
|
171
210
|
extends Partial<BoltdocsConfig>,
|
|
172
|
-
Partial<BoltdocsThemeConfig> {
|
|
211
|
+
Partial<BoltdocsThemeConfig> {
|
|
212
|
+
favicon?: string
|
|
213
|
+
ogImage?: string
|
|
214
|
+
}
|
|
173
215
|
|
|
174
216
|
/**
|
|
175
217
|
* Loads user's configuration file (e.g., `boltdocs.config.js` or `boltdocs.config.ts`) if it exists,
|
|
@@ -187,7 +229,7 @@ export async function resolveConfig(
|
|
|
187
229
|
|
|
188
230
|
const defaults: BoltdocsConfig = {
|
|
189
231
|
docsDir: path.resolve(docsDir),
|
|
190
|
-
|
|
232
|
+
theme: {
|
|
191
233
|
title: 'Boltdocs',
|
|
192
234
|
description: 'A Vite documentation framework',
|
|
193
235
|
navbar: [
|
|
@@ -230,18 +272,28 @@ export async function resolveConfig(
|
|
|
230
272
|
title: userConfig.title,
|
|
231
273
|
description: userConfig.description,
|
|
232
274
|
logo: userConfig.logo,
|
|
275
|
+
favicon: userConfig.favicon,
|
|
276
|
+
ogImage: userConfig.ogImage,
|
|
233
277
|
navbar: userConfig.navbar,
|
|
234
278
|
sidebar: userConfig.sidebar,
|
|
235
279
|
socialLinks: userConfig.socialLinks,
|
|
236
280
|
footer: userConfig.footer,
|
|
237
281
|
githubRepo: userConfig.githubRepo,
|
|
238
282
|
tabs: userConfig.tabs,
|
|
283
|
+
codeTheme: userConfig.codeTheme,
|
|
284
|
+
copyMarkdown: userConfig.copyMarkdown,
|
|
285
|
+
breadcrumbs: userConfig.breadcrumbs,
|
|
286
|
+
poweredBy: userConfig.poweredBy,
|
|
287
|
+
communityHelp: userConfig.communityHelp,
|
|
288
|
+
version: userConfig.version,
|
|
289
|
+
editLink: userConfig.editLink
|
|
239
290
|
}
|
|
240
291
|
|
|
241
|
-
// User can define properties at top level or inside themeConfig
|
|
292
|
+
// User can define properties at top level or inside themeConfig/theme
|
|
242
293
|
const userThemeConfig: BoltdocsThemeConfig = {
|
|
243
294
|
...themeConfigFromTop,
|
|
244
295
|
...(userConfig.themeConfig || {}),
|
|
296
|
+
...(userConfig.theme || {}),
|
|
245
297
|
}
|
|
246
298
|
|
|
247
299
|
// Clean undefined properties
|
|
@@ -263,13 +315,10 @@ export async function resolveConfig(
|
|
|
263
315
|
|
|
264
316
|
return {
|
|
265
317
|
docsDir: path.resolve(docsDir),
|
|
266
|
-
|
|
267
|
-
|
|
318
|
+
homePage: userConfig.homePage,
|
|
319
|
+
theme: {
|
|
320
|
+
...defaults.theme,
|
|
268
321
|
...cleanThemeConfig,
|
|
269
|
-
codeTheme:
|
|
270
|
-
cleanThemeConfig.codeTheme ||
|
|
271
|
-
(userConfig.themeConfig || userConfig).codeTheme ||
|
|
272
|
-
defaults.themeConfig?.codeTheme,
|
|
273
322
|
},
|
|
274
323
|
i18n: userConfig.i18n,
|
|
275
324
|
versions: userConfig.versions,
|
|
@@ -277,5 +326,8 @@ export async function resolveConfig(
|
|
|
277
326
|
plugins: userConfig.plugins || [],
|
|
278
327
|
external: userConfig.external,
|
|
279
328
|
integrations: userConfig.integrations,
|
|
329
|
+
robots: userConfig.robots,
|
|
330
|
+
vite: userConfig.vite,
|
|
280
331
|
}
|
|
281
332
|
}
|
|
333
|
+
|
package/src/node/index.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { Plugin } from 'vite'
|
|
1
|
+
import type { Plugin, InlineConfig } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
2
4
|
import { boltdocsPlugin } from './plugin/index'
|
|
3
5
|
import { boltdocsMdxPlugin } from './mdx'
|
|
4
6
|
import type { BoltdocsPluginOptions } from './plugin/index'
|
|
5
7
|
|
|
6
|
-
import { resolveConfig } from './config'
|
|
8
|
+
import { resolveConfig, type BoltdocsConfig } from './config'
|
|
7
9
|
|
|
8
10
|
export default async function boltdocs(
|
|
9
11
|
options?: BoltdocsPluginOptions,
|
|
@@ -11,7 +13,40 @@ export default async function boltdocs(
|
|
|
11
13
|
const docsDir = options?.docsDir || 'docs'
|
|
12
14
|
const config = await resolveConfig(docsDir)
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
// Merge options with config
|
|
17
|
+
const mergedOptions: BoltdocsPluginOptions = {
|
|
18
|
+
...options,
|
|
19
|
+
homePage: options?.homePage || config.homePage,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return [...boltdocsPlugin(mergedOptions, config), boltdocsMdxPlugin(config)]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generates the complete Vite configuration for a Boltdocs project.
|
|
27
|
+
* This is used by the Boltdocs CLI to run Vite without a user-defined vite.config.ts.
|
|
28
|
+
*/
|
|
29
|
+
export async function createViteConfig(
|
|
30
|
+
root: string,
|
|
31
|
+
mode: 'development' | 'production' = 'development',
|
|
32
|
+
): Promise<InlineConfig> {
|
|
33
|
+
const config = await resolveConfig('docs', root)
|
|
34
|
+
|
|
35
|
+
const viteConfig: InlineConfig = {
|
|
36
|
+
root,
|
|
37
|
+
mode,
|
|
38
|
+
plugins: [
|
|
39
|
+
react(),
|
|
40
|
+
tailwindcss(),
|
|
41
|
+
await boltdocs({
|
|
42
|
+
docsDir: config.docsDir,
|
|
43
|
+
homePage: config.homePage,
|
|
44
|
+
}),
|
|
45
|
+
],
|
|
46
|
+
...config.vite,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return viteConfig
|
|
15
50
|
}
|
|
16
51
|
|
|
17
52
|
export type { BoltdocsPluginOptions }
|
|
@@ -19,3 +54,4 @@ export { generateStaticPages } from './ssg'
|
|
|
19
54
|
export type { SSGOptions } from './ssg'
|
|
20
55
|
export type { RouteMeta } from './routes'
|
|
21
56
|
export type { BoltdocsConfig, BoltdocsThemeConfig } from './config'
|
|
57
|
+
export { resolveConfig, defineConfig } from './config'
|
package/src/node/plugin/entry.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { normalizePath } from '../utils'
|
|
|
2
2
|
import type { BoltdocsConfig } from '../config'
|
|
3
3
|
import type { BoltdocsPluginOptions } from './types'
|
|
4
4
|
import path from 'path'
|
|
5
|
+
import fs from 'fs'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Generates the raw source code for the virtual entry file (`\0virtual:boltdocs-entry`).
|
|
@@ -18,6 +19,11 @@ export function generateEntryCode(
|
|
|
18
19
|
const homeImport = options.homePage
|
|
19
20
|
? `import HomePage from '${normalizePath(options.homePage)}';`
|
|
20
21
|
: ''
|
|
22
|
+
|
|
23
|
+
// Auto-import index.css if it exists
|
|
24
|
+
const cssPath = path.resolve(process.cwd(), 'index.css')
|
|
25
|
+
const cssImport = fs.existsSync(cssPath) ? "import './index.css';" : ''
|
|
26
|
+
|
|
21
27
|
const homeOption = options.homePage ? 'homePage: HomePage,' : ''
|
|
22
28
|
const pluginComponents =
|
|
23
29
|
config?.plugins?.flatMap((p) => Object.entries(p.components || {})) || []
|
|
@@ -54,6 +60,7 @@ import { createBoltdocsApp as _createApp } from 'boltdocs/client';
|
|
|
54
60
|
import _routes from 'virtual:boltdocs-routes';
|
|
55
61
|
import _config from 'virtual:boltdocs-config';
|
|
56
62
|
import _user_mdx_components from 'virtual:boltdocs-mdx-components';
|
|
63
|
+
${cssImport}
|
|
57
64
|
${homeImport}
|
|
58
65
|
${componentImports}
|
|
59
66
|
${externalImports}
|
package/src/node/plugin/html.ts
CHANGED
|
@@ -1,27 +1,61 @@
|
|
|
1
1
|
import type { BoltdocsConfig } from '../config'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Provides a default HTML template if none is found in the project root.
|
|
5
|
+
*/
|
|
6
|
+
export function getHtmlTemplate(config: BoltdocsConfig): string {
|
|
7
|
+
const title = config.theme?.title || config.themeConfig?.title || 'Boltdocs'
|
|
8
|
+
return `<!doctype html>
|
|
9
|
+
<html lang="en">
|
|
10
|
+
<head>
|
|
11
|
+
<meta charset="UTF-8" />
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
13
|
+
<title>${title}</title>
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<div id="root"></div>
|
|
17
|
+
</body>
|
|
18
|
+
</html>`
|
|
19
|
+
}
|
|
20
|
+
|
|
3
21
|
/**
|
|
4
22
|
* Injects OpenGraph, Twitter, and generic SEO meta tags into the final HTML output.
|
|
5
23
|
* Also ensures the virtual entry file is injected if it's missing (e.g., standard Vite index.html).
|
|
6
24
|
*
|
|
7
|
-
* @param html - The original HTML string
|
|
8
|
-
* @param config - The resolved Boltdocs configuration containing site metadata
|
|
9
|
-
* @returns The modified HTML string with injected tags
|
|
25
|
+
* @param html - {string} The original HTML string
|
|
26
|
+
* @param config - {BoltdocsConfig} The resolved Boltdocs configuration containing site metadata
|
|
27
|
+
* @returns {string} The modified HTML string with injected tags
|
|
10
28
|
*/
|
|
11
29
|
export function injectHtmlMeta(html: string, config: BoltdocsConfig): string {
|
|
12
|
-
const
|
|
13
|
-
const
|
|
30
|
+
const theme = config.theme || config.themeConfig
|
|
31
|
+
const title = theme?.title || 'Boltdocs'
|
|
32
|
+
const description = theme?.description || ''
|
|
33
|
+
|
|
34
|
+
// Determine favicon
|
|
35
|
+
let favicon = theme?.favicon
|
|
36
|
+
if (!favicon && theme?.logo) {
|
|
37
|
+
if (typeof theme.logo === 'string') {
|
|
38
|
+
favicon = theme.logo
|
|
39
|
+
} else {
|
|
40
|
+
favicon = theme.logo.light || theme.logo.dark
|
|
41
|
+
}
|
|
42
|
+
}
|
|
14
43
|
|
|
15
44
|
const seoTags = [
|
|
45
|
+
favicon ? `<link rel="icon" href="${favicon}">` : '',
|
|
16
46
|
`<meta name="description" content="${description}">`,
|
|
17
47
|
`<meta property="og:title" content="${title}">`,
|
|
18
48
|
`<meta property="og:description" content="${description}">`,
|
|
49
|
+
theme?.ogImage ? `<meta property="og:image" content="${theme.ogImage}">` : '',
|
|
19
50
|
`<meta property="og:type" content="website">`,
|
|
20
|
-
`<meta name="twitter:card" content="
|
|
51
|
+
`<meta name="twitter:card" content="summary_large_image">`,
|
|
21
52
|
`<meta name="twitter:title" content="${title}">`,
|
|
22
53
|
`<meta name="twitter:description" content="${description}">`,
|
|
54
|
+
theme?.ogImage ? `<meta name="twitter:image" content="${theme.ogImage}">` : '',
|
|
23
55
|
`<meta name="generator" content="Boltdocs">`,
|
|
24
|
-
]
|
|
56
|
+
]
|
|
57
|
+
.filter(Boolean)
|
|
58
|
+
.join('\n ')
|
|
25
59
|
|
|
26
60
|
const themeScript = `
|
|
27
61
|
<script>
|
|
@@ -41,10 +75,16 @@ export function injectHtmlMeta(html: string, config: BoltdocsConfig): string {
|
|
|
41
75
|
</script>
|
|
42
76
|
`
|
|
43
77
|
|
|
44
|
-
|
|
78
|
+
// Use regex to replace title or inject it if missing
|
|
79
|
+
if (html.includes('<title>')) {
|
|
80
|
+
html = html.replace(/<title>.*?<\/title>/, `<title>${title}</title>`)
|
|
81
|
+
} else {
|
|
82
|
+
html = html.replace('</head>', ` <title>${title}</title>\n </head>`)
|
|
83
|
+
}
|
|
84
|
+
|
|
45
85
|
html = html.replace('</head>', ` ${seoTags}\n${themeScript} </head>`)
|
|
46
86
|
|
|
47
|
-
if (!html.includes('src/main')) {
|
|
87
|
+
if (!html.includes('src/main') && !html.includes('virtual:boltdocs-entry')) {
|
|
48
88
|
html = html.replace(
|
|
49
89
|
'</body>',
|
|
50
90
|
' <script type="module">import "virtual:boltdocs-entry";</script>\n </body>',
|
package/src/node/plugin/index.ts
CHANGED
|
@@ -5,12 +5,13 @@ import { resolveConfig, type BoltdocsConfig, CONFIG_FILES } from '../config'
|
|
|
5
5
|
import { generateStaticPages } from '../ssg'
|
|
6
6
|
import { normalizePath, isDocFile } from '../utils'
|
|
7
7
|
import path from 'path'
|
|
8
|
-
|
|
9
8
|
import type { BoltdocsPluginOptions } from './types'
|
|
10
9
|
import { generateEntryCode } from './entry'
|
|
11
|
-
import { injectHtmlMeta } from './html'
|
|
10
|
+
import { injectHtmlMeta, getHtmlTemplate } from './html'
|
|
11
|
+
import { generateRobotsTxt } from '../ssg/robots'
|
|
12
12
|
import fs from 'fs'
|
|
13
13
|
|
|
14
|
+
|
|
14
15
|
export * from './types'
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -63,7 +64,44 @@ export function boltdocsPlugin(
|
|
|
63
64
|
},
|
|
64
65
|
|
|
65
66
|
configureServer(server) {
|
|
66
|
-
//
|
|
67
|
+
// Serve robots.txt from config
|
|
68
|
+
server.middlewares.use((req, res, next) => {
|
|
69
|
+
if (req.url === '/robots.txt') {
|
|
70
|
+
const robots = generateRobotsTxt(config)
|
|
71
|
+
res.statusCode = 200
|
|
72
|
+
res.setHeader('Content-Type', 'text/plain')
|
|
73
|
+
res.end(robots)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
next()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Serve default HTML if index.html is missing
|
|
80
|
+
server.middlewares.use(async (req, res, next) => {
|
|
81
|
+
const url = req.url?.split('?')[0] || '/'
|
|
82
|
+
const accept = req.headers.accept || ''
|
|
83
|
+
|
|
84
|
+
if (
|
|
85
|
+
accept.includes('text/html') &&
|
|
86
|
+
!url.includes('.') // Simple check for assets
|
|
87
|
+
) {
|
|
88
|
+
const indexPath = path.resolve(process.cwd(), 'index.html')
|
|
89
|
+
if (!fs.existsSync(indexPath)) {
|
|
90
|
+
let html = getHtmlTemplate(config)
|
|
91
|
+
html = injectHtmlMeta(html, config)
|
|
92
|
+
html = await server.transformIndexHtml(req.url || '/', html)
|
|
93
|
+
res.statusCode = 200
|
|
94
|
+
res.setHeader('Content-Type', 'text/html')
|
|
95
|
+
res.end(html)
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
next()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
// Explicitly watch config files...
|
|
104
|
+
|
|
67
105
|
const configPaths = CONFIG_FILES.map((c) =>
|
|
68
106
|
path.resolve(process.cwd(), c),
|
|
69
107
|
)
|
|
@@ -172,6 +210,7 @@ export function boltdocsPlugin(
|
|
|
172
210
|
}
|
|
173
211
|
if (id === '\0virtual:boltdocs-config') {
|
|
174
212
|
const clientConfig = {
|
|
213
|
+
theme: config?.theme,
|
|
175
214
|
themeConfig: config?.themeConfig,
|
|
176
215
|
integrations: config?.integrations,
|
|
177
216
|
i18n: config?.i18n,
|
package/src/node/ssg/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { createRequire } from 'module'
|
|
|
8
8
|
import type { SSGOptions } from './options'
|
|
9
9
|
import { replaceMetaTags } from './meta'
|
|
10
10
|
import { generateSitemap } from './sitemap'
|
|
11
|
+
import { generateRobotsTxt } from './robots'
|
|
11
12
|
|
|
12
13
|
// Re-export options for consumers
|
|
13
14
|
export type { SSGOptions }
|
|
@@ -122,8 +123,12 @@ export async function generateStaticPages(options: SSGOptions): Promise<void> {
|
|
|
122
123
|
)
|
|
123
124
|
fs.writeFileSync(path.join(outDir, 'sitemap.xml'), sitemap, 'utf-8')
|
|
124
125
|
|
|
126
|
+
// Generate robots.txt
|
|
127
|
+
const robots = generateRobotsTxt(config!)
|
|
128
|
+
fs.writeFileSync(path.join(outDir, 'robots.txt'), robots, 'utf-8')
|
|
129
|
+
|
|
125
130
|
console.log(
|
|
126
|
-
`[boltdocs] Generated ${routes.length} static pages + sitemap.xml`,
|
|
131
|
+
`[boltdocs] Generated ${routes.length} static pages + sitemap.xml + robots.txt`,
|
|
127
132
|
)
|
|
128
133
|
|
|
129
134
|
// Ensure all cache operations (like index persistence) are finished
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { BoltdocsConfig } from '../config'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generates the content for a robots.txt file based on the Boltdocs configuration.
|
|
5
|
+
*
|
|
6
|
+
* @param config - The resolved Boltdocs configuration
|
|
7
|
+
* @returns The formatted robots.txt string
|
|
8
|
+
*/
|
|
9
|
+
export function generateRobotsTxt(config: BoltdocsConfig): string {
|
|
10
|
+
if (typeof config.robots === 'string') {
|
|
11
|
+
return config.robots
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const siteUrl = config.siteUrl?.replace(/\/$/, '') || ''
|
|
15
|
+
const robots = config.robots || {}
|
|
16
|
+
const rules = (robots as any).rules || [
|
|
17
|
+
{
|
|
18
|
+
userAgent: '*',
|
|
19
|
+
allow: '/',
|
|
20
|
+
},
|
|
21
|
+
]
|
|
22
|
+
const sitemaps = (robots as any).sitemaps || (siteUrl ? [`${siteUrl}/sitemap.xml`] : [])
|
|
23
|
+
|
|
24
|
+
let content = ''
|
|
25
|
+
|
|
26
|
+
for (const rule of rules) {
|
|
27
|
+
content += `User-agent: ${rule.userAgent}\n`
|
|
28
|
+
|
|
29
|
+
if (rule.disallow) {
|
|
30
|
+
const disallows = Array.isArray(rule.disallow) ? rule.disallow : [rule.disallow]
|
|
31
|
+
for (const d of disallows) {
|
|
32
|
+
content += `Disallow: ${d}\n`
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (rule.allow) {
|
|
37
|
+
const allows = Array.isArray(rule.allow) ? rule.allow : [rule.allow]
|
|
38
|
+
for (const a of allows) {
|
|
39
|
+
content += `Allow: ${a}\n`
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
content += '\n'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const sitemap of sitemaps) {
|
|
46
|
+
content += `Sitemap: ${sitemap}\n`
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return content.trim()
|
|
50
|
+
}
|