@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.
- 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 +35 -23
- 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.d.ts +1 -1
- package/lib/index.js +109 -112
- 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 +19 -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 +165 -36
- 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 +76 -37
- 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 +131 -112
- 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,16 +44,18 @@ 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,
|
|
57
57
|
options: PluginOptions,
|
|
58
|
-
): Plugin<BlogContent
|
|
58
|
+
): Plugin<BlogContent> {
|
|
59
59
|
if (options.admonitions) {
|
|
60
60
|
options.remarkPlugins = options.remarkPlugins.concat([
|
|
61
61
|
[admonitions, options.admonitions],
|
|
@@ -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),
|
|
@@ -88,38 +89,51 @@ export default function pluginContentBlog(
|
|
|
88
89
|
const aliasedSource = (source: string) =>
|
|
89
90
|
`~blog/${posixPath(path.relative(pluginDataDirRoot, source))}`;
|
|
90
91
|
|
|
91
|
-
let blogPosts: BlogPost[] = [];
|
|
92
|
-
|
|
93
92
|
return {
|
|
94
93
|
name: 'docusaurus-plugin-content-blog',
|
|
95
94
|
|
|
96
95
|
getPathsToWatch() {
|
|
97
|
-
const {include
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return include.map((pattern) => `${contentPath}/${pattern}`);
|
|
101
|
-
}),
|
|
96
|
+
const {include, authorsMapPath} = options;
|
|
97
|
+
const contentMarkdownGlobs = getContentPathList(contentPaths).flatMap(
|
|
98
|
+
(contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
|
|
102
99
|
);
|
|
103
|
-
},
|
|
104
100
|
|
|
105
|
-
|
|
106
|
-
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
|
+
);
|
|
107
108
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
109
|
+
return [authorsMapFilePath, ...contentMarkdownGlobs];
|
|
110
|
+
},
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
async getTranslationFiles() {
|
|
113
|
+
return getTranslationFiles(options);
|
|
113
114
|
},
|
|
114
115
|
|
|
115
116
|
// Fetches blog contents and returns metadata for the necessary routes.
|
|
116
117
|
async loadContent() {
|
|
117
|
-
const {
|
|
118
|
+
const {
|
|
119
|
+
postsPerPage: postsPerPageOption,
|
|
120
|
+
routeBasePath,
|
|
121
|
+
tagsBasePath,
|
|
122
|
+
blogDescription,
|
|
123
|
+
blogTitle,
|
|
124
|
+
blogSidebarTitle,
|
|
125
|
+
} = options;
|
|
118
126
|
|
|
119
|
-
blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
127
|
+
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
120
128
|
|
|
121
129
|
if (!blogPosts.length) {
|
|
122
|
-
return
|
|
130
|
+
return {
|
|
131
|
+
blogSidebarTitle,
|
|
132
|
+
blogPosts: [],
|
|
133
|
+
blogListPaginated: [],
|
|
134
|
+
blogTags: {},
|
|
135
|
+
blogTagsListPath: null,
|
|
136
|
+
};
|
|
123
137
|
}
|
|
124
138
|
|
|
125
139
|
// Colocate next and prev metadata.
|
|
@@ -145,18 +159,17 @@ export default function pluginContentBlog(
|
|
|
145
159
|
// Blog pagination routes.
|
|
146
160
|
// Example: `/blog`, `/blog/page/1`, `/blog/page/2`
|
|
147
161
|
const totalCount = blogPosts.length;
|
|
162
|
+
const postsPerPage =
|
|
163
|
+
postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption;
|
|
148
164
|
const numberOfPages = Math.ceil(totalCount / postsPerPage);
|
|
149
|
-
const
|
|
150
|
-
siteConfig: {baseUrl = ''},
|
|
151
|
-
} = context;
|
|
152
|
-
const basePageUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
165
|
+
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
153
166
|
|
|
154
167
|
const blogListPaginated: BlogPaginated[] = [];
|
|
155
168
|
|
|
156
169
|
function blogPaginationPermalink(page: number) {
|
|
157
170
|
return page > 0
|
|
158
|
-
? normalizeUrl([
|
|
159
|
-
:
|
|
171
|
+
? normalizeUrl([baseBlogUrl, `page/${page + 1}`])
|
|
172
|
+
: baseBlogUrl;
|
|
160
173
|
}
|
|
161
174
|
|
|
162
175
|
for (let page = 0; page < numberOfPages; page += 1) {
|
|
@@ -172,8 +185,8 @@ export default function pluginContentBlog(
|
|
|
172
185
|
page < numberOfPages - 1
|
|
173
186
|
? blogPaginationPermalink(page + 1)
|
|
174
187
|
: null,
|
|
175
|
-
blogDescription
|
|
176
|
-
blogTitle
|
|
188
|
+
blogDescription,
|
|
189
|
+
blogTitle,
|
|
177
190
|
},
|
|
178
191
|
items: blogPosts
|
|
179
192
|
.slice(page * postsPerPage, (page + 1) * postsPerPage)
|
|
@@ -181,46 +194,15 @@ export default function pluginContentBlog(
|
|
|
181
194
|
});
|
|
182
195
|
}
|
|
183
196
|
|
|
184
|
-
const blogTags: BlogTags =
|
|
185
|
-
const tagsPath = normalizeUrl([basePageUrl, 'tags']);
|
|
186
|
-
blogPosts.forEach((blogPost) => {
|
|
187
|
-
const {tags} = blogPost.metadata;
|
|
188
|
-
if (!tags || tags.length === 0) {
|
|
189
|
-
// TODO: Extract tags out into a separate plugin.
|
|
190
|
-
// eslint-disable-next-line no-param-reassign
|
|
191
|
-
blogPost.metadata.tags = [];
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
197
|
+
const blogTags: BlogTags = getBlogTags(blogPosts);
|
|
194
198
|
|
|
195
|
-
|
|
196
|
-
blogPost.metadata.tags = tags.map((tag) => {
|
|
197
|
-
if (typeof tag === 'string') {
|
|
198
|
-
const normalizedTag = kebabCase(tag);
|
|
199
|
-
const permalink = normalizeUrl([tagsPath, normalizedTag]);
|
|
200
|
-
if (!blogTags[normalizedTag]) {
|
|
201
|
-
blogTags[normalizedTag] = {
|
|
202
|
-
// Will only use the name of the first occurrence of the tag.
|
|
203
|
-
name: tag.toLowerCase(),
|
|
204
|
-
items: [],
|
|
205
|
-
permalink,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
blogTags[normalizedTag].items.push(blogPost.id);
|
|
210
|
-
|
|
211
|
-
return {
|
|
212
|
-
label: tag,
|
|
213
|
-
permalink,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
return tag;
|
|
217
|
-
});
|
|
218
|
-
});
|
|
199
|
+
const tagsPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
|
|
219
200
|
|
|
220
201
|
const blogTagsListPath =
|
|
221
202
|
Object.keys(blogTags).length > 0 ? tagsPath : null;
|
|
222
203
|
|
|
223
204
|
return {
|
|
205
|
+
blogSidebarTitle,
|
|
224
206
|
blogPosts,
|
|
225
207
|
blogListPaginated,
|
|
226
208
|
blogTags,
|
|
@@ -238,11 +220,14 @@ export default function pluginContentBlog(
|
|
|
238
220
|
blogPostComponent,
|
|
239
221
|
blogTagsListComponent,
|
|
240
222
|
blogTagsPostsComponent,
|
|
223
|
+
routeBasePath,
|
|
224
|
+
archiveBasePath,
|
|
241
225
|
} = options;
|
|
242
226
|
|
|
243
227
|
const {addRoute, createData} = actions;
|
|
244
228
|
const {
|
|
245
|
-
|
|
229
|
+
blogSidebarTitle,
|
|
230
|
+
blogPosts,
|
|
246
231
|
blogListPaginated,
|
|
247
232
|
blogTags,
|
|
248
233
|
blogTagsListPath,
|
|
@@ -253,7 +238,27 @@ export default function pluginContentBlog(
|
|
|
253
238
|
const sidebarBlogPosts =
|
|
254
239
|
options.blogSidebarCount === 'ALL'
|
|
255
240
|
? blogPosts
|
|
256
|
-
:
|
|
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
|
+
});
|
|
257
262
|
|
|
258
263
|
// This prop is useful to provide the blog list sidebar
|
|
259
264
|
const sidebarProp = await createData(
|
|
@@ -262,7 +267,7 @@ export default function pluginContentBlog(
|
|
|
262
267
|
`blog-post-list-prop-${pluginId}.json`,
|
|
263
268
|
JSON.stringify(
|
|
264
269
|
{
|
|
265
|
-
title:
|
|
270
|
+
title: blogSidebarTitle,
|
|
266
271
|
items: sidebarBlogPosts.map((blogPost) => ({
|
|
267
272
|
title: blogPost.metadata.title,
|
|
268
273
|
permalink: blogPost.metadata.permalink,
|
|
@@ -275,7 +280,7 @@ export default function pluginContentBlog(
|
|
|
275
280
|
|
|
276
281
|
// Create routes for blog entries.
|
|
277
282
|
await Promise.all(
|
|
278
|
-
|
|
283
|
+
blogPosts.map(async (blogPost) => {
|
|
279
284
|
const {id, metadata} = blogPost;
|
|
280
285
|
await createData(
|
|
281
286
|
// Note that this created data path must be in sync with
|
|
@@ -314,9 +319,9 @@ export default function pluginContentBlog(
|
|
|
314
319
|
exact: true,
|
|
315
320
|
modules: {
|
|
316
321
|
sidebar: aliasedSource(sidebarProp),
|
|
317
|
-
items: items.map((postID) =>
|
|
322
|
+
items: items.map((postID) =>
|
|
318
323
|
// To tell routes.js this is an import and not a nested object to recurse.
|
|
319
|
-
|
|
324
|
+
({
|
|
320
325
|
content: {
|
|
321
326
|
__import: true,
|
|
322
327
|
path: blogItemsToMetadata[postID].source,
|
|
@@ -324,8 +329,8 @@ export default function pluginContentBlog(
|
|
|
324
329
|
truncated: true,
|
|
325
330
|
},
|
|
326
331
|
},
|
|
327
|
-
}
|
|
328
|
-
|
|
332
|
+
}),
|
|
333
|
+
),
|
|
329
334
|
metadata: aliasedSource(pageMetadataPath),
|
|
330
335
|
},
|
|
331
336
|
});
|
|
@@ -343,6 +348,7 @@ export default function pluginContentBlog(
|
|
|
343
348
|
Object.keys(blogTags).map(async (tag) => {
|
|
344
349
|
const {name, items, permalink} = blogTags[tag];
|
|
345
350
|
|
|
351
|
+
// Refactor all this, see docs implementation
|
|
346
352
|
tagsModule[tag] = {
|
|
347
353
|
allTagsPath: blogTagsListPath,
|
|
348
354
|
slug: tag,
|
|
@@ -399,10 +405,15 @@ export default function pluginContentBlog(
|
|
|
399
405
|
}
|
|
400
406
|
},
|
|
401
407
|
|
|
408
|
+
translateContent({content, translationFiles}) {
|
|
409
|
+
return translateContent(content, translationFiles);
|
|
410
|
+
},
|
|
411
|
+
|
|
402
412
|
configureWebpack(
|
|
403
413
|
_config: Configuration,
|
|
404
414
|
isServer: boolean,
|
|
405
415
|
{getJSLoader}: ConfigureWebpackUtils,
|
|
416
|
+
content,
|
|
406
417
|
) {
|
|
407
418
|
const {
|
|
408
419
|
rehypePlugins,
|
|
@@ -416,7 +427,7 @@ export default function pluginContentBlog(
|
|
|
416
427
|
siteDir,
|
|
417
428
|
contentPaths,
|
|
418
429
|
truncateMarker,
|
|
419
|
-
sourceToPermalink: getSourceToPermalink(blogPosts),
|
|
430
|
+
sourceToPermalink: getSourceToPermalink(content.blogPosts),
|
|
420
431
|
onBrokenMarkdownLink: (brokenMarkdownLink) => {
|
|
421
432
|
if (onBrokenMarkdownLinks === 'ignore') {
|
|
422
433
|
return;
|
|
@@ -428,6 +439,7 @@ export default function pluginContentBlog(
|
|
|
428
439
|
},
|
|
429
440
|
};
|
|
430
441
|
|
|
442
|
+
const contentDirs = getContentPathList(contentPaths);
|
|
431
443
|
return {
|
|
432
444
|
resolve: {
|
|
433
445
|
alias: {
|
|
@@ -438,7 +450,7 @@ export default function pluginContentBlog(
|
|
|
438
450
|
rules: [
|
|
439
451
|
{
|
|
440
452
|
test: /(\.mdx?)$/,
|
|
441
|
-
include:
|
|
453
|
+
include: contentDirs
|
|
442
454
|
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
|
443
455
|
.map(addTrailingPathSeparator),
|
|
444
456
|
use: [
|
|
@@ -450,10 +462,17 @@ export default function pluginContentBlog(
|
|
|
450
462
|
rehypePlugins,
|
|
451
463
|
beforeDefaultRemarkPlugins,
|
|
452
464
|
beforeDefaultRehypePlugins,
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
465
|
+
staticDirs: siteConfig.staticDirectories.map((dir) =>
|
|
466
|
+
path.resolve(siteDir, dir),
|
|
467
|
+
),
|
|
468
|
+
siteDir,
|
|
469
|
+
isMDXPartial: createAbsoluteFilePathMatcher(
|
|
470
|
+
options.exclude,
|
|
471
|
+
contentDirs,
|
|
472
|
+
),
|
|
456
473
|
metadataPath: (mdxPath: string) => {
|
|
474
|
+
// Note that metadataPath must be the same/in-sync as
|
|
475
|
+
// the path from createData for each MDX.
|
|
457
476
|
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
|
458
477
|
return path.join(
|
|
459
478
|
dataDir,
|
|
@@ -463,6 +482,20 @@ export default function pluginContentBlog(
|
|
|
463
482
|
// For blog posts a title in markdown is always removed
|
|
464
483
|
// Blog posts title are rendered separately
|
|
465
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
|
+
}),
|
|
466
499
|
},
|
|
467
500
|
},
|
|
468
501
|
{
|
|
@@ -477,37 +510,26 @@ export default function pluginContentBlog(
|
|
|
477
510
|
},
|
|
478
511
|
|
|
479
512
|
async postBuild({outDir}: Props) {
|
|
480
|
-
if (!options.feedOptions
|
|
513
|
+
if (!options.feedOptions.type) {
|
|
481
514
|
return;
|
|
482
515
|
}
|
|
483
516
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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) {
|
|
487
521
|
return;
|
|
488
522
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
outDir,
|
|
496
|
-
options.routeBasePath,
|
|
497
|
-
`${feedType}.xml`,
|
|
498
|
-
);
|
|
499
|
-
const feedContent = feedType === 'rss' ? feed.rss2() : feed.atom1();
|
|
500
|
-
try {
|
|
501
|
-
await fs.outputFile(feedPath, feedContent);
|
|
502
|
-
} catch (err) {
|
|
503
|
-
throw new Error(`Generating ${feedType} feed failed: ${err}.`);
|
|
504
|
-
}
|
|
505
|
-
}),
|
|
506
|
-
);
|
|
523
|
+
await createBlogFeedFiles({
|
|
524
|
+
blogPosts,
|
|
525
|
+
options,
|
|
526
|
+
outDir,
|
|
527
|
+
siteConfig,
|
|
528
|
+
});
|
|
507
529
|
},
|
|
508
530
|
|
|
509
|
-
injectHtmlTags() {
|
|
510
|
-
if (!blogPosts.length) {
|
|
531
|
+
injectHtmlTags({content}) {
|
|
532
|
+
if (!content.blogPosts.length) {
|
|
511
533
|
return {};
|
|
512
534
|
}
|
|
513
535
|
|
|
@@ -516,20 +538,17 @@ export default function pluginContentBlog(
|
|
|
516
538
|
}
|
|
517
539
|
|
|
518
540
|
const feedTypes = options.feedOptions.type;
|
|
519
|
-
const
|
|
520
|
-
siteConfig: {title},
|
|
521
|
-
baseUrl,
|
|
522
|
-
} = context;
|
|
541
|
+
const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
|
|
523
542
|
const feedsConfig = {
|
|
524
543
|
rss: {
|
|
525
544
|
type: 'application/rss+xml',
|
|
526
545
|
path: 'rss.xml',
|
|
527
|
-
title: `${
|
|
546
|
+
title: `${feedTitle} RSS Feed`,
|
|
528
547
|
},
|
|
529
548
|
atom: {
|
|
530
549
|
type: 'application/atom+xml',
|
|
531
550
|
path: 'atom.xml',
|
|
532
|
-
title: `${
|
|
551
|
+
title: `${feedTitle} Atom Feed`,
|
|
533
552
|
},
|
|
534
553
|
};
|
|
535
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
|
}
|