@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.
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const tslib_1 = require("tslib");
10
+ const unist_util_visit_1 = tslib_1.__importDefault(require("unist-util-visit"));
11
+ const utils_1 = require("@docusaurus/utils");
12
+ /**
13
+ * In the blog list view, each post will be compiled separately. However, they
14
+ * may use the same footnote IDs. This leads to duplicated DOM IDs and inability
15
+ * to navigate to footnote references. This plugin fixes it by appending a
16
+ * unique hash to each reference/definition.
17
+ */
18
+ function plugin() {
19
+ return (root, vfile) => {
20
+ const suffix = `-${(0, utils_1.simpleHash)(vfile.path, 6)}`;
21
+ (0, unist_util_visit_1.default)(root, 'footnoteReference', (node) => {
22
+ node.identifier += suffix;
23
+ });
24
+ (0, unist_util_visit_1.default)(root, 'footnoteDefinition', (node) => {
25
+ node.identifier += suffix;
26
+ });
27
+ };
28
+ }
29
+ exports.default = plugin;
@@ -4,8 +4,7 @@
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
- import type { BlogContent } from './types';
8
- import type { TranslationFiles } from '@docusaurus/types';
9
- import type { PluginOptions } from '@docusaurus/plugin-content-blog';
10
- export declare function getTranslationFiles(options: PluginOptions): TranslationFiles;
11
- export declare function translateContent(content: BlogContent, translationFiles: TranslationFiles): BlogContent;
7
+ import type { TranslationFile } from '@docusaurus/types';
8
+ import type { PluginOptions, BlogContent } from '@docusaurus/plugin-content-blog';
9
+ export declare function getTranslationFiles(options: PluginOptions): TranslationFile[];
10
+ export declare function translateContent(content: BlogContent, translationFiles: TranslationFile[]): BlogContent;
@@ -14,8 +14,8 @@ function translateListPage(blogListPaginated, translations) {
14
14
  items,
15
15
  metadata: {
16
16
  ...metadata,
17
- blogTitle: translations.title.message,
18
- blogDescription: translations.description.message,
17
+ blogTitle: translations.title?.message ?? page.metadata.blogTitle,
18
+ blogDescription: translations.description?.message ?? page.metadata.blogDescription,
19
19
  },
20
20
  };
21
21
  });
@@ -43,10 +43,10 @@ function getTranslationFiles(options) {
43
43
  }
44
44
  exports.getTranslationFiles = getTranslationFiles;
45
45
  function translateContent(content, translationFiles) {
46
- const [{ content: optionsTranslations }] = translationFiles;
46
+ const { content: optionsTranslations } = translationFiles[0];
47
47
  return {
48
48
  ...content,
49
- blogSidebarTitle: optionsTranslations['sidebar.title'].message,
49
+ blogSidebarTitle: optionsTranslations['sidebar.title']?.message ?? content.blogSidebarTitle,
50
50
  blogListPaginated: translateListPage(content.blogListPaginated, optionsTranslations),
51
51
  };
52
52
  }
package/lib/types.d.ts CHANGED
@@ -4,84 +4,15 @@
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
- import type { Tag } from '@docusaurus/utils';
8
- import type { BrokenMarkdownLink, ContentPaths } from '@docusaurus/utils/lib/markdownLinks';
9
- import type { BlogPostFrontMatter, Author } from '@docusaurus/plugin-content-blog';
7
+ import type { BrokenMarkdownLink, ContentPaths } from '@docusaurus/utils';
10
8
  export declare type BlogContentPaths = ContentPaths;
11
- export interface BlogContent {
12
- blogSidebarTitle: string;
13
- blogPosts: BlogPost[];
14
- blogListPaginated: BlogPaginated[];
15
- blogTags: BlogTags;
16
- blogTagsListPath: string | null;
17
- }
18
- export interface BlogTags {
19
- [tagKey: string]: BlogTag;
20
- }
21
- export interface BlogTag {
22
- name: string;
23
- items: string[];
24
- permalink: string;
25
- pages: BlogPaginated[];
26
- }
27
- export interface BlogPost {
28
- id: string;
29
- metadata: MetaData;
30
- content: string;
31
- }
32
- export interface BlogPaginatedMetadata {
33
- permalink: string;
34
- page: number;
35
- postsPerPage: number;
36
- totalPages: number;
37
- totalCount: number;
38
- previousPage: string | null;
39
- nextPage: string | null;
40
- blogTitle: string;
41
- blogDescription: string;
42
- }
43
- export interface BlogPaginated {
44
- metadata: BlogPaginatedMetadata;
45
- items: string[];
46
- }
47
- export interface MetaData {
48
- permalink: string;
49
- source: string;
50
- description: string;
51
- date: Date;
52
- formattedDate: string;
53
- tags: Tag[];
54
- title: string;
55
- readingTime?: number;
56
- prevItem?: Paginator;
57
- nextItem?: Paginator;
58
- truncated: boolean;
59
- editUrl?: string;
60
- authors: Author[];
61
- frontMatter: BlogPostFrontMatter & Record<string, unknown>;
62
- }
63
- export interface Paginator {
64
- title: string;
65
- permalink: string;
66
- }
67
- export interface BlogItemsToMetadata {
68
- [key: string]: MetaData;
69
- }
70
- export interface TagsModule {
71
- [key: string]: TagModule;
72
- }
73
- export interface TagModule {
74
- allTagsPath: string;
75
- slug: string;
76
- name: string;
77
- count: number;
78
- permalink: string;
79
- }
80
9
  export declare type BlogBrokenMarkdownLink = BrokenMarkdownLink<BlogContentPaths>;
81
10
  export declare type BlogMarkdownLoaderOptions = {
82
11
  siteDir: string;
83
12
  contentPaths: BlogContentPaths;
84
13
  truncateMarker: RegExp;
85
- sourceToPermalink: Record<string, string>;
14
+ sourceToPermalink: {
15
+ [aliasedPath: string]: string;
16
+ };
86
17
  onBrokenMarkdownLink: (brokenMarkdownLink: BlogBrokenMarkdownLink) => void;
87
18
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/plugin-content-blog",
3
- "version": "2.0.0-beta.16",
3
+ "version": "2.0.0-beta.19",
4
4
  "description": "Blog plugin for Docusaurus.",
5
5
  "main": "lib/index.js",
6
6
  "types": "src/plugin-content-blog.d.ts",
@@ -18,24 +18,25 @@
18
18
  },
19
19
  "license": "MIT",
20
20
  "dependencies": {
21
- "@docusaurus/core": "2.0.0-beta.16",
22
- "@docusaurus/logger": "2.0.0-beta.16",
23
- "@docusaurus/mdx-loader": "2.0.0-beta.16",
24
- "@docusaurus/utils": "2.0.0-beta.16",
25
- "@docusaurus/utils-common": "2.0.0-beta.16",
26
- "@docusaurus/utils-validation": "2.0.0-beta.16",
21
+ "@docusaurus/core": "2.0.0-beta.19",
22
+ "@docusaurus/logger": "2.0.0-beta.19",
23
+ "@docusaurus/mdx-loader": "2.0.0-beta.19",
24
+ "@docusaurus/utils": "2.0.0-beta.19",
25
+ "@docusaurus/utils-common": "2.0.0-beta.19",
26
+ "@docusaurus/utils-validation": "2.0.0-beta.19",
27
27
  "cheerio": "^1.0.0-rc.10",
28
28
  "feed": "^4.2.2",
29
- "fs-extra": "^10.0.1",
29
+ "fs-extra": "^10.1.0",
30
30
  "lodash": "^4.17.21",
31
31
  "reading-time": "^1.5.0",
32
32
  "remark-admonitions": "^1.2.1",
33
- "tslib": "^2.3.1",
33
+ "tslib": "^2.4.0",
34
+ "unist-util-visit": "^2.0.3",
34
35
  "utility-types": "^3.10.0",
35
- "webpack": "^5.69.1"
36
+ "webpack": "^5.72.0"
36
37
  },
37
38
  "devDependencies": {
38
- "@docusaurus/types": "2.0.0-beta.16",
39
+ "@docusaurus/types": "2.0.0-beta.19",
39
40
  "escape-string-regexp": "^4.0.0"
40
41
  },
41
42
  "peerDependencies": {
@@ -45,5 +46,5 @@
45
46
  "engines": {
46
47
  "node": ">=14"
47
48
  },
48
- "gitHead": "eb43c4d4f95a4fb97dc9bb9dc615413e0dc2e1e7"
49
+ "gitHead": "a71e60a49cce93c1006ef10c41ac03187f057102"
49
50
  }
package/src/authors.ts CHANGED
@@ -15,21 +15,33 @@ import type {
15
15
  BlogPostFrontMatterAuthors,
16
16
  } from '@docusaurus/plugin-content-blog';
17
17
 
18
- export type AuthorsMap = Record<string, Author>;
19
-
20
- const AuthorsMapSchema = Joi.object<AuthorsMap>().pattern(
21
- Joi.string(),
22
- Joi.object({
23
- name: Joi.string(),
24
- url: URISchema,
25
- imageURL: URISchema,
26
- title: Joi.string(),
27
- })
28
- .rename('image_url', 'imageURL')
29
- .or('name', 'imageURL')
30
- .unknown()
31
- .required(),
32
- );
18
+ export type AuthorsMap = {[authorKey: string]: Author};
19
+
20
+ const AuthorsMapSchema = Joi.object<AuthorsMap>()
21
+ .pattern(
22
+ Joi.string(),
23
+ Joi.object({
24
+ name: Joi.string(),
25
+ url: URISchema,
26
+ imageURL: URISchema,
27
+ title: Joi.string(),
28
+ email: Joi.string(),
29
+ })
30
+ .rename('image_url', 'imageURL')
31
+ .or('name', 'imageURL')
32
+ .unknown()
33
+ .required()
34
+ .messages({
35
+ 'object.base':
36
+ '{#label} should be an author object containing properties like name, title, and imageURL.',
37
+ 'any.required':
38
+ '{#label} cannot be undefined. It should be an author object containing properties like name, title, and imageURL.',
39
+ }),
40
+ )
41
+ .messages({
42
+ 'object.base':
43
+ "The authors map file should contain an object where each entry contains an author key and the corresponding author's data.",
44
+ });
33
45
 
