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

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 (51) hide show
  1. package/lib/authors.d.ts +20 -0
  2. package/lib/authors.js +110 -0
  3. package/lib/blogFrontMatter.d.ts +1 -21
  4. package/lib/blogFrontMatter.js +31 -19
  5. package/lib/blogUtils.d.ts +24 -6
  6. package/lib/blogUtils.js +196 -143
  7. package/lib/feed.d.ts +15 -0
  8. package/lib/feed.js +99 -0
  9. package/lib/index.d.ts +4 -3
  10. package/lib/index.js +149 -163
  11. package/lib/markdownLoader.d.ts +3 -6
  12. package/lib/markdownLoader.js +5 -6
  13. package/lib/pluginOptionSchema.d.ts +3 -26
  14. package/lib/pluginOptionSchema.js +35 -10
  15. package/lib/translations.d.ts +11 -0
  16. package/lib/translations.js +53 -0
  17. package/lib/types.d.ts +10 -46
  18. package/package.json +21 -18
  19. package/src/authors.ts +153 -0
  20. package/src/blogFrontMatter.ts +44 -51
  21. package/src/blogUtils.ts +289 -195
  22. package/{types.d.ts → src/deps.d.ts} +0 -0
  23. package/src/feed.ts +170 -0
  24. package/src/index.ts +197 -194
  25. package/src/markdownLoader.ts +10 -15
  26. package/src/plugin-content-blog.d.ts +270 -0
  27. package/src/pluginOptionSchema.ts +41 -13
  28. package/src/translations.ts +64 -0
  29. package/src/types.ts +19 -53
  30. package/index.d.ts +0 -138
  31. package/lib/.tsbuildinfo +0 -1
  32. package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  33. package/src/__tests__/__fixtures__/website/blog/complex-slug.md +0 -7
  34. package/src/__tests__/__fixtures__/website/blog/date-matter.md +0 -5
  35. package/src/__tests__/__fixtures__/website/blog/draft.md +0 -6
  36. package/src/__tests__/__fixtures__/website/blog/heading-as-title.md +0 -5
  37. package/src/__tests__/__fixtures__/website/blog/simple-slug.md +0 -7
  38. package/src/__tests__/__fixtures__/website/blog-with-ref/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  39. package/src/__tests__/__fixtures__/website/blog-with-ref/post-with-broken-links.md +0 -11
  40. package/src/__tests__/__fixtures__/website/blog-with-ref/post.md +0 -5
  41. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  42. package/src/__tests__/__fixtures__/website-blog-without-date/blog/no date.md +0 -1
  43. package/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap +0 -76
  44. package/src/__tests__/__snapshots__/linkify.test.ts.snap +0 -24
  45. package/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap +0 -5
  46. package/src/__tests__/blogFrontMatter.test.ts +0 -317
  47. package/src/__tests__/generateBlogFeed.test.ts +0 -100
  48. package/src/__tests__/index.test.ts +0 -336
  49. package/src/__tests__/linkify.test.ts +0 -93
  50. package/src/__tests__/pluginOptionSchema.test.ts +0 -150
  51. package/tsconfig.json +0 -9
@@ -0,0 +1,270 @@
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 {RemarkAndRehypePluginOptions} from '@docusaurus/mdx-loader';
10
+ import type {FrontMatterTag} from '@docusaurus/utils';
11
+ import type {Overwrite} from 'utility-types';
12
+
13
+ export interface Assets {
14
+ image?: string;
15
+ authorsImageUrls: (string | undefined)[]; // Array of same size as the original MetaData.authors array
16
+ }
17
+
18
+ // We allow passing custom fields to authors, e.g., twitter
19
+ export interface Author extends Record<string, unknown> {
20
+ name?: string;
21
+ imageURL?: string;
22
+ url?: string;
23
+ title?: string;
24
+ email?: string;
25
+ }
26
+
27
+ export type BlogPostFrontMatter = {
28
+ id?: string;
29
+ title?: string;
30
+ description?: string;
31
+ tags?: FrontMatterTag[];
32
+ slug?: string;
33
+ draft?: boolean;
34
+ date?: Date | string; // Yaml automatically convert some string patterns as Date, but not all
35
+
36
+ authors?: BlogPostFrontMatterAuthors;
37
+
38
+ // We may want to deprecate those older author front matter fields later:
39
+ author?: string;
40
+ author_title?: string;
41
+ author_url?: string;
42
+ author_image_url?: string;
43
+
44
+ /** @deprecated */
45
+ authorTitle?: string;
46
+ /** @deprecated */
47
+ authorURL?: string;
48
+ /** @deprecated */
49
+ authorImageURL?: string;
50
+
51
+ image?: string;
52
+ keywords?: string[];
53
+ hide_table_of_contents?: boolean;
54
+ toc_min_heading_level?: number;
55
+ toc_max_heading_level?: number;
56
+ };
57
+
58
+ export type BlogPostFrontMatterAuthor = Record<string, unknown> & {
59
+ key?: string;
60
+ name?: string;
61
+ imageURL?: string;
62
+ url?: string;
63
+ title?: string;
64
+ };
65
+
66
+ // All the possible variants that the user can use for convenience
67
+ export type BlogPostFrontMatterAuthors =
68
+ | string
69
+ | BlogPostFrontMatterAuthor
70
+ | (string | BlogPostFrontMatterAuthor)[];
71
+
72
+ export type EditUrlFunction = (editUrlParams: {
73
+ blogDirPath: string;
74
+ blogPath: string;
75
+ permalink: string;
76
+ locale: string;
77
+ }) => string | undefined;
78
+
79
+ export type FeedType = 'rss' | 'atom' | 'json';
80
+ export type FeedOptions = {
81
+ type?: FeedType[] | null;
82
+ title?: string;
83
+ description?: string;
84
+ copyright: string;
85
+ language?: string;
86
+ };
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
+
93
+ // Duplicate from ngryman/reading-time to keep stability of API
94
+ type ReadingTimeOptions = {
95
+ wordsPerMinute?: number;
96
+ wordBound?: (char: string) => boolean;
97
+ };
98
+
99
+ export type ReadingTimeFunction = (params: {
100
+ content: string;
101
+ frontMatter?: BlogPostFrontMatter & Record<string, unknown>;
102
+ options?: ReadingTimeOptions;
103
+ }) => number;
104
+
105
+ export type ReadingTimeFunctionOption = (
106
+ params: Required<Omit<Parameters<ReadingTimeFunction>[0], 'options'>> & {
107
+ defaultReadingTime: ReadingTimeFunction;
108
+ },
109
+ ) => number | undefined;
110
+
111
+ export type PluginOptions = RemarkAndRehypePluginOptions & {
112
+ id?: string;
113
+ path: string;
114
+ routeBasePath: string;
115
+ tagsBasePath: string;
116
+ archiveBasePath: string | null;
117
+ include: string[];
118
+ exclude: string[];
119
+ postsPerPage: number | 'ALL';
120
+ blogListComponent: string;
121
+ blogPostComponent: string;
122
+ blogTagsListComponent: string;
123
+ blogTagsPostsComponent: string;
124
+ blogArchiveComponent: string;
125
+ blogTitle: string;
126
+ blogDescription: string;
127
+ blogSidebarCount: number | 'ALL';
128
+ blogSidebarTitle: string;
129
+ truncateMarker: RegExp;
130
+ showReadingTime: boolean;
131
+ feedOptions: {
132
+ type?: FeedType[] | null;
133
+ title?: string;
134
+ description?: string;
135
+ copyright: string;
136
+ language?: string;
137
+ };
138
+ editUrl?: string | EditUrlFunction;
139
+ editLocalizedFiles?: boolean;
140
+ admonitions: Record<string, unknown>;
141
+ authorsMapPath: string;
142
+ readingTime: ReadingTimeFunctionOption;
143
+ sortPosts: 'ascending' | 'descending';
144
+ };
145
+ // Options, as provided in the user config (before normalization)
146
+ export type Options = Overwrite<
147
+ Partial<PluginOptions>,
148
+ {feedOptions?: UserFeedOptions}
149
+ >;
150
+ }
151
+
152
+ declare module '@theme/BlogPostPage' {
153
+ import type {BlogSidebar} from '@theme/BlogSidebar';
154
+ import type {TOCItem} from '@docusaurus/types';
155
+ import type {
156
+ BlogPostFrontMatter,
157
+ Author,
158
+ Assets,
159
+ } from '@docusaurus/plugin-content-blog';
160
+
161
+ export type FrontMatter = BlogPostFrontMatter;
162
+
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
+ };
181
+
182
+ export type Content = {
183
+ readonly frontMatter: FrontMatter;
184
+ readonly assets: Assets;
185
+ readonly metadata: Metadata;
186
+ readonly toc: readonly TOCItem[];
187
+ (): JSX.Element;
188
+ };
189
+
190
+ export interface Props {
191
+ readonly sidebar: BlogSidebar;
192
+ readonly content: Content;
193
+ }
194
+
195
+ export default function BlogPostPage(props: Props): JSX.Element;
196
+ }
197
+
198
+ declare module '@theme/BlogListPage' {
199
+ import type {Content} from '@theme/BlogPostPage';
200
+ import type {BlogSidebar} from '@theme/BlogSidebar';
201
+
202
+ export type Metadata = {
203
+ readonly blogTitle: string;
204
+ readonly blogDescription: string;
205
+ readonly nextPage?: string;
206
+ readonly page: number;
207
+ readonly permalink: string;
208
+ readonly postsPerPage: number;
209
+ readonly previousPage?: string;
210
+ readonly totalCount: number;
211
+ readonly totalPages: number;
212
+ };
213
+
214
+ export interface Props {
215
+ readonly sidebar: BlogSidebar;
216
+ readonly metadata: Metadata;
217
+ readonly items: readonly {readonly content: Content}[];
218
+ }
219
+
220
+ export default function BlogListPage(props: Props): JSX.Element;
221
+ }
222
+
223
+ 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
+ };
233
+
234
+ export interface Props {
235
+ readonly sidebar: BlogSidebar;
236
+ readonly tags: Readonly<Record<string, Tag>>;
237
+ }
238
+
239
+ export default function BlogTagsListPage(props: Props): JSX.Element;
240
+ }
241
+
242
+ declare module '@theme/BlogTagsPostsPage' {
243
+ import type {BlogSidebar} from '@theme/BlogSidebar';
244
+ import type {Tag} from '@theme/BlogTagsListPage';
245
+ import type {Content} from '@theme/BlogPostPage';
246
+ import type {Metadata} from '@theme/BlogListPage';
247
+
248
+ export interface Props {
249
+ readonly sidebar: BlogSidebar;
250
+ readonly metadata: Tag;
251
+ readonly listMetadata: Metadata;
252
+ readonly items: readonly {readonly content: Content}[];
253
+ }
254
+
255
+ export default function BlogTagsPostsPage(props: Props): JSX.Element;
256
+ }
257
+
258
+ declare module '@theme/BlogArchivePage' {
259
+ import type {Content} from '@theme/BlogPostPage';
260
+
261
+ export type ArchiveBlogPost = Content;
262
+
263
+ export interface Props {
264
+ readonly archive: {
265
+ readonly blogPosts: readonly ArchiveBlogPost[];
266
+ };
267
+ }
268
+
269
+ export default function BlogArchivePage(props: Props): JSX.Element;
270
+ }
@@ -12,13 +12,15 @@ import {
12
12
  AdmonitionsSchema,
13
13
  URISchema,
14
14
  } from '@docusaurus/utils-validation';
