├── test ├── resources │ ├── empty │ │ └── .gitkeep │ ├── folder-within-md │ │ ├── README │ │ ├── README.txt │ │ └── not-markdown.md.txt │ ├── numeric-prefix │ │ ├── 1-one-file.md │ │ ├── 2-two-file.md │ │ ├── 1-1-1-one-file.md │ │ ├── 1-three-file.md │ │ ├── 1.1.1-four-file.md │ │ └── 1-folder │ │ │ └── 11-file.md │ ├── recursive │ │ ├── folderC │ │ │ └── .gitkeep │ │ ├── folderA │ │ │ ├── a.md │ │ │ └── folderAA │ │ │ │ ├── aa.md │ │ │ │ └── folderAAA │ │ │ │ └── folderAAAA │ │ │ │ └── folderAAAAA │ │ │ │ └── aaaaa.md │ │ └── folderB │ │ │ └── folderBB │ │ │ └── folderBBB │ │ │ └── bbb.md │ ├── same-name-path │ │ ├── a.md │ │ └── test │ │ │ └── resources │ │ │ └── same-name-path │ │ │ └── b.md │ ├── symlinks │ │ ├── scan │ │ │ ├── c │ │ │ ├── a.md │ │ │ └── b.md │ │ └── not-scan │ │ │ ├── b.md │ │ │ └── c │ │ │ ├── d.md │ │ │ └── e.md │ ├── general │ │ ├── a.md │ │ ├── b.md │ │ ├── c.md │ │ ├── folder │ │ │ ├── subFolder │ │ │ │ ├── empty │ │ │ │ │ └── .gitkeep │ │ │ │ ├── .hidden.md │ │ │ │ ├── .dot-directory │ │ │ │ │ └── hidden.md │ │ │ │ └── sub-folder-test.md │ │ │ ├── folder-test.md │ │ │ └── folder-test-2.md │ │ ├── test.md │ │ ├── folder-2 │ │ │ ├── folder2.md │ │ │ └── index.md │ │ └── .vitepress │ │ │ └── do-not-include.md │ ├── sort-by-name │ │ ├── aaa.md │ │ ├── aab.md │ │ ├── bdd.md │ │ ├── ccc.md │ │ ├── bbb.md │ │ └── bcc.md │ ├── capitalize │ │ ├── 1-abc-def.md │ │ ├── abc1def2g.md │ │ └── 2.md │ ├── folder-with-index │ │ ├── index.md │ │ ├── 1-a │ │ │ └── index.md │ │ ├── 10-a │ │ │ └── index.md │ │ └── 2-a │ │ │ └── index.md │ ├── .dot-directory │ │ ├── .dot-file.md │ │ └── normal-file.md │ ├── index-files │ │ ├── d │ │ │ └── index │ │ ├── index │ │ │ └── b.md │ │ ├── c │ │ │ └── index.md │ │ └── a │ │ │ └── testindex.md │ ├── format-folder │ │ ├── folder text hello │ │ │ └── file.md │ │ └── folder-text_sample │ │ │ └── file.md │ ├── folder-with-same-name-file │ │ ├── index.md │ │ └── folder-name │ │ │ ├── index.md │ │ │ └── folder-name.md │ ├── numeric-title │ │ ├── 0-file.md │ │ ├── 1-file.md │ │ ├── 10-file.md │ │ ├── 11-file.md │ │ ├── 2-file.md │ │ ├── 100-file.md │ │ ├── 3-afile.md │ │ └── 3-bfile.md │ ├── folder-with-special-char-file │ │ ├── hypen-doc.md │ │ ├── underscore_doc.md │ │ └── special@#$characters.md │ ├── title-with-date-header │ │ ├── 2024-01-01-hello │ │ │ ├── 2024-01-02-hi.md │ │ │ ├── 2024-01-01-hello.md │ │ │ └── 2024-02-01-hello.md │ │ └── 2024-02-01-test │ │ │ ├── 2024-02-02-hi.md │ │ │ ├── 2024-03-01-hi.md │ │ │ └── 2024-02-01-hello.md │ ├── number-title-and-filename │ │ ├── 2-how-to-use.md │ │ └── 1-introduction.md │ ├── format-title │ │ ├── test.md │ │ ├── test3.md │ │ └── test2.md │ ├── special-markdown │ │ └── special-markdown.md │ ├── frontmatter-basic │ │ ├── c.md │ │ ├── a.md │ │ ├── b.md │ │ └── d.md │ ├── frontmatter-order-with-decimal │ │ ├── c.md │ │ ├── d.md │ │ ├── e.md │ │ ├── a.md │ │ └── b.md │ └── frontmatter-custom-title-field │ │ ├── b.md │ │ └── a.md └── specs │ ├── multiple-sidebar.test.ts │ └── base.test.ts ├── docs ├── ko │ ├── guide │ │ ├── index.md │ │ └── getting-started.md │ ├── advanced-usage │ │ ├── index.md │ │ ├── multi-level-sidebar-with-indents.md │ │ ├── multiple-sidebars-how-to.md │ │ └── internationalization.md │ ├── troubleshooting │ │ ├── index.md │ │ └── err-require-esm.md │ ├── introduction.md │ └── index.md ├── en │ ├── guide │ │ ├── index.md │ │ └── getting-started.md │ ├── advanced-usage │ │ ├── index.md │ │ ├── multi-level-sidebar-with-indents.md │ │ ├── multiple-sidebars-how-to.md │ │ └── internationalization.md │ ├── troubleshooting │ │ ├── index.md │ │ └── err-require-esm.md │ ├── introduction.md │ └── index.md ├── zhHans │ ├── guide │ │ ├── index.md │ │ ├── getting-started.md │ │ └── options.md │ ├── advanced-usage │ │ ├── index.md │ │ ├── multi-level-sidebar-with-indents.md │ │ ├── multiple-sidebars-how-to.md │ │ └── internationalization.md │ ├── troubleshooting │ │ ├── index.md │ │ └── err-require-esm.md │ ├── introduction.md │ └── index.md ├── public │ ├── favicon.ico │ ├── logo-16.png │ ├── logo-32.png │ ├── sidebar.png │ ├── doc-collapsed-example.png │ ├── doc-multi-level-docs-after.png │ └── doc-multi-level-docs-before.png └── .vitepress │ ├── theme │ ├── custom.css │ └── index.js │ └── config.ts ├── pnpm-workspace.yaml ├── lib ├── index.ts ├── types.ts └── helper.ts ├── .vscode ├── extensions.json └── settings.json ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── fix_typo.yml │ ├── feature_request.yml │ └── bug_report.yml ├── workflows │ ├── pkg-pr-new.yml │ ├── run-test.yml │ └── publish-documentation.yml └── pull_request_template.md ├── .prettierignore ├── tsconfig.prod.json ├── terser.config.json ├── .editorconfig ├── .prettierrc ├── .npmignore ├── tsconfig.json ├── SECURITY.md ├── .gitignore ├── LICENSE ├── eslint.config.ts ├── package.json ├── CONTRIBUTING.md ├── README.md └── CODE_OF_CONDUCT.md /test/resources/empty/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/folder-within-md/README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/folder-within-md/README.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/numeric-prefix/1-one-file.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/numeric-prefix/2-two-file.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/recursive/folderC/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/same-name-path/a.md: -------------------------------------------------------------------------------- 1 | # a 2 | -------------------------------------------------------------------------------- /test/resources/symlinks/scan/c: -------------------------------------------------------------------------------- 1 | ../not-scan/c -------------------------------------------------------------------------------- /test/resources/general/a.md: -------------------------------------------------------------------------------- 1 | # A 2 | 3 | A 4 | -------------------------------------------------------------------------------- /test/resources/general/b.md: -------------------------------------------------------------------------------- 1 | # B 2 | 3 | B 4 | -------------------------------------------------------------------------------- /test/resources/general/c.md: -------------------------------------------------------------------------------- 1 | # C 2 | 3 | C 4 | -------------------------------------------------------------------------------- /test/resources/numeric-prefix/1-1-1-one-file.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/numeric-prefix/1-three-file.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/numeric-prefix/1.1.1-four-file.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/sort-by-name/aaa.md: -------------------------------------------------------------------------------- 1 | # AAA 2 | -------------------------------------------------------------------------------- /test/resources/sort-by-name/aab.md: -------------------------------------------------------------------------------- 1 | # AAB 2 | -------------------------------------------------------------------------------- /test/resources/sort-by-name/bdd.md: -------------------------------------------------------------------------------- 1 | # BDD 2 | -------------------------------------------------------------------------------- /test/resources/sort-by-name/ccc.md: -------------------------------------------------------------------------------- 1 | # CCC 2 | -------------------------------------------------------------------------------- /docs/ko/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 가이드 3 | --- 4 | -------------------------------------------------------------------------------- /test/resources/capitalize/1-abc-def.md: -------------------------------------------------------------------------------- 1 | empty file 2 | -------------------------------------------------------------------------------- /test/resources/capitalize/abc1def2g.md: -------------------------------------------------------------------------------- 1 | empty file 2 | -------------------------------------------------------------------------------- /test/resources/folder-with-index/index.md: -------------------------------------------------------------------------------- 1 | # Root 2 | -------------------------------------------------------------------------------- /test/resources/folder-within-md/not-markdown.md.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/numeric-prefix/1-folder/11-file.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/recursive/folderA/a.md: -------------------------------------------------------------------------------- 1 | # A File 2 | -------------------------------------------------------------------------------- /test/resources/symlinks/scan/a.md: -------------------------------------------------------------------------------- 1 | # A 2 | 3 | a 4 | -------------------------------------------------------------------------------- /test/resources/symlinks/scan/b.md: -------------------------------------------------------------------------------- 1 | ../not-scan/b.md -------------------------------------------------------------------------------- /docs/en/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Guide 3 | --- 4 | -------------------------------------------------------------------------------- /docs/zhHans/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 指南 3 | --- 4 | -------------------------------------------------------------------------------- /test/resources/.dot-directory/.dot-file.md: -------------------------------------------------------------------------------- 1 | # DotFile 2 | -------------------------------------------------------------------------------- /test/resources/.dot-directory/normal-file.md: -------------------------------------------------------------------------------- 1 | # File 2 | -------------------------------------------------------------------------------- /test/resources/capitalize/2.md: -------------------------------------------------------------------------------- 1 | # a hello world abc-def 2 | -------------------------------------------------------------------------------- /test/resources/folder-with-index/1-a/index.md: -------------------------------------------------------------------------------- 1 | # One 2 | -------------------------------------------------------------------------------- /test/resources/folder-with-index/10-a/index.md: -------------------------------------------------------------------------------- 1 | # Ten 2 | -------------------------------------------------------------------------------- /test/resources/folder-with-index/2-a/index.md: -------------------------------------------------------------------------------- 1 | # Two 2 | -------------------------------------------------------------------------------- /test/resources/general/folder/subFolder/empty/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/resources/general/test.md: -------------------------------------------------------------------------------- 1 | # TestFile 2 | 3 | AAA 4 | -------------------------------------------------------------------------------- /test/resources/symlinks/not-scan/b.md: -------------------------------------------------------------------------------- 1 | # B 2 | 3 | B 4 | -------------------------------------------------------------------------------- /test/resources/symlinks/not-scan/c/d.md: -------------------------------------------------------------------------------- 1 | # D 2 | 3 | D 4 | -------------------------------------------------------------------------------- /test/resources/symlinks/not-scan/c/e.md: -------------------------------------------------------------------------------- 1 | # E 2 | 3 | E 4 | -------------------------------------------------------------------------------- /docs/ko/advanced-usage/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 고급 3 | --- 4 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | onlyBuiltDependencies: 2 | - esbuild 3 | -------------------------------------------------------------------------------- /test/resources/index-files/d/index: -------------------------------------------------------------------------------- 1 | # D 2 | 3 | content 4 | -------------------------------------------------------------------------------- /test/resources/index-files/index/b.md: -------------------------------------------------------------------------------- 1 | # B 2 | 3 | content 4 | -------------------------------------------------------------------------------- /test/resources/recursive/folderA/folderAA/aa.md: -------------------------------------------------------------------------------- 1 | # AA File 2 | -------------------------------------------------------------------------------- /docs/ko/troubleshooting/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 문제 해결 3 | --- 4 | -------------------------------------------------------------------------------- /docs/zhHans/advanced-usage/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 高级使用 3 | --- 4 | -------------------------------------------------------------------------------- /docs/zhHans/troubleshooting/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 故障排除 3 | --- 4 | -------------------------------------------------------------------------------- /test/resources/format-folder/folder text hello/file.md: -------------------------------------------------------------------------------- 1 | # Hello 2 | -------------------------------------------------------------------------------- /test/resources/format-folder/folder-text_sample/file.md: -------------------------------------------------------------------------------- 1 | # Hello 2 | -------------------------------------------------------------------------------- /test/resources/general/folder/subFolder/.hidden.md: -------------------------------------------------------------------------------- 1 | # HiddenFile 2 | -------------------------------------------------------------------------------- /test/resources/index-files/c/index.md: -------------------------------------------------------------------------------- 1 | # Index 2 | 3 | content 4 | -------------------------------------------------------------------------------- /docs/en/advanced-usage/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Advanced Usage 3 | --- 4 | -------------------------------------------------------------------------------- /test/resources/folder-with-same-name-file/index.md: -------------------------------------------------------------------------------- 1 | # Root Index File 2 | -------------------------------------------------------------------------------- /test/resources/numeric-title/0-file.md: -------------------------------------------------------------------------------- 1 | # 0. Hello 2 | 3 | Content 4 | -------------------------------------------------------------------------------- /test/resources/numeric-title/1-file.md: -------------------------------------------------------------------------------- 1 | # 1. Hello 2 | 3 | Content 4 | -------------------------------------------------------------------------------- /test/resources/numeric-title/10-file.md: -------------------------------------------------------------------------------- 1 | # 10. Hello 2 | 3 | Content 4 | -------------------------------------------------------------------------------- /test/resources/numeric-title/11-file.md: -------------------------------------------------------------------------------- 1 | # 11. Hello 2 | 3 | Content 4 | -------------------------------------------------------------------------------- /test/resources/numeric-title/2-file.md: -------------------------------------------------------------------------------- 1 | # 2. Hello 2 | 3 | Content 4 | -------------------------------------------------------------------------------- /test/resources/recursive/folderB/folderBB/folderBBB/bbb.md: -------------------------------------------------------------------------------- 1 | # BBB File 2 | -------------------------------------------------------------------------------- /test/resources/same-name-path/test/resources/same-name-path/b.md: -------------------------------------------------------------------------------- 1 | # b 2 | -------------------------------------------------------------------------------- /docs/en/troubleshooting/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Troubleshooting 3 | --- 4 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | export { withSidebar, generateSidebar } from './sidebar.js'; 2 | -------------------------------------------------------------------------------- /test/resources/folder-with-special-char-file/hypen-doc.md: -------------------------------------------------------------------------------- 1 | # Hypen Doc File 2 | -------------------------------------------------------------------------------- /test/resources/general/folder-2/folder2.md: -------------------------------------------------------------------------------- 1 | # Folder2 File 2 | 3 | DDD 4 | -------------------------------------------------------------------------------- /test/resources/general/folder/folder-test.md: -------------------------------------------------------------------------------- 1 | # FolderTestFile 2 | 3 | BBB 4 | -------------------------------------------------------------------------------- /test/resources/index-files/a/testindex.md: -------------------------------------------------------------------------------- 1 | # Test Index 2 | 3 | content 4 | -------------------------------------------------------------------------------- /test/resources/numeric-title/100-file.md: -------------------------------------------------------------------------------- 1 | # 100. Hello 2 | 3 | Content 4 | -------------------------------------------------------------------------------- /test/resources/numeric-title/3-afile.md: -------------------------------------------------------------------------------- 1 | # 3. Hello (A) 2 | 3 | Content 4 | -------------------------------------------------------------------------------- /test/resources/numeric-title/3-bfile.md: -------------------------------------------------------------------------------- 1 | # 3. Hello (B) 2 | 3 | Content 4 | -------------------------------------------------------------------------------- /test/resources/folder-with-same-name-file/folder-name/index.md: -------------------------------------------------------------------------------- 1 | # Index File 2 | -------------------------------------------------------------------------------- /test/resources/general/folder-2/index.md: -------------------------------------------------------------------------------- 1 | # Folder2 Index File 2 | 3 | Index 4 | -------------------------------------------------------------------------------- /test/resources/sort-by-name/bbb.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: BBB 3 | --- 4 | 5 | # BBB 6 | -------------------------------------------------------------------------------- /test/resources/sort-by-name/bcc.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: bcc 3 | --- 4 | 5 | # BCC 6 | -------------------------------------------------------------------------------- /test/resources/title-with-date-header/2024-01-01-hello/2024-01-02-hi.md: -------------------------------------------------------------------------------- 1 | # File 2 | -------------------------------------------------------------------------------- /test/resources/title-with-date-header/2024-02-01-test/2024-02-02-hi.md: -------------------------------------------------------------------------------- 1 | # File 2 | -------------------------------------------------------------------------------- /test/resources/title-with-date-header/2024-02-01-test/2024-03-01-hi.md: -------------------------------------------------------------------------------- 1 | # File 2 | -------------------------------------------------------------------------------- /test/resources/folder-with-special-char-file/underscore_doc.md: -------------------------------------------------------------------------------- 1 | # Underscore Doc File 2 | -------------------------------------------------------------------------------- /test/resources/general/folder/folder-test-2.md: -------------------------------------------------------------------------------- 1 | ## FolderTestFile 2 2 | 3 | BBBBBB 4 | -------------------------------------------------------------------------------- /test/resources/general/folder/subFolder/.dot-directory/hidden.md: -------------------------------------------------------------------------------- 1 | # HiddenDirectory 2 | -------------------------------------------------------------------------------- /test/resources/title-with-date-header/2024-01-01-hello/2024-01-01-hello.md: -------------------------------------------------------------------------------- 1 | # File 2 | -------------------------------------------------------------------------------- /test/resources/title-with-date-header/2024-01-01-hello/2024-02-01-hello.md: -------------------------------------------------------------------------------- 1 | # File 2 | -------------------------------------------------------------------------------- /test/resources/title-with-date-header/2024-02-01-test/2024-02-01-hello.md: -------------------------------------------------------------------------------- 1 | # File 2 | -------------------------------------------------------------------------------- /test/resources/number-title-and-filename/2-how-to-use.md: -------------------------------------------------------------------------------- 1 | # 2. How-to-use 2 | 3 | Content 4 | -------------------------------------------------------------------------------- /test/resources/folder-with-special-char-file/special@#$characters.md: -------------------------------------------------------------------------------- 1 | # Special Characters File 2 | -------------------------------------------------------------------------------- /test/resources/format-title/test.md: -------------------------------------------------------------------------------- 1 | # html-format-title[link-title](https://github.com) 2 | -------------------------------------------------------------------------------- /test/resources/general/folder/subFolder/sub-folder-test.md: -------------------------------------------------------------------------------- 1 | # SubFolderTestFile 2 | 3 | CCC 4 | -------------------------------------------------------------------------------- /test/resources/number-title-and-filename/1-introduction.md: -------------------------------------------------------------------------------- 1 | # 1. Introduction 2 | 3 | Content 4 | -------------------------------------------------------------------------------- /test/resources/special-markdown/special-markdown.md: -------------------------------------------------------------------------------- 1 | # **[Test](https://github.com)** ~~Page~~ 2 | -------------------------------------------------------------------------------- /test/resources/general/.vitepress/do-not-include.md: -------------------------------------------------------------------------------- 1 | # Do not include this file 2 | 3 | content 4 | -------------------------------------------------------------------------------- /test/resources/recursive/folderA/folderAA/folderAAA/folderAAAA/folderAAAAA/aaaaa.md: -------------------------------------------------------------------------------- 1 | # AAAAA File 2 | -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooy2/vitepress-sidebar/HEAD/docs/public/favicon.ico -------------------------------------------------------------------------------- /docs/public/logo-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooy2/vitepress-sidebar/HEAD/docs/public/logo-16.png -------------------------------------------------------------------------------- /docs/public/logo-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooy2/vitepress-sidebar/HEAD/docs/public/logo-32.png -------------------------------------------------------------------------------- /docs/public/sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooy2/vitepress-sidebar/HEAD/docs/public/sidebar.png -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /test/resources/folder-with-same-name-file/folder-name/folder-name.md: -------------------------------------------------------------------------------- 1 | # Folder Text 2 | 3 | Folder Content 4 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/custom.css: -------------------------------------------------------------------------------- 1 | /* .vitepress/theme/custom.css */ 2 | :root { 3 | --vp-sidebar-width: 350px; 4 | } 5 | -------------------------------------------------------------------------------- /test/resources/format-title/test3.md: -------------------------------------------------------------------------------- 1 | #

abc-def_g

