@docusaurus/plugin-content-blog 3.3.2 → 3.5.0

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.
Files changed (62) hide show
  1. package/assets/atom.css +75 -0
  2. package/assets/atom.xsl +92 -0
  3. package/assets/rss.css +75 -0
  4. package/assets/rss.xsl +86 -0
  5. package/lib/authors.d.ts +9 -11
  6. package/lib/authors.js +42 -64
  7. package/lib/authorsMap.d.ts +23 -0
  8. package/lib/authorsMap.js +116 -0
  9. package/lib/authorsProblems.d.ts +21 -0
  10. package/lib/authorsProblems.js +51 -0
  11. package/lib/authorsSocials.d.ts +10 -0
  12. package/lib/authorsSocials.js +48 -0
  13. package/lib/blogUtils.d.ts +7 -12
  14. package/lib/blogUtils.js +44 -34
  15. package/lib/client/contexts.d.ts +33 -0
  16. package/lib/client/contexts.js +54 -0
  17. package/lib/client/index.d.ts +3 -3
  18. package/lib/client/index.js +3 -9
  19. package/lib/client/sidebarUtils.d.ts +21 -0
  20. package/lib/client/sidebarUtils.js +49 -0
  21. package/lib/client/sidebarUtils.test.d.ts +7 -0
  22. package/lib/client/sidebarUtils.test.js +43 -0
  23. package/lib/client/structuredDataUtils.d.ts +10 -0
  24. package/lib/client/structuredDataUtils.js +122 -0
  25. package/lib/feed.d.ts +8 -3
  26. package/lib/feed.js +111 -20
  27. package/lib/frontMatter.d.ts +0 -1
  28. package/lib/frontMatter.js +3 -2
  29. package/lib/index.d.ts +0 -1
  30. package/lib/index.js +132 -105
  31. package/lib/markdownLoader.js +3 -7
  32. package/lib/options.d.ts +4 -1
  33. package/lib/options.js +107 -26
  34. package/lib/props.d.ts +9 -2
  35. package/lib/props.js +23 -3
  36. package/lib/remark/footnoteIDFixer.js +1 -1
  37. package/lib/routes.d.ts +0 -1
  38. package/lib/routes.js +82 -14
  39. package/lib/translations.d.ts +0 -1
  40. package/lib/translations.js +2 -3
  41. package/lib/types.d.ts +1 -8
  42. package/package.json +13 -10
  43. package/src/authors.ts +56 -93
  44. package/src/authorsMap.ts +171 -0
  45. package/src/authorsProblems.ts +72 -0
  46. package/src/authorsSocials.ts +64 -0
  47. package/src/blogUtils.ts +51 -46
  48. package/src/client/contexts.tsx +95 -0
  49. package/src/client/index.tsx +24 -0
  50. package/src/client/sidebarUtils.test.ts +52 -0
  51. package/src/client/sidebarUtils.tsx +85 -0
  52. package/src/client/structuredDataUtils.ts +178 -0
  53. package/src/feed.ts +197 -18
  54. package/src/frontMatter.ts +2 -0
  55. package/src/index.ts +182 -137
  56. package/src/markdownLoader.ts +3 -7
  57. package/src/options.ts +132 -32
  58. package/src/plugin-content-blog.d.ts +252 -113
  59. package/src/props.ts +41 -1
  60. package/src/routes.ts +102 -12
  61. package/src/types.ts +1 -6
  62. package/src/client/index.ts +0 -20
@@ -4,7 +4,6 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
-
8
7
  /// <reference types="@docusaurus/module-type-aliases" />
9
8
 
