blob: c6ee1d6ce6df7603d7309d78c2ef658a39d84453 [file]
const fs = require('fs');
const path = require('path');
const SKIP_DIRS = new Set(['images', 'scripts', 'styles']);
const SKIP_FILES = new Set(['SUMMARY.md']);
const SECTION_ORDER = [
'introduction',
'quickstart',
'config',
'clients',
'guides',
'language',
'performance',
'changelog',
'contribution-guidelines',
];
function readFrontMatter(filePath) {
const text = fs.readFileSync(filePath, 'utf8');
const match = text.match(/^---\n([\s\S]*?)\n---/);
if (!match) {
return {};
}
const data = {};
for (const line of match[1].split('\n')) {
const item = line.match(/^([A-Za-z_][A-Za-z0-9_]*):\s*(.*)$/);
if (!item) {
continue;
}
data[item[1]] = item[2].replace(/^['"]|['"]$/g, '');
}
return data;
}
function labelFromPath(filePath, fallback) {
const fm = readFrontMatter(filePath);
return fm.linkTitle || fm.title || fallback;
}
function weight(filePath, name) {
const fm = fs.existsSync(filePath) ? readFrontMatter(filePath) : {};
if (fm.weight !== undefined && !Number.isNaN(Number(fm.weight))) {
return Number(fm.weight);
}
const sectionIndex = SECTION_ORDER.indexOf(name);
return sectionIndex === -1 ? 1000 : sectionIndex;
}
function stripExtension(fileName) {
return fileName.replace(/\.mdx?$/i, '');
}
function docId(relPath) {
return stripExtension(relPath).split(path.sep).join('/');
}
function titleCase(value) {
return value
.replace(/[-_]+/g, ' ')
.replace(/\b\w/g, (char) => char.toUpperCase());
}
function sortEntries(baseDir, relDir, entries) {
return entries.sort((a, b) => {
const aPath = path.join(baseDir, relDir, a.name, a.isDirectory() ? '_index.md' : '');
const bPath = path.join(baseDir, relDir, b.name, b.isDirectory() ? '_index.md' : '');
const aWeight = weight(a.isDirectory() ? aPath : path.join(baseDir, relDir, a.name), stripExtension(a.name));
const bWeight = weight(b.isDirectory() ? bPath : path.join(baseDir, relDir, b.name), stripExtension(b.name));
if (aWeight !== bWeight) {
return aWeight - bWeight;
}
return a.name.localeCompare(b.name);
});
}
function buildDirectory(baseDir, relDir = '') {
const dir = path.join(baseDir, relDir);
const entries = fs.readdirSync(dir, {withFileTypes: true});
const files = sortEntries(
baseDir,
relDir,
entries.filter(
(entry) =>
entry.isFile() &&
/\.mdx?$/i.test(entry.name) &&
entry.name !== '_index.md' &&
!SKIP_FILES.has(entry.name),
),
).map((entry) => ({
type: 'doc',
id: docId(path.join(relDir, entry.name)),
label: labelFromPath(path.join(baseDir, relDir, entry.name), titleCase(stripExtension(entry.name))),
}));
const dirs = sortEntries(
baseDir,
relDir,
entries.filter((entry) => entry.isDirectory() && !SKIP_DIRS.has(entry.name)),
).map((entry) => buildCategory(baseDir, path.join(relDir, entry.name)));
return [...dirs, ...files];
}
function buildCategory(baseDir, relDir) {
const indexFile = path.join(baseDir, relDir, '_index.md');
const label = fs.existsSync(indexFile)
? labelFromPath(indexFile, titleCase(path.basename(relDir)))
: titleCase(path.basename(relDir));
const category = {
type: 'category',
label,
collapsible: true,
collapsed: true,
items: buildDirectory(baseDir, relDir),
};
if (fs.existsSync(indexFile)) {
category.link = {
type: 'doc',
id: docId(path.join(relDir, '_index.md')),
};
}
return category;
}
function buildSidebar(baseDir) {
const items = [];
const rootIndex = path.join(baseDir, '_index.md');
if (fs.existsSync(rootIndex)) {
items.push({
type: 'doc',
id: '_index',
label: labelFromPath(rootIndex, 'Documentation'),
});
}
items.push(...buildDirectory(baseDir));
return items;
}
module.exports = {buildSidebar};