diff --git a/jest.config.js b/jest.config.js index 8bb2ddeeff..66bbe01be9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -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'], }; diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-category-shorthand.js b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-category-shorthand.js new file mode 100644 index 0000000000..296be1742d --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-category-shorthand.js @@ -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', + ], + }, + ], + }, + ], +}; diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-invalid-item.json b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-invalid-item.json deleted file mode 100644 index 71088ca369..0000000000 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-invalid-item.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "docs": [ - { - "a": "b", - "c": "d" - } - ] -} diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts index 0e3b8a0015..49a788dfd9 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts @@ -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(() => diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars.ts b/packages/docusaurus-plugin-content-docs/src/sidebars.ts index 73c2a6b2a5..81018e50eb 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars.ts @@ -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 { - type: 'doc', - id: item, - }; + 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; }, diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index fe56ac2714..4929797c59 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -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 { diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 6d76298b0f..e0f17c2be2 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -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", diff --git a/packages/docusaurus/src/index.ts b/packages/docusaurus/src/index.ts index 543b405a3b..4f79a8a343 100644 --- a/packages/docusaurus/src/index.ts +++ b/packages/docusaurus/src/index.ts @@ -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'; diff --git a/tsconfig.json b/tsconfig.json index c2f71bbdfb..bf338b3df6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "es2017", "module": "commonjs", - "lib": ["es2017"], + "lib": ["es2017","es2019.array"], "declaration": true, "declarationMap": true, diff --git a/website/docs/sidebar.md b/website/docs/sidebar.md index 1978b05e2c..67b432425a 100644 --- a/website/docs/sidebar.md +++ b/website/docs/sidebar.md @@ -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'], }, ], }, diff --git a/website/sidebars.js b/website/sidebars.js index d7ed65a2e9..d24d7328a9 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -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', diff --git a/yarn.lock b/yarn.lock index aa1d91972f..047cbedd32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"