@docusaurus/plugin-content-blog 2.0.0-beta.16 → 2.0.0-beta.19

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