electronjs.org-new/scripts/tasks/create-sidebar.ts

186 строки
5.1 KiB
TypeScript

import fs from 'fs-extra';
import json5 from 'json5';
import globby from 'globby';
import type {
Sidebars,
SidebarItemDoc,
} from '@docusaurus/plugin-content-docs/src/sidebars/types.js';
import logger from '@docusaurus/logger';
import prettier from 'prettier';
import path from 'path';
const IGNORE_LIST = [
'README',
'styleguide',
// these don't belong to any category yet
'api/accelerator',
'experimental',
// these have limited relevance
'tutorial/using-pepper-flash-plugin',
'latest/development/README',
'tutorial/support',
'api/synopsis',
];
const categoryAliases = new Map([['Tutorial', 'How To']]);
/**
* Capitalizes the first letter of each word in title.
* @param {string} title
*/
const capitalize = (title: string) => {
const words = title.split(' ');
const capitalizedWords = words.map((word) => {
return word[0].toUpperCase() + word.substring(1);
});
return capitalizedWords.join(' ');
};
/**
* Returns a category inside `sidebars` whose property
* `label` matches `category`.
* @param categoryName The category to find
* @param sidebars The sidebars object
* @param defaultTopLevel The default top level to add to the category if it does not exist
*/
const findCategoryForDocument = (
categoryName: string,
sidebars: Sidebars,
defaultTopLevel: string
) => {
const topLevelIds = Object.keys(sidebars);
const categoryAlias = categoryAliases.get(categoryName) || categoryName;
for (const topLevelId of topLevelIds) {
const entries = sidebars[topLevelId];
for (const category of entries) {
if (
category.type === 'category' &&
category.label.toLowerCase() === categoryAlias.toLowerCase()
) {
return category;
}
}
}
/*
If we reach this point, the category does not exist so we
create a new one and add it directly to sidebars.
Not a fan of modifying parameters though 😞
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const category: any = {
type: 'category',
label: categoryName,
items: [],
};
sidebars[defaultTopLevel].push(category);
return category;
};
/**
* Using the given `destination` as the source,
* adds any new document found at the end of each
* category while preserving the order.
* If the file does not exists, it gets created
* using the folder structure as the guide.
* @param root Root directory for the documentation
* @param destination The path where `sidebars.js` lives
*/
export const createSidebar = async (root: string, destination: string) => {
const documents = await globby(`**/*.md`, {
onlyFiles: true,
cwd: root,
});
const sidebars = (
fs.existsSync(destination) ? require(destination) : { docs: [], api: [] }
) as Sidebars;
const reverseLookup = new Map<string, SidebarItemDoc>();
const setRecursive = (category) => {
// Categories can also have a doc attached to them
if (category?.link?.type === 'doc' && category.link.id) {
reverseLookup.set(category.link.id, category);
}
// Go through all items in category
for (const item of category.items) {
// if doc is string shorthand
if (typeof item === 'string') {
reverseLookup.set(item, category);
// if doc is added as object syntax
} else if (item?.type === 'doc' && !!item.id) {
reverseLookup.set(item.id, category);
// if item is nested category, recurse
} else if (item?.type === 'category') {
setRecursive(item);
}
}
};
for (const id of Object.keys(sidebars)) {
for (const category of sidebars[id]) {
setRecursive(category);
}
}
let hasNewDocuments = false;
for (const document of documents) {
const documentId = document.replace('.md', '');
if (reverseLookup.has(documentId)) {
continue;
}
const ignore = IGNORE_LIST.some((ignore) => documentId.endsWith(ignore));
if (ignore) {
continue;
}
const segments = document.split('/');
// Documents are always under /latest/ or similar that are not relevant for the category
segments.shift();
// The last segment is the name of the file
segments.pop();
logger.info(`New document found: ${logger.green(document)}`);
hasNewDocuments = true;
const categoryId = segments
.map((segment) => capitalize(segment.replace(/-/g, ' ')))
.join(' ');
const defaultTopLevel = segments[0] === 'api' ? 'api' : 'docs';
const category = findCategoryForDocument(
categoryId,
sidebars,
defaultTopLevel
);
category.items.push(document.replace('.md', ''));
}
if (hasNewDocuments) {
logger.info(`Updating ${logger.green(destination)}`);
const sidebarString = `module.exports = ${json5.stringify(sidebars)};`;
// run it through our linter
const prettierConfig = await prettier.resolveConfig(
path.resolve(__dirname, '..', '..', '.prettierrc')
);
const formattedSidebarString = prettier.format(
sidebarString,
prettierConfig
);
await fs.writeFile(destination, formattedSidebarString, 'utf-8');
} else {
logger.info(`No new documents found`);
}
};