@@ -12,13 +12,16 @@ const DEFAULT_BLOG_CATEGORY = "Engineering";
1212const DEFAULT_POSTS_PER_PAGE_MODE = "ALL" ;
1313const PAGINATION_PATH_SEGMENT = "page" ;
1414const RANDOM_POSTS_LIMIT = 3 ;
15+ const AUTHOR_POSTS_PREVIEW_LIMIT = 3 ;
1516const CATEGORY_FALLBACK_SLUG = "category" ;
1617const BLOG_BASE_PATH = "/blog" ;
1718const BLOG_CATEGORIES_LIST_PATH = `${ BLOG_BASE_PATH } /categories` ;
19+ const BLOG_AUTHORS_LIST_PATH = `${ BLOG_BASE_PATH } /authors` ;
1820const BLOG_AUTHORS_BASE_PATH = `${ BLOG_BASE_PATH } /author` ;
1921const ALL_TAGS_LABEL = "All tags" ;
2022const TAGS_DATA_SUFFIX = "-tags" ;
2123const CATEGORIES_DATA_SUFFIX = "-categories" ;
24+ const AUTHORS_DATA_SUFFIX = "-authors" ;
2225const CATEGORY_DATA_SUFFIX = "-category" ;
2326const ALL_TAGS_DATA_SUFFIX = "-all-tags" ;
2427const LIST_DATA_SUFFIX = "-list" ;
@@ -30,6 +33,8 @@ const IS_FEATURED_FRONTMATTER_KEY = "is_featured";
3033const BLOG_POST_PAGE_COMPONENT = "@theme/BlogPostPage" ;
3134const BLOG_LIST_PAGE_COMPONENT = "@theme/BlogListPage" ;
3235const BLOG_AUTHOR_PAGE_COMPONENT = "@site/src/components/blog/author-page" ;
36+ const BLOG_AUTHORS_LIST_PAGE_COMPONENT =
37+ "@site/src/components/blog/authors-page" ;
3338const BLOG_TAGS_POSTS_PAGE_COMPONENT = "@theme/BlogTagsPostsPage" ;
3439const BLOG_CATEGORIES_LIST_PAGE_COMPONENT = "@theme/BlogCategoriesListPage" ;
3540const BLOG_CATEGORY_POSTS_PAGE_COMPONENT = "@theme/BlogCategoryPostsPage" ;
@@ -208,6 +213,87 @@ function toPostInfo(post) {
208213 } ;
209214}
210215
216+ function toComparableTimestamp ( dateValue ) {
217+ const timestamp = new Date ( dateValue ) . getTime ( ) ;
218+
219+ return Number . isNaN ( timestamp ) ? 0 : timestamp ;
220+ }
221+
222+ function sortBlogPostsByDateDescending ( blogPosts ) {
223+ return [ ...blogPosts ] . sort ( ( postA , postB ) => {
224+ const timestampDiff =
225+ toComparableTimestamp ( postB . metadata . date ) -
226+ toComparableTimestamp ( postA . metadata . date ) ;
227+
228+ if ( timestampDiff !== 0 ) {
229+ return timestampDiff ;
230+ }
231+
232+ return `${ postA . metadata . title ?? "" } ` . localeCompare (
233+ `${ postB . metadata . title ?? "" } ` ,
234+ ) ;
235+ } ) ;
236+ }
237+
238+ function createBlogAuthors ( { allBlogPosts, blogCategories } ) {
239+ const authorsMap = new Map ( ) ;
240+
241+ allBlogPosts . forEach ( ( post ) => {
242+ const author = post . metadata . authors ?. [ 0 ] ;
243+
244+ if ( ! author ?. key ) {
245+ return ;
246+ }
247+
248+ const existingEntry = authorsMap . get ( author . key ) ;
249+
250+ if ( existingEntry ) {
251+ existingEntry . posts . push ( post ) ;
252+ return ;
253+ }
254+
255+ authorsMap . set ( author . key , {
256+ author,
257+ permalink : `${ BLOG_AUTHORS_BASE_PATH } /${ author . key } ` ,
258+ posts : [ post ] ,
259+ } ) ;
260+ } ) ;
261+
262+ return [ ...authorsMap . values ( ) ]
263+ . map ( ( { author, permalink, posts } ) => {
264+ const sortedPosts = sortBlogPostsByDateDescending ( posts ) ;
265+
266+ return {
267+ author,
268+ permalink,
269+ count : sortedPosts . length ,
270+ recentPosts : sortedPosts
271+ . slice ( 0 , AUTHOR_POSTS_PREVIEW_LIMIT )
272+ . map ( ( post ) => ( {
273+ title : post . metadata . title ,
274+ permalink : post . metadata . permalink ,
275+ date : post . metadata . date ,
276+ formattedDate : post . metadata . formattedDate ,
277+ category : getBlogPostCategoryData ( {
278+ frontMatter : post . metadata . frontMatter ,
279+ blogCategories,
280+ } ) ,
281+ } ) ) ,
282+ } ;
283+ } )
284+ . sort ( ( authorA , authorB ) => {
285+ const countDiff = ( authorB . count ?? 0 ) - ( authorA . count ?? 0 ) ;
286+
287+ if ( countDiff !== 0 ) {
288+ return countDiff ;
289+ }
290+
291+ return `${ authorA . author ?. name ?? "" } ` . localeCompare (
292+ `${ authorB . author ?. name ?? "" } ` ,
293+ ) ;
294+ } ) ;
295+ }
296+
211297function getRelatedPosts ( allBlogPosts , metadata ) {
212298 const currentPostTags = normalizeTags ( metadata . frontMatter . tags ) ;
213299 const currentPostCategory = getBlogPostCategory ( metadata . frontMatter ) ;
@@ -732,6 +818,29 @@ async function blogPluginExtended(...pluginArgs) {
732818 } ) ,
733819 ) ;
734820
821+ const blogAuthors = createBlogAuthors ( {
822+ allBlogPosts,
823+ blogCategories,
824+ } ) ;
825+
826+ if ( blogAuthors . length > 0 ) {
827+ const authorsPropPath = await createData (
828+ `${ utils . docuHash (
829+ `${ BLOG_AUTHORS_LIST_PATH } ${ AUTHORS_DATA_SUFFIX } ` ,
830+ ) } ${ JSON_FILE_EXTENSION } `,
831+ JSON . stringify ( blogAuthors , null , 2 ) ,
832+ ) ;
833+
834+ addRoute ( {
835+ path : BLOG_AUTHORS_LIST_PATH ,
836+ component : BLOG_AUTHORS_LIST_PAGE_COMPONENT ,
837+ exact : true ,
838+ modules : {
839+ authors : aliasedSource ( authorsPropPath ) ,
840+ } ,
841+ } ) ;
842+ }
843+
735844 const authorsArray = allBlogPosts
736845 . map ( ( post ) => post . metadata . frontMatter . authors )
737846 . filter ( ( authorName ) => authorName !== undefined ) ;
0 commit comments