@docusaurus/plugin-content-blog 2.0.0-beta.fc64c12e4 → 2.0.0
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/authors.d.ts +22 -0
- package/lib/authors.js +122 -0
- package/lib/blogUtils.d.ts +27 -7
- package/lib/blogUtils.js +201 -145
- package/lib/feed.d.ts +15 -0
- package/lib/feed.js +102 -0
- package/lib/frontMatter.d.ts +10 -0
- package/lib/{blogFrontMatter.js → frontMatter.js} +31 -19
- package/lib/index.d.ts +4 -4
- package/lib/index.js +163 -194
- package/lib/markdownLoader.d.ts +3 -6
- package/lib/markdownLoader.js +6 -7
- package/lib/options.d.ts +10 -0
- package/lib/{pluginOptionSchema.js → options.js} +41 -14
- package/lib/remark/footnoteIDFixer.d.ts +14 -0
- package/lib/remark/footnoteIDFixer.js +29 -0
- package/lib/translations.d.ts +10 -0
- package/lib/translations.js +53 -0
- package/lib/types.d.ts +4 -110
- package/package.json +23 -19
- package/src/authors.ts +168 -0
- package/src/blogUtils.ts +305 -205
- package/src/feed.ts +171 -0
- package/src/frontMatter.ts +81 -0
- package/src/index.ts +223 -258
- package/src/markdownLoader.ts +11 -16
- package/src/{pluginOptionSchema.ts → options.ts} +54 -16
- package/src/plugin-content-blog.d.ts +587 -0
- package/src/remark/footnoteIDFixer.ts +29 -0
- package/src/translations.ts +69 -0
- package/src/types.ts +2 -129
- package/index.d.ts +0 -138
- package/lib/.tsbuildinfo +0 -1
- package/lib/blogFrontMatter.d.ts +0 -28
- package/lib/pluginOptionSchema.d.ts +0 -34
- package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
- package/src/__tests__/__fixtures__/website/blog/_partials/somePartial.md +0 -3
- package/src/__tests__/__fixtures__/website/blog/_partials/subfolder/somePartial.md +0 -3
- package/src/__tests__/__fixtures__/website/blog/_somePartial.md +0 -3
- package/src/__tests__/__fixtures__/website/blog/complex-slug.md +0 -7
- package/src/__tests__/__fixtures__/website/blog/date-matter.md +0 -5
- package/src/__tests__/__fixtures__/website/blog/draft.md +0 -6
- package/src/__tests__/__fixtures__/website/blog/heading-as-title.md +0 -5
- package/src/__tests__/__fixtures__/website/blog/simple-slug.md +0 -7
- package/src/__tests__/__fixtures__/website/blog-with-ref/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
- package/src/__tests__/__fixtures__/website/blog-with-ref/post-with-broken-links.md +0 -11
- package/src/__tests__/__fixtures__/website/blog-with-ref/post.md +0 -5
- package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
- package/src/__tests__/__fixtures__/website-blog-without-date/blog/no date.md +0 -1
- package/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap +0 -116
- package/src/__tests__/__snapshots__/linkify.test.ts.snap +0 -24
- package/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap +0 -5
- package/src/__tests__/blogFrontMatter.test.ts +0 -317
- package/src/__tests__/generateBlogFeed.test.ts +0 -102
- package/src/__tests__/index.test.ts +0 -336
- package/src/__tests__/linkify.test.ts +0 -93
- package/src/__tests__/pluginOptionSchema.test.ts +0 -150
- package/src/blogFrontMatter.ts +0 -88
- package/tsconfig.json +0 -9
- package/types.d.ts +0 -13
package/src/index.ts
CHANGED
|
@@ -5,76 +5,62 @@
|
|
|
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
|
-
import
|
|
9
|
+
import logger from '@docusaurus/logger';
|
|
11
10
|
import {
|
|
12
11
|
normalizeUrl,
|
|
13
12
|
docuHash,
|
|
14
13
|
aliasedSitePath,
|
|
15
14
|
getPluginI18nPath,
|
|
16
|
-
reportMessage,
|
|
17
15
|
posixPath,
|
|
18
16
|
addTrailingPathSeparator,
|
|
19
17
|
createAbsoluteFilePathMatcher,
|
|
18
|
+
getContentPathList,
|
|
19
|
+
getDataFilePath,
|
|
20
|
+
DEFAULT_PLUGIN_ID,
|
|
21
|
+
type TagsListItem,
|
|
22
|
+
type TagModule,
|
|
20
23
|
} from '@docusaurus/utils';
|
|
21
24
|
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
generateBlogPosts,
|
|
26
|
+
getSourceToPermalink,
|
|
27
|
+
getBlogTags,
|
|
28
|
+
paginateBlogPosts,
|
|
29
|
+
} from './blogUtils';
|
|
30
|
+
import footnoteIDFixer from './remark/footnoteIDFixer';
|
|
31
|
+
import {translateContent, getTranslationFiles} from './translations';
|
|
32
|
+
import {createBlogFeedFiles} from './feed';
|
|
26
33
|
|
|
27
|
-
import {
|
|
34
|
+
import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types';
|
|
35
|
+
import type {LoadContext, Plugin, HtmlTags} from '@docusaurus/types';
|
|
36
|
+
import type {
|
|
28
37
|
PluginOptions,
|
|
38
|
+
BlogPostFrontMatter,
|
|
39
|
+
BlogPostMetadata,
|
|
40
|
+
Assets,
|
|
41
|
+
BlogTag,
|
|
29
42
|
BlogTags,
|
|
30
43
|
BlogContent,
|
|
31
|
-
BlogItemsToMetadata,
|
|
32
|
-
TagsModule,
|
|
33
44
|
BlogPaginated,
|
|
34
|
-
|
|
35
|
-
BlogContentPaths,
|
|
36
|
-
BlogMarkdownLoaderOptions,
|
|
37
|
-
} from './types';
|
|
38
|
-
import {PluginOptionSchema} from './pluginOptionSchema';
|
|
39
|
-
import {
|
|
40
|
-
LoadContext,
|
|
41
|
-
ConfigureWebpackUtils,
|
|
42
|
-
Props,
|
|
43
|
-
Plugin,
|
|
44
|
-
HtmlTags,
|
|
45
|
-
OptionValidationContext,
|
|
46
|
-
ValidationResult,
|
|
47
|
-
} from '@docusaurus/types';
|
|
48
|
-
import {Configuration} from 'webpack';
|
|
49
|
-
import {
|
|
50
|
-
generateBlogFeed,
|
|
51
|
-
generateBlogPosts,
|
|
52
|
-
getContentPathList,
|
|
53
|
-
getSourceToPermalink,
|
|
54
|
-
} from './blogUtils';
|
|
45
|
+
} from '@docusaurus/plugin-content-blog';
|
|
55
46
|
|
|
56
|
-
export default function pluginContentBlog(
|
|
47
|
+
export default async function pluginContentBlog(
|
|
57
48
|
context: LoadContext,
|
|
58
49
|
options: PluginOptions,
|
|
59
|
-
): Plugin<BlogContent
|
|
60
|
-
if (options.admonitions) {
|
|
61
|
-
options.remarkPlugins = options.remarkPlugins.concat([
|
|
62
|
-
[admonitions, options.admonitions],
|
|
63
|
-
]);
|
|
64
|
-
}
|
|
65
|
-
|
|
50
|
+
): Promise<Plugin<BlogContent>> {
|
|
66
51
|
const {
|
|
67
52
|
siteDir,
|
|
68
|
-
siteConfig
|
|
53
|
+
siteConfig,
|
|
69
54
|
generatedFilesDir,
|
|
55
|
+
localizationDir,
|
|
70
56
|
i18n: {currentLocale},
|
|
71
57
|
} = context;
|
|
58
|
+
const {onBrokenMarkdownLinks, baseUrl} = siteConfig;
|
|
72
59
|
|
|
73
60
|
const contentPaths: BlogContentPaths = {
|
|
74
61
|
contentPath: path.resolve(siteDir, options.path),
|
|
75
62
|
contentPathLocalized: getPluginI18nPath({
|
|
76
|
-
|
|
77
|
-
locale: currentLocale,
|
|
63
|
+
localizationDir,
|
|
78
64
|
pluginName: 'docusaurus-plugin-content-blog',
|
|
79
65
|
pluginId: options.id,
|
|
80
66
|
}),
|
|
@@ -89,34 +75,52 @@ export default function pluginContentBlog(
|
|
|
89
75
|
const aliasedSource = (source: string) =>
|
|
90
76
|
`~blog/${posixPath(path.relative(pluginDataDirRoot, source))}`;
|
|
91
77
|
|
|
78
|
+
const authorsMapFilePath = await getDataFilePath({
|
|
79
|
+
filePath: options.authorsMapPath,
|
|
80
|
+
contentPaths,
|
|
81
|
+
});
|
|
82
|
+
|
|
92
83
|
return {
|
|
93
84
|
name: 'docusaurus-plugin-content-blog',
|
|
94
85
|
|
|
95
86
|
getPathsToWatch() {
|
|
96
|
-
const {include
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return include.map((pattern) => `${contentPath}/${pattern}`);
|
|
100
|
-
}),
|
|
87
|
+
const {include} = options;
|
|
88
|
+
const contentMarkdownGlobs = getContentPathList(contentPaths).flatMap(
|
|
89
|
+
(contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
|
|
101
90
|
);
|
|
91
|
+
|
|
92
|
+
return [authorsMapFilePath, ...contentMarkdownGlobs].filter(
|
|
93
|
+
Boolean,
|
|
94
|
+
) as string[];
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
getTranslationFiles() {
|
|
98
|
+
return getTranslationFiles(options);
|
|
102
99
|
},
|
|
103
100
|
|
|
104
101
|
// Fetches blog contents and returns metadata for the necessary routes.
|
|
105
102
|
async loadContent() {
|
|
106
|
-
const {
|
|
103
|
+
const {
|
|
104
|
+
postsPerPage: postsPerPageOption,
|
|
105
|
+
routeBasePath,
|
|
106
|
+
tagsBasePath,
|
|
107
|
+
blogDescription,
|
|
108
|
+
blogTitle,
|
|
109
|
+
blogSidebarTitle,
|
|
110
|
+
} = options;
|
|
107
111
|
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
options,
|
|
112
|
-
);
|
|
112
|
+
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
113
|
+
const blogTagsListPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
|
|
114
|
+
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
113
115
|
|
|
114
116
|
if (!blogPosts.length) {
|
|
115
117
|
return {
|
|
118
|
+
blogSidebarTitle,
|
|
116
119
|
blogPosts: [],
|
|
117
120
|
blogListPaginated: [],
|
|
118
121
|
blogTags: {},
|
|
119
|
-
blogTagsListPath
|
|
122
|
+
blogTagsListPath,
|
|
123
|
+
blogTagsPaginated: [],
|
|
120
124
|
};
|
|
121
125
|
}
|
|
122
126
|
|
|
@@ -140,85 +144,23 @@ export default function pluginContentBlog(
|
|
|
140
144
|
}
|
|
141
145
|
});
|
|
142
146
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
} = context;
|
|
150
|
-
const basePageUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
151
|
-
|
|
152
|
-
const blogListPaginated: BlogPaginated[] = [];
|
|
153
|
-
|
|
154
|
-
function blogPaginationPermalink(page: number) {
|
|
155
|
-
return page > 0
|
|
156
|
-
? normalizeUrl([basePageUrl, `page/${page + 1}`])
|
|
157
|
-
: basePageUrl;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
for (let page = 0; page < numberOfPages; page += 1) {
|
|
161
|
-
blogListPaginated.push({
|
|
162
|
-
metadata: {
|
|
163
|
-
permalink: blogPaginationPermalink(page),
|
|
164
|
-
page: page + 1,
|
|
165
|
-
postsPerPage,
|
|
166
|
-
totalPages: numberOfPages,
|
|
167
|
-
totalCount,
|
|
168
|
-
previousPage: page !== 0 ? blogPaginationPermalink(page - 1) : null,
|
|
169
|
-
nextPage:
|
|
170
|
-
page < numberOfPages - 1
|
|
171
|
-
? blogPaginationPermalink(page + 1)
|
|
172
|
-
: null,
|
|
173
|
-
blogDescription: options.blogDescription,
|
|
174
|
-
blogTitle: options.blogTitle,
|
|
175
|
-
},
|
|
176
|
-
items: blogPosts
|
|
177
|
-
.slice(page * postsPerPage, (page + 1) * postsPerPage)
|
|
178
|
-
.map((item) => item.id),
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
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
|
-
}
|
|
192
|
-
|
|
193
|
-
// eslint-disable-next-line no-param-reassign
|
|
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
|
-
});
|
|
147
|
+
const blogListPaginated: BlogPaginated[] = paginateBlogPosts({
|
|
148
|
+
blogPosts,
|
|
149
|
+
blogTitle,
|
|
150
|
+
blogDescription,
|
|
151
|
+
postsPerPageOption,
|
|
152
|
+
basePageUrl: baseBlogUrl,
|
|
216
153
|
});
|
|
217
154
|
|
|
218
|
-
const
|
|
219
|
-
|
|
155
|
+
const blogTags: BlogTags = getBlogTags({
|
|
156
|
+
blogPosts,
|
|
157
|
+
postsPerPageOption,
|
|
158
|
+
blogDescription,
|
|
159
|
+
blogTitle,
|
|
160
|
+
});
|
|
220
161
|
|
|
221
162
|
return {
|
|
163
|
+
blogSidebarTitle,
|
|
222
164
|
blogPosts,
|
|
223
165
|
blogListPaginated,
|
|
224
166
|
blogTags,
|
|
@@ -227,31 +169,67 @@ export default function pluginContentBlog(
|
|
|
227
169
|
},
|
|
228
170
|
|
|
229
171
|
async contentLoaded({content: blogContents, actions}) {
|
|
230
|
-
if (!blogContents) {
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
172
|
const {
|
|
235
173
|
blogListComponent,
|
|
236
174
|
blogPostComponent,
|
|
237
175
|
blogTagsListComponent,
|
|
238
176
|
blogTagsPostsComponent,
|
|
177
|
+
blogArchiveComponent,
|
|
178
|
+
routeBasePath,
|
|
179
|
+
archiveBasePath,
|
|
239
180
|
} = options;
|
|
240
181
|
|
|
241
182
|
const {addRoute, createData} = actions;
|
|
242
183
|
const {
|
|
184
|
+
blogSidebarTitle,
|
|
243
185
|
blogPosts,
|
|
244
186
|
blogListPaginated,
|
|
245
187
|
blogTags,
|
|
246
188
|
blogTagsListPath,
|
|
247
189
|
} = blogContents;
|
|
248
190
|
|
|
249
|
-
const blogItemsToMetadata:
|
|
191
|
+
const blogItemsToMetadata: {[postId: string]: BlogPostMetadata} = {};
|
|
250
192
|
|
|
251
193
|
const sidebarBlogPosts =
|
|
252
194
|
options.blogSidebarCount === 'ALL'
|
|
253
195
|
? blogPosts
|
|
254
|
-
:
|
|
196
|
+
: blogPosts.slice(0, options.blogSidebarCount);
|
|
197
|
+
|
|
198
|
+
function blogPostItemsModule(items: string[]) {
|
|
199
|
+
return items.map((postId) => {
|
|
200
|
+
const blogPostMetadata = blogItemsToMetadata[postId]!;
|
|
201
|
+
return {
|
|
202
|
+
content: {
|
|
203
|
+
__import: true,
|
|
204
|
+
path: blogPostMetadata.source,
|
|
205
|
+
query: {
|
|
206
|
+
truncated: true,
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (archiveBasePath && blogPosts.length) {
|
|
214
|
+
const archiveUrl = normalizeUrl([
|
|
215
|
+
baseUrl,
|
|
216
|
+
routeBasePath,
|
|
217
|
+
archiveBasePath,
|
|
218
|
+
]);
|
|
219
|
+
// Create a blog archive route
|
|
220
|
+
const archiveProp = await createData(
|
|
221
|
+
`${docuHash(archiveUrl)}.json`,
|
|
222
|
+
JSON.stringify({blogPosts}, null, 2),
|
|
223
|
+
);
|
|
224
|
+
addRoute({
|
|
225
|
+
path: archiveUrl,
|
|
226
|
+
component: blogArchiveComponent,
|
|
227
|
+
exact: true,
|
|
228
|
+
modules: {
|
|
229
|
+
archive: aliasedSource(archiveProp),
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
}
|
|
255
233
|
|
|
256
234
|
// This prop is useful to provide the blog list sidebar
|
|
257
235
|
const sidebarProp = await createData(
|
|
@@ -260,7 +238,7 @@ export default function pluginContentBlog(
|
|
|
260
238
|
`blog-post-list-prop-${pluginId}.json`,
|
|
261
239
|
JSON.stringify(
|
|
262
240
|
{
|
|
263
|
-
title:
|
|
241
|
+
title: blogSidebarTitle,
|
|
264
242
|
items: sidebarBlogPosts.map((blogPost) => ({
|
|
265
243
|
title: blogPost.metadata.title,
|
|
266
244
|
permalink: blogPost.metadata.permalink,
|
|
@@ -312,77 +290,28 @@ export default function pluginContentBlog(
|
|
|
312
290
|
exact: true,
|
|
313
291
|
modules: {
|
|
314
292
|
sidebar: aliasedSource(sidebarProp),
|
|
315
|
-
items: items
|
|
316
|
-
// To tell routes.js this is an import and not a nested object to recurse.
|
|
317
|
-
return {
|
|
318
|
-
content: {
|
|
319
|
-
__import: true,
|
|
320
|
-
path: blogItemsToMetadata[postID].source,
|
|
321
|
-
query: {
|
|
322
|
-
truncated: true,
|
|
323
|
-
},
|
|
324
|
-
},
|
|
325
|
-
};
|
|
326
|
-
}),
|
|
293
|
+
items: blogPostItemsModule(items),
|
|
327
294
|
metadata: aliasedSource(pageMetadataPath),
|
|
328
295
|
},
|
|
329
296
|
});
|
|
330
297
|
}),
|
|
331
298
|
);
|
|
332
299
|
|
|
333
|
-
// Tags.
|
|
334
|
-
if (
|
|
300
|
+
// Tags. This is the last part so we early-return if there are no tags.
|
|
301
|
+
if (Object.keys(blogTags).length === 0) {
|
|
335
302
|
return;
|
|
336
303
|
}
|
|
337
304
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
tagsModule[tag] = {
|
|
345
|
-
allTagsPath: blogTagsListPath,
|
|
346
|
-
slug: tag,
|
|
347
|
-
name,
|
|
348
|
-
count: items.length,
|
|
349
|
-
permalink,
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
const tagsMetadataPath = await createData(
|
|
353
|
-
`${docuHash(permalink)}.json`,
|
|
354
|
-
JSON.stringify(tagsModule[tag], null, 2),
|
|
355
|
-
);
|
|
305
|
+
async function createTagsListPage() {
|
|
306
|
+
const tagsProp: TagsListItem[] = Object.values(blogTags).map((tag) => ({
|
|
307
|
+
label: tag.label,
|
|
308
|
+
permalink: tag.permalink,
|
|
309
|
+
count: tag.items.length,
|
|
310
|
+
}));
|
|
356
311
|
|
|
357
|
-
|
|
358
|
-
path: permalink,
|
|
359
|
-
component: blogTagsPostsComponent,
|
|
360
|
-
exact: true,
|
|
361
|
-
modules: {
|
|
362
|
-
sidebar: aliasedSource(sidebarProp),
|
|
363
|
-
items: items.map((postID) => {
|
|
364
|
-
const metadata = blogItemsToMetadata[postID];
|
|
365
|
-
return {
|
|
366
|
-
content: {
|
|
367
|
-
__import: true,
|
|
368
|
-
path: metadata.source,
|
|
369
|
-
query: {
|
|
370
|
-
truncated: true,
|
|
371
|
-
},
|
|
372
|
-
},
|
|
373
|
-
};
|
|
374
|
-
}),
|
|
375
|
-
metadata: aliasedSource(tagsMetadataPath),
|
|
376
|
-
},
|
|
377
|
-
});
|
|
378
|
-
}),
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
// Only create /tags page if there are tags.
|
|
382
|
-
if (Object.keys(blogTags).length > 0) {
|
|
383
|
-
const tagsListPath = await createData(
|
|
312
|
+
const tagsPropPath = await createData(
|
|
384
313
|
`${docuHash(`${blogTagsListPath}-tags`)}.json`,
|
|
385
|
-
JSON.stringify(
|
|
314
|
+
JSON.stringify(tagsProp, null, 2),
|
|
386
315
|
);
|
|
387
316
|
|
|
388
317
|
addRoute({
|
|
@@ -391,19 +320,57 @@ export default function pluginContentBlog(
|
|
|
391
320
|
exact: true,
|
|
392
321
|
modules: {
|
|
393
322
|
sidebar: aliasedSource(sidebarProp),
|
|
394
|
-
tags: aliasedSource(
|
|
323
|
+
tags: aliasedSource(tagsPropPath),
|
|
395
324
|
},
|
|
396
325
|
});
|
|
397
326
|
}
|
|
327
|
+
|
|
328
|
+
async function createTagPostsListPage(tag: BlogTag): Promise<void> {
|
|
329
|
+
await Promise.all(
|
|
330
|
+
tag.pages.map(async (blogPaginated) => {
|
|
331
|
+
const {metadata, items} = blogPaginated;
|
|
332
|
+
const tagProp: TagModule = {
|
|
333
|
+
label: tag.label,
|
|
334
|
+
permalink: tag.permalink,
|
|
335
|
+
allTagsPath: blogTagsListPath,
|
|
336
|
+
count: tag.items.length,
|
|
337
|
+
};
|
|
338
|
+
const tagPropPath = await createData(
|
|
339
|
+
`${docuHash(metadata.permalink)}.json`,
|
|
340
|
+
JSON.stringify(tagProp, null, 2),
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
const listMetadataPath = await createData(
|
|
344
|
+
`${docuHash(metadata.permalink)}-list.json`,
|
|
345
|
+
JSON.stringify(metadata, null, 2),
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
addRoute({
|
|
349
|
+
path: metadata.permalink,
|
|
350
|
+
component: blogTagsPostsComponent,
|
|
351
|
+
exact: true,
|
|
352
|
+
modules: {
|
|
353
|
+
sidebar: aliasedSource(sidebarProp),
|
|
354
|
+
items: blogPostItemsModule(items),
|
|
355
|
+
tag: aliasedSource(tagPropPath),
|
|
356
|
+
listMetadata: aliasedSource(listMetadataPath),
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
}),
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
await createTagsListPage();
|
|
364
|
+
await Promise.all(Object.values(blogTags).map(createTagPostsListPage));
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
translateContent({content, translationFiles}) {
|
|
368
|
+
return translateContent(content, translationFiles);
|
|
398
369
|
},
|
|
399
370
|
|
|
400
|
-
configureWebpack(
|
|
401
|
-
_config: Configuration,
|
|
402
|
-
isServer: boolean,
|
|
403
|
-
{getJSLoader}: ConfigureWebpackUtils,
|
|
404
|
-
content,
|
|
405
|
-
) {
|
|
371
|
+
configureWebpack(_config, isServer, {getJSLoader}, content) {
|
|
406
372
|
const {
|
|
373
|
+
admonitions,
|
|
407
374
|
rehypePlugins,
|
|
408
375
|
remarkPlugins,
|
|
409
376
|
truncateMarker,
|
|
@@ -420,10 +387,9 @@ export default function pluginContentBlog(
|
|
|
420
387
|
if (onBrokenMarkdownLinks === 'ignore') {
|
|
421
388
|
return;
|
|
422
389
|
}
|
|
423
|
-
|
|
424
|
-
`Blog markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath}`,
|
|
390
|
+
logger.report(
|
|
425
391
|
onBrokenMarkdownLinks,
|
|
426
|
-
)
|
|
392
|
+
)`Blog markdown link couldn't be resolved: (url=${brokenMarkdownLink.link}) in path=${brokenMarkdownLink.filePath}`;
|
|
427
393
|
},
|
|
428
394
|
};
|
|
429
395
|
|
|
@@ -437,7 +403,7 @@ export default function pluginContentBlog(
|
|
|
437
403
|
module: {
|
|
438
404
|
rules: [
|
|
439
405
|
{
|
|
440
|
-
test:
|
|
406
|
+
test: /\.mdx?$/i,
|
|
441
407
|
include: contentDirs
|
|
442
408
|
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
|
443
409
|
.map(addTrailingPathSeparator),
|
|
@@ -446,11 +412,18 @@ export default function pluginContentBlog(
|
|
|
446
412
|
{
|
|
447
413
|
loader: require.resolve('@docusaurus/mdx-loader'),
|
|
448
414
|
options: {
|
|
415
|
+
admonitions,
|
|
449
416
|
remarkPlugins,
|
|
450
417
|
rehypePlugins,
|
|
451
|
-
beforeDefaultRemarkPlugins
|
|
418
|
+
beforeDefaultRemarkPlugins: [
|
|
419
|
+
footnoteIDFixer,
|
|
420
|
+
...beforeDefaultRemarkPlugins,
|
|
421
|
+
],
|
|
452
422
|
beforeDefaultRehypePlugins,
|
|
453
|
-
|
|
423
|
+
staticDirs: siteConfig.staticDirectories.map((dir) =>
|
|
424
|
+
path.resolve(siteDir, dir),
|
|
425
|
+
),
|
|
426
|
+
siteDir,
|
|
454
427
|
isMDXPartial: createAbsoluteFilePathMatcher(
|
|
455
428
|
options.exclude,
|
|
456
429
|
contentDirs,
|
|
@@ -467,6 +440,21 @@ export default function pluginContentBlog(
|
|
|
467
440
|
// For blog posts a title in markdown is always removed
|
|
468
441
|
// Blog posts title are rendered separately
|
|
469
442
|
removeContentTitle: true,
|
|
443
|
+
|
|
444
|
+
// Assets allow to convert some relative images paths to
|
|
445
|
+
// require() calls
|
|
446
|
+
createAssets: ({
|
|
447
|
+
frontMatter,
|
|
448
|
+
metadata,
|
|
449
|
+
}: {
|
|
450
|
+
frontMatter: BlogPostFrontMatter;
|
|
451
|
+
metadata: BlogPostMetadata;
|
|
452
|
+
}): Assets => ({
|
|
453
|
+
image: frontMatter.image,
|
|
454
|
+
authorsImageUrls: metadata.authors.map(
|
|
455
|
+
(author) => author.imageURL,
|
|
456
|
+
),
|
|
457
|
+
}),
|
|
470
458
|
},
|
|
471
459
|
},
|
|
472
460
|
{
|
|
@@ -480,72 +468,55 @@ export default function pluginContentBlog(
|
|
|
480
468
|
};
|
|
481
469
|
},
|
|
482
470
|
|
|
483
|
-
async postBuild({outDir}
|
|
484
|
-
if (!options.feedOptions
|
|
471
|
+
async postBuild({outDir, content}) {
|
|
472
|
+
if (!options.feedOptions.type) {
|
|
485
473
|
return;
|
|
486
474
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
if (!feed) {
|
|
475
|
+
const {blogPosts} = content;
|
|
476
|
+
if (!blogPosts.length) {
|
|
491
477
|
return;
|
|
492
478
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
-
);
|
|
479
|
+
await createBlogFeedFiles({
|
|
480
|
+
blogPosts,
|
|
481
|
+
options,
|
|
482
|
+
outDir,
|
|
483
|
+
siteConfig,
|
|
484
|
+
locale: currentLocale,
|
|
485
|
+
});
|
|
511
486
|
},
|
|
512
487
|
|
|
513
488
|
injectHtmlTags({content}) {
|
|
514
|
-
if (!content.blogPosts.length) {
|
|
515
|
-
return {};
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
if (!options.feedOptions?.type) {
|
|
489
|
+
if (!content.blogPosts.length || !options.feedOptions.type) {
|
|
519
490
|
return {};
|
|
520
491
|
}
|
|
521
492
|
|
|
522
493
|
const feedTypes = options.feedOptions.type;
|
|
523
|
-
const
|
|
524
|
-
siteConfig: {title},
|
|
525
|
-
baseUrl,
|
|
526
|
-
} = context;
|
|
494
|
+
const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
|
|
527
495
|
const feedsConfig = {
|
|
528
496
|
rss: {
|
|
529
497
|
type: 'application/rss+xml',
|
|
530
498
|
path: 'rss.xml',
|
|
531
|
-
title: `${
|
|
499
|
+
title: `${feedTitle} RSS Feed`,
|
|
532
500
|
},
|
|
533
501
|
atom: {
|
|
534
502
|
type: 'application/atom+xml',
|
|
535
503
|
path: 'atom.xml',
|
|
536
|
-
title: `${
|
|
504
|
+
title: `${feedTitle} Atom Feed`,
|
|
505
|
+
},
|
|
506
|
+
json: {
|
|
507
|
+
type: 'application/json',
|
|
508
|
+
path: 'feed.json',
|
|
509
|
+
title: `${feedTitle} JSON Feed`,
|
|
537
510
|
},
|
|
538
511
|
};
|
|
539
512
|
const headTags: HtmlTags = [];
|
|
540
513
|
|
|
541
514
|
feedTypes.forEach((feedType) => {
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
const {type, path: feedConfigPath, title: feedConfigTitle} = feedConfig;
|
|
515
|
+
const {
|
|
516
|
+
type,
|
|
517
|
+
path: feedConfigPath,
|
|
518
|
+
title: feedConfigTitle,
|
|
519
|
+
} = feedsConfig[feedType];
|
|
549
520
|
|
|
550
521
|
headTags.push({
|
|
551
522
|
tagName: 'link',
|
|
@@ -569,10 +540,4 @@ export default function pluginContentBlog(
|
|
|
569
540
|
};
|
|
570
541
|
}
|
|
571
542
|
|
|
572
|
-
export
|
|
573
|
-
validate,
|
|
574
|
-
options,
|
|
575
|
-
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
|
|
576
|
-
const validatedOptions = validate(PluginOptionSchema, options);
|
|
577
|
-
return validatedOptions;
|
|
578
|
-
}
|
|
543
|
+
export {validateOptions} from './options';
|