boltdocs 1.0.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/dist/CodeBlock-37XMKCYY.mjs +7 -0
- package/dist/PackageManagerTabs-4NWXLXQO.mjs +314 -0
- package/dist/Playground-OE2OE6B6.mjs +7 -0
- package/dist/SearchDialog-FTOQZ763.mjs +187 -0
- package/dist/SearchDialog-ZAZXYIFX.css +2147 -0
- package/dist/Video-I6QY4X7J.mjs +7 -0
- package/dist/chunk-2YRDWM6O.mjs +56 -0
- package/dist/chunk-PN4GCTYG.mjs +67 -0
- package/dist/chunk-X2TDGMTR.mjs +64 -0
- package/dist/chunk-X6BYQHVC.mjs +12 -0
- package/dist/chunk-Z7JHYNAS.mjs +57 -0
- package/dist/chunk-ZFCOLEXN.mjs +1644 -0
- package/dist/client/index.css +2147 -0
- package/dist/client/index.d.mts +298 -0
- package/dist/client/index.d.ts +298 -0
- package/dist/client/index.js +2793 -0
- package/dist/client/index.mjs +63 -0
- package/dist/client/ssr.css +2147 -0
- package/dist/client/ssr.d.mts +25 -0
- package/dist/client/ssr.d.ts +25 -0
- package/dist/client/ssr.js +2727 -0
- package/dist/client/ssr.mjs +32 -0
- package/dist/config-D2XmHJYe.d.mts +122 -0
- package/dist/config-D2XmHJYe.d.ts +122 -0
- package/dist/index-CRQKWAeo.d.mts +82 -0
- package/dist/index-CRQKWAeo.d.ts +82 -0
- package/dist/node/cli/index.d.mts +1 -0
- package/dist/node/cli/index.d.ts +1 -0
- package/dist/node/cli/index.js +199 -0
- package/dist/node/cli/index.mjs +154 -0
- package/dist/node/index.d.mts +79 -0
- package/dist/node/index.d.ts +79 -0
- package/dist/node/index.js +797 -0
- package/dist/node/index.mjs +719 -0
- package/package.json +79 -0
- package/src/client/app/index.tsx +422 -0
- package/src/client/app/preload.tsx +56 -0
- package/src/client/index.ts +40 -0
- package/src/client/ssr.tsx +50 -0
- package/src/client/theme/components/CodeBlock/CodeBlock.tsx +76 -0
- package/src/client/theme/components/CodeBlock/index.ts +1 -0
- package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +154 -0
- package/src/client/theme/components/PackageManagerTabs/index.ts +1 -0
- package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +64 -0
- package/src/client/theme/components/Playground/Playground.tsx +86 -0
- package/src/client/theme/components/Playground/index.ts +1 -0
- package/src/client/theme/components/Playground/playground.css +168 -0
- package/src/client/theme/components/Video/Video.tsx +84 -0
- package/src/client/theme/components/Video/index.ts +1 -0
- package/src/client/theme/components/Video/video.css +41 -0
- package/src/client/theme/components/mdx/Admonition.tsx +80 -0
- package/src/client/theme/components/mdx/Badge.tsx +31 -0
- package/src/client/theme/components/mdx/Button.tsx +50 -0
- package/src/client/theme/components/mdx/Card.tsx +80 -0
- package/src/client/theme/components/mdx/List.tsx +57 -0
- package/src/client/theme/components/mdx/Tabs.tsx +94 -0
- package/src/client/theme/components/mdx/index.ts +18 -0
- package/src/client/theme/components/mdx/mdx-components.css +405 -0
- package/src/client/theme/icons/bun.tsx +62 -0
- package/src/client/theme/icons/deno.tsx +20 -0
- package/src/client/theme/icons/discord.tsx +12 -0
- package/src/client/theme/icons/github.tsx +15 -0
- package/src/client/theme/icons/npm.tsx +13 -0
- package/src/client/theme/icons/pnpm.tsx +72 -0
- package/src/client/theme/icons/twitter.tsx +12 -0
- package/src/client/theme/styles/home.css +60 -0
- package/src/client/theme/styles/markdown.css +343 -0
- package/src/client/theme/styles/variables.css +162 -0
- package/src/client/theme/styles.css +38 -0
- package/src/client/theme/ui/BackgroundGradient/BackgroundGradient.tsx +10 -0
- package/src/client/theme/ui/BackgroundGradient/index.ts +1 -0
- package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +68 -0
- package/src/client/theme/ui/Breadcrumbs/index.ts +1 -0
- package/src/client/theme/ui/Footer/footer.css +32 -0
- package/src/client/theme/ui/Head/Head.tsx +69 -0
- package/src/client/theme/ui/Head/index.ts +1 -0
- package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +125 -0
- package/src/client/theme/ui/LanguageSwitcher/index.ts +1 -0
- package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +98 -0
- package/src/client/theme/ui/Layout/Layout.tsx +213 -0
- package/src/client/theme/ui/Layout/base.css +76 -0
- package/src/client/theme/ui/Layout/index.ts +2 -0
- package/src/client/theme/ui/Layout/pagination.css +72 -0
- package/src/client/theme/ui/Layout/responsive.css +40 -0
- package/src/client/theme/ui/Link/Link.tsx +202 -0
- package/src/client/theme/ui/Link/index.ts +2 -0
- package/src/client/theme/ui/Loading/Loading.tsx +10 -0
- package/src/client/theme/ui/Loading/index.ts +1 -0
- package/src/client/theme/ui/Loading/loading.css +30 -0
- package/src/client/theme/ui/Navbar/GithubStars.tsx +27 -0
- package/src/client/theme/ui/Navbar/Navbar.tsx +145 -0
- package/src/client/theme/ui/Navbar/index.ts +2 -0
- package/src/client/theme/ui/Navbar/navbar.css +233 -0
- package/src/client/theme/ui/NotFound/NotFound.tsx +20 -0
- package/src/client/theme/ui/NotFound/index.ts +1 -0
- package/src/client/theme/ui/NotFound/not-found.css +64 -0
- package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +192 -0
- package/src/client/theme/ui/OnThisPage/index.ts +1 -0
- package/src/client/theme/ui/OnThisPage/toc.css +132 -0
- package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +18 -0
- package/src/client/theme/ui/PoweredBy/index.ts +1 -0
- package/src/client/theme/ui/PoweredBy/powered-by.css +76 -0
- package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +199 -0
- package/src/client/theme/ui/SearchDialog/index.ts +1 -0
- package/src/client/theme/ui/SearchDialog/search.css +152 -0
- package/src/client/theme/ui/Sidebar/Sidebar.tsx +200 -0
- package/src/client/theme/ui/Sidebar/index.ts +1 -0
- package/src/client/theme/ui/Sidebar/sidebar.css +269 -0
- package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +69 -0
- package/src/client/theme/ui/ThemeToggle/index.ts +1 -0
- package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +136 -0
- package/src/client/theme/ui/VersionSwitcher/index.ts +1 -0
- package/src/client/utils.ts +26 -0
- package/src/node/cache.ts +94 -0
- package/src/node/cli/commands/config.ts +15 -0
- package/src/node/cli/commands/generate-css.ts +24 -0
- package/src/node/cli/constants.ts +70 -0
- package/src/node/cli/index.ts +22 -0
- package/src/node/config.ts +185 -0
- package/src/node/index.ts +21 -0
- package/src/node/mdx.ts +41 -0
- package/src/node/plugin/entry.ts +58 -0
- package/src/node/plugin/html.ts +55 -0
- package/src/node/plugin/index.ts +190 -0
- package/src/node/plugin/types.ts +11 -0
- package/src/node/routes/cache.ts +24 -0
- package/src/node/routes/index.ts +152 -0
- package/src/node/routes/parser.ts +127 -0
- package/src/node/routes/sorter.ts +42 -0
- package/src/node/routes/types.ts +49 -0
- package/src/node/ssg/index.ts +110 -0
- package/src/node/ssg/meta.ts +34 -0
- package/src/node/ssg/options.ts +13 -0
- package/src/node/ssg/sitemap.ts +54 -0
- package/src/node/utils.ts +134 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +22 -0
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "boltdocs",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight documentation generator for React projects.",
|
|
5
|
+
"main": "dist/node/index.js",
|
|
6
|
+
"module": "dist/node/index.mjs",
|
|
7
|
+
"types": "dist/node/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"boltdocs": "./dist/node/cli/index.js"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/node/index.d.ts",
|
|
17
|
+
"import": "./dist/node/index.mjs",
|
|
18
|
+
"require": "./dist/node/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./client": {
|
|
21
|
+
"types": "./dist/client/index.d.ts",
|
|
22
|
+
"import": "./dist/client/index.mjs",
|
|
23
|
+
"require": "./dist/client/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./style.css": "./dist/client/index.css"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup",
|
|
29
|
+
"dev": "tsup --watch"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"docs",
|
|
33
|
+
"documentation",
|
|
34
|
+
"static site generator",
|
|
35
|
+
"ssg",
|
|
36
|
+
"markdown",
|
|
37
|
+
"mdx",
|
|
38
|
+
"react",
|
|
39
|
+
"vite",
|
|
40
|
+
"typescript"
|
|
41
|
+
],
|
|
42
|
+
"author": "Jesus Alcala",
|
|
43
|
+
"license": "ISC",
|
|
44
|
+
"type": "commonjs",
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@mdx-js/react": "^3.1.1",
|
|
47
|
+
"@mdx-js/rollup": "^3.1.1",
|
|
48
|
+
"cac": "^7.0.0",
|
|
49
|
+
"fast-glob": "^3.3.3",
|
|
50
|
+
"github-slugger": "^2.0.0",
|
|
51
|
+
"gray-matter": "^4.0.3",
|
|
52
|
+
"lucide-react": "^0.575.0",
|
|
53
|
+
"react-live": "^4.1.8",
|
|
54
|
+
"react-router-dom": "^6.30.3",
|
|
55
|
+
"rehype-pretty-code": "^0.14.1",
|
|
56
|
+
"rehype-slug": "^6.0.0",
|
|
57
|
+
"remark-frontmatter": "^5.0.0",
|
|
58
|
+
"remark-gfm": "^4.0.1",
|
|
59
|
+
"sharp": "^0.34.5",
|
|
60
|
+
"shiki": "^3.23.0",
|
|
61
|
+
"svgo": "^4.0.0",
|
|
62
|
+
"unist-util-visit": "^5.1.0",
|
|
63
|
+
"vite": "^7.3.1",
|
|
64
|
+
"vite-plugin-image-optimizer": "^2.0.3"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"react": "^18.0.0",
|
|
68
|
+
"react-dom": "^18.0.0"
|
|
69
|
+
},
|
|
70
|
+
"packageManager": "pnpm@10.30.2",
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@types/node": "^25.3.2",
|
|
73
|
+
"@types/react": "^19.2.14",
|
|
74
|
+
"@types/react-dom": "^18.3.7",
|
|
75
|
+
"@types/react-router-dom": "^5.3.3",
|
|
76
|
+
"tsup": "^8.5.1",
|
|
77
|
+
"typescript": "^5.9.3"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import ReactDOM from "react-dom/client";
|
|
3
|
+
import {
|
|
4
|
+
BrowserRouter,
|
|
5
|
+
Routes,
|
|
6
|
+
Route,
|
|
7
|
+
Outlet,
|
|
8
|
+
useLocation,
|
|
9
|
+
} from "react-router-dom";
|
|
10
|
+
import { ThemeLayout } from "../theme/ui/Layout";
|
|
11
|
+
import { NotFound } from "../theme/ui/NotFound";
|
|
12
|
+
import { Loading } from "../theme/ui/Loading";
|
|
13
|
+
import { MDXProvider } from "@mdx-js/react";
|
|
14
|
+
import {
|
|
15
|
+
createContext,
|
|
16
|
+
useContext,
|
|
17
|
+
Suspense,
|
|
18
|
+
lazy,
|
|
19
|
+
useLayoutEffect,
|
|
20
|
+
} from "react";
|
|
21
|
+
import { Link as LucideLink } from "lucide-react";
|
|
22
|
+
|
|
23
|
+
export const ConfigContext = createContext<any>(null);
|
|
24
|
+
|
|
25
|
+
export function useConfig() {
|
|
26
|
+
return useContext(ConfigContext);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const CodeBlock = lazy(() =>
|
|
30
|
+
import("../theme/components/CodeBlock").then((m) => ({
|
|
31
|
+
default: m.CodeBlock,
|
|
32
|
+
})),
|
|
33
|
+
);
|
|
34
|
+
const Video = lazy(() =>
|
|
35
|
+
import("../theme/components/Video").then((m) => ({ default: m.Video })),
|
|
36
|
+
);
|
|
37
|
+
const PackageManagerTabs = lazy(() =>
|
|
38
|
+
import("../theme/components/PackageManagerTabs").then((m) => ({
|
|
39
|
+
default: m.PackageManagerTabs,
|
|
40
|
+
})),
|
|
41
|
+
);
|
|
42
|
+
const Playground = lazy(() =>
|
|
43
|
+
import("../theme/components/Playground").then((m) => ({
|
|
44
|
+
default: m.Playground,
|
|
45
|
+
})),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
import {
|
|
49
|
+
Button,
|
|
50
|
+
Badge,
|
|
51
|
+
Card,
|
|
52
|
+
Cards,
|
|
53
|
+
Tabs,
|
|
54
|
+
Tab,
|
|
55
|
+
Admonition,
|
|
56
|
+
Note,
|
|
57
|
+
Tip,
|
|
58
|
+
Warning,
|
|
59
|
+
Danger,
|
|
60
|
+
InfoBox,
|
|
61
|
+
List,
|
|
62
|
+
} from "../theme/components/mdx";
|
|
63
|
+
declare global {
|
|
64
|
+
interface ImportMeta {
|
|
65
|
+
env: Record<string, any>;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
import { PreloadProvider } from "./preload";
|
|
70
|
+
|
|
71
|
+
const Heading = ({
|
|
72
|
+
level,
|
|
73
|
+
id,
|
|
74
|
+
children,
|
|
75
|
+
}: {
|
|
76
|
+
level: number;
|
|
77
|
+
id?: string;
|
|
78
|
+
children: React.ReactNode;
|
|
79
|
+
}) => {
|
|
80
|
+
const Tag = `h${level}` as keyof JSX.IntrinsicElements;
|
|
81
|
+
return (
|
|
82
|
+
<Tag id={id} className="boltdocs-heading">
|
|
83
|
+
{children}
|
|
84
|
+
{id && (
|
|
85
|
+
<a href={`#${id}`} className="header-anchor" aria-label="Anchor">
|
|
86
|
+
<LucideLink size={16} />
|
|
87
|
+
</a>
|
|
88
|
+
)}
|
|
89
|
+
</Tag>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const mdxComponents = {
|
|
94
|
+
h1: (props: any) => <Heading level={1} {...props} />,
|
|
95
|
+
h2: (props: any) => <Heading level={2} {...props} />,
|
|
96
|
+
h3: (props: any) => <Heading level={3} {...props} />,
|
|
97
|
+
h4: (props: any) => <Heading level={4} {...props} />,
|
|
98
|
+
h5: (props: any) => <Heading level={5} {...props} />,
|
|
99
|
+
h6: (props: any) => <Heading level={6} {...props} />,
|
|
100
|
+
pre: (props: any) => {
|
|
101
|
+
return (
|
|
102
|
+
<Suspense fallback={<div className="code-block-skeleton" />}>
|
|
103
|
+
<CodeBlock {...props}>{props.children}</CodeBlock>
|
|
104
|
+
</Suspense>
|
|
105
|
+
);
|
|
106
|
+
},
|
|
107
|
+
video: (props: any) => (
|
|
108
|
+
<Suspense fallback={<div className="video-skeleton" />}>
|
|
109
|
+
<Video {...props} />
|
|
110
|
+
</Suspense>
|
|
111
|
+
),
|
|
112
|
+
PackageManagerTabs: (props: any) => (
|
|
113
|
+
<Suspense fallback={<div className="pkg-tabs-skeleton" />}>
|
|
114
|
+
<PackageManagerTabs {...props} />
|
|
115
|
+
</Suspense>
|
|
116
|
+
),
|
|
117
|
+
Playground: (props: any) => (
|
|
118
|
+
<Suspense fallback={<div className="playground-skeleton" />}>
|
|
119
|
+
<Playground {...props} />
|
|
120
|
+
</Suspense>
|
|
121
|
+
),
|
|
122
|
+
Button,
|
|
123
|
+
Badge,
|
|
124
|
+
Card,
|
|
125
|
+
Cards,
|
|
126
|
+
Tabs,
|
|
127
|
+
Tab,
|
|
128
|
+
Admonition,
|
|
129
|
+
Note,
|
|
130
|
+
Tip,
|
|
131
|
+
Warning,
|
|
132
|
+
Danger,
|
|
133
|
+
InfoBox,
|
|
134
|
+
List,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Metadata provided by the server for a specific route.
|
|
139
|
+
* Maps closely to the `RouteMeta` type in the Node environment.
|
|
140
|
+
*/
|
|
141
|
+
export interface ComponentRoute {
|
|
142
|
+
/** The final URL path */
|
|
143
|
+
path: string;
|
|
144
|
+
/** The absolute filesystem path of the source file */
|
|
145
|
+
componentPath: string;
|
|
146
|
+
/** The page title */
|
|
147
|
+
title: string;
|
|
148
|
+
/** Explicit order in the sidebar */
|
|
149
|
+
sidebarPosition?: number;
|
|
150
|
+
/** The relative path from the docs directory */
|
|
151
|
+
filePath: string;
|
|
152
|
+
/** The group directory name */
|
|
153
|
+
group?: string;
|
|
154
|
+
/** The display title of the group */
|
|
155
|
+
groupTitle?: string;
|
|
156
|
+
/** Explicit order of the group in the sidebar */
|
|
157
|
+
groupPosition?: number;
|
|
158
|
+
/** Extracted markdown headings for search indexing */
|
|
159
|
+
headings?: { level: number; text: string; id: string }[];
|
|
160
|
+
/** The locale this route belongs to, if i18n is configured */
|
|
161
|
+
locale?: string;
|
|
162
|
+
/** The version this route belongs to, if versioning is configured */
|
|
163
|
+
version?: string;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Configuration options for initializing the Boltdocs client app.
|
|
168
|
+
*/
|
|
169
|
+
export interface CreateBoltdocsAppOptions {
|
|
170
|
+
/** CSS selector for the DOM element where the app should mount (e.g. '#root') */
|
|
171
|
+
target: string;
|
|
172
|
+
/** Initial routes generated by the Vite plugin (`virtual:boltdocs-routes`) */
|
|
173
|
+
routes: ComponentRoute[];
|
|
174
|
+
/** Site configuration (`virtual:boltdocs-config`) */
|
|
175
|
+
config: any;
|
|
176
|
+
/** Dynamic import mapping from `import.meta.glob` for the documentation pages */
|
|
177
|
+
modules: Record<string, () => Promise<any>>;
|
|
178
|
+
/** The `import.meta.hot` instance necessary for fast refresh/HMR updates */
|
|
179
|
+
hot?: any;
|
|
180
|
+
/** Optional custom React component to render when visiting the root path ('/') */
|
|
181
|
+
homePage?: React.ComponentType;
|
|
182
|
+
/** Optional custom MDX components provided by plugins */
|
|
183
|
+
components?: Record<string, React.ComponentType<any>>;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function AppShell({
|
|
187
|
+
initialRoutes,
|
|
188
|
+
initialConfig,
|
|
189
|
+
modules,
|
|
190
|
+
hot,
|
|
191
|
+
homePage: HomePage,
|
|
192
|
+
components: customComponents = {},
|
|
193
|
+
}: {
|
|
194
|
+
initialRoutes: ComponentRoute[];
|
|
195
|
+
initialConfig: any;
|
|
196
|
+
modules: Record<string, () => Promise<any>>;
|
|
197
|
+
hot?: any;
|
|
198
|
+
homePage?: React.ComponentType;
|
|
199
|
+
components?: Record<string, React.ComponentType<any>>;
|
|
200
|
+
}) {
|
|
201
|
+
const [routesInfo, setRoutesInfo] = useState<ComponentRoute[]>(initialRoutes);
|
|
202
|
+
const [config] = useState(initialConfig);
|
|
203
|
+
const [resolvedRoutes, setResolvedRoutes] = useState<any[]>([]);
|
|
204
|
+
|
|
205
|
+
// Subscribe to HMR events
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
if (hot) {
|
|
208
|
+
hot.on("boltdocs:routes-update", (newRoutes: ComponentRoute[]) => {
|
|
209
|
+
setRoutesInfo(newRoutes);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}, [hot]);
|
|
213
|
+
|
|
214
|
+
// Resolve MDX components
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
const mapped = routesInfo
|
|
217
|
+
.filter(
|
|
218
|
+
(route) => !(HomePage && (route.path === "/" || route.path === "")),
|
|
219
|
+
)
|
|
220
|
+
.map((route) => {
|
|
221
|
+
const loaderKey = Object.keys(modules).find((k) =>
|
|
222
|
+
k.endsWith("/" + route.filePath),
|
|
223
|
+
);
|
|
224
|
+
const loader = loaderKey ? modules[loaderKey] : null;
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
...route,
|
|
228
|
+
Component: React.lazy(() => {
|
|
229
|
+
if (!loader)
|
|
230
|
+
return Promise.resolve({ default: () => <NotFound /> });
|
|
231
|
+
return loader() as any;
|
|
232
|
+
}),
|
|
233
|
+
};
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
setResolvedRoutes(mapped);
|
|
237
|
+
}, [routesInfo, modules]);
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<ConfigContext.Provider value={config}>
|
|
241
|
+
<PreloadProvider routes={routesInfo} modules={modules}>
|
|
242
|
+
<ScrollHandler />
|
|
243
|
+
<Routes>
|
|
244
|
+
{/* Custom home page WITHOUT docs layout */}
|
|
245
|
+
{HomePage && (
|
|
246
|
+
<Route
|
|
247
|
+
path="/"
|
|
248
|
+
element={
|
|
249
|
+
<ThemeLayout
|
|
250
|
+
config={config}
|
|
251
|
+
routes={routesInfo}
|
|
252
|
+
sidebar={null}
|
|
253
|
+
toc={null}
|
|
254
|
+
breadcrumbs={null}
|
|
255
|
+
{...config.themeConfig?.layoutProps}
|
|
256
|
+
>
|
|
257
|
+
<HomePage />
|
|
258
|
+
</ThemeLayout>
|
|
259
|
+
}
|
|
260
|
+
/>
|
|
261
|
+
)}
|
|
262
|
+
|
|
263
|
+
{/* Documentation pages WITH sidebar + TOC layout */}
|
|
264
|
+
<Route element={<DocsLayout config={config} routes={routesInfo} />}>
|
|
265
|
+
{resolvedRoutes.map((route: any) => (
|
|
266
|
+
<Route
|
|
267
|
+
key={route.path}
|
|
268
|
+
path={route.path === "" ? "/" : route.path}
|
|
269
|
+
element={
|
|
270
|
+
<React.Suspense fallback={<Loading />}>
|
|
271
|
+
<MdxPage
|
|
272
|
+
Component={route.Component}
|
|
273
|
+
customComponents={customComponents}
|
|
274
|
+
/>
|
|
275
|
+
</React.Suspense>
|
|
276
|
+
}
|
|
277
|
+
/>
|
|
278
|
+
))}
|
|
279
|
+
</Route>
|
|
280
|
+
|
|
281
|
+
<Route
|
|
282
|
+
path="*"
|
|
283
|
+
element={
|
|
284
|
+
<ThemeLayout
|
|
285
|
+
config={config}
|
|
286
|
+
routes={routesInfo}
|
|
287
|
+
{...config.themeConfig?.layoutProps}
|
|
288
|
+
>
|
|
289
|
+
<NotFound />
|
|
290
|
+
</ThemeLayout>
|
|
291
|
+
}
|
|
292
|
+
/>
|
|
293
|
+
</Routes>
|
|
294
|
+
</PreloadProvider>
|
|
295
|
+
</ConfigContext.Provider>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Handles scroll restoration and hash scrolling on navigation.
|
|
301
|
+
*/
|
|
302
|
+
function ScrollHandler() {
|
|
303
|
+
const { pathname, hash } = useLocation();
|
|
304
|
+
|
|
305
|
+
useLayoutEffect(() => {
|
|
306
|
+
if (hash) {
|
|
307
|
+
const id = hash.replace("#", "");
|
|
308
|
+
const element = document.getElementById(id);
|
|
309
|
+
if (element) {
|
|
310
|
+
const offset = 80;
|
|
311
|
+
const bodyRect = document.body.getBoundingClientRect().top;
|
|
312
|
+
const elementRect = element.getBoundingClientRect().top;
|
|
313
|
+
const elementPosition = elementRect - bodyRect;
|
|
314
|
+
const offsetPosition = elementPosition - offset;
|
|
315
|
+
|
|
316
|
+
window.scrollTo({
|
|
317
|
+
top: offsetPosition,
|
|
318
|
+
behavior: "smooth",
|
|
319
|
+
});
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
window.scrollTo(0, 0);
|
|
324
|
+
}, [pathname, hash]);
|
|
325
|
+
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Wrapper layout for doc pages (sidebar + content + TOC) */
|
|
330
|
+
function DocsLayout({
|
|
331
|
+
config,
|
|
332
|
+
routes,
|
|
333
|
+
}: {
|
|
334
|
+
config: any;
|
|
335
|
+
routes: ComponentRoute[];
|
|
336
|
+
}) {
|
|
337
|
+
return (
|
|
338
|
+
<ThemeLayout
|
|
339
|
+
config={config}
|
|
340
|
+
routes={routes}
|
|
341
|
+
{...config.themeConfig?.layoutProps}
|
|
342
|
+
>
|
|
343
|
+
<Outlet />
|
|
344
|
+
</ThemeLayout>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Renders an MDX page securely, injecting required custom components.
|
|
350
|
+
* For example, this overrides the default `<pre>` HTML tags emitted by MDX
|
|
351
|
+
* with the Boltdocs `CodeBlock` component for syntax highlighting.
|
|
352
|
+
*
|
|
353
|
+
* @param props - Contains the dynamically loaded React component representing the MDX page
|
|
354
|
+
*/
|
|
355
|
+
function MdxPage({
|
|
356
|
+
Component,
|
|
357
|
+
customComponents = {},
|
|
358
|
+
}: {
|
|
359
|
+
Component: React.LazyExoticComponent<any>;
|
|
360
|
+
customComponents?: Record<string, React.ComponentType<any>>;
|
|
361
|
+
}) {
|
|
362
|
+
const allComponents = { ...mdxComponents, ...customComponents };
|
|
363
|
+
return (
|
|
364
|
+
<MDXProvider components={allComponents}>
|
|
365
|
+
<Component />
|
|
366
|
+
</MDXProvider>
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Creates and mounts the Boltdocs documentation app.
|
|
372
|
+
*
|
|
373
|
+
* Usage:
|
|
374
|
+
* ```tsx
|
|
375
|
+
* import { createBoltdocsApp } from 'boltdocs/client'
|
|
376
|
+
* import routes from 'virtual:boltdocs-routes'
|
|
377
|
+
* import config from 'virtual:boltdocs-config'
|
|
378
|
+
* import 'boltdocs/style.css'
|
|
379
|
+
* import HomePage from './HomePage'
|
|
380
|
+
*
|
|
381
|
+
* createBoltdocsApp({
|
|
382
|
+
* target: '#root',
|
|
383
|
+
* routes,
|
|
384
|
+
* config,
|
|
385
|
+
* modules: import.meta.glob('/docs/**\/*.{md,mdx}'),
|
|
386
|
+
* hot: import.meta.hot,
|
|
387
|
+
* homePage: HomePage,
|
|
388
|
+
* })
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
export function createBoltdocsApp(options: CreateBoltdocsAppOptions) {
|
|
392
|
+
const { target, routes, config, modules, hot, homePage } = options;
|
|
393
|
+
const container = document.querySelector(target);
|
|
394
|
+
if (!container) {
|
|
395
|
+
throw new Error(
|
|
396
|
+
`[boltdocs] Mount target "${target}" not found in document.`,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const app = (
|
|
401
|
+
<React.StrictMode>
|
|
402
|
+
<BrowserRouter>
|
|
403
|
+
<AppShell
|
|
404
|
+
initialRoutes={routes}
|
|
405
|
+
initialConfig={config}
|
|
406
|
+
modules={modules}
|
|
407
|
+
hot={hot}
|
|
408
|
+
homePage={homePage}
|
|
409
|
+
components={options.components}
|
|
410
|
+
/>
|
|
411
|
+
</BrowserRouter>
|
|
412
|
+
</React.StrictMode>
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
// In production (built app), the HTML is pre-rendered by SSG, so we hydrate.
|
|
416
|
+
// In development, the root is empty, so we createRoot.
|
|
417
|
+
if (import.meta.env.PROD && container.innerHTML.trim() !== "") {
|
|
418
|
+
ReactDOM.hydrateRoot(container as HTMLElement, app);
|
|
419
|
+
} else {
|
|
420
|
+
ReactDOM.createRoot(container as HTMLElement).render(app);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { createContext, useContext, useCallback } from "react";
|
|
2
|
+
import { ComponentRoute } from "./index";
|
|
3
|
+
|
|
4
|
+
interface PreloadContextType {
|
|
5
|
+
preload: (path: string) => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const PreloadContext = createContext<PreloadContextType>({
|
|
9
|
+
preload: () => {},
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export function usePreload() {
|
|
13
|
+
return useContext(PreloadContext);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function PreloadProvider({
|
|
17
|
+
routes,
|
|
18
|
+
modules,
|
|
19
|
+
children,
|
|
20
|
+
}: {
|
|
21
|
+
routes: ComponentRoute[];
|
|
22
|
+
modules: Record<string, () => Promise<any>>;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}) {
|
|
25
|
+
const preload = useCallback(
|
|
26
|
+
(path: string) => {
|
|
27
|
+
// Normalize path (remove hash and search)
|
|
28
|
+
const cleanPath = path.split("#")[0].split("?")[0];
|
|
29
|
+
|
|
30
|
+
// Support index routes matching "/"
|
|
31
|
+
const route = routes.find(
|
|
32
|
+
(r) => r.path === cleanPath || (cleanPath === "/" && r.path === ""),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (route && route.filePath) {
|
|
36
|
+
const loaderKey = Object.keys(modules).find((k) =>
|
|
37
|
+
k.endsWith("/" + route.filePath),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (loaderKey) {
|
|
41
|
+
// Trigger the dynamic import
|
|
42
|
+
modules[loaderKey]().catch((err: any) => {
|
|
43
|
+
console.error(`[boltdocs] Failed to preload route ${path}:`, err);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
[routes, modules],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<PreloadContext.Provider value={{ preload }}>
|
|
53
|
+
{children}
|
|
54
|
+
</PreloadContext.Provider>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type { BoltdocsConfig, BoltdocsThemeConfig } from "../node/config";
|
|
2
|
+
export type { ComponentRoute, CreateBoltdocsAppOptions } from "./app";
|
|
3
|
+
export { createBoltdocsApp } from "./app";
|
|
4
|
+
export { ThemeLayout } from "./theme/ui/Layout";
|
|
5
|
+
export { Navbar } from "./theme/ui/Navbar";
|
|
6
|
+
export { Sidebar } from "./theme/ui/Sidebar";
|
|
7
|
+
export { OnThisPage } from "./theme/ui/OnThisPage";
|
|
8
|
+
export { Head } from "./theme/ui/Head";
|
|
9
|
+
export { Breadcrumbs } from "./theme/ui/Breadcrumbs";
|
|
10
|
+
export { BackgroundGradient } from "./theme/ui/BackgroundGradient";
|
|
11
|
+
export { Playground } from "./theme/components/Playground";
|
|
12
|
+
export { NotFound } from "./theme/ui/NotFound";
|
|
13
|
+
export { Loading } from "./theme/ui/Loading";
|
|
14
|
+
export { CodeBlock } from "./theme/components/CodeBlock";
|
|
15
|
+
export { Video } from "./theme/components/Video";
|
|
16
|
+
export {
|
|
17
|
+
Button,
|
|
18
|
+
Badge,
|
|
19
|
+
Card,
|
|
20
|
+
Cards,
|
|
21
|
+
Tabs,
|
|
22
|
+
Tab,
|
|
23
|
+
Admonition,
|
|
24
|
+
Note,
|
|
25
|
+
Tip,
|
|
26
|
+
Warning,
|
|
27
|
+
Danger,
|
|
28
|
+
InfoBox,
|
|
29
|
+
List,
|
|
30
|
+
} from "./theme/components/mdx";
|
|
31
|
+
export type {
|
|
32
|
+
ButtonProps,
|
|
33
|
+
BadgeProps,
|
|
34
|
+
CardProps,
|
|
35
|
+
CardsProps,
|
|
36
|
+
TabsProps,
|
|
37
|
+
TabProps,
|
|
38
|
+
AdmonitionProps,
|
|
39
|
+
ListProps,
|
|
40
|
+
} from "./theme/components/mdx";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import ReactDOMServer from "react-dom/server";
|
|
3
|
+
import { StaticRouter } from "react-router-dom/server";
|
|
4
|
+
import { AppShell, ComponentRoute } from "./app";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for rendering the Boltdocs application on the server (SSG).
|
|
8
|
+
*/
|
|
9
|
+
export interface RenderOptions {
|
|
10
|
+
/** The URL path currently being rendered */
|
|
11
|
+
path: string;
|
|
12
|
+
/** Initial routes generated by the Vite plugin (`virtual:boltdocs-routes`) */
|
|
13
|
+
routes: ComponentRoute[];
|
|
14
|
+
/** Site configuration (`virtual:boltdocs-config`) */
|
|
15
|
+
config: any;
|
|
16
|
+
/** Optional custom React component to render when visiting the root path ('/') */
|
|
17
|
+
homePage?: React.ComponentType;
|
|
18
|
+
/** Preloaded modules (since SSR cannot use dynamic imports easily) */
|
|
19
|
+
modules: Record<string, any>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Renders the full React application into an HTML string for a specific route.
|
|
24
|
+
* This is called by the Node SSG script during the Vite build process.
|
|
25
|
+
*/
|
|
26
|
+
export async function render(options: RenderOptions): Promise<string> {
|
|
27
|
+
const { path, routes, config, modules, homePage } = options;
|
|
28
|
+
|
|
29
|
+
// For SSR, we must resolve modules synchronously. We create a mock 'loader'
|
|
30
|
+
// that instantly returns the module since the SSG script already loaded it.
|
|
31
|
+
const resolvedModules: Record<string, () => Promise<any>> = {};
|
|
32
|
+
for (const [key, mod] of Object.entries(modules)) {
|
|
33
|
+
resolvedModules[key] = () => Promise.resolve(mod);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const html = ReactDOMServer.renderToString(
|
|
37
|
+
<React.StrictMode>
|
|
38
|
+
<StaticRouter location={path}>
|
|
39
|
+
<AppShell
|
|
40
|
+
initialRoutes={routes}
|
|
41
|
+
initialConfig={config}
|
|
42
|
+
modules={resolvedModules}
|
|
43
|
+
homePage={homePage}
|
|
44
|
+
/>
|
|
45
|
+
</StaticRouter>
|
|
46
|
+
</React.StrictMode>,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return html;
|
|
50
|
+
}
|