@docusaurus/plugin-content-blog 2.0.0-beta.15a2b59f9 → 2.0.0-beta.17
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 +20 -0
- package/lib/authors.js +110 -0
- package/lib/blogFrontMatter.d.ts +1 -21
- package/lib/blogFrontMatter.js +31 -19
- package/lib/blogUtils.d.ts +24 -6
- package/lib/blogUtils.js +196 -143
- package/lib/feed.d.ts +15 -0
- package/lib/feed.js +99 -0
- package/lib/index.d.ts +4 -3
- package/lib/index.js +149 -163
- package/lib/markdownLoader.d.ts +3 -6
- package/lib/markdownLoader.js +5 -6
- package/lib/pluginOptionSchema.d.ts +3 -26
- package/lib/pluginOptionSchema.js +35 -10
- package/lib/translations.d.ts +11 -0
- package/lib/translations.js +53 -0
- package/lib/types.d.ts +10 -46
- package/package.json +21 -18
- package/src/authors.ts +153 -0
- package/src/blogFrontMatter.ts +44 -51
- package/src/blogUtils.ts +289 -195
- package/{types.d.ts → src/deps.d.ts} +0 -0
- package/src/feed.ts +170 -0
- package/src/index.ts +197 -194
- package/src/markdownLoader.ts +10 -15
- package/src/plugin-content-blog.d.ts +270 -0
- package/src/pluginOptionSchema.ts +41 -13
- package/src/translations.ts +64 -0
- package/src/types.ts +19 -53
- package/index.d.ts +0 -138
- package/lib/.tsbuildinfo +0 -1
- 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/tsconfig.json +0 -9
package/src/index.ts
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
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';
|
|
11
10
|
import {
|
|
@@ -16,46 +15,50 @@ import {
|
|
|
16
15
|
reportMessage,
|
|
17
16
|
posixPath,
|
|
18
17
|
addTrailingPathSeparator,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
createAbsoluteFilePathMatcher,
|
|
19
|
+
getContentPathList,
|
|
20
|
+
getDataFilePath,
|
|
22
21
|
DEFAULT_PLUGIN_ID,
|
|
23
|
-
} from '@docusaurus/
|
|
24
|
-
import {
|
|
22
|
+
} from '@docusaurus/utils';
|
|
23
|
+
import {translateContent, getTranslationFiles} from './translations';
|
|
25
24
|
|
|
26
|
-
import {
|
|
27
|
-
|
|
25
|
+
import type {
|
|
26
|
+
BlogTag,
|
|
28
27
|
BlogTags,
|
|
29
28
|
BlogContent,
|
|
30
29
|
BlogItemsToMetadata,
|
|
31
30
|
TagsModule,
|
|
32
31
|
BlogPaginated,
|
|
33
|
-
BlogPost,
|
|
34
32
|
BlogContentPaths,
|
|
35
33
|
BlogMarkdownLoaderOptions,
|
|
34
|
+
MetaData,
|
|
35
|
+
TagModule,
|
|
36
36
|
} from './types';
|
|
37
37
|
import {PluginOptionSchema} from './pluginOptionSchema';
|
|
38
|
-
import {
|
|
38
|
+
import type {
|
|
39
39
|
LoadContext,
|
|
40
|
-
ConfigureWebpackUtils,
|
|
41
|
-
Props,
|
|
42
40
|
Plugin,
|
|
43
41
|
HtmlTags,
|
|
44
42
|
OptionValidationContext,
|
|
45
43
|
ValidationResult,
|
|
46
44
|
} from '@docusaurus/types';
|
|
47
|
-
import {Configuration} from 'webpack';
|
|
48
45
|
import {
|
|
49
|
-
generateBlogFeed,
|
|
50
46
|
generateBlogPosts,
|
|
51
|
-
getContentPathList,
|
|
52
47
|
getSourceToPermalink,
|
|
48
|
+
getBlogTags,
|
|
49
|
+
paginateBlogPosts,
|
|
53
50
|
} from './blogUtils';
|
|
51
|
+
import {createBlogFeedFiles} from './feed';
|
|
52
|
+
import type {
|
|
53
|
+
PluginOptions,
|
|
54
|
+
BlogPostFrontMatter,
|
|
55
|
+
Assets,
|
|
56
|
+
} from '@docusaurus/plugin-content-blog';
|
|
54
57
|
|
|
55
|
-
export default function pluginContentBlog(
|
|
58
|
+
export default async function pluginContentBlog(
|
|
56
59
|
context: LoadContext,
|
|
57
60
|
options: PluginOptions,
|
|
58
|
-
): Plugin<BlogContent
|
|
61
|
+
): Promise<Plugin<BlogContent>> {
|
|
59
62
|
if (options.admonitions) {
|
|
60
63
|
options.remarkPlugins = options.remarkPlugins.concat([
|
|
61
64
|
[admonitions, options.admonitions],
|
|
@@ -64,10 +67,11 @@ export default function pluginContentBlog(
|
|
|
64
67
|
|
|
65
68
|
const {
|
|
66
69
|
siteDir,
|
|
67
|
-
siteConfig
|
|
70
|
+
siteConfig,
|
|
68
71
|
generatedFilesDir,
|
|
69
72
|
i18n: {currentLocale},
|
|
70
73
|
} = context;
|
|
74
|
+
const {onBrokenMarkdownLinks, baseUrl} = siteConfig;
|
|
71
75
|
|
|
72
76
|
const contentPaths: BlogContentPaths = {
|
|
73
77
|
contentPath: path.resolve(siteDir, options.path),
|
|
@@ -88,44 +92,50 @@ export default function pluginContentBlog(
|
|
|
88
92
|
const aliasedSource = (source: string) =>
|
|
89
93
|
`~blog/${posixPath(path.relative(pluginDataDirRoot, source))}`;
|
|
90
94
|
|
|
95
|
+
const authorsMapFilePath = await getDataFilePath({
|
|
96
|
+
filePath: options.authorsMapPath,
|
|
97
|
+
contentPaths,
|
|
98
|
+
});
|
|
99
|
+
|
|
91
100
|
return {
|
|
92
101
|
name: 'docusaurus-plugin-content-blog',
|
|
93
102
|
|
|
94
103
|
getPathsToWatch() {
|
|
95
|
-
const {include
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return include.map((pattern) => `${contentPath}/${pattern}`);
|
|
99
|
-
}),
|
|
104
|
+
const {include} = options;
|
|
105
|
+
const contentMarkdownGlobs = getContentPathList(contentPaths).flatMap(
|
|
106
|
+
(contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
|
|
100
107
|
);
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
getClientModules() {
|
|
104
|
-
const modules = [];
|
|
105
108
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
+
return [authorsMapFilePath, ...contentMarkdownGlobs].filter(
|
|
110
|
+
Boolean,
|
|
111
|
+
) as string[];
|
|
112
|
+
},
|
|
109
113
|
|
|
110
|
-
|
|
114
|
+
async getTranslationFiles() {
|
|
115
|
+
return getTranslationFiles(options);
|
|
111
116
|
},
|
|
112
117
|
|
|
113
118
|
// Fetches blog contents and returns metadata for the necessary routes.
|
|
114
119
|
async loadContent() {
|
|
115
|
-
const {
|
|
120
|
+
const {
|
|
121
|
+
postsPerPage: postsPerPageOption,
|
|
122
|
+
routeBasePath,
|
|
123
|
+
tagsBasePath,
|
|
124
|
+
blogDescription,
|
|
125
|
+
blogTitle,
|
|
126
|
+
blogSidebarTitle,
|
|
127
|
+
} = options;
|
|
116
128
|
|
|
117
|
-
const blogPosts
|
|
118
|
-
contentPaths,
|
|
119
|
-
context,
|
|
120
|
-
options,
|
|
121
|
-
);
|
|
129
|
+
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
122
130
|
|
|
123
131
|
if (!blogPosts.length) {
|
|
124
132
|
return {
|
|
133
|
+
blogSidebarTitle,
|
|
125
134
|
blogPosts: [],
|
|
126
135
|
blogListPaginated: [],
|
|
127
136
|
blogTags: {},
|
|
128
137
|
blogTagsListPath: null,
|
|
138
|
+
blogTagsPaginated: [],
|
|
129
139
|
};
|
|
130
140
|
}
|
|
131
141
|
|
|
@@ -149,85 +159,30 @@ export default function pluginContentBlog(
|
|
|
149
159
|
}
|
|
150
160
|
});
|
|
151
161
|
|
|
152
|
-
|
|
153
|
-
// Example: `/blog`, `/blog/page/1`, `/blog/page/2`
|
|
154
|
-
const totalCount = blogPosts.length;
|
|
155
|
-
const numberOfPages = Math.ceil(totalCount / postsPerPage);
|
|
156
|
-
const {
|
|
157
|
-
siteConfig: {baseUrl = ''},
|
|
158
|
-
} = context;
|
|
159
|
-
const basePageUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
160
|
-
|
|
161
|
-
const blogListPaginated: BlogPaginated[] = [];
|
|
162
|
+
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
162
163
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
}
|
|
164
|
+
const blogListPaginated: BlogPaginated[] = paginateBlogPosts({
|
|
165
|
+
blogPosts,
|
|
166
|
+
blogTitle,
|
|
167
|
+
blogDescription,
|
|
168
|
+
postsPerPageOption,
|
|
169
|
+
basePageUrl: baseBlogUrl,
|
|
170
|
+
});
|
|
201
171
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
});
|
|
172
|
+
const blogTags: BlogTags = getBlogTags({
|
|
173
|
+
blogPosts,
|
|
174
|
+
postsPerPageOption,
|
|
175
|
+
blogDescription,
|
|
176
|
+
blogTitle,
|
|
225
177
|
});
|
|
226
178
|
|
|
179
|
+
const tagsPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
|
|
180
|
+
|
|
227
181
|
const blogTagsListPath =
|
|
228
182
|
Object.keys(blogTags).length > 0 ? tagsPath : null;
|
|
229
183
|
|
|
230
184
|
return {
|
|
185
|
+
blogSidebarTitle,
|
|
231
186
|
blogPosts,
|
|
232
187
|
blogListPaginated,
|
|
233
188
|
blogTags,
|
|
@@ -245,10 +200,14 @@ export default function pluginContentBlog(
|
|
|
245
200
|
blogPostComponent,
|
|
246
201
|
blogTagsListComponent,
|
|
247
202
|
blogTagsPostsComponent,
|
|
203
|
+
blogArchiveComponent,
|
|
204
|
+
routeBasePath,
|
|
205
|
+
archiveBasePath,
|
|
248
206
|
} = options;
|
|
249
207
|
|
|
250
208
|
const {addRoute, createData} = actions;
|
|
251
209
|
const {
|
|
210
|
+
blogSidebarTitle,
|
|
252
211
|
blogPosts,
|
|
253
212
|
blogListPaginated,
|
|
254
213
|
blogTags,
|
|
@@ -260,7 +219,28 @@ export default function pluginContentBlog(
|
|
|
260
219
|
const sidebarBlogPosts =
|
|
261
220
|
options.blogSidebarCount === 'ALL'
|
|
262
221
|
? blogPosts
|
|
263
|
-
:
|
|
222
|
+
: blogPosts.slice(0, options.blogSidebarCount);
|
|
223
|
+
|
|
224
|
+
if (archiveBasePath) {
|
|
225
|
+
const archiveUrl = normalizeUrl([
|
|
226
|
+
baseUrl,
|
|
227
|
+
routeBasePath,
|
|
228
|
+
archiveBasePath,
|
|
229
|
+
]);
|
|
230
|
+
// creates a blog archive route
|
|
231
|
+
const archiveProp = await createData(
|
|
232
|
+
`${docuHash(archiveUrl)}.json`,
|
|
233
|
+
JSON.stringify({blogPosts}, null, 2),
|
|
234
|
+
);
|
|
235
|
+
addRoute({
|
|
236
|
+
path: archiveUrl,
|
|
237
|
+
component: blogArchiveComponent,
|
|
238
|
+
exact: true,
|
|
239
|
+
modules: {
|
|
240
|
+
archive: aliasedSource(archiveProp),
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
}
|
|
264
244
|
|
|
265
245
|
// This prop is useful to provide the blog list sidebar
|
|
266
246
|
const sidebarProp = await createData(
|
|
@@ -269,7 +249,7 @@ export default function pluginContentBlog(
|
|
|
269
249
|
`blog-post-list-prop-${pluginId}.json`,
|
|
270
250
|
JSON.stringify(
|
|
271
251
|
{
|
|
272
|
-
title:
|
|
252
|
+
title: blogSidebarTitle,
|
|
273
253
|
items: sidebarBlogPosts.map((blogPost) => ({
|
|
274
254
|
title: blogPost.metadata.title,
|
|
275
255
|
permalink: blogPost.metadata.permalink,
|
|
@@ -321,9 +301,10 @@ export default function pluginContentBlog(
|
|
|
321
301
|
exact: true,
|
|
322
302
|
modules: {
|
|
323
303
|
sidebar: aliasedSource(sidebarProp),
|
|
324
|
-
items: items.map((postID) =>
|
|
325
|
-
// To tell routes.js this is an import and not a nested object
|
|
326
|
-
|
|
304
|
+
items: items.map((postID) =>
|
|
305
|
+
// To tell routes.js this is an import and not a nested object
|
|
306
|
+
// to recurse.
|
|
307
|
+
({
|
|
327
308
|
content: {
|
|
328
309
|
__import: true,
|
|
329
310
|
path: blogItemsToMetadata[postID].source,
|
|
@@ -331,8 +312,8 @@ export default function pluginContentBlog(
|
|
|
331
312
|
truncated: true,
|
|
332
313
|
},
|
|
333
314
|
},
|
|
334
|
-
}
|
|
335
|
-
|
|
315
|
+
}),
|
|
316
|
+
),
|
|
336
317
|
metadata: aliasedSource(pageMetadataPath),
|
|
337
318
|
},
|
|
338
319
|
});
|
|
@@ -344,49 +325,61 @@ export default function pluginContentBlog(
|
|
|
344
325
|
return;
|
|
345
326
|
}
|
|
346
327
|
|
|
347
|
-
const tagsModule: TagsModule =
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
Object.keys(blogTags).map(async (tag) => {
|
|
351
|
-
const {name, items, permalink} = blogTags[tag];
|
|
352
|
-
|
|
353
|
-
tagsModule[tag] = {
|
|
328
|
+
const tagsModule: TagsModule = Object.fromEntries(
|
|
329
|
+
Object.entries(blogTags).map(([tagKey, tag]) => {
|
|
330
|
+
const tagModule: TagModule = {
|
|
354
331
|
allTagsPath: blogTagsListPath,
|
|
355
|
-
slug:
|
|
356
|
-
name,
|
|
357
|
-
count: items.length,
|
|
358
|
-
permalink,
|
|
332
|
+
slug: tagKey,
|
|
333
|
+
name: tag.name,
|
|
334
|
+
count: tag.items.length,
|
|
335
|
+
permalink: tag.permalink,
|
|
359
336
|
};
|
|
360
|
-
|
|
361
|
-
const tagsMetadataPath = await createData(
|
|
362
|
-
`${docuHash(permalink)}.json`,
|
|
363
|
-
JSON.stringify(tagsModule[tag], null, 2),
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
addRoute({
|
|
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
|
-
});
|
|
337
|
+
return [tag.name, tagModule];
|
|
387
338
|
}),
|
|
388
339
|
);
|
|
389
340
|
|
|
341
|
+
async function createTagRoutes(tag: BlogTag): Promise<void> {
|
|
342
|
+
await Promise.all(
|
|
343
|
+
tag.pages.map(async (blogPaginated) => {
|
|
344
|
+
const {metadata, items} = blogPaginated;
|
|
345
|
+
const tagsMetadataPath = await createData(
|
|
346
|
+
`${docuHash(metadata.permalink)}.json`,
|
|
347
|
+
JSON.stringify(tagsModule[tag.name], null, 2),
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
const listMetadataPath = await createData(
|
|
351
|
+
`${docuHash(metadata.permalink)}-list.json`,
|
|
352
|
+
JSON.stringify(metadata, null, 2),
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
addRoute({
|
|
356
|
+
path: metadata.permalink,
|
|
357
|
+
component: blogTagsPostsComponent,
|
|
358
|
+
exact: true,
|
|
359
|
+
modules: {
|
|
360
|
+
sidebar: aliasedSource(sidebarProp),
|
|
361
|
+
items: items.map((postID) => {
|
|
362
|
+
const blogPostMetadata = blogItemsToMetadata[postID];
|
|
363
|
+
return {
|
|
364
|
+
content: {
|
|
365
|
+
__import: true,
|
|
366
|
+
path: blogPostMetadata.source,
|
|
367
|
+
query: {
|
|
368
|
+
truncated: true,
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
}),
|
|
373
|
+
metadata: aliasedSource(tagsMetadataPath),
|
|
374
|
+
listMetadata: aliasedSource(listMetadataPath),
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
}),
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
await Promise.all(Object.values(blogTags).map(createTagRoutes));
|
|
382
|
+
|
|
390
383
|
// Only create /tags page if there are tags.
|
|
391
384
|
if (Object.keys(blogTags).length > 0) {
|
|
392
385
|
const tagsListPath = await createData(
|
|
@@ -406,12 +399,11 @@ export default function pluginContentBlog(
|
|
|
406
399
|
}
|
|
407
400
|
},
|
|
408
401
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
) {
|
|
402
|
+
translateContent({content, translationFiles}) {
|
|
403
|
+
return translateContent(content, translationFiles);
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
configureWebpack(_config, isServer, {getJSLoader}, content) {
|
|
415
407
|
const {
|
|
416
408
|
rehypePlugins,
|
|
417
409
|
remarkPlugins,
|
|
@@ -436,6 +428,7 @@ export default function pluginContentBlog(
|
|
|
436
428
|
},
|
|
437
429
|
};
|
|
438
430
|
|
|
431
|
+
const contentDirs = getContentPathList(contentPaths);
|
|
439
432
|
return {
|
|
440
433
|
resolve: {
|
|
441
434
|
alias: {
|
|
@@ -445,8 +438,8 @@ export default function pluginContentBlog(
|
|
|
445
438
|
module: {
|
|
446
439
|
rules: [
|
|
447
440
|
{
|
|
448
|
-
test:
|
|
449
|
-
include:
|
|
441
|
+
test: /\.mdx?$/i,
|
|
442
|
+
include: contentDirs
|
|
450
443
|
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
|
451
444
|
.map(addTrailingPathSeparator),
|
|
452
445
|
use: [
|
|
@@ -458,10 +451,17 @@ export default function pluginContentBlog(
|
|
|
458
451
|
rehypePlugins,
|
|
459
452
|
beforeDefaultRemarkPlugins,
|
|
460
453
|
beforeDefaultRehypePlugins,
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
454
|
+
staticDirs: siteConfig.staticDirectories.map((dir) =>
|
|
455
|
+
path.resolve(siteDir, dir),
|
|
456
|
+
),
|
|
457
|
+
siteDir,
|
|
458
|
+
isMDXPartial: createAbsoluteFilePathMatcher(
|
|
459
|
+
options.exclude,
|
|
460
|
+
contentDirs,
|
|
461
|
+
),
|
|
464
462
|
metadataPath: (mdxPath: string) => {
|
|
463
|
+
// Note that metadataPath must be the same/in-sync as
|
|
464
|
+
// the path from createData for each MDX.
|
|
465
465
|
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
|
466
466
|
return path.join(
|
|
467
467
|
dataDir,
|
|
@@ -471,6 +471,21 @@ export default function pluginContentBlog(
|
|
|
471
471
|
// For blog posts a title in markdown is always removed
|
|
472
472
|
// Blog posts title are rendered separately
|
|
473
473
|
removeContentTitle: true,
|
|
474
|
+
|
|
475
|
+
// Assets allow to convert some relative images paths to
|
|
476
|
+
// require() calls
|
|
477
|
+
createAssets: ({
|
|
478
|
+
frontMatter,
|
|
479
|
+
metadata,
|
|
480
|
+
}: {
|
|
481
|
+
frontMatter: BlogPostFrontMatter;
|
|
482
|
+
metadata: MetaData;
|
|
483
|
+
}): Assets => ({
|
|
484
|
+
image: frontMatter.image,
|
|
485
|
+
authorsImageUrls: metadata.authors.map(
|
|
486
|
+
(author) => author.imageURL,
|
|
487
|
+
),
|
|
488
|
+
}),
|
|
474
489
|
},
|
|
475
490
|
},
|
|
476
491
|
{
|
|
@@ -484,34 +499,20 @@ export default function pluginContentBlog(
|
|
|
484
499
|
};
|
|
485
500
|
},
|
|
486
501
|
|
|
487
|
-
async postBuild({outDir}
|
|
488
|
-
if (!options.feedOptions
|
|
502
|
+
async postBuild({outDir, content}) {
|
|
503
|
+
if (!options.feedOptions.type) {
|
|
489
504
|
return;
|
|
490
505
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
if (!feed) {
|
|
506
|
+
const {blogPosts} = content;
|
|
507
|
+
if (!blogPosts.length) {
|
|
495
508
|
return;
|
|
496
509
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
outDir,
|
|
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
|
-
);
|
|
510
|
+
await createBlogFeedFiles({
|
|
511
|
+
blogPosts,
|
|
512
|
+
options,
|
|
513
|
+
outDir,
|
|
514
|
+
siteConfig,
|
|
515
|
+
});
|
|
515
516
|
},
|
|
516
517
|
|
|
517
518
|
injectHtmlTags({content}) {
|
|
@@ -524,20 +525,22 @@ export default function pluginContentBlog(
|
|
|
524
525
|
}
|
|
525
526
|
|
|
526
527
|
const feedTypes = options.feedOptions.type;
|
|
527
|
-
const
|
|
528
|
-
siteConfig: {title},
|
|
529
|
-
baseUrl,
|
|
530
|
-
} = context;
|
|
528
|
+
const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
|
|
531
529
|
const feedsConfig = {
|
|
532
530
|
rss: {
|
|
533
531
|
type: 'application/rss+xml',
|
|
534
532
|
path: 'rss.xml',
|
|
535
|
-
title: `${
|
|
533
|
+
title: `${feedTitle} RSS Feed`,
|
|
536
534
|
},
|
|
537
535
|
atom: {
|
|
538
536
|
type: 'application/atom+xml',
|
|
539
537
|
path: 'atom.xml',
|
|
540
|
-
title: `${
|
|
538
|
+
title: `${feedTitle} Atom Feed`,
|
|
539
|
+
},
|
|
540
|
+
json: {
|
|
541
|
+
type: 'application/json',
|
|
542
|
+
path: 'feed.json',
|
|
543
|
+
title: `${feedTitle} JSON Feed`,
|
|
541
544
|
},
|
|
542
545
|
};
|
|
543
546
|
const headTags: HtmlTags = [];
|
package/src/markdownLoader.ts
CHANGED
|
@@ -6,20 +6,17 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {truncate, linkify} from './blogUtils';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import type {BlogMarkdownLoaderOptions} from './types';
|
|
10
|
+
import type {LoaderContext} from 'webpack';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const markdownLoader: Loader = function (source) {
|
|
12
|
+
export default function markdownLoader(
|
|
13
|
+
this: LoaderContext<BlogMarkdownLoaderOptions>,
|
|
14
|
+
source: string,
|
|
15
|
+
): void {
|
|
19
16
|
const filePath = this.resourcePath;
|
|
20
|
-
const fileString = source
|
|
17
|
+
const fileString = source;
|
|
21
18
|
const callback = this.async();
|
|
22
|
-
const markdownLoaderOptions = this.getOptions()
|
|
19
|
+
const markdownLoaderOptions = this.getOptions();
|
|
23
20
|
|
|
24
21
|
// Linkify blog posts
|
|
25
22
|
let finalContent = linkify({
|
|
@@ -30,7 +27,7 @@ const markdownLoader: Loader = function (source) {
|
|
|
30
27
|
|
|
31
28
|
// Truncate content if requested (e.g: file.md?truncated=true).
|
|
32
29
|
const truncated: boolean | undefined = this.resourceQuery
|
|
33
|
-
? !!
|
|
30
|
+
? !!new URLSearchParams(this.resourceQuery.slice(1)).get('truncated')
|
|
34
31
|
: undefined;
|
|
35
32
|
|
|
36
33
|
if (truncated) {
|
|
@@ -38,6 +35,4 @@ const markdownLoader: Loader = function (source) {
|
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
return callback && callback(null, finalContent);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export default markdownLoader;
|
|
38
|
+
}
|