@docusaurus/plugin-content-blog 2.0.0-beta.ff31de0ff → 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 +214 -141
- package/lib/feed.d.ts +15 -0
- package/lib/feed.js +102 -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 +179 -205
- 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 -18
- package/src/authors.ts +168 -0
- package/src/blogUtils.ts +316 -196
- package/src/feed.ts +171 -0
- package/src/frontMatter.ts +81 -0
- package/src/index.ts +246 -268
- 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 -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/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,37 +75,53 @@ export default function pluginContentBlog(
|
|
|
88
75
|
const aliasedSource = (source: string) =>
|
|
89
76
|
`~blog/${posixPath(path.relative(pluginDataDirRoot, source))}`;
|
|
90
77
|
|
|
91
|
-
|
|
78
|
+
const authorsMapFilePath = await getDataFilePath({
|
|
79
|
+
filePath: options.authorsMapPath,
|
|
80
|
+
contentPaths,
|
|
81
|
+
});
|
|
92
82
|
|
|
93
83
|
return {
|
|
94
84
|
name: 'docusaurus-plugin-content-blog',
|
|
95
85
|
|
|
96
86
|
getPathsToWatch() {
|
|
97
|
-
const {include
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return include.map((pattern) => `${contentPath}/${pattern}`);
|
|
101
|
-
}),
|
|
87
|
+
const {include} = options;
|
|
88
|
+
const contentMarkdownGlobs = getContentPathList(contentPaths).flatMap(
|
|
89
|
+
(contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
|
|
102
90
|
);
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
getClientModules() {
|
|
106
|
-
const modules = [];
|
|
107
91
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
92
|
+
return [authorsMapFilePath, ...contentMarkdownGlobs].filter(
|
|
93
|
+
Boolean,
|
|
94
|
+
) as string[];
|
|
95
|
+
},
|
|
111
96
|
|
|
112
|
-
|
|
97
|
+
getTranslationFiles() {
|
|
98
|
+
return getTranslationFiles(options);
|
|
113
99
|
},
|
|
114
100
|
|
|
115
101
|
// Fetches blog contents and returns metadata for the necessary routes.
|
|
116
102
|
async loadContent() {
|
|
117
|
-
const {
|
|
103
|
+
const {
|
|
104
|
+
postsPerPage: postsPerPageOption,
|
|
105
|
+
routeBasePath,
|
|
106
|
+
tagsBasePath,
|
|
107
|
+
blogDescription,
|
|
108
|
+
blogTitle,
|
|
109
|
+
blogSidebarTitle,
|
|
110
|
+
} = options;
|
|
111
|
+
|
|
112
|
+
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
113
|
+
const blogTagsListPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
|
|
114
|
+
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
118
115
|
|
|
119
|
-
blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
120
116
|
if (!blogPosts.length) {
|
|
121
|
-
return
|
|
117
|
+
return {
|
|
118
|
+
blogSidebarTitle,
|
|
119
|
+
blogPosts: [],
|
|
120
|
+
blogListPaginated: [],
|
|
121
|
+
blogTags: {},
|
|
122
|
+
blogTagsListPath,
|
|
123
|
+
blogTagsPaginated: [],
|
|
124
|
+
};
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
// Colocate next and prev metadata.
|
|
@@ -141,85 +144,23 @@ export default function pluginContentBlog(
|
|
|
141
144
|
}
|
|
142
145
|
});
|
|
143
146
|
|
|
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
|
-
});
|
|
147
|
+
const blogListPaginated: BlogPaginated[] = paginateBlogPosts({
|
|
148
|
+
blogPosts,
|
|
149
|
+
blogTitle,
|
|
150
|
+
blogDescription,
|
|
151
|
+
postsPerPageOption,
|
|
152
|
+
basePageUrl: baseBlogUrl,
|
|
217
153
|
});
|
|
218
154
|
|
|
219
|
-
const
|
|
220
|
-
|
|
155
|
+
const blogTags: BlogTags = getBlogTags({
|
|
156
|
+
blogPosts,
|
|
157
|
+
postsPerPageOption,
|
|
158
|
+
blogDescription,
|
|
159
|
+
blogTitle,
|
|
160
|
+
});
|
|
221
161
|
|
|
222
162
|
return {
|
|
163
|
+
blogSidebarTitle,
|
|
223
164
|
blogPosts,
|
|
224
165
|
blogListPaginated,
|
|
225
166
|
blogTags,
|
|
@@ -228,31 +169,67 @@ export default function pluginContentBlog(
|
|
|
228
169
|
},
|
|
229
170
|
|
|
230
171
|
async contentLoaded({content: blogContents, actions}) {
|
|
231
|
-
if (!blogContents) {
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
172
|
const {
|
|
236
173
|
blogListComponent,
|
|
237
174
|
blogPostComponent,
|
|
238
175
|
blogTagsListComponent,
|
|
239
176
|
blogTagsPostsComponent,
|
|
177
|
+
blogArchiveComponent,
|
|
178
|
+
routeBasePath,
|
|
179
|
+
archiveBasePath,
|
|
240
180
|
} = options;
|
|
241
181
|
|
|
242
182
|
const {addRoute, createData} = actions;
|
|
243
183
|
const {
|
|
244
|
-
|
|
184
|
+
blogSidebarTitle,
|
|
185
|
+
blogPosts,
|
|
245
186
|
blogListPaginated,
|
|
246
187
|
blogTags,
|
|
247
188
|
blogTagsListPath,
|
|
248
189
|
} = blogContents;
|
|
249
190
|
|
|
250
|
-
const blogItemsToMetadata:
|
|
191
|
+
const blogItemsToMetadata: {[postId: string]: BlogPostMetadata} = {};
|
|
251
192
|
|
|
252
193
|
const sidebarBlogPosts =
|
|
253
194
|
options.blogSidebarCount === 'ALL'
|
|
254
195
|
? blogPosts
|
|
255
|
-
:
|
|
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
|
+
}
|
|
256
233
|
|
|
257
234
|
// This prop is useful to provide the blog list sidebar
|
|
258
235
|
const sidebarProp = await createData(
|
|
@@ -261,7 +238,7 @@ export default function pluginContentBlog(
|
|
|
261
238
|
`blog-post-list-prop-${pluginId}.json`,
|
|
262
239
|
JSON.stringify(
|
|
263
240
|
{
|
|
264
|
-
title:
|
|
241
|
+
title: blogSidebarTitle,
|
|
265
242
|
items: sidebarBlogPosts.map((blogPost) => ({
|
|
266
243
|
title: blogPost.metadata.title,
|
|
267
244
|
permalink: blogPost.metadata.permalink,
|
|
@@ -274,7 +251,7 @@ export default function pluginContentBlog(
|
|
|
274
251
|
|
|
275
252
|
// Create routes for blog entries.
|
|
276
253
|
await Promise.all(
|
|
277
|
-
|
|
254
|
+
blogPosts.map(async (blogPost) => {
|
|
278
255
|
const {id, metadata} = blogPost;
|
|
279
256
|
await createData(
|
|
280
257
|
// Note that this created data path must be in sync with
|
|
@@ -288,7 +265,7 @@ export default function pluginContentBlog(
|
|
|
288
265
|
component: blogPostComponent,
|
|
289
266
|
exact: true,
|
|
290
267
|
modules: {
|
|
291
|
-
sidebar: sidebarProp,
|
|
268
|
+
sidebar: aliasedSource(sidebarProp),
|
|
292
269
|
content: metadata.source,
|
|
293
270
|
},
|
|
294
271
|
});
|
|
@@ -312,78 +289,29 @@ export default function pluginContentBlog(
|
|
|
312
289
|
component: blogListComponent,
|
|
313
290
|
exact: true,
|
|
314
291
|
modules: {
|
|
315
|
-
sidebar: sidebarProp,
|
|
316
|
-
items: items
|
|
317
|
-
// To tell routes.js this is an import and not a nested object to recurse.
|
|
318
|
-
return {
|
|
319
|
-
content: {
|
|
320
|
-
__import: true,
|
|
321
|
-
path: blogItemsToMetadata[postID].source,
|
|
322
|
-
query: {
|
|
323
|
-
truncated: true,
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
};
|
|
327
|
-
}),
|
|
292
|
+
sidebar: aliasedSource(sidebarProp),
|
|
293
|
+
items: blogPostItemsModule(items),
|
|
328
294
|
metadata: aliasedSource(pageMetadataPath),
|
|
329
295
|
},
|
|
330
296
|
});
|
|
331
297
|
}),
|
|
332
298
|
);
|
|
333
299
|
|
|
334
|
-
// Tags.
|
|
335
|
-
if (
|
|
300
|
+
// Tags. This is the last part so we early-return if there are no tags.
|
|
301
|
+
if (Object.keys(blogTags).length === 0) {
|
|
336
302
|
return;
|
|
337
303
|
}
|
|
338
304
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
);
|
|
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
|
+
}));
|
|
381
311
|
|
|
382
|
-
|
|
383
|
-
if (Object.keys(blogTags).length > 0) {
|
|
384
|
-
const tagsListPath = await createData(
|
|
312
|
+
const tagsPropPath = await createData(
|
|
385
313
|
`${docuHash(`${blogTagsListPath}-tags`)}.json`,
|
|
386
|
-
JSON.stringify(
|
|
314
|
+
JSON.stringify(tagsProp, null, 2),
|
|
387
315
|
);
|
|
388
316
|
|
|
389
317
|
addRoute({
|
|
@@ -391,19 +319,58 @@ export default function pluginContentBlog(
|
|
|
391
319
|
component: blogTagsListComponent,
|
|
392
320
|
exact: true,
|
|
393
321
|
modules: {
|
|
394
|
-
sidebar: sidebarProp,
|
|
395
|
-
tags: aliasedSource(
|
|
322
|
+
sidebar: aliasedSource(sidebarProp),
|
|
323
|
+
tags: aliasedSource(tagsPropPath),
|
|
396
324
|
},
|
|
397
325
|
});
|
|
398
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);
|
|
399
369
|
},
|
|
400
370
|
|
|
401
|
-
configureWebpack(
|
|
402
|
-
_config: Configuration,
|
|
403
|
-
isServer: boolean,
|
|
404
|
-
{getJSLoader}: ConfigureWebpackUtils,
|
|
405
|
-
) {
|
|
371
|
+
configureWebpack(_config, isServer, {getJSLoader}, content) {
|
|
406
372
|
const {
|
|
373
|
+
admonitions,
|
|
407
374
|
rehypePlugins,
|
|
408
375
|
remarkPlugins,
|
|
409
376
|
truncateMarker,
|
|
@@ -415,18 +382,18 @@ export default function pluginContentBlog(
|
|
|
415
382
|
siteDir,
|
|
416
383
|
contentPaths,
|
|
417
384
|
truncateMarker,
|
|
418
|
-
sourceToPermalink: getSourceToPermalink(blogPosts),
|
|
385
|
+
sourceToPermalink: getSourceToPermalink(content.blogPosts),
|
|
419
386
|
onBrokenMarkdownLink: (brokenMarkdownLink) => {
|
|
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
|
|
|
396
|
+
const contentDirs = getContentPathList(contentPaths);
|
|
430
397
|
return {
|
|
431
398
|
resolve: {
|
|
432
399
|
alias: {
|
|
@@ -436,8 +403,8 @@ export default function pluginContentBlog(
|
|
|
436
403
|
module: {
|
|
437
404
|
rules: [
|
|
438
405
|
{
|
|
439
|
-
test:
|
|
440
|
-
include:
|
|
406
|
+
test: /\.mdx?$/i,
|
|
407
|
+
include: contentDirs
|
|
441
408
|
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
|
442
409
|
.map(addTrailingPathSeparator),
|
|
443
410
|
use: [
|
|
@@ -445,20 +412,49 @@ export default function pluginContentBlog(
|
|
|
445
412
|
{
|
|
446
413
|
loader: require.resolve('@docusaurus/mdx-loader'),
|
|
447
414
|
options: {
|
|
415
|
+
admonitions,
|
|
448
416
|
remarkPlugins,
|
|
449
417
|
rehypePlugins,
|
|
450
|
-
beforeDefaultRemarkPlugins
|
|
418
|
+
beforeDefaultRemarkPlugins: [
|
|
419
|
+
footnoteIDFixer,
|
|
420
|
+
...beforeDefaultRemarkPlugins,
|
|
421
|
+
],
|
|
451
422
|
beforeDefaultRehypePlugins,
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
423
|
+
staticDirs: siteConfig.staticDirectories.map((dir) =>
|
|
424
|
+
path.resolve(siteDir, dir),
|
|
425
|
+
),
|
|
426
|
+
siteDir,
|
|
427
|
+
isMDXPartial: createAbsoluteFilePathMatcher(
|
|
428
|
+
options.exclude,
|
|
429
|
+
contentDirs,
|
|
430
|
+
),
|
|
455
431
|
metadataPath: (mdxPath: string) => {
|
|
432
|
+
// Note that metadataPath must be the same/in-sync as
|
|
433
|
+
// the path from createData for each MDX.
|
|
456
434
|
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
|
457
435
|
return path.join(
|
|
458
436
|
dataDir,
|
|
459
437
|
`${docuHash(aliasedPath)}.json`,
|
|
460
438
|
);
|
|
461
439
|
},
|
|
440
|
+
// For blog posts a title in markdown is always removed
|
|
441
|
+
// Blog posts title are rendered separately
|
|
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
|
+
}),
|
|
462
458
|
},
|
|
463
459
|
},
|
|
464
460
|
{
|
|
@@ -472,67 +468,55 @@ export default function pluginContentBlog(
|
|
|
472
468
|
};
|
|
473
469
|
},
|
|
474
470
|
|
|
475
|
-
async postBuild({outDir}
|
|
476
|
-
if (!options.feedOptions
|
|
471
|
+
async postBuild({outDir, content}) {
|
|
472
|
+
if (!options.feedOptions.type) {
|
|
477
473
|
return;
|
|
478
474
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
if (!feed) {
|
|
475
|
+
const {blogPosts} = content;
|
|
476
|
+
if (!blogPosts.length) {
|
|
483
477
|
return;
|
|
484
478
|
}
|
|
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
|
-
);
|
|
479
|
+
await createBlogFeedFiles({
|
|
480
|
+
blogPosts,
|
|
481
|
+
options,
|
|
482
|
+
outDir,
|
|
483
|
+
siteConfig,
|
|
484
|
+
locale: currentLocale,
|
|
485
|
+
});
|
|
503
486
|
},
|
|
504
487
|
|
|
505
|
-
injectHtmlTags() {
|
|
506
|
-
if (!options.feedOptions
|
|
488
|
+
injectHtmlTags({content}) {
|
|
489
|
+
if (!content.blogPosts.length || !options.feedOptions.type) {
|
|
507
490
|
return {};
|
|
508
491
|
}
|
|
492
|
+
|
|
509
493
|
const feedTypes = options.feedOptions.type;
|
|
510
|
-
const
|
|
511
|
-
siteConfig: {title},
|
|
512
|
-
baseUrl,
|
|
513
|
-
} = context;
|
|
494
|
+
const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
|
|
514
495
|
const feedsConfig = {
|
|
515
496
|
rss: {
|
|
516
497
|
type: 'application/rss+xml',
|
|
517
498
|
path: 'rss.xml',
|
|
518
|
-
title: `${
|
|
499
|
+
title: `${feedTitle} RSS Feed`,
|
|
519
500
|
},
|
|
520
501
|
atom: {
|
|
521
502
|
type: 'application/atom+xml',
|
|
522
503
|
path: 'atom.xml',
|
|
523
|
-
title: `${
|
|
504
|
+
title: `${feedTitle} Atom Feed`,
|
|
505
|
+
},
|
|
506
|
+
json: {
|
|
507
|
+
type: 'application/json',
|
|
508
|
+
path: 'feed.json',
|
|
509
|
+
title: `${feedTitle} JSON Feed`,
|
|
524
510
|
},
|
|
525
511
|
};
|
|
526
512
|
const headTags: HtmlTags = [];
|
|
527
513
|
|
|
528
514
|
feedTypes.forEach((feedType) => {
|
|
529
|
-
const
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
const {type, path: feedConfigPath, title: feedConfigTitle} = feedConfig;
|
|
515
|
+
const {
|
|
516
|
+
type,
|
|
517
|
+
path: feedConfigPath,
|
|
518
|
+
title: feedConfigTitle,
|
|
519
|
+
} = feedsConfig[feedType];
|
|
536
520
|
|
|
537
521
|
headTags.push({
|
|
538
522
|
tagName: 'link',
|
|
@@ -556,10 +540,4 @@ export default function pluginContentBlog(
|
|
|
556
540
|
};
|
|
557
541
|
}
|
|
558
542
|
|
|
559
|
-
export
|
|
560
|
-
validate,
|
|
561
|
-
options,
|
|
562
|
-
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
|
|
563
|
-
const validatedOptions = validate(PluginOptionSchema, options);
|
|
564
|
-
return validatedOptions;
|
|
565
|
-
}
|
|
543
|
+
export {validateOptions} from './options';
|