mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-30 15:00:09 +02:00
refactor(v2): better docs metadata (#1815)
This commit is contained in:
parent
a04bd440b4
commit
e7ba8af6d9
10 changed files with 185 additions and 149 deletions
|
@ -28,14 +28,15 @@ describe('loadDocs', () => {
|
|||
path: pluginPath,
|
||||
sidebarPath,
|
||||
});
|
||||
const {docs: docsMetadata} = await plugin.loadContent();
|
||||
const {docsMetadata} = await plugin.loadContent();
|
||||
|
||||
expect(docsMetadata.hello).toEqual({
|
||||
category: 'Guides',
|
||||
id: 'hello',
|
||||
permalink: '/docs/hello',
|
||||
previous: 'foo/baz',
|
||||
previous_title: 'baz',
|
||||
previous: {
|
||||
title: 'baz',
|
||||
permalink: '/docs/foo/baz',
|
||||
},
|
||||
sidebar: 'docs',
|
||||
source: path.join('@site', pluginPath, 'hello.md'),
|
||||
title: 'Hello, World !',
|
||||
|
@ -43,10 +44,11 @@ describe('loadDocs', () => {
|
|||
});
|
||||
|
||||
expect(docsMetadata['foo/bar']).toEqual({
|
||||
category: 'Test',
|
||||
id: 'foo/bar',
|
||||
next: 'foo/baz',
|
||||
next_title: 'baz',
|
||||
next: {
|
||||
title: 'baz',
|
||||
permalink: '/docs/foo/baz',
|
||||
},
|
||||
permalink: '/docs/foo/bar',
|
||||
sidebar: 'docs',
|
||||
source: path.join('@site', pluginPath, 'foo', 'bar.md'),
|
||||
|
|
|
@ -43,36 +43,26 @@ describe('createOrder', () => {
|
|||
});
|
||||
expect(result).toEqual({
|
||||
doc1: {
|
||||
category: 'Category1',
|
||||
subCategory: 'Subcategory 1',
|
||||
next: 'doc2',
|
||||
previous: undefined,
|
||||
sidebar: 'docs',
|
||||
},
|
||||
doc2: {
|
||||
category: 'Category1',
|
||||
subCategory: 'Subcategory 2',
|
||||
next: 'doc3',
|
||||
previous: 'doc1',
|
||||
sidebar: 'docs',
|
||||
},
|
||||
doc3: {
|
||||
category: 'Category2',
|
||||
subCategory: undefined,
|
||||
next: 'doc4',
|
||||
previous: 'doc2',
|
||||
sidebar: 'docs',
|
||||
},
|
||||
doc4: {
|
||||
category: 'Category2',
|
||||
subCategory: undefined,
|
||||
next: undefined,
|
||||
previous: 'doc3',
|
||||
sidebar: 'docs',
|
||||
},
|
||||
doc5: {
|
||||
category: 'Category1',
|
||||
subCategory: undefined,
|
||||
next: undefined,
|
||||
previous: undefined,
|
||||
sidebar: 'otherDocs',
|
||||
|
@ -103,36 +93,26 @@ describe('createOrder', () => {
|
|||
});
|
||||
expect(result).toEqual({
|
||||
doc1: {
|
||||
category: 'Category1',
|
||||
subCategory: undefined,
|
||||
next: 'doc2',
|
||||
previous: undefined,
|
||||
sidebar: 'docs',
|
||||
},
|
||||
doc2: {
|
||||
category: 'Category1',
|
||||
subCategory: undefined,
|
||||
next: 'doc3',
|
||||
previous: 'doc1',
|
||||
sidebar: 'docs',
|
||||
},
|
||||
doc3: {
|
||||
category: 'Category2',
|
||||
subCategory: undefined,
|
||||
next: 'doc4',
|
||||
previous: 'doc2',
|
||||
sidebar: 'docs',
|
||||
},
|
||||
doc4: {
|
||||
category: 'Category2',
|
||||
subCategory: undefined,
|
||||
next: undefined,
|
||||
previous: 'doc3',
|
||||
sidebar: 'docs',
|
||||
},
|
||||
doc5: {
|
||||
category: 'Category1',
|
||||
subCategory: undefined,
|
||||
next: undefined,
|
||||
previous: undefined,
|
||||
sidebar: 'otherDocs',
|
||||
|
@ -164,22 +144,16 @@ describe('createOrder', () => {
|
|||
});
|
||||
expect(result).toEqual({
|
||||
doc1: {
|
||||
category: 'Category1',
|
||||
subCategory: undefined,
|
||||
next: undefined,
|
||||
previous: undefined,
|
||||
sidebar: 'docs',
|
||||
},
|
||||
'version-1.2.3-doc1': {
|
||||
category: 'Category2',
|
||||
subCategory: undefined,
|
||||
next: undefined,
|
||||
previous: 'version-1.2.3-doc2',
|
||||
sidebar: 'version-1.2.3-docs',
|
||||
},
|
||||
'version-1.2.3-doc2': {
|
||||
category: 'Category1',
|
||||
subCategory: undefined,
|
||||
next: 'version-1.2.3-doc1',
|
||||
previous: undefined,
|
||||
sidebar: 'version-1.2.3-docs',
|
||||
|
@ -227,22 +201,16 @@ describe('createOrder', () => {
|
|||
});
|
||||
expect(result).toEqual({
|
||||
doc2: {
|
||||
category: 'Category1',
|
||||
subCategory: 'Subcategory 2',
|
||||
next: 'doc3',
|
||||
previous: undefined,
|
||||
sidebar: 'docs',
|
||||
},
|
||||
doc3: {
|
||||
category: 'Category2',
|
||||
subCategory: undefined,
|
||||
next: undefined,
|
||||
previous: 'doc2',
|
||||
sidebar: 'docs',
|
||||
},
|
||||
doc5: {
|
||||
category: 'Category1',
|
||||
subCategory: undefined,
|
||||
next: undefined,
|
||||
previous: undefined,
|
||||
sidebar: 'otherDocs',
|
||||
|
|
|
@ -18,11 +18,17 @@ import {
|
|||
PluginOptions,
|
||||
Sidebar,
|
||||
Order,
|
||||
Metadata,
|
||||
DocsMetadata,
|
||||
LoadedContent,
|
||||
SourceToPermalink,
|
||||
PermalinkToId,
|
||||
PermalinkToSidebar,
|
||||
DocsSidebarItemCategory,
|
||||
SidebarItemLink,
|
||||
SidebarItemDoc,
|
||||
SidebarItemCategory,
|
||||
DocsSidebar,
|
||||
DocsBaseMetadata,
|
||||
MetadataRaw,
|
||||
} from './types';
|
||||
import {Configuration} from 'webpack';
|
||||
|
||||
|
@ -64,13 +70,15 @@ export default function pluginContentDocs(
|
|||
return null;
|
||||
}
|
||||
|
||||
const docsSidebars: Sidebar = loadSidebars(sidebarPath);
|
||||
const loadedSidebars: Sidebar = loadSidebars(sidebarPath);
|
||||
|
||||
// Build the docs ordering such as next, previous, category and sidebar.
|
||||
const order: Order = createOrder(docsSidebars);
|
||||
const order: Order = createOrder(loadedSidebars);
|
||||
|
||||
// Prepare metadata container.
|
||||
const docs: DocsMetadata = {};
|
||||
const docsMetadataRaw: {
|
||||
[id: string]: MetadataRaw;
|
||||
} = {};
|
||||
|
||||
// Metadata for default docs files.
|
||||
const docsFiles = await globby(include, {
|
||||
|
@ -78,7 +86,7 @@ export default function pluginContentDocs(
|
|||
});
|
||||
await Promise.all(
|
||||
docsFiles.map(async source => {
|
||||
const metadata: Metadata = await processMetadata(
|
||||
const metadata: MetadataRaw = await processMetadata(
|
||||
source,
|
||||
docsDir,
|
||||
order,
|
||||
|
@ -86,36 +94,99 @@ export default function pluginContentDocs(
|
|||
routeBasePath,
|
||||
siteDir,
|
||||
);
|
||||
docs[metadata.id] = metadata;
|
||||
docsMetadataRaw[metadata.id] = metadata;
|
||||
}),
|
||||
);
|
||||
|
||||
// Get the titles of the previous and next ids so that we can use them.
|
||||
Object.keys(docs).forEach(currentID => {
|
||||
const previousID = idx(docs, [currentID, 'previous']);
|
||||
// Construct docsMetadata
|
||||
const docsMetadata: DocsMetadata = {};
|
||||
Object.keys(docsMetadataRaw).forEach(currentID => {
|
||||
let previous;
|
||||
let next;
|
||||
const previousID = idx(docsMetadataRaw, [currentID, 'previous']);
|
||||
if (previousID) {
|
||||
const previousTitle = idx(docs, [previousID, 'title']);
|
||||
docs[currentID].previous_title = previousTitle || 'Previous';
|
||||
previous = {
|
||||
title: idx(docsMetadataRaw, [previousID, 'title']) || 'Previous',
|
||||
permalink: idx(docsMetadataRaw, [previousID, 'permalink']),
|
||||
};
|
||||
}
|
||||
const nextID = idx(docs, [currentID, 'next']);
|
||||
const nextID = idx(docsMetadataRaw, [currentID, 'next']);
|
||||
if (nextID) {
|
||||
const nextTitle = idx(docs, [nextID, 'title']);
|
||||
docs[currentID].next_title = nextTitle || 'Next';
|
||||
next = {
|
||||
title: idx(docsMetadataRaw, [nextID, 'title']) || 'Next',
|
||||
permalink: idx(docsMetadataRaw, [nextID, 'permalink']),
|
||||
};
|
||||
}
|
||||
docsMetadata[currentID] = {
|
||||
...docsMetadataRaw[currentID],
|
||||
previous,
|
||||
next,
|
||||
};
|
||||
});
|
||||
|
||||
const permalinkToSidebar: PermalinkToSidebar = {};
|
||||
Object.values(docsMetadataRaw).forEach(({source, permalink, sidebar}) => {
|
||||
sourceToPermalink[source] = permalink;
|
||||
if (sidebar) {
|
||||
permalinkToSidebar[permalink] = sidebar;
|
||||
}
|
||||
});
|
||||
|
||||
const permalinkToId: PermalinkToId = {};
|
||||
Object.values(docs).forEach(({id, source, permalink}) => {
|
||||
sourceToPermalink[source] = permalink;
|
||||
permalinkToId[permalink] = id;
|
||||
});
|
||||
const convertDocLink = (item: SidebarItemDoc): SidebarItemLink => {
|
||||
const linkID = item.id;
|
||||
const linkMetadata = docsMetadataRaw[linkID];
|
||||
|
||||
if (!linkMetadata) {
|
||||
throw new Error(
|
||||
`Improper sidebars file, document with id '${linkID}' not found.`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'link',
|
||||
label: linkMetadata.sidebar_label || linkMetadata.title,
|
||||
href: linkMetadata.permalink,
|
||||
};
|
||||
};
|
||||
|
||||
const normalizeCategory = (
|
||||
category: SidebarItemCategory,
|
||||
): DocsSidebarItemCategory => {
|
||||
const items = category.items.map(item => {
|
||||
switch (item.type) {
|
||||
case 'category':
|
||||
return normalizeCategory(item as SidebarItemCategory);
|
||||
case 'ref':
|
||||
case 'doc':
|
||||
return convertDocLink(item as SidebarItemDoc);
|
||||
case 'link':
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown sidebar item type: ${item.type}`);
|
||||
}
|
||||
return item as SidebarItemLink;
|
||||
});
|
||||
return {...category, items};
|
||||
};
|
||||
|
||||
// Transform the sidebar so that all sidebar item will be in the form of 'link' or 'category' only
|
||||
// This is what will be passed as props to the UI component
|
||||
const docsSidebars: DocsSidebar = Object.entries(loadedSidebars).reduce(
|
||||
(acc: DocsSidebar, [sidebarId, sidebarItemCategories]) => {
|
||||
acc[sidebarId] = sidebarItemCategories.map(sidebarItemCategory =>
|
||||
normalizeCategory(sidebarItemCategory),
|
||||
);
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
return {
|
||||
docs,
|
||||
docsMetadata,
|
||||
docsDir,
|
||||
docsSidebars,
|
||||
sourceToPermalink,
|
||||
permalinkToId,
|
||||
permalinkToSidebar,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -128,7 +199,7 @@ export default function pluginContentDocs(
|
|||
const {addRoute, createData} = actions;
|
||||
|
||||
const routes = await Promise.all(
|
||||
Object.values(content.docs).map(async metadataItem => {
|
||||
Object.values(content.docsMetadata).map(async metadataItem => {
|
||||
const metadataPath = await createData(
|
||||
`${docuHash(metadataItem.permalink)}.json`,
|
||||
JSON.stringify(metadataItem, null, 2),
|
||||
|
@ -145,13 +216,18 @@ export default function pluginContentDocs(
|
|||
}),
|
||||
);
|
||||
|
||||
const docsBaseMetadata: DocsBaseMetadata = {
|
||||
docsSidebars: content.docsSidebars,
|
||||
permalinkToSidebar: content.permalinkToSidebar,
|
||||
};
|
||||
|
||||
const docsBaseRoute = normalizeUrl([
|
||||
(context.siteConfig as DocusaurusConfig).baseUrl,
|
||||
routeBasePath,
|
||||
]);
|
||||
const docsMetadataPath = await createData(
|
||||
const docsBaseMetadataPath = await createData(
|
||||
`${docuHash(docsBaseRoute)}.json`,
|
||||
JSON.stringify(content, null, 2),
|
||||
JSON.stringify(docsBaseMetadata, null, 2),
|
||||
);
|
||||
|
||||
addRoute({
|
||||
|
@ -159,7 +235,7 @@ export default function pluginContentDocs(
|
|||
component: docLayoutComponent,
|
||||
routes,
|
||||
modules: {
|
||||
docsMetadata: docsMetadataPath,
|
||||
docsMetadata: docsBaseMetadataPath,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -74,8 +74,6 @@ export default async function processMetadata(
|
|||
const {id} = metadata;
|
||||
if (order[id]) {
|
||||
metadata.sidebar = order[id].sidebar;
|
||||
metadata.category = order[id].category;
|
||||
metadata.subCategory = order[id].subCategory;
|
||||
if (order[id].next) {
|
||||
metadata.next = order[id].next;
|
||||
}
|
||||
|
|
|
@ -21,26 +21,12 @@ export default function createOrder(allSidebars: Sidebar = {}): Order {
|
|||
const sidebar = allSidebars[sidebarId];
|
||||
|
||||
const ids: string[] = [];
|
||||
const categoryOrder: (string | undefined)[] = [];
|
||||
const subCategoryOrder: (string | undefined)[] = [];
|
||||
const indexItems = ({
|
||||
items,
|
||||
categoryLabel,
|
||||
subCategoryLabel,
|
||||
}: {
|
||||
items: SidebarItem[];
|
||||
categoryLabel?: string;
|
||||
subCategoryLabel?: string;
|
||||
}) => {
|
||||
const indexItems = ({items}: {items: SidebarItem[]}) => {
|
||||
items.forEach(item => {
|
||||
switch (item.type) {
|
||||
case 'category':
|
||||
indexItems({
|
||||
items: (item as SidebarItemCategory).items,
|
||||
categoryLabel:
|
||||
categoryLabel || (item as SidebarItemCategory).label,
|
||||
subCategoryLabel:
|
||||
categoryLabel && (item as SidebarItemCategory).label,
|
||||
});
|
||||
break;
|
||||
case 'ref':
|
||||
|
@ -49,8 +35,6 @@ export default function createOrder(allSidebars: Sidebar = {}): Order {
|
|||
break;
|
||||
case 'doc':
|
||||
ids.push((item as SidebarItemDoc).id);
|
||||
categoryOrder.push(categoryLabel);
|
||||
subCategoryOrder.push(subCategoryLabel);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
|
@ -80,8 +64,6 @@ export default function createOrder(allSidebars: Sidebar = {}): Order {
|
|||
previous,
|
||||
next,
|
||||
sidebar: sidebarId,
|
||||
category: categoryOrder[i],
|
||||
subCategory: subCategoryOrder[i],
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
@ -61,12 +61,20 @@ export interface Sidebar {
|
|||
[sidebarId: string]: SidebarItemCategory[];
|
||||
}
|
||||
|
||||
export interface DocsSidebarItemCategory {
|
||||
type: string;
|
||||
label: string;
|
||||
items: (SidebarItemLink | DocsSidebarItemCategory)[];
|
||||
}
|
||||
|
||||
export interface DocsSidebar {
|
||||
[sidebarId: string]: DocsSidebarItemCategory[];
|
||||
}
|
||||
|
||||
export interface OrderMetadata {
|
||||
previous?: string;
|
||||
next?: string;
|
||||
sidebar?: string;
|
||||
category?: string;
|
||||
subCategory?: string;
|
||||
}
|
||||
|
||||
export interface Order {
|
||||
|
@ -79,11 +87,18 @@ export interface MetadataRaw extends OrderMetadata {
|
|||
description: string;
|
||||
source: string;
|
||||
permalink: string;
|
||||
sidebar_label?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface Metadata extends MetadataRaw {
|
||||
previous_title?: string;
|
||||
next_title?: string;
|
||||
export interface Paginator {
|
||||
title: string;
|
||||
permalink: string;
|
||||
}
|
||||
|
||||
export interface Metadata extends Omit<MetadataRaw, 'previous' | 'next'> {
|
||||
previous?: Paginator;
|
||||
next?: Paginator;
|
||||
}
|
||||
|
||||
export interface DocsMetadata {
|
||||
|
@ -94,14 +109,19 @@ export interface SourceToPermalink {
|
|||
[source: string]: string;
|
||||
}
|
||||
|
||||
export interface PermalinkToId {
|
||||
export interface PermalinkToSidebar {
|
||||
[permalink: string]: string;
|
||||
}
|
||||
|
||||
export interface LoadedContent {
|
||||
docs: DocsMetadata;
|
||||
docsMetadata: DocsMetadata;
|
||||
docsDir: string;
|
||||
docsSidebars: Sidebar;
|
||||
sourceToPermalink: SourceToPermalink;
|
||||
permalinkToId: PermalinkToId;
|
||||
permalinkToSidebar: PermalinkToSidebar;
|
||||
}
|
||||
|
||||
export type DocsBaseMetadata = Pick<
|
||||
LoadedContent,
|
||||
'docsSidebars' | 'permalinkToSidebar'
|
||||
>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue