/** * 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 path from 'path'; import { Joi, RemarkPluginsSchema, RehypePluginsSchema, RecmaPluginsSchema, AdmonitionsSchema, RouteBasePathSchema, URISchema, } from '@docusaurus/utils-validation'; import {GlobExcludeDefault} from '@docusaurus/utils'; import type { PluginOptions, Options, FeedType, FeedXSLTOptions, } from '@docusaurus/plugin-content-blog'; import type {OptionValidationContext} from '@docusaurus/types'; export const DEFAULT_OPTIONS: PluginOptions = { feedOptions: { type: ['rss', 'atom'], copyright: '', limit: 20, xslt: { rss: null, atom: null, }, }, beforeDefaultRehypePlugins: [], beforeDefaultRemarkPlugins: [], admonitions: true, truncateMarker: /|\{\/\*\s*truncate\s*\*\/\}/, rehypePlugins: [], remarkPlugins: [], recmaPlugins: [], showReadingTime: true, blogTagsPostsComponent: '@theme/BlogTagsPostsPage', blogTagsListComponent: '@theme/BlogTagsListPage', blogAuthorsPostsComponent: '@theme/Blog/Pages/BlogAuthorsPostsPage', blogAuthorsListComponent: '@theme/Blog/Pages/BlogAuthorsListPage', blogPostComponent: '@theme/BlogPostPage', blogListComponent: '@theme/BlogListPage', blogArchiveComponent: '@theme/BlogArchivePage', blogDescription: 'Blog', blogTitle: 'Blog', blogSidebarCount: 5, blogSidebarTitle: 'Recent posts', postsPerPage: 10, include: ['**/*.{md,mdx}'], exclude: GlobExcludeDefault, routeBasePath: 'blog', tagsBasePath: 'tags', archiveBasePath: 'archive', pageBasePath: 'page', path: 'blog', editLocalizedFiles: false, authorsMapPath: 'authors.yml', readingTime: ({content, defaultReadingTime}) => defaultReadingTime({content}), sortPosts: 'descending', showLastUpdateTime: false, showLastUpdateAuthor: false, processBlogPosts: async () => undefined, onInlineTags: 'warn', tags: undefined, authorsBasePath: 'authors', onInlineAuthors: 'warn', onUntruncatedBlogPosts: 'warn', }; export const XSLTBuiltInPaths = { rss: path.resolve(__dirname, '..', 'assets', 'rss.xsl'), atom: path.resolve(__dirname, '..', 'assets', 'atom.xsl'), }; function normalizeXsltOption( option: string | null | boolean, type: 'rss' | 'atom', ): string | null { if (typeof option === 'string') { return option; } if (option === true) { return XSLTBuiltInPaths[type]; } return null; } function createXSLTFilePathSchema(type: 'atom' | 'rss') { return Joi.alternatives() .try( Joi.string().required(), Joi.boolean() .allow(null, () => undefined) .custom((val) => normalizeXsltOption(val, type)), ) .optional() .default(null); } const FeedXSLTOptionsSchema = Joi.alternatives() .try( Joi.object({ rss: createXSLTFilePathSchema('rss'), atom: createXSLTFilePathSchema('atom'), }).required(), Joi.boolean() .allow(null, () => undefined) .custom((val) => ({ rss: normalizeXsltOption(val, 'rss'), atom: normalizeXsltOption(val, 'atom'), })), ) .optional() .custom((val) => { if (val === null) { return { rss: null, atom: null, }; } return val; }) .default(DEFAULT_OPTIONS.feedOptions.xslt); const FeedOptionsSchema = Joi.object({ type: Joi.alternatives() .try( Joi.array().items(Joi.string().equal('rss', 'atom', 'json')), Joi.alternatives().conditional( Joi.string().equal('all', 'rss', 'atom', 'json'), { then: Joi.custom((val: FeedType | 'all') => val === 'all' ? ['rss', 'atom', 'json'] : [val], ), }, ), ) .allow(null) .default(DEFAULT_OPTIONS.feedOptions.type), xslt: FeedXSLTOptionsSchema, title: Joi.string().allow(''), description: Joi.string().allow(''), // Only add default value when user actually wants a feed (type is not null) copyright: Joi.when('type', { is: Joi.any().valid(null), then: Joi.string().optional(), otherwise: Joi.string() .allow('') .default(DEFAULT_OPTIONS.feedOptions.copyright), }), language: Joi.string(), createFeedItems: Joi.function(), limit: Joi.alternatives() .try(Joi.number(), Joi.valid(null), Joi.valid(false)) .default(DEFAULT_OPTIONS.feedOptions.limit), }).default(DEFAULT_OPTIONS.feedOptions); const PluginOptionSchema = Joi.object({ path: Joi.string().default(DEFAULT_OPTIONS.path), archiveBasePath: Joi.string() .default(DEFAULT_OPTIONS.archiveBasePath) .allow(null), routeBasePath: RouteBasePathSchema.default(DEFAULT_OPTIONS.routeBasePath), tagsBasePath: Joi.string().default(DEFAULT_OPTIONS.tagsBasePath), pageBasePath: Joi.string().default(DEFAULT_OPTIONS.pageBasePath), include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include), exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude), postsPerPage: Joi.alternatives() .try(Joi.equal('ALL').required(), Joi.number().integer().min(1).required()) .default(DEFAULT_OPTIONS.postsPerPage), blogListComponent: Joi.string().default(DEFAULT_OPTIONS.blogListComponent), blogPostComponent: Joi.string().default(DEFAULT_OPTIONS.blogPostComponent), blogTagsListComponent: Joi.string().default( DEFAULT_OPTIONS.blogTagsListComponent, ), blogTagsPostsComponent: Joi.string().default( DEFAULT_OPTIONS.blogTagsPostsComponent, ), blogAuthorsPostsComponent: Joi.string().default( DEFAULT_OPTIONS.blogAuthorsPostsComponent, ), blogAuthorsListComponent: Joi.string().default( DEFAULT_OPTIONS.blogAuthorsListComponent, ), blogArchiveComponent: Joi.string().default( DEFAULT_OPTIONS.blogArchiveComponent, ), blogTitle: Joi.string().allow('').default(DEFAULT_OPTIONS.blogTitle), blogDescription: Joi.string() .allow('') .default(DEFAULT_OPTIONS.blogDescription), blogSidebarCount: Joi.alternatives() .try(Joi.equal('ALL').required(), Joi.number().integer().min(0).required()) .default(DEFAULT_OPTIONS.blogSidebarCount), blogSidebarTitle: Joi.string().default(DEFAULT_OPTIONS.blogSidebarTitle), showReadingTime: Joi.bool().default(DEFAULT_OPTIONS.showReadingTime), remarkPlugins: RemarkPluginsSchema.default(DEFAULT_OPTIONS.remarkPlugins), rehypePlugins: RehypePluginsSchema.default(DEFAULT_OPTIONS.rehypePlugins), recmaPlugins: RecmaPluginsSchema.default(DEFAULT_OPTIONS.recmaPlugins), admonitions: AdmonitionsSchema.default(DEFAULT_OPTIONS.admonitions), editUrl: Joi.alternatives().try(URISchema, Joi.function()), editLocalizedFiles: Joi.boolean().default(DEFAULT_OPTIONS.editLocalizedFiles), truncateMarker: Joi.object().default(DEFAULT_OPTIONS.truncateMarker), beforeDefaultRemarkPlugins: RemarkPluginsSchema.default( DEFAULT_OPTIONS.beforeDefaultRemarkPlugins, ), beforeDefaultRehypePlugins: RehypePluginsSchema.default( DEFAULT_OPTIONS.beforeDefaultRehypePlugins, ), feedOptions: FeedOptionsSchema, authorsMapPath: Joi.string().default(DEFAULT_OPTIONS.authorsMapPath), readingTime: Joi.function().default(() => DEFAULT_OPTIONS.readingTime), sortPosts: Joi.string() .valid('descending', 'ascending') .default(DEFAULT_OPTIONS.sortPosts), showLastUpdateTime: Joi.bool().default(DEFAULT_OPTIONS.showLastUpdateTime), showLastUpdateAuthor: Joi.bool().default( DEFAULT_OPTIONS.showLastUpdateAuthor, ), processBlogPosts: Joi.function() .optional() .default(() => DEFAULT_OPTIONS.processBlogPosts), onInlineTags: Joi.string() .equal('ignore', 'log', 'warn', 'throw') .default(DEFAULT_OPTIONS.onInlineTags), tags: Joi.string() .disallow('') .allow(null, false) .default(() => DEFAULT_OPTIONS.tags), authorsBasePath: Joi.string() .default(DEFAULT_OPTIONS.authorsBasePath) .disallow(''), onInlineAuthors: Joi.string() .equal('ignore', 'log', 'warn', 'throw') .default(DEFAULT_OPTIONS.onInlineAuthors), onUntruncatedBlogPosts: Joi.string() .equal('ignore', 'log', 'warn', 'throw') .default(DEFAULT_OPTIONS.onUntruncatedBlogPosts), }).default(DEFAULT_OPTIONS); export function validateOptions({ validate, options, }: OptionValidationContext): PluginOptions { const validatedOptions = validate(PluginOptionSchema, options); return validatedOptions; }