@docusaurus/plugin-content-blog 2.0.0-beta.8e9b829d9 → 2.0.0-beta.9
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 -4
- package/lib/blogUtils.js +141 -135
- package/lib/feed.d.ts +20 -0
- package/lib/feed.js +90 -0
- package/lib/index.js +86 -85
- package/lib/markdownLoader.d.ts +3 -6
- package/lib/markdownLoader.js +5 -5
- package/lib/pluginOptionSchema.d.ts +3 -27
- package/lib/pluginOptionSchema.js +21 -7
- package/lib/translations.d.ts +10 -0
- package/lib/translations.js +53 -0
- package/lib/types.d.ts +52 -14
- package/package.json +15 -13
- 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/mdx-require-blog-post.mdx +14 -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__/__fixtures__/website/static/img/docusaurus.png +0 -0
- package/src/__tests__/__snapshots__/{generateBlogFeed.test.ts.snap → feed.test.ts.snap} +55 -7
- 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 → feed.test.ts} +32 -8
- package/src/__tests__/index.test.ts +73 -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 +201 -180
- package/src/feed.ts +129 -0
- package/src/index.ts +105 -88
- package/src/markdownLoader.ts +8 -12
- package/{index.d.ts → src/plugin-content-blog.d.ts} +35 -31
- package/src/pluginOptionSchema.ts +24 -9
- package/src/translations.ts +63 -0
- package/src/types.ts +67 -16
package/src/index.ts
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import fs from 'fs-extra';
|
|
9
8
|
import path from 'path';
|
|
10
9
|
import admonitions from 'remark-admonitions';
|
|
11
10
|
import {
|
|
@@ -22,7 +21,7 @@ import {
|
|
|
22
21
|
STATIC_DIR_NAME,
|
|
23
22
|
DEFAULT_PLUGIN_ID,
|
|
24
23
|
} from '@docusaurus/core/lib/constants';
|
|
25
|
-
import {
|
|
24
|
+
import {translateContent, getTranslationFiles} from './translations';
|
|
26
25
|
|
|
27
26
|
import {
|
|
28
27
|
PluginOptions,
|
|
@@ -31,9 +30,10 @@ import {
|
|
|
31
30
|
BlogItemsToMetadata,
|
|
32
31
|
TagsModule,
|
|
33
32
|
BlogPaginated,
|
|
34
|
-
BlogPost,
|
|
35
33
|
BlogContentPaths,
|
|
36
34
|
BlogMarkdownLoaderOptions,
|
|
35
|
+
MetaData,
|
|
36
|
+
Assets,
|
|
37
37
|
} from './types';
|
|
38
38
|
import {PluginOptionSchema} from './pluginOptionSchema';
|
|
39
39
|
import {
|
|
@@ -47,11 +47,13 @@ import {
|
|
|
47
47
|
} from '@docusaurus/types';
|
|
48
48
|
import {Configuration} from 'webpack';
|
|
49
49
|
import {
|
|
50
|
-
generateBlogFeed,
|
|
51
50
|
generateBlogPosts,
|
|
52
51
|
getContentPathList,
|
|
53
52
|
getSourceToPermalink,
|
|
53
|
+
getBlogTags,
|
|
54
54
|
} from './blogUtils';
|
|
55
|
+
import {BlogPostFrontMatter} from './blogFrontMatter';
|
|
56
|
+
import {createBlogFeedFiles} from './feed';
|
|
55
57
|
|
|
56
58
|
export default function pluginContentBlog(
|
|
57
59
|
context: LoadContext,
|
|
@@ -65,10 +67,11 @@ export default function pluginContentBlog(
|
|
|
65
67
|
|
|
66
68
|
const {
|
|
67
69
|
siteDir,
|
|
68
|
-
siteConfig
|
|
70
|
+
siteConfig,
|
|
69
71
|
generatedFilesDir,
|
|
70
72
|
i18n: {currentLocale},
|
|
71
73
|
} = context;
|
|
74
|
+
const {onBrokenMarkdownLinks, baseUrl} = siteConfig;
|
|
72
75
|
|
|
73
76
|
const contentPaths: BlogContentPaths = {
|
|
74
77
|
contentPath: path.resolve(siteDir, options.path),
|
|
@@ -93,26 +96,42 @@ export default function pluginContentBlog(
|
|
|
93
96
|
name: 'docusaurus-plugin-content-blog',
|
|
94
97
|
|
|
95
98
|
getPathsToWatch() {
|
|
96
|
-
const {include
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
const {include, authorsMapPath} = options;
|
|
100
|
+
const contentMarkdownGlobs = getContentPathList(contentPaths).flatMap(
|
|
101
|
+
(contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// TODO: we should read this path in plugin! but plugins do not support async init for now :'(
|
|
105
|
+
// const authorsMapFilePath = await getAuthorsMapFilePath({authorsMapPath,contentPaths,});
|
|
106
|
+
// simplified impl, better than nothing for now:
|
|
107
|
+
const authorsMapFilePath = path.join(
|
|
108
|
+
contentPaths.contentPath,
|
|
109
|
+
authorsMapPath,
|
|
101
110
|
);
|
|
111
|
+
|
|
112
|
+
return [authorsMapFilePath, ...contentMarkdownGlobs];
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
async getTranslationFiles() {
|
|
116
|
+
return getTranslationFiles(options);
|
|
102
117
|
},
|
|
103
118
|
|
|
104
119
|
// Fetches blog contents and returns metadata for the necessary routes.
|
|
105
120
|
async loadContent() {
|
|
106
|
-
const {
|
|
121
|
+
const {
|
|
122
|
+
postsPerPage: postsPerPageOption,
|
|
123
|
+
routeBasePath,
|
|
124
|
+
tagsBasePath,
|
|
125
|
+
blogDescription,
|
|
126
|
+
blogTitle,
|
|
127
|
+
blogSidebarTitle,
|
|
128
|
+
} = options;
|
|
107
129
|
|
|
108
|
-
const blogPosts
|
|
109
|
-
contentPaths,
|
|
110
|
-
context,
|
|
111
|
-
options,
|
|
112
|
-
);
|
|
130
|
+
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
113
131
|
|
|
114
132
|
if (!blogPosts.length) {
|
|
115
133
|
return {
|
|
134
|
+
blogSidebarTitle,
|
|
116
135
|
blogPosts: [],
|
|
117
136
|
blogListPaginated: [],
|
|
118
137
|
blogTags: {},
|
|
@@ -143,18 +162,17 @@ export default function pluginContentBlog(
|
|
|
143
162
|
// Blog pagination routes.
|
|
144
163
|
// Example: `/blog`, `/blog/page/1`, `/blog/page/2`
|
|
145
164
|
const totalCount = blogPosts.length;
|
|
165
|
+
const postsPerPage =
|
|
166
|
+
postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption;
|
|
146
167
|
const numberOfPages = Math.ceil(totalCount / postsPerPage);
|
|
147
|
-
const
|
|
148
|
-
siteConfig: {baseUrl = ''},
|
|
149
|
-
} = context;
|
|
150
|
-
const basePageUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
168
|
+
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
151
169
|
|
|
152
170
|
const blogListPaginated: BlogPaginated[] = [];
|
|
153
171
|
|
|
154
172
|
function blogPaginationPermalink(page: number) {
|
|
155
173
|
return page > 0
|
|
156
|
-
? normalizeUrl([
|
|
157
|
-
:
|
|
174
|
+
? normalizeUrl([baseBlogUrl, `page/${page + 1}`])
|
|
175
|
+
: baseBlogUrl;
|
|
158
176
|
}
|
|
159
177
|
|
|
160
178
|
for (let page = 0; page < numberOfPages; page += 1) {
|
|
@@ -170,8 +188,8 @@ export default function pluginContentBlog(
|
|
|
170
188
|
page < numberOfPages - 1
|
|
171
189
|
? blogPaginationPermalink(page + 1)
|
|
172
190
|
: null,
|
|
173
|
-
blogDescription
|
|
174
|
-
blogTitle
|
|
191
|
+
blogDescription,
|
|
192
|
+
blogTitle,
|
|
175
193
|
},
|
|
176
194
|
items: blogPosts
|
|
177
195
|
.slice(page * postsPerPage, (page + 1) * postsPerPage)
|
|
@@ -179,46 +197,15 @@ export default function pluginContentBlog(
|
|
|
179
197
|
});
|
|
180
198
|
}
|
|
181
199
|
|
|
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
|
-
}
|
|
200
|
+
const blogTags: BlogTags = getBlogTags(blogPosts);
|
|
192
201
|
|
|
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
|
-
});
|
|
202
|
+
const tagsPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
|
|
217
203
|
|
|
218
204
|
const blogTagsListPath =
|
|
219
205
|
Object.keys(blogTags).length > 0 ? tagsPath : null;
|
|
220
206
|
|
|
221
207
|
return {
|
|
208
|
+
blogSidebarTitle,
|
|
222
209
|
blogPosts,
|
|
223
210
|
blogListPaginated,
|
|
224
211
|
blogTags,
|
|
@@ -236,10 +223,13 @@ export default function pluginContentBlog(
|
|
|
236
223
|
blogPostComponent,
|
|
237
224
|
blogTagsListComponent,
|
|
238
225
|
blogTagsPostsComponent,
|
|
226
|
+
routeBasePath,
|
|
227
|
+
archiveBasePath,
|
|
239
228
|
} = options;
|
|
240
229
|
|
|
241
230
|
const {addRoute, createData} = actions;
|
|
242
231
|
const {
|
|
232
|
+
blogSidebarTitle,
|
|
243
233
|
blogPosts,
|
|
244
234
|
blogListPaginated,
|
|
245
235
|
blogTags,
|
|
@@ -251,7 +241,27 @@ export default function pluginContentBlog(
|
|
|
251
241
|
const sidebarBlogPosts =
|
|
252
242
|
options.blogSidebarCount === 'ALL'
|
|
253
243
|
? blogPosts
|
|
254
|
-
:
|
|
244
|
+
: blogPosts.slice(0, options.blogSidebarCount);
|
|
245
|
+
|
|
246
|
+
const archiveUrl = normalizeUrl([
|
|
247
|
+
baseUrl,
|
|
248
|
+
routeBasePath,
|
|
249
|
+
archiveBasePath,
|
|
250
|
+
]);
|
|
251
|
+
|
|
252
|
+
// creates a blog archive route
|
|
253
|
+
const archiveProp = await createData(
|
|
254
|
+
`${docuHash(archiveUrl)}.json`,
|
|
255
|
+
JSON.stringify({blogPosts}, null, 2),
|
|
256
|
+
);
|
|
257
|
+
addRoute({
|
|
258
|
+
path: archiveUrl,
|
|
259
|
+
component: '@theme/BlogArchivePage',
|
|
260
|
+
exact: true,
|
|
261
|
+
modules: {
|
|
262
|
+
archive: aliasedSource(archiveProp),
|
|
263
|
+
},
|
|
264
|
+
});
|
|
255
265
|
|
|
256
266
|
// This prop is useful to provide the blog list sidebar
|
|
257
267
|
const sidebarProp = await createData(
|
|
@@ -260,7 +270,7 @@ export default function pluginContentBlog(
|
|
|
260
270
|
`blog-post-list-prop-${pluginId}.json`,
|
|
261
271
|
JSON.stringify(
|
|
262
272
|
{
|
|
263
|
-
title:
|
|
273
|
+
title: blogSidebarTitle,
|
|
264
274
|
items: sidebarBlogPosts.map((blogPost) => ({
|
|
265
275
|
title: blogPost.metadata.title,
|
|
266
276
|
permalink: blogPost.metadata.permalink,
|
|
@@ -341,6 +351,7 @@ export default function pluginContentBlog(
|
|
|
341
351
|
Object.keys(blogTags).map(async (tag) => {
|
|
342
352
|
const {name, items, permalink} = blogTags[tag];
|
|
343
353
|
|
|
354
|
+
// Refactor all this, see docs implementation
|
|
344
355
|
tagsModule[tag] = {
|
|
345
356
|
allTagsPath: blogTagsListPath,
|
|
346
357
|
slug: tag,
|
|
@@ -397,6 +408,10 @@ export default function pluginContentBlog(
|
|
|
397
408
|
}
|
|
398
409
|
},
|
|
399
410
|
|
|
411
|
+
translateContent({content, translationFiles}) {
|
|
412
|
+
return translateContent(content, translationFiles);
|
|
413
|
+
},
|
|
414
|
+
|
|
400
415
|
configureWebpack(
|
|
401
416
|
_config: Configuration,
|
|
402
417
|
isServer: boolean,
|
|
@@ -467,6 +482,22 @@ export default function pluginContentBlog(
|
|
|
467
482
|
// For blog posts a title in markdown is always removed
|
|
468
483
|
// Blog posts title are rendered separately
|
|
469
484
|
removeContentTitle: true,
|
|
485
|
+
|
|
486
|
+
// Assets allow to convert some relative images paths to require() calls
|
|
487
|
+
createAssets: ({
|
|
488
|
+
frontMatter,
|
|
489
|
+
metadata,
|
|
490
|
+
}: {
|
|
491
|
+
frontMatter: BlogPostFrontMatter;
|
|
492
|
+
metadata: MetaData;
|
|
493
|
+
}): Assets => {
|
|
494
|
+
return {
|
|
495
|
+
image: frontMatter.image,
|
|
496
|
+
authorsImageUrls: metadata.authors.map(
|
|
497
|
+
(author) => author.imageURL,
|
|
498
|
+
),
|
|
499
|
+
};
|
|
500
|
+
},
|
|
470
501
|
},
|
|
471
502
|
},
|
|
472
503
|
{
|
|
@@ -481,33 +512,22 @@ export default function pluginContentBlog(
|
|
|
481
512
|
},
|
|
482
513
|
|
|
483
514
|
async postBuild({outDir}: Props) {
|
|
484
|
-
if (!options.feedOptions
|
|
515
|
+
if (!options.feedOptions.type) {
|
|
485
516
|
return;
|
|
486
517
|
}
|
|
487
518
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
519
|
+
// TODO: we shouldn't need to re-read the posts here!
|
|
520
|
+
// postBuild should receive loadedContent
|
|
521
|
+
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
522
|
+
if (!blogPosts.length) {
|
|
491
523
|
return;
|
|
492
524
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
outDir,
|
|
500
|
-
options.routeBasePath,
|
|
501
|
-
`${feedType}.xml`,
|
|
502
|
-
);
|
|
503
|
-
const feedContent = feedType === 'rss' ? feed.rss2() : feed.atom1();
|
|
504
|
-
try {
|
|
505
|
-
await fs.outputFile(feedPath, feedContent);
|
|
506
|
-
} catch (err) {
|
|
507
|
-
throw new Error(`Generating ${feedType} feed failed: ${err}.`);
|
|
508
|
-
}
|
|
509
|
-
}),
|
|
510
|
-
);
|
|
525
|
+
await createBlogFeedFiles({
|
|
526
|
+
blogPosts,
|
|
527
|
+
options,
|
|
528
|
+
outDir,
|
|
529
|
+
siteConfig,
|
|
530
|
+
});
|
|
511
531
|
},
|
|
512
532
|
|
|
513
533
|
injectHtmlTags({content}) {
|
|
@@ -520,20 +540,17 @@ export default function pluginContentBlog(
|
|
|
520
540
|
}
|
|
521
541
|
|
|
522
542
|
const feedTypes = options.feedOptions.type;
|
|
523
|
-
const
|
|
524
|
-
siteConfig: {title},
|
|
525
|
-
baseUrl,
|
|
526
|
-
} = context;
|
|
543
|
+
const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
|
|
527
544
|
const feedsConfig = {
|
|
528
545
|
rss: {
|
|
529
546
|
type: 'application/rss+xml',
|
|
530
547
|
path: 'rss.xml',
|
|
531
|
-
title: `${
|
|
548
|
+
title: `${feedTitle} RSS Feed`,
|
|
532
549
|
},
|
|
533
550
|
atom: {
|
|
534
551
|
type: 'application/atom+xml',
|
|
535
552
|
path: 'atom.xml',
|
|
536
|
-
title: `${
|
|
553
|
+
title: `${feedTitle} Atom Feed`,
|
|
537
554
|
},
|
|
538
555
|
};
|
|
539
556
|
const headTags: HtmlTags = [];
|
package/src/markdownLoader.ts
CHANGED
|
@@ -8,18 +8,16 @@
|
|
|
8
8
|
import {truncate, linkify} from './blogUtils';
|
|
9
9
|
import {parseQuery} from 'loader-utils';
|
|
10
10
|
import {BlogMarkdownLoaderOptions} from './types';
|
|
11
|
+
import type {LoaderContext} from 'webpack';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const markdownLoader: Loader = function (source) {
|
|
13
|
+
export default function markdownLoader(
|
|
14
|
+
this: LoaderContext<BlogMarkdownLoaderOptions>,
|
|
15
|
+
source: string,
|
|
16
|
+
): void {
|
|
19
17
|
const filePath = this.resourcePath;
|
|
20
|
-
const fileString = source
|
|
18
|
+
const fileString = source;
|
|
21
19
|
const callback = this.async();
|
|
22
|
-
const markdownLoaderOptions = this.getOptions()
|
|
20
|
+
const markdownLoaderOptions = this.getOptions();
|
|
23
21
|
|
|
24
22
|
// Linkify blog posts
|
|
25
23
|
let finalContent = linkify({
|
|
@@ -38,6 +36,4 @@ const markdownLoader: Loader = function (source) {
|
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
return callback && callback(null, finalContent);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export default markdownLoader;
|
|
39
|
+
}
|
|
@@ -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,29 @@ 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',
|
|
44
|
+
readingTime: ({content, defaultReadingTime}) => defaultReadingTime({content}),
|
|
40
45
|
};
|
|
41
46
|
|
|
42
|
-
export const PluginOptionSchema = Joi.object({
|
|
47
|
+
export const PluginOptionSchema = Joi.object<PluginOptions>({
|
|
43
48
|
path: Joi.string().default(DEFAULT_OPTIONS.path),
|
|
49
|
+
archiveBasePath: Joi.string().default(DEFAULT_OPTIONS.archiveBasePath),
|
|
44
50
|
routeBasePath: Joi.string()
|
|
45
51
|
// '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
|
|
46
52
|
// .allow('')
|
|
47
53
|
.default(DEFAULT_OPTIONS.routeBasePath),
|
|
54
|
+
tagsBasePath: Joi.string().default(DEFAULT_OPTIONS.tagsBasePath),
|
|
48
55
|
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
|
|
49
56
|
exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude),
|
|
50
|
-
postsPerPage: Joi.
|
|
51
|
-
.integer()
|
|
52
|
-
.min(1)
|
|
57
|
+
postsPerPage: Joi.alternatives()
|
|
58
|
+
.try(Joi.equal('ALL').required(), Joi.number().integer().min(1).required())
|
|
53
59
|
.default(DEFAULT_OPTIONS.postsPerPage),
|
|
54
60
|
blogListComponent: Joi.string().default(DEFAULT_OPTIONS.blogListComponent),
|
|
55
61
|
blogPostComponent: Joi.string().default(DEFAULT_OPTIONS.blogPostComponent),
|
|
@@ -64,7 +70,7 @@ export const PluginOptionSchema = Joi.object({
|
|
|
64
70
|
.allow('')
|
|
65
71
|
.default(DEFAULT_OPTIONS.blogDescription),
|
|
66
72
|
blogSidebarCount: Joi.alternatives()
|
|
67
|
-
.try(Joi.equal('ALL').required(), Joi.number().required())
|
|
73
|
+
.try(Joi.equal('ALL').required(), Joi.number().integer().min(0).required())
|
|
68
74
|
.default(DEFAULT_OPTIONS.blogSidebarCount),
|
|
69
75
|
blogSidebarTitle: Joi.string().default(DEFAULT_OPTIONS.blogSidebarTitle),
|
|
70
76
|
showReadingTime: Joi.bool().default(DEFAULT_OPTIONS.showReadingTime),
|
|
@@ -97,7 +103,16 @@ export const PluginOptionSchema = Joi.object({
|
|
|
97
103
|
.default(DEFAULT_OPTIONS.feedOptions.type),
|
|
98
104
|
title: Joi.string().allow(''),
|
|
99
105
|
description: Joi.string().allow(''),
|
|
100
|
-
|
|
106
|
+
// only add default value when user actually wants a feed (type is not null)
|
|
107
|
+
copyright: Joi.when('type', {
|
|
108
|
+
is: Joi.any().valid(null),
|
|
109
|
+
then: Joi.string().optional(),
|
|
110
|
+
otherwise: Joi.string()
|
|
111
|
+
.allow('')
|
|
112
|
+
.default(DEFAULT_OPTIONS.feedOptions.copyright),
|
|
113
|
+
}),
|
|
101
114
|
language: Joi.string(),
|
|
102
115
|
}).default(DEFAULT_OPTIONS.feedOptions),
|
|
116
|
+
authorsMapPath: Joi.string().default(DEFAULT_OPTIONS.authorsMapPath),
|
|
117
|
+
readingTime: Joi.function().default(() => DEFAULT_OPTIONS.readingTime),
|
|
103
118
|
});
|
|
@@ -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
|
+
}
|