vitepress-generate-sidebar
Version:
Vitepress generator sidebar based on file and directory structure.
110 lines (94 loc) • 3.25 kB
text/typescript
import startCase from 'lodash/startCase';
import sortBy from 'lodash/sortBy';
import remove from 'lodash/remove';
import { sep } from 'path';
import glob from 'glob';
type Sidebar = SidebarGroup[] | SidebarMulti;
interface SidebarMulti {
[path: string]: SidebarGroup[]
}
interface SidebarGroup {
text: string
items: SidebarItem[]
collapsible?: boolean
collapsed?: boolean
}
interface SidebarItem {
text: string
link: string
}
interface Options {
ignoreDirectory?: Array<string>, // Directoty path to ignore from being captured.
ignoreMDFiles?: Array<string>, // File path to ignore from being captured.
}
// handle md file name
const getName = (path: string) => {
let name = path.split(sep).pop() || path;
const argsIndex = name.lastIndexOf('--');
if (argsIndex > -1) {
name = name.substring(0, argsIndex);
}
// "001.guide" or "001-guide" or "001_guide" or "001 guide" -> "guide"
name = name.replace(/^\d+[.\-_ ]?/, '');
return startCase(name);
};
// handle dir name
const getDirName = (path: string) => {
let name = path.split(sep).shift() || path;
name = name.replace(/^\d+[.\-_ ]?/, '');
return startCase(name);
};
// Load all MD files in a specified directory
const getChildren = function (parentPath: string, ignoreMDFiles: Array<string> = []) {
const pattern = '/**/*.md';
const files = glob.sync(parentPath + pattern).map((path) => {
const newPath = path.slice(parentPath.length + 1, -3);
if (ignoreMDFiles?.length && ignoreMDFiles.findIndex(item => item === newPath) !== -1) {
return undefined;
}
return { path: newPath };
});
remove(files, file => file === undefined);
// Return the ordered list of files, sort by 'path'
return sortBy(files, ['path']).map(file => file?.path || '');
};
// Return sidebar config for given baseDir.
function side(baseDir: string, options?: Options) {
const mdFiles = getChildren(baseDir, options?.ignoreMDFiles);
const index = baseDir.lastIndexOf("\/");
const baseName = `/${baseDir.substring(index + 1, baseDir.length)}/`;
const sidebars: Sidebar = [];
// strip number of folder's name
mdFiles.forEach((item) => {
const dirName = getDirName(item);
if (options?.ignoreDirectory?.length
&& options?.ignoreDirectory.findIndex(item => getDirName(item) === dirName) !== -1) {
return;
}
const mdFileName = getName(item);
const sidebarItemIndex = sidebars.findIndex(sidebar => sidebar.text === dirName);
if (sidebarItemIndex !== -1) {
sidebars[sidebarItemIndex].items.push({
text: mdFileName,
link: baseName+item,
});
} else {
sidebars.push({
text: dirName,
collapsed: true,
items: [{
text: mdFileName,
link: baseName+item,
}],
});
}
});
// console.info('sidebar is create:', JSON.stringify(sidebars));
return sidebars;
}
/**
* Returns `sidebar` configuration for VitePress calculated using structure of directory and files in given path.
* @param {String} rootDir - Directory to get configuration for.
* @param {Options} options - Option to create configuration.
*/
export const getSideBar = (rootDir = './', options?: Options) => side(rootDir, options);