├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __test__ ├── docs │ ├── .vitepress │ │ └── config.js │ ├── 01.Introduction │ │ └── START.md │ ├── 02.Utils │ │ ├── dateUtil.md │ │ └── storeUtil.md │ └── index.md └── index.spec.js ├── babel.config.js ├── jest.config.mjs ├── lib ├── index.js ├── index.min.js └── index.mjs ├── package-lock.json ├── package.json ├── rollup.config.mjs ├── src └── index.ts ├── tsconfig.json └── types └── index.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{package.json,.*rc,*.yml,*.md,*.js,*.ts}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/* 2 | node_modules 3 | test/* 4 | coverage/* 5 | lib/* -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | plugins: [ 5 | '@typescript-eslint', 6 | ], 7 | extends: [ 8 | 'eslint-config-tencent', 9 | 'plugin:@typescript-eslint/eslint-recommended', 10 | 'plugin:@typescript-eslint/recommended', 11 | ], 12 | env: { 13 | node: true, 14 | jest: true, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /build 5 | /coverage 6 | 7 | # local env files 8 | .env.local 9 | .env.*.local 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw* 24 | 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.0.7 (2022-12-2) 2 | 3 | ## Features 4 | 5 | Support ignore Directory path from being captured 6 | 7 | # 1.0.5 (2022-11-23) 8 | 9 | ## Features 10 | 11 | Support ignore File path from being captured 12 | 13 | 14 | # 1.0.2 (2022-11-23) 15 | 16 | ## Features 17 | 18 | `getSideBar` Generator sidebar for [Vitepress](https://github.com/vuejs/vitepress) based on file and directory structure. 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Luciozhang 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Generator sidebar for [Vitepress](https://github.com/vuejs/vitepress) based on file and directory structure. 2 | 3 | [![NPM version](https://img.shields.io/npm/v/vitepress-plugin-autobar.svg)](https://www.npmjs.com/package/vitepress-plugin-autobar) [![NPM downloads](https://img.shields.io/npm/dm/vitepress-plugin-autobar.svg)](https://www.npmjs.com/package/vitepress-plugin-autobar) [![NPM License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/luciozhang/vitepress-plugin-autobar/blob/master/LICENSE) 4 | 5 | # Install 6 | 7 | ```shell 8 | npm install -D vitepress-plugin-autobar 9 | ``` 10 | 11 | # API 12 | 13 | ## getSideBar 14 | 15 | ```javascript 16 | getSideBar(rootDir = './', options?: Options) 17 | ``` 18 | 19 | - **rootDir**: `string` Directory to get configuration for 20 | - **options**: `Options`Option to create configuration 21 | 22 | Returns `sidebar` configuration for VitePress calculated using structure of directory and files in given path. 23 | 24 | Type of Options: 25 | 26 | ```typescript 27 | interface Options { 28 | ignoreDirectory?: Array, // Directoty path to ignore from being captured. 29 | ignoreMDFiles?: Array, // File path to ignore from being captured. 30 | } 31 | ``` 32 | 33 | # Usage 34 | 35 | ```javascript 36 | import { getSideBar } from 'vitepress-plugin-autobar' 37 | 38 | module.exports = { 39 | // ... 40 | themeConfig: { 41 | // ... 42 | sidebar: getSideBar("./docs"), 43 | }, 44 | }; 45 | ``` 46 | 47 | ## Ignore Some path 48 | 49 | You can pass options to keep some path out of the sidebar. 50 | 51 | ```javascript 52 | import { getSideBar } from 'vitepress-plugin-autobar' 53 | 54 | module.exports = { 55 | // ... 56 | themeConfig: { 57 | // ... 58 | sidebar: getSideBar("./docs", { 59 | ignoreMDFiles: ['index'], 60 | ignoreDirectory: ['node_modules'], 61 | }), 62 | }, 63 | }; 64 | ``` 65 | 66 | # How it work 67 | 68 | You directory may be like this. 69 | 70 | ```shell 71 | . 72 | ├─ docs 73 | │ ├─ .vitepress 74 | │ │ └─ config.js 75 | │ ├─ 01.Introduction 76 | │ │ └─ START.md 77 | │ ├─ 02.Utils 78 | │ │ └─ dateUtil.md 79 | │ │ └─ storeUtil.md 80 | │ └─ index.md 81 | ``` 82 | 83 | ## Generator sidebar 84 | 85 | - [x] Then `getSideBar` will return sidebar data like this. It will work well for vitepress. 86 | - [x] Sidebar will order by file path. 87 | 88 | - [x] Number in the file path will be removed. 89 | 90 | 91 | ```json 92 | [ 93 | { 94 | "text":"Introduction", 95 | "items":[ 96 | { 97 | "text":"START", 98 | "link":"01.Introduction/START" 99 | } 100 | ] 101 | }, 102 | { 103 | "text":"Utils", 104 | "items":[ 105 | { 106 | "text":"dateUtil", 107 | "link":"02.Utils/dateUtil" 108 | }, 109 | { 110 | "text":"storeUtil", 111 | "link":"02.Utils/storeUtil" 112 | } 113 | ] 114 | }, 115 | { 116 | "text":"Index", 117 | "items":[ 118 | { 119 | "text":"Index", 120 | "link":"index" 121 | } 122 | ] 123 | } 124 | ] 125 | ``` 126 | 127 | [The configuration for the sidebar in Vitepress](https://vitepress.vuejs.org/config/theme-configs#sidebar) 128 | 129 | # Future 130 | 131 | If vitepress supports plugins, this component will extend the functionality of plugins. 132 | 133 | # License 134 | MIT 135 | 136 | Copyright (c) 2022-present, luciozhang -------------------------------------------------------------------------------- /__test__/docs/.vitepress/config.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luciozhang/vitepress-plugin-autobar/19cb42699c880e2d41444ad5780f3b74a446fbb7/__test__/docs/.vitepress/config.js -------------------------------------------------------------------------------- /__test__/docs/01.Introduction/START.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luciozhang/vitepress-plugin-autobar/19cb42699c880e2d41444ad5780f3b74a446fbb7/__test__/docs/01.Introduction/START.md -------------------------------------------------------------------------------- /__test__/docs/02.Utils/dateUtil.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luciozhang/vitepress-plugin-autobar/19cb42699c880e2d41444ad5780f3b74a446fbb7/__test__/docs/02.Utils/dateUtil.md -------------------------------------------------------------------------------- /__test__/docs/02.Utils/storeUtil.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luciozhang/vitepress-plugin-autobar/19cb42699c880e2d41444ad5780f3b74a446fbb7/__test__/docs/02.Utils/storeUtil.md -------------------------------------------------------------------------------- /__test__/docs/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luciozhang/vitepress-plugin-autobar/19cb42699c880e2d41444ad5780f3b74a446fbb7/__test__/docs/index.md -------------------------------------------------------------------------------- /__test__/index.spec.js: -------------------------------------------------------------------------------- 1 | import { getSideBar } from '../lib/index.js'; 2 | 3 | const currentSideBarResult = '[{"text":"Introduction","items":[{"text":"START","link":"01.Introduction/START"}]},{"text":"Utils","items":[{"text":"Date Util","link":"02.Utils/dateUtil"},{"text":"Store Util","link":"02.Utils/storeUtil"}]},{"text":"Index","items":[{"text":"Index","link":"index"}]},{"text":"Node Modules","items":[{"text":"README","link":"node_modules/README"}]}]'; 4 | const currentSideBarResultWithoutIndex = '[{"text":"Introduction","items":[{"text":"START","link":"01.Introduction/START"}]},{"text":"Utils","items":[{"text":"Date Util","link":"02.Utils/dateUtil"},{"text":"Store Util","link":"02.Utils/storeUtil"}]},{"text":"Node Modules","items":[{"text":"README","link":"node_modules/README"}]}]'; 5 | const currentSideBarResultWithoutNodeModules = '[{"text":"Introduction","items":[{"text":"START","link":"01.Introduction/START"}]},{"text":"Utils","items":[{"text":"Date Util","link":"02.Utils/dateUtil"},{"text":"Store Util","link":"02.Utils/storeUtil"}]}]'; 6 | 7 | describe('getSideBar', () => { 8 | it('getSideBar correct', () => { 9 | const sidebar = getSideBar('./__test__/docs'); 10 | expect(JSON.stringify(sidebar)).toEqual(currentSideBarResult); 11 | }); 12 | 13 | it('getSideBar/ignoreMDFiles correct', () => { 14 | const sidebar = getSideBar('./__test__/docs', { 15 | ignoreMDFiles: ['index'], 16 | }); 17 | expect(JSON.stringify(sidebar)).toEqual(currentSideBarResultWithoutIndex); 18 | }); 19 | 20 | 21 | it('getSideBar/ignoreDirectory correct', () => { 22 | const sidebar = getSideBar('./__test__/docs', { 23 | ignoreMDFiles: ['index'], 24 | ignoreDirectory: ['node_modules'], 25 | }); 26 | console.log(sidebar); 27 | expect(JSON.stringify(sidebar)).toEqual(currentSideBarResultWithoutNodeModules); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { presets: ['@babel/preset-env'] }; 2 | -------------------------------------------------------------------------------- /jest.config.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property, visit: 3 | * https://jestjs.io/docs/configuration 4 | */ 5 | 6 | export default { 7 | clearMocks: true, 8 | collectCoverage: true, 9 | coverageDirectory: 'coverage', 10 | transformIgnorePatterns: [ 11 | '/node_modules/(?!lodash-es)', 12 | ], 13 | moduleNameMapper: { 14 | '^lodash-es$': 'lodash', 15 | }, 16 | transform: { 17 | '^.+\\.(js|jsx)$': 'babel-jest', 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | (function (factory) { 2 | typeof define === 'function' && define.amd ? define(factory) : 3 | factory(); 4 | })((function () { 'use strict'; 5 | 6 | Object.defineProperty(exports, "__esModule", { value: true }); 7 | exports.getSideBar = void 0; 8 | var tslib_1 = require("tslib"); 9 | var startCase_1 = tslib_1.__importDefault(require("lodash/startCase")); 10 | var sortBy_1 = tslib_1.__importDefault(require("lodash/sortBy")); 11 | var remove_1 = tslib_1.__importDefault(require("lodash/remove")); 12 | var path_1 = require("path"); 13 | var glob_1 = tslib_1.__importDefault(require("glob")); 14 | // handle md file name 15 | var getName = function (path) { 16 | var name = path.split(path_1.sep).pop() || path; 17 | var argsIndex = name.lastIndexOf('--'); 18 | if (argsIndex > -1) { 19 | name = name.substring(0, argsIndex); 20 | } 21 | // "001.guide" or "001-guide" or "001_guide" or "001 guide" -> "guide" 22 | name = name.replace(/^\d+[.\-_ ]?/, ''); 23 | return (0, startCase_1.default)(name); 24 | }; 25 | // handle dir name 26 | var getDirName = function (path) { 27 | var name = path.split(path_1.sep).shift() || path; 28 | name = name.replace(/^\d+[.\-_ ]?/, ''); 29 | return (0, startCase_1.default)(name); 30 | }; 31 | // Load all MD files in a specified directory 32 | var getChildren = function (parentPath, ignoreMDFiles) { 33 | if (ignoreMDFiles === void 0) { ignoreMDFiles = []; } 34 | var pattern = '/**/*.md'; 35 | var files = glob_1.default.sync(parentPath + pattern).map(function (path) { 36 | var newPath = path.slice(parentPath.length + 1, -3); 37 | if ((ignoreMDFiles === null || ignoreMDFiles === void 0 ? void 0 : ignoreMDFiles.length) && ignoreMDFiles.findIndex(function (item) { return item === newPath; }) !== -1) { 38 | return undefined; 39 | } 40 | return { path: newPath }; 41 | }); 42 | (0, remove_1.default)(files, function (file) { return file === undefined; }); 43 | // Return the ordered list of files, sort by 'path' 44 | return (0, sortBy_1.default)(files, ['path']).map(function (file) { return (file === null || file === void 0 ? void 0 : file.path) || ''; }); 45 | }; 46 | // Return sidebar config for given baseDir. 47 | function side(baseDir, options) { 48 | var mdFiles = getChildren(baseDir, options === null || options === void 0 ? void 0 : options.ignoreMDFiles); 49 | var sidebars = []; 50 | // strip number of folder's name 51 | mdFiles.forEach(function (item) { 52 | var _a; 53 | var dirName = getDirName(item); 54 | if (((_a = options === null || options === void 0 ? void 0 : options.ignoreDirectory) === null || _a === void 0 ? void 0 : _a.length) 55 | && (options === null || options === void 0 ? void 0 : options.ignoreDirectory.findIndex(function (item) { return getDirName(item) === dirName; })) !== -1) { 56 | return; 57 | } 58 | var mdFileName = getName(item); 59 | var sidebarItemIndex = sidebars.findIndex(function (sidebar) { return sidebar.text === dirName; }); 60 | if (sidebarItemIndex !== -1) { 61 | sidebars[sidebarItemIndex].items.push({ 62 | text: mdFileName, 63 | link: item, 64 | }); 65 | } 66 | else { 67 | sidebars.push({ 68 | text: dirName, 69 | items: [{ 70 | text: mdFileName, 71 | link: item, 72 | }], 73 | }); 74 | } 75 | }); 76 | console.info('sidebar is create:', JSON.stringify(sidebars)); 77 | return sidebars; 78 | } 79 | /** 80 | * Returns `sidebar` configuration for VitePress calculated using structure of directory and files in given path. 81 | * @param {String} rootDir - Directory to get configuration for. 82 | * @param {Options} options - Option to create configuration. 83 | */ 84 | var getSideBar = function (rootDir, options) { 85 | if (rootDir === void 0) { rootDir = './'; } 86 | return side(rootDir, options); 87 | }; 88 | exports.getSideBar = getSideBar; 89 | 90 | })); 91 | -------------------------------------------------------------------------------- /lib/index.min.js: -------------------------------------------------------------------------------- 1 | !function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.getSideBar=void 0;var e=require("tslib"),t=e.__importDefault(require("lodash/startCase")),i=e.__importDefault(require("lodash/sortBy")),n=e.__importDefault(require("lodash/remove")),r=require("path"),o=e.__importDefault(require("glob")),u=function(e){var i=e.split(r.sep).shift()||e;return i=i.replace(/^\d+[.\-_ ]?/,""),(0,t.default)(i)};function l(e,l){var d=function(e,t){void 0===t&&(t=[]);var r=o.default.sync(e+"/**/*.md").map((function(i){var n=i.slice(e.length+1,-3);if(!(null==t?void 0:t.length)||-1===t.findIndex((function(e){return e===n})))return{path:n}}));return(0,n.default)(r,(function(e){return void 0===e})),(0,i.default)(r,["path"]).map((function(e){return(null==e?void 0:e.path)||""}))}(e,null==l?void 0:l.ignoreMDFiles),f=[];return d.forEach((function(e){var i,n=u(e);if(!(null===(i=null==l?void 0:l.ignoreDirectory)||void 0===i?void 0:i.length)||-1===(null==l?void 0:l.ignoreDirectory.findIndex((function(e){return u(e)===n})))){var o,d,a,s=(d=(o=e).split(r.sep).pop()||o,(a=d.lastIndexOf("--"))>-1&&(d=d.substring(0,a)),d=d.replace(/^\d+[.\-_ ]?/,""),(0,t.default)(d)),c=f.findIndex((function(e){return e.text===n}));-1!==c?f[c].items.push({text:s,link:e}):f.push({text:n,items:[{text:s,link:e}]})}})),console.info("sidebar is create:",JSON.stringify(f)),f}exports.getSideBar=function(e,t){return void 0===e&&(e="./"),l(e,t)}})); 2 | -------------------------------------------------------------------------------- /lib/index.mjs: -------------------------------------------------------------------------------- 1 | Object.defineProperty(exports, "__esModule", { value: true }); 2 | exports.getSideBar = void 0; 3 | const tslib_1 = require("tslib"); 4 | const startCase_1 = tslib_1.__importDefault(require("lodash/startCase")); 5 | const sortBy_1 = tslib_1.__importDefault(require("lodash/sortBy")); 6 | const remove_1 = tslib_1.__importDefault(require("lodash/remove")); 7 | const path_1 = require("path"); 8 | const glob_1 = tslib_1.__importDefault(require("glob")); 9 | // handle md file name 10 | const getName = (path) => { 11 | let name = path.split(path_1.sep).pop() || path; 12 | const argsIndex = name.lastIndexOf('--'); 13 | if (argsIndex > -1) { 14 | name = name.substring(0, argsIndex); 15 | } 16 | // "001.guide" or "001-guide" or "001_guide" or "001 guide" -> "guide" 17 | name = name.replace(/^\d+[.\-_ ]?/, ''); 18 | return (0, startCase_1.default)(name); 19 | }; 20 | // handle dir name 21 | const getDirName = (path) => { 22 | let name = path.split(path_1.sep).shift() || path; 23 | name = name.replace(/^\d+[.\-_ ]?/, ''); 24 | return (0, startCase_1.default)(name); 25 | }; 26 | // Load all MD files in a specified directory 27 | const getChildren = function (parentPath, ignoreMDFiles = []) { 28 | const pattern = '/**/*.md'; 29 | const files = glob_1.default.sync(parentPath + pattern).map((path) => { 30 | const newPath = path.slice(parentPath.length + 1, -3); 31 | if (ignoreMDFiles?.length && ignoreMDFiles.findIndex(item => item === newPath) !== -1) { 32 | return undefined; 33 | } 34 | return { path: newPath }; 35 | }); 36 | (0, remove_1.default)(files, file => file === undefined); 37 | // Return the ordered list of files, sort by 'path' 38 | return (0, sortBy_1.default)(files, ['path']).map(file => file?.path || ''); 39 | }; 40 | // Return sidebar config for given baseDir. 41 | function side(baseDir, options) { 42 | const mdFiles = getChildren(baseDir, options?.ignoreMDFiles); 43 | const sidebars = []; 44 | // strip number of folder's name 45 | mdFiles.forEach((item) => { 46 | const dirName = getDirName(item); 47 | if (options?.ignoreDirectory?.length 48 | && options?.ignoreDirectory.findIndex(item => getDirName(item) === dirName) !== -1) { 49 | return; 50 | } 51 | const mdFileName = getName(item); 52 | const sidebarItemIndex = sidebars.findIndex(sidebar => sidebar.text === dirName); 53 | if (sidebarItemIndex !== -1) { 54 | sidebars[sidebarItemIndex].items.push({ 55 | text: mdFileName, 56 | link: item, 57 | }); 58 | } 59 | else { 60 | sidebars.push({ 61 | text: dirName, 62 | items: [{ 63 | text: mdFileName, 64 | link: item, 65 | }], 66 | }); 67 | } 68 | }); 69 | console.info('sidebar is create:', JSON.stringify(sidebars)); 70 | return sidebars; 71 | } 72 | /** 73 | * Returns `sidebar` configuration for VitePress calculated using structure of directory and files in given path. 74 | * @param {String} rootDir - Directory to get configuration for. 75 | * @param {Options} options - Option to create configuration. 76 | */ 77 | const getSideBar = (rootDir = './', options) => side(rootDir, options); 78 | exports.getSideBar = getSideBar; 79 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vitepress-plugin-autobar", 3 | "version": "1.0.8", 4 | "description": "Vitepress generator sidebar based on file and directory structure.", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "jest", 8 | "lint": "eslint --ext .ts,.js .", 9 | "lint:fix": "eslint --fix --ext .ts,.js .", 10 | "es": "tsc", 11 | "rollup": "rollup -c", 12 | "build": "npm run es && npm run rollup" 13 | }, 14 | "keywords": [ 15 | "vitepress", 16 | "sidebar", 17 | "auto", 18 | "auto sidebar", 19 | "autobar", 20 | "vitepress config" 21 | ], 22 | "author": "luciozhang", 23 | "license": "MIT", 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/luciozhang/vitepress-plugin-autobar.git" 27 | }, 28 | "dependencies": { 29 | "glob": "^8.0.3", 30 | "lodash": "^4.17.21" 31 | }, 32 | "devDependencies": { 33 | "@babel/preset-env": "^7.20.2", 34 | "@babel/preset-typescript": "^7.18.6", 35 | "@rollup/plugin-terser": "^0.1.0", 36 | "@rollup/plugin-typescript": "^9.0.2", 37 | "@types/glob": "^8.0.0", 38 | "@types/lodash-es": "^4.17.6", 39 | "@types/node": "^18.11.9", 40 | "@typescript-eslint/eslint-plugin": "^5.44.0", 41 | "@typescript-eslint/parser": "^5.44.0", 42 | "babel-jest": "^29.3.1", 43 | "eslint": "^8.28.0", 44 | "eslint-config-tencent": "^1.0.4", 45 | "jest": "^29.3.1", 46 | "rollup": "^3.4.0" 47 | } 48 | } -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import typescript from '@rollup/plugin-typescript'; 2 | import terser from '@rollup/plugin-terser'; 3 | import { defineConfig } from 'rollup'; 4 | 5 | const publicConfig = { 6 | format: 'umd', 7 | name: 'uodule', 8 | }; 9 | 10 | const config = defineConfig([ 11 | { 12 | input: 'src/index.ts', 13 | output: [ 14 | { 15 | file: 'lib/index.js', 16 | ...publicConfig, 17 | }, 18 | { 19 | file: 'lib/index.min.js', 20 | ...publicConfig, 21 | plugins: [ 22 | terser(), 23 | ], 24 | }, 25 | ], 26 | plugins: [ 27 | typescript({ 28 | declaration: false, 29 | target: 'ES5', 30 | }), 31 | ], 32 | }, 33 | { 34 | input: 'src/index.ts', 35 | output: { 36 | file: 'lib/index.mjs', 37 | format: 'esm', 38 | }, 39 | plugins: [ 40 | typescript({ 41 | declaration: false, 42 | }), 43 | ], 44 | }, 45 | ]); 46 | 47 | export default config; 48 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import startCase from 'lodash/startCase'; 2 | import sortBy from 'lodash/sortBy'; 3 | import remove from 'lodash/remove'; 4 | import { sep } from 'path'; 5 | import glob from 'glob'; 6 | 7 | type Sidebar = SidebarGroup[] | SidebarMulti; 8 | 9 | interface SidebarMulti { 10 | [path: string]: SidebarGroup[] 11 | } 12 | 13 | interface SidebarGroup { 14 | text: string 15 | items: SidebarItem[] 16 | collapsible?: boolean 17 | collapsed?: boolean 18 | } 19 | 20 | interface SidebarItem { 21 | text: string 22 | link: string 23 | } 24 | 25 | interface Options { 26 | ignoreDirectory?: Array, // Directoty path to ignore from being captured. 27 | ignoreMDFiles?: Array, // File path to ignore from being captured. 28 | } 29 | 30 | // handle md file name 31 | const getName = (path: string) => { 32 | let name = path.split(sep).pop() || path; 33 | const argsIndex = name.lastIndexOf('--'); 34 | if (argsIndex > -1) { 35 | name = name.substring(0, argsIndex); 36 | } 37 | 38 | // "001.guide" or "001-guide" or "001_guide" or "001 guide" -> "guide" 39 | name = name.replace(/^\d+[.\-_ ]?/, ''); 40 | 41 | return startCase(name); 42 | }; 43 | 44 | // handle dir name 45 | const getDirName = (path: string) => { 46 | let name = path.split(sep).shift() || path; 47 | name = name.replace(/^\d+[.\-_ ]?/, ''); 48 | 49 | return startCase(name); 50 | }; 51 | 52 | // Load all MD files in a specified directory 53 | const getChildren = function (parentPath: string, ignoreMDFiles: Array = []) { 54 | const pattern = '/**/*.md'; 55 | const files = glob.sync(parentPath + pattern).map((path) => { 56 | const newPath = path.slice(parentPath.length + 1, -3); 57 | if (ignoreMDFiles?.length && ignoreMDFiles.findIndex(item => item === newPath) !== -1) { 58 | return undefined; 59 | } 60 | return { path: newPath }; 61 | }); 62 | 63 | remove(files, file => file === undefined); 64 | // Return the ordered list of files, sort by 'path' 65 | return sortBy(files, ['path']).map(file => file?.path || ''); 66 | }; 67 | 68 | // Return sidebar config for given baseDir. 69 | function side(baseDir: string, options?: Options) { 70 | const mdFiles = getChildren(baseDir, options?.ignoreMDFiles); 71 | 72 | const sidebars: Sidebar = []; 73 | // strip number of folder's name 74 | mdFiles.forEach((item) => { 75 | const dirName = getDirName(item); 76 | if (options?.ignoreDirectory?.length 77 | && options?.ignoreDirectory.findIndex(item => getDirName(item) === dirName) !== -1) { 78 | return; 79 | } 80 | const mdFileName = getName(item); 81 | const sidebarItemIndex = sidebars.findIndex(sidebar => sidebar.text === dirName); 82 | if (sidebarItemIndex !== -1) { 83 | sidebars[sidebarItemIndex].items.push({ 84 | text: mdFileName, 85 | link: item, 86 | }); 87 | } else { 88 | sidebars.push({ 89 | text: dirName, 90 | items: [{ 91 | text: mdFileName, 92 | link: item, 93 | }], 94 | }); 95 | } 96 | }); 97 | 98 | console.info('sidebar is create:', JSON.stringify(sidebars)); 99 | return sidebars; 100 | } 101 | 102 | /** 103 | * Returns `sidebar` configuration for VitePress calculated using structure of directory and files in given path. 104 | * @param {String} rootDir - Directory to get configuration for. 105 | * @param {Options} options - Option to create configuration. 106 | */ 107 | export const getSideBar = (rootDir = './', options?: Options) => side(rootDir, options); 108 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "declarationDir": "types", 8 | "removeComments": false, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "skipLibCheck": true, 13 | "moduleResolution": "node" 14 | } 15 | } -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | interface SidebarGroup { 2 | text: string; 3 | items: SidebarItem[]; 4 | collapsible?: boolean; 5 | collapsed?: boolean; 6 | } 7 | interface SidebarItem { 8 | text: string; 9 | link: string; 10 | } 11 | interface Options { 12 | ignoreDirectory?: Array; 13 | ignoreMDFiles?: Array; 14 | } 15 | /** 16 | * Returns `sidebar` configuration for VitePress calculated using structure of directory and files in given path. 17 | * @param {String} rootDir - Directory to get configuration for. 18 | * @param {Options} options - Option to create configuration. 19 | */ 20 | export declare const getSideBar: (rootDir?: string, options?: Options) => SidebarGroup[]; 21 | export {}; 22 | --------------------------------------------------------------------------------