@docusaurus/plugin-content-blog 2.0.0-beta.1 → 2.0.0-beta.10

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 (66) hide show
  1. package/lib/.tsbuildinfo +1 -1
  2. package/lib/authors.d.ts +23 -0
  3. package/lib/authors.js +147 -0
  4. package/lib/blogFrontMatter.d.ts +19 -6
  5. package/lib/blogFrontMatter.js +35 -23
  6. package/lib/blogUtils.d.ts +10 -4
  7. package/lib/blogUtils.js +141 -136
  8. package/lib/feed.d.ts +20 -0
  9. package/lib/feed.js +90 -0
  10. package/lib/index.d.ts +1 -1
  11. package/lib/index.js +109 -112
  12. package/lib/markdownLoader.d.ts +3 -6
  13. package/lib/markdownLoader.js +5 -5
  14. package/lib/pluginOptionSchema.d.ts +3 -26
  15. package/lib/pluginOptionSchema.js +28 -7
  16. package/lib/translations.d.ts +10 -0
  17. package/lib/translations.js +53 -0
  18. package/lib/types.d.ts +54 -14
  19. package/package.json +19 -13
  20. package/src/__tests__/__fixtures__/authorsMapFiles/authors.json +29 -0
  21. package/src/__tests__/__fixtures__/authorsMapFiles/authors.yml +27 -0
  22. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.json +5 -0
  23. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.yml +3 -0
  24. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.json +3 -0
  25. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.yml +2 -0
  26. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.json +8 -0
  27. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.yml +3 -0
  28. package/src/__tests__/__fixtures__/component/Typography.tsx +6 -0
  29. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathEmpty/empty +0 -0
  30. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson1/authors.json +0 -0
  31. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson2/authors.json +0 -0
  32. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathNestedYml/sub/folder/authors.yml +0 -0
  33. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml1/authors.yml +0 -0
  34. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml2/authors.yml +0 -0
  35. package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +3 -0
  36. package/src/__tests__/__fixtures__/website/blog/_partials/somePartial.md +3 -0
  37. package/src/__tests__/__fixtures__/website/blog/_partials/subfolder/somePartial.md +3 -0
  38. package/src/__tests__/__fixtures__/website/blog/_somePartial.md +3 -0
  39. package/src/__tests__/__fixtures__/website/blog/authors.yml +4 -0
  40. package/src/__tests__/__fixtures__/website/blog/mdx-blog-post.mdx +36 -0
  41. package/src/__tests__/__fixtures__/website/blog/mdx-require-blog-post.mdx +14 -0
  42. package/src/__tests__/__fixtures__/website/blog/simple-slug.md +4 -0
  43. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md +3 -0
  44. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/authors.yml +5 -0
  45. package/src/__tests__/__fixtures__/website/static/img/docusaurus.png +0 -0
  46. package/src/__tests__/__snapshots__/feed.test.ts.snap +164 -0
  47. package/src/__tests__/__snapshots__/translations.test.ts.snap +64 -0
  48. package/src/__tests__/authors.test.ts +608 -0
  49. package/src/__tests__/blogFrontMatter.test.ts +165 -36
  50. package/src/__tests__/blogUtils.test.ts +94 -0
  51. package/src/__tests__/{generateBlogFeed.test.ts → feed.test.ts} +35 -9
  52. package/src/__tests__/index.test.ts +84 -12
  53. package/src/__tests__/pluginOptionSchema.test.ts +3 -3
  54. package/src/__tests__/translations.test.ts +92 -0
  55. package/src/authors.ts +198 -0
  56. package/src/blogFrontMatter.ts +76 -37
  57. package/src/blogUtils.ts +202 -179
  58. package/{types.d.ts → src/deps.d.ts} +0 -0
  59. package/src/feed.ts +129 -0
  60. package/src/index.ts +131 -112
  61. package/src/markdownLoader.ts +8 -12
  62. package/{index.d.ts → src/plugin-content-blog.d.ts} +35 -31
  63. package/src/pluginOptionSchema.ts +31 -9
  64. package/src/translations.ts +63 -0
  65. package/src/types.ts +69 -16
  66. package/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap +0 -76
