boltdocs 2.5.5 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/boltdocs.js +2 -2
- package/dist/client/index.cjs +6 -0
- package/dist/client/index.d.cts +1560 -0
- package/dist/client/index.d.ts +1219 -922
- package/dist/client/index.js +6 -1
- package/dist/client/theme/neutral.css +428 -0
- package/dist/node/cli-entry.cjs +8 -0
- package/dist/node/cli-entry.d.cts +2 -0
- package/dist/node/cli-entry.d.mts +2 -1
- package/dist/node/cli-entry.mjs +7 -5
- package/dist/node/index.cjs +6 -0
- package/dist/node/index.d.cts +519 -0
- package/dist/node/index.d.mts +374 -422
- package/dist/node/index.mjs +6 -1
- package/dist/node-BgvNl2Ay.mjs +89 -0
- package/dist/node-vkbb0MK7.cjs +89 -0
- package/dist/package-CR0HF9x3.mjs +6 -0
- package/dist/package-Dgmsc_l5.cjs +6 -0
- package/dist/search-dialog-3lvKsbVG.js +6 -0
- package/dist/search-dialog-DMK5OpgH.cjs +6 -0
- package/dist/use-search-C9bxCqfF.js +6 -0
- package/dist/use-search-DcfZSunO.cjs +6 -0
- package/package.json +26 -25
- package/src/client/app/config-context.tsx +38 -5
- package/src/client/app/doc-page.tsx +34 -0
- package/src/client/app/mdx-component.tsx +2 -3
- package/src/client/app/mdx-components-context.tsx +27 -2
- package/src/client/app/routes-context.tsx +34 -0
- package/src/client/app/scroll-handler.tsx +7 -4
- package/src/client/app/theme-context.tsx +71 -67
- package/src/client/components/default-layout.tsx +34 -33
- package/src/client/components/docs-layout.tsx +1 -2
- package/src/client/components/icons-dev.tsx +36 -5
- package/src/client/components/mdx/admonition.tsx +11 -27
- package/src/client/components/mdx/badge.tsx +1 -1
- package/src/client/components/mdx/button.tsx +3 -3
- package/src/client/components/mdx/card.tsx +1 -1
- package/src/client/components/mdx/code-block.tsx +90 -80
- package/src/client/components/mdx/component-preview.tsx +1 -5
- package/src/client/components/mdx/component-props.tsx +1 -1
- package/src/client/components/mdx/field.tsx +4 -5
- package/src/client/components/mdx/file-tree.tsx +6 -3
- package/src/client/components/mdx/hooks/use-code-block.ts +2 -2
- package/src/client/components/mdx/image.tsx +1 -1
- package/src/client/components/mdx/link.tsx +2 -2
- package/src/client/components/mdx/list.tsx +1 -1
- package/src/client/components/mdx/table.tsx +1 -1
- package/src/client/components/mdx/tabs.tsx +1 -1
- package/src/client/components/primitives/breadcrumbs.tsx +1 -7
- package/src/client/components/primitives/button-group.tsx +1 -1
- package/src/client/components/primitives/button.tsx +1 -1
- package/src/client/components/primitives/code-block.tsx +113 -0
- package/src/client/components/primitives/link.tsx +23 -41
- package/src/client/components/primitives/menu.tsx +5 -6
- package/src/client/components/primitives/navbar.tsx +6 -18
- package/src/client/components/primitives/navigation-menu.tsx +4 -4
- package/src/client/components/primitives/on-this-page.tsx +6 -10
- package/src/client/components/primitives/page-nav.tsx +4 -9
- package/src/client/components/primitives/popover.tsx +1 -1
- package/src/client/components/primitives/search-dialog.tsx +3 -6
- package/src/client/components/primitives/sidebar.tsx +80 -22
- package/src/client/components/primitives/skeleton.tsx +1 -1
- package/src/client/components/primitives/tabs.tsx +4 -11
- package/src/client/components/primitives/tooltip.tsx +3 -3
- package/src/client/components/ui-base/breadcrumbs.tsx +4 -6
- package/src/client/components/ui-base/copy-markdown.tsx +2 -7
- package/src/client/components/ui-base/github-stars.tsx +2 -2
- package/src/client/components/ui-base/head.tsx +58 -51
- package/src/client/components/ui-base/loading.tsx +2 -2
- package/src/client/components/ui-base/navbar.tsx +12 -14
- package/src/client/components/ui-base/not-found.tsx +1 -1
- package/src/client/components/ui-base/on-this-page.tsx +6 -6
- package/src/client/components/ui-base/page-nav.tsx +4 -8
- package/src/client/components/ui-base/search-dialog.tsx +10 -8
- package/src/client/components/ui-base/sidebar.tsx +76 -23
- package/src/client/components/ui-base/tabs.tsx +9 -8
- package/src/client/components/ui-base/theme-toggle.tsx +2 -2
- package/src/client/hooks/use-i18n.ts +3 -3
- package/src/client/hooks/use-localized-to.ts +1 -1
- package/src/client/hooks/use-navbar.ts +8 -6
- package/src/client/hooks/use-routes.ts +19 -11
- package/src/client/hooks/use-search.ts +1 -1
- package/src/client/hooks/use-sidebar.ts +48 -2
- package/src/client/hooks/use-tabs.ts +6 -2
- package/src/client/hooks/use-version.ts +3 -3
- package/src/client/index.ts +22 -22
- package/src/client/ssg/boltdocs-shell.tsx +127 -0
- package/src/client/ssg/create-routes.tsx +179 -0
- package/src/client/ssg/index.ts +3 -0
- package/src/client/ssg/mdx-page.tsx +37 -0
- package/src/client/store/boltdocs-context.tsx +66 -0
- package/src/client/theme/neutral.css +90 -50
- package/src/client/types.ts +5 -33
- package/src/client/utils/react-to-text.ts +34 -0
- package/CHANGELOG.md +0 -98
- package/dist/cache-3FOEPC2P.mjs +0 -1
- package/dist/chunk-5B5NKOW6.mjs +0 -77
- package/dist/chunk-J2PTDWZM.mjs +0 -1
- package/dist/chunk-TP5KMRD3.mjs +0 -1
- package/dist/chunk-Y4RE5KI7.mjs +0 -1
- package/dist/client/index.d.mts +0 -1263
- package/dist/client/index.mjs +0 -1
- package/dist/client/ssr.d.mts +0 -78
- package/dist/client/ssr.d.ts +0 -78
- package/dist/client/ssr.js +0 -1
- package/dist/client/ssr.mjs +0 -1
- package/dist/node/cli-entry.d.ts +0 -1
- package/dist/node/cli-entry.js +0 -82
- package/dist/node/index.d.ts +0 -567
- package/dist/node/index.js +0 -77
- package/dist/package-QFIAETHR.mjs +0 -1
- package/dist/search-dialog-O6VLVSOA.mjs +0 -1
- package/src/client/app/index.tsx +0 -345
- package/src/client/app/mdx-page.tsx +0 -15
- package/src/client/app/preload.tsx +0 -66
- package/src/client/app/router.tsx +0 -30
- package/src/client/integrations/codesandbox.ts +0 -179
- package/src/client/integrations/index.ts +0 -1
- package/src/client/ssr.tsx +0 -65
- package/src/client/store/use-boltdocs-store.ts +0 -44
- package/src/node/cache.ts +0 -408
- package/src/node/cli/build.ts +0 -53
- package/src/node/cli/dev.ts +0 -22
- package/src/node/cli/doctor.ts +0 -243
- package/src/node/cli/index.ts +0 -9
- package/src/node/cli/ui.ts +0 -54
- package/src/node/cli-entry.ts +0 -24
- package/src/node/config.ts +0 -382
- package/src/node/errors.ts +0 -44
- package/src/node/index.ts +0 -84
- package/src/node/mdx/cache.ts +0 -12
- package/src/node/mdx/highlighter.ts +0 -47
- package/src/node/mdx/index.ts +0 -122
- package/src/node/mdx/rehype-shiki.ts +0 -62
- package/src/node/mdx/remark-code-meta.ts +0 -35
- package/src/node/mdx/remark-shiki.ts +0 -61
- package/src/node/plugin/entry.ts +0 -87
- package/src/node/plugin/html.ts +0 -99
- package/src/node/plugin/index.ts +0 -478
- package/src/node/plugin/types.ts +0 -9
- package/src/node/plugins/index.ts +0 -17
- package/src/node/plugins/plugin-errors.ts +0 -62
- package/src/node/plugins/plugin-lifecycle.ts +0 -117
- package/src/node/plugins/plugin-sandbox.ts +0 -59
- package/src/node/plugins/plugin-store.ts +0 -54
- package/src/node/plugins/plugin-types.ts +0 -107
- package/src/node/plugins/plugin-validator.ts +0 -105
- package/src/node/routes/cache.ts +0 -28
- package/src/node/routes/index.ts +0 -293
- package/src/node/routes/parser.ts +0 -262
- package/src/node/routes/sorter.ts +0 -42
- package/src/node/routes/types.ts +0 -61
- package/src/node/schema/config.ts +0 -195
- package/src/node/schema/frontmatter.ts +0 -17
- package/src/node/search/index.ts +0 -55
- package/src/node/security/constants/index.ts +0 -10
- package/src/node/security/csp.ts +0 -31
- package/src/node/security/headers.ts +0 -27
- package/src/node/ssg/index.ts +0 -205
- package/src/node/ssg/meta.ts +0 -33
- package/src/node/ssg/options.ts +0 -15
- package/src/node/ssg/robots.ts +0 -53
- package/src/node/ssg/sitemap.ts +0 -55
- package/src/node/utils.ts +0 -349
- package/tsconfig.json +0 -26
- package/tsup.config.ts +0 -56
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useLocation } from 'react-router-dom'
|
|
2
|
-
import { useConfig } from '
|
|
2
|
+
import { useConfig } from '../app/config-context'
|
|
3
3
|
import type { ComponentRoute } from '../types'
|
|
4
4
|
|
|
5
5
|
export function useSidebar(routes: ComponentRoute[]) {
|
|
@@ -41,7 +41,53 @@ export function useSidebar(routes: ComponentRoute[]) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const groups = Array.from(groupsMap.values())
|
|
44
|
+
const groups = Array.from(groupsMap.values()).map((group) => {
|
|
45
|
+
const subRouteParents = new Map<string, ComponentRoute>()
|
|
46
|
+
const subRouteChildren = new Map<string, ComponentRoute[]>()
|
|
47
|
+
|
|
48
|
+
// First pass: Categorize as parent or child
|
|
49
|
+
for (const route of group.routes) {
|
|
50
|
+
if (route.subRouteGroup) {
|
|
51
|
+
const isParent =
|
|
52
|
+
route.path.endsWith(`/${route.subRouteGroup}`) ||
|
|
53
|
+
route.path.endsWith(`/${route.subRouteGroup}/`)
|
|
54
|
+
|
|
55
|
+
if (isParent && !subRouteParents.has(route.subRouteGroup)) {
|
|
56
|
+
subRouteParents.set(route.subRouteGroup, route)
|
|
57
|
+
} else {
|
|
58
|
+
if (!subRouteChildren.has(route.subRouteGroup)) {
|
|
59
|
+
subRouteChildren.set(route.subRouteGroup, [])
|
|
60
|
+
}
|
|
61
|
+
subRouteChildren.get(route.subRouteGroup)!.push(route)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const finalRoutes: ComponentRoute[] = []
|
|
67
|
+
const seenSubGroups = new Set<string>()
|
|
68
|
+
|
|
69
|
+
// Second pass: Assemble maintaining mostly original order
|
|
70
|
+
for (const route of group.routes) {
|
|
71
|
+
if (route.subRouteGroup) {
|
|
72
|
+
if (!seenSubGroups.has(route.subRouteGroup)) {
|
|
73
|
+
seenSubGroups.add(route.subRouteGroup)
|
|
74
|
+
const parent = subRouteParents.get(route.subRouteGroup)
|
|
75
|
+
const children = subRouteChildren.get(route.subRouteGroup) || []
|
|
76
|
+
|
|
77
|
+
if (parent) {
|
|
78
|
+
finalRoutes.push({ ...parent, subRoutes: children })
|
|
79
|
+
} else {
|
|
80
|
+
// Fallback
|
|
81
|
+
finalRoutes.push(...children)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
finalRoutes.push(route)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { ...group, routes: finalRoutes }
|
|
90
|
+
})
|
|
45
91
|
|
|
46
92
|
return {
|
|
47
93
|
groups,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useLocation } from 'react-router-dom'
|
|
2
2
|
import { useEffect, useState, useRef } from 'react'
|
|
3
|
-
import type { ComponentRoute, BoltdocsTab } from '
|
|
3
|
+
import type { ComponentRoute, BoltdocsTab } from '../types'
|
|
4
4
|
|
|
5
5
|
export function useTabs(
|
|
6
6
|
tabs: BoltdocsTab[] = [],
|
|
@@ -14,7 +14,11 @@ export function useTabs(
|
|
|
14
14
|
width: 0,
|
|
15
15
|
})
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const normalize = (p: string) =>
|
|
18
|
+
p.endsWith('/') && p.length > 1 ? p.slice(0, -1) : p
|
|
19
|
+
const currentPath = normalize(location.pathname)
|
|
20
|
+
|
|
21
|
+
const activeRoute = routes.find((r) => normalize(r.path) === currentPath)
|
|
18
22
|
const activeTabId = activeRoute?.tab?.toLowerCase()
|
|
19
23
|
const activeIndex = tabs.findIndex(
|
|
20
24
|
(tab) => tab.id.toLowerCase() === activeTabId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useNavigate } from 'react-router-dom'
|
|
2
|
-
import { getBaseFilePath } from '
|
|
2
|
+
import { getBaseFilePath } from '../utils/get-base-file-path'
|
|
3
3
|
import { useRoutes } from './use-routes'
|
|
4
|
-
import {
|
|
4
|
+
import { useBoltdocsContext } from '../store/boltdocs-context'
|
|
5
5
|
|
|
6
6
|
export interface VersionOption {
|
|
7
7
|
key: string
|
|
@@ -26,7 +26,7 @@ export function useVersion(): UseVersionReturn {
|
|
|
26
26
|
const { allRoutes, currentRoute, currentVersion, currentLocale, config } =
|
|
27
27
|
routeContext
|
|
28
28
|
const versions = config.versions
|
|
29
|
-
const setVersion =
|
|
29
|
+
const { setVersion } = useBoltdocsContext()
|
|
30
30
|
|
|
31
31
|
const handleVersionChange = (version: string) => {
|
|
32
32
|
if (!versions || version === currentVersion) return
|
package/src/client/index.ts
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
export type { BoltdocsConfig, BoltdocsThemeConfig } from '../
|
|
1
|
+
export type { BoltdocsConfig, BoltdocsThemeConfig } from '../shared/types'
|
|
2
|
+
export { defineConfig } from '../shared/config-utils'
|
|
2
3
|
export type {
|
|
3
4
|
ComponentRoute,
|
|
4
|
-
CreateBoltdocsAppOptions,
|
|
5
5
|
LayoutProps,
|
|
6
6
|
} from './types'
|
|
7
|
-
export
|
|
8
|
-
export { useConfig } from '
|
|
9
|
-
export { useTheme } from '
|
|
10
|
-
export { useRoutes } from '
|
|
11
|
-
export { useMdxComponents } from '
|
|
7
|
+
export * from './ssg'
|
|
8
|
+
export { useConfig } from './app/config-context'
|
|
9
|
+
export { useTheme } from './app/theme-context'
|
|
10
|
+
export { useRoutes } from './hooks/use-routes'
|
|
11
|
+
export { useMdxComponents } from './app/mdx-components-context'
|
|
12
12
|
|
|
13
13
|
// Hooks
|
|
14
|
-
export * from '
|
|
14
|
+
export * from './hooks/index'
|
|
15
15
|
// Composable layout building blocks
|
|
16
|
-
export { DocsLayout } from '
|
|
17
|
-
export { DefaultLayout } from '
|
|
16
|
+
export { DocsLayout } from './components/docs-layout'
|
|
17
|
+
export { DefaultLayout } from './components/default-layout'
|
|
18
18
|
|
|
19
19
|
// Default UI components (for use in custom layout.tsx)
|
|
20
|
-
export { Navbar } from '
|
|
21
|
-
export { Sidebar } from '
|
|
22
|
-
export { OnThisPage } from '
|
|
23
|
-
export { Head } from '
|
|
24
|
-
export { Breadcrumbs } from '
|
|
25
|
-
export { PageNav } from '
|
|
26
|
-
export { ErrorBoundary } from '
|
|
27
|
-
export { CopyMarkdown } from '
|
|
20
|
+
export { Navbar } from './components/ui-base/navbar'
|
|
21
|
+
export { Sidebar } from './components/ui-base/sidebar'
|
|
22
|
+
export { OnThisPage } from './components/ui-base/on-this-page'
|
|
23
|
+
export { Head } from './components/ui-base/head'
|
|
24
|
+
export { Breadcrumbs } from './components/ui-base/breadcrumbs'
|
|
25
|
+
export { PageNav } from './components/ui-base/page-nav'
|
|
26
|
+
export { ErrorBoundary } from './components/ui-base/error-boundary'
|
|
27
|
+
export { CopyMarkdown } from './components/ui-base/copy-markdown'
|
|
28
28
|
|
|
29
|
-
export { NotFound } from '
|
|
30
|
-
export { Loading } from '
|
|
31
|
-
export { CodeBlock } from '
|
|
32
|
-
export { Video } from '
|
|
29
|
+
export { NotFound } from './components/ui-base/not-found'
|
|
30
|
+
export { Loading } from './components/ui-base/loading'
|
|
31
|
+
export { CodeBlock } from './components/mdx/code-block'
|
|
32
|
+
export { Video } from './components/mdx/video'
|
|
33
33
|
|
|
34
34
|
// MDX Components
|
|
35
35
|
export {
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useEffect, useMemo } from 'react'
|
|
2
|
+
import { Outlet, useLocation, useNavigate } from 'react-router-dom'
|
|
3
|
+
import { RouterProvider } from 'react-aria-components'
|
|
4
|
+
import { BoltdocsProvider, useBoltdocsContext } from '../store/boltdocs-context'
|
|
5
|
+
import { ThemeProvider } from '../app/theme-context'
|
|
6
|
+
import { MdxComponentsProvider } from '../app/mdx-components-context'
|
|
7
|
+
import { HelmetProvider } from 'react-helmet-async'
|
|
8
|
+
import { ConfigContext } from '../app/config-context'
|
|
9
|
+
import { ScrollHandler } from '../app/scroll-handler'
|
|
10
|
+
import { mdxComponentsDefault } from '../app/mdx-component'
|
|
11
|
+
import { RoutesProvider } from '../app/routes-context'
|
|
12
|
+
import type { BoltdocsConfig } from '../../shared/types'
|
|
13
|
+
import type { ComponentRoute } from '../types'
|
|
14
|
+
|
|
15
|
+
import virtualCustomComponents from 'virtual:boltdocs-mdx-components'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Updates the HTML lang and dir attributes based on the current locale configuration.
|
|
19
|
+
*/
|
|
20
|
+
function I18nUpdater({ config }: { config: BoltdocsConfig }) {
|
|
21
|
+
const { currentLocale } = useBoltdocsContext()
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (!config.i18n || typeof document === 'undefined') return
|
|
25
|
+
const locale = currentLocale || config.i18n.defaultLocale
|
|
26
|
+
const localeConfig = config.i18n.localeConfigs?.[locale]
|
|
27
|
+
document.documentElement.lang = localeConfig?.htmlLang || locale || 'en'
|
|
28
|
+
document.documentElement.dir = localeConfig?.direction || 'ltr'
|
|
29
|
+
}, [currentLocale, config.i18n])
|
|
30
|
+
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Synchronizes the Zustand store with the current URL pathname.
|
|
36
|
+
*/
|
|
37
|
+
function StoreSync({ config }: { config: BoltdocsConfig }) {
|
|
38
|
+
const location = useLocation()
|
|
39
|
+
const { setLocale, setVersion, currentLocale, currentVersion } =
|
|
40
|
+
useBoltdocsContext()
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const parts = location.pathname.split('/').filter(Boolean)
|
|
44
|
+
let cIdx = 0
|
|
45
|
+
let detectedVersion = config.versions?.defaultVersion
|
|
46
|
+
let detectedLocale = config.i18n?.defaultLocale
|
|
47
|
+
|
|
48
|
+
// 0. Skip docs prefix if present
|
|
49
|
+
if (parts[cIdx] === 'docs') cIdx++
|
|
50
|
+
|
|
51
|
+
// 1. Version detection
|
|
52
|
+
if (config.versions && parts.length > cIdx) {
|
|
53
|
+
const versionMatch = config.versions.versions.find(
|
|
54
|
+
(v) => v.path === parts[cIdx],
|
|
55
|
+
)
|
|
56
|
+
if (versionMatch) {
|
|
57
|
+
detectedVersion = versionMatch.path
|
|
58
|
+
cIdx++
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. Locale detection
|
|
63
|
+
if (
|
|
64
|
+
config.i18n &&
|
|
65
|
+
parts.length > cIdx &&
|
|
66
|
+
config.i18n.locales[parts[cIdx]]
|
|
67
|
+
) {
|
|
68
|
+
detectedLocale = parts[cIdx]
|
|
69
|
+
} else if (config.i18n && parts.length === 0) {
|
|
70
|
+
detectedLocale = currentLocale || config.i18n.defaultLocale
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (detectedLocale !== currentLocale) setLocale(detectedLocale || '')
|
|
74
|
+
if (detectedVersion !== currentVersion) setVersion(detectedVersion ?? '')
|
|
75
|
+
}, [
|
|
76
|
+
location.pathname,
|
|
77
|
+
config,
|
|
78
|
+
setLocale,
|
|
79
|
+
setVersion,
|
|
80
|
+
currentLocale,
|
|
81
|
+
currentVersion,
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function BoltdocsShell({
|
|
88
|
+
config,
|
|
89
|
+
routes,
|
|
90
|
+
components = {},
|
|
91
|
+
}: {
|
|
92
|
+
config: BoltdocsConfig
|
|
93
|
+
routes: ComponentRoute[]
|
|
94
|
+
components?: Record<string, React.ComponentType>
|
|
95
|
+
}) {
|
|
96
|
+
const allComponents = useMemo(
|
|
97
|
+
() => ({
|
|
98
|
+
...mdxComponentsDefault,
|
|
99
|
+
...virtualCustomComponents,
|
|
100
|
+
...components,
|
|
101
|
+
}),
|
|
102
|
+
[components],
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
const navigate = useNavigate()
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<HelmetProvider>
|
|
109
|
+
<BoltdocsProvider>
|
|
110
|
+
<ThemeProvider>
|
|
111
|
+
<MdxComponentsProvider components={allComponents}>
|
|
112
|
+
<ConfigContext.Provider value={config}>
|
|
113
|
+
<RoutesProvider routes={routes}>
|
|
114
|
+
<RouterProvider navigate={navigate}>
|
|
115
|
+
<ScrollHandler />
|
|
116
|
+
<StoreSync config={config} />
|
|
117
|
+
<I18nUpdater config={config} />
|
|
118
|
+
<Outlet />
|
|
119
|
+
</RouterProvider>
|
|
120
|
+
</RoutesProvider>
|
|
121
|
+
</ConfigContext.Provider>
|
|
122
|
+
</MdxComponentsProvider>
|
|
123
|
+
</ThemeProvider>
|
|
124
|
+
</BoltdocsProvider>
|
|
125
|
+
</HelmetProvider>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
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/not-found'
|
|
6
|
+
|
|
7
|
+
interface CreateRoutesOptions {
|
|
8
|
+
routesData: ComponentRoute[]
|
|
9
|
+
config: BoltdocsConfig
|
|
10
|
+
mdxModules: Record<string, { default?: React.ComponentType }>
|
|
11
|
+
Layout: React.ComponentType<{ children: React.ReactNode }>
|
|
12
|
+
homePage?: React.ComponentType
|
|
13
|
+
externalPages?: Record<string, React.ComponentType>
|
|
14
|
+
externalLayout?: React.ComponentType<{ children: React.ReactNode }>
|
|
15
|
+
components?: Record<string, React.ComponentType>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Finds the matching module key from import.meta.glob for a given filePath.
|
|
20
|
+
*/
|
|
21
|
+
function findModuleKey(
|
|
22
|
+
modules: Record<string, any>,
|
|
23
|
+
filePath: string,
|
|
24
|
+
): string | undefined {
|
|
25
|
+
const normalizedFilePath = filePath.replace(/\\/g, '/')
|
|
26
|
+
return Object.keys(modules).find(
|
|
27
|
+
(key) =>
|
|
28
|
+
key.endsWith(`/${normalizedFilePath}`) ||
|
|
29
|
+
key.endsWith(normalizedFilePath),
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createRoutes(options: CreateRoutesOptions): RouteRecord[] {
|
|
34
|
+
const {
|
|
35
|
+
routesData,
|
|
36
|
+
config,
|
|
37
|
+
mdxModules,
|
|
38
|
+
Layout,
|
|
39
|
+
homePage: HomePage,
|
|
40
|
+
externalPages,
|
|
41
|
+
externalLayout,
|
|
42
|
+
components,
|
|
43
|
+
} = options
|
|
44
|
+
|
|
45
|
+
const EffectiveExternalLayout = externalLayout || Layout
|
|
46
|
+
|
|
47
|
+
const withBase = (path: string) => {
|
|
48
|
+
// Future support for base path in config
|
|
49
|
+
const base = config.base || '/'
|
|
50
|
+
if (path.startsWith(base)) return path
|
|
51
|
+
const b = base === '/' ? '' : base.replace(/\/$/, '')
|
|
52
|
+
const p = path.startsWith('/') ? path : `/${path}`
|
|
53
|
+
return `${b}${p}` || '/'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 1. Documentation routes
|
|
57
|
+
const docRoutes: RouteRecord[] = routesData.map((route) => {
|
|
58
|
+
const moduleKey = findModuleKey(mdxModules, route.filePath)
|
|
59
|
+
const MDXComponent = moduleKey ? mdxModules[moduleKey]?.default : null
|
|
60
|
+
|
|
61
|
+
const path = withBase(route.path === '' ? '/' : route.path)
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
path,
|
|
65
|
+
element: (
|
|
66
|
+
<MdxPage MDXComponent={MDXComponent} mdxComponents={components} />
|
|
67
|
+
),
|
|
68
|
+
loader: async () => ({
|
|
69
|
+
path,
|
|
70
|
+
frontmatter: {
|
|
71
|
+
title: route.title,
|
|
72
|
+
description: route.description || '',
|
|
73
|
+
},
|
|
74
|
+
headings: route.headings || [],
|
|
75
|
+
filePath: route.filePath,
|
|
76
|
+
locale: route.locale,
|
|
77
|
+
version: route.version,
|
|
78
|
+
group: route.group,
|
|
79
|
+
groupTitle: route.groupTitle,
|
|
80
|
+
}),
|
|
81
|
+
getStaticPaths: () => [path],
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const children: RouteRecord[] = [...docRoutes]
|
|
86
|
+
|
|
87
|
+
// 2. Home page route
|
|
88
|
+
if (HomePage) {
|
|
89
|
+
const homePaths = [withBase('/')]
|
|
90
|
+
if (config.i18n) {
|
|
91
|
+
Object.keys(config.i18n.locales).forEach((locale) => {
|
|
92
|
+
homePaths.push(withBase(`/${locale}`))
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
homePaths.forEach((path) => {
|
|
97
|
+
// Avoid duplicate routes if documentation also maps to '/'
|
|
98
|
+
if (!children.find((r) => r.path === path)) {
|
|
99
|
+
children.push({
|
|
100
|
+
path,
|
|
101
|
+
element: (
|
|
102
|
+
<EffectiveExternalLayout>
|
|
103
|
+
<HomePage />
|
|
104
|
+
</EffectiveExternalLayout>
|
|
105
|
+
),
|
|
106
|
+
getStaticPaths: () => [path],
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 3. External pages
|
|
113
|
+
if (externalPages) {
|
|
114
|
+
Object.entries(externalPages).forEach(([rawPath, ExtComponent]) => {
|
|
115
|
+
const path = withBase(rawPath)
|
|
116
|
+
if (!children.find((r) => r.path === path)) {
|
|
117
|
+
children.push({
|
|
118
|
+
path,
|
|
119
|
+
element: (
|
|
120
|
+
<EffectiveExternalLayout>
|
|
121
|
+
<ExtComponent />
|
|
122
|
+
</EffectiveExternalLayout>
|
|
123
|
+
),
|
|
124
|
+
getStaticPaths: () => [path],
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Also add i18n variants for external pages if needed
|
|
128
|
+
if (config.i18n) {
|
|
129
|
+
Object.keys(config.i18n.locales).forEach((locale) => {
|
|
130
|
+
const localePath = withBase(
|
|
131
|
+
`/${locale}${rawPath === '/' ? '' : rawPath}`,
|
|
132
|
+
)
|
|
133
|
+
if (!children.find((r) => r.path === localePath)) {
|
|
134
|
+
children.push({
|
|
135
|
+
path: localePath,
|
|
136
|
+
element: (
|
|
137
|
+
<EffectiveExternalLayout>
|
|
138
|
+
<ExtComponent />
|
|
139
|
+
</EffectiveExternalLayout>
|
|
140
|
+
),
|
|
141
|
+
getStaticPaths: () => [localePath],
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// --- 4. 404 catch-all ---
|
|
151
|
+
children.push({
|
|
152
|
+
path: '*',
|
|
153
|
+
element: (
|
|
154
|
+
<EffectiveExternalLayout>
|
|
155
|
+
<NotFound />
|
|
156
|
+
</EffectiveExternalLayout>
|
|
157
|
+
),
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// --- 5. Construct Metadata for UI Providers ---
|
|
161
|
+
// We need to pass the full metadata to BoltdocsShell so that Sidebar/Tabs can work.
|
|
162
|
+
const allMetadata: ComponentRoute[] = [...routesData]
|
|
163
|
+
|
|
164
|
+
// Wrap everything in the Boltdocs shell (providers)
|
|
165
|
+
return [
|
|
166
|
+
{
|
|
167
|
+
// No path = Layout Route
|
|
168
|
+
// This allows children to retain their absolute paths while being wrapped in the shell.
|
|
169
|
+
element: (
|
|
170
|
+
<BoltdocsShell
|
|
171
|
+
config={config}
|
|
172
|
+
routes={allMetadata}
|
|
173
|
+
components={components}
|
|
174
|
+
/>
|
|
175
|
+
),
|
|
176
|
+
children,
|
|
177
|
+
},
|
|
178
|
+
]
|
|
179
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
|
|
16
|
+
if (!MDXComponent) return null
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<DocPage
|
|
20
|
+
route={
|
|
21
|
+
{
|
|
22
|
+
path: data.path,
|
|
23
|
+
filePath: data.filePath,
|
|
24
|
+
title: data.frontmatter.title,
|
|
25
|
+
description: data.frontmatter.description,
|
|
26
|
+
headings: data.headings,
|
|
27
|
+
locale: data.locale,
|
|
28
|
+
version: data.version,
|
|
29
|
+
group: data.group,
|
|
30
|
+
groupTitle: data.groupTitle,
|
|
31
|
+
} as any
|
|
32
|
+
}
|
|
33
|
+
content={MDXComponent}
|
|
34
|
+
mdxComponents={components}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createContext, use, useMemo, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface BoltdocsState {
|
|
4
|
+
currentLocale: string
|
|
5
|
+
currentVersion: string
|
|
6
|
+
setLocale: (locale: string) => void
|
|
7
|
+
setVersion: (version: string) => void
|
|
8
|
+
hasHydrated: boolean
|
|
9
|
+
setHasHydrated: (hasHydrated: boolean) => void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const BOLTDOCS_CONTEXT_SYMBOL = Symbol.for('__BDOCS_BOLTDOCS_CONTEXT__')
|
|
13
|
+
const BOLTDOCS_INSTANCE_SYMBOL = Symbol.for('__BDOCS_BOLTDOCS_INSTANCE__')
|
|
14
|
+
|
|
15
|
+
const BoltdocsContext =
|
|
16
|
+
(globalThis as any)[BOLTDOCS_CONTEXT_SYMBOL] ||
|
|
17
|
+
((globalThis as any)[BOLTDOCS_CONTEXT_SYMBOL] = createContext<
|
|
18
|
+
BoltdocsState | undefined
|
|
19
|
+
>(undefined))
|
|
20
|
+
|
|
21
|
+
export function BoltdocsProvider({ children }: { children: React.ReactNode }) {
|
|
22
|
+
const [locale, setLocale] = useState('')
|
|
23
|
+
const [version, setVersion] = useState('')
|
|
24
|
+
const [hasHydrated, setHasHydrated] = useState(false)
|
|
25
|
+
|
|
26
|
+
const value = useMemo(
|
|
27
|
+
() => ({
|
|
28
|
+
currentLocale: locale,
|
|
29
|
+
currentVersion: version,
|
|
30
|
+
setLocale,
|
|
31
|
+
setVersion,
|
|
32
|
+
hasHydrated,
|
|
33
|
+
setHasHydrated,
|
|
34
|
+
}),
|
|
35
|
+
[locale, version, hasHydrated],
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
// Sync with global registry for dual-package fallback
|
|
39
|
+
if (typeof globalThis !== 'undefined') {
|
|
40
|
+
;(globalThis as any)[BOLTDOCS_INSTANCE_SYMBOL] = value
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<BoltdocsContext.Provider value={value}>
|
|
45
|
+
{children}
|
|
46
|
+
</BoltdocsContext.Provider>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function useBoltdocsContext() {
|
|
51
|
+
const context = use(BoltdocsContext)
|
|
52
|
+
|
|
53
|
+
// Fallback to global registry if context is missing (dual-package hazard safety net)
|
|
54
|
+
if (
|
|
55
|
+
!context &&
|
|
56
|
+
typeof globalThis !== 'undefined' &&
|
|
57
|
+
(globalThis as any)[BOLTDOCS_INSTANCE_SYMBOL]
|
|
58
|
+
) {
|
|
59
|
+
return (globalThis as any)[BOLTDOCS_INSTANCE_SYMBOL] as BoltdocsState
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!context) {
|
|
63
|
+
throw new Error('useBoltdocsContext must be used within a BoltdocsProvider')
|
|
64
|
+
}
|
|
65
|
+
return context as BoltdocsState
|
|
66
|
+
}
|