boltdocs 1.10.2 → 1.11.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/package.json +29 -7
- package/src/client/app/config-context.tsx +18 -0
- package/src/client/app/docs-layout.tsx +14 -0
- package/src/client/app/index.tsx +132 -260
- package/src/client/app/mdx-component.tsx +52 -0
- package/src/client/app/mdx-components-context.tsx +23 -0
- package/src/client/app/mdx-page.tsx +20 -0
- package/src/client/app/preload.tsx +38 -30
- package/src/client/app/router.tsx +30 -0
- package/src/client/app/scroll-handler.tsx +40 -0
- package/src/client/app/theme-context.tsx +75 -0
- package/src/client/components/default-layout.tsx +80 -0
- package/src/client/components/docs-layout.tsx +105 -0
- package/src/client/components/icons-dev.tsx +74 -0
- package/src/client/components/mdx/admonition.tsx +107 -0
- package/src/client/components/mdx/badge.tsx +41 -0
- package/src/client/components/mdx/button.tsx +35 -0
- package/src/client/components/mdx/card.tsx +124 -0
- package/src/client/components/mdx/code-block.tsx +119 -0
- package/src/client/components/mdx/component-preview.tsx +47 -0
- package/src/client/components/mdx/component-props.tsx +83 -0
- package/src/client/components/mdx/field.tsx +66 -0
- package/src/client/components/mdx/file-tree.tsx +287 -0
- package/src/client/components/mdx/hooks/use-code-block.ts +56 -0
- package/src/client/components/mdx/hooks/use-component-preview.ts +16 -0
- package/src/client/components/mdx/hooks/useTable.ts +74 -0
- package/src/client/components/mdx/hooks/useTabs.ts +68 -0
- package/src/client/components/mdx/image.tsx +23 -0
- package/src/client/components/mdx/index.ts +53 -0
- package/src/client/components/mdx/link.tsx +38 -0
- package/src/client/components/mdx/list.tsx +192 -0
- package/src/client/components/mdx/table.tsx +156 -0
- package/src/client/components/mdx/tabs.tsx +135 -0
- package/src/client/components/mdx/video.tsx +68 -0
- package/src/client/components/primitives/breadcrumbs.tsx +79 -0
- package/src/client/components/primitives/button-group.tsx +54 -0
- package/src/client/components/primitives/button.tsx +145 -0
- package/src/client/components/primitives/helpers/observer.ts +120 -0
- package/src/client/components/primitives/index.ts +17 -0
- package/src/client/components/primitives/link.tsx +122 -0
- package/src/client/components/primitives/menu.tsx +159 -0
- package/src/client/components/primitives/navbar.tsx +359 -0
- package/src/client/components/primitives/navigation-menu.tsx +116 -0
- package/src/client/components/primitives/on-this-page.tsx +461 -0
- package/src/client/components/primitives/page-nav.tsx +87 -0
- package/src/client/components/primitives/popover.tsx +47 -0
- package/src/client/components/primitives/search-dialog.tsx +183 -0
- package/src/client/components/primitives/sidebar.tsx +154 -0
- package/src/client/components/primitives/tabs.tsx +90 -0
- package/src/client/components/primitives/tooltip.tsx +83 -0
- package/src/client/components/primitives/types.ts +11 -0
- package/src/client/components/ui-base/breadcrumbs.tsx +42 -0
- package/src/client/components/ui-base/copy-markdown.tsx +112 -0
- package/src/client/components/ui-base/error-boundary.tsx +52 -0
- package/src/client/components/ui-base/github-stars.tsx +27 -0
- package/src/client/components/ui-base/head.tsx +69 -0
- package/src/client/components/ui-base/loading.tsx +87 -0
- package/src/client/components/ui-base/navbar.tsx +138 -0
- package/src/client/components/ui-base/not-found.tsx +24 -0
- package/src/client/components/ui-base/on-this-page.tsx +152 -0
- package/src/client/components/ui-base/page-nav.tsx +39 -0
- package/src/client/components/ui-base/powered-by.tsx +19 -0
- package/src/client/components/ui-base/progress-bar.tsx +67 -0
- package/src/client/components/ui-base/search-dialog.tsx +82 -0
- package/src/client/components/ui-base/sidebar.tsx +104 -0
- package/src/client/components/ui-base/tabs.tsx +65 -0
- package/src/client/components/ui-base/theme-toggle.tsx +32 -0
- package/src/client/hooks/index.ts +12 -0
- package/src/client/hooks/use-breadcrumbs.ts +22 -0
- package/src/client/hooks/use-i18n.ts +84 -0
- package/src/client/hooks/use-localized-to.ts +95 -0
- package/src/client/hooks/use-location.ts +5 -0
- package/src/client/hooks/use-navbar.ts +60 -0
- package/src/client/hooks/use-onthispage.ts +23 -0
- package/src/client/hooks/use-page-nav.ts +22 -0
- package/src/client/hooks/use-routes.ts +72 -0
- package/src/client/hooks/use-search.ts +71 -0
- package/src/client/hooks/use-sidebar.ts +49 -0
- package/src/client/hooks/use-tabs.ts +43 -0
- package/src/client/hooks/use-version.ts +78 -0
- package/src/client/index.ts +55 -17
- package/src/client/integrations/codesandbox.ts +179 -0
- package/src/client/ssr.tsx +27 -16
- package/src/client/theme/neutral.css +360 -0
- package/src/client/types.ts +131 -27
- package/src/client/utils/cn.ts +6 -0
- package/src/client/utils/copy-clipboard.ts +22 -0
- package/src/client/utils/get-base-file-path.ts +21 -0
- package/src/client/utils/github.ts +121 -0
- package/src/client/utils/use-on-change.ts +15 -0
- package/src/client/virtual.d.ts +24 -0
- package/src/node/cache.ts +156 -156
- package/src/node/config.ts +159 -103
- package/src/node/index.ts +13 -13
- package/src/node/mdx.ts +213 -61
- package/src/node/plugin/entry.ts +29 -18
- package/src/node/plugin/html.ts +11 -11
- package/src/node/plugin/index.ts +161 -83
- package/src/node/plugin/types.ts +2 -4
- package/src/node/routes/cache.ts +6 -6
- package/src/node/routes/index.ts +206 -113
- package/src/node/routes/parser.ts +106 -81
- package/src/node/routes/sorter.ts +15 -15
- package/src/node/routes/types.ts +24 -24
- package/src/node/ssg/index.ts +46 -46
- package/src/node/ssg/meta.ts +4 -4
- package/src/node/ssg/options.ts +5 -5
- package/src/node/ssg/sitemap.ts +14 -14
- package/src/node/utils.ts +31 -31
- package/tsconfig.json +25 -20
- package/tsup.config.ts +23 -14
- package/dist/PackageManagerTabs-NVT7G625.mjs +0 -99
- package/dist/SearchDialog-AGVF6JBO.mjs +0 -194
- package/dist/SearchDialog-YPDOM7Q6.css +0 -2847
- package/dist/Video-KNTY5BNO.mjs +0 -6
- package/dist/cache-KNL5B4EE.mjs +0 -12
- package/dist/chunk-7SFUJWTB.mjs +0 -211
- package/dist/chunk-FFBNU6IJ.mjs +0 -386
- package/dist/chunk-FMTOYQLO.mjs +0 -37
- package/dist/chunk-TKLQWU7H.mjs +0 -1920
- package/dist/chunk-Z7JHYNAS.mjs +0 -57
- package/dist/client/index.css +0 -2847
- package/dist/client/index.d.mts +0 -372
- package/dist/client/index.d.ts +0 -372
- package/dist/client/index.js +0 -3630
- package/dist/client/index.mjs +0 -697
- package/dist/client/ssr.css +0 -2847
- package/dist/client/ssr.d.mts +0 -27
- package/dist/client/ssr.d.ts +0 -27
- package/dist/client/ssr.js +0 -2928
- package/dist/client/ssr.mjs +0 -33
- package/dist/config-BsFQ-ErD.d.mts +0 -159
- package/dist/config-BsFQ-ErD.d.ts +0 -159
- package/dist/node/index.d.mts +0 -91
- package/dist/node/index.d.ts +0 -91
- package/dist/node/index.js +0 -1187
- package/dist/node/index.mjs +0 -762
- package/dist/types-Dj-bfnC3.d.mts +0 -74
- package/dist/types-Dj-bfnC3.d.ts +0 -74
- package/src/client/theme/components/CodeBlock/CodeBlock.tsx +0 -61
- package/src/client/theme/components/CodeBlock/index.ts +0 -1
- package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +0 -131
- package/src/client/theme/components/PackageManagerTabs/index.ts +0 -1
- package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +0 -64
- package/src/client/theme/components/Playground/Playground.tsx +0 -180
- package/src/client/theme/components/Playground/index.ts +0 -1
- package/src/client/theme/components/Playground/playground.css +0 -238
- package/src/client/theme/components/Video/Video.tsx +0 -84
- package/src/client/theme/components/Video/index.ts +0 -1
- package/src/client/theme/components/Video/video.css +0 -41
- package/src/client/theme/components/mdx/Admonition.tsx +0 -80
- package/src/client/theme/components/mdx/Badge.tsx +0 -31
- package/src/client/theme/components/mdx/Button.tsx +0 -50
- package/src/client/theme/components/mdx/Card.tsx +0 -80
- package/src/client/theme/components/mdx/Field.tsx +0 -60
- package/src/client/theme/components/mdx/FileTree.tsx +0 -229
- package/src/client/theme/components/mdx/List.tsx +0 -57
- package/src/client/theme/components/mdx/Table.tsx +0 -151
- package/src/client/theme/components/mdx/Tabs.tsx +0 -123
- package/src/client/theme/components/mdx/index.ts +0 -27
- package/src/client/theme/components/mdx/mdx-components.css +0 -764
- package/src/client/theme/icons/bun.tsx +0 -62
- package/src/client/theme/icons/deno.tsx +0 -20
- package/src/client/theme/icons/discord.tsx +0 -12
- package/src/client/theme/icons/github.tsx +0 -15
- package/src/client/theme/icons/npm.tsx +0 -13
- package/src/client/theme/icons/pnpm.tsx +0 -72
- package/src/client/theme/icons/twitter.tsx +0 -12
- package/src/client/theme/styles/markdown.css +0 -394
- package/src/client/theme/styles/variables.css +0 -175
- package/src/client/theme/styles.css +0 -39
- package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +0 -68
- package/src/client/theme/ui/Breadcrumbs/index.ts +0 -1
- package/src/client/theme/ui/CopyMarkdown/CopyMarkdown.tsx +0 -82
- package/src/client/theme/ui/CopyMarkdown/copy-markdown.css +0 -112
- package/src/client/theme/ui/CopyMarkdown/index.ts +0 -1
- package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +0 -50
- package/src/client/theme/ui/ErrorBoundary/error-boundary.css +0 -55
- package/src/client/theme/ui/ErrorBoundary/index.ts +0 -1
- package/src/client/theme/ui/Footer/footer.css +0 -32
- package/src/client/theme/ui/Head/Head.tsx +0 -69
- package/src/client/theme/ui/Head/index.ts +0 -1
- package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +0 -125
- package/src/client/theme/ui/LanguageSwitcher/index.ts +0 -1
- package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +0 -98
- package/src/client/theme/ui/Layout/Layout.tsx +0 -203
- package/src/client/theme/ui/Layout/base.css +0 -106
- package/src/client/theme/ui/Layout/index.ts +0 -2
- package/src/client/theme/ui/Layout/pagination.css +0 -72
- package/src/client/theme/ui/Layout/responsive.css +0 -47
- package/src/client/theme/ui/Link/Link.tsx +0 -392
- package/src/client/theme/ui/Link/LinkPreview.tsx +0 -59
- package/src/client/theme/ui/Link/index.ts +0 -2
- package/src/client/theme/ui/Link/link-preview.css +0 -48
- package/src/client/theme/ui/Loading/Loading.tsx +0 -10
- package/src/client/theme/ui/Loading/index.ts +0 -1
- package/src/client/theme/ui/Loading/loading.css +0 -30
- package/src/client/theme/ui/Navbar/GithubStars.tsx +0 -27
- package/src/client/theme/ui/Navbar/Navbar.tsx +0 -193
- package/src/client/theme/ui/Navbar/Tabs.tsx +0 -99
- package/src/client/theme/ui/Navbar/index.ts +0 -2
- package/src/client/theme/ui/Navbar/navbar.css +0 -347
- package/src/client/theme/ui/NotFound/NotFound.tsx +0 -19
- package/src/client/theme/ui/NotFound/index.ts +0 -1
- package/src/client/theme/ui/NotFound/not-found.css +0 -64
- package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +0 -244
- package/src/client/theme/ui/OnThisPage/index.ts +0 -1
- package/src/client/theme/ui/OnThisPage/toc.css +0 -152
- package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +0 -18
- package/src/client/theme/ui/PoweredBy/index.ts +0 -1
- package/src/client/theme/ui/PoweredBy/powered-by.css +0 -76
- package/src/client/theme/ui/ProgressBar/ProgressBar.css +0 -17
- package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +0 -51
- package/src/client/theme/ui/ProgressBar/index.ts +0 -1
- package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +0 -209
- package/src/client/theme/ui/SearchDialog/index.ts +0 -1
- package/src/client/theme/ui/SearchDialog/search.css +0 -152
- package/src/client/theme/ui/Sidebar/Sidebar.tsx +0 -244
- package/src/client/theme/ui/Sidebar/index.ts +0 -1
- package/src/client/theme/ui/Sidebar/sidebar.css +0 -230
- package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +0 -69
- package/src/client/theme/ui/ThemeToggle/index.ts +0 -1
- package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +0 -136
- package/src/client/theme/ui/VersionSwitcher/index.ts +0 -1
- package/src/client/utils.ts +0 -49
package/src/node/routes/index.ts
CHANGED
|
@@ -1,179 +1,272 @@
|
|
|
1
|
-
import fastGlob from
|
|
2
|
-
import { BoltdocsConfig } from
|
|
3
|
-
import { capitalize } from
|
|
1
|
+
import fastGlob from 'fast-glob'
|
|
2
|
+
import type { BoltdocsConfig } from '../config'
|
|
3
|
+
import { capitalize } from '../utils'
|
|
4
4
|
|
|
5
|
-
import { RouteMeta, ParsedDocFile } from
|
|
6
|
-
import { docCache, invalidateRouteCache, invalidateFile } from
|
|
7
|
-
import { parseDocFile } from
|
|
8
|
-
import { sortRoutes } from
|
|
5
|
+
import type { RouteMeta, ParsedDocFile } from './types'
|
|
6
|
+
import { docCache, invalidateRouteCache, invalidateFile } from './cache'
|
|
7
|
+
import { parseDocFile } from './parser'
|
|
8
|
+
import { sortRoutes } from './sorter'
|
|
9
9
|
|
|
10
10
|
// Re-export public API
|
|
11
|
-
export type { RouteMeta }
|
|
12
|
-
export { invalidateRouteCache, invalidateFile }
|
|
11
|
+
export type { RouteMeta }
|
|
12
|
+
export { invalidateRouteCache, invalidateFile }
|
|
13
|
+
|
|
14
|
+
// Cache for localized path computations
|
|
15
|
+
const localizedPathCache = new Map<string, string>()
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* Generates the entire route map for the documentation site.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
+
* OPTIMIZED: Uses Map-based i18n lookups, chunked processing, and path caching.
|
|
20
|
+
*
|
|
21
|
+
* Automatically handles versioning and i18n routing, including fallback
|
|
22
|
+
* generation for missing translations.
|
|
19
23
|
*
|
|
20
|
-
* @param docsDir - The root directory
|
|
21
|
-
* @param config -
|
|
22
|
-
* @param basePath - The base URL path
|
|
23
|
-
* @returns A promise
|
|
24
|
+
* @param docsDir - The root documentation directory
|
|
25
|
+
* @param config - The Boltdocs configuration
|
|
26
|
+
* @param basePath - The base URL path for the routes (default: '/docs')
|
|
27
|
+
* @returns A promise resolving to an array of RouteMeta objects
|
|
24
28
|
*/
|
|
25
29
|
export async function generateRoutes(
|
|
26
30
|
docsDir: string,
|
|
27
31
|
config?: BoltdocsConfig,
|
|
28
|
-
basePath: string =
|
|
32
|
+
basePath: string = '/docs',
|
|
29
33
|
): Promise<RouteMeta[]> {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
const start = performance.now()
|
|
35
|
+
|
|
36
|
+
// Load persistent cache
|
|
37
|
+
docCache.load()
|
|
38
|
+
|
|
39
|
+
// Clear path computation cache between generations
|
|
40
|
+
localizedPathCache.clear()
|
|
41
|
+
|
|
42
|
+
// Force re-parse if specifically requested (e.g. for content/config changes)
|
|
43
|
+
if (process.env.BOLTDOCS_FORCE_REPARSE === 'true' || config?.i18n) {
|
|
44
|
+
docCache.invalidateAll()
|
|
45
|
+
}
|
|
33
46
|
|
|
34
|
-
|
|
47
|
+
// 1. FAST SCAN
|
|
48
|
+
const files = await fastGlob(['**/*.md', '**/*.mdx'], {
|
|
35
49
|
cwd: docsDir,
|
|
36
50
|
absolute: true,
|
|
37
|
-
|
|
51
|
+
suppressErrors: true,
|
|
52
|
+
followSymbolicLinks: false,
|
|
53
|
+
})
|
|
38
54
|
|
|
39
55
|
// Prune cache entries for deleted files
|
|
40
|
-
docCache.pruneStale(new Set(files))
|
|
56
|
+
docCache.pruneStale(new Set(files))
|
|
41
57
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
// 2. CHUNKED PROCESSING (prevents blocking event loop)
|
|
59
|
+
const CHUNK_SIZE = 50
|
|
60
|
+
const parsed: ParsedDocFile[] = []
|
|
61
|
+
let cacheHits = 0
|
|
46
62
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
for (let i = 0; i < files.length; i += CHUNK_SIZE) {
|
|
64
|
+
const chunk = files.slice(i, i + CHUNK_SIZE)
|
|
65
|
+
|
|
66
|
+
const chunkResults = await Promise.all(
|
|
67
|
+
chunk.map(async (file) => {
|
|
68
|
+
const cached = docCache.get(file)
|
|
69
|
+
if (cached) {
|
|
70
|
+
cacheHits++
|
|
71
|
+
return cached
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const result = parseDocFile(file, docsDir, basePath, config)
|
|
75
|
+
docCache.set(file, result)
|
|
76
|
+
return result
|
|
77
|
+
}),
|
|
78
|
+
)
|
|
56
79
|
|
|
57
|
-
|
|
58
|
-
docCache.set(file, result);
|
|
59
|
-
return result;
|
|
60
|
-
}),
|
|
61
|
-
);
|
|
80
|
+
parsed.push(...chunkResults)
|
|
62
81
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
82
|
+
// Yield to event loop between chunks if there's more to process
|
|
83
|
+
if (i + CHUNK_SIZE < files.length) {
|
|
84
|
+
await new Promise((resolve) => setImmediate(resolve))
|
|
85
|
+
}
|
|
67
86
|
}
|
|
68
87
|
|
|
69
|
-
// Save cache after
|
|
70
|
-
docCache.save()
|
|
88
|
+
// Save cache after processing
|
|
89
|
+
docCache.save()
|
|
71
90
|
|
|
72
|
-
//
|
|
91
|
+
// 3. OPTIMIZED METADATA COLLECTION
|
|
73
92
|
const groupMeta = new Map<
|
|
74
93
|
string,
|
|
75
94
|
{ title: string; position?: number; icon?: string }
|
|
76
|
-
>()
|
|
95
|
+
>()
|
|
96
|
+
const groupIndexFiles: ParsedDocFile[] = []
|
|
97
|
+
|
|
77
98
|
for (const p of parsed) {
|
|
99
|
+
if (p.isGroupIndex && p.relativeDir) {
|
|
100
|
+
groupIndexFiles.push(p)
|
|
101
|
+
}
|
|
102
|
+
|
|
78
103
|
if (p.relativeDir) {
|
|
79
|
-
|
|
80
|
-
|
|
104
|
+
let entry = groupMeta.get(p.relativeDir)
|
|
105
|
+
if (!entry) {
|
|
106
|
+
entry = {
|
|
81
107
|
title: capitalize(p.relativeDir),
|
|
82
108
|
position: p.inferredGroupPosition,
|
|
83
109
|
icon: p.route.icon,
|
|
84
|
-
}
|
|
110
|
+
}
|
|
111
|
+
groupMeta.set(p.relativeDir, entry)
|
|
85
112
|
} else {
|
|
86
|
-
const entry = groupMeta.get(p.relativeDir)!;
|
|
87
113
|
if (
|
|
88
114
|
entry.position === undefined &&
|
|
89
115
|
p.inferredGroupPosition !== undefined
|
|
90
116
|
) {
|
|
91
|
-
entry.position = p.inferredGroupPosition
|
|
117
|
+
entry.position = p.inferredGroupPosition
|
|
92
118
|
}
|
|
93
119
|
if (!entry.icon && p.route.icon) {
|
|
94
|
-
entry.icon = p.route.icon
|
|
120
|
+
entry.icon = p.route.icon
|
|
95
121
|
}
|
|
96
122
|
}
|
|
97
123
|
}
|
|
124
|
+
}
|
|
98
125
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
126
|
+
// Override with explicit group index metadata
|
|
127
|
+
for (const p of groupIndexFiles) {
|
|
128
|
+
const entry = groupMeta.get(p.relativeDir!)!
|
|
129
|
+
if (p.groupMeta) {
|
|
130
|
+
entry.title = p.groupMeta.title
|
|
131
|
+
if (p.groupMeta.position !== undefined)
|
|
132
|
+
entry.position = p.groupMeta.position
|
|
133
|
+
if (p.groupMeta.icon) entry.icon = p.groupMeta.icon
|
|
108
134
|
}
|
|
109
135
|
}
|
|
110
136
|
|
|
111
|
-
//
|
|
112
|
-
const routes: RouteMeta[] = parsed.
|
|
113
|
-
|
|
114
|
-
const
|
|
137
|
+
// 4. BUILD BASE ROUTES
|
|
138
|
+
const routes: RouteMeta[] = new Array(parsed.length)
|
|
139
|
+
for (let i = 0; i < parsed.length; i++) {
|
|
140
|
+
const p = parsed[i]
|
|
141
|
+
const dir = p.relativeDir
|
|
142
|
+
const meta = dir ? groupMeta.get(dir) : undefined
|
|
115
143
|
|
|
116
|
-
|
|
144
|
+
routes[i] = {
|
|
117
145
|
...p.route,
|
|
118
146
|
group: dir,
|
|
119
147
|
groupTitle: meta?.title || (dir ? capitalize(dir) : undefined),
|
|
120
148
|
groupPosition: meta?.position,
|
|
121
149
|
groupIcon: meta?.icon,
|
|
122
|
-
}
|
|
123
|
-
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
124
152
|
|
|
125
|
-
//
|
|
153
|
+
// 5. OPTIMIZED I18N FALLBACKS
|
|
154
|
+
let finalRoutes = routes
|
|
126
155
|
if (config?.i18n) {
|
|
127
|
-
const
|
|
128
|
-
|
|
156
|
+
const fallbacks = generateI18nFallbacks(routes, config, basePath)
|
|
157
|
+
finalRoutes = [...routes, ...fallbacks]
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const sorted = sortRoutes(finalRoutes)
|
|
129
161
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
162
|
+
const duration = performance.now() - start
|
|
163
|
+
console.log(
|
|
164
|
+
`[boltdocs] Route generation: ${duration.toFixed(2)}ms (${files.length} files, ${cacheHits} cache hits)`,
|
|
165
|
+
)
|
|
134
166
|
|
|
135
|
-
|
|
136
|
-
|
|
167
|
+
return sorted
|
|
168
|
+
}
|
|
137
169
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
170
|
+
/**
|
|
171
|
+
* Generates fallback routes for missing translations.
|
|
172
|
+
* Optimization: Uses Map for O(1) existence checks instead of nested filters.
|
|
173
|
+
*/
|
|
174
|
+
function generateI18nFallbacks(
|
|
175
|
+
routes: RouteMeta[],
|
|
176
|
+
config: BoltdocsConfig,
|
|
177
|
+
basePath: string,
|
|
178
|
+
): RouteMeta[] {
|
|
179
|
+
const defaultLocale = config.i18n!.defaultLocale
|
|
180
|
+
const allLocales = Object.keys(config.i18n!.locales)
|
|
181
|
+
const fallbackRoutes: RouteMeta[] = []
|
|
141
182
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
prefix += "/" + defRoute.version;
|
|
146
|
-
}
|
|
183
|
+
// Index existing routes by locale for O(1) lookup
|
|
184
|
+
const routesByLocale = new Map<string, Set<string>>()
|
|
185
|
+
const defaultRoutes: RouteMeta[] = []
|
|
147
186
|
|
|
148
|
-
|
|
187
|
+
for (const r of routes) {
|
|
188
|
+
const locale = r.locale || defaultLocale
|
|
189
|
+
if (!routesByLocale.has(locale)) {
|
|
190
|
+
routesByLocale.set(locale, new Set())
|
|
191
|
+
}
|
|
192
|
+
routesByLocale.get(locale)!.add(r.path)
|
|
149
193
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
194
|
+
if (locale === defaultLocale) {
|
|
195
|
+
defaultRoutes.push(r)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
for (const locale of allLocales) {
|
|
200
|
+
if (locale === defaultLocale) continue
|
|
201
|
+
|
|
202
|
+
const localePaths = routesByLocale.get(locale) || new Set<string>()
|
|
203
|
+
|
|
204
|
+
for (const defRoute of defaultRoutes) {
|
|
205
|
+
const targetPath = computeLocalizedPath(
|
|
206
|
+
defRoute.path,
|
|
207
|
+
defaultLocale,
|
|
208
|
+
locale,
|
|
209
|
+
basePath,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if (!localePaths.has(targetPath)) {
|
|
213
|
+
fallbackRoutes.push({
|
|
214
|
+
...defRoute,
|
|
215
|
+
path: targetPath,
|
|
216
|
+
locale,
|
|
217
|
+
})
|
|
172
218
|
}
|
|
173
219
|
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return fallbackRoutes
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Computes a localized path based on the default locale and target locale.
|
|
227
|
+
* Uses a cache to avoid redundant string manipulation.
|
|
228
|
+
*/
|
|
229
|
+
function computeLocalizedPath(
|
|
230
|
+
path: string,
|
|
231
|
+
defaultLocale: string,
|
|
232
|
+
targetLocale: string,
|
|
233
|
+
basePath: string,
|
|
234
|
+
): string {
|
|
235
|
+
const cacheKey = `${path}:${targetLocale}`
|
|
236
|
+
const cached = localizedPathCache.get(cacheKey)
|
|
237
|
+
if (cached) return cached
|
|
238
|
+
|
|
239
|
+
let prefix = basePath
|
|
240
|
+
const versionMatch = path.match(new RegExp(`^${basePath}/(v[0-9]+)`))
|
|
241
|
+
if (versionMatch) {
|
|
242
|
+
prefix += '/' + versionMatch[1]
|
|
243
|
+
}
|
|
174
244
|
|
|
175
|
-
|
|
245
|
+
let pathAfterVersion = path.substring(prefix.length)
|
|
246
|
+
|
|
247
|
+
// Handle case where path already has default locale
|
|
248
|
+
const defaultLocaleSegment = `/${defaultLocale}`
|
|
249
|
+
if (pathAfterVersion.startsWith(defaultLocaleSegment + '/')) {
|
|
250
|
+
pathAfterVersion =
|
|
251
|
+
'/' +
|
|
252
|
+
targetLocale +
|
|
253
|
+
'/' +
|
|
254
|
+
pathAfterVersion.substring(defaultLocaleSegment.length + 1)
|
|
255
|
+
} else if (pathAfterVersion === defaultLocaleSegment) {
|
|
256
|
+
pathAfterVersion = '/' + targetLocale
|
|
257
|
+
} else if (pathAfterVersion === '/' || pathAfterVersion === '') {
|
|
258
|
+
pathAfterVersion = '/' + targetLocale
|
|
259
|
+
} else {
|
|
260
|
+
// Ensure pathAfterVersion starts with a slash if not already
|
|
261
|
+
const pathPrefix = pathAfterVersion.startsWith('/') ? '' : '/'
|
|
262
|
+
pathAfterVersion = '/' + targetLocale + pathPrefix + pathAfterVersion
|
|
176
263
|
}
|
|
177
264
|
|
|
178
|
-
|
|
265
|
+
const result = prefix + pathAfterVersion
|
|
266
|
+
|
|
267
|
+
// Simple cache eviction to prevent memory leaks in extreme cases
|
|
268
|
+
if (localizedPathCache.size > 2000) localizedPathCache.clear()
|
|
269
|
+
localizedPathCache.set(cacheKey, result)
|
|
270
|
+
|
|
271
|
+
return result
|
|
179
272
|
}
|