34
46
  export function validateAuthorsMap(content: unknown): AuthorsMap {
35
47
  return Joi.attempt(content, AuthorsMapSchema);
@@ -58,7 +70,7 @@ type AuthorsParam = {
58
70
  // We may want to deprecate those in favor of using only frontMatter.authors
59
71
  function getFrontMatterAuthorLegacy(
60
72
  frontMatter: BlogPostFrontMatter,
61
- ): BlogPostFrontMatterAuthor | undefined {
73
+ ): Author | undefined {
62
74
  const name = frontMatter.author;
63
75
  const title = frontMatter.author_title ?? frontMatter.authorTitle;
64
76
  const url = frontMatter.author_url ?? frontMatter.authorURL;
@@ -80,7 +92,7 @@ function normalizeFrontMatterAuthors(
80
92
  frontMatterAuthors: BlogPostFrontMatterAuthors = [],
81
93
  ): BlogPostFrontMatterAuthor[] {
82
94
  function normalizeAuthor(
83
- authorInput: string | BlogPostFrontMatterAuthor,
95
+ authorInput: string | Author,
84
96
  ): BlogPostFrontMatterAuthor {
85
97
  if (typeof authorInput === 'string') {
86
98
  // Technically, we could allow users to provide an author's name here, but
package/src/blogUtils.ts CHANGED
@@ -9,13 +9,7 @@ import fs from 'fs-extra';
9
9
  import path from 'path';
10
10
  import readingTime from 'reading-time';
11
11
  import _ from 'lodash';
12
- import type {
13
- BlogPost,
14
- BlogContentPaths,
15
- BlogMarkdownLoaderOptions,
16
- BlogTags,
17
- BlogPaginated,
18
- } from './types';
12
+ import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types';
19
13
  import {
20
14
  parseMarkdownString,
21
15
  normalizeUrl,
@@ -31,21 +25,24 @@ import {
31
25
  getContentPathList,
32
26
  } from '@docusaurus/utils';
33
27
  import type {LoadContext} from '@docusaurus/types';
34
- import {validateBlogPostFrontMatter} from './blogFrontMatter';
28
+ import {validateBlogPostFrontMatter} from './frontMatter';
35
29
  import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
36
30
  import logger from '@docusaurus/logger';
37
31
  import type {
38
32
  PluginOptions,
39
33
  ReadingTimeFunction,
34
+ BlogPost,
35
+ BlogTags,
36
+ BlogPaginated,
40
37
  } from '@docusaurus/plugin-content-blog';
41
38
 
42
39
  export function truncate(fileString: string, truncateMarker: RegExp): string {
43
40
  return fileString.split(truncateMarker, 1).shift()!;
44
41
  }
45
42
 
46
- export function getSourceToPermalink(
47
- blogPosts: BlogPost[],
48
- ): Record<string, string> {
43
+ export function getSourceToPermalink(blogPosts: BlogPost[]): {
44
+ [aliasedPath: string]: string;
45
+ } {
49
46
  return Object.fromEntries(
50
47
  blogPosts.map(({metadata: {source, permalink}}) => [source, permalink]),
51
48
  );
@@ -72,7 +69,9 @@ export function paginateBlogPosts({
72
69
  const pages: BlogPaginated[] = [];
73
70
 
74
71
  function permalink(page: number) {
75
- return page > 0 ? `${basePageUrl}/page/${page + 1}` : basePageUrl;
72
+ return page > 0
73
+ ? normalizeUrl([basePageUrl, `page/${page + 1}`])
74
+ : basePageUrl;
76
75
  }
77
76
 
78
77
  for (let page = 0; page < numberOfPages; page += 1) {
@@ -86,8 +85,8 @@ export function paginateBlogPosts({
86
85
  postsPerPage,
87
86
  totalPages: numberOfPages,
88
87
  totalCount,
89
- previousPage: page !== 0 ? permalink(page - 1) : null,
90
- nextPage: page < numberOfPages - 1 ? permalink(page + 1) : null,
88
+ previousPage: page !== 0 ? permalink(page - 1) : undefined,
89
+ nextPage: page < numberOfPages - 1 ? permalink(page + 1) : undefined,
91
90
  blogDescription,
92
91
  blogTitle,
93
92
  },
@@ -112,7 +111,7 @@ export function getBlogTags({
112
111
  );
113
112
 
114
113
  return _.mapValues(groups, ({tag, items: tagBlogPosts}) => ({
115
- name: tag.label,
114
+ label: tag.label,
116
115
  items: tagBlogPosts.map((item) => item.id),
117
116
  permalink: tag.permalink,
118
117
  pages: paginateBlogPosts({
@@ -139,23 +138,28 @@ export function parseBlogFileName(
139
138
  if (dateFilenameMatch) {
140
139
  const {folder, text, date: dateString} = dateFilenameMatch.groups!;
141
140
  // Always treat dates as UTC by adding the `Z`
142
- const date = new Date(`${dateString}Z`);
143
- const slugDate = dateString.replace(/-/g, '/');
144
- const slug = `/${slugDate}/${folder}${text}`;
145
- return {date, text, slug};
141
+ const date = new Date(`${dateString!}Z`);
142
+ const slugDate = dateString!.replace(/-/g, '/');
143
+ const slug = `/${slugDate}/${folder!}${text!}`;
144
+ return {date, text: text!, slug};
146
145
  }
147
146
  const text = blogSourceRelative.replace(/(?:\/index)?\.mdx?$/, '');
148
147
  const slug = `/${text}`;
149
148
  return {date: undefined, text, slug};
150
149
  }
151
150
 
152
- function formatBlogPostDate(locale: string, date: Date): string {
151
+ function formatBlogPostDate(
152
+ locale: string,
153
+ date: Date,
154
+ calendar: string,
155
+ ): string {
153
156
  try {
154
157
  return new Intl.DateTimeFormat(locale, {
155
158
  day: 'numeric',
156
159
  month: 'long',
157
160
  year: 'numeric',
158
161
  timeZone: 'UTC',
162
+ calendar,
159
163
  }).format(date);
160
164
  } catch (err) {
161
165
  logger.error`Can't format blog post date "${String(date)}"`;
@@ -245,13 +249,17 @@ async function processBlogSourceFile(
245
249
  });
246
250
  return result.date;
247
251
  } catch (err) {
248
- logger.error(err);
252
+ logger.warn(err);
249
253
  return (await fs.stat(blogSourceAbsolute)).birthtime;
250
254
  }
251
255
  }
252
256
 
253
257
  const date = await getDate();
254
- const formattedDate = formatBlogPostDate(i18n.currentLocale, date);
258
+ const formattedDate = formatBlogPostDate(
259
+ i18n.currentLocale,
260
+ date,
261
+ i18n.localeConfigs[i18n.currentLocale]!.calendar,
262
+ );
255
263
 
256
264
  const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text;
257
265
  const description = frontMatter.description ?? excerpt ?? '';
package/src/deps.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  declare module 'remark-admonitions' {
9
- type Options = Record<string, unknown>;
9
+ type Options = {[key: string]: unknown};
10
10
 
11
11
  const plugin: (options?: Options) => void;
12
12
  export = plugin;
package/src/feed.ts CHANGED
@@ -6,14 +6,8 @@
6
6
  */
7
7
 
8
8
  import {Feed, type Author as FeedAuthor, type Item as FeedItem} from 'feed';
9
- import type {BlogPost} from './types';
10
- import {
11
- normalizeUrl,
12
- posixPath,
13
- mapAsyncSequential,
14
- readOutputHTMLFile,
15
- } from '@docusaurus/utils';
16
- import cheerio from 'cheerio';
9
+ import {normalizeUrl, readOutputHTMLFile} from '@docusaurus/utils';
10
+ import {load as cheerioLoad} from 'cheerio';
17
11
  import type {DocusaurusConfig} from '@docusaurus/types';
18
12
  import path from 'path';
19
13
  import fs from 'fs-extra';
@@ -21,6 +15,7 @@ import type {
21
15
  FeedType,
22
16
  PluginOptions,
23
17
  Author,
18
+ BlogPost,
24
19
  } from '@docusaurus/plugin-content-blog';
25
20
  import {blogPostContainerID} from '@docusaurus/utils-common';
26
21
 
@@ -29,11 +24,13 @@ async function generateBlogFeed({
29
24
  options,
30
25
  siteConfig,
31
26
  outDir,
27
+ locale,
32
28
  }: {
33
29
  blogPosts: BlogPost[];
34
30
  options: PluginOptions;
35
31
  siteConfig: DocusaurusConfig;
36
32
  outDir: string;
33
+ locale: string;
37
34
  }): Promise<Feed | null> {
38
35
  if (!blogPosts.length) {
39
36
  return null;
@@ -43,65 +40,65 @@ async function generateBlogFeed({
43
40
  const {url: siteUrl, baseUrl, title, favicon} = siteConfig;
44
41
  const blogBaseUrl = normalizeUrl([siteUrl, baseUrl, routeBasePath]);
45
42
 
46
- const updated = blogPosts[0] && blogPosts[0].metadata.date;
43
+ const updated = blogPosts[0]?.metadata.date;
47
44
 
48
45
  const feed = new Feed({
49
46
  id: blogBaseUrl,
50
- title: feedOptions.title || `${title} Blog`,
47
+ title: feedOptions.title ?? `${title} Blog`,
51
48
  updated,
52
- language: feedOptions.language,
49
+ language: feedOptions.language ?? locale,
53
50
  link: blogBaseUrl,
54
- description: feedOptions.description || `${siteConfig.title} Blog`,
51
+ description: feedOptions.description ?? `${siteConfig.title} Blog`,
55
52
  favicon: favicon ? normalizeUrl([siteUrl, baseUrl, favicon]) : undefined,
56
53
  copyright: feedOptions.copyright,
57
54
  });
58
55
 
59
56
  function toFeedAuthor(author: Author): FeedAuthor {
60
- // TODO ask author emails?
61
- // RSS feed requires email to render authors
62
- return {name: author.name, link: author.url};
57
+ return {name: author.name, link: author.url, email: author.email};
63
58
  }
64
59
 
65
- await mapAsyncSequential(blogPosts, async (post) => {
66
- const {
67
- id,
68
- metadata: {
60
+ await Promise.all(
61
+ blogPosts.map(async (post) => {
62
+ const {
63
+ id,
64
+ metadata: {
65
+ title: metadataTitle,
66
+ permalink,
67
+ date,
68
+ description,
69
+ authors,
70
+ tags,
71
+ },
72
+ } = post;
73
+
74
+ const content = await readOutputHTMLFile(
75
+ permalink.replace(siteConfig.baseUrl, ''),
76
+ outDir,
77
+ siteConfig.trailingSlash,
78
+ );
79
+ const $ = cheerioLoad(content);
80
+
81
+ const feedItem: FeedItem = {
69
82
  title: metadataTitle,
70
- permalink,
83
+ id,
84
+ link: normalizeUrl([siteUrl, permalink]),
71
85
  date,
72
86
  description,
73
- authors,
74
- tags,
75
- },
76
- } = post;
77
-
78
- const content = await readOutputHTMLFile(
79
- permalink.replace(siteConfig.baseUrl, ''),
80
- outDir,
81
- siteConfig.trailingSlash,
82
- );
83
- const $ = cheerio.load(content);
84
-
85
- const feedItem: FeedItem = {
86
- title: metadataTitle,
87
- id,
88
- link: normalizeUrl([siteUrl, permalink]),
89
- date,
90
- description,
91
- // Atom feed demands the "term", while other feeds use "name"
92
- category: tags.map((tag) => ({name: tag.label, term: tag.label})),
93
- content: $(`#${blogPostContainerID}`).html()!,
94
- };
95
-
96
- // json1() method takes the first item of authors array
97
- // it causes an error when authors array is empty
98
- const feedItemAuthors = authors.map(toFeedAuthor);
99
- if (feedItemAuthors.length > 0) {
100
- feedItem.author = feedItemAuthors;
101
- }
102
-
103
- feed.addItem(feedItem);
104
- });
87
+ // Atom feed demands the "term", while other feeds use "name"
88
+ category: tags.map((tag) => ({name: tag.label, term: tag.label})),
89
+ content: $(`#${blogPostContainerID}`).html()!,
90
+ };
91
+
92
+ // json1() method takes the first item of authors array
93
+ // it causes an error when authors array is empty
94
+ const feedItemAuthors = authors.map(toFeedAuthor);
95
+ if (feedItemAuthors.length > 0) {
96
+ feedItem.author = feedItemAuthors;
97
+ }
98
+
99
+ return feedItem;
100
+ }),
101
+ ).then((items) => items.forEach(feed.addItem));
105
102
 
106
103
  return feed;
107
104
  }
@@ -128,10 +125,7 @@ async function createBlogFeedFile({
128
125
  }
129
126
  })();
130
127
  try {
131
- await fs.outputFile(
132
- posixPath(path.join(generatePath, feedPath)),
133
- feedContent,
134
- );
128
+ await fs.outputFile(path.join(generatePath, feedPath), feedContent);
135
129
  } catch (err) {
136
130
  throw new Error(`Generating ${feedType} feed failed: ${err}.`);
137
131
  }
@@ -142,17 +136,20 @@ export async function createBlogFeedFiles({
142
136
  options,
143
137
  siteConfig,
144
138
  outDir,
139
+ locale,
145
140
  }: {
146
141
  blogPosts: BlogPost[];
147
142
  options: PluginOptions;
148
143
  siteConfig: DocusaurusConfig;
149
144
  outDir: string;
145
+ locale: string;
150
146
  }): Promise<void> {
151
147
  const feed = await generateBlogFeed({
152
148
  blogPosts,
153
149
  options,
154
150
  siteConfig,
155
151
  outDir,
152
+ locale,
156
153
  });
157
154
 
158
155
  const feedTypes = options.feedOptions.type;
@@ -74,8 +74,8 @@ const BlogFrontMatterSchema = Joi.object<BlogPostFrontMatter>({
74
74
  '{#label} blog frontMatter field is deprecated. Please use {#alternative} instead.',
75
75
  });
76
76
 
77
- export function validateBlogPostFrontMatter(
78
- frontMatter: Record<string, unknown>,
79
- ): BlogPostFrontMatter {
77
+ export function validateBlogPostFrontMatter(frontMatter: {
78
+ [key: string]: unknown;
79
+ }): BlogPostFrontMatter {
80
80
  return validateFrontMatter(frontMatter, BlogFrontMatterSchema);
81
81
  }