mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-10 07:37:19 +02:00
feat(v2): allow nested sidebar category shorthand syntax (#2444)
* feat(v2): allow sidebar category shorthand syntax also for nested elements * Update sidebars-category-shorthand.js * Update sidebars-category-shorthand.js Co-authored-by: Yangshun Tay <tay.yang.shun@gmail.com>
This commit is contained in:
parent
85c124e3f1
commit
201c663318
12 changed files with 116 additions and 52 deletions
|
@ -27,5 +27,5 @@ module.exports = {
|
|||
transform: {
|
||||
'^.+\\.[jt]sx?$': 'babel-jest',
|
||||
},
|
||||
setupFiles: ['./jest/stylelint-rule-test.js'],
|
||||
setupFiles: ['./jest/stylelint-rule-test.js', 'array-flat-polyfill'],
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
docs: [
|
||||
{
|
||||
'level 1': [
|
||||
'a',
|
||||
{
|
||||
'level 2': [
|
||||
{
|
||||
'level 3': [
|
||||
'c',
|
||||
{
|
||||
'level 4': [
|
||||
'd',
|
||||
{
|
||||
'deeper more more': ['e'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
'f',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"docs": [
|
||||
{
|
||||
"a": "b",
|
||||
"c": "d"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -24,6 +24,17 @@ describe('loadSidebars', () => {
|
|||
expect(result).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('sidebars shortand and longform lead to exact same sidebar', async () => {
|
||||
const sidebarPath1 = path.join(fixtureDir, 'sidebars-category.js');
|
||||
const sidebarPath2 = path.join(
|
||||
fixtureDir,
|
||||
'sidebars-category-shorthand.js',
|
||||
);
|
||||
const sidebar1 = loadSidebars([sidebarPath1]);
|
||||
const sidebar2 = loadSidebars([sidebarPath2]);
|
||||
expect(sidebar1).toEqual(sidebar2);
|
||||
});
|
||||
|
||||
test('sidebars with category but category.items is not an array', async () => {
|
||||
const sidebarPath = path.join(
|
||||
fixtureDir,
|
||||
|
@ -93,15 +104,6 @@ describe('loadSidebars', () => {
|
|||
);
|
||||
});
|
||||
|
||||
test('sidebars with invalid sidebar item', async () => {
|
||||
const sidebarPath = path.join(fixtureDir, 'sidebars-invalid-item.json');
|
||||
expect(() =>
|
||||
loadSidebars([sidebarPath]),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Unknown sidebar item \\"{\\"a\\":\\"b\\",\\"c\\":\\"d\\"}\\"."`,
|
||||
);
|
||||
});
|
||||
|
||||
test('sidebars with unknown sidebar item type', async () => {
|
||||
const sidebarPath = path.join(fixtureDir, 'sidebars-unknown-type.json');
|
||||
expect(() =>
|
||||
|
|
|
@ -15,8 +15,28 @@ import {
|
|||
SidebarItemRaw,
|
||||
SidebarItemLink,
|
||||
SidebarItemDoc,
|
||||
SidebarCategoryShorthandRaw,
|
||||
} from './types';
|
||||
|
||||
function isCategoryShorthand(
|
||||
item: SidebarItemRaw,
|
||||
): item is SidebarCategoryShorthandRaw {
|
||||
return typeof item !== 'string' && !item.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert {category1: [item1,item2]} shorthand syntax to long-form syntax
|
||||
*/
|
||||
function normalizeCategoryShorthand(
|
||||
sidebar: SidebarCategoryShorthandRaw,
|
||||
): SidebarItemCategoryRaw[] {
|
||||
return Object.entries(sidebar).map(([label, items]) => ({
|
||||
type: 'category',
|
||||
label,
|
||||
items,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that item contains only allowed keys.
|
||||
*/
|
||||
|
@ -75,27 +95,29 @@ function assertIsLink(item: any): asserts item is SidebarItemLink {
|
|||
* Normalizes recursively item and all its children. Ensures that at the end
|
||||
* each item will be an object with the corresponding type.
|
||||
*/
|
||||
function normalizeItem(item: SidebarItemRaw): SidebarItem {
|
||||
function normalizeItem(item: SidebarItemRaw): SidebarItem[] {
|
||||
if (typeof item === 'string') {
|
||||
return {
|
||||
return [
|
||||
{
|
||||
type: 'doc',
|
||||
id: item,
|
||||
};
|
||||
},
|
||||
];
|
||||
}
|
||||
if (!item.type) {
|
||||
throw new Error(`Unknown sidebar item "${JSON.stringify(item)}".`);
|
||||
if (isCategoryShorthand(item)) {
|
||||
return normalizeCategoryShorthand(item).flatMap(normalizeItem);
|
||||
}
|
||||
switch (item.type) {
|
||||
case 'category':
|
||||
assertIsCategory(item);
|
||||
return {...item, items: item.items.map(normalizeItem)};
|
||||
return [{...item, items: item.items.flatMap(normalizeItem)}];
|
||||
case 'link':
|
||||
assertIsLink(item);
|
||||
return item;
|
||||
return [item];
|
||||
case 'ref':
|
||||
case 'doc':
|
||||
assertIsDoc(item);
|
||||
return item;
|
||||
return [item];
|
||||
default:
|
||||
throw new Error(`Unknown sidebar item type: ${item.type}`);
|
||||
}
|
||||
|
@ -107,20 +129,11 @@ function normalizeItem(item: SidebarItemRaw): SidebarItem {
|
|||
function normalizeSidebar(sidebars: SidebarRaw): Sidebar {
|
||||
return Object.entries(sidebars).reduce(
|
||||
(acc: Sidebar, [sidebarId, sidebar]) => {
|
||||
let normalizedSidebar: SidebarItemRaw[];
|
||||
const normalizedSidebar: SidebarItemRaw[] = Array.isArray(sidebar)
|
||||
? sidebar
|
||||
: normalizeCategoryShorthand(sidebar);
|
||||
|
||||
if (!Array.isArray(sidebar)) {
|
||||
// Convert sidebar to a more generic structure.
|
||||
normalizedSidebar = Object.entries(sidebar).map(([label, items]) => ({
|
||||
type: 'category',
|
||||
label,
|
||||
items,
|
||||
}));
|
||||
} else {
|
||||
normalizedSidebar = sidebar;
|
||||
}
|
||||
|
||||
acc[sidebarId] = normalizedSidebar.map(normalizeItem);
|
||||
acc[sidebarId] = normalizedSidebar.flatMap(normalizeItem);
|
||||
|
||||
return acc;
|
||||
},
|
||||
|
|
|
@ -55,6 +55,7 @@ export type SidebarItem =
|
|||
|
||||
export type SidebarItemRaw =
|
||||
| string
|
||||
| SidebarCategoryShorthandRaw
|
||||
| SidebarItemDoc
|
||||
| SidebarItemLink
|
||||
| SidebarItemCategoryRaw
|
||||
|
@ -63,13 +64,13 @@ export type SidebarItemRaw =
|
|||
[key: string]: any;
|
||||
};
|
||||
|
||||
export interface SidebarCategoryShorthandRaw {
|
||||
[sidebarCategory: string]: SidebarItemRaw[];
|
||||
}
|
||||
|
||||
// Sidebar given by user that is not normalized yet. e.g: sidebars.json
|
||||
export interface SidebarRaw {
|
||||
[sidebarId: string]:
|
||||
| {
|
||||
[sidebarCategory: string]: SidebarItemRaw[];
|
||||
}
|
||||
| SidebarItemRaw[];
|
||||
[sidebarId: string]: SidebarCategoryShorthandRaw | SidebarItemRaw[];
|
||||
}
|
||||
|
||||
export interface Sidebar {
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
"@babel/runtime": "^7.7.4",
|
||||
"@docusaurus/utils": "^2.0.0-alpha.48",
|
||||
"@endiliey/static-site-generator-webpack-plugin": "^4.0.0",
|
||||
"array-flat-polyfill": "^1.0.1",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.0",
|
||||
"cache-loader": "^4.1.0",
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import 'array-flat-polyfill';
|
||||
|
||||
export {build} from './commands/build';
|
||||
export {start} from './commands/start';
|
||||
export {swizzle} from './commands/swizzle';
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"module": "commonjs",
|
||||
"lib": ["es2017"],
|
||||
"lib": ["es2017","es2019.array"],
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ module.exports = {
|
|||
};
|
||||
```
|
||||
|
||||
If you don't want to rely on iteration order of JavaScript object keys for the category name, the following sidebar object is also equivalent of the above.
|
||||
Keep in mind that EcmaScript does not guarantee `Object.keys({a,b}) === ['a','b']` (yet, this is generally true). If you don't want to rely on iteration order of JavaScript object keys for the category name, the following sidebar object is also equivalent of the above shorthand syntax.
|
||||
|
||||
```js
|
||||
// sidebars.js
|
||||
|
@ -221,7 +221,23 @@ module.exports = {
|
|||
{
|
||||
type: 'category',
|
||||
label: 'Docs',
|
||||
items: ['markdown-features', 'sidebar'],
|
||||
items: ['markdown-features', 'sidebar', 'versioning'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
**Note**: it's possible to use the shorthand syntax to create nested categories:
|
||||
|
||||
```js
|
||||
// sidebars.js
|
||||
module.exports = {
|
||||
docs: {
|
||||
Guides: [
|
||||
'creating-pages',
|
||||
{
|
||||
Docs: ['markdown-features', 'sidebar', 'versioning'],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -19,9 +19,7 @@ module.exports = {
|
|||
'styling-layout',
|
||||
'static-assets',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Docs',
|
||||
items: ['markdown-features', 'sidebar', 'versioning'],
|
||||
Docs: ['markdown-features', 'sidebar', 'versioning'],
|
||||
},
|
||||
'blog',
|
||||
'search',
|
||||
|
|
|
@ -3147,6 +3147,11 @@ array-find-index@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
|
||||
integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
|
||||
|
||||
array-flat-polyfill@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/array-flat-polyfill/-/array-flat-polyfill-1.0.1.tgz#1e3a4255be619dfbffbfd1d635c1cf357cd034e7"
|
||||
integrity sha512-hfJmKupmQN0lwi0xG6FQ5U8Rd97RnIERplymOv/qpq8AoNKPPAnxJadjFA23FNWm88wykh9HmpLJUUwUtNU/iw==
|
||||
|
||||
array-flatten@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue