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