mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-13 00:57:53 +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
|
@ -95,7 +95,8 @@ Entries created:
|
|||
},
|
||||
|
||||
expectSnapshot: () => {
|
||||
// Sort the route config like in src/server/plugins/index.ts for consistent snapshot ordering
|
||||
// Sort the route config like in src/server/plugins/index.ts for
|
||||
// consistent snapshot ordering
|
||||
sortConfig(routeConfigs);
|
||||
expect(routeConfigs).not.toEqual([]);
|
||||
expect(routeConfigs).toMatchSnapshot('route config');
|
||||
|
@ -249,7 +250,8 @@ describe('simple website', () => {
|
|||
.spyOn(cliDocs, 'cliDocsVersionCommand')
|
||||
.mockImplementation();
|
||||
const cli = new commander.Command();
|
||||
// @ts-expect-error: in actual usage, we pass the static commander instead of the new command
|
||||
// @ts-expect-error: in actual usage, we pass the static commander instead
|
||||
// of the new command
|
||||
plugin.extendCli!(cli);
|
||||
cli.parse(['node', 'test', 'docs:version', '1.0.0']);
|
||||
expect(mock).toHaveBeenCalledTimes(1);
|
||||
|
@ -373,7 +375,8 @@ describe('versioned website', () => {
|
|||
.spyOn(cliDocs, 'cliDocsVersionCommand')
|
||||
.mockImplementation();
|
||||
const cli = new commander.Command();
|
||||
// @ts-expect-error: in actual usage, we pass the static commander instead of the new command
|
||||
// @ts-expect-error: in actual usage, we pass the static commander instead
|
||||
// of the new command
|
||||
plugin.extendCli!(cli);
|
||||
cli.parse(['node', 'test', 'docs:version', '2.0.0']);
|
||||
expect(mock).toHaveBeenCalledTimes(1);
|
||||
|
@ -522,7 +525,8 @@ describe('versioned website (community)', () => {
|
|||
.spyOn(cliDocs, 'cliDocsVersionCommand')
|
||||
.mockImplementation();
|
||||
const cli = new commander.Command();
|
||||
// @ts-expect-error: in actual usage, we pass the static commander instead of the new command
|
||||
// @ts-expect-error: in actual usage, we pass the static commander instead
|
||||
// of the new command
|
||||
plugin.extendCli!(cli);
|
||||
cli.parse(['node', 'test', `docs:version:${pluginId}`, '2.0.0']);
|
||||
expect(mock).toHaveBeenCalledTimes(1);
|
||||
|
@ -726,7 +730,8 @@ describe('site with partial autogenerated sidebars', () => {
|
|||
const {content} = await loadSite();
|
||||
const version = content.loadedVersions[0];
|
||||
|
||||
// Only looking at the docs of the autogen sidebar, others metadata should not be affected
|
||||
// Only looking at the docs of the autogen sidebar, others metadata should
|
||||
// not be affected
|
||||
|
||||
expect(getDocById(version, 'API/api-end')).toMatchSnapshot();
|
||||
expect(getDocById(version, 'API/api-overview')).toMatchSnapshot();
|
||||
|
@ -803,7 +808,8 @@ describe('site with custom sidebar items generator', () => {
|
|||
const generatorArg: SidebarItemsGeneratorOptionArgs =
|
||||
customSidebarItemsGeneratorMock.mock.calls[0][0];
|
||||
|
||||
// Make test pass even if docs are in different order and paths are absolutes
|
||||
// Make test pass even if docs are in different order and paths are
|
||||
// absolutes
|
||||
function makeDeterministic(
|
||||
arg: SidebarItemsGeneratorOptionArgs,
|
||||
): SidebarItemsGeneratorOptionArgs {
|
||||
|
|
|
@ -32,10 +32,12 @@ function createVersionedSidebarFile({
|
|||
version: string;
|
||||
}) {
|
||||
// Load current sidebar and create a new versioned sidebars file (if needed).
|
||||
// Note: we don't need the sidebars file to be normalized: it's ok to let plugin option changes to impact older, versioned sidebars
|
||||
// Note: we don't need the sidebars file to be normalized: it's ok to let
|
||||
// plugin option changes to impact older, versioned sidebars
|
||||
const sidebars = loadSidebarsFile(sidebarPath);
|
||||
|
||||
// Do not create a useless versioned sidebars file if sidebars file is empty or sidebars are disabled/false)
|
||||
// Do not create a useless versioned sidebars file if sidebars file is empty
|
||||
// or sidebars are disabled/false)
|
||||
const shouldCreateVersionedSidebarFile = Object.keys(sidebars).length > 0;
|
||||
|
||||
if (shouldCreateVersionedSidebarFile) {
|
||||
|
|
|
@ -28,7 +28,7 @@ export function getActivePlugin(
|
|||
options: GetActivePluginOptions = {},
|
||||
): ActivePlugin | undefined {
|
||||
const activeEntry = Object.entries(allPluginDatas)
|
||||
// A quick route sorting: '/android/foo' should match '/android' instead of '/'
|
||||
// Route sorting: '/android/foo' should match '/android' instead of '/'
|
||||
.sort((a, b) => b[1].path.localeCompare(a[1].path))
|
||||
.find(
|
||||
([, pluginData]) =>
|
||||
|
@ -67,7 +67,7 @@ export const getActiveVersion = (
|
|||
): GlobalVersion | undefined => {
|
||||
const lastVersion = getLatestVersion(data);
|
||||
// Last version is a route like /docs/*,
|
||||
// we need to try to match it last or it would match /docs/version-1.0/* as well
|
||||
// we need to match it last or it would match /docs/version-1.0/* as well
|
||||
const orderedVersionsMetadata = [
|
||||
...data.versions.filter((version) => version !== lastVersion),
|
||||
lastVersion,
|
||||
|
|
|
@ -27,12 +27,13 @@ import type {
|
|||
GetActivePluginOptions,
|
||||
} from '@docusaurus/plugin-content-docs/client';
|
||||
|
||||
// Important to use a constant object to avoid React useEffect executions etc...,
|
||||
// Important to use a constant object to avoid React useEffect executions etc.
|
||||
// see https://github.com/facebook/docusaurus/issues/5089
|
||||
const StableEmptyObject = {};
|
||||
|
||||
// Not using useAllPluginInstancesData() because in blog-only mode, docs hooks are still used by the theme
|
||||
// We need a fail-safe fallback when the docs plugin is not in use
|
||||
// Not using useAllPluginInstancesData() because in blog-only mode, docs hooks
|
||||
// are still used by the theme. We need a fail-safe fallback when the docs
|
||||
// plugin is not in use
|
||||
export const useAllDocsData = (): Record<string, GlobalPluginData> =>
|
||||
// useAllPluginInstancesData('docusaurus-plugin-content-docs');
|
||||
useGlobalData()['docusaurus-plugin-content-docs'] ?? StableEmptyObject;
|
||||
|
|
|
@ -139,7 +139,8 @@ function doProcessDocMetadata({
|
|||
const {
|
||||
custom_edit_url: customEditURL,
|
||||
|
||||
// Strip number prefixes by default (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc) by default,
|
||||
// Strip number prefixes by default
|
||||
// (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc)
|
||||
// but allow to disable this behavior with front matter
|
||||
parse_number_prefixes: parseNumberPrefixes = true,
|
||||
} = frontMatter;
|
||||
|
@ -164,7 +165,8 @@ function doProcessDocMetadata({
|
|||
throw new Error(`Document id "${baseID}" cannot include slash.`);
|
||||
}
|
||||
|
||||
// For autogenerated sidebars, sidebar position can come from filename number prefix or front matter
|
||||
// For autogenerated sidebars, sidebar position can come from filename number
|
||||
// prefix or front matter
|
||||
const sidebarPosition: number | undefined =
|
||||
frontMatter.sidebar_position ?? numberPrefix;
|
||||
|
||||
|
@ -205,8 +207,9 @@ function doProcessDocMetadata({
|
|||
numberPrefixParser: options.numberPrefixParser,
|
||||
});
|
||||
|
||||
// Note: the title is used by default for page title, sidebar label, pagination buttons...
|
||||
// frontMatter.title should be used in priority over contentTitle (because it can contain markdown/JSX syntax)
|
||||
// Note: the title is used by default for page title, sidebar label,
|
||||
// pagination buttons... frontMatter.title should be used in priority over
|
||||
// contentTitle (because it can contain markdown/JSX syntax)
|
||||
const title: string = frontMatter.title ?? contentTitle ?? baseID;
|
||||
|
||||
const description: string = frontMatter.description ?? excerpt ?? '';
|
||||
|
@ -233,9 +236,8 @@ function doProcessDocMetadata({
|
|||
? versionMetadata.versionEditUrlLocalized
|
||||
: versionMetadata.versionEditUrl;
|
||||
return getEditUrl(relativeFilePath, baseVersionEditUrl);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Assign all of object properties during instantiation (if possible) for
|
||||
|
@ -361,9 +363,8 @@ export function getMainDocId({
|
|||
doc.id === firstDocIdOfFirstSidebar ||
|
||||
doc.unversionedId === firstDocIdOfFirstSidebar,
|
||||
)!;
|
||||
} else {
|
||||
return docs[0];
|
||||
}
|
||||
return docs[0];
|
||||
}
|
||||
|
||||
return getMainDoc().unversionedId;
|
||||
|
@ -407,7 +408,8 @@ export function toCategoryIndexMatcherParam({
|
|||
}
|
||||
|
||||
/**
|
||||
* guides/sidebar/autogenerated.md -> 'autogenerated', '.md', ['sidebar', 'guides']
|
||||
* `guides/sidebar/autogenerated.md` ->
|
||||
* `'autogenerated', '.md', ['sidebar', 'guides']`
|
||||
*/
|
||||
export function splitPath(str: string): {
|
||||
/**
|
||||
|
@ -428,15 +430,17 @@ export function splitPath(str: string): {
|
|||
}
|
||||
|
||||
// Return both doc ids
|
||||
// TODO legacy retro-compatibility due to old versioned sidebars using versioned doc ids
|
||||
// ("id" should be removed & "versionedId" should be renamed to "id")
|
||||
// TODO legacy retro-compatibility due to old versioned sidebars using
|
||||
// versioned doc ids ("id" should be removed & "versionedId" should be renamed
|
||||
// to "id")
|
||||
export function getDocIds(doc: DocMetadataBase): [string, string] {
|
||||
return [doc.unversionedId, doc.id];
|
||||
}
|
||||
|
||||
// docs are indexed by both versioned and unversioned ids at the same time
|
||||
// TODO legacy retro-compatibility due to old versioned sidebars using versioned doc ids
|
||||
// ("id" should be removed & "versionedId" should be renamed to "id")
|
||||
// TODO legacy retro-compatibility due to old versioned sidebars using
|
||||
// versioned doc ids ("id" should be removed & "versionedId" should be renamed
|
||||
// to "id")
|
||||
export function createDocsByIdIndex<
|
||||
Doc extends {id: string; unversionedId: string},
|
||||
>(docs: Doc[]): Record<string, Doc> {
|
||||
|
|
|
@ -8,15 +8,16 @@
|
|||
import type {NumberPrefixParser} from '@docusaurus/plugin-content-docs';
|
||||
|
||||
// Best-effort to avoid parsing some patterns as number prefix
|
||||
const IgnoredPrefixPatterns = (function () {
|
||||
const IgnoredPrefixPatterns = (() => {
|
||||
// ignore common date-like patterns: https://github.com/facebook/docusaurus/issues/4640
|
||||
const DateLikePrefixRegex =
|
||||
/^((\d{2}|\d{4})[-_.]\d{2}([-_.](\d{2}|\d{4}))?)(.*)$/;
|
||||
|
||||
// ignore common versioning patterns: https://github.com/facebook/docusaurus/issues/4653
|
||||
// note: we could try to parse float numbers in filenames but that is probably not worth it
|
||||
// as a version such as "8.0" can be interpreted as both a version and a float
|
||||
// User can configure his own NumberPrefixParser if he wants 8.0 to be interpreted as a float
|
||||
// note: we could try to parse float numbers in filenames but that is
|
||||
// probably not worth it as a version such as "8.0" can be interpreted as both
|
||||
// a version and a float. User can configure her own NumberPrefixParser if
|
||||
// she wants 8.0 to be interpreted as a float
|
||||
const VersionLikePrefixRegex = /^(\d+[-_.]\d+)(.*)$/;
|
||||
|
||||
return new RegExp(
|
||||
|
|
|
@ -148,8 +148,9 @@ export function validateOptions({
|
|||
let options = userOptions;
|
||||
|
||||
if (options.sidebarCollapsible === false) {
|
||||
// When sidebarCollapsible=false and sidebarCollapsed=undefined, we don't want to have the inconsistency warning
|
||||
// We let options.sidebarCollapsible become the default value for options.sidebarCollapsed
|
||||
// When sidebarCollapsible=false and sidebarCollapsed=undefined, we don't
|
||||
// want to have the inconsistency warning. We let options.sidebarCollapsible
|
||||
// become the default value for options.sidebarCollapsed
|
||||
if (typeof options.sidebarCollapsed === 'undefined') {
|
||||
options = {
|
||||
...options,
|
||||
|
|
|
@ -45,7 +45,8 @@ declare module '@docusaurus/plugin-content-docs' {
|
|||
sidebarPath?: string | false | undefined;
|
||||
};
|
||||
|
||||
// TODO support custom version banner? {type: "error", content: "html content"}
|
||||
// TODO support custom version banner?
|
||||
// {type: "error", content: "html content"}
|
||||
export type VersionBanner = 'unreleased' | 'unmaintained';
|
||||
export type VersionOptions = {
|
||||
path?: string;
|
||||
|
|
|
@ -73,7 +73,8 @@ export async function createCategoryGeneratedIndexRoutes({
|
|||
modules: {
|
||||
categoryGeneratedIndex: aliasedSource(propData),
|
||||
},
|
||||
// Same as doc, this sidebar route attribute permits to associate this subpage to the given sidebar
|
||||
// Same as doc, this sidebar route attribute permits to associate this
|
||||
// subpage to the given sidebar
|
||||
...(sidebar && {sidebar}),
|
||||
};
|
||||
}
|
||||
|
@ -109,7 +110,8 @@ export async function createDocRoutes({
|
|||
content: metadataItem.source,
|
||||
},
|
||||
// Because the parent (DocPage) comp need to access it easily
|
||||
// This permits to render the sidebar once without unmount/remount when navigating (and preserve sidebar state)
|
||||
// This permits to render the sidebar once without unmount/remount when
|
||||
// navigating (and preserve sidebar state)
|
||||
...(metadataItem.sidebar && {
|
||||
sidebar: metadataItem.sidebar,
|
||||
}),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -48,17 +48,16 @@ export default function getSlug({
|
|||
function computeSlug(): string {
|
||||
if (frontMatterSlug?.startsWith('/')) {
|
||||
return frontMatterSlug;
|
||||
} else {
|
||||
const dirNameSlug = getDirNameSlug();
|
||||
if (
|
||||
!frontMatterSlug &&
|
||||
isCategoryIndex(toCategoryIndexMatcherParam({source, sourceDirName}))
|
||||
) {
|
||||
return dirNameSlug;
|
||||
}
|
||||
const baseSlug = frontMatterSlug || baseID;
|
||||
return resolvePathname(baseSlug, getDirNameSlug());
|
||||
}
|
||||
const dirNameSlug = getDirNameSlug();
|
||||
if (
|
||||
!frontMatterSlug &&
|
||||
isCategoryIndex(toCategoryIndexMatcherParam({source, sourceDirName}))
|
||||
) {
|
||||
return dirNameSlug;
|
||||
}
|
||||
const baseSlug = frontMatterSlug || baseID;
|
||||
return resolvePathname(baseSlug, getDirNameSlug());
|
||||
}
|
||||
|
||||
function ensureValidSlug(slug: string): string {
|
||||
|
|
|
@ -31,11 +31,10 @@ import {CURRENT_VERSION_NAME} from './constants';
|
|||
function getVersionFileName(versionName: string): string {
|
||||
if (versionName === CURRENT_VERSION_NAME) {
|
||||
return versionName;
|
||||
} else {
|
||||
// I don't like this "version-" prefix,
|
||||
// but it's for consistency with site/versioned_docs
|
||||
return `version-${versionName}`;
|
||||
}
|
||||
// I don't like this "version-" prefix,
|
||||
// but it's for consistency with site/versioned_docs
|
||||
return `version-${versionName}`;
|
||||
}
|
||||
|
||||
// TODO legacy, the sidebar name is like "version-2.0.0-alpha.66/docs"
|
||||
|
@ -68,7 +67,8 @@ function getDocTranslations(doc: DocMetadata): TranslationFileContent {
|
|||
? {
|
||||
[`${doc.unversionedId}.sidebar_label`]: {
|
||||
message: doc.sidebar_label,
|
||||
description: `The sidebar label for doc with id=${doc.unversionedId}`,
|
||||
description:
|
||||
`The sidebar label for doc with id=${doc.unversionedId}`,
|
||||
},
|
||||
}
|
||||
: undefined),
|
||||
|
@ -253,7 +253,8 @@ function getVersionTranslationFiles(version: LoadedVersion): TranslationFiles {
|
|||
const sidebarsTranslations: TranslationFileContent =
|
||||
getSidebarsTranslations(version);
|
||||
|
||||
// const docsTranslations: TranslationFileContent = getDocsTranslations(version);
|
||||
// const docsTranslations: TranslationFileContent =
|
||||
// getDocsTranslations(version);
|
||||
|
||||
return [
|
||||
{
|
||||
|
|
|
@ -33,11 +33,9 @@ import {resolveSidebarPathOption} from './sidebars';
|
|||
|
||||
// retro-compatibility: no prefix for the default plugin id
|
||||
function addPluginIdPrefix(fileOrDir: string, pluginId: string): string {
|
||||
if (pluginId === DEFAULT_PLUGIN_ID) {
|
||||
return fileOrDir;
|
||||
} else {
|
||||
return `${pluginId}_${fileOrDir}`;
|
||||
}
|
||||
return pluginId === DEFAULT_PLUGIN_ID
|
||||
? fileOrDir
|
||||
: `${pluginId}_${fileOrDir}`;
|
||||
}
|
||||
|
||||
export function getVersionedDocsDirPath(
|
||||
|
@ -96,9 +94,8 @@ async function readVersionsFile(
|
|||
const content = JSON.parse(await fs.readFile(versionsFilePath, 'utf8'));
|
||||
ensureValidVersionArray(content);
|
||||
return content;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function readVersionNames(
|
||||
|
@ -274,15 +271,13 @@ function getDefaultVersionBanner({
|
|||
return null;
|
||||
}
|
||||
// Upcoming versions: unreleased banner
|
||||
else if (
|
||||
if (
|
||||
versionNames.indexOf(versionName) < versionNames.indexOf(lastVersionName)
|
||||
) {
|
||||
return 'unreleased';
|
||||
}
|
||||
// Older versions: display unmaintained banner
|
||||
else {
|
||||
return 'unmaintained';
|
||||
}
|
||||
return 'unmaintained';
|
||||
}
|
||||
|
||||
function getVersionBanner({
|
||||
|
@ -443,8 +438,9 @@ function checkVersionMetadataPaths({
|
|||
);
|
||||
}
|
||||
|
||||
// If the current version defines a path to a sidebar file that does not exist, we throw!
|
||||
// Note: for versioned sidebars, the file may not exist (as we prefer to not create it rather than to create an empty file)
|
||||
// If the current version defines a path to a sidebar file that does not
|
||||
// exist, we throw! Note: for versioned sidebars, the file may not exist (as
|
||||
// we prefer to not create it rather than to create an empty file)
|
||||
// See https://github.com/facebook/docusaurus/issues/3366
|
||||
// See https://github.com/facebook/docusaurus/pull/4775
|
||||
if (
|
||||
|
@ -469,11 +465,10 @@ Please set the docs "sidebarPath" field in your config file to:
|
|||
function getDefaultLastVersionName(versionNames: string[]) {
|
||||
if (versionNames.length === 1) {
|
||||
return versionNames[0];
|
||||
} else {
|
||||
return versionNames.filter(
|
||||
(versionName) => versionName !== CURRENT_VERSION_NAME,
|
||||
)[0];
|
||||
}
|
||||
return versionNames.filter(
|
||||
(versionName) => versionName !== CURRENT_VERSION_NAME,
|
||||
)[0];
|
||||
}
|
||||
|
||||
function checkVersionsOptions(
|
||||
|
@ -544,9 +539,8 @@ function filterVersions(
|
|||
return versionNamesUnfiltered.filter((name) =>
|
||||
(options.onlyIncludeVersions || []).includes(name),
|
||||
);
|
||||
} else {
|
||||
return versionNamesUnfiltered;
|
||||
}
|
||||
return versionNamesUnfiltered;
|
||||
}
|
||||
|
||||
export async function readVersionsMetadata({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue