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:
Joshua Chen 2022-01-31 10:31:24 +08:00 committed by GitHub
parent b8ccb869f1
commit aa446b7a9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
167 changed files with 1157 additions and 960 deletions

View file

@ -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,
},
},

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -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');
}

View file

@ -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),