mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-19 03:02:30 +02:00
chore: clean up ESLint config, enable a few rules (#6514)
* chore: clean up ESLint config, enable a few rules * enable max-len for comments * fix build
This commit is contained in:
parent
b8ccb869f1
commit
aa446b7a9c
167 changed files with 1157 additions and 960 deletions
|
@ -205,7 +205,8 @@ describe('processSidebars', () => {
|
|||
link: {
|
||||
type: 'generated-index',
|
||||
slug: 'generated-cat-index-slug',
|
||||
// @ts-expect-error: TODO undefined should be allowed here, typing error needing refactor
|
||||
// @ts-expect-error: TODO undefined should be allowed here,
|
||||
// typing error needing refactor
|
||||
permalink: undefined,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -47,7 +47,8 @@ export type CategoryMetadataFile = {
|
|||
className?: string;
|
||||
link?: SidebarItemCategoryLinkConfig | null;
|
||||
|
||||
// TODO should we allow "items" here? how would this work? would an "autogenerated" type be allowed?
|
||||
// TODO should we allow "items" here? how would this work? would an
|
||||
// "autogenerated" type be allowed?
|
||||
// This mkdocs plugin do something like that: https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin/
|
||||
// cf comment: https://github.com/facebook/docusaurus/issues/3464#issuecomment-784765199
|
||||
};
|
||||
|
@ -56,16 +57,20 @@ type WithPosition<T> = T & {position?: number};
|
|||
|
||||
/**
|
||||
* A representation of the fs structure. For each object entry:
|
||||
* If it's a folder, the key is the directory name, and value is the directory content;
|
||||
* If it's a doc file, the key is the doc id prefixed with '$doc$/', and value is null
|
||||
* If it's a folder, the key is the directory name, and value is the directory
|
||||
* content; If it's a doc file, the key is the doc id prefixed with '$doc$/',
|
||||
* and value is null
|
||||
*/
|
||||
type Dir = {
|
||||
[item: string]: Dir | null;
|
||||
};
|
||||
|
||||
// TODO I now believe we should read all the category metadata files ahead of time: we may need this metadata to customize docs metadata
|
||||
// Example use-case being able to disable number prefix parsing at the folder level, or customize the default route path segment for an intermediate directory...
|
||||
// TODO later if there is `CategoryFolder/with-category-name-doc.md`, we may want to read the metadata as yaml on it
|
||||
// TODO I now believe we should read all the category metadata files ahead of
|
||||
// time: we may need this metadata to customize docs metadata
|
||||
// Example use-case being able to disable number prefix parsing at the folder
|
||||
// level, or customize the default base slug for an intermediate directory
|
||||
// TODO later if there is `CategoryFolder/with-category-name-doc.md`, we may
|
||||
// want to read the metadata as yaml on it
|
||||
// see https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
|
||||
async function readCategoryMetadataFile(
|
||||
categoryDirPath: string,
|
||||
|
@ -142,7 +147,8 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|||
* Step 2. Turn the linear file list into a tree structure.
|
||||
*/
|
||||
function treeify(docs: SidebarItemsGeneratorDoc[]): Dir {
|
||||
// Get the category breadcrumb of a doc (relative to the dir of the autogenerated sidebar item)
|
||||
// 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()
|
||||
|
@ -169,7 +175,7 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|||
}
|
||||
|
||||
/**
|
||||
* Step 3. Recursively transform the tree-like file structure to sidebar items.
|
||||
* 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: Dir): Promise<WithPosition<SidebarItem>[]> {
|
||||
|
@ -182,7 +188,8 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|||
type: 'doc',
|
||||
id,
|
||||
position,
|
||||
// We don't want these fields to magically appear in the generated sidebar
|
||||
// We don't want these fields to magically appear in the generated
|
||||
// sidebar
|
||||
...(label !== undefined && {label}),
|
||||
...(className !== undefined && {className}),
|
||||
};
|
||||
|
@ -225,13 +232,12 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|||
if (link !== undefined) {
|
||||
if (link && link.type === 'doc') {
|
||||
return findDocByLocalId(link.id)?.id || getDoc(link.id).id;
|
||||
} else {
|
||||
// We don't continue for other link types on purpose!
|
||||
// IE if user decide to use type "generated-index", we should not pick a README.md file as the linked doc
|
||||
return undefined;
|
||||
}
|
||||
// If a link is explicitly specified, we won't apply conventions
|
||||
return undefined;
|
||||
}
|
||||
// Apply default convention to pick index.md, README.md or <categoryName>.md as the category doc
|
||||
// Apply default convention to pick index.md, README.md or
|
||||
// <categoryName>.md as the category doc
|
||||
return findConventionalCategoryDocLink()?.id;
|
||||
}
|
||||
|
||||
|
@ -279,10 +285,11 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 (ie a whole Category
|
||||
* composed of multiple autogenerated items)
|
||||
* 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: WithPosition<SidebarItem>[]): SidebarItem[] {
|
||||
const processedSidebarItems = sidebarItems.map((item) => {
|
||||
|
@ -298,7 +305,6 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|||
return sortedSidebarItems.map(({position, ...item}) => item);
|
||||
}
|
||||
// TODO: the whole code is designed for pipeline operator
|
||||
// return getAutogenDocs() |> treeify |> await generateSidebar(^) |> sortItems;
|
||||
const docs = getAutogenDocs();
|
||||
const fsModel = treeify(docs);
|
||||
const sidebarWithPosition = await generateSidebar(fsModel);
|
||||
|
|
|
@ -60,7 +60,8 @@ function toSidebarItemsGeneratorVersion(
|
|||
return pick(version, ['versionName', 'contentPath']);
|
||||
}
|
||||
|
||||
// Handle the generation of autogenerated sidebar items and other post-processing checks
|
||||
// Handle the generation of autogenerated sidebar items and other
|
||||
// post-processing checks
|
||||
async function processSidebar(
|
||||
unprocessedSidebar: NormalizedSidebar,
|
||||
params: SidebarProcessorParams,
|
||||
|
@ -91,7 +92,8 @@ async function processSidebar(
|
|||
async function processAutoGeneratedItem(
|
||||
item: SidebarItemAutogenerated,
|
||||
): Promise<SidebarItem[]> {
|
||||
// TODO the returned type can't be trusted in practice (generator can be user-provided)
|
||||
// TODO the returned type can't be trusted in practice (generator can be
|
||||
// user-provided)
|
||||
const generatedItems = await sidebarItemsGenerator({
|
||||
item,
|
||||
numberPrefixParser,
|
||||
|
@ -106,7 +108,8 @@ async function processSidebar(
|
|||
normalizeItem(generatedItem, {...params, ...sidebarOptions}),
|
||||
);
|
||||
|
||||
// Process again... weird but sidebar item generated might generate some auto-generated items?
|
||||
// Process again... weird but sidebar item generated might generate some
|
||||
// auto-generated items?
|
||||
return processItems(generatedItemsNormalized);
|
||||
}
|
||||
|
||||
|
|
|
@ -205,7 +205,8 @@ export type SidebarItemsGenerator = (
|
|||
Promise<SidebarItem[]>;
|
||||
// Promise<SidebarItemConfig[]>;
|
||||
|
||||
// Also inject the default generator to conveniently wrap/enhance/sort the default sidebar gen logic
|
||||
// Also inject the default generator to conveniently wrap/enhance/sort the
|
||||
// default sidebar gen logic
|
||||
// see https://github.com/facebook/docusaurus/issues/4640#issuecomment-822292320
|
||||
export type SidebarItemsGeneratorOptionArgs = {
|
||||
defaultSidebarItemsGenerator: SidebarItemsGenerator;
|
||||
|
|
|
@ -16,7 +16,6 @@ import type {
|
|||
SidebarCategoriesShorthand,
|
||||
SidebarItemConfig,
|
||||
SidebarItemCategoryWithGeneratedIndex,
|
||||
SidebarItemCategoryWithLink,
|
||||
SidebarNavigationItem,
|
||||
} from './types';
|
||||
|
||||
|
@ -46,8 +45,11 @@ export function transformSidebarItems(
|
|||
return sidebar.map(transformRecursive);
|
||||
}
|
||||
|
||||
// Flatten sidebar items into a single flat array (containing categories/docs on the same level)
|
||||
// /!\ order matters (useful for next/prev nav), top categories appear before their child elements
|
||||
/**
|
||||
* Flatten sidebar items into a single flat array (containing categories/docs on
|
||||
* the same level). Order matters (useful for next/prev nav), top categories
|
||||
* appear before their child elements
|
||||
*/
|
||||
function flattenSidebarItems(items: SidebarItem[]): SidebarItem[] {
|
||||
function flattenRecursive(item: SidebarItem): SidebarItem[] {
|
||||
return item.type === 'category'
|
||||
|
@ -196,34 +198,33 @@ export function createSidebarsUtils(sidebars: Sidebars): SidebarsUtils {
|
|||
sidebarName = getSidebarNameByDocId(docId);
|
||||
}
|
||||
|
||||
if (sidebarName) {
|
||||
if (!sidebarNameToNavigationItems[sidebarName]) {
|
||||
throw new Error(
|
||||
`Doc with ID ${docId} wants to display sidebar ${sidebarName} but a sidebar with this name doesn't exist`,
|
||||
);
|
||||
}
|
||||
const navigationItems = sidebarNameToNavigationItems[sidebarName];
|
||||
const currentItemIndex = navigationItems.findIndex((item) => {
|
||||
if (item.type === 'doc') {
|
||||
return item.id === docId;
|
||||
}
|
||||
if (item.type === 'category' && item.link.type === 'doc') {
|
||||
return item.link.id === docId;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (currentItemIndex === -1) {
|
||||
return {sidebarName, next: undefined, previous: undefined};
|
||||
}
|
||||
|
||||
const {previous, next} = getElementsAround(
|
||||
navigationItems,
|
||||
currentItemIndex,
|
||||
);
|
||||
return {sidebarName, previous, next};
|
||||
} else {
|
||||
if (!sidebarName) {
|
||||
return emptySidebarNavigation();
|
||||
}
|
||||
if (!sidebarNameToNavigationItems[sidebarName]) {
|
||||
throw new Error(
|
||||
`Doc with ID ${docId} wants to display sidebar ${sidebarName} but a sidebar with this name doesn't exist`,
|
||||
);
|
||||
}
|
||||
const navigationItems = sidebarNameToNavigationItems[sidebarName];
|
||||
const currentItemIndex = navigationItems.findIndex((item) => {
|
||||
if (item.type === 'doc') {
|
||||
return item.id === docId;
|
||||
}
|
||||
if (item.type === 'category' && item.link.type === 'doc') {
|
||||
return item.link.id === docId;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (currentItemIndex === -1) {
|
||||
return {sidebarName, next: undefined, previous: undefined};
|
||||
}
|
||||
|
||||
const {previous, next} = getElementsAround(
|
||||
navigationItems,
|
||||
currentItemIndex,
|
||||
);
|
||||
return {sidebarName, previous, next};
|
||||
}
|
||||
|
||||
function getCategoryGeneratedIndexList(): SidebarItemCategoryWithGeneratedIndex[] {
|
||||
|
@ -237,8 +238,10 @@ export function createSidebarsUtils(sidebars: Sidebars): SidebarsUtils {
|
|||
});
|
||||
}
|
||||
|
||||
// We identity the category generated index by its permalink (should be unique)
|
||||
// More reliable than using object identity
|
||||
/**
|
||||
* We identity the category generated index by its permalink (should be
|
||||
* unique). More reliable than using object identity
|
||||
*/
|
||||
function getCategoryGeneratedIndexNavigation(
|
||||
categoryGeneratedIndexPermalink: string,
|
||||
): SidebarNavigation {
|
||||
|
@ -257,19 +260,18 @@ export function createSidebarsUtils(sidebars: Sidebars): SidebarsUtils {
|
|||
navigationItems.find(isCurrentCategoryGeneratedIndexItem),
|
||||
)?.[0];
|
||||
|
||||
if (sidebarName) {
|
||||
const navigationItems = sidebarNameToNavigationItems[sidebarName];
|
||||
const currentItemIndex = navigationItems.findIndex(
|
||||
isCurrentCategoryGeneratedIndexItem,
|
||||
);
|
||||
const {previous, next} = getElementsAround(
|
||||
navigationItems,
|
||||
currentItemIndex,
|
||||
);
|
||||
return {sidebarName, previous, next};
|
||||
} else {
|
||||
if (!sidebarName) {
|
||||
return emptySidebarNavigation();
|
||||
}
|
||||
const navigationItems = sidebarNameToNavigationItems[sidebarName];
|
||||
const currentItemIndex = navigationItems.findIndex(
|
||||
isCurrentCategoryGeneratedIndexItem,
|
||||
);
|
||||
const {previous, next} = getElementsAround(
|
||||
navigationItems,
|
||||
currentItemIndex,
|
||||
);
|
||||
return {sidebarName, previous, next};
|
||||
}
|
||||
|
||||
function checkSidebarsDocIds(validDocIds: string[], sidebarFilePath: string) {
|
||||
|
@ -322,11 +324,10 @@ Available document ids are:
|
|||
slug: item.link.slug,
|
||||
label: item.label,
|
||||
};
|
||||
} else {
|
||||
const firstSubItem = getFirstLink(item.items);
|
||||
if (firstSubItem) {
|
||||
return firstSubItem;
|
||||
}
|
||||
}
|
||||
const firstSubItem = getFirstLink(item.items);
|
||||
if (firstSubItem) {
|
||||
return firstSubItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -371,18 +372,6 @@ export function toNavigationLink(
|
|||
return doc;
|
||||
}
|
||||
|
||||
function handleCategory(category: SidebarItemCategoryWithLink): DocNavLink {
|
||||
if (category.link.type === 'doc') {
|
||||
return toDocNavigationLink(getDocById(category.link.id));
|
||||
} else if (category.link.type === 'generated-index') {
|
||||
return {
|
||||
title: category.label,
|
||||
permalink: category.link.permalink,
|
||||
};
|
||||
} else {
|
||||
throw new Error('unexpected category link type');
|
||||
}
|
||||
}
|
||||
if (!navigationItem) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -390,8 +379,15 @@ export function toNavigationLink(
|
|||
if (navigationItem.type === 'doc') {
|
||||
return toDocNavigationLink(getDocById(navigationItem.id));
|
||||
} else if (navigationItem.type === 'category') {
|
||||
return handleCategory(navigationItem);
|
||||
} else {
|
||||
throw new Error('unexpected navigation item');
|
||||
if (navigationItem.link.type === 'doc') {
|
||||
return toDocNavigationLink(getDocById(navigationItem.link.id));
|
||||
} else if (navigationItem.link.type === 'generated-index') {
|
||||
return {
|
||||
title: navigationItem.label,
|
||||
permalink: navigationItem.link.permalink,
|
||||
};
|
||||
}
|
||||
throw new Error('unexpected category link type');
|
||||
}
|
||||
throw new Error('unexpected navigation item');
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ import {isCategoriesShorthand} from './utils';
|
|||
import type {CategoryMetadataFile} from './generator';
|
||||
|
||||
// NOTE: we don't add any default values during validation on purpose!
|
||||
// Config types are exposed to users for typechecking and we use the same type in normalization
|
||||
// Config types are exposed to users for typechecking and we use the same type
|
||||
// in normalization
|
||||
|
||||
const sidebarItemBaseSchema = Joi.object<SidebarItemBase>({
|
||||
className: Joi.string(),
|
||||
|
@ -71,7 +72,8 @@ const sidebarItemCategoryLinkSchema = Joi.object<SidebarItemCategoryLink>()
|
|||
then: Joi.object<SidebarItemCategoryLinkGeneratedIndex>({
|
||||
type: 'generated-index',
|
||||
slug: Joi.string().optional(),
|
||||
// permalink: Joi.string().optional(), // No, this one is not in the user config, only in the normalized version
|
||||
// This one is not in the user config, only in the normalized version
|
||||
// permalink: Joi.string().optional(),
|
||||
title: Joi.string().optional(),
|
||||
description: Joi.string().optional(),
|
||||
image: Joi.string().optional(),
|
||||
|
@ -132,7 +134,8 @@ function validateSidebarItem(item: unknown): asserts item is SidebarItemConfig {
|
|||
return;
|
||||
}
|
||||
// TODO: remove once with proper Joi support
|
||||
// Because we can't use Joi to validate nested items (see above), we do it manually
|
||||
// Because we can't use Joi to validate nested items (see above), we do it
|
||||
// manually
|
||||
if (isCategoriesShorthand(item as SidebarItemConfig)) {
|
||||
Object.values(item as SidebarCategoriesShorthand).forEach((category) =>
|
||||
category.forEach(validateSidebarItem),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue