@docusaurus/plugin-content-blog 2.0.0-beta.12faed89d → 2.0.0-beta.13
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 +147 -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 -136
- package/lib/feed.d.ts +20 -0
- package/lib/feed.js +90 -0
- package/lib/index.js +99 -106
- package/lib/markdownLoader.d.ts +3 -6
- package/lib/markdownLoader.js +5 -5
- package/lib/pluginOptionSchema.d.ts +3 -26
- package/lib/pluginOptionSchema.js +28 -7
- package/lib/translations.d.ts +10 -0
- package/lib/translations.js +53 -0
- package/lib/types.d.ts +54 -14
- package/package.json +17 -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/_partials/somePartial.md +3 -0
- package/src/__tests__/__fixtures__/website/blog/_partials/subfolder/somePartial.md +3 -0
- package/src/__tests__/__fixtures__/website/blog/_somePartial.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__/feed.test.ts.snap +164 -0
- 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} +35 -9
- package/src/__tests__/index.test.ts +84 -12
- package/src/__tests__/pluginOptionSchema.test.ts +3 -3
- package/src/__tests__/translations.test.ts +92 -0
- package/src/authors.ts +198 -0
- package/src/blogFrontMatter.ts +71 -33
- package/src/blogUtils.ts +202 -179
- package/{types.d.ts → src/deps.d.ts} +0 -0
- package/src/feed.ts +129 -0
- package/src/index.ts +118 -107
- package/src/markdownLoader.ts +8 -12
- package/{index.d.ts → src/plugin-content-blog.d.ts} +35 -31
- package/src/pluginOptionSchema.ts +31 -9
- package/src/translations.ts +63 -0
- package/src/types.ts +69 -16
- package/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap +0 -76
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 {
|
|
@@ -16,12 +15,10 @@ import {
|
|
|
16
15
|
reportMessage,
|
|
17
16
|
posixPath,
|
|
18
17
|
addTrailingPathSeparator,
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
STATIC_DIR_NAME,
|
|
18
|
+
createAbsoluteFilePathMatcher,
|
|
22
19
|
DEFAULT_PLUGIN_ID,
|
|
23
|
-
} from '@docusaurus/
|
|
24
|
-
import {
|
|
20
|
+
} from '@docusaurus/utils';
|
|
21
|
+
import {translateContent, getTranslationFiles} from './translations';
|
|
25
22
|
|
|
26
23
|
import {
|
|
27
24
|
PluginOptions,
|
|
@@ -30,9 +27,10 @@ import {
|
|
|
30
27
|
BlogItemsToMetadata,
|
|
31
28
|
TagsModule,
|
|
32
29
|
BlogPaginated,
|
|
33
|
-
BlogPost,
|
|
34
30
|
BlogContentPaths,
|
|
35
31
|
BlogMarkdownLoaderOptions,
|
|
32
|
+
MetaData,
|
|
33
|
+
Assets,
|
|
36
34
|
} from './types';
|
|
37
35
|
import {PluginOptionSchema} from './pluginOptionSchema';
|
|
38
36
|
import {
|
|
@@ -46,11 +44,13 @@ import {
|
|
|
46
44
|
} from '@docusaurus/types';
|
|
47
45
|
import {Configuration} from 'webpack';
|
|
48
46
|
import {
|
|
49
|
-
generateBlogFeed,
|
|
50
47
|
generateBlogPosts,
|
|
51
48
|
getContentPathList,
|
|
52
49
|
getSourceToPermalink,
|
|
50
|
+
getBlogTags,
|
|
53
51
|
} from './blogUtils';
|
|
52
|
+
import {BlogPostFrontMatter} from './blogFrontMatter';
|
|
53
|
+
import {createBlogFeedFiles} from './feed';
|
|
54
54
|
|
|
55
55
|
export default function pluginContentBlog(
|
|
56
56
|
context: LoadContext,
|
|
@@ -64,10 +64,11 @@ export default function pluginContentBlog(
|
|
|
64
64
|
|
|
65
65
|
const {
|
|
66
66
|
siteDir,
|
|
67
|
-
siteConfig
|
|
67
|
+
siteConfig,
|
|
68
68
|
generatedFilesDir,
|
|
69
69
|
i18n: {currentLocale},
|
|
70
70
|
} = context;
|
|
71
|
+
const {onBrokenMarkdownLinks, baseUrl} = siteConfig;
|
|
71
72
|
|
|
72
73
|
const contentPaths: BlogContentPaths = {
|
|
73
74
|
contentPath: path.resolve(siteDir, options.path),
|
|
@@ -92,36 +93,42 @@ export default function pluginContentBlog(
|
|
|
92
93
|
name: 'docusaurus-plugin-content-blog',
|
|
93
94
|
|
|
94
95
|
getPathsToWatch() {
|
|
95
|
-
const {include
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return include.map((pattern) => `${contentPath}/${pattern}`);
|
|
99
|
-
}),
|
|
96
|
+
const {include, authorsMapPath} = options;
|
|
97
|
+
const contentMarkdownGlobs = getContentPathList(contentPaths).flatMap(
|
|
98
|
+
(contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
|
|
100
99
|
);
|
|
101
|
-
},
|
|
102
100
|
|
|
103
|
-
|
|
104
|
-
const
|
|
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
|
+
);
|
|
105
108
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
+
return [authorsMapFilePath, ...contentMarkdownGlobs];
|
|
110
|
+
},
|
|
109
111
|
|
|
110
|
-
|
|
112
|
+
async getTranslationFiles() {
|
|
113
|
+
return getTranslationFiles(options);
|
|
111
114
|
},
|
|
112
115
|
|
|
113
116
|
// Fetches blog contents and returns metadata for the necessary routes.
|
|
114
117
|
async loadContent() {
|
|
115
|
-
const {
|
|
118
|
+
const {
|
|
119
|
+
postsPerPage: postsPerPageOption,
|
|
120
|
+
routeBasePath,
|
|
121
|
+
tagsBasePath,
|
|
122
|
+
blogDescription,
|
|
123
|
+
blogTitle,
|
|
124
|
+
blogSidebarTitle,
|
|
125
|
+
} = options;
|
|
116
126
|
|
|
117
|
-
const blogPosts
|
|
118
|
-
contentPaths,
|
|
119
|
-
context,
|
|
120
|
-
options,
|
|
121
|
-
);
|
|
127
|
+
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
122
128
|
|
|
123
129
|
if (!blogPosts.length) {
|
|
124
130
|
return {
|
|
131
|
+
blogSidebarTitle,
|
|
125
132
|
blogPosts: [],
|
|
126
133
|
blogListPaginated: [],
|
|
127
134
|
blogTags: {},
|
|
@@ -152,18 +159,17 @@ export default function pluginContentBlog(
|
|
|
152
159
|
// Blog pagination routes.
|
|
153
160
|
// Example: `/blog`, `/blog/page/1`, `/blog/page/2`
|
|
154
161
|
const totalCount = blogPosts.length;
|
|
162
|
+
const postsPerPage =
|
|
163
|
+
postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption;
|
|
155
164
|
const numberOfPages = Math.ceil(totalCount / postsPerPage);
|
|
156
|
-
const
|
|
157
|
-
siteConfig: {baseUrl = ''},
|
|
158
|
-
} = context;
|
|
159
|
-
const basePageUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
165
|
+
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
160
166
|
|
|
161
167
|
const blogListPaginated: BlogPaginated[] = [];
|
|
162
168
|
|
|
163
169
|
function blogPaginationPermalink(page: number) {
|
|
164
170
|
return page > 0
|
|
165
|
-
? normalizeUrl([
|
|
166
|
-
:
|
|
171
|
+
? normalizeUrl([baseBlogUrl, `page/${page + 1}`])
|
|
172
|
+
: baseBlogUrl;
|
|
167
173
|
}
|
|
168
174
|
|
|
169
175
|
for (let page = 0; page < numberOfPages; page += 1) {
|
|
@@ -179,8 +185,8 @@ export default function pluginContentBlog(
|
|
|
179
185
|
page < numberOfPages - 1
|
|
180
186
|
? blogPaginationPermalink(page + 1)
|
|
181
187
|
: null,
|
|
182
|
-
blogDescription
|
|
183
|
-
blogTitle
|
|
188
|
+
blogDescription,
|
|
189
|
+
blogTitle,
|
|
184
190
|
},
|
|
185
191
|
items: blogPosts
|
|
186
192
|
.slice(page * postsPerPage, (page + 1) * postsPerPage)
|
|
@@ -188,46 +194,15 @@ export default function pluginContentBlog(
|
|
|
188
194
|
});
|
|
189
195
|
}
|
|
190
196
|
|
|
191
|
-
const blogTags: BlogTags =
|
|
192
|
-
const tagsPath = normalizeUrl([basePageUrl, 'tags']);
|
|
193
|
-
blogPosts.forEach((blogPost) => {
|
|
194
|
-
const {tags} = blogPost.metadata;
|
|
195
|
-
if (!tags || tags.length === 0) {
|
|
196
|
-
// TODO: Extract tags out into a separate plugin.
|
|
197
|
-
// eslint-disable-next-line no-param-reassign
|
|
198
|
-
blogPost.metadata.tags = [];
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
197
|
+
const blogTags: BlogTags = getBlogTags(blogPosts);
|
|
201
198
|
|
|
202
|
-
|
|
203
|
-
blogPost.metadata.tags = tags.map((tag) => {
|
|
204
|
-
if (typeof tag === 'string') {
|
|
205
|
-
const normalizedTag = kebabCase(tag);
|
|
206
|
-
const permalink = normalizeUrl([tagsPath, normalizedTag]);
|
|
207
|
-
if (!blogTags[normalizedTag]) {
|
|
208
|
-
blogTags[normalizedTag] = {
|
|
209
|
-
// Will only use the name of the first occurrence of the tag.
|
|
210
|
-
name: tag.toLowerCase(),
|
|
211
|
-
items: [],
|
|
212
|
-
permalink,
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
blogTags[normalizedTag].items.push(blogPost.id);
|
|
217
|
-
|
|
218
|
-
return {
|
|
219
|
-
label: tag,
|
|
220
|
-
permalink,
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
return tag;
|
|
224
|
-
});
|
|
225
|
-
});
|
|
199
|
+
const tagsPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
|
|
226
200
|
|
|
227
201
|
const blogTagsListPath =
|
|
228
202
|
Object.keys(blogTags).length > 0 ? tagsPath : null;
|
|
229
203
|
|
|
230
204
|
return {
|
|
205
|
+
blogSidebarTitle,
|
|
231
206
|
blogPosts,
|
|
232
207
|
blogListPaginated,
|
|
233
208
|
blogTags,
|
|
@@ -245,10 +220,13 @@ export default function pluginContentBlog(
|
|
|
245
220
|
blogPostComponent,
|
|
246
221
|
blogTagsListComponent,
|
|
247
222
|
blogTagsPostsComponent,
|
|
223
|
+
routeBasePath,
|
|
224
|
+
archiveBasePath,
|
|
248
225
|
} = options;
|
|
249
226
|
|
|
250
227
|
const {addRoute, createData} = actions;
|
|
251
228
|
const {
|
|
229
|
+
blogSidebarTitle,
|
|
252
230
|
blogPosts,
|
|
253
231
|
blogListPaginated,
|
|
254
232
|
blogTags,
|
|
@@ -260,7 +238,27 @@ export default function pluginContentBlog(
|
|
|
260
238
|
const sidebarBlogPosts =
|
|
261
239
|
options.blogSidebarCount === 'ALL'
|
|
262
240
|
? blogPosts
|
|
263
|
-
:
|
|
241
|
+
: blogPosts.slice(0, options.blogSidebarCount);
|
|
242
|
+
|
|
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
|
+
});
|
|
264
262
|
|
|
265
263
|
// This prop is useful to provide the blog list sidebar
|
|
266
264
|
const sidebarProp = await createData(
|
|
@@ -269,7 +267,7 @@ export default function pluginContentBlog(
|
|
|
269
267
|
`blog-post-list-prop-${pluginId}.json`,
|
|
270
268
|
JSON.stringify(
|
|
271
269
|
{
|
|
272
|
-
title:
|
|
270
|
+
title: blogSidebarTitle,
|
|
273
271
|
items: sidebarBlogPosts.map((blogPost) => ({
|
|
274
272
|
title: blogPost.metadata.title,
|
|
275
273
|
permalink: blogPost.metadata.permalink,
|
|
@@ -321,9 +319,9 @@ export default function pluginContentBlog(
|
|
|
321
319
|
exact: true,
|
|
322
320
|
modules: {
|
|
323
321
|
sidebar: aliasedSource(sidebarProp),
|
|
324
|
-
items: items.map((postID) =>
|
|
322
|
+
items: items.map((postID) =>
|
|
325
323
|
// To tell routes.js this is an import and not a nested object to recurse.
|
|
326
|
-
|
|
324
|
+
({
|
|
327
325
|
content: {
|
|
328
326
|
__import: true,
|
|
329
327
|
path: blogItemsToMetadata[postID].source,
|
|
@@ -331,8 +329,8 @@ export default function pluginContentBlog(
|
|
|
331
329
|
truncated: true,
|
|
332
330
|
},
|
|
333
331
|
},
|
|
334
|
-
}
|
|
335
|
-
|
|
332
|
+
}),
|
|
333
|
+
),
|
|
336
334
|
metadata: aliasedSource(pageMetadataPath),
|
|
337
335
|
},
|
|
338
336
|
});
|
|
@@ -350,6 +348,7 @@ export default function pluginContentBlog(
|
|
|
350
348
|
Object.keys(blogTags).map(async (tag) => {
|
|
351
349
|
const {name, items, permalink} = blogTags[tag];
|
|
352
350
|
|
|
351
|
+
// Refactor all this, see docs implementation
|
|
353
352
|
tagsModule[tag] = {
|
|
354
353
|
allTagsPath: blogTagsListPath,
|
|
355
354
|
slug: tag,
|
|
@@ -406,6 +405,10 @@ export default function pluginContentBlog(
|
|
|
406
405
|
}
|
|
407
406
|
},
|
|
408
407
|
|
|
408
|
+
translateContent({content, translationFiles}) {
|
|
409
|
+
return translateContent(content, translationFiles);
|
|
410
|
+
},
|
|
411
|
+
|
|
409
412
|
configureWebpack(
|
|
410
413
|
_config: Configuration,
|
|
411
414
|
isServer: boolean,
|
|
@@ -436,6 +439,7 @@ export default function pluginContentBlog(
|
|
|
436
439
|
},
|
|
437
440
|
};
|
|
438
441
|
|
|
442
|
+
const contentDirs = getContentPathList(contentPaths);
|
|
439
443
|
return {
|
|
440
444
|
resolve: {
|
|
441
445
|
alias: {
|
|
@@ -446,7 +450,7 @@ export default function pluginContentBlog(
|
|
|
446
450
|
rules: [
|
|
447
451
|
{
|
|
448
452
|
test: /(\.mdx?)$/,
|
|
449
|
-
include:
|
|
453
|
+
include: contentDirs
|
|
450
454
|
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
|
451
455
|
.map(addTrailingPathSeparator),
|
|
452
456
|
use: [
|
|
@@ -458,10 +462,17 @@ export default function pluginContentBlog(
|
|
|
458
462
|
rehypePlugins,
|
|
459
463
|
beforeDefaultRemarkPlugins,
|
|
460
464
|
beforeDefaultRehypePlugins,
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
465
|
+
staticDirs: siteConfig.staticDirectories.map((dir) =>
|
|
466
|
+
path.resolve(siteDir, dir),
|
|
467
|
+
),
|
|
468
|
+
siteDir,
|
|
469
|
+
isMDXPartial: createAbsoluteFilePathMatcher(
|
|
470
|
+
options.exclude,
|
|
471
|
+
contentDirs,
|
|
472
|
+
),
|
|
464
473
|
metadataPath: (mdxPath: string) => {
|
|
474
|
+
// Note that metadataPath must be the same/in-sync as
|
|
475
|
+
// the path from createData for each MDX.
|
|
465
476
|
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
|
466
477
|
return path.join(
|
|
467
478
|
dataDir,
|
|
@@ -471,6 +482,20 @@ export default function pluginContentBlog(
|
|
|
471
482
|
// For blog posts a title in markdown is always removed
|
|
472
483
|
// Blog posts title are rendered separately
|
|
473
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
|
+
image: frontMatter.image,
|
|
495
|
+
authorsImageUrls: metadata.authors.map(
|
|
496
|
+
(author) => author.imageURL,
|
|
497
|
+
),
|
|
498
|
+
}),
|
|
474
499
|
},
|
|
475
500
|
},
|
|
476
501
|
{
|
|
@@ -485,33 +510,22 @@ export default function pluginContentBlog(
|
|
|
485
510
|
},
|
|
486
511
|
|
|
487
512
|
async postBuild({outDir}: Props) {
|
|
488
|
-
if (!options.feedOptions
|
|
513
|
+
if (!options.feedOptions.type) {
|
|
489
514
|
return;
|
|
490
515
|
}
|
|
491
516
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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);
|
|
520
|
+
if (!blogPosts.length) {
|
|
495
521
|
return;
|
|
496
522
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
outDir,
|
|
504
|
-
options.routeBasePath,
|
|
505
|
-
`${feedType}.xml`,
|
|
506
|
-
);
|
|
507
|
-
const feedContent = feedType === 'rss' ? feed.rss2() : feed.atom1();
|
|
508
|
-
try {
|
|
509
|
-
await fs.outputFile(feedPath, feedContent);
|
|
510
|
-
} catch (err) {
|
|
511
|
-
throw new Error(`Generating ${feedType} feed failed: ${err}.`);
|
|
512
|
-
}
|
|
513
|
-
}),
|
|
514
|
-
);
|
|
523
|
+
await createBlogFeedFiles({
|
|
524
|
+
blogPosts,
|
|
525
|
+
options,
|
|
526
|
+
outDir,
|
|
527
|
+
siteConfig,
|
|
528
|
+
});
|
|
515
529
|
},
|
|
516
530
|
|
|
517
531
|
injectHtmlTags({content}) {
|
|
@@ -524,20 +538,17 @@ export default function pluginContentBlog(
|
|
|
524
538
|
}
|
|
525
539
|
|
|
526
540
|
const feedTypes = options.feedOptions.type;
|
|
527
|
-
const
|
|
528
|
-
siteConfig: {title},
|
|
529
|
-
baseUrl,
|
|
530
|
-
} = context;
|
|
541
|
+
const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
|
|
531
542
|
const feedsConfig = {
|
|
532
543
|
rss: {
|
|
533
544
|
type: 'application/rss+xml',
|
|
534
545
|
path: 'rss.xml',
|
|
535
|
-
title: `${
|
|
546
|
+
title: `${feedTitle} RSS Feed`,
|
|
536
547
|
},
|
|
537
548
|
atom: {
|
|
538
549
|
type: 'application/atom+xml',
|
|
539
550
|
path: 'atom.xml',
|
|
540
|
-
title: `${
|
|
551
|
+
title: `${feedTitle} Atom Feed`,
|
|
541
552
|
},
|
|
542
553
|
};
|
|
543
554
|
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
|
}
|
|
@@ -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: ['
|
|
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
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
});
|