From 269eaaa4013a08c73da7813be44d91d0a683be56 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:33:47 +0200 Subject: [PATCH 001/115] feat(blog): authors page --- .../src/authors.ts | 39 ++- .../src/blogUtils.ts | 70 +++++- .../src/feed.ts | 3 +- .../src/index.ts | 20 ++ .../src/options.ts | 16 ++ .../src/plugin-content-blog.d.ts | 96 +++++--- .../src/props.ts | 50 +++- .../src/routes.ts | 61 ++++- .../src/theme-classic.d.ts | 33 ++- .../src/theme/BlogAuthorPage/Author/index.tsx | 27 +++ .../BlogAuthorPage/Author/styles.module.css | 70 ++++++ .../AuthorsListByLetter/index.tsx | 52 ++++ .../AuthorsListByLetter/styles.module.css | 11 + .../BlogAuthorsListPage/index.tsx | 41 ++++ .../BlogAuthorsPostsPage/index.tsx | 115 +++++++++ .../BlogPostItem/Header/Author/index.tsx | 81 ++++++- packages/docusaurus-theme-common/src/index.ts | 6 + .../src/utils/ThemeClassNames.ts | 2 + .../src/utils/authorsUtils.ts | 55 +++++ packages/docusaurus-utils/src/authors.ts | 227 ++++++++++++++++++ packages/docusaurus-utils/src/index.ts | 10 + website/docusaurus.config.ts | 1 + 22 files changed, 1040 insertions(+), 46 deletions(-) create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/styles.module.css create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsListPage/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx create mode 100644 packages/docusaurus-theme-common/src/utils/authorsUtils.ts create mode 100644 packages/docusaurus-utils/src/authors.ts diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index c5fdad61ddfb..b0861603dec2 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -5,11 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import {getDataFileData, normalizeUrl} from '@docusaurus/utils'; +import {getDataFileData, normalizeUrl, type Author} from '@docusaurus/utils'; import {Joi, URISchema} from '@docusaurus/utils-validation'; +import logger from '@docusaurus/logger'; import type {BlogContentPaths} from './types'; import type { - Author, BlogPostFrontMatter, BlogPostFrontMatterAuthor, BlogPostFrontMatterAuthors, @@ -123,6 +123,11 @@ function normalizeFrontMatterAuthors( // we only support keys, otherwise, a typo in a key would fallback to // becoming a name and may end up unnoticed return {key: authorInput}; + } else if (typeof authorInput === 'object' && !('key' in authorInput)) { + return { + ...authorInput, + inline: true, + }; } return authorInput; } @@ -171,7 +176,7 @@ ${Object.keys(authorsMap) function fixAuthorImageBaseURL( authors: Author[], {baseUrl}: {baseUrl: string}, -) { +): Author[] { return authors.map((author) => ({ ...author, imageURL: normalizeImageUrl({imageURL: author.imageURL, baseUrl}), @@ -196,5 +201,33 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t return [authorLegacy]; } + const inlineAuthors = updatedAuthors.filter((author) => author.inline); + + const duplicateList = updatedAuthors.filter( + (author, index, self) => + index !== self.findIndex((t) => t.name === author.name), + ); + + // TODO need title check otherwise reports weird cases + if (inlineAuthors.length > 0 && params.frontMatter.title) { + logger.warn( + `Inline authors found in blog [${ + params.frontMatter.title + }] ${inlineAuthors.map((author) => author.name).join(', ')}`, + ); + } + + // TODO need title check otherwise reports weird cases + if (duplicateList.length > 0 && params.frontMatter.title) { + console.log('duplicateList', duplicateList); + logger.error( + `Duplicate authors found in blog post ${params.frontMatter.title} [${ + params.frontMatter.slug + }] front matter: ${duplicateList + .map((author) => author.name) + .join(', ')}`, + ); + } + return updatedAuthors; } diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index d7a777ff77b6..89d167781572 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -26,6 +26,9 @@ import { isDraft, readLastUpdateData, normalizeTags, + getAuthorVisibility, + groupAuthoredItems, + normalizeFrontMatterPageAuthors, } from '@docusaurus/utils'; import {getTagsFile} from '@docusaurus/utils-validation'; import {validateBlogPostFrontMatter} from './frontMatter'; @@ -38,6 +41,7 @@ import type { BlogPost, BlogTags, BlogPaginated, + BlogPageAuthors, } from '@docusaurus/plugin-content-blog'; import type {BlogContentPaths} from './types'; @@ -134,6 +138,59 @@ export function getBlogTags({ }); } +export function getBlogPageAuthors({ + blogPosts, + ...params +}: { + blogPosts: BlogPost[]; + blogTitle: string; + blogDescription: string; + postsPerPageOption: number | 'ALL'; + pageBasePath: string; +}): BlogPageAuthors { + const getPostPageAuthors = (blogPost: BlogPost) => + blogPost.metadata.pageAuthors; + + const groups = groupAuthoredItems(blogPosts, getPostPageAuthors); + + return _.mapValues(groups, ({author, items: authorBlogPosts}) => { + const authorVisibility = getAuthorVisibility({ + items: authorBlogPosts, + isUnlisted: (item: BlogPost) => item.metadata.unlisted, + }); + return { + name: author.name, + url: author.url, + title: author.title, + email: author.email, + items: authorVisibility.listedItems.map((item: BlogPost) => item.id), + permalink: author.permalink, + pages: author.permalink + ? paginateBlogPosts({ + blogPosts: authorVisibility.listedItems, + basePageUrl: author.permalink, + ...params, + }) + : [], + unlisted: authorVisibility.unlisted, + }; + }); +} + +// // ? is it useful ? +// function filterPageAuthors(authors: Author[]): PageAuthor[] { +// return authors +// .filter((author) => author.name !== undefined && author.name.length > 0) +// .map((author) => ({ +// name: author.name!, +// permalink: _.kebabCase(author.key as string), +// url: author.url, +// title: author.title, +// email: author.email, +// })) +// .filter((pageAuthor) => pageAuthor.permalink.length > 0); +// } + const DATE_FILENAME_REGEX = /^(?.*)(?\d{4}[-/]\d{1,2}[-/]\d{1,2})[-/]?(?.*?)(?:\/index)?.mdx?$/; @@ -317,7 +374,6 @@ async function processBlogSourceFile( routeBasePath, tagsRouteBasePath, ]); - const authors = getBlogPostAuthors({authorsMap, frontMatter, baseUrl}); const tags = normalizeTags({ options, @@ -327,6 +383,17 @@ async function processBlogSourceFile( tagsFile, }); + const authors = getBlogPostAuthors({authorsMap, frontMatter, baseUrl}); + const authorsBaseRoutePath = normalizeUrl([ + baseUrl, + routeBasePath, + options.authorsPageBasePath, + ]); + + const pageAuthors = options.generateAuthorsPage + ? normalizeFrontMatterPageAuthors(authorsBaseRoutePath, authors) + : []; + return { id: slug, metadata: { @@ -350,6 +417,7 @@ async function processBlogSourceFile( unlisted, lastUpdatedAt: lastUpdate.lastUpdatedAt, lastUpdatedBy: lastUpdate.lastUpdatedBy, + pageAuthors, }, content, }; diff --git a/packages/docusaurus-plugin-content-blog/src/feed.ts b/packages/docusaurus-plugin-content-blog/src/feed.ts index b8bc8c1481c9..f7954000b7fd 100644 --- a/packages/docusaurus-plugin-content-blog/src/feed.ts +++ b/packages/docusaurus-plugin-content-blog/src/feed.ts @@ -10,7 +10,7 @@ import fs from 'fs-extra'; import logger from '@docusaurus/logger'; import {Feed, type Author as FeedAuthor} from 'feed'; import * as srcset from 'srcset'; -import {normalizeUrl, readOutputHTMLFile} from '@docusaurus/utils'; +import {normalizeUrl, readOutputHTMLFile, type Author} from '@docusaurus/utils'; import { blogPostContainerID, applyTrailingSlash, @@ -20,7 +20,6 @@ import type {DocusaurusConfig, HtmlTags, LoadContext} from '@docusaurus/types'; import type { FeedType, PluginOptions, - Author, BlogPost, BlogFeedItem, } from '@docusaurus/plugin-content-blog'; diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 6d40bea301df..b760aa37489e 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -28,6 +28,7 @@ import { shouldBeListed, applyProcessBlogPosts, generateBlogPosts, + getBlogPageAuthors, } from './blogUtils'; import footnoteIDFixer from './remark/footnoteIDFixer'; import {translateContent, getTranslationFiles} from './translations'; @@ -44,6 +45,7 @@ import type { BlogTags, BlogContent, BlogPaginated, + BlogPageAuthors, } from '@docusaurus/plugin-content-blog'; import type {Options as MDXLoaderOptions} from '@docusaurus/mdx-loader/lib/loader'; import type {RuleSetUseItem} from 'webpack'; @@ -156,6 +158,7 @@ export default async function pluginContentBlog( postsPerPage: postsPerPageOption, routeBasePath, tagsBasePath, + authorsPageBasePath, blogDescription, blogTitle, blogSidebarTitle, @@ -164,6 +167,10 @@ export default async function pluginContentBlog( const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]); const blogTagsListPath = normalizeUrl([baseBlogUrl, tagsBasePath]); + const blogAuthorsListPath = normalizeUrl([ + baseBlogUrl, + authorsPageBasePath, + ]); let blogPosts = await generateBlogPosts(contentPaths, context, options); blogPosts = await applyProcessBlogPosts({ blogPosts, @@ -178,6 +185,8 @@ export default async function pluginContentBlog( blogListPaginated: [], blogTags: {}, blogTagsListPath, + blogPageAuthors: {}, + blogAuthorsListPath, }; } @@ -220,12 +229,23 @@ export default async function pluginContentBlog( pageBasePath, }); + const blogPageAuthors: BlogPageAuthors = getBlogPageAuthors({ + // TODO shouldn't we use listedBlogPosts here? (same for tags) + blogPosts, + postsPerPageOption, + blogDescription, + blogTitle, + pageBasePath, + }); + return { blogSidebarTitle, blogPosts, blogListPaginated, blogTags, blogTagsListPath, + blogPageAuthors, + blogAuthorsListPath, }; }, diff --git a/packages/docusaurus-plugin-content-blog/src/options.ts b/packages/docusaurus-plugin-content-blog/src/options.ts index 5835c32fc18f..b5ca98832747 100644 --- a/packages/docusaurus-plugin-content-blog/src/options.ts +++ b/packages/docusaurus-plugin-content-blog/src/options.ts @@ -32,6 +32,8 @@ export const DEFAULT_OPTIONS: PluginOptions = { showReadingTime: true, blogTagsPostsComponent: '@theme/BlogTagsPostsPage', blogTagsListComponent: '@theme/BlogTagsListPage', + blogAuthorsPostsComponent: '@theme/BlogAuthorPage/BlogAuthorsPostsPage', + blogAuthorsListComponent: '@theme/BlogAuthorPage/BlogAuthorsListPage', blogPostComponent: '@theme/BlogPostPage', blogListComponent: '@theme/BlogListPage', blogArchiveComponent: '@theme/BlogArchivePage', @@ -56,6 +58,8 @@ export const DEFAULT_OPTIONS: PluginOptions = { processBlogPosts: async () => undefined, onInlineTags: 'warn', tags: undefined, + generateAuthorsPage: true, + authorsPageBasePath: 'authors', }; const PluginOptionSchema = Joi.object({ @@ -79,6 +83,12 @@ const PluginOptionSchema = Joi.object({ 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, ), @@ -153,6 +163,12 @@ const PluginOptionSchema = Joi.object({ .disallow('') .allow(null, false) .default(() => DEFAULT_OPTIONS.tags), + generateAuthorsPage: Joi.boolean().default( + DEFAULT_OPTIONS.generateAuthorsPage, + ), + authorsPageBasePath: Joi.string() + .default(DEFAULT_OPTIONS.authorsPageBasePath) + .disallow(''), }).default(DEFAULT_OPTIONS); export function validateOptions({ diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index f4d4f135c061..8d307c4328ba 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -15,6 +15,7 @@ declare module '@docusaurus/plugin-content-blog' { LastUpdateData, FrontMatterLastUpdate, TagsPluginOptions, + PageAuthor, } from '@docusaurus/utils'; import type {DocusaurusConfig, Plugin, LoadContext} from '@docusaurus/types'; import type {Item as FeedItem} from 'feed'; @@ -43,37 +44,6 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the authorsImageUrls: (string | undefined)[]; }; - export type Author = { - /** - * If `name` doesn't exist, an `imageURL` is expected. - */ - name?: string; - /** - * The image path could be collocated, in which case - * `metadata.assets.authorsImageUrls` should be used instead. If `imageURL` - * doesn't exist, a `name` is expected. - */ - imageURL?: string; - /** - * Used to generate the author's link. - */ - url?: string; - /** - * Used as a subtitle for the author, e.g. "maintainer of Docusaurus" - */ - title?: string; - /** - * Mainly used for RSS feeds; if `url` doesn't exist, `email` can be used - * to generate a fallback `mailto:` URL. - */ - email?: string; - /** - * Unknown keys are allowed, so that we can pass custom fields to authors, - * e.g., `twitter`. - */ - [key: string]: unknown; - }; - /** * Everything is partial/unnormalized, because front matter is always * preserved as-is. Default values will be applied when generating metadata @@ -237,6 +207,10 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the readonly frontMatter: BlogPostFrontMatter & {[key: string]: unknown}; /** Tags, normalized. */ readonly tags: TagMetadata[]; + /** + * Page authors, for use in the Authors grouping page. + */ + readonly pageAuthors: PageAuthor[]; /** * Marks the post as unlisted and visibly hides it unless directly accessed. */ @@ -398,6 +372,10 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the blogTagsListComponent: string; /** Root component of the "posts containing tag" page. */ blogTagsPostsComponent: string; + /** Root component of the authors list page. */ + blogAuthorsListComponent: string; + /** Root component of the "posts containing author" page. */ + blogAuthorsPostsComponent: string; /** Root component of the blog archive page. */ blogArchiveComponent: string; /** Blog page title for better SEO. */ @@ -442,6 +420,10 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the * (filter, modify, delete, etc...). */ processBlogPosts: ProcessBlogPostsFn; + /* Whether to show the authors page */ + generateAuthorsPage: boolean; + /* Base path for the authors page */ + authorsPageBasePath: string; }; /** @@ -482,6 +464,8 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the blogListPaginated: BlogPaginated[]; blogTags: BlogTags; blogTagsListPath: string; + blogPageAuthors: BlogPageAuthors; + blogAuthorsListPath: string; }; export type BlogMetadata = { @@ -502,6 +486,17 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the unlisted: boolean; }; + export type BlogPageAuthors = { + [permalink: string]: BlogPageAuthor; + }; + + export type BlogPageAuthor = PageAuthor & { + /** Blog post permalinks. */ + items: string[]; + pages: BlogPaginated[]; + unlisted: boolean; + }; + export type BlogPost = { id: string; metadata: BlogPostMetadata; @@ -647,6 +642,45 @@ declare module '@theme/BlogTagsListPage' { export default function BlogTagsListPage(props: Props): JSX.Element; } +declare module '@theme/BlogAuthorsListPage' { + import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; + import type {PageAuthorsListItem} from '@docusaurus/utils'; + + export interface Props { + /** Blog sidebar. */ + readonly sidebar: BlogSidebar; + /** All authors declared in this blog. */ + readonly authors: PageAuthorsListItem[]; + } + + export default function BlogAuthorsListPage(props: Props): JSX.Element; +} + +declare module '@theme/BlogAuthorsPostsPage' { + import type {Content} from '@theme/BlogPostPage'; + import type { + BlogSidebar, + BlogPaginatedMetadata, + } from '@docusaurus/plugin-content-blog'; + import type {AuthorModule} from '@docusaurus/utils'; + + export interface Props { + /** Blog sidebar. */ + readonly sidebar: BlogSidebar; + /** Metadata of this author. */ + readonly author: AuthorModule; + /** Looks exactly the same as the posts list page */ + readonly listMetadata: BlogPaginatedMetadata; + /** + * Array of blog posts included on this page. Every post's metadata is also + * available. + */ + readonly items: readonly {readonly content: Content}[]; + } + + export default function BlogAuthorsPostsPage(props: Props): JSX.Element; +} + declare module '@theme/BlogTagsPostsPage' { import type {Content} from '@theme/BlogPostPage'; import type { diff --git a/packages/docusaurus-plugin-content-blog/src/props.ts b/packages/docusaurus-plugin-content-blog/src/props.ts index 517cd4a5f820..3895055d7599 100644 --- a/packages/docusaurus-plugin-content-blog/src/props.ts +++ b/packages/docusaurus-plugin-content-blog/src/props.ts @@ -4,8 +4,18 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import type {TagsListItem, TagModule} from '@docusaurus/utils'; -import type {BlogTag, BlogTags} from '@docusaurus/plugin-content-blog'; +import type { + TagsListItem, + TagModule, + AuthorsListItem, + AuthorModule, +} from '@docusaurus/utils'; +import type { + BlogPageAuthor, + BlogPageAuthors, + BlogTag, + BlogTags, +} from '@docusaurus/plugin-content-blog'; export function toTagsProp({blogTags}: {blogTags: BlogTags}): TagsListItem[] { return Object.values(blogTags) @@ -34,3 +44,39 @@ export function toTagProp({ unlisted: tag.unlisted, }; } + +export function toPageAuthorsProp({ + blogPageAuthors, +}: { + blogPageAuthors: BlogPageAuthors; +}): AuthorsListItem[] { + return Object.values(blogPageAuthors) + .filter((author) => !author.unlisted) + .map((author) => ({ + name: author.name, + permalink: author.permalink, + count: author.items.length, + url: author.url, + email: author.email, + title: author.title, + })); +} + +export function toPageAuthorProp({ + blogAuthorsListPath, + author, +}: { + blogAuthorsListPath: string; + author: BlogPageAuthor; +}): AuthorModule { + return { + name: author.name, + permalink: author.permalink, + allAuthorsPath: blogAuthorsListPath, + count: author.items.length, + unlisted: author.unlisted, + url: author.url, + email: author.email, + title: author.title, + }; +} diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index a810ce13dabc..a6a607ec9d67 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -13,7 +13,12 @@ import { } from '@docusaurus/utils'; import {shouldBeListed} from './blogUtils'; -import {toTagProp, toTagsProp} from './props'; +import { + toPageAuthorProp, + toPageAuthorsProp, + toTagProp, + toTagsProp, +} from './props'; import type { PluginContentLoadedActions, RouteConfig, @@ -27,6 +32,7 @@ import type { PluginOptions, BlogPost, BlogSidebar, + BlogPageAuthor, } from '@docusaurus/plugin-content-blog'; type CreateAllRoutesParam = { @@ -55,6 +61,8 @@ export async function buildAllRoutes({ blogListComponent, blogPostComponent, blogTagsListComponent, + blogAuthorsListComponent, + blogAuthorsPostsComponent, blogTagsPostsComponent, blogArchiveComponent, routeBasePath, @@ -68,7 +76,9 @@ export async function buildAllRoutes({ blogPosts, blogListPaginated, blogTags, + blogPageAuthors, blogTagsListPath, + blogAuthorsListPath, } = content; const listedBlogPosts = blogPosts.filter(shouldBeListed); @@ -254,10 +264,59 @@ export async function buildAllRoutes({ return [tagsListRoute, ...tagsPaginatedRoutes]; } + function createAuthorsRoutes(): RouteConfig[] { + // Check if we should generate the authors page and if there are authors. + if ( + !options.generateAuthorsPage || + Object.keys(blogPageAuthors).length === 0 + ) { + return []; + } + + const authorsListRoute: RouteConfig = { + path: blogAuthorsListPath, + component: blogAuthorsListComponent, + exact: true, + modules: { + sidebar: sidebarModulePath, + }, + props: { + authors: toPageAuthorsProp({blogPageAuthors}), + }, + }; + + function createAuthorPaginatedRoutes( + author: BlogPageAuthor, + ): RouteConfig[] { + return author.pages.map(({metadata, items}) => { + return { + path: metadata.permalink, + component: blogAuthorsPostsComponent, + exact: true, + modules: { + items: blogPostItemsModule(items), + sidebar: sidebarModulePath, + }, + props: { + author: toPageAuthorProp({author, blogAuthorsListPath}), + listMetadata: metadata, + }, + }; + }); + } + + const authorsPaginatedRoutes: RouteConfig[] = Object.values( + blogPageAuthors, + ).flatMap(createAuthorPaginatedRoutes); + + return [authorsListRoute, ...authorsPaginatedRoutes]; + } + return [ ...createBlogPostRoutes(), ...createBlogPostsPaginatedRoutes(), ...createTagsRoutes(), ...createArchiveRoute(), + ...createAuthorsRoutes(), ]; } diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 39e7195893b4..026904dd9cd6 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -281,8 +281,12 @@ declare module '@theme/BlogPostItem/Header/Info' { declare module '@theme/BlogPostItem/Header/Author' { import type {PropBlogPostContent} from '@docusaurus/plugin-content-blog'; + type AuthorProps = PropBlogPostContent['metadata']['authors'][number] & { + twitter?: string; + github?: string; + }; export interface Props { - readonly author: PropBlogPostContent['metadata']['authors'][number]; + readonly author: AuthorProps; readonly className?: string; } @@ -1528,6 +1532,33 @@ declare module '@theme/Tag' { export default function Tag(props: Props): JSX.Element; } +declare module '@theme/BlogAuthorPage/AuthorsListByLetter' { + import type {PageAuthorsListItem} from '@docusaurus/utils'; + + export interface Props { + readonly authors: readonly PageAuthorsListItem[]; + } + export default function AuthorsListByLetter(props: Props): JSX.Element; +} + +declare module '@theme/BlogAuthorPage/AuthorsListInline' { + import type {PageAuthor} from '@docusaurus/utils'; + + export interface Props { + readonly authors: readonly PageAuthor[]; + } + export default function AuthorsListInline(props: Props): JSX.Element; +} + +declare module '@theme/BlogAuthorPage/Author' { + import type {PageAuthorsListItem} from '@docusaurus/utils'; + import type {Optional} from 'utility-types'; + + export interface Props extends Optional {} + + export default function Author(props: Props): JSX.Element; +} + declare module '@theme/Unlisted' { export interface Props { className?: string; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx new file mode 100644 index 000000000000..e4fb90852b3d --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx @@ -0,0 +1,27 @@ +/** + * 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 React from 'react'; +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; +import type {Props} from '@theme/BlogAuthorPage/Author'; + +import styles from './styles.module.css'; + +export default function Author({permalink, name, count}: Props): JSX.Element { + return ( + + {name} + {count && {count}} + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css new file mode 100644 index 000000000000..3f8f446471d9 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css @@ -0,0 +1,70 @@ +/** + * 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. + */ + +:root { + --docusaurus-tag-list-border: var(--ifm-color-emphasis-300); +} + +.author { + border: 1px solid var(--docusaurus-tag-list-border); + transition: border var(--ifm-transition-fast); +} + +.author:hover { + --docusaurus-tag-list-border: var(--ifm-link-color); + text-decoration: none; +} + +.authorRegular { + border-radius: var(--ifm-global-radius); + padding: 0.2rem 0.5rem 0.3rem; + font-size: 90%; +} + +.authorWithCount { + display: flex; + align-items: center; + position: relative; + padding: 0 0.5rem 0 1rem; + border-left: 0; +} + +.authorWithCount::before, +.authorWithCount::after { + content: ''; + position: absolute; + top: 50%; + border: 1px solid var(--docusaurus-tag-list-border); + transition: inherit; +} + +.authorWithCount::before { + right: 100%; + transform: translate(50%, -50%) rotate(-45deg); + width: 1.18rem; + height: 1.18rem; + border-right: 0; + border-bottom: 0; +} + +.authorWithCount::after { + left: 0; + transform: translateY(-50%); + width: 0.5rem; + height: 0.5rem; + border-radius: 50%; +} + +.authorWithCount span { + background: var(--ifm-color-secondary); + color: var(--ifm-color-black); + font-size: 0.7rem; + line-height: 1.2; + border-radius: var(--ifm-global-radius); + padding: 0.1rem 0.4rem; + margin-left: 0.3rem; +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx new file mode 100644 index 000000000000..d44dd7bf49fb --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx @@ -0,0 +1,52 @@ +/** + * 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 React from 'react'; +import { + listAuthorsByLetters, + type AuthorLetterEntry, +} from '@docusaurus/theme-common'; +import Author from '@theme/BlogAuthorPage/Author'; +import type {Props} from '@theme/BlogAuthorPage/AuthorsListByLetter'; +import Heading from '@theme/Heading'; +import styles from './styles.module.css'; + +function AuthorLetterEntryItem({ + letterEntry, +}: { + letterEntry: AuthorLetterEntry; +}) { + return ( +
+ + {letterEntry.letter} + +
    + {letterEntry.authors.map((author) => ( +
  • + +
  • + ))} +
+
+
+ ); +} + +export default function AuthorsListByLetter({authors}: Props): JSX.Element { + const letterList = listAuthorsByLetters(authors); + return ( +
+ {letterList.map((letterEntry) => ( + + ))} +
+ ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/styles.module.css b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/styles.module.css new file mode 100644 index 000000000000..b06b913b6c0d --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/styles.module.css @@ -0,0 +1,11 @@ +/** + * 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. + */ + +.author { + display: inline-block; + margin: 0.5rem 0.5rem 0 1rem; +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsListPage/index.tsx new file mode 100644 index 000000000000..3f67a584eb8d --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsListPage/index.tsx @@ -0,0 +1,41 @@ +/** + * 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 React from 'react'; +import clsx from 'clsx'; +import { + PageMetadata, + HtmlClassNameProvider, + translateAuthorsPageTitle, + ThemeClassNames, +} from '@docusaurus/theme-common'; +import BlogLayout from '@theme/BlogLayout'; +import AuthorsListByLetter from '@theme/BlogAuthorPage/AuthorsListByLetter'; +import type {Props} from '@theme/BlogAuthorsListPage'; +import SearchMetadata from '@theme/SearchMetadata'; +import Heading from '@theme/Heading'; + +export default function BlogAuthorsListPage({ + authors, + sidebar, +}: Props): JSX.Element { + const title: string = translateAuthorsPageTitle(); + return ( + + + + + + {title} + + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx new file mode 100644 index 000000000000..21ea37522fc6 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx @@ -0,0 +1,115 @@ +/** + * 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 React from 'react'; +import clsx from 'clsx'; +import Translate, {translate} from '@docusaurus/Translate'; +import { + PageMetadata, + HtmlClassNameProvider, + ThemeClassNames, + usePluralForm, +} from '@docusaurus/theme-common'; +import Link from '@docusaurus/Link'; +import BlogLayout from '@theme/BlogLayout'; +import BlogListPaginator from '@theme/BlogListPaginator'; +import SearchMetadata from '@theme/SearchMetadata'; +import type {Props} from '@theme/BlogAuthorsPostsPage'; +import BlogPostItems from '@theme/BlogPostItems'; +import Unlisted from '@theme/Unlisted'; +import Heading from '@theme/Heading'; + +// Very simple pluralization: probably good enough for now +function useBlogPostsPlural() { + const {selectMessage} = usePluralForm(); + return (count: number) => + selectMessage( + count, + translate( + { + id: 'theme.blog.post.plurals', + description: + 'Pluralized label for "{count} posts". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)', + message: 'One post|{count} posts', + }, + {count}, + ), + ); +} + +function useBlogAuthorsPostsPageTitle(author: Props['author']): string { + const blogPostsPlural = useBlogPostsPlural(); + return translate( + { + id: 'theme.blog.authorTitle', + description: 'The title of the page for a blog author', + message: '{nPosts} contributed by "{authorName}"', + }, + {nPosts: blogPostsPlural(author.count), authorName: author.name}, + ); +} + +function BlogAuthorsPostsPageMetadata({author}: Props): JSX.Element { + const title = useBlogAuthorsPostsPageTitle(author); + return ( + <> + + + + ); +} + +function BlogAuthorsPostsPageContent({ + author, + items, + sidebar, + listMetadata, +}: Props): JSX.Element { + const title = useBlogAuthorsPostsPageTitle(author); + return ( + + {author.unlisted && } +
+ {title} +
    + {author.title &&
  • {author.title}
  • } + {author.email && ( +
  • + {author.email} +
  • + )} + {author.url && ( +
  • + Personnal website +
  • + )} +
+ + + View All Authors + + +
+ + +
+ ); +} +export default function BlogAuthorsPostsPage(props: Props): JSX.Element { + return ( + + + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx index 5d9935febb04..0bb46004f5a1 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx @@ -6,11 +6,60 @@ */ import React from 'react'; +import type {SVGProps} from 'react'; import clsx from 'clsx'; import Link, {type Props as LinkProps} from '@docusaurus/Link'; - +import {useBlogPost} from '@docusaurus/theme-common/internal'; import type {Props} from '@theme/BlogPostItem/Header/Author'; +type SocialProps = SVGProps & { + user: string; +}; + +function getUrl(user: string, baseUrl: string) { + return user.startsWith('https') ? user : `${baseUrl}${user}`; +} + +function Twitter(props: SocialProps) { + const twitterUrl = getUrl(props.user, 'https://x.com/'); + return ( + // eslint-disable-next-line @docusaurus/no-html-links + + + + + + ); +} + +function Github(props: SocialProps) { + const githubUrl = getUrl(props.user, 'https://github.com/'); + return ( + // eslint-disable-next-line @docusaurus/no-html-links + + + + + + ); +} + function MaybeLink(props: LinkProps): JSX.Element { if (props.href) { return ; @@ -22,8 +71,27 @@ export default function BlogPostItemHeaderAuthor({ author, className, }: Props): JSX.Element { - const {name, title, url, imageURL, email} = author; - const link = url || (email && `mailto:${email}`) || undefined; + const {name, title, twitter, github, url, imageURL, email} = author; + const { + metadata: {pageAuthors}, + } = useBlogPost(); + + const authorPermalink = pageAuthors.find( + (pageAuthor) => pageAuthor.name === name, + )?.permalink; + + const link = + authorPermalink || url || (email && `mailto:${email}`) || undefined; + + const renderSocialMedia = () => ( +
+ {twitter && } + {github && } +
+ ); + + const hasSocialMedia = twitter || github; + return (
{imageURL && ( @@ -31,7 +99,6 @@ export default function BlogPostItemHeaderAuthor({ {name} )} - {name && (
@@ -39,7 +106,11 @@ export default function BlogPostItemHeaderAuthor({ {name}
- {title && {title}} + {hasSocialMedia ? ( + renderSocialMedia() + ) : ( + {title} + )}
)}
diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 6859089b5a38..85e47c9c1530 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -81,6 +81,12 @@ export { type TagLetterEntry, } from './utils/tagsUtils'; +export { + type AuthorLetterEntry, + listAuthorsByLetters, + translateAuthorsPageTitle, +} from './utils/authorsUtils'; + export { useSearchQueryString, useSearchLinkCreator, diff --git a/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts b/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts index 1cdcda3ce50a..9dba038c5a5c 100644 --- a/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts +++ b/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts @@ -18,6 +18,8 @@ export const ThemeClassNames = { blogPostPage: 'blog-post-page', blogTagsListPage: 'blog-tags-list-page', blogTagPostListPage: 'blog-tags-post-list-page', + blogAuthorsListPage: 'blog-authors-list-page', + blogAuthorPostListPage: 'blog-authors-post-list-page', docsDocPage: 'docs-doc-page', docsTagsListPage: 'docs-tags-list-page', diff --git a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts new file mode 100644 index 000000000000..87251e25fb5f --- /dev/null +++ b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts @@ -0,0 +1,55 @@ +/** + * 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 {translate} from '@docusaurus/Translate'; +import type {AuthorsListItem} from '@docusaurus/utils'; + +export const translateAuthorsPageTitle = (): string => + translate({ + id: 'theme.tags.tagsPageTitle', + message: 'Tags', + description: 'The title of the tag list page', + }); + +export type AuthorLetterEntry = { + letter: string; + authors: AuthorsListItem[]; +}; + +function getAuthorLetter(author: string): string { + return author[0]!.toUpperCase(); +} + +/** + * Takes a list of auuthors (as provided by the content plugins), and groups + * them by their initials. + */ +export function listAuthorsByLetters( + authors: readonly AuthorsListItem[], +): AuthorLetterEntry[] { + const groups: {[initial: string]: AuthorsListItem[]} = {}; + Object.values(authors).forEach((author) => { + if (author.name) { + const initial = getAuthorLetter(author.name); + groups[initial] ??= []; + groups[initial]!.push(author); + } + }); + + return ( + Object.entries(groups) + // Sort letters + .sort(([letter1], [letter2]) => letter1.localeCompare(letter2)) + .map(([letter, letterAuthors]) => { + // Sort authors inside a letter + const sortedAuthors = letterAuthors.sort((author1, author2) => + (author1.name as string).localeCompare(author2.name as string), + ); + return {letter, authors: sortedAuthors}; + }) + ); +} diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts new file mode 100644 index 000000000000..2ed67132eee6 --- /dev/null +++ b/packages/docusaurus-utils/src/authors.ts @@ -0,0 +1,227 @@ +/** + * 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 _ from 'lodash'; +import {normalizeUrl} from './urlUtils'; + +export type Author = { + /** + * If `name` doesn't exist, an `imageURL` is expected. + */ + name?: string; + /** + * The image path could be collocated, in which case + * `metadata.assets.authorsImageUrls` should be used instead. If `imageURL` + * doesn't exist, a `name` is expected. + */ + imageURL?: string; + /** + * Used to generate the author's link. + */ + url?: string; + /** + * Used as a subtitle for the author, e.g. "maintainer of Docusaurus" + */ + title?: string; + /** + * Mainly used for RSS feeds; if `url` doesn't exist, `email` can be used + * to generate a fallback `mailto:` URL. + */ + email?: string; + /** + * Unknown keys are allowed, so that we can pass custom fields to authors, + * e.g., `twitter`. + */ + [key: string]: unknown; +}; + +export type PageAuthor = { + /** User name */ + name: string; + /** Permalink to this author's page, without the `/authors/` base path. */ + permalink: string; + + title: string | undefined; + email: string | undefined; + url: string | undefined; +}; + +/** What the authors list page should know about each author. */ +export type AuthorsListItem = PageAuthor & { + /** Number of posts/docs with this author. */ + count: number; +}; + +/** What the author's own page should know about the author. */ +export type AuthorModule = AuthorsListItem & { + /** The authors list page's permalink. */ + allAuthorsPath: string; + /** Is this author unlisted? (when it only contains unlisted items) */ + unlisted: boolean; + + title: string | undefined; + email: string | undefined; + url: string | undefined; +}; + +// We always apply tagsBaseRoutePath on purpose. For versioned docs, v1/doc.md +// and v2/doc.md tags with custom permalinks don't lead to the same created +// page. tagsBaseRoutePath is different for each doc version +function normalizeAuthorPermalink({ + authorsBaseRoutePath, + permalink, +}: { + authorsBaseRoutePath: string; + permalink: string; +}): string { + return normalizeUrl([authorsBaseRoutePath, permalink]); +} + +function normalizeFrontMatterAuthor( + authorsPath: string, + frontMatterPageAuthor: PageAuthor, +): PageAuthor { + function toPageAuthorObject(authorString: string): PageAuthor { + return { + name: authorString, + permalink: _.kebabCase(authorString), + url: undefined, + title: undefined, + email: undefined, + }; + } + + const author: PageAuthor = + typeof frontMatterPageAuthor === 'string' + ? toPageAuthorObject(frontMatterPageAuthor) + : frontMatterPageAuthor; + + return { + name: author.name, + permalink: normalizeAuthorPermalink({ + permalink: author.permalink, + authorsBaseRoutePath: authorsPath, + }), + url: author.url, + title: author.title, + email: author.email, + }; +} + +/** + * Takes author objects as they are defined in front matter, and normalizes each + * into a standard author object. The permalink is created by appending the + * sluggified label to `authorsPath`. Front matter authors already containing + * permalinks would still have `authorsPath` prepended. + * + * The result will always be unique by permalinks. The behavior with colliding + * permalinks is undetermined. + */ +export function normalizeFrontMatterPageAuthors( + /** Base path to append the author permalinks to. */ + authorsBaseRoutePath: string, + /** Can be `undefined`, so that we can directly pipe in + * `frontMatter.authors`. */ + frontMatterPageAuthors: Author[] | undefined = [], +): PageAuthor[] { + const pageAuthors = frontMatterPageAuthors + .filter((author) => author.name !== undefined && author.name.length > 0) + .map((author) => ({ + name: author.name!, + url: author.url, + title: author.title, + email: author.email, + permalink: _.kebabCase(author.key as string), + })) + .filter((pageAuthor) => pageAuthor.permalink.length > 0); + + const authors = pageAuthors.map((author) => + normalizeFrontMatterAuthor(authorsBaseRoutePath, author), + ); + + return _.uniqBy(authors, (author) => author.permalink); +} + +type AuthoredItemGroup = { + author: PageAuthor; + items: Item[]; +}; + +/** + * Permits to group docs/blog posts by author (provided by front matter). + * + * @returns a map from author permalink to the items and other relevant author + * data. + * The record is indexed by permalink, because routes must be unique in the end. + * Labels may vary on 2 MD files but they are normalized. Docs with + * label='some label' and label='some-label' should end up in the same page. + */ +export function groupAuthoredItems( + items: readonly Item[], + /** + * A callback telling me how to get the authors list of the current item. + * Usually simply getting it from some metadata of the current item. + */ + getItemPageAuthors: (item: Item) => readonly PageAuthor[], +): {[permalink: string]: AuthoredItemGroup} { + const result: {[permalink: string]: AuthoredItemGroup} = {}; + + items.forEach((item) => { + getItemPageAuthors(item).forEach((author) => { + // Init missing author groups + // TODO: it's not really clear what should be the behavior if 2 + // authors have the same permalink but the label is different for each + // For now, the first author found wins + result[author.permalink] ??= { + author, + items: [], + }; + + // Add item to group + result[author.permalink]!.items.push(item); + }); + }); + + // If user add twice the same author to a md doc (weird but possible), + // we don't want the item to appear twice in the list... + // TODO remove this and warn the user if we detect duplicates? + Object.values(result).forEach((group) => { + group.items = _.uniq(group.items); + }); + + return result; +} + +// TODO is it useful? +/** + * Permits to get the "author visibility" (hard to find a better name) + * IE, is this author listed or unlisted + * And which items should be listed when this author is browsed + */ +export function getAuthorVisibility({ + items, + isUnlisted, +}: { + items: Item[]; + isUnlisted: (item: Item) => boolean; +}): { + unlisted: boolean; + listedItems: Item[]; +} { + const allItemsUnlisted = items.every(isUnlisted); + // When a author is full of unlisted items, we display all the items + // when author is browsed, but we mark the author as unlisted + if (allItemsUnlisted) { + return {unlisted: true, listedItems: items}; + } + // When a author has some listed items, the author remains listed + // but we filter its unlisted items + return { + unlisted: false, + listedItems: items.filter((item) => !isUnlisted(item)), + }; +} diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 7201f8eec1a8..5a80dca938d2 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -129,3 +129,13 @@ export { } from './lastUpdateUtils'; export {normalizeTags, reportInlineTags} from './tags'; + +export { + type PageAuthor, + type AuthorsListItem, + type AuthorModule, + type Author, + getAuthorVisibility, + groupAuthoredItems, + normalizeFrontMatterPageAuthors, +} from './authors'; diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 093f70ec5a73..1fef4d8c563f 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -291,6 +291,7 @@ export default async function createConfigAsync() { copyright: `Copyright © ${new Date().getFullYear()} Facebook, Inc.`, language: defaultLocale, }, + generateAuthorsPage: false, }, ], [ From f734a9055749cd865a9ea8a0c3dd362610bf9968 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:54:01 +0200 Subject: [PATCH 002/115] fix build --- .../src/utils/structuredDataUtils.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts index 3a8a985e5cd8..f564b021c781 100644 --- a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts @@ -17,11 +17,9 @@ import type { Person, ImageObject, } from 'schema-dts'; -import type { - Author, - PropBlogPostContent, -} from '@docusaurus/plugin-content-blog'; +import type {PropBlogPostContent} from '@docusaurus/plugin-content-blog'; import type {DocusaurusConfig} from '@docusaurus/types'; +import type {Author} from '@docusaurus/utils'; const convertDate = (dateMs: number) => new Date(dateMs).toISOString(); From 29b3a64560b3c78dd23eb8286094704701d59922 Mon Sep 17 00:00:00 2001 From: OzakIOne Date: Thu, 13 Jun 2024 13:59:06 +0000 Subject: [PATCH 003/115] refactor: apply lint autofix --- project-words.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/project-words.txt b/project-words.txt index 0125953ec975..26ecb18f7fc4 100644 --- a/project-words.txt +++ b/project-words.txt @@ -22,6 +22,7 @@ Autogen autogenerating autohide Autolinks +auuthors Bartosz beforeinstallprompt Bhatt @@ -244,6 +245,7 @@ paraiso Paraiso pathinfo paularmstrong +Personnal philpl photoshop Photoshop @@ -321,6 +323,7 @@ showinfo Sida Simen slorber +sluggified sluggifies sluggify solana From f1a44278796f808f14dada49b1a2a21e39ed6f5f Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:51:21 +0200 Subject: [PATCH 004/115] fix typo --- package.json | 3 ++- .../src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx | 2 +- packages/docusaurus-theme-common/src/utils/authorsUtils.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c9abfeb1997c..26ec1372eb8f 100644 --- a/package.json +++ b/package.json @@ -116,5 +116,6 @@ "stylelint-config-prettier": "^9.0.5", "stylelint-config-standard": "^29.0.0", "typescript": "~5.4.5" - } + }, + "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72" } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx index 21ea37522fc6..eadc0597ac64 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx @@ -84,7 +84,7 @@ function BlogAuthorsPostsPageContent({ )} {author.url && (
  • - Personnal website + Personal website
  • )} diff --git a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts index 87251e25fb5f..14f8b89db2fd 100644 --- a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts @@ -25,7 +25,7 @@ function getAuthorLetter(author: string): string { } /** - * Takes a list of auuthors (as provided by the content plugins), and groups + * Takes a list of authors (as provided by the content plugins), and groups * them by their initials. */ export function listAuthorsByLetters( From 1792c78348507762c6713aaa3c854c601067ee43 Mon Sep 17 00:00:00 2001 From: OzakIOne Date: Fri, 14 Jun 2024 10:55:55 +0000 Subject: [PATCH 005/115] refactor: apply lint autofix --- project-words.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/project-words.txt b/project-words.txt index 26ecb18f7fc4..c624afd5f1f9 100644 --- a/project-words.txt +++ b/project-words.txt @@ -22,7 +22,6 @@ Autogen autogenerating autohide Autolinks -auuthors Bartosz beforeinstallprompt Bhatt @@ -245,7 +244,6 @@ paraiso Paraiso pathinfo paularmstrong -Personnal philpl photoshop Photoshop From e1c0e5e6baa57c14e4bf68168581a57b2bc1d555 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Fri, 14 Jun 2024 15:43:31 +0200 Subject: [PATCH 006/115] fix tests --- .../__snapshots__/index.test.ts.snap | 3 +++ .../src/__tests__/authors.test.ts | 21 +++++++++++++++---- .../src/__tests__/index.test.ts | 17 +++++++++++++++ .../src/blogUtils.ts | 8 +++---- .../locales/base/theme-common.json | 2 ++ packages/docusaurus-utils/src/authors.ts | 4 ---- 6 files changed, 42 insertions(+), 13 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap index a9ff614dbab4..7c4102bef728 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap @@ -147,6 +147,7 @@ exports[`blog plugin process blog posts load content 2`] = ` "permalink": "/blog/another/tags", "title": "Another With Tag", }, + "pageAuthors": [], "permalink": "/blog/simple/slug/another", "readingTime": 0.015, "source": "@site/blog/another-simple-slug-with-tags.md", @@ -186,6 +187,7 @@ exports[`blog plugin process blog posts load content 2`] = ` "permalink": "/blog/another/tags2", "title": "Another With Tag", }, + "pageAuthors": [], "permalink": "/blog/another/tags", "prevItem": { "permalink": "/blog/simple/slug/another", @@ -231,6 +233,7 @@ exports[`blog plugin process blog posts load content 2`] = ` "hasTruncateMarker": false, "lastUpdatedAt": undefined, "lastUpdatedBy": undefined, + "pageAuthors": [], "permalink": "/blog/another/tags2", "prevItem": { "permalink": "/blog/another/tags", diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index 052437c3dec6..aa44eb0e5120 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -202,7 +202,14 @@ describe('getBlogPostAuthors', () => { authorsMap: undefined, baseUrl: '/', }), - ).toEqual([{name: 'Sébastien Lorber', title: 'maintainer'}]); + ).toEqual([ + { + name: 'Sébastien Lorber', + title: 'maintainer', + inline: true, + imageURL: undefined, + }, + ]); }); it('can read authors Author[]', () => { @@ -218,8 +225,13 @@ describe('getBlogPostAuthors', () => { baseUrl: '/', }), ).toEqual([ - {name: 'Sébastien Lorber', title: 'maintainer'}, - {name: 'Yangshun Tay'}, + { + name: 'Sébastien Lorber', + title: 'maintainer', + inline: true, + imageURL: undefined, + }, + {name: 'Yangshun Tay', inline: true, imageURL: undefined}, ]); }); @@ -250,8 +262,9 @@ describe('getBlogPostAuthors', () => { name: 'Yangshun Tay', title: 'Yangshun title local override', extra: 42, + imageURL: undefined, }, - {name: 'Alexey'}, + {name: 'Alexey', inline: true, imageURL: undefined}, ]); }); diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts index 5280a72c18c8..37630fcfd369 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts @@ -196,6 +196,7 @@ describe('blog plugin', () => { permalink: '/blog/tags/date', }, ], + pageAuthors: [], nextItem: { permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash', title: 'Happy 1st Birthday Slash! (translated)', @@ -220,12 +221,15 @@ describe('blog plugin', () => { authors: [ { name: 'Yangshun Tay (translated)', + inline: true, + imageURL: undefined, }, { email: 'lorber.sebastien@gmail.com', key: 'slorber', name: 'Sébastien Lorber (translated)', title: 'Docusaurus maintainer (translated)', + imageURL: undefined, }, ], date: new Date('2018-12-14'), @@ -253,6 +257,15 @@ describe('blog plugin', () => { permalink: '/blog/tags/global-tag-permalink (en)', }, ], + pageAuthors: [ + { + email: 'lorber.sebastien@gmail.com', + name: 'Sébastien Lorber (translated)', + permalink: '/blog/authors/slorber', + title: 'Docusaurus maintainer (translated)', + url: undefined, + }, + ], prevItem: { permalink: '/blog/date-matter', title: 'date-matter', @@ -299,6 +312,7 @@ describe('blog plugin', () => { permalink: '/blog/tags/complex', }, ], + pageAuthors: [], hasTruncateMarker: false, unlisted: false, }); @@ -336,6 +350,7 @@ describe('blog plugin', () => { title: 'Simple Slug', }, tags: [], + pageAuthors: [], hasTruncateMarker: false, unlisted: false, }); @@ -357,6 +372,7 @@ describe('blog plugin', () => { }, prevItem: undefined, tags: [], + pageAuthors: [], nextItem: { permalink: '/blog/date-matter', title: 'date-matter', @@ -481,6 +497,7 @@ describe('blog plugin', () => { date: noDateSourceTime, frontMatter: {}, tags: [], + pageAuthors: [], prevItem: undefined, nextItem: undefined, hasTruncateMarker: false, diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 89d167781572..4b6aafdfbaee 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -384,11 +384,9 @@ async function processBlogSourceFile( }); const authors = getBlogPostAuthors({authorsMap, frontMatter, baseUrl}); - const authorsBaseRoutePath = normalizeUrl([ - baseUrl, - routeBasePath, - options.authorsPageBasePath, - ]); + const authorsBaseRoutePath = options.generateAuthorsPage + ? normalizeUrl([baseUrl, routeBasePath, options.authorsPageBasePath]) + : ''; const pageAuthors = options.generateAuthorsPage ? normalizeFrontMatterPageAuthors(authorsBaseRoutePath, authors) diff --git a/packages/docusaurus-theme-translations/locales/base/theme-common.json b/packages/docusaurus-theme-translations/locales/base/theme-common.json index fcaeb33e9a98..c6ec1d98c84a 100644 --- a/packages/docusaurus-theme-translations/locales/base/theme-common.json +++ b/packages/docusaurus-theme-translations/locales/base/theme-common.json @@ -138,6 +138,8 @@ "theme.tags.tagsPageLink___DESCRIPTION": "The label of the link targeting the tag list page", "theme.tags.tagsPageTitle": "Tags", "theme.tags.tagsPageTitle___DESCRIPTION": "The title of the tag list page", + "theme.authors.authorsPageLink": "View All Authors", + "theme.blog.authorTitle": "{nPosts} contributed by \"{authorName}\"", "theme.unlistedContent.message": "This page is unlisted. Search engines will not index it, and only users having a direct link can access it.", "theme.unlistedContent.message___DESCRIPTION": "The unlisted content banner message", "theme.unlistedContent.title": "Unlisted page", diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts index 2ed67132eee6..025249d62f69 100644 --- a/packages/docusaurus-utils/src/authors.ts +++ b/packages/docusaurus-utils/src/authors.ts @@ -62,10 +62,6 @@ export type AuthorModule = AuthorsListItem & { allAuthorsPath: string; /** Is this author unlisted? (when it only contains unlisted items) */ unlisted: boolean; - - title: string | undefined; - email: string | undefined; - url: string | undefined; }; // We always apply tagsBaseRoutePath on purpose. For versioned docs, v1/doc.md From 20e5f2999b3d7f5d830f6d2ed77d0fe6451e6452 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:22:56 +0200 Subject: [PATCH 007/115] fix tests --- .../src/plugin-content-blog.d.ts | 4 ++-- packages/docusaurus-theme-classic/src/theme-classic.d.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 8d307c4328ba..e974647b8fe8 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -644,13 +644,13 @@ declare module '@theme/BlogTagsListPage' { declare module '@theme/BlogAuthorsListPage' { import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; - import type {PageAuthorsListItem} from '@docusaurus/utils'; + import type {AuthorsListItem} from '@docusaurus/utils'; export interface Props { /** Blog sidebar. */ readonly sidebar: BlogSidebar; /** All authors declared in this blog. */ - readonly authors: PageAuthorsListItem[]; + readonly authors: AuthorsListItem[]; } export default function BlogAuthorsListPage(props: Props): JSX.Element; diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 026904dd9cd6..c53999dde9cb 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -1533,10 +1533,10 @@ declare module '@theme/Tag' { } declare module '@theme/BlogAuthorPage/AuthorsListByLetter' { - import type {PageAuthorsListItem} from '@docusaurus/utils'; + import type {AuthorsListItem} from '@docusaurus/utils'; export interface Props { - readonly authors: readonly PageAuthorsListItem[]; + readonly authors: readonly AuthorsListItem[]; } export default function AuthorsListByLetter(props: Props): JSX.Element; } @@ -1551,10 +1551,10 @@ declare module '@theme/BlogAuthorPage/AuthorsListInline' { } declare module '@theme/BlogAuthorPage/Author' { - import type {PageAuthorsListItem} from '@docusaurus/utils'; + import type {AuthorsListItem} from '@docusaurus/utils'; import type {Optional} from 'utility-types'; - export interface Props extends Optional {} + export interface Props extends Optional {} export default function Author(props: Props): JSX.Element; } From d018eb61fc4a163138f885f5c8e8de07290d4d44 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:41:23 +0200 Subject: [PATCH 008/115] fix typecheck --- .../docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index e974647b8fe8..76f8136e1783 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -16,6 +16,7 @@ declare module '@docusaurus/plugin-content-blog' { FrontMatterLastUpdate, TagsPluginOptions, PageAuthor, + Author, } from '@docusaurus/utils'; import type {DocusaurusConfig, Plugin, LoadContext} from '@docusaurus/types'; import type {Item as FeedItem} from 'feed'; From f29b69a050dfdecbc054265d7953d12b507c53a1 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Fri, 14 Jun 2024 17:02:07 +0200 Subject: [PATCH 009/115] update styles --- .../BlogPostItem/Header/Author/index.tsx | 19 +++++++++++++++---- .../Header/Author/styles.module.css | 17 +++++++++++++++++ website/blog/authors.yml | 1 + 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx index 0bb46004f5a1..18e6fbcc33bf 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx @@ -12,6 +12,8 @@ import Link, {type Props as LinkProps} from '@docusaurus/Link'; import {useBlogPost} from '@docusaurus/theme-common/internal'; import type {Props} from '@theme/BlogPostItem/Header/Author'; +import styles from './styles.module.css'; + type SocialProps = SVGProps & { user: string; }; @@ -24,12 +26,17 @@ function Twitter(props: SocialProps) { const twitterUrl = getUrl(props.user, 'https://x.com/'); return ( // eslint-disable-next-line @docusaurus/no-html-links - + + @@ -84,7 +95,7 @@ export default function BlogPostItemHeaderAuthor({ authorPermalink || url || (email && `mailto:${email}`) || undefined; const renderSocialMedia = () => ( -
    +
    {twitter && } {github && }
    diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css new file mode 100644 index 000000000000..ec21f2c232aa --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css @@ -0,0 +1,17 @@ +/** + * 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. + */ + +.authorSocial { + display: flex; + align-items: center; +} + +.socialIcon { + width: 1.5em; + height: 1.5em; + margin-right: 0.5em; +} diff --git a/website/blog/authors.yml b/website/blog/authors.yml index 5b84346cda7c..073afa0f9e2a 100644 --- a/website/blog/authors.yml +++ b/website/blog/authors.yml @@ -20,6 +20,7 @@ slorber: url: https://thisweekinreact.com image_url: https://github.com/slorber.png twitter: sebastienlorber + github: slorber email: sebastien@thisweekinreact.com yangshun: From cb4cc7865c2d7f1e438dd715c2983bdd6113d747 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Sun, 16 Jun 2024 22:33:07 +0200 Subject: [PATCH 010/115] fix test? --- package.json | 3 +-- .../src/plugin-content-blog.d.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 26ec1372eb8f..c9abfeb1997c 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,5 @@ "stylelint-config-prettier": "^9.0.5", "stylelint-config-standard": "^29.0.0", "typescript": "~5.4.5" - }, - "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72" + } } diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 76f8136e1783..097b713b3641 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -643,7 +643,7 @@ declare module '@theme/BlogTagsListPage' { export default function BlogTagsListPage(props: Props): JSX.Element; } -declare module '@theme/BlogAuthorsListPage' { +declare module '@theme/BlogAuthorPage/BlogAuthorsListPage' { import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; import type {AuthorsListItem} from '@docusaurus/utils'; @@ -657,7 +657,7 @@ declare module '@theme/BlogAuthorsListPage' { export default function BlogAuthorsListPage(props: Props): JSX.Element; } -declare module '@theme/BlogAuthorsPostsPage' { +declare module '@theme/BlogAuthorPage/BlogAuthorsPostsPage' { import type {Content} from '@theme/BlogPostPage'; import type { BlogSidebar, From 6e66bc73242a6fbe4494618a2fe1e8ecbb8a7e79 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Sun, 16 Jun 2024 22:40:02 +0200 Subject: [PATCH 011/115] revert --- .../src/plugin-content-blog.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 097b713b3641..76f8136e1783 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -643,7 +643,7 @@ declare module '@theme/BlogTagsListPage' { export default function BlogTagsListPage(props: Props): JSX.Element; } -declare module '@theme/BlogAuthorPage/BlogAuthorsListPage' { +declare module '@theme/BlogAuthorsListPage' { import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; import type {AuthorsListItem} from '@docusaurus/utils'; @@ -657,7 +657,7 @@ declare module '@theme/BlogAuthorPage/BlogAuthorsListPage' { export default function BlogAuthorsListPage(props: Props): JSX.Element; } -declare module '@theme/BlogAuthorPage/BlogAuthorsPostsPage' { +declare module '@theme/BlogAuthorsPostsPage' { import type {Content} from '@theme/BlogPostPage'; import type { BlogSidebar, From fc7f7d07683696fea8dc2e980f7797f519ccc21c Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Mon, 17 Jun 2024 16:59:28 +0200 Subject: [PATCH 012/115] remove socials & support unnamed author --- .../src/blogUtils.ts | 15 +--- .../src/props.ts | 2 + .../src/theme-classic.d.ts | 6 +- .../BlogPostItem/Header/Author/index.tsx | 80 +------------------ .../Header/Author/styles.module.css | 17 ---- packages/docusaurus-utils/src/authors.ts | 7 +- 6 files changed, 13 insertions(+), 114 deletions(-) delete mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 4b6aafdfbaee..49155457f11a 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -163,6 +163,7 @@ export function getBlogPageAuthors({ url: author.url, title: author.title, email: author.email, + key: author.key, items: authorVisibility.listedItems.map((item: BlogPost) => item.id), permalink: author.permalink, pages: author.permalink @@ -177,20 +178,6 @@ export function getBlogPageAuthors({ }); } -// // ? is it useful ? -// function filterPageAuthors(authors: Author[]): PageAuthor[] { -// return authors -// .filter((author) => author.name !== undefined && author.name.length > 0) -// .map((author) => ({ -// name: author.name!, -// permalink: _.kebabCase(author.key as string), -// url: author.url, -// title: author.title, -// email: author.email, -// })) -// .filter((pageAuthor) => pageAuthor.permalink.length > 0); -// } - const DATE_FILENAME_REGEX = /^(?.*)(?\d{4}[-/]\d{1,2}[-/]\d{1,2})[-/]?(?.*?)(?:\/index)?.mdx?$/; diff --git a/packages/docusaurus-plugin-content-blog/src/props.ts b/packages/docusaurus-plugin-content-blog/src/props.ts index 3895055d7599..bbd471865b6b 100644 --- a/packages/docusaurus-plugin-content-blog/src/props.ts +++ b/packages/docusaurus-plugin-content-blog/src/props.ts @@ -59,6 +59,7 @@ export function toPageAuthorsProp({ url: author.url, email: author.email, title: author.title, + key: author.key, })); } @@ -78,5 +79,6 @@ export function toPageAuthorProp({ url: author.url, email: author.email, title: author.title, + key: author.key, }; } diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index c53999dde9cb..6c34e269dc09 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -281,12 +281,8 @@ declare module '@theme/BlogPostItem/Header/Info' { declare module '@theme/BlogPostItem/Header/Author' { import type {PropBlogPostContent} from '@docusaurus/plugin-content-blog'; - type AuthorProps = PropBlogPostContent['metadata']['authors'][number] & { - twitter?: string; - github?: string; - }; export interface Props { - readonly author: AuthorProps; + readonly author: PropBlogPostContent['metadata']['authors'][number]; readonly className?: string; } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx index 18e6fbcc33bf..9164c98ecb0c 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx @@ -6,71 +6,11 @@ */ import React from 'react'; -import type {SVGProps} from 'react'; import clsx from 'clsx'; import Link, {type Props as LinkProps} from '@docusaurus/Link'; import {useBlogPost} from '@docusaurus/theme-common/internal'; import type {Props} from '@theme/BlogPostItem/Header/Author'; -import styles from './styles.module.css'; - -type SocialProps = SVGProps & { - user: string; -}; - -function getUrl(user: string, baseUrl: string) { - return user.startsWith('https') ? user : `${baseUrl}${user}`; -} - -function Twitter(props: SocialProps) { - const twitterUrl = getUrl(props.user, 'https://x.com/'); - return ( - // eslint-disable-next-line @docusaurus/no-html-links -
    - - - - - ); -} - -function Github(props: SocialProps) { - const githubUrl = getUrl(props.user, 'https://github.com/'); - return ( - // eslint-disable-next-line @docusaurus/no-html-links - - - - - - ); -} - function MaybeLink(props: LinkProps): JSX.Element { if (props.href) { return ; @@ -82,27 +22,18 @@ export default function BlogPostItemHeaderAuthor({ author, className, }: Props): JSX.Element { - const {name, title, twitter, github, url, imageURL, email} = author; + const {name, key, title, url, imageURL, email} = author; const { metadata: {pageAuthors}, } = useBlogPost(); const authorPermalink = pageAuthors.find( - (pageAuthor) => pageAuthor.name === name, + (pageAuthor) => pageAuthor.name === name || pageAuthor.key === key, )?.permalink; const link = authorPermalink || url || (email && `mailto:${email}`) || undefined; - const renderSocialMedia = () => ( -
    - {twitter && } - {github && } -
    - ); - - const hasSocialMedia = twitter || github; - return (
    {imageURL && ( @@ -110,6 +41,7 @@ export default function BlogPostItemHeaderAuthor({ {name} )} + {name && (
    @@ -117,11 +49,7 @@ export default function BlogPostItemHeaderAuthor({ {name}
    - {hasSocialMedia ? ( - renderSocialMedia() - ) : ( - {title} - )} + {title}
    )}
    diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css deleted file mode 100644 index ec21f2c232aa..000000000000 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 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. - */ - -.authorSocial { - display: flex; - align-items: center; -} - -.socialIcon { - width: 1.5em; - height: 1.5em; - margin-right: 0.5em; -} diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts index 025249d62f69..ffb01c55bd0e 100644 --- a/packages/docusaurus-utils/src/authors.ts +++ b/packages/docusaurus-utils/src/authors.ts @@ -45,6 +45,7 @@ export type PageAuthor = { /** Permalink to this author's page, without the `/authors/` base path. */ permalink: string; + key: string | undefined; title: string | undefined; email: string | undefined; url: string | undefined; @@ -88,6 +89,7 @@ function normalizeFrontMatterAuthor( url: undefined, title: undefined, email: undefined, + key: authorString, }; } @@ -105,6 +107,7 @@ function normalizeFrontMatterAuthor( url: author.url, title: author.title, email: author.email, + key: author.key, }; } @@ -125,12 +128,12 @@ export function normalizeFrontMatterPageAuthors( frontMatterPageAuthors: Author[] | undefined = [], ): PageAuthor[] { const pageAuthors = frontMatterPageAuthors - .filter((author) => author.name !== undefined && author.name.length > 0) .map((author) => ({ - name: author.name!, + name: author.name || (author.key as string), url: author.url, title: author.title, email: author.email, + key: author.key as string, permalink: _.kebabCase(author.key as string), })) .filter((pageAuthor) => pageAuthor.permalink.length > 0); From cac2f998aebace00e98d32f0da751f2862885630 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:42:00 +0200 Subject: [PATCH 013/115] remove duplication and inline check --- .../src/__tests__/authors.test.ts | 6 ++-- .../src/__tests__/index.test.ts | 2 +- .../src/authors.ts | 34 ------------------- 3 files changed, 3 insertions(+), 39 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index aa44eb0e5120..d3e95ba2f1e6 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -206,7 +206,6 @@ describe('getBlogPostAuthors', () => { { name: 'Sébastien Lorber', title: 'maintainer', - inline: true, imageURL: undefined, }, ]); @@ -228,10 +227,9 @@ describe('getBlogPostAuthors', () => { { name: 'Sébastien Lorber', title: 'maintainer', - inline: true, imageURL: undefined, }, - {name: 'Yangshun Tay', inline: true, imageURL: undefined}, + {name: 'Yangshun Tay', imageURL: undefined}, ]); }); @@ -264,7 +262,7 @@ describe('getBlogPostAuthors', () => { extra: 42, imageURL: undefined, }, - {name: 'Alexey', inline: true, imageURL: undefined}, + {name: 'Alexey', imageURL: undefined}, ]); }); diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts index 37630fcfd369..acc577f922d1 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts @@ -221,7 +221,6 @@ describe('blog plugin', () => { authors: [ { name: 'Yangshun Tay (translated)', - inline: true, imageURL: undefined, }, { @@ -263,6 +262,7 @@ describe('blog plugin', () => { name: 'Sébastien Lorber (translated)', permalink: '/blog/authors/slorber', title: 'Docusaurus maintainer (translated)', + key: 'slorber', url: undefined, }, ], diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index b0861603dec2..ffed16965cbe 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -7,7 +7,6 @@ import {getDataFileData, normalizeUrl, type Author} from '@docusaurus/utils'; import {Joi, URISchema} from '@docusaurus/utils-validation'; -import logger from '@docusaurus/logger'; import type {BlogContentPaths} from './types'; import type { BlogPostFrontMatter, @@ -123,11 +122,6 @@ function normalizeFrontMatterAuthors( // we only support keys, otherwise, a typo in a key would fallback to // becoming a name and may end up unnoticed return {key: authorInput}; - } else if (typeof authorInput === 'object' && !('key' in authorInput)) { - return { - ...authorInput, - inline: true, - }; } return authorInput; } @@ -201,33 +195,5 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t return [authorLegacy]; } - const inlineAuthors = updatedAuthors.filter((author) => author.inline); - - const duplicateList = updatedAuthors.filter( - (author, index, self) => - index !== self.findIndex((t) => t.name === author.name), - ); - - // TODO need title check otherwise reports weird cases - if (inlineAuthors.length > 0 && params.frontMatter.title) { - logger.warn( - `Inline authors found in blog [${ - params.frontMatter.title - }] ${inlineAuthors.map((author) => author.name).join(', ')}`, - ); - } - - // TODO need title check otherwise reports weird cases - if (duplicateList.length > 0 && params.frontMatter.title) { - console.log('duplicateList', duplicateList); - logger.error( - `Duplicate authors found in blog post ${params.frontMatter.title} [${ - params.frontMatter.slug - }] front matter: ${duplicateList - .map((author) => author.name) - .join(', ')}`, - ); - } - return updatedAuthors; } From 65db409a1055706d406ba42cd592c2b0e3075937 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 18 Jun 2024 17:31:41 +0200 Subject: [PATCH 014/115] feat: permalink support --- .../src/authors.ts | 33 +++++++++++++++++++ .../src/blogUtils.ts | 28 ++++++++++++---- .../src/options.ts | 4 --- .../src/plugin-content-blog.d.ts | 2 -- .../src/routes.ts | 7 ++-- packages/docusaurus-utils/src/authors.ts | 12 +++++-- website/blog/authors.yml | 6 ++++ website/docusaurus.config.ts | 1 - 8 files changed, 71 insertions(+), 22 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index ffed16965cbe..ff8361e0f01e 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -25,6 +25,8 @@ const AuthorsMapSchema = Joi.object() imageURL: URISchema, title: Joi.string(), email: Joi.string(), + generateAuthorPage: Joi.bool(), + permalink: Joi.string(), }) .rename('image_url', 'imageURL') .or('name', 'imageURL') @@ -197,3 +199,34 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t return updatedAuthors; } + +export function checkPermalinkCollisions( + authorsMap: _.Dictionary, +): void { + const permalinkCounts: {[key: string]: string[]} = {}; + + for (const [key, author] of Object.entries(authorsMap)) { + if (author.permalink) { + if (!permalinkCounts[author.permalink]) { + permalinkCounts[author.permalink] = []; + } + permalinkCounts[author.permalink]?.push(author.name ?? key); + } + } + + const collisions = Object.entries(permalinkCounts).filter( + ([, authors]) => authors.length > 1, + ); + + if (collisions.length > 0) { + let errorMessage = 'The following permalinks are duplicated:\n'; + + collisions.forEach(([permalink, authors]) => { + errorMessage += `Permalink: ${permalink}\nAuthors: ${authors.join( + ', ', + )}\n\n`; + }); + + throw new Error(errorMessage.trim()); + } +} diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 49155457f11a..c1e96397d6fc 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -32,7 +32,12 @@ import { } from '@docusaurus/utils'; import {getTagsFile} from '@docusaurus/utils-validation'; import {validateBlogPostFrontMatter} from './frontMatter'; -import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors'; +import { + type AuthorsMap, + getAuthorsMap, + getBlogPostAuthors, + checkPermalinkCollisions, +} from './authors'; import type {TagsFile} from '@docusaurus/utils'; import type {LoadContext, ParseFrontMatter} from '@docusaurus/types'; import type { @@ -371,13 +376,16 @@ async function processBlogSourceFile( }); const authors = getBlogPostAuthors({authorsMap, frontMatter, baseUrl}); - const authorsBaseRoutePath = options.generateAuthorsPage - ? normalizeUrl([baseUrl, routeBasePath, options.authorsPageBasePath]) - : ''; + const authorsBaseRoutePath = normalizeUrl([ + baseUrl, + routeBasePath, + options.authorsPageBasePath, + ]); - const pageAuthors = options.generateAuthorsPage - ? normalizeFrontMatterPageAuthors(authorsBaseRoutePath, authors) - : []; + const pageAuthors = normalizeFrontMatterPageAuthors( + authorsBaseRoutePath, + authors, + ); return { id: slug, @@ -428,6 +436,12 @@ export async function generateBlogPosts( contentPaths, authorsMapPath: options.authorsMapPath, }); + const pageAuthorsMap = _.pickBy( + authorsMap, + (author) => author.generateAuthorPage === true, + ); + + checkPermalinkCollisions(pageAuthorsMap); const tagsFile = await getTagsFile({contentPaths, tags: options.tags}); diff --git a/packages/docusaurus-plugin-content-blog/src/options.ts b/packages/docusaurus-plugin-content-blog/src/options.ts index b5ca98832747..6f6eaa1f16b0 100644 --- a/packages/docusaurus-plugin-content-blog/src/options.ts +++ b/packages/docusaurus-plugin-content-blog/src/options.ts @@ -58,7 +58,6 @@ export const DEFAULT_OPTIONS: PluginOptions = { processBlogPosts: async () => undefined, onInlineTags: 'warn', tags: undefined, - generateAuthorsPage: true, authorsPageBasePath: 'authors', }; @@ -163,9 +162,6 @@ const PluginOptionSchema = Joi.object({ .disallow('') .allow(null, false) .default(() => DEFAULT_OPTIONS.tags), - generateAuthorsPage: Joi.boolean().default( - DEFAULT_OPTIONS.generateAuthorsPage, - ), authorsPageBasePath: Joi.string() .default(DEFAULT_OPTIONS.authorsPageBasePath) .disallow(''), diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 76f8136e1783..f4b788f3896c 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -421,8 +421,6 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the * (filter, modify, delete, etc...). */ processBlogPosts: ProcessBlogPostsFn; - /* Whether to show the authors page */ - generateAuthorsPage: boolean; /* Base path for the authors page */ authorsPageBasePath: string; }; diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index a6a607ec9d67..edb7bcb0dc12 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -265,11 +265,8 @@ export async function buildAllRoutes({ } function createAuthorsRoutes(): RouteConfig[] { - // Check if we should generate the authors page and if there are authors. - if ( - !options.generateAuthorsPage || - Object.keys(blogPageAuthors).length === 0 - ) { + // Authors. This is the last part so we early-return if there are no tags. + if (Object.keys(blogPageAuthors).length === 0) { return []; } diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts index ffb01c55bd0e..7c5d7d1a514c 100644 --- a/packages/docusaurus-utils/src/authors.ts +++ b/packages/docusaurus-utils/src/authors.ts @@ -32,6 +32,9 @@ export type Author = { * to generate a fallback `mailto:` URL. */ email?: string; + // TODO add description + generateAuthorPage?: boolean; + permalink?: string; /** * Unknown keys are allowed, so that we can pass custom fields to authors, * e.g., `twitter`. @@ -128,15 +131,18 @@ export function normalizeFrontMatterPageAuthors( frontMatterPageAuthors: Author[] | undefined = [], ): PageAuthor[] { const pageAuthors = frontMatterPageAuthors + .filter((author) => author.generateAuthorPage === true) .map((author) => ({ name: author.name || (author.key as string), url: author.url, title: author.title, email: author.email, + // TODO investigate if the user put a key in file what will happen key: author.key as string, - permalink: _.kebabCase(author.key as string), - })) - .filter((pageAuthor) => pageAuthor.permalink.length > 0); + permalink: author.permalink + ? author.permalink + : _.kebabCase(author.key as string), + })); const authors = pageAuthors.map((author) => normalizeFrontMatterAuthor(authorsBaseRoutePath, author), diff --git a/website/blog/authors.yml b/website/blog/authors.yml index 073afa0f9e2a..68c48a854051 100644 --- a/website/blog/authors.yml +++ b/website/blog/authors.yml @@ -5,6 +5,7 @@ JMarcey: image_url: https://github.com/JoelMarcey.png email: jimarcey@gmail.com twitter: JoelMarcey + generateAuthorPage: true zpao: name: Paul O’Shannessy @@ -13,6 +14,7 @@ zpao: image_url: https://github.com/zpao.png email: jimarcey@gmail.com twitter: zpao + generateAuthorPage: true slorber: name: Sébastien Lorber @@ -22,6 +24,7 @@ slorber: twitter: sebastienlorber github: slorber email: sebastien@thisweekinreact.com + generateAuthorPage: true yangshun: name: Yangshun Tay @@ -30,6 +33,7 @@ yangshun: image_url: https://github.com/yangshun.png twitter: yangshunz email: tay.yang.shun@gmail.com + generateAuthorPage: true lex111: name: Alexey Pyltsyn @@ -37,6 +41,7 @@ lex111: url: https://github.com/lex111 image_url: https://github.com/lex111.png email: lex@php.net + generateAuthorPage: true Josh-Cena: name: Joshua Chen @@ -44,3 +49,4 @@ Josh-Cena: url: https://joshcena.com/ image_url: https://github.com/josh-cena.png email: sidachen2003@gmail.com + generateAuthorPage: true diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 1fef4d8c563f..093f70ec5a73 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -291,7 +291,6 @@ export default async function createConfigAsync() { copyright: `Copyright © ${new Date().getFullYear()} Facebook, Inc.`, language: defaultLocale, }, - generateAuthorsPage: false, }, ], [ From a6dbf715029fa70a8249ca18c098fe075a55dce7 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 18 Jun 2024 19:25:10 +0200 Subject: [PATCH 015/115] feat: author description --- .../src/__tests__/index.test.ts | 11 +---------- .../docusaurus-plugin-content-blog/src/authors.ts | 1 + .../src/blogUtils.ts | 15 ++++----------- .../docusaurus-plugin-content-blog/src/props.ts | 15 ++------------- .../src/theme/BlogAuthorPage/Author/index.tsx | 14 +++++++++++++- .../theme/BlogAuthorPage/Author/styles.module.css | 5 +++++ .../BlogAuthorPage/BlogAuthorsPostsPage/index.tsx | 7 +------ .../src/utils/authorsUtils.ts | 6 +++--- .../locales/base/theme-common.json | 1 + packages/docusaurus-utils/src/authors.ts | 10 ++++++++++ website/blog/authors.yml | 3 +++ 11 files changed, 44 insertions(+), 44 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts index acc577f922d1..2e2ad99aa1d7 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts @@ -256,16 +256,7 @@ describe('blog plugin', () => { permalink: '/blog/tags/global-tag-permalink (en)', }, ], - pageAuthors: [ - { - email: 'lorber.sebastien@gmail.com', - name: 'Sébastien Lorber (translated)', - permalink: '/blog/authors/slorber', - title: 'Docusaurus maintainer (translated)', - key: 'slorber', - url: undefined, - }, - ], + pageAuthors: [], prevItem: { permalink: '/blog/date-matter', title: 'date-matter', diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index ff8361e0f01e..10f7e9ced78c 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -27,6 +27,7 @@ const AuthorsMapSchema = Joi.object() email: Joi.string(), generateAuthorPage: Joi.bool(), permalink: Joi.string(), + description: Joi.string(), }) .rename('image_url', 'imageURL') .or('name', 'imageURL') diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index c1e96397d6fc..92618b7a9c88 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -164,13 +164,8 @@ export function getBlogPageAuthors({ isUnlisted: (item: BlogPost) => item.metadata.unlisted, }); return { - name: author.name, - url: author.url, - title: author.title, - email: author.email, - key: author.key, + ...author, items: authorVisibility.listedItems.map((item: BlogPost) => item.id), - permalink: author.permalink, pages: author.permalink ? paginateBlogPosts({ blogPosts: authorVisibility.listedItems, @@ -376,11 +371,9 @@ async function processBlogSourceFile( }); const authors = getBlogPostAuthors({authorsMap, frontMatter, baseUrl}); - const authorsBaseRoutePath = normalizeUrl([ - baseUrl, - routeBasePath, - options.authorsPageBasePath, - ]); + const authorsBaseRoutePath = options.authorsPageBasePath + ? normalizeUrl([baseUrl, routeBasePath, options.authorsPageBasePath]) + : ''; const pageAuthors = normalizeFrontMatterPageAuthors( authorsBaseRoutePath, diff --git a/packages/docusaurus-plugin-content-blog/src/props.ts b/packages/docusaurus-plugin-content-blog/src/props.ts index bbd471865b6b..354e08e38f28 100644 --- a/packages/docusaurus-plugin-content-blog/src/props.ts +++ b/packages/docusaurus-plugin-content-blog/src/props.ts @@ -53,13 +53,8 @@ export function toPageAuthorsProp({ return Object.values(blogPageAuthors) .filter((author) => !author.unlisted) .map((author) => ({ - name: author.name, - permalink: author.permalink, + ...author, count: author.items.length, - url: author.url, - email: author.email, - title: author.title, - key: author.key, })); } @@ -71,14 +66,8 @@ export function toPageAuthorProp({ author: BlogPageAuthor; }): AuthorModule { return { - name: author.name, - permalink: author.permalink, + ...author, allAuthorsPath: blogAuthorsListPath, count: author.items.length, - unlisted: author.unlisted, - url: author.url, - email: author.email, - title: author.title, - key: author.key, }; } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx index e4fb90852b3d..c3bc688ac27c 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx @@ -12,7 +12,12 @@ import type {Props} from '@theme/BlogAuthorPage/Author'; import styles from './styles.module.css'; -export default function Author({permalink, name, count}: Props): JSX.Element { +export default function Author({ + permalink, + name, + count, + imageURL, +}: Props): JSX.Element { return ( + {imageURL && ( + {name} + )} {name} {count && {count}} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css index 3f8f446471d9..eb6214f4efa8 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css @@ -68,3 +68,8 @@ padding: 0.1rem 0.4rem; margin-left: 0.3rem; } + +.authorImage { + width: 1.5rem; + height: 1.5rem; +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx index eadc0597ac64..6da9b54bc8e4 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx @@ -76,17 +76,12 @@ function BlogAuthorsPostsPageContent({
    {title}
      - {author.title &&
    • {author.title}
    • } - {author.email && ( -
    • - {author.email} -
    • - )} {author.url && (
    • Personal website
    • )} + {author.description &&
    • {author.description}
    • }
    translate({ - id: 'theme.tags.tagsPageTitle', - message: 'Tags', - description: 'The title of the tag list page', + id: 'theme.authors.authorsPageTitle', + message: 'Authors', + description: 'The title of the author list page', }); export type AuthorLetterEntry = { diff --git a/packages/docusaurus-theme-translations/locales/base/theme-common.json b/packages/docusaurus-theme-translations/locales/base/theme-common.json index c6ec1d98c84a..18d3a6c51b92 100644 --- a/packages/docusaurus-theme-translations/locales/base/theme-common.json +++ b/packages/docusaurus-theme-translations/locales/base/theme-common.json @@ -139,6 +139,7 @@ "theme.tags.tagsPageTitle": "Tags", "theme.tags.tagsPageTitle___DESCRIPTION": "The title of the tag list page", "theme.authors.authorsPageLink": "View All Authors", + "theme.authors.authorsPageTitle": "Authors", "theme.blog.authorTitle": "{nPosts} contributed by \"{authorName}\"", "theme.unlistedContent.message": "This page is unlisted. Search engines will not index it, and only users having a direct link can access it.", "theme.unlistedContent.message___DESCRIPTION": "The unlisted content banner message", diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts index 7c5d7d1a514c..2c4ef48e0fe9 100644 --- a/packages/docusaurus-utils/src/authors.ts +++ b/packages/docusaurus-utils/src/authors.ts @@ -35,6 +35,8 @@ export type Author = { // TODO add description generateAuthorPage?: boolean; permalink?: string; + description?: string; + /** * Unknown keys are allowed, so that we can pass custom fields to authors, * e.g., `twitter`. @@ -48,7 +50,9 @@ export type PageAuthor = { /** Permalink to this author's page, without the `/authors/` base path. */ permalink: string; + imageURL: string | undefined; key: string | undefined; + description: string | undefined; title: string | undefined; email: string | undefined; url: string | undefined; @@ -93,6 +97,8 @@ function normalizeFrontMatterAuthor( title: undefined, email: undefined, key: authorString, + description: undefined, + imageURL: undefined, }; } @@ -111,6 +117,8 @@ function normalizeFrontMatterAuthor( title: author.title, email: author.email, key: author.key, + description: author.description, + imageURL: author.imageURL, }; } @@ -136,12 +144,14 @@ export function normalizeFrontMatterPageAuthors( name: author.name || (author.key as string), url: author.url, title: author.title, + description: author.description, email: author.email, // TODO investigate if the user put a key in file what will happen key: author.key as string, permalink: author.permalink ? author.permalink : _.kebabCase(author.key as string), + imageURL: author.imageURL, })); const authors = pageAuthors.map((author) => diff --git a/website/blog/authors.yml b/website/blog/authors.yml index 68c48a854051..022af8ad20e2 100644 --- a/website/blog/authors.yml +++ b/website/blog/authors.yml @@ -25,6 +25,9 @@ slorber: github: slorber email: sebastien@thisweekinreact.com generateAuthorPage: true + description: > + A freelance React and React-Native developer near Paris and Docusaurus maintainer. Also runs ThisWeekInReact.com, a newsletter to stay updated with the React ecosystem. + yangshun: name: Yangshun Tay From 2f725cb104bb18cee1fda2ad18945075728312cb Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 19 Jun 2024 10:46:33 +0200 Subject: [PATCH 016/115] spread properties --- packages/docusaurus-utils/src/authors.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts index 2c4ef48e0fe9..330e952be876 100644 --- a/packages/docusaurus-utils/src/authors.ts +++ b/packages/docusaurus-utils/src/authors.ts @@ -108,17 +108,11 @@ function normalizeFrontMatterAuthor( : frontMatterPageAuthor; return { - name: author.name, + ...author, permalink: normalizeAuthorPermalink({ permalink: author.permalink, authorsBaseRoutePath: authorsPath, }), - url: author.url, - title: author.title, - email: author.email, - key: author.key, - description: author.description, - imageURL: author.imageURL, }; } @@ -158,7 +152,7 @@ export function normalizeFrontMatterPageAuthors( normalizeFrontMatterAuthor(authorsBaseRoutePath, author), ); - return _.uniqBy(authors, (author) => author.permalink); + return _.uniqBy(authors, 'permalink'); } type AuthoredItemGroup = { From 8bf3e4f273acf64dc251d4c3d25ace33ac79fb38 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:15:08 +0200 Subject: [PATCH 017/115] refactor --- packages/docusaurus-utils/src/authors.ts | 54 +++++++----------------- website/blog/authors.yml | 1 - 2 files changed, 15 insertions(+), 40 deletions(-) diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts index 330e952be876..e83bf1395134 100644 --- a/packages/docusaurus-utils/src/authors.ts +++ b/packages/docusaurus-utils/src/authors.ts @@ -87,30 +87,22 @@ function normalizeAuthorPermalink({ function normalizeFrontMatterAuthor( authorsPath: string, - frontMatterPageAuthor: PageAuthor, + author: Author, ): PageAuthor { - function toPageAuthorObject(authorString: string): PageAuthor { - return { - name: authorString, - permalink: _.kebabCase(authorString), - url: undefined, - title: undefined, - email: undefined, - key: authorString, - description: undefined, - imageURL: undefined, - }; - } - - const author: PageAuthor = - typeof frontMatterPageAuthor === 'string' - ? toPageAuthorObject(frontMatterPageAuthor) - : frontMatterPageAuthor; + const key = author.key as string; + const name = author.name || key; + const permalink = author.permalink || _.kebabCase(key); return { - ...author, + imageURL: author.imageURL, + url: author.url, + title: author.title, + email: author.email, + description: author.description, + name, + key, permalink: normalizeAuthorPermalink({ - permalink: author.permalink, + permalink, authorsBaseRoutePath: authorsPath, }), }; @@ -132,25 +124,9 @@ export function normalizeFrontMatterPageAuthors( * `frontMatter.authors`. */ frontMatterPageAuthors: Author[] | undefined = [], ): PageAuthor[] { - const pageAuthors = frontMatterPageAuthors - .filter((author) => author.generateAuthorPage === true) - .map((author) => ({ - name: author.name || (author.key as string), - url: author.url, - title: author.title, - description: author.description, - email: author.email, - // TODO investigate if the user put a key in file what will happen - key: author.key as string, - permalink: author.permalink - ? author.permalink - : _.kebabCase(author.key as string), - imageURL: author.imageURL, - })); - - const authors = pageAuthors.map((author) => - normalizeFrontMatterAuthor(authorsBaseRoutePath, author), - ); + const authors = frontMatterPageAuthors + .filter((author) => author.generateAuthorPage) + .map((author) => normalizeFrontMatterAuthor(authorsBaseRoutePath, author)); return _.uniqBy(authors, 'permalink'); } diff --git a/website/blog/authors.yml b/website/blog/authors.yml index 022af8ad20e2..53cf0f0eed32 100644 --- a/website/blog/authors.yml +++ b/website/blog/authors.yml @@ -22,7 +22,6 @@ slorber: url: https://thisweekinreact.com image_url: https://github.com/slorber.png twitter: sebastienlorber - github: slorber email: sebastien@thisweekinreact.com generateAuthorPage: true description: > From 2f69b6d5f680d9311af5e66ad2ff5e33d87426a5 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:34:57 +0200 Subject: [PATCH 018/115] refactor: image base url --- .../src/authors.ts | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 10f7e9ced78c..05ee2a66d754 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -135,7 +135,7 @@ function normalizeFrontMatterAuthors( } function getFrontMatterAuthors(params: AuthorsParam): Author[] { - const {authorsMap} = params; + const {authorsMap, baseUrl} = params; const frontMatterAuthors = normalizeFrontMatterAuthors( params.frontMatter.authors, ); @@ -160,36 +160,29 @@ ${Object.keys(authorsMap) } function toAuthor(frontMatterAuthor: BlogPostFrontMatterAuthor): Author { - return { + const author = { // Author def from authorsMap can be locally overridden by front matter ...getAuthorsMapAuthor(frontMatterAuthor.key), ...frontMatterAuthor, }; + + return { + ...author, + imageURL: normalizeImageUrl({imageURL: author.imageURL, baseUrl}), + }; } return frontMatterAuthors.map(toAuthor); } -function fixAuthorImageBaseURL( - authors: Author[], - {baseUrl}: {baseUrl: string}, -): Author[] { - return authors.map((author) => ({ - ...author, - imageURL: normalizeImageUrl({imageURL: author.imageURL, baseUrl}), - })); -} - export function getBlogPostAuthors(params: AuthorsParam): Author[] { const authorLegacy = getFrontMatterAuthorLegacy(params); const authors = getFrontMatterAuthors(params); - const updatedAuthors = fixAuthorImageBaseURL(authors, params); - if (authorLegacy) { // Technically, we could allow mixing legacy/authors front matter, but do we // really want to? - if (updatedAuthors.length > 0) { + if (authors.length > 0) { throw new Error( `To declare blog post authors, use the 'authors' front matter in priority. Don't mix 'authors' with other existing 'author_*' front matter. Choose one or the other, not both at the same time.`, @@ -198,7 +191,7 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t return [authorLegacy]; } - return updatedAuthors; + return authors; } export function checkPermalinkCollisions( From 80f368a79793d2e85f719c0fd85a8e7b5617a946 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:35:28 +0200 Subject: [PATCH 019/115] refactor: types --- .../src/index.ts | 1 - packages/docusaurus-utils/src/authors.ts | 68 ++++++------------- 2 files changed, 21 insertions(+), 48 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index b760aa37489e..6da03d986e23 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -230,7 +230,6 @@ export default async function pluginContentBlog( }); const blogPageAuthors: BlogPageAuthors = getBlogPageAuthors({ - // TODO shouldn't we use listedBlogPosts here? (same for tags) blogPosts, postsPerPageOption, blogDescription, diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts index e83bf1395134..fc0e5dee5c13 100644 --- a/packages/docusaurus-utils/src/authors.ts +++ b/packages/docusaurus-utils/src/authors.ts @@ -7,55 +7,46 @@ import _ from 'lodash'; import {normalizeUrl} from './urlUtils'; +import type {Optional} from 'utility-types'; -export type Author = { +export type Author = Optional & { + generateAuthorPage?: boolean; + /** + * Unknown keys are allowed, so that we can pass custom fields to authors, + * e.g., `twitter`. + */ + [key: string]: unknown; +}; + +export type PageAuthor = { /** * If `name` doesn't exist, an `imageURL` is expected. */ - name?: string; + name: string; /** * The image path could be collocated, in which case * `metadata.assets.authorsImageUrls` should be used instead. If `imageURL` * doesn't exist, a `name` is expected. */ - imageURL?: string; - /** - * Used to generate the author's link. - */ - url?: string; + imageURL: string; + /** Permalink to this author's page, without the `/authors/` base path. */ + permalink: string; /** * Used as a subtitle for the author, e.g. "maintainer of Docusaurus" */ - title?: string; + title: string | undefined; /** * Mainly used for RSS feeds; if `url` doesn't exist, `email` can be used * to generate a fallback `mailto:` URL. */ - email?: string; - // TODO add description - generateAuthorPage?: boolean; - permalink?: string; - description?: string; - + email: string | undefined; /** - * Unknown keys are allowed, so that we can pass custom fields to authors, - * e.g., `twitter`. + * Used to generate the author's link. */ - [key: string]: unknown; -}; - -export type PageAuthor = { - /** User name */ - name: string; - /** Permalink to this author's page, without the `/authors/` base path. */ - permalink: string; + url: string | undefined; - imageURL: string | undefined; key: string | undefined; description: string | undefined; - title: string | undefined; - email: string | undefined; - url: string | undefined; }; /** What the authors list page should know about each author. */ @@ -72,19 +63,6 @@ export type AuthorModule = AuthorsListItem & { unlisted: boolean; }; -// We always apply tagsBaseRoutePath on purpose. For versioned docs, v1/doc.md -// and v2/doc.md tags with custom permalinks don't lead to the same created -// page. tagsBaseRoutePath is different for each doc version -function normalizeAuthorPermalink({ - authorsBaseRoutePath, - permalink, -}: { - authorsBaseRoutePath: string; - permalink: string; -}): string { - return normalizeUrl([authorsBaseRoutePath, permalink]); -} - function normalizeFrontMatterAuthor( authorsPath: string, author: Author, @@ -94,17 +72,14 @@ function normalizeFrontMatterAuthor( const permalink = author.permalink || _.kebabCase(key); return { - imageURL: author.imageURL, + imageURL: author.imageURL ?? '', url: author.url, title: author.title, email: author.email, description: author.description, name, key, - permalink: normalizeAuthorPermalink({ - permalink, - authorsBaseRoutePath: authorsPath, - }), + permalink: normalizeUrl([authorsPath, permalink]), }; } @@ -181,7 +156,6 @@ export function groupAuthoredItems( return result; } -// TODO is it useful? /** * Permits to get the "author visibility" (hard to find a better name) * IE, is this author listed or unlisted From 574d45a3e3be64efc27334a8e4189a8d60d7549c Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:37:22 +0200 Subject: [PATCH 020/115] style: use blog post author ui --- .../src/theme/BlogAuthorPage/Author/index.tsx | 41 +++++++----- .../BlogAuthorPage/Author/styles.module.css | 63 +------------------ 2 files changed, 28 insertions(+), 76 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx index c3bc688ac27c..e4fd63b85d18 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx @@ -7,33 +7,44 @@ import React from 'react'; import clsx from 'clsx'; -import Link from '@docusaurus/Link'; +import Link, {type Props as LinkProps} from '@docusaurus/Link'; import type {Props} from '@theme/BlogAuthorPage/Author'; import styles from './styles.module.css'; +function MaybeLink(props: LinkProps): JSX.Element { + if (props.href) { + return ; + } + return <>{props.children}; +} + export default function Author({ permalink, name, count, + title, imageURL, }: Props): JSX.Element { return ( - +
    {imageURL && ( - {name} + + {name} + + )} + + {name && ( +
    +
    + + {name} + + {count} +
    + {title} +
    )} - {name} - {count && {count}} - +
    ); } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css index eb6214f4efa8..5ec389cd08fc 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css @@ -5,71 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -:root { - --docusaurus-tag-list-border: var(--ifm-color-emphasis-300); -} - -.author { - border: 1px solid var(--docusaurus-tag-list-border); - transition: border var(--ifm-transition-fast); -} - -.author:hover { - --docusaurus-tag-list-border: var(--ifm-link-color); - text-decoration: none; -} - -.authorRegular { - border-radius: var(--ifm-global-radius); - padding: 0.2rem 0.5rem 0.3rem; - font-size: 90%; -} - -.authorWithCount { - display: flex; - align-items: center; - position: relative; - padding: 0 0.5rem 0 1rem; - border-left: 0; -} - -.authorWithCount::before, -.authorWithCount::after { - content: ''; - position: absolute; - top: 50%; - border: 1px solid var(--docusaurus-tag-list-border); - transition: inherit; -} - -.authorWithCount::before { - right: 100%; - transform: translate(50%, -50%) rotate(-45deg); - width: 1.18rem; - height: 1.18rem; - border-right: 0; - border-bottom: 0; -} - -.authorWithCount::after { - left: 0; - transform: translateY(-50%); - width: 0.5rem; - height: 0.5rem; - border-radius: 50%; -} - -.authorWithCount span { +.count { background: var(--ifm-color-secondary); color: var(--ifm-color-black); - font-size: 0.7rem; + font-size: 0.8rem; line-height: 1.2; border-radius: var(--ifm-global-radius); padding: 0.1rem 0.4rem; margin-left: 0.3rem; } - -.authorImage { - width: 1.5rem; - height: 1.5rem; -} From ff32178a497ef94d2aaae6a47bb1c05cbe0d5c2d Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:53:11 +0200 Subject: [PATCH 021/115] fix bug when permalink is other author key with slash --- .../docusaurus-plugin-content-blog/src/authors.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 05ee2a66d754..1cf41b051e09 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -200,12 +200,11 @@ export function checkPermalinkCollisions( const permalinkCounts: {[key: string]: string[]} = {}; for (const [key, author] of Object.entries(authorsMap)) { - if (author.permalink) { - if (!permalinkCounts[author.permalink]) { - permalinkCounts[author.permalink] = []; - } - permalinkCounts[author.permalink]?.push(author.name ?? key); + const permalink = normalizeUrl(['/', author.permalink || key, '/']); + if (!permalinkCounts[permalink]) { + permalinkCounts[permalink] = []; } + permalinkCounts[permalink]?.push(author.name || key); } const collisions = Object.entries(permalinkCounts).filter( @@ -216,11 +215,9 @@ export function checkPermalinkCollisions( let errorMessage = 'The following permalinks are duplicated:\n'; collisions.forEach(([permalink, authors]) => { - errorMessage += `Permalink: ${permalink}\nAuthors: ${authors.join( - ', ', - )}\n\n`; + errorMessage += `Permalink: ${permalink}\nAuthors: ${authors.join(', ')}`; }); - throw new Error(errorMessage.trim()); + throw new Error(errorMessage); } } From d4f892fb8886eb566fc9d9eba6686fc1fa96117f Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:06:59 +0200 Subject: [PATCH 022/115] refactor --- .../src/blogUtils.ts | 6 +-- packages/docusaurus-utils/src/authors.ts | 44 ++++++++----------- packages/docusaurus-utils/src/index.ts | 2 +- 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 92618b7a9c88..fdae08c7fcdc 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -28,7 +28,7 @@ import { normalizeTags, getAuthorVisibility, groupAuthoredItems, - normalizeFrontMatterPageAuthors, + normalizePageAuthors, } from '@docusaurus/utils'; import {getTagsFile} from '@docusaurus/utils-validation'; import {validateBlogPostFrontMatter} from './frontMatter'; @@ -375,10 +375,10 @@ async function processBlogSourceFile( ? normalizeUrl([baseUrl, routeBasePath, options.authorsPageBasePath]) : ''; - const pageAuthors = normalizeFrontMatterPageAuthors( + const pageAuthors = normalizePageAuthors({ authorsBaseRoutePath, authors, - ); + }); return { id: slug, diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts index fc0e5dee5c13..a76d33e5bc93 100644 --- a/packages/docusaurus-utils/src/authors.ts +++ b/packages/docusaurus-utils/src/authors.ts @@ -63,10 +63,13 @@ export type AuthorModule = AuthorsListItem & { unlisted: boolean; }; -function normalizeFrontMatterAuthor( - authorsPath: string, - author: Author, -): PageAuthor { +function normalizePageAuthor({ + authorsBaseRoutePath, + author, +}: { + authorsBaseRoutePath: string; + author: Author; +}): PageAuthor { const key = author.key as string; const name = author.name || key; const permalink = author.permalink || _.kebabCase(key); @@ -79,31 +82,20 @@ function normalizeFrontMatterAuthor( description: author.description, name, key, - permalink: normalizeUrl([authorsPath, permalink]), + permalink: normalizeUrl([authorsBaseRoutePath, permalink]), }; } -/** - * Takes author objects as they are defined in front matter, and normalizes each - * into a standard author object. The permalink is created by appending the - * sluggified label to `authorsPath`. Front matter authors already containing - * permalinks would still have `authorsPath` prepended. - * - * The result will always be unique by permalinks. The behavior with colliding - * permalinks is undetermined. - */ -export function normalizeFrontMatterPageAuthors( - /** Base path to append the author permalinks to. */ - authorsBaseRoutePath: string, - /** Can be `undefined`, so that we can directly pipe in - * `frontMatter.authors`. */ - frontMatterPageAuthors: Author[] | undefined = [], -): PageAuthor[] { - const authors = frontMatterPageAuthors +export function normalizePageAuthors({ + authorsBaseRoutePath, + authors, +}: { + authorsBaseRoutePath: string; + authors: Author[] | undefined; +}): PageAuthor[] { + return (authors ?? []) .filter((author) => author.generateAuthorPage) - .map((author) => normalizeFrontMatterAuthor(authorsBaseRoutePath, author)); - - return _.uniqBy(authors, 'permalink'); + .map((author) => normalizePageAuthor({authorsBaseRoutePath, author})); } type AuthoredItemGroup = { @@ -148,7 +140,7 @@ export function groupAuthoredItems( // If user add twice the same author to a md doc (weird but possible), // we don't want the item to appear twice in the list... - // TODO remove this and warn the user if we detect duplicates? + // TODO wait for #10224 and remove below code Object.values(result).forEach((group) => { group.items = _.uniq(group.items); }); diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 5a80dca938d2..2660e5e6a874 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -137,5 +137,5 @@ export { type Author, getAuthorVisibility, groupAuthoredItems, - normalizeFrontMatterPageAuthors, + normalizePageAuthors, } from './authors'; From 788b198d99e6f7158b4a6c144858aa9bb0712f91 Mon Sep 17 00:00:00 2001 From: OzakIOne Date: Wed, 19 Jun 2024 15:11:48 +0000 Subject: [PATCH 023/115] refactor: apply lint autofix --- project-words.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/project-words.txt b/project-words.txt index c624afd5f1f9..0125953ec975 100644 --- a/project-words.txt +++ b/project-words.txt @@ -321,7 +321,6 @@ showinfo Sida Simen slorber -sluggified sluggifies sluggify solana From 16b0dbf7207dd0a53e0bd2199a676619a119549c Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:56:59 +0200 Subject: [PATCH 024/115] refactor --- packages/docusaurus-plugin-content-blog/src/authors.ts | 10 ++++++++-- .../docusaurus-plugin-content-blog/src/blogUtils.ts | 6 +----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 1cf41b051e09..b57d962a1851 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import _ from 'lodash'; import {getDataFileData, normalizeUrl, type Author} from '@docusaurus/utils'; import {Joi, URISchema} from '@docusaurus/utils-validation'; import type {BlogContentPaths} from './types'; @@ -195,11 +196,16 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t } export function checkPermalinkCollisions( - authorsMap: _.Dictionary, + authorsMap: AuthorsMap | undefined, ): void { + const pageAuthorsMap = _.pickBy( + authorsMap, + (author) => author.generateAuthorPage === true, + ); + const permalinkCounts: {[key: string]: string[]} = {}; - for (const [key, author] of Object.entries(authorsMap)) { + for (const [key, author] of Object.entries(pageAuthorsMap)) { const permalink = normalizeUrl(['/', author.permalink || key, '/']); if (!permalinkCounts[permalink]) { permalinkCounts[permalink] = []; diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index fdae08c7fcdc..9ac7ca6a6a2f 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -429,12 +429,8 @@ export async function generateBlogPosts( contentPaths, authorsMapPath: options.authorsMapPath, }); - const pageAuthorsMap = _.pickBy( - authorsMap, - (author) => author.generateAuthorPage === true, - ); - checkPermalinkCollisions(pageAuthorsMap); + checkPermalinkCollisions(authorsMap); const tagsFile = await getTagsFile({contentPaths, tags: options.tags}); From 5c789b301c5cb32695cd371e660ef91eb3f7d089 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:58:15 +0200 Subject: [PATCH 025/115] refactor remove pageAuthor prop, use author instead --- .../__snapshots__/index.test.ts.snap | 7 ++++--- .../src/__tests__/index.test.ts | 21 +++++++++++++------ .../src/blogUtils.ts | 13 +++++++----- .../src/plugin-content-blog.d.ts | 7 +------ packages/docusaurus-utils/src/authors.ts | 19 ++++++++++------- 5 files changed, 39 insertions(+), 28 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap index 7c4102bef728..246605182e50 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap @@ -120,8 +120,12 @@ exports[`blog plugin process blog posts load content 2`] = ` "metadata": { "authors": [ { + "description": undefined, + "email": undefined, "imageURL": undefined, + "key": undefined, "name": "Sébastien Lorber", + "permalink": "/blog/authors", "title": "Docusaurus maintainer", "url": "https://sebastienlorber.com", }, @@ -147,7 +151,6 @@ exports[`blog plugin process blog posts load content 2`] = ` "permalink": "/blog/another/tags", "title": "Another With Tag", }, - "pageAuthors": [], "permalink": "/blog/simple/slug/another", "readingTime": 0.015, "source": "@site/blog/another-simple-slug-with-tags.md", @@ -187,7 +190,6 @@ exports[`blog plugin process blog posts load content 2`] = ` "permalink": "/blog/another/tags2", "title": "Another With Tag", }, - "pageAuthors": [], "permalink": "/blog/another/tags", "prevItem": { "permalink": "/blog/simple/slug/another", @@ -233,7 +235,6 @@ exports[`blog plugin process blog posts load content 2`] = ` "hasTruncateMarker": false, "lastUpdatedAt": undefined, "lastUpdatedBy": undefined, - "pageAuthors": [], "permalink": "/blog/another/tags2", "prevItem": { "permalink": "/blog/another/tags", diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts index 2e2ad99aa1d7..a86fb2623b2c 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts @@ -196,7 +196,6 @@ describe('blog plugin', () => { permalink: '/blog/tags/date', }, ], - pageAuthors: [], nextItem: { permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash', title: 'Happy 1st Birthday Slash! (translated)', @@ -221,7 +220,13 @@ describe('blog plugin', () => { authors: [ { name: 'Yangshun Tay (translated)', + description: undefined, + email: undefined, imageURL: undefined, + key: undefined, + permalink: '/blog/authors', + title: undefined, + url: undefined, }, { email: 'lorber.sebastien@gmail.com', @@ -229,6 +234,9 @@ describe('blog plugin', () => { name: 'Sébastien Lorber (translated)', title: 'Docusaurus maintainer (translated)', imageURL: undefined, + description: undefined, + permalink: '/blog/authors/slorber', + url: undefined, }, ], date: new Date('2018-12-14'), @@ -256,7 +264,8 @@ describe('blog plugin', () => { permalink: '/blog/tags/global-tag-permalink (en)', }, ], - pageAuthors: [], + lastUpdatedAt: undefined, + lastUpdatedBy: undefined, prevItem: { permalink: '/blog/date-matter', title: 'date-matter', @@ -303,7 +312,6 @@ describe('blog plugin', () => { permalink: '/blog/tags/complex', }, ], - pageAuthors: [], hasTruncateMarker: false, unlisted: false, }); @@ -324,6 +332,10 @@ describe('blog plugin', () => { title: 'Docusaurus maintainer', url: 'https://sebastienlorber.com', imageURL: undefined, + description: undefined, + email: undefined, + key: undefined, + permalink: '/blog/authors', }, ], prevItem: undefined, @@ -341,7 +353,6 @@ describe('blog plugin', () => { title: 'Simple Slug', }, tags: [], - pageAuthors: [], hasTruncateMarker: false, unlisted: false, }); @@ -363,7 +374,6 @@ describe('blog plugin', () => { }, prevItem: undefined, tags: [], - pageAuthors: [], nextItem: { permalink: '/blog/date-matter', title: 'date-matter', @@ -488,7 +498,6 @@ describe('blog plugin', () => { date: noDateSourceTime, frontMatter: {}, tags: [], - pageAuthors: [], prevItem: undefined, nextItem: undefined, hasTruncateMarker: false, diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 9ac7ca6a6a2f..d1283bf888c3 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -153,10 +153,14 @@ export function getBlogPageAuthors({ postsPerPageOption: number | 'ALL'; pageBasePath: string; }): BlogPageAuthors { - const getPostPageAuthors = (blogPost: BlogPost) => - blogPost.metadata.pageAuthors; + const getPostPageAuthors = (blogPost: BlogPost) => blogPost.metadata.authors; - const groups = groupAuthoredItems(blogPosts, getPostPageAuthors); + const groups = groupAuthoredItems( + blogPosts.filter((blog) => + blog.metadata.authors.map((author) => author.generateAuthorPage), + ), + getPostPageAuthors, + ); return _.mapValues(groups, ({author, items: authorBlogPosts}) => { const authorVisibility = getAuthorVisibility({ @@ -398,12 +402,11 @@ async function processBlogSourceFile( }) : undefined, hasTruncateMarker: truncateMarker.test(content), - authors, + authors: pageAuthors, frontMatter, unlisted, lastUpdatedAt: lastUpdate.lastUpdatedAt, lastUpdatedBy: lastUpdate.lastUpdatedBy, - pageAuthors, }, content, }; diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index f4b788f3896c..8fa5c222933e 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -15,7 +15,6 @@ declare module '@docusaurus/plugin-content-blog' { LastUpdateData, FrontMatterLastUpdate, TagsPluginOptions, - PageAuthor, Author, } from '@docusaurus/utils'; import type {DocusaurusConfig, Plugin, LoadContext} from '@docusaurus/types'; @@ -208,10 +207,6 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the readonly frontMatter: BlogPostFrontMatter & {[key: string]: unknown}; /** Tags, normalized. */ readonly tags: TagMetadata[]; - /** - * Page authors, for use in the Authors grouping page. - */ - readonly pageAuthors: PageAuthor[]; /** * Marks the post as unlisted and visibly hides it unless directly accessed. */ @@ -489,7 +484,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the [permalink: string]: BlogPageAuthor; }; - export type BlogPageAuthor = PageAuthor & { + export type BlogPageAuthor = Author & { /** Blog post permalinks. */ items: string[]; pages: BlogPaginated[]; diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts index a76d33e5bc93..40e0f927f62b 100644 --- a/packages/docusaurus-utils/src/authors.ts +++ b/packages/docusaurus-utils/src/authors.ts @@ -28,7 +28,7 @@ export type PageAuthor = { * `metadata.assets.authorsImageUrls` should be used instead. If `imageURL` * doesn't exist, a `name` is expected. */ - imageURL: string; + imageURL?: string; /** Permalink to this author's page, without the `/authors/` base path. */ permalink: string; /** @@ -50,7 +50,7 @@ export type PageAuthor = { }; /** What the authors list page should know about each author. */ -export type AuthorsListItem = PageAuthor & { +export type AuthorsListItem = Author & { /** Number of posts/docs with this author. */ count: number; }; @@ -75,7 +75,7 @@ function normalizePageAuthor({ const permalink = author.permalink || _.kebabCase(key); return { - imageURL: author.imageURL ?? '', + imageURL: author.imageURL, url: author.url, title: author.title, email: author.email, @@ -93,13 +93,13 @@ export function normalizePageAuthors({ authorsBaseRoutePath: string; authors: Author[] | undefined; }): PageAuthor[] { - return (authors ?? []) - .filter((author) => author.generateAuthorPage) - .map((author) => normalizePageAuthor({authorsBaseRoutePath, author})); + return (authors ?? []).map((author) => + normalizePageAuthor({authorsBaseRoutePath, author}), + ); } type AuthoredItemGroup = { - author: PageAuthor; + author: Author; items: Item[]; }; @@ -118,12 +118,15 @@ export function groupAuthoredItems( * A callback telling me how to get the authors list of the current item. * Usually simply getting it from some metadata of the current item. */ - getItemPageAuthors: (item: Item) => readonly PageAuthor[], + getItemPageAuthors: (item: Item) => readonly Author[], ): {[permalink: string]: AuthoredItemGroup} { const result: {[permalink: string]: AuthoredItemGroup} = {}; items.forEach((item) => { getItemPageAuthors(item).forEach((author) => { + if (!author.permalink) { + return; + } // Init missing author groups // TODO: it's not really clear what should be the behavior if 2 // authors have the same permalink but the label is different for each From 151f49f1d08232294408e485a19e68eecf054ff0 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Thu, 20 Jun 2024 17:38:44 +0200 Subject: [PATCH 026/115] Revert "refactor remove pageAuthor prop, use author instead" This reverts commit 5c789b301c5cb32695cd371e660ef91eb3f7d089. --- .../__snapshots__/index.test.ts.snap | 7 +++---- .../src/__tests__/index.test.ts | 21 ++++++------------- .../src/blogUtils.ts | 13 +++++------- .../src/plugin-content-blog.d.ts | 7 ++++++- packages/docusaurus-utils/src/authors.ts | 19 +++++++---------- 5 files changed, 28 insertions(+), 39 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap index 246605182e50..7c4102bef728 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap @@ -120,12 +120,8 @@ exports[`blog plugin process blog posts load content 2`] = ` "metadata": { "authors": [ { - "description": undefined, - "email": undefined, "imageURL": undefined, - "key": undefined, "name": "Sébastien Lorber", - "permalink": "/blog/authors", "title": "Docusaurus maintainer", "url": "https://sebastienlorber.com", }, @@ -151,6 +147,7 @@ exports[`blog plugin process blog posts load content 2`] = ` "permalink": "/blog/another/tags", "title": "Another With Tag", }, + "pageAuthors": [], "permalink": "/blog/simple/slug/another", "readingTime": 0.015, "source": "@site/blog/another-simple-slug-with-tags.md", @@ -190,6 +187,7 @@ exports[`blog plugin process blog posts load content 2`] = ` "permalink": "/blog/another/tags2", "title": "Another With Tag", }, + "pageAuthors": [], "permalink": "/blog/another/tags", "prevItem": { "permalink": "/blog/simple/slug/another", @@ -235,6 +233,7 @@ exports[`blog plugin process blog posts load content 2`] = ` "hasTruncateMarker": false, "lastUpdatedAt": undefined, "lastUpdatedBy": undefined, + "pageAuthors": [], "permalink": "/blog/another/tags2", "prevItem": { "permalink": "/blog/another/tags", diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts index a86fb2623b2c..2e2ad99aa1d7 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts @@ -196,6 +196,7 @@ describe('blog plugin', () => { permalink: '/blog/tags/date', }, ], + pageAuthors: [], nextItem: { permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash', title: 'Happy 1st Birthday Slash! (translated)', @@ -220,13 +221,7 @@ describe('blog plugin', () => { authors: [ { name: 'Yangshun Tay (translated)', - description: undefined, - email: undefined, imageURL: undefined, - key: undefined, - permalink: '/blog/authors', - title: undefined, - url: undefined, }, { email: 'lorber.sebastien@gmail.com', @@ -234,9 +229,6 @@ describe('blog plugin', () => { name: 'Sébastien Lorber (translated)', title: 'Docusaurus maintainer (translated)', imageURL: undefined, - description: undefined, - permalink: '/blog/authors/slorber', - url: undefined, }, ], date: new Date('2018-12-14'), @@ -264,8 +256,7 @@ describe('blog plugin', () => { permalink: '/blog/tags/global-tag-permalink (en)', }, ], - lastUpdatedAt: undefined, - lastUpdatedBy: undefined, + pageAuthors: [], prevItem: { permalink: '/blog/date-matter', title: 'date-matter', @@ -312,6 +303,7 @@ describe('blog plugin', () => { permalink: '/blog/tags/complex', }, ], + pageAuthors: [], hasTruncateMarker: false, unlisted: false, }); @@ -332,10 +324,6 @@ describe('blog plugin', () => { title: 'Docusaurus maintainer', url: 'https://sebastienlorber.com', imageURL: undefined, - description: undefined, - email: undefined, - key: undefined, - permalink: '/blog/authors', }, ], prevItem: undefined, @@ -353,6 +341,7 @@ describe('blog plugin', () => { title: 'Simple Slug', }, tags: [], + pageAuthors: [], hasTruncateMarker: false, unlisted: false, }); @@ -374,6 +363,7 @@ describe('blog plugin', () => { }, prevItem: undefined, tags: [], + pageAuthors: [], nextItem: { permalink: '/blog/date-matter', title: 'date-matter', @@ -498,6 +488,7 @@ describe('blog plugin', () => { date: noDateSourceTime, frontMatter: {}, tags: [], + pageAuthors: [], prevItem: undefined, nextItem: undefined, hasTruncateMarker: false, diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index d1283bf888c3..9ac7ca6a6a2f 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -153,14 +153,10 @@ export function getBlogPageAuthors({ postsPerPageOption: number | 'ALL'; pageBasePath: string; }): BlogPageAuthors { - const getPostPageAuthors = (blogPost: BlogPost) => blogPost.metadata.authors; + const getPostPageAuthors = (blogPost: BlogPost) => + blogPost.metadata.pageAuthors; - const groups = groupAuthoredItems( - blogPosts.filter((blog) => - blog.metadata.authors.map((author) => author.generateAuthorPage), - ), - getPostPageAuthors, - ); + const groups = groupAuthoredItems(blogPosts, getPostPageAuthors); return _.mapValues(groups, ({author, items: authorBlogPosts}) => { const authorVisibility = getAuthorVisibility({ @@ -402,11 +398,12 @@ async function processBlogSourceFile( }) : undefined, hasTruncateMarker: truncateMarker.test(content), - authors: pageAuthors, + authors, frontMatter, unlisted, lastUpdatedAt: lastUpdate.lastUpdatedAt, lastUpdatedBy: lastUpdate.lastUpdatedBy, + pageAuthors, }, content, }; diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 8fa5c222933e..f4b788f3896c 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -15,6 +15,7 @@ declare module '@docusaurus/plugin-content-blog' { LastUpdateData, FrontMatterLastUpdate, TagsPluginOptions, + PageAuthor, Author, } from '@docusaurus/utils'; import type {DocusaurusConfig, Plugin, LoadContext} from '@docusaurus/types'; @@ -207,6 +208,10 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the readonly frontMatter: BlogPostFrontMatter & {[key: string]: unknown}; /** Tags, normalized. */ readonly tags: TagMetadata[]; + /** + * Page authors, for use in the Authors grouping page. + */ + readonly pageAuthors: PageAuthor[]; /** * Marks the post as unlisted and visibly hides it unless directly accessed. */ @@ -484,7 +489,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the [permalink: string]: BlogPageAuthor; }; - export type BlogPageAuthor = Author & { + export type BlogPageAuthor = PageAuthor & { /** Blog post permalinks. */ items: string[]; pages: BlogPaginated[]; diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts index 40e0f927f62b..a76d33e5bc93 100644 --- a/packages/docusaurus-utils/src/authors.ts +++ b/packages/docusaurus-utils/src/authors.ts @@ -28,7 +28,7 @@ export type PageAuthor = { * `metadata.assets.authorsImageUrls` should be used instead. If `imageURL` * doesn't exist, a `name` is expected. */ - imageURL?: string; + imageURL: string; /** Permalink to this author's page, without the `/authors/` base path. */ permalink: string; /** @@ -50,7 +50,7 @@ export type PageAuthor = { }; /** What the authors list page should know about each author. */ -export type AuthorsListItem = Author & { +export type AuthorsListItem = PageAuthor & { /** Number of posts/docs with this author. */ count: number; }; @@ -75,7 +75,7 @@ function normalizePageAuthor({ const permalink = author.permalink || _.kebabCase(key); return { - imageURL: author.imageURL, + imageURL: author.imageURL ?? '', url: author.url, title: author.title, email: author.email, @@ -93,13 +93,13 @@ export function normalizePageAuthors({ authorsBaseRoutePath: string; authors: Author[] | undefined; }): PageAuthor[] { - return (authors ?? []).map((author) => - normalizePageAuthor({authorsBaseRoutePath, author}), - ); + return (authors ?? []) + .filter((author) => author.generateAuthorPage) + .map((author) => normalizePageAuthor({authorsBaseRoutePath, author})); } type AuthoredItemGroup = { - author: Author; + author: PageAuthor; items: Item[]; }; @@ -118,15 +118,12 @@ export function groupAuthoredItems( * A callback telling me how to get the authors list of the current item. * Usually simply getting it from some metadata of the current item. */ - getItemPageAuthors: (item: Item) => readonly Author[], + getItemPageAuthors: (item: Item) => readonly PageAuthor[], ): {[permalink: string]: AuthoredItemGroup} { const result: {[permalink: string]: AuthoredItemGroup} = {}; items.forEach((item) => { getItemPageAuthors(item).forEach((author) => { - if (!author.permalink) { - return; - } // Init missing author groups // TODO: it's not really clear what should be the behavior if 2 // authors have the same permalink but the label is different for each From 0a353236211ab6bd24e77a200af20a9c7d2eccb6 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Sun, 23 Jun 2024 22:21:16 +0200 Subject: [PATCH 027/115] move authorsUtils to blog --- .../src/authors.ts | 156 ++++++++++++++- .../src/blogUtils.ts | 40 +--- .../src/feed.ts | 3 +- .../src/index.ts | 2 +- .../src/plugin-content-blog.d.ts | 58 +++++- .../src/props.ts | 5 +- .../src/utils/authorsUtils.ts | 2 +- .../src/utils/structuredDataUtils.ts | 6 +- packages/docusaurus-utils/src/authors.ts | 178 ------------------ packages/docusaurus-utils/src/index.ts | 10 - 10 files changed, 220 insertions(+), 240 deletions(-) delete mode 100644 packages/docusaurus-utils/src/authors.ts diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index b57d962a1851..09aa53eb967c 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -6,13 +6,18 @@ */ import _ from 'lodash'; -import {getDataFileData, normalizeUrl, type Author} from '@docusaurus/utils'; +import {getDataFileData, normalizeUrl} from '@docusaurus/utils'; import {Joi, URISchema} from '@docusaurus/utils-validation'; +import {paginateBlogPosts} from './blogUtils'; import type {BlogContentPaths} from './types'; import type { + Author, + BlogPageAuthors, + BlogPost, BlogPostFrontMatter, BlogPostFrontMatterAuthor, BlogPostFrontMatterAuthors, + PageAuthor, } from '@docusaurus/plugin-content-blog'; export type AuthorsMap = {[authorKey: string]: Author}; @@ -227,3 +232,152 @@ export function checkPermalinkCollisions( throw new Error(errorMessage); } } + +function normalizePageAuthor({ + authorsBaseRoutePath, + author, +}: { + authorsBaseRoutePath: string; + author: Author; +}): PageAuthor { + const key = author.key as string; + const name = author.name || key; + const permalink = author.permalink || _.kebabCase(key); + + return { + imageURL: author.imageURL ?? '', + url: author.url, + title: author.title, + email: author.email, + description: author.description, + name, + key, + permalink: normalizeUrl([authorsBaseRoutePath, permalink]), + }; +} + +export function normalizePageAuthors({ + authorsBaseRoutePath, + authors, +}: { + authorsBaseRoutePath: string; + authors: Author[] | undefined; +}): PageAuthor[] { + return (authors ?? []) + .filter((author) => author.generateAuthorPage) + .map((author) => normalizePageAuthor({authorsBaseRoutePath, author})); +} + +type AuthoredItemGroup = { + author: PageAuthor; + items: Item[]; +}; + +/** + * Permits to group docs/blog posts by author (provided by front matter). + * + * @returns a map from author permalink to the items and other relevant author + * data. + * The record is indexed by permalink, because routes must be unique in the end. + * Labels may vary on 2 MD files but they are normalized. Docs with + * label='some label' and label='some-label' should end up in the same page. + */ +export function groupAuthoredItems( + items: readonly Item[], + /** + * A callback telling me how to get the authors list of the current item. + * Usually simply getting it from some metadata of the current item. + */ + getItemPageAuthors: (item: Item) => readonly PageAuthor[], +): {[permalink: string]: AuthoredItemGroup} { + const result: {[permalink: string]: AuthoredItemGroup} = {}; + + items.forEach((item) => { + getItemPageAuthors(item).forEach((author) => { + // Init missing author groups + // TODO: it's not really clear what should be the behavior if 2 + // authors have the same permalink but the label is different for each + // For now, the first author found wins + result[author.permalink] ??= { + author, + items: [], + }; + + // Add item to group + result[author.permalink]!.items.push(item); + }); + }); + + // If user add twice the same author to a md doc (weird but possible), + // we don't want the item to appear twice in the list... + // TODO wait for #10224 and remove below code + Object.values(result).forEach((group) => { + group.items = _.uniq(group.items); + }); + + return result; +} + +/** + * Permits to get the "author visibility" (hard to find a better name) + * IE, is this author listed or unlisted + * And which items should be listed when this author is browsed + */ +export function getAuthorVisibility({ + items, + isUnlisted, +}: { + items: Item[]; + isUnlisted: (item: Item) => boolean; +}): { + unlisted: boolean; + listedItems: Item[]; +} { + const allItemsUnlisted = items.every(isUnlisted); + // When a author is full of unlisted items, we display all the items + // when author is browsed, but we mark the author as unlisted + if (allItemsUnlisted) { + return {unlisted: true, listedItems: items}; + } + // When a author has some listed items, the author remains listed + // but we filter its unlisted items + return { + unlisted: false, + listedItems: items.filter((item) => !isUnlisted(item)), + }; +} + +export function getBlogPageAuthors({ + blogPosts, + ...params +}: { + blogPosts: BlogPost[]; + blogTitle: string; + blogDescription: string; + postsPerPageOption: number | 'ALL'; + pageBasePath: string; +}): BlogPageAuthors { + const getPostPageAuthors = (blogPost: BlogPost) => + blogPost.metadata.pageAuthors; + + const groups = groupAuthoredItems(blogPosts, getPostPageAuthors); + + return _.mapValues(groups, ({author, items: authorBlogPosts}) => { + const authorVisibility = getAuthorVisibility({ + items: authorBlogPosts, + isUnlisted: (item: BlogPost) => item.metadata.unlisted, + }); + return { + ...author, + items: authorVisibility.listedItems.map((item: BlogPost) => item.id), + pages: author.permalink + ? paginateBlogPosts({ + blogPosts: authorVisibility.listedItems, + basePageUrl: author.permalink, + ...params, + }) + : [], + unlisted: authorVisibility.unlisted, + }; + }); +} diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 9ac7ca6a6a2f..ea4b5be18ce4 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -26,9 +26,6 @@ import { isDraft, readLastUpdateData, normalizeTags, - getAuthorVisibility, - groupAuthoredItems, - normalizePageAuthors, } from '@docusaurus/utils'; import {getTagsFile} from '@docusaurus/utils-validation'; import {validateBlogPostFrontMatter} from './frontMatter'; @@ -37,6 +34,7 @@ import { getAuthorsMap, getBlogPostAuthors, checkPermalinkCollisions, + normalizePageAuthors, } from './authors'; import type {TagsFile} from '@docusaurus/utils'; import type {LoadContext, ParseFrontMatter} from '@docusaurus/types'; @@ -46,7 +44,6 @@ import type { BlogPost, BlogTags, BlogPaginated, - BlogPageAuthors, } from '@docusaurus/plugin-content-blog'; import type {BlogContentPaths} from './types'; @@ -143,41 +140,6 @@ export function getBlogTags({ }); } -export function getBlogPageAuthors({ - blogPosts, - ...params -}: { - blogPosts: BlogPost[]; - blogTitle: string; - blogDescription: string; - postsPerPageOption: number | 'ALL'; - pageBasePath: string; -}): BlogPageAuthors { - const getPostPageAuthors = (blogPost: BlogPost) => - blogPost.metadata.pageAuthors; - - const groups = groupAuthoredItems(blogPosts, getPostPageAuthors); - - return _.mapValues(groups, ({author, items: authorBlogPosts}) => { - const authorVisibility = getAuthorVisibility({ - items: authorBlogPosts, - isUnlisted: (item: BlogPost) => item.metadata.unlisted, - }); - return { - ...author, - items: authorVisibility.listedItems.map((item: BlogPost) => item.id), - pages: author.permalink - ? paginateBlogPosts({ - blogPosts: authorVisibility.listedItems, - basePageUrl: author.permalink, - ...params, - }) - : [], - unlisted: authorVisibility.unlisted, - }; - }); -} - const DATE_FILENAME_REGEX = /^(?.*)(?\d{4}[-/]\d{1,2}[-/]\d{1,2})[-/]?(?.*?)(?:\/index)?.mdx?$/; diff --git a/packages/docusaurus-plugin-content-blog/src/feed.ts b/packages/docusaurus-plugin-content-blog/src/feed.ts index f7954000b7fd..b8bc8c1481c9 100644 --- a/packages/docusaurus-plugin-content-blog/src/feed.ts +++ b/packages/docusaurus-plugin-content-blog/src/feed.ts @@ -10,7 +10,7 @@ import fs from 'fs-extra'; import logger from '@docusaurus/logger'; import {Feed, type Author as FeedAuthor} from 'feed'; import * as srcset from 'srcset'; -import {normalizeUrl, readOutputHTMLFile, type Author} from '@docusaurus/utils'; +import {normalizeUrl, readOutputHTMLFile} from '@docusaurus/utils'; import { blogPostContainerID, applyTrailingSlash, @@ -20,6 +20,7 @@ import type {DocusaurusConfig, HtmlTags, LoadContext} from '@docusaurus/types'; import type { FeedType, PluginOptions, + Author, BlogPost, BlogFeedItem, } from '@docusaurus/plugin-content-blog'; diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 6da03d986e23..64fe57872a93 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -28,13 +28,13 @@ import { shouldBeListed, applyProcessBlogPosts, generateBlogPosts, - getBlogPageAuthors, } from './blogUtils'; import footnoteIDFixer from './remark/footnoteIDFixer'; import {translateContent, getTranslationFiles} from './translations'; import {createBlogFeedFiles, createFeedHtmlHeadTags} from './feed'; import {createAllRoutes} from './routes'; +import {getBlogPageAuthors} from './authors'; import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types'; import type {LoadContext, Plugin} from '@docusaurus/types'; import type { diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index f4b788f3896c..a185e7d80cfd 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -15,12 +15,10 @@ declare module '@docusaurus/plugin-content-blog' { LastUpdateData, FrontMatterLastUpdate, TagsPluginOptions, - PageAuthor, - Author, } from '@docusaurus/utils'; import type {DocusaurusConfig, Plugin, LoadContext} from '@docusaurus/types'; import type {Item as FeedItem} from 'feed'; - import type {Overwrite} from 'utility-types'; + import type {Optional, Overwrite} from 'utility-types'; export type Assets = { /** @@ -45,6 +43,60 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the authorsImageUrls: (string | undefined)[]; }; + export type Author = Optional & { + generateAuthorPage?: boolean; + /** + * Unknown keys are allowed, so that we can pass custom fields to authors, + * e.g., `twitter`. + */ + [key: string]: unknown; + }; + + export type PageAuthor = { + /** + * If `name` doesn't exist, an `imageURL` is expected. + */ + name: string; + /** + * The image path could be collocated, in which case + * `metadata.assets.authorsImageUrls` should be used instead. If `imageURL` + * doesn't exist, a `name` is expected. + */ + imageURL: string; + /** Permalink to this author's page, without the `/authors/` base path. */ + permalink: string; + /** + * Used as a subtitle for the author, e.g. "maintainer of Docusaurus" + */ + title: string | undefined; + /** + * Mainly used for RSS feeds; if `url` doesn't exist, `email` can be used + * to generate a fallback `mailto:` URL. + */ + email: string | undefined; + /** + * Used to generate the author's link. + */ + url: string | undefined; + + key: string | undefined; + description: string | undefined; + }; + + /** What the authors list page should know about each author. */ + export type AuthorsListItem = PageAuthor & { + /** Number of posts/docs with this author. */ + count: number; + }; + + /** What the author's own page should know about the author. */ + export type AuthorModule = AuthorsListItem & { + /** The authors list page's permalink. */ + allAuthorsPath: string; + /** Is this author unlisted? (when it only contains unlisted items) */ + unlisted: boolean; + }; + /** * Everything is partial/unnormalized, because front matter is always * preserved as-is. Default values will be applied when generating metadata diff --git a/packages/docusaurus-plugin-content-blog/src/props.ts b/packages/docusaurus-plugin-content-blog/src/props.ts index 354e08e38f28..87f3658237cf 100644 --- a/packages/docusaurus-plugin-content-blog/src/props.ts +++ b/packages/docusaurus-plugin-content-blog/src/props.ts @@ -4,13 +4,10 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ +import type {TagsListItem, TagModule} from '@docusaurus/utils'; import type { - TagsListItem, - TagModule, AuthorsListItem, AuthorModule, -} from '@docusaurus/utils'; -import type { BlogPageAuthor, BlogPageAuthors, BlogTag, diff --git a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts index 6b6fcf5ac250..2445b6b96f72 100644 --- a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts @@ -6,7 +6,7 @@ */ import {translate} from '@docusaurus/Translate'; -import type {AuthorsListItem} from '@docusaurus/utils'; +import type {AuthorsListItem} from '@docusaurus/plugin-content-blog'; export const translateAuthorsPageTitle = (): string => translate({ diff --git a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts index f564b021c781..3a8a985e5cd8 100644 --- a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts @@ -17,9 +17,11 @@ import type { Person, ImageObject, } from 'schema-dts'; -import type {PropBlogPostContent} from '@docusaurus/plugin-content-blog'; +import type { + Author, + PropBlogPostContent, +} from '@docusaurus/plugin-content-blog'; import type {DocusaurusConfig} from '@docusaurus/types'; -import type {Author} from '@docusaurus/utils'; const convertDate = (dateMs: number) => new Date(dateMs).toISOString(); diff --git a/packages/docusaurus-utils/src/authors.ts b/packages/docusaurus-utils/src/authors.ts deleted file mode 100644 index a76d33e5bc93..000000000000 --- a/packages/docusaurus-utils/src/authors.ts +++ /dev/null @@ -1,178 +0,0 @@ -/** - * 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 _ from 'lodash'; -import {normalizeUrl} from './urlUtils'; -import type {Optional} from 'utility-types'; - -export type Author = Optional & { - generateAuthorPage?: boolean; - /** - * Unknown keys are allowed, so that we can pass custom fields to authors, - * e.g., `twitter`. - */ - [key: string]: unknown; -}; - -export type PageAuthor = { - /** - * If `name` doesn't exist, an `imageURL` is expected. - */ - name: string; - /** - * The image path could be collocated, in which case - * `metadata.assets.authorsImageUrls` should be used instead. If `imageURL` - * doesn't exist, a `name` is expected. - */ - imageURL: string; - /** Permalink to this author's page, without the `/authors/` base path. */ - permalink: string; - /** - * Used as a subtitle for the author, e.g. "maintainer of Docusaurus" - */ - title: string | undefined; - /** - * Mainly used for RSS feeds; if `url` doesn't exist, `email` can be used - * to generate a fallback `mailto:` URL. - */ - email: string | undefined; - /** - * Used to generate the author's link. - */ - url: string | undefined; - - key: string | undefined; - description: string | undefined; -}; - -/** What the authors list page should know about each author. */ -export type AuthorsListItem = PageAuthor & { - /** Number of posts/docs with this author. */ - count: number; -}; - -/** What the author's own page should know about the author. */ -export type AuthorModule = AuthorsListItem & { - /** The authors list page's permalink. */ - allAuthorsPath: string; - /** Is this author unlisted? (when it only contains unlisted items) */ - unlisted: boolean; -}; - -function normalizePageAuthor({ - authorsBaseRoutePath, - author, -}: { - authorsBaseRoutePath: string; - author: Author; -}): PageAuthor { - const key = author.key as string; - const name = author.name || key; - const permalink = author.permalink || _.kebabCase(key); - - return { - imageURL: author.imageURL ?? '', - url: author.url, - title: author.title, - email: author.email, - description: author.description, - name, - key, - permalink: normalizeUrl([authorsBaseRoutePath, permalink]), - }; -} - -export function normalizePageAuthors({ - authorsBaseRoutePath, - authors, -}: { - authorsBaseRoutePath: string; - authors: Author[] | undefined; -}): PageAuthor[] { - return (authors ?? []) - .filter((author) => author.generateAuthorPage) - .map((author) => normalizePageAuthor({authorsBaseRoutePath, author})); -} - -type AuthoredItemGroup = { - author: PageAuthor; - items: Item[]; -}; - -/** - * Permits to group docs/blog posts by author (provided by front matter). - * - * @returns a map from author permalink to the items and other relevant author - * data. - * The record is indexed by permalink, because routes must be unique in the end. - * Labels may vary on 2 MD files but they are normalized. Docs with - * label='some label' and label='some-label' should end up in the same page. - */ -export function groupAuthoredItems( - items: readonly Item[], - /** - * A callback telling me how to get the authors list of the current item. - * Usually simply getting it from some metadata of the current item. - */ - getItemPageAuthors: (item: Item) => readonly PageAuthor[], -): {[permalink: string]: AuthoredItemGroup} { - const result: {[permalink: string]: AuthoredItemGroup} = {}; - - items.forEach((item) => { - getItemPageAuthors(item).forEach((author) => { - // Init missing author groups - // TODO: it's not really clear what should be the behavior if 2 - // authors have the same permalink but the label is different for each - // For now, the first author found wins - result[author.permalink] ??= { - author, - items: [], - }; - - // Add item to group - result[author.permalink]!.items.push(item); - }); - }); - - // If user add twice the same author to a md doc (weird but possible), - // we don't want the item to appear twice in the list... - // TODO wait for #10224 and remove below code - Object.values(result).forEach((group) => { - group.items = _.uniq(group.items); - }); - - return result; -} - -/** - * Permits to get the "author visibility" (hard to find a better name) - * IE, is this author listed or unlisted - * And which items should be listed when this author is browsed - */ -export function getAuthorVisibility({ - items, - isUnlisted, -}: { - items: Item[]; - isUnlisted: (item: Item) => boolean; -}): { - unlisted: boolean; - listedItems: Item[]; -} { - const allItemsUnlisted = items.every(isUnlisted); - // When a author is full of unlisted items, we display all the items - // when author is browsed, but we mark the author as unlisted - if (allItemsUnlisted) { - return {unlisted: true, listedItems: items}; - } - // When a author has some listed items, the author remains listed - // but we filter its unlisted items - return { - unlisted: false, - listedItems: items.filter((item) => !isUnlisted(item)), - }; -} diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 2660e5e6a874..7201f8eec1a8 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -129,13 +129,3 @@ export { } from './lastUpdateUtils'; export {normalizeTags, reportInlineTags} from './tags'; - -export { - type PageAuthor, - type AuthorsListItem, - type AuthorModule, - type Author, - getAuthorVisibility, - groupAuthoredItems, - normalizePageAuthors, -} from './authors'; From da3799180678229fdbc0f86e544ef41169d37252 Mon Sep 17 00:00:00 2001 From: sebastien Date: Mon, 24 Jun 2024 13:38:38 +0200 Subject: [PATCH 028/115] Refactor blog author pages code and types --- .../src/authors.ts | 289 ++++-------------- .../src/authorsMap.ts | 170 +++++++++++ .../src/blogUtils.ts | 25 +- .../src/index.ts | 2 + .../src/plugin-content-blog.d.ts | 74 +++-- .../src/theme/BlogAuthorPage/Author/index.tsx | 3 +- .../AuthorsListByLetter/index.tsx | 2 +- .../BlogPostItem/Header/Author/index.tsx | 12 +- website/blog/authors.yml | 12 +- 9 files changed, 290 insertions(+), 299 deletions(-) create mode 100644 packages/docusaurus-plugin-content-blog/src/authorsMap.ts diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 09aa53eb967c..9f010010b227 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -6,72 +6,16 @@ */ import _ from 'lodash'; -import {getDataFileData, normalizeUrl} from '@docusaurus/utils'; -import {Joi, URISchema} from '@docusaurus/utils-validation'; +import {normalizeUrl} from '@docusaurus/utils'; import {paginateBlogPosts} from './blogUtils'; -import type {BlogContentPaths} from './types'; import type { Author, BlogPageAuthors, BlogPost, BlogPostFrontMatter, BlogPostFrontMatterAuthor, - BlogPostFrontMatterAuthors, - PageAuthor, } from '@docusaurus/plugin-content-blog'; - -export type AuthorsMap = {[authorKey: string]: Author}; - -const AuthorsMapSchema = Joi.object() - .pattern( - Joi.string(), - Joi.object({ - name: Joi.string(), - url: URISchema, - imageURL: URISchema, - title: Joi.string(), - email: Joi.string(), - generateAuthorPage: Joi.bool(), - permalink: Joi.string(), - description: Joi.string(), - }) - .rename('image_url', 'imageURL') - .or('name', 'imageURL') - .unknown() - .required() - .messages({ - 'object.base': - '{#label} should be an author object containing properties like name, title, and imageURL.', - 'any.required': - '{#label} cannot be undefined. It should be an author object containing properties like name, title, and imageURL.', - }), - ) - .messages({ - 'object.base': - "The authors map file should contain an object where each entry contains an author key and the corresponding author's data.", - }); - -export function validateAuthorsMap(content: unknown): AuthorsMap { - const {error, value} = AuthorsMapSchema.validate(content); - if (error) { - throw error; - } - return value; -} - -export async function getAuthorsMap(params: { - authorsMapPath: string; - contentPaths: BlogContentPaths; -}): Promise { - return getDataFileData( - { - filePath: params.authorsMapPath, - contentPaths: params.contentPaths, - fileType: 'authors map', - }, - validateAuthorsMap, - ); -} +import type {AuthorsMap} from './authorsMap'; type AuthorsParam = { frontMatter: BlogPostFrontMatter; @@ -93,6 +37,7 @@ function normalizeImageUrl({ // Legacy v1/early-v2 front matter fields // We may want to deprecate those in favor of using only frontMatter.authors +// TODO Docusaurus v4: remove this legacy front matter function getFrontMatterAuthorLegacy({ baseUrl, frontMatter, @@ -114,37 +59,40 @@ function getFrontMatterAuthorLegacy({ title, url, imageURL, + // legacy front matter authors do not have an author key/page + key: null, + page: null, }; } return undefined; } -function normalizeFrontMatterAuthors( - frontMatterAuthors: BlogPostFrontMatterAuthors = [], -): BlogPostFrontMatterAuthor[] { - function normalizeAuthor( - authorInput: string | Author, - ): BlogPostFrontMatterAuthor { - if (typeof authorInput === 'string') { - // Technically, we could allow users to provide an author's name here, but - // we only support keys, otherwise, a typo in a key would fallback to - // becoming a name and may end up unnoticed - return {key: authorInput}; +function getFrontMatterAuthors(params: AuthorsParam): Author[] { + const {authorsMap, frontMatter, baseUrl} = params; + return normalizeFrontMatterAuthors().map(toAuthor); + + function normalizeFrontMatterAuthors(): BlogPostFrontMatterAuthor[] { + if (frontMatter.authors === undefined) { + return []; } - return authorInput; - } - return Array.isArray(frontMatterAuthors) - ? frontMatterAuthors.map(normalizeAuthor) - : [normalizeAuthor(frontMatterAuthors)]; -} + function normalizeAuthor( + authorInput: string | BlogPostFrontMatterAuthor, + ): BlogPostFrontMatterAuthor { + if (typeof authorInput === 'string') { + // We could allow users to provide an author's name here, but we only + // support keys, otherwise, a typo in a key would fall back to + // becoming a name and may end up unnoticed + return {key: authorInput}; + } + return authorInput; + } -function getFrontMatterAuthors(params: AuthorsParam): Author[] { - const {authorsMap, baseUrl} = params; - const frontMatterAuthors = normalizeFrontMatterAuthors( - params.frontMatter.authors, - ); + return Array.isArray(frontMatter.authors) + ? frontMatter.authors.map(normalizeAuthor) + : [normalizeAuthor(frontMatter.authors)]; + } function getAuthorsMapAuthor(key: string | undefined): Author | undefined { if (key) { @@ -174,11 +122,11 @@ ${Object.keys(authorsMap) return { ...author, + key: author.key ?? null, + page: author.page ?? null, imageURL: normalizeImageUrl({imageURL: author.imageURL, baseUrl}), }; } - - return frontMatterAuthors.map(toAuthor); } export function getBlogPostAuthors(params: AuthorsParam): Author[] { @@ -200,111 +148,36 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t return authors; } -export function checkPermalinkCollisions( - authorsMap: AuthorsMap | undefined, -): void { - const pageAuthorsMap = _.pickBy( - authorsMap, - (author) => author.generateAuthorPage === true, - ); - - const permalinkCounts: {[key: string]: string[]} = {}; - - for (const [key, author] of Object.entries(pageAuthorsMap)) { - const permalink = normalizeUrl(['/', author.permalink || key, '/']); - if (!permalinkCounts[permalink]) { - permalinkCounts[permalink] = []; - } - permalinkCounts[permalink]?.push(author.name || key); - } - - const collisions = Object.entries(permalinkCounts).filter( - ([, authors]) => authors.length > 1, - ); - - if (collisions.length > 0) { - let errorMessage = 'The following permalinks are duplicated:\n'; - - collisions.forEach(([permalink, authors]) => { - errorMessage += `Permalink: ${permalink}\nAuthors: ${authors.join(', ')}`; - }); - - throw new Error(errorMessage); - } -} - -function normalizePageAuthor({ - authorsBaseRoutePath, - author, -}: { - authorsBaseRoutePath: string; +type AuthoredItemGroup = { author: Author; -}): PageAuthor { - const key = author.key as string; - const name = author.name || key; - const permalink = author.permalink || _.kebabCase(key); - - return { - imageURL: author.imageURL ?? '', - url: author.url, - title: author.title, - email: author.email, - description: author.description, - name, - key, - permalink: normalizeUrl([authorsBaseRoutePath, permalink]), - }; -} - -export function normalizePageAuthors({ - authorsBaseRoutePath, - authors, -}: { - authorsBaseRoutePath: string; - authors: Author[] | undefined; -}): PageAuthor[] { - return (authors ?? []) - .filter((author) => author.generateAuthorPage) - .map((author) => normalizePageAuthor({authorsBaseRoutePath, author})); -} - -type AuthoredItemGroup = { - author: PageAuthor; - items: Item[]; + items: BlogPost[]; }; /** - * Permits to group docs/blog posts by author (provided by front matter). - * - * @returns a map from author permalink to the items and other relevant author - * data. - * The record is indexed by permalink, because routes must be unique in the end. - * Labels may vary on 2 MD files but they are normalized. Docs with - * label='some label' and label='some-label' should end up in the same page. + * Blog posts are grouped by permalink + * @param blogPosts */ -export function groupAuthoredItems( - items: readonly Item[], - /** - * A callback telling me how to get the authors list of the current item. - * Usually simply getting it from some metadata of the current item. - */ - getItemPageAuthors: (item: Item) => readonly PageAuthor[], -): {[permalink: string]: AuthoredItemGroup} { - const result: {[permalink: string]: AuthoredItemGroup} = {}; - - items.forEach((item) => { - getItemPageAuthors(item).forEach((author) => { - // Init missing author groups - // TODO: it's not really clear what should be the behavior if 2 - // authors have the same permalink but the label is different for each - // For now, the first author found wins - result[author.permalink] ??= { - author, - items: [], - }; - - // Add item to group - result[author.permalink]!.items.push(item); +// TODO would be better if iterated over the AuthorsMap instead of the blogPosts +export function groupBlogPostsByPermalink(blogPosts: readonly BlogPost[]): { + [permalink: string]: AuthoredItemGroup; +} { + const result: {[permalink: string]: AuthoredItemGroup} = {}; + + blogPosts.forEach((item) => { + item.metadata.authors.forEach((author) => { + if (author.page) { + // Init missing author groups + // TODO: it's not really clear what should be the behavior if 2 + // authors have the same permalink but the label is different for each + // For now, the first author found wins + result[author.page.permalink] ??= { + author, + items: [], + }; + + // Add item to group + result[author.page.permalink]!.items.push(item); + } }); }); @@ -318,35 +191,6 @@ export function groupAuthoredItems( return result; } -/** - * Permits to get the "author visibility" (hard to find a better name) - * IE, is this author listed or unlisted - * And which items should be listed when this author is browsed - */ -export function getAuthorVisibility({ - items, - isUnlisted, -}: { - items: Item[]; - isUnlisted: (item: Item) => boolean; -}): { - unlisted: boolean; - listedItems: Item[]; -} { - const allItemsUnlisted = items.every(isUnlisted); - // When a author is full of unlisted items, we display all the items - // when author is browsed, but we mark the author as unlisted - if (allItemsUnlisted) { - return {unlisted: true, listedItems: items}; - } - // When a author has some listed items, the author remains listed - // but we filter its unlisted items - return { - unlisted: false, - listedItems: items.filter((item) => !isUnlisted(item)), - }; -} - export function getBlogPageAuthors({ blogPosts, ...params @@ -357,27 +201,22 @@ export function getBlogPageAuthors({ postsPerPageOption: number | 'ALL'; pageBasePath: string; }): BlogPageAuthors { - const getPostPageAuthors = (blogPost: BlogPost) => - blogPost.metadata.pageAuthors; - - const groups = groupAuthoredItems(blogPosts, getPostPageAuthors); + const groups = groupBlogPostsByPermalink(blogPosts); - return _.mapValues(groups, ({author, items: authorBlogPosts}) => { - const authorVisibility = getAuthorVisibility({ - items: authorBlogPosts, - isUnlisted: (item: BlogPost) => item.metadata.unlisted, - }); + return _.mapValues(groups, ({author, items}) => { + const authorBlogPosts = items.filter( + (blogPost) => !blogPost.metadata.unlisted, + ); return { ...author, - items: authorVisibility.listedItems.map((item: BlogPost) => item.id), - pages: author.permalink + items: authorBlogPosts.map((blogPost) => blogPost.id), + pages: author.page ? paginateBlogPosts({ - blogPosts: authorVisibility.listedItems, - basePageUrl: author.permalink, + blogPosts: authorBlogPosts, + basePageUrl: author.page.permalink, ...params, }) : [], - unlisted: authorVisibility.unlisted, }; }); } diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts new file mode 100644 index 000000000000..7698fddf97b5 --- /dev/null +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -0,0 +1,170 @@ +/** + * 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 _ from 'lodash'; +import {getDataFileData, normalizeUrl} from '@docusaurus/utils'; +import {Joi, URISchema} from '@docusaurus/utils-validation'; +import type {BlogContentPaths} from './types'; +import type { + Author, + AuthorAttributes, + AuthorPage, +} from '@docusaurus/plugin-content-blog'; + +export type AuthorsMap = {[authorKey: string]: Author}; + +type AuthorInput = AuthorAttributes & { + page: boolean | AuthorPage; +}; + +type AuthorsMapInput = {[authorKey: string]: AuthorInput}; + +const AuthorPageSchema = Joi.object({ + permalink: Joi.string().required(), +}); + +const AuthorsMapInputSchema = Joi.object() + .pattern( + Joi.string(), + Joi.object({ + name: Joi.string(), + url: URISchema, + imageURL: URISchema, + title: Joi.string(), + email: Joi.string(), + page: Joi.alternatives(Joi.bool(), AuthorPageSchema), + permalink: Joi.string(), + description: Joi.string(), + }) + .rename('image_url', 'imageURL') + .or('name', 'imageURL') + .unknown() + .required() + .messages({ + 'object.base': + '{#label} should be an author object containing properties like name, title, and imageURL.', + 'any.required': + '{#label} cannot be undefined. It should be an author object containing properties like name, title, and imageURL.', + }), + ) + .messages({ + 'object.base': + "The authors map file should contain an object where each entry contains an author key and the corresponding author's data.", + }); + +export function checkPermalinkCollisions( + authorsMap: AuthorsMap | undefined, +): void { + const pageAuthorsMap = _.pickBy(authorsMap, (author) => !!author.page); + + const permalinkCounts: {[key: string]: string[]} = {}; + for (const [key, author] of Object.entries(pageAuthorsMap)) { + if (author.page) { + const {permalink} = author.page; + if (!permalinkCounts[permalink]) { + permalinkCounts[permalink] = []; + } + permalinkCounts[permalink]?.push(author.name || key); + } + } + + const collisions = Object.entries(permalinkCounts).filter( + ([, authors]) => authors.length > 1, + ); + + if (collisions.length > 0) { + let errorMessage = 'The following permalinks are duplicated:\n'; + + collisions.forEach(([permalink, authors]) => { + errorMessage += `Permalink: ${permalink}\nAuthors: ${authors.join(', ')}`; + }); + + throw new Error(errorMessage); + } +} + +function normalizeAuthor({ + authorsBaseRoutePath, + authorKey, + author, +}: { + authorsBaseRoutePath: string; + authorKey: string; + author: AuthorInput; +}): Author { + const name = author.name || authorKey; + + function getAuthorPage(): AuthorPage | null { + if (!author.page) { + return null; + } + const slug = + author.page === true ? _.kebabCase(authorKey) : author.page.permalink; + return { + permalink: normalizeUrl([authorsBaseRoutePath, slug]), + }; + } + + return { + key: authorKey, + page: getAuthorPage(), + name, + imageURL: author.imageURL, + url: author.url, + title: author.title, + email: author.email, + description: author.description, + }; +} + +function normalizeAuthorsMap({ + authorsBaseRoutePath, + authorsMapInput, +}: { + authorsBaseRoutePath: string; + authorsMapInput: AuthorsMapInput; +}): AuthorsMap { + return _.mapValues(authorsMapInput, (author, authorKey) => { + return normalizeAuthor({authorsBaseRoutePath, authorKey, author}); + }); +} + +function validateAuthorsMapInput(content: unknown): AuthorsMapInput { + const {error, value} = AuthorsMapInputSchema.validate(content); + if (error) { + throw error; + } + return value; +} + +async function getAuthorsMapInput(params: { + authorsMapPath: string; + contentPaths: BlogContentPaths; +}): Promise { + return getDataFileData( + { + filePath: params.authorsMapPath, + contentPaths: params.contentPaths, + fileType: 'authors map', + }, + validateAuthorsMapInput, + ); +} + +export async function getAuthorsMap(params: { + authorsMapPath: string; + authorsBaseRoutePath: string; + contentPaths: BlogContentPaths; +}): Promise { + const authorsMapInput = await getAuthorsMapInput(params); + if (!authorsMapInput) { + return undefined; + } + const authorsMap = normalizeAuthorsMap({authorsMapInput, ...params}); + checkPermalinkCollisions(authorsMap); + return authorsMap; +} diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index ea4b5be18ce4..45aded84c13a 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -29,13 +29,8 @@ import { } from '@docusaurus/utils'; import {getTagsFile} from '@docusaurus/utils-validation'; import {validateBlogPostFrontMatter} from './frontMatter'; -import { - type AuthorsMap, - getAuthorsMap, - getBlogPostAuthors, - checkPermalinkCollisions, - normalizePageAuthors, -} from './authors'; +import {getBlogPostAuthors} from './authors'; +import {getAuthorsMap, type AuthorsMap} from './authorsMap'; import type {TagsFile} from '@docusaurus/utils'; import type {LoadContext, ParseFrontMatter} from '@docusaurus/types'; import type { @@ -333,14 +328,6 @@ async function processBlogSourceFile( }); const authors = getBlogPostAuthors({authorsMap, frontMatter, baseUrl}); - const authorsBaseRoutePath = options.authorsPageBasePath - ? normalizeUrl([baseUrl, routeBasePath, options.authorsPageBasePath]) - : ''; - - const pageAuthors = normalizePageAuthors({ - authorsBaseRoutePath, - authors, - }); return { id: slug, @@ -365,7 +352,6 @@ async function processBlogSourceFile( unlisted, lastUpdatedAt: lastUpdate.lastUpdatedAt, lastUpdatedBy: lastUpdate.lastUpdatedBy, - pageAuthors, }, content, }; @@ -390,10 +376,13 @@ export async function generateBlogPosts( const authorsMap = await getAuthorsMap({ contentPaths, authorsMapPath: options.authorsMapPath, + authorsBaseRoutePath: normalizeUrl([ + context.baseUrl, + options.routeBasePath, + options.authorsPageBasePath, + ]), }); - checkPermalinkCollisions(authorsMap); - const tagsFile = await getTagsFile({contentPaths, tags: options.tags}); async function doProcessBlogSourceFile(blogSourceFile: string) { diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 64fe57872a93..b0f38ebc9d55 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -229,6 +229,8 @@ export default async function pluginContentBlog( pageBasePath, }); + // TODO: put "AuthorsMap" in Content + // Move blog post author pages aggregation logic in contentLoaded() const blogPageAuthors: BlogPageAuthors = getBlogPageAuthors({ blogPosts, postsPerPageOption, diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index a185e7d80cfd..0e0fe23eacb1 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -18,17 +18,11 @@ declare module '@docusaurus/plugin-content-blog' { } from '@docusaurus/utils'; import type {DocusaurusConfig, Plugin, LoadContext} from '@docusaurus/types'; import type {Item as FeedItem} from 'feed'; - import type {Optional, Overwrite} from 'utility-types'; + import type {Overwrite} from 'utility-types'; export type Assets = { /** - * If `metadata.yarn workspace website typecheck -4 -yarn workspace v1.22.19yarn workspace website typecheck -4 -yarn workspace v1.22.19yarn workspace website typecheck -4 -yarn workspace v1.22.19image` is a collocated image path, this entry will be the + * If `metadata.image` is a collocated image path, this entry will be the * bundler-generated image path. Otherwise, it's empty, and the image URL * should be accessed through `frontMatter.image`. */ @@ -43,48 +37,59 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the authorsImageUrls: (string | undefined)[]; }; - export type Author = Optional & { - generateAuthorPage?: boolean; - /** - * Unknown keys are allowed, so that we can pass custom fields to authors, - * e.g., `twitter`. - */ - [key: string]: unknown; - }; - - export type PageAuthor = { + export type AuthorAttributes = { /** * If `name` doesn't exist, an `imageURL` is expected. */ - name: string; + name?: string; /** * The image path could be collocated, in which case * `metadata.assets.authorsImageUrls` should be used instead. If `imageURL` * doesn't exist, a `name` is expected. */ - imageURL: string; - /** Permalink to this author's page, without the `/authors/` base path. */ - permalink: string; + imageURL?: string; + /** + * Used to generate the author's link. + */ + url?: string; /** * Used as a subtitle for the author, e.g. "maintainer of Docusaurus" */ - title: string | undefined; + title?: string; /** * Mainly used for RSS feeds; if `url` doesn't exist, `email` can be used * to generate a fallback `mailto:` URL. */ - email: string | undefined; + email?: string; /** - * Used to generate the author's link. + * Unknown keys are allowed, so that we can pass custom fields to authors. */ - url: string | undefined; + [attribute: string]: unknown; + }; + + /** + * Metadata of the author's page, if it exists. + */ + export type AuthorPage = {permalink: string}; - key: string | undefined; - description: string | undefined; + /** + * Normalized author metadata. + */ + export type Author = AuthorAttributes & { + /** + * Author key, if the author was loaded from the authors map. + * `null` means the author was declared inline. + */ + key: string | null; + /** + * Metadata of the author's page. + * `null` means the author doesn't have a dedicated author page. + */ + page: AuthorPage | null; }; /** What the authors list page should know about each author. */ - export type AuthorsListItem = PageAuthor & { + export type AuthorsListItem = Author & { /** Number of posts/docs with this author. */ count: number; }; @@ -93,8 +98,6 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the export type AuthorModule = AuthorsListItem & { /** The authors list page's permalink. */ allAuthorsPath: string; - /** Is this author unlisted? (when it only contains unlisted items) */ - unlisted: boolean; }; /** @@ -188,7 +191,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the last_update?: FrontMatterLastUpdate; }; - export type BlogPostFrontMatterAuthor = Author & { + export type BlogPostFrontMatterAuthor = AuthorAttributes & { /** * Will be normalized into the `imageURL` prop. */ @@ -260,10 +263,6 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the readonly frontMatter: BlogPostFrontMatter & {[key: string]: unknown}; /** Tags, normalized. */ readonly tags: TagMetadata[]; - /** - * Page authors, for use in the Authors grouping page. - */ - readonly pageAuthors: PageAuthor[]; /** * Marks the post as unlisted and visibly hides it unless directly accessed. */ @@ -541,11 +540,10 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the [permalink: string]: BlogPageAuthor; }; - export type BlogPageAuthor = PageAuthor & { + export type BlogPageAuthor = Author & { /** Blog post permalinks. */ items: string[]; pages: BlogPaginated[]; - unlisted: boolean; }; export type BlogPost = { diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx index e4fd63b85d18..22eabdb53fe4 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx @@ -20,12 +20,13 @@ function MaybeLink(props: LinkProps): JSX.Element { } export default function Author({ - permalink, + page, name, count, title, imageURL, }: Props): JSX.Element { + const permalink = page?.permalink; return (
    {imageURL && ( diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx index d44dd7bf49fb..7233e7e63720 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx @@ -27,7 +27,7 @@ function AuthorLetterEntryItem({
      {letterEntry.authors.map((author) => ( -
    • +
    • ))} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx index 9164c98ecb0c..ff2cc667f15e 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx @@ -8,7 +8,6 @@ import React from 'react'; import clsx from 'clsx'; import Link, {type Props as LinkProps} from '@docusaurus/Link'; -import {useBlogPost} from '@docusaurus/theme-common/internal'; import type {Props} from '@theme/BlogPostItem/Header/Author'; function MaybeLink(props: LinkProps): JSX.Element { @@ -22,17 +21,10 @@ export default function BlogPostItemHeaderAuthor({ author, className, }: Props): JSX.Element { - const {name, key, title, url, imageURL, email} = author; - const { - metadata: {pageAuthors}, - } = useBlogPost(); - - const authorPermalink = pageAuthors.find( - (pageAuthor) => pageAuthor.name === name || pageAuthor.key === key, - )?.permalink; + const {name, title, url, imageURL, email, page} = author; const link = - authorPermalink || url || (email && `mailto:${email}`) || undefined; + page?.permalink || url || (email && `mailto:${email}`) || undefined; return (
      diff --git a/website/blog/authors.yml b/website/blog/authors.yml index 53cf0f0eed32..15aa38761a92 100644 --- a/website/blog/authors.yml +++ b/website/blog/authors.yml @@ -5,7 +5,7 @@ JMarcey: image_url: https://github.com/JoelMarcey.png email: jimarcey@gmail.com twitter: JoelMarcey - generateAuthorPage: true + page: true zpao: name: Paul O’Shannessy @@ -14,7 +14,7 @@ zpao: image_url: https://github.com/zpao.png email: jimarcey@gmail.com twitter: zpao - generateAuthorPage: true + page: true slorber: name: Sébastien Lorber @@ -23,7 +23,7 @@ slorber: image_url: https://github.com/slorber.png twitter: sebastienlorber email: sebastien@thisweekinreact.com - generateAuthorPage: true + page: true description: > A freelance React and React-Native developer near Paris and Docusaurus maintainer. Also runs ThisWeekInReact.com, a newsletter to stay updated with the React ecosystem. @@ -35,7 +35,7 @@ yangshun: image_url: https://github.com/yangshun.png twitter: yangshunz email: tay.yang.shun@gmail.com - generateAuthorPage: true + page: true lex111: name: Alexey Pyltsyn @@ -43,7 +43,7 @@ lex111: url: https://github.com/lex111 image_url: https://github.com/lex111.png email: lex@php.net - generateAuthorPage: true + page: true Josh-Cena: name: Joshua Chen @@ -51,4 +51,4 @@ Josh-Cena: url: https://joshcena.com/ image_url: https://github.com/josh-cena.png email: sidachen2003@gmail.com - generateAuthorPage: true + page: true From d6df47c063d8bbe976c8db86c7c89ca1c6bddfcf Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:31:17 +0200 Subject: [PATCH 029/115] move useBlogPostsPlural & listByLetters --- .../AuthorsListByLetter/index.tsx | 6 +- .../BlogAuthorsPostsPage/index.tsx | 20 +----- .../src/theme/BlogTagsPostsPage/index.tsx | 20 +----- .../src/theme/TagsListByLetter/index.tsx | 3 +- packages/docusaurus-theme-common/src/index.ts | 9 +-- .../docusaurus-theme-common/src/internal.ts | 6 +- .../src/utils/authorsUtils.ts | 34 --------- .../src/utils/generalUtils.ts | 71 +++++++++++++++++++ .../src/utils/tagsUtils.ts | 32 --------- .../src/utils/usePluralForm.ts | 19 +++++ 10 files changed, 103 insertions(+), 117 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx index 7233e7e63720..cb6a63f17725 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx @@ -6,10 +6,8 @@ */ import React from 'react'; -import { - listAuthorsByLetters, - type AuthorLetterEntry, -} from '@docusaurus/theme-common'; +import {type AuthorLetterEntry} from '@docusaurus/theme-common'; +import {listAuthorsByLetters} from '@docusaurus/theme-common/internal'; import Author from '@theme/BlogAuthorPage/Author'; import type {Props} from '@theme/BlogAuthorPage/AuthorsListByLetter'; import Heading from '@theme/Heading'; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx index 6da9b54bc8e4..35da41f1994f 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx @@ -12,7 +12,7 @@ import { PageMetadata, HtmlClassNameProvider, ThemeClassNames, - usePluralForm, + useBlogPostsPlural, } from '@docusaurus/theme-common'; import Link from '@docusaurus/Link'; import BlogLayout from '@theme/BlogLayout'; @@ -23,24 +23,6 @@ import BlogPostItems from '@theme/BlogPostItems'; import Unlisted from '@theme/Unlisted'; import Heading from '@theme/Heading'; -// Very simple pluralization: probably good enough for now -function useBlogPostsPlural() { - const {selectMessage} = usePluralForm(); - return (count: number) => - selectMessage( - count, - translate( - { - id: 'theme.blog.post.plurals', - description: - 'Pluralized label for "{count} posts". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)', - message: 'One post|{count} posts', - }, - {count}, - ), - ); -} - function useBlogAuthorsPostsPageTitle(author: Props['author']): string { const blogPostsPlural = useBlogPostsPlural(); return translate( diff --git a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx index 45f71008db29..a3cc01eea8fb 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx @@ -12,7 +12,7 @@ import { PageMetadata, HtmlClassNameProvider, ThemeClassNames, - usePluralForm, + useBlogPostsPlural, } from '@docusaurus/theme-common'; import Link from '@docusaurus/Link'; import BlogLayout from '@theme/BlogLayout'; @@ -23,24 +23,6 @@ import BlogPostItems from '@theme/BlogPostItems'; import Unlisted from '@theme/Unlisted'; import Heading from '@theme/Heading'; -// Very simple pluralization: probably good enough for now -function useBlogPostsPlural() { - const {selectMessage} = usePluralForm(); - return (count: number) => - selectMessage( - count, - translate( - { - id: 'theme.blog.post.plurals', - description: - 'Pluralized label for "{count} posts". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)', - message: 'One post|{count} posts', - }, - {count}, - ), - ); -} - function useBlogTagsPostsPageTitle(tag: Props['tag']): string { const blogPostsPlural = useBlogPostsPlural(); return translate( diff --git a/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx b/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx index ffd28c1585b8..9f630d3a6199 100644 --- a/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx @@ -6,7 +6,8 @@ */ import React from 'react'; -import {listTagsByLetters, type TagLetterEntry} from '@docusaurus/theme-common'; +import {type TagLetterEntry} from '@docusaurus/theme-common'; +import {listTagsByLetters} from '@docusaurus/theme-common/internal'; import Tag from '@theme/Tag'; import type {Props} from '@theme/TagsListByLetter'; import Heading from '@theme/Heading'; diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 85e47c9c1530..4d9b315da8b8 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -44,7 +44,7 @@ export { useBlogPostStructuredData, } from './utils/structuredDataUtils'; -export {usePluralForm} from './utils/usePluralForm'; +export {usePluralForm, useBlogPostsPlural} from './utils/usePluralForm'; export {useCollapsible, Collapsible} from './components/Collapsible'; @@ -75,15 +75,10 @@ export {useWindowSize} from './hooks/useWindowSize'; * Note: we still guarantee retro-compatibility on those */ -export { - translateTagsPageTitle, - listTagsByLetters, - type TagLetterEntry, -} from './utils/tagsUtils'; +export {translateTagsPageTitle, type TagLetterEntry} from './utils/tagsUtils'; export { type AuthorLetterEntry, - listAuthorsByLetters, translateAuthorsPageTitle, } from './utils/authorsUtils'; diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts index 31f081a62118..705cf8349f46 100644 --- a/packages/docusaurus-theme-common/src/internal.ts +++ b/packages/docusaurus-theme-common/src/internal.ts @@ -76,7 +76,11 @@ export { useDocRootMetadata, } from './utils/docsUtils'; -export {useTitleFormatter} from './utils/generalUtils'; +export { + useTitleFormatter, + listTagsByLetters, + listAuthorsByLetters, +} from './utils/generalUtils'; export {useLocationChange} from './utils/useLocationChange'; diff --git a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts index 2445b6b96f72..268310835971 100644 --- a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts @@ -19,37 +19,3 @@ export type AuthorLetterEntry = { letter: string; authors: AuthorsListItem[]; }; - -function getAuthorLetter(author: string): string { - return author[0]!.toUpperCase(); -} - -/** - * Takes a list of authors (as provided by the content plugins), and groups - * them by their initials. - */ -export function listAuthorsByLetters( - authors: readonly AuthorsListItem[], -): AuthorLetterEntry[] { - const groups: {[initial: string]: AuthorsListItem[]} = {}; - Object.values(authors).forEach((author) => { - if (author.name) { - const initial = getAuthorLetter(author.name); - groups[initial] ??= []; - groups[initial]!.push(author); - } - }); - - return ( - Object.entries(groups) - // Sort letters - .sort(([letter1], [letter2]) => letter1.localeCompare(letter2)) - .map(([letter, letterAuthors]) => { - // Sort authors inside a letter - const sortedAuthors = letterAuthors.sort((author1, author2) => - (author1.name as string).localeCompare(author2.name as string), - ); - return {letter, authors: sortedAuthors}; - }) - ); -} diff --git a/packages/docusaurus-theme-common/src/utils/generalUtils.ts b/packages/docusaurus-theme-common/src/utils/generalUtils.ts index c6732b82ccf6..e06774d8c964 100644 --- a/packages/docusaurus-theme-common/src/utils/generalUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/generalUtils.ts @@ -6,6 +6,10 @@ */ import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import type {TagLetterEntry} from './tagsUtils'; +import type {AuthorsListItem} from '@docusaurus/plugin-content-blog'; +import type {TagsListItem} from '@docusaurus/utils'; +import type {AuthorLetterEntry} from './authorsUtils'; /** * Formats the page's title based on relevant site config and other contexts. @@ -17,3 +21,70 @@ export function useTitleFormatter(title?: string | undefined): string { ? `${title.trim()} ${titleDelimiter} ${siteTitle}` : siteTitle; } +interface HasLabel { + label: string; +} + +interface HasName { + name?: string; + imageURL?: string; +} + +type Entry = HasLabel | HasName; + +/** + * Takes a list of tags or author (as provided by the content plugins), + * and groups them by their initials. + */ +function listByLetters( + items: readonly T[], + getLetter: (item: T) => string, + getLabel: (item: T) => string, + mapResult: (letter: string, items: T[]) => R, +): R[] { + const groups: {[initial: string]: T[]} = {}; + items.forEach((item) => { + const initial = getLetter(item); + groups[initial] ??= []; + groups[initial]!.push(item); + }); + + return ( + Object.entries(groups) + // Sort letters + .sort(([letter1], [letter2]) => letter1.localeCompare(letter2)) + .map(([letter, groupedItems]) => { + // Sort items inside a letter + const sortedItems = groupedItems.sort((item1, item2) => + getLabel(item1).localeCompare(getLabel(item2)), + ); + return mapResult(letter, sortedItems); + }) + ); +} + +function getItemLetter(item: string): string { + return item[0]!.toUpperCase(); +} + +export function listTagsByLetters( + tags: readonly TagsListItem[], +): TagLetterEntry[] { + return listByLetters( + tags, + (tag) => getItemLetter(tag.label), + (tag) => tag.label, + (letter, items) => ({letter, tags: items}), + ); +} + +export function listAuthorsByLetters( + authors: readonly AuthorsListItem[], +): AuthorLetterEntry[] { + return listByLetters( + authors, + (author) => getItemLetter(author.name ?? author.imageURL ?? ''), + (author) => author.name ?? author.imageURL ?? '', + (letter, items) => ({letter, authors: items}), + ); +} diff --git a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts index 23ab3d40df5d..9f710b0cd8b4 100644 --- a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts @@ -16,35 +16,3 @@ export const translateTagsPageTitle = (): string => }); export type TagLetterEntry = {letter: string; tags: TagsListItem[]}; - -function getTagLetter(tag: string): string { - return tag[0]!.toUpperCase(); -} - -/** - * Takes a list of tags (as provided by the content plugins), and groups them by - * their initials. - */ -export function listTagsByLetters( - tags: readonly TagsListItem[], -): TagLetterEntry[] { - const groups: {[initial: string]: TagsListItem[]} = {}; - Object.values(tags).forEach((tag) => { - const initial = getTagLetter(tag.label); - groups[initial] ??= []; - groups[initial]!.push(tag); - }); - - return ( - Object.entries(groups) - // Sort letters - .sort(([letter1], [letter2]) => letter1.localeCompare(letter2)) - .map(([letter, letterTags]) => { - // Sort tags inside a letter - const sortedTags = letterTags.sort((tag1, tag2) => - tag1.label.localeCompare(tag2.label), - ); - return {letter, tags: sortedTags}; - }) - ); -} diff --git a/packages/docusaurus-theme-common/src/utils/usePluralForm.ts b/packages/docusaurus-theme-common/src/utils/usePluralForm.ts index 726d406db2d7..16ab0980de0b 100644 --- a/packages/docusaurus-theme-common/src/utils/usePluralForm.ts +++ b/packages/docusaurus-theme-common/src/utils/usePluralForm.ts @@ -7,6 +7,7 @@ import {useMemo} from 'react'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import {translate} from '@docusaurus/Translate'; // We want to ensurer a stable plural form order in all cases // It is more convenient and natural to handle "small values" first @@ -129,3 +130,21 @@ export function usePluralForm(): { selectPluralMessage(pluralMessages, count, localePluralForm), }; } + +// Very simple pluralization: probably good enough for now +export function useBlogPostsPlural(): (count: number) => string { + const {selectMessage} = usePluralForm(); + return (count: number) => + selectMessage( + count, + translate( + { + id: 'theme.blog.post.plurals', + description: + 'Pluralized label for "{count} posts". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)', + message: 'One post|{count} posts', + }, + {count}, + ), + ); +} From 6258f802c19a72ce9ed2195191580e61f61f4c91 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 25 Jun 2024 19:07:48 +0200 Subject: [PATCH 030/115] move for consistency with tags components --- packages/docusaurus-theme-classic/src/theme-classic.d.ts | 6 +++--- .../src/theme/{BlogAuthorPage => }/Author/index.tsx | 2 +- .../src/theme/{BlogAuthorPage => }/Author/styles.module.css | 0 .../{BlogAuthorPage => }/AuthorsListByLetter/index.tsx | 4 ++-- .../AuthorsListByLetter/styles.module.css | 0 .../{BlogAuthorPage => }/BlogAuthorsListPage/index.tsx | 2 +- .../{BlogAuthorPage => }/BlogAuthorsPostsPage/index.tsx | 0 7 files changed, 7 insertions(+), 7 deletions(-) rename packages/docusaurus-theme-classic/src/theme/{BlogAuthorPage => }/Author/index.tsx (95%) rename packages/docusaurus-theme-classic/src/theme/{BlogAuthorPage => }/Author/styles.module.css (100%) rename packages/docusaurus-theme-classic/src/theme/{BlogAuthorPage => }/AuthorsListByLetter/index.tsx (91%) rename packages/docusaurus-theme-classic/src/theme/{BlogAuthorPage => }/AuthorsListByLetter/styles.module.css (100%) rename packages/docusaurus-theme-classic/src/theme/{BlogAuthorPage => }/BlogAuthorsListPage/index.tsx (93%) rename packages/docusaurus-theme-classic/src/theme/{BlogAuthorPage => }/BlogAuthorsPostsPage/index.tsx (100%) diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 6c34e269dc09..9d7425f18796 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -1528,7 +1528,7 @@ declare module '@theme/Tag' { export default function Tag(props: Props): JSX.Element; } -declare module '@theme/BlogAuthorPage/AuthorsListByLetter' { +declare module '@theme/AuthorsListByLetter' { import type {AuthorsListItem} from '@docusaurus/utils'; export interface Props { @@ -1537,7 +1537,7 @@ declare module '@theme/BlogAuthorPage/AuthorsListByLetter' { export default function AuthorsListByLetter(props: Props): JSX.Element; } -declare module '@theme/BlogAuthorPage/AuthorsListInline' { +declare module '@theme/AuthorsListInline' { import type {PageAuthor} from '@docusaurus/utils'; export interface Props { @@ -1546,7 +1546,7 @@ declare module '@theme/BlogAuthorPage/AuthorsListInline' { export default function AuthorsListInline(props: Props): JSX.Element; } -declare module '@theme/BlogAuthorPage/Author' { +declare module '@theme/Author' { import type {AuthorsListItem} from '@docusaurus/utils'; import type {Optional} from 'utility-types'; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/Author/index.tsx similarity index 95% rename from packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx rename to packages/docusaurus-theme-classic/src/theme/Author/index.tsx index 22eabdb53fe4..4d3db12041a2 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Author/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import clsx from 'clsx'; import Link, {type Props as LinkProps} from '@docusaurus/Link'; -import type {Props} from '@theme/BlogAuthorPage/Author'; +import type {Props} from '@theme/Author'; import styles from './styles.module.css'; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Author/styles.module.css similarity index 100% rename from packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/Author/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/Author/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx b/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx similarity index 91% rename from packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx rename to packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx index cb6a63f17725..3b468df4bd7d 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx @@ -8,8 +8,8 @@ import React from 'react'; import {type AuthorLetterEntry} from '@docusaurus/theme-common'; import {listAuthorsByLetters} from '@docusaurus/theme-common/internal'; -import Author from '@theme/BlogAuthorPage/Author'; -import type {Props} from '@theme/BlogAuthorPage/AuthorsListByLetter'; +import Author from '@theme/Author'; +import type {Props} from '@theme/AuthorsListByLetter'; import Heading from '@theme/Heading'; import styles from './styles.module.css'; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/styles.module.css b/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/styles.module.css similarity index 100% rename from packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/AuthorsListByLetter/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx similarity index 93% rename from packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsListPage/index.tsx rename to packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx index 3f67a584eb8d..152e9d1888b6 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx @@ -13,7 +13,7 @@ import { ThemeClassNames, } from '@docusaurus/theme-common'; import BlogLayout from '@theme/BlogLayout'; -import AuthorsListByLetter from '@theme/BlogAuthorPage/AuthorsListByLetter'; +import AuthorsListByLetter from '@theme/AuthorsListByLetter'; import type {Props} from '@theme/BlogAuthorsListPage'; import SearchMetadata from '@theme/SearchMetadata'; import Heading from '@theme/Heading'; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx similarity index 100% rename from packages/docusaurus-theme-classic/src/theme/BlogAuthorPage/BlogAuthorsPostsPage/index.tsx rename to packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx From cf5f2ea5da6dbcd44b823514e75e1ac7956946ea Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 25 Jun 2024 21:24:36 +0200 Subject: [PATCH 031/115] update theme path --- packages/docusaurus-plugin-content-blog/src/options.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/options.ts b/packages/docusaurus-plugin-content-blog/src/options.ts index 6f6eaa1f16b0..c4c6decf1af5 100644 --- a/packages/docusaurus-plugin-content-blog/src/options.ts +++ b/packages/docusaurus-plugin-content-blog/src/options.ts @@ -32,8 +32,8 @@ export const DEFAULT_OPTIONS: PluginOptions = { showReadingTime: true, blogTagsPostsComponent: '@theme/BlogTagsPostsPage', blogTagsListComponent: '@theme/BlogTagsListPage', - blogAuthorsPostsComponent: '@theme/BlogAuthorPage/BlogAuthorsPostsPage', - blogAuthorsListComponent: '@theme/BlogAuthorPage/BlogAuthorsListPage', + blogAuthorsPostsComponent: '@theme/BlogAuthorsPostsPage', + blogAuthorsListComponent: '@theme/BlogAuthorsListPage', blogPostComponent: '@theme/BlogPostPage', blogListComponent: '@theme/BlogListPage', blogArchiveComponent: '@theme/BlogArchivePage', From 6fcd67fd4441cb4ecd1b2e6b5593226b5348552c Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 25 Jun 2024 22:00:01 +0200 Subject: [PATCH 032/115] fix test --- .../src/__tests__/authors.test.ts | 97 +++++++++++++------ .../src/authorsMap.ts | 2 +- .../src/utils/__tests__/tagUtils.test.ts | 23 +++-- 3 files changed, 83 insertions(+), 39 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index d3e95ba2f1e6..d8714d6091d6 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -6,12 +6,9 @@ */ import path from 'path'; -import { - type AuthorsMap, - getAuthorsMap, - getBlogPostAuthors, - validateAuthorsMap, -} from '../authors'; +import {getBlogPostAuthors} from '../authors'; +import {getAuthorsMap, validateAuthorsMapInput} from '../authorsMap'; +import type {AuthorsMap} from '../authorsMap'; describe('getBlogPostAuthors', () => { it('can read no authors', () => { @@ -109,7 +106,9 @@ describe('getBlogPostAuthors', () => { frontMatter: { authors: 'slorber', }, - authorsMap: {slorber: {name: 'Sébastien Lorber'}}, + authorsMap: { + slorber: {name: 'Sébastien Lorber', key: 'slorber', page: null}, + }, baseUrl: '/', }), ).toEqual([{key: 'slorber', name: 'Sébastien Lorber'}]); @@ -122,6 +121,8 @@ describe('getBlogPostAuthors', () => { slorber: { name: 'Sébastien Lorber', imageURL: 'https://github.com/slorber.png', + key: 'slorber', + page: null, }, }, baseUrl: '/', @@ -142,6 +143,8 @@ describe('getBlogPostAuthors', () => { slorber: { name: 'Sébastien Lorber', imageURL: '/img/slorber.png', + key: 'slorber', + page: null, }, }, baseUrl: '/', @@ -162,6 +165,8 @@ describe('getBlogPostAuthors', () => { slorber: { name: 'Sébastien Lorber', imageURL: '/img/slorber.png', + key: 'slorber', + page: null, }, }, baseUrl: '/baseUrl', @@ -182,8 +187,13 @@ describe('getBlogPostAuthors', () => { authors: ['slorber', 'yangshun'], }, authorsMap: { - slorber: {name: 'Sébastien Lorber', title: 'maintainer'}, - yangshun: {name: 'Yangshun Tay'}, + slorber: { + name: 'Sébastien Lorber', + title: 'maintainer', + key: 'slorber', + page: null, + }, + yangshun: {name: 'Yangshun Tay', key: 'yangshun', page: null}, }, baseUrl: '/', }), @@ -248,8 +258,18 @@ describe('getBlogPostAuthors', () => { ], }, authorsMap: { - slorber: {name: 'Sébastien Lorber', title: 'maintainer'}, - yangshun: {name: 'Yangshun Tay', title: 'Yangshun title original'}, + slorber: { + name: 'Sébastien Lorber', + title: 'maintainer', + key: 'slorber', + page: null, + }, + yangshun: { + name: 'Yangshun Tay', + title: 'Yangshun title original', + key: 'yangshun', + page: null, + }, }, baseUrl: '/', }), @@ -304,8 +324,8 @@ describe('getBlogPostAuthors', () => { }, authorsMap: { - yangshun: {name: 'Yangshun Tay'}, - jmarcey: {name: 'Joel Marcey'}, + yangshun: {name: 'Yangshun Tay', key: 'yangshun', page: null}, + jmarcey: {name: 'Joel Marcey', key: 'jmarcey', page: null}, }, baseUrl: '/', }), @@ -325,8 +345,8 @@ describe('getBlogPostAuthors', () => { }, authorsMap: { - yangshun: {name: 'Yangshun Tay'}, - jmarcey: {name: 'Joel Marcey'}, + yangshun: {name: 'Yangshun Tay', key: 'yangshun', page: null}, + jmarcey: {name: 'Joel Marcey', key: 'jmarcey', page: null}, }, baseUrl: '/', }), @@ -346,8 +366,8 @@ describe('getBlogPostAuthors', () => { }, authorsMap: { - yangshun: {name: 'Yangshun Tay'}, - jmarcey: {name: 'Joel Marcey'}, + yangshun: {name: 'Yangshun Tay', key: 'yangshun', page: null}, + jmarcey: {name: 'Joel Marcey', key: 'jmarcey', page: null}, }, baseUrl: '/', }), @@ -380,7 +400,9 @@ describe('getBlogPostAuthors', () => { authors: [{key: 'slorber'}], author_title: 'legacy title', }, - authorsMap: {slorber: {name: 'Sébastien Lorber'}}, + authorsMap: { + slorber: {name: 'Sébastien Lorber', key: 'slorber', page: null}, + }, baseUrl: '/', }), ).toThrowErrorMatchingInlineSnapshot(` @@ -402,6 +424,7 @@ describe('getAuthorsMap', () => { getAuthorsMap({ contentPaths, authorsMapPath: 'authors.yml', + authorsBaseRoutePath: '/authors', }), ).resolves.toBeDefined(); }); @@ -411,6 +434,7 @@ describe('getAuthorsMap', () => { getAuthorsMap({ contentPaths, authorsMapPath: 'authors.json', + authorsBaseRoutePath: '/authors', }), ).resolves.toBeDefined(); }); @@ -420,12 +444,13 @@ describe('getAuthorsMap', () => { getAuthorsMap({ contentPaths, authorsMapPath: 'authors_does_not_exist.yml', + authorsBaseRoutePath: '/authors', }), ).resolves.toBeUndefined(); }); }); -describe('validateAuthorsMap', () => { +describe('validateAuthorsMapInput', () => { it('accept valid authors map', () => { const authorsMap: AuthorsMap = { slorber: { @@ -433,19 +458,25 @@ describe('validateAuthorsMap', () => { title: 'maintainer', url: 'https://sebastienlorber.com', imageURL: 'https://github.com/slorber.png', + key: 'slorber', + page: null, }, yangshun: { name: 'Yangshun Tay', imageURL: 'https://github.com/yangshun.png', randomField: 42, + key: 'yangshun', + page: null, }, jmarcey: { name: 'Joel', title: 'creator of Docusaurus', hello: new Date(), + key: 'jmarcey', + page: null, }, }; - expect(validateAuthorsMap(authorsMap)).toEqual(authorsMap); + expect(validateAuthorsMapInput(authorsMap)).toEqual(authorsMap); }); it('rename snake case image_url to camelCase imageURL', () => { @@ -453,9 +484,11 @@ describe('validateAuthorsMap', () => { slorber: { name: 'Sébastien Lorber', image_url: 'https://github.com/slorber.png', + key: 'slorber', + page: null, }, }; - expect(validateAuthorsMap(authorsMap)).toEqual({ + expect(validateAuthorsMapInput(authorsMap)).toEqual({ slorber: { name: 'Sébastien Lorber', imageURL: 'https://github.com/slorber.png', @@ -468,19 +501,23 @@ describe('validateAuthorsMap', () => { slorber: { imageURL: 'https://github.com/slorber.png', url: 'https://github.com/slorber', + key: 'slorber', + page: null, }, }; - expect(validateAuthorsMap(authorsMap)).toEqual(authorsMap); + expect(validateAuthorsMapInput(authorsMap)).toEqual(authorsMap); }); it('reject author without name or image', () => { const authorsMap: AuthorsMap = { slorber: { title: 'foo', + key: 'slorber', + page: null, }, }; expect(() => - validateAuthorsMap(authorsMap), + validateAuthorsMapInput(authorsMap), ).toThrowErrorMatchingInlineSnapshot( `""slorber" must contain at least one of [name, imageURL]"`, ); @@ -488,7 +525,7 @@ describe('validateAuthorsMap', () => { it('reject undefined author', () => { expect(() => - validateAuthorsMap({ + validateAuthorsMapInput({ slorber: undefined, }), ).toThrowErrorMatchingInlineSnapshot( @@ -498,7 +535,7 @@ describe('validateAuthorsMap', () => { it('reject null author', () => { expect(() => - validateAuthorsMap({ + validateAuthorsMapInput({ slorber: null, }), ).toThrowErrorMatchingInlineSnapshot( @@ -508,21 +545,23 @@ describe('validateAuthorsMap', () => { it('reject array author', () => { expect(() => - validateAuthorsMap({slorber: []}), + validateAuthorsMapInput({slorber: []}), ).toThrowErrorMatchingInlineSnapshot( `""slorber" should be an author object containing properties like name, title, and imageURL."`, ); }); it('reject array content', () => { - expect(() => validateAuthorsMap([])).toThrowErrorMatchingInlineSnapshot( + expect(() => + validateAuthorsMapInput([]), + ).toThrowErrorMatchingInlineSnapshot( `"The authors map file should contain an object where each entry contains an author key and the corresponding author's data."`, ); }); it('reject flat author', () => { expect(() => - validateAuthorsMap({name: 'Sébastien'}), + validateAuthorsMapInput({name: 'Sébastien'}), ).toThrowErrorMatchingInlineSnapshot( `""name" should be an author object containing properties like name, title, and imageURL."`, ); @@ -534,7 +573,7 @@ describe('validateAuthorsMap', () => { slorber: [], }; expect(() => - validateAuthorsMap(authorsMap), + validateAuthorsMapInput(authorsMap), ).toThrowErrorMatchingInlineSnapshot( `""slorber" should be an author object containing properties like name, title, and imageURL."`, ); diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index 7698fddf97b5..5a30ef2dd044 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -133,7 +133,7 @@ function normalizeAuthorsMap({ }); } -function validateAuthorsMapInput(content: unknown): AuthorsMapInput { +export function validateAuthorsMapInput(content: unknown): AuthorsMapInput { const {error, value} = AuthorsMapInputSchema.validate(content); if (error) { throw error; diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts index 4e1f614d7702..e8087dc03dcf 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts +++ b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts @@ -6,43 +6,48 @@ */ import _ from 'lodash'; -import {listTagsByLetters} from '../tagsUtils'; +import {listTagsByLetters} from '../generalUtils'; +import type {TagsListItem} from '@docusaurus/utils'; describe('listTagsByLetters', () => { - type Param = Parameters[0]; - type Tag = Param[number]; type Result = ReturnType; it('creates letters list', () => { - const tag1: Tag = { + const tag1: TagsListItem = { label: 'tag1', permalink: '/tag1', count: 1, + description: '', }; - const tag2: Tag = { + const tag2: TagsListItem = { label: 'Tag2', permalink: '/tag2', count: 11, + description: '', }; - const tagZxy: Tag = { + const tagZxy: TagsListItem = { label: 'zxy', permalink: '/zxy', count: 987, + description: '', }; - const tagAbc: Tag = { + const tagAbc: TagsListItem = { label: 'Abc', permalink: '/abc', count: 123, + description: '', }; - const tagDef: Tag = { + const tagDef: TagsListItem = { label: 'def', permalink: '/def', count: 1, + description: '', }; - const tagAaa: Tag = { + const tagAaa: TagsListItem = { label: 'aaa', permalink: '/aaa', count: 10, + description: '', }; const expectedResult: Result = [ From 6b62c9888ae5f3b286073fadd85d657f31cfa18c Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 25 Jun 2024 22:23:47 +0200 Subject: [PATCH 033/115] fix tests --- .../src/__tests__/authors.test.ts | 97 ++++++++++++++++--- .../src/authorsMap.ts | 2 +- 2 files changed, 87 insertions(+), 12 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index d8714d6091d6..49206cde69b5 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -39,7 +39,15 @@ describe('getBlogPostAuthors', () => { authorsMap: undefined, baseUrl: '/', }), - ).toEqual([{name: 'Sébastien Lorber'}]); + ).toEqual([ + { + name: 'Sébastien Lorber', + imageURL: undefined, + key: null, + page: null, + title: undefined, + }, + ]); expect( getBlogPostAuthors({ frontMatter: { @@ -48,7 +56,15 @@ describe('getBlogPostAuthors', () => { authorsMap: undefined, baseUrl: '/', }), - ).toEqual([{title: 'maintainer'}]); + ).toEqual([ + { + title: 'maintainer', + imageURL: undefined, + key: null, + name: undefined, + page: null, + }, + ]); expect( getBlogPostAuthors({ frontMatter: { @@ -57,7 +73,14 @@ describe('getBlogPostAuthors', () => { authorsMap: undefined, baseUrl: '/', }), - ).toEqual([{imageURL: 'https://github.com/slorber.png'}]); + ).toEqual([ + { + imageURL: 'https://github.com/slorber.png', + key: null, + name: undefined, + page: null, + }, + ]); expect( getBlogPostAuthors({ frontMatter: { @@ -66,7 +89,14 @@ describe('getBlogPostAuthors', () => { authorsMap: undefined, baseUrl: '/', }), - ).toEqual([{imageURL: '/img/slorber.png'}]); + ).toEqual([ + { + imageURL: '/img/slorber.png', + key: null, + name: undefined, + page: null, + }, + ]); expect( getBlogPostAuthors({ frontMatter: { @@ -75,7 +105,15 @@ describe('getBlogPostAuthors', () => { authorsMap: undefined, baseUrl: '/baseURL', }), - ).toEqual([{imageURL: '/baseURL/img/slorber.png'}]); + ).toEqual([ + { + imageURL: '/baseURL/img/slorber.png', + + key: null, + name: undefined, + page: null, + }, + ]); expect( getBlogPostAuthors({ frontMatter: { @@ -96,6 +134,8 @@ describe('getBlogPostAuthors', () => { title: 'maintainer1', imageURL: 'https://github.com/slorber1.png', url: 'https://github.com/slorber1', + key: null, + page: null, }, ]); }); @@ -111,7 +151,14 @@ describe('getBlogPostAuthors', () => { }, baseUrl: '/', }), - ).toEqual([{key: 'slorber', name: 'Sébastien Lorber'}]); + ).toEqual([ + { + key: 'slorber', + name: 'Sébastien Lorber', + imageURL: undefined, + page: null, + }, + ]); expect( getBlogPostAuthors({ frontMatter: { @@ -132,6 +179,7 @@ describe('getBlogPostAuthors', () => { key: 'slorber', name: 'Sébastien Lorber', imageURL: 'https://github.com/slorber.png', + page: null, }, ]); expect( @@ -154,6 +202,7 @@ describe('getBlogPostAuthors', () => { key: 'slorber', name: 'Sébastien Lorber', imageURL: '/img/slorber.png', + page: null, }, ]); expect( @@ -176,6 +225,7 @@ describe('getBlogPostAuthors', () => { key: 'slorber', name: 'Sébastien Lorber', imageURL: '/baseUrl/img/slorber.png', + page: null, }, ]); }); @@ -198,8 +248,20 @@ describe('getBlogPostAuthors', () => { baseUrl: '/', }), ).toEqual([ - {key: 'slorber', name: 'Sébastien Lorber', title: 'maintainer'}, - {key: 'yangshun', name: 'Yangshun Tay'}, + { + key: 'slorber', + name: 'Sébastien Lorber', + title: 'maintainer', + imageURL: undefined, + page: null, + }, + { + key: 'yangshun', + name: 'Yangshun Tay', + imageURL: undefined, + + page: null, + }, ]); }); @@ -217,6 +279,8 @@ describe('getBlogPostAuthors', () => { name: 'Sébastien Lorber', title: 'maintainer', imageURL: undefined, + key: null, + page: null, }, ]); }); @@ -238,8 +302,10 @@ describe('getBlogPostAuthors', () => { name: 'Sébastien Lorber', title: 'maintainer', imageURL: undefined, + key: null, + page: null, }, - {name: 'Yangshun Tay', imageURL: undefined}, + {name: 'Yangshun Tay', imageURL: undefined, key: null, page: null}, ]); }); @@ -274,15 +340,22 @@ describe('getBlogPostAuthors', () => { baseUrl: '/', }), ).toEqual([ - {key: 'slorber', name: 'Sébastien Lorber', title: 'maintainer'}, + { + key: 'slorber', + name: 'Sébastien Lorber', + title: 'maintainer', + imageURL: undefined, + page: null, + }, { key: 'yangshun', name: 'Yangshun Tay', title: 'Yangshun title local override', extra: 42, imageURL: undefined, + page: null, }, - {name: 'Alexey', imageURL: undefined}, + {name: 'Alexey', imageURL: undefined, key: null, page: null}, ]); }); @@ -492,6 +565,8 @@ describe('validateAuthorsMapInput', () => { slorber: { name: 'Sébastien Lorber', imageURL: 'https://github.com/slorber.png', + page: null, + key: 'slorber', }, }); }); diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index 5a30ef2dd044..268f4f5b2672 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -36,7 +36,7 @@ const AuthorsMapInputSchema = Joi.object() imageURL: URISchema, title: Joi.string(), email: Joi.string(), - page: Joi.alternatives(Joi.bool(), AuthorPageSchema), + page: Joi.alternatives(Joi.bool(), null, AuthorPageSchema), permalink: Joi.string(), description: Joi.string(), }) From ba12369729f247417e61515f0528be7227c9ccff Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:15:29 +0200 Subject: [PATCH 034/115] fix type --- .../docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 23411c3b9fd8..cae21d19ecf5 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -38,7 +38,7 @@ declare module '@docusaurus/plugin-content-blog' { }; export type AuthorAttributes = { - key?: string; // TODO temporary, need refactor + key: string | null; // TODO temporary, need refactor /** * If `name` doesn't exist, an `imageURL` is expected. From ad66da7fe82ae0669c1bb61bcf0862c2ee0299e8 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Fri, 28 Jun 2024 16:46:14 +0200 Subject: [PATCH 035/115] typecheck --- .../src/authorsMap.ts | 2 +- .../src/plugin-content-blog.d.ts | 14 ++++++++++---- .../src/theme-classic.d.ts | 11 +++++------ .../src/theme/BlogAuthorsPostsPage/index.tsx | 15 +++++++++++---- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index 268f4f5b2672..5a30ef2dd044 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -36,7 +36,7 @@ const AuthorsMapInputSchema = Joi.object() imageURL: URISchema, title: Joi.string(), email: Joi.string(), - page: Joi.alternatives(Joi.bool(), null, AuthorPageSchema), + page: Joi.alternatives(Joi.bool(), AuthorPageSchema), permalink: Joi.string(), description: Joi.string(), }) diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index cae21d19ecf5..f108659d4151 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -38,7 +38,7 @@ declare module '@docusaurus/plugin-content-blog' { }; export type AuthorAttributes = { - key: string | null; // TODO temporary, need refactor + key?: string | null; // TODO temporary, need refactor /** * If `name` doesn't exist, an `imageURL` is expected. @@ -63,6 +63,10 @@ declare module '@docusaurus/plugin-content-blog' { * to generate a fallback `mailto:` URL. */ email?: string; + /** + * Description of the author. + */ + description?: string; /** * Unknown keys are allowed, so that we can pass custom fields to authors. */ @@ -696,8 +700,10 @@ declare module '@theme/BlogTagsListPage' { } declare module '@theme/BlogAuthorsListPage' { - import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; - import type {AuthorsListItem} from '@docusaurus/utils'; + import type { + AuthorsListItem, + BlogSidebar, + } from '@docusaurus/plugin-content-blog'; export interface Props { /** Blog sidebar. */ @@ -712,10 +718,10 @@ declare module '@theme/BlogAuthorsListPage' { declare module '@theme/BlogAuthorsPostsPage' { import type {Content} from '@theme/BlogPostPage'; import type { + AuthorModule, BlogSidebar, BlogPaginatedMetadata, } from '@docusaurus/plugin-content-blog'; - import type {AuthorModule} from '@docusaurus/utils'; export interface Props { /** Blog sidebar. */ diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 9d7425f18796..f29cb1ee5924 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -1529,7 +1529,7 @@ declare module '@theme/Tag' { } declare module '@theme/AuthorsListByLetter' { - import type {AuthorsListItem} from '@docusaurus/utils'; + import type {AuthorsListItem} from '@docusaurus/plugin-content-blog'; export interface Props { readonly authors: readonly AuthorsListItem[]; @@ -1538,19 +1538,18 @@ declare module '@theme/AuthorsListByLetter' { } declare module '@theme/AuthorsListInline' { - import type {PageAuthor} from '@docusaurus/utils'; + import type {Author} from '@docusaurus/plugin-content-blog'; export interface Props { - readonly authors: readonly PageAuthor[]; + readonly authors: readonly Author[]; } export default function AuthorsListInline(props: Props): JSX.Element; } declare module '@theme/Author' { - import type {AuthorsListItem} from '@docusaurus/utils'; - import type {Optional} from 'utility-types'; + import type {AuthorsListItem} from '@docusaurus/plugin-content-blog'; - export interface Props extends Optional {} + export interface Props extends AuthorsListItem {} export default function Author(props: Props): JSX.Element; } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx index 35da41f1994f..d8e13b7e0611 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx @@ -20,7 +20,6 @@ import BlogListPaginator from '@theme/BlogListPaginator'; import SearchMetadata from '@theme/SearchMetadata'; import type {Props} from '@theme/BlogAuthorsPostsPage'; import BlogPostItems from '@theme/BlogPostItems'; -import Unlisted from '@theme/Unlisted'; import Heading from '@theme/Heading'; function useBlogAuthorsPostsPageTitle(author: Props['author']): string { @@ -31,7 +30,10 @@ function useBlogAuthorsPostsPageTitle(author: Props['author']): string { description: 'The title of the page for a blog author', message: '{nPosts} contributed by "{authorName}"', }, - {nPosts: blogPostsPlural(author.count), authorName: author.name}, + { + nPosts: blogPostsPlural(author.count), + authorName: author.name || author.imageURL || '', + }, ); } @@ -54,13 +56,18 @@ function BlogAuthorsPostsPageContent({ const title = useBlogAuthorsPostsPageTitle(author); return ( - {author.unlisted && }
      {title}
        {author.url && (
      • - Personal website + + + Personal website + +
      • )} {author.description &&
      • {author.description}
      • } From f538f5f6cd54ee52550f29cd4176aab50371ad01 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:05:15 +0200 Subject: [PATCH 036/115] refactor: move logic in contentLoaded --- .../src/blogUtils.ts | 13 +------ .../src/index.ts | 39 ++++++++++--------- .../src/plugin-content-blog.d.ts | 5 ++- .../src/routes.ts | 22 ++++++++++- 4 files changed, 45 insertions(+), 34 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index a473e04f4c94..7269853f6ce5 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -30,8 +30,8 @@ import { import {getTagsFile} from '@docusaurus/utils-validation'; import {validateBlogPostFrontMatter} from './frontMatter'; import {getBlogPostAuthors} from './authors'; -import {getAuthorsMap, type AuthorsMap} from './authorsMap'; import {reportAuthorsProblems} from './authorsProblems'; +import type {AuthorsMap} from './authorsMap'; import type {TagsFile} from '@docusaurus/utils'; import type {LoadContext, ParseFrontMatter} from '@docusaurus/types'; import type { @@ -367,6 +367,7 @@ export async function generateBlogPosts( contentPaths: BlogContentPaths, context: LoadContext, options: PluginOptions, + authorsMap?: AuthorsMap, ): Promise { const {include, exclude} = options; @@ -379,16 +380,6 @@ export async function generateBlogPosts( ignore: exclude, }); - const authorsMap = await getAuthorsMap({ - contentPaths, - authorsMapPath: options.authorsMapPath, - authorsBaseRoutePath: normalizeUrl([ - context.baseUrl, - options.routeBasePath, - options.authorsPageBasePath, - ]), - }); - const tagsFile = await getTagsFile({contentPaths, tags: options.tags}); async function doProcessBlogSourceFile(blogSourceFile: string) { diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index eccbdf9d4aec..06752e1f3c0a 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -34,7 +34,7 @@ import {translateContent, getTranslationFiles} from './translations'; import {createBlogFeedFiles, createFeedHtmlHeadTags} from './feed'; import {createAllRoutes} from './routes'; -import {getBlogPageAuthors} from './authors'; +import {getAuthorsMap} from './authorsMap'; import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types'; import type {LoadContext, Plugin} from '@docusaurus/types'; import type { @@ -45,7 +45,6 @@ import type { BlogTags, BlogContent, BlogPaginated, - BlogPageAuthors, } from '@docusaurus/plugin-content-blog'; import type {Options as MDXLoaderOptions} from '@docusaurus/mdx-loader/lib/loader'; import type {RuleSetUseItem} from 'webpack'; @@ -158,7 +157,6 @@ export default async function pluginContentBlog( postsPerPage: postsPerPageOption, routeBasePath, tagsBasePath, - authorsPageBasePath, blogDescription, blogTitle, blogSidebarTitle, @@ -167,11 +165,23 @@ export default async function pluginContentBlog( const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]); const blogTagsListPath = normalizeUrl([baseBlogUrl, tagsBasePath]); - const blogAuthorsListPath = normalizeUrl([ - baseBlogUrl, - authorsPageBasePath, - ]); - let blogPosts = await generateBlogPosts(contentPaths, context, options); + + const authorsMap = await getAuthorsMap({ + contentPaths, + authorsMapPath: options.authorsMapPath, + authorsBaseRoutePath: normalizeUrl([ + context.baseUrl, + options.routeBasePath, + options.authorsPageBasePath, + ]), + }); + + let blogPosts = await generateBlogPosts( + contentPaths, + context, + options, + authorsMap, + ); blogPosts = await applyProcessBlogPosts({ blogPosts, processBlogPosts: options.processBlogPosts, @@ -185,8 +195,7 @@ export default async function pluginContentBlog( blogListPaginated: [], blogTags: {}, blogTagsListPath, - blogPageAuthors: {}, - blogAuthorsListPath, + authorsMap: {}, }; } @@ -231,13 +240,6 @@ export default async function pluginContentBlog( // TODO: put "AuthorsMap" in Content // Move blog post author pages aggregation logic in contentLoaded() - const blogPageAuthors: BlogPageAuthors = getBlogPageAuthors({ - blogPosts, - postsPerPageOption, - blogDescription, - blogTitle, - pageBasePath, - }); return { blogSidebarTitle, @@ -245,8 +247,7 @@ export default async function pluginContentBlog( blogListPaginated, blogTags, blogTagsListPath, - blogPageAuthors, - blogAuthorsListPath, + authorsMap, }; }, diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index f108659d4151..dd236809269f 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -516,14 +516,15 @@ declare module '@docusaurus/plugin-content-blog' { items: BlogSidebarItem[]; }; + import type {AuthorsMap} from './authorsMap'; + export type BlogContent = { blogSidebarTitle: string; blogPosts: BlogPost[]; blogListPaginated: BlogPaginated[]; blogTags: BlogTags; blogTagsListPath: string; - blogPageAuthors: BlogPageAuthors; - blogAuthorsListPath: string; + authorsMap: AuthorsMap; }; export type BlogMetadata = { diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index edb7bcb0dc12..bede48e22034 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -19,6 +19,7 @@ import { toTagProp, toTagsProp, } from './props'; +import {getBlogPageAuthors} from './authors'; import type { PluginContentLoadedActions, RouteConfig, @@ -33,6 +34,7 @@ import type { BlogPost, BlogSidebar, BlogPageAuthor, + BlogPageAuthors, } from '@docusaurus/plugin-content-blog'; type CreateAllRoutesParam = { @@ -68,6 +70,10 @@ export async function buildAllRoutes({ routeBasePath, archiveBasePath, blogTitle, + authorsPageBasePath, + postsPerPage, + blogDescription, + pageBasePath, } = options; const pluginId = options.id!; const {createData} = actions; @@ -76,11 +82,23 @@ export async function buildAllRoutes({ blogPosts, blogListPaginated, blogTags, - blogPageAuthors, blogTagsListPath, - blogAuthorsListPath, } = content; + const blogAuthorsListPath = normalizeUrl([ + baseUrl, + routeBasePath, + authorsPageBasePath, + ]); + + const blogPageAuthors: BlogPageAuthors = getBlogPageAuthors({ + blogPosts, + postsPerPageOption: postsPerPage, + blogDescription, + blogTitle, + pageBasePath, + }); + const listedBlogPosts = blogPosts.filter(shouldBeListed); const blogPostsById = _.keyBy(blogPosts, (post) => post.id); From a4fdaf1ce96671a4d25a7e196e7d8a3202ffa05c Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:10:26 +0200 Subject: [PATCH 037/115] move code --- .../src/routes.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index bede48e22034..571dba22f63e 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -85,20 +85,6 @@ export async function buildAllRoutes({ blogTagsListPath, } = content; - const blogAuthorsListPath = normalizeUrl([ - baseUrl, - routeBasePath, - authorsPageBasePath, - ]); - - const blogPageAuthors: BlogPageAuthors = getBlogPageAuthors({ - blogPosts, - postsPerPageOption: postsPerPage, - blogDescription, - blogTitle, - pageBasePath, - }); - const listedBlogPosts = blogPosts.filter(shouldBeListed); const blogPostsById = _.keyBy(blogPosts, (post) => post.id); @@ -283,6 +269,20 @@ export async function buildAllRoutes({ } function createAuthorsRoutes(): RouteConfig[] { + const blogAuthorsListPath = normalizeUrl([ + baseUrl, + routeBasePath, + authorsPageBasePath, + ]); + + const blogPageAuthors: BlogPageAuthors = getBlogPageAuthors({ + blogPosts, + postsPerPageOption: postsPerPage, + blogDescription, + blogTitle, + pageBasePath, + }); + // Authors. This is the last part so we early-return if there are no tags. if (Object.keys(blogPageAuthors).length === 0) { return []; From aec215442bc9e732629e8431a504ae160c9977ca Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:47:45 +0200 Subject: [PATCH 038/115] fix typecheck --- packages/docusaurus-plugin-content-blog/src/authors.ts | 2 +- packages/docusaurus-plugin-content-blog/src/authorsMap.ts | 3 +-- packages/docusaurus-plugin-content-blog/src/blogUtils.ts | 2 +- .../src/plugin-content-blog.d.ts | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 9f010010b227..177616a1c627 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -10,12 +10,12 @@ import {normalizeUrl} from '@docusaurus/utils'; import {paginateBlogPosts} from './blogUtils'; import type { Author, + AuthorsMap, BlogPageAuthors, BlogPost, BlogPostFrontMatter, BlogPostFrontMatterAuthor, } from '@docusaurus/plugin-content-blog'; -import type {AuthorsMap} from './authorsMap'; type AuthorsParam = { frontMatter: BlogPostFrontMatter; diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index 5a30ef2dd044..d904724751cf 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -13,10 +13,9 @@ import type { Author, AuthorAttributes, AuthorPage, + AuthorsMap, } from '@docusaurus/plugin-content-blog'; -export type AuthorsMap = {[authorKey: string]: Author}; - type AuthorInput = AuthorAttributes & { page: boolean | AuthorPage; }; diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 7269853f6ce5..5d4a92120e11 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -31,10 +31,10 @@ import {getTagsFile} from '@docusaurus/utils-validation'; import {validateBlogPostFrontMatter} from './frontMatter'; import {getBlogPostAuthors} from './authors'; import {reportAuthorsProblems} from './authorsProblems'; -import type {AuthorsMap} from './authorsMap'; import type {TagsFile} from '@docusaurus/utils'; import type {LoadContext, ParseFrontMatter} from '@docusaurus/types'; import type { + AuthorsMap, PluginOptions, ReadingTimeFunction, BlogPost, diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 28f34b6c283a..36d9a0379502 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -517,7 +517,7 @@ declare module '@docusaurus/plugin-content-blog' { items: BlogSidebarItem[]; }; - import type {AuthorsMap} from './authorsMap'; + export type AuthorsMap = {[authorKey: string]: Author}; export type BlogContent = { blogSidebarTitle: string; @@ -525,7 +525,7 @@ declare module '@docusaurus/plugin-content-blog' { blogListPaginated: BlogPaginated[]; blogTags: BlogTags; blogTagsListPath: string; - authorsMap: AuthorsMap; + authorsMap?: AuthorsMap; }; export type BlogMetadata = { From 44c426a966756eea485d3348a442242528f38aad Mon Sep 17 00:00:00 2001 From: sebastien Date: Tue, 2 Jul 2024 11:43:41 +0200 Subject: [PATCH 039/115] fix authorinput type mismatch --- packages/docusaurus-plugin-content-blog/src/authorsMap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index 268f4f5b2672..f8fcf598cc2b 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -18,7 +18,7 @@ import type { export type AuthorsMap = {[authorKey: string]: Author}; type AuthorInput = AuthorAttributes & { - page: boolean | AuthorPage; + page?: boolean | AuthorPage; }; type AuthorsMapInput = {[authorKey: string]: AuthorInput}; From e85a803164172a4e87d138d67a2740905eac98fc Mon Sep 17 00:00:00 2001 From: sebastien Date: Tue, 2 Jul 2024 12:20:34 +0200 Subject: [PATCH 040/115] fix types --- .../src/plugin-content-blog.d.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 36d9a0379502..41522c11afc9 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -38,8 +38,6 @@ declare module '@docusaurus/plugin-content-blog' { }; export type AuthorAttributes = { - key?: string | null; // TODO temporary, need refactor - /** * If `name` doesn't exist, an `imageURL` is expected. */ @@ -70,7 +68,7 @@ declare module '@docusaurus/plugin-content-blog' { /** * Unknown keys are allowed, so that we can pass custom fields to authors. */ - [attribute: string]: unknown; + [customAuthorAttribute: string]: unknown; }; /** From a9bf8209576154a654420787137917fb58a319c9 Mon Sep 17 00:00:00 2001 From: sebastien Date: Tue, 2 Jul 2024 12:34:06 +0200 Subject: [PATCH 041/115] little refactors --- .../src/plugin-content-blog.d.ts | 14 ++++++------ .../src/props.ts | 22 +++++++++---------- .../src/routes.ts | 8 +++---- .../src/theme-classic.d.ts | 8 +++---- .../src/utils/authorsUtils.ts | 4 ++-- .../src/utils/generalUtils.ts | 4 ++-- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 41522c11afc9..125863c2c2a3 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -93,13 +93,13 @@ declare module '@docusaurus/plugin-content-blog' { }; /** What the authors list page should know about each author. */ - export type AuthorsListItem = Author & { - /** Number of posts/docs with this author. */ + export type AuthorItemProp = Author & { + /** Number of blog posts with this author. */ count: number; }; /** What the author's own page should know about the author. */ - export type AuthorModule = AuthorsListItem & { + export type AuthorPageProp = AuthorItemProp & { /** The authors list page's permalink. */ allAuthorsPath: string; }; @@ -701,7 +701,7 @@ declare module '@theme/BlogTagsListPage' { declare module '@theme/BlogAuthorsListPage' { import type { - AuthorsListItem, + AuthorItemProp, BlogSidebar, } from '@docusaurus/plugin-content-blog'; @@ -709,7 +709,7 @@ declare module '@theme/BlogAuthorsListPage' { /** Blog sidebar. */ readonly sidebar: BlogSidebar; /** All authors declared in this blog. */ - readonly authors: AuthorsListItem[]; + readonly authors: AuthorItemProp[]; } export default function BlogAuthorsListPage(props: Props): JSX.Element; @@ -718,7 +718,7 @@ declare module '@theme/BlogAuthorsListPage' { declare module '@theme/BlogAuthorsPostsPage' { import type {Content} from '@theme/BlogPostPage'; import type { - AuthorModule, + AuthorPageProp, BlogSidebar, BlogPaginatedMetadata, } from '@docusaurus/plugin-content-blog'; @@ -727,7 +727,7 @@ declare module '@theme/BlogAuthorsPostsPage' { /** Blog sidebar. */ readonly sidebar: BlogSidebar; /** Metadata of this author. */ - readonly author: AuthorModule; + readonly author: AuthorPageProp; /** Looks exactly the same as the posts list page */ readonly listMetadata: BlogPaginatedMetadata; /** diff --git a/packages/docusaurus-plugin-content-blog/src/props.ts b/packages/docusaurus-plugin-content-blog/src/props.ts index 47b8b1793319..747bde32acf5 100644 --- a/packages/docusaurus-plugin-content-blog/src/props.ts +++ b/packages/docusaurus-plugin-content-blog/src/props.ts @@ -6,8 +6,8 @@ */ import type {TagsListItem, TagModule} from '@docusaurus/utils'; import type { - AuthorsListItem, - AuthorModule, + AuthorItemProp, + AuthorPageProp, BlogPageAuthor, BlogPageAuthors, BlogPost, @@ -44,26 +44,24 @@ export function toTagProp({ }; } -export function toPageAuthorsProp({ +export function toAuthorItemsProp({ blogPageAuthors, }: { blogPageAuthors: BlogPageAuthors; -}): AuthorsListItem[] { - return Object.values(blogPageAuthors) - .filter((author) => !author.unlisted) - .map((author) => ({ - ...author, - count: author.items.length, - })); +}): AuthorItemProp[] { + return Object.values(blogPageAuthors).map((author) => ({ + ...author, + count: author.items.length, + })); } -export function toPageAuthorProp({ +export function toAuthorPageProp({ blogAuthorsListPath, author, }: { blogAuthorsListPath: string; author: BlogPageAuthor; -}): AuthorModule { +}): AuthorPageProp { return { ...author, allAuthorsPath: blogAuthorsListPath, diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index 62b0da6ea187..fd58fcdcad3e 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -14,8 +14,8 @@ import { import {shouldBeListed} from './blogUtils'; import { - toPageAuthorProp, - toPageAuthorsProp, + toAuthorPageProp, + toAuthorItemsProp, toBlogSidebarProp, toTagProp, toTagsProp, @@ -292,7 +292,7 @@ export async function buildAllRoutes({ sidebar: sidebarModulePath, }, props: { - authors: toPageAuthorsProp({blogPageAuthors}), + authors: toAuthorItemsProp({blogPageAuthors}), }, }; @@ -309,7 +309,7 @@ export async function buildAllRoutes({ sidebar: sidebarModulePath, }, props: { - author: toPageAuthorProp({author, blogAuthorsListPath}), + author: toAuthorPageProp({author, blogAuthorsListPath}), listMetadata: metadata, }, }; diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 414c6ee08106..a9e8bca24242 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -1542,10 +1542,10 @@ declare module '@theme/Tag' { } declare module '@theme/AuthorsListByLetter' { - import type {AuthorsListItem} from '@docusaurus/plugin-content-blog'; + import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; export interface Props { - readonly authors: readonly AuthorsListItem[]; + readonly authors: readonly AuthorItemProp[]; } export default function AuthorsListByLetter(props: Props): JSX.Element; } @@ -1560,9 +1560,9 @@ declare module '@theme/AuthorsListInline' { } declare module '@theme/Author' { - import type {AuthorsListItem} from '@docusaurus/plugin-content-blog'; + import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; - export interface Props extends AuthorsListItem {} + export interface Props extends AuthorItemProp {} export default function Author(props: Props): JSX.Element; } diff --git a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts index 268310835971..dc2da3841f5b 100644 --- a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts @@ -6,7 +6,7 @@ */ import {translate} from '@docusaurus/Translate'; -import type {AuthorsListItem} from '@docusaurus/plugin-content-blog'; +import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; export const translateAuthorsPageTitle = (): string => translate({ @@ -17,5 +17,5 @@ export const translateAuthorsPageTitle = (): string => export type AuthorLetterEntry = { letter: string; - authors: AuthorsListItem[]; + authors: AuthorItemProp[]; }; diff --git a/packages/docusaurus-theme-common/src/utils/generalUtils.ts b/packages/docusaurus-theme-common/src/utils/generalUtils.ts index e06774d8c964..74afccafa0be 100644 --- a/packages/docusaurus-theme-common/src/utils/generalUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/generalUtils.ts @@ -7,7 +7,7 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import type {TagLetterEntry} from './tagsUtils'; -import type {AuthorsListItem} from '@docusaurus/plugin-content-blog'; +import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; import type {TagsListItem} from '@docusaurus/utils'; import type {AuthorLetterEntry} from './authorsUtils'; @@ -79,7 +79,7 @@ export function listTagsByLetters( } export function listAuthorsByLetters( - authors: readonly AuthorsListItem[], + authors: readonly AuthorItemProp[], ): AuthorLetterEntry[] { return listByLetters( authors, From 6a1ff0a075c44e9c1c27ab80d071f4234b4764b3 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:46:00 +0200 Subject: [PATCH 042/115] refactor (pagination not working) --- .../src/authors.ts | 75 ++------------ .../src/plugin-content-blog.d.ts | 13 +-- .../src/props.ts | 23 +++-- .../src/routes.ts | 99 +++++++++++-------- .../src/theme/BlogAuthorsPostsPage/index.tsx | 3 +- 5 files changed, 86 insertions(+), 127 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 177616a1c627..895a628ba218 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -7,11 +7,9 @@ import _ from 'lodash'; import {normalizeUrl} from '@docusaurus/utils'; -import {paginateBlogPosts} from './blogUtils'; import type { Author, AuthorsMap, - BlogPageAuthors, BlogPost, BlogPostFrontMatter, BlogPostFrontMatterAuthor, @@ -148,75 +146,18 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t return authors; } -type AuthoredItemGroup = { - author: Author; - items: BlogPost[]; -}; - /** - * Blog posts are grouped by permalink - * @param blogPosts + * Blog posts are grouped by author page permalink (if page exists) */ // TODO would be better if iterated over the AuthorsMap instead of the blogPosts -export function groupBlogPostsByPermalink(blogPosts: readonly BlogPost[]): { - [permalink: string]: AuthoredItemGroup; -} { - const result: {[permalink: string]: AuthoredItemGroup} = {}; - - blogPosts.forEach((item) => { - item.metadata.authors.forEach((author) => { - if (author.page) { - // Init missing author groups - // TODO: it's not really clear what should be the behavior if 2 - // authors have the same permalink but the label is different for each - // For now, the first author found wins - result[author.page.permalink] ??= { - author, - items: [], - }; - - // Add item to group - result[author.page.permalink]!.items.push(item); - } - }); - }); - - // If user add twice the same author to a md doc (weird but possible), - // we don't want the item to appear twice in the list... - // TODO wait for #10224 and remove below code - Object.values(result).forEach((group) => { - group.items = _.uniq(group.items); - }); - - return result; -} - -export function getBlogPageAuthors({ +export function getBlogPostsForAuthorKey({ blogPosts, - ...params + authorsMap, }: { blogPosts: BlogPost[]; - blogTitle: string; - blogDescription: string; - postsPerPageOption: number | 'ALL'; - pageBasePath: string; -}): BlogPageAuthors { - const groups = groupBlogPostsByPermalink(blogPosts); - - return _.mapValues(groups, ({author, items}) => { - const authorBlogPosts = items.filter( - (blogPost) => !blogPost.metadata.unlisted, - ); - return { - ...author, - items: authorBlogPosts.map((blogPost) => blogPost.id), - pages: author.page - ? paginateBlogPosts({ - blogPosts: authorBlogPosts, - basePageUrl: author.page.permalink, - ...params, - }) - : [], - }; - }); + authorsMap: AuthorsMap | undefined; +}): Record { + return _.mapValues(authorsMap, (author, key) => + blogPosts.filter((p) => p.metadata.authors.some((a) => a.key === key)), + ); } diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 125863c2c2a3..1d6ddab93ded 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -98,12 +98,6 @@ declare module '@docusaurus/plugin-content-blog' { count: number; }; - /** What the author's own page should know about the author. */ - export type AuthorPageProp = AuthorItemProp & { - /** The authors list page's permalink. */ - allAuthorsPath: string; - }; - /** * Everything is partial/unnormalized, because front matter is always * preserved as-is. Default values will be applied when generating metadata @@ -718,7 +712,7 @@ declare module '@theme/BlogAuthorsListPage' { declare module '@theme/BlogAuthorsPostsPage' { import type {Content} from '@theme/BlogPostPage'; import type { - AuthorPageProp, + AuthorItemProp, BlogSidebar, BlogPaginatedMetadata, } from '@docusaurus/plugin-content-blog'; @@ -727,7 +721,10 @@ declare module '@theme/BlogAuthorsPostsPage' { /** Blog sidebar. */ readonly sidebar: BlogSidebar; /** Metadata of this author. */ - readonly author: AuthorPageProp; + readonly author: AuthorItemProp; + // TODO + /** Link to the page of all authors. */ + readonly allAuthorsPath: string; /** Looks exactly the same as the posts list page */ readonly listMetadata: BlogPaginatedMetadata; /** diff --git a/packages/docusaurus-plugin-content-blog/src/props.ts b/packages/docusaurus-plugin-content-blog/src/props.ts index 747bde32acf5..778d95cf6dcf 100644 --- a/packages/docusaurus-plugin-content-blog/src/props.ts +++ b/packages/docusaurus-plugin-content-blog/src/props.ts @@ -6,9 +6,8 @@ */ import type {TagsListItem, TagModule} from '@docusaurus/utils'; import type { + Author, AuthorItemProp, - AuthorPageProp, - BlogPageAuthor, BlogPageAuthors, BlogPost, BlogSidebar, @@ -55,17 +54,25 @@ export function toAuthorItemsProp({ })); } +// TODO +// export function toAuthorProp({author}: {author: Author}): AuthorItemProp { +// return { +// ...author, +// // TODO +// count: 1, +// }; +// } + export function toAuthorPageProp({ - blogAuthorsListPath, author, + count, }: { - blogAuthorsListPath: string; - author: BlogPageAuthor; -}): AuthorPageProp { + author: Author; + count: number; +}): AuthorItemProp { return { ...author, - allAuthorsPath: blogAuthorsListPath, - count: author.items.length, + count, }; } diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index fd58fcdcad3e..ca10d5424110 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -11,16 +11,17 @@ import { docuHash, aliasedSitePathToRelativePath, } from '@docusaurus/utils'; -import {shouldBeListed} from './blogUtils'; +import {paginateBlogPosts, shouldBeListed} from './blogUtils'; import { toAuthorPageProp, - toAuthorItemsProp, + // toAuthorProp, + // toAuthorItemsProp, toBlogSidebarProp, toTagProp, toTagsProp, } from './props'; -import {getBlogPageAuthors} from './authors'; +import {getBlogPostsForAuthorKey} from './authors'; import type { PluginContentLoadedActions, RouteConfig, @@ -33,8 +34,10 @@ import type { BlogContent, PluginOptions, BlogPost, - BlogPageAuthor, - BlogPageAuthors, + // BlogPageAuthor, + // BlogPageAuthors, + // AuthorsMap, + Author, } from '@docusaurus/plugin-content-blog'; type CreateAllRoutesParam = { @@ -73,7 +76,7 @@ export async function buildAllRoutes({ authorsPageBasePath, postsPerPage, blogDescription, - pageBasePath, + // pageBasePath, } = options; const pluginId = options.id!; const {createData} = actions; @@ -83,6 +86,7 @@ export async function buildAllRoutes({ blogListPaginated, blogTags, blogTagsListPath, + authorsMap, } = content; const listedBlogPosts = blogPosts.filter(shouldBeListed); @@ -264,63 +268,72 @@ export async function buildAllRoutes({ return [tagsListRoute, ...tagsPaginatedRoutes]; } - function createAuthorsRoutes(): RouteConfig[] { + async function createAuthorsRoutes(): Promise { + const blogPostsByAuthorKey = getBlogPostsForAuthorKey({ + authorsMap, + blogPosts, + }); + const authors = Object.values(authorsMap ?? {}); + const blogAuthorsListPath = normalizeUrl([ baseUrl, routeBasePath, authorsPageBasePath, ]); - const blogPageAuthors: BlogPageAuthors = getBlogPageAuthors({ - blogPosts, - postsPerPageOption: postsPerPage, - blogDescription, - blogTitle, - pageBasePath, - }); + return Promise.all([ + createAuthorListRoute(), + ...authors.flatMap(createAuthorPaginatedRoute), + ]).then((routes) => routes.flat()); - // Authors. This is the last part so we early-return if there are no tags. - if (Object.keys(blogPageAuthors).length === 0) { - return []; + // Maybe authors with page: false could even appear on the list? + // to be defined 🤷 + async function createAuthorListRoute(): Promise { + return { + path: blogAuthorsListPath, + component: blogAuthorsListComponent, + exact: true, + modules: { + sidebar: sidebarModulePath, + }, + props: { + authors, + }, + }; } - const authorsListRoute: RouteConfig = { - path: blogAuthorsListPath, - component: blogAuthorsListComponent, - exact: true, - modules: { - sidebar: sidebarModulePath, - }, - props: { - authors: toAuthorItemsProp({blogPageAuthors}), - }, - }; - - function createAuthorPaginatedRoutes( - author: BlogPageAuthor, - ): RouteConfig[] { - return author.pages.map(({metadata, items}) => { + async function createAuthorPaginatedRoute( + author: Author, + ): Promise { + const authorBlogPosts = blogPostsByAuthorKey[author.key ?? '']; + // TODO + if (!author.page || authorBlogPosts === undefined) { + return []; + } + const pages = paginateBlogPosts({ + blogPosts: authorBlogPosts, + basePageUrl: author.page.permalink, + blogDescription, + blogTitle, + pageBasePath: authorsPageBasePath, + postsPerPageOption: postsPerPage, + }); + return pages.map(({metadata, items}) => { return { path: metadata.permalink, - component: blogAuthorsPostsComponent, - exact: true, modules: { items: blogPostItemsModule(items), sidebar: sidebarModulePath, }, props: { - author: toAuthorPageProp({author, blogAuthorsListPath}), + author: toAuthorPageProp({author, count: authorBlogPosts.length}), listMetadata: metadata, + allAuthorsPath: blogAuthorsListPath, }, + component: blogAuthorsPostsComponent, }; }); } - - const authorsPaginatedRoutes: RouteConfig[] = Object.values( - blogPageAuthors, - ).flatMap(createAuthorPaginatedRoutes); - - return [authorsListRoute, ...authorsPaginatedRoutes]; } return [ @@ -328,6 +341,6 @@ export async function buildAllRoutes({ ...createBlogPostsPaginatedRoutes(), ...createTagsRoutes(), ...createArchiveRoute(), - ...createAuthorsRoutes(), + ...(await createAuthorsRoutes()), ]; } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx index d8e13b7e0611..8020d408b990 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx @@ -52,6 +52,7 @@ function BlogAuthorsPostsPageContent({ items, sidebar, listMetadata, + allAuthorsPath, }: Props): JSX.Element { const title = useBlogAuthorsPostsPageTitle(author); return ( @@ -72,7 +73,7 @@ function BlogAuthorsPostsPageContent({ )} {author.description &&
      • {author.description}
      • }
      - + From ae795a3e560abf076d53f8be59432d06a5ac7b55 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 9 Jul 2024 10:27:23 +0200 Subject: [PATCH 043/115] pagination --- .../src/routes.ts | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index ca10d5424110..5e0c2e7c7439 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -310,17 +310,25 @@ export async function buildAllRoutes({ if (!author.page || authorBlogPosts === undefined) { return []; } - const pages = paginateBlogPosts({ - blogPosts: authorBlogPosts, - basePageUrl: author.page.permalink, - blogDescription, - blogTitle, - pageBasePath: authorsPageBasePath, - postsPerPageOption: postsPerPage, - }); - return pages.map(({metadata, items}) => { + + // TODO ugly ? + const data = { + items: authorBlogPosts.map((post) => post.id), + pages: paginateBlogPosts({ + blogPosts: authorBlogPosts, + basePageUrl: author.page.permalink, + blogDescription, + blogTitle, + pageBasePath: authorsPageBasePath, + postsPerPageOption: postsPerPage, + }), + }; + + return data.pages.map(({metadata, items}) => { return { path: metadata.permalink, + component: blogAuthorsPostsComponent, + exact: true, modules: { items: blogPostItemsModule(items), sidebar: sidebarModulePath, @@ -330,7 +338,6 @@ export async function buildAllRoutes({ listMetadata: metadata, allAuthorsPath: blogAuthorsListPath, }, - component: blogAuthorsPostsComponent, }; }); } From d4a64bb70c34431b24a7421812676faa3cfffcbd Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 9 Jul 2024 10:53:10 +0200 Subject: [PATCH 044/115] refactor --- .../src/utils/generalUtils.ts | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/packages/docusaurus-theme-common/src/utils/generalUtils.ts b/packages/docusaurus-theme-common/src/utils/generalUtils.ts index 74afccafa0be..9a5041a56633 100644 --- a/packages/docusaurus-theme-common/src/utils/generalUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/generalUtils.ts @@ -38,33 +38,25 @@ type Entry = HasLabel | HasName; */ function listByLetters( items: readonly T[], - getLetter: (item: T) => string, getLabel: (item: T) => string, mapResult: (letter: string, items: T[]) => R, ): R[] { const groups: {[initial: string]: T[]} = {}; items.forEach((item) => { - const initial = getLetter(item); + const label = getLabel(item); + const initial = label[0]!.toUpperCase(); groups[initial] ??= []; groups[initial]!.push(item); }); - return ( - Object.entries(groups) - // Sort letters - .sort(([letter1], [letter2]) => letter1.localeCompare(letter2)) - .map(([letter, groupedItems]) => { - // Sort items inside a letter - const sortedItems = groupedItems.sort((item1, item2) => - getLabel(item1).localeCompare(getLabel(item2)), - ); - return mapResult(letter, sortedItems); - }) - ); -} - -function getItemLetter(item: string): string { - return item[0]!.toUpperCase(); + return Object.entries(groups) + .sort(([letter1], [letter2]) => letter1.localeCompare(letter2)) + .map(([letter, groupedItems]) => { + const sortedItems = groupedItems.sort((item1, item2) => + getLabel(item1).localeCompare(getLabel(item2)), + ); + return mapResult(letter, sortedItems); + }); } export function listTagsByLetters( @@ -72,7 +64,6 @@ export function listTagsByLetters( ): TagLetterEntry[] { return listByLetters( tags, - (tag) => getItemLetter(tag.label), (tag) => tag.label, (letter, items) => ({letter, tags: items}), ); @@ -83,7 +74,6 @@ export function listAuthorsByLetters( ): AuthorLetterEntry[] { return listByLetters( authors, - (author) => getItemLetter(author.name ?? author.imageURL ?? ''), (author) => author.name ?? author.imageURL ?? '', (letter, items) => ({letter, authors: items}), ); From 66d457b5a1d9b8e3431a7bdb11f705723798b001 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 9 Jul 2024 11:24:37 +0200 Subject: [PATCH 045/115] refactor list letter & permalink collision --- .../src/__tests__/authors.test.ts | 1 - .../src/__tests__/authorsProblems.test.ts | 58 ++++++++++++++++++- .../src/authorsMap.ts | 49 ++++++++-------- packages/docusaurus-theme-common/package.json | 2 +- .../src/utils/authorsUtils.ts | 2 +- .../src/utils/generalUtils.ts | 35 ++++++----- .../src/utils/tagsUtils.ts | 2 +- 7 files changed, 107 insertions(+), 42 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index 49206cde69b5..1bf83dbaa954 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -644,7 +644,6 @@ describe('validateAuthorsMapInput', () => { it('reject non-map author', () => { const authorsMap: AuthorsMap = { - // @ts-expect-error: for tests slorber: [], }; expect(() => diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts index 84296df1c931..13e89de0b9d8 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts @@ -7,10 +7,13 @@ import {jest} from '@jest/globals'; import {reportDuplicateAuthors, reportInlineAuthors} from '../authorsProblems'; -import type {Author} from '@docusaurus/plugin-content-blog'; +import {checkPermalinkCollisions} from '../authorsMap'; +import type {Author, AuthorsMap} from '@docusaurus/plugin-content-blog'; const blogSourceRelative = 'doc.md'; +// TODO update TS types + describe('duplicate authors', () => { function testReport({authors}: {authors: Author[]}) { reportDuplicateAuthors({ @@ -176,3 +179,56 @@ describe('inline authors', () => { `); }); }); + +// TODO move to authorsMap.test.ts ? +describe('authors permalink collision', () => { + it('no collision', () => { + const authors: AuthorsMap = { + author1: { + name: 'author1', + key: 'author1', + page: { + permalink: '/author1', + }, + }, + author2: { + name: 'author2', + key: 'author2', + page: { + permalink: '/author2', + }, + }, + }; + + expect(() => { + checkPermalinkCollisions(authors); + }).not.toThrow(); + }); + + it('collision', () => { + const authors: AuthorsMap = { + author1: { + name: 'author1', + key: 'author1', + page: { + permalink: '/author1', + }, + }, + author2: { + name: 'author1', + key: 'author1', + page: { + permalink: '/author1', + }, + }, + }; + + expect(() => { + checkPermalinkCollisions(authors); + }).toThrowErrorMatchingInlineSnapshot(` + "The following permalinks are duplicated: + Permalink: /author1 + Authors: author1, author1" + `); + }); +}); diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index eeea1159c932..9cd8580dd7d6 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -58,31 +58,34 @@ const AuthorsMapInputSchema = Joi.object() export function checkPermalinkCollisions( authorsMap: AuthorsMap | undefined, ): void { - const pageAuthorsMap = _.pickBy(authorsMap, (author) => !!author.page); - - const permalinkCounts: {[key: string]: string[]} = {}; - for (const [key, author] of Object.entries(pageAuthorsMap)) { - if (author.page) { - const {permalink} = author.page; - if (!permalinkCounts[permalink]) { - permalinkCounts[permalink] = []; - } - permalinkCounts[permalink]?.push(author.name || key); - } + if (!authorsMap) { + return; } - const collisions = Object.entries(permalinkCounts).filter( - ([, authors]) => authors.length > 1, - ); - - if (collisions.length > 0) { - let errorMessage = 'The following permalinks are duplicated:\n'; - - collisions.forEach(([permalink, authors]) => { - errorMessage += `Permalink: ${permalink}\nAuthors: ${authors.join(', ')}`; - }); - - throw new Error(errorMessage); + const permalinkCounts = _(authorsMap) + // Filter to keep only authors with a page + .pickBy((author) => !!author.page) + // Group authors by their permalink + .groupBy((author) => author.page?.permalink) + // Filter to keep only permalinks with more than one author + .pickBy((authors) => authors.length > 1) + // Transform the object into an array of [permalink, authors] pairs + .toPairs() + .value(); + + if (permalinkCounts.length > 0) { + const errorMessage = permalinkCounts + .map( + ([permalink, authors]) => + `Permalink: ${permalink}\nAuthors: ${authors + .map((author) => author.name || 'Unknown') + .join(', ')}`, + ) + .join('\n'); + + throw new Error( + `The following permalinks are duplicated:\n${errorMessage}`, + ); } } diff --git a/packages/docusaurus-theme-common/package.json b/packages/docusaurus-theme-common/package.json index 523194bcc0e1..ee7bd6d7d7a9 100644 --- a/packages/docusaurus-theme-common/package.json +++ b/packages/docusaurus-theme-common/package.json @@ -41,6 +41,7 @@ "@types/react": "*", "@types/react-router-config": "*", "clsx": "^2.0.0", + "lodash": "^4.17.21", "parse-numeric-range": "^1.3.0", "prism-react-renderer": "^2.3.0", "tslib": "^2.6.0", @@ -50,7 +51,6 @@ "@docusaurus/core": "3.4.0", "@docusaurus/types": "3.4.0", "fs-extra": "^11.1.1", - "lodash": "^4.17.21", "schema-dts": "^1.1.2" }, "peerDependencies": { diff --git a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts index dc2da3841f5b..70e090c8c101 100644 --- a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts @@ -16,6 +16,6 @@ export const translateAuthorsPageTitle = (): string => }); export type AuthorLetterEntry = { - letter: string; + letter: string | undefined; authors: AuthorItemProp[]; }; diff --git a/packages/docusaurus-theme-common/src/utils/generalUtils.ts b/packages/docusaurus-theme-common/src/utils/generalUtils.ts index 9a5041a56633..315030d1b34e 100644 --- a/packages/docusaurus-theme-common/src/utils/generalUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/generalUtils.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import _ from 'lodash'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import type {TagLetterEntry} from './tagsUtils'; import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; @@ -38,25 +39,31 @@ type Entry = HasLabel | HasName; */ function listByLetters( items: readonly T[], - getLabel: (item: T) => string, - mapResult: (letter: string, items: T[]) => R, + getLabel: (item: T) => string | undefined, + mapResult: (letter: string | undefined, items: T[]) => R, ): R[] { - const groups: {[initial: string]: T[]} = {}; - items.forEach((item) => { + // Group items by their initial letter or undefined + const groups = _.groupBy(items, (item) => { const label = getLabel(item); - const initial = label[0]!.toUpperCase(); - groups[initial] ??= []; - groups[initial]!.push(item); + return label ? label[0]!.toUpperCase() : 'undefined'; }); - return Object.entries(groups) - .sort(([letter1], [letter2]) => letter1.localeCompare(letter2)) + // Convert groups object to array and sort + return _.chain(groups) + .toPairs() + .sortBy(([letter]) => (letter === 'undefined' ? '' : letter)) .map(([letter, groupedItems]) => { - const sortedItems = groupedItems.sort((item1, item2) => - getLabel(item1).localeCompare(getLabel(item2)), + // Sort items within each group + const sortedItems = _.sortBy( + groupedItems, + (item) => getLabel(item) ?? '', ); - return mapResult(letter, sortedItems); - }); + return mapResult( + letter === 'undefined' ? undefined : letter, + sortedItems, + ); + }) + .value(); } export function listTagsByLetters( @@ -74,7 +81,7 @@ export function listAuthorsByLetters( ): AuthorLetterEntry[] { return listByLetters( authors, - (author) => author.name ?? author.imageURL ?? '', + (author) => author.name, (letter, items) => ({letter, authors: items}), ); } diff --git a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts index 9f710b0cd8b4..e1ce2ea0acb3 100644 --- a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts @@ -15,4 +15,4 @@ export const translateTagsPageTitle = (): string => description: 'The title of the tag list page', }); -export type TagLetterEntry = {letter: string; tags: TagsListItem[]}; +export type TagLetterEntry = {letter: string | undefined; tags: TagsListItem[]}; From b8a5ae9b6d990f85864fca11116885505bc715a8 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 9 Jul 2024 12:05:27 +0200 Subject: [PATCH 046/115] remove authorKey fallback for name --- .../docusaurus-plugin-content-blog/src/authorsMap.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index 9cd8580dd7d6..b328fca62e0f 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -98,8 +98,6 @@ function normalizeAuthor({ authorKey: string; author: AuthorInput; }): Author { - const name = author.name || authorKey; - function getAuthorPage(): AuthorPage | null { if (!author.page) { return null; @@ -112,14 +110,9 @@ function normalizeAuthor({ } return { + ...author, key: authorKey, page: getAuthorPage(), - name, - imageURL: author.imageURL, - url: author.url, - title: author.title, - email: author.email, - description: author.description, }; } From 9d6d8d6c9d146daa3f1585edb2f39d7e2e4cea4f Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 9 Jul 2024 14:07:20 +0200 Subject: [PATCH 047/115] review --- packages/docusaurus-plugin-content-blog/src/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 06752e1f3c0a..babd0dae86fa 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -161,6 +161,7 @@ export default async function pluginContentBlog( blogTitle, blogSidebarTitle, pageBasePath, + authorsPageBasePath, } = options; const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]); @@ -171,8 +172,8 @@ export default async function pluginContentBlog( authorsMapPath: options.authorsMapPath, authorsBaseRoutePath: normalizeUrl([ context.baseUrl, - options.routeBasePath, - options.authorsPageBasePath, + routeBasePath, + authorsPageBasePath, ]), }); @@ -195,7 +196,7 @@ export default async function pluginContentBlog( blogListPaginated: [], blogTags: {}, blogTagsListPath, - authorsMap: {}, + authorsMap, }; } From 5893259fce56fa6d7d4ce9f12968206faa25a245 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:38:29 +0200 Subject: [PATCH 048/115] fix test --- .../src/__tests__/authors.test.ts | 33 +++++++++++-------- .../src/utils/__tests__/tagUtils.test.ts | 4 +-- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index 1bf83dbaa954..922122101aa0 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -7,8 +7,12 @@ import path from 'path'; import {getBlogPostAuthors} from '../authors'; -import {getAuthorsMap, validateAuthorsMapInput} from '../authorsMap'; -import type {AuthorsMap} from '../authorsMap'; +import { + getAuthorsMap, + validateAuthorsMapInput, +} from '../authorsMap'; +import type { + AuthorsMapInput} from '../authorsMap'; describe('getBlogPostAuthors', () => { it('can read no authors', () => { @@ -525,70 +529,70 @@ describe('getAuthorsMap', () => { describe('validateAuthorsMapInput', () => { it('accept valid authors map', () => { - const authorsMap: AuthorsMap = { + const authorsMap: AuthorsMapInput = { slorber: { name: 'Sébastien Lorber', title: 'maintainer', url: 'https://sebastienlorber.com', imageURL: 'https://github.com/slorber.png', key: 'slorber', - page: null, + page: false, }, yangshun: { name: 'Yangshun Tay', imageURL: 'https://github.com/yangshun.png', randomField: 42, key: 'yangshun', - page: null, + page: false, }, jmarcey: { name: 'Joel', title: 'creator of Docusaurus', hello: new Date(), key: 'jmarcey', - page: null, + page: false, }, }; expect(validateAuthorsMapInput(authorsMap)).toEqual(authorsMap); }); it('rename snake case image_url to camelCase imageURL', () => { - const authorsMap: AuthorsMap = { + const authorsMap: AuthorsMapInput = { slorber: { name: 'Sébastien Lorber', image_url: 'https://github.com/slorber.png', key: 'slorber', - page: null, + page: false, }, }; expect(validateAuthorsMapInput(authorsMap)).toEqual({ slorber: { name: 'Sébastien Lorber', imageURL: 'https://github.com/slorber.png', - page: null, + page: false, key: 'slorber', }, }); }); it('accept author with only image', () => { - const authorsMap: AuthorsMap = { + const authorsMap: AuthorsMapInput = { slorber: { imageURL: 'https://github.com/slorber.png', url: 'https://github.com/slorber', key: 'slorber', - page: null, + page: false, }, }; expect(validateAuthorsMapInput(authorsMap)).toEqual(authorsMap); }); it('reject author without name or image', () => { - const authorsMap: AuthorsMap = { + const authorsMap: AuthorsMapInput = { slorber: { title: 'foo', key: 'slorber', - page: null, + page: false, }, }; expect(() => @@ -643,7 +647,8 @@ describe('validateAuthorsMapInput', () => { }); it('reject non-map author', () => { - const authorsMap: AuthorsMap = { + const authorsMap: AuthorsMapInput = { + // @ts-expect-error: intentionally invalid slorber: [], }; expect(() => diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts index e8087dc03dcf..b0f2c01b419d 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts +++ b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts @@ -51,9 +51,9 @@ describe('listTagsByLetters', () => { }; const expectedResult: Result = [ - {letter: 'A', tags: [tagAaa, tagAbc]}, + {letter: 'A', tags: [tagAbc, tagAaa]}, {letter: 'D', tags: [tagDef]}, - {letter: 'T', tags: [tag1, tag2]}, + {letter: 'T', tags: [tag2, tag1]}, {letter: 'Z', tags: [tagZxy]}, ]; From c280f02b69da27d762807341e87e1862db1581c1 Mon Sep 17 00:00:00 2001 From: OzakIOne Date: Tue, 9 Jul 2024 13:43:08 +0000 Subject: [PATCH 049/115] refactor: apply lint autofix --- .../src/__tests__/authors.test.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index 922122101aa0..66965755d6ab 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -7,12 +7,8 @@ import path from 'path'; import {getBlogPostAuthors} from '../authors'; -import { - getAuthorsMap, - validateAuthorsMapInput, -} from '../authorsMap'; -import type { - AuthorsMapInput} from '../authorsMap'; +import {getAuthorsMap, validateAuthorsMapInput} from '../authorsMap'; +import type {AuthorsMapInput} from '../authorsMap'; describe('getBlogPostAuthors', () => { it('can read no authors', () => { From 78ab5ca91491efacd341ab9db79a85c2dbdc4bdc Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Tue, 9 Jul 2024 16:03:09 +0200 Subject: [PATCH 050/115] refactor review --- .../src/theme/AuthorsListByLetter/index.tsx | 7 ++-- .../src/theme/TagsListByLetter/index.tsx | 11 +++++-- packages/docusaurus-theme-common/src/index.ts | 9 +++-- .../src/utils/authorsUtils.ts | 6 ---- .../src/utils/generalUtils.ts | 33 +++++++------------ .../src/utils/tagsUtils.ts | 3 -- 6 files changed, 28 insertions(+), 41 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx b/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx index 3b468df4bd7d..bdd351972f8a 100644 --- a/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx @@ -6,17 +6,18 @@ */ import React from 'react'; -import {type AuthorLetterEntry} from '@docusaurus/theme-common'; +import {type LetterEntry} from '@docusaurus/theme-common'; import {listAuthorsByLetters} from '@docusaurus/theme-common/internal'; import Author from '@theme/Author'; import type {Props} from '@theme/AuthorsListByLetter'; import Heading from '@theme/Heading'; +import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; import styles from './styles.module.css'; function AuthorLetterEntryItem({ letterEntry, }: { - letterEntry: AuthorLetterEntry; + letterEntry: LetterEntry; }) { return (
      @@ -24,7 +25,7 @@ function AuthorLetterEntryItem({ {letterEntry.letter}
        - {letterEntry.authors.map((author) => ( + {letterEntry.items.map((author) => (
      • diff --git a/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx b/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx index 9f630d3a6199..1bf44d27d5d4 100644 --- a/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx @@ -6,21 +6,26 @@ */ import React from 'react'; -import {type TagLetterEntry} from '@docusaurus/theme-common'; +import {type LetterEntry} from '@docusaurus/theme-common'; import {listTagsByLetters} from '@docusaurus/theme-common/internal'; import Tag from '@theme/Tag'; import type {Props} from '@theme/TagsListByLetter'; import Heading from '@theme/Heading'; +import type {TagsListItem} from '@docusaurus/utils'; import styles from './styles.module.css'; -function TagLetterEntryItem({letterEntry}: {letterEntry: TagLetterEntry}) { +function TagLetterEntryItem({ + letterEntry, +}: { + letterEntry: LetterEntry; +}) { return (
        {letterEntry.letter}
          - {letterEntry.tags.map((tag) => ( + {letterEntry.items.map((tag) => (
        • diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 6ffc23fc1a0f..8b3cf755302b 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -75,12 +75,11 @@ export {useWindowSize} from './hooks/useWindowSize'; * Note: we still guarantee retro-compatibility on those */ -export {translateTagsPageTitle, type TagLetterEntry} from './utils/tagsUtils'; +export {translateTagsPageTitle} from './utils/tagsUtils'; -export { - type AuthorLetterEntry, - translateAuthorsPageTitle, -} from './utils/authorsUtils'; +export {translateAuthorsPageTitle} from './utils/authorsUtils'; + +export {type LetterEntry} from './utils/generalUtils'; export { useSearchQueryString, diff --git a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts index 70e090c8c101..cc05a203e517 100644 --- a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts @@ -6,7 +6,6 @@ */ import {translate} from '@docusaurus/Translate'; -import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; export const translateAuthorsPageTitle = (): string => translate({ @@ -14,8 +13,3 @@ export const translateAuthorsPageTitle = (): string => message: 'Authors', description: 'The title of the author list page', }); - -export type AuthorLetterEntry = { - letter: string | undefined; - authors: AuthorItemProp[]; -}; diff --git a/packages/docusaurus-theme-common/src/utils/generalUtils.ts b/packages/docusaurus-theme-common/src/utils/generalUtils.ts index 315030d1b34e..9ab66d8b6e5b 100644 --- a/packages/docusaurus-theme-common/src/utils/generalUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/generalUtils.ts @@ -7,10 +7,8 @@ import _ from 'lodash'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import type {TagLetterEntry} from './tagsUtils'; import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; import type {TagsListItem} from '@docusaurus/utils'; -import type {AuthorLetterEntry} from './authorsUtils'; /** * Formats the page's title based on relevant site config and other contexts. @@ -33,15 +31,16 @@ interface HasName { type Entry = HasLabel | HasName; +export type LetterEntry = {letter: string | undefined; items: T[]}; + /** * Takes a list of tags or author (as provided by the content plugins), * and groups them by their initials. */ -function listByLetters( +function listByLetters( items: readonly T[], getLabel: (item: T) => string | undefined, - mapResult: (letter: string | undefined, items: T[]) => R, -): R[] { +): LetterEntry[] { // Group items by their initial letter or undefined const groups = _.groupBy(items, (item) => { const label = getLabel(item); @@ -58,30 +57,22 @@ function listByLetters( groupedItems, (item) => getLabel(item) ?? '', ); - return mapResult( - letter === 'undefined' ? undefined : letter, - sortedItems, - ); + return { + letter: letter === 'undefined' ? undefined : letter, + items: sortedItems, + }; }) .value(); } export function listTagsByLetters( tags: readonly TagsListItem[], -): TagLetterEntry[] { - return listByLetters( - tags, - (tag) => tag.label, - (letter, items) => ({letter, tags: items}), - ); +): LetterEntry[] { + return listByLetters(tags, (tag) => tag.label); } export function listAuthorsByLetters( authors: readonly AuthorItemProp[], -): AuthorLetterEntry[] { - return listByLetters( - authors, - (author) => author.name, - (letter, items) => ({letter, authors: items}), - ); +): LetterEntry[] { + return listByLetters(authors, (author) => author.name); } diff --git a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts index e1ce2ea0acb3..e3c37e59e2b8 100644 --- a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts @@ -6,7 +6,6 @@ */ import {translate} from '@docusaurus/Translate'; -import type {TagsListItem} from '@docusaurus/utils'; export const translateTagsPageTitle = (): string => translate({ @@ -14,5 +13,3 @@ export const translateTagsPageTitle = (): string => message: 'Tags', description: 'The title of the tag list page', }); - -export type TagLetterEntry = {letter: string | undefined; tags: TagsListItem[]}; From 12962f48bc1bb5377066adeb231497576ca2f03f Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:21:52 +0200 Subject: [PATCH 051/115] refactor --- .../src/authorsMap.ts | 2 +- .../src/plugin-content-blog.d.ts | 10 -------- .../src/props.ts | 23 +------------------ .../src/routes.ts | 15 ++---------- .../src/utils/__tests__/tagUtils.test.ts | 8 +++---- 5 files changed, 8 insertions(+), 50 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index b328fca62e0f..36d38cdb98f9 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -20,7 +20,7 @@ type AuthorInput = AuthorAttributes & { page?: boolean | AuthorPage; }; -type AuthorsMapInput = {[authorKey: string]: AuthorInput}; +export type AuthorsMapInput = {[authorKey: string]: AuthorInput}; const AuthorPageSchema = Joi.object({ permalink: Joi.string().required(), diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 1d6ddab93ded..83038496458e 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -538,16 +538,6 @@ declare module '@docusaurus/plugin-content-blog' { unlisted: boolean; }; - export type BlogPageAuthors = { - [permalink: string]: BlogPageAuthor; - }; - - export type BlogPageAuthor = Author & { - /** Blog post permalinks. */ - items: string[]; - pages: BlogPaginated[]; - }; - export type BlogPost = { id: string; metadata: BlogPostMetadata; diff --git a/packages/docusaurus-plugin-content-blog/src/props.ts b/packages/docusaurus-plugin-content-blog/src/props.ts index 778d95cf6dcf..e3605f2f45e8 100644 --- a/packages/docusaurus-plugin-content-blog/src/props.ts +++ b/packages/docusaurus-plugin-content-blog/src/props.ts @@ -8,7 +8,6 @@ import type {TagsListItem, TagModule} from '@docusaurus/utils'; import type { Author, AuthorItemProp, - BlogPageAuthors, BlogPost, BlogSidebar, BlogTag, @@ -43,27 +42,7 @@ export function toTagProp({ }; } -export function toAuthorItemsProp({ - blogPageAuthors, -}: { - blogPageAuthors: BlogPageAuthors; -}): AuthorItemProp[] { - return Object.values(blogPageAuthors).map((author) => ({ - ...author, - count: author.items.length, - })); -} - -// TODO -// export function toAuthorProp({author}: {author: Author}): AuthorItemProp { -// return { -// ...author, -// // TODO -// count: 1, -// }; -// } - -export function toAuthorPageProp({ +export function toAuthorProp({ author, count, }: { diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index 5e0c2e7c7439..2c4ca988e263 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -13,14 +13,7 @@ import { } from '@docusaurus/utils'; import {paginateBlogPosts, shouldBeListed} from './blogUtils'; -import { - toAuthorPageProp, - // toAuthorProp, - // toAuthorItemsProp, - toBlogSidebarProp, - toTagProp, - toTagsProp, -} from './props'; +import {toAuthorProp, toBlogSidebarProp, toTagProp, toTagsProp} from './props'; import {getBlogPostsForAuthorKey} from './authors'; import type { PluginContentLoadedActions, @@ -34,9 +27,6 @@ import type { BlogContent, PluginOptions, BlogPost, - // BlogPageAuthor, - // BlogPageAuthors, - // AuthorsMap, Author, } from '@docusaurus/plugin-content-blog'; @@ -76,7 +66,6 @@ export async function buildAllRoutes({ authorsPageBasePath, postsPerPage, blogDescription, - // pageBasePath, } = options; const pluginId = options.id!; const {createData} = actions; @@ -334,7 +323,7 @@ export async function buildAllRoutes({ sidebar: sidebarModulePath, }, props: { - author: toAuthorPageProp({author, count: authorBlogPosts.length}), + author: toAuthorProp({author, count: authorBlogPosts.length}), listMetadata: metadata, allAuthorsPath: blogAuthorsListPath, }, diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts index b0f2c01b419d..857ba3f6468e 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts +++ b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts @@ -51,10 +51,10 @@ describe('listTagsByLetters', () => { }; const expectedResult: Result = [ - {letter: 'A', tags: [tagAbc, tagAaa]}, - {letter: 'D', tags: [tagDef]}, - {letter: 'T', tags: [tag2, tag1]}, - {letter: 'Z', tags: [tagZxy]}, + {letter: 'A', items: [tagAbc, tagAaa]}, + {letter: 'D', items: [tagDef]}, + {letter: 'T', items: [tag2, tag1]}, + {letter: 'Z', items: [tagZxy]}, ]; // Input order shouldn't matter, output is always consistently sorted From 5693caf80f6a0d18947add75bc53ff57bf8528fc Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:41:23 +0200 Subject: [PATCH 052/115] fix tests --- .../__snapshots__/index.test.ts.snap | 5 ++--- .../src/__tests__/feed.test.ts | 11 ++++++++++- .../src/__tests__/index.test.ts | 19 +++++++++++++------ .../src/index.ts | 8 +++----- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap index 7c4102bef728..30c280918767 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap @@ -121,7 +121,9 @@ exports[`blog plugin process blog posts load content 2`] = ` "authors": [ { "imageURL": undefined, + "key": null, "name": "Sébastien Lorber", + "page": null, "title": "Docusaurus maintainer", "url": "https://sebastienlorber.com", }, @@ -147,7 +149,6 @@ exports[`blog plugin process blog posts load content 2`] = ` "permalink": "/blog/another/tags", "title": "Another With Tag", }, - "pageAuthors": [], "permalink": "/blog/simple/slug/another", "readingTime": 0.015, "source": "@site/blog/another-simple-slug-with-tags.md", @@ -187,7 +188,6 @@ exports[`blog plugin process blog posts load content 2`] = ` "permalink": "/blog/another/tags2", "title": "Another With Tag", }, - "pageAuthors": [], "permalink": "/blog/another/tags", "prevItem": { "permalink": "/blog/simple/slug/another", @@ -233,7 +233,6 @@ exports[`blog plugin process blog posts load content 2`] = ` "hasTruncateMarker": false, "lastUpdatedAt": undefined, "lastUpdatedBy": undefined, - "pageAuthors": [], "permalink": "/blog/another/tags2", "prevItem": { "permalink": "/blog/another/tags", diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts index 3fbd4dbf27fd..ef57b4c7d63e 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts @@ -13,6 +13,7 @@ import {fromPartial} from '@total-typescript/shoehorn'; import {DEFAULT_OPTIONS} from '../options'; import {generateBlogPosts} from '../blogUtils'; import {createBlogFeedFiles} from '../feed'; +import {getAuthorsMap} from '../authorsMap'; import type {LoadContext, I18n} from '@docusaurus/types'; import type {BlogContentPaths} from '../types'; import type {PluginOptions} from '@docusaurus/plugin-content-blog'; @@ -51,10 +52,18 @@ async function testGenerateFeeds( context: LoadContext, options: PluginOptions, ): Promise { + const contentPaths = getBlogContentPaths(context.siteDir); + const authorsMap = await getAuthorsMap({ + contentPaths, + authorsMapPath: options.authorsMapPath, + authorsBaseRoutePath: '/authors', + }); + const blogPosts = await generateBlogPosts( - getBlogContentPaths(context.siteDir), + contentPaths, context, options, + authorsMap, ); await createBlogFeedFiles({ diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts index 2e2ad99aa1d7..075fd85e8162 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts @@ -196,7 +196,8 @@ describe('blog plugin', () => { permalink: '/blog/tags/date', }, ], - pageAuthors: [], + lastUpdatedAt: undefined, + lastUpdatedBy: undefined, nextItem: { permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash', title: 'Happy 1st Birthday Slash! (translated)', @@ -222,6 +223,8 @@ describe('blog plugin', () => { { name: 'Yangshun Tay (translated)', imageURL: undefined, + key: null, + page: null, }, { email: 'lorber.sebastien@gmail.com', @@ -229,6 +232,7 @@ describe('blog plugin', () => { name: 'Sébastien Lorber (translated)', title: 'Docusaurus maintainer (translated)', imageURL: undefined, + page: null, }, ], date: new Date('2018-12-14'), @@ -256,7 +260,8 @@ describe('blog plugin', () => { permalink: '/blog/tags/global-tag-permalink (en)', }, ], - pageAuthors: [], + lastUpdatedAt: undefined, + lastUpdatedBy: undefined, prevItem: { permalink: '/blog/date-matter', title: 'date-matter', @@ -303,7 +308,6 @@ describe('blog plugin', () => { permalink: '/blog/tags/complex', }, ], - pageAuthors: [], hasTruncateMarker: false, unlisted: false, }); @@ -324,6 +328,8 @@ describe('blog plugin', () => { title: 'Docusaurus maintainer', url: 'https://sebastienlorber.com', imageURL: undefined, + page: null, + key: null, }, ], prevItem: undefined, @@ -341,7 +347,8 @@ describe('blog plugin', () => { title: 'Simple Slug', }, tags: [], - pageAuthors: [], + lastUpdatedAt: undefined, + lastUpdatedBy: undefined, hasTruncateMarker: false, unlisted: false, }); @@ -363,7 +370,6 @@ describe('blog plugin', () => { }, prevItem: undefined, tags: [], - pageAuthors: [], nextItem: { permalink: '/blog/date-matter', title: 'date-matter', @@ -488,9 +494,10 @@ describe('blog plugin', () => { date: noDateSourceTime, frontMatter: {}, tags: [], - pageAuthors: [], prevItem: undefined, nextItem: undefined, + lastUpdatedAt: undefined, + lastUpdatedBy: undefined, hasTruncateMarker: false, unlisted: false, }); diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index babd0dae86fa..e8d23a2678ca 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -162,6 +162,7 @@ export default async function pluginContentBlog( blogSidebarTitle, pageBasePath, authorsPageBasePath, + authorsMapPath, } = options; const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]); @@ -169,9 +170,9 @@ export default async function pluginContentBlog( const authorsMap = await getAuthorsMap({ contentPaths, - authorsMapPath: options.authorsMapPath, + authorsMapPath, authorsBaseRoutePath: normalizeUrl([ - context.baseUrl, + baseUrl, routeBasePath, authorsPageBasePath, ]), @@ -239,9 +240,6 @@ export default async function pluginContentBlog( pageBasePath, }); - // TODO: put "AuthorsMap" in Content - // Move blog post author pages aggregation logic in contentLoaded() - return { blogSidebarTitle, blogPosts, From 2e1fc0337a336a286611f4ab1b39faf5261d68b8 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 10 Jul 2024 15:22:34 +0200 Subject: [PATCH 053/115] refactor --- .../src/__tests__/authorsMap.test.ts | 61 ++++++++++ .../src/__tests__/authorsProblems.test.ts | 114 ++---------------- .../src/authors.ts | 3 +- .../src/plugin-content-blog.d.ts | 3 +- .../src/routes.ts | 19 +-- .../src/theme/BlogAuthorsPostsPage/index.tsx | 5 +- .../locales/base/theme-common.json | 1 + 7 files changed, 92 insertions(+), 114 deletions(-) create mode 100644 packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts new file mode 100644 index 000000000000..d9d894978b4d --- /dev/null +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts @@ -0,0 +1,61 @@ +/** + * 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 {checkPermalinkCollisions} from '../authorsMap'; +import type {AuthorsMap} from '@docusaurus/plugin-content-blog'; + +describe('authors permalink collision', () => { + it('no collision', () => { + const authors: AuthorsMap = { + author1: { + name: 'author1', + key: 'author1', + page: { + permalink: '/author1', + }, + }, + author2: { + name: 'author2', + key: 'author2', + page: { + permalink: '/author2', + }, + }, + }; + + expect(() => { + checkPermalinkCollisions(authors); + }).not.toThrow(); + }); + + it('collision', () => { + const authors: AuthorsMap = { + author1: { + name: 'author1', + key: 'author1', + page: { + permalink: '/author1', + }, + }, + author2: { + name: 'author1', + key: 'author1', + page: { + permalink: '/author1', + }, + }, + }; + + expect(() => { + checkPermalinkCollisions(authors); + }).toThrowErrorMatchingInlineSnapshot(` + "The following permalinks are duplicated: + Permalink: /author1 + Authors: author1, author1" + `); + }); +}); diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts index 13e89de0b9d8..16746eeecc42 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts @@ -5,15 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import {jest} from '@jest/globals'; import {reportDuplicateAuthors, reportInlineAuthors} from '../authorsProblems'; -import {checkPermalinkCollisions} from '../authorsMap'; -import type {Author, AuthorsMap} from '@docusaurus/plugin-content-blog'; +import type {Author} from '@docusaurus/plugin-content-blog'; const blogSourceRelative = 'doc.md'; -// TODO update TS types - describe('duplicate authors', () => { function testReport({authors}: {authors: Author[]}) { reportDuplicateAuthors({ @@ -26,9 +22,13 @@ describe('duplicate authors', () => { const authors: Author[] = [ { name: 'Sébastien Lorber', + key: 'slorber', + page: null, }, { name: 'Sébastien Lorber', + key: 'slorber', + page: null, }, ]; @@ -45,11 +45,13 @@ describe('duplicate authors', () => { key: 'slorber', name: 'Sébastien Lorber 1', title: 'some title', + page: null, }, { key: 'slorber', name: 'Sébastien Lorber 2', imageURL: '/slorber.png', + page: null, }, ]; @@ -94,10 +96,12 @@ describe('inline authors', () => { { key: 'slorber', name: 'Sébastien Lorber', + page: null, }, { key: 'ozaki', name: 'Clément Couriol', + page: null, }, ]; @@ -113,13 +117,15 @@ describe('inline authors', () => { { key: 'slorber', name: 'Sébastien Lorber', + page: null, }, - {name: 'Inline author 1'}, + {name: 'Inline author 1', page: null, key: null}, { key: 'ozaki', name: 'Clément Couriol', + page: null, }, - {imageURL: '/inline-author2.png'}, + {imageURL: '/inline-author2.png', page: null, key: null}, ]; expect(() => @@ -137,98 +143,4 @@ describe('inline authors', () => { " `); }); - - it('warn inline authors', () => { - const authors: Author[] = [ - { - key: 'slorber', - name: 'Sébastien Lorber', - }, - {name: 'Inline author 1'}, - { - key: 'ozaki', - name: 'Clément Couriol', - }, - {imageURL: '/inline-author2.png'}, - ]; - - const consoleMock = jest - .spyOn(console, 'warn') - .mockImplementation(() => {}); - - expect(() => - testReport({ - authors, - options: { - onInlineAuthors: 'warn', - }, - }), - ).not.toThrow(); - expect(consoleMock).toHaveBeenCalledTimes(1); - expect(consoleMock.mock.calls[0]).toMatchInlineSnapshot(` - [ - "[WARNING] Some blog authors used in "doc.md" are not defined in "authors.yml": - - {"name":"Inline author 1"} - - {"imageURL":"/inline-author2.png"} - - Note that we recommend to declare authors once in a "authors.yml" file and reference them by key in blog posts front matter to avoid author info duplication. - But if you want to allow inline blog authors, you can disable this message by setting onInlineAuthors: 'ignore' in your blog plugin options. - More info at https://docusaurus.io/docs/blog - ", - ] - `); - }); -}); - -// TODO move to authorsMap.test.ts ? -describe('authors permalink collision', () => { - it('no collision', () => { - const authors: AuthorsMap = { - author1: { - name: 'author1', - key: 'author1', - page: { - permalink: '/author1', - }, - }, - author2: { - name: 'author2', - key: 'author2', - page: { - permalink: '/author2', - }, - }, - }; - - expect(() => { - checkPermalinkCollisions(authors); - }).not.toThrow(); - }); - - it('collision', () => { - const authors: AuthorsMap = { - author1: { - name: 'author1', - key: 'author1', - page: { - permalink: '/author1', - }, - }, - author2: { - name: 'author1', - key: 'author1', - page: { - permalink: '/author1', - }, - }, - }; - - expect(() => { - checkPermalinkCollisions(authors); - }).toThrowErrorMatchingInlineSnapshot(` - "The following permalinks are duplicated: - Permalink: /author1 - Authors: author1, author1" - `); - }); }); diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 895a628ba218..080e9bb00c46 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -147,9 +147,8 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t } /** - * Blog posts are grouped by author page permalink (if page exists) + * Blog posts grouped by author page permalink (if page exists) */ -// TODO would be better if iterated over the AuthorsMap instead of the blogPosts export function getBlogPostsForAuthorKey({ blogPosts, authorsMap, diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 83038496458e..448979362159 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -712,9 +712,8 @@ declare module '@theme/BlogAuthorsPostsPage' { readonly sidebar: BlogSidebar; /** Metadata of this author. */ readonly author: AuthorItemProp; - // TODO /** Link to the page of all authors. */ - readonly allAuthorsPath: string; + readonly authorsPageLink: string; /** Looks exactly the same as the posts list page */ readonly listMetadata: BlogPaginatedMetadata; /** diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index 2c4ca988e263..aae5dec4d34d 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -258,11 +258,15 @@ export async function buildAllRoutes({ } async function createAuthorsRoutes(): Promise { + if (authorsMap === undefined) { + return []; + } + const blogPostsByAuthorKey = getBlogPostsForAuthorKey({ authorsMap, blogPosts, }); - const authors = Object.values(authorsMap ?? {}); + const authors = Object.values(authorsMap); const blogAuthorsListPath = normalizeUrl([ baseUrl, @@ -272,7 +276,9 @@ export async function buildAllRoutes({ return Promise.all([ createAuthorListRoute(), - ...authors.flatMap(createAuthorPaginatedRoute), + ...Object.entries(authorsMap).flatMap(([authorKey, author]) => + createAuthorPaginatedRoute(authorKey, author), + ), ]).then((routes) => routes.flat()); // Maybe authors with page: false could even appear on the list? @@ -292,15 +298,14 @@ export async function buildAllRoutes({ } async function createAuthorPaginatedRoute( + authorKey: string, author: Author, ): Promise { - const authorBlogPosts = blogPostsByAuthorKey[author.key ?? '']; - // TODO - if (!author.page || authorBlogPosts === undefined) { + const authorBlogPosts = blogPostsByAuthorKey[authorKey]; + if (!author.page || !authorBlogPosts) { return []; } - // TODO ugly ? const data = { items: authorBlogPosts.map((post) => post.id), pages: paginateBlogPosts({ @@ -325,7 +330,7 @@ export async function buildAllRoutes({ props: { author: toAuthorProp({author, count: authorBlogPosts.length}), listMetadata: metadata, - allAuthorsPath: blogAuthorsListPath, + authorsPageLink: blogAuthorsListPath, }, }; }); diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx index 8020d408b990..e0d0408dbfe2 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx @@ -52,7 +52,7 @@ function BlogAuthorsPostsPageContent({ items, sidebar, listMetadata, - allAuthorsPath, + authorsPageLink, }: Props): JSX.Element { const title = useBlogAuthorsPostsPageTitle(author); return ( @@ -73,7 +73,7 @@ function BlogAuthorsPostsPageContent({ )} {author.description &&
        • {author.description}
        • }
        - + @@ -87,6 +87,7 @@ function BlogAuthorsPostsPageContent({ ); } export default function BlogAuthorsPostsPage(props: Props): JSX.Element { + console.log('props: ', props); return ( Date: Wed, 10 Jul 2024 15:39:16 +0200 Subject: [PATCH 054/115] refactor --- .../src/__tests__/authorsMap.test.ts | 4 ++-- .../src/__tests__/authorsProblems.test.ts | 10 +++++----- .../docusaurus-plugin-content-blog/src/authorsMap.ts | 1 - packages/docusaurus-plugin-content-blog/src/index.ts | 3 ++- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts index d9d894978b4d..f5307c6f2bbb 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts @@ -9,7 +9,7 @@ import {checkPermalinkCollisions} from '../authorsMap'; import type {AuthorsMap} from '@docusaurus/plugin-content-blog'; describe('authors permalink collision', () => { - it('no collision', () => { + it('dont throw when permalinks are unique', () => { const authors: AuthorsMap = { author1: { name: 'author1', @@ -32,7 +32,7 @@ describe('authors permalink collision', () => { }).not.toThrow(); }); - it('collision', () => { + it('throw when permalinks collide', () => { const authors: AuthorsMap = { author1: { name: 'author1', diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts index 16746eeecc42..ec64b3f225a0 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsProblems.test.ts @@ -22,12 +22,12 @@ describe('duplicate authors', () => { const authors: Author[] = [ { name: 'Sébastien Lorber', - key: 'slorber', + key: null, page: null, }, { name: 'Sébastien Lorber', - key: 'slorber', + key: null, page: null, }, ]; @@ -61,7 +61,7 @@ describe('duplicate authors', () => { }), ).toThrowErrorMatchingInlineSnapshot(` "Duplicate blog post authors were found in blog post "doc.md" front matter: - - {"key":"slorber","name":"Sébastien Lorber 2","imageURL":"/slorber.png"}" + - {"key":"slorber","name":"Sébastien Lorber 2","imageURL":"/slorber.png","page":null}" `); }); }); @@ -134,8 +134,8 @@ describe('inline authors', () => { }), ).toThrowErrorMatchingInlineSnapshot(` "Some blog authors used in "doc.md" are not defined in "authors.yml": - - {"name":"Inline author 1"} - - {"imageURL":"/inline-author2.png"} + - {"name":"Inline author 1","page":null,"key":null} + - {"imageURL":"/inline-author2.png","page":null,"key":null} Note that we recommend to declare authors once in a "authors.yml" file and reference them by key in blog posts front matter to avoid author info duplication. But if you want to allow inline blog authors, you can disable this message by setting onInlineAuthors: 'ignore' in your blog plugin options. diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index 36d38cdb98f9..52304518c07d 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -160,6 +160,5 @@ export async function getAuthorsMap(params: { return undefined; } const authorsMap = normalizeAuthorsMap({authorsMapInput, ...params}); - checkPermalinkCollisions(authorsMap); return authorsMap; } diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index e8d23a2678ca..865ea86170d1 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -34,7 +34,7 @@ import {translateContent, getTranslationFiles} from './translations'; import {createBlogFeedFiles, createFeedHtmlHeadTags} from './feed'; import {createAllRoutes} from './routes'; -import {getAuthorsMap} from './authorsMap'; +import {checkPermalinkCollisions, getAuthorsMap} from './authorsMap'; import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types'; import type {LoadContext, Plugin} from '@docusaurus/types'; import type { @@ -177,6 +177,7 @@ export default async function pluginContentBlog( authorsPageBasePath, ]), }); + checkPermalinkCollisions(authorsMap); let blogPosts = await generateBlogPosts( contentPaths, From bef4e4d02e2a1929e911808fceebca3cec56bee3 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 10 Jul 2024 15:52:54 +0200 Subject: [PATCH 055/115] remove unused key --- packages/docusaurus-plugin-content-blog/src/authorsMap.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index 52304518c07d..ce4e06f16bec 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -36,7 +36,6 @@ const AuthorsMapInputSchema = Joi.object() title: Joi.string(), email: Joi.string(), page: Joi.alternatives(Joi.bool(), AuthorPageSchema), - permalink: Joi.string(), description: Joi.string(), }) .rename('image_url', 'imageURL') From bb8e55404da249c30fd370a98d444b3e46d2567e Mon Sep 17 00:00:00 2001 From: OzakIOne Date: Wed, 10 Jul 2024 13:59:01 +0000 Subject: [PATCH 056/115] refactor: apply lint autofix --- project-words.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/project-words.txt b/project-words.txt index 8b8a8f463f28..0730f0ee9368 100644 --- a/project-words.txt +++ b/project-words.txt @@ -82,6 +82,7 @@ Dogfood dogfooding Dogfooding Dojocat +dont dyte Dyte easyops From e29650da3c23ce03424e4b84a239986cf666b998 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 10 Jul 2024 16:00:18 +0200 Subject: [PATCH 057/115] autolint --- .../src/__tests__/authorsMap.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts index f5307c6f2bbb..f834323f10d1 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts @@ -9,7 +9,7 @@ import {checkPermalinkCollisions} from '../authorsMap'; import type {AuthorsMap} from '@docusaurus/plugin-content-blog'; describe('authors permalink collision', () => { - it('dont throw when permalinks are unique', () => { + it('do not throw when permalinks are unique', () => { const authors: AuthorsMap = { author1: { name: 'author1', From 8e514535b8ec0855413f30c40ae9d0b4712288b0 Mon Sep 17 00:00:00 2001 From: OzakIOne Date: Wed, 10 Jul 2024 14:04:53 +0000 Subject: [PATCH 058/115] refactor: apply lint autofix --- project-words.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/project-words.txt b/project-words.txt index 0730f0ee9368..8b8a8f463f28 100644 --- a/project-words.txt +++ b/project-words.txt @@ -82,7 +82,6 @@ Dogfood dogfooding Dogfooding Dojocat -dont dyte Dyte easyops From 72c3923a0a96ed8f5ce71eb3eea96629704453bc Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Fri, 12 Jul 2024 13:44:02 +0200 Subject: [PATCH 059/115] refactor --- packages/docusaurus-theme-common/package.json | 2 +- .../src/utils/__tests__/generalUtils.test.tsx | 52 ++++++++++++++++++- .../src/utils/__tests__/tagUtils.test.ts | 4 +- .../src/utils/generalUtils.ts | 41 +++++++++------ 4 files changed, 80 insertions(+), 19 deletions(-) diff --git a/packages/docusaurus-theme-common/package.json b/packages/docusaurus-theme-common/package.json index ee7bd6d7d7a9..523194bcc0e1 100644 --- a/packages/docusaurus-theme-common/package.json +++ b/packages/docusaurus-theme-common/package.json @@ -41,7 +41,6 @@ "@types/react": "*", "@types/react-router-config": "*", "clsx": "^2.0.0", - "lodash": "^4.17.21", "parse-numeric-range": "^1.3.0", "prism-react-renderer": "^2.3.0", "tslib": "^2.6.0", @@ -51,6 +50,7 @@ "@docusaurus/core": "3.4.0", "@docusaurus/types": "3.4.0", "fs-extra": "^11.1.1", + "lodash": "^4.17.21", "schema-dts": "^1.1.2" }, "peerDependencies": { diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx b/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx index 02c2ad551993..4ca2ba04b54f 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx +++ b/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx @@ -8,7 +8,13 @@ import React from 'react'; import {renderHook} from '@testing-library/react-hooks'; import {Context} from '@docusaurus/core/src/client/docusaurusContext'; -import {useTitleFormatter} from '../generalUtils'; +import { + listByLetters, + useTitleFormatter, +} from '../generalUtils'; +import type { + Entry, + LetterEntry} from '../generalUtils'; import type {DocusaurusContext} from '@docusaurus/types'; describe('useTitleFormatter', () => { @@ -31,3 +37,47 @@ describe('useTitleFormatter', () => { expect(mockUseTitleFormatter(' ')).toBe('my site'); }); }); + +describe('listByLetters', () => { + it('group items by their initial letters', () => { + const items: Entry[] = [ + {label: 'Apple'}, + {label: 'Banana'}, + {label: 'apricot'}, + {name: 'Alice'}, + {name: 'Bob'}, + {name: 'Albert'}, + {label: 'avocado'}, + {name: undefined}, + ]; + + const result = listByLetters(items, (item) => + 'label' in item ? item.label : item.name, + ); + + const expected: LetterEntry[] = [ + { + letter: 'A', + items: [ + {name: 'Albert'}, + {name: 'Alice'}, + {label: 'Apple'}, + {label: 'apricot'}, + {label: 'avocado'}, + ], + }, + {letter: 'B', items: [{label: 'Banana'}, {name: 'Bob'}]}, + {letter: undefined, items: [{name: undefined}]}, + ]; + + expect(result).toEqual(expected); + }); + + it('handle empty input', () => { + const result = listByLetters([], (item) => + // @ts-expect-error: test edge case + 'label' in item ? item.label : item.name, + ); + expect(result).toEqual([]); + }); +}); diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts index 857ba3f6468e..0b54d9d064b3 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts +++ b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts @@ -51,9 +51,9 @@ describe('listTagsByLetters', () => { }; const expectedResult: Result = [ - {letter: 'A', items: [tagAbc, tagAaa]}, + {letter: 'A', items: [tagAaa, tagAbc]}, {letter: 'D', items: [tagDef]}, - {letter: 'T', items: [tag2, tag1]}, + {letter: 'T', items: [tag1, tag2]}, {letter: 'Z', items: [tagZxy]}, ]; diff --git a/packages/docusaurus-theme-common/src/utils/generalUtils.ts b/packages/docusaurus-theme-common/src/utils/generalUtils.ts index 9ab66d8b6e5b..5edc6c32c4cb 100644 --- a/packages/docusaurus-theme-common/src/utils/generalUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/generalUtils.ts @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import _ from 'lodash'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; import type {TagsListItem} from '@docusaurus/utils'; @@ -29,7 +28,7 @@ interface HasName { imageURL?: string; } -type Entry = HasLabel | HasName; +export type Entry = HasLabel | HasName; export type LetterEntry = {letter: string | undefined; items: T[]}; @@ -37,32 +36,44 @@ export type LetterEntry = {letter: string | undefined; items: T[]}; * Takes a list of tags or author (as provided by the content plugins), * and groups them by their initials. */ -function listByLetters( +export function listByLetters( items: readonly T[], getLabel: (item: T) => string | undefined, ): LetterEntry[] { // Group items by their initial letter or undefined - const groups = _.groupBy(items, (item) => { + const groups: Record = items.reduce((acc, item) => { const label = getLabel(item); - return label ? label[0]!.toUpperCase() : 'undefined'; - }); + const key = label ? label[0]!.toUpperCase() : 'undefined'; + if (!acc[key]) { + acc[key] = []; + } + acc[key].push(item); + return acc; + }, {} as Record); // Convert groups object to array and sort - return _.chain(groups) - .toPairs() - .sortBy(([letter]) => (letter === 'undefined' ? '' : letter)) + return Object.entries(groups) + .sort(([a], [b]) => { + if (a === 'undefined') { + return 1; + } else if (b === 'undefined') { + return -1; + } else { + return a.localeCompare(b); + } + }) .map(([letter, groupedItems]) => { // Sort items within each group - const sortedItems = _.sortBy( - groupedItems, - (item) => getLabel(item) ?? '', - ); + const sortedItems = groupedItems.slice().sort((a, b) => { + const labelA = getLabel(a) ?? ''; + const labelB = getLabel(b) ?? ''; + return labelA.localeCompare(labelB); + }); return { letter: letter === 'undefined' ? undefined : letter, items: sortedItems, }; - }) - .value(); + }); } export function listTagsByLetters( From a3c75ed302e89a69300f609be06140814f0eaa33 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:45:07 +0200 Subject: [PATCH 060/115] refactor --- .../src/authors.ts | 2 +- .../src/routes.ts | 66 ++++++++++++------- website/_dogfooding/_blog tests/authors.yml | 5 ++ 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 080e9bb00c46..90abc25ee750 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -149,7 +149,7 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t /** * Blog posts grouped by author page permalink (if page exists) */ -export function getBlogPostsForAuthorKey({ +export function groupBlogPostsByAuthorKey({ blogPosts, authorsMap, }: { diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index aae5dec4d34d..8d2f00a069e7 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -14,7 +14,7 @@ import { import {paginateBlogPosts, shouldBeListed} from './blogUtils'; import {toAuthorProp, toBlogSidebarProp, toTagProp, toTagsProp} from './props'; -import {getBlogPostsForAuthorKey} from './authors'; +import {groupBlogPostsByAuthorKey} from './authors'; import type { PluginContentLoadedActions, RouteConfig, @@ -262,13 +262,13 @@ export async function buildAllRoutes({ return []; } - const blogPostsByAuthorKey = getBlogPostsForAuthorKey({ + const blogPostsByAuthorKey = groupBlogPostsByAuthorKey({ authorsMap, blogPosts, }); - const authors = Object.values(authorsMap); + const authorEntries = Object.entries(authorsMap); - const blogAuthorsListPath = normalizeUrl([ + const authorsPageLink = normalizeUrl([ baseUrl, routeBasePath, authorsPageBasePath, @@ -276,23 +276,26 @@ export async function buildAllRoutes({ return Promise.all([ createAuthorListRoute(), - ...Object.entries(authorsMap).flatMap(([authorKey, author]) => + ...authorEntries.flatMap(([authorKey, author]) => createAuthorPaginatedRoute(authorKey, author), ), ]).then((routes) => routes.flat()); - // Maybe authors with page: false could even appear on the list? - // to be defined 🤷 async function createAuthorListRoute(): Promise { return { - path: blogAuthorsListPath, + path: authorsPageLink, component: blogAuthorsListComponent, exact: true, modules: { sidebar: sidebarModulePath, }, props: { - authors, + authors: authorEntries.map(([authorKey, author]) => + toAuthorProp({ + author, + count: blogPostsByAuthorKey[authorKey]?.length ?? 0, + }), + ), }, }; } @@ -301,24 +304,39 @@ export async function buildAllRoutes({ authorKey: string, author: Author, ): Promise { - const authorBlogPosts = blogPostsByAuthorKey[authorKey]; - if (!author.page || !authorBlogPosts) { + const authorBlogPosts = blogPostsByAuthorKey[authorKey] ?? []; + if (!author.page) { return []; } - const data = { - items: authorBlogPosts.map((post) => post.id), - pages: paginateBlogPosts({ - blogPosts: authorBlogPosts, - basePageUrl: author.page.permalink, - blogDescription, - blogTitle, - pageBasePath: authorsPageBasePath, - postsPerPageOption: postsPerPage, - }), - }; + // TODO add tests + const pages = paginateBlogPosts({ + blogPosts: authorBlogPosts, + basePageUrl: author.page.permalink, + blogDescription, + blogTitle, + pageBasePath: authorsPageBasePath, + postsPerPageOption: postsPerPage, + }); + + if (pages.length === 0) { + pages.push({ + items: [], + metadata: { + permalink: author.page.permalink, + page: 0, + postsPerPage: 5, + totalPages: 0, + totalCount: 0, + previousPage: undefined, + nextPage: undefined, + blogDescription, + blogTitle, + }, + }); + } - return data.pages.map(({metadata, items}) => { + return pages.map(({metadata, items}) => { return { path: metadata.permalink, component: blogAuthorsPostsComponent, @@ -330,7 +348,7 @@ export async function buildAllRoutes({ props: { author: toAuthorProp({author, count: authorBlogPosts.length}), listMetadata: metadata, - authorsPageLink: blogAuthorsListPath, + authorsPageLink, }, }; }); diff --git a/website/_dogfooding/_blog tests/authors.yml b/website/_dogfooding/_blog tests/authors.yml index fc6a50b90bc1..90da02321ef6 100644 --- a/website/_dogfooding/_blog tests/authors.yml +++ b/website/_dogfooding/_blog tests/authors.yml @@ -4,3 +4,8 @@ slorber: url: https://sebastienlorber.com image_url: https://github.com/slorber.png twitter: sebastienlorber + page: true + +ozaki: + name: ozaki + page: true From 4d370b99aa92ff4b5efd64e900801e415e4fafc7 Mon Sep 17 00:00:00 2001 From: OzakIOne Date: Fri, 12 Jul 2024 16:06:44 +0000 Subject: [PATCH 061/115] refactor: apply lint autofix --- .../src/utils/__tests__/generalUtils.test.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx b/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx index 4ca2ba04b54f..20bbf920459b 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx +++ b/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx @@ -8,13 +8,8 @@ import React from 'react'; import {renderHook} from '@testing-library/react-hooks'; import {Context} from '@docusaurus/core/src/client/docusaurusContext'; -import { - listByLetters, - useTitleFormatter, -} from '../generalUtils'; -import type { - Entry, - LetterEntry} from '../generalUtils'; +import {listByLetters, useTitleFormatter} from '../generalUtils'; +import type {Entry, LetterEntry} from '../generalUtils'; import type {DocusaurusContext} from '@docusaurus/types'; describe('useTitleFormatter', () => { From 1c410e12a13b7764e843486fa3c72b1f4a137206 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:32:40 +0200 Subject: [PATCH 062/115] refactor --- .../src/__tests__/authors.test.ts | 13 +++- .../src/authors.ts | 71 +------------------ .../src/authorsMap.ts | 12 ++++ website/blog/authors.yml | 1 - 4 files changed, 26 insertions(+), 71 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index 2580214efc53..f63d24674b50 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -7,7 +7,12 @@ import path from 'path'; import {getBlogPostAuthors} from '../authors'; -import {getAuthorsMap, validateAuthorsMapInput} from '../authorsMap'; +import { + getAuthorsMap, + validateAuthorsMap, + validateAuthorsMapInput, +} from '../authorsMap'; +import type {AuthorsMap} from '@docusaurus/plugin-content-blog'; import type {AuthorsMapInput} from '../authorsMap'; describe('getBlogPostAuthors', () => { @@ -359,6 +364,8 @@ describe('getBlogPostAuthors', () => { ]); }); + // TODO test normalize function individually, normalization are done after + // joi validation it('can normalize inline authors', () => { expect( getBlogPostAuthors({ @@ -733,6 +740,8 @@ describe('authors socials', () => { twitter: 'ozakione', github: 'ozakione', }, + key: 'ozaki', + page: false, }, }; @@ -782,6 +791,8 @@ describe('authors socials', () => { socials: { random: 'ozakione', }, + key: 'ozaki', + page: false, }, }; diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 21d7e8118079..90abc25ee750 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -6,82 +6,15 @@ */ import _ from 'lodash'; -import {getDataFileData, normalizeUrl} from '@docusaurus/utils'; -import {Joi, URISchema} from '@docusaurus/utils-validation'; -import {AuthorSocialsSchema, normalizeSocials} from './authorsSocials'; -import type {BlogContentPaths} from './types'; +import {normalizeUrl} from '@docusaurus/utils'; import type { Author, + AuthorsMap, BlogPost, BlogPostFrontMatter, BlogPostFrontMatterAuthor, } from '@docusaurus/plugin-content-blog'; -export type AuthorsMap = {[authorKey: string]: Author}; - -const AuthorsMapSchema = Joi.object() - .pattern( - Joi.string(), - Joi.object({ - name: Joi.string(), - url: URISchema, - imageURL: URISchema, - title: Joi.string(), - email: Joi.string(), - socials: AuthorSocialsSchema, - }) - .rename('image_url', 'imageURL') - .or('name', 'imageURL') - .unknown() - .required() - .messages({ - 'object.base': - '{#label} should be an author object containing properties like name, title, and imageURL.', - 'any.required': - '{#label} cannot be undefined. It should be an author object containing properties like name, title, and imageURL.', - }), - ) - .messages({ - 'object.base': - "The authors map file should contain an object where each entry contains an author key and the corresponding author's data.", - }); - -export function validateAuthorsMap(content: unknown): AuthorsMap { - const {error, value} = AuthorsMapSchema.validate(content); - if (error) { - throw error; - } - return value; -} - -function normalizeSocialAuthor(author: Author): Author { - return { - ...author, - socials: author.socials ? normalizeSocials(author.socials) : undefined, - }; -} - -function normalizeAuthorsMap(authorsMap: AuthorsMap): AuthorsMap { - return _.mapValues(authorsMap, normalizeSocialAuthor); -} - -export async function getAuthorsMap(params: { - authorsMapPath: string; - contentPaths: BlogContentPaths; -}): Promise { - const authorsMap = await getDataFileData( - { - filePath: params.authorsMapPath, - contentPaths: params.contentPaths, - fileType: 'authors map', - }, - // TODO annoying to test: tightly coupled FS reads + validation... - validateAuthorsMap, - ); - - return authorsMap ? normalizeAuthorsMap(authorsMap) : undefined; -} - type AuthorsParam = { frontMatter: BlogPostFrontMatter; authorsMap: AuthorsMap | undefined; diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index ce4e06f16bec..8fee82616126 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -8,6 +8,7 @@ import _ from 'lodash'; import {getDataFileData, normalizeUrl} from '@docusaurus/utils'; import {Joi, URISchema} from '@docusaurus/utils-validation'; +import {AuthorSocialsSchema, normalizeSocials} from './authorsSocials'; import type {BlogContentPaths} from './types'; import type { Author, @@ -36,6 +37,7 @@ const AuthorsMapInputSchema = Joi.object() title: Joi.string(), email: Joi.string(), page: Joi.alternatives(Joi.bool(), AuthorPageSchema), + socials: AuthorSocialsSchema, description: Joi.string(), }) .rename('image_url', 'imageURL') @@ -112,6 +114,7 @@ function normalizeAuthor({ ...author, key: authorKey, page: getAuthorPage(), + socials: author.socials ? normalizeSocials(author.socials) : undefined, }; } @@ -145,6 +148,7 @@ async function getAuthorsMapInput(params: { contentPaths: params.contentPaths, fileType: 'authors map', }, + // TODO annoying to test: tightly coupled FS reads + validation... validateAuthorsMapInput, ); } @@ -161,3 +165,11 @@ export async function getAuthorsMap(params: { const authorsMap = normalizeAuthorsMap({authorsMapInput, ...params}); return authorsMap; } + +export function validateAuthorsMap(content: unknown): AuthorsMapInput { + const {error, value} = AuthorsMapInputSchema.validate(content); + if (error) { + throw error; + } + return value; +} diff --git a/website/blog/authors.yml b/website/blog/authors.yml index a20778060947..12abe07366d1 100644 --- a/website/blog/authors.yml +++ b/website/blog/authors.yml @@ -27,7 +27,6 @@ slorber: description: > A freelance React and React-Native developer near Paris and Docusaurus maintainer. Also runs ThisWeekInReact.com, a newsletter to stay updated with the React ecosystem. - socials: x: sebastienlorber linkedin: sebastienlorber From 3823d22e1aea88a314696f9f6d329a1af31497ab Mon Sep 17 00:00:00 2001 From: OzakIOne Date: Mon, 15 Jul 2024 13:38:33 +0000 Subject: [PATCH 063/115] refactor: apply lint autofix --- website/blog/authors.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/website/blog/authors.yml b/website/blog/authors.yml index 12abe07366d1..a20778060947 100644 --- a/website/blog/authors.yml +++ b/website/blog/authors.yml @@ -27,6 +27,7 @@ slorber: description: > A freelance React and React-Native developer near Paris and Docusaurus maintainer. Also runs ThisWeekInReact.com, a newsletter to stay updated with the React ecosystem. + socials: x: sebastienlorber linkedin: sebastienlorber From 36ad47e0e44892743dfa4f706bbc38b7bd3c3dd9 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:51:46 +0200 Subject: [PATCH 064/115] refactor --- packages/docusaurus-theme-classic/src/theme-classic.d.ts | 4 +++- .../docusaurus-theme-classic/src/theme/Author/index.tsx | 6 +----- .../src/theme/AuthorsListByLetter/index.tsx | 2 +- .../_dogfooding/_blog tests/2024-07-03-dual-author.mdx | 8 +++----- .../_dogfooding/_blog tests/2024-07-03-single-author.mdx | 1 - 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index d2a71f025706..3fe62e95caf2 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -1624,7 +1624,9 @@ declare module '@theme/AuthorsListInline' { declare module '@theme/Author' { import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; - export interface Props extends AuthorItemProp {} + export interface Props { + author: AuthorItemProp; + } export default function Author(props: Props): JSX.Element; } diff --git a/packages/docusaurus-theme-classic/src/theme/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/Author/index.tsx index 4d3db12041a2..e85a01b371ef 100644 --- a/packages/docusaurus-theme-classic/src/theme/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Author/index.tsx @@ -20,11 +20,7 @@ function MaybeLink(props: LinkProps): JSX.Element { } export default function Author({ - page, - name, - count, - title, - imageURL, + author: {page, name, count, title, imageURL}, }: Props): JSX.Element { const permalink = page?.permalink; return ( diff --git a/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx b/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx index bdd351972f8a..aadf06bd4838 100644 --- a/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx @@ -27,7 +27,7 @@ function AuthorLetterEntryItem({
          {letterEntry.items.map((author) => (
        • - +
        • ))}
        diff --git a/website/_dogfooding/_blog tests/2024-07-03-dual-author.mdx b/website/_dogfooding/_blog tests/2024-07-03-dual-author.mdx index 0a7ff12b993b..13eb48e40a5e 100644 --- a/website/_dogfooding/_blog tests/2024-07-03-dual-author.mdx +++ b/website/_dogfooding/_blog tests/2024-07-03-dual-author.mdx @@ -4,17 +4,15 @@ authors: - name: Sébastien Lorber imageURL: https://github.com/slorber.png socials: - twitter: sebastienlorber - github: slorber - stackoverflow: 82609 - linkedin: sebastienlorber + twitter: https://twitter.com/sebastienlorber + github: https://github.com/slorber + linkedin: https://www.linkedin.com/in/sebastienlorber/ newsletter: https://thisweekinreact.com/newsletter - name: Sébastien Lorber imageURL: https://github.com/slorber.png socials: x: https://x.com/sebastienlorber github: https://github.com/slorber - stackoverflow: 82609 linkedin: https://www.linkedin.com/in/sebastienlorber/ newsletter: https://thisweekinreact.com/newsletter --- diff --git a/website/_dogfooding/_blog tests/2024-07-03-single-author.mdx b/website/_dogfooding/_blog tests/2024-07-03-single-author.mdx index 0a418f42e283..8b2d6c2d78a7 100644 --- a/website/_dogfooding/_blog tests/2024-07-03-single-author.mdx +++ b/website/_dogfooding/_blog tests/2024-07-03-single-author.mdx @@ -8,7 +8,6 @@ authors: x: https://x.com/sebastienlorber twitter: https://twitter.com/sebastienlorber github: https://github.com/slorber - stackoverflow: 82609 linkedin: https://www.linkedin.com/in/sebastienlorber/ newsletter: https://thisweekinreact.com/newsletter --- From 45e7c3813f2da5da1084d68ff4133870e07220c6 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 17 Jul 2024 11:48:56 +0200 Subject: [PATCH 065/115] UI test --- .../src/theme/BlogAuthorsPostsPage/index.tsx | 23 +++++-------- .../BlogAuthorsPostsPage/styles.module.css | 33 +++++++++++++++++++ .../Header/Author/Socials/index.tsx | 6 +++- 3 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx index fbaee3100a46..90e7588b4349 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx @@ -21,6 +21,9 @@ import SearchMetadata from '@theme/SearchMetadata'; import type {Props} from '@theme/BlogAuthorsPostsPage'; import BlogPostItems from '@theme/BlogPostItems'; import Heading from '@theme/Heading'; +import Author from '@theme/BlogPostItem/Header/Author'; + +import styles from './styles.module.css'; function useBlogAuthorsPostsPageTitle(author: Props['author']): string { const blogPostsPlural = useBlogPostsPlural(); @@ -59,20 +62,12 @@ function BlogAuthorsPostsPageContent({
        {title} -
          - {author.url && ( -
        • - - - Personal website - - -
        • - )} - {author.description &&
        • {author.description}
        • } -
        + + {author.description &&

        {author.description}

        } a > img { + width: calc(3rem * 2); + height: calc(3rem * 2); +} + +/* .postsPageAuthor>div { + justify-content: space-between; +} */ + +.postsPageAuthor span { + font-size: 1.75rem; + line-height: normal; +} + +.postsPageAuthor small { + font-size: 1.25rem; + line-height: normal; + margin-top: 0; +} + +.postsPageAuthor div > a, +.postsPageAuthor div > a > svg { + height: 1.25rem; + width: 1.25rem; + margin-top: 0; +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/Socials/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/Socials/index.tsx index 55866b14f2c2..f7a607ac9895 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/Socials/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/Socials/index.tsx @@ -50,7 +50,11 @@ function SocialLink({platform, link}: {platform: string; link: string}) { ); } -export default function AuthorSocials({author}: {author: Props['author']}) { +export default function AuthorSocials({ + author, +}: { + author: Props['author']; +}): JSX.Element { return (
        {Object.entries(author.socials ?? {}).map(([platform, linkUrl]) => { From 0c5a122dbcda692ad11a6d70fd086ce8f61ab696 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Wed, 17 Jul 2024 11:53:39 +0200 Subject: [PATCH 066/115] fix test --- .../src/__tests__/authors.test.ts | 80 +------------------ 1 file changed, 4 insertions(+), 76 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index f63d24674b50..f929d2cdb9b2 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -12,7 +12,6 @@ import { validateAuthorsMap, validateAuthorsMapInput, } from '../authorsMap'; -import type {AuthorsMap} from '@docusaurus/plugin-content-blog'; import type {AuthorsMapInput} from '../authorsMap'; describe('getBlogPostAuthors', () => { @@ -364,54 +363,6 @@ describe('getBlogPostAuthors', () => { ]); }); - // TODO test normalize function individually, normalization are done after - // joi validation - it('can normalize inline authors', () => { - expect( - getBlogPostAuthors({ - frontMatter: { - authors: [ - { - name: 'Seb1', - socials: { - x: 'https://x.com/sebastienlorber', - twitter: 'sebastienlorber', - github: 'slorber', - }, - }, - { - name: 'Seb2', - socials: { - x: 'sebastienlorber', - twitter: 'https://twitter.com/sebastienlorber', - github: 'https://github.com/slorber', - }, - }, - ], - }, - authorsMap: {}, - baseUrl: '/', - }), - ).toEqual([ - { - name: 'Seb1', - socials: { - x: 'https://x.com/sebastienlorber', - twitter: 'https://twitter.com/sebastienlorber', - github: 'https://github.com/slorber', - }, - }, - { - name: 'Seb2', - socials: { - x: 'https://x.com/sebastienlorber', - twitter: 'https://twitter.com/sebastienlorber', - github: 'https://github.com/slorber', - }, - }, - ]); - }); - it('throw when using author key with no authorsMap', () => { expect(() => getBlogPostAuthors({ @@ -574,29 +525,6 @@ describe('getAuthorsMap', () => { }), ).resolves.toBeUndefined(); }); - - describe('getAuthorsMap returns normalized', () => { - it('socials', async () => { - const authorsMap = await getAuthorsMap({ - contentPaths, - authorsMapPath: 'authors.yml', - }); - expect(authorsMap.slorber.socials).toMatchInlineSnapshot(` - { - "stackoverflow": "https://stackoverflow.com/users/82609", - "twitter": "https://twitter.com/sebastienlorber", - "x": "https://x.com/sebastienlorber", - } - `); - expect(authorsMap.JMarcey.socials).toMatchInlineSnapshot(` - { - "stackoverflow": "https://stackoverflow.com/users/102705/Joel-Marcey", - "twitter": "https://twitter.com/JoelMarcey", - "x": "https://x.com/JoelMarcey", - } - `); - }); - }); }); describe('validateAuthorsMapInput', () => { @@ -733,7 +661,7 @@ describe('validateAuthorsMapInput', () => { describe('authors socials', () => { it('valid known author map socials', () => { - const authorsMap: AuthorsMap = { + const authorsMap: AuthorsMapInput = { ozaki: { name: 'ozaki', socials: { @@ -749,7 +677,7 @@ describe('authors socials', () => { }); it('throw socials that are not strings', () => { - const authorsMap: AuthorsMap = { + const authorsMap: AuthorsMapInput = { ozaki: { name: 'ozaki', socials: { @@ -767,7 +695,7 @@ describe('authors socials', () => { }); it('throw socials that are objects', () => { - const authorsMap: AuthorsMap = { + const authorsMap: AuthorsMapInput = { ozaki: { name: 'ozaki', socials: { @@ -785,7 +713,7 @@ describe('authors socials', () => { }); it('valid unknown author map socials', () => { - const authorsMap: AuthorsMap = { + const authorsMap: AuthorsMapInput = { ozaki: { name: 'ozaki', socials: { From 00fc21d912b8d8568b16b3d929933d4d6b5f31fc Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:48:29 +0200 Subject: [PATCH 067/115] ui test --- .../src/theme-classic.d.ts | 5 +- .../src/theme/Author/index.tsx | 47 ----------------- .../src/theme/Author/styles.module.css | 16 ------ .../src/theme/AuthorsList/index.tsx | 27 ++++++++++ .../styles.module.css | 5 ++ .../src/theme/AuthorsListByLetter/index.tsx | 51 ------------------- .../src/theme/BlogAuthorsListPage/index.tsx | 4 +- .../BlogPostItem/Header/Author/index.tsx | 2 + .../Header/Author/styles.module.css | 10 ++++ 9 files changed, 49 insertions(+), 118 deletions(-) delete mode 100644 packages/docusaurus-theme-classic/src/theme/Author/index.tsx delete mode 100644 packages/docusaurus-theme-classic/src/theme/Author/styles.module.css create mode 100644 packages/docusaurus-theme-classic/src/theme/AuthorsList/index.tsx rename packages/docusaurus-theme-classic/src/theme/{AuthorsListByLetter => AuthorsList}/styles.module.css (78%) delete mode 100644 packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 3fe62e95caf2..5d9526c752c2 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -298,6 +298,7 @@ declare module '@theme/BlogPostItem/Header/Author' { readonly author: Author; readonly singleAuthor: boolean; readonly className?: string; + readonly count?: number; } export default function BlogPostItemHeaderAuthor(props: Props): JSX.Element; @@ -1603,13 +1604,13 @@ declare module '@theme/Tag' { export default function Tag(props: Props): JSX.Element; } -declare module '@theme/AuthorsListByLetter' { +declare module '@theme/AuthorsList' { import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; export interface Props { readonly authors: readonly AuthorItemProp[]; } - export default function AuthorsListByLetter(props: Props): JSX.Element; + export default function AuthorsList(props: Props): JSX.Element; } declare module '@theme/AuthorsListInline' { diff --git a/packages/docusaurus-theme-classic/src/theme/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/Author/index.tsx deleted file mode 100644 index e85a01b371ef..000000000000 --- a/packages/docusaurus-theme-classic/src/theme/Author/index.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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 React from 'react'; -import clsx from 'clsx'; -import Link, {type Props as LinkProps} from '@docusaurus/Link'; -import type {Props} from '@theme/Author'; - -import styles from './styles.module.css'; - -function MaybeLink(props: LinkProps): JSX.Element { - if (props.href) { - return ; - } - return <>{props.children}; -} - -export default function Author({ - author: {page, name, count, title, imageURL}, -}: Props): JSX.Element { - const permalink = page?.permalink; - return ( -
        - {imageURL && ( - - {name} - - )} - - {name && ( -
        -
        - - {name} - - {count} -
        - {title} -
        - )} -
        - ); -} diff --git a/packages/docusaurus-theme-classic/src/theme/Author/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Author/styles.module.css deleted file mode 100644 index 5ec389cd08fc..000000000000 --- a/packages/docusaurus-theme-classic/src/theme/Author/styles.module.css +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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. - */ - -.count { - background: var(--ifm-color-secondary); - color: var(--ifm-color-black); - font-size: 0.8rem; - line-height: 1.2; - border-radius: var(--ifm-global-radius); - padding: 0.1rem 0.4rem; - margin-left: 0.3rem; -} diff --git a/packages/docusaurus-theme-classic/src/theme/AuthorsList/index.tsx b/packages/docusaurus-theme-classic/src/theme/AuthorsList/index.tsx new file mode 100644 index 000000000000..0a69b26104d1 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/AuthorsList/index.tsx @@ -0,0 +1,27 @@ +/** + * 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 React from 'react'; +import clsx from 'clsx'; +import Author from '@theme/BlogPostItem/Header/Author'; +import type {Props} from '@theme/AuthorsList'; +import styles from './styles.module.css'; + +export default function AuthorsList({authors}: Props): JSX.Element { + return ( +
        + {authors.map((author) => ( + + ))} +
        + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/styles.module.css b/packages/docusaurus-theme-classic/src/theme/AuthorsList/styles.module.css similarity index 78% rename from packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/AuthorsList/styles.module.css index b06b913b6c0d..e091dc082cf9 100644 --- a/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/AuthorsList/styles.module.css @@ -9,3 +9,8 @@ display: inline-block; margin: 0.5rem 0.5rem 0 1rem; } + +section { + display: grid; + grid-template-columns: repeat(2, 1fr); +} diff --git a/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx b/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx deleted file mode 100644 index aadf06bd4838..000000000000 --- a/packages/docusaurus-theme-classic/src/theme/AuthorsListByLetter/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/** - * 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 React from 'react'; -import {type LetterEntry} from '@docusaurus/theme-common'; -import {listAuthorsByLetters} from '@docusaurus/theme-common/internal'; -import Author from '@theme/Author'; -import type {Props} from '@theme/AuthorsListByLetter'; -import Heading from '@theme/Heading'; -import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; -import styles from './styles.module.css'; - -function AuthorLetterEntryItem({ - letterEntry, -}: { - letterEntry: LetterEntry; -}) { - return ( -
        - - {letterEntry.letter} - -
          - {letterEntry.items.map((author) => ( -
        • - -
        • - ))} -
        -
        -
        - ); -} - -export default function AuthorsListByLetter({authors}: Props): JSX.Element { - const letterList = listAuthorsByLetters(authors); - return ( -
        - {letterList.map((letterEntry) => ( - - ))} -
        - ); -} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx index 152e9d1888b6..8a24a7339842 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx @@ -13,7 +13,7 @@ import { ThemeClassNames, } from '@docusaurus/theme-common'; import BlogLayout from '@theme/BlogLayout'; -import AuthorsListByLetter from '@theme/AuthorsListByLetter'; +import AuthorsList from '@theme/AuthorsList'; import type {Props} from '@theme/BlogAuthorsListPage'; import SearchMetadata from '@theme/SearchMetadata'; import Heading from '@theme/Heading'; @@ -34,7 +34,7 @@ export default function BlogAuthorsListPage({ {title} - + ); diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx index e30ac35cb3c1..c734a4c7a78f 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx @@ -32,6 +32,7 @@ export default function BlogPostItemHeaderAuthor({ // singleAuthor, // may be useful in the future, or for swizzle users author, className, + count, }: Props): JSX.Element { const {name, title, url, socials, imageURL, email, page} = author; const link = @@ -52,6 +53,7 @@ export default function BlogPostItemHeaderAuthor({
        {name} + {count && {count}}
        {!!title && } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css index 21ea5d40dc89..4629d264a19c 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css @@ -19,3 +19,13 @@ -webkit-line-clamp: 1; -webkit-box-orient: vertical; } + +.count { + background: var(--ifm-color-secondary); + color: var(--ifm-color-black); + font-size: 0.8rem; + line-height: 1.2; + border-radius: var(--ifm-global-radius); + padding: 0.1rem 0.4rem; + margin-left: 0.3rem; +} From 9d7ee533f97ec00c945ded5bcf8b77980046cb48 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 25 Jul 2024 18:04:00 +0200 Subject: [PATCH 068/115] move useBlogPostsPlural to internal --- .../src/theme/BlogAuthorsPostsPage/index.tsx | 2 +- .../src/theme/BlogTagsPostsPage/index.tsx | 2 +- packages/docusaurus-theme-common/src/internal.ts | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx index 90e7588b4349..027cd58bbaef 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx @@ -12,8 +12,8 @@ import { PageMetadata, HtmlClassNameProvider, ThemeClassNames, - useBlogPostsPlural, } from '@docusaurus/theme-common'; +import {useBlogPostsPlural} from '@docusaurus/theme-common/internal'; import Link from '@docusaurus/Link'; import BlogLayout from '@theme/BlogLayout'; import BlogListPaginator from '@theme/BlogListPaginator'; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx index a3cc01eea8fb..5ddff149d4e9 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx @@ -12,8 +12,8 @@ import { PageMetadata, HtmlClassNameProvider, ThemeClassNames, - useBlogPostsPlural, } from '@docusaurus/theme-common'; +import {useBlogPostsPlural} from '@docusaurus/theme-common/internal'; import Link from '@docusaurus/Link'; import BlogLayout from '@theme/BlogLayout'; import BlogListPaginator from '@theme/BlogListPaginator'; diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts index 0b0395c766bd..d631a7d39dde 100644 --- a/packages/docusaurus-theme-common/src/internal.ts +++ b/packages/docusaurus-theme-common/src/internal.ts @@ -94,3 +94,5 @@ export {useLockBodyScroll} from './hooks/useLockBodyScroll'; export {useCodeWordWrap} from './hooks/useCodeWordWrap'; export {getPrismCssVariables} from './utils/codeBlockUtils'; export {useBackToTopButton} from './hooks/useBackToTopButton'; + +export {useBlogPostsPlural} from './utils/usePluralForm'; From a092d1d2658b2569249b753d8b562fd455515be6 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 25 Jul 2024 18:29:44 +0200 Subject: [PATCH 069/115] add useful groupBlogPostsByAuthorKey comment --- packages/docusaurus-plugin-content-blog/src/authors.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authors.ts b/packages/docusaurus-plugin-content-blog/src/authors.ts index 90abc25ee750..1fe0ef923815 100644 --- a/packages/docusaurus-plugin-content-blog/src/authors.ts +++ b/packages/docusaurus-plugin-content-blog/src/authors.ts @@ -147,7 +147,8 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t } /** - * Blog posts grouped by author page permalink (if page exists) + * Group blog posts by author key + * Blog posts with only inline authors are ignored */ export function groupBlogPostsByAuthorKey({ blogPosts, From 6044010d61d35d999208a7e8af9a9fb47c9b1371 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 25 Jul 2024 18:43:59 +0200 Subject: [PATCH 070/115] add tests for groupBlogPostsByAuthorKey --- .../src/__tests__/authors.test.ts | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index f929d2cdb9b2..3b7311ea6aaa 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -6,14 +6,20 @@ */ import path from 'path'; -import {getBlogPostAuthors} from '../authors'; +import {fromPartial, type PartialDeep} from '@total-typescript/shoehorn'; +import {getBlogPostAuthors, groupBlogPostsByAuthorKey} from '../authors'; import { getAuthorsMap, validateAuthorsMap, validateAuthorsMapInput, } from '../authorsMap'; +import type {AuthorsMap, BlogPost} from '@docusaurus/plugin-content-blog'; import type {AuthorsMapInput} from '../authorsMap'; +function post(partial: PartialDeep): BlogPost { + return fromPartial(partial); +} + describe('getBlogPostAuthors', () => { it('can read no authors', () => { expect( @@ -727,3 +733,38 @@ describe('authors socials', () => { expect(validateAuthorsMap(authorsMap)).toEqual(authorsMap); }); }); + +describe('groupBlogPostsByAuthorKey', () => { + const authorsMap: AuthorsMap = fromPartial({ + ozaki: {}, + slorber: {}, + keyWithNoPost: {}, + }); + + it('can group blog posts', () => { + const post1 = post({metadata: {authors: [{key: 'ozaki'}]}}); + const post2 = post({ + metadata: {authors: [{key: 'slorber'}, {key: 'ozaki'}]}, + }); + const post3 = post({metadata: {authors: [{key: 'slorber'}]}}); + const post4 = post({ + metadata: {authors: [{name: 'Inline author 1'}, {key: 'slorber'}]}, + }); + const post5 = post({ + metadata: {authors: [{name: 'Inline author 2'}]}, + }); + const post6 = post({ + metadata: {authors: [{key: 'unknownKey'}]}, + }); + + const blogPosts = [post1, post2, post3, post4, post5, post6]; + + expect(groupBlogPostsByAuthorKey({authorsMap, blogPosts})).toEqual({ + ozaki: [post1, post2], + slorber: [post2, post3, post4], + keyWithNoPost: [], + // We don't care about this edge case, it doesn't happen in practice + unknownKey: undefined, + }); + }); +}); From 0260a3b52ab18d6afdeb542b7064d5f3bab2a8a2 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 25 Jul 2024 18:49:24 +0200 Subject: [PATCH 071/115] narrow typing for AuthorsMap --- packages/docusaurus-plugin-content-blog/src/authorsMap.ts | 2 +- .../docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index 8fee82616126..3e3d88477f38 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -98,7 +98,7 @@ function normalizeAuthor({ authorsBaseRoutePath: string; authorKey: string; author: AuthorInput; -}): Author { +}): Author & {key: string} { function getAuthorPage(): AuthorPage | null { if (!author.page) { return null; diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 1796e19abb3f..90d9a4f8a2f0 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -537,7 +537,7 @@ declare module '@docusaurus/plugin-content-blog' { items: BlogSidebarItem[]; }; - export type AuthorsMap = {[authorKey: string]: Author}; + export type AuthorsMap = {[authorKey: string]: Author & {key: string}}; export type BlogContent = { blogSidebarTitle: string; From 3a8687309522731c6c979ebd70b8c83e431d30b5 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 25 Jul 2024 18:51:47 +0200 Subject: [PATCH 072/115] rename checkAuthorsMapPermalinkCollisions --- .../src/__tests__/authorsMap.test.ts | 8 ++++---- packages/docusaurus-plugin-content-blog/src/authorsMap.ts | 2 +- packages/docusaurus-plugin-content-blog/src/index.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts index f834323f10d1..a36a67508a9a 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts @@ -5,10 +5,10 @@ * LICENSE file in the root directory of this source tree. */ -import {checkPermalinkCollisions} from '../authorsMap'; +import {checkAuthorsMapPermalinkCollisions} from '../authorsMap'; import type {AuthorsMap} from '@docusaurus/plugin-content-blog'; -describe('authors permalink collision', () => { +describe('checkAuthorsMapPermalinkCollisions', () => { it('do not throw when permalinks are unique', () => { const authors: AuthorsMap = { author1: { @@ -28,7 +28,7 @@ describe('authors permalink collision', () => { }; expect(() => { - checkPermalinkCollisions(authors); + checkAuthorsMapPermalinkCollisions(authors); }).not.toThrow(); }); @@ -51,7 +51,7 @@ describe('authors permalink collision', () => { }; expect(() => { - checkPermalinkCollisions(authors); + checkAuthorsMapPermalinkCollisions(authors); }).toThrowErrorMatchingInlineSnapshot(` "The following permalinks are duplicated: Permalink: /author1 diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index 3e3d88477f38..8cbb1a13dece 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -56,7 +56,7 @@ const AuthorsMapInputSchema = Joi.object() "The authors map file should contain an object where each entry contains an author key and the corresponding author's data.", }); -export function checkPermalinkCollisions( +export function checkAuthorsMapPermalinkCollisions( authorsMap: AuthorsMap | undefined, ): void { if (!authorsMap) { diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 865ea86170d1..b23acc8f959c 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -34,7 +34,7 @@ import {translateContent, getTranslationFiles} from './translations'; import {createBlogFeedFiles, createFeedHtmlHeadTags} from './feed'; import {createAllRoutes} from './routes'; -import {checkPermalinkCollisions, getAuthorsMap} from './authorsMap'; +import {checkAuthorsMapPermalinkCollisions, getAuthorsMap} from './authorsMap'; import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types'; import type {LoadContext, Plugin} from '@docusaurus/types'; import type { @@ -177,7 +177,7 @@ export default async function pluginContentBlog( authorsPageBasePath, ]), }); - checkPermalinkCollisions(authorsMap); + checkAuthorsMapPermalinkCollisions(authorsMap); let blogPosts = await generateBlogPosts( contentPaths, From 4f48e9e669548b99492aa6af9cfefb8a80d86d18 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 25 Jul 2024 19:15:30 +0200 Subject: [PATCH 073/115] refactor getDataFileData to readDataFile --- .../dataFiles/actualData/bad.json | 1 - .../__fixtures__/dataFiles/actualData/bad.yml | 1 - .../dataFiles/actualData/valid.json | 1 - .../dataFiles/actualData/valid.yml | 1 - .../dataFiles/dataFiles/dataFile.json | 1 + .../dataFiles/dataFiles/dataFile.yml | 1 + .../dataFiles/dataFiles/invalid.yml | 1 + .../dataFiles/localized/dataFile.yml | 1 + .../src/__tests__/dataFileUtils.test.ts | 52 ++++++++----------- .../docusaurus-utils/src/dataFileUtils.ts | 27 +++++----- packages/docusaurus-utils/src/index.ts | 2 +- 11 files changed, 40 insertions(+), 49 deletions(-) delete mode 100644 packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/bad.json delete mode 100644 packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/bad.yml delete mode 100644 packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/valid.json delete mode 100644 packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/valid.yml create mode 100644 packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/dataFile.json create mode 100644 packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/dataFile.yml create mode 100644 packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/invalid.yml create mode 100644 packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/localized/dataFile.yml diff --git a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/bad.json b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/bad.json deleted file mode 100644 index 3c27e19a80d4..000000000000 --- a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/bad.json +++ /dev/null @@ -1 +0,0 @@ -{"a": 2} diff --git a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/bad.yml b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/bad.yml deleted file mode 100644 index 9dfc208dffa8..000000000000 --- a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/bad.yml +++ /dev/null @@ -1 +0,0 @@ -a: 2 diff --git a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/valid.json b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/valid.json deleted file mode 100644 index cb5b2f69babc..000000000000 --- a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/valid.json +++ /dev/null @@ -1 +0,0 @@ -{"a": 1} diff --git a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/valid.yml b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/valid.yml deleted file mode 100644 index a8926a52d8dc..000000000000 --- a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/actualData/valid.yml +++ /dev/null @@ -1 +0,0 @@ -a: 1 diff --git a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/dataFile.json b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/dataFile.json new file mode 100644 index 000000000000..f06ab684a504 --- /dev/null +++ b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/dataFile.json @@ -0,0 +1 @@ +{"content": "json"} diff --git a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/dataFile.yml b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/dataFile.yml new file mode 100644 index 000000000000..59b8b3c49617 --- /dev/null +++ b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/dataFile.yml @@ -0,0 +1 @@ +content: original yaml diff --git a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/invalid.yml b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/invalid.yml new file mode 100644 index 000000000000..54bd7745b096 --- /dev/null +++ b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/invalid.yml @@ -0,0 +1 @@ +}{{{{12434665¨£%£%%£%£}}}} diff --git a/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/localized/dataFile.yml b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/localized/dataFile.yml new file mode 100644 index 000000000000..bf980e035eba --- /dev/null +++ b/packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/localized/dataFile.yml @@ -0,0 +1 @@ +content: localized yaml diff --git a/packages/docusaurus-utils/src/__tests__/dataFileUtils.test.ts b/packages/docusaurus-utils/src/__tests__/dataFileUtils.test.ts index 4d809a45c791..732442f05015 100644 --- a/packages/docusaurus-utils/src/__tests__/dataFileUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/dataFileUtils.test.ts @@ -10,7 +10,7 @@ import { findFolderContainingFile, getFolderContainingFile, getDataFilePath, - getDataFileData, + readDataFile, } from '../dataFileUtils'; describe('getDataFilePath', () => { @@ -125,46 +125,40 @@ describe('getDataFilePath', () => { }); describe('getDataFileData', () => { - const fixturesDir = path.join(__dirname, '__fixtures__/dataFiles/actualData'); - function readDataFile(filePath: string) { - return getDataFileData( - { - filePath, - contentPaths: {contentPath: fixturesDir, contentPathLocalized: ''}, - fileType: 'test', - }, - (content) => { - // @ts-expect-error: good enough - if (content.a !== 1) { - throw new Error('Nope'); - } - return content; - }, + function testFile(filePath: string) { + const contentPath = path.join( + __dirname, + '__fixtures__/dataFiles/dataFiles', ); + const contentPathLocalized = path.join(contentPath, 'localized'); + return readDataFile({ + filePath, + contentPaths: {contentPath, contentPathLocalized}, + }); } it('returns undefined for nonexistent file', async () => { - await expect(readDataFile('nonexistent.yml')).resolves.toBeUndefined(); - }); - - it('read valid yml author file', async () => { - await expect(readDataFile('valid.yml')).resolves.toEqual({a: 1}); + await expect(testFile('nonexistent.yml')).resolves.toBeUndefined(); }); it('read valid json author file', async () => { - await expect(readDataFile('valid.json')).resolves.toEqual({a: 1}); + await expect(testFile('dataFile.json')).resolves.toEqual({ + content: 'json', + }); }); - it('fail to read invalid yml', async () => { - await expect( - readDataFile('bad.yml'), - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Nope"`); + it('read valid yml author file using localized source in priority', async () => { + await expect(testFile('dataFile.yml')).resolves.toEqual({ + content: 'localized yaml', + }); }); - it('fail to read invalid json', async () => { + it('throw for invalid file', async () => { await expect( - readDataFile('bad.json'), - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Nope"`); + testFile('invalid.yml'), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"The file at "packages/docusaurus-utils/src/__tests__/__fixtures__/dataFiles/dataFiles/invalid.yml" looks invalid (not Yaml nor JSON)."`, + ); }); }); diff --git a/packages/docusaurus-utils/src/dataFileUtils.ts b/packages/docusaurus-utils/src/dataFileUtils.ts index ba671d6e8875..decd57da940b 100644 --- a/packages/docusaurus-utils/src/dataFileUtils.ts +++ b/packages/docusaurus-utils/src/dataFileUtils.ts @@ -43,31 +43,28 @@ export async function getDataFilePath({ } /** - * Looks up for a data file in the content paths, returns the object validated - * and normalized according to the `validate` callback. + * Looks up for a data file in the content paths + * Favors the localized content path over the base content path + * Currently supports Yaml and JSON data files + * It is the caller responsibility to validate and normalize the resulting data * * @returns `undefined` when file not found - * @throws Throws when validation fails, displaying a helpful context message. + * @throws Throws when data file can't be parsed */ -export async function getDataFileData( - params: DataFileParams & { - /** Used for the "The X file looks invalid" message. */ - fileType: string; - }, - validate: (content: unknown) => T, -): Promise { +export async function readDataFile(params: DataFileParams): Promise { const filePath = await getDataFilePath(params); if (!filePath) { return undefined; } try { const contentString = await fs.readFile(filePath, {encoding: 'utf8'}); - const unsafeContent = Yaml.load(contentString); - // TODO we shouldn't validate here: it makes validation harder to test - return validate(unsafeContent); + return Yaml.load(contentString); } catch (err) { - logger.error`The ${params.fileType} file at path=${filePath} looks invalid.`; - throw err; + const msg = logger.interpolate`The file at path=${path.relative( + process.cwd(), + filePath, + )} looks invalid (not Yaml nor JSON).`; + throw new Error(msg, {cause: err as Error}); } } diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 7201f8eec1a8..d9a24a408655 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -109,7 +109,7 @@ export {escapeShellArg} from './shellUtils'; export {loadFreshModule} from './moduleUtils'; export { getDataFilePath, - getDataFileData, + readDataFile, getContentPathList, findFolderContainingFile, getFolderContainingFile, From 12d72c6c5b4d9c8501264c08cb16e8766d0b9ef2 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 25 Jul 2024 19:15:47 +0200 Subject: [PATCH 074/115] move relevant tests to authorsMap test file --- .../src/__tests__/authors.test.ts | 246 ----------------- .../src/__tests__/authorsMap.test.ts | 248 +++++++++++++++++- .../src/authorsMap.ts | 16 +- 3 files changed, 253 insertions(+), 257 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts index 3b7311ea6aaa..7072e8124caa 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authors.test.ts @@ -5,16 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -import path from 'path'; import {fromPartial, type PartialDeep} from '@total-typescript/shoehorn'; import {getBlogPostAuthors, groupBlogPostsByAuthorKey} from '../authors'; -import { - getAuthorsMap, - validateAuthorsMap, - validateAuthorsMapInput, -} from '../authorsMap'; import type {AuthorsMap, BlogPost} from '@docusaurus/plugin-content-blog'; -import type {AuthorsMapInput} from '../authorsMap'; function post(partial: PartialDeep): BlogPost { return fromPartial(partial); @@ -495,245 +488,6 @@ describe('getBlogPostAuthors', () => { }); }); -describe('getAuthorsMap', () => { - const fixturesDir = path.join(__dirname, '__fixtures__/authorsMapFiles'); - const contentPaths = { - contentPathLocalized: fixturesDir, - contentPath: fixturesDir, - }; - - it('getAuthorsMap can read yml file', async () => { - await expect( - getAuthorsMap({ - contentPaths, - authorsMapPath: 'authors.yml', - authorsBaseRoutePath: '/authors', - }), - ).resolves.toBeDefined(); - }); - - it('getAuthorsMap can read json file', async () => { - await expect( - getAuthorsMap({ - contentPaths, - authorsMapPath: 'authors.json', - authorsBaseRoutePath: '/authors', - }), - ).resolves.toBeDefined(); - }); - - it('getAuthorsMap can return undefined if yaml file not found', async () => { - await expect( - getAuthorsMap({ - contentPaths, - authorsMapPath: 'authors_does_not_exist.yml', - authorsBaseRoutePath: '/authors', - }), - ).resolves.toBeUndefined(); - }); -}); - -describe('validateAuthorsMapInput', () => { - it('accept valid authors map', () => { - const authorsMap: AuthorsMapInput = { - slorber: { - name: 'Sébastien Lorber', - title: 'maintainer', - url: 'https://sebastienlorber.com', - imageURL: 'https://github.com/slorber.png', - key: 'slorber', - page: false, - }, - yangshun: { - name: 'Yangshun Tay', - imageURL: 'https://github.com/yangshun.png', - randomField: 42, - key: 'yangshun', - page: false, - }, - jmarcey: { - name: 'Joel', - title: 'creator of Docusaurus', - hello: new Date(), - key: 'jmarcey', - page: false, - }, - }; - expect(validateAuthorsMapInput(authorsMap)).toEqual(authorsMap); - }); - - it('rename snake case image_url to camelCase imageURL', () => { - const authorsMap: AuthorsMapInput = { - slorber: { - name: 'Sébastien Lorber', - image_url: 'https://github.com/slorber.png', - key: 'slorber', - page: false, - }, - }; - expect(validateAuthorsMapInput(authorsMap)).toEqual({ - slorber: { - name: 'Sébastien Lorber', - imageURL: 'https://github.com/slorber.png', - page: false, - key: 'slorber', - }, - }); - }); - - it('accept author with only image', () => { - const authorsMap: AuthorsMapInput = { - slorber: { - imageURL: 'https://github.com/slorber.png', - url: 'https://github.com/slorber', - key: 'slorber', - page: false, - }, - }; - expect(validateAuthorsMapInput(authorsMap)).toEqual(authorsMap); - }); - - it('reject author without name or image', () => { - const authorsMap: AuthorsMapInput = { - slorber: { - title: 'foo', - key: 'slorber', - page: false, - }, - }; - expect(() => - validateAuthorsMapInput(authorsMap), - ).toThrowErrorMatchingInlineSnapshot( - `""slorber" must contain at least one of [name, imageURL]"`, - ); - }); - - it('reject undefined author', () => { - expect(() => - validateAuthorsMapInput({ - slorber: undefined, - }), - ).toThrowErrorMatchingInlineSnapshot( - `""slorber" cannot be undefined. It should be an author object containing properties like name, title, and imageURL."`, - ); - }); - - it('reject null author', () => { - expect(() => - validateAuthorsMapInput({ - slorber: null, - }), - ).toThrowErrorMatchingInlineSnapshot( - `""slorber" should be an author object containing properties like name, title, and imageURL."`, - ); - }); - - it('reject array author', () => { - expect(() => - validateAuthorsMapInput({slorber: []}), - ).toThrowErrorMatchingInlineSnapshot( - `""slorber" should be an author object containing properties like name, title, and imageURL."`, - ); - }); - - it('reject array content', () => { - expect(() => - validateAuthorsMapInput([]), - ).toThrowErrorMatchingInlineSnapshot( - `"The authors map file should contain an object where each entry contains an author key and the corresponding author's data."`, - ); - }); - - it('reject flat author', () => { - expect(() => - validateAuthorsMapInput({name: 'Sébastien'}), - ).toThrowErrorMatchingInlineSnapshot( - `""name" should be an author object containing properties like name, title, and imageURL."`, - ); - }); - - it('reject non-map author', () => { - const authorsMap: AuthorsMapInput = { - // @ts-expect-error: intentionally invalid - slorber: [], - }; - expect(() => - validateAuthorsMapInput(authorsMap), - ).toThrowErrorMatchingInlineSnapshot( - `""slorber" should be an author object containing properties like name, title, and imageURL."`, - ); - }); -}); - -describe('authors socials', () => { - it('valid known author map socials', () => { - const authorsMap: AuthorsMapInput = { - ozaki: { - name: 'ozaki', - socials: { - twitter: 'ozakione', - github: 'ozakione', - }, - key: 'ozaki', - page: false, - }, - }; - - expect(validateAuthorsMap(authorsMap)).toEqual(authorsMap); - }); - - it('throw socials that are not strings', () => { - const authorsMap: AuthorsMapInput = { - ozaki: { - name: 'ozaki', - socials: { - // @ts-expect-error: for tests - twitter: 42, - }, - }, - }; - - expect(() => - validateAuthorsMap(authorsMap), - ).toThrowErrorMatchingInlineSnapshot( - `""ozaki.socials.twitter" must be a string"`, - ); - }); - - it('throw socials that are objects', () => { - const authorsMap: AuthorsMapInput = { - ozaki: { - name: 'ozaki', - socials: { - // @ts-expect-error: for tests - twitter: {link: 'ozakione'}, - }, - }, - }; - - expect(() => - validateAuthorsMap(authorsMap), - ).toThrowErrorMatchingInlineSnapshot( - `""ozaki.socials.twitter" must be a string"`, - ); - }); - - it('valid unknown author map socials', () => { - const authorsMap: AuthorsMapInput = { - ozaki: { - name: 'ozaki', - socials: { - random: 'ozakione', - }, - key: 'ozaki', - page: false, - }, - }; - - expect(validateAuthorsMap(authorsMap)).toEqual(authorsMap); - }); -}); - describe('groupBlogPostsByAuthorKey', () => { const authorsMap: AuthorsMap = fromPartial({ ozaki: {}, diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts index a36a67508a9a..b6393ede6854 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsMap.test.ts @@ -5,7 +5,14 @@ * LICENSE file in the root directory of this source tree. */ -import {checkAuthorsMapPermalinkCollisions} from '../authorsMap'; +import path from 'path'; +import { + type AuthorsMapInput, + checkAuthorsMapPermalinkCollisions, + getAuthorsMap, + validateAuthorsMap, + validateAuthorsMapInput, +} from '../authorsMap'; import type {AuthorsMap} from '@docusaurus/plugin-content-blog'; describe('checkAuthorsMapPermalinkCollisions', () => { @@ -59,3 +66,242 @@ describe('checkAuthorsMapPermalinkCollisions', () => { `); }); }); + +describe('getAuthorsMap', () => { + const fixturesDir = path.join(__dirname, '__fixtures__/authorsMapFiles'); + const contentPaths = { + contentPathLocalized: fixturesDir, + contentPath: fixturesDir, + }; + + it('getAuthorsMap can read yml file', async () => { + await expect( + getAuthorsMap({ + contentPaths, + authorsMapPath: 'authors.yml', + authorsBaseRoutePath: '/authors', + }), + ).resolves.toBeDefined(); + }); + + it('getAuthorsMap can read json file', async () => { + await expect( + getAuthorsMap({ + contentPaths, + authorsMapPath: 'authors.json', + authorsBaseRoutePath: '/authors', + }), + ).resolves.toBeDefined(); + }); + + it('getAuthorsMap can return undefined if yaml file not found', async () => { + await expect( + getAuthorsMap({ + contentPaths, + authorsMapPath: 'authors_does_not_exist.yml', + authorsBaseRoutePath: '/authors', + }), + ).resolves.toBeUndefined(); + }); +}); + +describe('validateAuthorsMapInput', () => { + it('accept valid authors map', () => { + const authorsMap: AuthorsMapInput = { + slorber: { + name: 'Sébastien Lorber', + title: 'maintainer', + url: 'https://sebastienlorber.com', + imageURL: 'https://github.com/slorber.png', + key: 'slorber', + page: false, + }, + yangshun: { + name: 'Yangshun Tay', + imageURL: 'https://github.com/yangshun.png', + randomField: 42, + key: 'yangshun', + page: false, + }, + jmarcey: { + name: 'Joel', + title: 'creator of Docusaurus', + hello: new Date(), + key: 'jmarcey', + page: false, + }, + }; + expect(validateAuthorsMapInput(authorsMap)).toEqual(authorsMap); + }); + + it('rename snake case image_url to camelCase imageURL', () => { + const authorsMap: AuthorsMapInput = { + slorber: { + name: 'Sébastien Lorber', + image_url: 'https://github.com/slorber.png', + key: 'slorber', + page: false, + }, + }; + expect(validateAuthorsMapInput(authorsMap)).toEqual({ + slorber: { + name: 'Sébastien Lorber', + imageURL: 'https://github.com/slorber.png', + page: false, + key: 'slorber', + }, + }); + }); + + it('accept author with only image', () => { + const authorsMap: AuthorsMapInput = { + slorber: { + imageURL: 'https://github.com/slorber.png', + url: 'https://github.com/slorber', + key: 'slorber', + page: false, + }, + }; + expect(validateAuthorsMapInput(authorsMap)).toEqual(authorsMap); + }); + + it('reject author without name or image', () => { + const authorsMap: AuthorsMapInput = { + slorber: { + title: 'foo', + key: 'slorber', + page: false, + }, + }; + expect(() => + validateAuthorsMapInput(authorsMap), + ).toThrowErrorMatchingInlineSnapshot( + `""slorber" must contain at least one of [name, imageURL]"`, + ); + }); + + it('reject undefined author', () => { + expect(() => + validateAuthorsMapInput({ + slorber: undefined, + }), + ).toThrowErrorMatchingInlineSnapshot( + `""slorber" cannot be undefined. It should be an author object containing properties like name, title, and imageURL."`, + ); + }); + + it('reject null author', () => { + expect(() => + validateAuthorsMapInput({ + slorber: null, + }), + ).toThrowErrorMatchingInlineSnapshot( + `""slorber" should be an author object containing properties like name, title, and imageURL."`, + ); + }); + + it('reject array author', () => { + expect(() => + validateAuthorsMapInput({slorber: []}), + ).toThrowErrorMatchingInlineSnapshot( + `""slorber" should be an author object containing properties like name, title, and imageURL."`, + ); + }); + + it('reject array content', () => { + expect(() => + validateAuthorsMapInput([]), + ).toThrowErrorMatchingInlineSnapshot( + `"The authors map file should contain an object where each entry contains an author key and the corresponding author's data."`, + ); + }); + + it('reject flat author', () => { + expect(() => + validateAuthorsMapInput({name: 'Sébastien'}), + ).toThrowErrorMatchingInlineSnapshot( + `""name" should be an author object containing properties like name, title, and imageURL."`, + ); + }); + + it('reject non-map author', () => { + const authorsMap: AuthorsMapInput = { + // @ts-expect-error: intentionally invalid + slorber: [], + }; + expect(() => + validateAuthorsMapInput(authorsMap), + ).toThrowErrorMatchingInlineSnapshot( + `""slorber" should be an author object containing properties like name, title, and imageURL."`, + ); + }); +}); + +describe('authors socials', () => { + it('valid known author map socials', () => { + const authorsMap: AuthorsMapInput = { + ozaki: { + name: 'ozaki', + socials: { + twitter: 'ozakione', + github: 'ozakione', + }, + key: 'ozaki', + page: false, + }, + }; + + expect(validateAuthorsMap(authorsMap)).toEqual(authorsMap); + }); + + it('throw socials that are not strings', () => { + const authorsMap: AuthorsMapInput = { + ozaki: { + name: 'ozaki', + socials: { + // @ts-expect-error: for tests + twitter: 42, + }, + }, + }; + + expect(() => + validateAuthorsMap(authorsMap), + ).toThrowErrorMatchingInlineSnapshot( + `""ozaki.socials.twitter" must be a string"`, + ); + }); + + it('throw socials that are objects', () => { + const authorsMap: AuthorsMapInput = { + ozaki: { + name: 'ozaki', + socials: { + // @ts-expect-error: for tests + twitter: {link: 'ozakione'}, + }, + }, + }; + + expect(() => + validateAuthorsMap(authorsMap), + ).toThrowErrorMatchingInlineSnapshot( + `""ozaki.socials.twitter" must be a string"`, + ); + }); + + it('valid unknown author map socials', () => { + const authorsMap: AuthorsMapInput = { + ozaki: { + name: 'ozaki', + socials: { + random: 'ozakione', + }, + key: 'ozaki', + page: false, + }, + }; + + expect(validateAuthorsMap(authorsMap)).toEqual(authorsMap); + }); +}); diff --git a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts index 8cbb1a13dece..d1378aa3f26d 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsMap.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsMap.ts @@ -6,7 +6,7 @@ */ import _ from 'lodash'; -import {getDataFileData, normalizeUrl} from '@docusaurus/utils'; +import {readDataFile, normalizeUrl} from '@docusaurus/utils'; import {Joi, URISchema} from '@docusaurus/utils-validation'; import {AuthorSocialsSchema, normalizeSocials} from './authorsSocials'; import type {BlogContentPaths} from './types'; @@ -142,15 +142,11 @@ async function getAuthorsMapInput(params: { authorsMapPath: string; contentPaths: BlogContentPaths; }): Promise { - return getDataFileData( - { - filePath: params.authorsMapPath, - contentPaths: params.contentPaths, - fileType: 'authors map', - }, - // TODO annoying to test: tightly coupled FS reads + validation... - validateAuthorsMapInput, - ); + const content = await readDataFile({ + filePath: params.authorsMapPath, + contentPaths: params.contentPaths, + }); + return content ? validateAuthorsMapInput(content) : undefined; } export async function getAuthorsMap(params: { From 9553099e35e5129f4dd23e25e356456147041f26 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 25 Jul 2024 19:16:35 +0200 Subject: [PATCH 075/115] remove useless function --- packages/docusaurus-theme-common/src/internal.ts | 6 +----- packages/docusaurus-theme-common/src/utils/generalUtils.ts | 7 ------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts index d631a7d39dde..9b3956f2d859 100644 --- a/packages/docusaurus-theme-common/src/internal.ts +++ b/packages/docusaurus-theme-common/src/internal.ts @@ -42,11 +42,7 @@ export { export {DEFAULT_SEARCH_TAG} from './utils/searchUtils'; -export { - useTitleFormatter, - listTagsByLetters, - listAuthorsByLetters, -} from './utils/generalUtils'; +export {useTitleFormatter, listTagsByLetters} from './utils/generalUtils'; export {useLocationChange} from './utils/useLocationChange'; diff --git a/packages/docusaurus-theme-common/src/utils/generalUtils.ts b/packages/docusaurus-theme-common/src/utils/generalUtils.ts index 5edc6c32c4cb..c8b67463e5e5 100644 --- a/packages/docusaurus-theme-common/src/utils/generalUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/generalUtils.ts @@ -6,7 +6,6 @@ */ import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; import type {TagsListItem} from '@docusaurus/utils'; /** @@ -81,9 +80,3 @@ export function listTagsByLetters( ): LetterEntry[] { return listByLetters(tags, (tag) => tag.label); } - -export function listAuthorsByLetters( - authors: readonly AuthorItemProp[], -): LetterEntry[] { - return listByLetters(authors, (author) => author.name); -} From ca2afb360e2d328be09d28313f6dd167cdba7c00 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 25 Jul 2024 19:39:48 +0200 Subject: [PATCH 076/115] rename toAuthorItemProp --- .../docusaurus-plugin-content-blog/src/props.ts | 2 +- .../docusaurus-plugin-content-blog/src/routes.ts | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/props.ts b/packages/docusaurus-plugin-content-blog/src/props.ts index e3605f2f45e8..6eae243dba2b 100644 --- a/packages/docusaurus-plugin-content-blog/src/props.ts +++ b/packages/docusaurus-plugin-content-blog/src/props.ts @@ -42,7 +42,7 @@ export function toTagProp({ }; } -export function toAuthorProp({ +export function toAuthorItemProp({ author, count, }: { diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index 8d2f00a069e7..52f5c297a4e1 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -13,7 +13,12 @@ import { } from '@docusaurus/utils'; import {paginateBlogPosts, shouldBeListed} from './blogUtils'; -import {toAuthorProp, toBlogSidebarProp, toTagProp, toTagsProp} from './props'; +import { + toAuthorItemProp, + toBlogSidebarProp, + toTagProp, + toTagsProp, +} from './props'; import {groupBlogPostsByAuthorKey} from './authors'; import type { PluginContentLoadedActions, @@ -291,7 +296,7 @@ export async function buildAllRoutes({ }, props: { authors: authorEntries.map(([authorKey, author]) => - toAuthorProp({ + toAuthorItemProp({ author, count: blogPostsByAuthorKey[authorKey]?.length ?? 0, }), @@ -346,9 +351,9 @@ export async function buildAllRoutes({ sidebar: sidebarModulePath, }, props: { - author: toAuthorProp({author, count: authorBlogPosts.length}), + author: toAuthorItemProp({author, count: authorBlogPosts.length}), listMetadata: metadata, - authorsPageLink, + authorsPageLink, // TODO move to blogMetadata }, }; }); From 7268707aaf1913466668fd32cb9d7d69c357ea05 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 25 Jul 2024 19:59:25 +0200 Subject: [PATCH 077/115] revert generic listByLetters generic util --- packages/docusaurus-theme-common/src/index.ts | 8 ++- .../src/utils/__tests__/tagUtils.test.ts | 10 +-- .../src/utils/generalUtils.ts | 63 ------------------- .../src/utils/tagsUtils.ts | 35 +++++++++++ 4 files changed, 45 insertions(+), 71 deletions(-) diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index b4a34dadb761..494d5caabae9 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -86,11 +86,13 @@ export {useWindowSize} from './hooks/useWindowSize'; * Note: we still guarantee retro-compatibility on those */ -export {translateTagsPageTitle} from './utils/tagsUtils'; - export {translateAuthorsPageTitle} from './utils/authorsUtils'; -export {type LetterEntry} from './utils/generalUtils'; +export { + translateTagsPageTitle, + listTagsByLetters, + type TagLetterEntry, +} from './utils/tagsUtils'; export { useSearchQueryString, diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts index 0b54d9d064b3..8e5fac8cef16 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts +++ b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts @@ -6,7 +6,7 @@ */ import _ from 'lodash'; -import {listTagsByLetters} from '../generalUtils'; +import {listTagsByLetters} from '../tagsUtils'; import type {TagsListItem} from '@docusaurus/utils'; describe('listTagsByLetters', () => { @@ -51,10 +51,10 @@ describe('listTagsByLetters', () => { }; const expectedResult: Result = [ - {letter: 'A', items: [tagAaa, tagAbc]}, - {letter: 'D', items: [tagDef]}, - {letter: 'T', items: [tag1, tag2]}, - {letter: 'Z', items: [tagZxy]}, + {letter: 'A', tags: [tagAaa, tagAbc]}, + {letter: 'D', tags: [tagDef]}, + {letter: 'T', tags: [tag1, tag2]}, + {letter: 'Z', tags: [tagZxy]}, ]; // Input order shouldn't matter, output is always consistently sorted diff --git a/packages/docusaurus-theme-common/src/utils/generalUtils.ts b/packages/docusaurus-theme-common/src/utils/generalUtils.ts index c8b67463e5e5..c6732b82ccf6 100644 --- a/packages/docusaurus-theme-common/src/utils/generalUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/generalUtils.ts @@ -6,7 +6,6 @@ */ import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import type {TagsListItem} from '@docusaurus/utils'; /** * Formats the page's title based on relevant site config and other contexts. @@ -18,65 +17,3 @@ export function useTitleFormatter(title?: string | undefined): string { ? `${title.trim()} ${titleDelimiter} ${siteTitle}` : siteTitle; } -interface HasLabel { - label: string; -} - -interface HasName { - name?: string; - imageURL?: string; -} - -export type Entry = HasLabel | HasName; - -export type LetterEntry = {letter: string | undefined; items: T[]}; - -/** - * Takes a list of tags or author (as provided by the content plugins), - * and groups them by their initials. - */ -export function listByLetters( - items: readonly T[], - getLabel: (item: T) => string | undefined, -): LetterEntry[] { - // Group items by their initial letter or undefined - const groups: Record = items.reduce((acc, item) => { - const label = getLabel(item); - const key = label ? label[0]!.toUpperCase() : 'undefined'; - if (!acc[key]) { - acc[key] = []; - } - acc[key].push(item); - return acc; - }, {} as Record); - - // Convert groups object to array and sort - return Object.entries(groups) - .sort(([a], [b]) => { - if (a === 'undefined') { - return 1; - } else if (b === 'undefined') { - return -1; - } else { - return a.localeCompare(b); - } - }) - .map(([letter, groupedItems]) => { - // Sort items within each group - const sortedItems = groupedItems.slice().sort((a, b) => { - const labelA = getLabel(a) ?? ''; - const labelB = getLabel(b) ?? ''; - return labelA.localeCompare(labelB); - }); - return { - letter: letter === 'undefined' ? undefined : letter, - items: sortedItems, - }; - }); -} - -export function listTagsByLetters( - tags: readonly TagsListItem[], -): LetterEntry[] { - return listByLetters(tags, (tag) => tag.label); -} diff --git a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts index e3c37e59e2b8..23ab3d40df5d 100644 --- a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts @@ -6,6 +6,7 @@ */ import {translate} from '@docusaurus/Translate'; +import type {TagsListItem} from '@docusaurus/utils'; export const translateTagsPageTitle = (): string => translate({ @@ -13,3 +14,37 @@ export const translateTagsPageTitle = (): string => message: 'Tags', description: 'The title of the tag list page', }); + +export type TagLetterEntry = {letter: string; tags: TagsListItem[]}; + +function getTagLetter(tag: string): string { + return tag[0]!.toUpperCase(); +} + +/** + * Takes a list of tags (as provided by the content plugins), and groups them by + * their initials. + */ +export function listTagsByLetters( + tags: readonly TagsListItem[], +): TagLetterEntry[] { + const groups: {[initial: string]: TagsListItem[]} = {}; + Object.values(tags).forEach((tag) => { + const initial = getTagLetter(tag.label); + groups[initial] ??= []; + groups[initial]!.push(tag); + }); + + return ( + Object.entries(groups) + // Sort letters + .sort(([letter1], [letter2]) => letter1.localeCompare(letter2)) + .map(([letter, letterTags]) => { + // Sort tags inside a letter + const sortedTags = letterTags.sort((tag1, tag2) => + tag1.label.localeCompare(tag2.label), + ); + return {letter, tags: sortedTags}; + }) + ); +} From 493f3d7a3a336728f32e7d504e279586345d3dc8 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 25 Jul 2024 20:01:40 +0200 Subject: [PATCH 078/115] revert generic listByLetters generic util --- .../src/theme/TagsListByLetter/index.tsx | 12 ++--- .../src/utils/__tests__/generalUtils.test.tsx | 47 +------------------ 2 files changed, 4 insertions(+), 55 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx b/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx index 1bf44d27d5d4..ffd28c1585b8 100644 --- a/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/TagsListByLetter/index.tsx @@ -6,26 +6,20 @@ */ import React from 'react'; -import {type LetterEntry} from '@docusaurus/theme-common'; -import {listTagsByLetters} from '@docusaurus/theme-common/internal'; +import {listTagsByLetters, type TagLetterEntry} from '@docusaurus/theme-common'; import Tag from '@theme/Tag'; import type {Props} from '@theme/TagsListByLetter'; import Heading from '@theme/Heading'; -import type {TagsListItem} from '@docusaurus/utils'; import styles from './styles.module.css'; -function TagLetterEntryItem({ - letterEntry, -}: { - letterEntry: LetterEntry; -}) { +function TagLetterEntryItem({letterEntry}: {letterEntry: TagLetterEntry}) { return (
        {letterEntry.letter}
          - {letterEntry.items.map((tag) => ( + {letterEntry.tags.map((tag) => (
        • diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx b/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx index 20bbf920459b..02c2ad551993 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx +++ b/packages/docusaurus-theme-common/src/utils/__tests__/generalUtils.test.tsx @@ -8,8 +8,7 @@ import React from 'react'; import {renderHook} from '@testing-library/react-hooks'; import {Context} from '@docusaurus/core/src/client/docusaurusContext'; -import {listByLetters, useTitleFormatter} from '../generalUtils'; -import type {Entry, LetterEntry} from '../generalUtils'; +import {useTitleFormatter} from '../generalUtils'; import type {DocusaurusContext} from '@docusaurus/types'; describe('useTitleFormatter', () => { @@ -32,47 +31,3 @@ describe('useTitleFormatter', () => { expect(mockUseTitleFormatter(' ')).toBe('my site'); }); }); - -describe('listByLetters', () => { - it('group items by their initial letters', () => { - const items: Entry[] = [ - {label: 'Apple'}, - {label: 'Banana'}, - {label: 'apricot'}, - {name: 'Alice'}, - {name: 'Bob'}, - {name: 'Albert'}, - {label: 'avocado'}, - {name: undefined}, - ]; - - const result = listByLetters(items, (item) => - 'label' in item ? item.label : item.name, - ); - - const expected: LetterEntry[] = [ - { - letter: 'A', - items: [ - {name: 'Albert'}, - {name: 'Alice'}, - {label: 'Apple'}, - {label: 'apricot'}, - {label: 'avocado'}, - ], - }, - {letter: 'B', items: [{label: 'Banana'}, {name: 'Bob'}]}, - {letter: undefined, items: [{name: undefined}]}, - ]; - - expect(result).toEqual(expected); - }); - - it('handle empty input', () => { - const result = listByLetters([], (item) => - // @ts-expect-error: test edge case - 'label' in item ? item.label : item.name, - ); - expect(result).toEqual([]); - }); -}); From 30d076bfe30b3904a9432365ec941eb035da5915 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 12:43:06 +0200 Subject: [PATCH 079/115] bad import --- packages/docusaurus-theme-common/src/internal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts index 9b3956f2d859..7929ed3f8539 100644 --- a/packages/docusaurus-theme-common/src/internal.ts +++ b/packages/docusaurus-theme-common/src/internal.ts @@ -42,7 +42,7 @@ export { export {DEFAULT_SEARCH_TAG} from './utils/searchUtils'; -export {useTitleFormatter, listTagsByLetters} from './utils/generalUtils'; +export {useTitleFormatter} from './utils/generalUtils'; export {useLocationChange} from './utils/useLocationChange'; From e4da374bbd2c30366f66039144d95298ed08b950 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 13:25:42 +0200 Subject: [PATCH 080/115] useless label --- .../docusaurus-theme-translations/locales/base/theme-common.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/docusaurus-theme-translations/locales/base/theme-common.json b/packages/docusaurus-theme-translations/locales/base/theme-common.json index 84cae72a1ece..18d3a6c51b92 100644 --- a/packages/docusaurus-theme-translations/locales/base/theme-common.json +++ b/packages/docusaurus-theme-translations/locales/base/theme-common.json @@ -140,7 +140,6 @@ "theme.tags.tagsPageTitle___DESCRIPTION": "The title of the tag list page", "theme.authors.authorsPageLink": "View All Authors", "theme.authors.authorsPageTitle": "Authors", - "theme.authors.website": "Personal website", "theme.blog.authorTitle": "{nPosts} contributed by \"{authorName}\"", "theme.unlistedContent.message": "This page is unlisted. Search engines will not index it, and only users having a direct link can access it.", "theme.unlistedContent.message___DESCRIPTION": "The unlisted content banner message", From 50f209cd10b28164a4a3707eed5ed1e1c6ece61f Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 13:48:59 +0200 Subject: [PATCH 081/115] rename test file to match impl --- .../src/utils/__tests__/{tagUtils.test.ts => tagsUtils.test.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/docusaurus-theme-common/src/utils/__tests__/{tagUtils.test.ts => tagsUtils.test.ts} (100%) diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts b/packages/docusaurus-theme-common/src/utils/__tests__/tagsUtils.test.ts similarity index 100% rename from packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts rename to packages/docusaurus-theme-common/src/utils/__tests__/tagsUtils.test.ts From 9d47ed1498574445bf8ef3157ed7c6a038a2bd8c Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 14:14:51 +0200 Subject: [PATCH 082/115] remove useless lastUpdated in test output --- .../src/__tests__/index.test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts index 075fd85e8162..4108b94e9f96 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts @@ -196,8 +196,6 @@ describe('blog plugin', () => { permalink: '/blog/tags/date', }, ], - lastUpdatedAt: undefined, - lastUpdatedBy: undefined, nextItem: { permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash', title: 'Happy 1st Birthday Slash! (translated)', @@ -260,8 +258,6 @@ describe('blog plugin', () => { permalink: '/blog/tags/global-tag-permalink (en)', }, ], - lastUpdatedAt: undefined, - lastUpdatedBy: undefined, prevItem: { permalink: '/blog/date-matter', title: 'date-matter', @@ -347,8 +343,6 @@ describe('blog plugin', () => { title: 'Simple Slug', }, tags: [], - lastUpdatedAt: undefined, - lastUpdatedBy: undefined, hasTruncateMarker: false, unlisted: false, }); From bd1c3d1a761745e13f7eb5fcecd4aed9868d6545 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 14:15:35 +0200 Subject: [PATCH 083/115] remove useless lastUpdated in test output --- .../docusaurus-plugin-content-blog/src/__tests__/index.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts index 4108b94e9f96..147ff98d93b6 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts @@ -490,8 +490,6 @@ describe('blog plugin', () => { tags: [], prevItem: undefined, nextItem: undefined, - lastUpdatedAt: undefined, - lastUpdatedBy: undefined, hasTruncateMarker: false, unlisted: false, }); From 64fa38da1ce7e44730580a1709135338fdcafb8e Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 14:23:48 +0200 Subject: [PATCH 084/115] add integration test for localized blog author page permalink --- .../src/__tests__/__fixtures__/website/blog/authors.yml | 3 ++- .../website/i18n/en/docusaurus-plugin-content-blog/authors.yml | 2 ++ .../docusaurus-plugin-content-blog/src/__tests__/index.test.ts | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/authors.yml b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/authors.yml index c44d2ee68da6..a0e3215c10e3 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/authors.yml +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/authors.yml @@ -1,5 +1,5 @@ slorber: - name: Sébastien Lorber + name: Sébastien Lsdsdorber title: Docusaurus maintainer email: lorber.sebastien@gmail.com url: https://sebastienlorber.com @@ -7,3 +7,4 @@ slorber: twitter: sebastienlorber x: https://x.com/sebastienlorber github: slorber + page: true diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/authors.yml b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/authors.yml index f509f4ff45ee..0e68e44c2151 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/authors.yml +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/authors.yml @@ -2,3 +2,5 @@ slorber: name: Sébastien Lorber (translated) title: Docusaurus maintainer (translated) email: lorber.sebastien@gmail.com + page: + permalink: "/slorber-custom-permalink-localized" diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts index 147ff98d93b6..b5ffcb7571b6 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts @@ -230,7 +230,7 @@ describe('blog plugin', () => { name: 'Sébastien Lorber (translated)', title: 'Docusaurus maintainer (translated)', imageURL: undefined, - page: null, + page: {permalink: '/blog/authors/slorber-custom-permalink-localized'}, }, ], date: new Date('2018-12-14'), From 519d2a962e8e80c3229e636500d3823fb873e6ac Mon Sep 17 00:00:00 2001 From: slorber Date: Fri, 26 Jul 2024 12:28:15 +0000 Subject: [PATCH 085/115] refactor: apply lint autofix --- project-words.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/project-words.txt b/project-words.txt index b9db52dfb400..defddb667d25 100644 --- a/project-words.txt +++ b/project-words.txt @@ -171,6 +171,7 @@ Lorber Lorber's lqip LQIP +Lsdsdorber lunrjs Marcey Marcey's From 7927b5d5724c817865a3161f630f51274cdbd6e2 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 14:33:23 +0200 Subject: [PATCH 086/115] commit typo --- .../src/__tests__/__fixtures__/website/blog/authors.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/authors.yml b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/authors.yml index a0e3215c10e3..6e7482b6ebd6 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/authors.yml +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/authors.yml @@ -1,5 +1,5 @@ slorber: - name: Sébastien Lsdsdorber + name: Sébastien Lorber title: Docusaurus maintainer email: lorber.sebastien@gmail.com url: https://sebastienlorber.com From ccfc5060c3003ddc2484a086ddc62222ca534a1b Mon Sep 17 00:00:00 2001 From: slorber Date: Fri, 26 Jul 2024 12:38:08 +0000 Subject: [PATCH 087/115] refactor: apply lint autofix --- project-words.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/project-words.txt b/project-words.txt index defddb667d25..b9db52dfb400 100644 --- a/project-words.txt +++ b/project-words.txt @@ -171,7 +171,6 @@ Lorber Lorber's lqip LQIP -Lsdsdorber lunrjs Marcey Marcey's From befb55280da1a135efac7b131e731282055ff9b1 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 15:14:14 +0200 Subject: [PATCH 088/115] refactor blog translations - introduce AuthorWithKey type --- .../src/plugin-content-blog.d.ts | 7 +- .../src/props.ts | 4 +- .../src/routes.ts | 17 ++-- .../src/theme/BlogAuthorsListPage/index.tsx | 4 +- .../src/theme/BlogAuthorsPostsPage/index.tsx | 27 ++----- .../src/theme/BlogTagsPostsPage/index.tsx | 16 +--- packages/docusaurus-theme-common/src/index.ts | 2 - .../docusaurus-theme-common/src/internal.ts | 7 +- .../src/translations/blogTranslations.tsx | 79 +++++++++++++++++++ .../src/utils/authorsUtils.ts | 15 ---- .../src/utils/usePluralForm.ts | 19 ----- .../locales/base/theme-common.json | 6 +- 12 files changed, 111 insertions(+), 92 deletions(-) create mode 100644 packages/docusaurus-theme-common/src/translations/blogTranslations.tsx delete mode 100644 packages/docusaurus-theme-common/src/utils/authorsUtils.ts diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 90d9a4f8a2f0..3c1b21285a2b 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -120,8 +120,11 @@ declare module '@docusaurus/plugin-content-blog' { page: AuthorPage | null; }; + /** Authors coming from the AuthorsMap always have a key */ + export type AuthorWithKey = Author & {key: string}; + /** What the authors list page should know about each author. */ - export type AuthorItemProp = Author & { + export type AuthorItemProp = AuthorWithKey & { /** Number of blog posts with this author. */ count: number; }; @@ -537,7 +540,7 @@ declare module '@docusaurus/plugin-content-blog' { items: BlogSidebarItem[]; }; - export type AuthorsMap = {[authorKey: string]: Author & {key: string}}; + export type AuthorsMap = {[authorKey: string]: AuthorWithKey}; export type BlogContent = { blogSidebarTitle: string; diff --git a/packages/docusaurus-plugin-content-blog/src/props.ts b/packages/docusaurus-plugin-content-blog/src/props.ts index 6eae243dba2b..df7afbb42563 100644 --- a/packages/docusaurus-plugin-content-blog/src/props.ts +++ b/packages/docusaurus-plugin-content-blog/src/props.ts @@ -6,8 +6,8 @@ */ import type {TagsListItem, TagModule} from '@docusaurus/utils'; import type { - Author, AuthorItemProp, + AuthorWithKey, BlogPost, BlogSidebar, BlogTag, @@ -46,7 +46,7 @@ export function toAuthorItemProp({ author, count, }: { - author: Author; + author: AuthorWithKey; count: number; }): AuthorItemProp { return { diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index 52f5c297a4e1..ca7753305bfa 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -32,7 +32,7 @@ import type { BlogContent, PluginOptions, BlogPost, - Author, + AuthorWithKey, } from '@docusaurus/plugin-content-blog'; type CreateAllRoutesParam = { @@ -271,7 +271,7 @@ export async function buildAllRoutes({ authorsMap, blogPosts, }); - const authorEntries = Object.entries(authorsMap); + const authors = Object.values(authorsMap); const authorsPageLink = normalizeUrl([ baseUrl, @@ -281,9 +281,7 @@ export async function buildAllRoutes({ return Promise.all([ createAuthorListRoute(), - ...authorEntries.flatMap(([authorKey, author]) => - createAuthorPaginatedRoute(authorKey, author), - ), + ...authors.flatMap(createAuthorPaginatedRoute), ]).then((routes) => routes.flat()); async function createAuthorListRoute(): Promise { @@ -295,10 +293,10 @@ export async function buildAllRoutes({ sidebar: sidebarModulePath, }, props: { - authors: authorEntries.map(([authorKey, author]) => + authors: authors.map((author) => toAuthorItemProp({ author, - count: blogPostsByAuthorKey[authorKey]?.length ?? 0, + count: blogPostsByAuthorKey[author.key]?.length ?? 0, }), ), }, @@ -306,10 +304,9 @@ export async function buildAllRoutes({ } async function createAuthorPaginatedRoute( - authorKey: string, - author: Author, + author: AuthorWithKey, ): Promise { - const authorBlogPosts = blogPostsByAuthorKey[authorKey] ?? []; + const authorBlogPosts = blogPostsByAuthorKey[author.key] ?? []; if (!author.page) { return []; } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx index 8a24a7339842..84d8492471f4 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx @@ -9,9 +9,9 @@ import clsx from 'clsx'; import { PageMetadata, HtmlClassNameProvider, - translateAuthorsPageTitle, ThemeClassNames, } from '@docusaurus/theme-common'; +import {translateBlogAuthorsPageTitle} from '@docusaurus/theme-common/internal'; import BlogLayout from '@theme/BlogLayout'; import AuthorsList from '@theme/AuthorsList'; import type {Props} from '@theme/BlogAuthorsListPage'; @@ -22,7 +22,7 @@ export default function BlogAuthorsListPage({ authors, sidebar, }: Props): JSX.Element { - const title: string = translateAuthorsPageTitle(); + const title: string = translateBlogAuthorsPageTitle(); return ( {author.description &&

          {author.description}

          } - - View All Authors - +
        diff --git a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx index 5ddff149d4e9..d6a716ff2d18 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx @@ -7,13 +7,13 @@ import React from 'react'; import clsx from 'clsx'; -import Translate, {translate} from '@docusaurus/Translate'; +import Translate from '@docusaurus/Translate'; import { PageMetadata, HtmlClassNameProvider, ThemeClassNames, } from '@docusaurus/theme-common'; -import {useBlogPostsPlural} from '@docusaurus/theme-common/internal'; +import {useBlogTagsPostsPageTitle} from '@docusaurus/theme-common/internal'; import Link from '@docusaurus/Link'; import BlogLayout from '@theme/BlogLayout'; import BlogListPaginator from '@theme/BlogListPaginator'; @@ -23,18 +23,6 @@ import BlogPostItems from '@theme/BlogPostItems'; import Unlisted from '@theme/Unlisted'; import Heading from '@theme/Heading'; -function useBlogTagsPostsPageTitle(tag: Props['tag']): string { - const blogPostsPlural = useBlogPostsPlural(); - return translate( - { - id: 'theme.blog.tagTitle', - description: 'The title of the page for a blog tag', - message: '{nPosts} tagged with "{tagName}"', - }, - {nPosts: blogPostsPlural(tag.count), tagName: tag.label}, - ); -} - function BlogTagsPostsPageMetadata({tag}: Props): JSX.Element { const title = useBlogTagsPostsPageTitle(tag); return ( diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 494d5caabae9..593da9842b34 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -86,8 +86,6 @@ export {useWindowSize} from './hooks/useWindowSize'; * Note: we still guarantee retro-compatibility on those */ -export {translateAuthorsPageTitle} from './utils/authorsUtils'; - export { translateTagsPageTitle, listTagsByLetters, diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts index 7929ed3f8539..4658e1cfb026 100644 --- a/packages/docusaurus-theme-common/src/internal.ts +++ b/packages/docusaurus-theme-common/src/internal.ts @@ -91,4 +91,9 @@ export {useCodeWordWrap} from './hooks/useCodeWordWrap'; export {getPrismCssVariables} from './utils/codeBlockUtils'; export {useBackToTopButton} from './hooks/useBackToTopButton'; -export {useBlogPostsPlural} from './utils/usePluralForm'; +export { + useBlogTagsPostsPageTitle, + useBlogAuthorsPostsPageTitle, + translateBlogAuthorsPageTitle, + BlogAuthorsViewAllLabel, +} from './translations/blogTranslations'; diff --git a/packages/docusaurus-theme-common/src/translations/blogTranslations.tsx b/packages/docusaurus-theme-common/src/translations/blogTranslations.tsx new file mode 100644 index 000000000000..e87450670b24 --- /dev/null +++ b/packages/docusaurus-theme-common/src/translations/blogTranslations.tsx @@ -0,0 +1,79 @@ +/** + * 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 React, {type ReactNode} from 'react'; +import Translate, {translate} from '@docusaurus/Translate'; +import {usePluralForm} from '../utils/usePluralForm'; + +// Only used internally +function useBlogPostsPlural(): (count: number) => string { + const {selectMessage} = usePluralForm(); + return (count: number) => + selectMessage( + count, + translate( + { + id: 'theme.blog.post.plurals', + description: + 'Pluralized label for "{count} posts". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)', + message: 'One post|{count} posts', + }, + {count}, + ), + ); +} + +export function useBlogTagsPostsPageTitle(tag: { + label: string; + count: number; +}): string { + const blogPostsPlural = useBlogPostsPlural(); + return translate( + { + id: 'theme.blog.tagTitle', + description: 'The title of the page for a blog tag', + message: '{nPosts} tagged with "{tagName}"', + }, + {nPosts: blogPostsPlural(tag.count), tagName: tag.label}, + ); +} + +export function useBlogAuthorsPostsPageTitle(author: { + key: string; + name?: string; + count: number; +}): string { + const blogPostsPlural = useBlogPostsPlural(); + return translate( + { + id: 'theme.blog.author.pageTitle', + description: 'The title of the page for a blog author', + message: '{authorName} - {nPosts}', + }, + { + nPosts: blogPostsPlural(author.count), + authorName: author.name || author.key, + }, + ); +} + +export const translateBlogAuthorsPageTitle = (): string => + translate({ + id: 'theme.blog.authors.pageTitle', + message: 'Authors', + description: 'The title of the authors page', + }); + +export function BlogAuthorsViewAllLabel(): ReactNode { + return ( + + View All Authors + + ); +} diff --git a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts b/packages/docusaurus-theme-common/src/utils/authorsUtils.ts deleted file mode 100644 index cc05a203e517..000000000000 --- a/packages/docusaurus-theme-common/src/utils/authorsUtils.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 {translate} from '@docusaurus/Translate'; - -export const translateAuthorsPageTitle = (): string => - translate({ - id: 'theme.authors.authorsPageTitle', - message: 'Authors', - description: 'The title of the author list page', - }); diff --git a/packages/docusaurus-theme-common/src/utils/usePluralForm.ts b/packages/docusaurus-theme-common/src/utils/usePluralForm.ts index 16ab0980de0b..726d406db2d7 100644 --- a/packages/docusaurus-theme-common/src/utils/usePluralForm.ts +++ b/packages/docusaurus-theme-common/src/utils/usePluralForm.ts @@ -7,7 +7,6 @@ import {useMemo} from 'react'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import {translate} from '@docusaurus/Translate'; // We want to ensurer a stable plural form order in all cases // It is more convenient and natural to handle "small values" first @@ -130,21 +129,3 @@ export function usePluralForm(): { selectPluralMessage(pluralMessages, count, localePluralForm), }; } - -// Very simple pluralization: probably good enough for now -export function useBlogPostsPlural(): (count: number) => string { - const {selectMessage} = usePluralForm(); - return (count: number) => - selectMessage( - count, - translate( - { - id: 'theme.blog.post.plurals', - description: - 'Pluralized label for "{count} posts". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)', - message: 'One post|{count} posts', - }, - {count}, - ), - ); -} diff --git a/packages/docusaurus-theme-translations/locales/base/theme-common.json b/packages/docusaurus-theme-translations/locales/base/theme-common.json index 18d3a6c51b92..8d5dc1856c84 100644 --- a/packages/docusaurus-theme-translations/locales/base/theme-common.json +++ b/packages/docusaurus-theme-translations/locales/base/theme-common.json @@ -41,6 +41,9 @@ "theme.admonition.tip___DESCRIPTION": "The default label used for the Tip admonition (:::tip)", "theme.admonition.warning": "warning", "theme.admonition.warning___DESCRIPTION": "The default label used for the Warning admonition (:::warning)", + "theme.blog.authors.pageTitle": "Authors", + "theme.blog.authors.viewAll": "View All Authors", + "theme.blog.author.pageTitle": "{authorName} - {nPosts}", "theme.blog.archive.description": "Archive", "theme.blog.archive.description___DESCRIPTION": "The page & hero description of the blog archive page", "theme.blog.archive.title": "Archive", @@ -138,9 +141,6 @@ "theme.tags.tagsPageLink___DESCRIPTION": "The label of the link targeting the tag list page", "theme.tags.tagsPageTitle": "Tags", "theme.tags.tagsPageTitle___DESCRIPTION": "The title of the tag list page", - "theme.authors.authorsPageLink": "View All Authors", - "theme.authors.authorsPageTitle": "Authors", - "theme.blog.authorTitle": "{nPosts} contributed by \"{authorName}\"", "theme.unlistedContent.message": "This page is unlisted. Search engines will not index it, and only users having a direct link can access it.", "theme.unlistedContent.message___DESCRIPTION": "The unlisted content banner message", "theme.unlistedContent.title": "Unlisted page", From 7376f6f92cbafd9674d9b86ed4854efe083110e2 Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:00:05 +0200 Subject: [PATCH 089/115] docs: add authors page section --- website/docs/blog.mdx | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/website/docs/blog.mdx b/website/docs/blog.mdx index 8ec62d11fea7..e59eee433581 100644 --- a/website/docs/blog.mdx +++ b/website/docs/blog.mdx @@ -401,6 +401,41 @@ An author, either declared through front matter or through the authors map, need ::: +### Authors page {#authors-page} + +Docusaurus generate an author's page listing all authors with their respective blog posts the author have `page: true`. + +By default the feature is disabled and will not generate the authors page. + +```yml title="website/blog/authors.yml" +slorber: + name: Sébastien Lorber + title: Docusaurus maintainer + url: https://sebastienlorber.com + image_url: https://github.com/slorber.png + page: true # Enable the authors page for this author + socials: + x: sebastienlorber + github: slorber +``` + +You can configure the route base path for the authors page using the `authorsPagePath` option in the plugin configuration. + +```js title="docusaurus.config.js" +export default { + presets: [ + [ + '@docusaurus/preset-classic', + { + blog: { + authorsPagePath: '/authors', + }, + }, + ], + ], +}; +``` + ## Blog post tags {#blog-post-tags} Tags are declared in the front matter and introduce another dimension of categorization. From 1025362675daa77206d3d200602f3ffb4b10c701 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 16:40:39 +0200 Subject: [PATCH 090/115] simplify routes => sync code --- .../docusaurus-plugin-content-blog/src/routes.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index ca7753305bfa..9de0e10326ba 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -262,8 +262,8 @@ export async function buildAllRoutes({ return [tagsListRoute, ...tagsPaginatedRoutes]; } - async function createAuthorsRoutes(): Promise { - if (authorsMap === undefined) { + function createAuthorsRoutes(): RouteConfig[] { + if (authorsMap === undefined || Object.keys(authorsMap).length === 0) { return []; } @@ -279,12 +279,12 @@ export async function buildAllRoutes({ authorsPageBasePath, ]); - return Promise.all([ + return [ createAuthorListRoute(), ...authors.flatMap(createAuthorPaginatedRoute), - ]).then((routes) => routes.flat()); + ]; - async function createAuthorListRoute(): Promise { + function createAuthorListRoute(): RouteConfig { return { path: authorsPageLink, component: blogAuthorsListComponent, @@ -303,9 +303,7 @@ export async function buildAllRoutes({ }; } - async function createAuthorPaginatedRoute( - author: AuthorWithKey, - ): Promise { + function createAuthorPaginatedRoute(author: AuthorWithKey): RouteConfig[] { const authorBlogPosts = blogPostsByAuthorKey[author.key] ?? []; if (!author.page) { return []; @@ -362,6 +360,6 @@ export async function buildAllRoutes({ ...createBlogPostsPaginatedRoutes(), ...createTagsRoutes(), ...createArchiveRoute(), - ...(await createAuthorsRoutes()), + ...createAuthorsRoutes(), ]; } From 295f7a1130863fac3e818de47d8151e014ceafdf Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 16:52:31 +0200 Subject: [PATCH 091/115] add authorsListPath to global blog metadata bundle --- .../src/plugin-content-blog.d.ts | 4 ++-- .../docusaurus-plugin-content-blog/src/routes.ts | 16 ++++++++-------- .../src/theme/BlogAuthorsPostsPage/index.tsx | 15 +++++++++++---- packages/docusaurus-utils/src/tags.ts | 1 + 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 3c1b21285a2b..67da48106245 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -554,6 +554,8 @@ declare module '@docusaurus/plugin-content-blog' { export type BlogMetadata = { /** the path to the base of the blog */ blogBasePath: string; + /** the path to the authors list page */ + authorsListPath: string; /** title of the overall blog */ blogTitle: string; }; @@ -743,8 +745,6 @@ declare module '@theme/BlogAuthorsPostsPage' { readonly sidebar: BlogSidebar; /** Metadata of this author. */ readonly author: AuthorItemProp; - /** Link to the page of all authors. */ - readonly authorsPageLink: string; /** Looks exactly the same as the posts list page */ readonly listMetadata: BlogPaginatedMetadata; /** diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index 9de0e10326ba..38095877c232 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -83,6 +83,12 @@ export async function buildAllRoutes({ authorsMap, } = content; + const authorsListPath = normalizeUrl([ + baseUrl, + routeBasePath, + authorsPageBasePath, + ]); + const listedBlogPosts = blogPosts.filter(shouldBeListed); const blogPostsById = _.keyBy(blogPosts, (post) => post.id); @@ -115,6 +121,7 @@ export async function buildAllRoutes({ const blogMetadata: BlogMetadata = { blogBasePath: normalizeUrl([baseUrl, routeBasePath]), blogTitle, + authorsListPath, }; const modulePath = await createData( `blogMetadata-${pluginId}.json`, @@ -273,12 +280,6 @@ export async function buildAllRoutes({ }); const authors = Object.values(authorsMap); - const authorsPageLink = normalizeUrl([ - baseUrl, - routeBasePath, - authorsPageBasePath, - ]); - return [ createAuthorListRoute(), ...authors.flatMap(createAuthorPaginatedRoute), @@ -286,7 +287,7 @@ export async function buildAllRoutes({ function createAuthorListRoute(): RouteConfig { return { - path: authorsPageLink, + path: authorsListPath, component: blogAuthorsListComponent, exact: true, modules: { @@ -348,7 +349,6 @@ export async function buildAllRoutes({ props: { author: toAuthorItemProp({author, count: authorBlogPosts.length}), listMetadata: metadata, - authorsPageLink, // TODO move to blogMetadata }, }; }); diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx index 969443debee3..0e4c9458691f 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx @@ -17,6 +17,7 @@ import { BlogAuthorsViewAllLabel, } from '@docusaurus/theme-common/internal'; import Link from '@docusaurus/Link'; +import {useBlogMetadata} from '@docusaurus/plugin-content-blog/client'; import BlogLayout from '@theme/BlogLayout'; import BlogListPaginator from '@theme/BlogListPaginator'; import SearchMetadata from '@theme/SearchMetadata'; @@ -37,12 +38,20 @@ function BlogAuthorsPostsPageMetadata({author}: Props): JSX.Element { ); } +function ViewAllAuthorsLink() { + const {authorsListPath} = useBlogMetadata(); + return ( + + + + ); +} + function BlogAuthorsPostsPageContent({ author, items, sidebar, listMetadata, - authorsPageLink, }: Props): JSX.Element { const title = useBlogAuthorsPostsPageTitle(author); return ( @@ -55,9 +64,7 @@ function BlogAuthorsPostsPageContent({ className={clsx(styles.postsPageAuthor)} /> {author.description &&

        {author.description}

        } - - - +
      diff --git a/packages/docusaurus-utils/src/tags.ts b/packages/docusaurus-utils/src/tags.ts index fd06bf2fea25..cef2ea7dd6f6 100644 --- a/packages/docusaurus-utils/src/tags.ts +++ b/packages/docusaurus-utils/src/tags.ts @@ -45,6 +45,7 @@ export type TagsListItem = Tag & { /** What the tag's own page should know about the tag. */ export type TagModule = TagsListItem & { /** The tags list page's permalink. */ + // TODO move this global value to a shared docs/blog bundle allTagsPath: string; /** Is this tag unlisted? (when it only contains unlisted items) */ unlisted: boolean; From cbd14e3b59cc319f540c657d120095529fa096ff Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 16:56:43 +0200 Subject: [PATCH 092/115] rename things for consistency --- packages/docusaurus-plugin-content-blog/src/index.ts | 4 ++-- packages/docusaurus-plugin-content-blog/src/options.ts | 6 +++--- .../src/plugin-content-blog.d.ts | 2 +- packages/docusaurus-plugin-content-blog/src/routes.ts | 6 +++--- .../src/theme/BlogAuthorsListPage/index.tsx | 4 ++-- .../src/theme/BlogAuthorsPostsPage/index.tsx | 6 +++--- packages/docusaurus-theme-common/src/internal.ts | 4 ++-- .../src/translations/blogTranslations.tsx | 8 ++++---- .../locales/base/theme-common.json | 2 +- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index b23acc8f959c..679924729e5f 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -161,7 +161,7 @@ export default async function pluginContentBlog( blogTitle, blogSidebarTitle, pageBasePath, - authorsPageBasePath, + authorsBasePath, authorsMapPath, } = options; @@ -174,7 +174,7 @@ export default async function pluginContentBlog( authorsBaseRoutePath: normalizeUrl([ baseUrl, routeBasePath, - authorsPageBasePath, + authorsBasePath, ]), }); checkAuthorsMapPermalinkCollisions(authorsMap); diff --git a/packages/docusaurus-plugin-content-blog/src/options.ts b/packages/docusaurus-plugin-content-blog/src/options.ts index 372b81d521a0..536174e3069c 100644 --- a/packages/docusaurus-plugin-content-blog/src/options.ts +++ b/packages/docusaurus-plugin-content-blog/src/options.ts @@ -60,7 +60,7 @@ export const DEFAULT_OPTIONS: PluginOptions = { processBlogPosts: async () => undefined, onInlineTags: 'warn', tags: undefined, - authorsPageBasePath: 'authors', + authorsBasePath: 'authors', onInlineAuthors: 'warn', }; @@ -166,8 +166,8 @@ const PluginOptionSchema = Joi.object({ .disallow('') .allow(null, false) .default(() => DEFAULT_OPTIONS.tags), - authorsPageBasePath: Joi.string() - .default(DEFAULT_OPTIONS.authorsPageBasePath) + authorsBasePath: Joi.string() + .default(DEFAULT_OPTIONS.authorsBasePath) .disallow(''), onInlineAuthors: Joi.string() .equal('ignore', 'log', 'warn', 'throw') diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 67da48106245..77cf054708d8 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -502,7 +502,7 @@ declare module '@docusaurus/plugin-content-blog' { */ processBlogPosts: ProcessBlogPostsFn; /* Base path for the authors page */ - authorsPageBasePath: string; + authorsBasePath: string; /** The behavior of Docusaurus when it finds inline authors. */ onInlineAuthors: 'ignore' | 'log' | 'warn' | 'throw'; }; diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index 38095877c232..2ebe104ee19f 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -68,7 +68,7 @@ export async function buildAllRoutes({ routeBasePath, archiveBasePath, blogTitle, - authorsPageBasePath, + authorsBasePath, postsPerPage, blogDescription, } = options; @@ -86,7 +86,7 @@ export async function buildAllRoutes({ const authorsListPath = normalizeUrl([ baseUrl, routeBasePath, - authorsPageBasePath, + authorsBasePath, ]); const listedBlogPosts = blogPosts.filter(shouldBeListed); @@ -316,7 +316,7 @@ export async function buildAllRoutes({ basePageUrl: author.page.permalink, blogDescription, blogTitle, - pageBasePath: authorsPageBasePath, + pageBasePath: authorsBasePath, postsPerPageOption: postsPerPage, }); diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx index 84d8492471f4..b78c56079f3f 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx @@ -11,7 +11,7 @@ import { HtmlClassNameProvider, ThemeClassNames, } from '@docusaurus/theme-common'; -import {translateBlogAuthorsPageTitle} from '@docusaurus/theme-common/internal'; +import {translateBlogAuthorsListPageTitle} from '@docusaurus/theme-common/internal'; import BlogLayout from '@theme/BlogLayout'; import AuthorsList from '@theme/AuthorsList'; import type {Props} from '@theme/BlogAuthorsListPage'; @@ -22,7 +22,7 @@ export default function BlogAuthorsListPage({ authors, sidebar, }: Props): JSX.Element { - const title: string = translateBlogAuthorsPageTitle(); + const title: string = translateBlogAuthorsListPageTitle(); return ( @@ -53,7 +53,7 @@ function BlogAuthorsPostsPageContent({ sidebar, listMetadata, }: Props): JSX.Element { - const title = useBlogAuthorsPostsPageTitle(author); + const title = useBlogAuthorPageTitle(author); return (
      diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts index 4658e1cfb026..e7568e8bb061 100644 --- a/packages/docusaurus-theme-common/src/internal.ts +++ b/packages/docusaurus-theme-common/src/internal.ts @@ -93,7 +93,7 @@ export {useBackToTopButton} from './hooks/useBackToTopButton'; export { useBlogTagsPostsPageTitle, - useBlogAuthorsPostsPageTitle, - translateBlogAuthorsPageTitle, + useBlogAuthorPageTitle, + translateBlogAuthorsListPageTitle, BlogAuthorsViewAllLabel, } from './translations/blogTranslations'; diff --git a/packages/docusaurus-theme-common/src/translations/blogTranslations.tsx b/packages/docusaurus-theme-common/src/translations/blogTranslations.tsx index e87450670b24..04ac84db1861 100644 --- a/packages/docusaurus-theme-common/src/translations/blogTranslations.tsx +++ b/packages/docusaurus-theme-common/src/translations/blogTranslations.tsx @@ -9,7 +9,7 @@ import React, {type ReactNode} from 'react'; import Translate, {translate} from '@docusaurus/Translate'; import {usePluralForm} from '../utils/usePluralForm'; -// Only used internally +// Only used locally function useBlogPostsPlural(): (count: number) => string { const {selectMessage} = usePluralForm(); return (count: number) => @@ -42,7 +42,7 @@ export function useBlogTagsPostsPageTitle(tag: { ); } -export function useBlogAuthorsPostsPageTitle(author: { +export function useBlogAuthorPageTitle(author: { key: string; name?: string; count: number; @@ -61,9 +61,9 @@ export function useBlogAuthorsPostsPageTitle(author: { ); } -export const translateBlogAuthorsPageTitle = (): string => +export const translateBlogAuthorsListPageTitle = (): string => translate({ - id: 'theme.blog.authors.pageTitle', + id: 'theme.blog.authorsList.pageTitle', message: 'Authors', description: 'The title of the authors page', }); diff --git a/packages/docusaurus-theme-translations/locales/base/theme-common.json b/packages/docusaurus-theme-translations/locales/base/theme-common.json index 8d5dc1856c84..cb6adddfe686 100644 --- a/packages/docusaurus-theme-translations/locales/base/theme-common.json +++ b/packages/docusaurus-theme-translations/locales/base/theme-common.json @@ -41,7 +41,7 @@ "theme.admonition.tip___DESCRIPTION": "The default label used for the Tip admonition (:::tip)", "theme.admonition.warning": "warning", "theme.admonition.warning___DESCRIPTION": "The default label used for the Warning admonition (:::warning)", - "theme.blog.authors.pageTitle": "Authors", + "theme.blog.authorsList.pageTitle": "Authors", "theme.blog.authors.viewAll": "View All Authors", "theme.blog.author.pageTitle": "{authorName} - {nPosts}", "theme.blog.archive.description": "Archive", From 8620ef4df921ecabdad86f0603d701a7f80478d0 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 16:58:05 +0200 Subject: [PATCH 093/115] rename things for consistency --- .../src/theme/BlogAuthorsPostsPage/index.tsx | 4 ++-- packages/docusaurus-theme-common/src/internal.ts | 2 +- .../src/translations/blogTranslations.tsx | 4 ++-- .../locales/base/theme-common.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx index 547df4fe431a..ef0cb7029683 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx @@ -14,7 +14,7 @@ import { } from '@docusaurus/theme-common'; import { useBlogAuthorPageTitle, - BlogAuthorsViewAllLabel, + BlogAuthorsListViewAllLabel, } from '@docusaurus/theme-common/internal'; import Link from '@docusaurus/Link'; import {useBlogMetadata} from '@docusaurus/plugin-content-blog/client'; @@ -42,7 +42,7 @@ function ViewAllAuthorsLink() { const {authorsListPath} = useBlogMetadata(); return ( - + ); } diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts index e7568e8bb061..8c3f5d6b7c72 100644 --- a/packages/docusaurus-theme-common/src/internal.ts +++ b/packages/docusaurus-theme-common/src/internal.ts @@ -95,5 +95,5 @@ export { useBlogTagsPostsPageTitle, useBlogAuthorPageTitle, translateBlogAuthorsListPageTitle, - BlogAuthorsViewAllLabel, + BlogAuthorsListViewAllLabel, } from './translations/blogTranslations'; diff --git a/packages/docusaurus-theme-common/src/translations/blogTranslations.tsx b/packages/docusaurus-theme-common/src/translations/blogTranslations.tsx index 04ac84db1861..0390ee063a10 100644 --- a/packages/docusaurus-theme-common/src/translations/blogTranslations.tsx +++ b/packages/docusaurus-theme-common/src/translations/blogTranslations.tsx @@ -68,10 +68,10 @@ export const translateBlogAuthorsListPageTitle = (): string => description: 'The title of the authors page', }); -export function BlogAuthorsViewAllLabel(): ReactNode { +export function BlogAuthorsListViewAllLabel(): ReactNode { return ( View All Authors diff --git a/packages/docusaurus-theme-translations/locales/base/theme-common.json b/packages/docusaurus-theme-translations/locales/base/theme-common.json index cb6adddfe686..3cc0b8b8b369 100644 --- a/packages/docusaurus-theme-translations/locales/base/theme-common.json +++ b/packages/docusaurus-theme-translations/locales/base/theme-common.json @@ -42,7 +42,7 @@ "theme.admonition.warning": "warning", "theme.admonition.warning___DESCRIPTION": "The default label used for the Warning admonition (:::warning)", "theme.blog.authorsList.pageTitle": "Authors", - "theme.blog.authors.viewAll": "View All Authors", + "theme.blog.authorsList.viewAll": "View All Authors", "theme.blog.author.pageTitle": "{authorName} - {nPosts}", "theme.blog.archive.description": "Archive", "theme.blog.archive.description___DESCRIPTION": "The page & hero description of the blog archive page", From de0119f4ddee310c04a8f1a5a19fda4fc77a438c Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 17:01:20 +0200 Subject: [PATCH 094/115] remove useless typedefs --- .../src/theme-classic.d.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 5d9526c752c2..5d61ce1809f1 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -1613,25 +1613,6 @@ declare module '@theme/AuthorsList' { export default function AuthorsList(props: Props): JSX.Element; } -declare module '@theme/AuthorsListInline' { - import type {Author} from '@docusaurus/plugin-content-blog'; - - export interface Props { - readonly authors: readonly Author[]; - } - export default function AuthorsListInline(props: Props): JSX.Element; -} - -declare module '@theme/Author' { - import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; - - export interface Props { - author: AuthorItemProp; - } - - export default function Author(props: Props): JSX.Element; -} - declare module '@theme/Unlisted' { export interface Props { className?: string; From 9d9a845c43b6f649854fa26896661da230faed18 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 17:45:42 +0200 Subject: [PATCH 095/115] refactor blog theme components organization --- .../src/options.ts | 4 +- .../src/plugin-content-blog.d.ts | 4 +- .../src/routes.ts | 6 ++ .../src/theme-classic.d.ts | 59 ++++++++----------- .../src/theme/AuthorsList/index.tsx | 27 --------- .../Components}/Author/Socials/index.tsx | 4 +- .../Author/Socials/styles.module.css | 0 .../Components}/Author/index.tsx | 8 +-- .../Components}/Author/styles.module.css | 0 .../Pages}/BlogAuthorsListPage/index.tsx | 21 ++++++- .../BlogAuthorsListPage}/styles.module.css | 0 .../Pages}/BlogAuthorsPostsPage/index.tsx | 18 +++--- .../BlogAuthorsPostsPage/styles.module.css | 0 .../BlogPostItem/Header/Authors/index.tsx | 4 +- 14 files changed, 66 insertions(+), 89 deletions(-) delete mode 100644 packages/docusaurus-theme-classic/src/theme/AuthorsList/index.tsx rename packages/docusaurus-theme-classic/src/theme/{BlogPostItem/Header => Blog/Components}/Author/Socials/index.tsx (94%) rename packages/docusaurus-theme-classic/src/theme/{BlogPostItem/Header => Blog/Components}/Author/Socials/styles.module.css (100%) rename packages/docusaurus-theme-classic/src/theme/{BlogPostItem/Header => Blog/Components}/Author/index.tsx (86%) rename packages/docusaurus-theme-classic/src/theme/{BlogPostItem/Header => Blog/Components}/Author/styles.module.css (100%) rename packages/docusaurus-theme-classic/src/theme/{ => Blog/Pages}/BlogAuthorsListPage/index.tsx (69%) rename packages/docusaurus-theme-classic/src/theme/{AuthorsList => Blog/Pages/BlogAuthorsListPage}/styles.module.css (100%) rename packages/docusaurus-theme-classic/src/theme/{ => Blog/Pages}/BlogAuthorsPostsPage/index.tsx (83%) rename packages/docusaurus-theme-classic/src/theme/{ => Blog/Pages}/BlogAuthorsPostsPage/styles.module.css (100%) diff --git a/packages/docusaurus-plugin-content-blog/src/options.ts b/packages/docusaurus-plugin-content-blog/src/options.ts index 536174e3069c..20e0c3427948 100644 --- a/packages/docusaurus-plugin-content-blog/src/options.ts +++ b/packages/docusaurus-plugin-content-blog/src/options.ts @@ -34,8 +34,8 @@ export const DEFAULT_OPTIONS: PluginOptions = { showReadingTime: true, blogTagsPostsComponent: '@theme/BlogTagsPostsPage', blogTagsListComponent: '@theme/BlogTagsListPage', - blogAuthorsPostsComponent: '@theme/BlogAuthorsPostsPage', - blogAuthorsListComponent: '@theme/BlogAuthorsListPage', + blogAuthorsPostsComponent: '@theme/Blog/Pages/BlogAuthorsPostsPage', + blogAuthorsListComponent: '@theme/Blog/Pages/BlogAuthorsListPage', blogPostComponent: '@theme/BlogPostPage', blogListComponent: '@theme/BlogListPage', blogArchiveComponent: '@theme/BlogArchivePage', diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 77cf054708d8..e0e5617ce792 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -716,7 +716,7 @@ declare module '@theme/BlogTagsListPage' { export default function BlogTagsListPage(props: Props): JSX.Element; } -declare module '@theme/BlogAuthorsListPage' { +declare module '@theme/Blog/Pages/BlogAuthorsListPage' { import type { AuthorItemProp, BlogSidebar, @@ -732,7 +732,7 @@ declare module '@theme/BlogAuthorsListPage' { export default function BlogAuthorsListPage(props: Props): JSX.Element; } -declare module '@theme/BlogAuthorsPostsPage' { +declare module '@theme/Blog/Pages/BlogAuthorsPostsPage' { import type {Content} from '@theme/BlogPostPage'; import type { AuthorItemProp, diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index 2ebe104ee19f..8dc520d53e4d 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -301,6 +301,9 @@ export async function buildAllRoutes({ }), ), }, + context: { + blogMetadata: blogMetadataModulePath, + }, }; } @@ -350,6 +353,9 @@ export async function buildAllRoutes({ author: toAuthorItemProp({author, count: authorBlogPosts.length}), listMetadata: metadata, }, + context: { + blogMetadata: blogMetadataModulePath, + }, }; }); } diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 5d61ce1809f1..f9c62c145aee 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -185,6 +185,30 @@ declare module '@theme/BackToTopButton' { export default function BackToTopButton(): JSX.Element; } +declare module '@theme/Blog/Components/Author' { + import type {Author} from '@docusaurus/plugin-content-blog'; + + export interface Props { + readonly author: Author; + readonly singleAuthor: boolean; + readonly className?: string; + readonly count?: number; + } + + export default function BlogAuthor(props: Props): JSX.Element; +} + +declare module '@theme/Blog/Components/Author/Socials' { + import type {Author} from '@docusaurus/plugin-content-blog'; + + export interface Props { + readonly author: Author; + readonly className?: string; + } + + export default function BlogAuthorSocials(props: Props): JSX.Element; +} + declare module '@theme/BlogListPaginator' { import type {BlogPaginatedMetadata} from '@docusaurus/plugin-content-blog'; @@ -291,32 +315,6 @@ declare module '@theme/BlogPostItem/Header/Info' { export default function BlogPostItemHeaderInfo(): JSX.Element; } -declare module '@theme/BlogPostItem/Header/Author' { - import type {Author} from '@docusaurus/plugin-content-blog'; - - export interface Props { - readonly author: Author; - readonly singleAuthor: boolean; - readonly className?: string; - readonly count?: number; - } - - export default function BlogPostItemHeaderAuthor(props: Props): JSX.Element; -} - -declare module '@theme/BlogPostItem/Header/Author/Socials' { - import type {Author} from '@docusaurus/plugin-content-blog'; - - export interface Props { - readonly author: Author; - readonly className?: string; - } - - export default function BlogPostItemHeaderAuthorSocials( - props: Props, - ): JSX.Element; -} - declare module '@theme/BlogPostItem/Header/Authors' { export interface Props { readonly className?: string; @@ -1604,15 +1602,6 @@ declare module '@theme/Tag' { export default function Tag(props: Props): JSX.Element; } -declare module '@theme/AuthorsList' { - import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; - - export interface Props { - readonly authors: readonly AuthorItemProp[]; - } - export default function AuthorsList(props: Props): JSX.Element; -} - declare module '@theme/Unlisted' { export interface Props { className?: string; diff --git a/packages/docusaurus-theme-classic/src/theme/AuthorsList/index.tsx b/packages/docusaurus-theme-classic/src/theme/AuthorsList/index.tsx deleted file mode 100644 index 0a69b26104d1..000000000000 --- a/packages/docusaurus-theme-classic/src/theme/AuthorsList/index.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 React from 'react'; -import clsx from 'clsx'; -import Author from '@theme/BlogPostItem/Header/Author'; -import type {Props} from '@theme/AuthorsList'; -import styles from './styles.module.css'; - -export default function AuthorsList({authors}: Props): JSX.Element { - return ( -
      - {authors.map((author) => ( - - ))} -
      - ); -} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/Socials/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/index.tsx similarity index 94% rename from packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/Socials/index.tsx rename to packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/index.tsx index f7a607ac9895..8552ba7cce26 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/Socials/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/index.tsx @@ -9,7 +9,7 @@ import type {ComponentType} from 'react'; import React from 'react'; import clsx from 'clsx'; import Link from '@docusaurus/Link'; -import type {Props} from '@theme/BlogPostItem/Header/Author/Socials'; +import type {Props} from '@theme/Blog/Components/Author/Socials'; import Twitter from '@theme/Icon/Socials/Twitter'; import GitHub from '@theme/Icon/Socials/GitHub'; @@ -50,7 +50,7 @@ function SocialLink({platform, link}: {platform: string; link: string}) { ); } -export default function AuthorSocials({ +export default function BlogAuthorSocials({ author, }: { author: Props['author']; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/Socials/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/styles.module.css similarity index 100% rename from packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/Socials/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx similarity index 86% rename from packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx rename to packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx index c734a4c7a78f..44f4866ddd80 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx @@ -8,9 +8,8 @@ import React from 'react'; import clsx from 'clsx'; import Link, {type Props as LinkProps} from '@docusaurus/Link'; -import AuthorSocials from '@theme/BlogPostItem/Header/Author/Socials'; - -import type {Props} from '@theme/BlogPostItem/Header/Author'; +import AuthorSocials from '@theme/Blog/Components/Author/Socials'; +import type {Props} from '@theme/Blog/Components/Author'; import styles from './styles.module.css'; function MaybeLink(props: LinkProps): JSX.Element { @@ -28,8 +27,7 @@ function AuthorTitle({title}: {title: string}) { ); } -export default function BlogPostItemHeaderAuthor({ - // singleAuthor, // may be useful in the future, or for swizzle users +export default function BlogAuthor({ author, className, count, diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/styles.module.css similarity index 100% rename from packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/index.tsx similarity index 69% rename from packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx rename to packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/index.tsx index b78c56079f3f..36669b42176a 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/index.tsx @@ -13,10 +13,26 @@ import { } from '@docusaurus/theme-common'; import {translateBlogAuthorsListPageTitle} from '@docusaurus/theme-common/internal'; import BlogLayout from '@theme/BlogLayout'; -import AuthorsList from '@theme/AuthorsList'; -import type {Props} from '@theme/BlogAuthorsListPage'; +import type {Props} from '@theme/Blog/Pages/BlogAuthorsListPage'; import SearchMetadata from '@theme/SearchMetadata'; import Heading from '@theme/Heading'; +import Author from '@theme/Blog/Components/Author'; +import styles from './styles.module.css'; + +function AuthorsList({authors}: {authors: Props['authors']}): JSX.Element { + return ( +
      + {authors.map((author) => ( + + ))} +
      + ); +} export default function BlogAuthorsListPage({ authors, @@ -30,7 +46,6 @@ export default function BlogAuthorsListPage({ ThemeClassNames.page.blogAuthorsListPage, )}> - {title} diff --git a/packages/docusaurus-theme-classic/src/theme/AuthorsList/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/styles.module.css similarity index 100% rename from packages/docusaurus-theme-classic/src/theme/AuthorsList/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/index.tsx similarity index 83% rename from packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx rename to packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/index.tsx index ef0cb7029683..e50c22d3d79f 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/index.tsx @@ -21,14 +21,14 @@ import {useBlogMetadata} from '@docusaurus/plugin-content-blog/client'; import BlogLayout from '@theme/BlogLayout'; import BlogListPaginator from '@theme/BlogListPaginator'; import SearchMetadata from '@theme/SearchMetadata'; -import type {Props} from '@theme/BlogAuthorsPostsPage'; +import type {Props} from '@theme/Blog/Pages/BlogAuthorsPostsPage'; import BlogPostItems from '@theme/BlogPostItems'; import Heading from '@theme/Heading'; -import Author from '@theme/BlogPostItem/Header/Author'; +import Author from '@theme/Blog/Components/Author'; import styles from './styles.module.css'; -function BlogAuthorsPostsPageMetadata({author}: Props): JSX.Element { +function Metadata({author}: Props): JSX.Element { const title = useBlogAuthorPageTitle(author); return ( <> @@ -47,12 +47,7 @@ function ViewAllAuthorsLink() { ); } -function BlogAuthorsPostsPageContent({ - author, - items, - sidebar, - listMetadata, -}: Props): JSX.Element { +function Content({author, items, sidebar, listMetadata}: Props): JSX.Element { const title = useBlogAuthorPageTitle(author); return ( @@ -71,6 +66,7 @@ function BlogAuthorsPostsPageContent({ ); } + export default function BlogAuthorsPostsPage(props: Props): JSX.Element { return ( - - + + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/styles.module.css similarity index 100% rename from packages/docusaurus-theme-classic/src/theme/BlogAuthorsPostsPage/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Authors/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Authors/index.tsx index 7ee6a472b22b..32555c649e44 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Authors/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Authors/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import clsx from 'clsx'; import {useBlogPost} from '@docusaurus/plugin-content-blog/client'; -import BlogPostItemHeaderAuthor from '@theme/BlogPostItem/Header/Author'; +import BlogAuthor from '@theme/Blog/Components/Author'; import type {Props} from '@theme/BlogPostItem/Header/Authors'; import styles from './styles.module.css'; @@ -40,7 +40,7 @@ export default function BlogPostItemHeaderAuthors({ imageOnly ? styles.imageOnlyAuthorCol : styles.authorCol, )} key={idx}> - Date: Fri, 26 Jul 2024 17:54:47 +0200 Subject: [PATCH 096/115] paginateBlogPosts should generate first page even when there's 0 items --- .../__snapshots__/blogUtils.test.ts.snap | 19 +++++++++++++++++++ .../src/__tests__/blogUtils.test.ts | 18 ++++++++++++++++++ .../src/blogUtils.ts | 2 +- .../src/routes.ts | 18 ------------------ 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/blogUtils.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/blogUtils.test.ts.snap index 038e71ca8f8e..12f6076df5ad 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/blogUtils.test.ts.snap +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/blogUtils.test.ts.snap @@ -25,6 +25,25 @@ exports[`paginateBlogPosts generates a single page 1`] = ` ] `; +exports[`paginateBlogPosts generates pages - 0 blog post 1`] = ` +[ + { + "items": [], + "metadata": { + "blogDescription": "Blog Description", + "blogTitle": "Blog Title", + "nextPage": undefined, + "page": 1, + "permalink": "/blog", + "postsPerPage": 2, + "previousPage": undefined, + "totalCount": 0, + "totalPages": 1, + }, + }, +] +`; + exports[`paginateBlogPosts generates pages 1`] = ` [ { diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/blogUtils.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/blogUtils.test.ts index 5b45f13a0823..a340ec61e79e 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/blogUtils.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/blogUtils.test.ts @@ -54,6 +54,24 @@ describe('paginateBlogPosts', () => { ).toMatchSnapshot(); }); + it('generates pages - 0 blog post', () => { + const pages = paginateBlogPosts({ + blogPosts: [], + basePageUrl: '/blog', + blogTitle: 'Blog Title', + blogDescription: 'Blog Description', + postsPerPageOption: 2, + pageBasePath: 'page', + }); + // As part ot https://github.com/facebook/docusaurus/pull/10216 + // it was decided that authors with "page: true" that haven't written any + // blog posts yet should still have a dedicated author page + // For this purpose, we generate an empty first page + expect(pages).toHaveLength(1); + expect(pages[0]!.items).toHaveLength(0); + expect(pages).toMatchSnapshot(); + }); + it('generates pages at blog root', () => { expect( paginateBlogPosts({ diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 5d4a92120e11..d26d319c0f1b 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -65,7 +65,7 @@ export function paginateBlogPosts({ const totalCount = blogPosts.length; const postsPerPage = postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption; - const numberOfPages = Math.ceil(totalCount / postsPerPage); + const numberOfPages = Math.max(1, Math.ceil(totalCount / postsPerPage)); const pages: BlogPaginated[] = []; diff --git a/packages/docusaurus-plugin-content-blog/src/routes.ts b/packages/docusaurus-plugin-content-blog/src/routes.ts index 8dc520d53e4d..ced92dc0fda9 100644 --- a/packages/docusaurus-plugin-content-blog/src/routes.ts +++ b/packages/docusaurus-plugin-content-blog/src/routes.ts @@ -313,7 +313,6 @@ export async function buildAllRoutes({ return []; } - // TODO add tests const pages = paginateBlogPosts({ blogPosts: authorBlogPosts, basePageUrl: author.page.permalink, @@ -323,23 +322,6 @@ export async function buildAllRoutes({ postsPerPageOption: postsPerPage, }); - if (pages.length === 0) { - pages.push({ - items: [], - metadata: { - permalink: author.page.permalink, - page: 0, - postsPerPage: 5, - totalPages: 0, - totalCount: 0, - previousPage: undefined, - nextPage: undefined, - blogDescription, - blogTitle, - }, - }); - } - return pages.map(({metadata, items}) => { return { path: metadata.permalink, From 2b26fbd35a7b6dcd3992981cd096d3df9166d166 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 17:55:48 +0200 Subject: [PATCH 097/115] Dogfood custom author page permalink --- website/_dogfooding/_blog tests/authors.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/_dogfooding/_blog tests/authors.yml b/website/_dogfooding/_blog tests/authors.yml index 90da02321ef6..040b9899ee19 100644 --- a/website/_dogfooding/_blog tests/authors.yml +++ b/website/_dogfooding/_blog tests/authors.yml @@ -8,4 +8,4 @@ slorber: ozaki: name: ozaki - page: true + page: {permalink: '/custom/ozaki/permalink'} From 242514473423325ed9f040d41bc1d33c94178e7e Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 18:09:44 +0200 Subject: [PATCH 098/115] remove unused css --- .../theme/Blog/Pages/BlogAuthorsListPage/styles.module.css | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/styles.module.css index e091dc082cf9..672585a4f3ce 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/styles.module.css @@ -5,11 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -.author { - display: inline-block; - margin: 0.5rem 0.5rem 0 1rem; -} - section { display: grid; grid-template-columns: repeat(2, 1fr); From ad929c0b24654ab43ed4590e2c0afee61f84eec8 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 18:12:22 +0200 Subject: [PATCH 099/115] rename ThemeClassNames for consistency --- .../src/theme/Blog/Pages/BlogAuthorsPostsPage/index.tsx | 2 +- packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/index.tsx index e50c22d3d79f..b8f4dcec2431 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/index.tsx @@ -72,7 +72,7 @@ export default function BlogAuthorsPostsPage(props: Props): JSX.Element { diff --git a/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts b/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts index 9dba038c5a5c..413a2492fa4a 100644 --- a/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts +++ b/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts @@ -19,7 +19,7 @@ export const ThemeClassNames = { blogTagsListPage: 'blog-tags-list-page', blogTagPostListPage: 'blog-tags-post-list-page', blogAuthorsListPage: 'blog-authors-list-page', - blogAuthorPostListPage: 'blog-authors-post-list-page', + blogAuthorsPostsPage: 'blog-authors-posts-page', docsDocPage: 'docs-doc-page', docsTagsListPage: 'docs-tags-list-page', From 12b3956ec65f4dc57f364d7b6ca955455cea610f Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 18:47:59 +0200 Subject: [PATCH 100/115] move count outside link --- .../src/theme/Blog/Components/Author/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx index 44f4866ddd80..7bf1304a2c73 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx @@ -51,8 +51,8 @@ export default function BlogAuthor({
      {name} - {count && {count}} + {count && {count}}
      {!!title && } {hasSocials && } From 0ebe2712e6be04de703ad58de85403a3b8375cbe Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 26 Jul 2024 19:13:23 +0200 Subject: [PATCH 101/115] polish display code --- .../src/theme-classic.d.ts | 2 +- .../theme/Blog/Components/Author/index.tsx | 35 ++++++++++++++--- .../Blog/Components/Author/styles.module.css | 38 +++++++++++++++++++ .../Blog/Pages/BlogAuthorsListPage/index.tsx | 30 +++++++++------ .../BlogAuthorsListPage/styles.module.css | 6 +-- .../Blog/Pages/BlogAuthorsPostsPage/index.tsx | 12 +----- .../BlogAuthorsPostsPage/styles.module.css | 33 ---------------- .../BlogPostItem/Header/Authors/index.tsx | 1 - 8 files changed, 92 insertions(+), 65 deletions(-) delete mode 100644 packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index f9c62c145aee..c35e7615af26 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -189,8 +189,8 @@ declare module '@theme/Blog/Components/Author' { import type {Author} from '@docusaurus/plugin-content-blog'; export interface Props { + readonly as?: 'h1' | 'h2'; readonly author: Author; - readonly singleAuthor: boolean; readonly className?: string; readonly count?: number; } diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx index 7bf1304a2c73..ecd020cddccd 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx @@ -10,6 +10,7 @@ import clsx from 'clsx'; import Link, {type Props as LinkProps} from '@docusaurus/Link'; import AuthorSocials from '@theme/Blog/Components/Author/Socials'; import type {Props} from '@theme/Blog/Components/Author'; +import Heading from '@theme/Heading'; import styles from './styles.module.css'; function MaybeLink(props: LinkProps): JSX.Element { @@ -27,7 +28,20 @@ function AuthorTitle({title}: {title: string}) { ); } +function AuthorName({name, as}: {name: string; as: Props['as']}) { + if (!as) { + return {name}; + } else { + return ( + + {name} + + ); + } +} + export default function BlogAuthor({ + as, author, className, count, @@ -39,19 +53,30 @@ export default function BlogAuthor({ const hasSocials = socials && Object.keys(socials).length > 0; return ( -
      +
      {imageURL && ( - {name} + {name} )} {(name || title) && (
      - - {name} - + {name && ( + + + + )} {count && {count}}
      {!!title && } diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/styles.module.css index 4629d264a19c..cd74824a2ab5 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/styles.module.css @@ -5,8 +5,32 @@ * LICENSE file in the root directory of this source tree. */ +.avatarImage { + --ifm-avatar-photo-size: 3.2rem; +} + +.author-as-h1 .avatarImage { + --ifm-avatar-photo-size: 6rem; +} + +.author-as-h2 .avatarImage { + --ifm-avatar-photo-size: 5.2rem; +} + .authorName { font-size: 1.1rem; + display: flex; + flex-direction: row; +} + +.author-as-h1 .authorName { + font-size: 2.4rem; + display: inline; +} + +.author-as-h2 .authorName { + font-size: 1.4rem; + display: inline; } .authorTitle { @@ -20,6 +44,20 @@ -webkit-box-orient: vertical; } +.author-as-h1 .authorTitle { + margin-top: 0.4rem; + margin-bottom: 0.2rem; + font-size: 1.2rem; + line-height: 1.2rem; +} + +.author-as-h2 .authorTitle { + margin-top: 0.3rem; + margin-bottom: 0.2rem; + font-size: 1rem; + line-height: 1rem; +} + .count { background: var(--ifm-color-secondary); color: var(--ifm-color-black); diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/index.tsx index 36669b42176a..71d60a0402a1 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsListPage/index.tsx @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, {type ReactNode} from 'react'; import clsx from 'clsx'; import { PageMetadata, @@ -17,19 +17,25 @@ import type {Props} from '@theme/Blog/Pages/BlogAuthorsListPage'; import SearchMetadata from '@theme/SearchMetadata'; import Heading from '@theme/Heading'; import Author from '@theme/Blog/Components/Author'; +import type {AuthorItemProp} from '@docusaurus/plugin-content-blog'; import styles from './styles.module.css'; -function AuthorsList({authors}: {authors: Props['authors']}): JSX.Element { +function AuthorListItem({author}: {author: AuthorItemProp}) { return ( -
      - {authors.map((author) => ( - - ))} +
    • + +
    • + ); +} + +function AuthorsList({authors}: {authors: Props['authors']}) { + return ( +
      +
        + {authors.map((author) => ( + + ))} +
      ); } @@ -37,7 +43,7 @@ function AuthorsList({authors}: {authors: Props['authors']}): JSX.Element { export default function BlogAuthorsListPage({ authors, sidebar, -}: Props): JSX.Element { +}: Props): ReactNode { const title: string = translateBlogAuthorsListPageTitle(); return (
      - {title} - + {author.description &&

      {author.description}

      }
      +
      diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/styles.module.css deleted file mode 100644 index 25e4e5c34494..000000000000 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Pages/BlogAuthorsPostsPage/styles.module.css +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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. - */ - -.postsPageAuthor > a > img { - width: calc(3rem * 2); - height: calc(3rem * 2); -} - -/* .postsPageAuthor>div { - justify-content: space-between; -} */ - -.postsPageAuthor span { - font-size: 1.75rem; - line-height: normal; -} - -.postsPageAuthor small { - font-size: 1.25rem; - line-height: normal; - margin-top: 0; -} - -.postsPageAuthor div > a, -.postsPageAuthor div > a > svg { - height: 1.25rem; - width: 1.25rem; - margin-top: 0; -} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Authors/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Authors/index.tsx index 32555c649e44..fc7b313dd278 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Authors/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Authors/index.tsx @@ -41,7 +41,6 @@ export default function BlogPostItemHeaderAuthors({ )} key={idx}> Date: Mon, 29 Jul 2024 10:15:21 +0200 Subject: [PATCH 102/115] swizzle config --- .../src/getSwizzleConfig.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/docusaurus-theme-classic/src/getSwizzleConfig.ts b/packages/docusaurus-theme-classic/src/getSwizzleConfig.ts index 1b0a871197e2..60363ec4849e 100644 --- a/packages/docusaurus-theme-classic/src/getSwizzleConfig.ts +++ b/packages/docusaurus-theme-classic/src/getSwizzleConfig.ts @@ -127,6 +127,27 @@ export default function getSwizzleConfig(): SwizzleConfig { description: 'The object mapping admonition type to a React component.\nUse it to add custom admonition type components, or replace existing ones.\nCan be ejected or wrapped (only manually, see our documentation).', }, + Blog: { + actions: { + // Forbidden because it's a parent folder, makes the CLI crash atm + eject: 'forbidden', + wrap: 'forbidden', + }, + }, + 'Blog/Components': { + actions: { + // Forbidden because it's a parent folder, makes the CLI crash atm + eject: 'forbidden', + wrap: 'forbidden', + }, + }, + 'Blog/Pages': { + actions: { + // Forbidden because it's a parent folder, makes the CLI crash atm + eject: 'forbidden', + wrap: 'forbidden', + }, + }, CodeBlock: { actions: { eject: 'safe', From ea494ca493f33eb77364b01c12c4ce571a99973b Mon Sep 17 00:00:00 2001 From: sebastien Date: Mon, 29 Jul 2024 10:49:05 +0200 Subject: [PATCH 103/115] fix changelog plugin --- .../changelog/theme/ChangelogItem/Header/Author/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/plugins/changelog/theme/ChangelogItem/Header/Author/index.tsx b/website/src/plugins/changelog/theme/ChangelogItem/Header/Author/index.tsx index 185505530d24..b37b0f5bf9e3 100644 --- a/website/src/plugins/changelog/theme/ChangelogItem/Header/Author/index.tsx +++ b/website/src/plugins/changelog/theme/ChangelogItem/Header/Author/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import clsx from 'clsx'; import Link from '@docusaurus/Link'; -import type {Props} from '@theme/BlogPostItem/Header/Author'; +import type {Props} from '@theme/Blog/Components/Author'; import styles from './styles.module.css'; From 3cd4f9f123d6c89502408a356826d72ae01cdc7b Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:39:27 +0200 Subject: [PATCH 104/115] feat: Add authors page configuration option to the blog plugin --- website/docs/api/plugins/plugin-content-blog.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/api/plugins/plugin-content-blog.mdx b/website/docs/api/plugins/plugin-content-blog.mdx index 2eb10ccfb42c..4a9e143f861a 100644 --- a/website/docs/api/plugins/plugin-content-blog.mdx +++ b/website/docs/api/plugins/plugin-content-blog.mdx @@ -50,6 +50,7 @@ Accepted fields: | `tagsBasePath` | `string` | `'tags'` | URL route for the tags section of your blog. Will be appended to `routeBasePath`. | | `pageBasePath` | `string` | `'page'` | URL route for the pages section of your blog. Will be appended to `routeBasePath`. | | `archiveBasePath` | string \| null | `'archive'` | URL route for the archive section of your blog. Will be appended to `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to disable generation of archive. | +| `authorsPagePath` | `string` | `'authors'` | URL route for the authors page section of your blog. Will be appended to `path`. | | `include` | `string[]` | `['**/*.{md,mdx}']` | Array of glob patterns matching Markdown files to be built, relative to the content path. | | `exclude` | `string[]` | _See example configuration_ | Array of glob patterns matching Markdown files to be excluded. Serves as refinement based on the `include` option. | | `postsPerPage` | number \| 'ALL' | `10` | Number of posts to show per page in the listing page. Use `'ALL'` to display all posts on one listing page. | From 63f8785a28c96eaf8e7f05cb24736a7011db50fd Mon Sep 17 00:00:00 2001 From: ozakione <29860391+OzakIOne@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:39:59 +0200 Subject: [PATCH 105/115] docs: clarify explanation --- website/docs/blog.mdx | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/website/docs/blog.mdx b/website/docs/blog.mdx index e59eee433581..27555c622d6b 100644 --- a/website/docs/blog.mdx +++ b/website/docs/blog.mdx @@ -403,39 +403,37 @@ An author, either declared through front matter or through the authors map, need ### Authors page {#authors-page} -Docusaurus generate an author's page listing all authors with their respective blog posts the author have `page: true`. +Docusaurus generate an author's page listing all authors with their respective blog posts if the author have `page: true`. -By default the feature is disabled and will not generate the authors page. +By default the permalink of the author is the author's key, this ensure unique permalink between authors. You can customize the permalink for the author, see example below. + +Blog posts must reference the author's key to be displayed on the author's page. Inline authors are not supported on the author's page. ```yml title="website/blog/authors.yml" -slorber: +slorber: # author key name: Sébastien Lorber title: Docusaurus maintainer url: https://sebastienlorber.com image_url: https://github.com/slorber.png - page: true # Enable the authors page for this author + page: true # Enable the authors page for this author (route will be /authors/slorber) socials: x: sebastienlorber github: slorber -``` - -You can configure the route base path for the authors page using the `authorsPagePath` option in the plugin configuration. -```js title="docusaurus.config.js" -export default { - presets: [ - [ - '@docusaurus/preset-classic', - { - blog: { - authorsPagePath: '/authors', - }, - }, - ], - ], -}; +jmarcey: + name: Joel Marcey + title: Co-creator of Docusaurus 1 + url: https://github.com/JoelMarcey + image_url: https://github.com/JoelMarcey.png + page: + permalink: '/joel marcey' + socials: + x: joelmarcey + github: JoelMarcey ``` +You can configure the route path for the authors page using the [`authorsPagePath`](api/plugins/plugin-content-blog.mdx#authorsPagePath). + ## Blog post tags {#blog-post-tags} Tags are declared in the front matter and introduce another dimension of categorization. From a23f8ecef135a03842a32d0caf7262e3f08b75c8 Mon Sep 17 00:00:00 2001 From: OzakIOne Date: Mon, 29 Jul 2024 11:44:53 +0000 Subject: [PATCH 106/115] refactor: apply lint autofix --- project-words.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/project-words.txt b/project-words.txt index b9db52dfb400..8f0dc5ec794b 100644 --- a/project-words.txt +++ b/project-words.txt @@ -172,6 +172,7 @@ Lorber's lqip LQIP lunrjs +marcey Marcey Marcey's markprompt From de556c9984481d56052e66ad6338832ac2bf05eb Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 1 Aug 2024 15:26:37 +0200 Subject: [PATCH 107/115] docs, rename authorsPagePath to authorsBasePath --- website/docs/api/plugins/plugin-content-blog.mdx | 2 +- website/docs/blog.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/api/plugins/plugin-content-blog.mdx b/website/docs/api/plugins/plugin-content-blog.mdx index 4a9e143f861a..4b9a5a0140a4 100644 --- a/website/docs/api/plugins/plugin-content-blog.mdx +++ b/website/docs/api/plugins/plugin-content-blog.mdx @@ -50,7 +50,7 @@ Accepted fields: | `tagsBasePath` | `string` | `'tags'` | URL route for the tags section of your blog. Will be appended to `routeBasePath`. | | `pageBasePath` | `string` | `'page'` | URL route for the pages section of your blog. Will be appended to `routeBasePath`. | | `archiveBasePath` | string \| null | `'archive'` | URL route for the archive section of your blog. Will be appended to `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to disable generation of archive. | -| `authorsPagePath` | `string` | `'authors'` | URL route for the authors page section of your blog. Will be appended to `path`. | +| `authorsBasePath` | `string` | `'authors'` | URL route for the authors pages of your blog. Will be appended to `path`. | | `include` | `string[]` | `['**/*.{md,mdx}']` | Array of glob patterns matching Markdown files to be built, relative to the content path. | | `exclude` | `string[]` | _See example configuration_ | Array of glob patterns matching Markdown files to be excluded. Serves as refinement based on the `include` option. | | `postsPerPage` | number \| 'ALL' | `10` | Number of posts to show per page in the listing page. Use `'ALL'` to display all posts on one listing page. | diff --git a/website/docs/blog.mdx b/website/docs/blog.mdx index 27555c622d6b..932b49278125 100644 --- a/website/docs/blog.mdx +++ b/website/docs/blog.mdx @@ -432,7 +432,7 @@ jmarcey: github: JoelMarcey ``` -You can configure the route path for the authors page using the [`authorsPagePath`](api/plugins/plugin-content-blog.mdx#authorsPagePath). +You can configure the route base path for the authors page using the [`authorsBasePath`](api/plugins/plugin-content-blog.mdx#authorsBasePath). ## Blog post tags {#blog-post-tags} From 75dd81fc3d1880b64245c8a34cdd4a7f92aecbee Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 1 Aug 2024 15:33:54 +0200 Subject: [PATCH 108/115] useful comment on BlopAuthor --- .../src/theme/Blog/Components/Author/index.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx index ecd020cddccd..9783775b1545 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx @@ -40,6 +40,10 @@ function AuthorName({name, as}: {name: string; as: Props['as']}) { } } +// Note: in the future we might want to have multiple "BlogAuthor" components +// Creating different display modes with the "as" prop may not be the best idea +// Explainer: https://kyleshevlin.com/prefer-multiple-compositions/ +// For now, we almost use the same design for all cases, so it's good enough export default function BlogAuthor({ as, author, From b3196f53c74052942e0654f56b56914a5922a866 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 1 Aug 2024 16:01:11 +0200 Subject: [PATCH 109/115] simplify authors pages doc --- website/docs/blog.mdx | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/website/docs/blog.mdx b/website/docs/blog.mdx index 932b49278125..2cd9a4eb6cd8 100644 --- a/website/docs/blog.mdx +++ b/website/docs/blog.mdx @@ -401,38 +401,38 @@ An author, either declared through front matter or through the authors map, need ::: -### Authors page {#authors-page} +### Authors pages {#authors-pages} -Docusaurus generate an author's page listing all authors with their respective blog posts if the author have `page: true`. +The authors pages feature is optional, and mainly useful for multi-author blogs. -By default the permalink of the author is the author's key, this ensure unique permalink between authors. You can customize the permalink for the author, see example below. - -Blog posts must reference the author's key to be displayed on the author's page. Inline authors are not supported on the author's page. +You can activate it independently for each author by adding `page: true` to the [global author configuration](#global-authors). ```yml title="website/blog/authors.yml" -slorber: # author key +slorber: name: Sébastien Lorber - title: Docusaurus maintainer - url: https://sebastienlorber.com - image_url: https://github.com/slorber.png - page: true # Enable the authors page for this author (route will be /authors/slorber) - socials: - x: sebastienlorber - github: slorber + // highlight-start + page: true # Turns the feature on - route will be /authors/slorber + // highlight-end jmarcey: name: Joel Marcey - title: Co-creator of Docusaurus 1 - url: https://github.com/JoelMarcey - image_url: https://github.com/JoelMarcey.png + // highlight-start page: - permalink: '/joel marcey' - socials: - x: joelmarcey - github: JoelMarcey + # Turns the feature on - route will be /authors/custom-author-url + permalink: '/custom-author-url' + // highlight-end ``` -You can configure the route base path for the authors page using the [`authorsBasePath`](api/plugins/plugin-content-blog.mdx#authorsBasePath). +The blog plugin will now generate for each of these authors: + +- a dedicated author page, listing all the blog posts they contributed to ([example](/blog/authors/slorber) +- an index page listing all the authors ([example](/blog/authors)) + +:::warning About inline authors + +Only [global authors](#global-authors) can opt in for this feature. [Inline authors](#inline-authors) are not supported. + +::: ## Blog post tags {#blog-post-tags} From 25546d4604072da22fd95d6fbe7d26003ffb4df4 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 1 Aug 2024 16:11:04 +0200 Subject: [PATCH 110/115] simplify authors pages doc --- website/docs/blog.mdx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/website/docs/blog.mdx b/website/docs/blog.mdx index 2cd9a4eb6cd8..e67781b2412b 100644 --- a/website/docs/blog.mdx +++ b/website/docs/blog.mdx @@ -405,7 +405,7 @@ An author, either declared through front matter or through the authors map, need The authors pages feature is optional, and mainly useful for multi-author blogs. -You can activate it independently for each author by adding `page: true` to the [global author configuration](#global-authors). +You can activate it independently for each author by adding `page: true` or `page: {permalink: string}` to the [global author configuration](#global-authors). ```yml title="website/blog/authors.yml" slorber: @@ -423,14 +423,14 @@ jmarcey: // highlight-end ``` -The blog plugin will now generate for each of these authors: +The blog plugin will now generate: -- a dedicated author page, listing all the blog posts they contributed to ([example](/blog/authors/slorber) -- an index page listing all the authors ([example](/blog/authors)) +- a dedicated author page for each author ([example](/blog/authors/slorber)) listing all the blog posts they contributed to +- an authors index page ([example](/blog/authors)) listing all these authors, in the order they appear in `authors.yml` :::warning About inline authors -Only [global authors](#global-authors) can opt in for this feature. [Inline authors](#inline-authors) are not supported. +Only [global authors](#global-authors) can activate this feature. [Inline authors](#inline-authors) are not supported. ::: From 35e704d2a7c5e0bbc77e36a9b0ac42a2ff554c6c Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 1 Aug 2024 16:16:44 +0200 Subject: [PATCH 111/115] simplify authors pages doc --- website/docs/blog.mdx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/website/docs/blog.mdx b/website/docs/blog.mdx index e67781b2412b..d93e250016c2 100644 --- a/website/docs/blog.mdx +++ b/website/docs/blog.mdx @@ -405,7 +405,10 @@ An author, either declared through front matter or through the authors map, need The authors pages feature is optional, and mainly useful for multi-author blogs. -You can activate it independently for each author by adding `page: true` or `page: {permalink: string}` to the [global author configuration](#global-authors). +You can activate it independently for each author by adding a `page` attribute to the [global author configuration](#global-authors): + +- `page: true` +- `page: {permalink: string}` ```yml title="website/blog/authors.yml" slorber: From e19765d675e11e2c0d10ebdd9655620ae86e8ac1 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 1 Aug 2024 16:17:19 +0200 Subject: [PATCH 112/115] simplify authors pages doc --- website/docs/api/plugins/plugin-content-blog.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/api/plugins/plugin-content-blog.mdx b/website/docs/api/plugins/plugin-content-blog.mdx index 4b9a5a0140a4..3f3a77b40be2 100644 --- a/website/docs/api/plugins/plugin-content-blog.mdx +++ b/website/docs/api/plugins/plugin-content-blog.mdx @@ -59,6 +59,8 @@ Accepted fields: | `blogTagsListComponent` | `string` | `'@theme/BlogTagsListPage'` | Root component of the tags list page. | | `blogTagsPostsComponent` | `string` | `'@theme/BlogTagsPostsPage'` | Root component of the "posts containing tag" page. | | `blogArchiveComponent` | `string` | `'@theme/BlogArchivePage'` | Root component of the blog archive page. | +| `blogAuthorsPostsComponent` | `string` | `'@theme/Blog/Pages/BlogAuthorsPostsPage'` | Root component of the blog author page. | +| `blogAuthorsListComponent` | `string` | `'@theme/Blog/Pages/BlogAuthorsListPage'` | Root component of the blog authors page index. | | `remarkPlugins` | `any[]` | `[]` | Remark plugins passed to MDX. | | `rehypePlugins` | `any[]` | `[]` | Rehype plugins passed to MDX. | | `rehypePlugins` | `any[]` | `[]` | Recma plugins passed to MDX. | From c632270e6c2aa5b97561feef89e570586e30a536 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 1 Aug 2024 16:17:27 +0200 Subject: [PATCH 113/115] Add missing plugin options --- website/docs/blog.mdx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/website/docs/blog.mdx b/website/docs/blog.mdx index d93e250016c2..e6cf18644e98 100644 --- a/website/docs/blog.mdx +++ b/website/docs/blog.mdx @@ -405,10 +405,7 @@ An author, either declared through front matter or through the authors map, need The authors pages feature is optional, and mainly useful for multi-author blogs. -You can activate it independently for each author by adding a `page` attribute to the [global author configuration](#global-authors): - -- `page: true` -- `page: {permalink: string}` +You can activate it independently for each author by adding a `page: true` attribute to the [global author configuration](#global-authors): ```yml title="website/blog/authors.yml" slorber: From 8b97d275bc04103e542e86af3f3ab3f6740cd823 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 1 Aug 2024 16:38:11 +0200 Subject: [PATCH 114/115] Add Authors File api ref --- .../docs/api/plugins/plugin-content-blog.mdx | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/website/docs/api/plugins/plugin-content-blog.mdx b/website/docs/api/plugins/plugin-content-blog.mdx index 3f3a77b40be2..3ad31c590296 100644 --- a/website/docs/api/plugins/plugin-content-blog.mdx +++ b/website/docs/api/plugins/plugin-content-blog.mdx @@ -301,6 +301,72 @@ import TagsFileApiRefSection from './_partial-tags-file-api-ref-section.mdx'; +## Authors File {#authors-file} + +Use the [`authors` plugin option](#authors) to configure the path of a YAML authors file. + +By convention, the plugin will look for a `authors.yml` file at the root of your blog content folder(s). + +This file can contain a list of predefined [global blog authors](../../blog.mdx#global-authors). You can reference these authors by their keys in Markdown files thanks to the [`authors` front matter](#markdown-front-matter). + +### Types {#authors-file-types} + +The YAML content of the provided authors file should respect the following shape: + +```tsx +type AuthorsMapInput = { + [authorKey: string]: AuthorInput; +}; + +type AuthorInput = { + name?: string; + title?: string; + description?: string; + imageURL?: string; + url?: string; + email?: string; + page?: boolean | {permalink: string}; + socials?: Record; + [customAuthorAttribute: string]: unknown; +}; +``` + +### Example {#authors-file-example} + +```yml title="tags.yml" +slorber: + name: Sébastien Lorber + title: Docusaurus maintainer + url: https://sebastienlorber.com + image_url: https://github.com/slorber.png + page: true + socials: + x: sebastienlorber + github: slorber + +jmarcey: + name: Joel Marcey + title: Co-creator of Docusaurus 1 + url: https://github.com/JoelMarcey + image_url: https://github.com/JoelMarcey.png + email: jimarcey@gmail.com + page: + permalink: '/joel-marcey' + socials: + x: joelmarcey + github: JoelMarcey +``` + +```md title="blog/my-blog-post.md" +--- +authors: [slorber, jmarcey] +--- + +# My Blog Post + +Content +``` + ## i18n {#i18n} Read the [i18n introduction](../../i18n/i18n-introduction.mdx) first. From 0b8b103fdc73f2213d071b35b414f9327fa0998a Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 1 Aug 2024 17:17:25 +0200 Subject: [PATCH 115/115] Polish CSS of blog authors pages --- .../Blog/Components/Author/Socials/index.tsx | 3 +- .../Author/Socials/styles.module.css | 9 ++++-- .../theme/Blog/Components/Author/index.tsx | 21 +++++++++----- .../Blog/Components/Author/styles.module.css | 29 +++++++++++-------- 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/index.tsx index 8552ba7cce26..59bdca629b16 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/index.tsx @@ -55,9 +55,10 @@ export default function BlogAuthorSocials({ }: { author: Props['author']; }): JSX.Element { + const entries = Object.entries(author.socials ?? {}); return (
      - {Object.entries(author.socials ?? {}).map(([platform, linkUrl]) => { + {entries.map(([platform, linkUrl]) => { return ; })}
      diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/styles.module.css index 1fca8b7e385e..7c1ffc07365a 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/styles.module.css @@ -10,7 +10,12 @@ } .authorSocials { - margin-top: 0.2rem; + /* + This ensures that container takes height even if there's no social link + This keeps author names aligned even if only some have socials + */ + height: var(--docusaurus-blog-social-icon-size); + display: flex; flex-wrap: wrap; align-items: center; @@ -25,7 +30,7 @@ height: var(--docusaurus-blog-social-icon-size); width: var(--docusaurus-blog-social-icon-size); line-height: 0; - margin-right: 0.3rem; + margin-right: 0.4rem; } .authorSocialIcon { diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx index 9783775b1545..5861b3da8091 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/index.tsx @@ -40,6 +40,10 @@ function AuthorName({name, as}: {name: string; as: Props['as']}) { } } +function AuthorBlogPostCount({count}: {count: number}) { + return {count}; +} + // Note: in the future we might want to have multiple "BlogAuthor" components // Creating different display modes with the "as" prop may not be the best idea // Explainer: https://kyleshevlin.com/prefer-multiple-compositions/ @@ -50,12 +54,10 @@ export default function BlogAuthor({ className, count, }: Props): JSX.Element { - const {name, title, url, socials, imageURL, email, page} = author; + const {name, title, url, imageURL, email, page} = author; const link = page?.permalink || url || (email && `mailto:${email}`) || undefined; - const hasSocials = socials && Object.keys(socials).length > 0; - return (
      {name} @@ -74,17 +76,22 @@ export default function BlogAuthor({ )} {(name || title) && ( -
      +
      {name && ( )} - {count && {count}} + {count && }
      {!!title && } - {hasSocials && } + + {/* + We always render AuthorSocials even if there's none + This keeps other things aligned with flexbox layout + */} +
      )}
      diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/styles.module.css index cd74824a2ab5..43feb0f2883e 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/styles.module.css @@ -5,36 +5,45 @@ * LICENSE file in the root directory of this source tree. */ -.avatarImage { - --ifm-avatar-photo-size: 3.2rem; +.authorImage { + --ifm-avatar-photo-size: 3.6rem; } -.author-as-h1 .avatarImage { - --ifm-avatar-photo-size: 6rem; +.author-as-h1 .authorImage { + --ifm-avatar-photo-size: 7rem; } -.author-as-h2 .avatarImage { - --ifm-avatar-photo-size: 5.2rem; +.author-as-h2 .authorImage { + --ifm-avatar-photo-size: 5.4rem; +} + +.authorDetails { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: space-around; } .authorName { font-size: 1.1rem; + line-height: 1.1rem; display: flex; flex-direction: row; } .author-as-h1 .authorName { font-size: 2.4rem; + line-height: 2.4rem; display: inline; } .author-as-h2 .authorName { font-size: 1.4rem; + line-height: 1.4rem; display: inline; } .authorTitle { - margin-top: 0.06rem; font-size: 0.8rem; line-height: 0.8rem; display: -webkit-box; @@ -45,20 +54,16 @@ } .author-as-h1 .authorTitle { - margin-top: 0.4rem; - margin-bottom: 0.2rem; font-size: 1.2rem; line-height: 1.2rem; } .author-as-h2 .authorTitle { - margin-top: 0.3rem; - margin-bottom: 0.2rem; font-size: 1rem; line-height: 1rem; } -.count { +.authorBlogPostCount { background: var(--ifm-color-secondary); color: var(--ifm-color-black); font-size: 0.8rem;