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.
Files changed (137) hide show
  1. package/dist/CodeBlock-37XMKCYY.mjs +7 -0
  2. package/dist/PackageManagerTabs-4NWXLXQO.mjs +314 -0
  3. package/dist/Playground-OE2OE6B6.mjs +7 -0
  4. package/dist/SearchDialog-FTOQZ763.mjs +187 -0
  5. package/dist/SearchDialog-ZAZXYIFX.css +2147 -0
  6. package/dist/Video-I6QY4X7J.mjs +7 -0
  7. package/dist/chunk-2YRDWM6O.mjs +56 -0
  8. package/dist/chunk-PN4GCTYG.mjs +67 -0
  9. package/dist/chunk-X2TDGMTR.mjs +64 -0
  10. package/dist/chunk-X6BYQHVC.mjs +12 -0
  11. package/dist/chunk-Z7JHYNAS.mjs +57 -0
  12. package/dist/chunk-ZFCOLEXN.mjs +1644 -0
  13. package/dist/client/index.css +2147 -0
  14. package/dist/client/index.d.mts +298 -0
  15. package/dist/client/index.d.ts +298 -0
  16. package/dist/client/index.js +2793 -0
  17. package/dist/client/index.mjs +63 -0
  18. package/dist/client/ssr.css +2147 -0
  19. package/dist/client/ssr.d.mts +25 -0
  20. package/dist/client/ssr.d.ts +25 -0
  21. package/dist/client/ssr.js +2727 -0
  22. package/dist/client/ssr.mjs +32 -0
  23. package/dist/config-D2XmHJYe.d.mts +122 -0
  24. package/dist/config-D2XmHJYe.d.ts +122 -0
  25. package/dist/index-CRQKWAeo.d.mts +82 -0
  26. package/dist/index-CRQKWAeo.d.ts +82 -0
  27. package/dist/node/cli/index.d.mts +1 -0
  28. package/dist/node/cli/index.d.ts +1 -0
  29. package/dist/node/cli/index.js +199 -0
  30. package/dist/node/cli/index.mjs +154 -0
  31. package/dist/node/index.d.mts +79 -0
  32. package/dist/node/index.d.ts +79 -0
  33. package/dist/node/index.js +797 -0
  34. package/dist/node/index.mjs +719 -0
  35. package/package.json +79 -0
  36. package/src/client/app/index.tsx +422 -0
  37. package/src/client/app/preload.tsx +56 -0
  38. package/src/client/index.ts +40 -0
  39. package/src/client/ssr.tsx +50 -0
  40. package/src/client/theme/components/CodeBlock/CodeBlock.tsx +76 -0
  41. package/src/client/theme/components/CodeBlock/index.ts +1 -0
  42. package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +154 -0
  43. package/src/client/theme/components/PackageManagerTabs/index.ts +1 -0
  44. package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +64 -0
  45. package/src/client/theme/components/Playground/Playground.tsx +86 -0
  46. package/src/client/theme/components/Playground/index.ts +1 -0
  47. package/src/client/theme/components/Playground/playground.css +168 -0
  48. package/src/client/theme/components/Video/Video.tsx +84 -0
  49. package/src/client/theme/components/Video/index.ts +1 -0
  50. package/src/client/theme/components/Video/video.css +41 -0
  51. package/src/client/theme/components/mdx/Admonition.tsx +80 -0
  52. package/src/client/theme/components/mdx/Badge.tsx +31 -0
  53. package/src/client/theme/components/mdx/Button.tsx +50 -0
  54. package/src/client/theme/components/mdx/Card.tsx +80 -0
  55. package/src/client/theme/components/mdx/List.tsx +57 -0
  56. package/src/client/theme/components/mdx/Tabs.tsx +94 -0
  57. package/src/client/theme/components/mdx/index.ts +18 -0
  58. package/src/client/theme/components/mdx/mdx-components.css +405 -0
  59. package/src/client/theme/icons/bun.tsx +62 -0
  60. package/src/client/theme/icons/deno.tsx +20 -0
  61. package/src/client/theme/icons/discord.tsx +12 -0
  62. package/src/client/theme/icons/github.tsx +15 -0
  63. package/src/client/theme/icons/npm.tsx +13 -0
  64. package/src/client/theme/icons/pnpm.tsx +72 -0
  65. package/src/client/theme/icons/twitter.tsx +12 -0
  66. package/src/client/theme/styles/home.css +60 -0
  67. package/src/client/theme/styles/markdown.css +343 -0
  68. package/src/client/theme/styles/variables.css +162 -0
  69. package/src/client/theme/styles.css +38 -0
  70. package/src/client/theme/ui/BackgroundGradient/BackgroundGradient.tsx +10 -0
  71. package/src/client/theme/ui/BackgroundGradient/index.ts +1 -0
  72. package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +68 -0
  73. package/src/client/theme/ui/Breadcrumbs/index.ts +1 -0
  74. package/src/client/theme/ui/Footer/footer.css +32 -0
  75. package/src/client/theme/ui/Head/Head.tsx +69 -0
  76. package/src/client/theme/ui/Head/index.ts +1 -0
  77. package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +125 -0
  78. package/src/client/theme/ui/LanguageSwitcher/index.ts +1 -0
  79. package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +98 -0
  80. package/src/client/theme/ui/Layout/Layout.tsx +213 -0
  81. package/src/client/theme/ui/Layout/base.css +76 -0
  82. package/src/client/theme/ui/Layout/index.ts +2 -0
  83. package/src/client/theme/ui/Layout/pagination.css +72 -0
  84. package/src/client/theme/ui/Layout/responsive.css +40 -0
  85. package/src/client/theme/ui/Link/Link.tsx +202 -0
  86. package/src/client/theme/ui/Link/index.ts +2 -0
  87. package/src/client/theme/ui/Loading/Loading.tsx +10 -0
  88. package/src/client/theme/ui/Loading/index.ts +1 -0
  89. package/src/client/theme/ui/Loading/loading.css +30 -0
  90. package/src/client/theme/ui/Navbar/GithubStars.tsx +27 -0
  91. package/src/client/theme/ui/Navbar/Navbar.tsx +145 -0
  92. package/src/client/theme/ui/Navbar/index.ts +2 -0
  93. package/src/client/theme/ui/Navbar/navbar.css +233 -0
  94. package/src/client/theme/ui/NotFound/NotFound.tsx +20 -0
  95. package/src/client/theme/ui/NotFound/index.ts +1 -0
  96. package/src/client/theme/ui/NotFound/not-found.css +64 -0
  97. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +192 -0
  98. package/src/client/theme/ui/OnThisPage/index.ts +1 -0
  99. package/src/client/theme/ui/OnThisPage/toc.css +132 -0
  100. package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +18 -0
  101. package/src/client/theme/ui/PoweredBy/index.ts +1 -0
  102. package/src/client/theme/ui/PoweredBy/powered-by.css +76 -0
  103. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +199 -0
  104. package/src/client/theme/ui/SearchDialog/index.ts +1 -0
  105. package/src/client/theme/ui/SearchDialog/search.css +152 -0
  106. package/src/client/theme/ui/Sidebar/Sidebar.tsx +200 -0
  107. package/src/client/theme/ui/Sidebar/index.ts +1 -0
  108. package/src/client/theme/ui/Sidebar/sidebar.css +269 -0
  109. package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +69 -0
  110. package/src/client/theme/ui/ThemeToggle/index.ts +1 -0
  111. package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +136 -0
  112. package/src/client/theme/ui/VersionSwitcher/index.ts +1 -0
  113. package/src/client/utils.ts +26 -0
  114. package/src/node/cache.ts +94 -0
  115. package/src/node/cli/commands/config.ts +15 -0
  116. package/src/node/cli/commands/generate-css.ts +24 -0
  117. package/src/node/cli/constants.ts +70 -0
  118. package/src/node/cli/index.ts +22 -0
  119. package/src/node/config.ts +185 -0
  120. package/src/node/index.ts +21 -0
  121. package/src/node/mdx.ts +41 -0
  122. package/src/node/plugin/entry.ts +58 -0
  123. package/src/node/plugin/html.ts +55 -0
  124. package/src/node/plugin/index.ts +190 -0
  125. package/src/node/plugin/types.ts +11 -0
  126. package/src/node/routes/cache.ts +24 -0
  127. package/src/node/routes/index.ts +152 -0
  128. package/src/node/routes/parser.ts +127 -0
  129. package/src/node/routes/sorter.ts +42 -0
  130. package/src/node/routes/types.ts +49 -0
  131. package/src/node/ssg/index.ts +110 -0
  132. package/src/node/ssg/meta.ts +34 -0
  133. package/src/node/ssg/options.ts +13 -0
  134. package/src/node/ssg/sitemap.ts +54 -0
  135. package/src/node/utils.ts +134 -0
  136. package/tsconfig.json +20 -0
  137. package/tsup.config.ts +22 -0
