react-docs-ui 0.7.4 → 0.7.6
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/dist/{DocsApp-B-VaP4ms.js → DocsApp-CCFHuucK.js} +138 -124
- package/dist/{MdxContent-CZLqZlNp.js → MdxContent-CJydH4kW.js} +1 -1
- package/dist/{MdxContent.lazy-i9phqEH3.js → MdxContent.lazy-ChT4eAlU.js} +1 -1
- package/dist/{SearchDialog-DWVre6R9.js → SearchDialog-Dh1RvEgs.js} +87 -85
- package/dist/{SearchRuntime-D0rSMKV3.js → SearchRuntime-wdAGDBv4.js} +5 -4
- package/dist/{architectureDiagram-2XIMDMQ5-DmhcSwl7.js → architectureDiagram-2XIMDMQ5-D5B3Bc8w.js} +1 -1
- package/dist/{chunk-GLR3WWYH-ShSppUzV.js → chunk-GLR3WWYH-C7t8eN-i.js} +1 -1
- package/dist/{chunk-NQ4KR5QH-BWRGvKed.js → chunk-NQ4KR5QH-ygKrPhgw.js} +1 -1
- package/dist/{chunk-WL4C6EOR-DLbU3P3W.js → chunk-WL4C6EOR-BX4P121X.js} +1 -1
- package/dist/{classDiagram-VBA2DB6C-D4y64v83.js → classDiagram-VBA2DB6C-CWWKW2ZN.js} +2 -2
- package/dist/{classDiagram-v2-RAHNMMFH-DXkt0Wd4.js → classDiagram-v2-RAHNMMFH-9SKKaVzh.js} +2 -2
- package/dist/{cose-bilkent-S5V4N54A-BX63fiTJ.js → cose-bilkent-S5V4N54A-DCXUWrka.js} +1 -1
- package/dist/docs-app.es.js +1 -1
- package/dist/{erDiagram-INFDFZHY-DjNq2UqZ.js → erDiagram-INFDFZHY-DdivnAXH.js} +1 -1
- package/dist/{flowDiagram-PKNHOUZH-AUeVhXRc.js → flowDiagram-PKNHOUZH-CgCGjn11.js} +1 -1
- package/dist/{mdx-components-m1cGPhc8.js → mdx-components-BLq-Umts.js} +1 -1
- package/dist/{mermaid.core-CtkJeRVj.js → mermaid.core-Dg_svxvF.js} +12 -12
- package/dist/{mindmap-definition-YRQLILUH-Cnl-qPDR.js → mindmap-definition-YRQLILUH-3st_5tCe.js} +1 -1
- package/dist/react-docs-ui.es.js +4 -4
- package/dist/{requirementDiagram-Z7DCOOCP-Bx6NAgqy.js → requirementDiagram-Z7DCOOCP-ClKKAQ0C.js} +1 -1
- package/dist/{stateDiagram-RAJIS63D-DFmbSHPX.js → stateDiagram-RAJIS63D-C8Ab__vp.js} +2 -2
- package/dist/{stateDiagram-v2-FVOUBMTO-TjYtTjLB.js → stateDiagram-v2-FVOUBMTO-BhQBkYI5.js} +2 -2
- package/dist/types/components/DocsLayout.d.ts.map +1 -1
- package/dist/types/components/ExportToolbar.d.ts +8 -1
- package/dist/types/components/ExportToolbar.d.ts.map +1 -1
- package/dist/types/components/HeaderNav.d.ts.map +1 -1
- package/dist/types/components/MobileSidebar.d.ts +2 -7
- package/dist/types/components/MobileSidebar.d.ts.map +1 -1
- package/dist/types/components/SearchLauncher.d.ts +2 -1
- package/dist/types/components/SearchLauncher.d.ts.map +1 -1
- package/dist/types/components/search/SearchProvider.d.ts +2 -1
- package/dist/types/components/search/SearchProvider.d.ts.map +1 -1
- package/dist/types/components/search/SearchRuntime.d.ts +2 -1
- package/dist/types/components/search/SearchRuntime.d.ts.map +1 -1
- package/dist/types/hooks/useScrollPosition.d.ts.map +1 -1
- package/dist/types/lib/config.d.ts +1 -0
- package/dist/types/lib/config.d.ts.map +1 -1
- package/dist/types/lib/search/runtime/search-engine.d.ts.map +1 -1
- package/dist/types/lib/search/runtime/types.d.ts +1 -0
- package/dist/types/lib/search/runtime/types.d.ts.map +1 -1
- package/package.json +143 -136
- package/scripts/cli.mjs +18 -0
- package/scripts/generate-changelog-index.mjs +89 -0
- package/scripts/generate-doc-git-meta.mjs +63 -0
- package/scripts/generate-llms-files.mjs +34 -0
- package/scripts/generate-search-index.mjs +236 -0
- package/scripts/generate-shiki-bundle.mjs +80 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { promisify } from "node:util"
|
|
2
|
+
import { execFile } from "node:child_process"
|
|
3
|
+
import { glob } from "glob"
|
|
4
|
+
import path from "node:path"
|
|
5
|
+
import fs from "node:fs/promises"
|
|
6
|
+
|
|
7
|
+
const execFileAsync = promisify(execFile)
|
|
8
|
+
const rootDir = process.cwd()
|
|
9
|
+
const publicDir = path.join(rootDir, "public")
|
|
10
|
+
const outputPath = path.join(publicDir, "doc-git-meta.json")
|
|
11
|
+
|
|
12
|
+
async function getGitValue(args) {
|
|
13
|
+
try {
|
|
14
|
+
const { stdout } = await execFileAsync("git", args, { cwd: rootDir })
|
|
15
|
+
return stdout.trim()
|
|
16
|
+
} catch {
|
|
17
|
+
return ""
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function main() {
|
|
22
|
+
const repoRoot = (await getGitValue(["rev-parse", "--show-toplevel"])) || rootDir
|
|
23
|
+
const files = await glob("public/docs/**/*.{md,mdx}", {
|
|
24
|
+
cwd: rootDir,
|
|
25
|
+
absolute: true,
|
|
26
|
+
nodir: true,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const entries = []
|
|
30
|
+
for (const file of files) {
|
|
31
|
+
const relativePath = path.relative(repoRoot, file).replace(/\\/g, "/")
|
|
32
|
+
const [lastUpdated, author] = await Promise.all([
|
|
33
|
+
getGitValue(["log", "-1", "--format=%cI", "--", relativePath]),
|
|
34
|
+
getGitValue(["log", "-1", "--format=%an", "--", relativePath]),
|
|
35
|
+
])
|
|
36
|
+
entries.push({
|
|
37
|
+
relativePath: path.relative(rootDir, file).replace(/\\/g, "/"),
|
|
38
|
+
meta: {
|
|
39
|
+
lastUpdated: lastUpdated || undefined,
|
|
40
|
+
author: author || undefined,
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
const docMeta = Object.fromEntries(
|
|
45
|
+
entries
|
|
46
|
+
.filter(({ meta }) => meta.lastUpdated || meta.author)
|
|
47
|
+
.map(({ relativePath, meta }) => [relativePath, meta])
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const payload = {
|
|
51
|
+
generatedAt: new Date().toISOString(),
|
|
52
|
+
files: docMeta,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
await fs.mkdir(publicDir, { recursive: true })
|
|
56
|
+
await fs.writeFile(outputPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8")
|
|
57
|
+
console.log(`[doc-git-meta] Wrote ${Object.keys(docMeta).length} entries to public/doc-git-meta.json`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
main().catch((error) => {
|
|
61
|
+
console.error("[doc-git-meta] Failed:", error)
|
|
62
|
+
process.exitCode = 1
|
|
63
|
+
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from "node:fs/promises"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import { glob } from "glob"
|
|
4
|
+
|
|
5
|
+
const rootDir = process.cwd()
|
|
6
|
+
const publicDir = path.join(rootDir, "public")
|
|
7
|
+
|
|
8
|
+
function stripFrontmatter(source) {
|
|
9
|
+
if (!source.startsWith("---\n")) return source
|
|
10
|
+
const endIndex = source.indexOf("\n---\n", 4)
|
|
11
|
+
return endIndex === -1 ? source : source.slice(endIndex + 5)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function main() {
|
|
15
|
+
const files = await glob("public/docs/**/*.{md,mdx}", { cwd: rootDir, absolute: true, nodir: true })
|
|
16
|
+
const docs = await Promise.all(files.map(async (file) => {
|
|
17
|
+
const relative = path.relative(rootDir, file).replace(/\\/g, "/")
|
|
18
|
+
const urlPath = relative.replace(/^public\/docs\//, "").replace(/\.(md|mdx)$/i, "")
|
|
19
|
+
const content = stripFrontmatter(await fs.readFile(file, "utf8")).trim()
|
|
20
|
+
return { relative, urlPath, content }
|
|
21
|
+
}))
|
|
22
|
+
|
|
23
|
+
const llms = docs.map((doc) => `- /${doc.urlPath} | ${doc.relative}`).join("\n")
|
|
24
|
+
const llmsFull = docs.map((doc) => `# /${doc.urlPath}\n\n${doc.content}`).join("\n\n")
|
|
25
|
+
|
|
26
|
+
await fs.writeFile(path.join(publicDir, "llms.txt"), `${llms}\n`, "utf8")
|
|
27
|
+
await fs.writeFile(path.join(publicDir, "llms-full.txt"), `${llmsFull}\n`, "utf8")
|
|
28
|
+
console.log(`[llms] Wrote ${docs.length} docs to llms.txt and llms-full.txt`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main().catch((error) => {
|
|
32
|
+
console.error("[llms] Failed:", error)
|
|
33
|
+
process.exitCode = 1
|
|
34
|
+
})
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
const ROOT_DIR = process.cwd()
|
|
5
|
+
const PUBLIC_DIR = path.join(ROOT_DIR, 'public')
|
|
6
|
+
const DOCS_DIR = path.join(PUBLIC_DIR, 'docs')
|
|
7
|
+
|
|
8
|
+
const SEARCH_INDEX_VERSION = '2.0.0'
|
|
9
|
+
const MAX_CONTENT_LENGTH = 300
|
|
10
|
+
|
|
11
|
+
function isChinese(text) {
|
|
12
|
+
return /[\u4e00-\u9fa5]/.test(text)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function tokenize(text) {
|
|
16
|
+
if (!text) return []
|
|
17
|
+
const tokens = []
|
|
18
|
+
const parts = text.split(/(\s+|[,。!?、;:""''()【】《》\n\r]+)/)
|
|
19
|
+
for (const part of parts) {
|
|
20
|
+
if (!part.trim()) continue
|
|
21
|
+
if (isChinese(part)) {
|
|
22
|
+
const chars = part.split('')
|
|
23
|
+
for (let i = 0; i < chars.length - 1; i++) {
|
|
24
|
+
tokens.push(chars[i] + chars[i + 1])
|
|
25
|
+
}
|
|
26
|
+
tokens.push(...chars.filter(c => c.trim()))
|
|
27
|
+
} else {
|
|
28
|
+
const words = part.toLowerCase().split(/[^a-zA-Z0-9]+/).filter(w => w.length > 0)
|
|
29
|
+
tokens.push(...words)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return [...new Set(tokens)]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseFrontmatter(content) {
|
|
36
|
+
const normalized = content.replace(/^\uFEFF/, '')
|
|
37
|
+
const lines = normalized.split(/\r?\n/)
|
|
38
|
+
const data = {}
|
|
39
|
+
let contentStart = 0
|
|
40
|
+
if (lines[0]?.startsWith('---')) {
|
|
41
|
+
let i = 1
|
|
42
|
+
while (i < lines.length && !lines[i]?.startsWith('---')) {
|
|
43
|
+
const line = lines[i]
|
|
44
|
+
const colonIndex = line?.indexOf(':') ?? -1
|
|
45
|
+
if (colonIndex > 0 && line) {
|
|
46
|
+
const key = line.substring(0, colonIndex).trim()
|
|
47
|
+
const value = line.substring(colonIndex + 1).trim()
|
|
48
|
+
data[key] = value
|
|
49
|
+
}
|
|
50
|
+
i++
|
|
51
|
+
}
|
|
52
|
+
if (i < lines.length && lines[i]?.startsWith('---')) {
|
|
53
|
+
contentStart = i + 1
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { data, content: lines.slice(contentStart).join('\n') }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function cleanContent(text) {
|
|
60
|
+
return text
|
|
61
|
+
.replace(/\s+/g, ' ')
|
|
62
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
63
|
+
.replace(/[#*`_~>|]/g, '')
|
|
64
|
+
.replace(/\$\$?[^$]+\$\$?/g, '')
|
|
65
|
+
.trim()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function extractTextContent(node) {
|
|
69
|
+
if (!node || typeof node !== 'object') return ''
|
|
70
|
+
if (node.type === 'text') return node.value || ''
|
|
71
|
+
if (node.type === 'inlineCode') return `\`${node.value || ''}\``
|
|
72
|
+
if (node.type === 'code') return node.value || ''
|
|
73
|
+
if ('children' in node && Array.isArray(node.children)) {
|
|
74
|
+
return node.children.map(extractTextContent).join(' ')
|
|
75
|
+
}
|
|
76
|
+
return ''
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function parseMarkdown(content) {
|
|
80
|
+
const { unified } = await import('unified')
|
|
81
|
+
const remarkParse = (await import('remark-parse')).default
|
|
82
|
+
const remarkGfm = (await import('remark-gfm')).default
|
|
83
|
+
const remarkMath = (await import('remark-math')).default
|
|
84
|
+
|
|
85
|
+
const { data: frontmatter, content: markdownContent } = parseFrontmatter(content)
|
|
86
|
+
const processor = unified().use(remarkParse).use(remarkGfm).use(remarkMath)
|
|
87
|
+
const tree = processor.parse(markdownContent)
|
|
88
|
+
|
|
89
|
+
const title = frontmatter.title || ''
|
|
90
|
+
const sections = []
|
|
91
|
+
let currentSection = null
|
|
92
|
+
let contentParts = []
|
|
93
|
+
|
|
94
|
+
const saveCurrentSection = () => {
|
|
95
|
+
if (currentSection && contentParts.length > 0) {
|
|
96
|
+
currentSection.content = cleanContent(contentParts.join(' '))
|
|
97
|
+
sections.push(currentSection)
|
|
98
|
+
}
|
|
99
|
+
contentParts = []
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (const node of tree.children) {
|
|
103
|
+
if (node.type === 'heading') {
|
|
104
|
+
saveCurrentSection()
|
|
105
|
+
currentSection = {
|
|
106
|
+
title: extractTextContent(node),
|
|
107
|
+
content: '',
|
|
108
|
+
level: node.depth,
|
|
109
|
+
}
|
|
110
|
+
} else if (currentSection) {
|
|
111
|
+
const text = extractTextContent(node)
|
|
112
|
+
if (text) contentParts.push(text)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
saveCurrentSection()
|
|
117
|
+
|
|
118
|
+
if (sections.length === 0 && title) {
|
|
119
|
+
const allContent = []
|
|
120
|
+
for (const node of tree.children) {
|
|
121
|
+
const text = extractTextContent(node)
|
|
122
|
+
if (text) allContent.push(text)
|
|
123
|
+
}
|
|
124
|
+
if (allContent.length > 0) {
|
|
125
|
+
sections.push({
|
|
126
|
+
title,
|
|
127
|
+
content: cleanContent(allContent.join(' ')),
|
|
128
|
+
level: 1,
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return { title, sections }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function scanDocsDirectory(docsDir, baseDir, lang, results = []) {
|
|
137
|
+
if (!fs.existsSync(docsDir)) return results
|
|
138
|
+
const entries = fs.readdirSync(docsDir, { withFileTypes: true })
|
|
139
|
+
for (const entry of entries) {
|
|
140
|
+
const fullPath = path.join(docsDir, entry.name)
|
|
141
|
+
if (entry.isDirectory()) {
|
|
142
|
+
scanDocsDirectory(fullPath, baseDir, lang, results)
|
|
143
|
+
} else if (entry.isFile() && ['.md', '.mdx'].includes(path.extname(entry.name))) {
|
|
144
|
+
results.push({ filePath: fullPath, relativePath: path.relative(baseDir, fullPath), lang })
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return results
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function generateId(docPath, sectionTitle) {
|
|
151
|
+
const base = docPath.replace(/[\/\\]/g, '-')
|
|
152
|
+
const anchor = sectionTitle.toLowerCase().replace(/[^\w\u4e00-\u9fa5]+/g, '-').replace(/^-+|-+$/g, '')
|
|
153
|
+
return anchor ? `${base}--${anchor}` : base
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function buildUrl(lang, docPath, sectionTitle) {
|
|
157
|
+
const anchor = sectionTitle.toLowerCase().replace(/[^\w\u4e00-\u9fa5]+/g, '-').replace(/^-+|-+$/g, '')
|
|
158
|
+
let url = `/${lang}/${docPath}`
|
|
159
|
+
if (anchor) url += `#${anchor}`
|
|
160
|
+
return url
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function truncateContent(content, maxLength) {
|
|
164
|
+
if (content.length <= maxLength) return content
|
|
165
|
+
return content.slice(0, maxLength)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function generateSearchIndex(lang) {
|
|
169
|
+
console.log(`Generating search index for language: ${lang}`)
|
|
170
|
+
const docsLangDir = path.join(DOCS_DIR, lang)
|
|
171
|
+
const files = scanDocsDirectory(docsLangDir, path.join(PUBLIC_DIR, 'docs'), lang)
|
|
172
|
+
const sections = []
|
|
173
|
+
|
|
174
|
+
for (const file of files) {
|
|
175
|
+
try {
|
|
176
|
+
const content = fs.readFileSync(file.filePath, 'utf-8')
|
|
177
|
+
const docPath = file.relativePath
|
|
178
|
+
.replace(new RegExp(`^${lang}[/\\\\]`), '')
|
|
179
|
+
.replace(/\.(md|mdx)$/, '')
|
|
180
|
+
.replace(/[/\\]index$/, '')
|
|
181
|
+
|
|
182
|
+
const parsed = await parseMarkdown(content)
|
|
183
|
+
|
|
184
|
+
for (const section of parsed.sections) {
|
|
185
|
+
const id = generateId(docPath, section.title)
|
|
186
|
+
const url = buildUrl(lang, docPath, section.title)
|
|
187
|
+
const fullText = section.title + ' ' + section.content
|
|
188
|
+
const tokens = await tokenize(fullText)
|
|
189
|
+
|
|
190
|
+
sections.push({
|
|
191
|
+
id,
|
|
192
|
+
pageTitle: parsed.title,
|
|
193
|
+
sectionTitle: section.title,
|
|
194
|
+
content: truncateContent(section.content, MAX_CONTENT_LENGTH),
|
|
195
|
+
url,
|
|
196
|
+
lang,
|
|
197
|
+
tokens,
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.warn(`Failed to process ${file.filePath}:`, error.message)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
version: SEARCH_INDEX_VERSION,
|
|
207
|
+
generatedAt: Date.now(),
|
|
208
|
+
lang,
|
|
209
|
+
sections,
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function main() {
|
|
214
|
+
console.log('Starting search index generation...')
|
|
215
|
+
if (!fs.existsSync(DOCS_DIR)) {
|
|
216
|
+
console.error('Docs directory not found:', DOCS_DIR)
|
|
217
|
+
process.exit(1)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const entries = fs.readdirSync(DOCS_DIR, { withFileTypes: true })
|
|
221
|
+
const langs = entries.filter(e => e.isDirectory()).map(e => e.name)
|
|
222
|
+
console.log('Found languages:', langs.join(', '))
|
|
223
|
+
|
|
224
|
+
for (const lang of langs) {
|
|
225
|
+
const index = await generateSearchIndex(lang)
|
|
226
|
+
const outputPath = path.join(PUBLIC_DIR, `search-index-${lang}.json`)
|
|
227
|
+
fs.writeFileSync(outputPath, JSON.stringify(index))
|
|
228
|
+
console.log(`Generated: ${outputPath} (${index.sections.length} sections)`)
|
|
229
|
+
}
|
|
230
|
+
console.log('Search index generation complete!')
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
main().catch(error => {
|
|
234
|
+
console.error('Error generating search index:', error)
|
|
235
|
+
process.exit(1)
|
|
236
|
+
})
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import fs from "node:fs"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import yaml from "js-yaml"
|
|
4
|
+
|
|
5
|
+
const rootDir = process.cwd()
|
|
6
|
+
const configDir = path.join(rootDir, "public", "config")
|
|
7
|
+
const outputFile = path.join(rootDir, "src", "generated", "shiki-bundle.ts")
|
|
8
|
+
const defaultLangs = [
|
|
9
|
+
"javascript", "typescript", "jsx", "tsx", "bash", "shell",
|
|
10
|
+
"python", "java", "c", "cpp", "csharp", "go", "rust", "ruby",
|
|
11
|
+
"php", "swift", "kotlin", "sql", "json", "yaml", "toml",
|
|
12
|
+
"markdown", "html", "css", "scss", "less", "vue", "svelte",
|
|
13
|
+
"docker", "nginx", "xml", "diff", "regex",
|
|
14
|
+
]
|
|
15
|
+
const langAliasMap = {
|
|
16
|
+
js: "javascript",
|
|
17
|
+
ts: "typescript",
|
|
18
|
+
sh: "shell",
|
|
19
|
+
shell: "bash",
|
|
20
|
+
yml: "yaml",
|
|
21
|
+
md: "markdown",
|
|
22
|
+
rb: "ruby",
|
|
23
|
+
py: "python",
|
|
24
|
+
dockerfile: "docker",
|
|
25
|
+
conf: "nginx",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function resolveLang(lang) {
|
|
29
|
+
const normalized = String(lang || "").trim().toLowerCase()
|
|
30
|
+
return normalized ? langAliasMap[normalized] || normalized : null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readYamlConfig(fileName) {
|
|
34
|
+
const filePath = path.join(configDir, fileName)
|
|
35
|
+
return fs.existsSync(filePath) ? yaml.load(fs.readFileSync(filePath, "utf8")) : null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function collectBundleConfig() {
|
|
39
|
+
const configs = ["site.yaml", "site.en.yaml"].map(readYamlConfig).filter(Boolean)
|
|
40
|
+
const langs = new Set()
|
|
41
|
+
const themes = new Set()
|
|
42
|
+
for (const config of configs) {
|
|
43
|
+
const configuredLangs = config?.codeHighlight?.langs
|
|
44
|
+
const sourceLangs = Array.isArray(configuredLangs) && configuredLangs.length > 0 ? configuredLangs : defaultLangs
|
|
45
|
+
sourceLangs.forEach(lang => {
|
|
46
|
+
const resolved = resolveLang(lang)
|
|
47
|
+
if (resolved) langs.add(resolved)
|
|
48
|
+
})
|
|
49
|
+
themes.add(String(config?.codeHighlight?.lightTheme || "github-light").trim())
|
|
50
|
+
themes.add(String(config?.codeHighlight?.darkTheme || "github-dark").trim())
|
|
51
|
+
}
|
|
52
|
+
if (langs.size === 0) defaultLangs.forEach(lang => langs.add(resolveLang(lang)))
|
|
53
|
+
if (themes.size === 0) {
|
|
54
|
+
themes.add("github-light")
|
|
55
|
+
themes.add("github-dark")
|
|
56
|
+
}
|
|
57
|
+
return { langs: Array.from(langs).sort(), themes: Array.from(themes).sort() }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function toObjectEntries(values, basePath) {
|
|
61
|
+
return values.map(value => ` ${JSON.stringify(value)}: () => import(${JSON.stringify(`${basePath}/${value}`)}),`).join("\n")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function main() {
|
|
65
|
+
const bundleConfig = collectBundleConfig()
|
|
66
|
+
const fileContent = `export const siteShikiBundle = {
|
|
67
|
+
langs: {
|
|
68
|
+
${toObjectEntries(bundleConfig.langs, "shiki/langs")}
|
|
69
|
+
},
|
|
70
|
+
themes: {
|
|
71
|
+
${toObjectEntries(bundleConfig.themes, "shiki/themes")}
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
`
|
|
75
|
+
fs.mkdirSync(path.dirname(outputFile), { recursive: true })
|
|
76
|
+
fs.writeFileSync(outputFile, fileContent, "utf8")
|
|
77
|
+
console.log(`[shiki-bundle] generated ${path.relative(rootDir, outputFile)} with ${bundleConfig.langs.length} languages and ${bundleConfig.themes.length} themes`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
main()
|