@docubook/flame 1.7.1 → 2.0.0-alpha.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/Context.tsx +1 -1
- package/.docu/components/Search.tsx +6 -6
- package/.docu/components/Sidebar.tsx +37 -8
- package/.docu/components/Theme.tsx +2 -2
- package/.docu/components/Toc.tsx +51 -5
- package/.docu/components/Typography.tsx +1 -1
- package/.docu/components/home/Hero.tsx +4 -4
- package/.docu/components/registry.ts +1 -1
- package/.docu/lib/build.deno.js +3 -3
- package/.docu/lib/{build.impl-TUVHCOIP.js → build.impl-WR24HDUT.js} +3 -3
- package/.docu/lib/build.node.js +3 -3
- package/.docu/lib/{chunk-ICNXW4Z4.js → chunk-2EYRWRDP.js} +66 -26
- package/.docu/lib/{chunk-PFXUCD2U.js → chunk-AJV2GEDF.js} +5 -4
- package/.docu/lib/{chunk-XMIEMHTQ.js → chunk-KEWRVASF.js} +3 -3
- package/.docu/lib/{chunk-B6LGUADD.js → chunk-QPF4DPZQ.js} +294 -177
- package/.docu/lib/chunk-UISOJ4RW.js +114 -0
- package/.docu/lib/{chunk-QPO4DS5F.js → chunk-VPV7KP7K.js} +56 -9
- package/.docu/lib/{chunk-QL6TQV3S.js → chunk-X7KPVCDN.js} +6 -6
- package/.docu/lib/clean.js +1 -1
- package/.docu/lib/deploy.deno.js +1 -1
- package/.docu/lib/deploy.node.js +1 -1
- package/.docu/lib/preview.deno.js +5 -3
- package/.docu/lib/preview.node.js +5 -3
- package/.docu/lib/server.deno.js +6 -4
- package/.docu/lib/server.node.js +6 -4
- package/.docu/node/build.impl.ts +72 -26
- package/.docu/node/build.ts +67 -17
- package/.docu/node/client.ts +64 -27
- package/.docu/node/deploy.shared.ts +5 -5
- package/.docu/node/deploy.ts +1 -1
- package/.docu/node/hydrate.node.ts +69 -4
- package/.docu/node/hydrate.ts +50 -1
- package/.docu/node/mdx-manifest.d.ts +5 -0
- package/.docu/node/mdx.ts +103 -17
- package/.docu/node/preview.deno.ts +1 -1
- package/.docu/node/preview.impl.ts +3 -3
- package/.docu/node/preview.node.ts +1 -1
- package/.docu/node/preview.ts +2 -2
- package/.docu/node/runtime/bun.ts +28 -0
- package/.docu/node/runtime/deno.ts +34 -0
- package/.docu/node/runtime/index.ts +4 -0
- package/.docu/node/runtime/node.ts +107 -0
- package/.docu/node/runtime/types.ts +19 -0
- package/.docu/node/search-indexer.ts +3 -2
- package/.docu/node/seo.ts +2 -2
- package/.docu/node/server-routes.ts +28 -13
- package/.docu/node/server.deno.ts +1 -1
- package/.docu/node/server.impl.ts +49 -3
- package/.docu/node/server.node.ts +1 -1
- package/.docu/node/server.ts +45 -1
- package/.docu/pages/docs/[[...slug]].tsx +13 -4
- package/.docu/pages/index.tsx +1 -1
- package/.docu/styles/globals.css +29 -20
- package/README.md +7 -48
- package/bin/cli.js +25 -7
- package/package.json +7 -6
- package/template/README.md +7 -48
|
@@ -76,7 +76,7 @@ export function Context({ className }: ContextProps) {
|
|
|
76
76
|
onClick={() => navigate(contextPath)}
|
|
77
77
|
className={cn(
|
|
78
78
|
"relative flex cursor-pointer items-center gap-2 rounded px-2 py-2 text-sm",
|
|
79
|
-
"text-left outline-none
|
|
79
|
+
"text-left transition-colors outline-none",
|
|
80
80
|
isActive
|
|
81
81
|
? "bg-primary/20 text-primary"
|
|
82
82
|
: "hover:bg-base-200 text-base-content/80 hover:text-base-content"
|
|
@@ -154,7 +154,7 @@ export default function Search({ className }: SearchProps) {
|
|
|
154
154
|
boxClassName="w-[calc(100%-2rem)] max-w-[640px] p-0 mx-auto relative z-10"
|
|
155
155
|
>
|
|
156
156
|
<div onKeyDown={handleKeyDown}>
|
|
157
|
-
<div className="px-4 pb-2
|
|
157
|
+
<div className="px-4 pt-4 pb-2">
|
|
158
158
|
<div className="border-base-300 flex items-center gap-3 rounded-lg border px-4 py-2.5">
|
|
159
159
|
<SearchIcon className="text-primary h-5 w-5 shrink-0" />
|
|
160
160
|
<input
|
|
@@ -163,7 +163,7 @@ export default function Search({ className }: SearchProps) {
|
|
|
163
163
|
onChange={handleQueryChange}
|
|
164
164
|
placeholder="Search documentation..."
|
|
165
165
|
autoFocus
|
|
166
|
-
className="placeholder:text-base-content/40 h-8 w-full border-none bg-transparent text-base outline-none ring-0 focus:outline-none
|
|
166
|
+
className="placeholder:text-base-content/40 h-8 w-full border-none bg-transparent text-base ring-0 outline-none focus:ring-0 focus:outline-none"
|
|
167
167
|
aria-label="Search documentation"
|
|
168
168
|
/>
|
|
169
169
|
{query && (
|
|
@@ -187,7 +187,7 @@ export default function Search({ className }: SearchProps) {
|
|
|
187
187
|
</div>
|
|
188
188
|
</div>
|
|
189
189
|
|
|
190
|
-
<div className="max-h-
|
|
190
|
+
<div className="max-h-100 overflow-y-auto px-3 py-2">
|
|
191
191
|
{query.trim().length >= 2 && results.length === 0 && (
|
|
192
192
|
<p className="text-base-content/50 py-8 text-center text-sm">
|
|
193
193
|
No results for “<span className="text-primary">{query}</span>”
|
|
@@ -248,10 +248,10 @@ function SearchTrigger({ onClick }: { onClick: () => void }) {
|
|
|
248
248
|
<SearchIcon className="h-4 w-4 shrink-0" />
|
|
249
249
|
<span className="flex-1 text-left">Search</span>
|
|
250
250
|
<div className="flex items-center gap-0.5">
|
|
251
|
-
<Kbd className="bg-primary text-
|
|
251
|
+
<Kbd className="bg-primary text-primary-foreground rounded-md p-1.5 text-[12px] font-medium">
|
|
252
252
|
<FnKey.Cmd />
|
|
253
253
|
</Kbd>
|
|
254
|
-
<Kbd className="bg-primary text-
|
|
254
|
+
<Kbd className="bg-primary text-primary-foreground rounded-md p-1.5 text-[12px] font-medium">
|
|
255
255
|
K
|
|
256
256
|
</Kbd>
|
|
257
257
|
</div>
|
|
@@ -289,7 +289,7 @@ function GroupedResults({
|
|
|
289
289
|
<div className="flex flex-col gap-1">
|
|
290
290
|
{groups.map((group) => (
|
|
291
291
|
<div key={group.section}>
|
|
292
|
-
<div className="text-base-content/50 px-2 pb-1
|
|
292
|
+
<div className="text-base-content/50 px-2 pt-3 pb-1 text-xs font-semibold">
|
|
293
293
|
{group.section}
|
|
294
294
|
</div>
|
|
295
295
|
{group.items.map(({ result, globalIndex }) => {
|
|
@@ -92,13 +92,21 @@ export function MobileBar({
|
|
|
92
92
|
useEffect(() => {
|
|
93
93
|
if (!tocs.length) return;
|
|
94
94
|
|
|
95
|
+
// Match the CSS scroll-margin-top (scroll-mt-54 = 216px) that anchor
|
|
96
|
+
// jumps rest at, so the highlight agrees with where a jump lands.
|
|
97
|
+
const firstEl = document.getElementById(tocs[0].href.slice(1));
|
|
98
|
+
const offset = firstEl ? parseFloat(getComputedStyle(firstEl).scrollMarginTop) || 100 : 100;
|
|
99
|
+
|
|
95
100
|
const handleScroll = () => {
|
|
96
101
|
let currentId: string | null = null;
|
|
97
102
|
for (const toc of tocs) {
|
|
98
103
|
const id = toc.href.slice(1);
|
|
99
104
|
const el = document.getElementById(id);
|
|
100
105
|
if (!el) continue;
|
|
101
|
-
|
|
106
|
+
// Round: smooth anchor scrolls land at sub-pixel positions (e.g.
|
|
107
|
+
// 216.4px), which would otherwise fail the <= offset comparison and
|
|
108
|
+
// leave the previous heading highlighted.
|
|
109
|
+
if (Math.round(el.getBoundingClientRect().top) <= offset) {
|
|
102
110
|
currentId = id;
|
|
103
111
|
} else {
|
|
104
112
|
break;
|
|
@@ -109,7 +117,13 @@ export function MobileBar({
|
|
|
109
117
|
|
|
110
118
|
handleScroll();
|
|
111
119
|
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
112
|
-
|
|
120
|
+
// Smooth anchor scrolls may not emit a trailing scroll event at the
|
|
121
|
+
// target position — recompute once the animation actually settles.
|
|
122
|
+
window.addEventListener("scrollend", handleScroll, { passive: true });
|
|
123
|
+
return () => {
|
|
124
|
+
window.removeEventListener("scroll", handleScroll);
|
|
125
|
+
window.removeEventListener("scrollend", handleScroll);
|
|
126
|
+
};
|
|
113
127
|
}, [tocs]);
|
|
114
128
|
|
|
115
129
|
useEffect(() => {
|
|
@@ -192,7 +206,10 @@ export function MobileBar({
|
|
|
192
206
|
<MobileTocList
|
|
193
207
|
tocs={tocs}
|
|
194
208
|
activeId={activeId}
|
|
195
|
-
onSelect={() =>
|
|
209
|
+
onSelect={(id) => {
|
|
210
|
+
setTocExpanded(false);
|
|
211
|
+
setActiveId(id);
|
|
212
|
+
}}
|
|
196
213
|
/>
|
|
197
214
|
) : (
|
|
198
215
|
<div className="flex flex-col items-center py-6 text-center">
|
|
@@ -216,7 +233,7 @@ function MobileTocList({
|
|
|
216
233
|
}: {
|
|
217
234
|
tocs: TocItem[];
|
|
218
235
|
activeId: string | null;
|
|
219
|
-
onSelect: () => void;
|
|
236
|
+
onSelect: (id: string) => void;
|
|
220
237
|
}) {
|
|
221
238
|
return (
|
|
222
239
|
<ul className="flex flex-col gap-0.5">
|
|
@@ -230,9 +247,21 @@ function MobileTocList({
|
|
|
230
247
|
href={toc.href}
|
|
231
248
|
onClick={(e) => {
|
|
232
249
|
e.preventDefault();
|
|
233
|
-
onSelect();
|
|
234
|
-
|
|
235
|
-
|
|
250
|
+
onSelect(id);
|
|
251
|
+
history.replaceState(null, "", toc.href);
|
|
252
|
+
const targetId = id;
|
|
253
|
+
// Wait for the bar collapse re-render so the measurement is
|
|
254
|
+
// accurate, then jump with the same offset the observer uses
|
|
255
|
+
// (scrollIntoView ignores scroll-margin-top on iOS Safari).
|
|
256
|
+
requestAnimationFrame(() => {
|
|
257
|
+
const el = document.getElementById(targetId);
|
|
258
|
+
if (!el) return;
|
|
259
|
+
const margin = parseFloat(getComputedStyle(el).scrollMarginTop) || 100;
|
|
260
|
+
window.scrollTo({
|
|
261
|
+
top: el.getBoundingClientRect().top + window.scrollY - margin,
|
|
262
|
+
behavior: "smooth",
|
|
263
|
+
});
|
|
264
|
+
});
|
|
236
265
|
}}
|
|
237
266
|
className={cn(
|
|
238
267
|
"block rounded px-2 py-1.5 text-sm transition-colors",
|
|
@@ -276,7 +305,7 @@ function MobileDrawer({
|
|
|
276
305
|
return (
|
|
277
306
|
<div className="fixed inset-0 z-[100]">
|
|
278
307
|
<div className="absolute inset-0 bg-black/40" onClick={onClose} aria-hidden="true" />
|
|
279
|
-
<div className="bg-base-100 absolute
|
|
308
|
+
<div className="bg-base-100 absolute top-0 right-0 flex h-full w-80 max-w-[85vw] flex-col shadow-xl">
|
|
280
309
|
<div className="border-base-200 flex items-center justify-between border-b px-4 py-3">
|
|
281
310
|
<div className="flex items-center gap-2">
|
|
282
311
|
<GitHubLink repoUrl={repoUrl} />
|
|
@@ -24,7 +24,7 @@ export function ThemeToggle({ className }: ThemeToggleProps) {
|
|
|
24
24
|
>
|
|
25
25
|
<Sun
|
|
26
26
|
className={`h-4 w-4 transition-colors ${
|
|
27
|
-
!isDark ? "text-
|
|
27
|
+
!isDark ? "text-primary-foreground" : "text-foreground"
|
|
28
28
|
}`}
|
|
29
29
|
/>
|
|
30
30
|
</button>
|
|
@@ -38,7 +38,7 @@ export function ThemeToggle({ className }: ThemeToggleProps) {
|
|
|
38
38
|
>
|
|
39
39
|
<Moon
|
|
40
40
|
className={`h-4 w-4 transition-colors ${
|
|
41
|
-
isDark ? "text-foreground" : "text-base-content/60"
|
|
41
|
+
isDark ? "text-primary-foreground" : "text-base-content/60"
|
|
42
42
|
}`}
|
|
43
43
|
/>
|
|
44
44
|
</button>
|
package/.docu/components/Toc.tsx
CHANGED
|
@@ -20,13 +20,29 @@ export default function Toc({ tocs }: TocProps) {
|
|
|
20
20
|
activeIdRef.current = activeId;
|
|
21
21
|
}, [activeId]);
|
|
22
22
|
|
|
23
|
+
// Single source of truth for the anchor offset — used by BOTH the scroll
|
|
24
|
+
// observer and the TOC click scroll so they can never disagree. Reads the
|
|
25
|
+
// CSS scroll-margin-top (prose-headings:scroll-mt-4 desktop /
|
|
26
|
+
// scroll-mt-16 mobile) so a change in one place updates both, and the
|
|
27
|
+
// heading lands exactly where the browser would put a native anchor jump.
|
|
28
|
+
function getAnchorOffset(): number {
|
|
29
|
+
if (typeof window === "undefined") return 100;
|
|
30
|
+
const first = tocs[0] ? document.getElementById(tocs[0].href.slice(1)) : null;
|
|
31
|
+
const margin = first ? parseFloat(getComputedStyle(first).scrollMarginTop) : 0;
|
|
32
|
+
return margin || 100;
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
useEffect(() => {
|
|
24
|
-
|
|
36
|
+
// Desktop-only TOC — on mobile this island is display:none but still
|
|
37
|
+
// mounted; letting its observer run would hijack the URL hash while the
|
|
38
|
+
// mobile bar owns TOC behavior there.
|
|
39
|
+
if (typeof window === "undefined" || window.innerWidth < 1024) return;
|
|
40
|
+
if (!tocs.length) return;
|
|
25
41
|
|
|
26
42
|
const isDesktop = window.innerWidth >= 1024;
|
|
27
43
|
const container = isDesktop ? document.getElementById("scroll-container") : null;
|
|
28
44
|
const scrollTarget = container || window;
|
|
29
|
-
const offset =
|
|
45
|
+
const offset = getAnchorOffset();
|
|
30
46
|
|
|
31
47
|
const handleScroll = () => {
|
|
32
48
|
if (clickedIdRef.current) return;
|
|
@@ -101,7 +117,7 @@ export default function Toc({ tocs }: TocProps) {
|
|
|
101
117
|
|
|
102
118
|
<div className="relative">
|
|
103
119
|
<div className="relative text-sm">
|
|
104
|
-
<div className="bg-base-300 absolute
|
|
120
|
+
<div className="bg-base-300 absolute top-0 left-0 h-full w-px" />
|
|
105
121
|
|
|
106
122
|
<div className="flex flex-col">
|
|
107
123
|
{tocs.map(({ href, level, text }) => {
|
|
@@ -143,7 +159,37 @@ export default function Toc({ tocs }: TocProps) {
|
|
|
143
159
|
e.preventDefault();
|
|
144
160
|
handleLinkClick(id);
|
|
145
161
|
const el = document.getElementById(id);
|
|
146
|
-
if (el)
|
|
162
|
+
if (el) {
|
|
163
|
+
// Manual scroll with the same offset the observer uses —
|
|
164
|
+
// scrollIntoView ignores scroll-margin-top on iOS Safari
|
|
165
|
+
// window-scroll, landing the heading under the sticky
|
|
166
|
+
// mobile bar.
|
|
167
|
+
const offset = getAnchorOffset();
|
|
168
|
+
const scroller =
|
|
169
|
+
window.innerWidth >= 1024
|
|
170
|
+
? document.getElementById("scroll-container")
|
|
171
|
+
: null;
|
|
172
|
+
const elTop = el.getBoundingClientRect().top;
|
|
173
|
+
if (scroller) {
|
|
174
|
+
// Container scroll space: element offset within the
|
|
175
|
+
// container minus the anchor offset (the container
|
|
176
|
+
// itself may sit below the viewport top, e.g. under
|
|
177
|
+
// the navbar).
|
|
178
|
+
scroller.scrollTo({
|
|
179
|
+
top:
|
|
180
|
+
elTop -
|
|
181
|
+
scroller.getBoundingClientRect().top +
|
|
182
|
+
scroller.scrollTop -
|
|
183
|
+
offset,
|
|
184
|
+
behavior: "smooth",
|
|
185
|
+
});
|
|
186
|
+
} else {
|
|
187
|
+
window.scrollTo({
|
|
188
|
+
top: elTop + window.scrollY - offset,
|
|
189
|
+
behavior: "smooth",
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
147
193
|
}}
|
|
148
194
|
className={cn(
|
|
149
195
|
"flex flex-1 items-center py-2 transition-all duration-200",
|
|
@@ -153,7 +199,7 @@ export default function Toc({ tocs }: TocProps) {
|
|
|
153
199
|
)}
|
|
154
200
|
style={{ paddingLeft: `${levelPadding + 6}px` }}
|
|
155
201
|
>
|
|
156
|
-
<span className="line-clamp-2 break-words
|
|
202
|
+
<span className="line-clamp-2 text-sm break-words">{text}</span>
|
|
157
203
|
</a>
|
|
158
204
|
</div>
|
|
159
205
|
);
|
|
@@ -2,7 +2,7 @@ import { PropsWithChildren } from "react";
|
|
|
2
2
|
|
|
3
3
|
export function Typography({ children }: PropsWithChildren) {
|
|
4
4
|
return (
|
|
5
|
-
<div className="prose prose-zinc dark:prose-invert prose-code:font-code dark:prose-code:bg-stone-900/25 prose-code:bg-stone-50 prose-pre:bg-background max-lg:prose-headings:scroll-mt-
|
|
5
|
+
<div className="prose prose-zinc dark:prose-invert prose-code:font-code dark:prose-code:bg-stone-900/25 prose-code:bg-stone-50 prose-pre:bg-background max-lg:prose-headings:scroll-mt-16 prose-headings:scroll-mt-4 prose-code:text-sm prose-code:leading-6 dark:prose-code:text-white prose-code:text-stone-800 prose-code:p-1 prose-code:rounded-md prose-code:border prose-img:rounded-md prose-img:border prose-code:before:content-none prose-code:after:content-none prose-code:px-1.5 prose-code:overflow-x-auto prose-img:my-3 prose-h2:my-4 prose-h2:mt-8 w-full max-w-full! pt-2">
|
|
6
6
|
{children}
|
|
7
7
|
</div>
|
|
8
8
|
);
|
|
@@ -34,8 +34,8 @@ function renderActionButtonIcon(iconName: string | undefined, className?: string
|
|
|
34
34
|
|
|
35
35
|
function ActionButton({ action }: ActionButtonProps) {
|
|
36
36
|
const themeClasses = {
|
|
37
|
-
primary: "bg-primary text-primary-
|
|
38
|
-
secondary: "bg-secondary text-secondary-
|
|
37
|
+
primary: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
38
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/90",
|
|
39
39
|
ghost: "bg-transparent text-muted-foreground border border-base-300 hover:bg-base-200",
|
|
40
40
|
};
|
|
41
41
|
|
|
@@ -64,11 +64,11 @@ export function Hero({ hero, className }: HeroProps) {
|
|
|
64
64
|
<div className={cn("mx-auto max-w-4xl px-6 py-32 sm:py-44", className)}>
|
|
65
65
|
<div className="text-center">
|
|
66
66
|
{tagline && <p className="text-primary mb-4 text-lg font-semibold">{tagline}</p>}
|
|
67
|
-
<h1 className="text-
|
|
67
|
+
<h1 className="text-5xl font-semibold tracking-tight text-balance sm:text-7xl">
|
|
68
68
|
{headline}
|
|
69
69
|
</h1>
|
|
70
70
|
{description && (
|
|
71
|
-
<p className="text-muted-foreground mt-8 text-
|
|
71
|
+
<p className="text-muted-foreground mt-8 text-lg text-pretty sm:text-xl">{description}</p>
|
|
72
72
|
)}
|
|
73
73
|
{actions && actions.length > 0 && (
|
|
74
74
|
<div className="mt-10 flex flex-wrap items-center justify-center gap-4">
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentType } from "react";
|
|
2
|
-
import { createMdxComponents } from "@docubook/
|
|
2
|
+
import { createMdxComponents } from "@docubook/markdown";
|
|
3
3
|
import { Dropdown, DropdownItem, DropdownLink } from "@docubook/ui-react/dropdown";
|
|
4
4
|
import { PaginationDocs } from "@docubook/ui-react/pagination";
|
|
5
5
|
import { Toggle, ToggleGroup } from "@docubook/ui-react/toggle";
|
package/.docu/lib/build.deno.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runBuildCli
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-2EYRWRDP.js";
|
|
4
|
+
import "./chunk-QPF4DPZQ.js";
|
|
5
5
|
import "./chunk-EOK6KATZ.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-AJV2GEDF.js";
|
|
7
7
|
import "./chunk-4IQXHHPF.js";
|
|
8
8
|
|
|
9
9
|
// .docu/node/build.deno.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runBuild,
|
|
3
3
|
runBuildCli
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-2EYRWRDP.js";
|
|
5
|
+
import "./chunk-QPF4DPZQ.js";
|
|
6
6
|
import "./chunk-EOK6KATZ.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-AJV2GEDF.js";
|
|
8
8
|
import "./chunk-4IQXHHPF.js";
|
|
9
9
|
export {
|
|
10
10
|
runBuild,
|
package/.docu/lib/build.node.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runBuildCli
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-2EYRWRDP.js";
|
|
4
|
+
import "./chunk-QPF4DPZQ.js";
|
|
5
5
|
import "./chunk-EOK6KATZ.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-AJV2GEDF.js";
|
|
7
7
|
import "./chunk-4IQXHHPF.js";
|
|
8
8
|
|
|
9
9
|
// .docu/node/build.node.ts
|
|
@@ -7,13 +7,15 @@ import {
|
|
|
7
7
|
buildClientBundle,
|
|
8
8
|
captureException,
|
|
9
9
|
compileMdx,
|
|
10
|
+
compileMdxModule,
|
|
10
11
|
computeInlineThemeCss,
|
|
12
|
+
frontmatterField,
|
|
11
13
|
generateSearchIndex,
|
|
12
14
|
getGitLastModifiedBatch,
|
|
13
15
|
htmlShell,
|
|
14
16
|
initSentry,
|
|
15
17
|
loadPlugins
|
|
16
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-QPF4DPZQ.js";
|
|
17
19
|
import {
|
|
18
20
|
cspHeader,
|
|
19
21
|
generateNonce,
|
|
@@ -21,7 +23,7 @@ import {
|
|
|
21
23
|
} from "./chunk-EOK6KATZ.js";
|
|
22
24
|
import {
|
|
23
25
|
logger
|
|
24
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-AJV2GEDF.js";
|
|
25
27
|
import {
|
|
26
28
|
ASSETS_DIR,
|
|
27
29
|
CACHE_FILE,
|
|
@@ -48,7 +50,7 @@ function buildSeoMeta(config, frontmatter, slug) {
|
|
|
48
50
|
url,
|
|
49
51
|
siteName: config.meta?.title || ""
|
|
50
52
|
};
|
|
51
|
-
const image =
|
|
53
|
+
const image = frontmatterField(frontmatter, "image") || config.meta?.ogImage;
|
|
52
54
|
if (image) {
|
|
53
55
|
try {
|
|
54
56
|
result.image = new URL(image, image.startsWith("/") ? baseURL : `${baseURL}/docs/`).href;
|
|
@@ -121,8 +123,8 @@ ${msg}`, { cause: err });
|
|
|
121
123
|
content
|
|
122
124
|
});
|
|
123
125
|
}
|
|
124
|
-
const title = (
|
|
125
|
-
const description =
|
|
126
|
+
const title = frontmatterField(frontmatter, "title") || slug || "Docs";
|
|
127
|
+
const description = frontmatterField(frontmatter, "description");
|
|
126
128
|
const slugParts = slug ? slug.split("/") : [];
|
|
127
129
|
const page = React.createElement(
|
|
128
130
|
DocsLayout,
|
|
@@ -131,12 +133,15 @@ ${msg}`, { cause: err });
|
|
|
131
133
|
slug: slugParts,
|
|
132
134
|
title,
|
|
133
135
|
description,
|
|
134
|
-
date: frontmatter
|
|
135
|
-
content:
|
|
136
|
+
date: frontmatterField(frontmatter, "date") || void 0,
|
|
137
|
+
// Render MDX content as its own root: client hydrates the island as a
|
|
138
|
+
// separate root, so SSR must be root-relative too or useId-based ids
|
|
139
|
+
// (mdx-compiler components) mismatch during hydration.
|
|
140
|
+
content: renderToString(result.content),
|
|
136
141
|
tocs: result.tocs,
|
|
137
142
|
filePath,
|
|
138
143
|
repoUrl: docuConfig.repo?.url,
|
|
139
|
-
|
|
144
|
+
mdxSlug: slug
|
|
140
145
|
})
|
|
141
146
|
);
|
|
142
147
|
const body = renderToString(page);
|
|
@@ -146,7 +151,7 @@ ${msg}`, { cause: err });
|
|
|
146
151
|
const depth = slug ? slug.split("/").length : 1;
|
|
147
152
|
const favicon = docuConfig.meta?.favicon || "/docs/assets/images/favicon.ico";
|
|
148
153
|
const seo = buildSeoMeta(docuConfig, frontmatter, slug || "");
|
|
149
|
-
const csp = cspHeader(nonce
|
|
154
|
+
const csp = cspHeader(nonce);
|
|
150
155
|
let html = htmlShell({
|
|
151
156
|
title,
|
|
152
157
|
description,
|
|
@@ -200,9 +205,55 @@ async function runBuild() {
|
|
|
200
205
|
const cache = args.force ? {} : await readCache();
|
|
201
206
|
let built = 0;
|
|
202
207
|
let skipped = 0;
|
|
208
|
+
const pluginsConfig = docuConfig.plugins ?? [];
|
|
209
|
+
const builder = pluginsConfig.length > 0 ? new BuildPluginBuilder(docuConfig) : null;
|
|
210
|
+
if (builder) {
|
|
211
|
+
const plugins = await loadPlugins(pluginsConfig);
|
|
212
|
+
for (const plugin of plugins) {
|
|
213
|
+
await plugin.setup(builder);
|
|
214
|
+
}
|
|
215
|
+
await builder.runOnStart();
|
|
216
|
+
}
|
|
217
|
+
const mdxSources = {};
|
|
218
|
+
const prePassTasks = mdxFiles.map(async (file) => {
|
|
219
|
+
let raw;
|
|
220
|
+
try {
|
|
221
|
+
raw = await readFile(file.absPath, "utf-8");
|
|
222
|
+
} catch {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
let content = raw;
|
|
226
|
+
if (builder) {
|
|
227
|
+
const relPath = file.absPath.replace(PROJECT_ROOT + "/", "");
|
|
228
|
+
const transformed = await builder.runOnLoad(relPath, content);
|
|
229
|
+
if (transformed?.contents) content = transformed.contents;
|
|
230
|
+
}
|
|
231
|
+
const remarkPlugins = builder?.collectRemarkPlugins();
|
|
232
|
+
const rehypePlugins = builder?.collectRehypePlugins();
|
|
233
|
+
mdxSources[file.path] = await compileMdxModule(content, remarkPlugins, rehypePlugins);
|
|
234
|
+
});
|
|
235
|
+
await Promise.all(prePassTasks);
|
|
236
|
+
const indexMdxPath = join(DOCS_DIR, "index.mdx");
|
|
237
|
+
if (existsSync(indexMdxPath)) {
|
|
238
|
+
try {
|
|
239
|
+
const indexRaw = await readFile(indexMdxPath, "utf-8");
|
|
240
|
+
let indexContent = indexRaw;
|
|
241
|
+
if (builder) {
|
|
242
|
+
const relPath = indexMdxPath.replace(PROJECT_ROOT + "/", "");
|
|
243
|
+
const transformed = await builder.runOnLoad(relPath, indexContent);
|
|
244
|
+
if (transformed?.contents) indexContent = transformed.contents;
|
|
245
|
+
}
|
|
246
|
+
mdxSources[""] = await compileMdxModule(
|
|
247
|
+
indexContent,
|
|
248
|
+
builder?.collectRemarkPlugins(),
|
|
249
|
+
builder?.collectRehypePlugins()
|
|
250
|
+
);
|
|
251
|
+
} catch {
|
|
252
|
+
}
|
|
253
|
+
}
|
|
203
254
|
logger.bundleStart();
|
|
204
255
|
let t = performance.now();
|
|
205
|
-
assetManifest = await buildClientBundle();
|
|
256
|
+
assetManifest = await buildClientBundle(mdxSources);
|
|
206
257
|
logger.bundleDone(Math.round(performance.now() - t));
|
|
207
258
|
inlineThemeCss = computeInlineThemeCss();
|
|
208
259
|
const lastManifest = cache["__assets__"];
|
|
@@ -214,15 +265,6 @@ async function runBuild() {
|
|
|
214
265
|
builtAt: Date.now()
|
|
215
266
|
};
|
|
216
267
|
}
|
|
217
|
-
const pluginsConfig = docuConfig.plugins ?? [];
|
|
218
|
-
const builder = pluginsConfig.length > 0 ? new BuildPluginBuilder(docuConfig) : null;
|
|
219
|
-
if (builder) {
|
|
220
|
-
const plugins = await loadPlugins(pluginsConfig);
|
|
221
|
-
for (const plugin of plugins) {
|
|
222
|
-
await plugin.setup(builder);
|
|
223
|
-
}
|
|
224
|
-
await builder.runOnStart();
|
|
225
|
-
}
|
|
226
268
|
logger.spinner.start("Building pages...");
|
|
227
269
|
t = performance.now();
|
|
228
270
|
const allRelPaths = mdxFiles.map((f) => f.absPath.replace(PROJECT_ROOT + "/", ""));
|
|
@@ -301,9 +343,9 @@ async function runBuild() {
|
|
|
301
343
|
await Promise.all(buildTasks.slice(i, i + CONCURRENCY).map((fn) => fn()));
|
|
302
344
|
}
|
|
303
345
|
try {
|
|
304
|
-
const
|
|
305
|
-
const indexRaw = await readFile(
|
|
306
|
-
const indexRelPath =
|
|
346
|
+
const indexMdxPath2 = join(DOCS_DIR, "index.mdx");
|
|
347
|
+
const indexRaw = await readFile(indexMdxPath2, "utf-8");
|
|
348
|
+
const indexRelPath = indexMdxPath2.replace(PROJECT_ROOT + "/", "");
|
|
307
349
|
const indexHtml = await renderDocsPage(
|
|
308
350
|
docuConfig,
|
|
309
351
|
"",
|
|
@@ -332,8 +374,7 @@ async function runBuild() {
|
|
|
332
374
|
body: renderToString(landingPage),
|
|
333
375
|
favicon: landingFavicon,
|
|
334
376
|
seo: landingSeo,
|
|
335
|
-
|
|
336
|
-
csp: cspHeader(landingNonce, true),
|
|
377
|
+
csp: cspHeader(landingNonce),
|
|
337
378
|
css: assetManifest.css,
|
|
338
379
|
js: assetManifest.js,
|
|
339
380
|
nonce: landingNonce,
|
|
@@ -353,8 +394,7 @@ async function runBuild() {
|
|
|
353
394
|
body: renderToString(notFoundPage),
|
|
354
395
|
favicon: notFoundFavicon,
|
|
355
396
|
headExtra: ['<meta name="robots" content="noindex,follow">'],
|
|
356
|
-
|
|
357
|
-
csp: cspHeader(notFoundNonce, true),
|
|
397
|
+
csp: cspHeader(notFoundNonce),
|
|
358
398
|
css: assetManifest.css,
|
|
359
399
|
js: assetManifest.js,
|
|
360
400
|
nonce: notFoundNonce,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
// package.json
|
|
6
6
|
var package_default = {
|
|
7
7
|
name: "@docubook/flame",
|
|
8
|
-
version: "
|
|
8
|
+
version: "2.0.0-alpha.0",
|
|
9
9
|
description: "A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.",
|
|
10
10
|
type: "module",
|
|
11
11
|
bin: {
|
|
@@ -62,10 +62,10 @@ var package_default = {
|
|
|
62
62
|
license: "MIT",
|
|
63
63
|
dependencies: {
|
|
64
64
|
"@docubook/core": "workspace:^",
|
|
65
|
-
"@docubook/
|
|
66
|
-
"@docubook/runt": "workspace:^",
|
|
65
|
+
"@docubook/markdown": "workspace:^",
|
|
67
66
|
"@docubook/themes-colors": "workspace:^",
|
|
68
67
|
"@docubook/ui-react": "workspace:^",
|
|
68
|
+
"@mdx-js/react": "^3.0.1",
|
|
69
69
|
"@tailwindcss/cli": "4.3.0",
|
|
70
70
|
"@tailwindcss/typography": "0.5.16",
|
|
71
71
|
"bun-plugin-tailwind": "^0.1.2",
|
|
@@ -74,7 +74,8 @@ var package_default = {
|
|
|
74
74
|
"lucide-react": "^1.14.0",
|
|
75
75
|
react: "^19.2.7",
|
|
76
76
|
"react-dom": "^19.2.7",
|
|
77
|
-
unified: "^11.0.0"
|
|
77
|
+
unified: "^11.0.0",
|
|
78
|
+
zod: "^4.4.3"
|
|
78
79
|
},
|
|
79
80
|
peerDependencies: {
|
|
80
81
|
"@sentry/bun": "^10.0.0"
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-EOK6KATZ.js";
|
|
8
8
|
import {
|
|
9
9
|
logger
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-AJV2GEDF.js";
|
|
11
11
|
import {
|
|
12
12
|
DIST_DIR
|
|
13
13
|
} from "./chunk-4IQXHHPF.js";
|
|
@@ -58,7 +58,7 @@ async function runPreview(adapter) {
|
|
|
58
58
|
headers: {
|
|
59
59
|
"Content-Type": "text/html",
|
|
60
60
|
...SECURITY_HEADERS,
|
|
61
|
-
"Content-Security-Policy": cspHeader(nonce
|
|
61
|
+
"Content-Security-Policy": cspHeader(nonce)
|
|
62
62
|
}
|
|
63
63
|
});
|
|
64
64
|
}
|
|
@@ -75,7 +75,7 @@ async function runPreview(adapter) {
|
|
|
75
75
|
headers: {
|
|
76
76
|
"Content-Type": "text/html",
|
|
77
77
|
...SECURITY_HEADERS,
|
|
78
|
-
"Content-Security-Policy": cspHeader(nonce
|
|
78
|
+
"Content-Security-Policy": cspHeader(nonce)
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
81
|
}
|