mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-11 16:17:25 +02:00
fix(plugin-docs,theme): refactor docs plugin routes and component tree (#7966)
This commit is contained in:
parent
c29218ea1d
commit
3b9b497d13
35 changed files with 1189 additions and 857 deletions
|
@ -1242,6 +1242,20 @@ exports[`simple website content: global data 1`] = `
|
||||||
|
|
||||||
exports[`simple website content: route config 1`] = `
|
exports[`simple website content: route config 1`] = `
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"component": "@theme/DocsRoot",
|
||||||
|
"exact": false,
|
||||||
|
"path": "/docs",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"component": "@theme/DocVersionRoot",
|
||||||
|
"exact": false,
|
||||||
|
"modules": {
|
||||||
|
"version": "~docs/version-current-metadata-prop-751.json",
|
||||||
|
},
|
||||||
|
"path": "/docs",
|
||||||
|
"priority": -1,
|
||||||
|
"routes": [
|
||||||
{
|
{
|
||||||
"component": "@theme/DocTagsListPage",
|
"component": "@theme/DocTagsListPage",
|
||||||
"exact": true,
|
"exact": true,
|
||||||
|
@ -1275,13 +1289,9 @@ exports[`simple website content: route config 1`] = `
|
||||||
"path": "/docs/tags/tag2-custom-permalink",
|
"path": "/docs/tags/tag2-custom-permalink",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"component": "@theme/DocPage",
|
"component": "@theme/DocRoot",
|
||||||
"exact": false,
|
"exact": false,
|
||||||
"modules": {
|
|
||||||
"versionMetadata": "~docs/version-current-metadata-prop-751.json",
|
|
||||||
},
|
|
||||||
"path": "/docs",
|
"path": "/docs",
|
||||||
"priority": -1,
|
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
"component": "@theme/DocItem",
|
"component": "@theme/DocItem",
|
||||||
|
@ -1454,6 +1464,10 @@ exports[`simple website content: route config 1`] = `
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -2724,13 +2738,23 @@ exports[`versioned website (community) content: global data 1`] = `
|
||||||
exports[`versioned website (community) content: route config 1`] = `
|
exports[`versioned website (community) content: route config 1`] = `
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"component": "@theme/DocPage",
|
"component": "@theme/DocsRoot",
|
||||||
|
"exact": false,
|
||||||
|
"path": "/community",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"component": "@theme/DocVersionRoot",
|
||||||
"exact": false,
|
"exact": false,
|
||||||
"modules": {
|
"modules": {
|
||||||
"versionMetadata": "~docs/version-current-metadata-prop-751.json",
|
"version": "~docs/version-current-metadata-prop-751.json",
|
||||||
},
|
},
|
||||||
"path": "/community/next",
|
"path": "/community/next",
|
||||||
"priority": undefined,
|
"priority": undefined,
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"component": "@theme/DocRoot",
|
||||||
|
"exact": false,
|
||||||
|
"path": "/community/next",
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
"component": "@theme/DocItem",
|
"component": "@theme/DocItem",
|
||||||
|
@ -2743,14 +2767,21 @@ exports[`versioned website (community) content: route config 1`] = `
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"component": "@theme/DocPage",
|
"component": "@theme/DocVersionRoot",
|
||||||
"exact": false,
|
"exact": false,
|
||||||
"modules": {
|
"modules": {
|
||||||
"versionMetadata": "~docs/version-1-0-0-metadata-prop-608.json",
|
"version": "~docs/version-1-0-0-metadata-prop-608.json",
|
||||||
},
|
},
|
||||||
"path": "/community",
|
"path": "/community",
|
||||||
"priority": -1,
|
"priority": -1,
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"component": "@theme/DocRoot",
|
||||||
|
"exact": false,
|
||||||
|
"path": "/community",
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
"component": "@theme/DocItem",
|
"component": "@theme/DocItem",
|
||||||
|
@ -2763,6 +2794,10 @@ exports[`versioned website (community) content: route config 1`] = `
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -3930,6 +3965,65 @@ exports[`versioned website content: global data 1`] = `
|
||||||
|
|
||||||
exports[`versioned website content: route config 1`] = `
|
exports[`versioned website content: route config 1`] = `
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"component": "@theme/DocsRoot",
|
||||||
|
"exact": false,
|
||||||
|
"path": "/docs",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"component": "@theme/DocVersionRoot",
|
||||||
|
"exact": false,
|
||||||
|
"modules": {
|
||||||
|
"version": "~docs/version-1-0-0-metadata-prop-608.json",
|
||||||
|
},
|
||||||
|
"path": "/docs/1.0.0",
|
||||||
|
"priority": undefined,
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"component": "@theme/DocRoot",
|
||||||
|
"exact": false,
|
||||||
|
"path": "/docs/1.0.0",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"component": "@theme/DocItem",
|
||||||
|
"exact": true,
|
||||||
|
"modules": {
|
||||||
|
"content": "@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md",
|
||||||
|
},
|
||||||
|
"path": "/docs/1.0.0/",
|
||||||
|
"sidebar": "version-1.0.0/docs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "@theme/DocItem",
|
||||||
|
"exact": true,
|
||||||
|
"modules": {
|
||||||
|
"content": "@site/versioned_docs/version-1.0.0/foo/bar.md",
|
||||||
|
},
|
||||||
|
"path": "/docs/1.0.0/foo/barSlug",
|
||||||
|
"sidebar": "version-1.0.0/docs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "@theme/DocItem",
|
||||||
|
"exact": true,
|
||||||
|
"modules": {
|
||||||
|
"content": "@site/versioned_docs/version-1.0.0/foo/baz.md",
|
||||||
|
},
|
||||||
|
"path": "/docs/1.0.0/foo/baz",
|
||||||
|
"sidebar": "version-1.0.0/docs",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "@theme/DocVersionRoot",
|
||||||
|
"exact": false,
|
||||||
|
"modules": {
|
||||||
|
"version": "~docs/version-current-metadata-prop-751.json",
|
||||||
|
},
|
||||||
|
"path": "/docs/next",
|
||||||
|
"priority": undefined,
|
||||||
|
"routes": [
|
||||||
{
|
{
|
||||||
"component": "@theme/DocTagsListPage",
|
"component": "@theme/DocTagsListPage",
|
||||||
"exact": true,
|
"exact": true,
|
||||||
|
@ -3963,51 +4057,9 @@ exports[`versioned website content: route config 1`] = `
|
||||||
"path": "/docs/next/tags/barTag-3-permalink",
|
"path": "/docs/next/tags/barTag-3-permalink",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"component": "@theme/DocPage",
|
"component": "@theme/DocRoot",
|
||||||
"exact": false,
|
"exact": false,
|
||||||
"modules": {
|
|
||||||
"versionMetadata": "~docs/version-1-0-0-metadata-prop-608.json",
|
|
||||||
},
|
|
||||||
"path": "/docs/1.0.0",
|
|
||||||
"priority": undefined,
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"component": "@theme/DocItem",
|
|
||||||
"exact": true,
|
|
||||||
"modules": {
|
|
||||||
"content": "@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md",
|
|
||||||
},
|
|
||||||
"path": "/docs/1.0.0/",
|
|
||||||
"sidebar": "version-1.0.0/docs",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"component": "@theme/DocItem",
|
|
||||||
"exact": true,
|
|
||||||
"modules": {
|
|
||||||
"content": "@site/versioned_docs/version-1.0.0/foo/bar.md",
|
|
||||||
},
|
|
||||||
"path": "/docs/1.0.0/foo/barSlug",
|
|
||||||
"sidebar": "version-1.0.0/docs",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"component": "@theme/DocItem",
|
|
||||||
"exact": true,
|
|
||||||
"modules": {
|
|
||||||
"content": "@site/versioned_docs/version-1.0.0/foo/baz.md",
|
|
||||||
},
|
|
||||||
"path": "/docs/1.0.0/foo/baz",
|
|
||||||
"sidebar": "version-1.0.0/docs",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"component": "@theme/DocPage",
|
|
||||||
"exact": false,
|
|
||||||
"modules": {
|
|
||||||
"versionMetadata": "~docs/version-current-metadata-prop-751.json",
|
|
||||||
},
|
|
||||||
"path": "/docs/next",
|
"path": "/docs/next",
|
||||||
"priority": undefined,
|
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
"component": "@theme/DocItem",
|
"component": "@theme/DocItem",
|
||||||
|
@ -4061,14 +4113,21 @@ exports[`versioned website content: route config 1`] = `
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"component": "@theme/DocPage",
|
"component": "@theme/DocVersionRoot",
|
||||||
"exact": false,
|
"exact": false,
|
||||||
"modules": {
|
"modules": {
|
||||||
"versionMetadata": "~docs/version-with-slugs-metadata-prop-2bf.json",
|
"version": "~docs/version-with-slugs-metadata-prop-2bf.json",
|
||||||
},
|
},
|
||||||
"path": "/docs/withSlugs",
|
"path": "/docs/withSlugs",
|
||||||
"priority": undefined,
|
"priority": undefined,
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"component": "@theme/DocRoot",
|
||||||
|
"exact": false,
|
||||||
|
"path": "/docs/withSlugs",
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
"component": "@theme/DocItem",
|
"component": "@theme/DocItem",
|
||||||
|
@ -4137,14 +4196,21 @@ exports[`versioned website content: route config 1`] = `
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"component": "@theme/DocPage",
|
"component": "@theme/DocVersionRoot",
|
||||||
"exact": false,
|
"exact": false,
|
||||||
"modules": {
|
"modules": {
|
||||||
"versionMetadata": "~docs/version-1-0-1-metadata-prop-e87.json",
|
"version": "~docs/version-1-0-1-metadata-prop-e87.json",
|
||||||
},
|
},
|
||||||
"path": "/docs",
|
"path": "/docs",
|
||||||
"priority": -1,
|
"priority": -1,
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"component": "@theme/DocRoot",
|
||||||
|
"exact": false,
|
||||||
|
"path": "/docs",
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
"component": "@theme/DocItem",
|
"component": "@theme/DocItem",
|
||||||
|
@ -4166,6 +4232,10 @@ exports[`versioned website content: route config 1`] = `
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,9 @@ describe('normalizeDocsPluginOptions', () => {
|
||||||
sidebarPath: 'my-sidebar', // Path to sidebar configuration for showing a list of markdown pages.
|
sidebarPath: 'my-sidebar', // Path to sidebar configuration for showing a list of markdown pages.
|
||||||
sidebarItemsGenerator: DefaultSidebarItemsGenerator,
|
sidebarItemsGenerator: DefaultSidebarItemsGenerator,
|
||||||
numberPrefixParser: DefaultNumberPrefixParser,
|
numberPrefixParser: DefaultNumberPrefixParser,
|
||||||
docLayoutComponent: '@theme/DocPage',
|
docsRootComponent: '@theme/DocsRoot',
|
||||||
|
docVersionRootComponent: '@theme/DocVersionRoot',
|
||||||
|
docRootComponent: '@theme/DocRoot',
|
||||||
docItemComponent: '@theme/DocItem',
|
docItemComponent: '@theme/DocItem',
|
||||||
docTagDocListComponent: '@theme/DocTagDocListPage',
|
docTagDocListComponent: '@theme/DocTagDocListPage',
|
||||||
docTagsListComponent: '@theme/DocTagsListPage',
|
docTagsListComponent: '@theme/DocTagsListPage',
|
||||||
|
|
|
@ -27,22 +27,18 @@ import {
|
||||||
addDocNavigation,
|
addDocNavigation,
|
||||||
type DocEnv,
|
type DocEnv,
|
||||||
} from './docs';
|
} from './docs';
|
||||||
import {readVersionsMetadata} from './versions';
|
import {readVersionsMetadata, toFullVersion} from './versions';
|
||||||
import {cliDocsVersionCommand} from './cli';
|
import {cliDocsVersionCommand} from './cli';
|
||||||
import {VERSIONS_JSON_FILE} from './constants';
|
import {VERSIONS_JSON_FILE} from './constants';
|
||||||
import {toGlobalDataVersion} from './globalData';
|
import {toGlobalDataVersion} from './globalData';
|
||||||
import {toTagDocListProp} from './props';
|
|
||||||
import {getCategoryGeneratedIndexMetadataList} from './categoryGeneratedIndex';
|
|
||||||
import {
|
import {
|
||||||
translateLoadedContent,
|
translateLoadedContent,
|
||||||
getLoadedContentTranslationFiles,
|
getLoadedContentTranslationFiles,
|
||||||
} from './translations';
|
} from './translations';
|
||||||
import {getVersionTags} from './tags';
|
import {createAllRoutes} from './routes';
|
||||||
import {createVersionRoutes} from './routes';
|
|
||||||
import {createSidebarsUtils} from './sidebars/utils';
|
import {createSidebarsUtils} from './sidebars/utils';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
PropTagsListPage,
|
|
||||||
PluginOptions,
|
PluginOptions,
|
||||||
DocMetadataBase,
|
DocMetadataBase,
|
||||||
VersionMetadata,
|
VersionMetadata,
|
||||||
|
@ -55,7 +51,6 @@ import type {
|
||||||
SourceToPermalink,
|
SourceToPermalink,
|
||||||
DocFile,
|
DocFile,
|
||||||
DocsMarkdownOption,
|
DocsMarkdownOption,
|
||||||
VersionTag,
|
|
||||||
FullVersion,
|
FullVersion,
|
||||||
} from './types';
|
} from './types';
|
||||||
import type {RuleSetRule} from 'webpack';
|
import type {RuleSetRule} from 'webpack';
|
||||||
|
@ -209,102 +204,20 @@ export default async function pluginContentDocs(
|
||||||
},
|
},
|
||||||
|
|
||||||
async contentLoaded({content, actions}) {
|
async contentLoaded({content, actions}) {
|
||||||
const {loadedVersions} = content;
|
const versions: FullVersion[] = content.loadedVersions.map(toFullVersion);
|
||||||
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,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
async function createVersionTagsRoutes(version: FullVersion) {
|
await createAllRoutes({
|
||||||
const versionTags = getVersionTags(version.docs);
|
baseUrl,
|
||||||
|
versions,
|
||||||
// TODO tags should be a sub route of the version route
|
options,
|
||||||
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,
|
actions,
|
||||||
}),
|
aliasedSource,
|
||||||
),
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// TODO tags should be a sub route of the version route
|
actions.setGlobalData({
|
||||||
await Promise.all(versions.map(createVersionTagsRoutes));
|
|
||||||
|
|
||||||
setGlobalData({
|
|
||||||
path: normalizeUrl([baseUrl, options.routeBasePath]),
|
path: normalizeUrl([baseUrl, options.routeBasePath]),
|
||||||
versions: versions.map(toGlobalDataVersion),
|
versions: versions.map(toGlobalDataVersion),
|
||||||
breadcrumbs,
|
breadcrumbs: options.breadcrumbs,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,9 @@ export const DEFAULT_OPTIONS: Omit<PluginOptions, 'id' | 'sidebarPath'> = {
|
||||||
exclude: GlobExcludeDefault,
|
exclude: GlobExcludeDefault,
|
||||||
sidebarItemsGenerator: DefaultSidebarItemsGenerator,
|
sidebarItemsGenerator: DefaultSidebarItemsGenerator,
|
||||||
numberPrefixParser: DefaultNumberPrefixParser,
|
numberPrefixParser: DefaultNumberPrefixParser,
|
||||||
docLayoutComponent: '@theme/DocPage',
|
docsRootComponent: '@theme/DocsRoot',
|
||||||
|
docVersionRootComponent: '@theme/DocVersionRoot',
|
||||||
|
docRootComponent: '@theme/DocRoot',
|
||||||
docItemComponent: '@theme/DocItem',
|
docItemComponent: '@theme/DocItem',
|
||||||
docTagDocListComponent: '@theme/DocTagDocListPage',
|
docTagDocListComponent: '@theme/DocTagDocListPage',
|
||||||
docTagsListComponent: '@theme/DocTagsListPage',
|
docTagsListComponent: '@theme/DocTagsListPage',
|
||||||
|
@ -104,7 +106,11 @@ const OptionsSchema = Joi.object<PluginOptions>({
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.default(() => DEFAULT_OPTIONS.numberPrefixParser),
|
.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),
|
docItemComponent: Joi.string().default(DEFAULT_OPTIONS.docItemComponent),
|
||||||
docTagsListComponent: Joi.string().default(
|
docTagsListComponent: Joi.string().default(
|
||||||
DEFAULT_OPTIONS.docTagsListComponent,
|
DEFAULT_OPTIONS.docTagsListComponent,
|
||||||
|
|
|
@ -198,10 +198,24 @@ declare module '@docusaurus/plugin-content-docs' {
|
||||||
*/
|
*/
|
||||||
exclude: string[];
|
exclude: string[];
|
||||||
/**
|
/**
|
||||||
* Root layout component of each doc page. Provides the version data
|
* Parent component of all the docs plugin pages (including all versions).
|
||||||
* context, and is not unmounted when switching docs.
|
* 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. */
|
/** Main doc container, with TOC, pagination, etc. */
|
||||||
docItemComponent: string;
|
docItemComponent: string;
|
||||||
/** Root component of the "docs containing tag X" page. */
|
/** Root component of the "docs containing tag X" page. */
|
||||||
|
@ -610,14 +624,32 @@ declare module '@theme/DocBreadcrumbs' {
|
||||||
export default function DocBreadcrumbs(): JSX.Element;
|
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 {PropVersionMetadata} from '@docusaurus/plugin-content-docs';
|
||||||
import type {RouteConfigComponentProps} from 'react-router-config';
|
import type {RouteConfigComponentProps} from 'react-router-config';
|
||||||
import type {Required} from 'utility-types';
|
import type {Required} from 'utility-types';
|
||||||
|
|
||||||
export interface Props extends Required<RouteConfigComponentProps, 'route'> {
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import {createDocsByIdIndex} from './docs';
|
import {createDocsByIdIndex} from './docs';
|
||||||
import type {VersionTag} from './types';
|
import type {VersionTag, VersionTags} from './types';
|
||||||
import type {
|
import type {
|
||||||
SidebarItemDoc,
|
SidebarItemDoc,
|
||||||
SidebarItem,
|
SidebarItem,
|
||||||
|
@ -21,6 +21,7 @@ import type {
|
||||||
PropSidebarItemCategory,
|
PropSidebarItemCategory,
|
||||||
PropTagDocList,
|
PropTagDocList,
|
||||||
PropTagDocListDoc,
|
PropTagDocListDoc,
|
||||||
|
PropTagsListPage,
|
||||||
PropSidebarItemLink,
|
PropSidebarItemLink,
|
||||||
PropVersionDocs,
|
PropVersionDocs,
|
||||||
DocMetadata,
|
DocMetadata,
|
||||||
|
@ -181,3 +182,13 @@ export function toTagDocListProp({
|
||||||
items: toDocListProp(),
|
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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
|
@ -5,30 +5,32 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
import logger from '@docusaurus/logger';
|
import logger from '@docusaurus/logger';
|
||||||
import {docuHash, createSlugger} from '@docusaurus/utils';
|
import {docuHash, createSlugger, normalizeUrl} from '@docusaurus/utils';
|
||||||
import {toVersionMetadataProp} from './props';
|
import {
|
||||||
|
toTagDocListProp,
|
||||||
|
toTagsListTagsProp,
|
||||||
|
toVersionMetadataProp,
|
||||||
|
} from './props';
|
||||||
|
import {getVersionTags} from './tags';
|
||||||
import type {PluginContentLoadedActions, RouteConfig} from '@docusaurus/types';
|
import type {PluginContentLoadedActions, RouteConfig} from '@docusaurus/types';
|
||||||
import type {FullVersion} from './types';
|
import type {FullVersion, VersionTag} from './types';
|
||||||
import type {
|
import type {
|
||||||
CategoryGeneratedIndexMetadata,
|
CategoryGeneratedIndexMetadata,
|
||||||
DocMetadata,
|
PluginOptions,
|
||||||
|
PropTagsListPage,
|
||||||
} from '@docusaurus/plugin-content-docs';
|
} from '@docusaurus/plugin-content-docs';
|
||||||
|
|
||||||
export async function createCategoryGeneratedIndexRoutes({
|
async function buildVersionCategoryGeneratedIndexRoutes({
|
||||||
version,
|
version,
|
||||||
actions,
|
actions,
|
||||||
docCategoryGeneratedIndexComponent,
|
options,
|
||||||
aliasedSource,
|
aliasedSource,
|
||||||
}: {
|
}: BuildVersionRoutesParam): Promise<RouteConfig[]> {
|
||||||
version: FullVersion;
|
|
||||||
actions: PluginContentLoadedActions;
|
|
||||||
docCategoryGeneratedIndexComponent: string;
|
|
||||||
aliasedSource: (str: string) => string;
|
|
||||||
}): Promise<RouteConfig[]> {
|
|
||||||
const slugs = createSlugger();
|
const slugs = createSlugger();
|
||||||
|
|
||||||
async function createCategoryGeneratedIndexRoute(
|
async function buildCategoryGeneratedIndexRoute(
|
||||||
categoryGeneratedIndex: CategoryGeneratedIndexMetadata,
|
categoryGeneratedIndex: CategoryGeneratedIndexMetadata,
|
||||||
): Promise<RouteConfig> {
|
): Promise<RouteConfig> {
|
||||||
const {sidebar, ...prop} = categoryGeneratedIndex;
|
const {sidebar, ...prop} = categoryGeneratedIndex;
|
||||||
|
@ -44,7 +46,7 @@ export async function createCategoryGeneratedIndexRoutes({
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path: categoryGeneratedIndex.permalink,
|
path: categoryGeneratedIndex.permalink,
|
||||||
component: docCategoryGeneratedIndexComponent,
|
component: options.docCategoryGeneratedIndexComponent,
|
||||||
exact: true,
|
exact: true,
|
||||||
modules: {
|
modules: {
|
||||||
categoryGeneratedIndex: aliasedSource(propData),
|
categoryGeneratedIndex: aliasedSource(propData),
|
||||||
|
@ -56,21 +58,17 @@ export async function createCategoryGeneratedIndexRoutes({
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
version.categoryGeneratedIndices.map(createCategoryGeneratedIndexRoute),
|
version.categoryGeneratedIndices.map(buildCategoryGeneratedIndexRoute),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createDocRoutes({
|
async function buildVersionDocRoutes({
|
||||||
docs,
|
version,
|
||||||
actions,
|
actions,
|
||||||
docItemComponent,
|
options,
|
||||||
}: {
|
}: BuildVersionRoutesParam): Promise<RouteConfig[]> {
|
||||||
docs: DocMetadata[];
|
|
||||||
actions: PluginContentLoadedActions;
|
|
||||||
docItemComponent: string;
|
|
||||||
}): Promise<RouteConfig[]> {
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
docs.map(async (metadataItem) => {
|
version.docs.map(async (metadataItem) => {
|
||||||
await actions.createData(
|
await actions.createData(
|
||||||
// Note that this created data path must be in sync with
|
// Note that this created data path must be in sync with
|
||||||
// metadataPath provided to mdx-loader.
|
// metadataPath provided to mdx-loader.
|
||||||
|
@ -80,12 +78,12 @@ export async function createDocRoutes({
|
||||||
|
|
||||||
const docRoute: RouteConfig = {
|
const docRoute: RouteConfig = {
|
||||||
path: metadataItem.permalink,
|
path: metadataItem.permalink,
|
||||||
component: docItemComponent,
|
component: options.docItemComponent,
|
||||||
exact: true,
|
exact: true,
|
||||||
modules: {
|
modules: {
|
||||||
content: metadataItem.source,
|
content: metadataItem.source,
|
||||||
},
|
},
|
||||||
// Because the parent (DocPage) comp need to access it easily
|
// Because the parent (DocRoot) comp need to access it easily
|
||||||
// This permits to render the sidebar once without unmount/remount when
|
// This permits to render the sidebar once without unmount/remount when
|
||||||
// navigating (and preserve sidebar state)
|
// navigating (and preserve sidebar state)
|
||||||
...(metadataItem.sidebar && {
|
...(metadataItem.sidebar && {
|
||||||
|
@ -98,62 +96,160 @@ export async function createDocRoutes({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createVersionRoutes({
|
async function buildVersionSidebarRoute(param: BuildVersionRoutesParam) {
|
||||||
version,
|
const [docRoutes, categoryGeneratedIndexRoutes] = await Promise.all([
|
||||||
actions,
|
buildVersionDocRoutes(param),
|
||||||
docItemComponent,
|
buildVersionCategoryGeneratedIndexRoutes(param),
|
||||||
docLayoutComponent,
|
|
||||||
docCategoryGeneratedIndexComponent,
|
|
||||||
pluginId,
|
|
||||||
aliasedSource,
|
|
||||||
}: {
|
|
||||||
version: FullVersion;
|
|
||||||
actions: PluginContentLoadedActions;
|
|
||||||
docLayoutComponent: string;
|
|
||||||
docItemComponent: string;
|
|
||||||
docCategoryGeneratedIndexComponent: string;
|
|
||||||
pluginId: string;
|
|
||||||
aliasedSource: (str: string) => string;
|
|
||||||
}): Promise<void> {
|
|
||||||
async function doCreateVersionRoutes(): Promise<void> {
|
|
||||||
const versionMetadata = toVersionMetadataProp(pluginId, version);
|
|
||||||
const versionMetadataPropPath = await actions.createData(
|
|
||||||
`${docuHash(`version-${version.versionName}-metadata-prop`)}.json`,
|
|
||||||
JSON.stringify(versionMetadata, null, 2),
|
|
||||||
);
|
|
||||||
|
|
||||||
async function createVersionSubRoutes() {
|
|
||||||
const [docRoutes, sidebarsRoutes] = await Promise.all([
|
|
||||||
createDocRoutes({docs: version.docs, actions, docItemComponent}),
|
|
||||||
createCategoryGeneratedIndexRoutes({
|
|
||||||
version,
|
|
||||||
actions,
|
|
||||||
docCategoryGeneratedIndexComponent,
|
|
||||||
aliasedSource,
|
|
||||||
}),
|
|
||||||
]);
|
]);
|
||||||
|
const subRoutes = [...docRoutes, ...categoryGeneratedIndexRoutes];
|
||||||
const routes = [...docRoutes, ...sidebarsRoutes];
|
return {
|
||||||
return routes.sort((a, b) => a.path.localeCompare(b.path));
|
path: param.version.path,
|
||||||
|
exact: false,
|
||||||
|
component: param.options.docRootComponent,
|
||||||
|
routes: subRoutes,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
actions.addRoute({
|
async function buildVersionTagsRoutes(
|
||||||
path: version.path,
|
param: BuildVersionRoutesParam,
|
||||||
// Allow matching /docs/* since this is the wrapping route
|
): Promise<RouteConfig[]> {
|
||||||
exact: false,
|
const {version, options, actions, aliasedSource} = param;
|
||||||
component: docLayoutComponent,
|
const versionTags = getVersionTags(version.docs);
|
||||||
routes: await createVersionSubRoutes(),
|
|
||||||
|
async function buildTagsListRoute(): Promise<RouteConfig | null> {
|
||||||
|
// Don't create a tags list page if there's no tag
|
||||||
|
if (Object.keys(versionTags).length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const tagsProp: PropTagsListPage['tags'] = toTagsListTagsProp(versionTags);
|
||||||
|
const tagsPropPath = await actions.createData(
|
||||||
|
`${docuHash(`tags-list-${version.versionName}-prop`)}.json`,
|
||||||
|
JSON.stringify(tagsProp, null, 2),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
path: version.tagsPath,
|
||||||
|
exact: true,
|
||||||
|
component: options.docTagsListComponent,
|
||||||
modules: {
|
modules: {
|
||||||
versionMetadata: aliasedSource(versionMetadataPropPath),
|
tags: aliasedSource(tagsPropPath),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildTagDocListRoute(tag: VersionTag): Promise<RouteConfig> {
|
||||||
|
const tagProps = toTagDocListProp({
|
||||||
|
allTagsPath: version.tagsPath,
|
||||||
|
tag,
|
||||||
|
docs: version.docs,
|
||||||
|
});
|
||||||
|
const tagPropPath = await actions.createData(
|
||||||
|
`${docuHash(`tag-${tag.permalink}`)}.json`,
|
||||||
|
JSON.stringify(tagProps, null, 2),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
path: tag.permalink,
|
||||||
|
component: options.docTagDocListComponent,
|
||||||
|
exact: true,
|
||||||
|
modules: {
|
||||||
|
tag: aliasedSource(tagPropPath),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [tagsListRoute, allTagsDocListRoutes] = await Promise.all([
|
||||||
|
buildTagsListRoute(),
|
||||||
|
Promise.all(Object.values(versionTags).map(buildTagDocListRoute)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return _.compact([tagsListRoute, ...allTagsDocListRoutes]);
|
||||||
|
}
|
||||||
|
|
||||||
|
type BuildVersionRoutesParam = Omit<BuildAllRoutesParam, 'versions'> & {
|
||||||
|
version: FullVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function buildVersionRoutes(
|
||||||
|
param: BuildVersionRoutesParam,
|
||||||
|
): Promise<RouteConfig> {
|
||||||
|
const {version, actions, options, aliasedSource} = param;
|
||||||
|
|
||||||
|
async function buildVersionSubRoutes() {
|
||||||
|
const [sidebarRoute, tagsRoutes] = await Promise.all([
|
||||||
|
buildVersionSidebarRoute(param),
|
||||||
|
buildVersionTagsRoutes(param),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [sidebarRoute, ...tagsRoutes];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doBuildVersionRoutes(): Promise<RouteConfig> {
|
||||||
|
const versionProp = toVersionMetadataProp(options.id, version);
|
||||||
|
const versionPropPath = await actions.createData(
|
||||||
|
`${docuHash(`version-${version.versionName}-metadata-prop`)}.json`,
|
||||||
|
JSON.stringify(versionProp, null, 2),
|
||||||
|
);
|
||||||
|
const subRoutes = await buildVersionSubRoutes();
|
||||||
|
return {
|
||||||
|
path: version.path,
|
||||||
|
exact: false,
|
||||||
|
component: options.docVersionRootComponent,
|
||||||
|
routes: subRoutes,
|
||||||
|
modules: {
|
||||||
|
version: aliasedSource(versionPropPath),
|
||||||
},
|
},
|
||||||
priority: version.routePriority,
|
priority: version.routePriority,
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await doCreateVersionRoutes();
|
return await doBuildVersionRoutes();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error`Can't create version routes for version name=${version.versionName}`;
|
logger.error`Can't create version routes for version name=${version.versionName}`;
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BuildAllRoutesParam = Omit<CreateAllRoutesParam, 'actions'> & {
|
||||||
|
actions: Omit<PluginContentLoadedActions, 'addRoute' | 'setGlobalData'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO we want this buildAllRoutes function to be easily testable
|
||||||
|
// Ideally, we should avoid side effects here (ie not injecting actions)
|
||||||
|
export async function buildAllRoutes(
|
||||||
|
param: BuildAllRoutesParam,
|
||||||
|
): Promise<RouteConfig[]> {
|
||||||
|
const subRoutes = await Promise.all(
|
||||||
|
param.versions.map((version) =>
|
||||||
|
buildVersionRoutes({
|
||||||
|
...param,
|
||||||
|
version,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// all docs routes are wrapped under a single parent route, this ensures
|
||||||
|
// the theme layout never unmounts/remounts when navigating between versions
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
path: normalizeUrl([param.baseUrl, param.options.routeBasePath]),
|
||||||
|
exact: false,
|
||||||
|
component: param.options.docsRootComponent,
|
||||||
|
routes: subRoutes,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateAllRoutesParam = {
|
||||||
|
baseUrl: string;
|
||||||
|
versions: FullVersion[];
|
||||||
|
options: PluginOptions;
|
||||||
|
actions: PluginContentLoadedActions;
|
||||||
|
aliasedSource: (str: string) => string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function createAllRoutes(
|
||||||
|
param: CreateAllRoutesParam,
|
||||||
|
): Promise<void> {
|
||||||
|
const routes = await buildAllRoutes(param);
|
||||||
|
routes.forEach(param.actions.addRoute);
|
||||||
|
}
|
||||||
|
|
|
@ -14,12 +14,16 @@ import {
|
||||||
getVersionMetadataPaths,
|
getVersionMetadataPaths,
|
||||||
readVersionNames,
|
readVersionNames,
|
||||||
} from './files';
|
} from './files';
|
||||||
|
import {createSidebarsUtils} from '../sidebars/utils';
|
||||||
|
import {getCategoryGeneratedIndexMetadataList} from '../categoryGeneratedIndex';
|
||||||
|
import type {FullVersion} from '../types';
|
||||||
|
import type {LoadContext} from '@docusaurus/types';
|
||||||
import type {
|
import type {
|
||||||
|
LoadedVersion,
|
||||||
PluginOptions,
|
PluginOptions,
|
||||||
VersionBanner,
|
VersionBanner,
|
||||||
VersionMetadata,
|
VersionMetadata,
|
||||||
} from '@docusaurus/plugin-content-docs';
|
} from '@docusaurus/plugin-content-docs';
|
||||||
import type {LoadContext} from '@docusaurus/types';
|
|
||||||
|
|
||||||
export type VersionContext = {
|
export type VersionContext = {
|
||||||
/** The version name to get banner of. */
|
/** The version name to get banner of. */
|
||||||
|
@ -252,3 +256,15 @@ export async function readVersionsMetadata({
|
||||||
);
|
);
|
||||||
return versionsMetadata;
|
return versionsMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toFullVersion(version: LoadedVersion): FullVersion {
|
||||||
|
const sidebarsUtils = createSidebarsUtils(version.sidebars);
|
||||||
|
return {
|
||||||
|
...version,
|
||||||
|
sidebarsUtils,
|
||||||
|
categoryGeneratedIndices: getCategoryGeneratedIndexMetadataList({
|
||||||
|
docs: version.docs,
|
||||||
|
sidebarsUtils,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -379,17 +379,17 @@ declare module '@theme/DocItem/Footer' {
|
||||||
export default function DocItemFooter(): JSX.Element;
|
export default function DocItemFooter(): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/DocPage/Layout' {
|
declare module '@theme/DocRoot/Layout' {
|
||||||
import type {ReactNode} from 'react';
|
import type {ReactNode} from 'react';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
readonly children: ReactNode;
|
readonly children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DocPageLayout(props: Props): JSX.Element;
|
export default function DocRootLayout(props: Props): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/DocPage/Layout/Sidebar' {
|
declare module '@theme/DocRoot/Layout/Sidebar' {
|
||||||
import type {Dispatch, SetStateAction} from 'react';
|
import type {Dispatch, SetStateAction} from 'react';
|
||||||
import type {PropSidebar} from '@docusaurus/plugin-content-docs';
|
import type {PropSidebar} from '@docusaurus/plugin-content-docs';
|
||||||
|
|
||||||
|
@ -399,20 +399,20 @@ declare module '@theme/DocPage/Layout/Sidebar' {
|
||||||
readonly setHiddenSidebarContainer: Dispatch<SetStateAction<boolean>>;
|
readonly setHiddenSidebarContainer: Dispatch<SetStateAction<boolean>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DocPageLayoutSidebar(props: Props): JSX.Element;
|
export default function DocRootLayoutSidebar(props: Props): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/DocPage/Layout/Sidebar/ExpandButton' {
|
declare module '@theme/DocRoot/Layout/Sidebar/ExpandButton' {
|
||||||
export interface Props {
|
export interface Props {
|
||||||
toggleSidebar: () => void;
|
toggleSidebar: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DocPageLayoutSidebarExpandButton(
|
export default function DocRootLayoutSidebarExpandButton(
|
||||||
props: Props,
|
props: Props,
|
||||||
): JSX.Element;
|
): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/DocPage/Layout/Main' {
|
declare module '@theme/DocRoot/Layout/Main' {
|
||||||
import type {ReactNode} from 'react';
|
import type {ReactNode} from 'react';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -420,7 +420,7 @@ declare module '@theme/DocPage/Layout/Main' {
|
||||||
readonly children: ReactNode;
|
readonly children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DocPageLayoutMain(props: Props): JSX.Element;
|
export default function DocRootLayoutMain(props: Props): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/DocPaginator' {
|
declare module '@theme/DocPaginator' {
|
||||||
|
@ -662,6 +662,14 @@ declare module '@theme/Heading' {
|
||||||
export default function Heading(props: Props): JSX.Element;
|
export default function Heading(props: Props): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '@theme/NotFound/Content' {
|
||||||
|
export interface Props {
|
||||||
|
readonly className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NotFoundContent(props: Props): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
declare module '@theme/Layout' {
|
declare module '@theme/Layout' {
|
||||||
import type {ReactNode} from 'react';
|
import type {ReactNode} from 'react';
|
||||||
|
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import {
|
|
||||||
HtmlClassNameProvider,
|
|
||||||
ThemeClassNames,
|
|
||||||
PageMetadata,
|
|
||||||
} from '@docusaurus/theme-common';
|
|
||||||
import {
|
|
||||||
docVersionSearchTag,
|
|
||||||
DocsSidebarProvider,
|
|
||||||
DocsVersionProvider,
|
|
||||||
useDocRouteMetadata,
|
|
||||||
} from '@docusaurus/theme-common/internal';
|
|
||||||
import DocPageLayout from '@theme/DocPage/Layout';
|
|
||||||
import NotFound from '@theme/NotFound';
|
|
||||||
import SearchMetadata from '@theme/SearchMetadata';
|
|
||||||
import type {Props} from '@theme/DocPage';
|
|
||||||
|
|
||||||
function DocPageMetadata(props: Props): JSX.Element {
|
|
||||||
const {versionMetadata} = props;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SearchMetadata
|
|
||||||
version={versionMetadata.version}
|
|
||||||
tag={docVersionSearchTag(
|
|
||||||
versionMetadata.pluginId,
|
|
||||||
versionMetadata.version,
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<PageMetadata>
|
|
||||||
{versionMetadata.noIndex && (
|
|
||||||
<meta name="robots" content="noindex, nofollow" />
|
|
||||||
)}
|
|
||||||
</PageMetadata>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function DocPage(props: Props): JSX.Element {
|
|
||||||
const {versionMetadata} = props;
|
|
||||||
const currentDocRouteMetadata = useDocRouteMetadata(props);
|
|
||||||
if (!currentDocRouteMetadata) {
|
|
||||||
return <NotFound />;
|
|
||||||
}
|
|
||||||
const {docElement, sidebarName, sidebarItems} = currentDocRouteMetadata;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DocPageMetadata {...props} />
|
|
||||||
<HtmlClassNameProvider
|
|
||||||
className={clsx(
|
|
||||||
// TODO: it should be removed from here
|
|
||||||
ThemeClassNames.wrapper.docsPages,
|
|
||||||
ThemeClassNames.page.docsDocPage,
|
|
||||||
props.versionMetadata.className,
|
|
||||||
)}>
|
|
||||||
<DocsVersionProvider version={versionMetadata}>
|
|
||||||
<DocsSidebarProvider name={sidebarName} items={sidebarItems}>
|
|
||||||
<DocPageLayout>{docElement}</DocPageLayout>
|
|
||||||
</DocsSidebarProvider>
|
|
||||||
</DocsVersionProvider>
|
|
||||||
</HtmlClassNameProvider>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -8,11 +8,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import {useDocsSidebar} from '@docusaurus/theme-common/internal';
|
import {useDocsSidebar} from '@docusaurus/theme-common/internal';
|
||||||
import type {Props} from '@theme/DocPage/Layout/Main';
|
import type {Props} from '@theme/DocRoot/Layout/Main';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
export default function DocPageLayoutMain({
|
export default function DocRootLayoutMain({
|
||||||
hiddenSidebarContainer,
|
hiddenSidebarContainer,
|
||||||
children,
|
children,
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
|
@ -8,11 +8,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {translate} from '@docusaurus/Translate';
|
import {translate} from '@docusaurus/Translate';
|
||||||
import IconArrow from '@theme/Icon/Arrow';
|
import IconArrow from '@theme/Icon/Arrow';
|
||||||
import type {Props} from '@theme/DocPage/Layout/Sidebar/ExpandButton';
|
import type {Props} from '@theme/DocRoot/Layout/Sidebar/ExpandButton';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
export default function DocPageLayoutSidebarExpandButton({
|
export default function DocRootLayoutSidebarExpandButton({
|
||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
return (
|
return (
|
|
@ -11,8 +11,8 @@ import {ThemeClassNames} from '@docusaurus/theme-common';
|
||||||
import {useDocsSidebar} from '@docusaurus/theme-common/internal';
|
import {useDocsSidebar} from '@docusaurus/theme-common/internal';
|
||||||
import {useLocation} from '@docusaurus/router';
|
import {useLocation} from '@docusaurus/router';
|
||||||
import DocSidebar from '@theme/DocSidebar';
|
import DocSidebar from '@theme/DocSidebar';
|
||||||
import ExpandButton from '@theme/DocPage/Layout/Sidebar/ExpandButton';
|
import ExpandButton from '@theme/DocRoot/Layout/Sidebar/ExpandButton';
|
||||||
import type {Props} from '@theme/DocPage/Layout/Sidebar';
|
import type {Props} from '@theme/DocRoot/Layout/Sidebar';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ function ResetOnSidebarChange({children}: {children: ReactNode}) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DocPageLayoutSidebar({
|
export default function DocRootLayoutSidebar({
|
||||||
sidebar,
|
sidebar,
|
||||||
hiddenSidebarContainer,
|
hiddenSidebarContainer,
|
||||||
setHiddenSidebarContainer,
|
setHiddenSidebarContainer,
|
|
@ -7,32 +7,31 @@
|
||||||
|
|
||||||
import React, {useState} from 'react';
|
import React, {useState} from 'react';
|
||||||
import {useDocsSidebar} from '@docusaurus/theme-common/internal';
|
import {useDocsSidebar} from '@docusaurus/theme-common/internal';
|
||||||
import Layout from '@theme/Layout';
|
|
||||||
import BackToTopButton from '@theme/BackToTopButton';
|
import BackToTopButton from '@theme/BackToTopButton';
|
||||||
import DocPageLayoutSidebar from '@theme/DocPage/Layout/Sidebar';
|
import DocRootLayoutSidebar from '@theme/DocRoot/Layout/Sidebar';
|
||||||
import DocPageLayoutMain from '@theme/DocPage/Layout/Main';
|
import DocRootLayoutMain from '@theme/DocRoot/Layout/Main';
|
||||||
import type {Props} from '@theme/DocPage/Layout';
|
import type {Props} from '@theme/DocRoot/Layout';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
export default function DocPageLayout({children}: Props): JSX.Element {
|
export default function DocRootLayout({children}: Props): JSX.Element {
|
||||||
const sidebar = useDocsSidebar();
|
const sidebar = useDocsSidebar();
|
||||||
const [hiddenSidebarContainer, setHiddenSidebarContainer] = useState(false);
|
const [hiddenSidebarContainer, setHiddenSidebarContainer] = useState(false);
|
||||||
return (
|
return (
|
||||||
<Layout wrapperClassName={styles.docsWrapper}>
|
<div className={styles.docsWrapper}>
|
||||||
<BackToTopButton />
|
<BackToTopButton />
|
||||||
<div className={styles.docPage}>
|
<div className={styles.docRoot}>
|
||||||
{sidebar && (
|
{sidebar && (
|
||||||
<DocPageLayoutSidebar
|
<DocRootLayoutSidebar
|
||||||
sidebar={sidebar.items}
|
sidebar={sidebar.items}
|
||||||
hiddenSidebarContainer={hiddenSidebarContainer}
|
hiddenSidebarContainer={hiddenSidebarContainer}
|
||||||
setHiddenSidebarContainer={setHiddenSidebarContainer}
|
setHiddenSidebarContainer={setHiddenSidebarContainer}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<DocPageLayoutMain hiddenSidebarContainer={hiddenSidebarContainer}>
|
<DocRootLayoutMain hiddenSidebarContainer={hiddenSidebarContainer}>
|
||||||
{children}
|
{children}
|
||||||
</DocPageLayoutMain>
|
</DocRootLayoutMain>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.docPage {
|
.docRoot {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import {HtmlClassNameProvider, ThemeClassNames} from '@docusaurus/theme-common';
|
||||||
|
import {
|
||||||
|
DocsSidebarProvider,
|
||||||
|
useDocRootMetadata,
|
||||||
|
} from '@docusaurus/theme-common/internal';
|
||||||
|
import DocRootLayout from '@theme/DocRoot/Layout';
|
||||||
|
import NotFoundContent from '@theme/NotFound/Content';
|
||||||
|
import type {Props} from '@theme/DocRoot';
|
||||||
|
|
||||||
|
export default function DocRoot(props: Props): JSX.Element {
|
||||||
|
const currentDocRouteMetadata = useDocRootMetadata(props);
|
||||||
|
if (!currentDocRouteMetadata) {
|
||||||
|
// We only render the not found content to avoid a double layout
|
||||||
|
// see https://github.com/facebook/docusaurus/pull/7966#pullrequestreview-1077276692
|
||||||
|
return <NotFoundContent />;
|
||||||
|
}
|
||||||
|
const {docElement, sidebarName, sidebarItems} = currentDocRouteMetadata;
|
||||||
|
return (
|
||||||
|
<HtmlClassNameProvider className={clsx(ThemeClassNames.page.docsDocPage)}>
|
||||||
|
<DocsSidebarProvider name={sidebarName} items={sidebarItems}>
|
||||||
|
<DocRootLayout>{docElement}</DocRootLayout>
|
||||||
|
</DocsSidebarProvider>
|
||||||
|
</HtmlClassNameProvider>
|
||||||
|
);
|
||||||
|
}
|
|
@ -15,7 +15,6 @@ import {
|
||||||
usePluralForm,
|
usePluralForm,
|
||||||
} from '@docusaurus/theme-common';
|
} from '@docusaurus/theme-common';
|
||||||
import Translate, {translate} from '@docusaurus/Translate';
|
import Translate, {translate} from '@docusaurus/Translate';
|
||||||
import Layout from '@theme/Layout';
|
|
||||||
import SearchMetadata from '@theme/SearchMetadata';
|
import SearchMetadata from '@theme/SearchMetadata';
|
||||||
import type {Props} from '@theme/DocTagDocListPage';
|
import type {Props} from '@theme/DocTagDocListPage';
|
||||||
|
|
||||||
|
@ -37,6 +36,18 @@ function useNDocsTaggedPlural() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function usePageTitle(props: Props): string {
|
||||||
|
const nDocsTaggedPlural = useNDocsTaggedPlural();
|
||||||
|
return translate(
|
||||||
|
{
|
||||||
|
id: 'theme.docs.tagDocListPageTitle',
|
||||||
|
description: 'The title of the page for a docs tag',
|
||||||
|
message: '{nDocsTagged} with "{tagName}"',
|
||||||
|
},
|
||||||
|
{nDocsTagged: nDocsTaggedPlural(props.tag.count), tagName: props.tag.label},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function DocItem({doc}: {doc: Props['tag']['items'][number]}): JSX.Element {
|
function DocItem({doc}: {doc: Props['tag']['items'][number]}): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<article className="margin-vert--lg">
|
<article className="margin-vert--lg">
|
||||||
|
@ -48,26 +59,24 @@ function DocItem({doc}: {doc: Props['tag']['items'][number]}): JSX.Element {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DocTagDocListPage({tag}: Props): JSX.Element {
|
function DocTagDocListPageMetadata({
|
||||||
const nDocsTaggedPlural = useNDocsTaggedPlural();
|
title,
|
||||||
const title = translate(
|
}: Props & {title: string}): JSX.Element {
|
||||||
{
|
|
||||||
id: 'theme.docs.tagDocListPageTitle',
|
|
||||||
description: 'The title of the page for a docs tag',
|
|
||||||
message: '{nDocsTagged} with "{tagName}"',
|
|
||||||
},
|
|
||||||
{nDocsTagged: nDocsTaggedPlural(tag.count), tagName: tag.label},
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HtmlClassNameProvider
|
<>
|
||||||
className={clsx(
|
|
||||||
ThemeClassNames.wrapper.docsPages,
|
|
||||||
ThemeClassNames.page.docsTagDocListPage,
|
|
||||||
)}>
|
|
||||||
<PageMetadata title={title} />
|
<PageMetadata title={title} />
|
||||||
<SearchMetadata tag="doc_tag_doc_list" />
|
<SearchMetadata tag="doc_tag_doc_list" />
|
||||||
<Layout>
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DocTagDocListPageContent({
|
||||||
|
tag,
|
||||||
|
title,
|
||||||
|
}: Props & {title: string}): JSX.Element {
|
||||||
|
return (
|
||||||
|
<HtmlClassNameProvider
|
||||||
|
className={clsx(ThemeClassNames.page.docsTagDocListPage)}>
|
||||||
<div className="container margin-vert--lg">
|
<div className="container margin-vert--lg">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<main className="col col--8 col--offset-2">
|
<main className="col col--8 col--offset-2">
|
||||||
|
@ -89,7 +98,16 @@ export default function DocTagDocListPage({tag}: Props): JSX.Element {
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
|
||||||
</HtmlClassNameProvider>
|
</HtmlClassNameProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function DocTagDocListPage(props: Props): JSX.Element {
|
||||||
|
const title = usePageTitle(props);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DocTagDocListPageMetadata {...props} title={title} />
|
||||||
|
<DocTagDocListPageContent {...props} title={title} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -13,22 +13,28 @@ import {
|
||||||
ThemeClassNames,
|
ThemeClassNames,
|
||||||
translateTagsPageTitle,
|
translateTagsPageTitle,
|
||||||
} from '@docusaurus/theme-common';
|
} from '@docusaurus/theme-common';
|
||||||
import Layout from '@theme/Layout';
|
|
||||||
import TagsListByLetter from '@theme/TagsListByLetter';
|
import TagsListByLetter from '@theme/TagsListByLetter';
|
||||||
import SearchMetadata from '@theme/SearchMetadata';
|
import SearchMetadata from '@theme/SearchMetadata';
|
||||||
import type {Props} from '@theme/DocTagsListPage';
|
import type {Props} from '@theme/DocTagsListPage';
|
||||||
|
|
||||||
export default function DocTagsListPage({tags}: Props): JSX.Element {
|
function DocTagsListPageMetadata({
|
||||||
const title = translateTagsPageTitle();
|
title,
|
||||||
|
}: Props & {title: string}): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<HtmlClassNameProvider
|
<>
|
||||||
className={clsx(
|
|
||||||
ThemeClassNames.wrapper.docsPages,
|
|
||||||
ThemeClassNames.page.docsTagsListPage,
|
|
||||||
)}>
|
|
||||||
<PageMetadata title={title} />
|
<PageMetadata title={title} />
|
||||||
<SearchMetadata tag="doc_tags_list" />
|
<SearchMetadata tag="doc_tags_list" />
|
||||||
<Layout>
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DocTagsListPageContent({
|
||||||
|
tags,
|
||||||
|
title,
|
||||||
|
}: Props & {title: string}): JSX.Element {
|
||||||
|
return (
|
||||||
|
<HtmlClassNameProvider
|
||||||
|
className={clsx(ThemeClassNames.page.docsTagsListPage)}>
|
||||||
<div className="container margin-vert--lg">
|
<div className="container margin-vert--lg">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<main className="col col--8 col--offset-2">
|
<main className="col col--8 col--offset-2">
|
||||||
|
@ -37,7 +43,16 @@ export default function DocTagsListPage({tags}: Props): JSX.Element {
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
|
||||||
</HtmlClassNameProvider>
|
</HtmlClassNameProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function DocTagsListPage(props: Props): JSX.Element {
|
||||||
|
const title = translateTagsPageTitle();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DocTagsListPageMetadata {...props} title={title} />
|
||||||
|
<DocTagsListPageContent {...props} title={title} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {HtmlClassNameProvider, PageMetadata} from '@docusaurus/theme-common';
|
||||||
|
import {
|
||||||
|
docVersionSearchTag,
|
||||||
|
DocsVersionProvider,
|
||||||
|
} from '@docusaurus/theme-common/internal';
|
||||||
|
import renderRoutes from '@docusaurus/renderRoutes';
|
||||||
|
import SearchMetadata from '@theme/SearchMetadata';
|
||||||
|
|
||||||
|
import type {Props} from '@theme/DocVersionRoot';
|
||||||
|
|
||||||
|
function DocVersionRootMetadata(props: Props): JSX.Element {
|
||||||
|
const {version} = props;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SearchMetadata
|
||||||
|
version={version.version}
|
||||||
|
tag={docVersionSearchTag(version.pluginId, version.version)}
|
||||||
|
/>
|
||||||
|
<PageMetadata>
|
||||||
|
{version.noIndex && <meta name="robots" content="noindex, nofollow" />}
|
||||||
|
</PageMetadata>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DocVersionRootContent(props: Props): JSX.Element {
|
||||||
|
const {version, route} = props;
|
||||||
|
return (
|
||||||
|
<HtmlClassNameProvider className={version.className}>
|
||||||
|
<DocsVersionProvider version={version}>
|
||||||
|
{renderRoutes(route.routes!)}
|
||||||
|
</DocsVersionProvider>
|
||||||
|
</HtmlClassNameProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default function DocVersionRoot(props: Props): JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DocVersionRootMetadata {...props} />
|
||||||
|
<DocVersionRootContent {...props} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import {ThemeClassNames, HtmlClassNameProvider} from '@docusaurus/theme-common';
|
||||||
|
import renderRoutes from '@docusaurus/renderRoutes';
|
||||||
|
import Layout from '@theme/Layout';
|
||||||
|
|
||||||
|
import type {Props} from '@theme/DocVersionRoot';
|
||||||
|
|
||||||
|
export default function DocsRoot(props: Props): JSX.Element {
|
||||||
|
return (
|
||||||
|
<HtmlClassNameProvider className={clsx(ThemeClassNames.wrapper.docsPages)}>
|
||||||
|
<Layout>{renderRoutes(props.route.routes!)}</Layout>
|
||||||
|
</HtmlClassNameProvider>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,54 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import Translate, {translate} from '@docusaurus/Translate';
|
|
||||||
import {PageMetadata} from '@docusaurus/theme-common';
|
|
||||||
import Layout from '@theme/Layout';
|
|
||||||
|
|
||||||
export default function NotFound(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMetadata
|
|
||||||
title={translate({
|
|
||||||
id: 'theme.NotFound.title',
|
|
||||||
message: 'Page Not Found',
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<Layout>
|
|
||||||
<main className="container margin-vert--xl">
|
|
||||||
<div className="row">
|
|
||||||
<div className="col col--6 col--offset-3">
|
|
||||||
<h1 className="hero__title">
|
|
||||||
<Translate
|
|
||||||
id="theme.NotFound.title"
|
|
||||||
description="The title of the 404 page">
|
|
||||||
Page Not Found
|
|
||||||
</Translate>
|
|
||||||
</h1>
|
|
||||||
<p>
|
|
||||||
<Translate
|
|
||||||
id="theme.NotFound.p1"
|
|
||||||
description="The first paragraph of the 404 page">
|
|
||||||
We could not find what you were looking for.
|
|
||||||
</Translate>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<Translate
|
|
||||||
id="theme.NotFound.p2"
|
|
||||||
description="The 2nd paragraph of the 404 page">
|
|
||||||
Please contact the owner of the site that linked you to the
|
|
||||||
original URL and let them know their link is broken.
|
|
||||||
</Translate>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</Layout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import Translate from '@docusaurus/Translate';
|
||||||
|
import type {Props} from '@theme/NotFound/Content';
|
||||||
|
|
||||||
|
export default function NotFoundContent({className}: Props): JSX.Element {
|
||||||
|
return (
|
||||||
|
<main className={clsx('container margin-vert--xl', className)}>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col col--6 col--offset-3">
|
||||||
|
<h1 className="hero__title">
|
||||||
|
<Translate
|
||||||
|
id="theme.NotFound.title"
|
||||||
|
description="The title of the 404 page">
|
||||||
|
Page Not Found
|
||||||
|
</Translate>
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
<Translate
|
||||||
|
id="theme.NotFound.p1"
|
||||||
|
description="The first paragraph of the 404 page">
|
||||||
|
We could not find what you were looking for.
|
||||||
|
</Translate>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<Translate
|
||||||
|
id="theme.NotFound.p2"
|
||||||
|
description="The 2nd paragraph of the 404 page">
|
||||||
|
Please contact the owner of the site that linked you to the
|
||||||
|
original URL and let them know their link is broken.
|
||||||
|
</Translate>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {translate} from '@docusaurus/Translate';
|
||||||
|
import {PageMetadata} from '@docusaurus/theme-common';
|
||||||
|
import Layout from '@theme/Layout';
|
||||||
|
import NotFoundContent from '@theme/NotFound/Content';
|
||||||
|
|
||||||
|
export default function Index(): JSX.Element {
|
||||||
|
const title = translate({
|
||||||
|
id: 'theme.NotFound.title',
|
||||||
|
message: 'Page Not Found',
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageMetadata title={title} />
|
||||||
|
<Layout>
|
||||||
|
<NotFoundContent />
|
||||||
|
</Layout>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -73,7 +73,7 @@ export {
|
||||||
useDocsVersionCandidates,
|
useDocsVersionCandidates,
|
||||||
useLayoutDoc,
|
useLayoutDoc,
|
||||||
useLayoutDocsSidebar,
|
useLayoutDocsSidebar,
|
||||||
useDocRouteMetadata,
|
useDocRootMetadata,
|
||||||
} from './utils/docsUtils';
|
} from './utils/docsUtils';
|
||||||
|
|
||||||
export {useTitleFormatter} from './utils/generalUtils';
|
export {useTitleFormatter} from './utils/generalUtils';
|
||||||
|
|
|
@ -27,6 +27,9 @@ export const ThemeClassNames = {
|
||||||
},
|
},
|
||||||
wrapper: {
|
wrapper: {
|
||||||
main: 'main-wrapper',
|
main: 'main-wrapper',
|
||||||
|
// TODO these wrapper class names are now quite useless
|
||||||
|
// TODO do breaking change later in 3.0
|
||||||
|
// we already add plugin name/id class on <html>: that's enough
|
||||||
blogPages: 'blog-wrapper',
|
blogPages: 'blog-wrapper',
|
||||||
docsPages: 'docs-wrapper',
|
docsPages: 'docs-wrapper',
|
||||||
mdxPages: 'mdx-wrapper',
|
mdxPages: 'mdx-wrapper',
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
type GlobalSidebar,
|
type GlobalSidebar,
|
||||||
type GlobalDoc,
|
type GlobalDoc,
|
||||||
} from '@docusaurus/plugin-content-docs/client';
|
} from '@docusaurus/plugin-content-docs/client';
|
||||||
import type {Props as DocPageProps} from '@theme/DocPage';
|
import type {Props as DocRootProps} from '@theme/DocRoot';
|
||||||
import {useDocsPreferredVersion} from '../contexts/docsPreferredVersion';
|
import {useDocsPreferredVersion} from '../contexts/docsPreferredVersion';
|
||||||
import {useDocsVersion} from '../contexts/docsVersion';
|
import {useDocsVersion} from '../contexts/docsVersion';
|
||||||
import {useDocsSidebar} from '../contexts/docsSidebar';
|
import {useDocsSidebar} from '../contexts/docsSidebar';
|
||||||
|
@ -290,14 +290,11 @@ Available doc ids are:
|
||||||
* version metadata, and the subroutes creating individual doc pages. This hook
|
* version metadata, and the subroutes creating individual doc pages. This hook
|
||||||
* will match the current location against all known sub-routes.
|
* will match the current location against all known sub-routes.
|
||||||
*
|
*
|
||||||
* @param props The props received by `@theme/DocPage`
|
* @param props The props received by `@theme/DocRoot`
|
||||||
* @returns The data of the relevant document at the current location, or `null`
|
* @returns The data of the relevant document at the current location, or `null`
|
||||||
* if no document associated with the current location can be found.
|
* if no document associated with the current location can be found.
|
||||||
*/
|
*/
|
||||||
export function useDocRouteMetadata({
|
export function useDocRootMetadata({route}: DocRootProps): null | {
|
||||||
route,
|
|
||||||
versionMetadata,
|
|
||||||
}: DocPageProps): null | {
|
|
||||||
/** The element that should be rendered at the current location. */
|
/** The element that should be rendered at the current location. */
|
||||||
docElement: JSX.Element;
|
docElement: JSX.Element;
|
||||||
/**
|
/**
|
||||||
|
@ -309,6 +306,7 @@ export function useDocRouteMetadata({
|
||||||
sidebarItems: PropSidebar | undefined;
|
sidebarItems: PropSidebar | undefined;
|
||||||
} {
|
} {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const versionMetadata = useDocsVersion();
|
||||||
const docRoutes = route.routes!;
|
const docRoutes = route.routes!;
|
||||||
const currentDocRoute = docRoutes.find((docRoute) =>
|
const currentDocRoute = docRoutes.find((docRoute) =>
|
||||||
matchPath(location.pathname, docRoute),
|
matchPath(location.pathname, docRoute),
|
||||||
|
|
|
@ -60,7 +60,7 @@ exports[`loadRoutes loads nested route config 1`] = `
|
||||||
{
|
{
|
||||||
"registry": {
|
"registry": {
|
||||||
"__comp---theme-doc-item-178-a40": "@theme/DocItem",
|
"__comp---theme-doc-item-178-a40": "@theme/DocItem",
|
||||||
"__comp---theme-doc-page-1-be-9be": "@theme/DocPage",
|
"__comp---theme-doc-roota-94-67a": "@theme/DocRoot",
|
||||||
"content---docs-foo-baz-8-ce-61e": "docs/foo/baz.md",
|
"content---docs-foo-baz-8-ce-61e": "docs/foo/baz.md",
|
||||||
"content---docs-helloaff-811": "docs/hello.md",
|
"content---docs-helloaff-811": "docs/hello.md",
|
||||||
"docsMetadata---docs-routef-34-881": "docs-b5f.json",
|
"docsMetadata---docs-routef-34-881": "docs-b5f.json",
|
||||||
|
@ -77,8 +77,8 @@ exports[`loadRoutes loads nested route config 1`] = `
|
||||||
"content": "content---docs-helloaff-811",
|
"content": "content---docs-helloaff-811",
|
||||||
"metadata": "metadata---docs-hello-956-741",
|
"metadata": "metadata---docs-hello-956-741",
|
||||||
},
|
},
|
||||||
"/docs:route-502": {
|
"/docs:route-9d0": {
|
||||||
"__comp": "__comp---theme-doc-page-1-be-9be",
|
"__comp": "__comp---theme-doc-roota-94-67a",
|
||||||
"docsMetadata": "docsMetadata---docs-routef-34-881",
|
"docsMetadata": "docsMetadata---docs-routef-34-881",
|
||||||
},
|
},
|
||||||
"docs/foo/baz-eb2": {
|
"docs/foo/baz-eb2": {
|
||||||
|
@ -96,7 +96,7 @@ import ComponentCreator from '@docusaurus/ComponentCreator';
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
path: '/docs:route',
|
path: '/docs:route',
|
||||||
component: ComponentCreator('/docs:route', '502'),
|
component: ComponentCreator('/docs:route', '9d0'),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/docs/hello',
|
path: '/docs/hello',
|
||||||
|
|
|
@ -103,7 +103,7 @@ describe('handleDuplicateRoutes', () => {
|
||||||
describe('loadRoutes', () => {
|
describe('loadRoutes', () => {
|
||||||
it('loads nested route config', () => {
|
it('loads nested route config', () => {
|
||||||
const nestedRouteConfig: RouteConfig = {
|
const nestedRouteConfig: RouteConfig = {
|
||||||
component: '@theme/DocPage',
|
component: '@theme/DocRoot',
|
||||||
path: '/docs:route',
|
path: '/docs:route',
|
||||||
modules: {
|
modules: {
|
||||||
docsMetadata: 'docs-b5f.json',
|
docsMetadata: 'docs-b5f.json',
|
||||||
|
|
|
@ -103,3 +103,45 @@ exports[`sortConfig sorts route config given a baseURL 1`] = `
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`sortConfig sorts route config recursively 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"component": "",
|
||||||
|
"exact": true,
|
||||||
|
"path": "/some/page",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "",
|
||||||
|
"path": "/docs",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"component": "",
|
||||||
|
"exact": true,
|
||||||
|
"path": "/docs/tags",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "",
|
||||||
|
"exact": true,
|
||||||
|
"path": "/docs/tags/someTag",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "",
|
||||||
|
"path": "/docs",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"component": "",
|
||||||
|
"exact": true,
|
||||||
|
"path": "/docs/doc1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "",
|
||||||
|
"exact": true,
|
||||||
|
"path": "/docs/doc2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
|
@ -207,6 +207,52 @@ describe('sortConfig', () => {
|
||||||
expect(routes).toMatchSnapshot();
|
expect(routes).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sorts route config recursively', () => {
|
||||||
|
const routes: RouteConfig[] = [
|
||||||
|
{
|
||||||
|
path: '/docs',
|
||||||
|
component: '',
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/docs/tags',
|
||||||
|
component: '',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/docs',
|
||||||
|
component: '',
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/docs/doc1',
|
||||||
|
component: '',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/docs/doc2',
|
||||||
|
component: '',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/docs/tags/someTag',
|
||||||
|
component: '',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/some/page',
|
||||||
|
component: '',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
sortConfig(routes);
|
||||||
|
|
||||||
|
expect(routes).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
it('sorts route config given a baseURL', () => {
|
it('sorts route config given a baseURL', () => {
|
||||||
const baseURL = '/latest/';
|
const baseURL = '/latest/';
|
||||||
const routes: RouteConfig[] = [
|
const routes: RouteConfig[] = [
|
||||||
|
|
|
@ -63,6 +63,8 @@ export function sortConfig(
|
||||||
});
|
});
|
||||||
|
|
||||||
routeConfigs.forEach((routeConfig) => {
|
routeConfigs.forEach((routeConfig) => {
|
||||||
routeConfig.routes?.sort((a, b) => a.path.localeCompare(b.path));
|
if (routeConfig.routes) {
|
||||||
|
sortConfig(routeConfig.routes, baseUrl);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,9 @@ Accepted fields:
|
||||||
| `sidebarCollapsed` | `boolean` | `true` | Whether sidebar categories are collapsed by default. See also [Expanded categories by default](/docs/sidebar#expanded-categories-by-default) |
|
| `sidebarCollapsed` | `boolean` | `true` | Whether sidebar categories are collapsed by default. See also [Expanded categories by default](/docs/sidebar#expanded-categories-by-default) |
|
||||||
| `sidebarItemsGenerator` | <a href="#SidebarGenerator"><code>SidebarGenerator</code></a> | _Omitted_ | Function used to replace the sidebar items of type `'autogenerated'` with real sidebar items (docs, categories, links...). See also [Customize the sidebar items generator](/docs/sidebar#customize-the-sidebar-items-generator) |
|
| `sidebarItemsGenerator` | <a href="#SidebarGenerator"><code>SidebarGenerator</code></a> | _Omitted_ | Function used to replace the sidebar items of type `'autogenerated'` with real sidebar items (docs, categories, links...). See also [Customize the sidebar items generator](/docs/sidebar#customize-the-sidebar-items-generator) |
|
||||||
| `numberPrefixParser` | <code>boolean \|</code> <a href="#PrefixParser"><code>PrefixParser</code></a> | _Omitted_ | Custom parsing logic to extract number prefixes from file names. Use `false` to disable this behavior and leave the docs untouched, and `true` to use the default parser. See also [Using number prefixes](/docs/sidebar#using-number-prefixes) |
|
| `numberPrefixParser` | <code>boolean \|</code> <a href="#PrefixParser"><code>PrefixParser</code></a> | _Omitted_ | Custom parsing logic to extract number prefixes from file names. Use `false` to disable this behavior and leave the docs untouched, and `true` to use the default parser. See also [Using number prefixes](/docs/sidebar#using-number-prefixes) |
|
||||||
| `docLayoutComponent` | `string` | `'@theme/DocPage'` | Root layout component of each doc page. Provides the version data context, and is not unmounted when switching docs. |
|
| `docsRootComponent` | `string` | `'@theme/DocsRoot'` | Parent component of all the docs plugin pages (including all versions). Stays mounted when navigation between docs pages and versions. |
|
||||||
|
| `docVersionRootComponent` | `string` | `'@theme/DocVersionLayout'` | Parent component of all docs pages of an individual version (doc pages with sidebars, tags pages). Stays mounted when navigation between pages of that specific version. |
|
||||||
|
| `docRootComponent` | `string` | `'@theme/DocPage'` | Parent component of all doc pages with sidebars (regular docs pages, category generated index pages). Stays mounted when navigation between such pages. |
|
||||||
| `docItemComponent` | `string` | `'@theme/DocItem'` | Main doc container, with TOC, pagination, etc. |
|
| `docItemComponent` | `string` | `'@theme/DocItem'` | Main doc container, with TOC, pagination, etc. |
|
||||||
| `docTagsListComponent` | `string` | `'@theme/DocTagsListPage'` | Root component of the tags list page |
|
| `docTagsListComponent` | `string` | `'@theme/DocTagsListPage'` | Root component of the tags list page |
|
||||||
| `docTagDocListComponent` | `string` | `'@theme/DocTagDocListPage'` | Root component of the "docs containing tag X" page. |
|
| `docTagDocListComponent` | `string` | `'@theme/DocTagDocListPage'` | Root component of the "docs containing tag X" page. |
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue