@docusaurus/plugin-content-blog 0.0.0-6010 → 0.0.0-6014

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,13 +22,7 @@ declare module '@docusaurus/plugin-content-blog' {
22
22
 
23
23
  export type Assets = {
24
24
  /**
25
- * If `metadata.yarn workspace website typecheck
26
- 4
27
- yarn workspace v1.22.19yarn workspace website typecheck
28
- 4
29
- yarn workspace v1.22.19yarn workspace website typecheck
30
- 4
31
- yarn workspace v1.22.19image` is a collocated image path, this entry will be the
25
+ * If `metadata.image` is a collocated image path, this entry will be the
32
26
  * bundler-generated image path. Otherwise, it's empty, and the image URL
33
27
  * should be accessed through `frontMatter.image`.
34
28
  */
@@ -66,9 +60,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
66
60
  [customAuthorSocialPlatform: string]: string;
67
61
  };
68
62
 
69
- export type Author = {
70
- key?: string; // TODO temporary, need refactor
71
-
63
+ export type AuthorAttributes = {
72
64
  /**
73
65
  * If `name` doesn't exist, an `imageURL` is expected.
74
66
  */
@@ -98,11 +90,45 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
98
90
  */
99
91
  socials?: AuthorSocials;
100
92
  /**
101
- * Unknown keys are allowed, so that we can pass custom fields to authors,
93
+ * Description of the author.
94
+ */
95
+ description?: string;
96
+ /**
97
+ * Unknown keys are allowed, so that we can pass custom fields to authors.
102
98
  */
103
99
  [customAuthorAttribute: string]: unknown;
104
100
  };
105
101
 
102
+ /**
103
+ * Metadata of the author's page, if it exists.
104
+ */
105
+ export type AuthorPage = {permalink: string};
106
+
107
+ /**
108
+ * Normalized author metadata.
109
+ */
110
+ export type Author = AuthorAttributes & {
111
+ /**
112
+ * Author key, if the author was loaded from the authors map.
113
+ * `null` means the author was declared inline.
114
+ */
115
+ key: string | null;
116
+ /**
117
+ * Metadata of the author's page.
118
+ * `null` means the author doesn't have a dedicated author page.
119
+ */
120
+ page: AuthorPage | null;
121
+ };
122
+
123
+ /** Authors coming from the AuthorsMap always have a key */
124
+ export type AuthorWithKey = Author & {key: string};
125
+
126
+ /** What the authors list page should know about each author. */
127
+ export type AuthorItemProp = AuthorWithKey & {
128
+ /** Number of blog posts with this author. */
129
+ count: number;
130
+ };
131
+
106
132
  /**
107
133
  * Everything is partial/unnormalized, because front matter is always
108
134
  * preserved as-is. Default values will be applied when generating metadata
@@ -194,7 +220,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
194
220
  last_update?: FrontMatterLastUpdate;
195
221
  };
196
222
 
197
- export type BlogPostFrontMatterAuthor = Author & {
223
+ export type BlogPostFrontMatterAuthor = AuthorAttributes & {
198
224
  /**
199
225
  * Will be normalized into the `imageURL` prop.
200
226
  */
@@ -427,6 +453,10 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
427
453
  blogTagsListComponent: string;
428
454
  /** Root component of the "posts containing tag" page. */
429
455
  blogTagsPostsComponent: string;
456
+ /** Root component of the authors list page. */
457
+ blogAuthorsListComponent: string;
458
+ /** Root component of the "posts containing author" page. */
459
+ blogAuthorsPostsComponent: string;
430
460
  /** Root component of the blog archive page. */
431
461
  blogArchiveComponent: string;
432
462
  /** Blog page title for better SEO. */
@@ -471,6 +501,8 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
471
501
  * (filter, modify, delete, etc...).
472
502
  */
473
503
  processBlogPosts: ProcessBlogPostsFn;
504
+ /* Base path for the authors page */
505
+ authorsBasePath: string;
474
506
  /** The behavior of Docusaurus when it finds inline authors. */
475
507
  onInlineAuthors: 'ignore' | 'log' | 'warn' | 'throw';
476
508
  };
@@ -508,17 +540,22 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
508
540
  items: BlogSidebarItem[];
509
541
  };
510
542
 
543
+ export type AuthorsMap = {[authorKey: string]: AuthorWithKey};
544
+
511
545
  export type BlogContent = {
512
546
  blogSidebarTitle: string;
513
547
  blogPosts: BlogPost[];
514
548
  blogListPaginated: BlogPaginated[];
515
549
  blogTags: BlogTags;
516
550
  blogTagsListPath: string;
551
+ authorsMap?: AuthorsMap;
517
552
  };
518
553
 
519
554
  export type BlogMetadata = {
520
555
  /** the path to the base of the blog */
521
556
  blogBasePath: string;
557
+ /** the path to the authors list page */
558
+ authorsListPath: string;
522
559
  /** title of the overall blog */
523
560
  blogTitle: string;
524
561
  };
@@ -679,6 +716,47 @@ declare module '@theme/BlogTagsListPage' {
679
716
  export default function BlogTagsListPage(props: Props): JSX.Element;
680
717
  }
681
718
 
719
+ declare module '@theme/Blog/Pages/BlogAuthorsListPage' {
720
+ import type {
721
+ AuthorItemProp,
722
+ BlogSidebar,
723
+ } from '@docusaurus/plugin-content-blog';
724
+
725
+ export interface Props {
726
+ /** Blog sidebar. */
727
+ readonly sidebar: BlogSidebar;
728
+ /** All authors declared in this blog. */
729
+ readonly authors: AuthorItemProp[];
730
+ }
731
+
732
+ export default function BlogAuthorsListPage(props: Props): JSX.Element;
733
+ }
734
+
735
+ declare module '@theme/Blog/Pages/BlogAuthorsPostsPage' {
736
+ import type {Content} from '@theme/BlogPostPage';
737
+ import type {
738
+ AuthorItemProp,
739
+ BlogSidebar,
740
+ BlogPaginatedMetadata,
741
+ } from '@docusaurus/plugin-content-blog';
742
+
743
+ export interface Props {
744
+ /** Blog sidebar. */
745
+ readonly sidebar: BlogSidebar;
746
+ /** Metadata of this author. */
747
+ readonly author: AuthorItemProp;
748
+ /** Looks exactly the same as the posts list page */
749
+ readonly listMetadata: BlogPaginatedMetadata;
750
+ /**
751
+ * Array of blog posts included on this page. Every post's metadata is also
752
+ * available.
753
+ */
754
+ readonly items: readonly {readonly content: Content}[];
755
+ }
756
+
757
+ export default function BlogAuthorsPostsPage(props: Props): JSX.Element;
758
+ }
759
+
682
760
  declare module '@theme/BlogTagsPostsPage' {
683
761
  import type {Content} from '@theme/BlogPostPage';
684
762
  import type {
package/src/props.ts CHANGED
@@ -6,6 +6,8 @@
6
6
  */
7
7
  import type {TagsListItem, TagModule} from '@docusaurus/utils';
8
8
  import type {
9
+ AuthorItemProp,
10
+ AuthorWithKey,
9
11
  BlogPost,
10
12
  BlogSidebar,
11
13
  BlogTag,
@@ -40,6 +42,19 @@ export function toTagProp({
40
42
  };
41
43
  }
42
44
 
45
+ export function toAuthorItemProp({
46
+ author,
47
+ count,
48
+ }: {
49
+ author: AuthorWithKey;
50
+ count: number;
51
+ }): AuthorItemProp {
52
+ return {
53
+ ...author,
54
+ count,
55
+ };
56
+ }
57
+
43
58
  export function toBlogSidebarProp({
44
59
  blogSidebarTitle,
45
60
  blogPosts,
package/src/routes.ts CHANGED
@@ -11,9 +11,15 @@ import {
11
11
  docuHash,
12
12
  aliasedSitePathToRelativePath,
13
13
  } from '@docusaurus/utils';
14
- import {shouldBeListed} from './blogUtils';
14
+ import {paginateBlogPosts, shouldBeListed} from './blogUtils';
15
15
 
16
- import {toBlogSidebarProp, toTagProp, toTagsProp} from './props';
16
+ import {
17
+ toAuthorItemProp,
18
+ toBlogSidebarProp,
19
+ toTagProp,
20
+ toTagsProp,
21
+ } from './props';
22
+ import {groupBlogPostsByAuthorKey} from './authors';
17
23
  import type {
18
24
  PluginContentLoadedActions,
19
25
  RouteConfig,
@@ -26,6 +32,7 @@ import type {
26
32
  BlogContent,
27
33
  PluginOptions,
28
34
  BlogPost,
35
+ AuthorWithKey,
29
36
  } from '@docusaurus/plugin-content-blog';
30
37
 
31
38
  type CreateAllRoutesParam = {
@@ -54,11 +61,16 @@ export async function buildAllRoutes({
54
61
  blogListComponent,
55
62
  blogPostComponent,
56
63
  blogTagsListComponent,
64
+ blogAuthorsListComponent,
65
+ blogAuthorsPostsComponent,
57
66
  blogTagsPostsComponent,
58
67
  blogArchiveComponent,
59
68
  routeBasePath,
60
69
  archiveBasePath,
61
70
  blogTitle,
71
+ authorsBasePath,
72
+ postsPerPage,
73
+ blogDescription,
62
74
  } = options;
63
75
  const pluginId = options.id!;
64
76
  const {createData} = actions;
@@ -68,8 +80,15 @@ export async function buildAllRoutes({
68
80
  blogListPaginated,
69
81
  blogTags,
70
82
  blogTagsListPath,
83
+ authorsMap,
71
84
  } = content;
72
85
 
86
+ const authorsListPath = normalizeUrl([
87
+ baseUrl,
88
+ routeBasePath,
89
+ authorsBasePath,
90
+ ]);
91
+
73
92
  const listedBlogPosts = blogPosts.filter(shouldBeListed);
74
93
 
75
94
  const blogPostsById = _.keyBy(blogPosts, (post) => post.id);
@@ -102,6 +121,7 @@ export async function buildAllRoutes({
102
121
  const blogMetadata: BlogMetadata = {
103
122
  blogBasePath: normalizeUrl([baseUrl, routeBasePath]),
104
123
  blogTitle,
124
+ authorsListPath,
105
125
  };
106
126
  const modulePath = await createData(
107
127
  `blogMetadata-${pluginId}.json`,
@@ -249,10 +269,85 @@ export async function buildAllRoutes({
249
269
  return [tagsListRoute, ...tagsPaginatedRoutes];
250
270
  }
251
271
 
272
+ function createAuthorsRoutes(): RouteConfig[] {
273
+ if (authorsMap === undefined || Object.keys(authorsMap).length === 0) {
274
+ return [];
275
+ }
276
+
277
+ const blogPostsByAuthorKey = groupBlogPostsByAuthorKey({
278
+ authorsMap,
279
+ blogPosts,
280
+ });
281
+ const authors = Object.values(authorsMap);
282
+
283
+ return [
284
+ createAuthorListRoute(),
285
+ ...authors.flatMap(createAuthorPaginatedRoute),
286
+ ];
287
+
288
+ function createAuthorListRoute(): RouteConfig {
289
+ return {
290
+ path: authorsListPath,
291
+ component: blogAuthorsListComponent,
292
+ exact: true,
293
+ modules: {
294
+ sidebar: sidebarModulePath,
295
+ },
296
+ props: {
297
+ authors: authors.map((author) =>
298
+ toAuthorItemProp({
299
+ author,
300
+ count: blogPostsByAuthorKey[author.key]?.length ?? 0,
301
+ }),
302
+ ),
303
+ },
304
+ context: {
305
+ blogMetadata: blogMetadataModulePath,
306
+ },
307
+ };
308
+ }
309
+
310
+ function createAuthorPaginatedRoute(author: AuthorWithKey): RouteConfig[] {
311
+ const authorBlogPosts = blogPostsByAuthorKey[author.key] ?? [];
312
+ if (!author.page) {
313
+ return [];
314
+ }
315
+
316
+ const pages = paginateBlogPosts({
317
+ blogPosts: authorBlogPosts,
318
+ basePageUrl: author.page.permalink,
319
+ blogDescription,
320
+ blogTitle,
321
+ pageBasePath: authorsBasePath,
322
+ postsPerPageOption: postsPerPage,
323
+ });
324
+
325
+ return pages.map(({metadata, items}) => {
326
+ return {
327
+ path: metadata.permalink,
328
+ component: blogAuthorsPostsComponent,
329
+ exact: true,
330
+ modules: {
331
+ items: blogPostItemsModule(items),
332
+ sidebar: sidebarModulePath,
333
+ },
334
+ props: {
335
+ author: toAuthorItemProp({author, count: authorBlogPosts.length}),
336
+ listMetadata: metadata,
337
+ },
338
+ context: {
339
+ blogMetadata: blogMetadataModulePath,
340
+ },
341
+ };
342
+ });
343
+ }
344
+ }
345
+
252
346
  return [
253
347
  ...createBlogPostRoutes(),
254
348
  ...createBlogPostsPaginatedRoutes(),
255
349
  ...createTagsRoutes(),
256
350
  ...createArchiveRoute(),
351
+ ...createAuthorsRoutes(),
257
352
  ];
258
353
  }