@docubook/flame 1.0.0-beta.70 → 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/.docu/components/Anchor.tsx +1 -1
- package/.docu/components/Breadcrumb.tsx +6 -1
- package/.docu/components/Context.tsx +2 -1
- package/.docu/components/Navbar.tsx +2 -2
- package/.docu/components/Pagination.tsx +15 -2
- package/.docu/components/Search.tsx +4 -10
- package/.docu/components/Sidebar.tsx +15 -4
- package/.docu/components/Theme.tsx +1 -1
- package/.docu/components/registry.ts +21 -87
- package/.docu/node/build.ts +29 -7
- package/.docu/node/client.ts +1 -8
- package/.docu/node/html.ts +12 -0
- package/.docu/node/mdx.ts +13 -3
- package/.docu/node/parse-tocs.ts +10 -0
- package/.docu/node/search-indexer.ts +18 -22
- package/.docu/node/server.ts +1 -13
- package/.docu/node/utils.ts +33 -2
- package/.docu/pages/docs/[[...slug]].tsx +6 -1
- package/.docu/styles/globals.css +8 -0
- package/README.md +2 -2
- package/package.json +9 -5
- package/template/docs/index.mdx +123 -17
- package/.docu/components/base/breadcrumbs.tsx +0 -64
- package/.docu/components/base/collapse.tsx +0 -186
- package/.docu/components/base/drawer.tsx +0 -164
- package/.docu/components/base/dropdown.tsx +0 -94
- package/.docu/components/base/input.tsx +0 -53
- package/.docu/components/base/kbd.tsx +0 -51
- package/.docu/components/base/modal.tsx +0 -56
- package/.docu/components/base/navbar.tsx +0 -132
- package/.docu/components/base/pagination.tsx +0 -548
- package/.docu/components/base/theme-controller.tsx +0 -358
- package/.docu/components/base/toggle.tsx +0 -172
|
@@ -34,7 +34,7 @@ export default function Anchor({
|
|
|
34
34
|
|
|
35
35
|
const activeClass = isActive ? activeClassName : "";
|
|
36
36
|
const baseClasses = cn(
|
|
37
|
-
"
|
|
37
|
+
"hover:underline transition-colors",
|
|
38
38
|
className,
|
|
39
39
|
activeClass,
|
|
40
40
|
disabled && "cursor-not-allowed opacity-50"
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Breadcrumb,
|
|
3
|
+
BreadcrumbItem,
|
|
4
|
+
BreadcrumbList,
|
|
5
|
+
BreadcrumbPage,
|
|
6
|
+
} from "@docubook/ui-react/breadcrumbs";
|
|
2
7
|
|
|
3
8
|
export interface DocsBreadcrumbProps {
|
|
4
9
|
paths: string[];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { cn } from "../node/utils";
|
|
5
|
-
import { Dropdown, DropdownItem } from "
|
|
5
|
+
import { Dropdown, DropdownItem } from "@docubook/ui-react/dropdown";
|
|
6
6
|
import { routes } from "../node/client-routes";
|
|
7
7
|
import { ChevronsUpDown, Check, type LucideIcon } from "lucide-react";
|
|
8
8
|
import * as LucideIcons from "lucide-react";
|
|
@@ -51,6 +51,7 @@ export function Context({ className }: ContextProps) {
|
|
|
51
51
|
<Dropdown
|
|
52
52
|
className={cn("w-full", className)}
|
|
53
53
|
align="start"
|
|
54
|
+
menuClassName="w-full"
|
|
54
55
|
trigger={
|
|
55
56
|
<div className="border-base-300 flex items-center justify-between rounded-lg border px-3 py-2 text-sm font-semibold">
|
|
56
57
|
<div className="flex min-w-0 items-center gap-2">
|
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
NavMenu as BaseNavMenu,
|
|
9
9
|
NavMenuLink as BaseNavMenuLink,
|
|
10
10
|
type NavMenuItem as BaseNavMenuItem,
|
|
11
|
-
} from "
|
|
11
|
+
} from "@docubook/ui-react/navbar";
|
|
12
12
|
|
|
13
|
-
export type { NavMenuItem } from "
|
|
13
|
+
export type { NavMenuItem } from "@docubook/ui-react/navbar";
|
|
14
14
|
|
|
15
15
|
interface AppNavMenuItem {
|
|
16
16
|
title: string;
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import type { ReactNode } from "react";
|
|
3
4
|
import { getPreviousNext } from "../node/route";
|
|
4
|
-
import { PaginationDocs } from "
|
|
5
|
+
import { PaginationDocs } from "@docubook/ui-react/pagination";
|
|
5
6
|
|
|
6
7
|
interface PaginationProps {
|
|
7
8
|
pathname: string;
|
|
8
9
|
className?: string;
|
|
10
|
+
prevIcon?: ReactNode;
|
|
11
|
+
nextIcon?: ReactNode;
|
|
12
|
+
linkClassName?: string;
|
|
9
13
|
}
|
|
10
14
|
|
|
11
|
-
export default function Pagination({
|
|
15
|
+
export default function Pagination({
|
|
16
|
+
pathname,
|
|
17
|
+
className,
|
|
18
|
+
prevIcon,
|
|
19
|
+
nextIcon,
|
|
20
|
+
linkClassName,
|
|
21
|
+
}: PaginationProps) {
|
|
12
22
|
const { prev, next } = getPreviousNext(pathname);
|
|
13
23
|
|
|
14
24
|
if (!prev && !next) {
|
|
@@ -20,6 +30,9 @@ export default function Pagination({ pathname, className }: PaginationProps) {
|
|
|
20
30
|
prev={prev ? { href: `/docs${prev.href}`, title: prev.title } : undefined}
|
|
21
31
|
next={next ? { href: `/docs${next.href}`, title: next.title } : undefined}
|
|
22
32
|
className={className}
|
|
33
|
+
prevIcon={prevIcon}
|
|
34
|
+
nextIcon={nextIcon}
|
|
35
|
+
linkClassName={linkClassName}
|
|
23
36
|
/>
|
|
24
37
|
);
|
|
25
38
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useRef, useCallback } from "react";
|
|
4
4
|
import { Search as SearchIcon, FileText, CornerDownLeft, Hash, AlignLeft } from "lucide-react";
|
|
5
|
-
import { Modal, useModal } from "
|
|
6
|
-
import { Kbd, FnKey } from "
|
|
5
|
+
import { Modal, useModal } from "@docubook/ui-react/modal";
|
|
6
|
+
import { Kbd, FnKey } from "@docubook/ui-react/kbd";
|
|
7
7
|
import { cn } from "../node/utils";
|
|
8
8
|
import { search, type SearchResult } from "../node/search";
|
|
9
9
|
import type { SearchRecord } from "../node/search-indexer";
|
|
@@ -217,16 +217,10 @@ function SearchTrigger({ onClick }: { onClick: () => void }) {
|
|
|
217
217
|
<SearchIcon className="h-4 w-4 shrink-0" />
|
|
218
218
|
<span className="flex-1 text-left">Search</span>
|
|
219
219
|
<div className="flex items-center gap-0.5">
|
|
220
|
-
<Kbd
|
|
221
|
-
size="s"
|
|
222
|
-
className="bg-primary text-[12px text-base-300 dark:text-foreground rounded-md p-1.5 font-medium"
|
|
223
|
-
>
|
|
220
|
+
<Kbd className="bg-primary text-[12px text-base-300 dark:text-foreground rounded-md p-1.5 font-medium">
|
|
224
221
|
<FnKey.Cmd />
|
|
225
222
|
</Kbd>
|
|
226
|
-
<Kbd
|
|
227
|
-
size="s"
|
|
228
|
-
className="bg-primary text-base-300 dark:text-foreground rounded-md p-1.5 text-[12px] font-medium"
|
|
229
|
-
>
|
|
223
|
+
<Kbd className="bg-primary text-base-300 dark:text-foreground rounded-md p-1.5 text-[12px] font-medium">
|
|
230
224
|
K
|
|
231
225
|
</Kbd>
|
|
232
226
|
</div>
|
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
PanelRightClose,
|
|
10
10
|
FileText,
|
|
11
11
|
} from "lucide-react";
|
|
12
|
-
import { Dropdown
|
|
12
|
+
import { Dropdown } from "@docubook/ui-react/dropdown";
|
|
13
13
|
import { cn } from "../node/utils";
|
|
14
|
+
import Anchor from "./Anchor";
|
|
14
15
|
import { Context } from "./Context";
|
|
15
16
|
import Menu from "./Menu";
|
|
16
17
|
import { ThemeToggle } from "./Theme";
|
|
@@ -132,6 +133,7 @@ export function MobileBar({
|
|
|
132
133
|
<div className="flex items-center gap-1 p-2">
|
|
133
134
|
<Dropdown
|
|
134
135
|
align="start"
|
|
136
|
+
menuClassName="w-52 min-w-0"
|
|
135
137
|
trigger={
|
|
136
138
|
<span className="btn btn-ghost btn-sm btn-square">
|
|
137
139
|
<EllipsisVertical className="text-base-content/60 h-5 w-5" />
|
|
@@ -139,9 +141,18 @@ export function MobileBar({
|
|
|
139
141
|
}
|
|
140
142
|
>
|
|
141
143
|
{(docuConfig.navbar?.menu || []).map((item) => (
|
|
142
|
-
<
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
<li key={item.href} className="text-base-content/80 hover:text-base-content">
|
|
145
|
+
<Anchor
|
|
146
|
+
href={item.href}
|
|
147
|
+
activeWhen={(path: string) =>
|
|
148
|
+
path === item.href || path.startsWith(item.href + "/")
|
|
149
|
+
}
|
|
150
|
+
activeClassName="text-primary font-semibold"
|
|
151
|
+
className="block rounded px-2 py-2 text-sm"
|
|
152
|
+
>
|
|
153
|
+
{item.title}
|
|
154
|
+
</Anchor>
|
|
155
|
+
</li>
|
|
145
156
|
))}
|
|
146
157
|
</Dropdown>
|
|
147
158
|
|
|
@@ -1,56 +1,32 @@
|
|
|
1
1
|
import type { ComponentType } from "react";
|
|
2
2
|
import { createMdxComponents } from "@docubook/mdx-content";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
DropdownLabel,
|
|
8
|
-
DropdownDivider,
|
|
9
|
-
} from "./base/dropdown";
|
|
10
|
-
import {
|
|
11
|
-
Pagination,
|
|
12
|
-
PaginationItem,
|
|
13
|
-
PaginationButtons,
|
|
14
|
-
PaginationRange,
|
|
15
|
-
PaginationInfo,
|
|
16
|
-
PaginationFull,
|
|
17
|
-
PaginationDocs,
|
|
18
|
-
} from "./base/pagination";
|
|
19
|
-
import { Toggle, ToggleGroup } from "./base/toggle";
|
|
20
|
-
import {
|
|
21
|
-
ThemeController,
|
|
22
|
-
ThemeControllerToggle,
|
|
23
|
-
ThemeControllerSelect,
|
|
24
|
-
ThemeControllerRadio,
|
|
25
|
-
} from "./base/theme-controller";
|
|
3
|
+
import { Dropdown, DropdownItem, DropdownLink } from "@docubook/ui-react/dropdown";
|
|
4
|
+
import { PaginationDocs } from "@docubook/ui-react/pagination";
|
|
5
|
+
import { Toggle, ToggleGroup } from "@docubook/ui-react/toggle";
|
|
6
|
+
import { ThemeControllerToggle } from "@docubook/ui-react/theme-controller";
|
|
26
7
|
import {
|
|
27
8
|
Breadcrumb,
|
|
28
9
|
BreadcrumbItem,
|
|
29
|
-
BreadcrumbLink,
|
|
30
10
|
BreadcrumbPage,
|
|
31
|
-
BreadcrumbSeparator,
|
|
32
11
|
BreadcrumbList,
|
|
33
|
-
} from "
|
|
34
|
-
import Collapse,
|
|
12
|
+
} from "@docubook/ui-react/breadcrumbs";
|
|
13
|
+
import { Collapse, Accordion } from "@docubook/ui-react/collapse";
|
|
35
14
|
import {
|
|
36
15
|
Drawer,
|
|
37
16
|
useDrawerState,
|
|
38
17
|
DrawerTrigger,
|
|
39
18
|
DrawerContent,
|
|
40
19
|
DrawerSidePanel,
|
|
41
|
-
} from "
|
|
42
|
-
import { Input, InputGroup } from "
|
|
43
|
-
import { Kbd, FnKey } from "
|
|
44
|
-
import { Modal,
|
|
20
|
+
} from "@docubook/ui-react/drawer";
|
|
21
|
+
import { Input, InputGroup } from "@docubook/ui-react/input";
|
|
22
|
+
import { Kbd, FnKey } from "@docubook/ui-react/kbd";
|
|
23
|
+
import { Modal, useModal } from "@docubook/ui-react/modal";
|
|
45
24
|
import {
|
|
46
25
|
Navbar as BaseNavbar,
|
|
47
|
-
NavbarContainer,
|
|
48
26
|
Logo as BaseLogo,
|
|
49
27
|
NavMenu as BaseNavMenu,
|
|
50
28
|
NavMenuLink,
|
|
51
|
-
|
|
52
|
-
NavbarVersion,
|
|
53
|
-
} from "./base/navbar";
|
|
29
|
+
} from "@docubook/ui-react/navbar";
|
|
54
30
|
import PaginationComp from "./Pagination";
|
|
55
31
|
import Anchor from "./Anchor";
|
|
56
32
|
import { Footer } from "./Footer";
|
|
@@ -71,40 +47,17 @@ import {
|
|
|
71
47
|
import Search from "./Search";
|
|
72
48
|
import Sidebar from "./Sidebar";
|
|
73
49
|
|
|
74
|
-
export { Dropdown, DropdownItem, DropdownLink
|
|
75
|
-
export {
|
|
76
|
-
Pagination,
|
|
77
|
-
PaginationItem,
|
|
78
|
-
PaginationButtons,
|
|
79
|
-
PaginationRange,
|
|
80
|
-
PaginationInfo,
|
|
81
|
-
PaginationFull,
|
|
82
|
-
PaginationDocs,
|
|
83
|
-
};
|
|
50
|
+
export { Dropdown, DropdownItem, DropdownLink };
|
|
51
|
+
export { PaginationDocs };
|
|
84
52
|
export { Toggle, ToggleGroup };
|
|
85
|
-
export {
|
|
86
|
-
export {
|
|
87
|
-
Breadcrumb,
|
|
88
|
-
BreadcrumbItem,
|
|
89
|
-
BreadcrumbLink,
|
|
90
|
-
BreadcrumbPage,
|
|
91
|
-
BreadcrumbSeparator,
|
|
92
|
-
BreadcrumbList,
|
|
93
|
-
};
|
|
53
|
+
export { ThemeControllerToggle };
|
|
54
|
+
export { Breadcrumb, BreadcrumbItem, BreadcrumbPage, BreadcrumbList };
|
|
94
55
|
export { Collapse, Accordion };
|
|
95
56
|
export { Drawer, useDrawerState, DrawerTrigger, DrawerContent, DrawerSidePanel };
|
|
96
57
|
export { Input, InputGroup };
|
|
97
58
|
export { Kbd, FnKey };
|
|
98
|
-
export { Modal,
|
|
99
|
-
export {
|
|
100
|
-
BaseNavbar,
|
|
101
|
-
NavbarContainer,
|
|
102
|
-
BaseLogo,
|
|
103
|
-
BaseNavMenu,
|
|
104
|
-
NavMenuLink,
|
|
105
|
-
NavToggle,
|
|
106
|
-
NavbarVersion,
|
|
107
|
-
};
|
|
59
|
+
export { Modal, useModal };
|
|
60
|
+
export { BaseNavbar, BaseLogo, BaseNavMenu, NavMenuLink };
|
|
108
61
|
export { Footer };
|
|
109
62
|
export { ThemeToggle };
|
|
110
63
|
export { Context };
|
|
@@ -122,16 +75,8 @@ const BASE_COMPONENTS = {
|
|
|
122
75
|
Dropdown,
|
|
123
76
|
DropdownItem,
|
|
124
77
|
DropdownLink,
|
|
125
|
-
DropdownLabel,
|
|
126
|
-
DropdownDivider,
|
|
127
78
|
|
|
128
79
|
// Pagination
|
|
129
|
-
Pagination,
|
|
130
|
-
PaginationItem,
|
|
131
|
-
PaginationButtons,
|
|
132
|
-
PaginationRange,
|
|
133
|
-
PaginationInfo,
|
|
134
|
-
PaginationFull,
|
|
135
80
|
PaginationDocs,
|
|
136
81
|
|
|
137
82
|
// Toggle
|
|
@@ -139,17 +84,12 @@ const BASE_COMPONENTS = {
|
|
|
139
84
|
ToggleGroup,
|
|
140
85
|
|
|
141
86
|
// Theme
|
|
142
|
-
ThemeController,
|
|
143
87
|
ThemeControllerToggle,
|
|
144
|
-
ThemeControllerSelect,
|
|
145
|
-
ThemeControllerRadio,
|
|
146
88
|
|
|
147
89
|
// Breadcrumb
|
|
148
90
|
Breadcrumb,
|
|
149
91
|
BreadcrumbItem,
|
|
150
|
-
BreadcrumbLink,
|
|
151
92
|
BreadcrumbPage,
|
|
152
|
-
BreadcrumbSeparator,
|
|
153
93
|
BreadcrumbList,
|
|
154
94
|
|
|
155
95
|
// Accordion
|
|
@@ -173,17 +113,13 @@ const BASE_COMPONENTS = {
|
|
|
173
113
|
|
|
174
114
|
// Modal
|
|
175
115
|
Modal,
|
|
176
|
-
ModalAction,
|
|
177
116
|
useModal,
|
|
178
117
|
|
|
179
118
|
// Base Navbar
|
|
180
119
|
BaseNavbar,
|
|
181
|
-
NavbarContainer,
|
|
182
120
|
BaseLogo,
|
|
183
121
|
BaseNavMenu,
|
|
184
122
|
NavMenuLink,
|
|
185
|
-
NavToggle,
|
|
186
|
-
NavbarVersion,
|
|
187
123
|
} as const;
|
|
188
124
|
|
|
189
125
|
const APP_COMPONENTS = {
|
|
@@ -216,15 +152,13 @@ export function createComponentsRegistry(): Record<string, unknown> {
|
|
|
216
152
|
export async function createFullComponentsRegistry(): Promise<Record<string, unknown>> {
|
|
217
153
|
const baseComponents = createComponentsRegistry();
|
|
218
154
|
|
|
219
|
-
let mdxComponents: ComponentRegistry = {};
|
|
220
155
|
try {
|
|
221
156
|
const mdx = createMdxComponents() as unknown as ComponentRegistry;
|
|
222
|
-
|
|
223
|
-
} catch {
|
|
224
|
-
|
|
157
|
+
return { ...baseComponents, ...mdx };
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error("Failed to load MDX components:", error);
|
|
160
|
+
return { ...baseComponents };
|
|
225
161
|
}
|
|
226
|
-
|
|
227
|
-
return { ...baseComponents, ...mdxComponents };
|
|
228
162
|
}
|
|
229
163
|
|
|
230
164
|
export const componentsRegistry = createComponentsRegistry();
|
package/.docu/node/build.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { createHash } from "node:crypto";
|
|
|
4
4
|
import { join, dirname } from "node:path";
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { renderToString } from "react-dom/server";
|
|
7
|
-
import { compileMdx } from "./mdx";
|
|
7
|
+
import { compileMdx, getGitLastModifiedBatch } from "./mdx";
|
|
8
8
|
import {
|
|
9
9
|
DOCS_DIR,
|
|
10
10
|
DIST_DIR,
|
|
@@ -83,7 +83,11 @@ async function findMdxFiles(dir: string, baseDir = ""): Promise<{ path: string;
|
|
|
83
83
|
return files;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function
|
|
86
|
+
export function parseConcurrency(): number {
|
|
87
|
+
return Math.max(1, parseInt(process.env.BUILD_CONCURRENCY || "10", 10) || 10);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function shouldRebuild(path: string, mtime: number, cache: BuildCache): boolean {
|
|
87
91
|
const cached = cache[path];
|
|
88
92
|
if (!cached) return true;
|
|
89
93
|
return mtime > cached.builtAt;
|
|
@@ -103,10 +107,15 @@ function htmlShell(title: string, description: string, body: string): string {
|
|
|
103
107
|
});
|
|
104
108
|
}
|
|
105
109
|
|
|
106
|
-
async function renderDocsPage(
|
|
110
|
+
async function renderDocsPage(
|
|
111
|
+
slug: string,
|
|
112
|
+
rawMdx: string,
|
|
113
|
+
filePath: string,
|
|
114
|
+
gitDates?: Map<string, string>
|
|
115
|
+
): Promise<string> {
|
|
107
116
|
let result;
|
|
108
117
|
try {
|
|
109
|
-
result = await compileMdx(rawMdx, filePath);
|
|
118
|
+
result = await compileMdx(rawMdx, filePath, gitDates);
|
|
110
119
|
} catch (err) {
|
|
111
120
|
const msg = err instanceof Error ? err.message : "Unknown MDX error";
|
|
112
121
|
throw new Error(`MDX Error in: docs/${slug}.mdx\n${msg}`, { cause: err });
|
|
@@ -194,7 +203,20 @@ async function build() {
|
|
|
194
203
|
logger.spinner.start("Building pages...");
|
|
195
204
|
t = performance.now();
|
|
196
205
|
|
|
197
|
-
const
|
|
206
|
+
const allRelPaths = mdxFiles
|
|
207
|
+
.map((f) => {
|
|
208
|
+
const mdxPath1 = join(DOCS_DIR, f.path, "index.mdx");
|
|
209
|
+
const mdxPath2 = join(DOCS_DIR, `${f.path}.mdx`);
|
|
210
|
+
const mdxPath3 = join(DOCS_DIR, `${f.path}.md`);
|
|
211
|
+
for (const p of [mdxPath1, mdxPath2, mdxPath3]) {
|
|
212
|
+
if (existsSync(p)) return p.replace(PROJECT_ROOT + "/", "");
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
})
|
|
216
|
+
.filter((p): p is string => p !== null);
|
|
217
|
+
const gitDates = await getGitLastModifiedBatch(allRelPaths);
|
|
218
|
+
|
|
219
|
+
const CONCURRENCY = parseConcurrency();
|
|
198
220
|
const buildTasks = [];
|
|
199
221
|
const errors: string[] = [];
|
|
200
222
|
|
|
@@ -232,7 +254,7 @@ async function build() {
|
|
|
232
254
|
|
|
233
255
|
buildTasks.push(async () => {
|
|
234
256
|
try {
|
|
235
|
-
const html = await renderDocsPage(capturedFile.path, capturedRawMdx, relPath);
|
|
257
|
+
const html = await renderDocsPage(capturedFile.path, capturedRawMdx, relPath, gitDates);
|
|
236
258
|
const outputPath = join(DIST_DIR, "docs", `${capturedFile.path}.html`);
|
|
237
259
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
238
260
|
await writeFile(outputPath, html);
|
|
@@ -258,7 +280,7 @@ async function build() {
|
|
|
258
280
|
const indexMdxPath = join(DOCS_DIR, "index.mdx");
|
|
259
281
|
const indexRaw = await readFile(indexMdxPath, "utf-8");
|
|
260
282
|
const indexRelPath = indexMdxPath.replace(PROJECT_ROOT + "/", "");
|
|
261
|
-
const indexHtml = await renderDocsPage("", indexRaw, indexRelPath);
|
|
283
|
+
const indexHtml = await renderDocsPage("", indexRaw, indexRelPath, gitDates);
|
|
262
284
|
await mkdir(join(DIST_DIR, "docs"), { recursive: true });
|
|
263
285
|
await writeFile(join(DIST_DIR, "docs", "index.html"), indexHtml);
|
|
264
286
|
} catch (err) {
|
package/.docu/node/client.ts
CHANGED
|
@@ -5,16 +5,9 @@ import { createMdxComponents } from "@docubook/mdx-content";
|
|
|
5
5
|
import Sidebar, { MobileBar } from "../components/Sidebar";
|
|
6
6
|
import Toc from "../components/Toc";
|
|
7
7
|
import { ThemeToggle } from "../components/Theme";
|
|
8
|
+
import { safeParseTocs } from "./parse-tocs";
|
|
8
9
|
import type { TocItem } from "./types";
|
|
9
10
|
|
|
10
|
-
function safeParseTocs(raw: string | undefined): TocItem[] {
|
|
11
|
-
try {
|
|
12
|
-
return JSON.parse(raw || "[]");
|
|
13
|
-
} catch {
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
11
|
function mountIsland(
|
|
19
12
|
id: string,
|
|
20
13
|
render: (el: HTMLElement) => React.ReactElement,
|
package/.docu/node/html.ts
CHANGED
|
@@ -29,3 +29,15 @@ export function htmlShell(opts: HtmlShellOptions): string {
|
|
|
29
29
|
</body>
|
|
30
30
|
</html>`;
|
|
31
31
|
}
|
|
32
|
+
|
|
33
|
+
export function hmrScript(nonce: string): string {
|
|
34
|
+
return `<script nonce="${Bun.escapeHTML(nonce)}">
|
|
35
|
+
(function(){
|
|
36
|
+
const es = new EventSource("/__hmr");
|
|
37
|
+
es.onmessage = function(e) {
|
|
38
|
+
if (e.data === "reload") window.location.reload();
|
|
39
|
+
};
|
|
40
|
+
es.onerror = function() { es.close(); setTimeout(() => { window.location.reload(); }, 2000); };
|
|
41
|
+
})();
|
|
42
|
+
</script>`;
|
|
43
|
+
}
|
package/.docu/node/mdx.ts
CHANGED
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
MDXRemote,
|
|
9
9
|
} from "@docubook/core";
|
|
10
10
|
import { createMdxComponents } from "@docubook/mdx-content";
|
|
11
|
-
import { getGitLastModified } from "./utils";
|
|
11
|
+
import { getGitLastModified, getGitLastModifiedBatch } from "./utils";
|
|
12
|
+
|
|
13
|
+
export { getGitLastModifiedBatch };
|
|
12
14
|
|
|
13
15
|
export interface MdxResult {
|
|
14
16
|
content: React.ReactElement;
|
|
@@ -17,7 +19,11 @@ export interface MdxResult {
|
|
|
17
19
|
tocs: ReturnType<typeof extractTocsFromRawMdx>;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
export async function compileMdx(
|
|
22
|
+
export async function compileMdx(
|
|
23
|
+
rawMdx: string,
|
|
24
|
+
filePath: string,
|
|
25
|
+
gitDates?: Map<string, string>
|
|
26
|
+
): Promise<MdxResult> {
|
|
21
27
|
const tocs = extractTocsFromRawMdx(rawMdx);
|
|
22
28
|
const { frontmatter, strippedContent } = extractFrontmatterWithContent<{
|
|
23
29
|
title?: string;
|
|
@@ -40,7 +46,11 @@ export async function compileMdx(rawMdx: string, filePath: string): Promise<MdxR
|
|
|
40
46
|
components,
|
|
41
47
|
});
|
|
42
48
|
|
|
43
|
-
const date =
|
|
49
|
+
const date =
|
|
50
|
+
frontmatter.date ||
|
|
51
|
+
gitDates?.get(filePath) ||
|
|
52
|
+
(await getGitLastModified(filePath)) ||
|
|
53
|
+
undefined;
|
|
44
54
|
|
|
45
55
|
return {
|
|
46
56
|
content,
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { readFile, writeFile, readdir, mkdir } from "node:fs/promises";
|
|
15
15
|
import { resolve, join } from "node:path";
|
|
16
|
+
import { extractFrontmatterWithContent } from "@docubook/core";
|
|
16
17
|
import { DOCS_DIR, ASSETS_DIR, loadDocuConfig } from "./paths";
|
|
17
18
|
|
|
18
19
|
const docuConfig = loadDocuConfig();
|
|
@@ -37,25 +38,6 @@ interface Frontmatter {
|
|
|
37
38
|
description?: string;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
function parseFrontmatter(raw: string): { frontmatter: Frontmatter; content: string } {
|
|
41
|
-
if (!raw.startsWith("---")) return { frontmatter: {}, content: raw };
|
|
42
|
-
const end = raw.indexOf("---", 3);
|
|
43
|
-
if (end < 0) return { frontmatter: {}, content: raw };
|
|
44
|
-
|
|
45
|
-
const fm: Frontmatter = {};
|
|
46
|
-
const lines = raw.slice(3, end).trim().split("\n");
|
|
47
|
-
for (const line of lines) {
|
|
48
|
-
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
49
|
-
if (match) {
|
|
50
|
-
const [, key, value] = match;
|
|
51
|
-
if (key === "title" || key === "description") {
|
|
52
|
-
fm[key] = value.trim();
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return { frontmatter: fm, content: raw.slice(end + 3) };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
41
|
function getSectionTitle(filePath: string): string {
|
|
60
42
|
const parts = filePath.split("/");
|
|
61
43
|
if (parts.length > 1) {
|
|
@@ -74,8 +56,20 @@ function slugify(text: string): string {
|
|
|
74
56
|
.replace(/^-+|-+$/g, "");
|
|
75
57
|
}
|
|
76
58
|
|
|
59
|
+
function stripJsx(content: string): string {
|
|
60
|
+
let result = content;
|
|
61
|
+
let prev = "";
|
|
62
|
+
while (result !== prev) {
|
|
63
|
+
prev = result;
|
|
64
|
+
result = result
|
|
65
|
+
.replace(/<[A-Z][\w.]*[\s\S]*?\/>/g, "")
|
|
66
|
+
.replace(/<[A-Z][\w.]*[\s\S]*?>([\s\S]*?)<\/[A-Z][\w.]*>/g, "$1");
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
77
71
|
function extractRecords(filePath: string, raw: string): SearchRecord[] {
|
|
78
|
-
const { frontmatter, content } =
|
|
72
|
+
const { frontmatter, strippedContent: content } = extractFrontmatterWithContent<Frontmatter>(raw);
|
|
79
73
|
const records: SearchRecord[] = [];
|
|
80
74
|
const url = `/docs/${filePath}`;
|
|
81
75
|
const lvl0 = getSectionTitle(filePath);
|
|
@@ -100,7 +94,8 @@ function extractRecords(filePath: string, raw: string): SearchRecord[] {
|
|
|
100
94
|
});
|
|
101
95
|
}
|
|
102
96
|
|
|
103
|
-
const
|
|
97
|
+
const plainContent = stripJsx(content);
|
|
98
|
+
const lines = plainContent.split("\n");
|
|
104
99
|
let currentParagraph: string[] = [];
|
|
105
100
|
let inCodeBlock = false;
|
|
106
101
|
|
|
@@ -137,7 +132,6 @@ function extractRecords(filePath: string, raw: string): SearchRecord[] {
|
|
|
137
132
|
if (inCodeBlock) continue;
|
|
138
133
|
|
|
139
134
|
if (/^import\s+[\w{*]/.test(trimmed) || /^export\s+[\w{*]/.test(trimmed)) continue;
|
|
140
|
-
if (/^<\/?[A-Z][\w.]*[\s/>]/.test(trimmed) && !/>(.+)<\//.test(trimmed)) continue;
|
|
141
135
|
|
|
142
136
|
const headingMatch = trimmed.match(/^(#{1,6})\s+(.+)$/);
|
|
143
137
|
if (headingMatch) {
|
|
@@ -170,6 +164,8 @@ function extractRecords(filePath: string, raw: string): SearchRecord[] {
|
|
|
170
164
|
continue;
|
|
171
165
|
}
|
|
172
166
|
|
|
167
|
+
if (/^\|.+\|/.test(trimmed)) continue;
|
|
168
|
+
|
|
173
169
|
const cleaned = trimmed
|
|
174
170
|
.replace(/^[-*+]\s+/, "") // list markers
|
|
175
171
|
.replace(/^\d+\.\s+/, "") // ordered list
|
package/.docu/node/server.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { generateSearchIndex } from "./search-indexer";
|
|
|
16
16
|
import { logger } from "./logger";
|
|
17
17
|
import { initSentry, captureException } from "./sentry";
|
|
18
18
|
import { SECURITY_HEADERS, generateNonce, htmlResponse } from "./security";
|
|
19
|
-
import { htmlShell as createHtmlShell } from "./html";
|
|
19
|
+
import { htmlShell as createHtmlShell, hmrScript } from "./html";
|
|
20
20
|
|
|
21
21
|
const docuConfig = loadDocuConfig();
|
|
22
22
|
|
|
@@ -50,18 +50,6 @@ try {
|
|
|
50
50
|
|
|
51
51
|
const hmrClients = new Set<ReadableStreamDefaultController>();
|
|
52
52
|
|
|
53
|
-
function hmrScript(nonce: string): string {
|
|
54
|
-
return `<script nonce="${Bun.escapeHTML(nonce)}">
|
|
55
|
-
(function(){
|
|
56
|
-
const es = new EventSource("/__hmr");
|
|
57
|
-
es.onmessage = function(e) {
|
|
58
|
-
if (e.data === "reload") window.location.reload();
|
|
59
|
-
};
|
|
60
|
-
es.onerror = function() { es.close(); setTimeout(() => { window.location.reload(); }, 2000); };
|
|
61
|
-
})();
|
|
62
|
-
</script>`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
53
|
let hmrTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
66
54
|
const watcher = watch(DOCS_DIR, { recursive: true }, (_event, filename) => {
|
|
67
55
|
if (!filename || (!filename.endsWith(".mdx") && !filename.endsWith(".md"))) return;
|