Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(plugin-docs,theme): refactor docs plugin routes and component tree #7966

Merged
merged 15 commits into from Aug 18, 2022

Large diffs are not rendered by default.

Expand Up @@ -49,7 +49,9 @@ describe('normalizeDocsPluginOptions', () => {
sidebarPath: 'my-sidebar', // Path to sidebar configuration for showing a list of markdown pages.
sidebarItemsGenerator: DefaultSidebarItemsGenerator,
numberPrefixParser: DefaultNumberPrefixParser,
docLayoutComponent: '@theme/DocPage',
docsRootComponent: '@theme/DocsRoot',
docVersionRootComponent: '@theme/DocVersionRoot',
docRootComponent: '@theme/DocRoot',
Comment on lines +52 to +54
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New Root theme component we decided with @Josh-Cena .

@theme/DocPage becomes @theme/DocRoot

Hierarchy is: @theme/DocsRoot > @theme/DocVersionRoot > @theme/DocRoot

docItemComponent: '@theme/DocItem',
docTagDocListComponent: '@theme/DocTagDocListPage',
docTagsListComponent: '@theme/DocTagsListPage',
Expand Down
111 changes: 12 additions & 99 deletions packages/docusaurus-plugin-content-docs/src/index.ts
Expand Up @@ -27,22 +27,18 @@ import {
addDocNavigation,
type DocEnv,
} from './docs';
import {readVersionsMetadata} from './versions';
import {readVersionsMetadata, toFullVersion} from './versions';
import {cliDocsVersionCommand} from './cli';
import {VERSIONS_JSON_FILE} from './constants';
import {toGlobalDataVersion} from './globalData';
import {toTagDocListProp} from './props';
import {getCategoryGeneratedIndexMetadataList} from './categoryGeneratedIndex';
import {
translateLoadedContent,
getLoadedContentTranslationFiles,
} from './translations';
import {getVersionTags} from './tags';
import {createVersionRoutes} from './routes';
import {createAllRoutes} from './routes';
import {createSidebarsUtils} from './sidebars/utils';

import type {
PropTagsListPage,
PluginOptions,
DocMetadataBase,
VersionMetadata,
Expand All @@ -55,7 +51,6 @@ import type {
SourceToPermalink,
DocFile,
DocsMarkdownOption,
VersionTag,
FullVersion,
} from './types';
import type {RuleSetRule} from 'webpack';
Expand Down Expand Up @@ -209,102 +204,20 @@ export default async function pluginContentDocs(
},

async contentLoaded({content, actions}) {
const {loadedVersions} = content;
const {
docLayoutComponent,
docItemComponent,
docCategoryGeneratedIndexComponent,
breadcrumbs,
} = options;
const {addRoute, createData, setGlobalData} = actions;
const versions: FullVersion[] = loadedVersions.map((version) => {
const sidebarsUtils = createSidebarsUtils(version.sidebars);
return {
...version,
sidebarsUtils,
categoryGeneratedIndices: getCategoryGeneratedIndexMetadataList({
docs: version.docs,
sidebarsUtils,
}),
};
const versions: FullVersion[] = content.loadedVersions.map(toFullVersion);

await createAllRoutes({
baseUrl,
versions,
options,
actions,
aliasedSource,
});

async function createVersionTagsRoutes(version: FullVersion) {
const versionTags = getVersionTags(version.docs);

// TODO tags should be a sub route of the version route
async function createTagsListPage() {
const tagsProp: PropTagsListPage['tags'] = Object.values(
versionTags,
).map((tagValue) => ({
label: tagValue.label,
permalink: tagValue.permalink,
count: tagValue.docIds.length,
}));

// Only create /tags page if there are tags.
if (tagsProp.length > 0) {
const tagsPropPath = await createData(
`${docuHash(`tags-list-${version.versionName}-prop`)}.json`,
JSON.stringify(tagsProp, null, 2),
);
addRoute({
path: version.tagsPath,
exact: true,
component: options.docTagsListComponent,
modules: {
tags: aliasedSource(tagsPropPath),
},
});
}
}

// TODO tags should be a sub route of the version route
async function createTagDocListPage(tag: VersionTag) {
const tagProps = toTagDocListProp({
allTagsPath: version.tagsPath,
tag,
docs: version.docs,
});
const tagPropPath = await createData(
`${docuHash(`tag-${tag.permalink}`)}.json`,
JSON.stringify(tagProps, null, 2),
);
addRoute({
path: tag.permalink,
component: options.docTagDocListComponent,
exact: true,
modules: {
tag: aliasedSource(tagPropPath),
},
});
}

await createTagsListPage();
await Promise.all(Object.values(versionTags).map(createTagDocListPage));
}

await Promise.all(
versions.map((version) =>
createVersionRoutes({
version,
docItemComponent,
docLayoutComponent,
docCategoryGeneratedIndexComponent,
pluginId,
aliasedSource,
actions,
}),
),
);

// TODO tags should be a sub route of the version route
await Promise.all(versions.map(createVersionTagsRoutes));

setGlobalData({
actions.setGlobalData({
path: normalizeUrl([baseUrl, options.routeBasePath]),
versions: versions.map(toGlobalDataVersion),
breadcrumbs,
breadcrumbs: options.breadcrumbs,
});
},

Expand Down
10 changes: 8 additions & 2 deletions packages/docusaurus-plugin-content-docs/src/options.ts
Expand Up @@ -30,7 +30,9 @@ export const DEFAULT_OPTIONS: Omit<PluginOptions, 'id' | 'sidebarPath'> = {
exclude: GlobExcludeDefault,
sidebarItemsGenerator: DefaultSidebarItemsGenerator,
numberPrefixParser: DefaultNumberPrefixParser,
docLayoutComponent: '@theme/DocPage',
docsRootComponent: '@theme/DocsRoot',
docVersionRootComponent: '@theme/DocVersionRoot',
docRootComponent: '@theme/DocRoot',
docItemComponent: '@theme/DocItem',
docTagDocListComponent: '@theme/DocTagDocListPage',
docTagsListComponent: '@theme/DocTagsListPage',
Expand Down Expand Up @@ -104,7 +106,11 @@ const OptionsSchema = Joi.object<PluginOptions>({
}),
)
.default(() => DEFAULT_OPTIONS.numberPrefixParser),
docLayoutComponent: Joi.string().default(DEFAULT_OPTIONS.docLayoutComponent),
docsRootComponent: Joi.string().default(DEFAULT_OPTIONS.docsRootComponent),
docVersionRootComponent: Joi.string().default(
DEFAULT_OPTIONS.docVersionRootComponent,
),
docRootComponent: Joi.string().default(DEFAULT_OPTIONS.docRootComponent),
docItemComponent: Joi.string().default(DEFAULT_OPTIONS.docItemComponent),
docTagsListComponent: Joi.string().default(
DEFAULT_OPTIONS.docTagsListComponent,
Expand Down
Expand Up @@ -198,10 +198,24 @@ declare module '@docusaurus/plugin-content-docs' {
*/
exclude: string[];
/**
* Root layout component of each doc page. Provides the version data
* context, and is not unmounted when switching docs.
* Parent component of all the docs plugin pages (including all versions).
* Stays mounted when navigation between docs pages and versions.
*/
docLayoutComponent: string;
docsRootComponent: string;
/**
* Parent component of all docs pages of an individual version:
* - docs pages with sidebars
* - tags pages
* Stays mounted when navigation between pages of that specific version.
*/
docVersionRootComponent: string;
/**
* Parent component of all docs pages with sidebars:
* - regular docs pages
* - category generated index pages
* Stays mounted when navigation between such pages.
*/
docRootComponent: string;
/** Main doc container, with TOC, pagination, etc. */
docItemComponent: string;
/** Root component of the "docs containing tag X" page. */
Expand Down Expand Up @@ -610,14 +624,32 @@ declare module '@theme/DocBreadcrumbs' {
export default function DocBreadcrumbs(): JSX.Element;
}

declare module '@theme/DocPage' {
declare module '@theme/DocsRoot' {
import type {RouteConfigComponentProps} from 'react-router-config';
import type {Required} from 'utility-types';

export interface Props extends Required<RouteConfigComponentProps, 'route'> {}

export default function DocsRoot(props: Props): JSX.Element;
}

declare module '@theme/DocVersionRoot' {
import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs';
import type {RouteConfigComponentProps} from 'react-router-config';
import type {Required} from 'utility-types';

export interface Props extends Required<RouteConfigComponentProps, 'route'> {
readonly versionMetadata: PropVersionMetadata;
readonly version: PropVersionMetadata;
}

export default function DocPage(props: Props): JSX.Element;
export default function DocVersionRoot(props: Props): JSX.Element;
}

declare module '@theme/DocRoot' {
import type {RouteConfigComponentProps} from 'react-router-config';
import type {Required} from 'utility-types';

export interface Props extends Required<RouteConfigComponentProps, 'route'> {}

export default function DocRoot(props: Props): JSX.Element;
}
13 changes: 12 additions & 1 deletion packages/docusaurus-plugin-content-docs/src/props.ts
Expand Up @@ -7,7 +7,7 @@

import _ from 'lodash';
import {createDocsByIdIndex} from './docs';
import type {VersionTag} from './types';
import type {VersionTag, VersionTags} from './types';
import type {
SidebarItemDoc,
SidebarItem,
Expand All @@ -21,6 +21,7 @@ import type {
PropSidebarItemCategory,
PropTagDocList,
PropTagDocListDoc,
PropTagsListPage,
PropSidebarItemLink,
PropVersionDocs,
DocMetadata,
Expand Down Expand Up @@ -181,3 +182,13 @@ export function toTagDocListProp({
items: toDocListProp(),
};
}

export function toTagsListTagsProp(
versionTags: VersionTags,
): PropTagsListPage['tags'] {
return Object.values(versionTags).map((tagValue) => ({
label: tagValue.label,
permalink: tagValue.permalink,
count: tagValue.docIds.length,
}));
}