boltdocs 2.7.9 → 2.7.11

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 (174) hide show
  1. package/dist/{cache-DorPMFgW.cjs → cache-Ba-DZQNH.cjs} +1 -1
  2. package/dist/{cache-CQKlT4fI.mjs → cache-BuMZ58L5.mjs} +1 -1
  3. package/dist/chunk-CU-zTemE.cjs +6 -0
  4. package/dist/client/index.cjs +1929 -1
  5. package/dist/client/index.js +1880 -1
  6. package/dist/client/mdx.cjs +7 -1
  7. package/dist/client/mdx.js +7 -1
  8. package/dist/client/primitives.cjs +60 -1
  9. package/dist/client/primitives.js +20 -1
  10. package/dist/docs-layout-BXHV0xw_.cjs +1431 -0
  11. package/dist/docs-layout-DwFndmj5.js +1231 -0
  12. package/dist/doctor-Be7Ly1oM.mjs +21 -0
  13. package/dist/{doctor-D4_Y7M4p.cjs → doctor-CrytFkqW.cjs} +1 -1
  14. package/dist/doctor-jMxWZyLJ.cjs +21 -0
  15. package/dist/generator-CHqxiQhF.cjs +21 -0
  16. package/dist/generator-ClVanhvi.mjs +21 -0
  17. package/dist/icons-dev-3cZMyt8r.cjs +1204 -0
  18. package/dist/icons-dev-Df8OQ481.js +839 -0
  19. package/dist/image-DtrI2cw3.cjs +268 -0
  20. package/dist/image-jxPb-2iV.js +214 -0
  21. package/dist/mdx-BdWkJTeB.cjs +523 -0
  22. package/dist/mdx-UTTLFWJq.js +494 -0
  23. package/dist/meta-loader-CWg2gnbY.mjs +6 -0
  24. package/dist/meta-loader-Cv9O0Pzl.cjs +6 -0
  25. package/dist/node/cli-entry.cjs +1 -1
  26. package/dist/node/cli-entry.mjs +1 -1
  27. package/dist/node/index.cjs +1 -1
  28. package/dist/node/index.mjs +1 -1
  29. package/dist/node/routes/worker.cjs +1 -1
  30. package/dist/node/routes/worker.mjs +1 -1
  31. package/dist/node-BSM4qcDK.cjs +111 -0
  32. package/dist/node-BspZN3R2.mjs +111 -0
  33. package/dist/{package-VfQM94VL.cjs → package-DIIrjuWI.cjs} +1 -1
  34. package/dist/{package-B4MD00N3.mjs → package-K0zsjGIz.mjs} +1 -1
  35. package/dist/{parser-Bh11BsdA.cjs → parser-Aq8LoH-0.cjs} +1 -1
  36. package/dist/{parser-DYRzXWmA.cjs → parser-CdNbqN5y.cjs} +1 -1
  37. package/dist/parser-nE792MLO.mjs +6 -0
  38. package/dist/rolldown-runtime-fkIsjY3S.mjs +6 -0
  39. package/dist/{routes-Co1mRM58.cjs → routes-2k3tbUmC.cjs} +1 -1
  40. package/dist/routes-CpxZIsMM.mjs +6 -0
  41. package/dist/{routes-CHf76Ye4.cjs → routes-DP1vmWRj.cjs} +1 -1
  42. package/dist/search-dialog-BHuIiUC6.js +8 -0
  43. package/dist/search-dialog-BNF10tDl.js +375 -0
  44. package/dist/search-dialog-BwkDuI9R.cjs +220 -0
  45. package/dist/search-dialog-C7xuvyNk.cjs +386 -0
  46. package/dist/search-dialog-CIQg6k8c.cjs +8 -0
  47. package/dist/search-dialog-D-DDN7zJ.js +208 -0
  48. package/dist/utils-CG65J0Sc.mjs +7 -0
  49. package/dist/utils-CKunkU96.cjs +7 -0
  50. package/dist/{worker-pool-BwU8ckrg.cjs → worker-pool-Crbqgw5R.cjs} +1 -1
  51. package/package.json +5 -5
  52. package/dist/chunk-Ds5LZdWN.cjs +0 -6
  53. package/dist/docs-layout-KoWNZc8_.js +0 -6
  54. package/dist/docs-layout-x2yKt2cL.cjs +0 -6
  55. package/dist/doctor-BD1BSB03.mjs +0 -23
  56. package/dist/doctor-BHc9ua6r.cjs +0 -23
  57. package/dist/generator-DGW6pkCC.cjs +0 -22
  58. package/dist/generator-Dv3wEmhZ.mjs +0 -22
  59. package/dist/icons-dev-B_RZIyxu.js +0 -6
  60. package/dist/icons-dev-BlV3wWFT.cjs +0 -6
  61. package/dist/image-BHhTvQzr.cjs +0 -6
  62. package/dist/image-CqKzYD8f.js +0 -6
  63. package/dist/mdx-DudBEac0.js +0 -7
  64. package/dist/mdx-r4cDQxWu.cjs +0 -7
  65. package/dist/meta-loader-0gJ4PtBC.cjs +0 -6
  66. package/dist/meta-loader-9IpAHWDS.mjs +0 -6
  67. package/dist/node-DBaH7kat.mjs +0 -111
  68. package/dist/node-t5C3Q85p.cjs +0 -111
  69. package/dist/parser-9cVdK7w9.mjs +0 -6
  70. package/dist/routes-DwrMa5-z.mjs +0 -6
  71. package/dist/search-dialog-B584t9ZF.js +0 -6
  72. package/dist/search-dialog-BvBopRsZ.cjs +0 -6
  73. package/dist/search-dialog-ByvGScjt.js +0 -6
  74. package/dist/search-dialog-Cyko6TJm.cjs +0 -6
  75. package/dist/search-dialog-D6BNohIJ.js +0 -6
  76. package/dist/search-dialog-DuYTIefy.cjs +0 -6
  77. package/dist/utils-BxNAXhZZ.mjs +0 -7
  78. package/dist/utils-Clzu7jvb.cjs +0 -7
  79. package/src/client/app/config-context.tsx +0 -51
  80. package/src/client/app/doc-page.tsx +0 -38
  81. package/src/client/app/docs-layout.tsx +0 -28
  82. package/src/client/app/head.tsx +0 -122
  83. package/src/client/app/helmet-compat.tsx +0 -36
  84. package/src/client/app/mdx-component.tsx +0 -8
  85. package/src/client/app/mdx-components-context.tsx +0 -72
  86. package/src/client/app/routes-context.tsx +0 -34
  87. package/src/client/app/scroll-handler.tsx +0 -74
  88. package/src/client/app/theme-context.tsx +0 -103
  89. package/src/client/app/ui-context.tsx +0 -42
  90. package/src/client/components/docs-layout-default.tsx +0 -85
  91. package/src/client/components/icons-dev.tsx +0 -282
  92. package/src/client/components/mdx/callout.tsx +0 -97
  93. package/src/client/components/mdx/card.tsx +0 -99
  94. package/src/client/components/mdx/cards.tsx +0 -27
  95. package/src/client/components/mdx/code-block.tsx +0 -184
  96. package/src/client/components/mdx/field.tsx +0 -33
  97. package/src/client/components/mdx/image.tsx +0 -44
  98. package/src/client/components/mdx/index.ts +0 -19
  99. package/src/client/components/mdx/table.tsx +0 -54
  100. package/src/client/components/mdx/typographics.tsx +0 -120
  101. package/src/client/components/mdx/use-code-block.ts +0 -34
  102. package/src/client/components/primitives/breadcrumbs.tsx +0 -54
  103. package/src/client/components/primitives/button-group.tsx +0 -54
  104. package/src/client/components/primitives/button.tsx +0 -6
  105. package/src/client/components/primitives/code-block.tsx +0 -120
  106. package/src/client/components/primitives/docs-layout.tsx +0 -125
  107. package/src/client/components/primitives/error-boundary.tsx +0 -107
  108. package/src/client/components/primitives/heading.tsx +0 -128
  109. package/src/client/components/primitives/helpers/observer.ts +0 -141
  110. package/src/client/components/primitives/image.tsx +0 -26
  111. package/src/client/components/primitives/link.tsx +0 -102
  112. package/src/client/components/primitives/menu.tsx +0 -137
  113. package/src/client/components/primitives/navbar.tsx +0 -466
  114. package/src/client/components/primitives/on-this-page.tsx +0 -430
  115. package/src/client/components/primitives/page-nav.tsx +0 -51
  116. package/src/client/components/primitives/popover.tsx +0 -28
  117. package/src/client/components/primitives/search-dialog.tsx +0 -193
  118. package/src/client/components/primitives/sidebar.tsx +0 -423
  119. package/src/client/components/primitives/skeleton.tsx +0 -26
  120. package/src/client/components/primitives/tabs.tsx +0 -70
  121. package/src/client/components/primitives/tooltip.tsx +0 -81
  122. package/src/client/components/primitives/types.ts +0 -11
  123. package/src/client/components/ui-base/banner.tsx +0 -66
  124. package/src/client/components/ui-base/breadcrumbs.tsx +0 -44
  125. package/src/client/components/ui-base/copy-markdown.tsx +0 -107
  126. package/src/client/components/ui-base/error-boundary.tsx +0 -15
  127. package/src/client/components/ui-base/github-stars.tsx +0 -29
  128. package/src/client/components/ui-base/icons.tsx +0 -240
  129. package/src/client/components/ui-base/index.ts +0 -16
  130. package/src/client/components/ui-base/last-updated.tsx +0 -27
  131. package/src/client/components/ui-base/navbar.tsx +0 -266
  132. package/src/client/components/ui-base/not-found.tsx +0 -26
  133. package/src/client/components/ui-base/on-this-page.tsx +0 -57
  134. package/src/client/components/ui-base/page-nav.tsx +0 -50
  135. package/src/client/components/ui-base/search-dialog.tsx +0 -163
  136. package/src/client/components/ui-base/search-highlight.tsx +0 -10
  137. package/src/client/components/ui-base/sidebar.tsx +0 -92
  138. package/src/client/components/ui-base/tabs.tsx +0 -83
  139. package/src/client/components/ui-base/theme-toggle.tsx +0 -130
  140. package/src/client/components/ui-base/version-i18n.tsx +0 -80
  141. package/src/client/hooks/index.ts +0 -13
  142. package/src/client/hooks/use-analytics.ts +0 -272
  143. package/src/client/hooks/use-breadcrumbs.ts +0 -22
  144. package/src/client/hooks/use-i18n.ts +0 -182
  145. package/src/client/hooks/use-localized-to.ts +0 -113
  146. package/src/client/hooks/use-location.ts +0 -5
  147. package/src/client/hooks/use-navbar.ts +0 -130
  148. package/src/client/hooks/use-page-nav.ts +0 -46
  149. package/src/client/hooks/use-routes.ts +0 -108
  150. package/src/client/hooks/use-search-highlight.ts +0 -185
  151. package/src/client/hooks/use-search.ts +0 -118
  152. package/src/client/hooks/use-sidebar.ts +0 -205
  153. package/src/client/hooks/use-tabs.ts +0 -46
  154. package/src/client/hooks/use-version.ts +0 -111
  155. package/src/client/index.ts +0 -31
  156. package/src/client/mdx.ts +0 -2
  157. package/src/client/primitives.ts +0 -19
  158. package/src/client/ssg/boltdocs-shell.tsx +0 -148
  159. package/src/client/ssg/create-routes.tsx +0 -473
  160. package/src/client/ssg/index.ts +0 -4
  161. package/src/client/ssg/mdx-page.tsx +0 -38
  162. package/src/client/store/boltdocs-context.tsx +0 -137
  163. package/src/client/theme/neutral.css +0 -141
  164. package/src/client/theme/reset.css +0 -189
  165. package/src/client/types.ts +0 -116
  166. package/src/client/utils/cn.ts +0 -6
  167. package/src/client/utils/copy-clipboard.ts +0 -22
  168. package/src/client/utils/get-base-file-path.ts +0 -21
  169. package/src/client/utils/github.ts +0 -121
  170. package/src/client/utils/i18n.ts +0 -23
  171. package/src/client/utils/path.ts +0 -9
  172. package/src/client/utils/react-to-text.ts +0 -34
  173. package/src/client/virtual.d.ts +0 -24
  174. /package/dist/{worker-pool-Bd8Y9KDv.mjs → worker-pool-CGn7DrLb.mjs} +0 -0
