@docusaurus/plugin-content-blog 2.0.0-beta.2 → 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 +201 -145
- package/lib/feed.d.ts +15 -0
- package/lib/feed.js +100 -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 -185
- 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 -18
- package/src/authors.ts +164 -0
- package/src/blogUtils.ts +306 -204
- 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 +225 -246
- 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 -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/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,44 +82,52 @@ export default function pluginContentBlog(
|
|
|
88
82
|
const aliasedSource = (source: string) =>
|
|
89
83
|
`~blog/${posixPath(path.relative(pluginDataDirRoot, source))}`;
|
|
90
84
|
|
|
85
|
+
const authorsMapFilePath = await getDataFilePath({
|
|
86
|
+
filePath: options.authorsMapPath,
|
|
87
|
+
contentPaths,
|
|
88
|
+
});
|
|
89
|
+
|
|
91
90
|
return {
|
|
92
91
|
name: 'docusaurus-plugin-content-blog',
|
|
93
92
|
|
|
94
93
|
getPathsToWatch() {
|
|
95
|
-
const {include
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return include.map((pattern) => `${contentPath}/${pattern}`);
|
|
99
|
-
}),
|
|
94
|
+
const {include} = options;
|
|
95
|
+
const contentMarkdownGlobs = getContentPathList(contentPaths).flatMap(
|
|
96
|
+
(contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
|
|
100
97
|
);
|
|
101
|
-
},
|
|
102
98
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
modules.push(require.resolve('remark-admonitions/styles/infima.css'));
|
|
108
|
-
}
|
|
99
|
+
return [authorsMapFilePath, ...contentMarkdownGlobs].filter(
|
|
100
|
+
Boolean,
|
|
101
|
+
) as string[];
|
|
102
|
+
},
|
|
109
103
|
|
|
110
|
-
|
|
104
|
+
getTranslationFiles() {
|
|
105
|
+
return getTranslationFiles(options);
|
|
111
106
|
},
|
|
112
107
|
|
|
113
108
|
// Fetches blog contents and returns metadata for the necessary routes.
|
|
114
109
|
async loadContent() {
|
|
115
|
-
const {
|
|
110
|
+
const {
|
|
111
|
+
postsPerPage: postsPerPageOption,
|
|
112
|
+
routeBasePath,
|
|
113
|
+
tagsBasePath,
|
|
114
|
+
blogDescription,
|
|
115
|
+
blogTitle,
|
|
116
|
+
blogSidebarTitle,
|
|
117
|
+
} = options;
|
|
116
118
|
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
options,
|
|
121
|
-
);
|
|
119
|
+
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
120
|
+
const blogTagsListPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
|
|
121
|
+
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
122
122
|
|
|
123
123
|
if (!blogPosts.length) {
|
|
124
124
|
return {
|
|
125
|
+
blogSidebarTitle,
|
|
125
126
|
blogPosts: [],
|
|
126
127
|
blogListPaginated: [],
|
|
127
128
|
blogTags: {},
|
|
128
|
-
blogTagsListPath
|
|
129
|
+
blogTagsListPath,
|
|
130
|
+
blogTagsPaginated: [],
|
|
129
131
|
};
|
|
130
132
|
}
|
|
131
133
|
|
|
@@ -149,85 +151,23 @@ export default function pluginContentBlog(
|
|
|
149
151
|
}
|
|
150
152
|
});
|
|
151
153
|
|
|
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
|
-
});
|
|
154
|
+
const blogListPaginated: BlogPaginated[] = paginateBlogPosts({
|
|
155
|
+
blogPosts,
|
|
156
|
+
blogTitle,
|
|
157
|
+
blogDescription,
|
|
158
|
+
postsPerPageOption,
|
|
159
|
+
basePageUrl: baseBlogUrl,
|
|
225
160
|
});
|
|
226
161
|
|
|
227
|
-
const
|
|
228
|
-
|
|
162
|
+
const blogTags: BlogTags = getBlogTags({
|
|
163
|
+
blogPosts,
|
|
164
|
+
postsPerPageOption,
|
|
165
|
+
blogDescription,
|
|
166
|
+
blogTitle,
|
|
167
|
+
});
|
|
229
168
|
|
|
230
169
|
return {
|
|
170
|
+
blogSidebarTitle,
|
|
231
171
|
blogPosts,
|
|
232
172
|
blogListPaginated,
|
|
233
173
|
blogTags,
|
|
@@ -245,22 +185,47 @@ export default function pluginContentBlog(
|
|
|
245
185
|
blogPostComponent,
|
|
246
186
|
blogTagsListComponent,
|
|
247
187
|
blogTagsPostsComponent,
|
|
188
|
+
blogArchiveComponent,
|
|
189
|
+
routeBasePath,
|
|
190
|
+
archiveBasePath,
|
|
248
191
|
} = options;
|
|
249
192
|
|
|
250
193
|
const {addRoute, createData} = actions;
|
|
251
194
|
const {
|
|
195
|
+
blogSidebarTitle,
|
|
252
196
|
blogPosts,
|
|
253
197
|
blogListPaginated,
|
|
254
198
|
blogTags,
|
|
255
199
|
blogTagsListPath,
|
|
256
200
|
} = blogContents;
|
|
257
201
|
|
|
258
|
-
const blogItemsToMetadata:
|
|
202
|
+
const blogItemsToMetadata: {[postId: string]: BlogPostMetadata} = {};
|
|
259
203
|
|
|
260
204
|
const sidebarBlogPosts =
|
|
261
205
|
options.blogSidebarCount === 'ALL'
|
|
262
206
|
? blogPosts
|
|
263
|
-
:
|
|
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
|
+
}
|
|
264
229
|
|
|
265
230
|
// This prop is useful to provide the blog list sidebar
|
|
266
231
|
const sidebarProp = await createData(
|
|
@@ -269,7 +234,7 @@ export default function pluginContentBlog(
|
|
|
269
234
|
`blog-post-list-prop-${pluginId}.json`,
|
|
270
235
|
JSON.stringify(
|
|
271
236
|
{
|
|
272
|
-
title:
|
|
237
|
+
title: blogSidebarTitle,
|
|
273
238
|
items: sidebarBlogPosts.map((blogPost) => ({
|
|
274
239
|
title: blogPost.metadata.title,
|
|
275
240
|
permalink: blogPost.metadata.permalink,
|
|
@@ -321,77 +286,36 @@ export default function pluginContentBlog(
|
|
|
321
286
|
exact: true,
|
|
322
287
|
modules: {
|
|
323
288
|
sidebar: aliasedSource(sidebarProp),
|
|
324
|
-
items: items.map((postID) => {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
query: {
|
|
331
|
-
truncated: true,
|
|
332
|
-
},
|
|
289
|
+
items: items.map((postID) => ({
|
|
290
|
+
content: {
|
|
291
|
+
__import: true,
|
|
292
|
+
path: blogItemsToMetadata[postID]!.source,
|
|
293
|
+
query: {
|
|
294
|
+
truncated: true,
|
|
333
295
|
},
|
|
334
|
-
}
|
|
335
|
-
}),
|
|
296
|
+
},
|
|
297
|
+
})),
|
|
336
298
|
metadata: aliasedSource(pageMetadataPath),
|
|
337
299
|
},
|
|
338
300
|
});
|
|
339
301
|
}),
|
|
340
302
|
);
|
|
341
303
|
|
|
342
|
-
// Tags.
|
|
343
|
-
if (
|
|
304
|
+
// Tags. This is the last part so we early-return if there are no tags.
|
|
305
|
+
if (Object.keys(blogTags).length === 0) {
|
|
344
306
|
return;
|
|
345
307
|
}
|
|
346
308
|
|
|
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
|
-
);
|
|
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
|
+
}));
|
|
365
315
|
|
|
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(
|
|
316
|
+
const tagsPropPath = await createData(
|
|
393
317
|
`${docuHash(`${blogTagsListPath}-tags`)}.json`,
|
|
394
|
-
JSON.stringify(
|
|
318
|
+
JSON.stringify(tagsProp, null, 2),
|
|
395
319
|
);
|
|
396
320
|
|
|
397
321
|
addRoute({
|
|
@@ -400,18 +324,66 @@ export default function pluginContentBlog(
|
|
|
400
324
|
exact: true,
|
|
401
325
|
modules: {
|
|
402
326
|
sidebar: aliasedSource(sidebarProp),
|
|
403
|
-
tags: aliasedSource(
|
|
327
|
+
tags: aliasedSource(tagsPropPath),
|
|
404
328
|
},
|
|
405
329
|
});
|
|
406
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));
|
|
407
380
|
},
|
|
408
381
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
) {
|
|
382
|
+
translateContent({content, translationFiles}) {
|
|
383
|
+
return translateContent(content, translationFiles);
|
|
384
|
+
},
|
|
385
|
+
|
|
386
|
+
configureWebpack(_config, isServer, {getJSLoader}, content) {
|
|
415
387
|
const {
|
|
416
388
|
rehypePlugins,
|
|
417
389
|
remarkPlugins,
|
|
@@ -436,6 +408,7 @@ export default function pluginContentBlog(
|
|
|
436
408
|
},
|
|
437
409
|
};
|
|
438
410
|
|
|
411
|
+
const contentDirs = getContentPathList(contentPaths);
|
|
439
412
|
return {
|
|
440
413
|
resolve: {
|
|
441
414
|
alias: {
|
|
@@ -445,8 +418,8 @@ export default function pluginContentBlog(
|
|
|
445
418
|
module: {
|
|
446
419
|
rules: [
|
|
447
420
|
{
|
|
448
|
-
test:
|
|
449
|
-
include:
|
|
421
|
+
test: /\.mdx?$/i,
|
|
422
|
+
include: contentDirs
|
|
450
423
|
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
|
451
424
|
.map(addTrailingPathSeparator),
|
|
452
425
|
use: [
|
|
@@ -456,12 +429,22 @@ export default function pluginContentBlog(
|
|
|
456
429
|
options: {
|
|
457
430
|
remarkPlugins,
|
|
458
431
|
rehypePlugins,
|
|
459
|
-
beforeDefaultRemarkPlugins
|
|
432
|
+
beforeDefaultRemarkPlugins: [
|
|
433
|
+
footnoteIDFixer,
|
|
434
|
+
...beforeDefaultRemarkPlugins,
|
|
435
|
+
],
|
|
460
436
|
beforeDefaultRehypePlugins,
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
437
|
+
staticDirs: siteConfig.staticDirectories.map((dir) =>
|
|
438
|
+
path.resolve(siteDir, dir),
|
|
439
|
+
),
|
|
440
|
+
siteDir,
|
|
441
|
+
isMDXPartial: createAbsoluteFilePathMatcher(
|
|
442
|
+
options.exclude,
|
|
443
|
+
contentDirs,
|
|
444
|
+
),
|
|
464
445
|
metadataPath: (mdxPath: string) => {
|
|
446
|
+
// Note that metadataPath must be the same/in-sync as
|
|
447
|
+
// the path from createData for each MDX.
|
|
465
448
|
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
|
466
449
|
return path.join(
|
|
467
450
|
dataDir,
|
|
@@ -471,6 +454,21 @@ export default function pluginContentBlog(
|
|
|
471
454
|
// For blog posts a title in markdown is always removed
|
|
472
455
|
// Blog posts title are rendered separately
|
|
473
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
|
+
}),
|
|
474
472
|
},
|
|
475
473
|
},
|
|
476
474
|
{
|
|
@@ -484,34 +482,21 @@ export default function pluginContentBlog(
|
|
|
484
482
|
};
|
|
485
483
|
},
|
|
486
484
|
|
|
487
|
-
async postBuild({outDir}
|
|
488
|
-
if (!options.feedOptions
|
|
485
|
+
async postBuild({outDir, content}) {
|
|
486
|
+
if (!options.feedOptions.type) {
|
|
489
487
|
return;
|
|
490
488
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
if (!feed) {
|
|
489
|
+
const {blogPosts} = content;
|
|
490
|
+
if (!blogPosts.length) {
|
|
495
491
|
return;
|
|
496
492
|
}
|
|
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
|
-
);
|
|
493
|
+
await createBlogFeedFiles({
|
|
494
|
+
blogPosts,
|
|
495
|
+
options,
|
|
496
|
+
outDir,
|
|
497
|
+
siteConfig,
|
|
498
|
+
locale: currentLocale,
|
|
499
|
+
});
|
|
515
500
|
},
|
|
516
501
|
|
|
517
502
|
injectHtmlTags({content}) {
|
|
@@ -524,32 +509,32 @@ export default function pluginContentBlog(
|
|
|
524
509
|
}
|
|
525
510
|
|
|
526
511
|
const feedTypes = options.feedOptions.type;
|
|
527
|
-
const
|
|
528
|
-
siteConfig: {title},
|
|
529
|
-
baseUrl,
|
|
530
|
-
} = context;
|
|
512
|
+
const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
|
|
531
513
|
const feedsConfig = {
|
|
532
514
|
rss: {
|
|
533
515
|
type: 'application/rss+xml',
|
|
534
516
|
path: 'rss.xml',
|
|
535
|
-
title: `${
|
|
517
|
+
title: `${feedTitle} RSS Feed`,
|
|
536
518
|
},
|
|
537
519
|
atom: {
|
|
538
520
|
type: 'application/atom+xml',
|
|
539
521
|
path: 'atom.xml',
|
|
540
|
-
title: `${
|
|
522
|
+
title: `${feedTitle} Atom Feed`,
|
|
523
|
+
},
|
|
524
|
+
json: {
|
|
525
|
+
type: 'application/json',
|
|
526
|
+
path: 'feed.json',
|
|
527
|
+
title: `${feedTitle} JSON Feed`,
|
|
541
528
|
},
|
|
542
529
|
};
|
|
543
530
|
const headTags: HtmlTags = [];
|
|
544
531
|
|
|
545
532
|
feedTypes.forEach((feedType) => {
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
const {type, path: feedConfigPath, title: feedConfigTitle} = feedConfig;
|
|
533
|
+
const {
|
|
534
|
+
type,
|
|
535
|
+
path: feedConfigPath,
|
|
536
|
+
title: feedConfigTitle,
|
|
537
|
+
} = feedsConfig[feedType];
|
|
553
538
|
|
|
554
539
|
headTags.push({
|
|
555
540
|
tagName: 'link',
|
|
@@ -573,10 +558,4 @@ export default function pluginContentBlog(
|
|
|
573
558
|
};
|
|
574
559
|
}
|
|
575
560
|
|
|
576
|
-
export
|
|
577
|
-
validate,
|
|
578
|
-
options,
|
|
579
|
-
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
|
|
580
|
-
const validatedOptions = validate(PluginOptionSchema, options);
|
|
581
|
-
return validatedOptions;
|
|
582
|
-
}
|
|
561
|
+
export {validateOptions} from './options';
|