@docusaurus/plugin-content-blog 2.0.0-beta.12faed89d → 2.0.0-beta.14
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 +23 -0
- package/lib/authors.js +147 -0
- package/lib/blogFrontMatter.d.ts +19 -6
- package/lib/blogFrontMatter.js +31 -19
- package/lib/blogUtils.d.ts +10 -4
- package/lib/blogUtils.js +142 -137
- package/lib/feed.d.ts +20 -0
- package/lib/feed.js +105 -0
- package/lib/index.js +104 -106
- package/lib/markdownLoader.d.ts +3 -6
- package/lib/markdownLoader.js +5 -5
- package/lib/pluginOptionSchema.d.ts +3 -26
- package/lib/pluginOptionSchema.js +30 -9
- package/lib/translations.d.ts +10 -0
- package/lib/translations.js +53 -0
- package/lib/types.d.ts +55 -15
- package/package.json +17 -13
- package/src/authors.ts +196 -0
- package/src/blogFrontMatter.ts +71 -33
- package/src/blogUtils.ts +196 -181
- package/{types.d.ts → src/deps.d.ts} +0 -0
- package/src/feed.ts +149 -0
- package/src/index.ts +123 -107
- package/src/markdownLoader.ts +8 -12
- package/{index.d.ts → src/plugin-content-blog.d.ts} +35 -31
- package/src/pluginOptionSchema.ts +34 -12
- package/src/translations.ts +63 -0
- package/src/types.ts +69 -16
- 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/feed.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {Feed, Author as FeedAuthor, Item as FeedItem} from 'feed';
|
|
9
|
+
import {PluginOptions, Author, BlogPost, FeedType} from './types';
|
|
10
|
+
import {normalizeUrl, mdxToHtml} from '@docusaurus/utils';
|
|
11
|
+
import {DocusaurusConfig} from '@docusaurus/types';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import fs from 'fs-extra';
|
|
14
|
+
|
|
15
|
+
// TODO this is temporary until we handle mdxToHtml better
|
|
16
|
+
// It's hard to convert reliably JSX/require calls to an html feed content
|
|
17
|
+
// See https://github.com/facebook/docusaurus/issues/5664
|
|
18
|
+
function mdxToFeedContent(mdxContent: string): string | undefined {
|
|
19
|
+
try {
|
|
20
|
+
return mdxToHtml(mdxContent);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
// TODO will we need a plugin option to configure how to handle such an error
|
|
23
|
+
// Swallow the error on purpose for now, until we understand better the problem space
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function generateBlogFeed({
|
|
29
|
+
blogPosts,
|
|
30
|
+
options,
|
|
31
|
+
siteConfig,
|
|
32
|
+
}: {
|
|
33
|
+
blogPosts: BlogPost[];
|
|
34
|
+
options: PluginOptions;
|
|
35
|
+
siteConfig: DocusaurusConfig;
|
|
36
|
+
}): Promise<Feed | null> {
|
|
37
|
+
if (!blogPosts.length) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const {feedOptions, routeBasePath} = options;
|
|
42
|
+
const {url: siteUrl, baseUrl, title, favicon} = siteConfig;
|
|
43
|
+
const blogBaseUrl = normalizeUrl([siteUrl, baseUrl, routeBasePath]);
|
|
44
|
+
|
|
45
|
+
const updated =
|
|
46
|
+
(blogPosts[0] && blogPosts[0].metadata.date) ||
|
|
47
|
+
new Date('2015-10-25T16:29:00.000-07:00'); // weird legacy magic date
|
|
48
|
+
|
|
49
|
+
const feed = new Feed({
|
|
50
|
+
id: blogBaseUrl,
|
|
51
|
+
title: feedOptions.title || `${title} Blog`,
|
|
52
|
+
updated,
|
|
53
|
+
language: feedOptions.language,
|
|
54
|
+
link: blogBaseUrl,
|
|
55
|
+
description: feedOptions.description || `${siteConfig.title} Blog`,
|
|
56
|
+
favicon: favicon ? normalizeUrl([siteUrl, baseUrl, favicon]) : undefined,
|
|
57
|
+
copyright: feedOptions.copyright,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function toFeedAuthor(author: Author): FeedAuthor {
|
|
61
|
+
// TODO ask author emails?
|
|
62
|
+
// RSS feed requires email to render authors
|
|
63
|
+
return {name: author.name, link: author.url};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
blogPosts.forEach((post) => {
|
|
67
|
+
const {
|
|
68
|
+
id,
|
|
69
|
+
metadata: {title: metadataTitle, permalink, date, description, authors},
|
|
70
|
+
} = post;
|
|
71
|
+
|
|
72
|
+
const feedItem: FeedItem = {
|
|
73
|
+
title: metadataTitle,
|
|
74
|
+
id,
|
|
75
|
+
link: normalizeUrl([siteUrl, permalink]),
|
|
76
|
+
date,
|
|
77
|
+
description,
|
|
78
|
+
content: mdxToFeedContent(post.content),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// json1() method takes the first item of authors array
|
|
82
|
+
// it causes an error when authors array is empty
|
|
83
|
+
const feedItemAuthors = authors.map(toFeedAuthor);
|
|
84
|
+
if (feedItemAuthors.length > 0) {
|
|
85
|
+
feedItem.author = feedItemAuthors;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
feed.addItem(feedItem);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return feed;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function createBlogFeedFile({
|
|
95
|
+
feed,
|
|
96
|
+
feedType,
|
|
97
|
+
generatePath,
|
|
98
|
+
}: {
|
|
99
|
+
feed: Feed;
|
|
100
|
+
feedType: FeedType;
|
|
101
|
+
generatePath: string;
|
|
102
|
+
}) {
|
|
103
|
+
const [feedContent, feedPath] = (() => {
|
|
104
|
+
switch (feedType) {
|
|
105
|
+
case 'rss':
|
|
106
|
+
return [feed.rss2(), 'rss.xml'];
|
|
107
|
+
case 'json':
|
|
108
|
+
return [feed.json1(), 'feed.json'];
|
|
109
|
+
case 'atom':
|
|
110
|
+
return [feed.atom1(), 'atom.xml'];
|
|
111
|
+
default:
|
|
112
|
+
throw new Error(`Feed type ${feedType} not supported.`);
|
|
113
|
+
}
|
|
114
|
+
})();
|
|
115
|
+
try {
|
|
116
|
+
await fs.outputFile(path.join(generatePath, feedPath), feedContent);
|
|
117
|
+
} catch (err) {
|
|
118
|
+
throw new Error(`Generating ${feedType} feed failed: ${err}.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function createBlogFeedFiles({
|
|
123
|
+
blogPosts,
|
|
124
|
+
options,
|
|
125
|
+
siteConfig,
|
|
126
|
+
outDir,
|
|
127
|
+
}: {
|
|
128
|
+
blogPosts: BlogPost[];
|
|
129
|
+
options: PluginOptions;
|
|
130
|
+
siteConfig: DocusaurusConfig;
|
|
131
|
+
outDir: string;
|
|
132
|
+
}): Promise<void> {
|
|
133
|
+
const feed = await generateBlogFeed({blogPosts, options, siteConfig});
|
|
134
|
+
|
|
135
|
+
const feedTypes = options.feedOptions.type;
|
|
136
|
+
if (!feed || !feedTypes) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await Promise.all(
|
|
141
|
+
feedTypes.map((feedType) =>
|
|
142
|
+
createBlogFeedFile({
|
|
143
|
+
feed,
|
|
144
|
+
feedType,
|
|
145
|
+
generatePath: path.join(outDir, options.routeBasePath),
|
|
146
|
+
}),
|
|
147
|
+
),
|
|
148
|
+
);
|
|
149
|
+
}
|
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,12 +15,10 @@ import {
|
|
|
16
15
|
reportMessage,
|
|
17
16
|
posixPath,
|
|
18
17
|
addTrailingPathSeparator,
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
STATIC_DIR_NAME,
|
|
18
|
+
createAbsoluteFilePathMatcher,
|
|
22
19
|
DEFAULT_PLUGIN_ID,
|
|
23
|
-
} from '@docusaurus/
|
|
24
|
-
import {
|
|
20
|
+
} from '@docusaurus/utils';
|
|
21
|
+
import {translateContent, getTranslationFiles} from './translations';
|
|
25
22
|
|
|
26
23
|
import {
|
|
27
24
|
PluginOptions,
|
|
@@ -30,9 +27,10 @@ import {
|
|
|
30
27
|
BlogItemsToMetadata,
|
|
31
28
|
TagsModule,
|
|
32
29
|
BlogPaginated,
|
|
33
|
-
BlogPost,
|
|
34
30
|
BlogContentPaths,
|
|
35
31
|
BlogMarkdownLoaderOptions,
|
|
32
|
+
MetaData,
|
|
33
|
+
Assets,
|
|
36
34
|
} from './types';
|
|
37
35
|
import {PluginOptionSchema} from './pluginOptionSchema';
|
|
38
36
|
import {
|
|
@@ -46,11 +44,13 @@ import {
|
|
|
46
44
|
} from '@docusaurus/types';
|
|
47
45
|
import {Configuration} from 'webpack';
|
|
48
46
|
import {
|
|
49
|
-
generateBlogFeed,
|
|
50
47
|
generateBlogPosts,
|
|
51
48
|
getContentPathList,
|
|
52
49
|
getSourceToPermalink,
|
|
50
|
+
getBlogTags,
|
|
53
51
|
} from './blogUtils';
|
|
52
|
+
import {BlogPostFrontMatter} from './blogFrontMatter';
|
|
53
|
+
import {createBlogFeedFiles} from './feed';
|
|
54
54
|
|
|
55
55
|
export default function pluginContentBlog(
|
|
56
56
|
context: LoadContext,
|
|
@@ -64,10 +64,11 @@ export default function pluginContentBlog(
|
|
|
64
64
|
|
|
65
65
|
const {
|
|
66
66
|
siteDir,
|
|
67
|
-
siteConfig
|
|
67
|
+
siteConfig,
|
|
68
68
|
generatedFilesDir,
|
|
69
69
|
i18n: {currentLocale},
|
|
70
70
|
} = context;
|
|
71
|
+
const {onBrokenMarkdownLinks, baseUrl} = siteConfig;
|
|
71
72
|
|
|
72
73
|
const contentPaths: BlogContentPaths = {
|
|
73
74
|
contentPath: path.resolve(siteDir, options.path),
|
|
@@ -92,36 +93,42 @@ export default function pluginContentBlog(
|
|
|
92
93
|
name: 'docusaurus-plugin-content-blog',
|
|
93
94
|
|
|
94
95
|
getPathsToWatch() {
|
|
95
|
-
const {include
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return include.map((pattern) => `${contentPath}/${pattern}`);
|
|
99
|
-
}),
|
|
96
|
+
const {include, authorsMapPath} = options;
|
|
97
|
+
const contentMarkdownGlobs = getContentPathList(contentPaths).flatMap(
|
|
98
|
+
(contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
|
|
100
99
|
);
|
|
101
|
-
},
|
|
102
100
|
|
|
103
|
-
|
|
104
|
-
const
|
|
101
|
+
// TODO: we should read this path in plugin! but plugins do not support async init for now :'(
|
|
102
|
+
// const authorsMapFilePath = await getAuthorsMapFilePath({authorsMapPath,contentPaths,});
|
|
103
|
+
// simplified impl, better than nothing for now:
|
|
104
|
+
const authorsMapFilePath = path.join(
|
|
105
|
+
contentPaths.contentPath,
|
|
106
|
+
authorsMapPath,
|
|
107
|
+
);
|
|
105
108
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
+
return [authorsMapFilePath, ...contentMarkdownGlobs];
|
|
110
|
+
},
|
|
109
111
|
|
|
110
|
-
|
|
112
|
+
async getTranslationFiles() {
|
|
113
|
+
return getTranslationFiles(options);
|
|
111
114
|
},
|
|
112
115
|
|
|
113
116
|
// Fetches blog contents and returns metadata for the necessary routes.
|
|
114
117
|
async loadContent() {
|
|
115
|
-
const {
|
|
118
|
+
const {
|
|
119
|
+
postsPerPage: postsPerPageOption,
|
|
120
|
+
routeBasePath,
|
|
121
|
+
tagsBasePath,
|
|
122
|
+
blogDescription,
|
|
123
|
+
blogTitle,
|
|
124
|
+
blogSidebarTitle,
|
|
125
|
+
} = options;
|
|
116
126
|
|
|
117
|
-
const blogPosts
|
|
118
|
-
contentPaths,
|
|
119
|
-
context,
|
|
120
|
-
options,
|
|
121
|
-
);
|
|
127
|
+
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
122
128
|
|
|
123
129
|
if (!blogPosts.length) {
|
|
124
130
|
return {
|
|
131
|
+
blogSidebarTitle,
|
|
125
132
|
blogPosts: [],
|
|
126
133
|
blogListPaginated: [],
|
|
127
134
|
blogTags: {},
|
|
@@ -152,18 +159,17 @@ export default function pluginContentBlog(
|
|
|
152
159
|
// Blog pagination routes.
|
|
153
160
|
// Example: `/blog`, `/blog/page/1`, `/blog/page/2`
|
|
154
161
|
const totalCount = blogPosts.length;
|
|
162
|
+
const postsPerPage =
|
|
163
|
+
postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption;
|
|
155
164
|
const numberOfPages = Math.ceil(totalCount / postsPerPage);
|
|
156
|
-
const
|
|
157
|
-
siteConfig: {baseUrl = ''},
|
|
158
|
-
} = context;
|
|
159
|
-
const basePageUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
165
|
+
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
160
166
|
|
|
161
167
|
const blogListPaginated: BlogPaginated[] = [];
|
|
162
168
|
|
|
163
169
|
function blogPaginationPermalink(page: number) {
|
|
164
170
|
return page > 0
|
|
165
|
-
? normalizeUrl([
|
|
166
|
-
:
|
|
171
|
+
? normalizeUrl([baseBlogUrl, `page/${page + 1}`])
|
|
172
|
+
: baseBlogUrl;
|
|
167
173
|
}
|
|
168
174
|
|
|
169
175
|
for (let page = 0; page < numberOfPages; page += 1) {
|
|
@@ -179,8 +185,8 @@ export default function pluginContentBlog(
|
|
|
179
185
|
page < numberOfPages - 1
|
|
180
186
|
? blogPaginationPermalink(page + 1)
|
|
181
187
|
: null,
|
|
182
|
-
blogDescription
|
|
183
|
-
blogTitle
|
|
188
|
+
blogDescription,
|
|
189
|
+
blogTitle,
|
|
184
190
|
},
|
|
185
191
|
items: blogPosts
|
|
186
192
|
.slice(page * postsPerPage, (page + 1) * postsPerPage)
|
|
@@ -188,46 +194,15 @@ export default function pluginContentBlog(
|
|
|
188
194
|
});
|
|
189
195
|
}
|
|
190
196
|
|
|
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
|
-
}
|
|
197
|
+
const blogTags: BlogTags = getBlogTags(blogPosts);
|
|
201
198
|
|
|
202
|
-
|
|
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
|
-
});
|
|
225
|
-
});
|
|
199
|
+
const tagsPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
|
|
226
200
|
|
|
227
201
|
const blogTagsListPath =
|
|
228
202
|
Object.keys(blogTags).length > 0 ? tagsPath : null;
|
|
229
203
|
|
|
230
204
|
return {
|
|
205
|
+
blogSidebarTitle,
|
|
231
206
|
blogPosts,
|
|
232
207
|
blogListPaginated,
|
|
233
208
|
blogTags,
|
|
@@ -245,10 +220,13 @@ export default function pluginContentBlog(
|
|
|
245
220
|
blogPostComponent,
|
|
246
221
|
blogTagsListComponent,
|
|
247
222
|
blogTagsPostsComponent,
|
|
223
|
+
routeBasePath,
|
|
224
|
+
archiveBasePath,
|
|
248
225
|
} = options;
|
|
249
226
|
|
|
250
227
|
const {addRoute, createData} = actions;
|
|
251
228
|
const {
|
|
229
|
+
blogSidebarTitle,
|
|
252
230
|
blogPosts,
|
|
253
231
|
blogListPaginated,
|
|
254
232
|
blogTags,
|
|
@@ -260,7 +238,27 @@ export default function pluginContentBlog(
|
|
|
260
238
|
const sidebarBlogPosts =
|
|
261
239
|
options.blogSidebarCount === 'ALL'
|
|
262
240
|
? blogPosts
|
|
263
|
-
:
|
|
241
|
+
: blogPosts.slice(0, options.blogSidebarCount);
|
|
242
|
+
|
|
243
|
+
const archiveUrl = normalizeUrl([
|
|
244
|
+
baseUrl,
|
|
245
|
+
routeBasePath,
|
|
246
|
+
archiveBasePath,
|
|
247
|
+
]);
|
|
248
|
+
|
|
249
|
+
// creates a blog archive route
|
|
250
|
+
const archiveProp = await createData(
|
|
251
|
+
`${docuHash(archiveUrl)}.json`,
|
|
252
|
+
JSON.stringify({blogPosts}, null, 2),
|
|
253
|
+
);
|
|
254
|
+
addRoute({
|
|
255
|
+
path: archiveUrl,
|
|
256
|
+
component: '@theme/BlogArchivePage',
|
|
257
|
+
exact: true,
|
|
258
|
+
modules: {
|
|
259
|
+
archive: aliasedSource(archiveProp),
|
|
260
|
+
},
|
|
261
|
+
});
|
|
264
262
|
|
|
265
263
|
// This prop is useful to provide the blog list sidebar
|
|
266
264
|
const sidebarProp = await createData(
|
|
@@ -269,7 +267,7 @@ export default function pluginContentBlog(
|
|
|
269
267
|
`blog-post-list-prop-${pluginId}.json`,
|
|
270
268
|
JSON.stringify(
|
|
271
269
|
{
|
|
272
|
-
title:
|
|
270
|
+
title: blogSidebarTitle,
|
|
273
271
|
items: sidebarBlogPosts.map((blogPost) => ({
|
|
274
272
|
title: blogPost.metadata.title,
|
|
275
273
|
permalink: blogPost.metadata.permalink,
|
|
@@ -321,9 +319,9 @@ export default function pluginContentBlog(
|
|
|
321
319
|
exact: true,
|
|
322
320
|
modules: {
|
|
323
321
|
sidebar: aliasedSource(sidebarProp),
|
|
324
|
-
items: items.map((postID) =>
|
|
322
|
+
items: items.map((postID) =>
|
|
325
323
|
// To tell routes.js this is an import and not a nested object to recurse.
|
|
326
|
-
|
|
324
|
+
({
|
|
327
325
|
content: {
|
|
328
326
|
__import: true,
|
|
329
327
|
path: blogItemsToMetadata[postID].source,
|
|
@@ -331,8 +329,8 @@ export default function pluginContentBlog(
|
|
|
331
329
|
truncated: true,
|
|
332
330
|
},
|
|
333
331
|
},
|
|
334
|
-
}
|
|
335
|
-
|
|
332
|
+
}),
|
|
333
|
+
),
|
|
336
334
|
metadata: aliasedSource(pageMetadataPath),
|
|
337
335
|
},
|
|
338
336
|
});
|
|
@@ -350,6 +348,7 @@ export default function pluginContentBlog(
|
|
|
350
348
|
Object.keys(blogTags).map(async (tag) => {
|
|
351
349
|
const {name, items, permalink} = blogTags[tag];
|
|
352
350
|
|
|
351
|
+
// Refactor all this, see docs implementation
|
|
353
352
|
tagsModule[tag] = {
|
|
354
353
|
allTagsPath: blogTagsListPath,
|
|
355
354
|
slug: tag,
|
|
@@ -406,6 +405,10 @@ export default function pluginContentBlog(
|
|
|
406
405
|
}
|
|
407
406
|
},
|
|
408
407
|
|
|
408
|
+
translateContent({content, translationFiles}) {
|
|
409
|
+
return translateContent(content, translationFiles);
|
|
410
|
+
},
|
|
411
|
+
|
|
409
412
|
configureWebpack(
|
|
410
413
|
_config: Configuration,
|
|
411
414
|
isServer: boolean,
|
|
@@ -436,6 +439,7 @@ export default function pluginContentBlog(
|
|
|
436
439
|
},
|
|
437
440
|
};
|
|
438
441
|
|
|
442
|
+
const contentDirs = getContentPathList(contentPaths);
|
|
439
443
|
return {
|
|
440
444
|
resolve: {
|
|
441
445
|
alias: {
|
|
@@ -446,7 +450,7 @@ export default function pluginContentBlog(
|
|
|
446
450
|
rules: [
|
|
447
451
|
{
|
|
448
452
|
test: /(\.mdx?)$/,
|
|
449
|
-
include:
|
|
453
|
+
include: contentDirs
|
|
450
454
|
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
|
451
455
|
.map(addTrailingPathSeparator),
|
|
452
456
|
use: [
|
|
@@ -458,10 +462,17 @@ export default function pluginContentBlog(
|
|
|
458
462
|
rehypePlugins,
|
|
459
463
|
beforeDefaultRemarkPlugins,
|
|
460
464
|
beforeDefaultRehypePlugins,
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
465
|
+
staticDirs: siteConfig.staticDirectories.map((dir) =>
|
|
466
|
+
path.resolve(siteDir, dir),
|
|
467
|
+
),
|
|
468
|
+
siteDir,
|
|
469
|
+
isMDXPartial: createAbsoluteFilePathMatcher(
|
|
470
|
+
options.exclude,
|
|
471
|
+
contentDirs,
|
|
472
|
+
),
|
|
464
473
|
metadataPath: (mdxPath: string) => {
|
|
474
|
+
// Note that metadataPath must be the same/in-sync as
|
|
475
|
+
// the path from createData for each MDX.
|
|
465
476
|
const aliasedPath = aliasedSitePath(mdxPath, siteDir);
|
|
466
477
|
return path.join(
|
|
467
478
|
dataDir,
|
|
@@ -471,6 +482,20 @@ export default function pluginContentBlog(
|
|
|
471
482
|
// For blog posts a title in markdown is always removed
|
|
472
483
|
// Blog posts title are rendered separately
|
|
473
484
|
removeContentTitle: true,
|
|
485
|
+
|
|
486
|
+
// Assets allow to convert some relative images paths to require() calls
|
|
487
|
+
createAssets: ({
|
|
488
|
+
frontMatter,
|
|
489
|
+
metadata,
|
|
490
|
+
}: {
|
|
491
|
+
frontMatter: BlogPostFrontMatter;
|
|
492
|
+
metadata: MetaData;
|
|
493
|
+
}): Assets => ({
|
|
494
|
+
image: frontMatter.image,
|
|
495
|
+
authorsImageUrls: metadata.authors.map(
|
|
496
|
+
(author) => author.imageURL,
|
|
497
|
+
),
|
|
498
|
+
}),
|
|
474
499
|
},
|
|
475
500
|
},
|
|
476
501
|
{
|
|
@@ -485,33 +510,22 @@ export default function pluginContentBlog(
|
|
|
485
510
|
},
|
|
486
511
|
|
|
487
512
|
async postBuild({outDir}: Props) {
|
|
488
|
-
if (!options.feedOptions
|
|
513
|
+
if (!options.feedOptions.type) {
|
|
489
514
|
return;
|
|
490
515
|
}
|
|
491
516
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
517
|
+
// TODO: we shouldn't need to re-read the posts here!
|
|
518
|
+
// postBuild should receive loadedContent
|
|
519
|
+
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
520
|
+
if (!blogPosts.length) {
|
|
495
521
|
return;
|
|
496
522
|
}
|
|
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
|
-
);
|
|
523
|
+
await createBlogFeedFiles({
|
|
524
|
+
blogPosts,
|
|
525
|
+
options,
|
|
526
|
+
outDir,
|
|
527
|
+
siteConfig,
|
|
528
|
+
});
|
|
515
529
|
},
|
|
516
530
|
|
|
517
531
|
injectHtmlTags({content}) {
|
|
@@ -524,20 +538,22 @@ export default function pluginContentBlog(
|
|
|
524
538
|
}
|
|
525
539
|
|
|
526
540
|
const feedTypes = options.feedOptions.type;
|
|
527
|
-
const
|
|
528
|
-
siteConfig: {title},
|
|
529
|
-
baseUrl,
|
|
530
|
-
} = context;
|
|
541
|
+
const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
|
|
531
542
|
const feedsConfig = {
|
|
532
543
|
rss: {
|
|
533
544
|
type: 'application/rss+xml',
|
|
534
545
|
path: 'rss.xml',
|
|
535
|
-
title: `${
|
|
546
|
+
title: `${feedTitle} RSS Feed`,
|
|
536
547
|
},
|
|
537
548
|
atom: {
|
|
538
549
|
type: 'application/atom+xml',
|
|
539
550
|
path: 'atom.xml',
|
|
540
|
-
title: `${
|
|
551
|
+
title: `${feedTitle} Atom Feed`,
|
|
552
|
+
},
|
|
553
|
+
json: {
|
|
554
|
+
type: 'application/json',
|
|
555
|
+
path: 'feed.json',
|
|
556
|
+
title: `${feedTitle} JSON Feed`,
|
|
541
557
|
},
|
|
542
558
|
};
|
|
543
559
|
const headTags: HtmlTags = [];
|
package/src/markdownLoader.ts
CHANGED
|
@@ -8,18 +8,16 @@
|
|
|
8
8
|
import {truncate, linkify} from './blogUtils';
|
|
9
9
|
import {parseQuery} from 'loader-utils';
|
|
10
10
|
import {BlogMarkdownLoaderOptions} from './types';
|
|
11
|
+
import type {LoaderContext} from 'webpack';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const markdownLoader: Loader = function (source) {
|
|
13
|
+
export default function markdownLoader(
|
|
14
|
+
this: LoaderContext<BlogMarkdownLoaderOptions>,
|
|
15
|
+
source: string,
|
|
16
|
+
): void {
|
|
19
17
|
const filePath = this.resourcePath;
|
|
20
|
-
const fileString = source
|
|
18
|
+
const fileString = source;
|
|
21
19
|
const callback = this.async();
|
|
22
|
-
const markdownLoaderOptions = this.getOptions()
|
|
20
|
+
const markdownLoaderOptions = this.getOptions();
|
|
23
21
|
|
|
24
22
|
// Linkify blog posts
|
|
25
23
|
let finalContent = linkify({
|
|
@@ -38,6 +36,4 @@ const markdownLoader: Loader = function (source) {
|
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
return callback && callback(null, finalContent);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export default markdownLoader;
|
|
39
|
+
}
|