@@ -0,0 +1,136 @@
1
+ import { useState, useRef, useEffect } from "react";
2
+ import { Layers, ChevronDown } from "lucide-react";
3
+ import { useNavigate, useLocation } from "react-router-dom";
4
+ import { BoltdocsVersionsConfig } from "../../../../node/config";
5
+ import { ComponentRoute } from "../../../app";
6
+
7
+ function getBaseFilePath(
8
+ filePath: string,
9
+ version: string | undefined,
10
+ locale: string | undefined,
11
+ ): string {
12
+ let path = filePath;
13
+ if (version && (path === version || path.startsWith(version + "/"))) {
14
+ path = path === version ? "index.md" : path.slice(version.length + 1);
15
+ }
16
+ if (locale && (path === locale || path.startsWith(locale + "/"))) {
17
+ path = path === locale ? "index.md" : path.slice(locale.length + 1);
18
+ }
19
+ return path;
20
+ }
21
+
22
+ export function VersionSwitcher({
23
+ versions,
24
+ currentVersion,
25
+ currentLocale,
26
+ allRoutes,
27
+ }: {
28
+ versions: BoltdocsVersionsConfig;
29
+ currentVersion: string;
30
+ currentLocale?: string;
31
+ allRoutes: ComponentRoute[];
32
+ }) {
33
+ const [isOpen, setIsOpen] = useState(false);
34
+ const dropdownRef = useRef<HTMLDivElement>(null);
35
+ const navigate = useNavigate();
36
+ const location = useLocation();
37
+
38
+ useEffect(() => {
39
+ function handleClickOutside(event: MouseEvent) {
40
+ if (
41
+ dropdownRef.current &&
42
+ !dropdownRef.current.contains(event.target as Node)
43
+ ) {
44
+ setIsOpen(false);
45
+ }
46
+ }
47
+ document.addEventListener("mousedown", handleClickOutside);
48
+ return () => document.removeEventListener("mousedown", handleClickOutside);
49
+ }, []);
50
+
51
+ const handleSelect = (version: string) => {
52
+ setIsOpen(false);
53
+ if (version === currentVersion) return;
54
+
55
+ const currentRoute = allRoutes.find((r) => r.path === location.pathname);
56
+ let targetPath = `/docs/${version}`; // Default to root of the new version
57
+
58
+ if (currentRoute) {
59
+ const baseFile = getBaseFilePath(
60
+ currentRoute.filePath,
61
+ currentRoute.version,
62
+ currentRoute.locale,
63
+ );
64
+
65
+ const targetRoute = allRoutes.find(
66
+ (r) =>
67
+ getBaseFilePath(r.filePath, r.version, r.locale) === baseFile &&
68
+ (r.version || versions.defaultVersion) === version &&
69
+ (currentLocale ? r.locale === currentLocale : !r.locale),
70
+ );
71
+
72
+ if (targetRoute) {
73
+ targetPath = targetRoute.path;
74
+ } else {
75
+ // Fallback to version index
76
+ const versionIndexRoute = allRoutes.find(
77
+ (r) =>
78
+ getBaseFilePath(r.filePath, r.version, r.locale) === "index.md" &&
79
+ (r.version || versions.defaultVersion) === version &&
80
+ (currentLocale ? r.locale === currentLocale : !r.locale),
81
+ );
82
+ targetPath = versionIndexRoute
83
+ ? versionIndexRoute.path
84
+ : `/docs/${version}${currentLocale ? `/${currentLocale}` : ""}`;
85
+ }
86
+ }
87
+
88
+ navigate(targetPath);
89
+ };
90
+
91
+ return (
92
+ <div
93
+ className="boltdocs-version-switcher"
94
+ ref={dropdownRef}
95
+ style={{ position: "relative", display: "flex", alignItems: "center" }}
96
+ >
97
+ <button
98
+ className="navbar-version"
99
+ onClick={() => setIsOpen(!isOpen)}
100
+ aria-label="Switch version"
101
+ aria-expanded={isOpen}
102
+ aria-haspopup="listbox"
103
+ style={{
104
+ fontFamily: "inherit",
105
+ padding: "0.25rem 0.5rem",
106
+ marginLeft: "0.5rem",
107
+ }}
108
+ >
109
+ <span>{versions.versions[currentVersion] || currentVersion}</span>
110
+ <ChevronDown size={14} />
111
+ </button>
112
+
113
+ {isOpen && (
114
+ <div
115
+ className="language-dropdown"
116
+ style={{
117
+ left: "0.5rem",
118
+ right: "auto",
119
+ minWidth: "150px",
120
+ top: "calc(100% + 8px)",
121
+ }}
122
+ >
123
+ {Object.entries(versions.versions).map(([key, label]) => (
124
+ <button
125
+ key={key}
126
+ className={`language-option ${key === currentVersion ? "active" : ""}`}
127
+ onClick={() => handleSelect(key)}
128
+ >
129
+ {label}
130
+ </button>
131
+ ))}
132
+ </div>
133
+ )}
134
+ </div>
135
+ );
136
+ }
@@ -0,0 +1 @@
1
+ export { VersionSwitcher } from "./VersionSwitcher";
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Get the number of stars for a GitHub repository.
3
+ * @param repo - The repository name in the format "owner/repo".
4
+ * @returns The number of stars for the repository.
5
+ */
6
+ export async function getStarsRepo(repo: string) {
7
+ const response = await fetch(`https://api.github.com/repos/${repo}`);
8
+ const data = await response.json();
9
+ if (data.stargazers_count !== undefined) {
10
+ return formatStars(data.stargazers_count);
11
+ } else {
12
+ return "0"; // Fallback
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Format a number of stars in a compact form.
18
+ * @param count - The number of stars to format.
19
+ * @returns The formatted number of stars.
20
+ */
21
+ const formatStars = (count: number): string => {
22
+ return Intl.NumberFormat("en", {
23
+ notation: "compact",
24
+ compactDisplay: "short",
25
+ }).format(count);
26
+ };
@@ -0,0 +1,94 @@
1
+ import { getFileMtime } from "./utils";
2
+
3
+ /**
4
+ * Per-file cache entry. Stores parsed data + the mtime at parse time.
5
+ */
6
+ interface FileCacheEntry<T> {
7
+ data: T;
8
+ mtime: number;
9
+ }
10
+
11
+ /**
12
+ * Generic file-based cache with per-file granularity.
13
+ * Only re-parses files whose mtime has changed.
14
+ */
15
+ export class FileCache<T> {
16
+ private entries = new Map<string, FileCacheEntry<T>>();
17
+
18
+ /**
19
+ * Retrieves parsed data for a file from the cache.
20
+ * Compares the current filesystem mtime with the cached mtime.
21
+ *
22
+ * @param filePath - The absolute path of the file
23
+ * @returns The cached data if valid, or `null` if the file has changed or doesn't exist
24
+ */
25
+ get(filePath: string): T | null {
26
+ const entry = this.entries.get(filePath);
27
+ if (!entry) return null;
28
+
29
+ const currentMtime = getFileMtime(filePath);
30
+ if (currentMtime !== entry.mtime) return null;
31
+
32
+ return entry.data;
33
+ }
34
+
35
+ /**
36
+ * Stores parsed data for a file in the cache, recording its current mtime.
37
+ *
38
+ * @param filePath - The absolute path to the file
39
+ * @param data - The parsed data to store
40
+ */
41
+ set(filePath: string, data: T): void {
42
+ this.entries.set(filePath, {
43
+ data,
44
+ mtime: getFileMtime(filePath),
45
+ });
46
+ }
47
+
48
+ /**
49
+ * Checks if a specific file's cache is still valid (based on its mtime).
50
+ *
51
+ * @param filePath - The absolute path to the file
52
+ * @returns `true` if the cache is valid, `false` otherwise
53
+ */
54
+ isValid(filePath: string): boolean {
55
+ return this.get(filePath) !== null;
56
+ }
57
+
58
+ /**
59
+ * Manually removes a specific file from the cache.
60
+ * Useful when forcefully invalidating a single updated file.
61
+ *
62
+ * @param filePath - The absolute path to the file
63
+ */
64
+ invalidate(filePath: string): void {
65
+ this.entries.delete(filePath);
66
+ }
67
+
68
+ /**
69
+ * Clears the entire cache, forcing all files to be re-parsed on the next request.
70
+ * Useful when global dependencies (like config) change.
71
+ */
72
+ invalidateAll(): void {
73
+ this.entries.clear();
74
+ }
75
+
76
+ /**
77
+ * Removes cached entries for files that no longer exist on the filesystem.
78
+ * Prevents memory leaks from deleted files.
79
+ *
80
+ * @param currentFiles - A Set of absolute file paths currently discovered on the disk
81
+ */
82
+ pruneStale(currentFiles: Set<string>): void {
83
+ for (const key of this.entries.keys()) {
84
+ if (!currentFiles.has(key)) {
85
+ this.entries.delete(key);
86
+ }
87
+ }
88
+ }
89
+
90
+ /** Number of cached entries */
91
+ get size(): number {
92
+ return this.entries.size;
93
+ }
94
+ }
@@ -0,0 +1,15 @@
1
+ import path from "path";
2
+ import { resolveConfig } from "../../config";
3
+
4
+ export async function configAction(root?: string) {
5
+ const rootDir = root ? path.resolve(root) : process.cwd();
6
+ const docsDir = path.resolve(rootDir, "docs");
7
+
8
+ try {
9
+ const config = await resolveConfig(docsDir);
10
+ process.stdout.write(JSON.stringify(config, null, 2) + "\n");
11
+ } catch (e) {
12
+ process.stderr.write("Failed to resolve configuration: " + e + "\n");
13
+ process.exit(1);
14
+ }
15
+ }
@@ -0,0 +1,24 @@
1
+ import path from "path";
2
+ import fs from "node:fs";
3
+ import { DEFAULT_CSS_VARIABLES } from "../constants";
4
+
5
+ export function generateCssAction(outputPath?: string) {
6
+ try {
7
+ const target = process.cwd();
8
+ const filename = outputPath || "custom.css";
9
+ const filepath = path.resolve(target, filename);
10
+
11
+ if (fs.existsSync(filepath)) {
12
+ process.stderr.write(
13
+ "Error: File " + filename + " already exists in " + target + ".\n",
14
+ );
15
+ process.exit(1);
16
+ }
17
+
18
+ fs.writeFileSync(filepath, DEFAULT_CSS_VARIABLES);
19
+ process.stdout.write("Success! Generated " + filename + "\n");
20
+ } catch (e) {
21
+ process.stderr.write("Failed to generate CSS: " + e + "\n");
22
+ process.exit(1);
23
+ }
24
+ }
@@ -0,0 +1,70 @@
1
+ export const DEFAULT_CSS_VARIABLES = `:root {
2
+ /* ─ Base palette ─ */
3
+ --ld-bg-main: #0a0a0f;
4
+ --ld-bg-soft: #0f0f18;
5
+ --ld-bg-mute: #141420;
6
+ --ld-surface: #1a1a2e;
7
+ --ld-border-subtle: rgba(255, 255, 255, 0.06);
8
+ --ld-border-strong: rgba(255, 255, 255, 0.12);
9
+
10
+ /* ─ Text ─ */
11
+ --ld-text-main: #e4e4ed;
12
+ --ld-text-muted: #9d9db5;
13
+ --ld-text-dim: #6b6b85;
14
+
15
+ /* ─ Accent ─ */
16
+ --ld-color-primary: #ffff;
17
+ --ld-color-primary-hover: #f8f8f8;
18
+ --ld-color-primary-muted: rgba(255, 255, 255, 0.05);
19
+ --ld-color-primary-glow: rgba(255, 255, 255, 0.15);
20
+ --ld-color-primary-text: #010101;
21
+ --ld-color-accent: #ffff; /* Yellow accent for TOC */
22
+
23
+ /* ─ Code ─ */
24
+ --ld-code-bg: #0d0d14;
25
+ --ld-code-header: #111119;
26
+ --ld-code-text: #d4d4d4;
27
+
28
+ /* ─ Typography ─ */
29
+ --ld-font-sans: "Inter", system-ui, -apple-system, sans-serif;
30
+ --ld-font-mono:
31
+ "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
32
+ monospace;
33
+
34
+ /* ─ Dimensions ─ */
35
+ --ld-navbar-height: 3.5rem;
36
+ --ld-sidebar-width: 14.5rem;
37
+ --ld-toc-width: 13rem;
38
+ --ld-content-max-width: 820px;
39
+ --ld-radius-sm: 4px;
40
+ --ld-radius-md: 8px;
41
+ --ld-radius-lg: 12px;
42
+ --ld-radius-full: 9999px;
43
+ }
44
+
45
+ [data-theme="light"],
46
+ .theme-light {
47
+ --ld-bg-main: #ffffff;
48
+ --ld-bg-soft: #f9fafb;
49
+ --ld-bg-mute: #f3f4f6;
50
+
51
+ --ld-surface: #ffffff;
52
+
53
+ --ld-border-subtle: #e5e7eb;
54
+ --ld-border-strong: #d1d5db;
55
+
56
+ --ld-text-main: #111827;
57
+ --ld-text-muted: #4b5563;
58
+ --ld-text-dim: #6b7280;
59
+
60
+ --ld-color-primary: #010101;
61
+ --ld-color-primary-hover: #010101;
62
+ --ld-color-primary-muted: rgba(127, 19, 236, 0.1);
63
+ --ld-color-primary-glow: rgba(127, 19, 236, 0.25);
64
+ --ld-color-primary-text: #010101;
65
+
66
+ --ld-code-bg: #f3f4f6;
67
+ --ld-code-header: #e5e7eb;
68
+ --ld-code-text: #1f2937;
69
+ }
70
+ `;
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import cac from "cac";
3
+ import { configAction } from "./commands/config";
4
+ import { generateCssAction } from "./commands/generate-css";
5
+
6
+ const cli = cac("boltdocs");
7
+
8
+ cli
9
+ .command("config [root]", "Output the resolved boltdocs configuration")
10
+ .action(configAction);
11
+
12
+ cli
13
+ .command(
14
+ "generate:css [path]",
15
+ "Generate a custom.css file with default Boltdocs CSS variables",
16
+ )
17
+ .action(generateCssAction);
18
+
19
+ cli.help();
20
+ cli.version("1.0.0");
21
+
22
+ cli.parse();
@@ -0,0 +1,185 @@
1
+ import path from "path";
2
+ import { pathToFileURL } from "url";
3
+ import fs from "fs";
4
+ import type { Plugin as VitePlugin } from "vite";
5
+
6
+ /**
7
+ * Represents a single social link in the configuration.
8
+ */
9
+ export interface BoltdocsSocialLink {
10
+ /** Identifier for the icon (e.g., 'github', 'twitter') */
11
+ icon: "discord" | "x" | string;
12
+ /** The URL the social link points to */
13
+ link: string;
14
+ }
15
+
16
+ /**
17
+ * Configuration for the site footer.
18
+ */
19
+ export interface BoltdocsFooterConfig {
20
+ /** Text to display in the footer (HTML is supported) */
21
+ text?: string;
22
+ }
23
+
24
+ /**
25
+ * Theme-specific configuration options governing the appearance and navigation of the site.
26
+ */
27
+ export interface BoltdocsThemeConfig {
28
+ /** The global title of the documentation site */
29
+ title?: string;
30
+ /** The global description of the site (used for SEO) */
31
+ description?: string;
32
+ /** URL path to the site logo */
33
+ logo?: string;
34
+ /** Items to display in the top navigation bar */
35
+ navbar?: Array<{ text: string; link: string }>;
36
+ /** Items to display in the sidebar, organized optionally by group URLs */
37
+ sidebar?: Record<string, Array<{ text: string; link: string }>>;
38
+ /** Social links to display in the navigation bar */
39
+ socialLinks?: BoltdocsSocialLink[];
40
+ /** Site footer configuration */
41
+ footer?: BoltdocsFooterConfig;
42
+ /** Whether to show breadcrumbs navigation (default: true) */
43
+ breadcrumbs?: boolean;
44
+ /** Path to a custom CSS file that overrides theme variables */
45
+ customCss?: string;
46
+ /** URL template for 'Edit this page'. Use :path as a placeholder. */
47
+ editLink?: string;
48
+ /** URL for the 'Community help' link. */
49
+ communityHelp?: string;
50
+ /** The current version of the project (e.g., 'v2.8.9'). Displayed in the Navbar. */
51
+ version?: string;
52
+ /** The GitHub repository in the format 'owner/repo' to fetch and display star count. */
53
+ githubRepo?: string;
54
+ /** Whether to show the 'Powered by LiteDocs' badge in the sidebar (default: true) */
55
+ poweredBy?: boolean;
56
+ /** Granular layout customization props */
57
+ layoutProps?: {
58
+ navbar?: any;
59
+ sidebar?: any;
60
+ toc?: any;
61
+ background?: any;
62
+ head?: any;
63
+ breadcrumbs?: any;
64
+ className?: string;
65
+ style?: any;
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Configuration for internationalization (i18n).
71
+ */
72
+ export interface BoltdocsI18nConfig {
73
+ /** The default locale (e.g., 'en') */
74
+ defaultLocale: string;
75
+ /** Available locales and their display names (e.g., { en: 'English', es: 'Español' }) */
76
+ locales: Record<string, string>;
77
+ }
78
+
79
+ /**
80
+ * Configuration for documentation versioning.
81
+ */
82
+ export interface BoltdocsVersionsConfig {
83
+ /** The default version (e.g., 'v2') */
84
+ defaultVersion: string;
85
+ /** Available versions and their display names (e.g., { v1: 'Version 1.x', v2: 'Version 2.x' }) */
86
+ versions: Record<string, string>;
87
+ }
88
+
89
+ /**
90
+ * Defines a Boltdocs plugin that can extend the build process and client-side functionality.
91
+ */
92
+ export interface BoltdocsPlugin {
93
+ /** A unique name for the plugin */
94
+ name: string;
95
+ /** Whether to run this plugin before or after default ones (optional) */
96
+ enforce?: "pre" | "post";
97
+ /** Optional remark plugins to add to the MDX pipeline */
98
+ remarkPlugins?: any[];
99
+ /** Optional rehype plugins to add to the MDX pipeline */
100
+ rehypePlugins?: any[];
101
+ /** Optional Vite plugins to inject into the build process */
102
+ vitePlugins?: VitePlugin[];
103
+ /** Optional custom React components to register in MDX. Map of Name -> Module Path. */
104
+ components?: Record<string, string>;
105
+ }
106
+
107
+ /**
108
+ * The root configuration object for Boltdocs.
109
+ */
110
+ export interface BoltdocsConfig {
111
+ /** The base URL of the site, used for generating the sitemap */
112
+ siteUrl?: string;
113
+ /** Configuration pertaining to the UI and appearance */
114
+ themeConfig?: BoltdocsThemeConfig;
115
+ /** The root directory containing markdown documentation files (default: 'docs') */
116
+ docsDir?: string;
117
+ /** Configuration for internationalization */
118
+ i18n?: BoltdocsI18nConfig;
119
+ /** Configuration for documentation versioning */
120
+ versions?: BoltdocsVersionsConfig;
121
+ /** Custom plugins for extending functionality */
122
+ plugins?: BoltdocsPlugin[];
123
+ }
124
+
125
+ export const CONFIG_FILES = [
126
+ "boltdocs.config.js",
127
+ "boltdocs.config.mjs",
128
+ "boltdocs.config.ts",
129
+ ];
130
+
131
+ /**
132
+ * Loads user's configuration file (e.g., `boltdocs.config.js` or `boltdocs.config.ts`) if it exists,
133
+ * merges it with the default configuration, and returns the final `BoltdocsConfig`.
134
+ *
135
+ * @param docsDir - The fallback/default documentation directory
136
+ * @returns A promise resolving to the final merged configuration object
137
+ */
138
+ export async function resolveConfig(docsDir: string): Promise<BoltdocsConfig> {
139
+ const projectRoot = process.cwd();
140
+
141
+ const defaults: BoltdocsConfig = {
142
+ docsDir: path.resolve(docsDir),
143
+ themeConfig: {
144
+ title: "Boltdocs",
145
+ description: "A Vite documentation framework",
146
+ navbar: [
147
+ { text: "Home", link: "/" },
148
+ { text: "Documentation", link: "/docs" },
149
+ ],
150
+ },
151
+ };
152
+
153
+ // Try to load user config
154
+ for (const filename of CONFIG_FILES) {
155
+ const configPath = path.resolve(projectRoot, filename);
156
+ if (fs.existsSync(configPath)) {
157
+ try {
158
+ // Add a timestamp query parameter to bust the ESM cache
159
+ const fileUrl = pathToFileURL(configPath).href + "?t=" + Date.now();
160
+ const mod = await import(fileUrl);
161
+ const userConfig = mod.default || mod;
162
+
163
+ // Merge user themeConfig into defaults
164
+ // Support new format where user exports BoltdocsConfig directly
165
+ const userThemeConfig = userConfig.themeConfig || userConfig;
166
+
167
+ return {
168
+ docsDir: path.resolve(docsDir),
169
+ themeConfig: {
170
+ ...defaults.themeConfig,
171
+ ...userThemeConfig,
172
+ },
173
+ i18n: userConfig.i18n,
174
+ versions: userConfig.versions,
175
+ siteUrl: userConfig.siteUrl,
176
+ plugins: userConfig.plugins || [],
177
+ };
178
+ } catch (e) {
179
+ console.warn(`[boltdocs] Failed to load config from ${filename}:`, e);
180
+ }
181
+ }
182
+ }
183
+
184
+ return defaults;
185
+ }
@@ -0,0 +1,21 @@
1
+ import { Plugin } from "vite";
2
+ import { boltdocsPlugin } from "./plugin/index";
3
+ import { boltdocsMdxPlugin } from "./mdx";
4
+ import { BoltdocsPluginOptions } from "./plugin/index";
5
+
6
+ import { resolveConfig } from "./config";
7
+
8
+ export default async function boltdocs(
9
+ options?: BoltdocsPluginOptions,
10
+ ): Promise<Plugin[]> {
11
+ const docsDir = options?.docsDir || "docs";
12
+ const config = await resolveConfig(docsDir);
13
+
14
+ return [...boltdocsPlugin(options, config), boltdocsMdxPlugin(config)];
15
+ }
16
+
17
+ export type { BoltdocsPluginOptions };
18
+ export { generateStaticPages } from "./ssg";
19
+ export type { SSGOptions } from "./ssg";
20
+ export type { RouteMeta } from "./routes";
21
+ export type { BoltdocsConfig, BoltdocsThemeConfig } from "./config";
@@ -0,0 +1,41 @@
1
+ import mdxPlugin from "@mdx-js/rollup";
2
+ import remarkGfm from "remark-gfm";
3
+ import remarkFrontmatter from "remark-frontmatter";
4
+ import rehypeSlug from "rehype-slug";
5
+ import rehypePrettyCode from "rehype-pretty-code";
6
+ import type { Plugin } from "vite";
7
+
8
+ import type { BoltdocsConfig } from "./config";
9
+
10
+ /**
11
+ * Configures the MDX compiler for Vite using `@mdx-js/rollup`.
12
+ * Includes standard remark and rehype plugins for GitHub Flavored Markdown (GFM),
13
+ * frontmatter extraction, auto-linking headers, and syntax highlighting via `rehype-pretty-code`.
14
+ *
15
+ * @param config - The Boltdocs configuration containing custom plugins
16
+ * @returns A Vite plugin configured for MDX parsing
17
+ */
18
+ export function boltdocsMdxPlugin(config?: BoltdocsConfig): Plugin {
19
+ const extraRemarkPlugins =
20
+ config?.plugins?.flatMap((p) => p.remarkPlugins || []) || [];
21
+ const extraRehypePlugins =
22
+ config?.plugins?.flatMap((p) => p.rehypePlugins || []) || [];
23
+
24
+ return mdxPlugin({
25
+ remarkPlugins: [remarkGfm, remarkFrontmatter, ...extraRemarkPlugins],
26
+ rehypePlugins: [
27
+ rehypeSlug,
28
+ ...extraRehypePlugins,
29
+ [
30
+ rehypePrettyCode,
31
+ {
32
+ theme: "one-dark-pro",
33
+ keepBackground: false,
34
+ },
35
+ ],
36
+ ],
37
+ // Provide React as default for JSX
38
+ jsxRuntime: "automatic",
39
+ providerImportSource: "@mdx-js/react",
40
+ }) as Plugin;
41
+ }