@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.
Files changed (72) hide show
  1. package/.docu/components/Context.tsx +6 -5
  2. package/.docu/components/DocsLayout.tsx +7 -4
  3. package/.docu/components/Menu.tsx +10 -9
  4. package/.docu/components/Navbar.tsx +24 -22
  5. package/.docu/components/Pagination.tsx +3 -2
  6. package/.docu/components/Search.tsx +6 -1
  7. package/.docu/components/Sidebar.tsx +22 -21
  8. package/.docu/components/Sublink.tsx +3 -3
  9. package/.docu/lib/build.deno.js +1 -1
  10. package/.docu/lib/build.impl-DxI2YYqL.js +2 -0
  11. package/.docu/lib/{build.impl-BSt6Vikt.js → build.impl-oTVkE6D9.js} +60 -31
  12. package/.docu/lib/build.impl-oTVkE6D9.js.map +1 -0
  13. package/.docu/lib/build.node.js +1 -1
  14. package/.docu/lib/clean.js +1 -1
  15. package/.docu/lib/deploy.deno.js +1 -1
  16. package/.docu/lib/deploy.node.js +1 -1
  17. package/.docu/lib/{deploy.shared-D6S_FE7d.js → deploy.shared-p8UHVvAw.js} +27 -8
  18. package/.docu/lib/deploy.shared-p8UHVvAw.js.map +1 -0
  19. package/.docu/lib/{html.shared-CsEFmy_Y.js → html.shared-RPCM1y5Q.js} +264 -73
  20. package/.docu/lib/html.shared-RPCM1y5Q.js.map +1 -0
  21. package/.docu/lib/{logger-DFKQUy8M.js → logger-NdMEl8u7.js} +3 -3
  22. package/.docu/lib/{logger-DFKQUy8M.js.map → logger-NdMEl8u7.js.map} +1 -1
  23. package/.docu/lib/paths-CHXABfXa.js +332 -0
  24. package/.docu/lib/paths-CHXABfXa.js.map +1 -0
  25. package/.docu/lib/preview.deno.js +1 -1
  26. package/.docu/lib/{preview.impl-BfESMUM7.js → preview.impl-CLHIPgry.js} +4 -4
  27. package/.docu/lib/preview.impl-CLHIPgry.js.map +1 -0
  28. package/.docu/lib/preview.node.js +1 -1
  29. package/.docu/lib/{utils-B_CoyKie.js → server-utils-BYWB7A6T.js} +3 -21
  30. package/.docu/lib/server-utils-BYWB7A6T.js.map +1 -0
  31. package/.docu/lib/server.deno.js +1 -1
  32. package/.docu/lib/{server.impl-CZ4eMe5L.js → server.impl-C4t3Xqb5.js} +24 -19
  33. package/.docu/lib/server.impl-C4t3Xqb5.js.map +1 -0
  34. package/.docu/lib/server.node.js +1 -1
  35. package/.docu/node/base-path.ts +306 -0
  36. package/.docu/node/build.impl.ts +50 -17
  37. package/.docu/node/build.ts +50 -17
  38. package/.docu/node/cache-key.ts +92 -4
  39. package/.docu/node/client-routes.ts +16 -1
  40. package/.docu/node/deploy.shared.ts +51 -15
  41. package/.docu/node/deploy.ts +7 -3
  42. package/.docu/node/html.shared.ts +33 -3
  43. package/.docu/node/html.ts +18 -3
  44. package/.docu/node/hydrate.node.ts +6 -0
  45. package/.docu/node/hydrate.ts +16 -1
  46. package/.docu/node/mdx.ts +102 -23
  47. package/.docu/node/paths.ts +115 -0
  48. package/.docu/node/preview.impl.ts +1 -1
  49. package/.docu/node/preview.ts +1 -1
  50. package/.docu/node/search-indexer.ts +20 -3
  51. package/.docu/node/seo.ts +41 -7
  52. package/.docu/node/server-routes.ts +34 -11
  53. package/.docu/node/server-utils.ts +152 -0
  54. package/.docu/node/server.impl.ts +22 -12
  55. package/.docu/node/server.ts +23 -27
  56. package/.docu/node/types.ts +13 -0
  57. package/.docu/node/utils.ts +56 -149
  58. package/.docu/pages/404.tsx +2 -1
  59. package/.docu/pages/index.tsx +6 -13
  60. package/bin/cli.js +17 -21
  61. package/docu.schema.json +10 -1
  62. package/package.json +5 -5
  63. package/template/docu.json +1 -0
  64. package/.docu/lib/build.impl-BSt6Vikt.js.map +0 -1
  65. package/.docu/lib/build.impl-xNDEh8DJ.js +0 -2
  66. package/.docu/lib/deploy.shared-D6S_FE7d.js.map +0 -1
  67. package/.docu/lib/html.shared-CsEFmy_Y.js.map +0 -1
  68. package/.docu/lib/paths-Cbz-dj4s.js +0 -72
  69. package/.docu/lib/paths-Cbz-dj4s.js.map +0 -1
  70. package/.docu/lib/preview.impl-BfESMUM7.js.map +0 -1
  71. package/.docu/lib/server.impl-CZ4eMe5L.js.map +0 -1
  72. 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(/^\/docs/, "");
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 activeRoute = pathname.startsWith("/docs") ? getActiveContextRoute(pathname) : undefined;
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 || !pathname.startsWith("/docs") || contextRoutes.length === 0) {
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(`/docs${firstItemPath}`);
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 = "/docs" }: DocsLayoutProps) {
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 isDocsActive = item.href === "/docs";
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: item.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("/docs")) return undefined;
19
- const match = path.match(/^\/docs\/([^/]+)/);
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 : "/docs")
33
+ () => pathname || (typeof window !== "undefined" ? window.location.pathname : basePath)
34
34
  );
