boltdocs 1.10.2 → 2.0.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/dist/cache-7G6D532T.mjs +1 -0
- package/dist/chunk-A4HQPEPU.mjs +1 -0
- package/dist/chunk-BA5NH5HU.mjs +1 -0
- package/dist/chunk-BQCD3DWG.mjs +1 -0
- package/dist/chunk-H63UMKYF.mjs +1 -0
- package/dist/chunk-IWHRQHS7.mjs +1 -0
- package/dist/chunk-JZXLCA2E.mjs +1 -0
- package/dist/chunk-MFU7Q6WF.mjs +1 -0
- package/dist/chunk-QYPNX5UN.mjs +1 -0
- package/dist/chunk-XEAPSFMB.mjs +1 -0
- package/dist/client/components/mdx/index.d.mts +209 -0
- package/dist/client/components/mdx/index.d.ts +209 -0
- package/dist/client/components/mdx/index.js +1 -0
- package/dist/client/components/mdx/index.mjs +1 -0
- package/dist/client/hooks/index.d.mts +133 -0
- package/dist/client/hooks/index.d.ts +133 -0
- package/dist/client/hooks/index.js +1 -0
- package/dist/client/hooks/index.mjs +1 -0
- package/dist/client/index.d.mts +138 -298
- package/dist/client/index.d.ts +138 -298
- package/dist/client/index.js +1 -3630
- package/dist/client/index.mjs +1 -697
- package/dist/client/ssr.d.mts +7 -3
- package/dist/client/ssr.d.ts +7 -3
- package/dist/client/ssr.js +1 -2928
- package/dist/client/ssr.mjs +1 -33
- package/dist/{config-BsFQ-ErD.d.ts → config-CX4l-ZNp.d.mts} +42 -35
- package/dist/{config-BsFQ-ErD.d.mts → config-CX4l-ZNp.d.ts} +42 -35
- package/dist/node/index.d.mts +2 -4
- package/dist/node/index.d.ts +2 -4
- package/dist/node/index.js +31 -1161
- package/dist/node/index.mjs +31 -736
- package/dist/search-dialog-EB3N4TYM.mjs +1 -0
- package/dist/types-BuZWFT7r.d.ts +159 -0
- package/dist/types-CvT-SGbK.d.mts +159 -0
- package/dist/use-routes-5bAtAAYX.d.mts +30 -0
- package/dist/use-routes-BefRXY3v.d.ts +30 -0
- package/package.json +34 -12
- package/src/client/app/config-context.tsx +18 -0
- package/src/client/app/docs-layout.tsx +14 -0
- package/src/client/app/index.tsx +137 -262
- 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 -84
- 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 +102 -82
- package/src/node/routes/sorter.ts +15 -15
- package/src/node/routes/types.ts +24 -24
- package/src/node/ssg/index.ts +73 -47
- 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 +54 -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/ssr.css +0 -2847
- 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/cache.ts
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import crypto from
|
|
4
|
-
import zlib from
|
|
5
|
-
import { promisify } from
|
|
6
|
-
import { getFileMtime } from
|
|
7
|
-
|
|
8
|
-
const writeFile = promisify(fs.writeFile)
|
|
9
|
-
const readFile = promisify(fs.readFile)
|
|
10
|
-
const mkdir = promisify(fs.mkdir)
|
|
11
|
-
const rename = promisify(fs.rename)
|
|
12
|
-
const unlink = promisify(fs.unlink)
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import crypto from 'crypto'
|
|
4
|
+
import zlib from 'zlib'
|
|
5
|
+
import { promisify } from 'util'
|
|
6
|
+
import { getFileMtime } from './utils'
|
|
7
|
+
|
|
8
|
+
const writeFile = promisify(fs.writeFile)
|
|
9
|
+
const readFile = promisify(fs.readFile)
|
|
10
|
+
const mkdir = promisify(fs.mkdir)
|
|
11
|
+
const rename = promisify(fs.rename)
|
|
12
|
+
const unlink = promisify(fs.unlink)
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Configuration constants for the caching system.
|
|
16
16
|
*/
|
|
17
|
-
const CACHE_DIR = process.env.BOLTDOCS_CACHE_DIR ||
|
|
18
|
-
const ASSETS_DIR =
|
|
19
|
-
const SHARDS_DIR =
|
|
17
|
+
const CACHE_DIR = process.env.BOLTDOCS_CACHE_DIR || '.boltdocs'
|
|
18
|
+
const ASSETS_DIR = 'assets'
|
|
19
|
+
const SHARDS_DIR = 'shards'
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Default limits for the caching system.
|
|
23
23
|
*/
|
|
24
24
|
const DEFAULT_LRU_LIMIT = parseInt(
|
|
25
|
-
process.env.BOLTDOCS_CACHE_LRU_LIMIT ||
|
|
25
|
+
process.env.BOLTDOCS_CACHE_LRU_LIMIT || '2000',
|
|
26
26
|
10,
|
|
27
|
-
)
|
|
28
|
-
const DEFAULT_COMPRESS = process.env.BOLTDOCS_CACHE_COMPRESS !==
|
|
27
|
+
)
|
|
28
|
+
const DEFAULT_COMPRESS = process.env.BOLTDOCS_CACHE_COMPRESS !== '0'
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Simple LRU cache implementation to prevent memory leaks.
|
|
32
32
|
*/
|
|
33
33
|
class LRUCache<K, V> {
|
|
34
|
-
private cache = new Map<K, V>()
|
|
34
|
+
private cache = new Map<K, V>()
|
|
35
35
|
constructor(private limit: number) {}
|
|
36
36
|
|
|
37
37
|
get(key: K): V | undefined {
|
|
38
|
-
const val = this.cache.get(key)
|
|
38
|
+
const val = this.cache.get(key)
|
|
39
39
|
if (val !== undefined) {
|
|
40
|
-
this.cache.delete(key)
|
|
41
|
-
this.cache.set(key, val)
|
|
40
|
+
this.cache.delete(key)
|
|
41
|
+
this.cache.set(key, val)
|
|
42
42
|
}
|
|
43
|
-
return val
|
|
43
|
+
return val
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
set(key: K, value: V): void {
|
|
47
47
|
if (this.cache.has(key)) {
|
|
48
|
-
this.cache.delete(key)
|
|
48
|
+
this.cache.delete(key)
|
|
49
49
|
} else if (this.cache.size >= this.limit) {
|
|
50
|
-
const firstKey = this.cache.keys().next().value
|
|
50
|
+
const firstKey = this.cache.keys().next().value
|
|
51
51
|
if (firstKey !== undefined) {
|
|
52
|
-
this.cache.delete(firstKey)
|
|
52
|
+
this.cache.delete(firstKey)
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
this.cache.set(key, value)
|
|
55
|
+
this.cache.set(key, value)
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
get size() {
|
|
59
|
-
return this.cache.size
|
|
59
|
+
return this.cache.size
|
|
60
60
|
}
|
|
61
61
|
clear() {
|
|
62
|
-
this.cache.clear()
|
|
62
|
+
this.cache.clear()
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -67,44 +67,44 @@ class LRUCache<K, V> {
|
|
|
67
67
|
* Simple background task queue to prevent blocking the main thread during IO.
|
|
68
68
|
*/
|
|
69
69
|
class BackgroundQueue {
|
|
70
|
-
private queue: Promise<any> = Promise.resolve()
|
|
71
|
-
private pendingCount = 0
|
|
70
|
+
private queue: Promise<any> = Promise.resolve()
|
|
71
|
+
private pendingCount = 0
|
|
72
72
|
|
|
73
73
|
add(task: () => Promise<any>) {
|
|
74
|
-
this.pendingCount
|
|
74
|
+
this.pendingCount++
|
|
75
75
|
this.queue = this.queue.then(task).finally(() => {
|
|
76
|
-
this.pendingCount
|
|
77
|
-
})
|
|
76
|
+
this.pendingCount--
|
|
77
|
+
})
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
async flush() {
|
|
81
|
-
await this.queue
|
|
81
|
+
await this.queue
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
get pending() {
|
|
85
|
-
return this.pendingCount
|
|
85
|
+
return this.pendingCount
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
const backgroundQueue = new BackgroundQueue()
|
|
89
|
+
const backgroundQueue = new BackgroundQueue()
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
92
|
* Generic file-based cache with per-file granularity and asynchronous persistence.
|
|
93
93
|
*/
|
|
94
94
|
export class FileCache<T> {
|
|
95
|
-
private entries = new Map<string, { data: T; mtime: number }>()
|
|
96
|
-
private readonly cachePath: string | null = null
|
|
97
|
-
private readonly compress: boolean
|
|
95
|
+
private entries = new Map<string, { data: T; mtime: number }>()
|
|
96
|
+
private readonly cachePath: string | null = null
|
|
97
|
+
private readonly compress: boolean
|
|
98
98
|
|
|
99
99
|
constructor(
|
|
100
100
|
options: { name?: string; root?: string; compress?: boolean } = {},
|
|
101
101
|
) {
|
|
102
102
|
this.compress =
|
|
103
|
-
options.compress !== undefined ? options.compress : DEFAULT_COMPRESS
|
|
103
|
+
options.compress !== undefined ? options.compress : DEFAULT_COMPRESS
|
|
104
104
|
if (options.name) {
|
|
105
|
-
const root = options.root || process.cwd()
|
|
106
|
-
const ext = this.compress ?
|
|
107
|
-
this.cachePath = path.resolve(root, CACHE_DIR, `${options.name}.${ext}`)
|
|
105
|
+
const root = options.root || process.cwd()
|
|
106
|
+
const ext = this.compress ? 'json.gz' : 'json'
|
|
107
|
+
this.cachePath = path.resolve(root, CACHE_DIR, `${options.name}.${ext}`)
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -112,16 +112,16 @@ export class FileCache<T> {
|
|
|
112
112
|
* Loads the cache. Synchronous for startup simplicity but uses fast I/O.
|
|
113
113
|
*/
|
|
114
114
|
load(): void {
|
|
115
|
-
if (process.env.BOLTDOCS_NO_CACHE ===
|
|
116
|
-
if (!this.cachePath || !fs.existsSync(this.cachePath)) return
|
|
115
|
+
if (process.env.BOLTDOCS_NO_CACHE === '1') return
|
|
116
|
+
if (!this.cachePath || !fs.existsSync(this.cachePath)) return
|
|
117
117
|
|
|
118
118
|
try {
|
|
119
|
-
let raw = fs.readFileSync(this.cachePath)
|
|
120
|
-
if (this.cachePath.endsWith(
|
|
121
|
-
raw = zlib.gunzipSync(raw)
|
|
119
|
+
let raw = fs.readFileSync(this.cachePath)
|
|
120
|
+
if (this.cachePath.endsWith('.gz')) {
|
|
121
|
+
raw = zlib.gunzipSync(raw)
|
|
122
122
|
}
|
|
123
|
-
const data = JSON.parse(raw.toString(
|
|
124
|
-
this.entries = new Map(Object.entries(data))
|
|
123
|
+
const data = JSON.parse(raw.toString('utf-8'))
|
|
124
|
+
this.entries = new Map(Object.entries(data))
|
|
125
125
|
} catch (e) {
|
|
126
126
|
// Fallback: ignore cache errors
|
|
127
127
|
}
|
|
@@ -131,72 +131,72 @@ export class FileCache<T> {
|
|
|
131
131
|
* Saves the cache in the background.
|
|
132
132
|
*/
|
|
133
133
|
save(): void {
|
|
134
|
-
if (process.env.BOLTDOCS_NO_CACHE ===
|
|
135
|
-
if (!this.cachePath) return
|
|
134
|
+
if (process.env.BOLTDOCS_NO_CACHE === '1') return
|
|
135
|
+
if (!this.cachePath) return
|
|
136
136
|
|
|
137
|
-
const data = Object.fromEntries(this.entries)
|
|
138
|
-
const content = JSON.stringify(data)
|
|
139
|
-
const target = this.cachePath
|
|
140
|
-
const useCompress = this.compress
|
|
137
|
+
const data = Object.fromEntries(this.entries)
|
|
138
|
+
const content = JSON.stringify(data)
|
|
139
|
+
const target = this.cachePath
|
|
140
|
+
const useCompress = this.compress
|
|
141
141
|
|
|
142
142
|
backgroundQueue.add(async () => {
|
|
143
143
|
try {
|
|
144
|
-
await mkdir(path.dirname(target), { recursive: true })
|
|
145
|
-
let buffer = Buffer.from(content)
|
|
144
|
+
await mkdir(path.dirname(target), { recursive: true })
|
|
145
|
+
let buffer = Buffer.from(content)
|
|
146
146
|
if (useCompress) {
|
|
147
|
-
buffer = zlib.gzipSync(buffer)
|
|
147
|
+
buffer = zlib.gzipSync(buffer)
|
|
148
148
|
}
|
|
149
|
-
const tempPath = `${target}.${crypto.randomBytes(4).toString(
|
|
150
|
-
await writeFile(tempPath, buffer)
|
|
151
|
-
await rename(tempPath, target)
|
|
149
|
+
const tempPath = `${target}.${crypto.randomBytes(4).toString('hex')}.tmp`
|
|
150
|
+
await writeFile(tempPath, buffer)
|
|
151
|
+
await rename(tempPath, target)
|
|
152
152
|
} catch (e) {
|
|
153
153
|
// Fallback: critical error logging skipped for performance
|
|
154
154
|
}
|
|
155
|
-
})
|
|
155
|
+
})
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
get(filePath: string): T | null {
|
|
159
|
-
const entry = this.entries.get(filePath)
|
|
160
|
-
if (!entry) return null
|
|
161
|
-
if (getFileMtime(filePath) !== entry.mtime) return null
|
|
162
|
-
return entry.data
|
|
159
|
+
const entry = this.entries.get(filePath)
|
|
160
|
+
if (!entry) return null
|
|
161
|
+
if (getFileMtime(filePath) !== entry.mtime) return null
|
|
162
|
+
return entry.data
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
set(filePath: string, data: T): void {
|
|
166
166
|
this.entries.set(filePath, {
|
|
167
167
|
data,
|
|
168
168
|
mtime: getFileMtime(filePath),
|
|
169
|
-
})
|
|
169
|
+
})
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
isValid(filePath: string): boolean {
|
|
173
|
-
const entry = this.entries.get(filePath)
|
|
174
|
-
if (!entry) return false
|
|
175
|
-
return getFileMtime(filePath) === entry.mtime
|
|
173
|
+
const entry = this.entries.get(filePath)
|
|
174
|
+
if (!entry) return false
|
|
175
|
+
return getFileMtime(filePath) === entry.mtime
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
invalidate(filePath: string): void {
|
|
179
|
-
this.entries.delete(filePath)
|
|
179
|
+
this.entries.delete(filePath)
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
invalidateAll(): void {
|
|
183
|
-
this.entries.clear()
|
|
183
|
+
this.entries.clear()
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
pruneStale(currentFiles: Set<string>): void {
|
|
187
187
|
for (const key of this.entries.keys()) {
|
|
188
188
|
if (!currentFiles.has(key)) {
|
|
189
|
-
this.entries.delete(key)
|
|
189
|
+
this.entries.delete(key)
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
get size(): number {
|
|
195
|
-
return this.entries.size
|
|
195
|
+
return this.entries.size
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
async flush() {
|
|
199
|
-
await backgroundQueue.flush()
|
|
199
|
+
await backgroundQueue.flush()
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
202
|
|
|
@@ -205,28 +205,28 @@ export class FileCache<T> {
|
|
|
205
205
|
* Uses a memory index and individual files for each entry to avoid massive JSON parsing.
|
|
206
206
|
*/
|
|
207
207
|
export class TransformCache {
|
|
208
|
-
private index = new Map<string, string>()
|
|
209
|
-
private memoryCache = new LRUCache<string, string>(DEFAULT_LRU_LIMIT)
|
|
210
|
-
private readonly baseDir: string
|
|
211
|
-
private readonly shardsDir: string
|
|
212
|
-
private readonly indexPath: string
|
|
208
|
+
private index = new Map<string, string>() // key -> hash
|
|
209
|
+
private memoryCache = new LRUCache<string, string>(DEFAULT_LRU_LIMIT)
|
|
210
|
+
private readonly baseDir: string
|
|
211
|
+
private readonly shardsDir: string
|
|
212
|
+
private readonly indexPath: string
|
|
213
213
|
|
|
214
214
|
constructor(name: string, root: string = process.cwd()) {
|
|
215
|
-
this.baseDir = path.resolve(root, CACHE_DIR, `transform-${name}`)
|
|
216
|
-
this.shardsDir = path.resolve(this.baseDir, SHARDS_DIR)
|
|
217
|
-
this.indexPath = path.resolve(this.baseDir,
|
|
215
|
+
this.baseDir = path.resolve(root, CACHE_DIR, `transform-${name}`)
|
|
216
|
+
this.shardsDir = path.resolve(this.baseDir, SHARDS_DIR)
|
|
217
|
+
this.indexPath = path.resolve(this.baseDir, 'index.json')
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
/**
|
|
221
221
|
* Loads the index into memory.
|
|
222
222
|
*/
|
|
223
223
|
load(): void {
|
|
224
|
-
if (process.env.BOLTDOCS_NO_CACHE ===
|
|
225
|
-
if (!fs.existsSync(this.indexPath)) return
|
|
224
|
+
if (process.env.BOLTDOCS_NO_CACHE === '1') return
|
|
225
|
+
if (!fs.existsSync(this.indexPath)) return
|
|
226
226
|
|
|
227
227
|
try {
|
|
228
|
-
const data = fs.readFileSync(this.indexPath,
|
|
229
|
-
this.index = new Map(Object.entries(JSON.parse(data)))
|
|
228
|
+
const data = fs.readFileSync(this.indexPath, 'utf-8')
|
|
229
|
+
this.index = new Map(Object.entries(JSON.parse(data)))
|
|
230
230
|
} catch (e) {
|
|
231
231
|
// Index might be corrupt, ignore
|
|
232
232
|
}
|
|
@@ -236,51 +236,51 @@ export class TransformCache {
|
|
|
236
236
|
* Persists the index in background.
|
|
237
237
|
*/
|
|
238
238
|
save(): void {
|
|
239
|
-
if (process.env.BOLTDOCS_NO_CACHE ===
|
|
240
|
-
const data = JSON.stringify(Object.fromEntries(this.index))
|
|
241
|
-
const target = this.indexPath
|
|
239
|
+
if (process.env.BOLTDOCS_NO_CACHE === '1') return
|
|
240
|
+
const data = JSON.stringify(Object.fromEntries(this.index))
|
|
241
|
+
const target = this.indexPath
|
|
242
242
|
|
|
243
243
|
backgroundQueue.add(async () => {
|
|
244
|
-
await mkdir(path.dirname(target), { recursive: true })
|
|
245
|
-
await writeFile(target, data)
|
|
246
|
-
})
|
|
244
|
+
await mkdir(path.dirname(target), { recursive: true })
|
|
245
|
+
await writeFile(target, data)
|
|
246
|
+
})
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
/**
|
|
250
250
|
* Batch Read: Retrieves multiple transformation results concurrently.
|
|
251
251
|
*/
|
|
252
252
|
async getMany(keys: string[]): Promise<Map<string, string>> {
|
|
253
|
-
const results = new Map<string, string>()
|
|
254
|
-
const toLoad: string[] = []
|
|
253
|
+
const results = new Map<string, string>()
|
|
254
|
+
const toLoad: string[] = []
|
|
255
255
|
|
|
256
256
|
for (const key of keys) {
|
|
257
|
-
const mem = this.memoryCache.get(key)
|
|
258
|
-
if (mem) results.set(key, mem)
|
|
259
|
-
else if (this.index.has(key)) toLoad.push(key)
|
|
257
|
+
const mem = this.memoryCache.get(key)
|
|
258
|
+
if (mem) results.set(key, mem)
|
|
259
|
+
else if (this.index.has(key)) toLoad.push(key)
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
if (toLoad.length > 0) {
|
|
263
263
|
const shards = await Promise.all(
|
|
264
264
|
toLoad.map(async (key) => {
|
|
265
|
-
const hash = this.index.get(key)
|
|
266
|
-
const shardPath = path.resolve(this.shardsDir, `${hash}.gz`)
|
|
265
|
+
const hash = this.index.get(key)!
|
|
266
|
+
const shardPath = path.resolve(this.shardsDir, `${hash}.gz`)
|
|
267
267
|
try {
|
|
268
|
-
const compressed = await readFile(shardPath)
|
|
269
|
-
const decompressed = zlib.gunzipSync(compressed).toString(
|
|
270
|
-
this.memoryCache.set(key, decompressed)
|
|
271
|
-
return { key, val: decompressed }
|
|
268
|
+
const compressed = await readFile(shardPath)
|
|
269
|
+
const decompressed = zlib.gunzipSync(compressed).toString('utf-8')
|
|
270
|
+
this.memoryCache.set(key, decompressed)
|
|
271
|
+
return { key, val: decompressed }
|
|
272
272
|
} catch (e) {
|
|
273
|
-
return null
|
|
273
|
+
return null
|
|
274
274
|
}
|
|
275
275
|
}),
|
|
276
|
-
)
|
|
276
|
+
)
|
|
277
277
|
|
|
278
278
|
for (const s of shards) {
|
|
279
|
-
if (s) results.set(s.key, s.val)
|
|
279
|
+
if (s) results.set(s.key, s.val)
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
-
return results
|
|
283
|
+
return results
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
/**
|
|
@@ -288,24 +288,24 @@ export class TransformCache {
|
|
|
288
288
|
*/
|
|
289
289
|
get(key: string): string | null {
|
|
290
290
|
// 1. Check memory first (LRU)
|
|
291
|
-
const mem = this.memoryCache.get(key)
|
|
292
|
-
if (mem) return mem
|
|
291
|
+
const mem = this.memoryCache.get(key)
|
|
292
|
+
if (mem) return mem
|
|
293
293
|
|
|
294
294
|
// 2. Check index
|
|
295
|
-
const hash = this.index.get(key)
|
|
296
|
-
if (!hash) return null
|
|
295
|
+
const hash = this.index.get(key)
|
|
296
|
+
if (!hash) return null
|
|
297
297
|
|
|
298
298
|
// 3. Load from shard (synchronous read for Vite's transform hook compatibility)
|
|
299
|
-
const shardPath = path.resolve(this.shardsDir, `${hash}.gz`)
|
|
300
|
-
if (!fs.existsSync(shardPath)) return null
|
|
299
|
+
const shardPath = path.resolve(this.shardsDir, `${hash}.gz`)
|
|
300
|
+
if (!fs.existsSync(shardPath)) return null
|
|
301
301
|
|
|
302
302
|
try {
|
|
303
|
-
const compressed = fs.readFileSync(shardPath)
|
|
304
|
-
const decompressed = zlib.gunzipSync(compressed).toString(
|
|
305
|
-
this.memoryCache.set(key, decompressed)
|
|
306
|
-
return decompressed
|
|
303
|
+
const compressed = fs.readFileSync(shardPath)
|
|
304
|
+
const decompressed = zlib.gunzipSync(compressed).toString('utf-8')
|
|
305
|
+
this.memoryCache.set(key, decompressed)
|
|
306
|
+
return decompressed
|
|
307
307
|
} catch (e) {
|
|
308
|
-
return null
|
|
308
|
+
return null
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
|
|
@@ -313,30 +313,30 @@ export class TransformCache {
|
|
|
313
313
|
* Stores a transformation result.
|
|
314
314
|
*/
|
|
315
315
|
set(key: string, result: string): void {
|
|
316
|
-
const hash = crypto.createHash(
|
|
317
|
-
this.index.set(key, hash)
|
|
318
|
-
this.memoryCache.set(key, result)
|
|
316
|
+
const hash = crypto.createHash('md5').update(result).digest('hex')
|
|
317
|
+
this.index.set(key, hash)
|
|
318
|
+
this.memoryCache.set(key, result)
|
|
319
319
|
|
|
320
|
-
const shardPath = path.resolve(this.shardsDir, `${hash}.gz`)
|
|
320
|
+
const shardPath = path.resolve(this.shardsDir, `${hash}.gz`)
|
|
321
321
|
|
|
322
322
|
// Background write shard
|
|
323
323
|
backgroundQueue.add(async () => {
|
|
324
|
-
if (fs.existsSync(shardPath)) return
|
|
325
|
-
await mkdir(this.shardsDir, { recursive: true })
|
|
324
|
+
if (fs.existsSync(shardPath)) return // Already exists
|
|
325
|
+
await mkdir(this.shardsDir, { recursive: true })
|
|
326
326
|
|
|
327
|
-
const compressed = zlib.gzipSync(Buffer.from(result))
|
|
328
|
-
const tempPath = `${shardPath}.${crypto.randomBytes(4).toString(
|
|
329
|
-
await writeFile(tempPath, compressed)
|
|
330
|
-
await rename(tempPath, shardPath)
|
|
331
|
-
})
|
|
327
|
+
const compressed = zlib.gzipSync(Buffer.from(result))
|
|
328
|
+
const tempPath = `${shardPath}.${crypto.randomBytes(4).toString('hex')}.tmp`
|
|
329
|
+
await writeFile(tempPath, compressed)
|
|
330
|
+
await rename(tempPath, shardPath)
|
|
331
|
+
})
|
|
332
332
|
}
|
|
333
333
|
|
|
334
334
|
get size() {
|
|
335
|
-
return this.index.size
|
|
335
|
+
return this.index.size
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
async flush() {
|
|
339
|
-
await backgroundQueue.flush()
|
|
339
|
+
await backgroundQueue.flush()
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
342
|
|
|
@@ -344,59 +344,59 @@ export class TransformCache {
|
|
|
344
344
|
* Specialized cache for processed assets (e.g., optimized images).
|
|
345
345
|
*/
|
|
346
346
|
export class AssetCache {
|
|
347
|
-
private readonly assetsDir: string
|
|
347
|
+
private readonly assetsDir: string
|
|
348
348
|
|
|
349
349
|
constructor(root: string = process.cwd()) {
|
|
350
|
-
this.assetsDir = path.resolve(root, CACHE_DIR, ASSETS_DIR)
|
|
350
|
+
this.assetsDir = path.resolve(root, CACHE_DIR, ASSETS_DIR)
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
private getFileHash(filePath: string): string {
|
|
354
354
|
return crypto
|
|
355
|
-
.createHash(
|
|
355
|
+
.createHash('md5')
|
|
356
356
|
.update(fs.readFileSync(filePath))
|
|
357
|
-
.digest(
|
|
357
|
+
.digest('hex')
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
get(sourcePath: string, cacheKey: string): string | null {
|
|
361
|
-
if (!fs.existsSync(sourcePath)) return null
|
|
362
|
-
const sourceHash = this.getFileHash(sourcePath)
|
|
361
|
+
if (!fs.existsSync(sourcePath)) return null
|
|
362
|
+
const sourceHash = this.getFileHash(sourcePath)
|
|
363
363
|
const cachedPath = this.getCachedPath(
|
|
364
364
|
sourcePath,
|
|
365
365
|
`${cacheKey}-${sourceHash}`,
|
|
366
|
-
)
|
|
367
|
-
return fs.existsSync(cachedPath) ? cachedPath : null
|
|
366
|
+
)
|
|
367
|
+
return fs.existsSync(cachedPath) ? cachedPath : null
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
set(sourcePath: string, cacheKey: string, content: Buffer | string): void {
|
|
371
|
-
const sourceHash = this.getFileHash(sourcePath)
|
|
371
|
+
const sourceHash = this.getFileHash(sourcePath)
|
|
372
372
|
const cachedPath = this.getCachedPath(
|
|
373
373
|
sourcePath,
|
|
374
374
|
`${cacheKey}-${sourceHash}`,
|
|
375
|
-
)
|
|
375
|
+
)
|
|
376
376
|
|
|
377
377
|
backgroundQueue.add(async () => {
|
|
378
|
-
await mkdir(this.assetsDir, { recursive: true })
|
|
379
|
-
const tempPath = `${cachedPath}.${crypto.randomBytes(4).toString(
|
|
380
|
-
await writeFile(tempPath, content)
|
|
381
|
-
await rename(tempPath, cachedPath)
|
|
382
|
-
})
|
|
378
|
+
await mkdir(this.assetsDir, { recursive: true })
|
|
379
|
+
const tempPath = `${cachedPath}.${crypto.randomBytes(4).toString('hex')}.tmp`
|
|
380
|
+
await writeFile(tempPath, content)
|
|
381
|
+
await rename(tempPath, cachedPath)
|
|
382
|
+
})
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
private getCachedPath(sourcePath: string, cacheKey: string): string {
|
|
386
|
-
const ext = path.extname(sourcePath)
|
|
387
|
-
const name = path.basename(sourcePath, ext)
|
|
388
|
-
const safeKey = cacheKey.replace(/[^a-z0-9]/gi,
|
|
389
|
-
return path.join(this.assetsDir, `${name}.${safeKey}${ext}`)
|
|
386
|
+
const ext = path.extname(sourcePath)
|
|
387
|
+
const name = path.basename(sourcePath, ext)
|
|
388
|
+
const safeKey = cacheKey.replace(/[^a-z0-9]/gi, '-').toLowerCase()
|
|
389
|
+
return path.join(this.assetsDir, `${name}.${safeKey}${ext}`)
|
|
390
390
|
}
|
|
391
391
|
|
|
392
392
|
clear(): void {
|
|
393
393
|
if (fs.existsSync(this.assetsDir)) {
|
|
394
|
-
fs.rmSync(this.assetsDir, { recursive: true, force: true })
|
|
394
|
+
fs.rmSync(this.assetsDir, { recursive: true, force: true })
|
|
395
395
|
}
|
|
396
396
|
}
|
|
397
397
|
|
|
398
398
|
async flush() {
|
|
399
|
-
await backgroundQueue.flush()
|
|
399
|
+
await backgroundQueue.flush()
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
402
|
|
|
@@ -404,5 +404,5 @@ export class AssetCache {
|
|
|
404
404
|
* Flushes all pending background cache operations.
|
|
405
405
|
*/
|
|
406
406
|
export async function flushCache() {
|
|
407
|
-
await backgroundQueue.flush()
|
|
407
|
+
await backgroundQueue.flush()
|
|
408
408
|
}
|