@docusaurus/plugin-content-blog 2.0.0-beta.18 → 2.0.0-beta.19
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 +2 -2
- package/lib/blogUtils.js +4 -3
- package/lib/feed.d.ts +1 -2
- package/lib/feed.js +4 -4
- package/lib/index.d.ts +1 -2
- package/lib/index.js +40 -38
- package/lib/options.js +1 -1
- package/lib/remark/footnoteIDFixer.d.ts +14 -0
- package/lib/remark/footnoteIDFixer.js +29 -0
- package/lib/translations.d.ts +4 -5
- package/lib/types.d.ts +0 -27
- package/package.json +13 -12
- package/src/blogUtils.ts +16 -10
- package/src/feed.ts +42 -48
- package/src/index.ts +61 -68
- package/src/options.ts +1 -1
- package/src/plugin-content-blog.d.ts +102 -113
- package/src/remark/footnoteIDFixer.ts +29 -0
- package/src/translations.ts +8 -5
- package/src/types.ts +0 -36
package/lib/blogUtils.d.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
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 type {
|
|
7
|
+
import type { BlogContentPaths, BlogMarkdownLoaderOptions } from './types';
|
|
8
8
|
import type { LoadContext } from '@docusaurus/types';
|
|
9
|
-
import type { PluginOptions } from '@docusaurus/plugin-content-blog';
|
|
9
|
+
import type { PluginOptions, BlogPost, BlogTags, BlogPaginated } from '@docusaurus/plugin-content-blog';
|
|
10
10
|
export declare function truncate(fileString: string, truncateMarker: RegExp): string;
|
|
11
11
|
export declare function getSourceToPermalink(blogPosts: BlogPost[]): {
|
|
12
12
|
[aliasedPath: string]: string;
|
package/lib/blogUtils.js
CHANGED
|
@@ -58,7 +58,7 @@ exports.paginateBlogPosts = paginateBlogPosts;
|
|
|
58
58
|
function getBlogTags({ blogPosts, ...params }) {
|
|
59
59
|
const groups = (0, utils_1.groupTaggedItems)(blogPosts, (blogPost) => blogPost.metadata.tags);
|
|
60
60
|
return lodash_1.default.mapValues(groups, ({ tag, items: tagBlogPosts }) => ({
|
|
61
|
-
|
|
61
|
+
label: tag.label,
|
|
62
62
|
items: tagBlogPosts.map((item) => item.id),
|
|
63
63
|
permalink: tag.permalink,
|
|
64
64
|
pages: paginateBlogPosts({
|
|
@@ -85,13 +85,14 @@ function parseBlogFileName(blogSourceRelative) {
|
|
|
85
85
|
return { date: undefined, text, slug };
|
|
86
86
|
}
|
|
87
87
|
exports.parseBlogFileName = parseBlogFileName;
|
|
88
|
-
function formatBlogPostDate(locale, date) {
|
|
88
|
+
function formatBlogPostDate(locale, date, calendar) {
|
|
89
89
|
try {
|
|
90
90
|
return new Intl.DateTimeFormat(locale, {
|
|
91
91
|
day: 'numeric',
|
|
92
92
|
month: 'long',
|
|
93
93
|
year: 'numeric',
|
|
94
94
|
timeZone: 'UTC',
|
|
95
|
+
calendar,
|
|
95
96
|
}).format(date);
|
|
96
97
|
}
|
|
97
98
|
catch (err) {
|
|
@@ -157,7 +158,7 @@ async function processBlogSourceFile(blogSourceRelative, contentPaths, context,
|
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
160
|
const date = await getDate();
|
|
160
|
-
const formattedDate = formatBlogPostDate(i18n.currentLocale, date);
|
|
161
|
+
const formattedDate = formatBlogPostDate(i18n.currentLocale, date, i18n.localeConfigs[i18n.currentLocale].calendar);
|
|
161
162
|
const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text;
|
|
162
163
|
const description = frontMatter.description ?? excerpt ?? '';
|
|
163
164
|
const slug = frontMatter.slug || parsedBlogFileName.slug;
|
package/lib/feed.d.ts
CHANGED
|
@@ -4,9 +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 type { BlogPost } from './types';
|
|
8
7
|
import type { DocusaurusConfig } from '@docusaurus/types';
|
|
9
|
-
import type { PluginOptions } from '@docusaurus/plugin-content-blog';
|
|
8
|
+
import type { PluginOptions, BlogPost } from '@docusaurus/plugin-content-blog';
|
|
10
9
|
export declare function createBlogFeedFiles({ blogPosts, options, siteConfig, outDir, locale, }: {
|
|
11
10
|
blogPosts: BlogPost[];
|
|
12
11
|
options: PluginOptions;
|
package/lib/feed.js
CHANGED
|
@@ -35,7 +35,7 @@ async function generateBlogFeed({ blogPosts, options, siteConfig, outDir, locale
|
|
|
35
35
|
function toFeedAuthor(author) {
|
|
36
36
|
return { name: author.name, link: author.url, email: author.email };
|
|
37
37
|
}
|
|
38
|
-
await (
|
|
38
|
+
await Promise.all(blogPosts.map(async (post) => {
|
|
39
39
|
const { id, metadata: { title: metadataTitle, permalink, date, description, authors, tags, }, } = post;
|
|
40
40
|
const content = await (0, utils_1.readOutputHTMLFile)(permalink.replace(siteConfig.baseUrl, ''), outDir, siteConfig.trailingSlash);
|
|
41
41
|
const $ = (0, cheerio_1.load)(content);
|
|
@@ -55,8 +55,8 @@ async function generateBlogFeed({ blogPosts, options, siteConfig, outDir, locale
|
|
|
55
55
|
if (feedItemAuthors.length > 0) {
|
|
56
56
|
feedItem.author = feedItemAuthors;
|
|
57
57
|
}
|
|
58
|
-
|
|
59
|
-
});
|
|
58
|
+
return feedItem;
|
|
59
|
+
})).then((items) => items.forEach(feed.addItem));
|
|
60
60
|
return feed;
|
|
61
61
|
}
|
|
62
62
|
async function createBlogFeedFile({ feed, feedType, generatePath, }) {
|
|
@@ -73,7 +73,7 @@ async function createBlogFeedFile({ feed, feedType, generatePath, }) {
|
|
|
73
73
|
}
|
|
74
74
|
})();
|
|
75
75
|
try {
|
|
76
|
-
await fs_extra_1.default.outputFile(
|
|
76
|
+
await fs_extra_1.default.outputFile(path_1.default.join(generatePath, feedPath), feedContent);
|
|
77
77
|
}
|
|
78
78
|
catch (err) {
|
|
79
79
|
throw new Error(`Generating ${feedType} feed failed: ${err}.`);
|
package/lib/index.d.ts
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
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 type { BlogContent } from './types';
|
|
8
7
|
import type { LoadContext, Plugin } from '@docusaurus/types';
|
|
9
|
-
import type { PluginOptions } from '@docusaurus/plugin-content-blog';
|
|
8
|
+
import type { PluginOptions, BlogContent } from '@docusaurus/plugin-content-blog';
|
|
10
9
|
export default function pluginContentBlog(context: LoadContext, options: PluginOptions): Promise<Plugin<BlogContent>>;
|
|
11
10
|
export { validateOptions } from './options';
|
package/lib/index.js
CHANGED
|
@@ -10,6 +10,7 @@ exports.validateOptions = void 0;
|
|
|
10
10
|
const tslib_1 = require("tslib");
|
|
11
11
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
12
12
|
const remark_admonitions_1 = tslib_1.__importDefault(require("remark-admonitions"));
|
|
13
|
+
const footnoteIDFixer_1 = tslib_1.__importDefault(require("./remark/footnoteIDFixer"));
|
|
13
14
|
const utils_1 = require("@docusaurus/utils");
|
|
14
15
|
const translations_1 = require("./translations");
|
|
15
16
|
const blogUtils_1 = require("./blogUtils");
|
|
@@ -46,12 +47,14 @@ async function pluginContentBlog(context, options) {
|
|
|
46
47
|
const contentMarkdownGlobs = (0, utils_1.getContentPathList)(contentPaths).flatMap((contentPath) => include.map((pattern) => `${contentPath}/${pattern}`));
|
|
47
48
|
return [authorsMapFilePath, ...contentMarkdownGlobs].filter(Boolean);
|
|
48
49
|
},
|
|
49
|
-
|
|
50
|
+
getTranslationFiles() {
|
|
50
51
|
return (0, translations_1.getTranslationFiles)(options);
|
|
51
52
|
},
|
|
52
53
|
// Fetches blog contents and returns metadata for the necessary routes.
|
|
53
54
|
async loadContent() {
|
|
54
55
|
const { postsPerPage: postsPerPageOption, routeBasePath, tagsBasePath, blogDescription, blogTitle, blogSidebarTitle, } = options;
|
|
56
|
+
const baseBlogUrl = (0, utils_1.normalizeUrl)([baseUrl, routeBasePath]);
|
|
57
|
+
const blogTagsListPath = (0, utils_1.normalizeUrl)([baseBlogUrl, tagsBasePath]);
|
|
55
58
|
const blogPosts = await (0, blogUtils_1.generateBlogPosts)(contentPaths, context, options);
|
|
56
59
|
if (!blogPosts.length) {
|
|
57
60
|
return {
|
|
@@ -59,7 +62,7 @@ async function pluginContentBlog(context, options) {
|
|
|
59
62
|
blogPosts: [],
|
|
60
63
|
blogListPaginated: [],
|
|
61
64
|
blogTags: {},
|
|
62
|
-
blogTagsListPath
|
|
65
|
+
blogTagsListPath,
|
|
63
66
|
blogTagsPaginated: [],
|
|
64
67
|
};
|
|
65
68
|
}
|
|
@@ -80,7 +83,6 @@ async function pluginContentBlog(context, options) {
|
|
|
80
83
|
};
|
|
81
84
|
}
|
|
82
85
|
});
|
|
83
|
-
const baseBlogUrl = (0, utils_1.normalizeUrl)([baseUrl, routeBasePath]);
|
|
84
86
|
const blogListPaginated = (0, blogUtils_1.paginateBlogPosts)({
|
|
85
87
|
blogPosts,
|
|
86
88
|
blogTitle,
|
|
@@ -94,8 +96,6 @@ async function pluginContentBlog(context, options) {
|
|
|
94
96
|
blogDescription,
|
|
95
97
|
blogTitle,
|
|
96
98
|
});
|
|
97
|
-
const tagsPath = (0, utils_1.normalizeUrl)([baseBlogUrl, tagsBasePath]);
|
|
98
|
-
const blogTagsListPath = Object.keys(blogTags).length > 0 ? tagsPath : null;
|
|
99
99
|
return {
|
|
100
100
|
blogSidebarTitle,
|
|
101
101
|
blogPosts,
|
|
@@ -121,7 +121,7 @@ async function pluginContentBlog(context, options) {
|
|
|
121
121
|
routeBasePath,
|
|
122
122
|
archiveBasePath,
|
|
123
123
|
]);
|
|
124
|
-
//
|
|
124
|
+
// Create a blog archive route
|
|
125
125
|
const archiveProp = await createData(`${(0, utils_1.docuHash)(archiveUrl)}.json`, JSON.stringify({ blogPosts }, null, 2));
|
|
126
126
|
addRoute({
|
|
127
127
|
path: archiveUrl,
|
|
@@ -172,10 +172,7 @@ async function pluginContentBlog(context, options) {
|
|
|
172
172
|
exact: true,
|
|
173
173
|
modules: {
|
|
174
174
|
sidebar: aliasedSource(sidebarProp),
|
|
175
|
-
items: items.map((postID) =>
|
|
176
|
-
// To tell routes.js this is an import and not a nested object
|
|
177
|
-
// to recurse.
|
|
178
|
-
({
|
|
175
|
+
items: items.map((postID) => ({
|
|
179
176
|
content: {
|
|
180
177
|
__import: true,
|
|
181
178
|
path: blogItemsToMetadata[postID].source,
|
|
@@ -188,23 +185,37 @@ async function pluginContentBlog(context, options) {
|
|
|
188
185
|
},
|
|
189
186
|
});
|
|
190
187
|
}));
|
|
191
|
-
// Tags.
|
|
192
|
-
if (
|
|
188
|
+
// Tags. This is the last part so we early-return if there are no tags.
|
|
189
|
+
if (Object.keys(blogTags).length === 0) {
|
|
193
190
|
return;
|
|
194
191
|
}
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
name: tag.name,
|
|
199
|
-
count: tag.items.length,
|
|
192
|
+
async function createTagsListPage() {
|
|
193
|
+
const tagsProp = Object.values(blogTags).map((tag) => ({
|
|
194
|
+
label: tag.label,
|
|
200
195
|
permalink: tag.permalink,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
196
|
+
count: tag.items.length,
|
|
197
|
+
}));
|
|
198
|
+
const tagsPropPath = await createData(`${(0, utils_1.docuHash)(`${blogTagsListPath}-tags`)}.json`, JSON.stringify(tagsProp, null, 2));
|
|
199
|
+
addRoute({
|
|
200
|
+
path: blogTagsListPath,
|
|
201
|
+
component: blogTagsListComponent,
|
|
202
|
+
exact: true,
|
|
203
|
+
modules: {
|
|
204
|
+
sidebar: aliasedSource(sidebarProp),
|
|
205
|
+
tags: aliasedSource(tagsPropPath),
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
async function createTagPostsListPage(tag) {
|
|
205
210
|
await Promise.all(tag.pages.map(async (blogPaginated) => {
|
|
206
211
|
const { metadata, items } = blogPaginated;
|
|
207
|
-
const
|
|
212
|
+
const tagProp = {
|
|
213
|
+
label: tag.label,
|
|
214
|
+
permalink: tag.permalink,
|
|
215
|
+
allTagsPath: blogTagsListPath,
|
|
216
|
+
count: tag.items.length,
|
|
217
|
+
};
|
|
218
|
+
const tagPropPath = await createData(`${(0, utils_1.docuHash)(metadata.permalink)}.json`, JSON.stringify(tagProp, null, 2));
|
|
208
219
|
const listMetadataPath = await createData(`${(0, utils_1.docuHash)(metadata.permalink)}-list.json`, JSON.stringify(metadata, null, 2));
|
|
209
220
|
addRoute({
|
|
210
221
|
path: metadata.permalink,
|
|
@@ -224,26 +235,14 @@ async function pluginContentBlog(context, options) {
|
|
|
224
235
|
},
|
|
225
236
|
};
|
|
226
237
|
}),
|
|
227
|
-
|
|
238
|
+
tag: aliasedSource(tagPropPath),
|
|
228
239
|
listMetadata: aliasedSource(listMetadataPath),
|
|
229
240
|
},
|
|
230
241
|
});
|
|
231
242
|
}));
|
|
232
243
|
}
|
|
233
|
-
await
|
|
234
|
-
|
|
235
|
-
if (Object.keys(blogTags).length > 0) {
|
|
236
|
-
const tagsListPath = await createData(`${(0, utils_1.docuHash)(`${blogTagsListPath}-tags`)}.json`, JSON.stringify(tagsModule, null, 2));
|
|
237
|
-
addRoute({
|
|
238
|
-
path: blogTagsListPath,
|
|
239
|
-
component: blogTagsListComponent,
|
|
240
|
-
exact: true,
|
|
241
|
-
modules: {
|
|
242
|
-
sidebar: aliasedSource(sidebarProp),
|
|
243
|
-
tags: aliasedSource(tagsListPath),
|
|
244
|
-
},
|
|
245
|
-
});
|
|
246
|
-
}
|
|
244
|
+
await createTagsListPage();
|
|
245
|
+
await Promise.all(Object.values(blogTags).map(createTagPostsListPage));
|
|
247
246
|
},
|
|
248
247
|
translateContent({ content, translationFiles }) {
|
|
249
248
|
return (0, translations_1.translateContent)(content, translationFiles);
|
|
@@ -283,7 +282,10 @@ async function pluginContentBlog(context, options) {
|
|
|
283
282
|
options: {
|
|
284
283
|
remarkPlugins,
|
|
285
284
|
rehypePlugins,
|
|
286
|
-
beforeDefaultRemarkPlugins
|
|
285
|
+
beforeDefaultRemarkPlugins: [
|
|
286
|
+
footnoteIDFixer_1.default,
|
|
287
|
+
...beforeDefaultRemarkPlugins,
|
|
288
|
+
],
|
|
287
289
|
beforeDefaultRehypePlugins,
|
|
288
290
|
staticDirs: siteConfig.staticDirectories.map((dir) => path_1.default.resolve(siteDir, dir)),
|
|
289
291
|
siteDir,
|
package/lib/options.js
CHANGED
|
@@ -85,7 +85,7 @@ const PluginOptionSchema = utils_validation_1.Joi.object({
|
|
|
85
85
|
.default(exports.DEFAULT_OPTIONS.feedOptions.type),
|
|
86
86
|
title: utils_validation_1.Joi.string().allow(''),
|
|
87
87
|
description: utils_validation_1.Joi.string().allow(''),
|
|
88
|
-
//
|
|
88
|
+
// Only add default value when user actually wants a feed (type is not null)
|
|
89
89
|
copyright: utils_validation_1.Joi.when('type', {
|
|
90
90
|
is: utils_validation_1.Joi.any().valid(null),
|
|
91
91
|
then: utils_validation_1.Joi.string().optional(),
|
|
@@ -0,0 +1,14 @@
|
|
|
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 { Transformer } from 'unified';
|
|
8
|
+
/**
|
|
9
|
+
* In the blog list view, each post will be compiled separately. However, they
|
|
10
|
+
* may use the same footnote IDs. This leads to duplicated DOM IDs and inability
|
|
11
|
+
* to navigate to footnote references. This plugin fixes it by appending a
|
|
12
|
+
* unique hash to each reference/definition.
|
|
13
|
+
*/
|
|
14
|
+
export default function plugin(): Transformer;
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
const tslib_1 = require("tslib");
|
|
10
|
+
const unist_util_visit_1 = tslib_1.__importDefault(require("unist-util-visit"));
|
|
11
|
+
const utils_1 = require("@docusaurus/utils");
|
|
12
|
+
/**
|
|
13
|
+
* In the blog list view, each post will be compiled separately. However, they
|
|
14
|
+
* may use the same footnote IDs. This leads to duplicated DOM IDs and inability
|
|
15
|
+
* to navigate to footnote references. This plugin fixes it by appending a
|
|
16
|
+
* unique hash to each reference/definition.
|
|
17
|
+
*/
|
|
18
|
+
function plugin() {
|
|
19
|
+
return (root, vfile) => {
|
|
20
|
+
const suffix = `-${(0, utils_1.simpleHash)(vfile.path, 6)}`;
|
|
21
|
+
(0, unist_util_visit_1.default)(root, 'footnoteReference', (node) => {
|
|
22
|
+
node.identifier += suffix;
|
|
23
|
+
});
|
|
24
|
+
(0, unist_util_visit_1.default)(root, 'footnoteDefinition', (node) => {
|
|
25
|
+
node.identifier += suffix;
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
exports.default = plugin;
|
package/lib/translations.d.ts
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
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 type {
|
|
8
|
-
import type {
|
|
9
|
-
|
|
10
|
-
export declare function
|
|
11
|
-
export declare function translateContent(content: BlogContent, translationFiles: TranslationFiles): BlogContent;
|
|
7
|
+
import type { TranslationFile } from '@docusaurus/types';
|
|
8
|
+
import type { PluginOptions, BlogContent } from '@docusaurus/plugin-content-blog';
|
|
9
|
+
export declare function getTranslationFiles(options: PluginOptions): TranslationFile[];
|
|
10
|
+
export declare function translateContent(content: BlogContent, translationFiles: TranslationFile[]): BlogContent;
|
package/lib/types.d.ts
CHANGED
|
@@ -5,34 +5,7 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
import type { BrokenMarkdownLink, ContentPaths } from '@docusaurus/utils';
|
|
8
|
-
import type { BlogPostMetadata } from '@docusaurus/plugin-content-blog';
|
|
9
|
-
import type { Metadata as BlogPaginatedMetadata } from '@theme/BlogListPage';
|
|
10
8
|
export declare type BlogContentPaths = ContentPaths;
|
|
11
|
-
export interface BlogContent {
|
|
12
|
-
blogSidebarTitle: string;
|
|
13
|
-
blogPosts: BlogPost[];
|
|
14
|
-
blogListPaginated: BlogPaginated[];
|
|
15
|
-
blogTags: BlogTags;
|
|
16
|
-
blogTagsListPath: string | null;
|
|
17
|
-
}
|
|
18
|
-
export interface BlogTags {
|
|
19
|
-
[tagKey: string]: BlogTag;
|
|
20
|
-
}
|
|
21
|
-
export interface BlogTag {
|
|
22
|
-
name: string;
|
|
23
|
-
items: string[];
|
|
24
|
-
permalink: string;
|
|
25
|
-
pages: BlogPaginated[];
|
|
26
|
-
}
|
|
27
|
-
export interface BlogPost {
|
|
28
|
-
id: string;
|
|
29
|
-
metadata: BlogPostMetadata;
|
|
30
|
-
content: string;
|
|
31
|
-
}
|
|
32
|
-
export interface BlogPaginated {
|
|
33
|
-
metadata: BlogPaginatedMetadata;
|
|
34
|
-
items: string[];
|
|
35
|
-
}
|
|
36
9
|
export declare type BlogBrokenMarkdownLink = BrokenMarkdownLink<BlogContentPaths>;
|
|
37
10
|
export declare type BlogMarkdownLoaderOptions = {
|
|
38
11
|
siteDir: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/plugin-content-blog",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.19",
|
|
4
4
|
"description": "Blog plugin for Docusaurus.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "src/plugin-content-blog.d.ts",
|
|
@@ -18,24 +18,25 @@
|
|
|
18
18
|
},
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@docusaurus/core": "2.0.0-beta.
|
|
22
|
-
"@docusaurus/logger": "2.0.0-beta.
|
|
23
|
-
"@docusaurus/mdx-loader": "2.0.0-beta.
|
|
24
|
-
"@docusaurus/utils": "2.0.0-beta.
|
|
25
|
-
"@docusaurus/utils-common": "2.0.0-beta.
|
|
26
|
-
"@docusaurus/utils-validation": "2.0.0-beta.
|
|
21
|
+
"@docusaurus/core": "2.0.0-beta.19",
|
|
22
|
+
"@docusaurus/logger": "2.0.0-beta.19",
|
|
23
|
+
"@docusaurus/mdx-loader": "2.0.0-beta.19",
|
|
24
|
+
"@docusaurus/utils": "2.0.0-beta.19",
|
|
25
|
+
"@docusaurus/utils-common": "2.0.0-beta.19",
|
|
26
|
+
"@docusaurus/utils-validation": "2.0.0-beta.19",
|
|
27
27
|
"cheerio": "^1.0.0-rc.10",
|
|
28
28
|
"feed": "^4.2.2",
|
|
29
|
-
"fs-extra": "^10.0
|
|
29
|
+
"fs-extra": "^10.1.0",
|
|
30
30
|
"lodash": "^4.17.21",
|
|
31
31
|
"reading-time": "^1.5.0",
|
|
32
32
|
"remark-admonitions": "^1.2.1",
|
|
33
|
-
"tslib": "^2.
|
|
33
|
+
"tslib": "^2.4.0",
|
|
34
|
+
"unist-util-visit": "^2.0.3",
|
|
34
35
|
"utility-types": "^3.10.0",
|
|
35
|
-
"webpack": "^5.
|
|
36
|
+
"webpack": "^5.72.0"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
|
-
"@docusaurus/types": "2.0.0-beta.
|
|
39
|
+
"@docusaurus/types": "2.0.0-beta.19",
|
|
39
40
|
"escape-string-regexp": "^4.0.0"
|
|
40
41
|
},
|
|
41
42
|
"peerDependencies": {
|
|
@@ -45,5 +46,5 @@
|
|
|
45
46
|
"engines": {
|
|
46
47
|
"node": ">=14"
|
|
47
48
|
},
|
|
48
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "a71e60a49cce93c1006ef10c41ac03187f057102"
|
|
49
50
|
}
|
package/src/blogUtils.ts
CHANGED
|
@@ -9,13 +9,7 @@ import fs from 'fs-extra';
|
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import readingTime from 'reading-time';
|
|
11
11
|
import _ from 'lodash';
|
|
12
|
-
import type {
|
|
13
|
-
BlogPost,
|
|
14
|
-
BlogContentPaths,
|
|
15
|
-
BlogMarkdownLoaderOptions,
|
|
16
|
-
BlogTags,
|
|
17
|
-
BlogPaginated,
|
|
18
|
-
} from './types';
|
|
12
|
+
import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types';
|
|
19
13
|
import {
|
|
20
14
|
parseMarkdownString,
|
|
21
15
|
normalizeUrl,
|
|
@@ -37,6 +31,9 @@ import logger from '@docusaurus/logger';
|
|
|
37
31
|
import type {
|
|
38
32
|
PluginOptions,
|
|
39
33
|
ReadingTimeFunction,
|
|
34
|
+
BlogPost,
|
|
35
|
+
BlogTags,
|
|
36
|
+
BlogPaginated,
|
|
40
37
|
} from '@docusaurus/plugin-content-blog';
|
|
41
38
|
|
|
42
39
|
export function truncate(fileString: string, truncateMarker: RegExp): string {
|
|
@@ -114,7 +111,7 @@ export function getBlogTags({
|
|
|
114
111
|
);
|
|
115
112
|
|
|
116
113
|
return _.mapValues(groups, ({tag, items: tagBlogPosts}) => ({
|
|
117
|
-
|
|
114
|
+
label: tag.label,
|
|
118
115
|
items: tagBlogPosts.map((item) => item.id),
|
|
119
116
|
permalink: tag.permalink,
|
|
120
117
|
pages: paginateBlogPosts({
|
|
@@ -151,13 +148,18 @@ export function parseBlogFileName(
|
|
|
151
148
|
return {date: undefined, text, slug};
|
|
152
149
|
}
|
|
153
150
|
|
|
154
|
-
function formatBlogPostDate(
|
|
151
|
+
function formatBlogPostDate(
|
|
152
|
+
locale: string,
|
|
153
|
+
date: Date,
|
|
154
|
+
calendar: string,
|
|
155
|
+
): string {
|
|
155
156
|
try {
|
|
156
157
|
return new Intl.DateTimeFormat(locale, {
|
|
157
158
|
day: 'numeric',
|
|
158
159
|
month: 'long',
|
|
159
160
|
year: 'numeric',
|
|
160
161
|
timeZone: 'UTC',
|
|
162
|
+
calendar,
|
|
161
163
|
}).format(date);
|
|
162
164
|
} catch (err) {
|
|
163
165
|
logger.error`Can't format blog post date "${String(date)}"`;
|
|
@@ -253,7 +255,11 @@ async function processBlogSourceFile(
|
|
|
253
255
|
}
|
|
254
256
|
|
|
255
257
|
const date = await getDate();
|
|
256
|
-
const formattedDate = formatBlogPostDate(
|
|
258
|
+
const formattedDate = formatBlogPostDate(
|
|
259
|
+
i18n.currentLocale,
|
|
260
|
+
date,
|
|
261
|
+
i18n.localeConfigs[i18n.currentLocale]!.calendar,
|
|
262
|
+
);
|
|
257
263
|
|
|
258
264
|
const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text;
|
|
259
265
|
const description = frontMatter.description ?? excerpt ?? '';
|
package/src/feed.ts
CHANGED
|
@@ -6,13 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {Feed, type Author as FeedAuthor, type Item as FeedItem} from 'feed';
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
normalizeUrl,
|
|
12
|
-
posixPath,
|
|
13
|
-
mapAsyncSequential,
|
|
14
|
-
readOutputHTMLFile,
|
|
15
|
-
} from '@docusaurus/utils';
|
|
9
|
+
import {normalizeUrl, readOutputHTMLFile} from '@docusaurus/utils';
|
|
16
10
|
import {load as cheerioLoad} from 'cheerio';
|
|
17
11
|
import type {DocusaurusConfig} from '@docusaurus/types';
|
|
18
12
|
import path from 'path';
|
|
@@ -21,6 +15,7 @@ import type {
|
|
|
21
15
|
FeedType,
|
|
22
16
|
PluginOptions,
|
|
23
17
|
Author,
|
|
18
|
+
BlogPost,
|
|
24
19
|
} from '@docusaurus/plugin-content-blog';
|
|
25
20
|
import {blogPostContainerID} from '@docusaurus/utils-common';
|
|
26
21
|
|
|
@@ -62,46 +57,48 @@ async function generateBlogFeed({
|
|
|
62
57
|
return {name: author.name, link: author.url, email: author.email};
|
|
63
58
|
}
|
|
64
59
|
|
|
65
|
-
await
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
await Promise.all(
|
|
61
|
+
blogPosts.map(async (post) => {
|
|
62
|
+
const {
|
|
63
|
+
id,
|
|
64
|
+
metadata: {
|
|
65
|
+
title: metadataTitle,
|
|
66
|
+
permalink,
|
|
67
|
+
date,
|
|
68
|
+
description,
|
|
69
|
+
authors,
|
|
70
|
+
tags,
|
|
71
|
+
},
|
|
72
|
+
} = post;
|
|
73
|
+
|
|
74
|
+
const content = await readOutputHTMLFile(
|
|
75
|
+
permalink.replace(siteConfig.baseUrl, ''),
|
|
76
|
+
outDir,
|
|
77
|
+
siteConfig.trailingSlash,
|
|
78
|
+
);
|
|
79
|
+
const $ = cheerioLoad(content);
|
|
80
|
+
|
|
81
|
+
const feedItem: FeedItem = {
|
|
69
82
|
title: metadataTitle,
|
|
70
|
-
|
|
83
|
+
id,
|
|
84
|
+
link: normalizeUrl([siteUrl, permalink]),
|
|
71
85
|
date,
|
|
72
86
|
description,
|
|
73
|
-
|
|
74
|
-
tags,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
link: normalizeUrl([siteUrl, permalink]),
|
|
89
|
-
date,
|
|
90
|
-
description,
|
|
91
|
-
// Atom feed demands the "term", while other feeds use "name"
|
|
92
|
-
category: tags.map((tag) => ({name: tag.label, term: tag.label})),
|
|
93
|
-
content: $(`#${blogPostContainerID}`).html()!,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// json1() method takes the first item of authors array
|
|
97
|
-
// it causes an error when authors array is empty
|
|
98
|
-
const feedItemAuthors = authors.map(toFeedAuthor);
|
|
99
|
-
if (feedItemAuthors.length > 0) {
|
|
100
|
-
feedItem.author = feedItemAuthors;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
feed.addItem(feedItem);
|
|
104
|
-
});
|
|
87
|
+
// Atom feed demands the "term", while other feeds use "name"
|
|
88
|
+
category: tags.map((tag) => ({name: tag.label, term: tag.label})),
|
|
89
|
+
content: $(`#${blogPostContainerID}`).html()!,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// json1() method takes the first item of authors array
|
|
93
|
+
// it causes an error when authors array is empty
|
|
94
|
+
const feedItemAuthors = authors.map(toFeedAuthor);
|
|
95
|
+
if (feedItemAuthors.length > 0) {
|
|
96
|
+
feedItem.author = feedItemAuthors;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return feedItem;
|
|
100
|
+
}),
|
|
101
|
+
).then((items) => items.forEach(feed.addItem));
|
|
105
102
|
|
|
106
103
|
return feed;
|
|
107
104
|
}
|
|
@@ -128,10 +125,7 @@ async function createBlogFeedFile({
|
|
|
128
125
|
}
|
|
129
126
|
})();
|
|
130
127
|
try {
|
|
131
|
-
await fs.outputFile(
|
|
132
|
-
posixPath(path.join(generatePath, feedPath)),
|
|
133
|
-
feedContent,
|
|
134
|
-
);
|
|
128
|
+
await fs.outputFile(path.join(generatePath, feedPath), feedContent);
|
|
135
129
|
} catch (err) {
|
|
136
130
|
throw new Error(`Generating ${feedType} feed failed: ${err}.`);
|
|
137
131
|
}
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import admonitions from 'remark-admonitions';
|
|
10
|
+
import footnoteIDFixer from './remark/footnoteIDFixer';
|
|
10
11
|
import {
|
|
11
12
|
normalizeUrl,
|
|
12
13
|
docuHash,
|
|
@@ -19,17 +20,12 @@ import {
|
|
|
19
20
|
getContentPathList,
|
|
20
21
|
getDataFilePath,
|
|
21
22
|
DEFAULT_PLUGIN_ID,
|
|
23
|
+
type TagsListItem,
|
|
24
|
+
type TagModule,
|
|
22
25
|
} from '@docusaurus/utils';
|
|
23
26
|
import {translateContent, getTranslationFiles} from './translations';
|
|
24
27
|
|
|
25
|
-
import type {
|
|
26
|
-
BlogTag,
|
|
27
|
-
BlogTags,
|
|
28
|
-
BlogContent,
|
|
29
|
-
BlogPaginated,
|
|
30
|
-
BlogContentPaths,
|
|
31
|
-
BlogMarkdownLoaderOptions,
|
|
32
|
-
} from './types';
|
|
28
|
+
import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types';
|
|
33
29
|
import type {LoadContext, Plugin, HtmlTags} from '@docusaurus/types';
|
|
34
30
|
import {
|
|
35
31
|
generateBlogPosts,
|
|
@@ -43,7 +39,10 @@ import type {
|
|
|
43
39
|
BlogPostFrontMatter,
|
|
44
40
|
BlogPostMetadata,
|
|
45
41
|
Assets,
|
|
46
|
-
|
|
42
|
+
BlogTag,
|
|
43
|
+
BlogTags,
|
|
44
|
+
BlogContent,
|
|
45
|
+
BlogPaginated,
|
|
47
46
|
} from '@docusaurus/plugin-content-blog';
|
|
48
47
|
|
|
49
48
|
export default async function pluginContentBlog(
|
|
@@ -102,7 +101,7 @@ export default async function pluginContentBlog(
|
|
|
102
101
|
) as string[];
|
|
103
102
|
},
|
|
104
103
|
|
|
105
|
-
|
|
104
|
+
getTranslationFiles() {
|
|
106
105
|
return getTranslationFiles(options);
|
|
107
106
|
},
|
|
108
107
|
|
|
@@ -117,6 +116,8 @@ export default async function pluginContentBlog(
|
|
|
117
116
|
blogSidebarTitle,
|
|
118
117
|
} = options;
|
|
119
118
|
|
|
119
|
+
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
120
|
+
const blogTagsListPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
|
|
120
121
|
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
121
122
|
|
|
122
123
|
if (!blogPosts.length) {
|
|
@@ -125,7 +126,7 @@ export default async function pluginContentBlog(
|
|
|
125
126
|
blogPosts: [],
|
|
126
127
|
blogListPaginated: [],
|
|
127
128
|
blogTags: {},
|
|
128
|
-
blogTagsListPath
|
|
129
|
+
blogTagsListPath,
|
|
129
130
|
blogTagsPaginated: [],
|
|
130
131
|
};
|
|
131
132
|
}
|
|
@@ -150,8 +151,6 @@ export default async function pluginContentBlog(
|
|
|
150
151
|
}
|
|
151
152
|
});
|
|
152
153
|
|
|
153
|
-
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
|
|
154
|
-
|
|
155
154
|
const blogListPaginated: BlogPaginated[] = paginateBlogPosts({
|
|
156
155
|
blogPosts,
|
|
157
156
|
blogTitle,
|
|
@@ -167,11 +166,6 @@ export default async function pluginContentBlog(
|
|
|
167
166
|
blogTitle,
|
|
168
167
|
});
|
|
169
168
|
|
|
170
|
-
const tagsPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
|
|
171
|
-
|
|
172
|
-
const blogTagsListPath =
|
|
173
|
-
Object.keys(blogTags).length > 0 ? tagsPath : null;
|
|
174
|
-
|
|
175
169
|
return {
|
|
176
170
|
blogSidebarTitle,
|
|
177
171
|
blogPosts,
|
|
@@ -218,7 +212,7 @@ export default async function pluginContentBlog(
|
|
|
218
212
|
routeBasePath,
|
|
219
213
|
archiveBasePath,
|
|
220
214
|
]);
|
|
221
|
-
//
|
|
215
|
+
// Create a blog archive route
|
|
222
216
|
const archiveProp = await createData(
|
|
223
217
|
`${docuHash(archiveUrl)}.json`,
|
|
224
218
|
JSON.stringify({blogPosts}, null, 2),
|
|
@@ -292,49 +286,62 @@ export default async function pluginContentBlog(
|
|
|
292
286
|
exact: true,
|
|
293
287
|
modules: {
|
|
294
288
|
sidebar: aliasedSource(sidebarProp),
|
|
295
|
-
items: items.map((postID) =>
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
path: blogItemsToMetadata[postID]!.source,
|
|
302
|
-
query: {
|
|
303
|
-
truncated: true,
|
|
304
|
-
},
|
|
289
|
+
items: items.map((postID) => ({
|
|
290
|
+
content: {
|
|
291
|
+
__import: true,
|
|
292
|
+
path: blogItemsToMetadata[postID]!.source,
|
|
293
|
+
query: {
|
|
294
|
+
truncated: true,
|
|
305
295
|
},
|
|
306
|
-
}
|
|
307
|
-
),
|
|
296
|
+
},
|
|
297
|
+
})),
|
|
308
298
|
metadata: aliasedSource(pageMetadataPath),
|
|
309
299
|
},
|
|
310
300
|
});
|
|
311
301
|
}),
|
|
312
302
|
);
|
|
313
303
|
|
|
314
|
-
// Tags.
|
|
315
|
-
if (
|
|
304
|
+
// Tags. This is the last part so we early-return if there are no tags.
|
|
305
|
+
if (Object.keys(blogTags).length === 0) {
|
|
316
306
|
return;
|
|
317
307
|
}
|
|
318
308
|
|
|
319
|
-
|
|
320
|
-
Object.
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
309
|
+
async function createTagsListPage() {
|
|
310
|
+
const tagsProp: TagsListItem[] = Object.values(blogTags).map((tag) => ({
|
|
311
|
+
label: tag.label,
|
|
312
|
+
permalink: tag.permalink,
|
|
313
|
+
count: tag.items.length,
|
|
314
|
+
}));
|
|
315
|
+
|
|
316
|
+
const tagsPropPath = await createData(
|
|
317
|
+
`${docuHash(`${blogTagsListPath}-tags`)}.json`,
|
|
318
|
+
JSON.stringify(tagsProp, null, 2),
|
|
319
|
+
);
|
|
330
320
|
|
|
331
|
-
|
|
321
|
+
addRoute({
|
|
322
|
+
path: blogTagsListPath,
|
|
323
|
+
component: blogTagsListComponent,
|
|
324
|
+
exact: true,
|
|
325
|
+
modules: {
|
|
326
|
+
sidebar: aliasedSource(sidebarProp),
|
|
327
|
+
tags: aliasedSource(tagsPropPath),
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async function createTagPostsListPage(tag: BlogTag): Promise<void> {
|
|
332
333
|
await Promise.all(
|
|
333
334
|
tag.pages.map(async (blogPaginated) => {
|
|
334
335
|
const {metadata, items} = blogPaginated;
|
|
335
|
-
const
|
|
336
|
+
const tagProp: TagModule = {
|
|
337
|
+
label: tag.label,
|
|
338
|
+
permalink: tag.permalink,
|
|
339
|
+
allTagsPath: blogTagsListPath,
|
|
340
|
+
count: tag.items.length,
|
|
341
|
+
};
|
|
342
|
+
const tagPropPath = await createData(
|
|
336
343
|
`${docuHash(metadata.permalink)}.json`,
|
|
337
|
-
JSON.stringify(
|
|
344
|
+
JSON.stringify(tagProp, null, 2),
|
|
338
345
|
);
|
|
339
346
|
|
|
340
347
|
const listMetadataPath = await createData(
|
|
@@ -360,7 +367,7 @@ export default async function pluginContentBlog(
|
|
|
360
367
|
},
|
|
361
368
|
};
|
|
362
369
|
}),
|
|
363
|
-
|
|
370
|
+
tag: aliasedSource(tagPropPath),
|
|
364
371
|
listMetadata: aliasedSource(listMetadataPath),
|
|
365
372
|
},
|
|
366
373
|
});
|
|
@@ -368,25 +375,8 @@ export default async function pluginContentBlog(
|
|
|
368
375
|
);
|
|
369
376
|
}
|
|
370
377
|
|
|
371
|
-
await
|
|
372
|
-
|
|
373
|
-
// Only create /tags page if there are tags.
|
|
374
|
-
if (Object.keys(blogTags).length > 0) {
|
|
375
|
-
const tagsListPath = await createData(
|
|
376
|
-
`${docuHash(`${blogTagsListPath}-tags`)}.json`,
|
|
377
|
-
JSON.stringify(tagsModule, null, 2),
|
|
378
|
-
);
|
|
379
|
-
|
|
380
|
-
addRoute({
|
|
381
|
-
path: blogTagsListPath,
|
|
382
|
-
component: blogTagsListComponent,
|
|
383
|
-
exact: true,
|
|
384
|
-
modules: {
|
|
385
|
-
sidebar: aliasedSource(sidebarProp),
|
|
386
|
-
tags: aliasedSource(tagsListPath),
|
|
387
|
-
},
|
|
388
|
-
});
|
|
389
|
-
}
|
|
378
|
+
await createTagsListPage();
|
|
379
|
+
await Promise.all(Object.values(blogTags).map(createTagPostsListPage));
|
|
390
380
|
},
|
|
391
381
|
|
|
392
382
|
translateContent({content, translationFiles}) {
|
|
@@ -439,7 +429,10 @@ export default async function pluginContentBlog(
|
|
|
439
429
|
options: {
|
|
440
430
|
remarkPlugins,
|
|
441
431
|
rehypePlugins,
|
|
442
|
-
beforeDefaultRemarkPlugins
|
|
432
|
+
beforeDefaultRemarkPlugins: [
|
|
433
|
+
footnoteIDFixer,
|
|
434
|
+
...beforeDefaultRemarkPlugins,
|
|
435
|
+
],
|
|
443
436
|
beforeDefaultRehypePlugins,
|
|
444
437
|
staticDirs: siteConfig.staticDirectories.map((dir) =>
|
|
445
438
|
path.resolve(siteDir, dir),
|
package/src/options.ts
CHANGED
|
@@ -111,7 +111,7 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
|
|
|
111
111
|
.default(DEFAULT_OPTIONS.feedOptions.type),
|
|
112
112
|
title: Joi.string().allow(''),
|
|
113
113
|
description: Joi.string().allow(''),
|
|
114
|
-
//
|
|
114
|
+
// Only add default value when user actually wants a feed (type is not null)
|
|
115
115
|
copyright: Joi.when('type', {
|
|
116
116
|
is: Joi.any().valid(null),
|
|
117
117
|
then: Joi.string().optional(),
|
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
declare module '@docusaurus/plugin-content-blog' {
|
|
9
|
-
import type {
|
|
10
|
-
import type {FrontMatterTag} from '@docusaurus/utils';
|
|
9
|
+
import type {MDXOptions} from '@docusaurus/mdx-loader';
|
|
10
|
+
import type {FrontMatterTag, Tag} from '@docusaurus/utils';
|
|
11
|
+
import type {Plugin, LoadContext} from '@docusaurus/types';
|
|
11
12
|
import type {Overwrite} from 'utility-types';
|
|
12
13
|
|
|
13
|
-
export
|
|
14
|
+
export type Assets = {
|
|
14
15
|
/**
|
|
15
16
|
* If `metadata.image` is a collocated image path, this entry will be the
|
|
16
17
|
* bundler-generated image path. Otherwise, it's empty, and the image URL
|
|
@@ -25,13 +26,9 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
25
26
|
* should be accessed through `authors.imageURL`.
|
|
26
27
|
*/
|
|
27
28
|
authorsImageUrls: (string | undefined)[];
|
|
28
|
-
}
|
|
29
|
+
};
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
* Unknown keys are allowed, so that we can pass custom fields to authors,
|
|
32
|
-
* e.g., `twitter`.
|
|
33
|
-
*/
|
|
34
|
-
export interface Author extends Record<string, unknown> {
|
|
31
|
+
export type Author = {
|
|
35
32
|
/**
|
|
36
33
|
* If `name` doesn't exist, an `imageURL` is expected.
|
|
37
34
|
*/
|
|
@@ -55,7 +52,12 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
55
52
|
* to generate a fallback `mailto:` URL.
|
|
56
53
|
*/
|
|
57
54
|
email?: string;
|
|
58
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Unknown keys are allowed, so that we can pass custom fields to authors,
|
|
57
|
+
* e.g., `twitter`.
|
|
58
|
+
*/
|
|
59
|
+
[key: string]: unknown;
|
|
60
|
+
};
|
|
59
61
|
|
|
60
62
|
/**
|
|
61
63
|
* Everything is partial/unnormalized, because front matter is always
|
|
@@ -81,10 +83,7 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
81
83
|
* @see {@link BlogPostMetadata.tags}
|
|
82
84
|
*/
|
|
83
85
|
tags?: FrontMatterTag[];
|
|
84
|
-
/**
|
|
85
|
-
* Custom slug appended after /<baseUrl>/<routeBasePath>/
|
|
86
|
-
* @see {@link BlogPostMetadata.slug}
|
|
87
|
-
*/
|
|
86
|
+
/** Custom slug appended after `/<baseUrl>/<routeBasePath>/` */
|
|
88
87
|
slug?: string;
|
|
89
88
|
/**
|
|
90
89
|
* Marks the post as draft and excludes it from the production build.
|
|
@@ -130,25 +129,18 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
130
129
|
/** @deprecated v1 legacy */
|
|
131
130
|
authorImageURL?: string;
|
|
132
131
|
|
|
133
|
-
/**
|
|
134
|
-
* @see {@link BlogPostMetadata.image}
|
|
135
|
-
*/
|
|
132
|
+
/** Used in the head meta. Should use `assets.image` in priority. */
|
|
136
133
|
image?: string;
|
|
137
|
-
/**
|
|
138
|
-
* Used in the head meta
|
|
139
|
-
*/
|
|
134
|
+
/** Used in the head meta. */
|
|
140
135
|
keywords?: string[];
|
|
141
|
-
/**
|
|
142
|
-
* Hide the right TOC
|
|
143
|
-
*/
|
|
136
|
+
/** Hide the right TOC. */
|
|
144
137
|
hide_table_of_contents?: boolean;
|
|
145
138
|
/**
|
|
146
|
-
* Minimum TOC heading level
|
|
139
|
+
* Minimum TOC heading level. Must be between 2 and 6 and lower or equal to
|
|
140
|
+
* the max value.
|
|
147
141
|
*/
|
|
148
142
|
toc_min_heading_level?: number;
|
|
149
|
-
/**
|
|
150
|
-
* Maximum TOC heading level
|
|
151
|
-
*/
|
|
143
|
+
/** Maximum TOC heading level. Must be between 2 and 6. */
|
|
152
144
|
toc_max_heading_level?: number;
|
|
153
145
|
};
|
|
154
146
|
|
|
@@ -175,9 +167,7 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
175
167
|
| (string | BlogPostFrontMatterAuthor)[];
|
|
176
168
|
|
|
177
169
|
export type BlogPostMetadata = {
|
|
178
|
-
/**
|
|
179
|
-
* Path to the Markdown source, with `@site` alias.
|
|
180
|
-
*/
|
|
170
|
+
/** Path to the Markdown source, with `@site` alias. */
|
|
181
171
|
readonly source: string;
|
|
182
172
|
/**
|
|
183
173
|
* Used to generate the page h1 heading, tab title, and pagination title.
|
|
@@ -193,9 +183,7 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
193
183
|
* render the date regardless of the existence of `Intl.DateTimeFormat`.
|
|
194
184
|
*/
|
|
195
185
|
readonly formattedDate: string;
|
|
196
|
-
/**
|
|
197
|
-
* Full link including base URL.
|
|
198
|
-
*/
|
|
186
|
+
/** Full link including base URL. */
|
|
199
187
|
readonly permalink: string;
|
|
200
188
|
/**
|
|
201
189
|
* Description used in the meta. Could be an empty string (empty content)
|
|
@@ -229,17 +217,10 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
229
217
|
* `assets.authorsImageUrls` on client side.
|
|
230
218
|
*/
|
|
231
219
|
readonly authors: Author[];
|
|
232
|
-
/**
|
|
233
|
-
* Front matter, as-is.
|
|
234
|
-
*/
|
|
220
|
+
/** Front matter, as-is. */
|
|
235
221
|
readonly frontMatter: BlogPostFrontMatter & {[key: string]: unknown};
|
|
236
|
-
/**
|
|
237
|
-
|
|
238
|
-
*/
|
|
239
|
-
readonly tags: readonly {
|
|
240
|
-
readonly label: string;
|
|
241
|
-
readonly permalink: string;
|
|
242
|
-
}[];
|
|
222
|
+
/** Tags, normalized. */
|
|
223
|
+
readonly tags: Tag[];
|
|
243
224
|
};
|
|
244
225
|
/**
|
|
245
226
|
* @returns The edit URL that's directly plugged into metadata.
|
|
@@ -250,17 +231,11 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
250
231
|
* site path. Usually the same as `options.path` but can be localized
|
|
251
232
|
*/
|
|
252
233
|
blogDirPath: string;
|
|
253
|
-
/**
|
|
254
|
-
* Path to this post file, relative to `blogDirPath`
|
|
255
|
-
*/
|
|
234
|
+
/** Path to this post file, relative to `blogDirPath`. */
|
|
256
235
|
blogPath: string;
|
|
257
|
-
/**
|
|
258
|
-
* @see {@link BlogPostMetadata.permalink}
|
|
259
|
-
*/
|
|
236
|
+
/** @see {@link BlogPostMetadata.permalink} */
|
|
260
237
|
permalink: string;
|
|
261
|
-
/**
|
|
262
|
-
* Locale name.
|
|
263
|
-
*/
|
|
238
|
+
/** Locale name. */
|
|
264
239
|
locale: string;
|
|
265
240
|
}) => string | undefined;
|
|
266
241
|
|
|
@@ -325,7 +300,7 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
325
300
|
/**
|
|
326
301
|
* Plugin options after normalization.
|
|
327
302
|
*/
|
|
328
|
-
export type PluginOptions =
|
|
303
|
+
export type PluginOptions = MDXOptions & {
|
|
329
304
|
/** Plugin ID. */
|
|
330
305
|
id?: string;
|
|
331
306
|
/**
|
|
@@ -432,25 +407,70 @@ declare module '@docusaurus/plugin-content-blog' {
|
|
|
432
407
|
}
|
|
433
408
|
>;
|
|
434
409
|
|
|
435
|
-
export type TagModule = {
|
|
436
|
-
/** Permalink of the tag's own page. */
|
|
437
|
-
permalink: string;
|
|
438
|
-
/** Name of the tag. */
|
|
439
|
-
name: string;
|
|
440
|
-
/** Number of posts with this tag. */
|
|
441
|
-
count: number;
|
|
442
|
-
/** The tags list page. */
|
|
443
|
-
allTagsPath: string;
|
|
444
|
-
};
|
|
445
|
-
|
|
446
410
|
export type BlogSidebar = {
|
|
447
411
|
title: string;
|
|
448
412
|
items: {title: string; permalink: string}[];
|
|
449
413
|
};
|
|
414
|
+
|
|
415
|
+
export type BlogContent = {
|
|
416
|
+
blogSidebarTitle: string;
|
|
417
|
+
blogPosts: BlogPost[];
|
|
418
|
+
blogListPaginated: BlogPaginated[];
|
|
419
|
+
blogTags: BlogTags;
|
|
420
|
+
blogTagsListPath: string;
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
export type BlogTags = {
|
|
424
|
+
[permalink: string]: BlogTag;
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
export type BlogTag = Tag & {
|
|
428
|
+
/** Blog post permalinks. */
|
|
429
|
+
items: string[];
|
|
430
|
+
pages: BlogPaginated[];
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
export type BlogPost = {
|
|
434
|
+
id: string;
|
|
435
|
+
metadata: BlogPostMetadata;
|
|
436
|
+
content: string;
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
export type BlogPaginatedMetadata = {
|
|
440
|
+
/** Title of the entire blog. */
|
|
441
|
+
readonly blogTitle: string;
|
|
442
|
+
/** Blog description. */
|
|
443
|
+
readonly blogDescription: string;
|
|
444
|
+
/** Permalink to the next list page. */
|
|
445
|
+
readonly nextPage?: string;
|
|
446
|
+
/** Permalink of the current page. */
|
|
447
|
+
readonly permalink: string;
|
|
448
|
+
/** Permalink to the previous list page. */
|
|
449
|
+
readonly previousPage?: string;
|
|
450
|
+
/** Index of the current page, 1-based. */
|
|
451
|
+
readonly page: number;
|
|
452
|
+
/** Posts displayed on each list page. */
|
|
453
|
+
readonly postsPerPage: number;
|
|
454
|
+
/** Total number of posts in the entire blog. */
|
|
455
|
+
readonly totalCount: number;
|
|
456
|
+
/** Total number of list pages. */
|
|
457
|
+
readonly totalPages: number;
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
export type BlogPaginated = {
|
|
461
|
+
metadata: BlogPaginatedMetadata;
|
|
462
|
+
/** Blog post permalinks. */
|
|
463
|
+
items: string[];
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
export default function pluginContentBlog(
|
|
467
|
+
context: LoadContext,
|
|
468
|
+
options: PluginOptions,
|
|
469
|
+
): Promise<Plugin<BlogContent>>;
|
|
450
470
|
}
|
|
451
471
|
|
|
452
472
|
declare module '@theme/BlogPostPage' {
|
|
453
|
-
import type {
|
|
473
|
+
import type {LoadedMDXContent} from '@docusaurus/mdx-loader';
|
|
454
474
|
import type {
|
|
455
475
|
BlogPostFrontMatter,
|
|
456
476
|
BlogPostMetadata,
|
|
@@ -469,24 +489,7 @@ declare module '@theme/BlogPostPage' {
|
|
|
469
489
|
}
|
|
470
490
|
>;
|
|
471
491
|
|
|
472
|
-
export type Content =
|
|
473
|
-
// TODO remove this. `metadata.frontMatter` is preferred because it can be
|
|
474
|
-
// accessed in enhanced plugins
|
|
475
|
-
/** Same as `metadata.frontMatter` */
|
|
476
|
-
readonly frontMatter: FrontMatter;
|
|
477
|
-
/**
|
|
478
|
-
* Usually image assets that may be collocated like `./img/thumbnail.png`.
|
|
479
|
-
* The loader would also bundle these assets and the client should use these
|
|
480
|
-
* in priority.
|
|
481
|
-
*/
|
|
482
|
-
readonly assets: Assets;
|
|
483
|
-
/** Metadata of the post. */
|
|
484
|
-
readonly metadata: Metadata;
|
|
485
|
-
/** A list of TOC items (headings). */
|
|
486
|
-
readonly toc: readonly TOCItem[];
|
|
487
|
-
/** Renders the actual MDX content. */
|
|
488
|
-
(): JSX.Element;
|
|
489
|
-
};
|
|
492
|
+
export type Content = LoadedMDXContent<FrontMatter, Metadata, Assets>;
|
|
490
493
|
|
|
491
494
|
export interface Props {
|
|
492
495
|
/** Blog sidebar. */
|
|
@@ -500,34 +503,16 @@ declare module '@theme/BlogPostPage' {
|
|
|
500
503
|
|
|
501
504
|
declare module '@theme/BlogListPage' {
|
|
502
505
|
import type {Content} from '@theme/BlogPostPage';
|
|
503
|
-
import type {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
readonly blogTitle: string;
|
|
508
|
-
/** Blog description. */
|
|
509
|
-
readonly blogDescription: string;
|
|
510
|
-
/** Permalink to the next list page. */
|
|
511
|
-
readonly nextPage?: string;
|
|
512
|
-
/** Permalink of the current page. */
|
|
513
|
-
readonly permalink: string;
|
|
514
|
-
/** Permalink to the previous list page. */
|
|
515
|
-
readonly previousPage?: string;
|
|
516
|
-
/** Index of the current page, 1-based. */
|
|
517
|
-
readonly page: number;
|
|
518
|
-
/** Posts displayed on each list page. */
|
|
519
|
-
readonly postsPerPage: number;
|
|
520
|
-
/** Total number of posts in the entire blog. */
|
|
521
|
-
readonly totalCount: number;
|
|
522
|
-
/** Total number of list pages. */
|
|
523
|
-
readonly totalPages: number;
|
|
524
|
-
};
|
|
506
|
+
import type {
|
|
507
|
+
BlogSidebar,
|
|
508
|
+
BlogPaginatedMetadata,
|
|
509
|
+
} from '@docusaurus/plugin-content-blog';
|
|
525
510
|
|
|
526
511
|
export interface Props {
|
|
527
512
|
/** Blog sidebar. */
|
|
528
513
|
readonly sidebar: BlogSidebar;
|
|
529
514
|
/** Metadata of the current listing page. */
|
|
530
|
-
readonly metadata:
|
|
515
|
+
readonly metadata: BlogPaginatedMetadata;
|
|
531
516
|
/**
|
|
532
517
|
* Array of blog posts included on this page. Every post's metadata is also
|
|
533
518
|
* available.
|
|
@@ -539,30 +524,34 @@ declare module '@theme/BlogListPage' {
|
|
|
539
524
|
}
|
|
540
525
|
|
|
541
526
|
declare module '@theme/BlogTagsListPage' {
|
|
542
|
-
import type {BlogSidebar
|
|
527
|
+
import type {BlogSidebar} from '@docusaurus/plugin-content-blog';
|
|
528
|
+
import type {TagsListItem} from '@docusaurus/utils';
|
|
543
529
|
|
|
544
530
|
export interface Props {
|
|
545
531
|
/** Blog sidebar. */
|
|
546
532
|
readonly sidebar: BlogSidebar;
|
|
547
|
-
/**
|
|
548
|
-
readonly tags:
|
|
533
|
+
/** All tags declared in this blog. */
|
|
534
|
+
readonly tags: TagsListItem[];
|
|
549
535
|
}
|
|
550
536
|
|
|
551
537
|
export default function BlogTagsListPage(props: Props): JSX.Element;
|
|
552
538
|
}
|
|
553
539
|
|
|
554
540
|
declare module '@theme/BlogTagsPostsPage' {
|
|
555
|
-
import type {
|
|
541
|
+
import type {
|
|
542
|
+
BlogSidebar,
|
|
543
|
+
BlogPaginatedMetadata,
|
|
544
|
+
} from '@docusaurus/plugin-content-blog';
|
|
556
545
|
import type {Content} from '@theme/BlogPostPage';
|
|
557
|
-
import type {
|
|
546
|
+
import type {TagModule} from '@docusaurus/utils';
|
|
558
547
|
|
|
559
548
|
export interface Props {
|
|
560
549
|
/** Blog sidebar. */
|
|
561
550
|
readonly sidebar: BlogSidebar;
|
|
562
551
|
/** Metadata of this tag. */
|
|
563
|
-
readonly
|
|
552
|
+
readonly tag: TagModule;
|
|
564
553
|
/** Looks exactly the same as the posts list page */
|
|
565
|
-
readonly listMetadata:
|
|
554
|
+
readonly listMetadata: BlogPaginatedMetadata;
|
|
566
555
|
/**
|
|
567
556
|
* Array of blog posts included on this page. Every post's metadata is also
|
|
568
557
|
* available.
|
|
@@ -0,0 +1,29 @@
|
|
|
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 visit from 'unist-util-visit';
|
|
9
|
+
import {simpleHash} from '@docusaurus/utils';
|
|
10
|
+
import type {Transformer} from 'unified';
|
|
11
|
+
import type {FootnoteReference, FootnoteDefinition} from 'mdast';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* In the blog list view, each post will be compiled separately. However, they
|
|
15
|
+
* may use the same footnote IDs. This leads to duplicated DOM IDs and inability
|
|
16
|
+
* to navigate to footnote references. This plugin fixes it by appending a
|
|
17
|
+
* unique hash to each reference/definition.
|
|
18
|
+
*/
|
|
19
|
+
export default function plugin(): Transformer {
|
|
20
|
+
return (root, vfile) => {
|
|
21
|
+
const suffix = `-${simpleHash(vfile.path!, 6)}`;
|
|
22
|
+
visit(root, 'footnoteReference', (node: FootnoteReference) => {
|
|
23
|
+
node.identifier += suffix;
|
|
24
|
+
});
|
|
25
|
+
visit(root, 'footnoteDefinition', (node: FootnoteDefinition) => {
|
|
26
|
+
node.identifier += suffix;
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}
|
package/src/translations.ts
CHANGED
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {
|
|
9
|
-
import type {
|
|
10
|
-
|
|
8
|
+
import type {TranslationFileContent, TranslationFile} from '@docusaurus/types';
|
|
9
|
+
import type {
|
|
10
|
+
PluginOptions,
|
|
11
|
+
BlogContent,
|
|
12
|
+
BlogPaginated,
|
|
13
|
+
} from '@docusaurus/plugin-content-blog';
|
|
11
14
|
|
|
12
15
|
function translateListPage(
|
|
13
16
|
blogListPaginated: BlogPaginated[],
|
|
@@ -27,7 +30,7 @@ function translateListPage(
|
|
|
27
30
|
});
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
export function getTranslationFiles(options: PluginOptions):
|
|
33
|
+
export function getTranslationFiles(options: PluginOptions): TranslationFile[] {
|
|
31
34
|
return [
|
|
32
35
|
{
|
|
33
36
|
path: 'options',
|
|
@@ -51,7 +54,7 @@ export function getTranslationFiles(options: PluginOptions): TranslationFiles {
|
|
|
51
54
|
|
|
52
55
|
export function translateContent(
|
|
53
56
|
content: BlogContent,
|
|
54
|
-
translationFiles:
|
|
57
|
+
translationFiles: TranslationFile[],
|
|
55
58
|
): BlogContent {
|
|
56
59
|
const {content: optionsTranslations} = translationFiles[0]!;
|
|
57
60
|
return {
|
package/src/types.ts
CHANGED
|
@@ -6,45 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type {BrokenMarkdownLink, ContentPaths} from '@docusaurus/utils';
|
|
9
|
-
import type {BlogPostMetadata} from '@docusaurus/plugin-content-blog';
|
|
10
|
-
import type {Metadata as BlogPaginatedMetadata} from '@theme/BlogListPage';
|
|
11
9
|
|
|
12
10
|
export type BlogContentPaths = ContentPaths;
|
|
13
11
|
|
|
14
|
-
export interface BlogContent {
|
|
15
|
-
blogSidebarTitle: string;
|
|
16
|
-
blogPosts: BlogPost[];
|
|
17
|
-
blogListPaginated: BlogPaginated[];
|
|
18
|
-
blogTags: BlogTags;
|
|
19
|
-
blogTagsListPath: string | null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface BlogTags {
|
|
23
|
-
// TODO, the key is the tag slug/permalink
|
|
24
|
-
// This is due to legacy frontmatter: tags:
|
|
25
|
-
// [{label: "xyz", permalink: "/1"}, {label: "xyz", permalink: "/2"}]
|
|
26
|
-
// Soon we should forbid declaring permalink through frontmatter
|
|
27
|
-
[tagKey: string]: BlogTag;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface BlogTag {
|
|
31
|
-
name: string;
|
|
32
|
-
items: string[]; // blog post permalinks
|
|
33
|
-
permalink: string;
|
|
34
|
-
pages: BlogPaginated[];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface BlogPost {
|
|
38
|
-
id: string;
|
|
39
|
-
metadata: BlogPostMetadata;
|
|
40
|
-
content: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface BlogPaginated {
|
|
44
|
-
metadata: BlogPaginatedMetadata;
|
|
45
|
-
items: string[]; // blog post permalinks
|
|
46
|
-
}
|
|
47
|
-
|
|
48
12
|
export type BlogBrokenMarkdownLink = BrokenMarkdownLink<BlogContentPaths>;
|
|
49
13
|
export type BlogMarkdownLoaderOptions = {
|
|
50
14
|
siteDir: string;
|