35
35
 
36
- if (!currentPath.startsWith("/docs")) return null;
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 = `/docs${parentRouteHref}${item.href}`;
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={`/docs${parentRouteHref}`}
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 === "/docs" || currentPath === "/docs/";
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="/docs"
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
- return <BaseLogo src={logo?.src} alt={logo?.alt} text={logoText} href="/docs" />;
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={item.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
- <li key={item.title + item.href}>
128
- <Anchor
129
- href={item.href}
130
- activeWhen={
131
- currentPath
132
- ? (path: string) =>
133
- path === item.href ||
134
- path.startsWith(item.href + "/") ||
135
- path.endsWith(item.href + ".html")
136
- : undefined
137
- }
138
- activeClassName="text-primary font-semibold"
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(`/docs${prev.href}`),
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(`/docs${next.href}`),
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
- const res = await fetch("/assets/search-index.json");
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
- <li
159
- key={item.href}
160
- role="menuitem"
161
- className="text-base-content/80 hover:text-base-content hover:bg-base-200 cursor-pointer"
162
- >
163
- <Anchor
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
- {item.title}
172
- </Anchor>
173
- </li>
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}` : `/docs${href}`;
58
+ const fullHref = parentHref ? `${parentHref}${href}` : `${basePath}${href}`;
59
59
  const currentPathname =
60
- pathnameProp || (typeof window !== "undefined" ? window.location.pathname : "/docs");
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
@@ -1,4 +1,4 @@
1
- import { n as runBuildCli } from "./build.impl-BSt6Vikt.js";
1
+ import { n as runBuildCli } from "./build.impl-oTVkE6D9.js";
2
2
  //#region .docu/node/build.deno.ts
3
3
  await runBuildCli();
4
4
  process.exit(0);
@@ -0,0 +1,2 @@
1
+ import { n as runBuildCli } from "./build.impl-oTVkE6D9.js";
2
+ export { runBuildCli };
@@ -1,7 +1,7 @@
1
- import { a as DOCS_DIR, c as PAGES_DIR, f as loadDocuConfig, i as DOCS_ASSETS_DIR, l as PROJECT_ROOT, n as CACHE_FILE, r as DIST_DIR, t as ASSETS_DIR } from "./paths-Cbz-dj4s.js";
2
- import { C as hashMdxSources, D as loadPlugins, E as BuildPluginBuilder, S as atomicWriteFile, T as runtimeStamp, _ 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-CsEFmy_Y.js";
3
- import { c as scanMdxFiles, d as cspHeader, p as generateNonce, s as resolveDocsIndexSource } from "./utils-B_CoyKie.js";
4
- import { t as logger } from "./logger-DFKQUy8M.js";
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}/docs/${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) try {
32
- result.image = new URL(image, image.startsWith("/") ? baseURL : `${baseURL}/docs/`).href;
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
- result.image = image;
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 !== 5 || meta.runtime !== runtimeStamp()) return {};
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: `5:${runtimeStamp()}`,
94
+ hash: `6:${runtimeStamp()}`,
74
95
  mtime: 0,
75
96
  builtAt: Date.now(),
76
- version: 5,
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 ? slug.split("/").length : 1;
170
- const favicon = docuConfig.meta?.favicon || "/docs/assets/images/favicon.ico";
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(DIST_DIR, "docs", "assets"));
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(DIST_DIR, "docs", `${file.path}.html`);
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(DIST_DIR, "docs", `${file.path}.html`);
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(DIST_DIR, "docs", `${capturedFile.path}.html`);
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
- await mkdir(join(DIST_DIR, "docs"), { recursive: true });
352
- await writeFile(join(DIST_DIR, "docs", "index.html"), indexHtml);
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 || "/docs/assets/images/favicon.ico";
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 || "/docs/assets/images/favicon.ico";
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(DIST_DIR, "docs", `${f.path}.html`)
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-BSt6Vikt.js.map
485
+ //# sourceMappingURL=build.impl-oTVkE6D9.js.map