@docusaurus/plugin-content-blog 2.0.0-beta.fc64c12e4 → 2.0.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 (60) hide show
  1. package/lib/authors.d.ts +22 -0
  2. package/lib/authors.js +122 -0
  3. package/lib/blogUtils.d.ts +27 -7
  4. package/lib/blogUtils.js +201 -145
  5. package/lib/feed.d.ts +15 -0
  6. package/lib/feed.js +102 -0
  7. package/lib/frontMatter.d.ts +10 -0
  8. package/lib/{blogFrontMatter.js → frontMatter.js} +31 -19
  9. package/lib/index.d.ts +4 -4
  10. package/lib/index.js +163 -194
  11. package/lib/markdownLoader.d.ts +3 -6
  12. package/lib/markdownLoader.js +6 -7
  13. package/lib/options.d.ts +10 -0
  14. package/lib/{pluginOptionSchema.js → options.js} +41 -14
  15. package/lib/remark/footnoteIDFixer.d.ts +14 -0
  16. package/lib/remark/footnoteIDFixer.js +29 -0
  17. package/lib/translations.d.ts +10 -0
  18. package/lib/translations.js +53 -0
  19. package/lib/types.d.ts +4 -110
  20. package/package.json +23 -19
  21. package/src/authors.ts +168 -0
  22. package/src/blogUtils.ts +305 -205
  23. package/src/feed.ts +171 -0
  24. package/src/frontMatter.ts +81 -0
  25. package/src/index.ts +223 -258
  26. package/src/markdownLoader.ts +11 -16
  27. package/src/{pluginOptionSchema.ts → options.ts} +54 -16
  28. package/src/plugin-content-blog.d.ts +587 -0
  29. package/src/remark/footnoteIDFixer.ts +29 -0
  30. package/src/translations.ts +69 -0
  31. package/src/types.ts +2 -129
  32. package/index.d.ts +0 -138
  33. package/lib/.tsbuildinfo +0 -1
  34. package/lib/blogFrontMatter.d.ts +0 -28
  35. package/lib/pluginOptionSchema.d.ts +0 -34
  36. package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  37. package/src/__tests__/__fixtures__/website/blog/_partials/somePartial.md +0 -3
  38. package/src/__tests__/__fixtures__/website/blog/_partials/subfolder/somePartial.md +0 -3
  39. package/src/__tests__/__fixtures__/website/blog/_somePartial.md +0 -3
  40. package/src/__tests__/__fixtures__/website/blog/complex-slug.md +0 -7
  41. package/src/__tests__/__fixtures__/website/blog/date-matter.md +0 -5
  42. package/src/__tests__/__fixtures__/website/blog/draft.md +0 -6
  43. package/src/__tests__/__fixtures__/website/blog/heading-as-title.md +0 -5
  44. package/src/__tests__/__fixtures__/website/blog/simple-slug.md +0 -7
  45. package/src/__tests__/__fixtures__/website/blog-with-ref/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  46. package/src/__tests__/__fixtures__/website/blog-with-ref/post-with-broken-links.md +0 -11
  47. package/src/__tests__/__fixtures__/website/blog-with-ref/post.md +0 -5
  48. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  49. package/src/__tests__/__fixtures__/website-blog-without-date/blog/no date.md +0 -1
  50. package/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap +0 -116
  51. package/src/__tests__/__snapshots__/linkify.test.ts.snap +0 -24
  52. package/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap +0 -5
  53. package/src/__tests__/blogFrontMatter.test.ts +0 -317
  54. package/src/__tests__/generateBlogFeed.test.ts +0 -102
  55. package/src/__tests__/index.test.ts +0 -336
  56. package/src/__tests__/linkify.test.ts +0 -93
  57. package/src/__tests__/pluginOptionSchema.test.ts +0 -150
  58. package/src/blogFrontMatter.ts +0 -88
  59. package/tsconfig.json +0 -9
  60. package/types.d.ts +0 -13
@@ -0,0 +1,587 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ declare module '@docusaurus/plugin-content-blog' {
9
+ import type {LoadedMDXContent} from '@docusaurus/mdx-loader';
10
+ import type {MDXOptions} from '@docusaurus/mdx-loader';
11
+ import type {FrontMatterTag, Tag} from '@docusaurus/utils';
12
+ import type {Plugin, LoadContext} from '@docusaurus/types';
13
+ import type {Overwrite} from 'utility-types';
14
+
15
+ export type Assets = {
16
+ /**
17
+ * If `metadata.image` is a collocated image path, this entry will be the
18
+ * bundler-generated image path. Otherwise, it's empty, and the image URL
19
+ * should be accessed through `frontMatter.image`.
20
+ */
21
+ image?: string;
22
+ /**
23
+ * Array where each item is 1-1 correlated with the `metadata.authors` array
24
+ * so that client can render the correct author images. If the author's
25
+ * image is a local file path, the slot will be filled with the bundler-
26
+ * generated image path; otherwise, it's empty, and the author's image URL
27
+ * should be accessed through `authors.imageURL`.
28
+ */
29
+ authorsImageUrls: (string | undefined)[];
30
+ };
31
+
32
+ export type Author = {
33
+ /**
34
+ * If `name` doesn't exist, an `imageURL` is expected.
35
+ */
36
+ name?: string;
37
+ /**
38
+ * The image path could be collocated, in which case
39
+ * `metadata.assets.authorsImageUrls` should be used instead. If `imageURL`
40
+ * doesn't exist, a `name` is expected.
41
+ */
42
+ imageURL?: string;
43
+ /**
44
+ * Used to generate the author's link.
45
+ */
46
+ url?: string;
47
+ /**
48
+ * Used as a subtitle for the author, e.g. "maintainer of Docusaurus"
49
+ */
50
+ title?: string;
51
+ /**
52
+ * Mainly used for RSS feeds; if `url` doesn't exist, `email` can be used
53
+ * to generate a fallback `mailto:` URL.
54
+ */
55
+ email?: string;
56
+ /**
57
+ * Unknown keys are allowed, so that we can pass custom fields to authors,
58
+ * e.g., `twitter`.
59
+ */
60
+ [key: string]: unknown;
61
+ };
62
+
63
+ /**
64
+ * Everything is partial/unnormalized, because front matter is always
65
+ * preserved as-is. Default values will be applied when generating metadata
66
+ */
67
+ export type BlogPostFrontMatter = {
68
+ /**
69
+ * @deprecated Use `slug` instead.
70
+ */
71
+ id?: string;
72
+ /**
73
+ * Will override the default title collected from h1 heading.
74
+ * @see {@link BlogPostMetadata.title}
75
+ */
76
+ title?: string;
77
+ /**
78
+ * Will override the default excerpt.
79
+ * @see {@link BlogPostMetadata.description}
80
+ */
81
+ description?: string;
82
+ /**
83
+ * Front matter tags, unnormalized.
84
+ * @see {@link BlogPostMetadata.tags}
85
+ */
86
+ tags?: FrontMatterTag[];
87
+ /** Custom slug appended after `/<baseUrl>/<routeBasePath>/` */
88
+ slug?: string;
89
+ /**
90
+ * Marks the post as draft and excludes it from the production build.
91
+ */
92
+ draft?: boolean;
93
+ /**
94
+ * Will override the default publish date inferred from git/filename. Yaml
95
+ * only converts standard yyyy-MM-dd format to dates, so this may stay as a
96
+ * plain string.
97
+ * @see {@link BlogPostMetadata.date}
98
+ */
99
+ date?: Date | string;
100
+ /**
101
+ * Authors, unnormalized.
102
+ * @see {@link BlogPostMetadata.authors}
103
+ */
104
+ authors?: BlogPostFrontMatterAuthors;
105
+ /**
106
+ * To be deprecated
107
+ * @see {@link BlogPostFrontMatterAuthor.name}
108
+ */
109
+ author?: string;
110
+ /**
111
+ * To be deprecated
112
+ * @see {@link BlogPostFrontMatterAuthor.title}
113
+ */
114
+ author_title?: string;
115
+ /**
116
+ * To be deprecated
117
+ * @see {@link BlogPostFrontMatterAuthor.url}
118
+ */
119
+ author_url?: string;
120
+ /**
121
+ * To be deprecated
122
+ * @see {@link BlogPostFrontMatterAuthor.imageURL}
123
+ */
124
+ author_image_url?: string;
125
+
126
+ /** @deprecated v1 legacy */
127
+ authorTitle?: string;
128
+ /** @deprecated v1 legacy */
129
+ authorURL?: string;
130
+ /** @deprecated v1 legacy */
131
+ authorImageURL?: string;
132
+
133
+ /** Used in the head meta. Should use `assets.image` in priority. */
134
+ image?: string;
135
+ /** Used in the head meta. */
136
+ keywords?: string[];
137
+ /** Hide the right TOC. */
138
+ hide_table_of_contents?: boolean;
139
+ /**
140
+ * Minimum TOC heading level. Must be between 2 and 6 and lower or equal to
141
+ * the max value.
142
+ */
143
+ toc_min_heading_level?: number;
144
+ /** Maximum TOC heading level. Must be between 2 and 6. */
145
+ toc_max_heading_level?: number;
146
+ };
147
+
148
+ export type BlogPostFrontMatterAuthor = Author & {
149
+ /**
150
+ * Will be normalized into the `imageURL` prop.
151
+ */
152
+ image_url?: string;
153
+ /**
154
+ * References an existing author in the authors map.
155
+ */
156
+ key?: string;
157
+ };
158
+
159
+ /**
160
+ * Blog post authors can be declared in front matter as a string key
161
+ * (referencing an author in authors map), an object (partially overriding the
162
+ * data in authors map, or a completely new author), or an array of a mix of
163
+ * both.
164
+ */
165
+ export type BlogPostFrontMatterAuthors =
166
+ | string
167
+ | BlogPostFrontMatterAuthor
168
+ | (string | BlogPostFrontMatterAuthor)[];
169
+
170
+ export type BlogPostMetadata = {
171
+ /** Path to the Markdown source, with `@site` alias. */
172
+ readonly source: string;
173
+ /**
174
+ * Used to generate the page h1 heading, tab title, and pagination title.
175
+ */
176
+ readonly title: string;
177
+ /**
178
+ * The publish date of the post. On client side, this will be serialized
179
+ * into a string.
180
+ */
181
+ readonly date: Date;
182
+ /**
183
+ * Publish date formatted according to the locale, so that the client can
184
+ * render the date regardless of the existence of `Intl.DateTimeFormat`.
185
+ */
186
+ readonly formattedDate: string;
187
+ /** Full link including base URL. */
188
+ readonly permalink: string;
189
+ /**
190
+ * Description used in the meta. Could be an empty string (empty content)
191
+ */
192
+ readonly description: string;
193
+ /**
194
+ * Absolute URL to the editing page of the post. Undefined if the post
195
+ * shouldn't be edited.
196
+ */
197
+ readonly editUrl?: string;
198
+ /**
199
+ * Reading time in minutes calculated based on word count.
200
+ */
201
+ readonly readingTime?: number;
202
+ /**
203
+ * Whether the truncate marker exists in the post's content.
204
+ */
205
+ readonly hasTruncateMarker: boolean;
206
+ /**
207
+ * Used in pagination. Generated after the other metadata, so not readonly.
208
+ * Content is just a subset of another post's metadata.
209
+ */
210
+ nextItem?: {readonly title: string; readonly permalink: string};
211
+ /**
212
+ * Used in pagination. Generated after the other metadata, so not readonly.
213
+ * Content is just a subset of another post's metadata.
214
+ */
215
+ prevItem?: {readonly title: string; readonly permalink: string};
216
+ /**
217
+ * Author metadata, normalized. Should be used in joint with
218
+ * `assets.authorsImageUrls` on client side.
219
+ */
220
+ readonly authors: Author[];
221
+ /** Front matter, as-is. */
222
+ readonly frontMatter: BlogPostFrontMatter & {[key: string]: unknown};
223
+ /** Tags, normalized. */
224
+ readonly tags: Tag[];
225
+ };
226
+ /**
227
+ * @returns The edit URL that's directly plugged into metadata.
228
+ */
229
+ export type EditUrlFunction = (editUrlParams: {
230
+ /**
231
+ * The root content directory containing this post file, relative to the
232
+ * site path. Usually the same as `options.path` but can be localized
233
+ */
234
+ blogDirPath: string;
235
+ /** Path to this post file, relative to `blogDirPath`. */
236
+ blogPath: string;
237
+ /** @see {@link BlogPostMetadata.permalink} */
238
+ permalink: string;
239
+ /** Locale name. */
240
+ locale: string;
241
+ }) => string | undefined;
242
+
243
+ export type FeedType = 'rss' | 'atom' | 'json';
244
+ /**
245
+ * Normalized feed options used within code.
246
+ */
247
+ export type FeedOptions = {
248
+ /** If `null`, no feed is generated. */
249
+ type?: FeedType[] | null;
250
+ /** Title of generated feed. */
251
+ title?: string;
252
+ /** Description of generated feed. */
253
+ description?: string;
254
+ /** Copyright notice. Required because the feed library marked it that. */
255
+ copyright: string;
256
+ /** Language of the feed. */
257
+ language?: string;
258
+ };
259
+
260
+ /**
261
+ * Duplicate from ngryman/reading-time to keep stability of API.
262
+ */
263
+ type ReadingTimeOptions = {
264
+ wordsPerMinute?: number;
265
+ /**
266
+ * @param char The character to be matched.
267
+ * @returns `true` if this character is a word bound.
268
+ */
269
+ wordBound?: (char: string) => boolean;
270
+ };
271
+
272
+ /**
273
+ * Represents the default reading time implementation.
274
+ * @returns The reading time directly plugged into metadata.
275
+ */
276
+ export type ReadingTimeFunction = (params: {
277
+ /** Markdown content. */
278
+ content: string;
279
+ /** Front matter. */
280
+ frontMatter?: BlogPostFrontMatter & {[key: string]: unknown};
281
+ /** Options accepted by ngryman/reading-time. */
282
+ options?: ReadingTimeOptions;
283
+ }) => number;
284
+
285
+ /**
286
+ * @returns The reading time directly plugged into metadata. `undefined` to
287
+ * hide reading time for a specific post.
288
+ */
289
+ export type ReadingTimeFunctionOption = (
290
+ /**
291
+ * The `options` is not provided by the caller; the user can inject their
292
+ * own option values into `defaultReadingTime`
293
+ */
294
+ params: Required<Omit<Parameters<ReadingTimeFunction>[0], 'options'>> & {
295
+ /**
296
+ * The default reading time implementation from ngryman/reading-time.
297
+ */
298
+ defaultReadingTime: ReadingTimeFunction;
299
+ },
300
+ ) => number | undefined;
301
+ /**
302
+ * Plugin options after normalization.
303
+ */
304
+ export type PluginOptions = MDXOptions & {
305
+ /** Plugin ID. */
306
+ id?: string;
307
+ /**
308
+ * Path to the blog content directory on the file system, relative to site
309
+ * directory.
310
+ */
311
+ path: string;
312
+ /**
313
+ * URL route for the blog section of your site. **DO NOT** include a
314
+ * trailing slash. Use `/` to put the blog at root path.
315
+ */
316
+ routeBasePath: string;
317
+ /**
318
+ * URL route for the tags section of your blog. Will be appended to
319
+ * `routeBasePath`. **DO NOT** include a trailing slash.
320
+ */
321
+ tagsBasePath: string;
322
+ /**
323
+ * URL route for the archive section of your blog. Will be appended to
324
+ * `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to
325
+ * disable generation of archive.
326
+ */
327
+ archiveBasePath: string | null;
328
+ /**
329
+ * Array of glob patterns matching Markdown files to be built, relative to
330
+ * the content path.
331
+ */
332
+ include: string[];
333
+ /**
334
+ * Array of glob patterns matching Markdown files to be excluded. Serves as
335
+ * refinement based on the `include` option.
336
+ */
337
+ exclude: string[];
338
+ /**
339
+ * Number of posts to show per page in the listing page. Use `'ALL'` to
340
+ * display all posts on one listing page.
341
+ */
342
+ postsPerPage: number | 'ALL';
343
+ /** Root component of the blog listing page. */
344
+ blogListComponent: string;
345
+ /** Root component of each blog post page. */
346
+ blogPostComponent: string;
347
+ /** Root component of the tags list page. */
348
+ blogTagsListComponent: string;
349
+ /** Root component of the "posts containing tag" page. */
350
+ blogTagsPostsComponent: string;
351
+ /** Root component of the blog archive page. */
352
+ blogArchiveComponent: string;
353
+ /** Blog page title for better SEO. */
354
+ blogTitle: string;
355
+ /** Blog page meta description for better SEO. */
356
+ blogDescription: string;
357
+ /**
358
+ * Number of blog post elements to show in the blog sidebar. `'ALL'` to show
359
+ * all blog posts; `0` to disable.
360
+ */
361
+ blogSidebarCount: number | 'ALL';
362
+ /** Title of the blog sidebar. */
363
+ blogSidebarTitle: string;
364
+ /** Truncate marker marking where the summary ends. */
365
+ truncateMarker: RegExp;
366
+ /** Show estimated reading time for the blog post. */
367
+ showReadingTime: boolean;
368
+ /** Blog feed. */
369
+ feedOptions: FeedOptions;
370
+ /**
371
+ * Base URL to edit your site. The final URL is computed by `editUrl +
372
+ * relativePostPath`. Using a function allows more nuanced control for each
373
+ * file. Omitting this variable entirely will disable edit links.
374
+ */
375
+ editUrl?: string | EditUrlFunction;
376
+ /**
377
+ * The edit URL will target the localized file, instead of the original
378
+ * unlocalized file. Ignored when `editUrl` is a function.
379
+ */
380
+ editLocalizedFiles?: boolean;
381
+ /** Path to the authors map file, relative to the blog content directory. */
382
+ authorsMapPath: string;
383
+ /** A callback to customize the reading time number displayed. */
384
+ readingTime: ReadingTimeFunctionOption;
385
+ /** Governs the direction of blog post sorting. */
386
+ sortPosts: 'ascending' | 'descending';
387
+ };
388
+
389
+ /**
390
+ * Feed options, as provided by user config. `type` accepts `all` as shortcut
391
+ */
392
+ export type UserFeedOptions = Overwrite<
393
+ Partial<FeedOptions>,
394
+ {
395
+ /** Type of feed to be generated. Use `null` to disable generation. */
396
+ type?: FeedOptions['type'] | 'all' | FeedType;
397
+ }
398
+ >;
399
+ /**
400
+ * Options as provided in the user config (before normalization)
401
+ */
402
+ export type Options = Overwrite<
403
+ Partial<PluginOptions>,
404
+ {
405
+ /** Blog feed. */
406
+ feedOptions?: UserFeedOptions;
407
+ }
408
+ >;
409
+
410
+ export type BlogSidebar = {
411
+ title: string;
412
+ items: {title: string; permalink: string}[];
413
+ };
414
+
415
+ export type BlogContent = {
416
+ blogSidebarTitle: string;
417
+ blogPosts: BlogPost[];
418
+ blogListPaginated: BlogPaginated[];
419
+ blogTags: BlogTags;
420
+ blogTagsListPath: string;
421
+ };
422
+
423
+ export type BlogTags = {
424
+ [permalink: string]: BlogTag;
425
+ };
426
+
427
+ export type BlogTag = Tag & {
428
+ /** Blog post permalinks. */
429
+ items: string[];
430
+ pages: BlogPaginated[];
431
+ };
432
+
433
+ export type BlogPost = {
434
+ id: string;
435
+ metadata: BlogPostMetadata;
436
+ content: string;
437
+ };
438
+
439
+ export type BlogPaginatedMetadata = {
440
+ /** Title of the entire blog. */
441
+ readonly blogTitle: string;
442
+ /** Blog description. */
443
+ readonly blogDescription: string;
444
+ /** Permalink to the next list page. */
445
+ readonly nextPage?: string;
446
+ /** Permalink of the current page. */
447
+ readonly permalink: string;
448
+ /** Permalink to the previous list page. */
449
+ readonly previousPage?: string;
450
+ /** Index of the current page, 1-based. */
451
+ readonly page: number;
452
+ /** Posts displayed on each list page. */
453
+ readonly postsPerPage: number;
454
+ /** Total number of posts in the entire blog. */
455
+ readonly totalCount: number;
456
+ /** Total number of list pages. */
457
+ readonly totalPages: number;
458
+ };
459
+
460
+ export type BlogPaginated = {
461
+ metadata: BlogPaginatedMetadata;
462
+ /** Blog post permalinks. */
463
+ items: string[];
464
+ };
465
+
466
+ type PropBlogPostMetadata = Overwrite<
467
+ BlogPostMetadata,
468
+ {
469
+ /** The publish date of the post. Serialized from the `Date` object. */
470
+ date: string;
471
+ }
472
+ >;
473
+
474
+ export type PropBlogPostContent = LoadedMDXContent<
475
+ BlogPostFrontMatter,
476
+ PropBlogPostMetadata,
477
+ Assets
478
+ >;
479
+
480
+ export default function pluginContentBlog(
481
+ context: LoadContext,
482
+ options: PluginOptions,
483
+ ): Promise<Plugin<BlogContent>>;
484
+ }
485
+
486
+ declare module '@theme/BlogPostPage' {
487
+ import type {
488
+ BlogPostFrontMatter,
489
+ BlogSidebar,
490
+ PropBlogPostContent,
491
+ } from '@docusaurus/plugin-content-blog';
492
+
493
+ export type FrontMatter = BlogPostFrontMatter;
494
+
495
+ export type Content = PropBlogPostContent;
496
+
497
+ export interface Props {
498
+ /** Blog sidebar. */
499
+ readonly sidebar: BlogSidebar;
500
+ /** Content of this post as an MDX component, with useful metadata. */
501
+ readonly content: Content;
502
+ }
503
+
504
+ export default function BlogPostPage(props: Props): JSX.Element;
505
+ }
506
+
507
+ declare module '@theme/BlogPostPage/Metadata' {
508
+ export default function BlogPostPageMetadata(): JSX.Element;
509
+ }
510
+
511
+ declare module '@theme/BlogListPage' {
512
+ import type {Content} from '@theme/BlogPostPage';
513
+ import type {
514
+ BlogSidebar,
515
+ BlogPaginatedMetadata,
516
+ } from '@docusaurus/plugin-content-blog';
517
+
518
+ export interface Props {
519
+ /** Blog sidebar. */
520
+ readonly sidebar: BlogSidebar;
521
+ /** Metadata of the current listing page. */
522
+ readonly metadata: BlogPaginatedMetadata;
523
+ /**
524
+ * Array of blog posts included on this page. Every post's metadata is also
525
+ * available.
526
+ */
527
+ readonly items: readonly {readonly content: Content}[];
528
+ }
529
+
530
+ export default function BlogListPage(props: Props): JSX.Element;
531
+ }
532
+
533
+ declare module '@theme/BlogTagsListPage' {
534
+ import type {BlogSidebar} from '@docusaurus/plugin-content-blog';
535
+ import type {TagsListItem} from '@docusaurus/utils';
536
+
537
+ export interface Props {
538
+ /** Blog sidebar. */
539
+ readonly sidebar: BlogSidebar;
540
+ /** All tags declared in this blog. */
541
+ readonly tags: TagsListItem[];
542
+ }
543
+
544
+ export default function BlogTagsListPage(props: Props): JSX.Element;
545
+ }
546
+
547
+ declare module '@theme/BlogTagsPostsPage' {
548
+ import type {Content} from '@theme/BlogPostPage';
549
+ import type {
550
+ BlogSidebar,
551
+ BlogPaginatedMetadata,
552
+ } from '@docusaurus/plugin-content-blog';
553
+ import type {TagModule} from '@docusaurus/utils';
554
+
555
+ export interface Props {
556
+ /** Blog sidebar. */
557
+ readonly sidebar: BlogSidebar;
558
+ /** Metadata of this tag. */
559
+ readonly tag: TagModule;
560
+ /** Looks exactly the same as the posts list page */
561
+ readonly listMetadata: BlogPaginatedMetadata;
562
+ /**
563
+ * Array of blog posts included on this page. Every post's metadata is also
564
+ * available.
565
+ */
566
+ readonly items: readonly {readonly content: Content}[];
567
+ }
568
+
569
+ export default function BlogTagsPostsPage(props: Props): JSX.Element;
570
+ }
571
+
572
+ declare module '@theme/BlogArchivePage' {
573
+ import type {Content} from '@theme/BlogPostPage';
574
+
575
+ /** We may add extra metadata or prune some metadata from here */
576
+ export type ArchiveBlogPost = Content;
577
+
578
+ export interface Props {
579
+ /** The entirety of the blog's data. */
580
+ readonly archive: {
581
+ /** All posts. Can select any useful data/metadata to render. */
582
+ readonly blogPosts: readonly ArchiveBlogPost[];
583
+ };
584
+ }
585
+
586
+ export default function BlogArchivePage(props: Props): JSX.Element;
587
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import visit from 'unist-util-visit';
9
+ import {simpleHash} from '@docusaurus/utils';
10
+ import type {Transformer} from 'unified';
11
+ import type {FootnoteReference, FootnoteDefinition} from 'mdast';
12
+
13
+ /**
14
+ * In the blog list view, each post will be compiled separately. However, they
15
+ * may use the same footnote IDs. This leads to duplicated DOM IDs and inability
16
+ * to navigate to footnote references. This plugin fixes it by appending a
17
+ * unique hash to each reference/definition.
18
+ */
19
+ export default function plugin(): Transformer {
20
+ return (root, vfile) => {
21
+ const suffix = `-${simpleHash(vfile.path!, 6)}`;
22
+ visit(root, 'footnoteReference', (node: FootnoteReference) => {
23
+ node.identifier += suffix;
24
+ });
25
+ visit(root, 'footnoteDefinition', (node: FootnoteDefinition) => {
26
+ node.identifier += suffix;
27
+ });
28
+ };
29
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import type {TranslationFileContent, TranslationFile} from '@docusaurus/types';
9
+ import type {
10
+ PluginOptions,
11
+ BlogContent,
12
+ BlogPaginated,
13
+ } from '@docusaurus/plugin-content-blog';
14
+
15
+ function translateListPage(
16
+ blogListPaginated: BlogPaginated[],
17
+ translations: TranslationFileContent,
18
+ ) {
19
+ return blogListPaginated.map((page) => {
20
+ const {items, metadata} = page;
21
+ return {
22
+ items,
23
+ metadata: {
24
+ ...metadata,
25
+ blogTitle: translations.title?.message ?? page.metadata.blogTitle,
26
+ blogDescription:
27
+ translations.description?.message ?? page.metadata.blogDescription,
28
+ },
29
+ };
30
+ });
31
+ }
32
+
33
+ export function getTranslationFiles(options: PluginOptions): TranslationFile[] {
34
+ return [
35
+ {
36
+ path: 'options',
37
+ content: {
38
+ title: {
39
+ message: options.blogTitle,
40
+ description: 'The title for the blog used in SEO',
41
+ },
42
+ description: {
43
+ message: options.blogDescription,
44
+ description: 'The description for the blog used in SEO',
45
+ },
46
+ 'sidebar.title': {
47
+ message: options.blogSidebarTitle,
48
+ description: 'The label for the left sidebar',
49
+ },
50
+ },
51
+ },
52
+ ];
53
+ }
54
+
55
+ export function translateContent(
56
+ content: BlogContent,
57
+ translationFiles: TranslationFile[],
58
+ ): BlogContent {
59
+ const {content: optionsTranslations} = translationFiles[0]!;
60
+ return {
61
+ ...content,
62
+ blogSidebarTitle:
63
+ optionsTranslations['sidebar.title']?.message ?? content.blogSidebarTitle,
64
+ blogListPaginated: translateListPage(
65
+ content.blogListPaginated,
66
+ optionsTranslations,
67
+ ),
68
+ };
69
+ }