@docusaurus/plugin-content-blog 2.0.0-beta.6f366f4b4 → 2.0.0-beta.7
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.
- package/lib/.tsbuildinfo +1 -1
- package/lib/authors.d.ts +23 -0
- package/lib/authors.js +150 -0
- package/lib/blogFrontMatter.d.ts +19 -6
- package/lib/blogFrontMatter.js +31 -19
- package/lib/blogUtils.d.ts +10 -2
- package/lib/blogUtils.js +146 -104
- package/lib/index.js +76 -70
- package/lib/markdownLoader.js +3 -3
- package/lib/pluginOptionSchema.d.ts +3 -27
- package/lib/pluginOptionSchema.js +19 -7
- package/lib/translations.d.ts +10 -0
- package/lib/translations.js +53 -0
- package/lib/types.d.ts +37 -14
- package/package.json +13 -11
- package/src/__tests__/__fixtures__/authorsMapFiles/authors.json +29 -0
- package/src/__tests__/__fixtures__/authorsMapFiles/authors.yml +27 -0
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.json +5 -0
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.yml +3 -0
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.json +3 -0
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.yml +2 -0
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.json +8 -0
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.yml +3 -0
- package/src/__tests__/__fixtures__/component/Typography.tsx +6 -0
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathEmpty/empty +0 -0
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson1/authors.json +0 -0
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson2/authors.json +0 -0
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathNestedYml/sub/folder/authors.yml +0 -0
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml1/authors.yml +0 -0
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml2/authors.yml +0 -0
- package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +3 -0
- package/src/__tests__/__fixtures__/website/blog/authors.yml +4 -0
- package/src/__tests__/__fixtures__/website/blog/mdx-blog-post.mdx +36 -0
- package/src/__tests__/__fixtures__/website/blog/simple-slug.md +4 -0
- package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md +3 -0
- package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/authors.yml +5 -0
- package/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap +41 -3
- package/src/__tests__/__snapshots__/translations.test.ts.snap +64 -0
- package/src/__tests__/authors.test.ts +608 -0
- package/src/__tests__/blogFrontMatter.test.ts +93 -16
- package/src/__tests__/blogUtils.test.ts +94 -0
- package/src/__tests__/generateBlogFeed.test.ts +4 -0
- package/src/__tests__/index.test.ts +63 -12
- package/src/__tests__/pluginOptionSchema.test.ts +3 -3
- package/src/__tests__/translations.test.ts +92 -0
- package/src/authors.ts +202 -0
- package/src/blogFrontMatter.ts +73 -33
- package/src/blogUtils.ts +205 -132
- package/src/index.ts +92 -61
- package/{index.d.ts → src/plugin-content-blog.d.ts} +35 -31
- package/src/pluginOptionSchema.ts +22 -9
- package/src/translations.ts +63 -0
- package/src/types.ts +47 -16
package/src/index.ts
CHANGED
|
@@ -22,7 +22,8 @@ import {
|
|
|
22
22
|
STATIC_DIR_NAME,
|
|
23
23
|
DEFAULT_PLUGIN_ID,
|
|
24
24
|
} from '@docusaurus/core/lib/constants';
|
|
25
|
-
import {
|
|
25
|
+
import {translateContent, getTranslationFiles} from './translations';
|
|
26
|
+
import {flatten, take} from 'lodash';
|
|
26
27
|
|
|
27
28
|
import {
|
|
28
29
|
PluginOptions,
|
|
@@ -31,9 +32,10 @@ import {
|
|
|
31
32
|
BlogItemsToMetadata,
|
|
32
33
|
TagsModule,
|
|
33
34
|
BlogPaginated,
|
|
34
|
-
BlogPost,
|
|
35
35
|
BlogContentPaths,
|
|
36
36
|
BlogMarkdownLoaderOptions,
|
|
37
|
+
MetaData,
|
|
38
|
+
Assets,
|
|
37
39
|
} from './types';
|
|
38
40
|
import {PluginOptionSchema} from './pluginOptionSchema';
|
|
39
41
|
import {
|
|
@@ -51,7 +53,9 @@ import {
|
|
|
51
53
|
generateBlogPosts,
|
|
52
54
|
getContentPathList,
|
|
53
55
|
getSourceToPermalink,
|
|
56
|
+
getBlogTags,
|
|
54
57
|
} from './blogUtils';
|
|
58
|
+
import {BlogPostFrontMatter} from './blogFrontMatter';
|
|
55
59
|
|
|
56
60
|
export default function pluginContentBlog(
|
|
57
61
|
context: LoadContext,
|
|
@@ -65,7 +69,7 @@ export default function pluginContentBlog(
|
|
|
65
69
|
|
|
66
70
|
const {
|
|
67
71
|
siteDir,
|
|
68
|
-
siteConfig: {onBrokenMarkdownLinks},
|
|
72
|
+
siteConfig: {onBrokenMarkdownLinks, baseUrl},
|
|
69
73
|
generatedFilesDir,
|
|
70
74
|
i18n: {currentLocale},
|
|
71
75
|
} = context;
|
|
@@ -93,26 +97,44 @@ export default function pluginContentBlog(
|
|
|
93
97
|
name: 'docusaurus-plugin-content-blog',
|
|
94
98
|
|
|
95
99
|
getPathsToWatch() {
|
|
96
|
-
const {include
|
|
97
|
-
|
|
100
|
+
const {include, authorsMapPath} = options;
|
|
101
|
+
const contentMarkdownGlobs = flatten(
|
|
98
102
|
getContentPathList(contentPaths).map((contentPath) => {
|
|
99
103
|
return include.map((pattern) => `${contentPath}/${pattern}`);
|
|
100
104
|
}),
|
|
101
105
|
);
|
|
106
|
+
|
|
107
|
+
// TODO: we should read this path in plugin! but plugins do not support async init for now :'(
|
|
108
|
+
// const authorsMapFilePath = await getAuthorsMapFilePath({authorsMapPath,contentPaths,});
|
|
109
|
+
// simplified impl, better than nothing for now:
|
|
110
|
+
const authorsMapFilePath = path.join(
|
|
111
|
+
contentPaths.contentPath,
|
|
112
|
+
authorsMapPath,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
return [authorsMapFilePath, ...contentMarkdownGlobs];
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
async getTranslationFiles() {
|
|
119
|
+
return getTranslationFiles(options);
|
|
102
120
|
},
|
|
103
121
|
|
|
104
122
|
// Fetches blog contents and returns metadata for the necessary routes.
|
|
105
123
|
async loadContent() {
|
|
106
|
-
const {
|
|
124
|
+
const {
|
|
125
|
+
postsPerPage: postsPerPageOption,
|
|
126
|
+
routeBasePath,
|
|
127
|
+
tagsBasePath,
|
|
128
|
+
blogDescription,
|
|
129
|
+
blogTitle,
|
|
130
|
+
blogSidebarTitle,
|
|
131
|
+
} = options;
|
|
107
132
|
|
|
108
|
-
const blogPosts
|
|
109
|
-
contentPaths,
|
|
110
|
-
context,
|
|
111
|
-
options,
|
|
112
|
-
);
|
|
133
|
+
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
113
134
|
|
|
114
135
|
if (!blogPosts.length) {
|
|
115
136
|
return {
|
|
137
|
+
blogSidebarTitle,
|
|
116
138
|
blogPosts: [],
|
|
117
139
|
blogListPaginated: [],
|
|
118
140
|
blogTags: {},
|
|
@@ -143,18 +165,17 @@ export default function pluginContentBlog(
|
|
|
143
165
|
// Blog pagination routes.
|
|
144
166
|
// Example: `/blog`, `/blog/page/1`, `/blog/page/2`
|
|
145
167
|
const totalCount = blogPosts.length;
|
|
168
|
+
const postsPerPage =
|
|
169
|
+
postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption;
|
|
146
170
|
const numberOfPages = Math.ceil(totalCount / postsPerPage);
|
|
147
|
-
const
|
|
148
|
-
siteConfig: {baseUrl = ''},
|
|
149
|
-
} = context;
|
|
150
|
-
const basePageUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
171
|
+
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
151
172
|
|
|
152
173
|
const blogListPaginated: BlogPaginated[] = [];
|
|
153
174
|
|
|
154
175
|
function blogPaginationPermalink(page: number) {
|
|
155
176
|
return page > 0
|
|
156
|
-
? normalizeUrl([
|
|
157
|
-
:
|
|
177
|
+
? normalizeUrl([baseBlogUrl, `page/${page + 1}`])
|
|
178
|
+
: baseBlogUrl;
|
|
158
179
|
}
|
|
159
180
|
|
|
160
181
|
for (let page = 0; page < numberOfPages; page += 1) {
|
|
@@ -170,8 +191,8 @@ export default function pluginContentBlog(
|
|
|
170
191
|
page < numberOfPages - 1
|
|
171
192
|
? blogPaginationPermalink(page + 1)
|
|
172
193
|
: null,
|
|
173
|
-
blogDescription
|
|
174
|
-
blogTitle
|
|
194
|
+
blogDescription,
|
|
195
|
+
blogTitle,
|
|
175
196
|
},
|
|
176
197
|
items: blogPosts
|
|
177
198
|
.slice(page * postsPerPage, (page + 1) * postsPerPage)
|
|
@@ -179,46 +200,15 @@ export default function pluginContentBlog(
|
|
|
179
200
|
});
|
|
180
201
|
}
|
|
181
202
|
|
|
182
|
-
const blogTags: BlogTags =
|
|
183
|
-
const tagsPath = normalizeUrl([basePageUrl, 'tags']);
|
|
184
|
-
blogPosts.forEach((blogPost) => {
|
|
185
|
-
const {tags} = blogPost.metadata;
|
|
186
|
-
if (!tags || tags.length === 0) {
|
|
187
|
-
// TODO: Extract tags out into a separate plugin.
|
|
188
|
-
// eslint-disable-next-line no-param-reassign
|
|
189
|
-
blogPost.metadata.tags = [];
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
203
|
+
const blogTags: BlogTags = getBlogTags(blogPosts);
|
|
192
204
|
|
|
193
|
-
|
|
194
|
-
blogPost.metadata.tags = tags.map((tag) => {
|
|
195
|
-
if (typeof tag === 'string') {
|
|
196
|
-
const normalizedTag = kebabCase(tag);
|
|
197
|
-
const permalink = normalizeUrl([tagsPath, normalizedTag]);
|
|
198
|
-
if (!blogTags[normalizedTag]) {
|
|
199
|
-
blogTags[normalizedTag] = {
|
|
200
|
-
// Will only use the name of the first occurrence of the tag.
|
|
201
|
-
name: tag.toLowerCase(),
|
|
202
|
-
items: [],
|
|
203
|
-
permalink,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
blogTags[normalizedTag].items.push(blogPost.id);
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
label: tag,
|
|
211
|
-
permalink,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
return tag;
|
|
215
|
-
});
|
|
216
|
-
});
|
|
205
|
+
const tagsPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
|
|
217
206
|
|
|
218
207
|
const blogTagsListPath =
|
|
219
208
|
Object.keys(blogTags).length > 0 ? tagsPath : null;
|
|
220
209
|
|
|
221
210
|
return {
|
|
211
|
+
blogSidebarTitle,
|
|
222
212
|
blogPosts,
|
|
223
213
|
blogListPaginated,
|
|
224
214
|
blogTags,
|
|
@@ -236,10 +226,13 @@ export default function pluginContentBlog(
|
|
|
236
226
|
blogPostComponent,
|
|
237
227
|
blogTagsListComponent,
|
|
238
228
|
blogTagsPostsComponent,
|
|
229
|
+
routeBasePath,
|
|
230
|
+
archiveBasePath,
|
|
239
231
|
} = options;
|
|
240
232
|
|
|
241
233
|
const {addRoute, createData} = actions;
|
|
242
234
|
const {
|
|
235
|
+
blogSidebarTitle,
|
|
243
236
|
blogPosts,
|
|
244
237
|
blogListPaginated,
|
|
245
238
|
blogTags,
|
|
@@ -253,6 +246,26 @@ export default function pluginContentBlog(
|
|
|
253
246
|
? blogPosts
|
|
254
247
|
: take(blogPosts, options.blogSidebarCount);
|
|
255
248
|
|
|
249
|
+
const archiveUrl = normalizeUrl([
|
|
250
|
+
baseUrl,
|
|
251
|
+
routeBasePath,
|
|
252
|
+
archiveBasePath,
|
|
253
|
+
]);
|
|
254
|
+
|
|
255
|
+
// creates a blog archive route
|
|
256
|
+
const archiveProp = await createData(
|
|
257
|
+
`${docuHash(archiveUrl)}.json`,
|
|
258
|
+
JSON.stringify({blogPosts}, null, 2),
|
|
259
|
+
);
|
|
260
|
+
addRoute({
|
|
261
|
+
path: archiveUrl,
|
|
262
|
+
component: '@theme/BlogArchivePage',
|
|
263
|
+
exact: true,
|
|
264
|
+
modules: {
|
|
265
|
+
archive: aliasedSource(archiveProp),
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
|
|
256
269
|
// This prop is useful to provide the blog list sidebar
|
|
257
270
|
const sidebarProp = await createData(
|
|
258
271
|
// Note that this created data path must be in sync with
|
|
@@ -260,7 +273,7 @@ export default function pluginContentBlog(
|
|
|
260
273
|
`blog-post-list-prop-${pluginId}.json`,
|
|
261
274
|
JSON.stringify(
|
|
262
275
|
{
|
|
263
|
-
title:
|
|
276
|
+
title: blogSidebarTitle,
|
|
264
277
|
items: sidebarBlogPosts.map((blogPost) => ({
|
|
265
278
|
title: blogPost.metadata.title,
|
|
266
279
|
permalink: blogPost.metadata.permalink,
|
|
@@ -341,6 +354,7 @@ export default function pluginContentBlog(
|
|
|
341
354
|
Object.keys(blogTags).map(async (tag) => {
|
|
342
355
|
const {name, items, permalink} = blogTags[tag];
|
|
343
356
|
|
|
357
|
+
// Refactor all this, see docs implementation
|
|
344
358
|
tagsModule[tag] = {
|
|
345
359
|
allTagsPath: blogTagsListPath,
|
|
346
360
|
slug: tag,
|
|
@@ -397,6 +411,10 @@ export default function pluginContentBlog(
|
|
|
397
411
|
}
|
|
398
412
|
},
|
|
399
413
|
|
|
414
|
+
translateContent({content, translationFiles}) {
|
|
415
|
+
return translateContent(content, translationFiles);
|
|
416
|
+
},
|
|
417
|
+
|
|
400
418
|
configureWebpack(
|
|
401
419
|
_config: Configuration,
|
|
402
420
|
isServer: boolean,
|
|
@@ -467,6 +485,22 @@ export default function pluginContentBlog(
|
|
|
467
485
|
// For blog posts a title in markdown is always removed
|
|
468
486
|
// Blog posts title are rendered separately
|
|
469
487
|
removeContentTitle: true,
|
|
488
|
+
|
|
489
|
+
// Assets allow to convert some relative images paths to require() calls
|
|
490
|
+
createAssets: ({
|
|
491
|
+
frontMatter,
|
|
492
|
+
metadata,
|
|
493
|
+
}: {
|
|
494
|
+
frontMatter: BlogPostFrontMatter;
|
|
495
|
+
metadata: MetaData;
|
|
496
|
+
}): Assets => {
|
|
497
|
+
return {
|
|
498
|
+
image: frontMatter.image,
|
|
499
|
+
authorsImageUrls: metadata.authors.map(
|
|
500
|
+
(author) => author.imageURL,
|
|
501
|
+
),
|
|
502
|
+
};
|
|
503
|
+
},
|
|
470
504
|
},
|
|
471
505
|
},
|
|
472
506
|
{
|
|
@@ -481,7 +515,7 @@ export default function pluginContentBlog(
|
|
|
481
515
|
},
|
|
482
516
|
|
|
483
517
|
async postBuild({outDir}: Props) {
|
|
484
|
-
if (!options.feedOptions
|
|
518
|
+
if (!options.feedOptions.type) {
|
|
485
519
|
return;
|
|
486
520
|
}
|
|
487
521
|
|
|
@@ -520,20 +554,17 @@ export default function pluginContentBlog(
|
|
|
520
554
|
}
|
|
521
555
|
|
|
522
556
|
const feedTypes = options.feedOptions.type;
|
|
523
|
-
const
|
|
524
|
-
siteConfig: {title},
|
|
525
|
-
baseUrl,
|
|
526
|
-
} = context;
|
|
557
|
+
const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
|
|
527
558
|
const feedsConfig = {
|
|
528
559
|
rss: {
|
|
529
560
|
type: 'application/rss+xml',
|
|
530
561
|
path: 'rss.xml',
|
|
531
|
-
title: `${
|
|
562
|
+
title: `${feedTitle} RSS Feed`,
|
|
532
563
|
},
|
|
533
564
|
atom: {
|
|
534
565
|
type: 'application/atom+xml',
|
|
535
566
|
path: 'atom.xml',
|
|
536
|
-
title: `${
|
|
567
|
+
title: `${feedTitle} Atom Feed`,
|
|
537
568
|
},
|
|
538
569
|
};
|
|
539
570
|
const headTags: HtmlTags = [];
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
declare module '@docusaurus/plugin-content-blog' {
|
|
9
|
+
export type Options = Partial<import('./types').UserPluginOptions>;
|
|
10
|
+
}
|
|
10
11
|
|
|
11
12
|
declare module '@theme/BlogSidebar' {
|
|
12
13
|
export type BlogSidebarItem = {title: string; permalink: string};
|
|
@@ -15,32 +16,20 @@ declare module '@theme/BlogSidebar' {
|
|
|
15
16
|
items: BlogSidebarItem[];
|
|
16
17
|
};
|
|
17
18
|
|
|
18
|
-
export
|
|
19
|
+
export interface Props {
|
|
19
20
|
readonly sidebar: BlogSidebar;
|
|
20
|
-
}
|
|
21
|
+
}
|
|
21
22
|
|
|
22
23
|
const BlogSidebar: (props: Props) => JSX.Element;
|
|
23
24
|
export default BlogSidebar;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
declare module '@theme/BlogPostPage' {
|
|
27
|
-
import type {TOCItem} from '@docusaurus/types';
|
|
28
28
|
import type {BlogSidebar} from '@theme/BlogSidebar';
|
|
29
|
+
import type {TOCItem} from '@docusaurus/types';
|
|
29
30
|
|
|
30
|
-
export type FrontMatter =
|
|
31
|
-
|
|
32
|
-
readonly author?: string;
|
|
33
|
-
readonly image?: string;
|
|
34
|
-
readonly tags?: readonly string[];
|
|
35
|
-
readonly keywords?: readonly string[];
|
|
36
|
-
readonly author_url?: string;
|
|
37
|
-
readonly authorURL?: string;
|
|
38
|
-
readonly author_title?: string;
|
|
39
|
-
readonly authorTitle?: string;
|
|
40
|
-
readonly author_image_url?: string;
|
|
41
|
-
readonly authorImageURL?: string;
|
|
42
|
-
readonly hide_table_of_contents?: boolean;
|
|
43
|
-
};
|
|
31
|
+
export type FrontMatter = import('./blogFrontMatter').BlogPostFrontMatter;
|
|
32
|
+
export type Assets = import('./types').Assets;
|
|
44
33
|
|
|
45
34
|
export type Metadata = {
|
|
46
35
|
readonly title: string;
|
|
@@ -53,6 +42,7 @@ declare module '@theme/BlogPostPage' {
|
|
|
53
42
|
readonly truncated?: string;
|
|
54
43
|
readonly nextItem?: {readonly title: string; readonly permalink: string};
|
|
55
44
|
readonly prevItem?: {readonly title: string; readonly permalink: string};
|
|
45
|
+
readonly authors: import('./types').Author[];
|
|
56
46
|
readonly tags: readonly {
|
|
57
47
|
readonly label: string;
|
|
58
48
|
readonly permalink: string;
|
|
@@ -61,15 +51,16 @@ declare module '@theme/BlogPostPage' {
|
|
|
61
51
|
|
|
62
52
|
export type Content = {
|
|
63
53
|
readonly frontMatter: FrontMatter;
|
|
54
|
+
readonly assets: Assets;
|
|
64
55
|
readonly metadata: Metadata;
|
|
65
56
|
readonly toc: readonly TOCItem[];
|
|
66
57
|
(): JSX.Element;
|
|
67
58
|
};
|
|
68
59
|
|
|
69
|
-
export
|
|
60
|
+
export interface Props {
|
|
70
61
|
readonly sidebar: BlogSidebar;
|
|
71
62
|
readonly content: Content;
|
|
72
|
-
}
|
|
63
|
+
}
|
|
73
64
|
|
|
74
65
|
const BlogPostPage: (props: Props) => JSX.Element;
|
|
75
66
|
export default BlogPostPage;
|
|
@@ -79,10 +70,6 @@ declare module '@theme/BlogListPage' {
|
|
|
79
70
|
import type {Content} from '@theme/BlogPostPage';
|
|
80
71
|
import type {BlogSidebar} from '@theme/BlogSidebar';
|
|
81
72
|
|
|
82
|
-
export type Item = {
|
|
83
|
-
readonly content: () => JSX.Element;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
73
|
export type Metadata = {
|
|
87
74
|
readonly blogTitle: string;
|
|
88
75
|
readonly blogDescription: string;
|
|
@@ -95,11 +82,11 @@ declare module '@theme/BlogListPage' {
|
|
|
95
82
|
readonly totalPages: number;
|
|
96
83
|
};
|
|
97
84
|
|
|
98
|
-
export
|
|
85
|
+
export interface Props {
|
|
99
86
|
readonly sidebar: BlogSidebar;
|
|
100
87
|
readonly metadata: Metadata;
|
|
101
88
|
readonly items: readonly {readonly content: Content}[];
|
|
102
|
-
}
|
|
89
|
+
}
|
|
103
90
|
|
|
104
91
|
const BlogListPage: (props: Props) => JSX.Element;
|
|
105
92
|
export default BlogListPage;
|
|
@@ -116,10 +103,10 @@ declare module '@theme/BlogTagsListPage' {
|
|
|
116
103
|
slug: string;
|
|
117
104
|
};
|
|
118
105
|
|
|
119
|
-
export
|
|
106
|
+
export interface Props {
|
|
120
107
|
readonly sidebar: BlogSidebar;
|
|
121
108
|
readonly tags: Readonly<Record<string, Tag>>;
|
|
122
|
-
}
|
|
109
|
+
}
|
|
123
110
|
|
|
124
111
|
const BlogTagsListPage: (props: Props) => JSX.Element;
|
|
125
112
|
export default BlogTagsListPage;
|
|
@@ -130,9 +117,26 @@ declare module '@theme/BlogTagsPostsPage' {
|
|
|
130
117
|
import type {Tag} from '@theme/BlogTagsListPage';
|
|
131
118
|
import type {Content} from '@theme/BlogPostPage';
|
|
132
119
|
|
|
133
|
-
export
|
|
120
|
+
export interface Props {
|
|
134
121
|
readonly sidebar: BlogSidebar;
|
|
135
122
|
readonly metadata: Tag;
|
|
136
123
|
readonly items: readonly {readonly content: Content}[];
|
|
137
|
-
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const BlogTagsPostsPage: (props: Props) => JSX.Element;
|
|
127
|
+
export default BlogTagsPostsPage;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
declare module '@theme/BlogArchivePage' {
|
|
131
|
+
import type {Content} from '@theme/BlogPostPage';
|
|
132
|
+
|
|
133
|
+
export type ArchiveBlogPost = Content;
|
|
134
|
+
|
|
135
|
+
export interface Props {
|
|
136
|
+
readonly archive: {
|
|
137
|
+
readonly blogPosts: readonly ArchiveBlogPost[];
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export default function BlogArchivePage(props: Props): JSX.Element;
|
|
138
142
|
}
|
|
@@ -13,9 +13,10 @@ import {
|
|
|
13
13
|
URISchema,
|
|
14
14
|
} from '@docusaurus/utils-validation';
|
|
15
15
|
import {GlobExcludeDefault} from '@docusaurus/utils';
|
|
16
|
+
import {PluginOptions} from './types';
|
|
16
17
|
|
|
17
|
-
export const DEFAULT_OPTIONS = {
|
|
18
|
-
feedOptions: {type: ['rss', 'atom']},
|
|
18
|
+
export const DEFAULT_OPTIONS: PluginOptions = {
|
|
19
|
+
feedOptions: {type: ['rss', 'atom'], copyright: ''},
|
|
19
20
|
beforeDefaultRehypePlugins: [],
|
|
20
21
|
beforeDefaultRemarkPlugins: [],
|
|
21
22
|
admonitions: {},
|
|
@@ -32,24 +33,28 @@ export const DEFAULT_OPTIONS = {
|
|
|
32
33
|
blogSidebarCount: 5,
|
|
33
34
|
blogSidebarTitle: 'Recent posts',
|
|
34
35
|
postsPerPage: 10,
|
|
35
|
-
include: ['
|
|
36
|
+
include: ['**/*.{md,mdx}'],
|
|
36
37
|
exclude: GlobExcludeDefault,
|
|
37
38
|
routeBasePath: 'blog',
|
|
39
|
+
tagsBasePath: 'tags',
|
|
40
|
+
archiveBasePath: 'archive',
|
|
38
41
|
path: 'blog',
|
|
39
42
|
editLocalizedFiles: false,
|
|
43
|
+
authorsMapPath: 'authors.yml',
|
|
40
44
|
};
|
|
41
45
|
|
|
42
|
-
export const PluginOptionSchema = Joi.object({
|
|
46
|
+
export const PluginOptionSchema = Joi.object<PluginOptions>({
|
|
43
47
|
path: Joi.string().default(DEFAULT_OPTIONS.path),
|
|
48
|
+
archiveBasePath: Joi.string().default(DEFAULT_OPTIONS.archiveBasePath),
|
|
44
49
|
routeBasePath: Joi.string()
|
|
45
50
|
// '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
|
|
46
51
|
// .allow('')
|
|
47
52
|
.default(DEFAULT_OPTIONS.routeBasePath),
|
|
53
|
+
tagsBasePath: Joi.string().default(DEFAULT_OPTIONS.tagsBasePath),
|
|
48
54
|
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
|
|
49
55
|
exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude),
|
|
50
|
-
postsPerPage: Joi.
|
|
51
|
-
.integer()
|
|
52
|
-
.min(1)
|
|
56
|
+
postsPerPage: Joi.alternatives()
|
|
57
|
+
.try(Joi.equal('ALL').required(), Joi.number().integer().min(1).required())
|
|
53
58
|
.default(DEFAULT_OPTIONS.postsPerPage),
|
|
54
59
|
blogListComponent: Joi.string().default(DEFAULT_OPTIONS.blogListComponent),
|
|
55
60
|
blogPostComponent: Joi.string().default(DEFAULT_OPTIONS.blogPostComponent),
|
|
@@ -64,7 +69,7 @@ export const PluginOptionSchema = Joi.object({
|
|
|
64
69
|
.allow('')
|
|
65
70
|
.default(DEFAULT_OPTIONS.blogDescription),
|
|
66
71
|
blogSidebarCount: Joi.alternatives()
|
|
67
|
-
.try(Joi.equal('ALL').required(), Joi.number().required())
|
|
72
|
+
.try(Joi.equal('ALL').required(), Joi.number().integer().min(0).required())
|
|
68
73
|
.default(DEFAULT_OPTIONS.blogSidebarCount),
|
|
69
74
|
blogSidebarTitle: Joi.string().default(DEFAULT_OPTIONS.blogSidebarTitle),
|
|
70
75
|
showReadingTime: Joi.bool().default(DEFAULT_OPTIONS.showReadingTime),
|
|
@@ -97,7 +102,15 @@ export const PluginOptionSchema = Joi.object({
|
|
|
97
102
|
.default(DEFAULT_OPTIONS.feedOptions.type),
|
|
98
103
|
title: Joi.string().allow(''),
|
|
99
104
|
description: Joi.string().allow(''),
|
|
100
|
-
|
|
105
|
+
// only add default value when user actually wants a feed (type is not null)
|
|
106
|
+
copyright: Joi.when('type', {
|
|
107
|
+
is: Joi.any().valid(null),
|
|
108
|
+
then: Joi.string().optional(),
|
|
109
|
+
otherwise: Joi.string()
|
|
110
|
+
.allow('')
|
|
111
|
+
.default(DEFAULT_OPTIONS.feedOptions.copyright),
|
|
112
|
+
}),
|
|
101
113
|
language: Joi.string(),
|
|
102
114
|
}).default(DEFAULT_OPTIONS.feedOptions),
|
|
115
|
+
authorsMapPath: Joi.string().default(DEFAULT_OPTIONS.authorsMapPath),
|
|
103
116
|
});
|
|
@@ -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
|
+
}
|