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

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 (76) hide show
  1. package/lib/authors.d.ts +5 -8
  2. package/lib/authors.js +15 -53
  3. package/lib/blogFrontMatter.d.ts +1 -34
  4. package/lib/blogFrontMatter.js +3 -3
  5. package/lib/blogUtils.d.ts +3 -3
  6. package/lib/blogUtils.js +28 -22
  7. package/lib/feed.d.ts +3 -8
  8. package/lib/feed.js +46 -35
  9. package/lib/index.d.ts +4 -3
  10. package/lib/index.js +33 -28
  11. package/lib/markdownLoader.d.ts +1 -1
  12. package/lib/markdownLoader.js +1 -2
  13. package/lib/pluginOptionSchema.d.ts +1 -1
  14. package/lib/pluginOptionSchema.js +5 -3
  15. package/lib/translations.d.ts +2 -1
  16. package/lib/translations.js +3 -3
  17. package/lib/types.d.ts +2 -79
  18. package/package.json +11 -12
  19. package/src/authors.ts +22 -69
  20. package/src/blogFrontMatter.ts +5 -50
  21. package/src/blogUtils.ts +38 -34
  22. package/src/feed.ts +80 -37
  23. package/src/index.ts +48 -44
  24. package/src/markdownLoader.ts +2 -3
  25. package/src/plugin-content-blog.d.ts +147 -4
  26. package/src/pluginOptionSchema.ts +7 -5
  27. package/src/translations.ts +5 -4
  28. package/src/types.ts +5 -97
  29. package/lib/.tsbuildinfo +0 -1
  30. package/src/__tests__/__fixtures__/authorsMapFiles/authors.json +0 -29
  31. package/src/__tests__/__fixtures__/authorsMapFiles/authors.yml +0 -27
  32. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.json +0 -5
  33. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.yml +0 -3
  34. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.json +0 -3
  35. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.yml +0 -2
  36. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.json +0 -8
  37. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.yml +0 -3
  38. package/src/__tests__/__fixtures__/component/Typography.tsx +0 -6
  39. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathEmpty/empty +0 -0
  40. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson1/authors.json +0 -0
  41. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson2/authors.json +0 -0
  42. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathNestedYml/sub/folder/authors.yml +0 -0
  43. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml1/authors.yml +0 -0
  44. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml2/authors.yml +0 -0
  45. package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -8
  46. package/src/__tests__/__fixtures__/website/blog/_partials/somePartial.md +0 -3
  47. package/src/__tests__/__fixtures__/website/blog/_partials/subfolder/somePartial.md +0 -3
  48. package/src/__tests__/__fixtures__/website/blog/_somePartial.md +0 -3
  49. package/src/__tests__/__fixtures__/website/blog/authors.yml +0 -4
  50. package/src/__tests__/__fixtures__/website/blog/complex-slug.md +0 -7
  51. package/src/__tests__/__fixtures__/website/blog/date-matter.md +0 -5
  52. package/src/__tests__/__fixtures__/website/blog/draft.md +0 -6
  53. package/src/__tests__/__fixtures__/website/blog/heading-as-title.md +0 -5
  54. package/src/__tests__/__fixtures__/website/blog/mdx-blog-post.mdx +0 -36
  55. package/src/__tests__/__fixtures__/website/blog/mdx-require-blog-post.mdx +0 -14
  56. package/src/__tests__/__fixtures__/website/blog/simple-slug.md +0 -11
  57. package/src/__tests__/__fixtures__/website/blog-with-ref/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  58. package/src/__tests__/__fixtures__/website/blog-with-ref/post-with-broken-links.md +0 -11
  59. package/src/__tests__/__fixtures__/website/blog-with-ref/post.md +0 -5
  60. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -8
  61. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/authors.yml +0 -5
  62. package/src/__tests__/__fixtures__/website/static/img/docusaurus.png +0 -0
  63. package/src/__tests__/__fixtures__/website-blog-without-date/blog/no date.md +0 -1
  64. package/src/__tests__/__snapshots__/feed.test.ts.snap +0 -164
  65. package/src/__tests__/__snapshots__/linkify.test.ts.snap +0 -24
  66. package/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap +0 -5
  67. package/src/__tests__/__snapshots__/translations.test.ts.snap +0 -64
  68. package/src/__tests__/authors.test.ts +0 -608
  69. package/src/__tests__/blogFrontMatter.test.ts +0 -394
  70. package/src/__tests__/blogUtils.test.ts +0 -94
  71. package/src/__tests__/feed.test.ts +0 -126
  72. package/src/__tests__/index.test.ts +0 -408
  73. package/src/__tests__/linkify.test.ts +0 -93
  74. package/src/__tests__/pluginOptionSchema.test.ts +0 -150
  75. package/src/__tests__/translations.test.ts +0 -92
  76. package/tsconfig.json +0 -9
