@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/lib/blogUtils.js
CHANGED
|
@@ -6,34 +6,83 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.
|
|
9
|
+
exports.linkify = exports.generateBlogPosts = exports.parseBlogFileName = exports.getBlogTags = exports.paginateBlogPosts = exports.getSourceToPermalink = exports.truncate = void 0;
|
|
10
10
|
const tslib_1 = require("tslib");
|
|
11
11
|
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
12
|
-
const globby_1 = tslib_1.__importDefault(require("globby"));
|
|
13
|
-
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
14
12
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
15
13
|
const reading_time_1 = tslib_1.__importDefault(require("reading-time"));
|
|
16
|
-
const
|
|
17
|
-
const lodash_1 = require("lodash");
|
|
14
|
+
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
18
15
|
const utils_1 = require("@docusaurus/utils");
|
|
19
16
|
const blogFrontMatter_1 = require("./blogFrontMatter");
|
|
17
|
+
const authors_1 = require("./authors");
|
|
18
|
+
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
20
19
|
function truncate(fileString, truncateMarker) {
|
|
21
20
|
return fileString.split(truncateMarker, 1).shift();
|
|
22
21
|
}
|
|
23
22
|
exports.truncate = truncate;
|
|
24
23
|
function getSourceToPermalink(blogPosts) {
|
|
25
|
-
return
|
|
24
|
+
return Object.fromEntries(blogPosts.map(({ metadata: { source, permalink } }) => [source, permalink]));
|
|
26
25
|
}
|
|
27
26
|
exports.getSourceToPermalink = getSourceToPermalink;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
function paginateBlogPosts({ blogPosts, basePageUrl, blogTitle, blogDescription, postsPerPageOption, }) {
|
|
28
|
+
const totalCount = blogPosts.length;
|
|
29
|
+
const postsPerPage = postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption;
|
|
30
|
+
const numberOfPages = Math.ceil(totalCount / postsPerPage);
|
|
31
|
+
const pages = [];
|
|
32
|
+
function permalink(page) {
|
|
33
|
+
return page > 0 ? `${basePageUrl}/page/${page + 1}` : basePageUrl;
|
|
34
|
+
}
|
|
35
|
+
for (let page = 0; page < numberOfPages; page += 1) {
|
|
36
|
+
pages.push({
|
|
37
|
+
items: blogPosts
|
|
38
|
+
.slice(page * postsPerPage, (page + 1) * postsPerPage)
|
|
39
|
+
.map((item) => item.id),
|
|
40
|
+
metadata: {
|
|
41
|
+
permalink: permalink(page),
|
|
42
|
+
page: page + 1,
|
|
43
|
+
postsPerPage,
|
|
44
|
+
totalPages: numberOfPages,
|
|
45
|
+
totalCount,
|
|
46
|
+
previousPage: page !== 0 ? permalink(page - 1) : null,
|
|
47
|
+
nextPage: page < numberOfPages - 1 ? permalink(page + 1) : null,
|
|
48
|
+
blogDescription,
|
|
49
|
+
blogTitle,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return pages;
|
|
54
|
+
}
|
|
55
|
+
exports.paginateBlogPosts = paginateBlogPosts;
|
|
56
|
+
function getBlogTags({ blogPosts, ...params }) {
|
|
57
|
+
const groups = (0, utils_1.groupTaggedItems)(blogPosts, (blogPost) => blogPost.metadata.tags);
|
|
58
|
+
return lodash_1.default.mapValues(groups, ({ tag, items: tagBlogPosts }) => ({
|
|
59
|
+
name: tag.label,
|
|
60
|
+
items: tagBlogPosts.map((item) => item.id),
|
|
61
|
+
permalink: tag.permalink,
|
|
62
|
+
pages: paginateBlogPosts({
|
|
63
|
+
blogPosts: tagBlogPosts,
|
|
64
|
+
basePageUrl: tag.permalink,
|
|
65
|
+
...params,
|
|
66
|
+
}),
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
exports.getBlogTags = getBlogTags;
|
|
70
|
+
const DATE_FILENAME_REGEX = /^(?<folder>.*)(?<date>\d{4}[-/]\d{1,2}[-/]\d{1,2})[-/]?(?<text>.*?)(?:\/index)?.mdx?$/;
|
|
71
|
+
function parseBlogFileName(blogSourceRelative) {
|
|
72
|
+
const dateFilenameMatch = blogSourceRelative.match(DATE_FILENAME_REGEX);
|
|
73
|
+
if (dateFilenameMatch) {
|
|
74
|
+
const { folder, text, date: dateString } = dateFilenameMatch.groups;
|
|
75
|
+
// Always treat dates as UTC by adding the `Z`
|
|
76
|
+
const date = new Date(`${dateString}Z`);
|
|
77
|
+
const slugDate = dateString.replace(/-/g, '/');
|
|
78
|
+
const slug = `/${slugDate}/${folder}${text}`;
|
|
79
|
+
return { date, text, slug };
|
|
80
|
+
}
|
|
81
|
+
const text = blogSourceRelative.replace(/(?:\/index)?\.mdx?$/, '');
|
|
82
|
+
const slug = `/${text}`;
|
|
83
|
+
return { date: undefined, text, slug };
|
|
36
84
|
}
|
|
85
|
+
exports.parseBlogFileName = parseBlogFileName;
|
|
37
86
|
function formatBlogPostDate(locale, date) {
|
|
38
87
|
try {
|
|
39
88
|
return new Intl.DateTimeFormat(locale, {
|
|
@@ -43,150 +92,159 @@ function formatBlogPostDate(locale, date) {
|
|
|
43
92
|
timeZone: 'UTC',
|
|
44
93
|
}).format(date);
|
|
45
94
|
}
|
|
46
|
-
catch (
|
|
47
|
-
|
|
95
|
+
catch (err) {
|
|
96
|
+
logger_1.default.error `Can't format blog post date "${String(date)}"`;
|
|
97
|
+
throw err;
|
|
48
98
|
}
|
|
49
99
|
}
|
|
50
|
-
async function
|
|
51
|
-
|
|
52
|
-
|
|
100
|
+
async function parseBlogPostMarkdownFile(blogSourceAbsolute) {
|
|
101
|
+
const markdownString = await fs_extra_1.default.readFile(blogSourceAbsolute, 'utf-8');
|
|
102
|
+
try {
|
|
103
|
+
const result = (0, utils_1.parseMarkdownString)(markdownString, {
|
|
104
|
+
removeContentTitle: true,
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
...result,
|
|
108
|
+
frontMatter: (0, blogFrontMatter_1.validateBlogPostFrontMatter)(result.frontMatter),
|
|
109
|
+
};
|
|
53
110
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return null;
|
|
111
|
+
catch (err) {
|
|
112
|
+
logger_1.default.error `Error while parsing blog post file path=${blogSourceAbsolute}.`;
|
|
113
|
+
throw err;
|
|
58
114
|
}
|
|
59
|
-
const { feedOptions, routeBasePath } = options;
|
|
60
|
-
const { url: siteUrl, baseUrl, title, favicon } = siteConfig;
|
|
61
|
-
const blogBaseUrl = utils_1.normalizeUrl([siteUrl, baseUrl, routeBasePath]);
|
|
62
|
-
const updated = (blogPosts[0] && blogPosts[0].metadata.date) ||
|
|
63
|
-
new Date('2015-10-25T16:29:00.000-07:00');
|
|
64
|
-
const feed = new feed_1.Feed({
|
|
65
|
-
id: blogBaseUrl,
|
|
66
|
-
title: feedOptions.title || `${title} Blog`,
|
|
67
|
-
updated,
|
|
68
|
-
language: feedOptions.language,
|
|
69
|
-
link: blogBaseUrl,
|
|
70
|
-
description: feedOptions.description || `${siteConfig.title} Blog`,
|
|
71
|
-
favicon: favicon ? utils_1.normalizeUrl([siteUrl, baseUrl, favicon]) : undefined,
|
|
72
|
-
copyright: feedOptions.copyright,
|
|
73
|
-
});
|
|
74
|
-
blogPosts.forEach((post) => {
|
|
75
|
-
const { id, metadata: { title: metadataTitle, permalink, date, description }, } = post;
|
|
76
|
-
feed.addItem({
|
|
77
|
-
title: metadataTitle,
|
|
78
|
-
id,
|
|
79
|
-
link: utils_1.normalizeUrl([siteUrl, permalink]),
|
|
80
|
-
date,
|
|
81
|
-
description,
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
return feed;
|
|
85
115
|
}
|
|
86
|
-
|
|
87
|
-
async function
|
|
88
|
-
const {
|
|
89
|
-
|
|
90
|
-
|
|
116
|
+
const defaultReadingTime = ({ content, options }) => (0, reading_time_1.default)(content, options).minutes;
|
|
117
|
+
async function processBlogSourceFile(blogSourceRelative, contentPaths, context, options, authorsMap) {
|
|
118
|
+
const { siteConfig: { baseUrl }, siteDir, i18n, } = context;
|
|
119
|
+
const { routeBasePath, tagsBasePath: tagsRouteBasePath, truncateMarker, showReadingTime, editUrl, } = options;
|
|
120
|
+
// Lookup in localized folder in priority
|
|
121
|
+
const blogDirPath = await (0, utils_1.getFolderContainingFile)((0, utils_1.getContentPathList)(contentPaths), blogSourceRelative);
|
|
122
|
+
const blogSourceAbsolute = path_1.default.join(blogDirPath, blogSourceRelative);
|
|
123
|
+
const { frontMatter, content, contentTitle, excerpt } = await parseBlogPostMarkdownFile(blogSourceAbsolute);
|
|
124
|
+
const aliasedSource = (0, utils_1.aliasedSitePath)(blogSourceAbsolute, siteDir);
|
|
125
|
+
if (frontMatter.draft && process.env.NODE_ENV === 'production') {
|
|
126
|
+
return undefined;
|
|
91
127
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
async function processBlogSourceFile(blogSourceFile) {
|
|
98
|
-
var _a, _b, _c, _d, _e, _f;
|
|
99
|
-
// Lookup in localized folder in priority
|
|
100
|
-
const blogDirPath = await utils_1.getFolderContainingFile(getContentPathList(contentPaths), blogSourceFile);
|
|
101
|
-
const source = path_1.default.join(blogDirPath, blogSourceFile);
|
|
102
|
-
const { frontMatter: unsafeFrontMatter, content, contentTitle, excerpt, } = await utils_1.parseMarkdownFile(source, { removeContentTitle: true });
|
|
103
|
-
const frontMatter = blogFrontMatter_1.validateBlogPostFrontMatter(unsafeFrontMatter);
|
|
104
|
-
const aliasedSource = utils_1.aliasedSitePath(source, siteDir);
|
|
105
|
-
const blogFileName = path_1.default.basename(blogSourceFile);
|
|
106
|
-
if (frontMatter.draft && process.env.NODE_ENV === 'production') {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
if (frontMatter.id) {
|
|
110
|
-
console.warn(chalk_1.default.yellow(`"id" header option is deprecated in ${blogFileName} file. Please use "slug" option instead.`));
|
|
111
|
-
}
|
|
112
|
-
let date;
|
|
113
|
-
// Extract date and title from filename.
|
|
114
|
-
const dateFilenameMatch = blogFileName.match(DATE_FILENAME_PATTERN);
|
|
115
|
-
let linkName = blogFileName.replace(/\.mdx?$/, '');
|
|
116
|
-
if (dateFilenameMatch) {
|
|
117
|
-
const [, dateString, name] = dateFilenameMatch;
|
|
118
|
-
// Always treat dates as UTC by adding the `Z`
|
|
119
|
-
date = new Date(`${dateString}Z`);
|
|
120
|
-
linkName = name;
|
|
121
|
-
}
|
|
128
|
+
if (frontMatter.id) {
|
|
129
|
+
logger_1.default.warn `name=${'id'} header option is deprecated in path=${blogSourceRelative} file. Please use name=${'slug'} option instead.`;
|
|
130
|
+
}
|
|
131
|
+
const parsedBlogFileName = parseBlogFileName(blogSourceRelative);
|
|
132
|
+
async function getDate() {
|
|
122
133
|
// Prefer user-defined date.
|
|
123
134
|
if (frontMatter.date) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
date = date !== null && date !== void 0 ? date : (await fs_extra_1.default.stat(source)).birthtime;
|
|
128
|
-
const formattedDate = formatBlogPostDate(i18n.currentLocale, date);
|
|
129
|
-
const title = (_b = (_a = frontMatter.title) !== null && _a !== void 0 ? _a : contentTitle) !== null && _b !== void 0 ? _b : linkName;
|
|
130
|
-
const description = (_d = (_c = frontMatter.description) !== null && _c !== void 0 ? _c : excerpt) !== null && _d !== void 0 ? _d : '';
|
|
131
|
-
const slug = frontMatter.slug ||
|
|
132
|
-
(dateFilenameMatch ? toUrl({ date, link: linkName }) : linkName);
|
|
133
|
-
const permalink = utils_1.normalizeUrl([baseUrl, routeBasePath, slug]);
|
|
134
|
-
function getBlogEditUrl() {
|
|
135
|
-
const blogPathRelative = path_1.default.relative(blogDirPath, path_1.default.resolve(source));
|
|
136
|
-
if (typeof editUrl === 'function') {
|
|
137
|
-
return editUrl({
|
|
138
|
-
blogDirPath: utils_1.posixPath(path_1.default.relative(siteDir, blogDirPath)),
|
|
139
|
-
blogPath: utils_1.posixPath(blogPathRelative),
|
|
140
|
-
permalink,
|
|
141
|
-
locale: i18n.currentLocale,
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
else if (typeof editUrl === 'string') {
|
|
145
|
-
const isLocalized = blogDirPath === contentPaths.contentPathLocalized;
|
|
146
|
-
const fileContentPath = isLocalized && options.editLocalizedFiles
|
|
147
|
-
? contentPaths.contentPathLocalized
|
|
148
|
-
: contentPaths.contentPath;
|
|
149
|
-
const contentPathEditUrl = utils_1.normalizeUrl([
|
|
150
|
-
editUrl,
|
|
151
|
-
utils_1.posixPath(path_1.default.relative(siteDir, fileContentPath)),
|
|
152
|
-
]);
|
|
153
|
-
return utils_1.getEditUrl(blogPathRelative, contentPathEditUrl);
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
return undefined;
|
|
135
|
+
if (typeof frontMatter.date === 'string') {
|
|
136
|
+
// Always treat dates as UTC by adding the `Z`
|
|
137
|
+
return new Date(`${frontMatter.date}Z`);
|
|
157
138
|
}
|
|
139
|
+
// YAML only converts YYYY-MM-DD to dates and leaves others as strings.
|
|
140
|
+
return frontMatter.date;
|
|
158
141
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
142
|
+
else if (parsedBlogFileName.date) {
|
|
143
|
+
return parsedBlogFileName.date;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const result = (0, utils_1.getFileCommitDate)(blogSourceAbsolute, {
|
|
147
|
+
age: 'oldest',
|
|
148
|
+
includeAuthor: false,
|
|
149
|
+
});
|
|
150
|
+
return result.date;
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
logger_1.default.error(err);
|
|
154
|
+
return (await fs_extra_1.default.stat(blogSourceAbsolute)).birthtime;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const date = await getDate();
|
|
158
|
+
const formattedDate = formatBlogPostDate(i18n.currentLocale, date);
|
|
159
|
+
const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text;
|
|
160
|
+
const description = frontMatter.description ?? excerpt ?? '';
|
|
161
|
+
const slug = frontMatter.slug || parsedBlogFileName.slug;
|
|
162
|
+
const permalink = (0, utils_1.normalizeUrl)([baseUrl, routeBasePath, slug]);
|
|
163
|
+
function getBlogEditUrl() {
|
|
164
|
+
const blogPathRelative = path_1.default.relative(blogDirPath, path_1.default.resolve(blogSourceAbsolute));
|
|
165
|
+
if (typeof editUrl === 'function') {
|
|
166
|
+
return editUrl({
|
|
167
|
+
blogDirPath: (0, utils_1.posixPath)(path_1.default.relative(siteDir, blogDirPath)),
|
|
168
|
+
blogPath: (0, utils_1.posixPath)(blogPathRelative),
|
|
162
169
|
permalink,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
170
|
+
locale: i18n.currentLocale,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
else if (typeof editUrl === 'string') {
|
|
174
|
+
const isLocalized = blogDirPath === contentPaths.contentPathLocalized;
|
|
175
|
+
const fileContentPath = isLocalized && options.editLocalizedFiles
|
|
176
|
+
? contentPaths.contentPathLocalized
|
|
177
|
+
: contentPaths.contentPath;
|
|
178
|
+
const contentPathEditUrl = (0, utils_1.normalizeUrl)([
|
|
179
|
+
editUrl,
|
|
180
|
+
(0, utils_1.posixPath)(path_1.default.relative(siteDir, fileContentPath)),
|
|
181
|
+
]);
|
|
182
|
+
return (0, utils_1.getEditUrl)(blogPathRelative, contentPathEditUrl);
|
|
183
|
+
}
|
|
184
|
+
return undefined;
|
|
174
185
|
}
|
|
175
|
-
|
|
186
|
+
const tagsBasePath = (0, utils_1.normalizeUrl)([
|
|
187
|
+
baseUrl,
|
|
188
|
+
routeBasePath,
|
|
189
|
+
tagsRouteBasePath,
|
|
190
|
+
]);
|
|
191
|
+
const authors = (0, authors_1.getBlogPostAuthors)({ authorsMap, frontMatter });
|
|
192
|
+
return {
|
|
193
|
+
id: slug,
|
|
194
|
+
metadata: {
|
|
195
|
+
permalink,
|
|
196
|
+
editUrl: getBlogEditUrl(),
|
|
197
|
+
source: aliasedSource,
|
|
198
|
+
title,
|
|
199
|
+
description,
|
|
200
|
+
date,
|
|
201
|
+
formattedDate,
|
|
202
|
+
tags: (0, utils_1.normalizeFrontMatterTags)(tagsBasePath, frontMatter.tags),
|
|
203
|
+
readingTime: showReadingTime
|
|
204
|
+
? options.readingTime({
|
|
205
|
+
content,
|
|
206
|
+
frontMatter,
|
|
207
|
+
defaultReadingTime,
|
|
208
|
+
})
|
|
209
|
+
: undefined,
|
|
210
|
+
truncated: truncateMarker?.test(content) || false,
|
|
211
|
+
authors,
|
|
212
|
+
frontMatter,
|
|
213
|
+
},
|
|
214
|
+
content,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
async function generateBlogPosts(contentPaths, context, options) {
|
|
218
|
+
const { include, exclude } = options;
|
|
219
|
+
if (!(await fs_extra_1.default.pathExists(contentPaths.contentPath))) {
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
const blogSourceFiles = await (0, utils_1.Globby)(include, {
|
|
223
|
+
cwd: contentPaths.contentPath,
|
|
224
|
+
ignore: exclude,
|
|
225
|
+
});
|
|
226
|
+
const authorsMap = await (0, authors_1.getAuthorsMap)({
|
|
227
|
+
contentPaths,
|
|
228
|
+
authorsMapPath: options.authorsMapPath,
|
|
229
|
+
});
|
|
230
|
+
const blogPosts = (await Promise.all(blogSourceFiles.map(async (blogSourceFile) => {
|
|
176
231
|
try {
|
|
177
|
-
return await processBlogSourceFile(blogSourceFile);
|
|
232
|
+
return await processBlogSourceFile(blogSourceFile, contentPaths, context, options, authorsMap);
|
|
178
233
|
}
|
|
179
|
-
catch (
|
|
180
|
-
|
|
181
|
-
throw
|
|
234
|
+
catch (err) {
|
|
235
|
+
logger_1.default.error `Processing of blog source file path=${blogSourceFile} failed.`;
|
|
236
|
+
throw err;
|
|
182
237
|
}
|
|
183
|
-
}));
|
|
238
|
+
}))).filter(Boolean);
|
|
184
239
|
blogPosts.sort((a, b) => b.metadata.date.getTime() - a.metadata.date.getTime());
|
|
240
|
+
if (options.sortPosts === 'ascending') {
|
|
241
|
+
return blogPosts.reverse();
|
|
242
|
+
}
|
|
185
243
|
return blogPosts;
|
|
186
244
|
}
|
|
187
245
|
exports.generateBlogPosts = generateBlogPosts;
|
|
188
246
|
function linkify({ filePath, contentPaths, fileString, siteDir, sourceToPermalink, onBrokenMarkdownLink, }) {
|
|
189
|
-
const { newContent, brokenMarkdownLinks } = utils_1.replaceMarkdownLinks({
|
|
247
|
+
const { newContent, brokenMarkdownLinks } = (0, utils_1.replaceMarkdownLinks)({
|
|
190
248
|
siteDir,
|
|
191
249
|
fileString,
|
|
192
250
|
filePath,
|
|
@@ -197,8 +255,3 @@ function linkify({ filePath, contentPaths, fileString, siteDir, sourceToPermalin
|
|
|
197
255
|
return newContent;
|
|
198
256
|
}
|
|
199
257
|
exports.linkify = linkify;
|
|
200
|
-
// Order matters: we look in priority in localized folder
|
|
201
|
-
function getContentPathList(contentPaths) {
|
|
202
|
-
return [contentPaths.contentPathLocalized, contentPaths.contentPath];
|
|
203
|
-
}
|
|
204
|
-
exports.getContentPathList = getContentPathList;
|
package/lib/feed.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
import type { BlogPost } from './types';
|
|
8
|
+
import type { DocusaurusConfig } from '@docusaurus/types';
|
|
9
|
+
import type { PluginOptions } from '@docusaurus/plugin-content-blog';
|
|
10
|
+
export declare function createBlogFeedFiles({ blogPosts, options, siteConfig, outDir, }: {
|
|
11
|
+
blogPosts: BlogPost[];
|
|
12
|
+
options: PluginOptions;
|
|
13
|
+
siteConfig: DocusaurusConfig;
|
|
14
|
+
outDir: string;
|
|
15
|
+
}): Promise<void>;
|
package/lib/feed.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.createBlogFeedFiles = void 0;
|
|
10
|
+
const tslib_1 = require("tslib");
|
|
11
|
+
const feed_1 = require("feed");
|
|
12
|
+
const utils_1 = require("@docusaurus/utils");
|
|
13
|
+
const cheerio_1 = tslib_1.__importDefault(require("cheerio"));
|
|
14
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
15
|
+
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
16
|
+
const utils_common_1 = require("@docusaurus/utils-common");
|
|
17
|
+
async function generateBlogFeed({ blogPosts, options, siteConfig, outDir, }) {
|
|
18
|
+
if (!blogPosts.length) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const { feedOptions, routeBasePath } = options;
|
|
22
|
+
const { url: siteUrl, baseUrl, title, favicon } = siteConfig;
|
|
23
|
+
const blogBaseUrl = (0, utils_1.normalizeUrl)([siteUrl, baseUrl, routeBasePath]);
|
|
24
|
+
const updated = blogPosts[0] && blogPosts[0].metadata.date;
|
|
25
|
+
const feed = new feed_1.Feed({
|
|
26
|
+
id: blogBaseUrl,
|
|
27
|
+
title: feedOptions.title || `${title} Blog`,
|
|
28
|
+
updated,
|
|
29
|
+
language: feedOptions.language,
|
|
30
|
+
link: blogBaseUrl,
|
|
31
|
+
description: feedOptions.description || `${siteConfig.title} Blog`,
|
|
32
|
+
favicon: favicon ? (0, utils_1.normalizeUrl)([siteUrl, baseUrl, favicon]) : undefined,
|
|
33
|
+
copyright: feedOptions.copyright,
|
|
34
|
+
});
|
|
35
|
+
function toFeedAuthor(author) {
|
|
36
|
+
return { name: author.name, link: author.url, email: author.email };
|
|
37
|
+
}
|
|
38
|
+
await (0, utils_1.mapAsyncSequential)(blogPosts, async (post) => {
|
|
39
|
+
const { id, metadata: { title: metadataTitle, permalink, date, description, authors, tags, }, } = post;
|
|
40
|
+
const content = await (0, utils_1.readOutputHTMLFile)(permalink.replace(siteConfig.baseUrl, ''), outDir, siteConfig.trailingSlash);
|
|
41
|
+
const $ = cheerio_1.default.load(content);
|
|
42
|
+
const feedItem = {
|
|
43
|
+
title: metadataTitle,
|
|
44
|
+
id,
|
|
45
|
+
link: (0, utils_1.normalizeUrl)([siteUrl, permalink]),
|
|
46
|
+
date,
|
|
47
|
+
description,
|
|
48
|
+
// Atom feed demands the "term", while other feeds use "name"
|
|
49
|
+
category: tags.map((tag) => ({ name: tag.label, term: tag.label })),
|
|
50
|
+
content: $(`#${utils_common_1.blogPostContainerID}`).html(),
|
|
51
|
+
};
|
|
52
|
+
// json1() method takes the first item of authors array
|
|
53
|
+
// it causes an error when authors array is empty
|
|
54
|
+
const feedItemAuthors = authors.map(toFeedAuthor);
|
|
55
|
+
if (feedItemAuthors.length > 0) {
|
|
56
|
+
feedItem.author = feedItemAuthors;
|
|
57
|
+
}
|
|
58
|
+
feed.addItem(feedItem);
|
|
59
|
+
});
|
|
60
|
+
return feed;
|
|
61
|
+
}
|
|
62
|
+
async function createBlogFeedFile({ feed, feedType, generatePath, }) {
|
|
63
|
+
const [feedContent, feedPath] = (() => {
|
|
64
|
+
switch (feedType) {
|
|
65
|
+
case 'rss':
|
|
66
|
+
return [feed.rss2(), 'rss.xml'];
|
|
67
|
+
case 'json':
|
|
68
|
+
return [feed.json1(), 'feed.json'];
|
|
69
|
+
case 'atom':
|
|
70
|
+
return [feed.atom1(), 'atom.xml'];
|
|
71
|
+
default:
|
|
72
|
+
throw new Error(`Feed type ${feedType} not supported.`);
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
75
|
+
try {
|
|
76
|
+
await fs_extra_1.default.outputFile((0, utils_1.posixPath)(path_1.default.join(generatePath, feedPath)), feedContent);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
throw new Error(`Generating ${feedType} feed failed: ${err}.`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function createBlogFeedFiles({ blogPosts, options, siteConfig, outDir, }) {
|
|
83
|
+
const feed = await generateBlogFeed({
|
|
84
|
+
blogPosts,
|
|
85
|
+
options,
|
|
86
|
+
siteConfig,
|
|
87
|
+
outDir,
|
|
88
|
+
});
|
|
89
|
+
const feedTypes = options.feedOptions.type;
|
|
90
|
+
if (!feed || !feedTypes) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
await Promise.all(feedTypes.map((feedType) => createBlogFeedFile({
|
|
94
|
+
feed,
|
|
95
|
+
feedType,
|
|
96
|
+
generatePath: path_1.default.join(outDir, options.routeBasePath),
|
|
97
|
+
})));
|
|
98
|
+
}
|
|
99
|
+
exports.createBlogFeedFiles = createBlogFeedFiles;
|
package/lib/index.d.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import { LoadContext, Plugin, OptionValidationContext, ValidationResult } from '@docusaurus/types';
|
|
9
|
-
|
|
7
|
+
import type { BlogContent } from './types';
|
|
8
|
+
import type { LoadContext, Plugin, OptionValidationContext, ValidationResult } from '@docusaurus/types';
|
|
9
|
+
import type { PluginOptions } from '@docusaurus/plugin-content-blog';
|
|
10
|
+
export default function pluginContentBlog(context: LoadContext, options: PluginOptions): Promise<Plugin<BlogContent>>;
|
|
10
11
|
export declare function validateOptions({ validate, options, }: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions>;
|