@docusaurus/plugin-content-blog 3.1.0 → 3.2.0
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/blogUtils.d.ts +7 -1
- package/lib/blogUtils.js +15 -21
- package/lib/client/index.d.ts +8 -0
- package/lib/client/index.js +15 -0
- package/lib/feed.js +11 -5
- package/lib/frontMatter.d.ts +0 -6
- package/lib/frontMatter.js +3 -2
- package/lib/index.js +25 -5
- package/lib/options.js +10 -0
- package/package.json +26 -11
- package/src/blogUtils.ts +31 -27
- package/src/client/index.ts +20 -0
- package/src/feed.ts +22 -7
- package/src/frontMatter.ts +5 -4
- package/src/index.ts +42 -5
- package/src/options.ts +12 -0
- package/src/plugin-content-blog.d.ts +66 -8
- package/.docusaurus/DONT-EDIT-THIS-FOLDER +0 -5
- package/.docusaurus/client-modules.js +0 -5
- package/.docusaurus/codeTranslations.json +0 -1
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-only-aeb.json +0 -10
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-only-archive-245.json +0 -312
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-only-page-2-d48.json +0 -11
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-only-page-3-8b6.json +0 -10
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-only-tags-birthday-c96-list.json +0 -9
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-only-tags-birthday-c96.json +0 -7
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-only-tags-complex-cf3-list.json +0 -9
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-only-tags-complex-cf3.json +0 -7
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-only-tags-date-c24-list.json +0 -9
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-only-tags-date-c24.json +0 -7
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-only-tags-tags-344.json +0 -17
- package/.docusaurus/docusaurus-plugin-content-blog/default/blog-post-list-prop-default.json +0 -30
- package/.docusaurus/docusaurus-plugin-content-blog/default/plugin-route-context-module-100.json +0 -4
- package/.docusaurus/docusaurus-plugin-content-blog/default/site-src-tests-fixtures-website-blog-2018-12-14-happy-first-birthday-slash-md-d1e.json +0 -46
- package/.docusaurus/docusaurus-plugin-content-blog/default/site-src-tests-fixtures-website-blog-blog-with-links-mdx-fe5.json +0 -22
- package/.docusaurus/docusaurus-plugin-content-blog/default/site-src-tests-fixtures-website-blog-complex-slug-md-314.json +0 -40
- package/.docusaurus/docusaurus-plugin-content-blog/default/site-src-tests-fixtures-website-blog-date-matter-md-191.json +0 -33
- package/.docusaurus/docusaurus-plugin-content-blog/default/site-src-tests-fixtures-website-blog-heading-as-title-md-10c.json +0 -25
- package/.docusaurus/docusaurus-plugin-content-blog/default/site-src-tests-fixtures-website-blog-mdx-blog-post-mdx-025.json +0 -26
- package/.docusaurus/docusaurus-plugin-content-blog/default/site-src-tests-fixtures-website-blog-mdx-require-blog-post-mdx-4ba.json +0 -26
- package/.docusaurus/docusaurus-plugin-content-blog/default/site-src-tests-fixtures-website-blog-simple-slug-md-324.json +0 -36
- package/.docusaurus/docusaurus-plugin-content-blog/default/site-src-tests-fixtures-website-blog-unlisted-md-5cb.json +0 -18
- package/.docusaurus/docusaurus-theme-search-algolia/default/plugin-route-context-module-100.json +0 -4
- package/.docusaurus/docusaurus.config.mjs +0 -246
- package/.docusaurus/globalData.json +0 -1
- package/.docusaurus/i18n.json +0 -17
- package/.docusaurus/registry.js +0 -38
- package/.docusaurus/routes.js +0 -99
- package/.docusaurus/routesChunkNames.json +0 -194
- package/.docusaurus/site-metadata.json +0 -24
package/lib/blogUtils.d.ts
CHANGED
|
@@ -11,12 +11,13 @@ export declare function truncate(fileString: string, truncateMarker: RegExp): st
|
|
|
11
11
|
export declare function getSourceToPermalink(blogPosts: BlogPost[]): {
|
|
12
12
|
[aliasedPath: string]: string;
|
|
13
13
|
};
|
|
14
|
-
export declare function paginateBlogPosts({ blogPosts, basePageUrl, blogTitle, blogDescription, postsPerPageOption, }: {
|
|
14
|
+
export declare function paginateBlogPosts({ blogPosts, basePageUrl, blogTitle, blogDescription, postsPerPageOption, pageBasePath, }: {
|
|
15
15
|
blogPosts: BlogPost[];
|
|
16
16
|
basePageUrl: string;
|
|
17
17
|
blogTitle: string;
|
|
18
18
|
blogDescription: string;
|
|
19
19
|
postsPerPageOption: number | 'ALL';
|
|
20
|
+
pageBasePath: string;
|
|
20
21
|
}): BlogPaginated[];
|
|
21
22
|
export declare function shouldBeListed(blogPost: BlogPost): boolean;
|
|
22
23
|
export declare function getBlogTags({ blogPosts, ...params }: {
|
|
@@ -24,6 +25,7 @@ export declare function getBlogTags({ blogPosts, ...params }: {
|
|
|
24
25
|
blogTitle: string;
|
|
25
26
|
blogDescription: string;
|
|
26
27
|
postsPerPageOption: number | 'ALL';
|
|
28
|
+
pageBasePath: string;
|
|
27
29
|
}): BlogTags;
|
|
28
30
|
type ParsedBlogFileName = {
|
|
29
31
|
date: Date | undefined;
|
|
@@ -37,4 +39,8 @@ export type LinkifyParams = {
|
|
|
37
39
|
fileString: string;
|
|
38
40
|
} & Pick<BlogMarkdownLoaderOptions, 'sourceToPermalink' | 'siteDir' | 'contentPaths' | 'onBrokenMarkdownLink'>;
|
|
39
41
|
export declare function linkify({ filePath, contentPaths, fileString, siteDir, sourceToPermalink, onBrokenMarkdownLink, }: LinkifyParams): string;
|
|
42
|
+
export declare function applyProcessBlogPosts({ blogPosts, processBlogPosts, }: {
|
|
43
|
+
blogPosts: BlogPost[];
|
|
44
|
+
processBlogPosts: PluginOptions['processBlogPosts'];
|
|
45
|
+
}): Promise<BlogPost[]>;
|
|
40
46
|
export {};
|
package/lib/blogUtils.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.linkify = exports.generateBlogPosts = exports.parseBlogFileName = exports.getBlogTags = exports.shouldBeListed = exports.paginateBlogPosts = exports.getSourceToPermalink = exports.truncate = void 0;
|
|
9
|
+
exports.applyProcessBlogPosts = exports.linkify = exports.generateBlogPosts = exports.parseBlogFileName = exports.getBlogTags = exports.shouldBeListed = 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
12
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
@@ -24,14 +24,14 @@ function getSourceToPermalink(blogPosts) {
|
|
|
24
24
|
return Object.fromEntries(blogPosts.map(({ metadata: { source, permalink } }) => [source, permalink]));
|
|
25
25
|
}
|
|
26
26
|
exports.getSourceToPermalink = getSourceToPermalink;
|
|
27
|
-
function paginateBlogPosts({ blogPosts, basePageUrl, blogTitle, blogDescription, postsPerPageOption, }) {
|
|
27
|
+
function paginateBlogPosts({ blogPosts, basePageUrl, blogTitle, blogDescription, postsPerPageOption, pageBasePath, }) {
|
|
28
28
|
const totalCount = blogPosts.length;
|
|
29
29
|
const postsPerPage = postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption;
|
|
30
30
|
const numberOfPages = Math.ceil(totalCount / postsPerPage);
|
|
31
31
|
const pages = [];
|
|
32
32
|
function permalink(page) {
|
|
33
33
|
return page > 0
|
|
34
|
-
? (0, utils_1.normalizeUrl)([basePageUrl,
|
|
34
|
+
? (0, utils_1.normalizeUrl)([basePageUrl, pageBasePath, `${page + 1}`])
|
|
35
35
|
: basePageUrl;
|
|
36
36
|
}
|
|
37
37
|
for (let page = 0; page < numberOfPages; page += 1) {
|
|
@@ -96,21 +96,6 @@ function parseBlogFileName(blogSourceRelative) {
|
|
|
96
96
|
return { date: undefined, text, slug };
|
|
97
97
|
}
|
|
98
98
|
exports.parseBlogFileName = parseBlogFileName;
|
|
99
|
-
function formatBlogPostDate(locale, date, calendar) {
|
|
100
|
-
try {
|
|
101
|
-
return new Intl.DateTimeFormat(locale, {
|
|
102
|
-
day: 'numeric',
|
|
103
|
-
month: 'long',
|
|
104
|
-
year: 'numeric',
|
|
105
|
-
timeZone: 'UTC',
|
|
106
|
-
calendar,
|
|
107
|
-
}).format(date);
|
|
108
|
-
}
|
|
109
|
-
catch (err) {
|
|
110
|
-
logger_1.default.error `Can't format blog post date "${String(date)}"`;
|
|
111
|
-
throw err;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
99
|
async function parseBlogPostMarkdownFile({ filePath, parseFrontMatter, }) {
|
|
115
100
|
const fileContent = await fs_extra_1.default.readFile(filePath, 'utf-8');
|
|
116
101
|
try {
|
|
@@ -142,6 +127,7 @@ async function processBlogSourceFile(blogSourceRelative, contentPaths, context,
|
|
|
142
127
|
parseFrontMatter,
|
|
143
128
|
});
|
|
144
129
|
const aliasedSource = (0, utils_1.aliasedSitePath)(blogSourceAbsolute, siteDir);
|
|
130
|
+
const lastUpdate = await (0, utils_1.readLastUpdateData)(blogSourceAbsolute, options, frontMatter.last_update);
|
|
145
131
|
const draft = (0, utils_1.isDraft)({ frontMatter });
|
|
146
132
|
const unlisted = (0, utils_1.isUnlisted)({ frontMatter });
|
|
147
133
|
if (draft) {
|
|
@@ -165,7 +151,7 @@ async function processBlogSourceFile(blogSourceRelative, contentPaths, context,
|
|
|
165
151
|
return parsedBlogFileName.date;
|
|
166
152
|
}
|
|
167
153
|
try {
|
|
168
|
-
const result = (0, utils_1.getFileCommitDate)(blogSourceAbsolute, {
|
|
154
|
+
const result = await (0, utils_1.getFileCommitDate)(blogSourceAbsolute, {
|
|
169
155
|
age: 'oldest',
|
|
170
156
|
includeAuthor: false,
|
|
171
157
|
});
|
|
@@ -177,7 +163,6 @@ async function processBlogSourceFile(blogSourceRelative, contentPaths, context,
|
|
|
177
163
|
}
|
|
178
164
|
}
|
|
179
165
|
const date = await getDate();
|
|
180
|
-
const formattedDate = formatBlogPostDate(i18n.currentLocale, date, i18n.localeConfigs[i18n.currentLocale].calendar);
|
|
181
166
|
const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text;
|
|
182
167
|
const description = frontMatter.description ?? excerpt ?? '';
|
|
183
168
|
const slug = frontMatter.slug ?? parsedBlogFileName.slug;
|
|
@@ -220,7 +205,6 @@ async function processBlogSourceFile(blogSourceRelative, contentPaths, context,
|
|
|
220
205
|
title,
|
|
221
206
|
description,
|
|
222
207
|
date,
|
|
223
|
-
formattedDate,
|
|
224
208
|
tags: (0, utils_1.normalizeFrontMatterTags)(tagsBasePath, frontMatter.tags),
|
|
225
209
|
readingTime: showReadingTime
|
|
226
210
|
? options.readingTime({
|
|
@@ -233,6 +217,8 @@ async function processBlogSourceFile(blogSourceRelative, contentPaths, context,
|
|
|
233
217
|
authors,
|
|
234
218
|
frontMatter,
|
|
235
219
|
unlisted,
|
|
220
|
+
lastUpdatedAt: lastUpdate.lastUpdatedAt,
|
|
221
|
+
lastUpdatedBy: lastUpdate.lastUpdatedBy,
|
|
236
222
|
},
|
|
237
223
|
content,
|
|
238
224
|
};
|
|
@@ -278,3 +264,11 @@ function linkify({ filePath, contentPaths, fileString, siteDir, sourceToPermalin
|
|
|
278
264
|
return newContent;
|
|
279
265
|
}
|
|
280
266
|
exports.linkify = linkify;
|
|
267
|
+
async function applyProcessBlogPosts({ blogPosts, processBlogPosts, }) {
|
|
268
|
+
const processedBlogPosts = await processBlogPosts({ blogPosts });
|
|
269
|
+
if (Array.isArray(processedBlogPosts)) {
|
|
270
|
+
return processedBlogPosts;
|
|
271
|
+
}
|
|
272
|
+
return blogPosts;
|
|
273
|
+
}
|
|
274
|
+
exports.applyProcessBlogPosts = applyProcessBlogPosts;
|
|
@@ -0,0 +1,8 @@
|
|
|
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 { BlogMetadata } from '@docusaurus/plugin-content-blog';
|
|
8
|
+
export declare function useBlogMetadata(): BlogMetadata;
|
|
@@ -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 useRouteContext from '@docusaurus/useRouteContext';
|
|
8
|
+
export function useBlogMetadata() {
|
|
9
|
+
const routeContext = useRouteContext();
|
|
10
|
+
const blogMetadata = routeContext?.data?.blogMetadata;
|
|
11
|
+
if (!blogMetadata) {
|
|
12
|
+
throw new Error("useBlogMetadata() can't be called on the current route because the blog metadata could not be found in route context");
|
|
13
|
+
}
|
|
14
|
+
return blogMetadata;
|
|
15
|
+
}
|
package/lib/feed.js
CHANGED
|
@@ -21,8 +21,11 @@ async function generateBlogFeed({ blogPosts, options, siteConfig, outDir, locale
|
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
23
23
|
const { feedOptions, routeBasePath } = options;
|
|
24
|
-
const { url: siteUrl, baseUrl, title, favicon } = siteConfig;
|
|
25
|
-
const blogBaseUrl = (0, utils_1.normalizeUrl)([siteUrl, baseUrl, routeBasePath])
|
|
24
|
+
const { url: siteUrl, baseUrl, title, favicon, trailingSlash } = siteConfig;
|
|
25
|
+
const blogBaseUrl = (0, utils_common_1.applyTrailingSlash)((0, utils_1.normalizeUrl)([siteUrl, baseUrl, routeBasePath]), {
|
|
26
|
+
trailingSlash,
|
|
27
|
+
baseUrl,
|
|
28
|
+
});
|
|
26
29
|
const blogPostsForFeed = feedOptions.limit === false || feedOptions.limit === null
|
|
27
30
|
? blogPosts
|
|
28
31
|
: blogPosts.slice(0, feedOptions.limit);
|
|
@@ -48,15 +51,18 @@ async function generateBlogFeed({ blogPosts, options, siteConfig, outDir, locale
|
|
|
48
51
|
return feed;
|
|
49
52
|
}
|
|
50
53
|
async function defaultCreateFeedItems({ blogPosts, siteConfig, outDir, }) {
|
|
51
|
-
const { url: siteUrl } = siteConfig;
|
|
54
|
+
const { url: siteUrl, baseUrl, trailingSlash } = siteConfig;
|
|
52
55
|
function toFeedAuthor(author) {
|
|
53
56
|
return { name: author.name, link: author.url, email: author.email };
|
|
54
57
|
}
|
|
55
58
|
return Promise.all(blogPosts.map(async (post) => {
|
|
56
59
|
const { metadata: { title: metadataTitle, permalink, date, description, authors, tags, }, } = post;
|
|
57
|
-
const content = await (0, utils_1.readOutputHTMLFile)(permalink.replace(
|
|
60
|
+
const content = await (0, utils_1.readOutputHTMLFile)(permalink.replace(baseUrl, ''), outDir, trailingSlash);
|
|
58
61
|
const $ = (0, cheerio_1.load)(content);
|
|
59
|
-
const blogPostAbsoluteUrl = (0, utils_1.normalizeUrl)([siteUrl, permalink])
|
|
62
|
+
const blogPostAbsoluteUrl = (0, utils_common_1.applyTrailingSlash)((0, utils_1.normalizeUrl)([siteUrl, permalink]), {
|
|
63
|
+
trailingSlash,
|
|
64
|
+
baseUrl,
|
|
65
|
+
});
|
|
60
66
|
const toAbsoluteUrl = (src) => String(new URL(src, blogPostAbsoluteUrl));
|
|
61
67
|
// Make links and image urls absolute
|
|
62
68
|
// See https://github.com/facebook/docusaurus/issues/9136
|
package/lib/frontMatter.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
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
1
|
import type { BlogPostFrontMatter } from '@docusaurus/plugin-content-blog';
|
|
8
2
|
export declare function validateBlogPostFrontMatter(frontMatter: {
|
|
9
3
|
[key: string]: unknown;
|
package/lib/frontMatter.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateBlogPostFrontMatter = void 0;
|
|
2
4
|
/**
|
|
3
5
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
4
6
|
*
|
|
5
7
|
* This source code is licensed under the MIT license found in the
|
|
6
8
|
* LICENSE file in the root directory of this source tree.
|
|
7
9
|
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.validateBlogPostFrontMatter = void 0;
|
|
10
10
|
const utils_validation_1 = require("@docusaurus/utils-validation");
|
|
11
11
|
const BlogPostFrontMatterAuthorSchema = utils_validation_1.JoiFrontMatter.object({
|
|
12
12
|
key: utils_validation_1.JoiFrontMatter.string(),
|
|
@@ -52,6 +52,7 @@ const BlogFrontMatterSchema = utils_validation_1.JoiFrontMatter.object({
|
|
|
52
52
|
keywords: utils_validation_1.JoiFrontMatter.array().items(utils_validation_1.JoiFrontMatter.string().required()),
|
|
53
53
|
hide_table_of_contents: utils_validation_1.JoiFrontMatter.boolean(),
|
|
54
54
|
...utils_validation_1.FrontMatterTOCHeadingLevels,
|
|
55
|
+
last_update: utils_validation_1.FrontMatterLastUpdateSchema,
|
|
55
56
|
})
|
|
56
57
|
.messages({
|
|
57
58
|
'deprecate.error': '{#label} blog frontMatter field is deprecated. Please use {#alternative} instead.',
|
package/lib/index.js
CHANGED
|
@@ -47,10 +47,14 @@ async function pluginContentBlog(context, options) {
|
|
|
47
47
|
},
|
|
48
48
|
// Fetches blog contents and returns metadata for the necessary routes.
|
|
49
49
|
async loadContent() {
|
|
50
|
-
const { postsPerPage: postsPerPageOption, routeBasePath, tagsBasePath, blogDescription, blogTitle, blogSidebarTitle, } = options;
|
|
50
|
+
const { postsPerPage: postsPerPageOption, routeBasePath, tagsBasePath, blogDescription, blogTitle, blogSidebarTitle, pageBasePath, } = options;
|
|
51
51
|
const baseBlogUrl = (0, utils_1.normalizeUrl)([baseUrl, routeBasePath]);
|
|
52
52
|
const blogTagsListPath = (0, utils_1.normalizeUrl)([baseBlogUrl, tagsBasePath]);
|
|
53
|
-
|
|
53
|
+
let blogPosts = await (0, blogUtils_1.generateBlogPosts)(contentPaths, context, options);
|
|
54
|
+
blogPosts = await (0, blogUtils_1.applyProcessBlogPosts)({
|
|
55
|
+
blogPosts,
|
|
56
|
+
processBlogPosts: options.processBlogPosts,
|
|
57
|
+
});
|
|
54
58
|
const listedBlogPosts = blogPosts.filter(blogUtils_1.shouldBeListed);
|
|
55
59
|
if (!blogPosts.length) {
|
|
56
60
|
return {
|
|
@@ -59,10 +63,9 @@ async function pluginContentBlog(context, options) {
|
|
|
59
63
|
blogListPaginated: [],
|
|
60
64
|
blogTags: {},
|
|
61
65
|
blogTagsListPath,
|
|
62
|
-
blogTagsPaginated: [],
|
|
63
66
|
};
|
|
64
67
|
}
|
|
65
|
-
//
|
|
68
|
+
// Collocate next and prev metadata.
|
|
66
69
|
listedBlogPosts.forEach((blogPost, index) => {
|
|
67
70
|
const prevItem = index > 0 ? listedBlogPosts[index - 1] : null;
|
|
68
71
|
if (prevItem) {
|
|
@@ -87,12 +90,14 @@ async function pluginContentBlog(context, options) {
|
|
|
87
90
|
blogDescription,
|
|
88
91
|
postsPerPageOption,
|
|
89
92
|
basePageUrl: baseBlogUrl,
|
|
93
|
+
pageBasePath,
|
|
90
94
|
});
|
|
91
95
|
const blogTags = (0, blogUtils_1.getBlogTags)({
|
|
92
96
|
blogPosts,
|
|
93
97
|
postsPerPageOption,
|
|
94
98
|
blogDescription,
|
|
95
99
|
blogTitle,
|
|
100
|
+
pageBasePath,
|
|
96
101
|
});
|
|
97
102
|
return {
|
|
98
103
|
blogSidebarTitle,
|
|
@@ -103,7 +108,7 @@ async function pluginContentBlog(context, options) {
|
|
|
103
108
|
};
|
|
104
109
|
},
|
|
105
110
|
async contentLoaded({ content: blogContents, actions }) {
|
|
106
|
-
const { blogListComponent, blogPostComponent, blogTagsListComponent, blogTagsPostsComponent, blogArchiveComponent, routeBasePath, archiveBasePath, } = options;
|
|
111
|
+
const { blogListComponent, blogPostComponent, blogTagsListComponent, blogTagsPostsComponent, blogArchiveComponent, routeBasePath, archiveBasePath, blogTitle, } = options;
|
|
107
112
|
const { addRoute, createData } = actions;
|
|
108
113
|
const { blogSidebarTitle, blogPosts, blogListPaginated, blogTags, blogTagsListPath, } = blogContents;
|
|
109
114
|
const listedBlogPosts = blogPosts.filter(blogUtils_1.shouldBeListed);
|
|
@@ -154,6 +159,17 @@ async function pluginContentBlog(context, options) {
|
|
|
154
159
|
unlisted: blogPost.metadata.unlisted,
|
|
155
160
|
})),
|
|
156
161
|
}, null, 2));
|
|
162
|
+
const blogMetadata = {
|
|
163
|
+
blogBasePath: (0, utils_1.normalizeUrl)([baseUrl, routeBasePath]),
|
|
164
|
+
blogTitle,
|
|
165
|
+
};
|
|
166
|
+
const blogMetadataPath = await createData(`blogMetadata-${pluginId}.json`, JSON.stringify(blogMetadata, null, 2));
|
|
167
|
+
function createBlogPostRouteMetadata(blogPostMeta) {
|
|
168
|
+
return {
|
|
169
|
+
sourceFilePath: (0, utils_1.aliasedSitePathToRelativePath)(blogPostMeta.source),
|
|
170
|
+
lastUpdatedAt: blogPostMeta.lastUpdatedAt,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
157
173
|
// Create routes for blog entries.
|
|
158
174
|
await Promise.all(blogPosts.map(async (blogPost) => {
|
|
159
175
|
const { id, metadata } = blogPost;
|
|
@@ -169,6 +185,10 @@ async function pluginContentBlog(context, options) {
|
|
|
169
185
|
sidebar: aliasedSource(sidebarProp),
|
|
170
186
|
content: metadata.source,
|
|
171
187
|
},
|
|
188
|
+
metadata: createBlogPostRouteMetadata(metadata),
|
|
189
|
+
context: {
|
|
190
|
+
blogMetadata: aliasedSource(blogMetadataPath),
|
|
191
|
+
},
|
|
172
192
|
});
|
|
173
193
|
blogItemsToMetadata[id] = metadata;
|
|
174
194
|
}));
|
package/lib/options.js
CHANGED
|
@@ -33,11 +33,15 @@ exports.DEFAULT_OPTIONS = {
|
|
|
33
33
|
routeBasePath: 'blog',
|
|
34
34
|
tagsBasePath: 'tags',
|
|
35
35
|
archiveBasePath: 'archive',
|
|
36
|
+
pageBasePath: 'page',
|
|
36
37
|
path: 'blog',
|
|
37
38
|
editLocalizedFiles: false,
|
|
38
39
|
authorsMapPath: 'authors.yml',
|
|
39
40
|
readingTime: ({ content, defaultReadingTime }) => defaultReadingTime({ content }),
|
|
40
41
|
sortPosts: 'descending',
|
|
42
|
+
showLastUpdateTime: false,
|
|
43
|
+
showLastUpdateAuthor: false,
|
|
44
|
+
processBlogPosts: async () => undefined,
|
|
41
45
|
};
|
|
42
46
|
const PluginOptionSchema = utils_validation_1.Joi.object({
|
|
43
47
|
path: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.path),
|
|
@@ -46,6 +50,7 @@ const PluginOptionSchema = utils_validation_1.Joi.object({
|
|
|
46
50
|
.allow(null),
|
|
47
51
|
routeBasePath: utils_validation_1.RouteBasePathSchema.default(exports.DEFAULT_OPTIONS.routeBasePath),
|
|
48
52
|
tagsBasePath: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.tagsBasePath),
|
|
53
|
+
pageBasePath: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.pageBasePath),
|
|
49
54
|
include: utils_validation_1.Joi.array().items(utils_validation_1.Joi.string()).default(exports.DEFAULT_OPTIONS.include),
|
|
50
55
|
exclude: utils_validation_1.Joi.array().items(utils_validation_1.Joi.string()).default(exports.DEFAULT_OPTIONS.exclude),
|
|
51
56
|
postsPerPage: utils_validation_1.Joi.alternatives()
|
|
@@ -101,6 +106,11 @@ const PluginOptionSchema = utils_validation_1.Joi.object({
|
|
|
101
106
|
sortPosts: utils_validation_1.Joi.string()
|
|
102
107
|
.valid('descending', 'ascending')
|
|
103
108
|
.default(exports.DEFAULT_OPTIONS.sortPosts),
|
|
109
|
+
showLastUpdateTime: utils_validation_1.Joi.bool().default(exports.DEFAULT_OPTIONS.showLastUpdateTime),
|
|
110
|
+
showLastUpdateAuthor: utils_validation_1.Joi.bool().default(exports.DEFAULT_OPTIONS.showLastUpdateAuthor),
|
|
111
|
+
processBlogPosts: utils_validation_1.Joi.function()
|
|
112
|
+
.optional()
|
|
113
|
+
.default(() => exports.DEFAULT_OPTIONS.processBlogPosts),
|
|
104
114
|
}).default(exports.DEFAULT_OPTIONS);
|
|
105
115
|
function validateOptions({ validate, options, }) {
|
|
106
116
|
const validatedOptions = validate(PluginOptionSchema, options);
|
package/package.json
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/plugin-content-blog",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Blog plugin for Docusaurus.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "src/plugin-content-blog.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
"./lib/*": "./lib/*",
|
|
9
|
+
"./src/*": "./src/*",
|
|
10
|
+
"./client": {
|
|
11
|
+
"type": "./lib/client/index.d.ts",
|
|
12
|
+
"default": "./lib/client/index.js"
|
|
13
|
+
},
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./src/plugin-content-blog.d.ts",
|
|
16
|
+
"default": "./lib/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
7
19
|
"scripts": {
|
|
8
|
-
"build": "tsc",
|
|
9
|
-
"watch": "tsc --watch",
|
|
20
|
+
"build": "tsc --build",
|
|
21
|
+
"watch": "tsc --build --watch",
|
|
10
22
|
"test:generate-build-snap": "yarn docusaurus build src/__tests__/__fixtures__/website --out-dir build-snap && yarn rimraf src/__tests__/__fixtures__/website/.docusaurus && yarn rimraf src/__tests__/__fixtures__/website/build-snap/assets && git add src/__tests__/__fixtures__/website/build-snap"
|
|
11
23
|
},
|
|
12
24
|
"repository": {
|
|
@@ -19,13 +31,13 @@
|
|
|
19
31
|
},
|
|
20
32
|
"license": "MIT",
|
|
21
33
|
"dependencies": {
|
|
22
|
-
"@docusaurus/core": "3.
|
|
23
|
-
"@docusaurus/logger": "3.
|
|
24
|
-
"@docusaurus/mdx-loader": "3.
|
|
25
|
-
"@docusaurus/types": "3.
|
|
26
|
-
"@docusaurus/utils": "3.
|
|
27
|
-
"@docusaurus/utils-common": "3.
|
|
28
|
-
"@docusaurus/utils-validation": "3.
|
|
34
|
+
"@docusaurus/core": "3.2.0",
|
|
35
|
+
"@docusaurus/logger": "3.2.0",
|
|
36
|
+
"@docusaurus/mdx-loader": "3.2.0",
|
|
37
|
+
"@docusaurus/types": "3.2.0",
|
|
38
|
+
"@docusaurus/utils": "3.2.0",
|
|
39
|
+
"@docusaurus/utils-common": "3.2.0",
|
|
40
|
+
"@docusaurus/utils-validation": "3.2.0",
|
|
29
41
|
"cheerio": "^1.0.0-rc.12",
|
|
30
42
|
"feed": "^4.2.2",
|
|
31
43
|
"fs-extra": "^11.1.1",
|
|
@@ -44,5 +56,8 @@
|
|
|
44
56
|
"engines": {
|
|
45
57
|
"node": ">=18.0"
|
|
46
58
|
},
|
|
47
|
-
"
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@total-typescript/shoehorn": "^0.1.2"
|
|
61
|
+
},
|
|
62
|
+
"gitHead": "5af143651b26b39761361acd96e9c5be7ba0cb25"
|
|
48
63
|
}
|
package/src/blogUtils.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
getContentPathList,
|
|
27
27
|
isUnlisted,
|
|
28
28
|
isDraft,
|
|
29
|
+
readLastUpdateData,
|
|
29
30
|
} from '@docusaurus/utils';
|
|
30
31
|
import {validateBlogPostFrontMatter} from './frontMatter';
|
|
31
32
|
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
|
|
@@ -57,12 +58,14 @@ export function paginateBlogPosts({
|
|
|
57
58
|
blogTitle,
|
|
58
59
|
blogDescription,
|
|
59
60
|
postsPerPageOption,
|
|
61
|
+
pageBasePath,
|
|
60
62
|
}: {
|
|
61
63
|
blogPosts: BlogPost[];
|
|
62
64
|
basePageUrl: string;
|
|
63
65
|
blogTitle: string;
|
|
64
66
|
blogDescription: string;
|
|
65
67
|
postsPerPageOption: number | 'ALL';
|
|
68
|
+
pageBasePath: string;
|
|
66
69
|
}): BlogPaginated[] {
|
|
67
70
|
const totalCount = blogPosts.length;
|
|
68
71
|
const postsPerPage =
|
|
@@ -73,7 +76,7 @@ export function paginateBlogPosts({
|
|
|
73
76
|
|
|
74
77
|
function permalink(page: number) {
|
|
75
78
|
return page > 0
|
|
76
|
-
? normalizeUrl([basePageUrl,
|
|
79
|
+
? normalizeUrl([basePageUrl, pageBasePath, `${page + 1}`])
|
|
77
80
|
: basePageUrl;
|
|
78
81
|
}
|
|
79
82
|
|
|
@@ -111,6 +114,7 @@ export function getBlogTags({
|
|
|
111
114
|
blogTitle: string;
|
|
112
115
|
blogDescription: string;
|
|
113
116
|
postsPerPageOption: number | 'ALL';
|
|
117
|
+
pageBasePath: string;
|
|
114
118
|
}): BlogTags {
|
|
115
119
|
const groups = groupTaggedItems(
|
|
116
120
|
blogPosts,
|
|
@@ -161,25 +165,6 @@ export function parseBlogFileName(
|
|
|
161
165
|
return {date: undefined, text, slug};
|
|
162
166
|
}
|
|
163
167
|
|
|
164
|
-
function formatBlogPostDate(
|
|
165
|
-
locale: string,
|
|
166
|
-
date: Date,
|
|
167
|
-
calendar: string,
|
|
168
|
-
): string {
|
|
169
|
-
try {
|
|
170
|
-
return new Intl.DateTimeFormat(locale, {
|
|
171
|
-
day: 'numeric',
|
|
172
|
-
month: 'long',
|
|
173
|
-
year: 'numeric',
|
|
174
|
-
timeZone: 'UTC',
|
|
175
|
-
calendar,
|
|
176
|
-
}).format(date);
|
|
177
|
-
} catch (err) {
|
|
178
|
-
logger.error`Can't format blog post date "${String(date)}"`;
|
|
179
|
-
throw err;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
168
|
async function parseBlogPostMarkdownFile({
|
|
184
169
|
filePath,
|
|
185
170
|
parseFrontMatter,
|
|
@@ -247,6 +232,12 @@ async function processBlogSourceFile(
|
|
|
247
232
|
|
|
248
233
|
const aliasedSource = aliasedSitePath(blogSourceAbsolute, siteDir);
|
|
249
234
|
|
|
235
|
+
const lastUpdate = await readLastUpdateData(
|
|
236
|
+
blogSourceAbsolute,
|
|
237
|
+
options,
|
|
238
|
+
frontMatter.last_update,
|
|
239
|
+
);
|
|
240
|
+
|
|
250
241
|
const draft = isDraft({frontMatter});
|
|
251
242
|
const unlisted = isUnlisted({frontMatter});
|
|
252
243
|
|
|
@@ -274,10 +265,11 @@ async function processBlogSourceFile(
|
|
|
274
265
|
}
|
|
275
266
|
|
|
276
267
|
try {
|
|
277
|
-
const result = getFileCommitDate(blogSourceAbsolute, {
|
|
268
|
+
const result = await getFileCommitDate(blogSourceAbsolute, {
|
|
278
269
|
age: 'oldest',
|
|
279
270
|
includeAuthor: false,
|
|
280
271
|
});
|
|
272
|
+
|
|
281
273
|
return result.date;
|
|
282
274
|
} catch (err) {
|
|
283
275
|
logger.warn(err);
|
|
@@ -286,11 +278,6 @@ async function processBlogSourceFile(
|
|
|
286
278
|
}
|
|
287
279
|
|
|
288
280
|
const date = await getDate();
|
|
289
|
-
const formattedDate = formatBlogPostDate(
|
|
290
|
-
i18n.currentLocale,
|
|
291
|
-
date,
|
|
292
|
-
i18n.localeConfigs[i18n.currentLocale]!.calendar,
|
|
293
|
-
);
|
|
294
281
|
|
|
295
282
|
const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text;
|
|
296
283
|
const description = frontMatter.description ?? excerpt ?? '';
|
|
@@ -345,7 +332,6 @@ async function processBlogSourceFile(
|
|
|
345
332
|
title,
|
|
346
333
|
description,
|
|
347
334
|
date,
|
|
348
|
-
formattedDate,
|
|
349
335
|
tags: normalizeFrontMatterTags(tagsBasePath, frontMatter.tags),
|
|
350
336
|
readingTime: showReadingTime
|
|
351
337
|
? options.readingTime({
|
|
@@ -358,6 +344,8 @@ async function processBlogSourceFile(
|
|
|
358
344
|
authors,
|
|
359
345
|
frontMatter,
|
|
360
346
|
unlisted,
|
|
347
|
+
lastUpdatedAt: lastUpdate.lastUpdatedAt,
|
|
348
|
+
lastUpdatedBy: lastUpdate.lastUpdatedBy,
|
|
361
349
|
},
|
|
362
350
|
content,
|
|
363
351
|
};
|
|
@@ -443,3 +431,19 @@ export function linkify({
|
|
|
443
431
|
|
|
444
432
|
return newContent;
|
|
445
433
|
}
|
|
434
|
+
|
|
435
|
+
export async function applyProcessBlogPosts({
|
|
436
|
+
blogPosts,
|
|
437
|
+
processBlogPosts,
|
|
438
|
+
}: {
|
|
439
|
+
blogPosts: BlogPost[];
|
|
440
|
+
processBlogPosts: PluginOptions['processBlogPosts'];
|
|
441
|
+
}): Promise<BlogPost[]> {
|
|
442
|
+
const processedBlogPosts = await processBlogPosts({blogPosts});
|
|
443
|
+
|
|
444
|
+
if (Array.isArray(processedBlogPosts)) {
|
|
445
|
+
return processedBlogPosts;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return blogPosts;
|
|
449
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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 useRouteContext from '@docusaurus/useRouteContext';
|
|
9
|
+
import type {BlogMetadata} from '@docusaurus/plugin-content-blog';
|
|
10
|
+
|
|
11
|
+
export function useBlogMetadata(): BlogMetadata {
|
|
12
|
+
const routeContext = useRouteContext();
|
|
13
|
+
const blogMetadata = routeContext?.data?.blogMetadata;
|
|
14
|
+
if (!blogMetadata) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
"useBlogMetadata() can't be called on the current route because the blog metadata could not be found in route context",
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return blogMetadata as BlogMetadata;
|
|
20
|
+
}
|
package/src/feed.ts
CHANGED
|
@@ -11,7 +11,10 @@ import logger from '@docusaurus/logger';
|
|
|
11
11
|
import {Feed, type Author as FeedAuthor} from 'feed';
|
|
12
12
|
import * as srcset from 'srcset';
|
|
13
13
|
import {normalizeUrl, readOutputHTMLFile} from '@docusaurus/utils';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
blogPostContainerID,
|
|
16
|
+
applyTrailingSlash,
|
|
17
|
+
} from '@docusaurus/utils-common';
|
|
15
18
|
import {load as cheerioLoad} from 'cheerio';
|
|
16
19
|
import type {DocusaurusConfig} from '@docusaurus/types';
|
|
17
20
|
import type {
|
|
@@ -40,8 +43,14 @@ async function generateBlogFeed({
|
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
const {feedOptions, routeBasePath} = options;
|
|
43
|
-
const {url: siteUrl, baseUrl, title, favicon} = siteConfig;
|
|
44
|
-
const blogBaseUrl =
|
|
46
|
+
const {url: siteUrl, baseUrl, title, favicon, trailingSlash} = siteConfig;
|
|
47
|
+
const blogBaseUrl = applyTrailingSlash(
|
|
48
|
+
normalizeUrl([siteUrl, baseUrl, routeBasePath]),
|
|
49
|
+
{
|
|
50
|
+
trailingSlash,
|
|
51
|
+
baseUrl,
|
|
52
|
+
},
|
|
53
|
+
);
|
|
45
54
|
|
|
46
55
|
const blogPostsForFeed =
|
|
47
56
|
feedOptions.limit === false || feedOptions.limit === null
|
|
@@ -85,7 +94,7 @@ async function defaultCreateFeedItems({
|
|
|
85
94
|
siteConfig: DocusaurusConfig;
|
|
86
95
|
outDir: string;
|
|
87
96
|
}): Promise<BlogFeedItem[]> {
|
|
88
|
-
const {url: siteUrl} = siteConfig;
|
|
97
|
+
const {url: siteUrl, baseUrl, trailingSlash} = siteConfig;
|
|
89
98
|
|
|
90
99
|
function toFeedAuthor(author: Author): FeedAuthor {
|
|
91
100
|
return {name: author.name, link: author.url, email: author.email};
|
|
@@ -105,13 +114,19 @@ async function defaultCreateFeedItems({
|
|
|
105
114
|
} = post;
|
|
106
115
|
|
|
107
116
|
const content = await readOutputHTMLFile(
|
|
108
|
-
permalink.replace(
|
|
117
|
+
permalink.replace(baseUrl, ''),
|
|
109
118
|
outDir,
|
|
110
|
-
|
|
119
|
+
trailingSlash,
|
|
111
120
|
);
|
|
112
121
|
const $ = cheerioLoad(content);
|
|
113
122
|
|
|
114
|
-
const blogPostAbsoluteUrl =
|
|
123
|
+
const blogPostAbsoluteUrl = applyTrailingSlash(
|
|
124
|
+
normalizeUrl([siteUrl, permalink]),
|
|
125
|
+
{
|
|
126
|
+
trailingSlash,
|
|
127
|
+
baseUrl,
|
|
128
|
+
},
|
|
129
|
+
);
|
|
115
130
|
|
|
116
131
|
const toAbsoluteUrl = (src: string) =>
|
|
117
132
|
String(new URL(src, blogPostAbsoluteUrl));
|
package/src/frontMatter.ts
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
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
|
-
|
|
8
7
|
import {
|
|
8
|
+
ContentVisibilitySchema,
|
|
9
|
+
FrontMatterLastUpdateSchema,
|
|
10
|
+
FrontMatterTOCHeadingLevels,
|
|
11
|
+
FrontMatterTagsSchema,
|
|
9
12
|
JoiFrontMatter as Joi, // Custom instance for front matter
|
|
10
13
|
URISchema,
|
|
11
14
|
validateFrontMatter,
|
|
12
|
-
FrontMatterTagsSchema,
|
|
13
|
-
FrontMatterTOCHeadingLevels,
|
|
14
|
-
ContentVisibilitySchema,
|
|
15
15
|
} from '@docusaurus/utils-validation';
|
|
16
16
|
import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
|
|
17
17
|
|
|
@@ -69,6 +69,7 @@ const BlogFrontMatterSchema = Joi.object<BlogPostFrontMatter>({
|
|
|
69
69
|
hide_table_of_contents: Joi.boolean(),
|
|
70
70
|
|
|
71
71
|
...FrontMatterTOCHeadingLevels,
|
|
72
|
+
last_update: FrontMatterLastUpdateSchema,
|
|
72
73
|
})
|
|
73
74
|
.messages({
|
|
74
75
|
'deprecate.error':
|