@@ -12,9 +12,11 @@ import {
12
12
  AdmonitionsSchema,
13
13
  URISchema,
14
14
  } from '@docusaurus/utils-validation';
15
+ import {GlobExcludeDefault} from '@docusaurus/utils';
16
+ import {PluginOptions} from './types';
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: {},
@@ -31,22 +33,30 @@ export const DEFAULT_OPTIONS = {
31
33
  blogSidebarCount: 5,
32
34
  blogSidebarTitle: 'Recent posts',
33
35
  postsPerPage: 10,
34
- include: ['*.md', '*.mdx'],
36
+ include: ['**/*.{md,mdx}'],
37
+ exclude: GlobExcludeDefault,
35
38
  routeBasePath: 'blog',
39
+ tagsBasePath: 'tags',
40
+ archiveBasePath: 'archive',
36
41
  path: 'blog',
37
42
  editLocalizedFiles: false,
43
+ authorsMapPath: 'authors.yml',
44
+ readingTime: ({content, defaultReadingTime}) => defaultReadingTime({content}),
45
+ sortPosts: 'descending',
38
46
  };
39
47
 
40
- export const PluginOptionSchema = Joi.object({
48
+ export const PluginOptionSchema = Joi.object<PluginOptions>({
41
49
  path: Joi.string().default(DEFAULT_OPTIONS.path),
50
+ archiveBasePath: Joi.string().default(DEFAULT_OPTIONS.archiveBasePath),
42
51
  routeBasePath: Joi.string()
43
52
  // '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
44
53
  // .allow('')
45
54
  .default(DEFAULT_OPTIONS.routeBasePath),
55
+ tagsBasePath: Joi.string().default(DEFAULT_OPTIONS.tagsBasePath),
46
56
  include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
47
- postsPerPage: Joi.number()
48
- .integer()
49
- .min(1)
57
+ exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude),
58
+ postsPerPage: Joi.alternatives()
59
+ .try(Joi.equal('ALL').required(), Joi.number().integer().min(1).required())
50
60
  .default(DEFAULT_OPTIONS.postsPerPage),
51
61
  blogListComponent: Joi.string().default(DEFAULT_OPTIONS.blogListComponent),
52
62
  blogPostComponent: Joi.string().default(DEFAULT_OPTIONS.blogPostComponent),
@@ -61,7 +71,7 @@ export const PluginOptionSchema = Joi.object({
61
71
  .allow('')
62
72
  .default(DEFAULT_OPTIONS.blogDescription),
63
73
  blogSidebarCount: Joi.alternatives()
64
- .try(Joi.equal('ALL').required(), Joi.number().required())
74
+ .try(Joi.equal('ALL').required(), Joi.number().integer().min(0).required())
65
75
  .default(DEFAULT_OPTIONS.blogSidebarCount),
66
76
  blogSidebarTitle: Joi.string().default(DEFAULT_OPTIONS.blogSidebarTitle),
67
77
  showReadingTime: Joi.bool().default(DEFAULT_OPTIONS.showReadingTime),
@@ -94,7 +104,19 @@ export const PluginOptionSchema = Joi.object({
94
104
  .default(DEFAULT_OPTIONS.feedOptions.type),
95
105
  title: Joi.string().allow(''),
96
106
  description: Joi.string().allow(''),
97
- copyright: Joi.string(),
107
+ // only add default value when user actually wants a feed (type is not null)
108
+ copyright: Joi.when('type', {
109
+ is: Joi.any().valid(null),
110
+ then: Joi.string().optional(),
111
+ otherwise: Joi.string()
112
+ .allow('')
113
+ .default(DEFAULT_OPTIONS.feedOptions.copyright),
114
+ }),
98
115
  language: Joi.string(),
99
116
  }).default(DEFAULT_OPTIONS.feedOptions),
117
+ authorsMapPath: Joi.string().default(DEFAULT_OPTIONS.authorsMapPath),
118
+ readingTime: Joi.function().default(() => DEFAULT_OPTIONS.readingTime),
119
+ sortPosts: Joi.string()
120
+ .valid('descending', 'ascending')
121
+ .default(DEFAULT_OPTIONS.sortPosts),
100
122
  });
@@ -0,0 +1,63 @@
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, PluginOptions, BlogPaginated} from './types';
9
+ import type {TranslationFileContent, TranslationFiles} from '@docusaurus/types';
10
+
11
+ function translateListPage(
12
+ blogListPaginated: BlogPaginated[],
13
+ translations: TranslationFileContent,
14
+ ) {
15
+ return blogListPaginated.map((page) => {
16
+ const {items, metadata} = page;
17
+ return {
18
+ items,
19
+ metadata: {
20
+ ...metadata,
21
+ blogTitle: translations.title.message,
22
+ blogDescription: translations.description.message,
23
+ },
24
+ };
25
+ });
26
+ }
27
+
28
+ export function getTranslationFiles(options: PluginOptions): TranslationFiles {
29
+ return [
30
+ {
31
+ path: 'options',
32
+ content: {
33
+ title: {
34
+ message: options.blogTitle,
35
+ description: 'The title for the blog used in SEO',
36
+ },
37
+ description: {
38
+ message: options.blogDescription,
39
+ description: 'The description for the blog used in SEO',
40
+ },
41
+ 'sidebar.title': {
42
+ message: options.blogSidebarTitle,
43
+ description: 'The label for the left sidebar',
44
+ },
45
+ },
46
+ },
47
+ ];
48
+ }
49
+
50
+ export function translateContent(
51
+ content: BlogContent,
52
+ translationFiles: TranslationFiles,
53
+ ): BlogContent {
54
+ const [{content: optonsTranslations}] = translationFiles;
55
+ return {
56
+ ...content,
57
+ blogSidebarTitle: optonsTranslations['sidebar.title'].message,
58
+ blogListPaginated: translateListPage(
59
+ content.blogListPaginated,
60
+ optonsTranslations,
61
+ ),
62
+ };
63
+ }
package/src/types.ts CHANGED
@@ -6,27 +6,40 @@
6
6
  */
7
7
 
8
8
  import type {RemarkAndRehypePluginOptions} from '@docusaurus/mdx-loader';
9
- import {
9
+ import type {Tag} from '@docusaurus/utils';
10
+ import type {
10
11
  BrokenMarkdownLink,
11
12
  ContentPaths,
12
13
  } from '@docusaurus/utils/lib/markdownLinks';
14
+ import {Overwrite} from 'utility-types';
15
+ import {BlogPostFrontMatter} from './blogFrontMatter';
13
16
 
14
17
  export type BlogContentPaths = ContentPaths;
15
18
 
16
19
  export interface BlogContent {
20
+ blogSidebarTitle: string;
17
21
  blogPosts: BlogPost[];
18
22
  blogListPaginated: BlogPaginated[];
19
23
  blogTags: BlogTags;
20
24
  blogTagsListPath: string | null;
21
25
  }
22
26
 
23
- export interface DateLink {
24
- date: Date;
25
- link: string;
26
- }
27
-
28
27
  export type FeedType = 'rss' | 'atom';
29
28
 
29
+ export type FeedOptions = {
30
+ type?: FeedType[] | null;
31
+ title?: string;
32
+ description?: string;
33
+ copyright: string;
34
+ language?: string;
35
+ };
36
+
37
+ // Feed options, as provided by user config
38
+ export type UserFeedOptions = Overwrite<
39
+ Partial<FeedOptions>,
40
+ {type?: FeedOptions['type'] | 'all'} // Handle the type: "all" shortcut
41
+ >;
42
+
30
43
  export type EditUrlFunction = (editUrlParams: {
31
44
  blogDirPath: string;
32
45
  blogPath: string;
@@ -34,12 +47,33 @@ export type EditUrlFunction = (editUrlParams: {
34
47
  locale: string;
35
48
  }) => string | undefined;
36
49
 
37
- export interface PluginOptions extends RemarkAndRehypePluginOptions {
50
+ // Duplicate from ngryman/reading-time to keep stability of API
51
+ type ReadingTimeOptions = {
52
+ wordsPerMinute?: number;
53
+ wordBound?: (char: string) => boolean;
54
+ };
55
+
56
+ export type ReadingTimeFunction = (params: {
57
+ content: string;
58
+ frontMatter?: BlogPostFrontMatter & Record<string, unknown>;
59
+ options?: ReadingTimeOptions;
60
+ }) => number;
61
+
62
+ export type ReadingTimeFunctionOption = (
63
+ params: Required<Omit<Parameters<ReadingTimeFunction>[0], 'options'>> & {
64
+ defaultReadingTime: ReadingTimeFunction;
65
+ },
66
+ ) => number | undefined;
67
+
68
+ export type PluginOptions = RemarkAndRehypePluginOptions & {
38
69
  id?: string;
39
70
  path: string;
40
71
  routeBasePath: string;
72
+ tagsBasePath: string;
73
+ archiveBasePath: string;
41
74
  include: string[];
42
- postsPerPage: number;
75
+ exclude: string[];
76
+ postsPerPage: number | 'ALL';
43
77
  blogListComponent: string;
44
78
  blogPostComponent: string;
45
79
  blogTagsListComponent: string;
@@ -51,7 +85,7 @@ export interface PluginOptions extends RemarkAndRehypePluginOptions {
51
85
  truncateMarker: RegExp;
52
86
  showReadingTime: boolean;
53
87
  feedOptions: {
54
- type?: [FeedType] | null;
88
+ type?: FeedType[] | null;
55
89
  title?: string;
56
90
  description?: string;
57
91
  copyright: string;
@@ -60,7 +94,16 @@ export interface PluginOptions extends RemarkAndRehypePluginOptions {
60
94
  editUrl?: string | EditUrlFunction;
61
95
  editLocalizedFiles?: boolean;
62
96
  admonitions: Record<string, unknown>;
63
- }
97
+ authorsMapPath: string;
98
+ readingTime: ReadingTimeFunctionOption;
99
+ sortPosts: 'ascending' | 'descending';
100
+ };
101
+
102
+ // Options, as provided in the user config (before normalization)
103
+ export type UserPluginOptions = Overwrite<
104
+ Partial<PluginOptions>,
105
+ {feedOptions?: UserFeedOptions}
106
+ >;
64
107
 
65
108
  export interface BlogTags {
66
109
  [key: string]: BlogTag;
@@ -75,6 +118,7 @@ export interface BlogTag {
75
118
  export interface BlogPost {
76
119
  id: string;
77
120
  metadata: MetaData;
121
+ content: string;
78
122
  }
79
123
 
80
124
  export interface BlogPaginatedMetadata {
@@ -94,28 +138,37 @@ export interface BlogPaginated {
94
138
  items: string[];
95
139
  }
96
140
 
141
+ // We allow passing custom fields to authors, e.g., twitter
142
+ export interface Author extends Record<string, unknown> {
143
+ name?: string;
144
+ imageURL?: string;
145
+ url?: string;
146
+ title?: string;
147
+ }
148
+
97
149
  export interface MetaData {
98
150
  permalink: string;
99
151
  source: string;
100
152
  description: string;
101
153
  date: Date;
102
154
  formattedDate: string;
103
- tags: (Tag | string)[];
155
+ tags: Tag[];
104
156
  title: string;
105
157
  readingTime?: number;
106
158
  prevItem?: Paginator;
107
159
  nextItem?: Paginator;
108
160
  truncated: boolean;
109
161
  editUrl?: string;
162
+ authors: Author[];
110
163
  }
111
164
 
112
- export interface Paginator {
113
- title: string;
114
- permalink: string;
165
+ export interface Assets {
166
+ image?: string;
167
+ authorsImageUrls: (string | undefined)[]; // Array of same size as the original MetaData.authors array
115
168
  }
116
169
 
117
- export interface Tag {
118
- label: string;
170
+ export interface Paginator {
171
+ title: string;
119
172
  permalink: string;
120
173
  }
121
174
 
@@ -1,76 +0,0 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`blogFeed atom should not show feed without posts 1`] = `null`;
4
-
5
- exports[`blogFeed atom shows feed item for each post 1`] = `
6
- "<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
7
- <feed xmlns=\\"http://www.w3.org/2005/Atom\\">
8
- <id>https://docusaurus.io/myBaseUrl/blog</id>
9
- <title>Hello Blog</title>
10
- <updated>2020-02-27T00:00:00.000Z</updated>
11
- <generator>https://github.com/jpmonette/feed</generator>
12
- <link rel=\\"alternate\\" href=\\"https://docusaurus.io/myBaseUrl/blog\\"/>
13
- <subtitle>Hello Blog</subtitle>
14
- <icon>https://docusaurus.io/myBaseUrl/image/favicon.ico</icon>
15
- <rights>Copyright</rights>
16
- <entry>
17
- <title type=\\"html\\"><![CDATA[draft]]></title>
18
- <id>draft</id>
19
- <link href=\\"https://docusaurus.io/myBaseUrl/blog/draft\\"/>
20
- <updated>2020-02-27T00:00:00.000Z</updated>
21
- <summary type=\\"html\\"><![CDATA[this post should not be published yet]]></summary>
22
- </entry>
23
- <entry>
24
- <title type=\\"html\\"><![CDATA[date-matter]]></title>
25
- <id>date-matter</id>
26
- <link href=\\"https://docusaurus.io/myBaseUrl/blog/date-matter\\"/>
27
- <updated>2019-01-01T00:00:00.000Z</updated>
28
- <summary type=\\"html\\"><![CDATA[date inside front matter]]></summary>
29
- </entry>
30
- <entry>
31
- <title type=\\"html\\"><![CDATA[Happy 1st Birthday Slash! (translated)]]></title>
32
- <id>Happy 1st Birthday Slash! (translated)</id>
33
- <link href=\\"https://docusaurus.io/myBaseUrl/blog/2018/12/14/Happy-First-Birthday-Slash\\"/>
34
- <updated>2018-12-14T00:00:00.000Z</updated>
35
- <summary type=\\"html\\"><![CDATA[Happy birthday! (translated)]]></summary>
36
- </entry>
37
- </feed>"
38
- `;
39
-
40
- exports[`blogFeed rss should not show feed without posts 1`] = `null`;
41
-
42
- exports[`blogFeed rss shows feed item for each post 1`] = `
43
- "<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
44
- <rss version=\\"2.0\\">
45
- <channel>
46
- <title>Hello Blog</title>
47
- <link>https://docusaurus.io/myBaseUrl/blog</link>
48
- <description>Hello Blog</description>
49
- <lastBuildDate>Thu, 27 Feb 2020 00:00:00 GMT</lastBuildDate>
50
- <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
51
- <generator>https://github.com/jpmonette/feed</generator>
52
- <copyright>Copyright</copyright>
53
- <item>
54
- <title><![CDATA[draft]]></title>
55
- <link>https://docusaurus.io/myBaseUrl/blog/draft</link>
56
- <guid>draft</guid>
57
- <pubDate>Thu, 27 Feb 2020 00:00:00 GMT</pubDate>
58
- <description><![CDATA[this post should not be published yet]]></description>
59
- </item>
60
- <item>
61
- <title><![CDATA[date-matter]]></title>
62
- <link>https://docusaurus.io/myBaseUrl/blog/date-matter</link>
63
- <guid>date-matter</guid>
64
- <pubDate>Tue, 01 Jan 2019 00:00:00 GMT</pubDate>
65
- <description><![CDATA[date inside front matter]]></description>
66
- </item>
67
- <item>
68
- <title><![CDATA[Happy 1st Birthday Slash! (translated)]]></title>
69
- <link>https://docusaurus.io/myBaseUrl/blog/2018/12/14/Happy-First-Birthday-Slash</link>
70
- <guid>Happy 1st Birthday Slash! (translated)</guid>
71
- <pubDate>Fri, 14 Dec 2018 00:00:00 GMT</pubDate>
72
- <description><![CDATA[Happy birthday! (translated)]]></description>
73
- </item>
74
- </channel>
75
- </rss>"
76
- `;