10
9
  declare module '@docusaurus/plugin-content-blog' {
@@ -12,9 +11,10 @@ declare module '@docusaurus/plugin-content-blog' {
12
11
  import type {MDXOptions} from '@docusaurus/mdx-loader';
13
12
  import type {
14
13
  FrontMatterTag,
15
- Tag,
14
+ TagMetadata,
16
15
  LastUpdateData,
17
16
  FrontMatterLastUpdate,
17
+ TagsPluginOptions,
18
18
  } from '@docusaurus/utils';
19
19
  import type {DocusaurusConfig, Plugin, LoadContext} from '@docusaurus/types';
20
20
  import type {Item as FeedItem} from 'feed';
@@ -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
  */
@@ -43,7 +37,30 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
43
37
  authorsImageUrls: (string | undefined)[];
44
38
  };
45
39
 
46
- export type Author = {
40
+ /**
41
+ * Note we don't pre-define all possible platforms
42
+ * Users can add their own custom platforms if needed
43
+ */
44
+ export type SocialPlatformKey =
45
+ | 'twitter'
46
+ | 'github'
47
+ | 'linkedin'
48
+ | 'stackoverflow'
49
+ | 'x';
50
+
51
+ /**
52
+ * Social platforms of the author.
53
+ * The record value is usually the fully qualified link of the social profile.
54
+ * For pre-defined platforms, it's possible to pass a handle instead
55
+ */
56
+ export type AuthorSocials = Partial<Record<SocialPlatformKey, string>> & {
57
+ /**
58
+ * Unknown keys are allowed: users can pass additional social platforms
59
+ */
60
+ [customAuthorSocialPlatform: string]: string;
61
+ };
62
+
63
+ export type AuthorAttributes = {
47
64
  /**
48
65
  * If `name` doesn't exist, an `imageURL` is expected.
49
66
  */
@@ -68,10 +85,48 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
68
85
  */
69
86
  email?: string;
70
87
  /**
71
- * Unknown keys are allowed, so that we can pass custom fields to authors,
72
- * e.g., `twitter`.
88
+ * Social platforms of the author
89
+ * Usually displayed as a list of social icon links.
90
+ */
91
+ socials?: AuthorSocials;
92
+ /**
93
+ * Description of the author.
94
+ */
95
+ description?: string;
96
+ /**
97
+ * Unknown keys are allowed, so that we can pass custom fields to authors.
73
98
  */
74
- [key: string]: unknown;
99
+ [customAuthorAttribute: string]: unknown;
100
+ };
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;
75
130
  };
76
131
 
77
132
  /**
@@ -165,7 +220,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
165
220
  last_update?: FrontMatterLastUpdate;
166
221
  };
167
222
 
168
- export type BlogPostFrontMatterAuthor = Author & {
223
+ export type BlogPostFrontMatterAuthor = AuthorAttributes & {
169
224
  /**
170
225
  * Will be normalized into the `imageURL` prop.
171
226
  */
@@ -236,7 +291,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
236
291
  /** Front matter, as-is. */
237
292
  readonly frontMatter: BlogPostFrontMatter & {[key: string]: unknown};
238
293
  /** Tags, normalized. */
239
- readonly tags: Tag[];
294
+ readonly tags: TagMetadata[];
240
295
  /**
241
296
  * Marks the post as unlisted and visibly hides it unless directly accessed.
242
297
  */
@@ -260,10 +315,26 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
260
315
  }) => string | undefined;
261
316
 
262
317
  export type FeedType = 'rss' | 'atom' | 'json';
318
+
319
+ export type FeedXSLTOptions = {
320
+ /**
321
+ * RSS XSLT file path, relative to the blog content folder.
322
+ * If null, no XSLT file is used and the feed will be displayed as raw XML.
323
+ */
324
+ rss: string | null;
325
+ /**
326
+ * Atom XSLT file path, relative to the blog content folder.
327
+ * If null, no XSLT file is used and the feed will be displayed as raw XML.
328
+ */
329
+ atom: string | null;
330
+ };
331
+
263
332
  /**
264
333
  * Normalized feed options used within code.
265
334
  */
