Files
documentation/node_modules/@docusaurus/plugin-content-docs/lib/sidebars/generator.js
2024-03-22 03:47:51 +05:30

211 lines
9.7 KiB
JavaScript

"use strict";
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultSidebarItemsGenerator = exports.CategoryMetadataFilenamePattern = exports.CategoryMetadataFilenameBase = void 0;
const tslib_1 = require("tslib");
const path_1 = tslib_1.__importDefault(require("path"));
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const utils_1 = require("@docusaurus/utils");
const docs_1 = require("../docs");
const BreadcrumbSeparator = '/';
// Just an alias to the make code more explicit
function getLocalDocId(docId) {
return lodash_1.default.last(docId.split('/'));
}
exports.CategoryMetadataFilenameBase = '_category_';
exports.CategoryMetadataFilenamePattern = '_category_.{json,yml,yaml}';
// Comment for this feature: https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
const DefaultSidebarItemsGenerator = ({ numberPrefixParser, isCategoryIndex, docs: allDocs, item: { dirName: autogenDir }, categoriesMetadata, }) => {
const docsById = (0, docs_1.createDocsByIdIndex)(allDocs);
const findDoc = (docId) => docsById[docId];
const getDoc = (docId) => {
const doc = findDoc(docId);
if (!doc) {
throw new Error(`Can't find any doc with ID ${docId}.
Available doc IDs:
- ${Object.keys(docsById).join('\n- ')}`);
}
return doc;
};
/**
* Step 1. Extract the docs that are in the autogen dir.
*/
function getAutogenDocs() {
function isInAutogeneratedDir(doc) {
return (
// Doc at the root of the autogenerated sidebar dir
doc.sourceDirName === autogenDir ||
// Autogen dir is . and doc is in subfolder
autogenDir === '.' ||
// Autogen dir is not . and doc is in subfolder
// "api/myDoc" startsWith "api/" (note "api2/myDoc" is not included)
doc.sourceDirName.startsWith((0, utils_1.addTrailingSlash)(autogenDir)));
}
const docs = allDocs.filter(isInAutogeneratedDir);
if (docs.length === 0) {
logger_1.default.warn `No docs found in path=${autogenDir}: can't auto-generate a sidebar.`;
}
return docs;
}
/**
* Step 2. Turn the linear file list into a tree structure.
*/
function treeify(docs) {
// Get the category breadcrumb of a doc (relative to the dir of the
// autogenerated sidebar item)
// autogenDir=a/b and docDir=a/b/c/d => returns [c, d]
// autogenDir=a/b and docDir=a/b => returns []
// TODO: try to use path.relative()
function getRelativeBreadcrumb(doc) {
return autogenDir === doc.sourceDirName
? []
: doc.sourceDirName
.replace((0, utils_1.addTrailingSlash)(autogenDir), '')
.split(BreadcrumbSeparator);
}
const treeRoot = {};
docs.forEach((doc) => {
const breadcrumb = getRelativeBreadcrumb(doc);
// We walk down the file's path to generate the fs structure
let currentDir = treeRoot;
breadcrumb.forEach((dir) => {
if (typeof currentDir[dir] === 'undefined') {
currentDir[dir] = {}; // Create new folder.
}
currentDir = currentDir[dir]; // Go into the subdirectory.
});
// We've walked through the path. Register the file in this directory.
currentDir[path_1.default.basename(doc.source)] = doc.id;
});
return treeRoot;
}
/**
* Step 3. Recursively transform the tree-like structure to sidebar items.
* (From a record to an array of items, akin to normalizing shorthand)
*/
function generateSidebar(fsModel) {
function createDocItem(id, fullPath, fileName) {
const { sidebarPosition: position, frontMatter: { sidebar_label: label, sidebar_class_name: className, sidebar_custom_props: customProps, }, } = getDoc(id);
return {
type: 'doc',
id,
position,
source: fileName,
// We don't want these fields to magically appear in the generated
// sidebar
...(label !== undefined && { label }),
...(className !== undefined && { className }),
...(customProps !== undefined && { customProps }),
};
}
function createCategoryItem(dir, fullPath, folderName) {
const categoryMetadata = categoriesMetadata[path_1.default.posix.join(autogenDir, fullPath)];
const allItems = Object.entries(dir).map(([key, content]) => dirToItem(content, key, `${fullPath}/${key}`));
// Try to match a doc inside the category folder,
// using the "local id" (myDoc) or "qualified id" (dirName/myDoc)
function findDocByLocalId(localId) {
return allItems.find((item) => item.type === 'doc' && getLocalDocId(item.id) === localId);
}
function findConventionalCategoryDocLink() {
return allItems.find((item) => {
if (item.type !== 'doc') {
return false;
}
const doc = getDoc(item.id);
return isCategoryIndex((0, docs_1.toCategoryIndexMatcherParam)(doc));
});
}
// In addition to the ID, this function also retrieves metadata of the
// linked doc that could be used as fallback values for category metadata
function getCategoryLinkedDocMetadata() {
const link = categoryMetadata?.link;
if (link !== undefined && link?.type !== 'doc') {
// If a link is explicitly specified, we won't apply conventions
return undefined;
}
const id = link
? findDocByLocalId(link.id)?.id ?? getDoc(link.id).id
: findConventionalCategoryDocLink()?.id;
if (!id) {
return undefined;
}
const doc = getDoc(id);
return {
id,
position: doc.sidebarPosition,
label: doc.frontMatter.sidebar_label ?? doc.title,
customProps: doc.frontMatter.sidebar_custom_props,
className: doc.frontMatter.sidebar_class_name,
};
}
const categoryLinkedDoc = getCategoryLinkedDocMetadata();
const link = categoryLinkedDoc
? {
type: 'doc',
id: categoryLinkedDoc.id, // We "remap" a potentially "local id" to a "qualified id"
}
: categoryMetadata?.link;
// If a doc is linked, remove it from the category subItems
const items = allItems.filter((item) => !(item.type === 'doc' && item.id === categoryLinkedDoc?.id));
const className = categoryMetadata?.className ?? categoryLinkedDoc?.className;
const customProps = categoryMetadata?.customProps ?? categoryLinkedDoc?.customProps;
const { filename, numberPrefix } = numberPrefixParser(folderName);
return {
type: 'category',
label: categoryMetadata?.label ?? categoryLinkedDoc?.label ?? filename,
collapsible: categoryMetadata?.collapsible,
collapsed: categoryMetadata?.collapsed,
position: categoryMetadata?.position ??
categoryLinkedDoc?.position ??
numberPrefix,
source: folderName,
...(customProps !== undefined && { customProps }),
...(className !== undefined && { className }),
items,
...(link && { link }),
};
}
function dirToItem(dir, // The directory item to be transformed.
itemKey, // File/folder name; for categories, it's used to generate the next `relativePath`.
fullPath) {
return typeof dir === 'object'
? createCategoryItem(dir, fullPath, itemKey)
: createDocItem(dir, fullPath, itemKey);
}
return Object.entries(fsModel).map(([key, content]) => dirToItem(content, key, key));
}
/**
* Step 4. Recursively sort the categories/docs + remove the "position"
* attribute from final output. Note: the "position" is only used to sort
* "inside" a sidebar slice. It is not used to sort across multiple
* consecutive sidebar slices (i.e. a whole category composed of multiple
* autogenerated items)
*/
function sortItems(sidebarItems) {
const processedSidebarItems = sidebarItems.map((item) => {
if (item.type === 'category') {
return { ...item, items: sortItems(item.items) };
}
return item;
});
const sortedSidebarItems = lodash_1.default.sortBy(processedSidebarItems, [
'position',
'source',
]);
return sortedSidebarItems.map(({ position, source, ...item }) => item);
}
// TODO: the whole code is designed for pipeline operator
const docs = getAutogenDocs();
const fsModel = treeify(docs);
const sidebarWithPosition = generateSidebar(fsModel);
const sortedSidebar = sortItems(sidebarWithPosition);
return sortedSidebar;
};
exports.DefaultSidebarItemsGenerator = DefaultSidebarItemsGenerator;