/
githubmirror
/
docusaurus
Обзор
Документация
Войти
/
githubmirror
/
docusaurus
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/docusaurus-plugin-content-docs/src/sidebars/validation.ts
189 строк
6 KB
Sébastien Lorber
feat(docs): sidebar item `key` attribute - fix docs translations key conflicts (#11228)
03 июл 2025, 14:40
Не верифицирован
03 июл 2025, 14:40
da08536
Код
Авторство
О чём код?
/** * 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 {Joi, URISchema} from '@docusaurus/utils-validation'; import type { SidebarItemConfig, SidebarItemBase, SidebarItemAutogenerated, SidebarItemDoc, SidebarItemHtml, SidebarItemLink, SidebarItemCategoryConfig, SidebarItemCategoryLink, SidebarItemCategoryLinkDoc, SidebarItemCategoryLinkGeneratedIndex, NormalizedSidebars, NormalizedSidebarItem, NormalizedSidebarItemCategory, CategoryMetadataFile, } from './types'; // NOTE: we don't add any default values during validation on purpose! // Config types are exposed to users for typechecking and we use the same type // in normalization const sidebarItemBaseSchema = Joi.object<SidebarItemBase>({ key: Joi.string(), className: Joi.string(), customProps: Joi.object().unknown(), }); const sidebarItemAutogeneratedSchema = sidebarItemBaseSchema.append<SidebarItemAutogenerated>({ type: 'autogenerated', dirName: Joi.string() .required() .pattern(/^[^/](?:.*[^/])?$/) .message( '"dirName" must be a dir path relative to the docs folder root, and should not start or end with slash', ), }); const sidebarItemDocSchema = sidebarItemBaseSchema.append<SidebarItemDoc>({ type: Joi.string().valid('doc', 'ref').required(), id: Joi.string().required(), label: Joi.string(), translatable: Joi.boolean(), }); const sidebarItemHtmlSchema = sidebarItemBaseSchema.append<SidebarItemHtml>({ type: 'html', value: Joi.string().required(), defaultStyle: Joi.boolean(), }); const sidebarItemLinkSchema = sidebarItemBaseSchema.append<SidebarItemLink>({ type: 'link', href: URISchema.required(), autoAddBaseUrl: Joi.boolean(), label: Joi.string() .required() .messages({'any.unknown': '"label" must be a string'}), description: Joi.string().optional().messages({ 'any.unknown': '"description" must be a string', }), }); const sidebarItemCategoryLinkSchema = Joi.object<SidebarItemCategoryLink>() .allow(null) .when('.type', { switch: [ { is: 'doc', then: Joi.object<SidebarItemCategoryLinkDoc>({ type: 'doc', id: Joi.string().required(), }), }, { is: 'generated-index', then: Joi.object<SidebarItemCategoryLinkGeneratedIndex>({ type: 'generated-index', slug: Joi.string().optional(), // This one is not in the user config, only in the normalized version // permalink: Joi.string().optional(), title: Joi.string().optional(), description: Joi.string().optional(), image: Joi.string().optional(), keywords: [Joi.string(), Joi.array().items(Joi.string())], }), }, { is: Joi.required(), then: Joi.forbidden().messages({ 'any.unknown': 'Unknown sidebar category link type "{.type}".', }), }, ], }); const sidebarItemCategorySchema = sidebarItemBaseSchema.append<SidebarItemCategoryConfig>({ type: 'category', label: Joi.string() .required() .messages({'any.unknown': '"label" must be a string'}), items: Joi.array() .required() .messages({'any.unknown': '"items" must be an array'}), // TODO: Joi doesn't allow mutual recursion. See https://github.com/sideway/joi/issues/2611 // .items(Joi.link('#sidebarItemSchema')), link: sidebarItemCategoryLinkSchema, collapsed: Joi.boolean().messages({ 'any.unknown': '"collapsed" must be a boolean', }), collapsible: Joi.boolean().messages({ 'any.unknown': '"collapsible" must be a boolean', }), description: Joi.string().optional().messages({ 'any.unknown': '"description" must be a string', }), }); const sidebarItemSchema = Joi.object<SidebarItemConfig>().when('.type', { switch: [ {is: 'link', then: sidebarItemLinkSchema}, { is: Joi.string().valid('doc', 'ref').required(), then: sidebarItemDocSchema, }, {is: 'html', then: sidebarItemHtmlSchema}, {is: 'autogenerated', then: sidebarItemAutogeneratedSchema}, {is: 'category', then: sidebarItemCategorySchema}, { is: Joi.any().required(), then: Joi.forbidden().messages({ 'any.unknown': 'Unknown sidebar item type "{.type}".', }), }, ], }); // .id('sidebarItemSchema'); function validateSidebarItem( item: unknown, ): asserts item is NormalizedSidebarItem { // TODO: remove once with proper Joi support // Because we can't use Joi to validate nested items (see above), we do it // manually Joi.assert(item, sidebarItemSchema); if ((item as NormalizedSidebarItem).type === 'category') { (item as NormalizedSidebarItemCategory).items.forEach(validateSidebarItem); } } export function validateSidebars(sidebars: { [sidebarId: string]: unknown; }): asserts sidebars is NormalizedSidebars { Object.values(sidebars as NormalizedSidebars).forEach((sidebar) => { sidebar.forEach(validateSidebarItem); }); } const categoryMetadataFileSchema = Joi.object<CategoryMetadataFile>({ key: Joi.string(), label: Joi.string(), description: Joi.string(), position: Joi.number(), collapsed: Joi.boolean(), collapsible: Joi.boolean(), className: Joi.string(), link: sidebarItemCategoryLinkSchema, customProps: Joi.object().unknown(), }); export function validateCategoryMetadataFile( unsafeContent: unknown, ): CategoryMetadataFile { const {error, value} = categoryMetadataFileSchema.validate(unsafeContent); if (error) { throw error; } return value; }