/** * Copyright (c) 2017-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ const fs = require('fs'); const importFresh = require('import-fresh'); /** * Check that item contains only allowed keys * * @param {Object} item * @param {Array} keys */ function assertItem(item, keys) { const unknownKeys = Object.keys(item).filter( key => !keys.includes(key) && key !== 'type', ); if (unknownKeys.length) { throw new Error( `Unknown sidebar item keys: ${unknownKeys}. Item: ${JSON.stringify( item, )}`, ); } } /** * Normalizes recursively category and all its children. Ensures, that at the end * each item will be an object with the corresponding type * * @param {Array} category * @param {number} [level=0] * * @return {Array} */ function normalizeCategory(category, level = 0) { if (level === 2) { throw new Error( `Can not process ${ category.label } category. Categories can be nested only one level deep.`, ); } assertItem(category, ['items', 'label']); if (!Array.isArray(category.items)) { throw new Error( `Error loading ${category.label} category. Category items must be array.`, ); } const items = category.items.map(item => { switch (item.type) { case 'category': return normalizeCategory(item, level + 1); case 'link': assertItem(item, ['href', 'label']); break; case 'ref': assertItem(item, ['id', 'label']); break; default: if (typeof item === 'string') { return { type: 'doc', id: item, }; } if (item.type !== 'doc') { throw new Error(`Unknown sidebar item type: ${item.type}`); } assertItem(item, ['id', 'label']); break; } return item; }); return {...category, items}; } /** * Converts sidebars object to mapping to arrays of sidebar item objects * * @param {{[key: string]: Object}} sidebars * * @return {{[key: string]: Array}} */ function normalizeSidebar(sidebars) { return Object.entries(sidebars).reduce((acc, [sidebarId, sidebar]) => { let normalizedSidebar = sidebar; if (!Array.isArray(sidebar)) { // convert sidebar to a more generic structure normalizedSidebar = Object.entries(sidebar).map(([label, items]) => ({ type: 'category', label, items, })); } acc[sidebarId] = normalizedSidebar.map(item => normalizeCategory(item)); return acc; }, {}); } module.exports = function loadSidebars(sidebarPath) { // We don't want sidebars to be cached because of hotreloading. let allSidebars = {}; if (sidebarPath && fs.existsSync(sidebarPath)) { allSidebars = importFresh(sidebarPath); } return normalizeSidebar(allSidebars); };