15
+ import {GlobExcludeDefault} from '@docusaurus/utils';
16
+ import type {PluginOptions} from '@docusaurus/plugin-content-blog';
15
17
 
16
- export const DEFAULT_OPTIONS = {
17
- feedOptions: {type: ['rss', 'atom']},
18
+ export const DEFAULT_OPTIONS: PluginOptions = {
19
+ feedOptions: {type: ['rss', 'atom'], copyright: ''},
18
20
  beforeDefaultRehypePlugins: [],
19
21
  beforeDefaultRemarkPlugins: [],
20
22
  admonitions: {},
21
- truncateMarker: /<!--\s*(truncate)\s*-->/,
23
+ truncateMarker: /<!--\s*truncate\s*-->/,
22
24
  rehypePlugins: [],
23
25
  remarkPlugins: [],
24
26
  showReadingTime: true,
@@ -26,27 +28,38 @@ export const DEFAULT_OPTIONS = {
26
28
  blogTagsListComponent: '@theme/BlogTagsListPage',
27
29
  blogPostComponent: '@theme/BlogPostPage',
28
30
  blogListComponent: '@theme/BlogListPage',
31
+ blogArchiveComponent: '@theme/BlogArchivePage',
29
32
  blogDescription: 'Blog',
30
33
  blogTitle: 'Blog',
31
34
  blogSidebarCount: 5,
32
35
  blogSidebarTitle: 'Recent posts',
33
36
  postsPerPage: 10,
34
- include: ['*.md', '*.mdx'],
37
+ include: ['**/*.{md,mdx}'],
38
+ exclude: GlobExcludeDefault,
35
39
  routeBasePath: 'blog',
40
+ tagsBasePath: 'tags',
41
+ archiveBasePath: 'archive',
36
42
  path: 'blog',
37
43
  editLocalizedFiles: false,
44
+ authorsMapPath: 'authors.yml',
45
+ readingTime: ({content, defaultReadingTime}) => defaultReadingTime({content}),
46
+ sortPosts: 'descending',
38
47
  };
39
48
 
40
- export const PluginOptionSchema = Joi.object({
49
+ export const PluginOptionSchema = Joi.object<PluginOptions>({
41
50
  path: Joi.string().default(DEFAULT_OPTIONS.path),
51
+ archiveBasePath: Joi.string()
52
+ .default(DEFAULT_OPTIONS.archiveBasePath)
53
+ .allow(null),
42
54
  routeBasePath: Joi.string()
43
55
  // '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
44
56
  // .allow('')
45
57
  .default(DEFAULT_OPTIONS.routeBasePath),
58
+ tagsBasePath: Joi.string().default(DEFAULT_OPTIONS.tagsBasePath),
46
59
  include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
47
- postsPerPage: Joi.number()
48
- .integer()
49
- .min(1)
60
+ exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude),
61
+ postsPerPage: Joi.alternatives()
62
+ .try(Joi.equal('ALL').required(), Joi.number().integer().min(1).required())
50
63
  .default(DEFAULT_OPTIONS.postsPerPage),
51
64
  blogListComponent: Joi.string().default(DEFAULT_OPTIONS.blogListComponent),
52
65
  blogPostComponent: Joi.string().default(DEFAULT_OPTIONS.blogPostComponent),
@@ -56,12 +69,15 @@ export const PluginOptionSchema = Joi.object({
56
69
  blogTagsPostsComponent: Joi.string().default(
57
70
  DEFAULT_OPTIONS.blogTagsPostsComponent,
58
71
  ),
72
+ blogArchiveComponent: Joi.string().default(
73
+ DEFAULT_OPTIONS.blogArchiveComponent,
74
+ ),
59
75
  blogTitle: Joi.string().allow('').default(DEFAULT_OPTIONS.blogTitle),
60
76
  blogDescription: Joi.string()
61
77
  .allow('')
62
78
  .default(DEFAULT_OPTIONS.blogDescription),
63
79
  blogSidebarCount: Joi.alternatives()
64
- .try(Joi.equal('ALL').required(), Joi.number().required())
80
+ .try(Joi.equal('ALL').required(), Joi.number().integer().min(0).required())
65
81
  .default(DEFAULT_OPTIONS.blogSidebarCount),
66
82
  blogSidebarTitle: Joi.string().default(DEFAULT_OPTIONS.blogSidebarTitle),
67
83
  showReadingTime: Joi.bool().default(DEFAULT_OPTIONS.showReadingTime),
@@ -80,12 +96,12 @@ export const PluginOptionSchema = Joi.object({
80
96
  feedOptions: Joi.object({
81
97
  type: Joi.alternatives()
82
98
  .try(
83
- Joi.array().items(Joi.string()),
99
+ Joi.array().items(Joi.string().equal('rss', 'atom', 'json')),
84
100
  Joi.alternatives().conditional(
85
- Joi.string().equal('all', 'rss', 'atom'),
101
+ Joi.string().equal('all', 'rss', 'atom', 'json'),
86
102
  {
87
103
  then: Joi.custom((val) =>
88
- val === 'all' ? ['rss', 'atom'] : [val],
104
+ val === 'all' ? ['rss', 'atom', 'json'] : [val],
89
105
  ),
90
106
  },
91
107
  ),
@@ -94,7 +110,19 @@ export const PluginOptionSchema = Joi.object({
94
110
  .default(DEFAULT_OPTIONS.feedOptions.type),
95
111
  title: Joi.string().allow(''),
96
112
  description: Joi.string().allow(''),
97
- copyright: Joi.string(),
113
+ // only add default value when user actually wants a feed (type is not null)
114
+ copyright: Joi.when('type', {
115
+ is: Joi.any().valid(null),
116
+ then: Joi.string().optional(),
117
+ otherwise: Joi.string()
118
+ .allow('')
119
+ .default(DEFAULT_OPTIONS.feedOptions.copyright),
120
+ }),
98
121
  language: Joi.string(),
99
122
  }).default(DEFAULT_OPTIONS.feedOptions),
123
+ authorsMapPath: Joi.string().default(DEFAULT_OPTIONS.authorsMapPath),
124
+ readingTime: Joi.function().default(() => DEFAULT_OPTIONS.readingTime),
125
+ sortPosts: Joi.string()
126
+ .valid('descending', 'ascending')
127
+ .default(DEFAULT_OPTIONS.sortPosts),
100
128
  });
@@ -0,0 +1,64 @@
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 {BlogContent, BlogPaginated} from './types';
9
+ import type {TranslationFileContent, TranslationFiles} from '@docusaurus/types';
10
+ import type {PluginOptions} from '@docusaurus/plugin-content-blog';
11
+
12
+ function translateListPage(
13
+ blogListPaginated: BlogPaginated[],
14
+ translations: TranslationFileContent,
15
+ ) {
16
+ return blogListPaginated.map((page) => {
17
+ const {items, metadata} = page;
18
+ return {
19
+ items,
20
+ metadata: {
21
+ ...metadata,
22
+ blogTitle: translations.title.message,
23
+ blogDescription: translations.description.message,
24
+ },
25
+ };
26
+ });
27
+ }
28
+
29
+ export function getTranslationFiles(options: PluginOptions): TranslationFiles {
30
+ return [
31
+ {
32
+ path: 'options',
33
+ content: {
34
+ title: {
35
+ message: options.blogTitle,
36
+ description: 'The title for the blog used in SEO',
37
+ },
38
+ description: {
39
+ message: options.blogDescription,
40
+ description: 'The description for the blog used in SEO',
41
+ },
42
+ 'sidebar.title': {
43
+ message: options.blogSidebarTitle,
44
+ description: 'The label for the left sidebar',
45
+ },
46
+ },
47
+ },
48
+ ];
49
+ }
50
+
51
+ export function translateContent(
52
+ content: BlogContent,
53
+ translationFiles: TranslationFiles,
54
+ ): BlogContent {
55
+ const [{content: optionsTranslations}] = translationFiles;
56
+ return {
57
+ ...content,
58
+ blogSidebarTitle: optionsTranslations['sidebar.title'].message,
59
+ blogListPaginated: translateListPage(
60
+ content.blogListPaginated,
61
+ optionsTranslations,
62
+ ),
63
+ };
64
+ }
package/src/types.ts CHANGED
@@ -5,76 +5,45 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import type {RemarkAndRehypePluginOptions} from '@docusaurus/mdx-loader';
9
- import {
8
+ import type {Tag} from '@docusaurus/utils';
9
+ import type {
10
10
  BrokenMarkdownLink,
11
11
  ContentPaths,
12
12
  } from '@docusaurus/utils/lib/markdownLinks';
13
+ import type {
14
+ BlogPostFrontMatter,
15
+ Author,
16
+ } from '@docusaurus/plugin-content-blog';
13
17
 
14
18
  export type BlogContentPaths = ContentPaths;
15
19
 
16
20
  export interface BlogContent {
21
+ blogSidebarTitle: string;
17
22
  blogPosts: BlogPost[];
18
23
  blogListPaginated: BlogPaginated[];
19
24
  blogTags: BlogTags;
20
25
  blogTagsListPath: string | null;
21
26
  }
22
27
 
23
- export interface DateLink {
24
- date: Date;
25
- link: string;
26
- }
27
-
28
- export type FeedType = 'rss' | 'atom';
29
-
30
- export type EditUrlFunction = (editUrlParams: {
31
- blogDirPath: string;
32
- blogPath: string;
33
- permalink: string;
34
- locale: string;
35
- }) => string | undefined;
36
-
37
- export interface PluginOptions extends RemarkAndRehypePluginOptions {
38
- id?: string;
39
- path: string;
40
- routeBasePath: string;
41
- include: string[];
42
- postsPerPage: number;
43
- blogListComponent: string;
44
- blogPostComponent: string;
45
- blogTagsListComponent: string;
46
- blogTagsPostsComponent: string;
47
- blogTitle: string;
48
- blogDescription: string;
49
- blogSidebarCount: number | 'ALL';
50
- blogSidebarTitle: string;
51
- truncateMarker: RegExp;
52
- showReadingTime: boolean;
53
- feedOptions: {
54
- type?: [FeedType] | null;
55
- title?: string;
56
- description?: string;
57
- copyright: string;
58
- language?: string;
59
- };
60
- editUrl?: string | EditUrlFunction;
61
- editLocalizedFiles?: boolean;
62
- admonitions: Record<string, unknown>;
63
- }
64
-
65
28
  export interface BlogTags {
66
- [key: string]: BlogTag;
29
+ // TODO, the key is the tag slug/permalink
30
+ // This is due to legacy frontmatter: tags:
31
+ // [{label: "xyz", permalink: "/1"}, {label: "xyz", permalink: "/2"}]
32
+ // Soon we should forbid declaring permalink through frontmatter
33
+ [tagKey: string]: BlogTag;
67
34
  }
68
35
 
69
36
  export interface BlogTag {
70
37
  name: string;
71
- items: string[];
38
+ items: string[]; // blog post permalinks
72
39
  permalink: string;
40
+ pages: BlogPaginated[];
73
41
  }
74
42
 
75
43
  export interface BlogPost {
76
44
  id: string;
77
45
  metadata: MetaData;
46
+ content: string;
78
47
  }
79
48
 
80
49
  export interface BlogPaginatedMetadata {
@@ -91,7 +60,7 @@ export interface BlogPaginatedMetadata {
91
60
 
92
61
  export interface BlogPaginated {
93
62
  metadata: BlogPaginatedMetadata;
94
- items: string[];
63
+ items: string[]; // blog post permalinks
95
64
  }
96
65
 
97
66
  export interface MetaData {
@@ -100,13 +69,15 @@ export interface MetaData {
100
69
  description: string;
101
70
  date: Date;
102
71
  formattedDate: string;
103
- tags: (Tag | string)[];
72
+ tags: Tag[];
104
73
  title: string;
105
74
  readingTime?: number;
106
75
  prevItem?: Paginator;
107
76
  nextItem?: Paginator;
108
77
  truncated: boolean;
109
78
  editUrl?: string;
79
+ authors: Author[];
80
+ frontMatter: BlogPostFrontMatter & Record<string, unknown>;
110
81
  }
111
82
 
112
83
  export interface Paginator {
@@ -114,11 +85,6 @@ export interface Paginator {
114
85
  permalink: string;
115
86
  }
116
87
 
117
- export interface Tag {
118
- label: string;
119
- permalink: string;
120
- }
121
-
122
88
  export interface BlogItemsToMetadata {
123
89
  [key: string]: MetaData;
124
90
  }