@@ -1,19 +0,0 @@
1
- export * from './components/primitives/docs-layout'
2
- export * from './components/primitives/button-group'
3
- export * from './components/primitives/tabs'
4
- export * from './components/primitives/sidebar'
5
- export * from './components/primitives/on-this-page'
6
- export * from './components/primitives/code-block'
7
- export * from './components/primitives/button'
8
- export * from './components/primitives/popover'
9
- export * from './components/primitives/tooltip'
10
- export * from './components/primitives/link'
11
- export * from './components/primitives/error-boundary'
12
- export * from './components/primitives/heading'
13
- export * from './components/primitives/image'
14
- export * from './components/primitives/menu'
15
- export * from './components/primitives/page-nav'
16
- export * from './components/primitives/search-dialog'
17
- export * from './components/primitives/skeleton'
18
- export * from './components/primitives/breadcrumbs'
19
- export * from './components/primitives/navbar'
@@ -1,148 +0,0 @@
1
- import { useEffect, useMemo } from 'react'
2
- import { Outlet, useLocation } from 'react-router-dom'
3
- import { BoltdocsProvider, useBoltdocsContext } from '../store/boltdocs-context'
4
- import { ThemeProvider } from '../app/theme-context'
5
- import { MdxComponentsProvider } from '../app/mdx-components-context'
6
- import { HelmetProvider } from '../app/helmet-compat'
7
- import { ConfigContext } from '../app/config-context'
8
- import { ScrollHandler } from '../app/scroll-handler'
9
- import { mdxComponentsDefault } from '../app/mdx-component'
10
- import { RoutesProvider } from '../app/routes-context'
11
- import type { BoltdocsConfig } from '../../shared/types'
12
- import type { ComponentRoute } from '../types'
13
- import { UIProvider } from '../app/ui-context'
14
-
15
- import virtualCustomComponents from 'virtual:boltdocs-mdx-components'
16
-
17
- /** Normalize a path: strip trailing slash unless it is exactly '/'. */
18
- function normalizePath(p: string): string {
19
- return p.endsWith('/') && p.length > 1 ? p.slice(0, -1) : p
20
- }
21
-
22
- /**
23
- * Updates the HTML lang and dir attributes based on the current locale configuration.
24
- */
25
- function I18nUpdater({ config }: { config: BoltdocsConfig }) {
26
- const { currentLocale } = useBoltdocsContext()
27
-
28
- useEffect(() => {
29
- if (!config.i18n || typeof document === 'undefined') return
30
- const locale = currentLocale || config.i18n.defaultLocale
31
- const localeConfig = config.i18n.localeConfigs?.[locale]
32
- document.documentElement.lang = localeConfig?.htmlLang || locale || 'en'
33
- document.documentElement.dir = localeConfig?.direction || 'ltr'
34
- }, [currentLocale, config.i18n])
35
-
36
- return null
37
- }
38
-
39
- /**
40
- * Synchronizes the Zustand store with the current URL pathname.
41
- * Receives a pre-built Map for O(1) route lookups instead of O(n) .find().
42
- */
43
- function StoreSync({
44
- config,
45
- routeMap,
46
- }: {
47
- config: BoltdocsConfig
48
- routeMap: Map<string, ComponentRoute>
49
- }) {
50
- const location = useLocation()
51
- const { setLocale, setVersion } = useBoltdocsContext()
52
-
53
- useEffect(() => {
54
- const currentPath = normalizePath(location.pathname)
55
- const matchedRoute = routeMap.get(currentPath)
56
-
57
- if (matchedRoute) {
58
- if (config.i18n) {
59
- const targetLocale = matchedRoute.locale || config.i18n.defaultLocale
60
- setLocale(targetLocale)
61
- }
62
- if (config.versions) {
63
- const targetVersion =
64
- matchedRoute.version || config.versions.defaultVersion
65
- setVersion(targetVersion)
66
- }
67
- }
68
- }, [location.pathname, config, routeMap, setLocale, setVersion])
69
-
70
- return null
71
- }
72
-
73
- export function BoltdocsShell({
74
- config,
75
- routes,
76
- components = {},
77
- }: {
78
- config: BoltdocsConfig
79
- routes: ComponentRoute[]
80
- components?: Record<string, React.ComponentType>
81
- }) {
82
- const allComponents = useMemo(
83
- () => ({
84
- ...mdxComponentsDefault,
85
- ...virtualCustomComponents,
86
- ...components,
87
- }),
88
- [components],
89
- )
90
-
91
- const { pathname } = useLocation()
92
-
93
- const currentPath = useMemo(() => normalizePath(pathname || '/'), [pathname])
94
-
95
- // Build a single O(1) lookup Map from the routes array.
96
- // This replaces the 3 separate O(n) .find() calls that previously ran on every render.
97
- const routeMap = useMemo(() => {
98
- const map = new Map<string, ComponentRoute>()
99
- for (const r of routes) {
100
- const key = normalizePath(r.path === '' ? '/' : r.path)
101
- map.set(key, r)
102
- }
103
- return map
104
- }, [routes])
105
-
106
- // Calculate frame-perfect initial values derived AUTHORITATIVELY from the static route match
107
- const initialData = useMemo(() => {
108
- const matched = routeMap.get(currentPath)
109
-
110
- let initLocale = undefined
111
- let initVersion = undefined
112
-
113
- if (matched) {
114
- if (config.i18n) {
115
- initLocale = matched.locale || config.i18n.defaultLocale
116
- }
117
- if (config.versions) {
118
- initVersion = matched.version || config.versions.defaultVersion
119
- }
120
- }
121
-
122
- return { initLocale, initVersion }
123
- }, [currentPath, config, routeMap])
124
-
125
- return (
126
- <HelmetProvider>
127
- <RoutesProvider routes={routes}>
128
- <ThemeProvider>
129
- <UIProvider>
130
- <MdxComponentsProvider components={allComponents}>
131
- <ConfigContext.Provider value={config}>
132
- <ScrollHandler />
133
- <BoltdocsProvider
134
- initialLocale={initialData.initLocale}
135
- initialVersion={initialData.initVersion}
136
- >
137
- <StoreSync config={config} routeMap={routeMap} />
138
- <I18nUpdater config={config} />
139
- <Outlet />
140
- </BoltdocsProvider>
141
- </ConfigContext.Provider>
142
- </MdxComponentsProvider>
143
- </UIProvider>
144
- </ThemeProvider>
145
- </RoutesProvider>
146
- </HelmetProvider>
147
- )
148
- }
@@ -1,473 +0,0 @@
1
- import type { RouteRecord } from '@bdocs/ssg'
2
- import type { ComponentRoute, BoltdocsConfig } from '../types'
3
- import { MdxPage } from './mdx-page'
4
- import { BoltdocsShell } from './boltdocs-shell'
5
- import { NotFound } from '../components/ui-base'
6
- const Loading = () => <div className="text-muted text-sm py-4">Loading...</div>
7
- import type React from 'react'
8
- import { useEffect } from 'react'
9
-
10
- interface CreateRoutesOptions {
11
- routesData: ComponentRoute[]
12
- config: BoltdocsConfig
13
- mdxModules: Record<string, any>
14
- Layout: React.ComponentType<{ children: React.ReactNode }>
15
-
16
- externalPages?: Record<string, React.ComponentType>
17
- externalLayout?: React.ComponentType<{ children: React.ReactNode }>
18
- components?: Record<string, React.ComponentType>
19
- }
20
-
21
- /**
22
- * Stable component to render MDX pages.
23
- * By being outside createRoutes, it prevents React from unmounting the page on HMR.
24
- */
25
- const MdxRouteElement = ({
26
- moduleLoader,
27
- moduleKey,
28
- route,
29
- components,
30
- }: {
31
- moduleLoader: any
32
- moduleKey: string | undefined
33
- route: ComponentRoute
34
- components: any
35
- }) => {
36
- const MDXComponent = moduleLoader?.default ?? moduleLoader ?? null
37
-
38
- useEffect(() => {
39
- if (!import.meta.hot || !moduleKey) return
40
-
41
- const handler = (data: { relPath: string }) => {
42
- const incoming = data.relPath.replace(/\\/g, '/').replace(/^\//, '')
43
- const routeFile = route.filePath.replace(/\\/g, '/').replace(/^\//, '')
44
-
45
- if (incoming !== routeFile) return
46
-
47
- const cacheBustUrl = moduleKey + '?t=' + Date.now()
48
- import(/* @vite-ignore */ cacheBustUrl).then((m: any) => {
49
- MDXComponent
50
- })
51
- }
52
-
53
- import.meta.hot.on('boltdocs:mdx-update', handler)
54
- return () => import.meta.hot?.off('boltdocs:mdx-update', handler)
55
- }, [moduleKey, route.filePath])
56
-
57
- if (!MDXComponent) return <Loading />
58
-
59
- return <MdxPage MDXComponent={MDXComponent} mdxComponents={components} />
60
- }
61
-
62
- import { useMdxComponents } from '../app/mdx-components-context'
63
-
64
- const NotFoundWrapper = () => {
65
- const components = useMdxComponents()
66
- const ActiveNotFound = components.NotFound || components['404'] || NotFound
67
- return <ActiveNotFound />
68
- }
69
-
70
- import { DocsLayout } from '../app/docs-layout'
71
-
72
- export function createRoutes(options: CreateRoutesOptions): RouteRecord[] {
73
- const {
74
- routesData,
75
- config,
76
- mdxModules,
77
- externalPages,
78
- externalLayout,
79
- components,
80
- } = options
81
-
82
- const EffectiveExternalLayout =
83
- externalLayout || (({ children }: any) => <>{children}</>)
84
-
85
- const withBase = (path: string) => {
86
- // Future support for base path in config
87
- const base = config.base || '/'
88
- if (path.startsWith(base)) return path
89
- const b = base === '/' ? '' : base.replace(/\/$/, '')
90
- const p = path.startsWith('/') ? path : `/${path}`
91
- return `${b}${p}` || '/'
92
- }
93
-
94
- const defaultVersionMetadata: ComponentRoute[] = []
95
-
96
- // Inject virtual explicit routes for default version to ensure paths like /docs/latest/... aren't 404s
97
- const defaultVersion = config.versions?.defaultVersion
98
- const docsBase = (config.base || '/docs').replace(/\/$/, '')
99
-
100
- // Base path under which all doc routes are nested (e.g., "/docs")
101
- // Used to compute relative child paths for correct React Router nesting
102
- let baseDocsPath = (config.base || '/docs').replace(/\/$/, '')
103
- if (!baseDocsPath) baseDocsPath = '/'
104
-
105
- if (defaultVersion) {
106
- routesData.forEach((route) => {
107
- // If this route explicitly already belongs to a version, do not clone.
108
- if (route.version) return
109
-
110
- // Compute path without docs base prefix to properly place version token
111
- const p = route.path || ''
112
- const subPath = p.startsWith(docsBase)
113
- ? p.substring(docsBase.length).replace(/^\//, '')
114
- : p.replace(/^\//, '')
115
-
116
- // Detect if it already includes the target version segment
117
- const hasVersionPrefix =
118
- subPath === defaultVersion || subPath.startsWith(`${defaultVersion}/`)
119
-
120
- if (!hasVersionPrefix) {
121
- // Standardize reconstruction: [docsBase] / [version] / [remaining_path]
122
- const explicitPath =
123
- `${docsBase}/${defaultVersion}/${subPath}`
124
- .replace(/\/+/g, '/')
125
- .replace(/\/$/, '') || '/'
126
-
127
- defaultVersionMetadata.push({
128
- ...route,
129
- path: explicitPath,
130
- version: defaultVersion,
131
- })
132
- }
133
- })
134
- }
135
-
136
- const docMetadata = [...routesData, ...defaultVersionMetadata]
137
-
138
- // 0. Build a single pre-computed lookup map for the MDX modules (O(N) build, O(1) access).
139
- // This replaces the inner findModuleKey loops that executed an O(N) scan for EVERY route.
140
- const moduleMap = new Map<string, string>()
141
- const mdxModuleKeys = Object.keys(mdxModules)
142
-
143
- if (mdxModuleKeys.length > 0) {
144
- // Detect docs directory structure from keys (e.g., "/docs/intro.md")
145
- const firstKeyNormalized = mdxModuleKeys[0].replace(/\\/g, '/')
146
- const parts = firstKeyNormalized.split('/').filter(Boolean)
147
- const docsDirName = parts[0] || 'docs'
148
- const primaryPrefix = `/${docsDirName}/`
149
- const altPrefix = `./${docsDirName}/`
150
-
151
- for (const rawKey of mdxModuleKeys) {
152
- const k = rawKey.replace(/\\/g, '/')
153
- let relativePath = ''
154
- if (k.indexOf(primaryPrefix) !== -1) {
155
- relativePath = k.substring(
156
- k.indexOf(primaryPrefix) + primaryPrefix.length,
157
- )
158
- } else if (k.startsWith(altPrefix)) {
159
- relativePath = k.substring(altPrefix.length)
160
- }
161
-
162
- if (relativePath) {
163
- moduleMap.set(relativePath, rawKey)
164
- } else {
165
- // Fallback: store full normalized key as a catch-all
166
- moduleMap.set(k, rawKey)
167
- }
168
- }
169
- }
170
-
171
- // 1. Documentation routes
172
- const docRoutes: RouteRecord[] = docMetadata.map((route) => {
173
- // Perform constant-time lookup using the pre-computed map
174
- const normalizedFilePath = route.filePath.replace(/\\/g, '/')
175
- const moduleKey = moduleMap.get(normalizedFilePath)
176
- const moduleLoader = moduleKey ? mdxModules[moduleKey] : null
177
- const fullPath = withBase(route.path === '' ? '/' : route.path)
178
- const path =
179
- fullPath === baseDocsPath
180
- ? '.'
181
- : fullPath.startsWith(baseDocsPath + '/')
182
- ? fullPath.slice(baseDocsPath.length + 1)
183
- : fullPath
184
-
185
- return {
186
- path,
187
- element: (
188
- <MdxRouteElement
189
- key={moduleKey || path}
190
- moduleKey={moduleKey}
191
- moduleLoader={moduleLoader}
192
- route={route}
193
- components={components}
194
- />
195
- ),
196
- loader: async () => ({
197
- path,
198
- frontmatter: {
199
- title: route.title,
200
- description: route.description || '',
201
- ...(route.frontmatter || {}),
202
- },
203
- headings: route.headings || [],
204
- filePath: route.filePath,
205
- locale: route.locale,
206
- version: route.version,
207
- group: route.group,
208
- groupTitle: route.groupTitle,
209
- date: route.date,
210
- lastUpdated: route.lastUpdated,
211
- }),
212
- getStaticPaths: () => [path],
213
- }
214
- })
215
-
216
- // 2. Auto-fallback for the base paths (e.g. /docs, /docs/es) to the first documentation page
217
-
218
- const locales = config.i18n?.locales
219
- ? Array.isArray(config.i18n.locales)
220
- ? config.i18n.locales
221
- : Object.keys(config.i18n.locales)
222
- : []
223
-
224
- // 2a. Generate dynamic permutation matrix of version/locale combinations
225
- const allVersions = config.versions?.versions?.map((v) => v.path) || []
226
-
227
- const targetBasePaths: Array<{
228
- path: string
229
- filter: (p: string) => boolean
230
- }> = []
231
-
232
- // Insert base root always
233
- targetBasePaths.push({
234
- path: baseDocsPath,
235
- filter: () => true, // Take first available doc generally
236
- })
237
-
238
- // Permutation builder: version loop nested with locale loop
239
- // Ensures paths like /docs/v2.0, /docs/es, and /docs/v2.0/es ALL receive fallback logic.
240
- const subPaths: string[] = []
241
- if (allVersions.length > 0) {
242
- allVersions.forEach((v) => subPaths.push(`/${v}`))
243
- }
244
- if (locales.length > 0) {
245
- locales.forEach((l) => subPaths.push(`/${l}`))
246
- }
247
- if (allVersions.length > 0 && locales.length > 0) {
248
- allVersions.forEach((v) => {
249
- locales.forEach((l) => {
250
- subPaths.push(`/${v}/${l}`)
251
- })
252
- })
253
- }
254
-
255
- // Map permutations onto the physical base docs route
256
- subPaths.forEach((sp) => {
257
- const fullP = baseDocsPath === '/' ? sp : `${baseDocsPath}${sp}`
258
- targetBasePaths.push({
259
- path: fullP,
260
- filter: (rp) => rp.startsWith(fullP.replace(/\/$/, '') + '/'),
261
- })
262
- })
263
-
264
- // Pre-compute a Set of absolute and normalized path strings from the real routes
265
- // to perform O(1) validation checks within the redirection loops below.
266
- const docPathRegistry = new Set(
267
- docRoutes.map((r) => (r.path || '').replace(/\/$/, '')),
268
- )
269
-
270
- // Pre-compute external pages paths so we do not hijack them with redirects
271
- const externalPaths = new Set<string>()
272
- if (externalPages) {
273
- Object.keys(externalPages).forEach((rawPath) => {
274
- const p = rawPath.startsWith('/') ? rawPath : `/${rawPath}`
275
- externalPaths.add(p.replace(/\/$/, ''))
276
- if (config.i18n) {
277
- Object.keys(config.i18n.locales).forEach((locale) => {
278
- externalPaths.add(
279
- `/${locale}${p === '/' ? '' : p}`.replace(/\/$/, ''),
280
- )
281
- })
282
- }
283
- })
284
- }
285
-
286
- // 2b. Deploy smart redirects
287
- targetBasePaths.forEach(({ path: bPath, filter }) => {
288
- if (bPath === '/') return // Never hijack global app root
289
-
290
- const normalizedPath = bPath.replace(/\/$/, '')
291
- const hasExplicitMatch =
292
- docPathRegistry.has(normalizedPath) || externalPaths.has(normalizedPath)
293
-
294
- if (!hasExplicitMatch) {
295
- const defaultTab = config.theme?.tabs?.[0]?.id
296
- const defaultTabPath = defaultTab
297
- ? `${normalizedPath}/${defaultTab}`.replace(/\/+/g, '/')
298
- : null
299
-
300
- // Prioritize: Find a real route that matches the default tab first, then fall back to the first route beginning with this pattern.
301
- let matchedRouteObj: RouteRecord | undefined =
302
- defaultTabPath && docPathRegistry.has(defaultTabPath.replace(/\/$/, ''))
303
- ? docRoutes.find(
304
- (r) =>
305
- r.path.replace(/\/$/, '') === defaultTabPath.replace(/\/$/, ''),
306
- )
307
- : docRoutes.find((r) => filter(r.path) && r.path !== normalizedPath)
308
-
309
- // Ultimate fallback: the absolute first document
310
- if (!matchedRouteObj && docRoutes.length > 0) {
311
- matchedRouteObj = docRoutes[0]
312
- }
313
-
314
- if (matchedRouteObj) {
315
- const redirectPath =
316
- bPath === baseDocsPath
317
- ? '.'
318
- : bPath.startsWith(baseDocsPath + '/')
319
- ? bPath.slice(baseDocsPath.length + 1)
320
- : bPath
321
-
322
- // Use `index: true` for the docs base path (e.g. /docs) instead of
323
- // `path: '.'`. The dot-path is a client-only React Router feature: it
324
- // means "same URL as parent" in createBrowserRouter, but createStaticHandler
325
- // does not recognise it during SSG. This caused the static handler to skip
326
- // the fallback loader entirely → loaderData was empty for /docs → the SSR
327
- // rendered an empty content slot while the client hydrated with the first
328
- // doc's element → structural mismatch → visual page duplication on refresh.
329
- // Index routes are correctly matched by both browser and static handler.
330
- const isBasePathFallback = redirectPath === '.'
331
- docRoutes.push({
332
- ...(isBasePathFallback
333
- ? { index: true as const }
334
- : { path: redirectPath }),
335
- element: matchedRouteObj.element,
336
- loader: matchedRouteObj.loader,
337
- getStaticPaths: () => [],
338
- })
339
-
340
- const matchedMetaObj = docMetadata.find((m) => {
341
- const fullPath = withBase(m.path === '' ? '/' : m.path)
342
- const p =
343
- fullPath === baseDocsPath
344
- ? '.'
345
- : fullPath.startsWith(baseDocsPath + '/')
346
- ? fullPath.slice(baseDocsPath.length + 1)
347
- : fullPath
348
- return p === matchedRouteObj.path
349
- })
350
-
351
- if (matchedMetaObj) {
352
- const canonicalPath = withBase(matchedMetaObj.path)
353
- const canonicalUrl = config.siteUrl
354
- ? `${config.siteUrl.replace(/\/$/, '')}${canonicalPath}`
355
- : canonicalPath
356
-
357
- docMetadata.push({
358
- ...matchedMetaObj,
359
- path: bPath,
360
- filePath: '',
361
- slugParts: [],
362
- seo: {
363
- ...matchedMetaObj.seo,
364
- canonical: canonicalUrl,
365
- },
366
- })
367
- }
368
- }
369
- }
370
- })
371
-
372
- // Group all documentation routes under the persistent DocsLayout
373
- const docsLayoutRoute: RouteRecord = {
374
- path: baseDocsPath,
375
- element: <DocsLayout />,
376
- children: docRoutes,
377
- }
378
-
379
- const children: RouteRecord[] = [docsLayoutRoute]
380
-
381
- // 3. External pages
382
- const externalMetadata: ComponentRoute[] = []
383
- if (externalPages) {
384
- Object.entries(externalPages).forEach(([rawPath, ExtComponent]) => {
385
- // Use the raw path directly (do not prefix with base docs path)
386
- const path = rawPath.startsWith('/') ? rawPath : `/${rawPath}`
387
- if (!children.find((r) => r.path === path)) {
388
- externalMetadata.push({
389
- path,
390
- locale: config.i18n?.defaultLocale,
391
- title:
392
- rawPath === '/'
393
- ? 'Home'
394
- : rawPath.replace(/^\//, '').split('/').pop() || 'Page',
395
- filePath: '',
396
- headings: [],
397
- } as any)
398
-
399
- children.push({
400
- path,
401
- element: (
402
- <EffectiveExternalLayout>
403
- <ExtComponent />
404
- </EffectiveExternalLayout>
405
- ),
406
- loader: async () => ({
407
- path,
408
- locale: config.i18n?.defaultLocale,
409
- }),
410
- getStaticPaths: () => [path],
411
- })
412
-
413
- // Also add i18n variants for external pages if needed (do not prefix with base docs path)
414
- if (config.i18n) {
415
- Object.keys(config.i18n.locales).forEach((locale) => {
416
- const localePath = `/${locale}${rawPath === '/' ? '' : rawPath}`
417
- if (!children.find((r) => r.path === localePath)) {
418
- externalMetadata.push({
419
- path: localePath,
420
- locale,
421
- title: rawPath,
422
- filePath: '',
423
- headings: [],
424
- } as any)
425
-
426
- children.push({
427
- path: localePath,
428
- element: (
429
- <EffectiveExternalLayout>
430
- <ExtComponent />
431
- </EffectiveExternalLayout>
432
- ),
433
- loader: async () => ({
434
- path: localePath,
435
- locale,
436
- }),
437
- getStaticPaths: () => [localePath],
438
- })
439
- }
440
- })
441
- }
442
- }
443
- })
444
- }
445
-
446
- // --- 4. 404 catch-all ---
447
- children.push({
448
- path: '*',
449
- element: (
450
- <EffectiveExternalLayout>
451
- <NotFoundWrapper />
452
- </EffectiveExternalLayout>
453
- ),
454
- })
455
-
456
- const allMetadata = [...docMetadata, ...externalMetadata]
457
-
458
- // Wrap everything in the Boltdocs shell (providers)
459
- return [
460
- {
461
- // No path = Layout Route
462
- // This allows children to retain their absolute paths while being wrapped in the shell.
463
- element: (
464
- <BoltdocsShell
465
- config={config}
466
- routes={allMetadata}
467
- components={components}
468
- />
469
- ),
470
- children,
471
- },
472
- ]
473
- }
@@ -1,4 +0,0 @@
1
- export { ViteReactSSG } from '@bdocs/ssg'
2
- export { createRoutes } from './create-routes'
3
- export { MdxPage } from './mdx-page'
4
- export { BoltdocsShell } from './boltdocs-shell'
@@ -1,38 +0,0 @@
1
- import { useLoaderData } from 'react-router-dom'
2
- import { DocPage } from '../app/doc-page'
3
-
4
- /**
5
- * Renders an MDX page by consuming pre-loaded module data.
6
- * Uses DocPage to ensure consistent layout and metadata application.
7
- */
8
- export function MdxPage({
9
- MDXComponent: propMDX,
10
- mdxComponents: propComponents,
11
- }: any) {
12
- const data = useLoaderData() as any
13
- const MDXComponent = propMDX || data?.MDXComponent
14
- const components = propComponents || data?.mdxComponents
15
- if (!MDXComponent) return null
16
-
17
- return (
18
- <DocPage
19
- route={
20
- {
21
- path: data.path,
22
- filePath: data.filePath,
23
- title: data.frontmatter.title,
24
- description: data.frontmatter.description,
25
- headings: data.headings,
26
- locale: data.locale,
27
- version: data.version,
28
- group: data.group,
29
- groupTitle: data.groupTitle,
30
- lastUpdated: data.lastUpdated,
31
- frontmatter: data.frontmatter,
32
- } as any
33
- }
34
- content={MDXComponent}
35
- mdxComponents={components}
36
- />
37
- )
38
- }