2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: jooy2 4 | custom: ['https://cdget.com/donate'] 5 | -------------------------------------------------------------------------------- /docs/public/doc-collapsed-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooy2/vitepress-sidebar/HEAD/docs/public/doc-collapsed-example.png -------------------------------------------------------------------------------- /test/resources/format-title/test2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ~~hello-world_1~~ hello-world_2 hello-world_4 3 | --- 4 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.js: -------------------------------------------------------------------------------- 1 | import DefaultTheme from 'vitepress/theme'; 2 | import './custom.css'; 3 | 4 | export default DefaultTheme; 5 | -------------------------------------------------------------------------------- /docs/public/doc-multi-level-docs-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooy2/vitepress-sidebar/HEAD/docs/public/doc-multi-level-docs-after.png -------------------------------------------------------------------------------- /docs/public/doc-multi-level-docs-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooy2/vitepress-sidebar/HEAD/docs/public/doc-multi-level-docs-before.png -------------------------------------------------------------------------------- /test/resources/frontmatter-basic/c.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: C Frontmatter 3 | order: 3 4 | date: 2023-02-15 5 | author: cdget.com 6 | --- 7 | 8 | # C File 9 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | docs-dist/ 3 | .idea/ 4 | .vscode/ 5 | docs/.vitepress/cache 6 | node_modules/ 7 | 8 | *-lock.json 9 | *-lock.yaml 10 | -------------------------------------------------------------------------------- /tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["test/**/*.test.ts", "docs-dist/*", "docs/.vitepress/*", "eslint.config.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /test/resources/frontmatter-order-with-decimal/c.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: C Frontmatter 3 | order: 2 4 | date: 2023-02-15 5 | author: cdget.com 6 | --- 7 | 8 | # C File 9 | -------------------------------------------------------------------------------- /test/resources/frontmatter-basic/a.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A Frontmatter 3 | order: 10 4 | date: 2023-01-05 5 | author: cdget.com 6 | exclude: true 7 | --- 8 | 9 | # A File 10 | -------------------------------------------------------------------------------- /test/resources/frontmatter-basic/b.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: B Frontmatter 3 | order: 2 4 | date: 2023-01-01 5 | author: cdget.com 6 | exclude: false 7 | --- 8 | 9 | # B File 10 | -------------------------------------------------------------------------------- /test/resources/frontmatter-basic/d.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: D Frontmatter 3 | order: 1 4 | date: 2024-01-01 5 | author: cdget.com 6 | exclude: true 7 | --- 8 | 9 | # D File 10 | -------------------------------------------------------------------------------- /test/resources/frontmatter-order-with-decimal/d.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: D Frontmatter 3 | order: 1 4 | date: 2024-01-01 5 | author: cdget.com 6 | exclude: true 7 | --- 8 | 9 | # D File 10 | -------------------------------------------------------------------------------- /test/resources/frontmatter-order-with-decimal/e.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: D Frontmatter 3 | order: 3 4 | date: 2024-01-01 5 | author: cdget.com 6 | exclude: true 7 | --- 8 | 9 | # D File 10 | -------------------------------------------------------------------------------- /test/resources/frontmatter-custom-title-field/b.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: B Frontmatter 3 | order: 1 4 | date: 2024-07-05 5 | author: liudonghua 6 | exclude: true 7 | --- 8 | 9 | # A File 10 | -------------------------------------------------------------------------------- /test/resources/frontmatter-order-with-decimal/a.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A Frontmatter 3 | order: 2.2 4 | date: 2023-01-05 5 | author: cdget.com 6 | exclude: true 7 | --- 8 | 9 | # A File 10 | -------------------------------------------------------------------------------- /test/resources/frontmatter-order-with-decimal/b.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: B Frontmatter 3 | order: 2.3 4 | date: 2023-01-01 5 | author: cdget.com 6 | exclude: false 7 | --- 8 | 9 | # B File 10 | -------------------------------------------------------------------------------- /terser.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "module": true, 3 | "compress": true, 4 | "ie8": false, 5 | "safari10": false, 6 | "mangle": {}, 7 | "output": {}, 8 | "parse": {}, 9 | "rename": {} 10 | } 11 | -------------------------------------------------------------------------------- /test/resources/frontmatter-custom-title-field/a.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A Frontmatter 3 | sidebar_title: A Frontmatter Customized 4 | order: 1 5 | date: 2024-07-05 6 | author: liudonghua 7 | exclude: true 8 | --- 9 | 10 | # A File 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | insert_final_newline = true 10 | max_line_length = 100 11 | trim_trailing_whitespace = true 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "quoteProps": "as-needed", 4 | "trailingComma": "none", 5 | "bracketSpacing": true, 6 | "bracketSameLine": false, 7 | "arrowParens": "always", 8 | "insertPragma": false, 9 | "requirePragma": false, 10 | "proseWrap": "never", 11 | "htmlWhitespaceSensitivity": "strict", 12 | "endOfLine": "lf" 13 | } 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | #contact_links: 3 | # - name: GitHub Community Support 4 | # url: https://github.com/orgs/community/discussions 5 | # about: Please ask and answer questions here. 6 | # - name: GitHub Security Bug Bounty 7 | # url: https://bounty.github.com/ 8 | # about: Please report security vulnerabilities here. 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # System files 2 | .DS_Store 3 | Thumbs.db 4 | *.log 5 | 6 | # IDEs 7 | .idea/ 8 | .vscode/ 9 | 10 | # Project files 11 | lib/ 12 | test/ 13 | docs/ 14 | docs-dist/ 15 | CHANGELOG.md 16 | CODE_OF_CONDUCT.md 17 | SECURITY.md 18 | CONTRIBUTING.md 19 | 20 | # For development 21 | .git* 22 | .eslintignore 23 | eslint.config.ts 24 | .prettierignore 25 | .prettierrc 26 | .editorconfig 27 | terser.config.json 28 | tsconfig.json 29 | tsconfig.prod.json 30 | -------------------------------------------------------------------------------- /.github/workflows/pkg-pr-new.yml: -------------------------------------------------------------------------------- 1 | name: Publish any commit to pkg.pr.new 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout code 9 | uses: actions/checkout@v4 10 | - run: | 11 | corepack enable pnpm 12 | corepack use pnpm@latest 13 | - uses: actions/setup-node@v4 14 | with: 15 | node-version: 24 16 | cache: 'pnpm' 17 | - name: Install dependencies 18 | run: pnpm install 19 | - name: Build 20 | run: pnpm build 21 | - run: pnpm dlx pkg-pr-new publish 22 | -------------------------------------------------------------------------------- /docs/zhHans/introduction.md: -------------------------------------------------------------------------------- 1 | # 导言 2 | 3 | **VitePress Sidebar**是 **[VitePress](https://vitepress.dev/zh/)** 的一个插件,可通过简单的设置自动配置和管理页面的侧边栏。 4 | 5 | - ⚡️ 针对最新版**VitePress**进行了优化 6 | - ⚡️ 易于使用,有很多选项可根据自己的喜好进行定制 7 | - ⚡️ 轻量级捆绑文件大小 8 | - ⚡️ 支持 [多个侧边栏](https://vitepress.dev/zh/reference/default-theme-sidebar#multiple-sidebars) 9 | - ⚡️ 支持[Frontmatter](https://vitepress.dev/zh/guide/frontmatter) 10 | - ⚡️ 支持[TypeScript](https://www.typescriptlang.org/zh/) 11 | - ⚡️ 自定义分类、特殊字符转换、文件和文件夹过滤器等菜单! 12 | 13 | ## 实际用途 14 | 15 | **VitePress侧边栏**用于各种项目环境,包括我自己的网络服务。 16 | 17 | - 探索使用的包: https://github.com/jooy2/vitepress-sidebar/network/dependents 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "lib", 4 | "target": "esnext", 5 | "module": "Preserve", 6 | "moduleDetection": "force", 7 | "lib": ["esnext"], 8 | "esModuleInterop": true, 9 | "isolatedModules": true, 10 | "resolveJsonModule": true, 11 | "allowJs": true, 12 | "allowSyntheticDefaultImports": true, 13 | "skipLibCheck": true, 14 | "declaration": true, 15 | "outDir": "./dist", 16 | "strict": true, 17 | "newLine": "lf" 18 | }, 19 | "include": [ 20 | "lib/**/*.ts", 21 | "lib/**/*.d.ts", 22 | "test/**/*.test.ts", 23 | "docs/.vitepress/config.ts", 24 | "eslint.config.ts" 25 | ], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /docs/ko/introduction.md: -------------------------------------------------------------------------------- 1 | # 소개 2 | 3 | **VitePress Sidebar**는 번거로운 작업 없이 한번의 설정만으로 사이드바 메뉴를 자동으로 생성하는 **[VitePress](https://vitepress.dev/ko/)** 플러그인입니다. 수많은 문서에 대한 분류를 손쉽게 만들어 시간을 절약하세요. 4 | 5 | - ⚡️ 최신 버전의 **VitePress**을 지원합니다. 6 | - ⚡️ 간편하게 사용하고, 원하는 대로 사용자 지정할 수 있습니다. 7 | - ⚡️ 가벼운 번들 파일 크기 8 | - ⚡️ [다중 사이드바](https://vitepress.dev/ko/reference/default-theme-sidebar#multiple-sidebars) 지원 9 | - ⚡️ [Frontmatter](https://vitepress.dev/ko/guide/frontmatter) 지원 10 | - ⚡️ [TypeScript](https://www.typescriptlang.org) 지원 11 | - ⚡️ 정렬, 특수 문자 변환, 파일 및 폴더 필터 등을 위한 메뉴를 사용자 지정하세요! 12 | 13 | ## 어디에서 사용되나요? 14 | 15 | **VitePress Sidebar**는 다양한 프로젝트 환경에서 활용되고 있습니다. 16 | 17 | - GitHub 프로젝트에서 현재 활용 중인 패키지를 살펴보세요: https://github.com/jooy2/vitepress-sidebar/network/dependents 18 | -------------------------------------------------------------------------------- /docs/zhHans/troubleshooting/err-require-esm.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'CommonJS: ERR_REQUIRE_ESM' 3 | --- 4 | 5 | # `CommonJS: ERR_REQUIRE_ESM` 6 | 7 | `vitepress-sidebar`是一个**ESM**模块。如果您的项目使用**CJS**,则需要将其转换为**ESM**模块。 8 | 9 | 如需了解**ESM**模块的更多信息,请参阅以下内容:https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c 10 | 11 | 为解决这些问题,有以下几种解决方案: 12 | 13 | ### 解决方案 A 14 | 15 | 如果您想在 CJS 项目中使用该模块,请将文件扩展名从`.js` 改为 `.mjs`,然后再试一次。您可以为特定文件定义模块脚本。 16 | 17 | ### 解决方案 B 18 | 19 | 在`package.json`文件中,添加`"type":"module"`行。这可能需要将项目转换为 ESM 项目。 20 | 21 | ```json5 22 | { 23 | name: 'docs', 24 | type: 'module', // <-- 添加此内容 25 | version: '1.0.0', 26 | scripts: { 27 | dev: 'vitepress dev src', 28 | build: 'vitepress build src', 29 | serve: 'vitepress serve src' 30 | } 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.settings.useSplitJSON": true, 3 | "eslint.format.enable": true, 4 | "eslint.lintTask.enable": false, 5 | "eslint.codeActionsOnSave.rules": null, 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": "explicit" 8 | }, 9 | "eslint.validate": ["javascript"], 10 | "files.autoSave": "afterDelay", 11 | "editor.formatOnSave": true, 12 | "editor.wordWrap": "on", 13 | "editor.defaultFormatter": "esbenp.prettier-vscode", 14 | "[javascript]": { 15 | "editor.defaultFormatter": "esbenp.prettier-vscode" 16 | }, 17 | "editor.tabSize": 2, 18 | "prettier.embeddedLanguageFormatting": "off", 19 | "prettier.enable": true, 20 | "files.associations": { 21 | "*.mjs": "javascript", 22 | "*.cjs": "javascript", 23 | "*.mts": "typescript" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/fix_typo.yml: -------------------------------------------------------------------------------- 1 | name: 'Fix typo request' 2 | description: Request to fix a typo or bad translation in this project 3 | labels: ['typo'] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Before creating an issue, please read the following: 9 | 10 | - Search to see if the same issue already exists, and keep the title concise and accurate so that it's easy for others to understand and search for. 11 | - Please create a separate issue for each type of issue. 12 | - Please be as detailed as possible and write in English so that we can handle your issue quickly. 13 | - type: textarea 14 | attributes: 15 | label: Describe the issue 16 | description: Please describe where the typo occurs and a list of text that needs to be corrected. 17 | validations: 18 | required: true 19 | -------------------------------------------------------------------------------- /docs/ko/troubleshooting/err-require-esm.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'CommonJS: ERR_REQUIRE_ESM' 3 | --- 4 | 5 | # `CommonJS: ERR_REQUIRE_ESM` 6 | 7 | `vitepress-sidebar`는 **ESM** 모듈입니다. 프로젝트에서 **CJS**를 사용하는 경우 **ESM** 모듈로 변환해야 합니다. 8 | 9 | **ESM** 모듈에 대한 자세한 내용은 아래를 참조하세요: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c 10 | 11 | 이러한 문제를 해결하기 위한 몇 가지 해결책이 아래에 나와 있습니다: 12 | 13 | ### 해결책 A 14 | 15 | CJS 프로젝트에 사용하려는 경우 파일 확장자를 `.js`에서 `.mjs`로 변경한 후 다시 시도하세요. 특정 파일에 모듈 스크립트를 사용하도록 정의할 수 있습니다. 16 | 17 | ### 해결책 B 18 | 19 | `package.json` 파일에 `"type": "module"` 줄을 추가합니다. 이 경우 프로젝트를 ESM 프로젝트로 변환해야 할 수도 있습니다. 20 | 21 | ```json5 22 | { 23 | name: 'docs', 24 | type: 'module', // <-- 이 부분을 추가하세요. 25 | version: '1.0.0', 26 | scripts: { 27 | dev: 'vitepress dev src', 28 | build: 'vitepress build src', 29 | serve: 'vitepress serve src' 30 | } 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting Security Issues 4 | 5 | To report a security vulnerability, create an issue on GitHub on the "Open a draft security advisory " page on GitHub: https://github.com/jooy2/vitepress-sidebar/security/advisories/new 6 | 7 | Also, send private instructions in advance via maintainer email. Do not submit vulnerability-related content as a general issue. 8 | 9 | ## Security compliance 10 | 11 | Project maintainers are quickly addressing reported security vulnerabilities in the project and providing relevant patches. 12 | 13 | We report these to the relevant users and handle the correspondence to prevent the issue from recurring. 14 | 15 | ## Security recommendations 16 | 17 | We recommend that users of project sources use the latest version, which addresses possible security vulnerabilities. 18 | 19 | ## Contact 20 | 21 | - Administrator: jooy2.contact@gmail.com 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: 'Feature request' 2 | description: Report a feature request in this project. 3 | labels: ['enhancement'] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Before creating an issue, please read the following: 9 | 10 | - Search to see if the same issue already exists, and keep the title concise and accurate so that it's easy for others to understand and search for. 11 | - Please create a separate issue for each type of issue. 12 | - Please be as detailed as possible and write in English so that we can handle your issue quickly. 13 | - type: textarea 14 | attributes: 15 | label: Describe the feature 16 | description: Feel free to describe any features or improvements you would like to see. You can attach text or images of examples, behavior, etc. from other projects to elaborate. 17 | validations: 18 | required: true 19 | -------------------------------------------------------------------------------- /docs/en/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | **VitePress Sidebar** is a plugin for **[VitePress](https://vitepress.dev)** that automatically configures and manages the sidebar of your page with simple settings. 4 | 5 | - ⚡️ Optimized for the latest version of **VitePress** 6 | - ⚡️ Easy to use, lots of options to customize to your liking 7 | - ⚡️ Lightweight bundle file size, zero dependencies 8 | - ⚡️ [Multiple Sidebars](https://vitepress.dev/reference/default-theme-sidebar#multiple-sidebars) support 9 | - ⚡️ [Frontmatter](https://vitepress.dev/guide/frontmatter) support 10 | - ⚡️ [TypeScript](https://www.typescriptlang.org) support 11 | - ⚡️ Customize menus for sorting, special character conversion, file and folder filters, and more! 12 | 13 | ## Real-world Uses 14 | 15 | **VitePress Sidebar** is utilized in a variety of project environments, including my own web services. 16 | 17 | - To explore packages used other than: https://github.com/jooy2/vitepress-sidebar/network/dependents 18 | -------------------------------------------------------------------------------- /docs/en/troubleshooting/err-require-esm.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'CommonJS: ERR_REQUIRE_ESM' 3 | --- 4 | 5 | # `CommonJS: ERR_REQUIRE_ESM` 6 | 7 | `vitepress-sidebar` is an **ESM** module. If your project is using **CJS**, you will need to convert it to an **ESM** module. 8 | 9 | For more information about the **ESM** module, see below: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c 10 | 11 | To address these issues, there are several solutions below: 12 | 13 | ### Solution A 14 | 15 | If you are trying to use it with a CJS project, change the file extension from `.js` to `.mjs` and try again. You can define that you want to use the module script for a specific file. 16 | 17 | ### Solution B 18 | 19 | in the `package.json` file, add the line `"type": "module"` line. This may require the project to be converted to an ESM project. 20 | 21 | ```json5 22 | { 23 | name: 'docs', 24 | type: 'module', // <-- Add this 25 | version: '1.0.0', 26 | scripts: { 27 | dev: 'vitepress dev src', 28 | build: 'vitepress build src', 29 | serve: 'vitepress serve src' 30 | } 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore for NodeJS Projects 2 | # ---------- Start of common ignore files 3 | 4 | # Node artifact files 5 | node_modules/ 6 | 7 | # Log files 8 | *.log 9 | 10 | # dotenv environment variables file 11 | .env 12 | 13 | # JetBrains IDEs 14 | .idea/ 15 | *.iml 16 | 17 | # Visual Studio Code IDE 18 | .vscode/* 19 | !.vscode/settings.json 20 | !.vscode/tasks.json 21 | !.vscode/launch.json 22 | !.vscode/extensions.json 23 | !.vscode/*.code-snippets 24 | 25 | # Local History for Visual Studio Code 26 | .history/ 27 | 28 | # Built Visual Studio Code Extensions 29 | *.vsix 30 | 31 | # Generated by MacOS 32 | .DS_Store 33 | .AppleDouble 34 | .LSOverride 35 | 36 | # Generated by Windows 37 | Thumbs.db 38 | [Dd]esktop.ini 39 | $RECYCLE.BIN/ 40 | 41 | # Applications 42 | *.app 43 | *.pkg 44 | *.dmg 45 | *.exe 46 | *.war 47 | *.deb 48 | 49 | # Large media files 50 | *.mp4 51 | *.tiff 52 | *.avi 53 | *.flv 54 | *.mov 55 | *.wmv 56 | 57 | # ---------- End of common ignore files 58 | 59 | # Project files 60 | dist/ 61 | docs-dist/ 62 | 63 | # VitePress files 64 | docs/.vitepress/.temp 65 | docs/.vitepress/.temp/* 66 | docs/.vitepress/cache 67 | docs/.vitepress/cache/* 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2025 CDGet (https://cdget.com). 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /.github/workflows/run-test.yml: -------------------------------------------------------------------------------- 1 | name: run-test 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | push: 7 | paths: 8 | - '**' 9 | - '!LICENSE' 10 | - '!*.md' 11 | - '!.github/**' 12 | - '!.vscode/**' 13 | - '!docs/**' 14 | - '.github/workflows/run-test.yml' 15 | # trigger deployment manually 16 | workflow_dispatch: 17 | 18 | jobs: 19 | build-and-run-test: 20 | runs-on: ${{ matrix.os }} 21 | name: Test module from Node.js ${{ matrix.node_version }} on ${{ matrix.os }} 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | node_version: ['20', '22', '24'] 26 | os: [ubuntu-latest, windows-latest, macos-latest] 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | name: Setup Node.js ${{ matrix.node_version }} 31 | with: 32 | # fetch all commits to get last updated time or other git log info 33 | fetch-depth: 0 34 | - uses: actions/setup-node@v4 35 | with: 36 | node-version: ${{ matrix.node_version }} 37 | cache: npm 38 | cache-dependency-path: '**/package-lock.json' 39 | - run: npm ci && npm run test 40 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## Pull request checklist 8 | 9 | You should familiarize yourself with the files `README.md`, `CONTRIBUTING.md`, and `CODE_OF_CONDUCT.md` in the root of your project. 10 | 11 | - If an issue has been created for this, add `(fixes #{ISSUE_NUMBER})` to the end of the commit description. In `{ISSUE_NUMBER}`, please include the relevant issue number. 12 | - If you need to update or add to the article, please update the relevant content. If a multilingual article exists, you should update all relevant content in your own language, except for translations. 13 | - Add or update test code if it exists and is needed. Also, verify that the tests pass. 14 | - If this PR is not yet complete, keep the PR in draft status. If it's no longer valid, close the PR with an explanation. 15 | 16 | 19 | 20 | ### What did you change? 21 | 22 | ### Why did you make the change? 23 | 24 | ### How does this work? 25 | -------------------------------------------------------------------------------- /docs/zhHans/advanced-usage/multi-level-sidebar-with-indents.md: -------------------------------------------------------------------------------- 1 | # 带缩进的多级侧边栏 2 | 3 | 在多层侧边栏中,菜单显示时每层都会缩进。不过,VitePress 默认从第二层开始缩进。例如 4 | 5 | ![Multi level docs before](/doc-multi-level-docs-before.png) 6 | 7 | 上面,`directory-level-2`是`directory-level-1`的子文件,但看起来处于相同的层级。 8 | 9 | 这不是VitePress侧边栏的问题,要解决这个问题,您需要通过**[VitePress的自定义CSS](https://vitepress.dev/zh/guide/extending-default-theme#customizing-css)**自定义现有主题的样式。 10 | 11 | 在`.vitepress`目录下创建一个`theme`目录,以覆盖现有样式所需的样式。然后在`theme`目录下创建一个`index.js`文件(如果您使用的是Typescript,请使用`index.ts`而不是`index.js`)和一个`custom.css`文件。 12 | 13 | ```text 14 | / 15 | ├─ package.json 16 | ├─ src/ 17 | ├─ docs/ 18 | │ ├─ .vitepress/ 19 | │ │ └─ theme/ <------------ Add this 20 | │ │ ├─ custom.css <------------ Add this 21 | │ │ └─ index.js <------------ Add this 22 | │ ├─ example.md 23 | │ └─ index.md 24 | └─ ... 25 | ``` 26 | 27 | 然后在 `index.js` 文件中添加以下内容: 28 | 29 | ```javascript 30 | import DefaultTheme from 'vitepress/theme'; 31 | import './custom.css'; 32 | 33 | export default DefaultTheme; 34 | ``` 35 | 36 | 接下来,在 `custom.css` 文件中添加以下内容: 37 | 38 | ```css 39 | .group:has([role='button']) .VPSidebarItem.level-0 .items { 40 | padding-left: 16px !important; 41 | border-left: 1px solid var(--vp-c-divider); 42 | border-radius: 2px; 43 | transition: background-color 0.25s; 44 | } 45 | ``` 46 | 47 | 现在启动 VitePress 服务器。这样就能更容易地看到子内容所在组的第一级层次结构。 48 | 49 | ![Multi level docs before](/doc-multi-level-docs-after.png) 50 | 51 | 需要注意的是,这里看到的垂直分隔线只是用CSS创建的;它应该创建为一个带有CSS类名为`indicator`的`div`,所以你应该知道,当你以后创建动态页面时,垂直分隔线可能不会被选中。 52 | -------------------------------------------------------------------------------- /eslint.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, globalIgnores } from 'eslint/config'; 2 | import pluginJs from '@eslint/js'; 3 | import pluginTypeScriptESLint from 'typescript-eslint'; 4 | import parserTypeScript from '@typescript-eslint/parser'; 5 | import pluginNode from 'eslint-plugin-n'; 6 | import configPrettier from 'eslint-config-prettier'; 7 | 8 | import globals from 'globals'; 9 | 10 | export default defineConfig( 11 | pluginJs.configs.recommended, 12 | pluginTypeScriptESLint.configs.recommended, 13 | pluginNode.configs['flat/recommended-script'], 14 | globalIgnores([ 15 | '**/.idea', 16 | '**/.vscode', 17 | '**/node_modules', 18 | '**/dist', 19 | '**/docs/.vitepress/cache', 20 | '**/docs-dist', 21 | '**/*-lock.json', 22 | '**/*-lock.yaml' 23 | ]), 24 | { 25 | files: ['**/*.{js,mjs,cjs,ts}'], 26 | languageOptions: { 27 | ecmaVersion: 'latest', 28 | sourceType: 'module', 29 | globals: { 30 | ...globals.node 31 | }, 32 | parserOptions: { 33 | parser: parserTypeScript, 34 | ecmaVersion: 2022, 35 | requireConfigFile: false 36 | } 37 | }, 38 | rules: { 39 | eqeqeq: 'error', 40 | 'no-unused-vars': 'off', 41 | 'no-case-declarations': 'off', 42 | 'no-trailing-spaces': 'error', 43 | 'no-unsafe-optional-chaining': 'off', 44 | 'no-control-regex': 'off', 45 | 'n/no-missing-import': 'off', 46 | 'n/no-unpublished-import': 'off', 47 | 'n/no-unsupported-features/node-builtins': 'off', 48 | '@typescript-eslint/no-explicit-any': 'off' 49 | } 50 | }, 51 | configPrettier 52 | ); 53 | -------------------------------------------------------------------------------- /docs/ko/advanced-usage/multi-level-sidebar-with-indents.md: -------------------------------------------------------------------------------- 1 | # 다중 레벨 사이드바의 들여쓰기 2 | 3 | 다중 사이드바에서는 메뉴가 각 계층마다 들여쓰기로 표시됩니다. 그러나 VitePress는 기본적으로 두 번째 계층부터 들여쓰기를 시작합니다. 예를 들어: 4 | 5 | ![Multi level docs before](/doc-multi-level-docs-before.png) 6 | 7 | 위의 `directory-level-2`는 `directory-level-1`의 하위 파일이지만 같은 계층 구조에 있는 것으로 보입니다. 8 | 9 | 이 문제는 VitePress 사이드바의 문제가 아니므로 이 문제를 해결하려면 **[VitePress의 사용자 정의 CSS](https://vitepress.dev/ko/guide/extending-default-theme#customizing-css)** 기능을 사용하여 기존 테마의 스타일을 사용자 정의해야 합니다. 10 | 11 | `.vitepress` 디렉토리에 `theme` 디렉토리를 만들어 기존 스타일에 필요한 스타일을 재정의합니다. 그런 다음 `theme` 디렉토리 안에 `index.js` 파일(타입스크립트를 사용하는 경우 `index.js` 대신 `index.ts`를 사용)과 `custom.css` 파일을 만듭니다. 12 | 13 | ```text 14 | / 15 | ├─ package.json 16 | ├─ src/ 17 | ├─ docs/ 18 | │ ├─ .vitepress/ 19 | │ │ └─ theme/ <------------ 이 줄 추가 20 | │ │ ├─ custom.css <------------ 이 줄 추가 21 | │ │ └─ index.js <------------ 이 줄 추가 22 | │ ├─ example.md 23 | │ └─ index.md 24 | └─ ... 25 | ``` 26 | 27 | 그런 다음 `index.js` 파일에 다음을 추가합니다: 28 | 29 | ```javascript 30 | import DefaultTheme from 'vitepress/theme'; 31 | import './custom.css'; 32 | 33 | export default DefaultTheme; 34 | ``` 35 | 36 | 다음으로 `custom.css` 파일에 다음을 추가합니다: 37 | 38 | ```css 39 | .group:has([role='button']) .VPSidebarItem.level-0 .items { 40 | padding-left: 16px !important; 41 | border-left: 1px solid var(--vp-c-divider); 42 | border-radius: 2px; 43 | transition: background-color 0.25s; 44 | } 45 | ``` 46 | 47 | 이제 VitePress 서버를 시작합니다. 이렇게 하면 하위 콘텐츠가 존재하는 그룹의 첫 번째 레벨의 계층 구조를 더 쉽게 확인할 수 있습니다. 48 | 49 | ![Multi level docs before](/doc-multi-level-docs-after.png) 50 | 51 | 여기에서 보이는 세로선은 CSS로만 생성된 것으로, `indicator`라는 CSS 클래스가 있는 `div`로 생성되어야 하므로 향후 동적 페이지를 작성할 때 세로선이 선택되지 않을 수 있다는 점에 유의해야 합니다. 52 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 'Bug report' 2 | description: Report a bug in this project. 3 | labels: ['bug'] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Before creating an issue, please read the following: 9 | 10 | - Read the `README.md` file on the project page or the documentation file and compare your code to the intent of the project. 11 | - Search to see if the same issue already exists, and keep the title concise and accurate so that it's easy for others to understand and search for. 12 | - Please create a separate issue for each type of issue. 13 | - For modular projects, make sure you're using the latest version of the module. 14 | - Please be as detailed as possible and write in English so that we can handle your issue quickly. 15 | - type: textarea 16 | attributes: 17 | label: Describe the bug 18 | description: | 19 | For the issue you are experiencing, please describe in detail what you are seeing, the error message, and the impact of the issue. If you are able to reproduce the issue, please list the steps in order. You can attach an image or video if necessary. 20 | validations: 21 | required: true 22 | - type: textarea 23 | attributes: 24 | label: Expected behavior 25 | description: Describe how it should be handled when it's normal behavior or what needs to be fixed. 26 | validations: 27 | - type: input 28 | attributes: 29 | label: Your environment - System OS 30 | description: Please describe the full range of OSes you are experiencing the issue with, preferably including the version. 31 | placeholder: Windows 11, macOS 15.x, Linux Ubuntu 24.04, Android 15, iOS 16... 32 | validations: 33 | - type: input 34 | attributes: 35 | label: Your environment - Web Browser 36 | description: If relevant, please describe the web browser you are currently using. 37 | placeholder: Google Chrome, Microsoft Edge, Mozilla Firefox... 38 | validations: 39 | -------------------------------------------------------------------------------- /docs/en/advanced-usage/multi-level-sidebar-with-indents.md: -------------------------------------------------------------------------------- 1 | # Multi-level-sidebar with indents 2 | 3 | In a multi-level sidebar, the menu is displayed with indentation for each tier. However, VitePress starts indenting from the second tier by default. For example: 4 | 5 | ![Multi level docs before](/doc-multi-level-docs-before.png) 6 | 7 | Above, `directory-level-2` is a subfile of `directory-level-1`, but it appears to be in the same hierarchy. 8 | 9 | This is not an issue with VitePress Sidebar, so to fix it, you'll need to customize the styling of your existing theme via **[VitePress' Custom CSS](https://vitepress.dev/guide/extending-default-theme#customizing-css)**. 10 | 11 | Create a `theme` directory in the `.vitepress` directory to override the styles required by the existing styles. Then, inside the `theme` directory, create an `index.js` file (If you're using Typescript, use `index.ts` instead of `index.js`) and a `custom.css` file. 12 | 13 | ```text 14 | / 15 | ├─ package.json 16 | ├─ src/ 17 | ├─ docs/ 18 | │ ├─ .vitepress/ 19 | │ │ └─ theme/ <------------ Add this 20 | │ │ ├─ custom.css <------------ Add this 21 | │ │ └─ index.js <------------ Add this 22 | │ ├─ example.md 23 | │ └─ index.md 24 | └─ ... 25 | ``` 26 | 27 | Then add the following to the `index.js` file: 28 | 29 | ```javascript 30 | import DefaultTheme from 'vitepress/theme'; 31 | import './custom.css'; 32 | 33 | export default DefaultTheme; 34 | ``` 35 | 36 | Next, add the following to the `custom.css` file: 37 | 38 | ```css 39 | .group:has([role='button']) .VPSidebarItem.level-0 .items { 40 | padding-left: 16px !important; 41 | border-left: 1px solid var(--vp-c-divider); 42 | border-radius: 2px; 43 | transition: background-color 0.25s; 44 | } 45 | ``` 46 | 47 | Now start the VitePress server. This will make it easier to see the hierarchy of the first level of the group where the child content exists. 48 | 49 | ![Multi level docs before](/doc-multi-level-docs-after.png) 50 | 51 | It's important to note that the vertical divider you see here was only created with CSS; it should have been created as a `div` with a CSS class called `indicator`, so you should be aware that the vertical divider may not be selected when you build dynamic pages in the future. 52 | -------------------------------------------------------------------------------- /.github/workflows/publish-documentation.yml: -------------------------------------------------------------------------------- 1 | name: publish-documentation 2 | 3 | on: 4 | # run every time a push occurs in the docs folder 5 | push: 6 | branches: [main] 7 | paths: 8 | - docs/** 9 | - .github/workflows/documents.yml 10 | - package.json 11 | - CHANGELOG.md 12 | - package-lock.json 13 | # trigger deployment manually 14 | workflow_dispatch: 15 | 16 | jobs: 17 | publish-documentation: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | # fetch all commits to get last updated time or other git log info 24 | fetch-depth: 0 25 | 26 | - name: Setup Node.js 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version: '24' 30 | 31 | # cache node_modules 32 | - name: Cache dependencies 33 | uses: actions/cache@v4 34 | id: npm-cache 35 | with: 36 | path: | 37 | **/node_modules 38 | key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} 39 | restore-keys: | 40 | ${{ runner.os }}-npm- 41 | 42 | # copy changelog.md file 43 | - name: Copy CHANGELOG.md file 44 | run: | 45 | cp CHANGELOG.md ./docs/en/changelog.md 46 | cp CHANGELOG.md ./docs/ko/changelog.md 47 | cp CHANGELOG.md ./docs/zhHans/changelog.md 48 | 49 | # install dependencies if the cache did not hit 50 | - name: Install dependencies 51 | if: steps.npm-cache.outputs.cache-hit != 'true' 52 | run: npm install 53 | 54 | # run build script 55 | - name: Build VitePress site 56 | run: npm run docs:build 57 | 58 | # please check out the docs of the workflow for more details 59 | # @see https://github.com/crazy-max/ghaction-github-pages 60 | - name: Deploy to GitHub Pages 61 | uses: crazy-max/ghaction-github-pages@v4 62 | with: 63 | # deploy to gh-pages branch 64 | target_branch: gh-pages 65 | # deploy the default output dir of VitePress 66 | build_dir: docs-dist 67 | jekyll: false 68 | fqdn: 'vitepress-sidebar.cdget.com' 69 | env: 70 | # @see https://docs.github.com/en/actions/reference/authentication-in-a-workflow#about-the-github_token-secret 71 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 72 | -------------------------------------------------------------------------------- /lib/types.ts: -------------------------------------------------------------------------------- 1 | export declare interface VitePressSidebarOptions { 2 | documentRootPath?: string; 3 | scanStartPath?: string; 4 | resolvePath?: string; 5 | basePath?: string; 6 | collapsed?: boolean | null | undefined; 7 | collapseDepth?: number; 8 | hyphenToSpace?: boolean; 9 | underscoreToSpace?: boolean; 10 | capitalizeFirst?: boolean; 11 | capitalizeEachWords?: boolean; 12 | includeRootIndexFile?: boolean; 13 | includeFolderIndexFile?: boolean; 14 | useTitleFromFileHeading?: boolean; 15 | useTitleFromFrontmatter?: boolean; 16 | useFolderTitleFromIndexFile?: boolean; 17 | useFolderLinkFromIndexFile?: boolean; 18 | useFolderLinkFromSameNameSubFile?: boolean; 19 | includeDotFiles?: boolean; 20 | folderLinkNotIncludesFileName?: boolean; 21 | includeEmptyFolder?: boolean; 22 | sortMenusByName?: boolean; 23 | sortMenusByFrontmatterOrder?: boolean; 24 | sortMenusByFrontmatterDate?: boolean; 25 | sortMenusByFileDatePrefix?: boolean; 26 | sortMenusOrderByDescending?: boolean; 27 | sortMenusOrderNumericallyFromTitle?: boolean; 28 | sortMenusOrderNumericallyFromLink?: boolean; 29 | sortFolderTo?: null | undefined | 'top' | 'bottom'; 30 | keepMarkdownSyntaxFromTitle?: boolean; 31 | debugPrint?: boolean; 32 | manualSortFileNameByPriority?: string[]; 33 | excludeByFolderDepth?: number; 34 | excludeByGlobPattern?: string[]; 35 | excludeFilesByFrontmatterFieldName?: string; 36 | followSymlinks?: boolean; 37 | removePrefixAfterOrdering?: boolean; 38 | prefixSeparator?: string | RegExp; 39 | rootGroupText?: string; 40 | rootGroupLink?: string; 41 | rootGroupCollapsed?: boolean | null | undefined; 42 | frontmatterOrderDefaultValue?: number; 43 | frontmatterTitleFieldName?: string; 44 | /** 45 | * @deprecated `excludePattern` has renamed `excludeByGlobPattern`. This option will be removed in a future version. 46 | */ 47 | excludePattern?: string[]; 48 | } 49 | 50 | export declare interface SidebarListItem { 51 | [key: string]: any; 52 | } 53 | 54 | export declare interface SortByObjectKeyOptions { 55 | arr: SidebarListItem; 56 | key: string; 57 | desc?: boolean; 58 | numerically?: boolean; 59 | datePrefixSeparator?: string | RegExp; 60 | dateSortFromFrontmatter?: boolean; 61 | dateSortFromTextWithPrefix?: boolean; 62 | } 63 | 64 | export declare type AnyValueObject = { [key: string]: any }; 65 | 66 | /* 67 | * Types from: `vitepress/types/default-theme.d.ts` 68 | */ 69 | export type SidebarItem = { 70 | text?: string; 71 | link?: string; 72 | items?: SidebarItem[]; 73 | collapsed?: boolean; 74 | }; 75 | 76 | export interface SidebarMultiItem { 77 | base: string; 78 | items: SidebarItem[]; 79 | } 80 | 81 | export interface SidebarMulti { 82 | [path: string]: SidebarMultiItem; 83 | } 84 | 85 | export type Sidebar = SidebarItem[] | SidebarMulti; 86 | /* 87 | * END 88 | */ 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vitepress-sidebar", 3 | "version": "1.33.1", 4 | "description": "A VitePress auto sidebar plugin that automatically creates a simple configuration.", 5 | "type": "module", 6 | "types": "dist/index.d.ts", 7 | "author": "CDGet ", 8 | "license": "MIT", 9 | "homepage": "https://vitepress-sidebar.cdget.com", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/jooy2/vitepress-sidebar" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/jooy2/vitepress-sidebar/issues" 16 | }, 17 | "scripts": { 18 | "build": "npm run format:fix && tsc --project tsconfig.prod.json && npm run minify", 19 | "test": "npm run build && glob -c \"tsx --test\" \"./test/**/*.test.ts\"", 20 | "lint": "eslint . --ext .js,.ts .", 21 | "lint:fix": "eslint . --ext .js,.ts --fix .", 22 | "minify": "terser-glob 'dist/**/*.js' --config-file terser.config.json", 23 | "prepare": "npm run build", 24 | "format": "prettier .", 25 | "format:fix": "prettier . --write", 26 | "docs:dev": "npm run build && vitepress dev docs", 27 | "docs:build": "npm run build && vitepress build docs", 28 | "docs:serve": "npm run build && npm run docs:build && vitepress serve docs" 29 | }, 30 | "engines": { 31 | "node": ">=18.0.0" 32 | }, 33 | "main": "dist/index.js", 34 | "exports": { 35 | ".": { 36 | "types": "./dist/index.d.ts", 37 | "module": "./dist/index.js", 38 | "default": "./dist/index.js" 39 | }, 40 | "./types": { 41 | "types": "./dist/types.d.ts", 42 | "module": "./dist/types.js", 43 | "default": "./dist/types.js" 44 | } 45 | }, 46 | "typesVersions": { 47 | "*": { 48 | "index.d.ts": [ 49 | "dist/index.d.ts" 50 | ] 51 | } 52 | }, 53 | "keywords": [ 54 | "vitepress", 55 | "vitepress-plugin", 56 | "vitepress-plugin-sidebar", 57 | "vitepress-sidebar", 58 | "vitepress-auto-sidebar", 59 | "vitepress-plugin-auto-sidebar", 60 | "vitepress-menu", 61 | "vitepress-sidemenu", 62 | "util", 63 | "utility", 64 | "sidebar", 65 | "sidebar-generator", 66 | "generator", 67 | "plugin", 68 | "sidemenu", 69 | "submenu", 70 | "menu", 71 | "autosidebar" 72 | ], 73 | "devDependencies": { 74 | "@eslint/js": "^9.39.1", 75 | "@types/node": "^24.10.1", 76 | "@typescript-eslint/parser": "^8.48.0", 77 | "eslint": "^9.39.1", 78 | "eslint-config-prettier": "^10.1.8", 79 | "eslint-plugin-n": "^17.23.1", 80 | "globals": "^16.5.0", 81 | "jiti": "^2.6.1", 82 | "prettier": "^3.7.2", 83 | "terser-glob": "^1.1.0", 84 | "tsx": "^4.20.6", 85 | "typescript": "^5.9.3", 86 | "typescript-eslint": "^8.48.0", 87 | "vitepress": "^1.6.4", 88 | "vitepress-i18n": "^1.3.4" 89 | }, 90 | "dependencies": { 91 | "glob": "11.1.0", 92 | "gray-matter": "4.0.3", 93 | "qsu": "^1.10.4" 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Project 2 | 3 | Thank you for contributing to the project. Your contributions will help us take the project to the next level. 4 | 5 | This project adheres to the Contributor Covenant code of conduct. Your contribution implies that you have read and agree to this policy. Any behavior that undermines the quality of the project community, including this policy, will be warned or restricted by the maintainers. 6 | 7 | ## Issues 8 | 9 | Issues can be created on the following page: https://github.com/jooy2/vitepress-sidebar/issues 10 | 11 | Alternatively, you can email the package maintainer. However, we prefer to track progress via GitHub Issues. 12 | 13 | When creating an issue, keep the following in mind: 14 | 15 | - Please specify the correct category selection based on the format of the issue (e.g., bug report, feature request). 16 | - Check to see if there are duplicate issues. 17 | - Describe in detail what is happening and what needs to be fixed. You may need additional materials such as images or video. 18 | - Use appropriate keyword titles to make it easy for others to search and understand. 19 | - Please use English in all content. 20 | - You may need to describe the environment in which the issue occurs. 21 | 22 | ## How to contribute (Pull Requests) 23 | 24 | ### Write the code you want to change 25 | 26 | Here's the process for contributing to the project: 27 | 28 | 1. Clone the project (or rebase to the latest commit in the main branch) 29 | 2. Install the package (if the package manager exists) 30 | 3. Setting up lint or code formatter in the IDE (if your project includes a linter) and installing the relevant plugins. Some projects may use specific commands to check rules and perform formatting after module installation and before committing. 31 | 4. Write the code that needs to be fixed 32 | 5. Update the documentation (if it exists) or create a new one. If your project supports multilingual documentation, update the documentation for all languages. You can fill in the content in your own language and not translate it. 33 | 6. Add or modify tests as needed (if test code exists). You should also verify that existing tests pass. 34 | 35 | ### Write a commit message 36 | 37 | While we don't have strict restrictions on commit messages, we recommend that you follow the recommendations below whenever possible: 38 | 39 | - Write in English. 40 | - Use the ` symbol to name functions, variables, or folders and files. 41 | - Use a format like `xxx: message (fixes #1)`. The content in parentheses is optional. 42 | - The message includes a summary of what was modified. 43 | - It's a good idea to separate multiple modifications into their own commit messages. 44 | 45 | It is recommended that you include a tag at the beginning of the commit message. Between the tag and the message, use `: ` between the tag and the message. 46 | 47 | tags conform to the ["Udacity Git Commit Message Style Guide"](https://udacity.github.io/git-styleguide). However, you are welcome to use tags not listed here for additional situations. 48 | 49 | - `feat`: A new feature 50 | - `fix`: A bug fix 51 | - `docs`: Changes to documentation 52 | - `style`: Formatting, missing semicolons, etc.; no code change 53 | - `refactor`: Refactoring production code 54 | - `test`: Adding tests, refactoring test; no production code change 55 | - `chore`: Updating build tasks, package manager configs, etc.; no production code change 56 | 57 | Informal tags: 58 | 59 | - `package`: Modifications to package settings, modules, or GitHub projects 60 | - `typo`: Fix typos 61 | 62 | ### Create a pull request 63 | 64 | When creating a pull request, keep the following in mind: 65 | 66 | - Include a specific description of what the modification is, why it needs to be made, and how it works. 67 | - Check to see if there are duplicate pull requests. 68 | - Please use English in all content. 69 | 70 | Typically, a project maintainer will review and test your code before merging it into the project. This process can take some time, and they may ask you for further edits or clarifications in the comments. 71 | -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { withSidebar } from '../../dist/index.js'; 2 | import packageJson from '../../package.json' with { type: 'json' }; 3 | import { defineConfig, UserConfig } from 'vitepress'; 4 | import { withI18n } from 'vitepress-i18n'; 5 | import type { VitePressI18nOptions } from 'vitepress-i18n/types'; 6 | import type { VitePressSidebarOptions } from '../../dist/types.js'; 7 | 8 | const defaultLocale: string = 'en'; 9 | const supportLocales: string[] = [defaultLocale, 'ko', 'zhHans']; 10 | const editLinkPattern = `${packageJson.repository.url}/edit/main/docs/:path`; 11 | 12 | const commonSidebarConfig: VitePressSidebarOptions = { 13 | debugPrint: true, 14 | manualSortFileNameByPriority: ['introduction.md', 'guide', 'advanced-usage'], 15 | excludeByGlobPattern: ['changelog.md'], 16 | collapsed: false, 17 | capitalizeFirst: true, 18 | useTitleFromFileHeading: true, 19 | useTitleFromFrontmatter: true, 20 | useFolderTitleFromIndexFile: true, 21 | frontmatterOrderDefaultValue: 9, // For 'CHANGELOG.md' 22 | sortMenusByFrontmatterOrder: true 23 | }; 24 | 25 | const vitePressSidebarConfig = [ 26 | ...supportLocales.map((lang) => { 27 | return { 28 | ...commonSidebarConfig, 29 | documentRootPath: `/docs/${lang}`, 30 | resolvePath: defaultLocale === lang ? '/' : `/${lang}/`, 31 | ...(defaultLocale === lang ? {} : { basePath: `/${lang}/` }) 32 | }; 33 | }) 34 | ]; 35 | 36 | const vitePressI18nConfig: VitePressI18nOptions = { 37 | locales: supportLocales, 38 | debugPrint: true, 39 | rootLocale: defaultLocale, 40 | searchProvider: 'local', 41 | description: { 42 | en: 'VitePress Sidebar is a VitePress plugin that automatically generates sidebar menus with one setup and no hassle. Save time by easily creating taxonomies for tons of articles.', 43 | ko: 'VitePress Sidebar는 번거로운 작업 없이 한번의 설정만으로 사이드바 메뉴를 자동으로 생성하는 VitePress 플러그인입니다. 수많은 문서에 대한 분류를 손쉽게 만들어 시간을 절약하세요.', 44 | zhHans: 45 | 'VitePress Sidebar是一款VitePress插件,只需一次设置即可自动生成侧边栏菜单,无需任何麻烦。轻松为大量文章创建分类,节省时间。' 46 | }, 47 | themeConfig: { 48 | en: { 49 | nav: [ 50 | { 51 | text: 'Installation', 52 | link: '/guide/getting-started' 53 | }, 54 | { 55 | text: 'Options', 56 | link: '/guide/options' 57 | }, 58 | { 59 | text: 'Changelog', 60 | link: 'changelog' 61 | } 62 | ] 63 | }, 64 | ko: { 65 | nav: [ 66 | { 67 | text: '설치', 68 | link: '/ko/guide/getting-started' 69 | }, 70 | { 71 | text: '옵션', 72 | link: '/ko/guide/options' 73 | }, 74 | { 75 | text: '변경사항', 76 | link: '/ko/changelog' 77 | } 78 | ] 79 | }, 80 | zhHans: { 81 | nav: [ 82 | { 83 | text: '安装', 84 | link: '/zhHans/guide/getting-started' 85 | }, 86 | { 87 | text: '选项', 88 | link: '/zhHans/guide/options' 89 | }, 90 | { 91 | text: '变化', 92 | link: '/zhHans/changelog' 93 | } 94 | ] 95 | } 96 | } 97 | }; 98 | 99 | // Ref: https://vitepress.dev/reference/site-config 100 | const vitePressConfig: UserConfig = { 101 | title: 'VitePress Sidebar', 102 | lastUpdated: true, 103 | outDir: '../docs-dist', 104 | cleanUrls: true, 105 | metaChunk: true, 106 | rewrites: { 107 | 'en/:rest*': ':rest*' 108 | }, 109 | head: [ 110 | ['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/logo-32.png' }], 111 | ['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/logo-16.png' }], 112 | ['link', { rel: 'shortcut icon', href: '/favicon.ico' }] 113 | ], 114 | sitemap: { 115 | hostname: packageJson.homepage 116 | }, 117 | themeConfig: { 118 | logo: { src: '/logo-32.png', width: 24, height: 24 }, 119 | editLink: { 120 | pattern: editLinkPattern 121 | }, 122 | socialLinks: [ 123 | { icon: 'npm', link: 'https://www.npmjs.com/package/vitepress-sidebar' }, 124 | { icon: 'github', link: packageJson.repository.url.replace('.git', '') } 125 | ], 126 | footer: { 127 | message: 'Released under the MIT License', 128 | copyright: '© CDGet' 129 | } 130 | } 131 | }; 132 | 133 | export default defineConfig( 134 | withSidebar(withI18n(vitePressConfig, vitePressI18nConfig), vitePressSidebarConfig) 135 | ); 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🔌 VitePress Sidebar 2 | 3 | [![awesome-vitepress](https://awesome.re/mentioned-badge.svg)](https://github.com/logicspark/awesome-vitepress-v1) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jooy2/vitepress-sidebar/blob/main/LICENSE) ![Programming Language Usage](https://img.shields.io/github/languages/top/jooy2/vitepress-sidebar) ![Commit Count](https://img.shields.io/github/commit-activity/y/jooy2/vitepress-sidebar) [![npm downloads](https://img.shields.io/npm/dm/vitepress-sidebar.svg)](https://www.npmjs.com/package/vitepress-sidebar) [![npm latest package](https://img.shields.io/npm/v/vitepress-sidebar/latest.svg)](https://www.npmjs.com/package/vitepress-sidebar) ![npm bundle size](https://img.shields.io/bundlephobia/min/vitepress-sidebar) [![Followers](https://img.shields.io/github/followers/jooy2?style=social)](https://github.com/jooy2) ![Stars](https://img.shields.io/github/stars/jooy2/vitepress-sidebar?style=social) 4 | 5 | **VitePress Sidebar** is a plugin for **[VitePress](https://vitepress.dev)** that automatically configures and manages the sidebar of your page with simple settings. 6 | 7 | - ⚡️ Optimized for the latest version of **VitePress** 8 | - ⚡️ Easy to use, lots of options to customize to your liking 9 | - ⚡️ Lightweight bundle file size 10 | - ⚡️ [Multiple Sidebars](https://vitepress.dev/reference/default-theme-sidebar#multiple-sidebars) support 11 | - ⚡️ [Frontmatter](https://vitepress.dev/guide/frontmatter) support 12 | - ⚡️ [TypeScript](https://www.typescriptlang.org) support 13 | - ⚡️ Customize menus for sorting, special character conversion, file and folder filters, and more! 14 | 15 | ## [Documentation (Getting Started & All option lists)](https://vitepress-sidebar.cdget.com/guide/getting-started) 16 | 17 | Installing and using the package and defining all the utility methods can be found on the documentation page below: https://vitepress-sidebar.cdget.com/guide/getting-started 18 | 19 | ```javascript 20 | import { withSidebar } from 'vitepress-sidebar'; 21 | 22 | const vitePressConfigs = { 23 | title: 'VitePress Sidebar', 24 | themeConfig: { 25 | // ... 26 | } 27 | }; 28 | 29 | export default defineConfig( 30 | withSidebar(vitePressConfigs, { 31 | /* 32 | * For detailed instructions, see the links below: 33 | * https://vitepress-sidebar.cdget.com/guide/options 34 | */ 35 | // 36 | // ============ [ RESOLVING PATHS ] ============ 37 | // documentRootPath: '/', 38 | // scanStartPath: null, 39 | // resolvePath: null, 40 | // basePath: null, 41 | // followSymlinks: false, 42 | // 43 | // ============ [ GROUPING ] ============ 44 | // collapsed: true, 45 | // collapseDepth: 2, 46 | // rootGroupText: 'Contents', 47 | // rootGroupLink: 'https://github.com/jooy2', 48 | // rootGroupCollapsed: false, 49 | // 50 | // ============ [ GETTING MENU TITLE ] ============ 51 | // useTitleFromFileHeading: true, 52 | // useTitleFromFrontmatter: true, 53 | // useFolderLinkFromIndexFile: false, 54 | // useFolderTitleFromIndexFile: false, 55 | // frontmatterTitleFieldName: 'title', 56 | // 57 | // ============ [ GETTING MENU LINK ] ============ 58 | // useFolderLinkFromSameNameSubFile: false, 59 | // useFolderLinkFromIndexFile: false, 60 | // folderLinkNotIncludesFileName: false, 61 | // 62 | // ============ [ INCLUDE / EXCLUDE ] ============ 63 | // excludeByGlobPattern: ['README.md', 'folder/'], 64 | // excludeFilesByFrontmatterFieldName: 'exclude', 65 | // excludeByFolderDepth: undefined, 66 | // includeDotFiles: false, 67 | // includeEmptyFolder: false, 68 | // includeRootIndexFile: false, 69 | // includeFolderIndexFile: false, 70 | // 71 | // ============ [ STYLING MENU TITLE ] ============ 72 | // hyphenToSpace: true, 73 | // underscoreToSpace: true, 74 | // capitalizeFirst: false, 75 | // capitalizeEachWords: false, 76 | // keepMarkdownSyntaxFromTitle: false, 77 | // removePrefixAfterOrdering: false, 78 | // prefixSeparator: '.', 79 | // 80 | // ============ [ SORTING ] ============ 81 | // manualSortFileNameByPriority: ['first.md', 'second', 'third.md'], 82 | // sortFolderTo: null, 83 | // sortMenusByName: false, 84 | // sortMenusByFileDatePrefix: false, 85 | // sortMenusByFrontmatterOrder: false, 86 | // frontmatterOrderDefaultValue: 0, 87 | // sortMenusByFrontmatterDate: false, 88 | // sortMenusOrderByDescending: false, 89 | // sortMenusOrderNumericallyFromTitle: false, 90 | // sortMenusOrderNumericallyFromLink: false, 91 | // 92 | // ============ [ MISC ] ============ 93 | // debugPrint: false, 94 | }) 95 | ); 96 | ``` 97 | 98 | ## Real-world Uses 99 | 100 | **VitePress Sidebar** is utilized in a variety of project environments, including my own web services. 101 | 102 | - To explore packages used other than: https://github.com/jooy2/vitepress-sidebar/network/dependents 103 | 104 | ## Contributing 105 | 106 | Anyone can contribute to the project by reporting new issues or submitting a pull request. For more information, please see [CONTRIBUTING.md](CONTRIBUTING.md). 107 | 108 | ## License 109 | 110 | Please see the [LICENSE](LICENSE) file for more information about project owners, usage rights, and more. 111 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to a positive environment for our community include: 12 | 13 | - Demonstrating empathy and kindness toward other people 14 | - Being respectful of differing opinions, viewpoints, and experiences 15 | - Giving and gracefully accepting constructive feedback 16 | - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 17 | - Focusing on what is best not just for us as individuals, but for the overall community 18 | 19 | Examples of unacceptable behavior include: 20 | 21 | - The use of sexualized language or imagery, and sexual attention or advances of any kind 22 | - Trolling, insulting or derogatory comments, and personal or political attacks 23 | - Public or private harassment 24 | - Publishing others' private information, such as a physical or email address, without their explicit permission 25 | - Other conduct which could reasonably be considered inappropriate in a professional setting 26 | 27 | ## Enforcement Responsibilities 28 | 29 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 32 | 33 | ## Scope 34 | 35 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 36 | 37 | ## Enforcement 38 | 39 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at https://cdget.com/contact. All complaints will be reviewed and investigated promptly and fairly. 40 | 41 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 42 | 43 | ## Enforcement Guidelines 44 | 45 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 46 | 47 | ### 1. Correction 48 | 49 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 50 | 51 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 52 | 53 | ### 2. Warning 54 | 55 | **Community Impact**: A violation through a single incident or series of actions. 56 | 57 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 58 | 59 | ### 3. Temporary Ban 60 | 61 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 62 | 63 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 64 | 65 | ### 4. Permanent Ban 66 | 67 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 68 | 69 | **Consequence**: A permanent ban from any sort of public interaction within the community. 70 | 71 | ## Attribution 72 | 73 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 74 | 75 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 76 | 77 | [homepage]: https://www.contributor-covenant.org 78 | 79 | For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. 80 | -------------------------------------------------------------------------------- /docs/zhHans/advanced-usage/multiple-sidebars-how-to.md: -------------------------------------------------------------------------------- 1 | # 多侧边栏操作方法 2 | 3 | 多侧边栏是一项允许根据特定 URI 路径显示不同侧边栏菜单的功能。 4 | 5 | 只需在 `vitepress-sidebar` 中进行一些简单设置,就能轻松实现这一功能。最终,**VitePress**将按照预期输出选项。 6 | 7 | 要先了解有关多侧边栏的更多信息,我们建议您查看下面**VitePress 的**官方文档: 8 | 9 | https://vitepress.dev/zh/reference/default-theme-sidebar#multiple-sidebars 10 | 11 | ## 基本用法 12 | 13 | 首先,假设你有一个名为 `docs` 的根项目,其中有名为 `guide` 和 `config` 的子目录,就像这样: 14 | 15 | ```text 16 | docs/ 17 | ├─ guide/ 18 | │ ├─ index.md 19 | │ ├─ one.md 20 | │ ├─ two.md 21 | │ └─ do-not-include.md 22 | └─ config/ 23 | ├─ index.md 24 | ├─ three.md 25 | └─ four.md 26 | ``` 27 | 28 | 当URL位于`/guide`页面时,用户希望菜单仅显示`guide`的子菜单,隐藏`config`的子菜单。同样,当`guide`位于`/config`页面时,您希望隐藏`guide`的子菜单。 29 | 30 | 要在 `vitepress-sidebar` 中实现此功能,您需要采用与现有设置不同的方法。 31 | 32 | 像以前一样使用`withSidebar`函数,但传递一个数组。该数组至少包含一个来自`vitepress-sidebar`的选项。数组中的值可以是任意数量的URL。当然,您也可以使用不同的设置进行配置。 33 | 34 | ```javascript 35 | // 必须传递数组参数!!!! 36 | const vitePressConfigs = { 37 | /* ... */ 38 | }; 39 | 40 | export default defineConfig( 41 | withSidebar(vitePressConfigs, [ 42 | { 43 | documentRootPath: 'docs', 44 | scanStartPath: 'guide', 45 | basePath: '/guide/', 46 | resolvePath: '/guide/', 47 | useTitleFromFileHeading: true 48 | }, 49 | { 50 | documentRootPath: 'docs', 51 | scanStartPath: 'config', 52 | resolvePath: '/config/', 53 | useTitleFromFrontmatter: true 54 | } 55 | ]) 56 | ); 57 | ``` 58 | 59 | 这些选项的值在结果中的使用情况如下: 60 | 61 | ```text 62 | { 63 | : [ 64 | { 65 | base: , 66 | items: [...] // `/path/to/items` 67 | } 68 | ] 69 | } 70 | ``` 71 | 72 | 下面是上述设置的输出示例: 73 | 74 | ```json5 75 | { 76 | '/guide/': { 77 | base: '/guide/', 78 | items: [ 79 | { 80 | text: 'One', 81 | link: 'one' 82 | }, 83 | { 84 | text: 'Two', 85 | link: 'two' 86 | } 87 | ] 88 | }, 89 | '/config/': { 90 | base: '/config/', 91 | items: [ 92 | { 93 | text: 'Three', 94 | link: 'three' 95 | }, 96 | { 97 | text: 'Four', 98 | link: 'four' 99 | } 100 | ] 101 | } 102 | } 103 | ``` 104 | 105 | ## 多个侧边栏选项 106 | 107 | 以下选项可用于多个侧边栏:`scanStartPath`、`basePath`和`resolvePath`。每个选项都是可选的,但应根据具体情况正确使用。 108 | 109 | 下文将对每个选项进行说明。但我们建议您首先参考[选项](/zhHans/guide/options)页面上对每个选项的描述。 110 | 111 | 以下描述基于以下示例: 112 | 113 | ```text 114 | docs/ 115 | ├─ .vitepress/ 116 | ├─ guide/ 117 | │ ├─ api/ 118 | │ │ ├─ api-one.md 119 | │ │ └─ api-two.md 120 | │ ├─ one.md 121 | │ └─ two.md 122 | └─ config/ 123 | ├─ index.md 124 | ├─ three.md 125 | └─ four.md 126 | ``` 127 | 128 | ### `scanStartPath` 129 | 130 | 此选项用于为不同的路由规则指定不同的根目录。`documentRootPath`是实际要扫描的根目录(即`.vitepress`目录所在的位置),而`scanStartPath`是此路由规则中实际要显示的根目录。 131 | 132 | 例如,若要仅包含`/guide`目录中的文件,请将`scanStartPath`的值指定为`guide`。但是,`documentRootPath`中的路径不应包含在内。 133 | 134 | ### `resolvePath` 135 | 136 | VitePress使用此选项在遇到特定URI时显示相关菜单。例如,如果您想在到达`example.com/guide/api`时仅显示`guide/api`目录的内容,则`resolvePath`的值为`/guide/api`。建议您在路径前添加`/`。 137 | 138 | 通常,它的值与 `scanStartPath` 类似,但有时您可能需要为 **i18n** 路由指定不同的值。 139 | 140 | ### `basePath` 141 | 142 | 此选项主要用于VitePress的重写规则,否则为可选。 143 | 144 | 它取代了VitePress中`base`路径的值。如果未指定该值,则指定`resolvePath`的值或根路径(`/`)。 145 | 146 | 如果目录的实际路径与URI中的路径结构不同,您应该能够通过重写功能导航到页面。通常情况下,侧边栏会根据根目录生成路径,而不会引用VitePress中的重写路径。 147 | 148 | 例如,假设您有一个重写规则,如下所示: 149 | 150 | ```javascript 151 | const vitePressConfigs = { 152 | rewrites: { 153 | 'guide/:page': 'help/:page' 154 | } 155 | }; 156 | 157 | const vitePressSidebarConfigs = [ 158 | { 159 | documentRootPath: 'docs', 160 | scanStartPath: 'guide', 161 | resolvePath: '/guide/' 162 | } 163 | ]; 164 | 165 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs)); 166 | ``` 167 | 168 | `guide/one.md` 文档显示在 `help/one` 的路径中。但是,如果您这样做,侧边栏将不会显示菜单,因为它会尝试找到 `help/one`,而这是路径本身。 169 | 170 | 要解决这个问题,请将`basePath`中的路径改为`help`: 171 | 172 | ```javascript 173 | const vitePressConfigs = { 174 | rewrites: { 175 | 'guide/:page': 'help/:page' 176 | } 177 | }; 178 | 179 | const vitePressSidebarConfigs = [ 180 | { 181 | documentRootPath: 'docs', 182 | scanStartPath: 'guide', 183 | basePath: 'help', // <---------------------- 添加这一行 184 | resolvePath: '/guide/' 185 | } 186 | ]; 187 | 188 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs)); 189 | ``` 190 | 191 | ## 显示带有复杂路径和 URI 的菜单 192 | 193 | 上面的例子通常是在路径按步骤定义的情况下,但当你想显示按步骤深入的文件夹时,特别是当 URI 较短或使用与实际文件夹路径不同的约定时,你需要使用额外的方法。例如,你有一个这样的文件夹结构: 194 | 195 | ```text 196 | docs/ 197 | ├─ guide/ 198 | │ ├─ api/ 199 | │ │ ├─ api-one.md 200 | │ │ └─ api-two.md 201 | │ ├─ one.md 202 | │ └─ two.md 203 | └─ config/ 204 | ├─ index.md 205 | ├─ three.md 206 | └─ four.md 207 | ``` 208 | 209 | 这次,我们希望当到达单级 URI `/api` 时,在 `docs/guide/api` 中显示菜单。预期的菜单仅显示 `api-one.md` 和 `api-two.md`。 210 | 211 | ```javascript 212 | withSidebar([ 213 | { 214 | documentRootPath: 'docs', 215 | scanStartPath: 'guide/api', 216 | resolvePath: '/api/' 217 | } 218 | ]); 219 | ``` 220 | 221 | 但是,如果您这样配置选项,将无法显示菜单,因为`api`目录是`guide`的子目录。**VitePress**无法检测到这一点,并会导航到一个不存在的文档。 222 | 223 | 要解决这个问题,您需要同时使用**VitePress的**路由功能,请参阅以下文章以获取说明: 224 | 225 | https://vitepress.dev/zh/guide/routing#route-rewrites 226 | 227 | 按照上面的示例,在 `defineConfig` 中的 VitePress 设置中添加 `rewrites` 选项: 228 | 229 | ```javascript 230 | const vitePressConfigs = { 231 | /* [START] Add This */ 232 | rewrites: { 233 | 'guide/api/:page': 'api/:page' 234 | } 235 | /* [END] Add This */ 236 | }; 237 | 238 | const vitePressSidebarConfigs = { 239 | documentRootPath: 'docs', 240 | scanStartPath: 'guide/api', 241 | resolvePath: '/api/' 242 | }; 243 | 244 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs)); 245 | ``` 246 | 247 | 现在,当 URI 路径以 `/api` 开头时,将显示 `docs/guide/api` 的子菜单! 248 | -------------------------------------------------------------------------------- /test/specs/multiple-sidebar.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { describe, it } from 'node:test'; 3 | import { generateSidebar } from '../../dist'; 4 | 5 | const TEST_DIR_BASE = 'test/resources'; 6 | 7 | describe('Test: multiple sidebars', () => { 8 | it('Multiple Sidebars (A)', () => { 9 | assert.deepEqual( 10 | generateSidebar([ 11 | { 12 | documentRootPath: `${TEST_DIR_BASE}/general`, 13 | resolvePath: '/' 14 | }, 15 | { 16 | documentRootPath: `${TEST_DIR_BASE}/general`, 17 | scanStartPath: 'folder', 18 | resolvePath: '/folder/' 19 | }, 20 | { 21 | documentRootPath: `${TEST_DIR_BASE}/general`, 22 | scanStartPath: 'folder-2', 23 | resolvePath: '/folder-2' 24 | } 25 | ]), 26 | { 27 | '/': { 28 | base: '/', 29 | items: [ 30 | { 31 | text: 'a', 32 | link: 'a' 33 | }, 34 | { 35 | text: 'b', 36 | link: 'b' 37 | }, 38 | { 39 | text: 'c', 40 | link: 'c' 41 | }, 42 | { 43 | text: 'folder', 44 | items: [ 45 | { 46 | text: 'folder-test-2', 47 | link: 'folder/folder-test-2' 48 | }, 49 | { 50 | text: 'folder-test', 51 | link: 'folder/folder-test' 52 | }, 53 | { 54 | text: 'subFolder', 55 | items: [ 56 | { 57 | text: 'sub-folder-test', 58 | link: 'folder/subFolder/sub-folder-test' 59 | } 60 | ] 61 | } 62 | ] 63 | }, 64 | { 65 | text: 'folder-2', 66 | items: [ 67 | { 68 | text: 'folder2', 69 | link: 'folder-2/folder2' 70 | } 71 | ] 72 | }, 73 | { 74 | text: 'test', 75 | link: 'test' 76 | } 77 | ] 78 | }, 79 | '/folder/': { 80 | base: '/folder/', 81 | items: [ 82 | { 83 | text: 'folder-test-2', 84 | link: 'folder-test-2' 85 | }, 86 | { 87 | text: 'folder-test', 88 | link: 'folder-test' 89 | }, 90 | { 91 | text: 'subFolder', 92 | items: [ 93 | { 94 | text: 'sub-folder-test', 95 | link: 'subFolder/sub-folder-test' 96 | } 97 | ] 98 | } 99 | ] 100 | }, 101 | '/folder-2': { 102 | base: '/folder-2', 103 | items: [ 104 | { 105 | text: 'folder2', 106 | link: 'folder2' 107 | } 108 | ] 109 | } 110 | } 111 | ); 112 | }); 113 | 114 | it('Multiple Sidebars (B)', () => { 115 | assert.deepEqual( 116 | generateSidebar([ 117 | { 118 | documentRootPath: `${TEST_DIR_BASE}/general`, 119 | scanStartPath: 'folder/subFolder', 120 | resolvePath: '/folder' 121 | } 122 | ]), 123 | { 124 | '/folder': { 125 | base: '/folder', 126 | items: [ 127 | { 128 | text: 'sub-folder-test', 129 | link: 'sub-folder-test' 130 | } 131 | ] 132 | } 133 | } 134 | ); 135 | }); 136 | 137 | it('Multiple Sidebars (C)', () => { 138 | assert.deepEqual( 139 | generateSidebar([ 140 | { 141 | documentRootPath: `${TEST_DIR_BASE}/general`, 142 | scanStartPath: 'folder', 143 | resolvePath: '/folder/', 144 | includeRootIndexFile: true, 145 | includeFolderIndexFile: true 146 | }, 147 | { 148 | documentRootPath: `${TEST_DIR_BASE}/general`, 149 | scanStartPath: 'folder-2', 150 | resolvePath: '/folder-2/', 151 | includeRootIndexFile: true, 152 | includeFolderIndexFile: true, 153 | useFolderLinkFromIndexFile: true 154 | } 155 | ]), 156 | { 157 | '/folder/': { 158 | base: '/folder/', 159 | items: [ 160 | { 161 | text: 'folder-test-2', 162 | link: 'folder-test-2' 163 | }, 164 | { 165 | text: 'folder-test', 166 | link: 'folder-test' 167 | }, 168 | { 169 | text: 'subFolder', 170 | items: [ 171 | { 172 | text: 'sub-folder-test', 173 | link: 'subFolder/sub-folder-test' 174 | } 175 | ] 176 | } 177 | ] 178 | }, 179 | '/folder-2/': { 180 | base: '/folder-2/', 181 | items: [ 182 | { 183 | text: 'folder2', 184 | link: 'folder2' 185 | }, 186 | { 187 | text: 'index', 188 | link: 'index.md' 189 | } 190 | ] 191 | } 192 | } 193 | ); 194 | }); 195 | 196 | it('Multiple Sidebars (D)', () => { 197 | assert.deepEqual( 198 | generateSidebar([ 199 | { 200 | documentRootPath: `${TEST_DIR_BASE}/general`, 201 | scanStartPath: 'folder/subFolder', 202 | basePath: 'page', 203 | resolvePath: 'folder' 204 | } 205 | ]), 206 | { 207 | folder: { 208 | base: 'page', 209 | items: [ 210 | { 211 | text: 'sub-folder-test', 212 | link: 'sub-folder-test' 213 | } 214 | ] 215 | } 216 | } 217 | ); 218 | }); 219 | }); 220 | -------------------------------------------------------------------------------- /docs/ko/advanced-usage/multiple-sidebars-how-to.md: -------------------------------------------------------------------------------- 1 | # 다중 사이드바 2 | 3 | 다중 사이드바는 특정 URI 경로에 따라 서로 다른 사이드바 메뉴를 표시할 수 있는 기능입니다. 4 | 5 | 이것은 몇 가지 간단한 설정으로 `vitepress-sidebar`에서 쉽게 구현할 수 있습니다. 결국 **VitePress**는 의도한 대로 옵션을 출력합니다. 6 | 7 | 다중 사이드바에 대해 자세히 알아보려면 먼저 아래의 **VitePress** 공식 문서를 살펴보는 것이 좋습니다: 8 | 9 | https://vitepress.dev/ko/reference/default-theme-sidebar#multiple-sidebars 10 | 11 | ## 기본 사용법 12 | 13 | 먼저, 다음과 같이 `docs`라는 루트 프로젝트와 `guide` 및 `config`라는 하위 디렉터리가 있다고 가정해 보겠습니다: 14 | 15 | ```text 16 | docs/ 17 | ├─ guide/ 18 | │ ├─ index.md 19 | │ ├─ one.md 20 | │ ├─ two.md 21 | │ └─ do-not-include.md 22 | └─ config/ 23 | ├─ index.md 24 | ├─ three.md 25 | └─ four.md 26 | ``` 27 | 28 | URL이 `/guide` 페이지에 있는 경우 사용자는 메뉴에 `guide`의 하위 메뉴만 표시하고 `config`의 하위 메뉴는 숨기기를 원합니다. 마찬가지로 `/config` 페이지에 `guide`의 하위 메뉴가 있을 때 하위 메뉴를 숨기려고 합니다. 29 | 30 | 이를 `vitepress-sidebar`에서 구현하려면 기존 설정과 다르게 접근해야 합니다. 31 | 32 | 이전과 같이 `withSidebar` 함수를 사용하되 배열을 전달합니다. 배열에는 `vitepress-sidebar`의 옵션이 하나 이상 포함됩니다. 배열의 값은 원하는 만큼의 URL을 지정할 수 있습니다. 물론 다른 설정으로 구성할 수도 있습니다. 33 | 34 | ```javascript 35 | // 배열 인수를 전달해야 함!!!! 36 | const vitePressConfigs = { 37 | /* ... */ 38 | }; 39 | 40 | export default defineConfig( 41 | withSidebar(vitePressConfigs, [ 42 | { 43 | documentRootPath: 'docs', 44 | scanStartPath: 'guide', 45 | basePath: '/guide/', 46 | resolvePath: '/guide/', 47 | useTitleFromFileHeading: true 48 | }, 49 | { 50 | documentRootPath: 'docs', 51 | scanStartPath: 'config', 52 | resolvePath: '/config/', 53 | useTitleFromFrontmatter: true 54 | } 55 | ]) 56 | ); 57 | ``` 58 | 59 | 이러한 옵션의 값은 다음과 같이 결과에 사용됩니다: 60 | 61 | ```text 62 | { 63 | : [ 64 | { 65 | base: , 66 | items: [...] // `/path/to/items` 67 | } 68 | ] 69 | } 70 | ``` 71 | 72 | 다음은 위 설정의 출력 예시입니다: 73 | 74 | ```json5 75 | { 76 | '/guide/': { 77 | base: '/guide/', 78 | items: [ 79 | { 80 | text: 'One', 81 | link: 'one' 82 | }, 83 | { 84 | text: 'Two', 85 | link: 'two' 86 | } 87 | ] 88 | }, 89 | '/config/': { 90 | base: '/config/', 91 | items: [ 92 | { 93 | text: 'Three', 94 | link: 'three' 95 | }, 96 | { 97 | text: 'Four', 98 | link: 'four' 99 | } 100 | ] 101 | } 102 | } 103 | ``` 104 | 105 | ## 다중 사이드바 설정 106 | 107 | 다중 사이드바에서 사용할 수 있는 옵션은 다음과 같습니다: `scanStartPath`, `basePath` 및 `resolvePath`. 각 옵션은 선택 사항이지만 상황에 따라 올바르게 사용할 수 있어야 합니다. 108 | 109 | 각 옵션은 아래에 설명되어 있습니다. 그러나 먼저 [옵션](/ko/guide/options) 페이지에서 각 옵션에 대한 설명을 참조하는 것이 좋습니다. 110 | 111 | 아래 설명은 다음 예시를 기반으로 합니다: 112 | 113 | ```text 114 | docs/ 115 | ├─ .vitepress/ 116 | ├─ guide/ 117 | │ ├─ api/ 118 | │ │ ├─ api-one.md 119 | │ │ └─ api-two.md 120 | │ ├─ one.md 121 | │ └─ two.md 122 | └─ config/ 123 | ├─ index.md 124 | ├─ three.md 125 | └─ four.md 126 | ``` 127 | 128 | ### `scanStartPath` 129 | 130 | 이 옵션은 다른 라우팅 규칙의 루트 경로로 다른 디렉터리를 지정하는 데 사용됩니다. `documenRootPath`는 실제로 스캔할 루트 경로(`.vitepress` 디렉터리가 있는 위치)이고 `scanStartPath`는 이 경로 규칙에서 실제로 표시되어야 하는 루트 경로입니다. 131 | 132 | 예를 들어 `/guide` 디렉터리에 있는 파일만 포함하려면 `scanStartPath`의 값을 `guide`로 지정합니다. 단, `documentRootPath`의 경로는 포함되지 않아야 합니다. 133 | 134 | ### `resolvePath` 135 | 136 | 이 옵션은 특정 URI를 발견했을 때 관련 메뉴를 표시하기 위해 VitePress에서 사용합니다. 예를 들어 `example.com/guide/api`에 도달할 때 `guide/api` 디렉토리의 내용만 표시하려면 `resolvePath`의 값은 `/guide/api`가 됩니다. 경로 앞에 `/`를 포함하는 것이 좋습니다. 137 | 138 | 이 값은 일반적으로 `scanStartPath`와 비슷한 값을 갖지만, **i18n** 라우팅을 위해 다르게 지정해야 하는 경우도 있습니다. 139 | 140 | ### `basePath` 141 | 142 | 이 옵션은 주로 VitePress의 `rewrite` 규칙으로 작업할 때 사용되며, 그 외에는 선택 사항입니다. 143 | 144 | VitePress에서 `base` 경로의 값을 대체합니다. 이 값을 지정하지 않으면 `resolvePath` 값 또는 루트 경로(`/`)가 지정됩니다. 145 | 146 | 디렉토리의 실제 경로가 URI의 경로 구조와 다른 경우 다시 쓰기를 통해 제공된 페이지로 이동할 수 있어야 합니다. 일반적으로 사이드바는 루트 디렉터리를 기반으로 경로를 생성하며 VitePress의 다시 쓰기 경로를 참조하지 않습니다. 147 | 148 | 예를 들어 다음과 같은 재작성 규칙이 있다고 가정해 보겠습니다: 149 | 150 | ```javascript 151 | const vitePressConfigs = { 152 | rewrites: { 153 | 'guide/:page': 'help/:page' 154 | } 155 | }; 156 | 157 | const vitePressSidebarConfigs = [ 158 | { 159 | documentRootPath: 'docs', 160 | scanStartPath: 'guide', 161 | resolvePath: '/guide/' 162 | } 163 | ]; 164 | 165 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs)); 166 | ``` 167 | 168 | `guide/one.md` 문서가 `help/one` 경로에 표시됩니다. 그러나 이렇게 하면 사이드바가 경로인 `help/one`을 그대로 찾으려고 하기 때문에 메뉴가 표시되지 않습니다. 169 | 170 | 이 문제를 해결하려면 `basePath`의 경로를 `help`로 변경하세요: 171 | 172 | ```javascript 173 | const vitePressConfigs = { 174 | rewrites: { 175 | 'guide/:page': 'help/:page' 176 | } 177 | }; 178 | 179 | const vitePressSidebarConfigs = [ 180 | { 181 | documentRootPath: 'docs', 182 | scanStartPath: 'guide', 183 | basePath: 'help', // <---------------------- 이 라인을 추가합니다. 184 | resolvePath: '/guide/' 185 | } 186 | ]; 187 | 188 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs)); 189 | ``` 190 | 191 | ## 복잡한 경로 및 URI가 있는 메뉴 표시하기 192 | 193 | 위의 예는 일반적으로 경로가 단계로 정의된 경우이지만, 단계가 깊은 폴더를 표시하려는 경우, 특히 URI가 더 짧거나 실제 폴더 경로와 다른 규칙을 사용하는 경우에는 추가 방법을 사용해야 합니다. 예를 들어 다음과 같은 폴더 구조가 있습니다: 194 | 195 | ```text 196 | docs/ 197 | ├─ guide/ 198 | │ ├─ api/ 199 | │ │ ├─ api-one.md 200 | │ │ └─ api-two.md 201 | │ ├─ one.md 202 | │ └─ two.md 203 | └─ config/ 204 | ├─ index.md 205 | ├─ three.md 206 | └─ four.md 207 | ``` 208 | 209 | 이번에는 `/api`라는 한 단계 URI에 도달했을 때 `docs/guide/api`의 메뉴를 표시하고 싶습니다. 예상되는 메뉴는 `api-one.md`와 `api-two.md`만 표시하는 것입니다. 210 | 211 | ```javascript 212 | withSidebar([ 213 | { 214 | documentRootPath: 'docs', 215 | scanStartPath: 'guide/api', 216 | resolvePath: '/api/' 217 | } 218 | ]); 219 | ``` 220 | 221 | 하지만 이렇게 옵션을 구성하면 `api` 디렉터리가 `guide`의 하위 디렉터리이기 때문에 메뉴를 표시할 수 없습니다. **VitePress**는 이를 감지하지 못하고 존재하지 않는 문서로 이동합니다. 222 | 223 | 이를 해결하려면 **VitePress**의 라우팅 기능을 병행해서 사용해야 합니다. 관련 내용은 아래 글을 참고하세요: 224 | 225 | https://vitepress.dev/ko/guide/routing#route-rewrites 226 | 227 | 위의 예에 따라 `defineConfig`의 VitePress 설정에 `rewrites` 옵션을 추가합니다: 228 | 229 | ```javascript 230 | const vitePressConfigs = { 231 | /* [START] Add This */ 232 | rewrites: { 233 | 'guide/api/:page': 'api/:page' 234 | } 235 | /* [END] Add This */ 236 | }; 237 | 238 | const vitePressSidebarConfigs = { 239 | documentRootPath: 'docs', 240 | scanStartPath: 'guide/api', 241 | resolvePath: '/api/' 242 | }; 243 | 244 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs)); 245 | ``` 246 | 247 | 이제 URI 경로가 `/api`로 시작하면 `docs/guide/api`의 하위 메뉴가 표시됩니다! 248 | -------------------------------------------------------------------------------- /docs/zhHans/advanced-usage/internationalization.md: -------------------------------------------------------------------------------- 1 | # 国际化 2 | 3 | 本页介绍如何使用 VitePress Sidebar 实现国际化(i18n)。 4 | 5 | VitePress 支持多语言文档。如果您将翻译好的标记文件放在每种语言的目录中,并在 VitePress 的 `defineConfig` 中使用 `locales` 选项,就会显示语言选择器,并显示指定目录中每种语言的文档。 6 | 7 | ## 翻译页面布局(手动) 8 | 9 | 首先,VitePress 允许您翻译各种布局界面元素。所谓布局,是指 “上一页”、“下一页”、“目录”、“更改主题 ”等功能的所有相关文本。 它还包括翻译搜索功能中出现的所有文本。 10 | 11 | VitePress 页面上的各种界面(布局)文本可提供特定语言的 `locales` 翻译。例如: 12 | 13 | ```text 14 | "locales": { 15 | "root": { 16 | "lang": "en-US", 17 | "label": "English", 18 | "description": "VitePress Sidebar is a VitePress plugin that automatically generates sidebar menus with one setup and no hassle. Save time by easily creating taxonomies for tons of articles.", 19 | "themeConfig": { 20 | "docFooter": { 21 | "prev": "Previous page", 22 | "next": "Next page" 23 | }, 24 | "outline": { 25 | "label": "On this page" 26 | }, 27 | "lastUpdated": { 28 | "text": "Last updated" 29 | }, 30 | "langMenuLabel": "Change language", 31 | "returnToTopLabel": "Return to top", 32 | "sidebarMenuLabel": "Menu", 33 | "darkModeSwitchLabel": "Appearance", 34 | "lightModeSwitchTitle": "Switch to light theme", 35 | "darkModeSwitchTitle": "Switch to dark theme" 36 | } 37 | }, 38 | "ko": { 39 | "lang": "ko-KR", 40 | "label": "한국어", 41 | "description": "VitePress Sidebar는 번거로운 작업 없이 한번의 설정만으로 사이드바 메뉴를 자니다. 수많은 문서에 대한 분류를 손쉽게 만들어 시간을 절약하세요.", 42 | "themeConfig": { 43 | "docFooter": { 44 | "prev": "이전", 45 | "next": "다음" 46 | }, 47 | "outline": { 48 | "label": "이 페이지 콘텐츠" 49 | }, 50 | "lastUpdated": { 51 | "text": "업데이트 일자" 52 | }, 53 | "langMenuLabel": "언어 변경", 54 | "returnToTopLabel": "맨 위로", 55 | "sidebarMenuLabel": "사이드바 메뉴", 56 | "darkModeSwitchLabel": "다크 모드", 57 | "lightModeSwitchTitle": "라이트 모드로 변경", 58 | "darkModeSwitchTitle": "다크 모드로 변경" 59 | } 60 | } 61 | } 62 | ``` 63 | 64 | (`root` "语言指页面上的主要语言)。 65 | 66 | 要了解有关翻译布局的更多信息,请参阅以下文章: https://vitepress.dev/zh/guide/i18n 67 | 68 | 对于搜索功能中出现的文本,需要在 `defineConfig` 中的 `themeConfig.search` 选项中进行设置,例如: 69 | 70 | ```text 71 | "themeConfig": { 72 | "search": { 73 | "provider": "local", 74 | "options": { 75 | "locales": { 76 | "root": { 77 | "translations": { 78 | "button": { 79 | "buttonText": "Search", 80 | "buttonAriaLabel": "Search" 81 | }, 82 | "modal": { 83 | "displayDetails": "Display detailed list", 84 | "resetButtonTitle": "Reset search", 85 | "backButtonTitle": "Close search", 86 | "noResultsText": "No results for", 87 | "footer": { 88 | "selectText": "to select", 89 | "selectKeyAriaLabel": "enter", 90 | "navigateText": "to navigate", 91 | "navigateUpKeyAriaLabel": "up arrow", 92 | "navigateDownKeyAriaLabel": "down arrow", 93 | "closeText": "to close", 94 | "closeKeyAriaLabel": "escape" 95 | } 96 | } 97 | } 98 | }, 99 | "ko": { 100 | "translations": { 101 | "button": { 102 | "buttonText": "검색", 103 | "buttonAriaLabel": "검색" 104 | }, 105 | "modal": { 106 | "displayDetails": "상세 목록 표시", 107 | "resetButtonTitle": "검색 초기화", 108 | "backButtonTitle": "검색 닫기", 109 | "noResultsText": "결과를 찾을 수 없음", 110 | "footer": { 111 | "selectText": "선택", 112 | "selectKeyAriaLabel": "선택하기", 113 | "navigateText": "탐색", 114 | "navigateUpKeyAriaLabel": "위로", 115 | "navigateDownKeyAriaLabel": "아래로", 116 | "closeText": "닫기", 117 | "closeKeyAriaLabel": "esc" 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | } 126 | ``` 127 | 128 | ## 翻译页面布局(自动) 129 | 130 | [VitePress I18n](https://vitepress-i18n.cdget.com)是VitePress侧边栏的一个家族插件,可让您自动完成上述手动翻译过程。只需进行一些简单的设置,您就不必花费数小时来翻译布局! 131 | 132 | 使用以下命令安装 VitePress I18n 模块: 133 | 134 | ```shell 135 | $ npm i -D vitepress-i18n 136 | ``` 137 | 138 | 在 `defineConfig` 中,将其设置为: 139 | 140 | ```javascript 141 | import { withSidebar } from 'vitepress-sidebar'; 142 | import { withI18n } from 'vitepress-i18n'; 143 | 144 | const vitePressConfig = { 145 | // VitePress config 146 | }; 147 | 148 | const vitePressSidebarConfig = { 149 | // VitePress Sidebar config 150 | }; 151 | 152 | const vitePressI18nConfig = { 153 | // VitePress I18n config 154 | locales: ['en', 'ko'], // first locale 'en' is root locale 155 | searchProvider: 'local' // enable search with auto translation 156 | }; 157 | 158 | export default defineConfig( 159 | withSidebar(withI18n(vitePressConfig, vitePressI18nConfig), vitePressSidebarConfig) 160 | ); 161 | ``` 162 | 163 | 现在,您可以将界面翻译工作交给模块来完成!当然,您也可以自定义详细文本。 164 | 165 | 更多信息,请阅读 VitePress I18n 文档页面: https://vitepress-i18n.cdget.com/guide/getting-started 166 | 167 | ## 特定语言侧边栏设置 168 | 169 | 在 VitePress 中,您可以根据不同的语言显示不同的侧边栏。例如,假设您的文件夹结构如下: 170 | 171 | ```text 172 | / 173 | ├─ package.json 174 | ├─ docs/ 175 | │ ├─ .vitepress/ 176 | │ │ └─ config.js 177 | │ ├─ en/ 178 | │ │ └─ abc.md 179 | │ ├─ ko/ 180 | │ │ └─ abc.md 181 | │ └─ zhHans/ 182 | │ └─ abc.md 183 | └─ ... 184 | ``` 185 | 186 | 在示例中,`abc.md` 是翻译成每种语言的同一文档。如果将语言分别改为英文、韩文和中文,侧边栏将扫描每个目录`en`、`ko`和`zhHans`,以获取该语言的标题。 187 | 188 | 下面举例说明如何做到这一点: 189 | 190 | ```javascript 191 | const rootLocale = 'en' 192 | const supportedLocales = [rootLocale, 'ko', 'zhHans']; 193 | 194 | const vitePressConfigs = { 195 | rewrites: { 196 | 'en/:rest*': ':rest*' 197 | } 198 | } 199 | 200 | const commonSidebarConfigs = { 201 | // Sidebar common configurations 202 | } 203 | 204 | const vitePressSidebarConfigs = [ 205 | ...supportedLocales.map((lang) => { 206 | return { 207 | ...commonSidebarConfigs, 208 | ...(rootLocale === lang ? {} : { basePath: `/${lang}/` }), // If using `rewrites` option 209 | documentRootPath: `/docs/${lang}`, 210 | resolvePath: rootLocale === lang ? '/' : `/${lang}/`, 211 | }; 212 | }) 213 | ] 214 | 215 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs) 216 | ``` 217 | 218 | 首先,`rewrites` 允许您在使用英语作为根语言(`en` 目录)时抑制 URI 路径中的 `/en/`(这是可选的)。 219 | 220 | 下一步,`sidebar` 的设置与上一步类似,但需要为每种语言设置多个侧边栏和目录。 下文将解释每个选项: 221 | 222 | - `basePath`: 与 `rewrites` 规则一起使用时,可防止根本地化指向 `/en/` 路径。 223 | - `documentRootPath`: 实际文档文件所在的根路径。 对于每种语言,查找 `en`、`ko` 和 `zhHans` 目录。 224 | - `resolvePath`: `/docs` 实际扫描侧边栏列表的目录中文件的位置。 为每种语言指定目录位置,不包括根本地语言。 225 | 226 | 这样可以确保侧边栏只显示指定目录下每种语言的文档。以上只是一个示例,您可能需要根据项目规模或目录结构设置不同的选项。 227 | -------------------------------------------------------------------------------- /docs/ko/advanced-usage/internationalization.md: -------------------------------------------------------------------------------- 1 | # 국제화 2 | 3 | 이 페이지에서는 VitePress Sidebar를 사용하여 i18n (internationalization)을 달성하는 방법을 기술합니다. 4 | 5 | VitePress에서는 다국어 문서를 지원합니다. 번역된 마크다운 파일을 각 언어별 디렉토리에 배치하고 VitePress의 `defineConfig`의 `locales` 옵션을 사용할 경우 언어 선택기가 표시되며 각 언어별로 지정된 디렉토리의 문서를 표시합니다. 6 | 7 | ## 페이지 레이아웃 번역 (수동) 8 | 9 | 먼저 VitePress에서는 각종 레이아웃 인터페이스 요소를 번역할 수 있습니다. 여기서 레이아웃은 '이전', '다음', '목차', '테마 변경'과 같은 기능에 대한 관련된 모든 텍스트를 의미합니다. 또한 검색 기능에 표시되는 모든 텍스트 번역을 포함합니다. 10 | 11 | VitePress 페이지에 있는 각종 인터페이스(레이아웃) 텍스트는 `locales`에 언어별로 번역된 텍스트를 제공할 수 있습니다. 예를 들면 다음과 같습니다: 12 | 13 | ```text 14 | "locales": { 15 | "root": { 16 | "lang": "en-US", 17 | "label": "English", 18 | "description": "VitePress Sidebar is a VitePress plugin that automatically generates sidebar menus with one setup and no hassle. Save time by easily creating taxonomies for tons of articles.", 19 | "themeConfig": { 20 | "docFooter": { 21 | "prev": "Previous page", 22 | "next": "Next page" 23 | }, 24 | "outline": { 25 | "label": "On this page" 26 | }, 27 | "lastUpdated": { 28 | "text": "Last updated" 29 | }, 30 | "langMenuLabel": "Change language", 31 | "returnToTopLabel": "Return to top", 32 | "sidebarMenuLabel": "Menu", 33 | "darkModeSwitchLabel": "Appearance", 34 | "lightModeSwitchTitle": "Switch to light theme", 35 | "darkModeSwitchTitle": "Switch to dark theme" 36 | } 37 | }, 38 | "ko": { 39 | "lang": "ko-KR", 40 | "label": "한국어", 41 | "description": "VitePress Sidebar는 번거로운 작업 없이 한번의 설정만으로 사이드바 메뉴를 자니다. 수많은 문서에 대한 분류를 손쉽게 만들어 시간을 절약하세요.", 42 | "themeConfig": { 43 | "docFooter": { 44 | "prev": "이전", 45 | "next": "다음" 46 | }, 47 | "outline": { 48 | "label": "이 페이지 콘텐츠" 49 | }, 50 | "lastUpdated": { 51 | "text": "업데이트 일자" 52 | }, 53 | "langMenuLabel": "언어 변경", 54 | "returnToTopLabel": "맨 위로", 55 | "sidebarMenuLabel": "사이드바 메뉴", 56 | "darkModeSwitchLabel": "다크 모드", 57 | "lightModeSwitchTitle": "라이트 모드로 변경", 58 | "darkModeSwitchTitle": "다크 모드로 변경" 59 | } 60 | } 61 | } 62 | ``` 63 | 64 | (`root` 언어는 페이지에서 주 언어를 의미합니다.) 65 | 66 | 레이아웃 번역을 자세히 알아보려면 다음 문서를 참고하세요: https://vitepress.dev/ko/guide/i18n 67 | 68 | 검색 기능에 표시되는 텍스트의 경우 `defineConfig`의 `themeConfig.search` 옵션에서 설정해야 합니다. 예를 들면 다음과 같습니다: 69 | 70 | ```text 71 | "themeConfig": { 72 | "search": { 73 | "provider": "local", 74 | "options": { 75 | "locales": { 76 | "root": { 77 | "translations": { 78 | "button": { 79 | "buttonText": "Search", 80 | "buttonAriaLabel": "Search" 81 | }, 82 | "modal": { 83 | "displayDetails": "Display detailed list", 84 | "resetButtonTitle": "Reset search", 85 | "backButtonTitle": "Close search", 86 | "noResultsText": "No results for", 87 | "footer": { 88 | "selectText": "to select", 89 | "selectKeyAriaLabel": "enter", 90 | "navigateText": "to navigate", 91 | "navigateUpKeyAriaLabel": "up arrow", 92 | "navigateDownKeyAriaLabel": "down arrow", 93 | "closeText": "to close", 94 | "closeKeyAriaLabel": "escape" 95 | } 96 | } 97 | } 98 | }, 99 | "ko": { 100 | "translations": { 101 | "button": { 102 | "buttonText": "검색", 103 | "buttonAriaLabel": "검색" 104 | }, 105 | "modal": { 106 | "displayDetails": "상세 목록 표시", 107 | "resetButtonTitle": "검색 초기화", 108 | "backButtonTitle": "검색 닫기", 109 | "noResultsText": "결과를 찾을 수 없음", 110 | "footer": { 111 | "selectText": "선택", 112 | "selectKeyAriaLabel": "선택하기", 113 | "navigateText": "탐색", 114 | "navigateUpKeyAriaLabel": "위로", 115 | "navigateDownKeyAriaLabel": "아래로", 116 | "closeText": "닫기", 117 | "closeKeyAriaLabel": "esc" 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | } 126 | ``` 127 | 128 | ## 페이지 레이아웃 번역 (자동) 129 | 130 | VitePress Sidebar의 패밀리 플러그인인 [VitePress I18n](https://vitepress-i18n.cdget.com/ko/)을 사용하면 위와 같이 수동으로 번역하는 과정을 자동화할 수 있습니다. 몇가지 간단한 설정만으로 레이아웃 번역에 시간을 투자하지 않아도 됩니다! 131 | 132 | 아래 명령어로 VitePress I18n 모듈을 설치하세요: 133 | 134 | ```shell 135 | $ npm i -D vitepress-i18n 136 | ``` 137 | 138 | 그리고 `defineConfig`에 다음과 같이 설정합니다: 139 | 140 | ```javascript 141 | import { withSidebar } from 'vitepress-sidebar'; 142 | import { withI18n } from 'vitepress-i18n'; 143 | 144 | const vitePressConfig = { 145 | // VitePress config 146 | }; 147 | 148 | const vitePressSidebarConfig = { 149 | // VitePress Sidebar config 150 | }; 151 | 152 | const vitePressI18nConfig = { 153 | // VitePress I18n config 154 | locales: ['en', 'ko'], // first locale 'en' is root locale 155 | searchProvider: 'local' // enable search with auto translation 156 | }; 157 | 158 | export default defineConfig( 159 | withSidebar(withI18n(vitePressConfig, vitePressI18nConfig), vitePressSidebarConfig) 160 | ); 161 | ``` 162 | 163 | 이제 인터페이스 번역은 모듈에게 맡기세요! 물론 세부적인 텍스트도 커스터마이징 할 수 있습니다. 164 | 165 | 자세한 내용은 VitePress I18n의 문서 페이지를 읽어주세요: https://vitepress-i18n.cdget.com/ko/guide/getting-started 166 | 167 | ## 언어별 사이드바 설정 168 | 169 | VitePress에서 언어별로 사이드바를 다르게 표시할 수 있습니다. 예를 들면 다음과 같은 폴더 구조를 가지고 있다고 가정해보겠습니다: 170 | 171 | ```text 172 | / 173 | ├─ package.json 174 | ├─ docs/ 175 | │ ├─ .vitepress/ 176 | │ │ └─ config.js 177 | │ ├─ en/ 178 | │ │ └─ abc.md 179 | │ ├─ ko/ 180 | │ │ └─ abc.md 181 | │ └─ zhHans/ 182 | │ └─ abc.md 183 | └─ ... 184 | ``` 185 | 186 | 예시의 `abc.md`는 각 언어로 번역된 같은 문서입니다. 언어를 각각 영어, 한국어, 중국어로 변경할 경우 사이드바는 각 디렉토리 `en`, `ko`, `zhHans`를 스캔하여 언어에 맞는 제목을 가져 올 것입니다. 187 | 188 | 이를 달성하는 예시는 다음과 같습니다: 189 | 190 | ```javascript 191 | const rootLocale = 'en' 192 | const supportedLocales = [rootLocale, 'ko', 'zhHans']; 193 | 194 | const vitePressConfigs = { 195 | rewrites: { 196 | 'en/:rest*': ':rest*' 197 | } 198 | } 199 | 200 | const commonSidebarConfigs = { 201 | // Sidebar common configurations 202 | } 203 | 204 | const vitePressSidebarConfigs = [ 205 | ...supportedLocales.map((lang) => { 206 | return { 207 | ...commonSidebarConfigs, 208 | ...(rootLocale === lang ? {} : { basePath: `/${lang}/` }), // If using `rewrites` option 209 | documentRootPath: `/docs/${lang}`, 210 | resolvePath: rootLocale === lang ? '/' : `/${lang}/`, 211 | }; 212 | }) 213 | ] 214 | 215 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs) 216 | ``` 217 | 218 | 먼저 `rewrites`는 root 언어인 영어(`en` 디렉토리)를 사용 중일 때, URI 경로에 `/en/`을 표시하지 않게 해줍니다. (이는 선택사항입니다.) 219 | 220 | 다음으로 `sidebar`의 설정은 기존과 비슷하지만, 각 언어별 디렉토리로 구성된 다중 사이드바를 설정해야 합니다. 각 옵션에 대한 설명은 다음과 같습니다: 221 | 222 | - `basePath`: `rewrites` 규칙과 함께 사용 중일 때 root 로캐일이면 `/en/` 경로를 가리키지 않도록 합니다. 223 | - `documentRootPath`: 실제 문서 파일이 위치한 루트 경로입니다. 각 언어별로 `en`, `ko`, `zhHans` 디렉토리를 찾도록 합니다. 224 | - `resolvePath`: `/docs` 디렉토리로부터 실제로 사이드바 목록을 스캔해야 할 파일의 위치입니다. root 로캐일을 제외하고 각 언어별로 디렉토리 위치를 지정합니다. 225 | 226 | 이렇게 하면 언어 마다 지정한 디렉토리의 문서만 사이드바에서 표시할 수 있도록 합니다. 위는 예시이므로 프로젝트의 규모나 디렉토리 구조에 따라 옵션 설정을 다르게 해야 할 수 있습니다. 227 | -------------------------------------------------------------------------------- /docs/zhHans/guide/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 1 3 | --- 4 | 5 | # 入门 6 | 7 | 本页面将指导您安装和使用"VitePress Sidebar"模块。 8 | 9 | ## 安装 10 | 11 | 首先,在使用本模块之前,您可能需要预先配置 **[VitePress](https://vitepress.dev/zh/)**。 12 | 13 | 我们建议使用 **Node.js 20.x** 或更高版本。**VitePress Sidebar**是用`ESM`编写的。要在 "CommonJS" 中使用它,[请参见此处的说明](/zhHans/troubleshooting/err-require-esm)。 14 | 15 | 您需要使用 [NPM](https://www.npmjs.com/package/vitepress-sidebar) 或任何其他 Node 模块包管理器安装该模块。该软件包应安装在 `devDependencies` 中,因为它仅在开发人员环境中使用。使用下面的命令: 16 | 17 | ```shell 18 | # via npm 19 | $ npm i -D vitepress-sidebar 20 | 21 | # via yarn 22 | $ yarn add -D vitepress-sidebar 23 | 24 | # via pnpm 25 | $ pnpm i -D vitepress-sidebar 26 | ``` 27 | 28 | ## 工作原理 29 | 30 | VitePress Sidebar 会根据您在项目文件夹中指定的文件夹路径(`documentRootPath`),分层扫描您的文件夹和标记文件。 31 | 32 | 然后,它会根据您的设置对某些文件进行排除、排序和格式化,读取侧边栏菜单的标题,最后根据 VitePress 要求的侧边栏规范输出设置数据。 33 | 34 | 因此,VitePress 的 `config.js` 文件应如下所示 35 | 36 | ```javascript 37 | export default { 38 | themeConfig: { 39 | sidebar: [ 40 | // VitePress Sidebar 的输出 41 | { 42 | text: 'Guide', 43 | items: [ 44 | { text: 'Introduction', link: '/introduction' }, 45 | { text: 'Getting Started', link: '/getting-started' } 46 | ] 47 | } 48 | ] 49 | } 50 | }; 51 | ``` 52 | 53 | 这样就无需在 “边栏 ”中手动创建每个菜单。 54 | 55 | ## 如何使用 56 | 57 | VitePress Sidebar 可通过两个函数自动生成侧边栏:`withSidebar` 和 `generateSidebar`。它们的行为相同,但在何处使用这两个函数却不同。一般来说,我们建议使用`withSidebar`。 58 | 59 | 要将安装的模块导入代码,请打开VitePress的`config.js`文件。请注意,该文件位于`.vitepress`目录下,扩展名可能有所不同,具体取决于您的项目。 60 | 61 | 文件,并以以下两种方式之一使用 `vitepress-sidebar`. 62 | 63 | ### 1. 使用 `withSidebar`(推荐) 64 | 65 | `withSidebar` 用于 `defineConfig` 级别。请注意,VitePress 的配置对象应放在第一个参数中,VitePress 侧边栏的选项应放在第二个参数中。 66 | 67 | VitePress 侧边栏将覆盖 VitePress 中现有选项所需的任何附加选项。 您已手动设置的任何 `sidebar` 选项都将被新选项覆盖。 68 | 69 | ```javascript 70 | // `.vitepress/config.js` 71 | import { withSidebar } from 'vitepress-sidebar'; 72 | 73 | const vitePressOptions = { 74 | // VitePress's options here... 75 | title: 'VitePress Sidebar', 76 | themeConfig: { 77 | // ... 78 | } 79 | }; 80 | 81 | const vitePressSidebarOptions = { 82 | // VitePress Sidebar's options here... 83 | documentRootPath: '/', 84 | collapsed: false, 85 | capitalizeFirst: true 86 | }; 87 | 88 | export default defineConfig(withSidebar(vitePressOptions, vitePressSidebarOptions)); 89 | ``` 90 | 91 | ### 2. 使用 `generateSidebar` 92 | 93 | `generateSidebar` 在`themeConfig.sidebar`级别可用。当需要对更详细的 `themeConfig` 设置进行代码分离时,可以使用此功能。 94 | 95 | ```javascript 96 | // `.vitepress/config.js` 97 | import { generateSidebar } from 'vitepress-sidebar'; 98 | 99 | export default defineConfig({ 100 | themeConfig: { 101 | sidebar: generateSidebar({ 102 | // VitePress Sidebar's options here... 103 | }) 104 | } 105 | }); 106 | ``` 107 | 108 | 要扫描您的项目文档,VitePress Sidebar需要通过 `documentRootPath` 选项指定工作路径来了解正确的位置。默认是`/`,但如果你的VitePress项目位于一个单独的文件夹中,如`docs`,根据你的项目,你将需要自己指定路径。 109 | 110 | 根据项目根路径,`documentRootPath` 中的路径将写入 `.vitePress` 文件夹所在的路径。 111 | 112 | ```text 113 | / 114 | ├─ package.json 115 | ├─ src/ 116 | ├─ docs/ <--------------- `documentRootPath` ('/docs') 117 | │ ├─ .vitepress/ 118 | │ ├─ another-directory/ 119 | │ ├─ hello.md 120 | │ └─ index.md 121 | └─ ... 122 | ``` 123 | 124 | 如果您的项目结构如上,则需要这样设置: 125 | 126 | ```javascript 127 | // `.vitepress/config.js` 128 | import { withSidebar } from 'vitepress-sidebar'; 129 | 130 | const vitePressOptions = {}; 131 | 132 | const vitePressSidebarOptions = { 133 | documentRootPath: '/docs' 134 | }; 135 | 136 | export default defineConfig(withSidebar(vitePressOptions, vitePressSidebarOptions)); 137 | ``` 138 | 139 | 要测试侧边栏结果如何打印,请在构建 VitePress 时将 `debugPrint` 选项设置为 `true`。你应该能在控制台中看到输出结果。 140 | 141 | 有关 VitePress Sidebar 配置的更多信息,请参阅下面的 **[选项](/zhHans/guide/options)** 部分。 142 | 143 | ## 代码示例 144 | 145 | ```javascript 146 | import { withSidebar } from 'vitepress-sidebar'; 147 | 148 | const vitePressConfigs = { 149 | title: 'VitePress Sidebar', 150 | themeConfig: { 151 | // ... 152 | } 153 | }; 154 | 155 | export default defineConfig( 156 | withSidebar(vitePressConfigs, { 157 | /* 158 | * 有关详细说明,请参阅下面的链接: 159 | * https://vitepress-sidebar.cdget.com/zhHans/guide/options 160 | */ 161 | // 162 | // ============ [ RESOLVING PATHS ] ============ 163 | // documentRootPath: '/', 164 | // scanStartPath: null, 165 | // resolvePath: null, 166 | // basePath: null, 167 | // followSymlinks: false, 168 | // 169 | // ============ [ GROUPING ] ============ 170 | // collapsed: true, 171 | // collapseDepth: 2, 172 | // rootGroupText: 'Contents', 173 | // rootGroupLink: 'https://github.com/jooy2', 174 | // rootGroupCollapsed: false, 175 | // 176 | // ============ [ GETTING MENU TITLE ] ============ 177 | // useTitleFromFileHeading: true, 178 | // useTitleFromFrontmatter: true, 179 | // useFolderLinkFromIndexFile: false, 180 | // useFolderTitleFromIndexFile: false, 181 | // frontmatterTitleFieldName: 'title', 182 | // 183 | // ============ [ GETTING MENU LINK ] ============ 184 | // useFolderLinkFromSameNameSubFile: false, 185 | // useFolderLinkFromIndexFile: false, 186 | // folderLinkNotIncludesFileName: false, 187 | // 188 | // ============ [ INCLUDE / EXCLUDE ] ============ 189 | // excludeByGlobPattern: ['README.md', 'folder/'], 190 | // excludeFilesByFrontmatterFieldName: 'exclude', 191 | // excludeByFolderDepth: undefined, 192 | // includeDotFiles: false, 193 | // includeEmptyFolder: false, 194 | // includeRootIndexFile: false, 195 | // includeFolderIndexFile: false, 196 | // 197 | // ============ [ STYLING MENU TITLE ] ============ 198 | // hyphenToSpace: true, 199 | // underscoreToSpace: true, 200 | // capitalizeFirst: false, 201 | // capitalizeEachWords: false, 202 | // keepMarkdownSyntaxFromTitle: false, 203 | // removePrefixAfterOrdering: false, 204 | // prefixSeparator: '.', 205 | // 206 | // ============ [ SORTING ] ============ 207 | // manualSortFileNameByPriority: ['first.md', 'second', 'third.md'], 208 | // sortFolderTo: null, 209 | // sortMenusByName: false, 210 | // sortMenusByFileDatePrefix: false, 211 | // sortMenusByFrontmatterOrder: false, 212 | // frontmatterOrderDefaultValue: 0, 213 | // sortMenusByFrontmatterDate: false, 214 | // sortMenusOrderByDescending: false, 215 | // sortMenusOrderNumericallyFromTitle: false, 216 | // sortMenusOrderNumericallyFromLink: false, 217 | // 218 | // ============ [ MISC ] ============ 219 | // debugPrint: false, 220 | }) 221 | ); 222 | ``` 223 | 224 | ## 输出示例 225 | 226 | ```text 227 | { 228 | documentRootPath: 'javascript', 229 | useTitleFromFileHeading: true, 230 | hyphenToSpace: true, 231 | excludeByGlobPattern: ['vitepress-how-to'] 232 | } 233 | 234 | /* 235 | [ 236 | { 237 | text: 'examples', 238 | items: [ 239 | { 240 | text: 'Examples', 241 | link: '/javascript/examples/examples' 242 | } 243 | ] 244 | }, 245 | { 246 | text: 'functions', 247 | items: [ 248 | { 249 | text: 'prototypes', 250 | items: [ 251 | { 252 | text: 'Array', 253 | items: [ 254 | { 255 | text: 'Array.indexOf', 256 | link: '/javascript/functions/prototypes/Array/Array.indexOf' 257 | } 258 | ] 259 | } 260 | ] 261 | } 262 | ] 263 | }, 264 | { 265 | text: 'Getting Started', 266 | link: '/javascript/getting_started' 267 | } 268 | ]; 269 | */ 270 | ``` 271 | -------------------------------------------------------------------------------- /docs/ko/guide/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 1 3 | --- 4 | 5 | # 시작하기 6 | 7 | 이 페이지에서는 VitePress Sidebar 모듈의 설치 및 사용 방법을 안내합니다. 8 | 9 | ## 설치 10 | 11 | 먼저 이 모듈을 사용하기 전에 **[VitePress](https://vitepress.dev/ko/)** 모듈을 사전 구성해야 할 수 있습니다. 12 | 13 | Node.js 버전은 20.x 이상을 사용하는 것이 좋습니다. **VitePress Sidebar**는 `ESM`으로 작성되었습니다. CommonJS 환경에서 사용하려면 [여기 지침을 참조하세요](/ko/troubleshooting/err-require-esm). 14 | 15 | [NPM](https://www.npmjs.com/package/vitepress-sidebar) 또는 다른 노드 모듈 패키지 관리자를 사용하여 모듈을 설치할 수 있습니다. 이 패키지는 개발자 환경에서만 사용되므로 `devDependencies`에 설치해야 합니다. 아래 명령어로 설치하세요: 16 | 17 | ```shell 18 | # `npm`으로 설치 19 | $ npm i -D vitepress-sidebar 20 | 21 | # `yarn`으로 설치 22 | $ yarn add -D vitepress-sidebar 23 | 24 | # `pnpm`으로 설치 25 | $ pnpm i -D vitepress-sidebar 26 | ``` 27 | 28 | ## 동작 과정 29 | 30 | VitePress Sidebar는 귀하의 프로젝트의 폴더에서 지정한 폴더 경로(`documentRootPath`)를 기준으로 폴더와 마크다운 파일을 계층별로 스캔합니다. 31 | 32 | 이후 설정에 따라 특정 파일을 제외, 정렬, 포맷팅하여 사이드바 메뉴의 제목을 읽어온 후 VitePress에서 요구하는 사이드바 스펙에 따라 설정 데이터를 최종적으로 출력하게 됩니다. 33 | 34 | 결과적으로는 VitePress의 `config.js` 파일은 다음과 같이 변환 될 것입니다. 35 | 36 | ```javascript 37 | export default { 38 | themeConfig: { 39 | sidebar: [ 40 | // VitePress Sidebar의 출력 결과 41 | { 42 | text: 'Guide', 43 | items: [ 44 | { text: 'Introduction', link: '/introduction' }, 45 | { text: 'Getting Started', link: '/getting-started' } 46 | ] 47 | } 48 | ] 49 | } 50 | }; 51 | ``` 52 | 53 | 이로 인해 `sidebar`의 각 메뉴에 대한 수동 작성이 필요하지 않습니다. 54 | 55 | ## 사용 방법 56 | 57 | VitePress Sidebar는 `withSidebar`와 `generateSidebar` 두가지 함수로 사이드 바를 자동으로 생성할 수 있습니다. 이 둘의 동작은 같지만 함수를 사용하는 위치가 다릅니다. 일반적으로는 `withSidebar`를 사용하는 것을 권장합니다. 58 | 59 | 설치한 모듈을 코드에 가져오려면 VitePress의 `config.js` 파일을 엽니다. 이 파일은 `.vitepress` 디렉토리에 위치하며 프로젝트에 따라 다른 확장자의 이름일 수 있으니 유의하시기 바랍니다. 60 | 61 | 파일을 열고 아래 두가지 방법 중 하나를 사용하여 `vitepress-sidebar`를 사용할 수 있습니다: 62 | 63 | ### 1. `withSidebar` 사용 (권장) 64 | 65 | `withSidebar`는 `defineConfig`레벨에서 사용합니다. 이 때 주의할 점은 VitePress의 설정 객체가 첫번째 파라미터에, 이후 VitePress Sidebar의 옵션이 두번째 파라미터에 위치해야 합니다. 66 | 67 | VitePress Sidebar는 기존 VitePress의 옵션에 필요한 추가 옵션을 오버라이딩 할 것입니다. 기존에 설정한 수동 `sidebar` 옵션은 새 옵션에 의해 무시됩니다. 68 | 69 | ```javascript 70 | // `.vitepress/config.js` 71 | import { withSidebar } from 'vitepress-sidebar'; 72 | 73 | const vitePressOptions = { 74 | // VitePress의 옵션 75 | title: 'VitePress Sidebar', 76 | themeConfig: { 77 | // ... 78 | } 79 | }; 80 | 81 | const vitePressSidebarOptions = { 82 | // VitePress Sidebar의 옵션 83 | documentRootPath: '/', 84 | collapsed: false, 85 | capitalizeFirst: true 86 | }; 87 | 88 | export default defineConfig(withSidebar(vitePressOptions, vitePressSidebarOptions)); 89 | ``` 90 | 91 | ### 2. `generateSidebar` 사용 92 | 93 | `generateSidebar`는 `themeConfig.sidebar` 레벨에서 사용할 수 있습니다. 이는 좀 더 세부적인 `themeConfig` 설정을 위해 코드 분리가 필요할 때 사용할 수 있습니다. 94 | 95 | ```javascript 96 | // `.vitepress/config.js` 97 | import { generateSidebar } from 'vitepress-sidebar'; 98 | 99 | export default defineConfig({ 100 | themeConfig: { 101 | sidebar: generateSidebar({ 102 | // VitePress Sidebar의 옵션 103 | }) 104 | } 105 | }); 106 | ``` 107 | 108 | VitePress Sidebar는 프로젝트의 문서를 스캔하기 위해 `documentRootPath` 옵션으로 작업 경로를 지정하여 올바른 위치를 알려주어야 합니다. 기본값은 `/`이지만 프로젝트에 따라 `docs`와 같이 별도의 폴더에 VitePress 프로젝트가 위치하는 경우에는 직접 경로를 지정해야 합니다. 109 | 110 | 프로젝트 루트 경로를 기준으로, `documentRootPath`의 경로는 `.vitePress` 폴더가 위치한 경로를 작성합니다. 111 | 112 | ```text 113 | / 114 | ├─ package.json 115 | ├─ src/ 116 | ├─ docs/ <--------------- `documentRootPath` ('/docs') 117 | │ ├─ .vitepress/ 118 | │ ├─ another-directory/ 119 | │ ├─ hello.md 120 | │ └─ index.md 121 | └─ ... 122 | ``` 123 | 124 | 위와 같은 구조의 프로젝트인 경우 아래와 같이 설정해야 합니다: 125 | 126 | ```javascript 127 | // `.vitepress/config.js` 128 | import { withSidebar } from 'vitepress-sidebar'; 129 | 130 | const vitePressOptions = {}; 131 | 132 | const vitePressSidebarOptions = { 133 | documentRootPath: '/docs' 134 | }; 135 | 136 | export default defineConfig(withSidebar(vitePressOptions, vitePressSidebarOptions)); 137 | ``` 138 | 139 | 사이드바 결과가 어떻게 출력되는지 테스트하려면 `debugPrint` 옵션을 `true`로 설정하여 VitePress를 빌드해 보세요. 콘솔에 출력이 표시될 것입니다. 140 | 141 | VitePress Sidebar의 설정에 대한 자세한 내용은 아래 **[옵션](/ko/guide/options)** 섹션을 참조하세요. 142 | 143 | ## 코드 예시 144 | 145 | ```javascript 146 | import { withSidebar } from 'vitepress-sidebar'; 147 | 148 | const vitePressConfigs = { 149 | title: 'VitePress Sidebar', 150 | themeConfig: { 151 | // ... 152 | } 153 | }; 154 | 155 | export default defineConfig( 156 | withSidebar(vitePressConfigs, { 157 | /* 158 | * 자세한 지침은 아래 링크를 참조하세요: 159 | * https://vitepress-sidebar.cdget.com/guide/options 160 | */ 161 | // 162 | // ============ [ RESOLVING PATHS ] ============ 163 | // documentRootPath: '/', 164 | // scanStartPath: null, 165 | // resolvePath: null, 166 | // basePath: null, 167 | // followSymlinks: false, 168 | // 169 | // ============ [ GROUPING ] ============ 170 | // collapsed: true, 171 | // collapseDepth: 2, 172 | // rootGroupText: 'Contents', 173 | // rootGroupLink: 'https://github.com/jooy2', 174 | // rootGroupCollapsed: false, 175 | // 176 | // ============ [ GETTING MENU TITLE ] ============ 177 | // useTitleFromFileHeading: true, 178 | // useTitleFromFrontmatter: true, 179 | // useFolderLinkFromIndexFile: false, 180 | // useFolderTitleFromIndexFile: false, 181 | // frontmatterTitleFieldName: 'title', 182 | // 183 | // ============ [ GETTING MENU LINK ] ============ 184 | // useFolderLinkFromSameNameSubFile: false, 185 | // useFolderLinkFromIndexFile: false, 186 | // folderLinkNotIncludesFileName: false, 187 | // 188 | // ============ [ INCLUDE / EXCLUDE ] ============ 189 | // excludeByGlobPattern: ['README.md', 'folder/'], 190 | // excludeFilesByFrontmatterFieldName: 'exclude', 191 | // excludeByFolderDepth: undefined, 192 | // includeDotFiles: false, 193 | // includeEmptyFolder: false, 194 | // includeRootIndexFile: false, 195 | // includeFolderIndexFile: false, 196 | // 197 | // ============ [ STYLING MENU TITLE ] ============ 198 | // hyphenToSpace: true, 199 | // underscoreToSpace: true, 200 | // capitalizeFirst: false, 201 | // capitalizeEachWords: false, 202 | // keepMarkdownSyntaxFromTitle: false, 203 | // removePrefixAfterOrdering: false, 204 | // prefixSeparator: '.', 205 | // 206 | // ============ [ SORTING ] ============ 207 | // manualSortFileNameByPriority: ['first.md', 'second', 'third.md'], 208 | // sortFolderTo: null, 209 | // sortMenusByName: false, 210 | // sortMenusByFileDatePrefix: false, 211 | // sortMenusByFrontmatterOrder: false, 212 | // frontmatterOrderDefaultValue: 0, 213 | // sortMenusByFrontmatterDate: false, 214 | // sortMenusOrderByDescending: false, 215 | // sortMenusOrderNumericallyFromTitle: false, 216 | // sortMenusOrderNumericallyFromLink: false, 217 | // 218 | // ============ [ MISC ] ============ 219 | // debugPrint: false, 220 | }) 221 | ); 222 | ``` 223 | 224 | ## 출력 예시 225 | 226 | ```text 227 | { 228 | documentRootPath: 'javascript', 229 | useTitleFromFileHeading: true, 230 | hyphenToSpace: true, 231 | excludeByGlobPattern: ['vitepress-how-to'] 232 | } 233 | 234 | /* 235 | [ 236 | { 237 | text: 'examples', 238 | items: [ 239 | { 240 | text: 'Examples', 241 | link: '/javascript/examples/examples' 242 | } 243 | ] 244 | }, 245 | { 246 | text: 'functions', 247 | items: [ 248 | { 249 | text: 'prototypes', 250 | items: [ 251 | { 252 | text: 'Array', 253 | items: [ 254 | { 255 | text: 'Array.indexOf', 256 | link: '/javascript/functions/prototypes/Array/Array.indexOf' 257 | } 258 | ] 259 | } 260 | ] 261 | } 262 | ] 263 | }, 264 | { 265 | text: 'Getting Started', 266 | link: '/javascript/getting_started' 267 | } 268 | ]; 269 | */ 270 | ``` 271 | -------------------------------------------------------------------------------- /docs/en/advanced-usage/multiple-sidebars-how-to.md: -------------------------------------------------------------------------------- 1 | # Multiple Sidebars How-to 2 | 3 | Multiple sidebars is a feature that allows you to display different sidebar menus based on a specific URI path. 4 | 5 | This is easily implemented in `vitepress-sidebar` with a few simple settings. In the end, **VitePress** will output the options as intended. 6 | 7 | To learn more about Multiple sidebars first, we recommend taking a look at **VitePress'** official documentation below: 8 | 9 | https://vitepress.dev/reference/default-theme-sidebar#multiple-sidebars 10 | 11 | ## Basic usage 12 | 13 | First, let's assume you have a root project called `docs` with subdirectories called `guide` and `config`, like this: 14 | 15 | ```text 16 | docs/ 17 | ├─ guide/ 18 | │ ├─ index.md 19 | │ ├─ one.md 20 | │ ├─ two.md 21 | │ └─ do-not-include.md 22 | └─ config/ 23 | ├─ index.md 24 | ├─ three.md 25 | └─ four.md 26 | ``` 27 | 28 | When the URL is located on a `/guide` page, the user wants the menu to show only the submenu of `guide` and hide the submenu of `config`. Similarly, you want to hide the submenu of `guide` when it is located on the `/config` page. 29 | 30 | To implement this in `vitepress-sidebar`, you need to approach it differently from the existing setup. 31 | 32 | Use the `withSidebar` function as before, but pass an array. The array will contain at least one option from `vitepress-sidebar`. The values in the array can be as many URLs as you want to specify. Of course, you can also configure them with different settings. 33 | 34 | ```javascript 35 | // Must pass array arguments!!!! 36 | const vitePressConfigs = { 37 | /* ... */ 38 | }; 39 | 40 | export default defineConfig( 41 | withSidebar(vitePressConfigs, [ 42 | { 43 | documentRootPath: 'docs', 44 | scanStartPath: 'guide', 45 | basePath: '/guide/', 46 | resolvePath: '/guide/', 47 | useTitleFromFileHeading: true 48 | }, 49 | { 50 | documentRootPath: 'docs', 51 | scanStartPath: 'config', 52 | resolvePath: '/config/', 53 | useTitleFromFrontmatter: true 54 | } 55 | ]) 56 | ); 57 | ``` 58 | 59 | The values of these options are used in the results as follows: 60 | 61 | ```text 62 | { 63 | : [ 64 | { 65 | base: , 66 | items: [...] // `/path/to/items` 67 | } 68 | ] 69 | } 70 | ``` 71 | 72 | Here's an example of the output from the above setup: 73 | 74 | ```json5 75 | { 76 | '/guide/': { 77 | base: '/guide/', 78 | items: [ 79 | { 80 | text: 'One', 81 | link: 'one' 82 | }, 83 | { 84 | text: 'Two', 85 | link: 'two' 86 | } 87 | ] 88 | }, 89 | '/config/': { 90 | base: '/config/', 91 | items: [ 92 | { 93 | text: 'Three', 94 | link: 'three' 95 | }, 96 | { 97 | text: 'Four', 98 | link: 'four' 99 | } 100 | ] 101 | } 102 | } 103 | ``` 104 | 105 | ## Multiple sidebar options 106 | 107 | The following options are available in Multiple sidebars: `scanStartPath`, `basePath`, and `resolvePath`. Each option is optional, but should be able to be used correctly depending on the situation. 108 | 109 | Each option is described below. However, we recommend that you first refer to the descriptions of each option on the [Options](/guide/options) page. 110 | 111 | The descriptions below are based on the following examples: 112 | 113 | ```text 114 | docs/ 115 | ├─ .vitepress/ 116 | ├─ guide/ 117 | │ ├─ api/ 118 | │ │ ├─ api-one.md 119 | │ │ └─ api-two.md 120 | │ ├─ one.md 121 | │ └─ two.md 122 | └─ config/ 123 | ├─ index.md 124 | ├─ three.md 125 | └─ four.md 126 | ``` 127 | 128 | ### `scanStartPath` 129 | 130 | This option is used to specify different directories as root paths for different routing rules. While `documentRootPath` is the root path that will actually be scanned (where the `.vitepress` directory is located), `scanStartPath` is the root path that should actually be seen in this route rule. 131 | 132 | For example, to include only files in the `/guide` directory, specify the value of `scanStartPath` as `guide`. However, the path in `documentRootPath` should not be included. 133 | 134 | ### `resolvePath` 135 | 136 | This option is used by VitePress to display the relevant menu when it encounters a specific URI. For example, if you want to display only the contents of the `guide/api` directory when reaching `example.com/guide/api`, the value of `resolvePath` would be `/guide/api`. It is recommended that you include `/` in front of the path. 137 | 138 | This will usually have a similar value to `scanStartPath`, but sometimes you may need to specify it differently for **i18n** routing. 139 | 140 | ### `basePath` 141 | 142 | This option is primarily utilized when working with VitePress' rewrite rules, and is optional otherwise. 143 | 144 | It replaces the value of the `base` path in VitePress. If this value is not specified, the value of `resolvePath` or the root path (`/`) is specified. 145 | 146 | If the actual path to the directory is different from the path structure in the URI, you should be able to navigate to the page provided by rewrite. Typically, the sidebar will generate a path based on the root directory and will not reference the rewrite path in VitePress. 147 | 148 | For example, suppose you have a rewrite rule that looks like this: 149 | 150 | ```javascript 151 | const vitePressConfigs = { 152 | rewrites: { 153 | 'guide/:page': 'help/:page' 154 | } 155 | }; 156 | 157 | const vitePressSidebarConfigs = [ 158 | { 159 | documentRootPath: 'docs', 160 | scanStartPath: 'guide', 161 | resolvePath: '/guide/' 162 | } 163 | ]; 164 | 165 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs)); 166 | ``` 167 | 168 | The `guide/one.md` document is displayed in the path to `help/one`. However, if you do this, the sidebar will not display the menu because it will try to find `help/one`, which is the path as it is. 169 | 170 | To fix this, change the path in `basePath` to `help`: 171 | 172 | ```javascript 173 | const vitePressConfigs = { 174 | rewrites: { 175 | 'guide/:page': 'help/:page' 176 | } 177 | }; 178 | 179 | const vitePressSidebarConfigs = [ 180 | { 181 | documentRootPath: 'docs', 182 | scanStartPath: 'guide', 183 | basePath: 'help', // <---------------------- Add this 184 | resolvePath: '/guide/' 185 | } 186 | ]; 187 | 188 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs)); 189 | ``` 190 | 191 | ## Displaying menus with complex paths and URIs 192 | 193 | The above example is typically when the path is defined in steps, but when you want to show folders that are deep in steps, especially when the URI is shorter or uses different conventions than the actual folder path, you need to use additional methods. For example, you have a folder structure like this: 194 | 195 | ```text 196 | docs/ 197 | ├─ guide/ 198 | │ ├─ api/ 199 | │ │ ├─ api-one.md 200 | │ │ └─ api-two.md 201 | │ ├─ one.md 202 | │ └─ two.md 203 | └─ config/ 204 | ├─ index.md 205 | ├─ three.md 206 | └─ four.md 207 | ``` 208 | 209 | This time, we want to show the menu in `docs/guide/api` when we reach the one-level URI `/api`. The expected menu is to show only `api-one.md` and `api-two.md`. 210 | 211 | ```javascript 212 | withSidebar([ 213 | { 214 | documentRootPath: 'docs', 215 | scanStartPath: 'guide/api', 216 | resolvePath: '/api/' 217 | } 218 | ]); 219 | ``` 220 | 221 | However, if you configure the options like this, you won't be able to display the menu, because the `api` directory is a subdirectory of `guide`. **VitePress** won't detect this and will navigate to a non-existent document. 222 | 223 | To solve this, you need to use **VitePress'** Routing feature in parallel, see the article below for an explanation: 224 | 225 | https://vitepress.dev/guide/routing#route-rewrites 226 | 227 | Add the `rewrites` option to the VitePress settings in `defineConfig` following the example above: 228 | 229 | ```javascript 230 | const vitePressConfigs = { 231 | /* [START] Add This */ 232 | rewrites: { 233 | 'guide/api/:page': 'api/:page' 234 | } 235 | /* [END] Add This */ 236 | }; 237 | 238 | const vitePressSidebarConfigs = { 239 | documentRootPath: 'docs', 240 | scanStartPath: 'guide/api', 241 | resolvePath: '/api/' 242 | }; 243 | 244 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs)); 245 | ``` 246 | 247 | Now this will show a submenu of `docs/guide/api` when the URI path starts with `/api`! 248 | -------------------------------------------------------------------------------- /docs/en/advanced-usage/internationalization.md: -------------------------------------------------------------------------------- 1 | # Internationalization 2 | 3 | This page describes how to achieve internationalization (i18n) using VitePress Sidebar. 4 | 5 | VitePress supports multilingual documentation. If you place translated markdown files in a directory for each language and use the `locales` option in VitePress's `defineConfig`, a language selector will be displayed and will show documents in the specified directory for each language. 6 | 7 | ## Translate page layouts (manually) 8 | 9 | First, VitePress allows you to translate various layout interface elements. By layout, we mean all the associated text for features like 'Previous', 'Next', 'Table of Contents', and 'Change Theme'. It also includes translating any text that appears in the search function. 10 | 11 | Various interface (layout) texts on a VitePress page can provide language-specific translations for `locales`. For example 12 | 13 | ```text 14 | "locales": { 15 | "root": { 16 | "lang": "en-US", 17 | "label": "English", 18 | "description": "VitePress Sidebar is a VitePress plugin that automatically generates sidebar menus with one setup and no hassle. Save time by easily creating taxonomies for tons of articles.", 19 | "themeConfig": { 20 | "docFooter": { 21 | "prev": "Previous page", 22 | "next": "Next page" 23 | }, 24 | "outline": { 25 | "label": "On this page" 26 | }, 27 | "lastUpdated": { 28 | "text": "Last updated" 29 | }, 30 | "langMenuLabel": "Change language", 31 | "returnToTopLabel": "Return to top", 32 | "sidebarMenuLabel": "Menu", 33 | "darkModeSwitchLabel": "Appearance", 34 | "lightModeSwitchTitle": "Switch to light theme", 35 | "darkModeSwitchTitle": "Switch to dark theme" 36 | } 37 | }, 38 | "ko": { 39 | "lang": "ko-KR", 40 | "label": "한국어", 41 | "description": "VitePress Sidebar는 번거로운 작업 없이 한번의 설정만으로 사이드바 메뉴를 자니다. 수많은 문서에 대한 분류를 손쉽게 만들어 시간을 절약하세요.", 42 | "themeConfig": { 43 | "docFooter": { 44 | "prev": "이전", 45 | "next": "다음" 46 | }, 47 | "outline": { 48 | "label": "이 페이지 콘텐츠" 49 | }, 50 | "lastUpdated": { 51 | "text": "업데이트 일자" 52 | }, 53 | "langMenuLabel": "언어 변경", 54 | "returnToTopLabel": "맨 위로", 55 | "sidebarMenuLabel": "사이드바 메뉴", 56 | "darkModeSwitchLabel": "다크 모드", 57 | "lightModeSwitchTitle": "라이트 모드로 변경", 58 | "darkModeSwitchTitle": "다크 모드로 변경" 59 | } 60 | } 61 | } 62 | ``` 63 | 64 | (The `root` language is the primary language on the page). 65 | 66 | To learn more about translating layouts, see the following articles: https://vitepress.dev/guide/i18n 67 | 68 | For the text that appears in the search function, you need to set it in the `themeConfig.search` option in `defineConfig`, for example 69 | 70 | ```text 71 | "themeConfig": { 72 | "search": { 73 | "provider": "local", 74 | "options": { 75 | "locales": { 76 | "root": { 77 | "translations": { 78 | "button": { 79 | "buttonText": "Search", 80 | "buttonAriaLabel": "Search" 81 | }, 82 | "modal": { 83 | "displayDetails": "Display detailed list", 84 | "resetButtonTitle": "Reset search", 85 | "backButtonTitle": "Close search", 86 | "noResultsText": "No results for", 87 | "footer": { 88 | "selectText": "to select", 89 | "selectKeyAriaLabel": "enter", 90 | "navigateText": "to navigate", 91 | "navigateUpKeyAriaLabel": "up arrow", 92 | "navigateDownKeyAriaLabel": "down arrow", 93 | "closeText": "to close", 94 | "closeKeyAriaLabel": "escape" 95 | } 96 | } 97 | } 98 | }, 99 | "ko": { 100 | "translations": { 101 | "button": { 102 | "buttonText": "검색", 103 | "buttonAriaLabel": "검색" 104 | }, 105 | "modal": { 106 | "displayDetails": "상세 목록 표시", 107 | "resetButtonTitle": "검색 초기화", 108 | "backButtonTitle": "검색 닫기", 109 | "noResultsText": "결과를 찾을 수 없음", 110 | "footer": { 111 | "selectText": "선택", 112 | "selectKeyAriaLabel": "선택하기", 113 | "navigateText": "탐색", 114 | "navigateUpKeyAriaLabel": "위로", 115 | "navigateDownKeyAriaLabel": "아래로", 116 | "closeText": "닫기", 117 | "closeKeyAriaLabel": "esc" 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | } 126 | ``` 127 | 128 | ## Translate page layouts (automatic) 129 | 130 | [VitePress I18n](https://vitepress-i18n.cdget.com), a family plugin of VitePress Sidebar, allows you to automate the process of translating manually like above. With a few simple settings, you can stop spending hours translating layouts! 131 | 132 | Install the VitePress I18n module with the command below: 133 | 134 | ```shell 135 | $ npm i -D vitepress-i18n 136 | ``` 137 | 138 | And in `defineConfig`, set the following: 139 | 140 | ```javascript 141 | import { withSidebar } from 'vitepress-sidebar'; 142 | import { withI18n } from 'vitepress-i18n'; 143 | 144 | const vitePressConfig = { 145 | // VitePress config 146 | }; 147 | 148 | const vitePressSidebarConfig = { 149 | // VitePress Sidebar config 150 | }; 151 | 152 | const vitePressI18nConfig = { 153 | // VitePress I18n config 154 | locales: ['en', 'ko'], // first locale 'en' is root locale 155 | searchProvider: 'local' // enable search with auto translation 156 | }; 157 | 158 | export default defineConfig( 159 | withSidebar(withI18n(vitePressConfig, vitePressI18nConfig), vitePressSidebarConfig) 160 | ); 161 | ``` 162 | 163 | Now leave the interface translation to the module! Of course, you can customize the detailed text as well. 164 | 165 | For more information, read the documentation page for VitePress I18n: https://vitepress-i18n.cdget.com/guide/getting-started 166 | 167 | ## Language-specific sidebar settings 168 | 169 | In VitePress, you can display the sidebar differently for different languages. For example, let's say you have a folder structure like this 170 | 171 | ```text 172 | / 173 | ├─ package.json 174 | ├─ docs/ 175 | │ ├─ .vitepress/ 176 | │ │ └─ config.js 177 | │ ├─ en/ 178 | │ │ └─ abc.md 179 | │ ├─ ko/ 180 | │ │ └─ abc.md 181 | │ └─ zhHans/ 182 | │ └─ abc.md 183 | └─ ... 184 | ``` 185 | 186 | In the example, `abc.md` is the same document translated into each language. If you change the language to English, Korean, and Chinese respectively, the sidebar will scan each directory `en`, `ko`, and `zhHans` to get the title for the language. 187 | 188 | Here's an example of how to accomplish this: 189 | 190 | ```javascript 191 | const rootLocale = 'en' 192 | const supportedLocales = [rootLocale, 'ko', 'zhHans']; 193 | 194 | const vitePressConfigs = { 195 | rewrites: { 196 | 'en/:rest*': ':rest*' 197 | } 198 | } 199 | 200 | const commonSidebarConfigs = { 201 | // Sidebar common configurations 202 | } 203 | 204 | const vitePressSidebarConfigs = [ 205 | ...supportedLocales.map((lang) => { 206 | return { 207 | ...commonSidebarConfigs, 208 | ...(rootLocale === lang ? {} : { basePath: `/${lang}/` }), // If using `rewrites` option 209 | documentRootPath: `/docs/${lang}`, 210 | resolvePath: rootLocale === lang ? '/' : `/${lang}/`, 211 | }; 212 | }) 213 | ] 214 | 215 | export default defineConfig(withSidebar(vitePressConfigs, vitePressSidebarConfigs) 216 | ``` 217 | 218 | First, `rewrites` allows you to suppress `/en/` in the URI path when you are using English as the root language (the `en` directory). (This is optional.) 219 | 220 | Next, the setup of `sidebar` is similar to the previous one, but you need to set up multiple sidebars with directories for each language. Here's a description of each option: 221 | 222 | - `basePath`: When used in conjunction with the `rewrites` rule, it prevents the root locale from pointing to the `/en/` path. 223 | - `documentRootPath`: The root path where the actual documentation files are located. For each language, look for the `en`, `ko`, and `zhHans` directories. 224 | - `resolvePath`: `/docs` The location of the file from the directory that should actually scan the sidebar listings. Specify the directory location for each language, excluding the root locale. 225 | 226 | This ensures that the sidebar only displays articles from the specified directory per language. The above is an example, so you may need to set the options differently depending on the size of your project or directory structure. 227 | -------------------------------------------------------------------------------- /docs/zhHans/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | title: VitePress Sidebar 5 | titleTemplate: 功能强大的自动侧边栏生成器 6 | 7 | hero: 8 | name: VitePress Sidebar 9 | text: 功能强大的自动侧边栏生成器 10 | tagline: VitePress自动侧边栏插件,可自动创建一个简单的配置 11 | actions: 12 | - theme: brand 13 | text: 入门 14 | link: /zhHans/guide/getting-started 15 | - theme: alt 16 | text: 选项 17 | link: /zhHans/guide/options 18 | - theme: alt 19 | text: GitHub 20 | link: https://github.com/jooy2/vitepress-sidebar 21 | image: 22 | src: /sidebar.png 23 | alt: Sidebar 24 | 25 | features: 26 | - icon: 27 | title: 与 VitePress 的最佳搭配 28 | details: 针对最新版本的VitePress进行了优化。 29 | - icon: 30 | title: 轻松配置细粒度侧边栏 31 | details: 简单易用,大量选项可按你的喜好自定义。自定义菜单可用于排序、特殊字符转换、文件和文件夹过滤器等! 32 | - icon: 33 | title: 可广泛扩展的用例 34 | details: 支持多个侧边栏、Frontmatter 和 TypeScript,以处理各种使用情况。 35 | - icon: 36 | title: 可靠的维护支持 37 | details: 我们的下载量超过 2K+,在现实世界中有许多使用案例,而且我们拥有快速的技术支持。 38 | --- 39 | 40 | 61 | -------------------------------------------------------------------------------- /docs/en/guide/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 1 3 | --- 4 | 5 | # Getting Started 6 | 7 | This page walks you through the installation and use of the VitePress Sidebar module. 8 | 9 | ## Installation 10 | 11 | First, you may need to pre-configure **[VitePress](https://vitepress.dev)** before using this module. 12 | 13 | We recommend using **Node.js 20.x** or higher. The **VitePress Sidebar** is written in `ESM`. To use it in `CommonJS`, [see instructions here](/troubleshooting/err-require-esm). 14 | 15 | You will need to install the module using [NPM](https://www.npmjs.com/package/vitepress-sidebar) or any other Node module package manager. The package should be installed in `devDependencies` as it is only used in the developer environment. Use the command below: 16 | 17 | ```shell 18 | # via npm 19 | $ npm i -D vitepress-sidebar 20 | 21 | # via yarn 22 | $ yarn add -D vitepress-sidebar 23 | 24 | # via pnpm 25 | $ pnpm i -D vitepress-sidebar 26 | ``` 27 | 28 | ## How it works 29 | 30 | VitePress Sidebar scans your folders and markdown files hierarchically based on the folder path (`documentRootPath`) you specify in your project's folders. 31 | 32 | It will then exclude, sort, and format certain files based on your settings to read the titles of the sidebar menus, and finally output the settings data according to the sidebar specs required by VitePress. 33 | 34 | As a result, VitePress's `config.js` file should look something like this 35 | 36 | ```javascript 37 | export default { 38 | themeConfig: { 39 | sidebar: [ 40 | // VitePress Sidebar's output 41 | { 42 | text: 'Guide', 43 | items: [ 44 | { text: 'Introduction', link: '/introduction' }, 45 | { text: 'Getting Started', link: '/getting-started' } 46 | ] 47 | } 48 | ] 49 | } 50 | }; 51 | ``` 52 | 53 | This eliminates the need for manual creation for each menu in the `sidebar`. 54 | 55 | ## How to use 56 | 57 | VitePress Sidebar can automatically generate sidebars with two functions: `withSidebar` and `generateSidebar`. They have the same behavior, but where to use the functions is different. In general, we recommend using `withSidebar`. 58 | 59 | To import the modules you installed into your code, open VitePress's `config.js` file. Note that this file is located in the `.vitepress` directory and may have a different extension depending on your project. 60 | 61 | File and use `vitepress-sidebar` in one of the two ways below: 62 | 63 | ### 1. Using `withSidebar` (recommended) 64 | 65 | The `withSidebar` is used at the `defineConfig` level. Note that the configuration object from VitePress should be placed in the first parameter and the options from the VitePress Sidebar in the second parameter. 66 | 67 | VitePress Sidebar will override any additional options required by the existing options in VitePress. Any manual `sidebar` options you may have set previously will be overridden by the new options. 68 | 69 | ```javascript 70 | // `.vitepress/config.js` 71 | import { withSidebar } from 'vitepress-sidebar'; 72 | 73 | const vitePressOptions = { 74 | // VitePress's options here... 75 | title: 'VitePress Sidebar', 76 | themeConfig: { 77 | // ... 78 | } 79 | }; 80 | 81 | const vitePressSidebarOptions = { 82 | // VitePress Sidebar's options here... 83 | documentRootPath: '/', 84 | collapsed: false, 85 | capitalizeFirst: true 86 | }; 87 | 88 | export default defineConfig(withSidebar(vitePressOptions, vitePressSidebarOptions)); 89 | ``` 90 | 91 | ### 2. Using `generateSidebar` 92 | 93 | `generateSidebar` is available at the `themeConfig.sidebar` level. This can be used when code separation is needed for more detailed `themeConfig` settings. 94 | 95 | ```javascript 96 | // `.vitepress/config.js` 97 | import { generateSidebar } from 'vitepress-sidebar'; 98 | 99 | export default defineConfig({ 100 | themeConfig: { 101 | sidebar: generateSidebar({ 102 | // VitePress Sidebar's options here... 103 | }) 104 | } 105 | }); 106 | ``` 107 | 108 | To scan your project's documents, VitePress Sidebar needs to know the correct location by specifying the working path with the `documentRootPath` option. The default is `/`, but if your VitePress project is located in a separate folder, such as `docs`, depending on your project, you will need to specify the path yourself. 109 | 110 | Based on the project root path, the path in `documentRootPath` will write the path where the `.vitePress` folder is located. 111 | 112 | ```text 113 | / 114 | ├─ package.json 115 | ├─ src/ 116 | ├─ docs/ <--------------- `documentRootPath` ('/docs') 117 | │ ├─ .vitepress/ 118 | │ ├─ another-directory/ 119 | │ ├─ hello.md 120 | │ └─ index.md 121 | └─ ... 122 | ``` 123 | 124 | If your project is structured like the one above, you'll need to set it up like this: 125 | 126 | ```javascript 127 | // `.vitepress/config.js` 128 | import { withSidebar } from 'vitepress-sidebar'; 129 | 130 | const vitePressOptions = {}; 131 | 132 | const vitePressSidebarOptions = { 133 | documentRootPath: '/docs' 134 | }; 135 | 136 | export default defineConfig(withSidebar(vitePressOptions, vitePressSidebarOptions)); 137 | ``` 138 | 139 | To test how the sidebar results are printed, try building VitePress with the `debugPrint` option set to `true`. You should see the output in the console. 140 | 141 | For more information about the configuration of VitePress Sidebar, see **[Options](/guide/options)** section below. 142 | 143 | ## Code Example 144 | 145 | ```javascript 146 | import { withSidebar } from 'vitepress-sidebar'; 147 | 148 | const vitePressConfigs = { 149 | title: 'VitePress Sidebar', 150 | themeConfig: { 151 | // ... 152 | } 153 | }; 154 | 155 | export default defineConfig( 156 | withSidebar(vitePressConfigs, { 157 | /* 158 | * For detailed instructions, see the links below: 159 | * https://vitepress-sidebar.cdget.com/guide/options 160 | */ 161 | // 162 | // ============ [ RESOLVING PATHS ] ============ 163 | // documentRootPath: '/', 164 | // scanStartPath: null, 165 | // resolvePath: null, 166 | // basePath: null, 167 | // followSymlinks: false, 168 | // 169 | // ============ [ GROUPING ] ============ 170 | // collapsed: true, 171 | // collapseDepth: 2, 172 | // rootGroupText: 'Contents', 173 | // rootGroupLink: 'https://github.com/jooy2', 174 | // rootGroupCollapsed: false, 175 | // 176 | // ============ [ GETTING MENU TITLE ] ============ 177 | // useTitleFromFileHeading: true, 178 | // useTitleFromFrontmatter: true, 179 | // useFolderLinkFromIndexFile: false, 180 | // useFolderTitleFromIndexFile: false, 181 | // frontmatterTitleFieldName: 'title', 182 | // 183 | // ============ [ GETTING MENU LINK ] ============ 184 | // useFolderLinkFromSameNameSubFile: false, 185 | // useFolderLinkFromIndexFile: false, 186 | // folderLinkNotIncludesFileName: false, 187 | // 188 | // ============ [ INCLUDE / EXCLUDE ] ============ 189 | // excludeByGlobPattern: ['README.md', 'folder/'], 190 | // excludeFilesByFrontmatterFieldName: 'exclude', 191 | // excludeByFolderDepth: undefined, 192 | // includeDotFiles: false, 193 | // includeEmptyFolder: false, 194 | // includeRootIndexFile: false, 195 | // includeFolderIndexFile: false, 196 | // 197 | // ============ [ STYLING MENU TITLE ] ============ 198 | // hyphenToSpace: true, 199 | // underscoreToSpace: true, 200 | // capitalizeFirst: false, 201 | // capitalizeEachWords: false, 202 | // keepMarkdownSyntaxFromTitle: false, 203 | // removePrefixAfterOrdering: false, 204 | // prefixSeparator: '.', 205 | // 206 | // ============ [ SORTING ] ============ 207 | // manualSortFileNameByPriority: ['first.md', 'second', 'third.md'], 208 | // sortFolderTo: null, 209 | // sortMenusByName: false, 210 | // sortMenusByFileDatePrefix: false, 211 | // sortMenusByFrontmatterOrder: false, 212 | // frontmatterOrderDefaultValue: 0, 213 | // sortMenusByFrontmatterDate: false, 214 | // sortMenusOrderByDescending: false, 215 | // sortMenusOrderNumericallyFromTitle: false, 216 | // sortMenusOrderNumericallyFromLink: false, 217 | // 218 | // ============ [ MISC ] ============ 219 | // debugPrint: false, 220 | }) 221 | ); 222 | ``` 223 | 224 | ## Example output 225 | 226 | ```text 227 | { 228 | documentRootPath: 'javascript', 229 | useTitleFromFileHeading: true, 230 | hyphenToSpace: true, 231 | excludeByGlobPattern: ['vitepress-how-to'] 232 | } 233 | 234 | /* 235 | [ 236 | { 237 | text: 'examples', 238 | items: [ 239 | { 240 | text: 'Examples', 241 | link: '/javascript/examples/examples' 242 | } 243 | ] 244 | }, 245 | { 246 | text: 'functions', 247 | items: [ 248 | { 249 | text: 'prototypes', 250 | items: [ 251 | { 252 | text: 'Array', 253 | items: [ 254 | { 255 | text: 'Array.indexOf', 256 | link: '/javascript/functions/prototypes/Array/Array.indexOf' 257 | } 258 | ] 259 | } 260 | ] 261 | } 262 | ] 263 | }, 264 | { 265 | text: 'Getting Started', 266 | link: '/javascript/getting_started' 267 | } 268 | ]; 269 | */ 270 | ``` 271 | -------------------------------------------------------------------------------- /docs/ko/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | title: VitePress Sidebar 5 | titleTemplate: 강력한 사이드바 생성 자동화 도구 6 | 7 | hero: 8 | name: VitePress Sidebar 9 | text: 강력한 사이드바 생성 자동화 도구 10 | tagline: 간단한 구성으로 알아서 생성하는 VitePress의 사이드바 플러그인을 소개합니다. 11 | actions: 12 | - theme: brand 13 | text: 시작하기 14 | link: /ko/guide/getting-started 15 | - theme: alt 16 | text: 옵션 17 | link: /ko/guide/options 18 | - theme: alt 19 | text: GitHub 20 | link: https://github.com/jooy2/vitepress-sidebar 21 | image: 22 | src: /sidebar.png 23 | alt: Sidebar 24 | 25 | features: 26 | - icon: 27 | title: VitePress를 효율적으로 활용 28 | details: 최신 버전의 VitePress에 최적화되었습니다. 29 | - icon: 30 | title: 쉽고 세분화된 사이드바 구성 31 | details: 사용하기 쉽고, 원하는 대로 사용자 지정할 수 있는 다양한 옵션이 있습니다. 정렬, 특수 문자 변환, 파일 및 폴더 필터 등을 위한 메뉴를 사용자 지정하세요! 32 | - icon: 33 | title: 광범위한 사용 사례 34 | details: 다양한 사용 사례를 처리할 수 있도록 다중 사이드바, Frontmatter, TypeScript를 지원합니다. 35 | - icon: 36 | title: 안정적인 유지 관리 지원 37 | details: 2K 이상의 다운로드를 통해 실제 사용 사례가 많으며, 빠른 기술 지원을 제공합니다. 38 | --- 39 | 40 | 61 | -------------------------------------------------------------------------------- /test/specs/base.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { describe, it } from 'node:test'; 3 | import { generateSidebar, withSidebar } from '../../dist'; 4 | 5 | const TEST_DIR_BASE = 'test/resources'; 6 | 7 | describe('Test: base test', () => { 8 | it('Without configurations', () => { 9 | assert.deepStrictEqual( 10 | generateSidebar({ 11 | documentRootPath: `${TEST_DIR_BASE}/general` 12 | }), 13 | [ 14 | { 15 | text: 'a', 16 | link: '/a' 17 | }, 18 | { 19 | text: 'b', 20 | link: '/b' 21 | }, 22 | { 23 | text: 'c', 24 | link: '/c' 25 | }, 26 | { 27 | text: 'folder', 28 | items: [ 29 | { 30 | text: 'folder-test-2', 31 | link: '/folder/folder-test-2' 32 | }, 33 | { 34 | text: 'folder-test', 35 | link: '/folder/folder-test' 36 | }, 37 | { 38 | text: 'subFolder', 39 | items: [ 40 | { 41 | text: 'sub-folder-test', 42 | link: '/folder/subFolder/sub-folder-test' 43 | } 44 | ] 45 | } 46 | ] 47 | }, 48 | { 49 | text: 'folder-2', 50 | items: [ 51 | { 52 | text: 'folder2', 53 | link: '/folder-2/folder2' 54 | } 55 | ] 56 | }, 57 | { 58 | text: 'test', 59 | link: '/test' 60 | } 61 | ] 62 | ); 63 | }); 64 | 65 | it('With complex configurations (A)', () => { 66 | assert.deepEqual( 67 | generateSidebar({ 68 | documentRootPath: `${TEST_DIR_BASE}/general`, 69 | collapsed: false, 70 | hyphenToSpace: true, 71 | underscoreToSpace: true, 72 | includeRootIndexFile: true, 73 | useTitleFromFrontmatter: true, 74 | excludeByGlobPattern: ['a.md', 'c.md', 'folder-2/'] 75 | }), 76 | [ 77 | { 78 | text: 'b', 79 | link: '/b' 80 | }, 81 | { 82 | text: 'folder', 83 | items: [ 84 | { 85 | text: 'folder test 2', 86 | link: '/folder/folder-test-2' 87 | }, 88 | { 89 | text: 'folder test', 90 | link: '/folder/folder-test' 91 | }, 92 | { 93 | text: 'subFolder', 94 | items: [ 95 | { 96 | text: 'sub folder test', 97 | link: '/folder/subFolder/sub-folder-test' 98 | } 99 | ], 100 | collapsed: false 101 | } 102 | ], 103 | collapsed: false 104 | }, 105 | { 106 | text: 'test', 107 | link: '/test' 108 | } 109 | ] 110 | ); 111 | }); 112 | 113 | it('With complex configurations (B)', () => { 114 | assert.deepEqual( 115 | generateSidebar({ 116 | documentRootPath: `${TEST_DIR_BASE}/general`, 117 | includeEmptyFolder: true, 118 | includeDotFiles: true, 119 | excludeByGlobPattern: ['subFolder/'], 120 | hyphenToSpace: true, 121 | underscoreToSpace: true, 122 | capitalizeFirst: true, 123 | useTitleFromFrontmatter: true, 124 | useTitleFromFileHeading: true 125 | }), 126 | [ 127 | { 128 | text: 'A', 129 | link: '/a' 130 | }, 131 | { 132 | text: 'B', 133 | link: '/b' 134 | }, 135 | { 136 | text: 'C', 137 | link: '/c' 138 | }, 139 | { 140 | text: 'Folder', 141 | items: [ 142 | { 143 | text: 'Folder test 2', 144 | link: '/folder/folder-test-2' 145 | }, 146 | { 147 | text: 'FolderTestFile', 148 | link: '/folder/folder-test' 149 | } 150 | ] 151 | }, 152 | { 153 | text: 'Folder 2', 154 | items: [ 155 | { 156 | text: 'Folder2 File', 157 | link: '/folder-2/folder2' 158 | } 159 | ] 160 | }, 161 | { 162 | text: 'TestFile', 163 | link: '/test' 164 | } 165 | ] 166 | ); 167 | }); 168 | 169 | it('API: With complex configurations (C)', () => { 170 | assert.deepEqual( 171 | generateSidebar({ 172 | documentRootPath: `${TEST_DIR_BASE}/number-title-and-filename`, 173 | excludeByGlobPattern: ['index.md'], 174 | capitalizeFirst: true, 175 | collapsed: true, 176 | sortMenusOrderNumericallyFromTitle: true, 177 | useFolderTitleFromIndexFile: true, 178 | useFolderLinkFromSameNameSubFile: true, 179 | useTitleFromFileHeading: true, 180 | useTitleFromFrontmatter: true, 181 | hyphenToSpace: true 182 | }), 183 | [ 184 | { 185 | link: '/1-introduction', 186 | text: '1. Introduction' 187 | }, 188 | { 189 | link: '/2-how-to-use', 190 | text: '2. How to use' 191 | } 192 | ] 193 | ); 194 | }); 195 | 196 | it('withSidebar: basic', () => { 197 | assert.deepStrictEqual( 198 | withSidebar( 199 | { 200 | title: 'VitePress Sidebar', 201 | themeConfig: { 202 | sidebar: [ 203 | { 204 | text: 'Not used', 205 | link: '/' 206 | } 207 | ], 208 | logo: { src: '/logo.png' }, 209 | footer: { 210 | message: 'Footer' 211 | } 212 | } 213 | }, 214 | { 215 | documentRootPath: `${TEST_DIR_BASE}/general` 216 | } 217 | ), 218 | { 219 | title: 'VitePress Sidebar', 220 | themeConfig: { 221 | sidebar: [ 222 | { 223 | text: 'a', 224 | link: '/a' 225 | }, 226 | { 227 | text: 'b', 228 | link: '/b' 229 | }, 230 | { 231 | text: 'c', 232 | link: '/c' 233 | }, 234 | { 235 | text: 'folder', 236 | items: [ 237 | { 238 | text: 'folder-test-2', 239 | link: '/folder/folder-test-2' 240 | }, 241 | { 242 | text: 'folder-test', 243 | link: '/folder/folder-test' 244 | }, 245 | { 246 | text: 'subFolder', 247 | items: [ 248 | { 249 | text: 'sub-folder-test', 250 | link: '/folder/subFolder/sub-folder-test' 251 | } 252 | ] 253 | } 254 | ] 255 | }, 256 | { 257 | text: 'folder-2', 258 | items: [ 259 | { 260 | text: 'folder2', 261 | link: '/folder-2/folder2' 262 | } 263 | ] 264 | }, 265 | { 266 | text: 'test', 267 | link: '/test' 268 | } 269 | ], 270 | logo: { 271 | src: '/logo.png' 272 | }, 273 | footer: { 274 | message: 'Footer' 275 | } 276 | } 277 | } 278 | ); 279 | }); 280 | 281 | it('Contains a path with the same name as `documentRootPath`', () => { 282 | assert.deepStrictEqual( 283 | generateSidebar({ 284 | documentRootPath: `${TEST_DIR_BASE}/same-name-path` 285 | }), 286 | [ 287 | { 288 | text: 'a', 289 | link: '/a' 290 | }, 291 | { 292 | text: 'test', 293 | items: [ 294 | { 295 | text: 'resources', 296 | items: [ 297 | { 298 | text: 'same-name-path', 299 | items: [ 300 | { 301 | text: 'b', 302 | link: '/test/resources/same-name-path/b' 303 | } 304 | ] 305 | } 306 | ] 307 | } 308 | ] 309 | } 310 | ] 311 | ); 312 | }); 313 | 314 | it('`index.md` file must be used correctly according to the situation.', () => { 315 | assert.deepEqual( 316 | generateSidebar({ 317 | documentRootPath: `${TEST_DIR_BASE}/index-files`, 318 | useTitleFromFileHeading: true, 319 | excludeByGlobPattern: ['abc'] 320 | }), 321 | [ 322 | { 323 | text: 'a', 324 | items: [ 325 | { 326 | text: 'Test Index', 327 | link: '/a/testindex' 328 | } 329 | ] 330 | }, 331 | { 332 | text: 'index', 333 | items: [ 334 | { 335 | text: 'B', 336 | link: '/index/b' 337 | } 338 | ] 339 | } 340 | ] 341 | ); 342 | }); 343 | }); 344 | -------------------------------------------------------------------------------- /docs/en/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | title: VitePress Sidebar 5 | titleTemplate: Powerful auto sidebar generator 6 | 7 | hero: 8 | name: VitePress Sidebar 9 | text: Powerful auto sidebar generator 10 | tagline: A VitePress auto sidebar plugin that automatically creates a simple configuration 11 | actions: 12 | - theme: brand 13 | text: Getting Started 14 | link: /guide/getting-started 15 | - theme: alt 16 | text: Options 17 | link: /guide/options 18 | - theme: alt 19 | text: GitHub 20 | link: https://github.com/jooy2/vitepress-sidebar 21 | image: 22 | src: /sidebar.png 23 | alt: Sidebar 24 | 25 | features: 26 | - icon: 27 | title: Best Pairings with VitePress 28 | details: Optimized for the latest version of VitePress. 29 | - icon: 30 | title: Easy and granular sidebar configuration 31 | details: Easy to use, lots of options to customize to your liking. Customize menus for sorting, special character conversion, file and folder filters, and more! 32 | - icon: 33 | title: Broadly scalable use cases 34 | details: Support for multiple sidebars, Frontmatter, and TypeScript to handle a variety of use cases. 35 | - icon: 36 | title: Reliable maintenance support 37 | details: With 2K+ downloads, there are many real-world use cases, and we have fast technical support. 38 | --- 39 | 40 | 61 | -------------------------------------------------------------------------------- /lib/helper.ts: -------------------------------------------------------------------------------- 1 | // Get a single value of type T from Frontmatter 2 | // Defaults to defaultValue 3 | import { readFileSync } from 'fs'; 4 | import matter from 'gray-matter'; 5 | import { capitalizeFirst } from 'qsu'; 6 | import type { 7 | AnyValueObject, 8 | SidebarItem, 9 | SidebarListItem, 10 | SortByObjectKeyOptions, 11 | VitePressSidebarOptions 12 | } from './types.ts'; 13 | 14 | export function generateNotTogetherMessage(options: string[]): string { 15 | return `These options cannot be used together: ${options.join(', ')}`; 16 | } 17 | 18 | export function getValueFromFrontmatter(filePath: string, key: string, defaultValue: T): T { 19 | try { 20 | const fileData = readFileSync(filePath, 'utf-8'); 21 | const { data } = matter(fileData); 22 | 23 | // Try for using gray-matter 24 | if (data?.[key]) { 25 | return data[key]; 26 | } 27 | 28 | // Try manual parsing 29 | const lines = fileData.split('\n'); 30 | let frontmatterStart = false; 31 | 32 | for (let i = 0, len = lines.length; i < len; i += 1) { 33 | const str = lines[i].toString().replace('\r', ''); 34 | 35 | if (/^---$/.test(str)) { 36 | frontmatterStart = true; 37 | } 38 | if (new RegExp(`^${key}: (.*)`).test(str) && frontmatterStart) { 39 | return JSON.parse(str.replace(`${key}: `, '')) as T; 40 | } 41 | } 42 | } catch { 43 | return defaultValue; 44 | } 45 | return defaultValue; 46 | } 47 | 48 | export function getOrderFromFrontmatter(filePath: string, defaultOrder: number): number { 49 | return parseFloat(getValueFromFrontmatter(filePath, 'order', defaultOrder.toString())); 50 | } 51 | 52 | export function getDateFromFrontmatter(filePath: string): string { 53 | return getValueFromFrontmatter(filePath, 'date', '0001-01-01'); 54 | } 55 | 56 | export function getExcludeFromFrontmatter( 57 | filePath: string, 58 | excludeFrontmatterFieldName?: string 59 | ): boolean { 60 | if (!excludeFrontmatterFieldName) { 61 | return false; 62 | } 63 | 64 | return getValueFromFrontmatter(filePath, excludeFrontmatterFieldName, false); 65 | } 66 | 67 | export function formatTitle( 68 | options: VitePressSidebarOptions, 69 | title: string, 70 | fromTitleHeading = false 71 | ): string { 72 | const htmlTags: string[] = []; 73 | const h1Headers: string[] = []; 74 | const htmlPlaceholder = '\u0001'; 75 | const h1Placeholder = '\u0002'; 76 | let text = title; 77 | 78 | // Replace HTML tags and Markdown h1 headers with placeholders 79 | text = text.replace(/<[^>]*>/g, (match) => { 80 | htmlTags.push(match); 81 | return htmlPlaceholder; 82 | }); 83 | text = text.replace(/^(#+.*)$/gm, (match) => { 84 | h1Headers.push(match); 85 | return h1Placeholder; 86 | }); 87 | 88 | // Remove certain Markdown format 89 | if (fromTitleHeading && !options.keepMarkdownSyntaxFromTitle) { 90 | text = text.replace(/\*{1,2}([^*]+?)\*{1,2}/g, '$1'); 91 | text = text.replace(/_{1,2}([^_]+?)_{1,2}/g, '$1'); 92 | text = text.replace(/~{1,2}([^~]+?)~{1,2}/g, '$1'); 93 | text = text.replace(/`{1,3}([^`]+?)`{1,3}/g, '$1'); 94 | } 95 | 96 | // Replace text [START] 97 | if (options.hyphenToSpace) { 98 | text = text.replace(/-/g, ' '); 99 | } 100 | if (options.underscoreToSpace) { 101 | text = text.replace(/_/g, ' '); 102 | } 103 | if (options.capitalizeEachWords) { 104 | let lastChar = ''; 105 | 106 | for (let i = 0; i < text.length; i += 1) { 107 | if ((i === 0 || !/[a-zA-Z0-9]/.test(lastChar)) && /[a-z]/.test(text[i])) { 108 | text = text.slice(0, i) + text[i].toUpperCase() + text.slice(i + 1); 109 | } 110 | 111 | lastChar = text[i]; 112 | } 113 | } else if (options.capitalizeFirst) { 114 | text = capitalizeFirst(text); 115 | } 116 | // Replace text [END] 117 | 118 | // Restore Markdown headers and HTML tags 119 | let h1Index = -1; 120 | let htmlIndex = -1; 121 | text = text.replace(new RegExp(h1Placeholder, 'g'), () => { 122 | h1Index += 1; 123 | return h1Headers[h1Index]; 124 | }); 125 | text = text.replace(new RegExp(htmlPlaceholder, 'g'), () => { 126 | htmlIndex += 1; 127 | return htmlTags[htmlIndex]; 128 | }); 129 | 130 | return text; 131 | } 132 | 133 | export function getTitleFromMd( 134 | fileName: string, 135 | filePath: string, 136 | options: VitePressSidebarOptions, 137 | isDirectory: boolean, 138 | callbackTitleReceived?: () => void 139 | ): string { 140 | if (isDirectory) { 141 | return formatTitle(options, fileName); 142 | } 143 | 144 | if (options.useTitleFromFrontmatter) { 145 | // Use content frontmatter title value instead of file name 146 | let value = getValueFromFrontmatter( 147 | filePath, 148 | options.frontmatterTitleFieldName || 'title', 149 | undefined 150 | ); 151 | // Try to use title front-matter as fallback 152 | if (!value) { 153 | value = getValueFromFrontmatter(filePath, 'title', undefined); 154 | } 155 | if (value) { 156 | callbackTitleReceived?.(); 157 | return formatTitle(options, value); 158 | } 159 | } 160 | 161 | if (options.useTitleFromFileHeading) { 162 | // Use content 'h1' string instead of file name 163 | try { 164 | const data = readFileSync(filePath, 'utf-8'); 165 | const lines = data.split('\n'); 166 | 167 | for (let i = 0, len = lines.length; i < len; i += 1) { 168 | let str = lines[i].toString().replace('\r', ''); 169 | 170 | if (/^# /.test(str)) { 171 | str = str.replace(/^# /, ''); 172 | 173 | if (/\[(.*)]\(.*\)/.test(str)) { 174 | // Remove hyperlink from h1 if exists 175 | const execValue = /(.*)?\[(.*)]\((.*)\)(.*)?/.exec(str) || ''; 176 | 177 | str = 178 | execValue.length > 0 179 | ? `${execValue[1] || ''}${execValue[2] || ''}${execValue[4] || ''}` 180 | : ''; 181 | } 182 | 183 | callbackTitleReceived?.(); 184 | return formatTitle(options, str, true); 185 | } 186 | } 187 | } catch { 188 | return 'Unknown'; 189 | } 190 | } 191 | 192 | return formatTitle(options, fileName.replace(/\.md$/, '')); 193 | } 194 | 195 | export function sortByFileTypes( 196 | arrItems: SidebarListItem, 197 | sortFolderTo: 'top' | 'bottom' 198 | ): object[] { 199 | for (let i = 0; i < arrItems.length; i += 1) { 200 | if (arrItems[i].items && arrItems[i].items.length) { 201 | arrItems[i].items = sortByFileTypes(arrItems[i].items, sortFolderTo); 202 | } 203 | } 204 | 205 | const itemFolders = arrItems.filter((item: SidebarItem) => Object.hasOwn(item, 'items')); 206 | const itemFiles = arrItems.filter((item: SidebarItem) => !Object.hasOwn(item, 'items')); 207 | 208 | if (sortFolderTo === 'top') { 209 | return [...itemFolders, ...itemFiles]; 210 | } 211 | 212 | return [...itemFiles, ...itemFolders]; 213 | } 214 | 215 | export function sortByObjectKey(options: SortByObjectKeyOptions): object[] { 216 | for (let i = 0; i < options.arr.length; i += 1) { 217 | if (options.arr[i].items && options.arr[i].items.length) { 218 | options.arr[i].items = sortByObjectKey({ 219 | ...options, 220 | arr: options.arr[i].items 221 | }); 222 | } 223 | } 224 | 225 | const basicCollator = new Intl.Collator([], { 226 | numeric: options.numerically, 227 | sensitivity: 'base' 228 | }); 229 | let result; 230 | 231 | if (options.dateSortFromFrontmatter) { 232 | result = options.arr.sort( 233 | (a: SidebarListItem, b: SidebarListItem) => 234 | new Date(a[options.key]).valueOf() - new Date(b[options.key]).valueOf() 235 | ); 236 | 237 | if (options.desc) { 238 | result = result.reverse(); 239 | } 240 | } else if (options.dateSortFromTextWithPrefix) { 241 | const dateRegex = /^[0-9]{4}-[0-9]{2}-[0-9]{2}/g; 242 | 243 | result = options.arr.sort((a: SidebarListItem, b: SidebarListItem) => { 244 | const aDate = a[options.key].split(dateRegex)?.[0]; 245 | const bDate = b[options.key].split(dateRegex)?.[0]; 246 | 247 | return new Date(aDate).valueOf() - new Date(bDate).valueOf(); 248 | }); 249 | 250 | if (options.desc) { 251 | result = result.reverse(); 252 | } 253 | } else { 254 | result = options.arr.sort((a: SidebarListItem, b: SidebarListItem) => { 255 | const compareResult = basicCollator.compare(a[options.key], b[options.key]); 256 | 257 | return options.desc ? -compareResult : compareResult; 258 | }); 259 | } 260 | 261 | return result; 262 | } 263 | 264 | export function deepDeleteKey(obj: SidebarListItem, key: string): void { 265 | if (typeof obj !== 'object' || obj === null) { 266 | return; 267 | } 268 | 269 | if (Object.hasOwn(obj, key)) { 270 | delete obj[key]; 271 | } 272 | 273 | Object.keys(obj).forEach((item) => { 274 | if (typeof obj[item] === 'object') { 275 | deepDeleteKey(obj[item], key); 276 | } 277 | }); 278 | } 279 | 280 | export function removePrefixFromTitleAndLink( 281 | sidebarList: SidebarListItem, 282 | options: VitePressSidebarOptions 283 | ): SidebarListItem { 284 | const sidebarListLength = sidebarList.length; 285 | 286 | for (let i = 0; i < sidebarListLength; i += 1) { 287 | const obj = sidebarList[i]; 288 | 289 | for (let j = 0; j < Object.keys(obj).length; j += 1) { 290 | const key = Object.keys(obj)[j]; 291 | 292 | if (key === 'text') { 293 | if ( 294 | !( 295 | !(options.prefixSeparator instanceof RegExp) && 296 | obj[key].indexOf(options.prefixSeparator) === -1 297 | ) 298 | ) { 299 | const splitItem = obj[key].split(options.prefixSeparator); 300 | 301 | splitItem.shift(); 302 | 303 | obj[key] = splitItem.join(options.prefixSeparator); 304 | } 305 | } else if (key === 'items') { 306 | obj[key] = removePrefixFromTitleAndLink(obj[key], options); 307 | } 308 | } 309 | } 310 | 311 | return sidebarList; 312 | } 313 | 314 | export function debugPrint(optionItems?: AnyValueObject, sidebarResult?: AnyValueObject): void { 315 | process.stdout.write( 316 | `\n${'='.repeat(50)}\n${JSON.stringify(optionItems, null, 2)}\n${'-'.repeat( 317 | 50 318 | )}\n${JSON.stringify(sidebarResult, null, 2)}\n${'='.repeat(50)}\n\n` 319 | ); 320 | } 321 | -------------------------------------------------------------------------------- /docs/zhHans/guide/options.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 2 3 | --- 4 | 5 | # 侧边栏选项 6 | 7 | 本页介绍 VitePress 侧边栏的所有选项。 8 | 9 | ## @ 快速搜索 10 | 11 | | 解决路径问题 | 分组 | 12 | | ------------------------------------- | ----------------------------------------- | 13 | | [documentRootPath](#documentrootpath) | [collapsed](#collapsed) | 14 | | [scanStartPath](#scanstartpath) | [collapseDepth](#collapsedepth) | 15 | | [resolvePath](#resolvepath) | [rootGroupText](#rootgrouptext) | 16 | | [basePath](#basepath) | [rootGroupLink](#rootgrouplink) | 17 | | [followSymlinks](#followsymlinks) | [rootGroupCollapsed](#rootgroupcollapsed) | 18 | 19 | | 获取菜单标题 | 获取菜单链接 | 20 | | --- | --- | 21 | | [useTitleFromFileHeading](#usetitlefromfileheading) | [useFolderLinkFromSameNameSubFile](#usefolderlinkfromsamenamesubfile) | 22 | | [useTitleFromFrontmatter](#usetitlefromfrontmatter) | [folderLinkNotIncludesFileName](#folderlinknotincludesfilename) | 23 | | [useFolderTitleFromIndexFile](#usefoldertitlefromindexfile) | [useFolderLinkFromIndexFile](#usefolderlinkfromindexfile) | 24 | | [frontmatterTitleFieldName](#frontmattertitlefieldname) | | 25 | 26 | | 包括/排除 | 菜单标题样式 | 27 | | --- | --- | 28 | | [excludeByGlobPattern](#excludebyglobpattern) | [hyphenToSpace](#hyphentospace) | 29 | | [excludeFilesByFrontmatterFieldName](#excludefilesbyfrontmatterfieldname) | [underscoreToSpace](#underscoretospace) | 30 | | [excludeByFolderDepth](#excludebyfolderdepth) | [capitalizeFirst](#capitalizefirst) | 31 | | [includeDotFiles](#includedotfiles) | [capitalizeEachWords](#capitalizeeachwords) | 32 | | [includeEmptyFolder](#sortmenusbyfrontmatterdate) | [keepMarkdownSyntaxFromTitle](#keepmarkdownsyntaxfromtitle) | 33 | | [includeRootIndexFile](#sortmenusbyfrontmatterdate) | [removePrefixAfterOrdering](#removeprefixafterordering) | 34 | | [includeFolderIndexFile](#sortmenusbyfrontmatterdate) | [prefixSeparator](#prefixseparator) | 35 | 36 | | 分类 | 杂项 | 37 | | --- | --- | 38 | | [manualSortFileNameByPriority](#manualsortfilenamebypriority) | [debugPrint](#debugprint) | 39 | | [sortFolderTo](#sortfolderto) | | 40 | | [sortMenusByName](#sortmenusbyname) | | 41 | | [sortMenusByFileDatePrefix](#sortmenusbyfiledateprefix) | | 42 | | [sortMenusByFrontmatterOrder](#sortmenusbyfrontmatterorder) | | 43 | | [frontmatterOrderDefaultValue](#frontmatterorderdefaultvalue) | | 44 | | [sortMenusByFrontmatterDate](#sortmenusbyfrontmatterdate) | | 45 | | [sortMenusOrderByDescending](#sortmenusorderbydescending) | | 46 | | [sortMenusOrderNumericallyFromTitle](#sortmenusordernumericallyfromtitle) | | 47 | | [sortMenusOrderNumericallyFromLink](#sortmenusordernumericallyfromlink) | | 48 | 49 | ## `documentRootPath` 50 | 51 | - Type: `string` 52 | - Default: `'/'` 53 | 54 | 文档文件所在的顶级路径。默认值为 `/`。 55 | 56 | 这是 `.vitepress`目录所在的路径,如果项目根目录中文档所在的文件夹是 `/docs`,则该选项的值应设为 `docs` 或 `/docs`。 57 | 58 | ```text 59 | / 60 | ├─ package.json 61 | ├─ src/ 62 | ├─ docs/ <--------------- `documentRootPath` ('/docs') 63 | │ ├─ .vitepress/ <------ VitePress 配置目录 64 | │ ├─ another-directory/ 65 | │ ├─ hello.md 66 | │ └─ index.md 67 | └─ ... 68 | ``` 69 | 70 | ## `scanStartPath` 71 | 72 | - Type: `string|null` 73 | - Default: `null` 74 | 75 | 此选项用于配置多个侧边栏。您可以在 **[多个侧边栏](/zhHans/advanced-usage/multiple-sidebars-how-to)** 页面上了解更多信息。 76 | 77 | 用于扫描文档列表的根目录路径。在`documentRootPath`中设置的路径中的文件,在`scanStartPath`中设置的路径之外,不会被扫描。如果您指定了`scanStartPath`,建议您也设置`documentRootPath`,因为`documentRootPath`中设置的父路径应该出现在`link`中。 78 | 79 | 例如,如果根路径是`/docs`,要扫描的文件是`/docs/sub-dir/scan-me`,则设置如下: 80 | 81 | - `documentRootPath`: `/docs`, 82 | - `scanStartPath`: `sub-dir/scan-me` (请勿包含 `documentRootPath` 的路径。) 83 | 84 | ## `resolvePath` 85 | 86 | - Type: `string|null` 87 | - Default: `null` 88 | 89 | 此选项用于配置多个侧边栏。您可以在 **[多个侧边栏](/zhHans/advanced-usage/multiple-sidebars-how-to)** 页面上了解更多信息。 90 | 91 | 输入路径,为每个路径显示不同的侧边栏。路径前必须包含`/`。没有此值的选项将设置为根路径(`/`)。 92 | 93 | 例如: `/`, `/path/sub-path`, `/guide/`... 94 | 95 | ## `basePath` 96 | 97 | - Type: `string|null` 98 | - Default: `null` 99 | 100 | 此选项用于配置多个侧边栏。您可以在 **[多个侧边栏](/zhHans/advanced-usage/multiple-sidebars-how-to)** 页面上了解更多信息。 101 | 102 | 如果路径因VitePress的重写选项而改变,则可以使用此选项。它替换VitePress中的基本路径。如果此值不存在,则将使用来自`resolvePath`的值。 103 | 104 | ## `followSymLinks` 105 | 106 | - Type: `boolean` 107 | - Default: `false` 108 | 109 | 如果该值为 `true`,则在扫描目录时,会包含任何已设置符号链接的目录或文件,并将其添加到菜单中。如果符号链接配置不正确或链接设置复杂,请务必小心,因为这可能导致性能低下或无限扫描。 110 | 111 | ## `useTitleFromFileHeading` 112 | 113 | - Type: `boolean` 114 | - Default: `false` 115 | 116 | 如果值为 `true`,则显示带有 `.md` 文件中 `h1` 标题内容的标题。如果文件中不存在 `h1` 标题,则显示 `Unknown`。 117 | 118 | 默认菜单项按文件夹树顺序排序,因此如果您想按更改后的菜单名称重新排序,请将`sortMenusByName`选项设置为`true`。 119 | 120 | ## `useTitleFromFrontmatter` 121 | 122 | - Type: `boolean` 123 | - Default: `false` 124 | 125 | 如果值为`true`,则根据文件`Frontmatter`中`title`的值显示标题。如果无法解析该值,则如果`useTitleFromFileHeading`选项为`true`,则从`h1`标签中获取该值,如果失败,则从文件名中获取该值。 126 | 127 | `Frontmatter`应位于文档顶部,并应如下所示(在 `title:` 值和标题之间需要留出空格)。 128 | 129 | ```markdown 130 | --- 131 | title: Hello World 132 | --- 133 | ``` 134 | 135 | ## `frontmatterTitleFieldName` 136 | 137 | - Type: `string` 138 | - Default: `title` 139 | 140 | 根据文件中指定的Frontmatter中的键名显示菜单标题。如果指定的值在Frontmatter中不存在,将使用默认的`title`作为后备。 141 | 142 | ```markdown 143 | --- 144 | name: This is frontmatter title value. 145 | --- 146 | ``` 147 | 148 | 欲了解更多信息,请参阅以下文章: https://vitepress.dev/guide/frontmatter 149 | 150 | 默认菜单项是按文件夹树顺序排序的,因此如果想按更改后的菜单名称重新排序,请将 `sortMenusByName` 选项设置为 `true`。 151 | 152 | ## `useFolderTitleFromIndexFile` 153 | 154 | - Type: `boolean` 155 | - Default: `false` 156 | 157 | 如果该值为 `true`,则使用当前文件夹的 `index.md` 文件中的信息来获取菜单名称。如果不存在 `index.md` 文件,则使用文件夹名称。由于我们通常从 `index.md` 文件中获取 `index` 名称,因此建议同时使用 `useTitleFromFileHeading` 或 `useTitleFromFrontmatter` 选项,从该文件的 Markdown 标题或 Frontmatter 中获取标题。 158 | 159 | 侧边栏菜单会隐藏 `index.md` 文件,但如果 `includeFolderIndexFile` 选项为 `true`,索引文件就会显示在菜单中。 160 | 161 | ## `useFolderLinkFromIndexFile` 162 | 163 | - Type: `boolean` 164 | - Default: `false` 165 | 166 | 如果此值为 `true`,将指定一个指向文件夹的链接,以便您可以导航到当前文件夹中的 `index.md` 文件。如果 `index.md` 文件不存在,则不会创建链接。 167 | 168 | 侧边栏菜单会隐藏 `index.md` 文件,但如果 `includeFolderIndexFile` 选项为 `true`,则可在菜单中显示索引文件。 169 | 170 | ## `manualSortFileNameByPriority` 171 | 172 | - Type: `Array` 173 | - Default: `[]` 174 | 175 | 按文件名(包括扩展名)数组的顺序排序。如果数组中没有与文件名匹配的值,排序优先级将被退回。这适用于文件和目录,同样的排列规则也适用于子目录。 176 | 177 | ## `sortFolderTo` 178 | 179 | - Type: `undefined | 'top' | 'bottom'` 180 | - Default: `undefined` 181 | 182 | 完成所有排序后,文件夹和文件将被分批放置。如果值为 `top`,则所有文件夹都放在文件上方;如果为 `bottom`,则放在文件下方。子文件夹中的项目也会一起排序。 183 | 184 | ## `sortMenusByName` 185 | 186 | - Type: `boolean` 187 | - Default: `false` 188 | 189 | 按名称对菜单项中的项目进行排序。通常情况下,文件夹扫描是按名称升序排序的,因此,如果不应用此选项,则应用默认排序,但如果使用`useTitleFromFileHeading`或`useTitleFromFrontmatter`选项,则可能需要按名称重新排序,因为菜单名称已更改。此选项强制按名称排序,即使菜单名称已更改也是如此。 190 | 191 | ## `sortMenusByFileDatePrefix` 192 | 193 | - Type: `boolean` 194 | - Default: `false` 195 | 196 | 如果值为 `true`,则按菜单项名称中的日期前缀排序。日期格式必须是 `YYYY-MM-DD` 格式(例如 `2024-01-01-menu-name`, `2024-01-02.menu-name`...) 197 | 198 | 要删除菜单文本中残留的日期前缀,可以使用 `prefixSeparator` 和 `removePrefixAfterOrdering` 选项。 199 | 200 | 默认菜单项是按文件夹树顺序排序的,因此如果想按更改后的菜单名称重新排序,请将 `sortMenusByName` 选项设置为 `true`。 201 | 202 | ## `sortMenusByFrontmatterOrder` 203 | 204 | - Type: `boolean` 205 | - Default: `false` 206 | 207 | 按 frontmatter 的 `order` 属性对菜单项排序。对于每个文件夹,按 `order` 属性的值(数字)升序排序,如果 `sortMenusOrderByDescending` 选项为 `true`,则按降序排序。如果 `order` 属性的值不是数字或不存在,则 `order` 会被判定为 `0`。 208 | 209 | ## `sortMenusByFrontmatterDate` 210 | 211 | - Type: `boolean` 212 | - Default: `false` 213 | 214 | 根据前端的`date`属性对菜单项进行排序。它还会按日期升序(如果`sortMenusOrderByDescending`为`true`,则按日期降序)对`date`属性值进行排序。日期格式必须符合`YYYY-MM-DD`或JavaScript Date数据类型。 215 | 216 | ## `sortMenusOrderByDescending` 217 | 218 | - Type: `boolean` 219 | - Default: `false` 220 | 221 | 如果此值为 `true`,则按降序排列菜单项中的项目。只有当 `sortMenusByName` 或 `sortMenusByFrontmatterOrder` 为 `true`时,才会启用此选项。 222 | 223 | ## `sortMenusOrderNumericallyFromTitle` 224 | 225 | - Type: `boolean` 226 | - Default: `false` 227 | 228 | 如果该值为`true`,则如果菜单名称以数字开头,则按数字而不是名称排序。例如,如果您有名为`1-a`、`10-a`和`2-a`的文件,则常规排序将按名称排序,即`['1-a', '10-a', '2-a']`。这会导致菜单以非预期的顺序显示,因为`10-a`优先于`2-a`。 229 | 230 | 使用此选项,它们按以下顺序排序:`['1-a', '2-a', '10-a']` 231 | 232 | 如果您希望按降序排序,则应与`sortMenusOrderByDescending`选项一起使用。 233 | 234 | ## `sortMenusOrderNumericallyFromLink` 235 | 236 | - Type: `boolean` 237 | - Default: `false` 238 | 239 | 如果此值为`true`,则如果菜单名称以数字开头,则按数字而不是名称排序。此选项与`sortMenusOrderNumericallyFromTitle`相同,但按链接而不是文件标题排序。因此,它不能与`sortMenusOrderNumericallyFromTitle`选项一起使用。 240 | 241 | 如果您希望按降序排序,则应与`sortMenusOrderByDescending`选项一起使用。 242 | 243 | ## `frontmatterOrderDefaultValue` 244 | 245 | - Type: `number` 246 | - Default: `0` 247 | 248 | 设置 frontmatter 的 `order` 属性未设置时的默认值。该选项仅在 `sortMenusByFrontmatterOrder` 为 `true` 时启用。 249 | 250 | ## `collapsed` 251 | 252 | - Type: `boolean` 253 | - Default: `false` 254 | 255 | 如果未指定`collapsed`选项(`null` 或 `undefined`),则不使用分组折叠/展开,所有菜单将一次性显示。如果为`false`,则创建菜单时所有分组都处于展开状态。如果为`true`,则创建菜单时所有分组都处于折叠状态。 256 | 257 | (即使值为`true`,如果菜单位于折叠组中的文档中,也可能被展开。) 258 | 259 | ![Collapsed Example](/doc-collapsed-example.png) 260 | 261 | ## `collapseDepth` 262 | 263 | - Type: `number` 264 | - Default: `1` 265 | 266 | 在指定的深度,菜单组会折叠。指定该选项后,组的折叠/展开将自动启用。顶层文件夹的深度为 `1`。 267 | 268 | ## `hyphenToSpace` 269 | 270 | - Type: `boolean` 271 | - Default: `false` 272 | 273 | 如果值为 `true`,文件名中的`-`符号将转换为空格并显示为标题。通过 Markdown 标题或 frontmatter 导入菜单名称时,该选项也会受到影响。 274 | 275 | ## `underscoreToSpace` 276 | 277 | - Type: `boolean` 278 | - Default: `false` 279 | 280 | 如果值为 `true`,文件名中的`_`符号将转换为空格并显示为标题。通过 Markdown 标题或 frontmatter 导入菜单名称时,该选项也会受到影响。 281 | 282 | ## `capitalizeFirst` 283 | 284 | - Type: `boolean` 285 | - Default: `false` 286 | 287 | 如果值为 `true`,菜单名称的第一个字母将强制为大写。当菜单名称通过 Markdown 标题或 frontmatter 导入时,该选项也会受到影响。 288 | 289 | ## `capitalizeEachWords` 290 | 291 | - Type: `boolean` 292 | - Default: `false` 293 | 294 | 如果值为 `true`,将大写由特殊字符分隔的单词的所有首字母。当菜单名称通过 markdown 标头或 Frontmatter 导入时,该选项也会受到影响。 295 | 296 | 例如,`abc def ghi`和`abc-def ghi`将分别变为`Abc Def Ghi`和`Abc-Def Ghi`。 297 | 298 | ## `excludeByGlobPattern` 299 | 300 | - Type: `Array` 301 | - Default: `[]` 302 | 303 | [glob]() 根据文件模式字符串数组排除文件或文件夹。 304 | 305 | 例如,该值可能如下所示`['abc/', 'def.md', 'ghi/file-**']`这将分别排除所有路径中的`abc`目录和子目录、`def.md`文件以及`ghi`路径中以`file-`开头的文件,这些文件和文件夹将被排除在菜单之外。 306 | 307 | ## `excludeFilesByFrontmatterFieldName` 308 | 309 | - Type: `string|null` 310 | - Default: `null` 311 | 312 | 指定前缀字段名称为`true`的文档将从菜单中排除。 313 | 314 | 如果未指定选项或选项值未定义,则忽略该选项。 315 | 316 | 例如,如果选项值为`exclude`,则菜单中不会显示内容包含`exclude: true`的文档。 317 | 318 | ```markdown 319 | --- 320 | title: This article is excluded. 321 | exclude: true 322 | --- 323 | 324 | # Article 325 | 326 | Content 327 | ``` 328 | 329 | 根据选项的值,您可以使用其他名称,如`draft`、`hide`等,来代替`exclude`。 330 | 331 | ## `excludeByFolderDepth` 332 | 333 | - Type: `number|null` 334 | - Default: `null` 335 | 336 | 扫描文件夹时,当达到指定的深度数时,不再扫描子文件夹和文件,也不会在菜单中显示。最上层为 `1`。 337 | 338 | 例如,在下面的结构中,如果选项值为 `3`,则菜单将从第三个深度开始被抑制。 339 | 340 | ```text 341 | root/ <---------- depth: 1 / scan: yes 342 | ├─ aaa1/ <---------- depth: 1 343 | │ ├─ bbb/ <---------- depth: 2 344 | │ │ ├─ b1.md <---------- depth: 3 / scan: no 345 | │ │ ├─ ccc/ <---------- depth: 3 346 | │ │ │ └─ c1.md <---------- depth: 4 / scan: no 347 | │ │ └─ b1.md <---------- depth: 3 / scan: no 348 | │ └─ a1.md <---------- depth: 2 / scan: yes 349 | └─ aaa2/ <---------- depth: 1 350 | └─ aaa1.md <---------- depth: 2 / scan: yes 351 | ``` 352 | 353 | ## `includeDotFiles` 354 | 355 | - Type: `boolean` 356 | - Default: `false` 357 | 358 | 通常情况下,如果文件和文件夹名称前有句点(`.`),它们会被视为隐藏文件,不会在列表中显示。但是,如果此选项为`true`,则强制在列表中显示所有隐藏文件和文件夹。 359 | 360 | ## `includeEmptyFolder` 361 | 362 | - Type: `boolean` 363 | - Default: `false` 364 | 365 | 如果值为`true`,则还会显示不存在md文件的目录。 366 | 367 | ## `includeRootIndexFile` 368 | 369 | - Type: `boolean` 370 | - Default: `false` 371 | 372 | 如果值为`true`,则还要在侧边栏菜单中包含顶级路径`index.md`文件。使用`includeFolderIndexFile`选项还可以包含子项目的索引文件。(如果文件不存在,则忽略它。) 373 | 374 | ## `includeFolderIndexFile` 375 | 376 | - Type: `boolean` 377 | - Default: `false` 378 | 379 | 如果值为`true`,则还要在侧边栏菜单中包含文件夹路径`index.md`文件。使用`includeRootIndexFile`选项还可以包含根项目的索引文件。(如果文件不存在,则忽略它。) 380 | 381 | ## `removePrefixAfterOrdering` 382 | 383 | - Type: `boolean` 384 | - Default: `false` 385 | 386 | 从所有操作完成后显示的菜单项的每个菜单标题中删除特定的前缀。如果您想按文件名中的数字排序,而不使用前缀的排序,并且不希望该数字在菜单中显示,这是理想的选择。 387 | 388 | 例如,如果默认使用前缀分隔符(`.`),则以下菜单将重命名为 389 | 390 | - 文件名:`1.hello` -> 菜单名:`hello` 391 | - 文件名:`1.1.hello` -> 菜单名:`1.hello` 392 | - 文件名:`1-1.hello` -> 菜单名:`hello` 393 | 394 | 根据分隔符仅删除一次字母,因此子项(如`1.1.`)应使用`1-1`.。或者,您可以在前缀分隔符值上设置正则表达式来绕过它。 395 | 396 | 可与`prefixSeparator`选项一起使用。更多信息请参阅该选项的描述。 397 | 398 | (注A:前缀仅影响标题,链接将使用文件链接的原始形式)。 399 | 400 | (备注B:如果您使用`useTitleFromFileHeading`或`useTitleFromFrontmatter`选项,则忽略此选项)。 401 | 402 | ## `prefixSeparator` 403 | 404 | - Type: `string|RegExp` 405 | - Default: `'.'` 406 | 407 | 此选项只能与 `removePrefixAfterOrdering` 选项结合使用以删除前缀。 408 | 409 | 从提取的菜单文本中删除指定数量字符(至少一个)的第一部分。例如,如果菜单名称为 `1. Text`,并且您将 `prefixSeparator` 值设置为 `. `,则结果将仅为 `Text`。 410 | 411 | 您还可以使用正则表达式。与正则表达式匹配的值将被删除。例如,要删除 `2024-01-01-hello` 中字符串之前的日期,请将 `prefixSeparator` 值指定为 `/[0-9]{4}-[0-9]{2}-[0-9]{2}-/g`。结果为 `hello`。 412 | 413 | ## `rootGroupText` 414 | 415 | - Type: `string` 416 | - Default: `'Table of Contents'` 417 | 418 | rootGroup 指定整个菜单组,而与目录结构无关。这将使用一个菜单步骤,因此您在使用时应格外小心。如果您不需要 rootGroup 选项,可以将其禁用。如果指定此值,则指定顶级菜单的名称。 419 | 420 | ## `rootGroupLink` 421 | 422 | - Type: `string` 423 | - Default: `null` 424 | 425 | 有关 rootGroup 的更多信息,请参阅 `rootGroupText` 选项说明。指定此值可指定指向 rootGroup 的链接。如果值为空,则不添加链接。 426 | 427 | ## `rootGroupCollapsed` 428 | 429 | - Type: `boolean` 430 | - Default: `null` 431 | 432 | 有关 rootGroup 的更多信息,请参阅 `rootGroupText` 选项说明。`rootGroupCollapsed`选项设置是否展开根组的子项。如果指定的默认值为 `null` 或 `undefined`,则不显示展开/折叠按钮。如果该值为 `true`,子项将以折叠方式显示;如果为 `false`,子项将以展开方式显示。 433 | 434 | 此选项仅适用于顶层项目。有关一般项目的折叠性,请参阅 `collapsed` 选项。 435 | 436 | ## `useFolderLinkFromSameNameSubFile` 437 | 438 | - Type: `boolean` 439 | - Default: `false` 440 | 441 | 如果此值为`true`,则当存在与文件夹同名的子文件时,将在文件夹中创建一个链接,用于导航至该文件,而该文件不会显示在子项中。 442 | 443 | 例如,如果您有一个文件夹,如下所示: 444 | 445 | ``` 446 | docs/ 447 | ├─ guide/ 448 | │ ├─ api/ 449 | │ │ └─ api.md 450 | │ ├─ one.md 451 | │ └─ two.md 452 | └─ config/ 453 | └─ index.md 454 | ``` 455 | 456 | 在 `api` 文件夹中添加了一个链接,而 `api` 文件夹中的 `api` 页面不包含在菜单列表中。点击文件夹中的链接会显示 `api/api.md`中的文件。 457 | 458 | ## `folderLinkNotIncludesFileName` 459 | 460 | - Type: `boolean` 461 | - Default: `false` 462 | 463 | 此选项仅在特殊情况下使用:当您有 [rewrite](https://vitepress.dev/guide/routing#route-rewrites) 规则并且存在具有相同文件夹名称的子文件时,请将其与 `useFolderLinkFromSameNameSubFile` 选项并行使用。 464 | 465 | **注意:** 如果您在未配置 VitePress rewrites 的情况下启用此选项,点击文件夹链接将导致 404 错误。请确保在您的 VitePress 配置中设置相应的 rewrite 规则。 466 | 467 | 如果此值为 `true`,则在建立文件夹链接时,忽略子项的存在,并仅将链接指定为文件夹路径。 468 | 469 | 例如,如果您有一个如下所示的文件夹: 470 | 471 | ``` 472 | docs/ 473 | ├─ guide/ 474 | │ ├─ api/ 475 | │ │ └─ api.md 476 | │ ├─ one.md 477 | │ └─ two.md 478 | └─ config/ 479 | └─ index.md 480 | ``` 481 | 482 | 使用 `useFolderLinkFromSameNameSubFile` 选项,单击 guide/api 文件夹菜单将带您进入 `guide/api/api`,但如果您使用 `folderLinkNotIncludesFileName` 选项,则链接将为 `guide/api/`。 483 | 484 | 要使此功能正常工作,您需要配置 VitePress rewrites 将文件夹路径映射到实际文件。在您的 `.vitepress/config.ts` 中添加以下内容: 485 | 486 | ```typescript 487 | export default defineConfig({ 488 | rewrites: { 489 | 'guide/api/api.md': 'guide/api/index.md' 490 | } 491 | }); 492 | ``` 493 | 494 | 或者使用动态 rewrite 函数来处理多个文件夹: 495 | 496 | ```typescript 497 | export default defineConfig({ 498 | rewrites(id) { 499 | // 将 'folder/folder.md' 重写为 'folder/index.md' 500 | return id.replace(/([^/]+)\/\1\.md$/, '$1/index.md'); 501 | } 502 | }); 503 | ``` 504 | 505 | ## `keepMarkdownSyntaxFromTitle` 506 | 507 | - Type: `boolean` 508 | - Default: `false` 509 | 510 | 如果此值为 `true`,则保留标题文本中包含的 Markdown 语法,而不删除它。通常会保留任何高亮或内联代码。无论是否使用此选项,超链接文本都会被移除。 511 | 512 | ## `debugPrint` 513 | 514 | - Type: `boolean` 515 | - Default: `false` 516 | 517 | 如果该值为`true`,则会将执行后创建的对象打印到控制台日志中。如果您配置了多个侧边栏,即使只包含其中一个选项,它也会输出所有侧边栏的结果。 518 | --------------------------------------------------------------------------------