package/src/feed.ts CHANGED
@@ -5,34 +5,35 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {Feed, Author as FeedAuthor} from 'feed';
9
- import {PluginOptions, Author, BlogPost, FeedType} from './types';
10
- import {normalizeUrl, mdxToHtml} from '@docusaurus/utils';
11
- import {DocusaurusConfig} from '@docusaurus/types';
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';
17
+ import type {DocusaurusConfig} from '@docusaurus/types';
12
18
  import path from 'path';
13
19
  import fs from 'fs-extra';
20
+ import type {
21
+ FeedType,
22
+ PluginOptions,
23
+ Author,
24
+ } from '@docusaurus/plugin-content-blog';
25
+ import {blogPostContainerID} from '@docusaurus/utils-common';
14
26
 
15
- // TODO this is temporary until we handle mdxToHtml better
16
- // It's hard to convert reliably JSX/require calls to an html feed content
17
- // See https://github.com/facebook/docusaurus/issues/5664
18
- function mdxToFeedContent(mdxContent: string): string | undefined {
19
- try {
20
- return mdxToHtml(mdxContent);
21
- } catch (e) {
22
- // TODO will we need a plugin option to configure how to handle such an error
23
- // Swallow the error on purpose for now, until we understand better the problem space
24
- return undefined;
25
- }
26
- }
27
-
28
- export async function generateBlogFeed({
27
+ async function generateBlogFeed({
29
28
  blogPosts,
30
29
  options,
31
30
  siteConfig,
31
+ outDir,
32
32
  }: {
33
33
  blogPosts: BlogPost[];
34
34
  options: PluginOptions;
35
35
  siteConfig: DocusaurusConfig;
36
+ outDir: string;
36
37
  }): Promise<Feed | null> {
37
38
  if (!blogPosts.length) {
38
39
  return null;
@@ -42,9 +43,7 @@ export async function generateBlogFeed({
42
43
  const {url: siteUrl, baseUrl, title, favicon} = siteConfig;
43
44
  const blogBaseUrl = normalizeUrl([siteUrl, baseUrl, routeBasePath]);
44
45
 
45
- const updated =
46
- (blogPosts[0] && blogPosts[0].metadata.date) ||
47
- new Date('2015-10-25T16:29:00.000-07:00'); // weird legacy magic date
46
+ const updated = blogPosts[0] && blogPosts[0].metadata.date;
48
47
 
49
48
  const feed = new Feed({
50
49
  id: blogBaseUrl,
@@ -63,20 +62,45 @@ export async function generateBlogFeed({
63
62
  return {name: author.name, link: author.url};
64
63
  }
65
64
 
66
- blogPosts.forEach((post) => {
65
+ await mapAsyncSequential(blogPosts, async (post) => {
67
66
  const {
68
67
  id,
69
- metadata: {title: metadataTitle, permalink, date, description, authors},
68
+ metadata: {
69
+ title: metadataTitle,
70
+ permalink,
71
+ date,
72
+ description,
73
+ authors,
74
+ tags,
75
+ },
70
76
  } = post;
71
- feed.addItem({
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 = {
72
86
  title: metadataTitle,
73
87
  id,
74
88
  link: normalizeUrl([siteUrl, permalink]),
75
89
  date,
76
90
  description,
77
- content: mdxToFeedContent(post.content),
78
- author: authors.map(toFeedAuthor),
79
- });
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);
80
104
  });
81
105
 
82
106
  return feed;
@@ -85,15 +109,29 @@ export async function generateBlogFeed({
85
109
  async function createBlogFeedFile({
86
110
  feed,
87
111
  feedType,
88
- filePath,
112
+ generatePath,
89
113
  }: {
90
114
  feed: Feed;
91
115
  feedType: FeedType;
92
- filePath: string;
116
+ generatePath: string;
93
117
  }) {
94
- const feedContent = feedType === 'rss' ? feed.rss2() : feed.atom1();
118
+ const [feedContent, feedPath] = (() => {
119
+ switch (feedType) {
120
+ case 'rss':
121
+ return [feed.rss2(), 'rss.xml'];
122
+ case 'json':
123
+ return [feed.json1(), 'feed.json'];
124
+ case 'atom':
125
+ return [feed.atom1(), 'atom.xml'];
126
+ default:
127
+ throw new Error(`Feed type ${feedType} not supported.`);
128
+ }
129
+ })();
95
130
  try {
96
- await fs.outputFile(filePath, feedContent);
131
+ await fs.outputFile(
132
+ posixPath(path.join(generatePath, feedPath)),
133
+ feedContent,
134
+ );
97
135
  } catch (err) {
98
136
  throw new Error(`Generating ${feedType} feed failed: ${err}.`);
99
137
  }
@@ -110,7 +148,12 @@ export async function createBlogFeedFiles({
110
148
  siteConfig: DocusaurusConfig;
111
149
  outDir: string;
112
150
  }): Promise<void> {
113
- const feed = await generateBlogFeed({blogPosts, options, siteConfig});
151
+ const feed = await generateBlogFeed({
152
+ blogPosts,
153
+ options,
154
+ siteConfig,
155
+ outDir,
156
+ });
114
157
 
115
158
  const feedTypes = options.feedOptions.type;
116
159
  if (!feed || !feedTypes) {
@@ -118,12 +161,12 @@ export async function createBlogFeedFiles({
118
161
  }
119
162
 
120
163
  await Promise.all(
121
- feedTypes.map(async (feedType) => {
122
- await createBlogFeedFile({
164
+ feedTypes.map((feedType) =>
165
+ createBlogFeedFile({
123
166
  feed,
124
167
  feedType,
125
- filePath: path.join(outDir, options.routeBasePath, `${feedType}.xml`),
126
- });
127
- }),
168
+ generatePath: path.join(outDir, options.routeBasePath),
169
+ }),
170
+ ),
128
171
  );
129
172
  }
package/src/index.ts CHANGED
@@ -16,12 +16,13 @@ import {
16
16
  posixPath,
17
17
  addTrailingPathSeparator,
18
18
  createAbsoluteFilePathMatcher,
19
+ getContentPathList,
20
+ getDataFilePath,
19
21
  DEFAULT_PLUGIN_ID,
20
22
  } from '@docusaurus/utils';
21
23
  import {translateContent, getTranslationFiles} from './translations';
22
24
 
23
- import {
24
- PluginOptions,
25
+ import type {
25
26
  BlogTags,
26
27
  BlogContent,
27
28
  BlogItemsToMetadata,
@@ -30,32 +31,33 @@ import {
30
31
  BlogContentPaths,
31
32
  BlogMarkdownLoaderOptions,
32
33
  MetaData,
33
- Assets,
34
34
  } from './types';
35
35
  import {PluginOptionSchema} from './pluginOptionSchema';
36
- import {
36
+ import type {
37
37
  LoadContext,
38
38
  ConfigureWebpackUtils,
39
- Props,
40
39
  Plugin,
41
40
  HtmlTags,
42
41
  OptionValidationContext,
43
42
  ValidationResult,
44
43
  } from '@docusaurus/types';
45
- import {Configuration} from 'webpack';
44
+ import type {Configuration} from 'webpack';
46
45
  import {
47
46
  generateBlogPosts,
48
- getContentPathList,
49
47
  getSourceToPermalink,
50
48
  getBlogTags,
51
49
  } from './blogUtils';
52
- import {BlogPostFrontMatter} from './blogFrontMatter';
53
50
  import {createBlogFeedFiles} from './feed';
51
+ import type {
52
+ PluginOptions,
53
+ BlogPostFrontMatter,
54
+ Assets,
55
+ } from '@docusaurus/plugin-content-blog';
54
56
 
55
- export default function pluginContentBlog(
57
+ export default async function pluginContentBlog(
56
58
  context: LoadContext,
57
59
  options: PluginOptions,
58
- ): Plugin<BlogContent> {
60
+ ): Promise<Plugin<BlogContent>> {
59
61
  if (options.admonitions) {
60
62
  options.remarkPlugins = options.remarkPlugins.concat([
61
63
  [admonitions, options.admonitions],
@@ -89,24 +91,23 @@ export default function pluginContentBlog(
89
91
  const aliasedSource = (source: string) =>
90
92
  `~blog/${posixPath(path.relative(pluginDataDirRoot, source))}`;
91
93
 
94
+ const authorsMapFilePath = await getDataFilePath({
95
+ filePath: options.authorsMapPath,
96
+ contentPaths,
97
+ });
98
+
92
99
  return {
93
100
  name: 'docusaurus-plugin-content-blog',
94
101
 
95
102
  getPathsToWatch() {
96
- const {include, authorsMapPath} = options;
103
+ const {include} = options;
97
104
  const contentMarkdownGlobs = getContentPathList(contentPaths).flatMap(
98
105
  (contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
99
106
  );
100
107
 
101
- // TODO: we should read this path in plugin! but plugins do not support async init for now :'(
102
- // const authorsMapFilePath = await getAuthorsMapFilePath({authorsMapPath,contentPaths,});
103
- // simplified impl, better than nothing for now:
104
- const authorsMapFilePath = path.join(
105
- contentPaths.contentPath,
106
- authorsMapPath,
107
- );
108
-
109
- return [authorsMapFilePath, ...contentMarkdownGlobs];
108
+ return [authorsMapFilePath, ...contentMarkdownGlobs].filter(
109
+ Boolean,
110
+ ) as string[];
110
111
  },
111
112
 
112
113
  async getTranslationFiles() {
@@ -240,25 +241,26 @@ export default function pluginContentBlog(
240
241
  ? blogPosts
241
242
  : blogPosts.slice(0, options.blogSidebarCount);
242
243
 
243
- const archiveUrl = normalizeUrl([
244
- baseUrl,
245
- routeBasePath,
246
- archiveBasePath,
247
- ]);
248
-
249
- // creates a blog archive route
250
- const archiveProp = await createData(
251
- `${docuHash(archiveUrl)}.json`,
252
- JSON.stringify({blogPosts}, null, 2),
253
- );
254
- addRoute({
255
- path: archiveUrl,
256
- component: '@theme/BlogArchivePage',
257
- exact: true,
258
- modules: {
259
- archive: aliasedSource(archiveProp),
260
- },
261
- });
244
+ if (archiveBasePath) {
245
+ const archiveUrl = normalizeUrl([
246
+ baseUrl,
247
+ routeBasePath,
248
+ archiveBasePath,
249
+ ]);
250
+ // creates a blog archive route
251
+ const archiveProp = await createData(
252
+ `${docuHash(archiveUrl)}.json`,
253
+ JSON.stringify({blogPosts}, null, 2),
254
+ );
255
+ addRoute({
256
+ path: archiveUrl,
257
+ component: '@theme/BlogArchivePage',
258
+ exact: true,
259
+ modules: {
260
+ archive: aliasedSource(archiveProp),
261
+ },
262
+ });
263
+ }
262
264
 
263
265
  // This prop is useful to provide the blog list sidebar
264
266
  const sidebarProp = await createData(
@@ -509,14 +511,11 @@ export default function pluginContentBlog(
509
511
  };
510
512
  },
511
513
 
512
- async postBuild({outDir}: Props) {
514
+ async postBuild({outDir, content}) {
513
515
  if (!options.feedOptions.type) {
514
516
  return;
515
517
  }
516
-
517
- // TODO: we shouldn't need to re-read the posts here!
518
- // postBuild should receive loadedContent
519
- const blogPosts = await generateBlogPosts(contentPaths, context, options);
518
+ const {blogPosts} = content;
520
519
  if (!blogPosts.length) {
521
520
  return;
522
521
  }
@@ -550,6 +549,11 @@ export default function pluginContentBlog(
550
549
  path: 'atom.xml',
551
550
  title: `${feedTitle} Atom Feed`,
552
551
  },
552
+ json: {
553
+ type: 'application/json',
554
+ path: 'feed.json',
555
+ title: `${feedTitle} JSON Feed`,
556
+ },
553
557
  };
554
558
  const headTags: HtmlTags = [];
555
559
 
@@ -6,8 +6,7 @@
6
6
  */
7
7
 
8
8
  import {truncate, linkify} from './blogUtils';
9
- import {parseQuery} from 'loader-utils';
10
- import {BlogMarkdownLoaderOptions} from './types';
9
+ import type {BlogMarkdownLoaderOptions} from './types';
11
10
  import type {LoaderContext} from 'webpack';
12
11
 
13
12
  export default function markdownLoader(
@@ -28,7 +27,7 @@ export default function markdownLoader(
28
27
 
29
28
  // Truncate content if requested (e.g: file.md?truncated=true).
30
29
  const truncated: boolean | undefined = this.resourceQuery
31
- ? !!parseQuery(this.resourceQuery).truncated
30
+ ? !!new URLSearchParams(this.resourceQuery.slice(1)).get('truncated')
32
31
  : undefined;
33
32
 
34
33
  if (truncated) {
@@ -6,7 +6,145 @@
6
6
  */
7
7
 
8
8
  declare module '@docusaurus/plugin-content-blog' {
9
- export type Options = Partial<import('./types').UserPluginOptions>;
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
+ }
25
+
26
+ export type BlogPostFrontMatter = {
27
+ id?: string;
28
+ title?: string;
29
+ description?: string;
30
+ tags?: FrontMatterTag[];
31
+ slug?: string;
32
+ draft?: boolean;
33
+ date?: Date | string; // Yaml automatically convert some string patterns as Date, but not all
34
+
35
+ authors?: BlogPostFrontMatterAuthors;
36
+
37
+ // We may want to deprecate those older author front matter fields later:
38
+ author?: string;
39
+ author_title?: string;
40
+ author_url?: string;
41
+ author_image_url?: string;
42
+
43
+ /** @deprecated */
44
+ authorTitle?: string;
45
+ /** @deprecated */
46
+ authorURL?: string;
47
+ /** @deprecated */
48
+ authorImageURL?: string;
49
+
50
+ image?: string;
51
+ keywords?: string[];
52
+ hide_table_of_contents?: boolean;
53
+ toc_min_heading_level?: number;
54
+ toc_max_heading_level?: number;
55
+ };
56
+
57
+ export type BlogPostFrontMatterAuthor = Record<string, unknown> & {
58
+ key?: string;
59
+ name?: string;
60
+ imageURL?: string;
61
+ url?: string;
62
+ title?: string;
63
+ };
64
+
65
+ // All the possible variants that the user can use for convenience
66
+ export type BlogPostFrontMatterAuthors =
67
+ | string
68
+ | BlogPostFrontMatterAuthor
69
+ | (string | BlogPostFrontMatterAuthor)[];
70
+
71
+ export type EditUrlFunction = (editUrlParams: {
72
+ blogDirPath: string;
73
+ blogPath: string;
74
+ permalink: string;
75
+ locale: string;
76
+ }) => string | undefined;
77
+
78
+ export type FeedType = 'rss' | 'atom' | 'json';
79
+ export type FeedOptions = {
80
+ type?: FeedType[] | null;
81
+ title?: string;
82
+ description?: string;
83
+ copyright: string;
84
+ language?: string;
85
+ };
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
+
92
+ // Duplicate from ngryman/reading-time to keep stability of API
93
+ type ReadingTimeOptions = {
94
+ wordsPerMinute?: number;
95
+ wordBound?: (char: string) => boolean;
96
+ };
97
+
98
+ export type ReadingTimeFunction = (params: {
99
+ content: string;
100
+ frontMatter?: BlogPostFrontMatter & Record<string, unknown>;
101
+ options?: ReadingTimeOptions;
102
+ }) => number;
103
+
104
+ export type ReadingTimeFunctionOption = (
105
+ params: Required<Omit<Parameters<ReadingTimeFunction>[0], 'options'>> & {
106
+ defaultReadingTime: ReadingTimeFunction;
107
+ },
108
+ ) => number | undefined;
109
+
110
+ export type PluginOptions = RemarkAndRehypePluginOptions & {
111
+ id?: string;
112
+ path: string;
113
+ routeBasePath: string;
114
+ tagsBasePath: string;
115
+ archiveBasePath: string | null;
116
+ include: string[];
117
+ exclude: string[];
118
+ postsPerPage: number | 'ALL';
119
+ blogListComponent: string;
120
+ blogPostComponent: string;
121
+ blogTagsListComponent: string;
122
+ blogTagsPostsComponent: string;
123
+ blogTitle: string;
124
+ blogDescription: string;
125
+ blogSidebarCount: number | 'ALL';
126
+ blogSidebarTitle: string;
127
+ truncateMarker: RegExp;
128
+ showReadingTime: boolean;
129
+ feedOptions: {
130
+ type?: FeedType[] | null;
131
+ title?: string;
132
+ description?: string;
133
+ copyright: string;
134
+ language?: string;
135
+ };
136
+ editUrl?: string | EditUrlFunction;
137
+ editLocalizedFiles?: boolean;
138
+ admonitions: Record<string, unknown>;
139
+ authorsMapPath: string;
140
+ readingTime: ReadingTimeFunctionOption;
141
+ sortPosts: 'ascending' | 'descending';
142
+ };
143
+ // Options, as provided in the user config (before normalization)
144
+ export type Options = Overwrite<
145
+ Partial<PluginOptions>,
146
+ {feedOptions?: UserFeedOptions}
147
+ >;
10
148
  }
11
149
 
12
150
  declare module '@theme/BlogSidebar' {
@@ -27,9 +165,13 @@ declare module '@theme/BlogSidebar' {
27
165
  declare module '@theme/BlogPostPage' {
28
166
  import type {BlogSidebar} from '@theme/BlogSidebar';
29
167
  import type {TOCItem} from '@docusaurus/types';
168
+ import type {
169
+ BlogPostFrontMatter,
170
+ Author,
171
+ Assets,
172
+ } from '@docusaurus/plugin-content-blog';
30
173
 
31
- export type FrontMatter = import('./blogFrontMatter').BlogPostFrontMatter;
32
- export type Assets = import('./types').Assets;
174
+ export type FrontMatter = BlogPostFrontMatter;
33
175
 
34
176
  export type Metadata = {
35
177
  readonly title: string;
@@ -42,7 +184,8 @@ declare module '@theme/BlogPostPage' {
42
184
  readonly truncated?: string;
43
185
  readonly nextItem?: {readonly title: string; readonly permalink: string};
44
186
  readonly prevItem?: {readonly title: string; readonly permalink: string};
45
- readonly authors: import('./types').Author[];
187
+ readonly authors: Author[];
188
+ readonly frontMatter: FrontMatter & Record<string, unknown>;
46
189
  readonly tags: readonly {
47
190
  readonly label: string;
48
191
  readonly permalink: string;
@@ -13,7 +13,7 @@ import {
13
13
  URISchema,
14
14
  } from '@docusaurus/utils-validation';
15
15
  import {GlobExcludeDefault} from '@docusaurus/utils';
16
- import {PluginOptions} from './types';
16
+ import type {PluginOptions} from '@docusaurus/plugin-content-blog';
17
17
 
18
18
  export const DEFAULT_OPTIONS: PluginOptions = {
19
19
  feedOptions: {type: ['rss', 'atom'], copyright: ''},
@@ -47,7 +47,9 @@ export const DEFAULT_OPTIONS: PluginOptions = {
47
47
 
48
48
  export const PluginOptionSchema = Joi.object<PluginOptions>({
49
49
  path: Joi.string().default(DEFAULT_OPTIONS.path),
50
- archiveBasePath: Joi.string().default(DEFAULT_OPTIONS.archiveBasePath),
50
+ archiveBasePath: Joi.string()
51
+ .default(DEFAULT_OPTIONS.archiveBasePath)
52
+ .allow(null),
51
53
  routeBasePath: Joi.string()
52
54
  // '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
53
55
  // .allow('')
@@ -90,12 +92,12 @@ export const PluginOptionSchema = Joi.object<PluginOptions>({
90
92
  feedOptions: Joi.object({
91
93
  type: Joi.alternatives()
92
94
  .try(
93
- Joi.array().items(Joi.string()),
95
+ Joi.array().items(Joi.string().equal('rss', 'atom', 'json')),
94
96
  Joi.alternatives().conditional(
95
- Joi.string().equal('all', 'rss', 'atom'),
97
+ Joi.string().equal('all', 'rss', 'atom', 'json'),
96
98
  {
97
99
  then: Joi.custom((val) =>
98
- val === 'all' ? ['rss', 'atom'] : [val],
100
+ val === 'all' ? ['rss', 'atom', 'json'] : [val],
99
101
  ),
100
102
  },
101
103
  ),
@@ -5,8 +5,9 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import type {BlogContent, PluginOptions, BlogPaginated} from './types';
8
+ import type {BlogContent, BlogPaginated} from './types';
9
9
  import type {TranslationFileContent, TranslationFiles} from '@docusaurus/types';
10
+ import type {PluginOptions} from '@docusaurus/plugin-content-blog';
10
11
 
11
12
  function translateListPage(
12
13
  blogListPaginated: BlogPaginated[],
@@ -51,13 +52,13 @@ export function translateContent(
51
52
  content: BlogContent,
52
53
  translationFiles: TranslationFiles,
53
54
  ): BlogContent {
54
- const [{content: optonsTranslations}] = translationFiles;
55
+ const [{content: optionsTranslations}] = translationFiles;
55
56
  return {
56
57
  ...content,
57
- blogSidebarTitle: optonsTranslations['sidebar.title'].message,
58
+ blogSidebarTitle: optionsTranslations['sidebar.title'].message,
58
59
  blogListPaginated: translateListPage(
59
60
  content.blogListPaginated,
60
- optonsTranslations,
61
+ optionsTranslations,
61
62
  ),
62
63
  };
63
64
  }