@docubook/flame 2.0.3 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.docu/components/Context.tsx +6 -5
- package/.docu/components/DocsLayout.tsx +7 -4
- package/.docu/components/Menu.tsx +10 -9
- package/.docu/components/Navbar.tsx +24 -22
- package/.docu/components/Pagination.tsx +3 -2
- package/.docu/components/Search.tsx +6 -1
- package/.docu/components/Sidebar.tsx +22 -21
- package/.docu/components/Sublink.tsx +3 -3
- package/.docu/lib/build.deno.js +1 -1
- package/.docu/lib/build.impl-DxI2YYqL.js +2 -0
- package/.docu/lib/{build.impl-BSt6Vikt.js → build.impl-oTVkE6D9.js} +60 -31
- package/.docu/lib/build.impl-oTVkE6D9.js.map +1 -0
- package/.docu/lib/build.node.js +1 -1
- 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/{deploy.shared-D6S_FE7d.js → deploy.shared-p8UHVvAw.js} +27 -8
- package/.docu/lib/deploy.shared-p8UHVvAw.js.map +1 -0
- package/.docu/lib/{html.shared-CsEFmy_Y.js → html.shared-RPCM1y5Q.js} +264 -73
- package/.docu/lib/html.shared-RPCM1y5Q.js.map +1 -0
- package/.docu/lib/{logger-DFKQUy8M.js → logger-NdMEl8u7.js} +3 -3
- package/.docu/lib/{logger-DFKQUy8M.js.map → logger-NdMEl8u7.js.map} +1 -1
- package/.docu/lib/paths-CHXABfXa.js +332 -0
- package/.docu/lib/paths-CHXABfXa.js.map +1 -0
- package/.docu/lib/preview.deno.js +1 -1
- package/.docu/lib/{preview.impl-BfESMUM7.js → preview.impl-CLHIPgry.js} +4 -4
- package/.docu/lib/preview.impl-CLHIPgry.js.map +1 -0
- package/.docu/lib/preview.node.js +1 -1
- package/.docu/lib/{utils-B_CoyKie.js → server-utils-BYWB7A6T.js} +3 -21
- package/.docu/lib/server-utils-BYWB7A6T.js.map +1 -0
- package/.docu/lib/server.deno.js +1 -1
- package/.docu/lib/{server.impl-CZ4eMe5L.js → server.impl-C4t3Xqb5.js} +24 -19
- package/.docu/lib/server.impl-C4t3Xqb5.js.map +1 -0
- package/.docu/lib/server.node.js +1 -1
- package/.docu/node/base-path.ts +306 -0
- package/.docu/node/build.impl.ts +50 -17
- package/.docu/node/build.ts +50 -17
- package/.docu/node/cache-key.ts +92 -4
- package/.docu/node/client-routes.ts +16 -1
- package/.docu/node/deploy.shared.ts +51 -15
- package/.docu/node/deploy.ts +7 -3
- package/.docu/node/html.shared.ts +33 -3
- package/.docu/node/html.ts +18 -3
- package/.docu/node/hydrate.node.ts +6 -0
- package/.docu/node/hydrate.ts +16 -1
- package/.docu/node/mdx.ts +102 -23
- package/.docu/node/paths.ts +115 -0
- package/.docu/node/preview.impl.ts +1 -1
- package/.docu/node/preview.ts +1 -1
- package/.docu/node/search-indexer.ts +20 -3
- package/.docu/node/seo.ts +41 -7
- package/.docu/node/server-routes.ts +34 -11
- package/.docu/node/server-utils.ts +152 -0
- package/.docu/node/server.impl.ts +22 -12
- package/.docu/node/server.ts +23 -27
- package/.docu/node/types.ts +13 -0
- package/.docu/node/utils.ts +56 -149
- package/.docu/pages/404.tsx +2 -1
- package/.docu/pages/index.tsx +6 -13
- package/bin/cli.js +17 -21
- package/docu.schema.json +10 -1
- package/package.json +5 -5
- package/template/docu.json +1 -0
- package/.docu/lib/build.impl-BSt6Vikt.js.map +0 -1
- package/.docu/lib/build.impl-xNDEh8DJ.js +0 -2
- package/.docu/lib/deploy.shared-D6S_FE7d.js.map +0 -1
- package/.docu/lib/html.shared-CsEFmy_Y.js.map +0 -1
- package/.docu/lib/paths-Cbz-dj4s.js +0 -72
- package/.docu/lib/paths-Cbz-dj4s.js.map +0 -1
- package/.docu/lib/preview.impl-BfESMUM7.js.map +0 -1
- package/.docu/lib/server.impl-CZ4eMe5L.js.map +0 -1
- package/.docu/lib/utils-B_CoyKie.js.map +0 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { cn, docsHtmlHref } from "../node/utils";
|
|
5
5
|
import { Dropdown, DropdownItem } from "@docubook/ui-react/dropdown";
|
|
6
|
-
import { routes, config as docuConfig } from "../node/client-routes";
|
|
6
|
+
import { routes, config as docuConfig, basePath } from "../node/client-routes";
|
|
7
7
|
import { ChevronsUpDown, Check } from "lucide-react";
|
|
8
8
|
import { renderLucideIcon } from "./Lucide";
|
|
9
9
|
|
|
@@ -21,7 +21,7 @@ function getFirstItemHref(route: { href: string; items?: { href: string }[] }):
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function getActiveContextRoute(path: string) {
|
|
24
|
-
const docPath = path.replace(
|
|
24
|
+
const docPath = basePath.length > 0 ? path.replace(new RegExp(`^${basePath}`), "") : path;
|
|
25
25
|
return getContextRoutes().find((route) => docPath.startsWith(route.href));
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -33,12 +33,13 @@ export function Context({ className }: ContextProps) {
|
|
|
33
33
|
|
|
34
34
|
const mode = docuConfig.sidebar?.context || "dropdown";
|
|
35
35
|
if (mode === "separator") return null;
|
|
36
|
-
const
|
|
36
|
+
const isDocsPath = basePath.length === 0 || pathname.startsWith(basePath);
|
|
37
|
+
const activeRoute = isDocsPath ? getActiveContextRoute(pathname) : undefined;
|
|
37
38
|
const contextRoutes = getContextRoutes();
|
|
38
39
|
const fallbackRoute = routes[0];
|
|
39
40
|
const displayRoute = activeRoute || fallbackRoute;
|
|
40
41
|
|
|
41
|
-
if (!mounted || !
|
|
42
|
+
if (!mounted || !isDocsPath || contextRoutes.length === 0) {
|
|
42
43
|
return null;
|
|
43
44
|
}
|
|
44
45
|
|
|
@@ -68,7 +69,7 @@ export function Context({ className }: ContextProps) {
|
|
|
68
69
|
{contextRoutes.map((route) => {
|
|
69
70
|
const isActive = activeRoute?.href === route.href;
|
|
70
71
|
const firstItemPath = getFirstItemHref(route);
|
|
71
|
-
const contextPath = docsHtmlHref(
|
|
72
|
+
const contextPath = docsHtmlHref(`${basePath}${firstItemPath}`);
|
|
72
73
|
|
|
73
74
|
return (
|
|
74
75
|
<DropdownItem
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { loadDocuConfig } from "../node/paths";
|
|
2
|
+
import { loadDocuConfig, resolveBasePath } from "../node/paths";
|
|
3
|
+
import { docsNavHref } from "../node/utils";
|
|
3
4
|
import Menu from "./Menu";
|
|
4
5
|
|
|
5
6
|
const docuConfig = loadDocuConfig();
|
|
7
|
+
const basePath = resolveBasePath(docuConfig);
|
|
6
8
|
|
|
7
9
|
interface DocsLayoutProps {
|
|
8
10
|
children?: React.ReactNode;
|
|
@@ -10,7 +12,7 @@ interface DocsLayoutProps {
|
|
|
10
12
|
pathname?: string;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
export function DocsLayout({ children, repoUrl, pathname =
|
|
15
|
+
export function DocsLayout({ children, repoUrl, pathname = basePath }: DocsLayoutProps) {
|
|
14
16
|
return React.createElement(
|
|
15
17
|
"div",
|
|
16
18
|
{ className: "docs-layout flex flex-col min-h-screen w-full" },
|
|
@@ -45,12 +47,13 @@ export function DocsLayout({ children, repoUrl, pathname = "/docs" }: DocsLayout
|
|
|
45
47
|
{ className: "flex items-center gap-6 text-sm font-medium text-base-content/80" },
|
|
46
48
|
...(docuConfig.navbar?.menu || []).map((item: { title: string; href: string }) => {
|
|
47
49
|
const isExternal = /^https?:\/\//.test(item.href);
|
|
48
|
-
const
|
|
50
|
+
const href = isExternal ? item.href : docsNavHref(item.href, basePath);
|
|
51
|
+
const isDocsActive = href === basePath;
|
|
49
52
|
return React.createElement(
|
|
50
53
|
"a",
|
|
51
54
|
{
|
|
52
55
|
key: item.title,
|
|
53
|
-
href
|
|
56
|
+
href,
|
|
54
57
|
className: `flex items-center gap-1 hover:text-base-content transition-colors${isDocsActive ? " text-primary font-semibold" : ""}`,
|
|
55
58
|
...(isExternal ? { target: "_blank", rel: "noopener noreferrer" } : {}),
|
|
56
59
|
},
|
|
@@ -5,7 +5,7 @@ import Sublink, { GroupAccordionProvider } from "./Sublink";
|
|
|
5
5
|
import SidebarGroupHeader from "./SidebarGroupHeader";
|
|
6
6
|
import type { DocuRoute } from "../node/types";
|
|
7
7
|
import { cn } from "../node/utils";
|
|
8
|
-
import { config as docuConfig } from "../node/client-routes";
|
|
8
|
+
import { config as docuConfig, basePath } from "../node/client-routes";
|
|
9
9
|
|
|
10
10
|
interface MenuProps {
|
|
11
11
|
onNavigate?: () => void;
|
|
@@ -15,8 +15,8 @@ interface MenuProps {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function getCurrentContext(path: string): string | undefined {
|
|
18
|
-
if (!path.startsWith(
|
|
19
|
-
const match = path.match(
|
|
18
|
+
if (!path.startsWith(basePath)) return undefined;
|
|
19
|
+
const match = path.match(new RegExp(`^${basePath}/([^/]+)`));
|
|
20
20
|
return match ? match[1] : undefined;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -30,10 +30,11 @@ function getContextRoute(contextPath: string, routeList: DocuRoute[]): DocuRoute
|
|
|
30
30
|
export default function Menu({ onNavigate, className = "", pathname, routes = [] }: MenuProps) {
|
|
31
31
|
const menuRoutes = routes;
|
|
32
32
|
const [currentPath] = useState(
|
|
33
|
-
() => pathname || (typeof window !== "undefined" ? window.location.pathname :
|
|
33
|
+
() => pathname || (typeof window !== "undefined" ? window.location.pathname : basePath)
|
|
34
34
|
);
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
// Root deployment has no prefix to match against: every path belongs to docs.
|
|
37
|
+
if (basePath.length > 0 && !currentPath.startsWith(basePath)) return null;
|
|
37
38
|
|
|
38
39
|
const mode = docuConfig.sidebar?.context || "dropdown";
|
|
39
40
|
const navProps = {
|
|
@@ -43,7 +44,7 @@ export default function Menu({ onNavigate, className = "", pathname, routes = []
|
|
|
43
44
|
|
|
44
45
|
// Shared nav item with border-left overlap wrapper
|
|
45
46
|
const renderBorderItem = (item: DocuRoute, parentRouteHref: string, key: string) => {
|
|
46
|
-
const fullHref =
|
|
47
|
+
const fullHref = `${basePath}${parentRouteHref}${item.href}`;
|
|
47
48
|
const isActive = currentPath === fullHref || currentPath === `${fullHref}.html`;
|
|
48
49
|
return (
|
|
49
50
|
<li key={key}>
|
|
@@ -59,7 +60,7 @@ export default function Menu({ onNavigate, className = "", pathname, routes = []
|
|
|
59
60
|
href={item.href}
|
|
60
61
|
level={0}
|
|
61
62
|
onNavigate={onNavigate}
|
|
62
|
-
parentHref={
|
|
63
|
+
parentHref={`${basePath}${parentRouteHref}`}
|
|
63
64
|
/>
|
|
64
65
|
</div>
|
|
65
66
|
</div>
|
|
@@ -106,7 +107,7 @@ export default function Menu({ onNavigate, className = "", pathname, routes = []
|
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
// Dropdown mode: render only the active context section
|
|
109
|
-
const isDocsRoot = currentPath ===
|
|
110
|
+
const isDocsRoot = currentPath === basePath || currentPath === `${basePath}/`;
|
|
110
111
|
const currentContext = isDocsRoot
|
|
111
112
|
? menuRoutes[0]?.href.replace(/^\/+|\/+$/, "")
|
|
112
113
|
: getCurrentContext(currentPath);
|
|
@@ -132,7 +133,7 @@ export default function Menu({ onNavigate, className = "", pathname, routes = []
|
|
|
132
133
|
href={contextRoute.href}
|
|
133
134
|
level={0}
|
|
134
135
|
onNavigate={onNavigate}
|
|
135
|
-
parentHref=
|
|
136
|
+
parentHref={basePath}
|
|
136
137
|
/>
|
|
137
138
|
</li>
|
|
138
139
|
</ul>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import Anchor from "./Anchor";
|
|
4
|
-
import { cn } from "../node/utils";
|
|
5
|
-
import { config as docuConfig } from "../node/client-routes";
|
|
4
|
+
import { cn, docsNavActive, docsNavHref, rebaseContentPath } from "../node/utils";
|
|
5
|
+
import { config as docuConfig, basePath } from "../node/client-routes";
|
|
6
6
|
import {
|
|
7
7
|
Navbar as BaseNavbar,
|
|
8
8
|
Logo as BaseLogo,
|
|
@@ -27,7 +27,11 @@ interface NavbarProps {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function AppLogo({ logo, logoText }: { logo?: { src?: string; alt?: string }; logoText?: string }) {
|
|
30
|
-
|
|
30
|
+
// `logo.src` is authored as a root-relative path on disk (e.g.
|
|
31
|
+
// "/docs/assets/images/docu.svg"); re-base it onto the configured prefix so
|
|
32
|
+
// the logo keeps resolving after a subpath change.
|
|
33
|
+
const src = logo?.src ? rebaseContentPath(logo.src, basePath) : undefined;
|
|
34
|
+
return <BaseLogo src={src} alt={logo?.alt} text={logoText} href={basePath || "/"} />;
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
function AppNavMenu({ menu, currentPath }: { menu: AppNavMenuItem[]; currentPath?: string }) {
|
|
@@ -83,9 +87,11 @@ export function NavMenuLink({ item, isActive }: { item: BaseNavMenuItem; isActiv
|
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
export function NavItem({ item }: { item: BaseNavMenuItem }) {
|
|
90
|
+
const href = docsNavHref(item.href, basePath);
|
|
86
91
|
return (
|
|
87
92
|
<Anchor
|
|
88
|
-
href={
|
|
93
|
+
href={href}
|
|
94
|
+
activeWhen={(path: string) => docsNavActive(path, href)}
|
|
89
95
|
activeClassName="text-primary font-semibold"
|
|
90
96
|
className="text-sm font-medium"
|
|
91
97
|
>
|
|
@@ -123,24 +129,20 @@ export function MobileMenuToggle({
|
|
|
123
129
|
tabIndex={0}
|
|
124
130
|
className="dropdown-content menu bg-base-200 rounded-box z-[1] mt-2 w-52 p-2 shadow"
|
|
125
131
|
>
|
|
126
|
-
{menu.map((item) =>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
{item.title}
|
|
141
|
-
</Anchor>
|
|
142
|
-
</li>
|
|
143
|
-
))}
|
|
132
|
+
{menu.map((item) => {
|
|
133
|
+
const href = docsNavHref(item.href, basePath);
|
|
134
|
+
return (
|
|
135
|
+
<li key={item.title + item.href}>
|
|
136
|
+
<Anchor
|
|
137
|
+
href={href}
|
|
138
|
+
activeWhen={currentPath ? (path: string) => docsNavActive(path, href) : undefined}
|
|
139
|
+
activeClassName="text-primary font-semibold"
|
|
140
|
+
>
|
|
141
|
+
{item.title}
|
|
142
|
+
</Anchor>
|
|
143
|
+
</li>
|
|
144
|
+
);
|
|
145
|
+
})}
|
|
144
146
|
</ul>
|
|
145
147
|
</details>
|
|
146
148
|
</div>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
import { getPreviousNext } from "../node/route";
|
|
5
5
|
import { docsHtmlHref } from "../node/utils";
|
|
6
|
+
import { basePath } from "../node/client-routes";
|
|
6
7
|
import { PaginationDocs } from "@docubook/ui-react/pagination";
|
|
7
8
|
|
|
8
9
|
interface PaginationProps {
|
|
@@ -34,7 +35,7 @@ export default function Pagination({
|
|
|
34
35
|
prev={
|
|
35
36
|
prev
|
|
36
37
|
? {
|
|
37
|
-
href: docsHtmlHref(
|
|
38
|
+
href: docsHtmlHref(`${basePath}${prev.href}`),
|
|
38
39
|
title: prev.title,
|
|
39
40
|
description: prev.description,
|
|
40
41
|
}
|
|
@@ -43,7 +44,7 @@ export default function Pagination({
|
|
|
43
44
|
next={
|
|
44
45
|
next
|
|
45
46
|
? {
|
|
46
|
-
href: docsHtmlHref(
|
|
47
|
+
href: docsHtmlHref(`${basePath}${next.href}`),
|
|
47
48
|
title: next.title,
|
|
48
49
|
description: next.description,
|
|
49
50
|
}
|
|
@@ -37,6 +37,7 @@ FnKey.configure({
|
|
|
37
37
|
});
|
|
38
38
|
import { cn } from "../node/utils";
|
|
39
39
|
import { search, type SearchResult } from "../node/search";
|
|
40
|
+
import { deployPath } from "../node/client-routes";
|
|
40
41
|
import type { SearchRecord } from "../node/search-indexer";
|
|
41
42
|
|
|
42
43
|
interface SearchProps {
|
|
@@ -48,7 +49,11 @@ let cmdKRegistered = false;
|
|
|
48
49
|
|
|
49
50
|
async function loadIndex(): Promise<SearchRecord[]> {
|
|
50
51
|
if (indexCache) return indexCache;
|
|
51
|
-
|
|
52
|
+
// The search index is generated into the dist-root assets tree — next to the
|
|
53
|
+
// hash-named bundles, not under the docs prefix. A host that serves the dist
|
|
54
|
+
// from a subpath (GitHub Pages project site) contributes `meta.baseURL`'s
|
|
55
|
+
// path, which no relative depth can express.
|
|
56
|
+
const res = await fetch(`${deployPath}/assets/search-index.json`);
|
|
52
57
|
indexCache = await res.json();
|
|
53
58
|
return indexCache!;
|
|
54
59
|
}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
FileText,
|
|
11
11
|
} from "lucide-react";
|
|
12
12
|
import { Dropdown } from "@docubook/ui-react/dropdown";
|
|
13
|
-
import { cn } from "../node/utils";
|
|
13
|
+
import { cn, docsNavActive, docsNavHref, rebaseContentPath } from "../node/utils";
|
|
14
14
|
import Anchor from "./Anchor";
|
|
15
15
|
import { Context } from "./Context";
|
|
16
16
|
import Menu from "./Menu";
|
|
@@ -18,7 +18,7 @@ import { ThemeToggle } from "./Theme";
|
|
|
18
18
|
import { GitHubLink } from "./Navbar";
|
|
19
19
|
import Search from "./Search";
|
|
20
20
|
import type { TocItem } from "../node/types";
|
|
21
|
-
import { config as docuConfig, routes } from "../node/client-routes";
|
|
21
|
+
import { config as docuConfig, routes, basePath } from "../node/client-routes";
|
|
22
22
|
|
|
23
23
|
interface SidebarProps {
|
|
24
24
|
tocs?: TocItem[];
|
|
@@ -41,10 +41,10 @@ function DesktopSidebar({ className, repoUrl }: { className?: string; repoUrl?:
|
|
|
41
41
|
<div className={cn("flex h-full flex-col", className)}>
|
|
42
42
|
{/* Logo */}
|
|
43
43
|
<div className="flex h-14 shrink-0 items-center px-5">
|
|
44
|
-
<a href="/" className="flex items-center gap-2">
|
|
44
|
+
<a href={basePath || "/"} className="flex items-center gap-2">
|
|
45
45
|
{docuConfig.navbar?.logo?.src && (
|
|
46
46
|
<img
|
|
47
|
-
src={docuConfig.navbar.logo.src}
|
|
47
|
+
src={rebaseContentPath(docuConfig.navbar.logo.src, basePath)}
|
|
48
48
|
alt={docuConfig.navbar.logo.alt || ""}
|
|
49
49
|
className="h-6 w-6"
|
|
50
50
|
/>
|
|
@@ -154,24 +154,25 @@ export function MobileBar({
|
|
|
154
154
|
</span>
|
|
155
155
|
}
|
|
156
156
|
>
|
|
157
|
-
{(docuConfig.navbar?.menu || []).map((item) =>
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
href={item.href}
|
|
165
|
-
activeWhen={(path: string) =>
|
|
166
|
-
path === item.href || path.startsWith(item.href + "/")
|
|
167
|
-
}
|
|
168
|
-
activeClassName="text-primary font-semibold"
|
|
169
|
-
className="block rounded px-2 py-2 text-sm"
|
|
157
|
+
{(docuConfig.navbar?.menu || []).map((item) => {
|
|
158
|
+
const href = docsNavHref(item.href, basePath);
|
|
159
|
+
return (
|
|
160
|
+
<li
|
|
161
|
+
key={item.href}
|
|
162
|
+
role="menuitem"
|
|
163
|
+
className="text-base-content/80 hover:text-base-content hover:bg-base-200 cursor-pointer"
|
|
170
164
|
>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
165
|
+
<Anchor
|
|
166
|
+
href={href}
|
|
167
|
+
activeWhen={(path: string) => docsNavActive(path, href)}
|
|
168
|
+
activeClassName="text-primary font-semibold"
|
|
169
|
+
className="block rounded px-2 py-2 text-sm"
|
|
170
|
+
>
|
|
171
|
+
{item.title}
|
|
172
|
+
</Anchor>
|
|
173
|
+
</li>
|
|
174
|
+
);
|
|
175
|
+
})}
|
|
175
176
|
</Dropdown>
|
|
176
177
|
|
|
177
178
|
<button
|
|
@@ -13,7 +13,7 @@ import { ChevronDown } from "lucide-react";
|
|
|
13
13
|
import Anchor from "./Anchor";
|
|
14
14
|
import type { DocuRoute } from "../node/types";
|
|
15
15
|
import { cn, docsHtmlHref } from "../node/utils";
|
|
16
|
-
import { config as docuConfig } from "../node/client-routes";
|
|
16
|
+
import { config as docuConfig, basePath } from "../node/client-routes";
|
|
17
17
|
|
|
18
18
|
/** Exclusive accordion for level >= 2 sidebar groups — opening one group
|
|
19
19
|
* closes the previously open one. All level >= 2 groups default to closed
|
|
@@ -55,9 +55,9 @@ export default function Sublink({
|
|
|
55
55
|
parentHref = "",
|
|
56
56
|
pathname: pathnameProp,
|
|
57
57
|
}: SublinkProps) {
|
|
58
|
-
const fullHref = parentHref ? `${parentHref}${href}` :
|
|
58
|
+
const fullHref = parentHref ? `${parentHref}${href}` : `${basePath}${href}`;
|
|
59
59
|
const currentPathname =
|
|
60
|
-
pathnameProp || (typeof window !== "undefined" ? window.location.pathname :
|
|
60
|
+
pathnameProp || (typeof window !== "undefined" ? window.location.pathname : basePath);
|
|
61
61
|
|
|
62
62
|
// Groups with children are exclusive accordions (default closed, expand on
|
|
63
63
|
// click). In separator mode every nav item renders at level 0 (sections are
|
package/.docu/lib/build.deno.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as DOCS_DIR,
|
|
2
|
-
import { C as hashMdxSources, D as
|
|
3
|
-
import {
|
|
4
|
-
import { t as logger } from "./logger-
|
|
1
|
+
import { D as resolveDeployPath, E as resolveBasePath, S as docsDepth, T as rebaseContentPath, _ as loadDocuConfig, a as DOCS_DIR, f as assertValidBasePath, g as deployPath, h as defaultFavicon, i as DOCS_ASSETS_DIR, l as PAGES_DIR, n as CACHE_FILE, o as DOCS_OUT_DIR, r as DIST_DIR, t as ASSETS_DIR, u as PROJECT_ROOT, y as servedBasePath } from "./paths-CHXABfXa.js";
|
|
2
|
+
import { C as hashMdxSources, D as BuildPluginBuilder, E as runtimeStamp, O as loadPlugins, S as atomicWriteFile, T as renderToolchainStamp, _ as getPageStripped, a as IndexPage, b as buildClientBundle, c as captureException, d as clearDerivedPageCaches, f as compileMdx, g as getPageFrontmatter, h as getPageContent, i as DocsLayout, l as initSentry, m as frontmatterField, o as NotFoundPage, p as compileMdxModule, r as htmlShell, s as DocsPage, u as generateSearchIndex, v as registerPageContent, w as hookMemoryPressure, x as computeInlineThemeCss, y as getGitLastModifiedBatch } from "./html.shared-RPCM1y5Q.js";
|
|
3
|
+
import { a as cspHeader, n as resolveDocsIndexSource, r as scanMdxFiles, s as generateNonce } from "./server-utils-BYWB7A6T.js";
|
|
4
|
+
import { t as logger } from "./logger-NdMEl8u7.js";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { copyFile, mkdir, readFile, readdir, rename, unlink, writeFile } from "node:fs/promises";
|
|
@@ -20,20 +20,41 @@ function isCacheEntry(value) {
|
|
|
20
20
|
/**
|
|
21
21
|
* Build SEO metadata from config and per-page frontmatter.
|
|
22
22
|
* All fields are derived from existing data — no extra config required.
|
|
23
|
+
*
|
|
24
|
+
* All paths are prefixed with the configured base path so canonical and OG
|
|
25
|
+
* URLs stay correct when the site is served under a subpath.
|
|
23
26
|
*/
|
|
24
27
|
function buildSeoMeta(config, frontmatter, slug) {
|
|
25
28
|
const baseURL = config.meta?.baseURL?.replace(/\/+$/, "") || "";
|
|
29
|
+
const prefix = resolveBasePath(config);
|
|
26
30
|
const result = {
|
|
27
|
-
url: slug ? `${baseURL}
|
|
31
|
+
url: slug ? `${baseURL}${prefix}/${slug}` : `${baseURL}/`,
|
|
28
32
|
siteName: config.meta?.title || ""
|
|
29
33
|
};
|
|
30
34
|
const image = frontmatterField(frontmatter, "image") || config.meta?.ogImage;
|
|
31
|
-
if (image)
|
|
32
|
-
|
|
35
|
+
if (image) result.image = resolveOgImage(image, baseURL, prefix, resolveDeployPath(config));
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve an OG image path to an absolute URL.
|
|
40
|
+
*
|
|
41
|
+
* Handles four shapes:
|
|
42
|
+
* - absolute (`https://…`) — used verbatim
|
|
43
|
+
* - root-relative already carrying the prefix (`/repo/assets/og.png`) — used as is
|
|
44
|
+
* - root-relative still carrying the default prefix (`/docs/assets/og.png`) —
|
|
45
|
+
* re-based onto `prefix`, so an existing `docu.json` keeps working after the
|
|
46
|
+
* site moves to a different subpath
|
|
47
|
+
* - bare relative (`og.png`) — resolved against the prefix directory
|
|
48
|
+
*/
|
|
49
|
+
function resolveOgImage(image, baseURL, prefix, deploymentPath) {
|
|
50
|
+
try {
|
|
51
|
+
if (/^[a-zA-Z][a-zA-Z\d+.-]*:\/\//.test(image)) return new URL(image).href;
|
|
52
|
+
const rebased = rebaseContentPath(image.startsWith("/") ? image : `${prefix}/${image}`, prefix);
|
|
53
|
+
const withinRoot = rebased.startsWith(`${deploymentPath}/`) ? rebased.slice(deploymentPath.length) : rebased;
|
|
54
|
+
return new URL(withinRoot.replace(/^\/+/, ""), `${baseURL}/`).href;
|
|
33
55
|
} catch {
|
|
34
|
-
|
|
56
|
+
return image;
|
|
35
57
|
}
|
|
36
|
-
return result;
|
|
37
58
|
}
|
|
38
59
|
//#endregion
|
|
39
60
|
//#region .docu/node/build.impl.ts
|
|
@@ -60,7 +81,7 @@ async function readCache() {
|
|
|
60
81
|
const data = await readFile(CACHE_FILE, "utf-8");
|
|
61
82
|
const parsed = JSON.parse(data);
|
|
62
83
|
const meta = parsed.__meta__;
|
|
63
|
-
if (!meta || meta.version !==
|
|
84
|
+
if (!meta || meta.version !== 6 || meta.runtime !== runtimeStamp() || meta.render !== renderToolchainStamp()) return {};
|
|
64
85
|
return parsed;
|
|
65
86
|
}
|
|
66
87
|
} catch (err) {
|
|
@@ -70,11 +91,12 @@ async function readCache() {
|
|
|
70
91
|
}
|
|
71
92
|
function stampCache(cache) {
|
|
72
93
|
cache.__meta__ = {
|
|
73
|
-
hash: `
|
|
94
|
+
hash: `6:${runtimeStamp()}`,
|
|
74
95
|
mtime: 0,
|
|
75
96
|
builtAt: Date.now(),
|
|
76
|
-
version:
|
|
77
|
-
runtime: runtimeStamp()
|
|
97
|
+
version: 6,
|
|
98
|
+
runtime: runtimeStamp(),
|
|
99
|
+
render: renderToolchainStamp()
|
|
78
100
|
};
|
|
79
101
|
}
|
|
80
102
|
async function writeCache(cache) {
|
|
@@ -166,8 +188,8 @@ async function renderDocsPage(docuConfig, slug, rawMdx, filePath, gitDates, buil
|
|
|
166
188
|
};
|
|
167
189
|
const headExtra = builder?.collectHead(ctx);
|
|
168
190
|
const bodyExtra = builder?.collectBody(ctx);
|
|
169
|
-
const depth = slug
|
|
170
|
-
const favicon = docuConfig.meta?.favicon ||
|
|
191
|
+
const depth = docsDepth(slug, servedBasePath());
|
|
192
|
+
const favicon = docuConfig.meta?.favicon || defaultFavicon();
|
|
171
193
|
const seo = buildSeoMeta(docuConfig, frontmatter, slug || "");
|
|
172
194
|
const csp = nonce ? cspHeader(nonce) : void 0;
|
|
173
195
|
let html = htmlShell({
|
|
@@ -182,6 +204,7 @@ async function renderDocsPage(docuConfig, slug, rawMdx, filePath, gitDates, buil
|
|
|
182
204
|
nonce,
|
|
183
205
|
themeCss: inlineThemeCss,
|
|
184
206
|
depth,
|
|
207
|
+
basePath: servedBasePath(),
|
|
185
208
|
headExtra,
|
|
186
209
|
bodyExtra
|
|
187
210
|
});
|
|
@@ -203,6 +226,7 @@ async function runBuild() {
|
|
|
203
226
|
const docuConfig = loadDocuConfig();
|
|
204
227
|
const args = parseArgs();
|
|
205
228
|
hookMemoryPressure(clearDerivedPageCaches);
|
|
229
|
+
for (const warning of assertValidBasePath()) logger.warn(warning.message);
|
|
206
230
|
logger.buildStart();
|
|
207
231
|
if (args.clean) {
|
|
208
232
|
const { rm } = await import("node:fs/promises");
|
|
@@ -217,7 +241,7 @@ async function runBuild() {
|
|
|
217
241
|
}
|
|
218
242
|
await mkdir(DIST_DIR, { recursive: true });
|
|
219
243
|
await mkdir(ASSETS_DIR, { recursive: true });
|
|
220
|
-
await copyDirectoryRecursive(DOCS_ASSETS_DIR, join(
|
|
244
|
+
await copyDirectoryRecursive(DOCS_ASSETS_DIR, join(DOCS_OUT_DIR, "assets"));
|
|
221
245
|
const mdxFiles = await scanMdxFiles(DOCS_DIR);
|
|
222
246
|
const cache = args.force ? {} : await readCache();
|
|
223
247
|
let built = 0;
|
|
@@ -250,6 +274,11 @@ async function runBuild() {
|
|
|
250
274
|
});
|
|
251
275
|
await Promise.all(prePassTasks);
|
|
252
276
|
const indexSource = resolveDocsIndexSource(DOCS_DIR);
|
|
277
|
+
const docsIndexPath = join(DOCS_OUT_DIR, "index.html");
|
|
278
|
+
if (!indexSource && DOCS_OUT_DIR !== DIST_DIR) {
|
|
279
|
+
const { rm } = await import("node:fs/promises");
|
|
280
|
+
await rm(docsIndexPath, { force: true });
|
|
281
|
+
}
|
|
253
282
|
if (indexSource) try {
|
|
254
283
|
let indexContent = await readFile(indexSource, "utf-8");
|
|
255
284
|
if (builder) {
|
|
@@ -291,7 +320,7 @@ async function runBuild() {
|
|
|
291
320
|
for (const file of mdxFiles) {
|
|
292
321
|
const rebuildDecision = shouldRebuild(file.path, file.mtime, cache);
|
|
293
322
|
if (rebuildDecision === "no" && !builder) {
|
|
294
|
-
const outputPath = join(
|
|
323
|
+
const outputPath = join(DOCS_OUT_DIR, `${file.path}.html`);
|
|
295
324
|
if (existsSync(outputPath) && !assetsChanged) {
|
|
296
325
|
skipped++;
|
|
297
326
|
continue;
|
|
@@ -309,7 +338,7 @@ async function runBuild() {
|
|
|
309
338
|
const cached = cache[file.path];
|
|
310
339
|
if (isCacheEntry(cached) && cached.hash === contentHash) {
|
|
311
340
|
if (!assetsChanged) {
|
|
312
|
-
const outputPath = join(
|
|
341
|
+
const outputPath = join(DOCS_OUT_DIR, `${file.path}.html`);
|
|
313
342
|
if (existsSync(outputPath)) {
|
|
314
343
|
cache[file.path] = {
|
|
315
344
|
...cached,
|
|
@@ -329,7 +358,7 @@ async function runBuild() {
|
|
|
329
358
|
try {
|
|
330
359
|
const pageNonce = generateNonce();
|
|
331
360
|
const html = await renderDocsPage(docuConfig, capturedFile.path, capturedRawMdx, relPath, gitDates, builder, pageNonce);
|
|
332
|
-
const outputPath = join(
|
|
361
|
+
const outputPath = join(DOCS_OUT_DIR, `${capturedFile.path}.html`);
|
|
333
362
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
334
363
|
await writeFile(outputPath, html);
|
|
335
364
|
cache[capturedFile.path] = {
|
|
@@ -348,20 +377,17 @@ async function runBuild() {
|
|
|
348
377
|
for (let i = 0; i < buildTasks.length; i += CONCURRENCY) await Promise.all(buildTasks.slice(i, i + CONCURRENCY).map((fn) => fn()));
|
|
349
378
|
if (indexSource) try {
|
|
350
379
|
const indexHtml = await renderDocsPage(docuConfig, "", await readFile(indexSource, "utf-8"), indexSource.replace(PROJECT_ROOT + "/", ""), gitDates, builder, generateNonce());
|
|
351
|
-
|
|
352
|
-
|
|
380
|
+
if (DOCS_OUT_DIR !== DIST_DIR) {
|
|
381
|
+
await mkdir(DOCS_OUT_DIR, { recursive: true });
|
|
382
|
+
await writeFile(docsIndexPath, indexHtml);
|
|
383
|
+
}
|
|
353
384
|
} catch (err) {
|
|
354
385
|
const msg = err instanceof Error ? err.message : String(err);
|
|
355
386
|
errors.push(`index: ${msg}`);
|
|
356
387
|
console.error(`\n❌ Failed to build index: ${msg}\n`);
|
|
357
388
|
}
|
|
358
|
-
else {
|
|
359
|
-
const msg = "docs root index: docs/index.mdx (or docs/index.md) not found";
|
|
360
|
-
errors.push(`index: ${msg}`);
|
|
361
|
-
console.error(`\n❌ Failed to build index: ${msg}\n`);
|
|
362
|
-
}
|
|
363
389
|
const landingPage = React.createElement(IndexPage);
|
|
364
|
-
const landingFavicon = docuConfig.meta?.favicon ||
|
|
390
|
+
const landingFavicon = docuConfig.meta?.favicon || defaultFavicon();
|
|
365
391
|
const landingSeo = buildSeoMeta(docuConfig, docuConfig.meta, "");
|
|
366
392
|
const landingNonce = generateNonce();
|
|
367
393
|
const landingContext = {
|
|
@@ -383,13 +409,14 @@ async function runBuild() {
|
|
|
383
409
|
js: landingContext.assets.js,
|
|
384
410
|
nonce: landingNonce,
|
|
385
411
|
themeCss: inlineThemeCss,
|
|
412
|
+
basePath: servedBasePath(),
|
|
386
413
|
headExtra: builder?.collectHead(landingContext),
|
|
387
414
|
bodyExtra: builder?.collectBody(landingContext)
|
|
388
415
|
});
|
|
389
416
|
if (builder) landingHtml = await builder.runTransformHtmlChain(landingHtml, landingContext);
|
|
390
417
|
await writeFile(join(DIST_DIR, "index.html"), landingHtml);
|
|
391
418
|
const notFoundPage = React.createElement(NotFoundPage);
|
|
392
|
-
const notFoundFavicon = docuConfig.meta?.favicon ||
|
|
419
|
+
const notFoundFavicon = docuConfig.meta?.favicon || defaultFavicon();
|
|
393
420
|
const notFoundNonce = generateNonce();
|
|
394
421
|
const notFoundContext = {
|
|
395
422
|
pageType: "notFound",
|
|
@@ -411,7 +438,9 @@ async function runBuild() {
|
|
|
411
438
|
js: notFoundContext.assets.js,
|
|
412
439
|
nonce: notFoundNonce,
|
|
413
440
|
themeCss: inlineThemeCss,
|
|
414
|
-
absoluteAssets: true
|
|
441
|
+
absoluteAssets: true,
|
|
442
|
+
basePath: servedBasePath(),
|
|
443
|
+
deployPath: deployPath()
|
|
415
444
|
});
|
|
416
445
|
if (builder) notFoundHtml = await builder.runTransformHtmlChain(notFoundHtml, notFoundContext);
|
|
417
446
|
await writeFile(join(DIST_DIR, "404.html"), notFoundHtml);
|
|
@@ -421,7 +450,7 @@ async function runBuild() {
|
|
|
421
450
|
slug: f.path,
|
|
422
451
|
title: f.path.split("/").pop() || f.path,
|
|
423
452
|
filePath: join(DOCS_DIR, f.path),
|
|
424
|
-
outputPath: join(
|
|
453
|
+
outputPath: join(DOCS_OUT_DIR, `${f.path}.html`)
|
|
425
454
|
}));
|
|
426
455
|
await builder.runOnEnd(pages, {
|
|
427
456
|
assetManifest,
|
|
@@ -453,4 +482,4 @@ async function runBuildCli() {
|
|
|
453
482
|
//#endregion
|
|
454
483
|
export { runBuildCli as n, runBuild as t };
|
|
455
484
|
|
|
456
|
-
//# sourceMappingURL=build.impl-
|
|
485
|
+
//# sourceMappingURL=build.impl-oTVkE6D9.js.map
|