@docusaurus/plugin-content-blog 2.0.0-beta.17 → 2.0.0-beta.18

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