Closed mrleblanc101 closed 6 years ago
Might have found something. In config.js add:
const dirTree = require('directory-tree');
const path = require('path');
const projets = dirTree(path.join(__dirname, '../projets'), {extensions:/\.md/});
module.exports = {
[...] // Shortened for lisibility
themeConfig: {
sidebar: {
'/projets/': projets.children.map(children => path.parse(children.name).name !== 'README' ? path.parse(children.name).name : '' ),
},
}
};
Still need a lot of work and some cleanup but here's what i have right now for recursive directory:
var projets = [];
dirTree(path.join(__dirname, '../projets'), {extensions:/\.md/}, (item, PATH) => projets.push(item));
projets = projets.map(children => {
return path.parse(children.name).name !== 'README' ? path.join.apply(null, children.path.split(path.sep).slice(7)) : path.join.apply(null, children.path.split(path.sep).slice(7)).replace('README.md', '');
});
The two major hurdle i have right now are:
1) Sidebar can't resolve current directory if path = 'README.md' or 'index.html', it need to be empty String which seems weird, maybe we could change only that and forget about the rest of the issue ?
2) I'm not super familiar with Node.js path so my code to transform the path from /Users/mrleblanc/GitHub/alexandrie/docs/projets/client-b/test.md
to /projets/client-b/test.md
is probably extremely ugly
3) I might have to change how I'm doing this if i want to nest 'Projects' under their appropriate 'Client'
4) Using my multiple sidebar method force me to have a 'directory' sidebar even in sub-page because sidebar: auto in the file does not overwrite it
Thank you for your interest in vuepress
. and I also read your requirement carefully but I still cannot clearly get what you really want.
In my opinion, I think that you want something that helps you to generate the config
automatically according to the file structure, but this is not a common need and different people may have different generation needs, in addition, it also can be done in user-land.
BTW, If you are not familiar to Node.js, sorry but I cannot help you.
@ulivz I think the goal is an automated sidebar. The above script is a workaround to generate this.
I have the same requirement, but the above answer needs to be run in the node environment
I like this idea, too. To be honest, that's what I expected to happen.
I've got a docs/components
folder for some components. I automatically create a subfolder for each component and put a README.md
file into this folder since I thought that would be the way to go to generate the page tree automatically. Currently, I need to update the sidebar pages manually each time I add a new component.
With the folder structure already in place, an option to automatically render the navigation as described here would be awesome.
I used the following code to generate a list of all markdown files in reverse order. I use my VuePress instance as my notes viewer app.
Aside: My files start with a YYYYMMDD-*.md
pattern so I personally added a .reverse()
in the markdownFiles line for easier reference.
Note: 1 new NPM dependency (glob
)
npm install glob
/.vuepress/config.js
const glob = require('glob');
let markdownFiles = glob.sync('docs/*/.md').map(f => '/' + f); // update the docs/*/.md pattern with your folder structure
module.exports = { ...... themeConfig: { sidebar: markdownFiles }, };
Hope this helps someone!
Why is this closed and why do we have to manually create the tree in the config? It is not very handy...
I took what Prefect does and added functionality for reading the order
value of the YAML metadata if it exists, otherwise it sorts by filename:
const _ = require('lodash');
const fs = require('fs');
const glob = require('glob');
const markdownIt = require('markdown-it');
const meta = require('markdown-it-meta');
// Load all MD files in a specified directory and order by metadata 'order' value
const getChildren = function(parent_path, dir) {
files = glob
.sync(parent_path + (dir ? `/${dir}` : '') + '/**/*.md')
.map(path => {
// Instantiate MarkdownIt
md = new markdownIt();
// Add markdown-it-meta
md.use(meta);
// Get the order value
file = fs.readFileSync(path, 'utf8');
md.render(file);
order = md.meta.order;
// Remove "parent_path" and ".md"
path = path.slice(parent_path.length + 1, -3);
// Remove "README", making it the de facto index page
if (path.endsWith('README')) {
path = path.slice(0, -6);
}
return {
path,
order
};
});
// Return the ordered list of files, sort by 'order' then 'path'
return _.sortBy(files, ['order', 'path'])
.map(file => file.path);
};
module.exports = {
getChildren,
};
Usage:
sidebar: {
'/get-children/': [{
title: 'Example 1',
collapsable: true,
children: getChildren('docs/example-1'),
}, {
title: 'Example 2',
collapsable: true,
children: getChildren('docs/example-2'),
}],
// if more than three 2 dirs deep add a second parameter for target subdirectory
'/example3/': getChildren('docs/another/location', 'subdirectory')
}
I'm sure this can be simplified, please let me know if you clean it up or spot problems. Make sure there are no trailing or leading slashes in your directory calls.
@a-teammate I think we can perfectly use this for our website :)
Give it a try: https://www.npmjs.com/package/vuepress-bar
I know this is a closed case. I came here to find a solution and liked @benjivm solution. Then I developed the idea of @benjivm a little further to create both navbar and sidebar for directory structure and add sort feature to directories too.
In hope helping others, I shared it by publishing above node module.
Thanks for the idea.
const getFileName = name => {
let arr = []
fs.readdirSync(${RDOCS}${name}
)
.filter(function(file) {
return /.(js|md)$/i.test(file)
})
.map(function(file) {
s1 = file.substring(0, file.indexOf('.'))
let res = ''
if (s1 === 'readme' || s1 === 'README') {
res = ADOCS + name + '/'
} else {
res = ADOCS + name + '/' + s1
}
arr.push(res)
})
return arr
}
I do not think it is a personal need, many people dose need this! A situation where someone wants a specific folder auto generated is very common. As you receive 28 disagree and none agree , I really think you should consider open it. @ulivz
Just adding my voice to those that think this is a valuable feature... Downloading the third party package to do it (thank you @ozum)
@ulivz, OP gave you an amazing write-up, attempted his own solutions, said he didn't understand node PATHs, and then you chided him about not being able to write javascript and closed his issue. Not cool.
I'm happy to submit a PR if you'd like. I much prefer supporting features to using third-party plugins.
The goal of vuepress (correct me if I'm wrong) is to easily create documentation websites or static websites in general, by using convention over configuration. If I had to write my own config generator that defeats the purpose of this tool and violates the "convention over configuration" principle.
@ulivz just because you don't understand, doesn't mean its stupid. You should have asked for help instead of shutting this down.
@ulivz just because you don't understand, doesn't mean its stupid. You should have asked for help instead of shutting this down.
He is not in charge anymore.
Besides, the core team would be happy if someone can create a PR.
@MichaelJCole why not reopen this?
Is there any news on the subject? :-) This has to go in, in Vuepress. I vote for it.
This works:
This should work. At least it works in my project.
const sidebar: SidebarConfig = [];
glob
.sync('docs/**/*.md')
.map((path) => path.replace('docs/', ''))
.sort()
.forEach((path) =>
path.split('/').forEach((name, index, array) => {
let children = sidebar
for (let i = 0; i < index; i++) {
children = (
children.find(
(child) => typeof child === 'object' && child.text === array[i]
) as SidebarGroup
).children
}
if (name === 'index.md' || name === 'README.md') {
children.push(
`/${path
.replace('.md', '')
.replace('index', '')
.replace('README', '')}`
)
return
}
if (name.endsWith('.md')) {
children.push(`/${path.replace('.md', '')}`)
return
}
const child = children.find(
(child) => typeof child === 'object' && child.text === name
) as SidebarGroup
if (!child) {
children.push({ text: name, children: [], collapsible: true })
}
})
)
module.exports = {
title: '.....',
description:'....',
plugins: [searchPlugin()],
theme: defaultTheme({
repo: '.....',
docsDir: 'docs',
sidebar,
}),
};
Is there some solution vailable for Vuepress V2? I desperately need automatic sidebar population, otherwise my config.js would explode. I don't want to hack this together.
vuepress2 repo are at https://github.com/vuepress/core
talking any thing related to vuepress2 here is useless. vuepress-theme-hope support this feature out of box (would be @vuepress/theme-pro in the next future)
If yet anyone have the problem. After a lot of research and playing with the stuff. I finally have a solution which works perfectly.
//config.js
import path from "path";
import fs from "fs";
const docsDir = path.resolve(__dirname, "../");
const getSidebarItems = (dir) => {
const items = [];
const files = fs.readdirSync(dir, { withFileTypes: true });
files.forEach((file) => {
if (file.isDirectory() && file.name !== ".vuepress") {
const folderName = file.name;
const folderPath = path.join(dir, folderName);
const children = fs
.readdirSync(folderPath, { withFileTypes: true })
.filter((child) => child.isFile() && child.name.endsWith(".md"))
.map((child) => child.name.replace(".md", ""));
if (children.length === 1 && children[0] === "README") {
// Folder with just README.md
items.push({
text: folderName.replace(/-/g, " ").toUpperCase(),
link: `/${folderName}/`,
});
} else {
// Folder with other markdown files
items.push({
text: folderName.replace(/-/g, " ").toUpperCase(),
link: `/${folderName}/`,
prefix: `/${folderName}`,
children: children.filter((child) => child !== "README"),
});
}
}
});
return items;
};
const generateSidebar = () => {
return getSidebarItems(docsDir);
};
//Then just replace the sidebar
export default defineUserConfig({
//....
theme: defaultTheme({
//....
sidebar: generateSidebar(),
}),
//....
});
Feature request
Hi,
I would like to suggest a new feature for the sidebar component. Here's a brief description of my issue. I would like to have an index page for a directory (called 'Projects') which contains sub-directory
('Project A', 'Project B', 'Project C', etc...)
which each can contain multiple page of documentation('index.md', 'deploy.md', 'contribute.md', 'wiki.md', 'faq.md' ,etc...)
. The index of 'Projects' (README.md) has a description of how to use this tool and how to contribute and other important information my company would like to give them and it also has a sidebar which in theory would list all the active projects in from my company like in the screenshot bellow.What would make this even better is if it would still respect the
sidebarDepth: 2
andcollapsable: true
option already available in vuepress. For example, I could take my first example further and group all my project in a client repository. If the sidebar depth is specified, I could even see sub-sub-directory and they would be visible depending on the value of collapsable.It would be great if we could list those directory in the sidebar automatically similarly to the
sidebar: 'auto'
feature, but instead of displaying the headers of the current page, it would display the sub-directory of the current directory.The first thing I tried is using a custom layout but after extracting the default theme using
vuepress eject
to check how the default layout works, I realized it wouldn't work as the sidebar is outside the layout component. I also initially considered making a custom theme for this but the way the sidebar is handled with a helper would need me to rework a lot of the code and it would also prevent me from getting any vuepress default theme update in the future and as the project is very young, I don't wan to lose this ability as it already is evolving quite quickly which would probably require me to restart everything in a few months if a I want to update.Finaly, I think I've found a way to do something similar using the multiple-sidebar option https://vuepress.vuejs.org/default-theme-config/#multiple-sidebars, but it require me to hardcode all the value which is cumbersome because we add projects/clients on a regular basis.
The way I suggest it would works is that it would automatically list the sub-directory if you add one. No need to go and edit you
.vuepress/config.js
every time. Clicking the directory name would take you to theREADME.md
orindex.html
depending on your structure.Something similar could be added to the nav-bar at the top of the page. Initially I wanted to list the sub-directory in a dropdown in the nav-bar but decided it would be better to have an index for my 'Project' directory with some instruction/explanation but the problem is the same! We would need to hardcode the value for the dropdown and it wouldn't be as flexible as there is no way of adding a second level inside the dropdown.
How should this be implemented in your opinion?
I suggest using 'directory, 'sub-directory' or 'folder' String as value of
sidebar
in.vuepress/config.js
or in the page front-matter just like 'auto' String already works for headings in the sidebar.Are you willing to work on this yourself?**
I can help, but I'm not sure my experience with Vue and VuePress is big enough to make this myself from scratch.