This commit is contained in:
SoilZHu
2026-05-09 00:39:59 +08:00
parent aee0486288
commit c71e397908
8 changed files with 325 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
import { defineConfig } from 'vitepress'
import { generateNavAndSidebar } from './navSidebar.mjs'
const { nav, sidebar } = generateNavAndSidebar(process.cwd())
export default defineConfig({
lang: 'zh-CN',
title: 'CookLikeHOC',
description: '像老乡鸡那样做饭',
lastUpdated: true,
cleanUrls: true,
base: '/CookLikeHOC/',
ignoreDeadLinks: true,
srcExclude: ['**/README.md'],
themeConfig: {
logo: '/logo.png',
nav: [
{ text: '首页', link: '/' },
...nav,
{ text: 'GitHub', link: 'https://github.com/Gar-b-age/CookLikeHOC' },
],
sidebar,
search: { provider: 'local' },
outline: [2, 3],
docFooter: { prev: '上一页', next: '下一页' },
lastUpdatedText: '上次更新',
},
vite: { server: { host: true } },
})
+33
View File
@@ -0,0 +1,33 @@
import { defineConfig } from 'vitepress'
import { generateNavAndSidebar } from './navSidebar'
const { nav, sidebar } = generateNavAndSidebar(process.cwd())
export default defineConfig({
lang: 'zh-CN',
title: 'CookLikeHOC',
description: '像老乡鸡那样做饭',
lastUpdated: true,
cleanUrls: true,
themeConfig: {
logo: '/logo.png',
nav: [
{ text: '首页', link: '/' },
...nav,
{ text: 'GitHub', link: 'https://github.com/Gar-b-age/CookLikeHOC' },
],
sidebar,
search: {
provider: 'local'
},
outline: [2, 3],
docFooter: {
prev: '上一页',
next: '下一页',
},
lastUpdatedText: '上次更新',
},
vite: {
server: { host: true },
},
})
+64
View File
@@ -0,0 +1,64 @@
import fs from 'node:fs'
import path from 'node:path'
const DOC_EXT = ['.md']
const EXCLUDED_DIRS = new Set(['.git', '.github', '.vitepress', 'node_modules', 'public', 'docs', 'images', 'docker_support'])
function isDirectory(p) {
return fs.existsSync(p) && fs.statSync(p).isDirectory()
}
function isMarkdown(p) {
return fs.existsSync(p) && fs.statSync(p).isFile() && DOC_EXT.includes(path.extname(p))
}
function titleFromName(name) {
return name.replace(/\.md$/i, '')
}
function sortByPinyinOrName(a, b) {
return a.localeCompare(b, 'zh-Hans-CN-u-co-pinyin')
}
export function generateNavAndSidebar(rootDir) {
const entries = fs.readdirSync(rootDir)
const sections = entries
.filter((e) => isDirectory(path.join(rootDir, e)))
.filter((e) => !EXCLUDED_DIRS.has(e) && !e.startsWith('.'))
.sort(sortByPinyinOrName)
const nav = []
const sidebar = {}
for (const dir of sections) {
const abs = path.join(rootDir, dir)
const readme = ['README.md', 'readme.md', 'index.md'].find((n) => fs.existsSync(path.join(abs, n)))
const files = fs
.readdirSync(abs)
.filter((f) => isMarkdown(path.join(abs, f)))
.sort(sortByPinyinOrName)
const items = files.map((f) => ({
text: titleFromName(f),
link: `/${encodeURI(dir)}/${encodeURI(f)}`,
}))
if (items.length > 0) {
sidebar[`/${dir}/`] = [
{
text: dir,
items,
},
]
if (readme) {
nav.push({ text: dir, link: `/${encodeURI(dir)}/${encodeURI(readme)}` })
} else {
nav.push({ text: dir, link: items[0].link })
}
} else {
nav.push({ text: dir, link: `/${encodeURI(dir)}/` })
}
}
return { nav, sidebar }
}
+101
View File
@@ -0,0 +1,101 @@
import fs from 'node:fs'
import path from 'node:path'
import type { DefaultTheme } from 'vitepress'
export type SidebarItem = DefaultTheme.SidebarItem
export type NavItem = DefaultTheme.NavItem
export type Sidebar = DefaultTheme.Sidebar
const DOC_EXT = ['.md']
const EXCLUDED_DIRS = new Set([
'.git',
'.github',
'.vitepress',
'node_modules',
'images',
'docker_support',
'public',
'docs',
'images',
'docker_support',
])
function isDirectory(p: string) {
return fs.existsSync(p) && fs.statSync(p).isDirectory()
}
function isMarkdown(p: string) {
return fs.existsSync(p) && fs.statSync(p).isFile() && DOC_EXT.includes(path.extname(p))
}
function titleFromName(name: string) {
// strip extension & use as-is (Chinese names kept)
return name.replace(/\.md$/i, '')
}
function sortByPinyinOrName(a: string, b: string) {
return a.localeCompare(b, 'zh-Hans-CN-u-co-pinyin')
}
export function generateNavAndSidebar(rootDir: string) {
const entries = fs.readdirSync(rootDir)
const sections = entries
.filter((e) => isDirectory(path.join(rootDir, e)))
.filter((e) => !EXCLUDED_DIRS.has(e) && !e.startsWith('.'))
sections.sort(sortByPinyinOrName)
const nav: NavItem[] = []
const sidebar: Sidebar = {}
// This is the item type we generate from files. It has a required text and link.
// It is compatible with both NavItem and SidebarItem.
type LinkItem = { text: string; link: string }
for (const dir of sections) {
const abs = path.join(rootDir, dir)
const files = fs
.readdirSync(abs)
.filter((f) => isMarkdown(path.join(abs, f)))
.sort(sortByPinyinOrName)
const items: LinkItem[] = files.map((f) => ({
text: titleFromName(f),
link: `/${encodeURI(dir)}/${encodeURI(f)}`,
}))
if (items.length > 0) {
const readme = ['README.md', 'readme.md', 'index.md'].find((n) =>
fs.existsSync(path.join(abs, n)),
)
const readmeURI = readme ? `/${encodeURI(dir)}/${encodeURI(readme)}` : undefined
let sectionLink: string
let sectionItems: LinkItem[]
if (readmeURI) {
sectionLink = readmeURI
sectionItems = items.filter((i) => i.link !== readmeURI)
} else {
sectionLink = items[0].link!
sectionItems = items.slice(1)
}
sidebar[`/${dir}/`] = [
{
text: dir,
link: sectionLink,
items: sectionItems,
},
]
nav.push({
text: dir,
link: sectionLink,
})
} else {
nav.push({ text: dir, link: `/${encodeURI(dir)}/` })
}
}
return { nav, sidebar }
}
+66
View File
@@ -0,0 +1,66 @@
import fs from 'node:fs'
import path from 'node:path'
const ROOT = process.cwd()
const EXCLUDED_DIRS = new Set(['.git', '.github', '.vitepress', 'node_modules', 'public', 'docs', 'images', 'docker_support'])
function isDirectory(p) {
return fs.existsSync(p) && fs.statSync(p).isDirectory()
}
function isMarkdown(p) {
return fs.existsSync(p) && fs.statSync(p).isFile() && path.extname(p).toLowerCase() === '.md'
}
function sortByPinyinOrName(a, b) {
return a.localeCompare(b, 'zh-Hans-CN-u-co-pinyin')
}
function titleFromName(name) {
return name.replace(/\.md$/i, '')
}
function buildIndexContent(dirName, files) {
const header = `# ${dirName}\n\n<!-- AUTO-GENERATED: index for ${dirName}. Edit source files instead. -->\n\n`
if (files.length === 0) return header + '(暂无条目)\n'
const list = files
.sort(sortByPinyinOrName)
.map((f) => `- [${titleFromName(f)}](${encodeURI('./' + f)})`)
.join('\n')
return header + list + '\n'
}
function shouldOverwriteExisting(readmePath) {
if (!fs.existsSync(readmePath)) return true
const content = fs.readFileSync(readmePath, 'utf8')
// 仅覆盖带有标记的自动生成文件,避免覆盖人工维护的索引
return content.includes('AUTO-GENERATED: index')
}
function main() {
const entries = fs.readdirSync(ROOT)
const dirs = entries
.filter((e) => isDirectory(path.join(ROOT, e)))
.filter((e) => !EXCLUDED_DIRS.has(e) && !e.startsWith('.'))
.sort(sortByPinyinOrName)
let changed = 0
for (const dir of dirs) {
const abs = path.join(ROOT, dir)
const files = fs
.readdirSync(abs)
.filter((f) => isMarkdown(path.join(abs, f)))
.filter((f) => f.toLowerCase() !== 'readme.md' && f.toLowerCase() !== 'index.md')
.sort(sortByPinyinOrName)
const readmePath = path.join(abs, 'README.md')
if (!shouldOverwriteExisting(readmePath)) continue
const content = buildIndexContent(dir, files)
fs.writeFileSync(readmePath, content, 'utf8')
changed++
}
console.log(`[generate-indexes] updated ${changed} index file(s).`)
}
main()
+6
View File
@@ -0,0 +1,6 @@
import DefaultTheme from 'vitepress/theme'
import './style.css'
export default {
extends: DefaultTheme,
}
+7
View File
@@ -0,0 +1,7 @@
:root{
--vp-font-family-base: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Noto Sans", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
}
.VPDoc .VPDocAsideOutline {
max-height: calc(100vh - 8rem);
}
+19
View File
@@ -0,0 +1,19 @@
---
layout: home
hero:
name: CookLikeHOC
text: 像老乡鸡那样做饭
tagline: 文字来自《老乡鸡菜品溯源报告》,并做归纳、编辑与整理
actions:
- theme: brand
text: 开始浏览
link: /炒菜/README
- theme: alt
text: GitHub
link: https://github.com/Gar-b-age/CookLikeHOC
features:
- title: 新更新
details: 已添加2026年发布的《老乡鸡菜品溯源报告 2.0》中新出现的菜品。
- title: 开始做菜吗
details: 好
---