266
335
  export type FeedOptions = {
336
+ /** Enable feeds xslt stylesheets */
337
+ xslt: FeedXSLTOptions;
267
338
  /** If `null`, no feed is generated. */
268
339
  type?: FeedType[] | null;
269
340
  /** Title of generated feed. */
@@ -345,103 +416,122 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
345
416
  /**
346
417
  * Plugin options after normalization.
347
418
  */
348
- export type PluginOptions = MDXOptions & {
349
- /** Plugin ID. */
350
- id?: string;
351
- /**
352
- * Path to the blog content directory on the file system, relative to site
353
- * directory.
354
- */
355
- path: string;
356
- /**
357
- * URL route for the blog section of your site. **DO NOT** include a
358
- * trailing slash. Use `/` to put the blog at root path.
359
- */
360
- routeBasePath: string;
361
- /**
362
- * URL route for the tags section of your blog. Will be appended to
363
- * `routeBasePath`.
364
- */
365
- tagsBasePath: string;
366
- /**
367
- * URL route for the pages section of your blog. Will be appended to
368
- * `routeBasePath`.
369
- */
370
- pageBasePath: string;
371
- /**
372
- * URL route for the archive section of your blog. Will be appended to
373
- * `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to
374
- * disable generation of archive.
375
- */
376
- archiveBasePath: string | null;
377
- /**
378
- * Array of glob patterns matching Markdown files to be built, relative to
379
- * the content path.
380
- */
381
- include: string[];
382
- /**
383
- * Array of glob patterns matching Markdown files to be excluded. Serves as
384
- * refinement based on the `include` option.
385
- */
386
- exclude: string[];
387
- /**
388
- * Number of posts to show per page in the listing page. Use `'ALL'` to
389
- * display all posts on one listing page.
390
- */
391
- postsPerPage: number | 'ALL';
392
- /** Root component of the blog listing page. */
393
- blogListComponent: string;
394
- /** Root component of each blog post page. */
395
- blogPostComponent: string;
396
- /** Root component of the tags list page. */
397
- blogTagsListComponent: string;
398
- /** Root component of the "posts containing tag" page. */
399
- blogTagsPostsComponent: string;
400
- /** Root component of the blog archive page. */
401
- blogArchiveComponent: string;
402
- /** Blog page title for better SEO. */
403
- blogTitle: string;
404
- /** Blog page meta description for better SEO. */
405
- blogDescription: string;
406
- /**
407
- * Number of blog post elements to show in the blog sidebar. `'ALL'` to show
408
- * all blog posts; `0` to disable.
409
- */
410
- blogSidebarCount: number | 'ALL';
411
- /** Title of the blog sidebar. */
412
- blogSidebarTitle: string;
413
- /** Truncate marker marking where the summary ends. */
414
- truncateMarker: RegExp;
415
- /** Show estimated reading time for the blog post. */
416
- showReadingTime: boolean;
417
- /** Blog feed. */
418
- feedOptions: FeedOptions;
419
- /**
420
- * Base URL to edit your site. The final URL is computed by `editUrl +
421
- * relativePostPath`. Using a function allows more nuanced control for each
422
- * file. Omitting this variable entirely will disable edit links.
423
- */
424
- editUrl?: string | EditUrlFunction;
425
- /**
426
- * The edit URL will target the localized file, instead of the original
427
- * unlocalized file. Ignored when `editUrl` is a function.
428
- */
429
- editLocalizedFiles?: boolean;
430
- /** Path to the authors map file, relative to the blog content directory. */
431
- authorsMapPath: string;
432
- /** A callback to customize the reading time number displayed. */
433
- readingTime: ReadingTimeFunctionOption;
434
- /** Governs the direction of blog post sorting. */
435
- sortPosts: 'ascending' | 'descending';
436
- /** Whether to display the last date the doc was updated. */
437
- showLastUpdateTime: boolean;
438
- /** Whether to display the author who last updated the doc. */
439
- showLastUpdateAuthor: boolean;
440
- /** An optional function which can be used to transform blog posts
441
- * (filter, modify, delete, etc...).
442
- */
443
- processBlogPosts: ProcessBlogPostsFn;
444
- };
419
+ export type PluginOptions = MDXOptions &
420
+ TagsPluginOptions & {
421
+ /** Plugin ID. */
422
+ id?: string;
423
+ /**
424
+ * Path to the blog content directory on the file system, relative to site
425
+ * directory.
426
+ */
427
+ path: string;
428
+ /**
429
+ * URL route for the blog section of your site. **DO NOT** include a
430
+ * trailing slash. Use `/` to put the blog at root path.
431
+ */
432
+ routeBasePath: string;
433
+ /**
434
+ * URL route for the tags section of your blog. Will be appended to
435
+ * `routeBasePath`.
436
+ */
437
+ tagsBasePath: string;
438
+ /**
439
+ * URL route for the pages section of your blog. Will be appended to
440
+ * `routeBasePath`.
441
+ */
442
+ pageBasePath: string;
443
+ /**
444
+ * URL route for the archive section of your blog. Will be appended to
445
+ * `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to
446
+ * disable generation of archive.
447
+ */
448
+ archiveBasePath: string | null;
449
+ /**
450
+ * Array of glob patterns matching Markdown files to be built, relative to
451
+ * the content path.
452
+ */
453
+ include: string[];
454
+ /**
455
+ * Array of glob patterns matching Markdown files to be excluded. Serves as
456
+ * refinement based on the `include` option.
457
+ */
458
+ exclude: string[];
459
+ /**
460
+ * Number of posts to show per page in the listing page. Use `'ALL'` to
461
+ * display all posts on one listing page.
462
+ */
463
+ postsPerPage: number | 'ALL';
464
+ /** Root component of the blog listing page. */
465
+ blogListComponent: string;
466
+ /** Root component of each blog post page. */
467
+ blogPostComponent: string;
468
+ /** Root component of the tags list page. */
469
+ blogTagsListComponent: string;
470
+ /** Root component of the "posts containing tag" page. */
471
+ blogTagsPostsComponent: string;
472
+ /** Root component of the authors list page. */
473
+ blogAuthorsListComponent: string;
474
+ /** Root component of the "posts containing author" page. */
475
+ blogAuthorsPostsComponent: string;
476
+ /** Root component of the blog archive page. */
477
+ blogArchiveComponent: string;
478
+ /** Blog page title for better SEO. */
479
+ blogTitle: string;
480
+ /** Blog page meta description for better SEO. */
481
+ blogDescription: string;
482
+ /**
483
+ * Number of blog post elements to show in the blog sidebar. `'ALL'` to show
484
+ * all blog posts; `0` to disable.
485
+ */
486
+ blogSidebarCount: number | 'ALL';
487
+ /** Title of the blog sidebar. */
488
+ blogSidebarTitle: string;
489
+ /** Truncate marker marking where the summary ends. */
490
+ truncateMarker: RegExp;
491
+ /** Show estimated reading time for the blog post. */
492
+ showReadingTime: boolean;
493
+ /** Blog feed. */
494
+ feedOptions: FeedOptions;
495
+ /**
496
+ * Base URL to edit your site. The final URL is computed by `editUrl +
497
+ * relativePostPath`. Using a function allows more nuanced control for each
498
+ * file. Omitting this variable entirely will disable edit links.
499
+ */
500
+ editUrl?: string | EditUrlFunction;
501
+ /**
502
+ * The edit URL will target the localized file, instead of the original
503
+ * unlocalized file. Ignored when `editUrl` is a function.
504
+ */
505
+ editLocalizedFiles?: boolean;
506
+ /** Path to the authors map file, relative to the blog content directory. */
507
+ authorsMapPath: string;
508
+ /** A callback to customize the reading time number displayed. */
509
+ readingTime: ReadingTimeFunctionOption;
510
+ /** Governs the direction of blog post sorting. */
511
+ sortPosts: 'ascending' | 'descending';
512
+ /** Whether to display the last date the doc was updated. */
513
+ showLastUpdateTime: boolean;
514
+ /** Whether to display the author who last updated the doc. */
515
+ showLastUpdateAuthor: boolean;
516
+ /** An optional function which can be used to transform blog posts
517
+ * (filter, modify, delete, etc...).
518
+ */
519
+ processBlogPosts: ProcessBlogPostsFn;
520
+ /* Base path for the authors page */
521
+ authorsBasePath: string;
522
+ /** The behavior of Docusaurus when it finds inline authors. */
523
+ onInlineAuthors: 'ignore' | 'log' | 'warn' | 'throw';
524
+ /** The behavior of Docusaurus when it finds untruncated blog posts. */
525
+ onUntruncatedBlogPosts: 'ignore' | 'log' | 'warn' | 'throw';
526
+ };
527
+
528
+ export type UserFeedXSLTOptions =
529
+ | boolean
530
+ | null
531
+ | {
532
+ rss?: string | boolean | null;
533
+ atom?: string | boolean | null;
534
+ };
445
535
 
446
536
  /**
447
537
  * Feed options, as provided by user config. `type` accepts `all` as shortcut
@@ -451,6 +541,8 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
451
541
  {
452
542
  /** Type of feed to be generated. Use `null` to disable generation. */
453
543
  type?: FeedOptions['type'] | 'all' | FeedType;
544
+ /** User-provided XSLT config for feeds, un-normalized */
545
+ xslt?: UserFeedXSLTOptions;
454
546
  }
455
547
  >;
456
548
  /**
@@ -468,6 +560,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
468
560
  title: string;
469
561
  permalink: string;
470
562
  unlisted: boolean;
563
+ date: Date | string;
471
564
  };
472
565
 
473
566
  export type BlogSidebar = {
@@ -475,17 +568,22 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
475
568
  items: BlogSidebarItem[];
476
569
  };
477
570
 
571
+ export type AuthorsMap = {[authorKey: string]: AuthorWithKey};
572
+
478
573
  export type BlogContent = {
479
574
  blogSidebarTitle: string;
480
575
  blogPosts: BlogPost[];
481
576
  blogListPaginated: BlogPaginated[];
482
577
  blogTags: BlogTags;
483
578
  blogTagsListPath: string;
579
+ authorsMap?: AuthorsMap;
484
580
  };
485
581
 
486
582
  export type BlogMetadata = {
487
583
  /** the path to the base of the blog */
488
584
  blogBasePath: string;
585
+ /** the path to the authors list page */
586
+ authorsListPath: string;
489
587
  /** title of the overall blog */
490
588
  blogTitle: string;
491
589
  };
@@ -494,7 +592,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
494
592
  [permalink: string]: BlogTag;
495
593
  };
496
594
 
497
- export type BlogTag = Tag & {
595
+ export type BlogTag = TagMetadata & {
498
596
  /** Blog post permalinks. */
499
597
  items: string[];
500
598
  pages: BlogPaginated[];
@@ -646,6 +744,47 @@ declare module '@theme/BlogTagsListPage' {
646
744
  export default function BlogTagsListPage(props: Props): JSX.Element;
647
745
  }
648
746
 
747
+ declare module '@theme/Blog/Pages/BlogAuthorsListPage' {
748
+ import type {
749
+ AuthorItemProp,
750
+ BlogSidebar,
751
+ } from '@docusaurus/plugin-content-blog';
752
+
753
+ export interface Props {
754
+ /** Blog sidebar. */
755
+ readonly sidebar: BlogSidebar;
756
+ /** All authors declared in this blog. */
757
+ readonly authors: AuthorItemProp[];
758
+ }
759
+
760
+ export default function BlogAuthorsListPage(props: Props): JSX.Element;
761
+ }
762
+
763
+ declare module '@theme/Blog/Pages/BlogAuthorsPostsPage' {
764
+ import type {Content} from '@theme/BlogPostPage';
765
+ import type {
766
+ AuthorItemProp,
767
+ BlogSidebar,
768
+ BlogPaginatedMetadata,
769
+ } from '@docusaurus/plugin-content-blog';
770
+
771
+ export interface Props {
772
+ /** Blog sidebar. */
773
+ readonly sidebar: BlogSidebar;
774
+ /** Metadata of this author. */
775
+ readonly author: AuthorItemProp;
776
+ /** Looks exactly the same as the posts list page */
777
+ readonly listMetadata: BlogPaginatedMetadata;
778
+ /**
779
+ * Array of blog posts included on this page. Every post's metadata is also
780
+ * available.
781
+ */
782
+ readonly items: readonly {readonly content: Content}[];
783
+ }
784
+
785
+ export default function BlogAuthorsPostsPage(props: Props): JSX.Element;
786
+ }
787
+
649
788
  declare module '@theme/BlogTagsPostsPage' {
650
789
  import type {Content} from '@theme/BlogPostPage';
651
790
  import type {
package/src/props.ts CHANGED
@@ -5,7 +5,14 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import type {TagsListItem, TagModule} from '@docusaurus/utils';
8
- import type {BlogTag, BlogTags} from '@docusaurus/plugin-content-blog';
8
+ import type {
9
+ AuthorItemProp,
10
+ AuthorWithKey,
11
+ BlogPost,
12
+ BlogSidebar,
13
+ BlogTag,
14
+ BlogTags,
15
+ } from '@docusaurus/plugin-content-blog';
9
16
 
10
17
  export function toTagsProp({blogTags}: {blogTags: BlogTags}): TagsListItem[] {
11
18
  return Object.values(blogTags)
@@ -13,6 +20,7 @@ export function toTagsProp({blogTags}: {blogTags: BlogTags}): TagsListItem[] {
13
20
  .map((tag) => ({
14
21
  label: tag.label,
15
22
  permalink: tag.permalink,
23
+ description: tag.description,
16
24
  count: tag.items.length,
17
25
  }));
18
26
  }
@@ -27,8 +35,40 @@ export function toTagProp({
27
35
  return {
28
36
  label: tag.label,
29
37
  permalink: tag.permalink,
38
+ description: tag.description,
30
39
  allTagsPath: blogTagsListPath,
31
40
  count: tag.items.length,
32
41
  unlisted: tag.unlisted,
33
42
  };
34
43
  }
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
+
58
+ export function toBlogSidebarProp({
59
+ blogSidebarTitle,
60
+ blogPosts,
61
+ }: {
62
+ blogSidebarTitle: string;
63
+ blogPosts: BlogPost[];
64
+ }): BlogSidebar {
65
+ return {
66
+ title: blogSidebarTitle,
67
+ items: blogPosts.map((blogPost) => ({
68
+ title: blogPost.metadata.title,
69
+ permalink: blogPost.metadata.permalink,
70
+ unlisted: blogPost.metadata.unlisted,
71
+ date: blogPost.metadata.date,
72
+ })),
73
+ };
74
+ }