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