@docusaurus/plugin-content-blog 2.0.0-beta.13 → 2.0.0-beta.15
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 +5 -8
- package/lib/authors.js +15 -53
- package/lib/blogFrontMatter.d.ts +1 -34
- package/lib/blogFrontMatter.js +3 -3
- package/lib/blogUtils.d.ts +3 -3
- package/lib/blogUtils.js +28 -22
- package/lib/feed.d.ts +3 -8
- package/lib/feed.js +46 -35
- package/lib/index.d.ts +4 -3
- package/lib/index.js +33 -28
- package/lib/markdownLoader.d.ts +1 -1
- package/lib/markdownLoader.js +1 -2
- package/lib/pluginOptionSchema.d.ts +1 -1
- package/lib/pluginOptionSchema.js +5 -3
- package/lib/translations.d.ts +2 -1
- package/lib/translations.js +3 -3
- package/lib/types.d.ts +2 -79
- package/package.json +11 -12
- package/src/authors.ts +22 -69
- package/src/blogFrontMatter.ts +5 -50
- package/src/blogUtils.ts +38 -34
- package/src/feed.ts +80 -37
- package/src/index.ts +48 -44
- package/src/markdownLoader.ts +2 -3
- package/src/plugin-content-blog.d.ts +147 -4
- package/src/pluginOptionSchema.ts +7 -5
- package/src/translations.ts +5 -4
- package/src/types.ts +5 -97
- package/lib/.tsbuildinfo +0 -1
- package/src/__tests__/__fixtures__/authorsMapFiles/authors.json +0 -29
- package/src/__tests__/__fixtures__/authorsMapFiles/authors.yml +0 -27
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.json +0 -5
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.yml +0 -3
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.json +0 -3
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.yml +0 -2
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.json +0 -8
- package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.yml +0 -3
- package/src/__tests__/__fixtures__/component/Typography.tsx +0 -6
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathEmpty/empty +0 -0
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson1/authors.json +0 -0
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson2/authors.json +0 -0
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathNestedYml/sub/folder/authors.yml +0 -0
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml1/authors.yml +0 -0
- package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml2/authors.yml +0 -0
- package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -8
- package/src/__tests__/__fixtures__/website/blog/_partials/somePartial.md +0 -3
- package/src/__tests__/__fixtures__/website/blog/_partials/subfolder/somePartial.md +0 -3
- package/src/__tests__/__fixtures__/website/blog/_somePartial.md +0 -3
- package/src/__tests__/__fixtures__/website/blog/authors.yml +0 -4
- 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/mdx-blog-post.mdx +0 -36
- package/src/__tests__/__fixtures__/website/blog/mdx-require-blog-post.mdx +0 -14
- package/src/__tests__/__fixtures__/website/blog/simple-slug.md +0 -11
- 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 -8
- package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/authors.yml +0 -5
- package/src/__tests__/__fixtures__/website/static/img/docusaurus.png +0 -0
- package/src/__tests__/__fixtures__/website-blog-without-date/blog/no date.md +0 -1
- package/src/__tests__/__snapshots__/feed.test.ts.snap +0 -164
- package/src/__tests__/__snapshots__/linkify.test.ts.snap +0 -24
- package/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap +0 -5
- package/src/__tests__/__snapshots__/translations.test.ts.snap +0 -64
- package/src/__tests__/authors.test.ts +0 -608
- package/src/__tests__/blogFrontMatter.test.ts +0 -394
- package/src/__tests__/blogUtils.test.ts +0 -94
- package/src/__tests__/feed.test.ts +0 -126
- package/src/__tests__/index.test.ts +0 -408
- package/src/__tests__/linkify.test.ts +0 -93
- package/src/__tests__/pluginOptionSchema.test.ts +0 -150
- package/src/__tests__/translations.test.ts +0 -92
- package/tsconfig.json +0 -9
package/src/feed.ts
CHANGED
|
@@ -5,34 +5,35 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {Feed, Author as FeedAuthor} from 'feed';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
8
|
+
import {Feed, type Author as FeedAuthor, type Item as FeedItem} from 'feed';
|
|
9
|
+
import type {BlogPost} from './types';
|
|
10
|
+
import {
|
|
11
|
+
normalizeUrl,
|
|
12
|
+
posixPath,
|
|
13
|
+
mapAsyncSequential,
|
|
14
|
+
readOutputHTMLFile,
|
|
15
|
+
} from '@docusaurus/utils';
|
|
16
|
+
import cheerio from 'cheerio';
|
|
17
|
+
import type {DocusaurusConfig} from '@docusaurus/types';
|
|
12
18
|
import path from 'path';
|
|
13
19
|
import fs from 'fs-extra';
|
|
20
|
+
import type {
|
|
21
|
+
FeedType,
|
|
22
|
+
PluginOptions,
|
|
23
|
+
Author,
|
|
24
|
+
} from '@docusaurus/plugin-content-blog';
|
|
25
|
+
import {blogPostContainerID} from '@docusaurus/utils-common';
|
|
14
26
|
|
|
15
|
-
|
|
16
|
-
// It's hard to convert reliably JSX/require calls to an html feed content
|
|
17
|
-
// See https://github.com/facebook/docusaurus/issues/5664
|
|
18
|
-
function mdxToFeedContent(mdxContent: string): string | undefined {
|
|
19
|
-
try {
|
|
20
|
-
return mdxToHtml(mdxContent);
|
|
21
|
-
} catch (e) {
|
|
22
|
-
// TODO will we need a plugin option to configure how to handle such an error
|
|
23
|
-
// Swallow the error on purpose for now, until we understand better the problem space
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export async function generateBlogFeed({
|
|
27
|
+
async function generateBlogFeed({
|
|
29
28
|
blogPosts,
|
|
30
29
|
options,
|
|
31
30
|
siteConfig,
|
|
31
|
+
outDir,
|
|
32
32
|
}: {
|
|
33
33
|
blogPosts: BlogPost[];
|
|
34
34
|
options: PluginOptions;
|
|
35
35
|
siteConfig: DocusaurusConfig;
|
|
36
|
+
outDir: string;
|
|
36
37
|
}): Promise<Feed | null> {
|
|
37
38
|
if (!blogPosts.length) {
|
|
38
39
|
return null;
|
|
@@ -42,9 +43,7 @@ export async function generateBlogFeed({
|
|
|
42
43
|
const {url: siteUrl, baseUrl, title, favicon} = siteConfig;
|
|
43
44
|
const blogBaseUrl = normalizeUrl([siteUrl, baseUrl, routeBasePath]);
|
|
44
45
|
|
|
45
|
-
const updated =
|
|
46
|
-
(blogPosts[0] && blogPosts[0].metadata.date) ||
|
|
47
|
-
new Date('2015-10-25T16:29:00.000-07:00'); // weird legacy magic date
|
|
46
|
+
const updated = blogPosts[0] && blogPosts[0].metadata.date;
|
|
48
47
|
|
|
49
48
|
const feed = new Feed({
|
|
50
49
|
id: blogBaseUrl,
|
|
@@ -63,20 +62,45 @@ export async function generateBlogFeed({
|
|
|
63
62
|
return {name: author.name, link: author.url};
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
blogPosts
|
|
65
|
+
await mapAsyncSequential(blogPosts, async (post) => {
|
|
67
66
|
const {
|
|
68
67
|
id,
|
|
69
|
-
metadata: {
|
|
68
|
+
metadata: {
|
|
69
|
+
title: metadataTitle,
|
|
70
|
+
permalink,
|
|
71
|
+
date,
|
|
72
|
+
description,
|
|
73
|
+
authors,
|
|
74
|
+
tags,
|
|
75
|
+
},
|
|
70
76
|
} = post;
|
|
71
|
-
|
|
77
|
+
|
|
78
|
+
const content = await readOutputHTMLFile(
|
|
79
|
+
permalink.replace(siteConfig.baseUrl, ''),
|
|
80
|
+
outDir,
|
|
81
|
+
siteConfig.trailingSlash,
|
|
82
|
+
);
|
|
83
|
+
const $ = cheerio.load(content);
|
|
84
|
+
|
|
85
|
+
const feedItem: FeedItem = {
|
|
72
86
|
title: metadataTitle,
|
|
73
87
|
id,
|
|
74
88
|
link: normalizeUrl([siteUrl, permalink]),
|
|
75
89
|
date,
|
|
76
90
|
description,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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);
|
|
80
104
|
});
|
|
81
105
|
|
|
82
106
|
return feed;
|
|
@@ -85,15 +109,29 @@ export async function generateBlogFeed({
|
|
|
85
109
|
async function createBlogFeedFile({
|
|
86
110
|
feed,
|
|
87
111
|
feedType,
|
|
88
|
-
|
|
112
|
+
generatePath,
|
|
89
113
|
}: {
|
|
90
114
|
feed: Feed;
|
|
91
115
|
feedType: FeedType;
|
|
92
|
-
|
|
116
|
+
generatePath: string;
|
|
93
117
|
}) {
|
|
94
|
-
const feedContent =
|
|
118
|
+
const [feedContent, feedPath] = (() => {
|
|
119
|
+
switch (feedType) {
|
|
120
|
+
case 'rss':
|
|
121
|
+
return [feed.rss2(), 'rss.xml'];
|
|
122
|
+
case 'json':
|
|
123
|
+
return [feed.json1(), 'feed.json'];
|
|
124
|
+
case 'atom':
|
|
125
|
+
return [feed.atom1(), 'atom.xml'];
|
|
126
|
+
default:
|
|
127
|
+
throw new Error(`Feed type ${feedType} not supported.`);
|
|
128
|
+
}
|
|
129
|
+
})();
|
|
95
130
|
try {
|
|
96
|
-
await fs.outputFile(
|
|
131
|
+
await fs.outputFile(
|
|
132
|
+
posixPath(path.join(generatePath, feedPath)),
|
|
133
|
+
feedContent,
|
|
134
|
+
);
|
|
97
135
|
} catch (err) {
|
|
98
136
|
throw new Error(`Generating ${feedType} feed failed: ${err}.`);
|
|
99
137
|
}
|
|
@@ -110,7 +148,12 @@ export async function createBlogFeedFiles({
|
|
|
110
148
|
siteConfig: DocusaurusConfig;
|
|
111
149
|
outDir: string;
|
|
112
150
|
}): Promise<void> {
|
|
113
|
-
const feed = await generateBlogFeed({
|
|
151
|
+
const feed = await generateBlogFeed({
|
|
152
|
+
blogPosts,
|
|
153
|
+
options,
|
|
154
|
+
siteConfig,
|
|
155
|
+
outDir,
|
|
156
|
+
});
|
|
114
157
|
|
|
115
158
|
const feedTypes = options.feedOptions.type;
|
|
116
159
|
if (!feed || !feedTypes) {
|
|
@@ -118,12 +161,12 @@ export async function createBlogFeedFiles({
|
|
|
118
161
|
}
|
|
119
162
|
|
|
120
163
|
await Promise.all(
|
|
121
|
-
feedTypes.map(
|
|
122
|
-
|
|
164
|
+
feedTypes.map((feedType) =>
|
|
165
|
+
createBlogFeedFile({
|
|
123
166
|
feed,
|
|
124
167
|
feedType,
|
|
125
|
-
|
|
126
|
-
})
|
|
127
|
-
|
|
168
|
+
generatePath: path.join(outDir, options.routeBasePath),
|
|
169
|
+
}),
|
|
170
|
+
),
|
|
128
171
|
);
|
|
129
172
|
}
|
package/src/index.ts
CHANGED
|
@@ -16,12 +16,13 @@ import {
|
|
|
16
16
|
posixPath,
|
|
17
17
|
addTrailingPathSeparator,
|
|
18
18
|
createAbsoluteFilePathMatcher,
|
|
19
|
+
getContentPathList,
|
|
20
|
+
getDataFilePath,
|
|
19
21
|
DEFAULT_PLUGIN_ID,
|
|
20
22
|
} from '@docusaurus/utils';
|
|
21
23
|
import {translateContent, getTranslationFiles} from './translations';
|
|
22
24
|
|
|
23
|
-
import {
|
|
24
|
-
PluginOptions,
|
|
25
|
+
import type {
|
|
25
26
|
BlogTags,
|
|
26
27
|
BlogContent,
|
|
27
28
|
BlogItemsToMetadata,
|
|
@@ -30,32 +31,33 @@ import {
|
|
|
30
31
|
BlogContentPaths,
|
|
31
32
|
BlogMarkdownLoaderOptions,
|
|
32
33
|
MetaData,
|
|
33
|
-
Assets,
|
|
34
34
|
} from './types';
|
|
35
35
|
import {PluginOptionSchema} from './pluginOptionSchema';
|
|
36
|
-
import {
|
|
36
|
+
import type {
|
|
37
37
|
LoadContext,
|
|
38
38
|
ConfigureWebpackUtils,
|
|
39
|
-
Props,
|
|
40
39
|
Plugin,
|
|
41
40
|
HtmlTags,
|
|
42
41
|
OptionValidationContext,
|
|
43
42
|
ValidationResult,
|
|
44
43
|
} from '@docusaurus/types';
|
|
45
|
-
import {Configuration} from 'webpack';
|
|
44
|
+
import type {Configuration} from 'webpack';
|
|
46
45
|
import {
|
|
47
46
|
generateBlogPosts,
|
|
48
|
-
getContentPathList,
|
|
49
47
|
getSourceToPermalink,
|
|
50
48
|
getBlogTags,
|
|
51
49
|
} from './blogUtils';
|
|
52
|
-
import {BlogPostFrontMatter} from './blogFrontMatter';
|
|
53
50
|
import {createBlogFeedFiles} from './feed';
|
|
51
|
+
import type {
|
|
52
|
+
PluginOptions,
|
|
53
|
+
BlogPostFrontMatter,
|
|
54
|
+
Assets,
|
|
55
|
+
} from '@docusaurus/plugin-content-blog';
|
|
54
56
|
|
|
55
|
-
export default function pluginContentBlog(
|
|
57
|
+
export default async function pluginContentBlog(
|
|
56
58
|
context: LoadContext,
|
|
57
59
|
options: PluginOptions,
|
|
58
|
-
): Plugin<BlogContent
|
|
60
|
+
): Promise<Plugin<BlogContent>> {
|
|
59
61
|
if (options.admonitions) {
|
|
60
62
|
options.remarkPlugins = options.remarkPlugins.concat([
|
|
61
63
|
[admonitions, options.admonitions],
|
|
@@ -89,24 +91,23 @@ export default function pluginContentBlog(
|
|
|
89
91
|
const aliasedSource = (source: string) =>
|
|
90
92
|
`~blog/${posixPath(path.relative(pluginDataDirRoot, source))}`;
|
|
91
93
|
|
|
94
|
+
const authorsMapFilePath = await getDataFilePath({
|
|
95
|
+
filePath: options.authorsMapPath,
|
|
96
|
+
contentPaths,
|
|
97
|
+
});
|
|
98
|
+
|
|
92
99
|
return {
|
|
93
100
|
name: 'docusaurus-plugin-content-blog',
|
|
94
101
|
|
|
95
102
|
getPathsToWatch() {
|
|
96
|
-
const {include
|
|
103
|
+
const {include} = options;
|
|
97
104
|
const contentMarkdownGlobs = getContentPathList(contentPaths).flatMap(
|
|
98
105
|
(contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
|
|
99
106
|
);
|
|
100
107
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const authorsMapFilePath = path.join(
|
|
105
|
-
contentPaths.contentPath,
|
|
106
|
-
authorsMapPath,
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
return [authorsMapFilePath, ...contentMarkdownGlobs];
|
|
108
|
+
return [authorsMapFilePath, ...contentMarkdownGlobs].filter(
|
|
109
|
+
Boolean,
|
|
110
|
+
) as string[];
|
|
110
111
|
},
|
|
111
112
|
|
|
112
113
|
async getTranslationFiles() {
|
|
@@ -240,25 +241,26 @@ export default function pluginContentBlog(
|
|
|
240
241
|
? blogPosts
|
|
241
242
|
: blogPosts.slice(0, options.blogSidebarCount);
|
|
242
243
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
244
|
+
if (archiveBasePath) {
|
|
245
|
+
const archiveUrl = normalizeUrl([
|
|
246
|
+
baseUrl,
|
|
247
|
+
routeBasePath,
|
|
248
|
+
archiveBasePath,
|
|
249
|
+
]);
|
|
250
|
+
// creates a blog archive route
|
|
251
|
+
const archiveProp = await createData(
|
|
252
|
+
`${docuHash(archiveUrl)}.json`,
|
|
253
|
+
JSON.stringify({blogPosts}, null, 2),
|
|
254
|
+
);
|
|
255
|
+
addRoute({
|
|
256
|
+
path: archiveUrl,
|
|
257
|
+
component: '@theme/BlogArchivePage',
|
|
258
|
+
exact: true,
|
|
259
|
+
modules: {
|
|
260
|
+
archive: aliasedSource(archiveProp),
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
}
|
|
262
264
|
|
|
263
265
|
// This prop is useful to provide the blog list sidebar
|
|
264
266
|
const sidebarProp = await createData(
|
|
@@ -509,14 +511,11 @@ export default function pluginContentBlog(
|
|
|
509
511
|
};
|
|
510
512
|
},
|
|
511
513
|
|
|
512
|
-
async postBuild({outDir}
|
|
514
|
+
async postBuild({outDir, content}) {
|
|
513
515
|
if (!options.feedOptions.type) {
|
|
514
516
|
return;
|
|
515
517
|
}
|
|
516
|
-
|
|
517
|
-
// TODO: we shouldn't need to re-read the posts here!
|
|
518
|
-
// postBuild should receive loadedContent
|
|
519
|
-
const blogPosts = await generateBlogPosts(contentPaths, context, options);
|
|
518
|
+
const {blogPosts} = content;
|
|
520
519
|
if (!blogPosts.length) {
|
|
521
520
|
return;
|
|
522
521
|
}
|
|
@@ -550,6 +549,11 @@ export default function pluginContentBlog(
|
|
|
550
549
|
path: 'atom.xml',
|
|
551
550
|
title: `${feedTitle} Atom Feed`,
|
|
552
551
|
},
|
|
552
|
+
json: {
|
|
553
|
+
type: 'application/json',
|
|
554
|
+
path: 'feed.json',
|
|
555
|
+
title: `${feedTitle} JSON Feed`,
|
|
556
|
+
},
|
|
553
557
|
};
|
|
554
558
|
const headTags: HtmlTags = [];
|
|
555
559
|
|
package/src/markdownLoader.ts
CHANGED
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {truncate, linkify} from './blogUtils';
|
|
9
|
-
import {
|
|
10
|
-
import {BlogMarkdownLoaderOptions} from './types';
|
|
9
|
+
import type {BlogMarkdownLoaderOptions} from './types';
|
|
11
10
|
import type {LoaderContext} from 'webpack';
|
|
12
11
|
|
|
13
12
|
export default function markdownLoader(
|
|
@@ -28,7 +27,7 @@ export default function markdownLoader(
|
|
|
28
27
|
|
|
29
28
|
// Truncate content if requested (e.g: file.md?truncated=true).
|
|
30
29
|
const truncated: boolean | undefined = this.resourceQuery
|
|
31
|
-
? !!
|
|
30
|
+
? !!new URLSearchParams(this.resourceQuery.slice(1)).get('truncated')
|
|
32
31
|
: undefined;
|
|
33
32
|
|
|
34
33
|
if (truncated) {
|
|
@@ -6,7 +6,145 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
declare module '@docusaurus/plugin-content-blog' {
|
|
9
|
-
|
|
9
|
+
import type {RemarkAndRehypePluginOptions} from '@docusaurus/mdx-loader';
|
|
10
|
+
import type {FrontMatterTag} from '@docusaurus/utils';
|
|
11
|
+
import type {Overwrite} from 'utility-types';
|
|
12
|
+
|
|
13
|
+
export interface Assets {
|
|
14
|
+
image?: string;
|
|
15
|
+
authorsImageUrls: (string | undefined)[]; // Array of same size as the original MetaData.authors array
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// We allow passing custom fields to authors, e.g., twitter
|
|
19
|
+
export interface Author extends Record<string, unknown> {
|
|
20
|
+
name?: string;
|
|
21
|
+
imageURL?: string;
|
|
22
|
+
url?: string;
|
|
23
|
+
title?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type BlogPostFrontMatter = {
|
|
27
|
+
id?: string;
|
|
28
|
+
title?: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
tags?: FrontMatterTag[];
|
|
31
|
+
slug?: string;
|
|
32
|
+
draft?: boolean;
|
|
33
|
+
date?: Date | string; // Yaml automatically convert some string patterns as Date, but not all
|
|
34
|
+
|
|
35
|
+
authors?: BlogPostFrontMatterAuthors;
|
|
36
|
+
|
|
37
|
+
// We may want to deprecate those older author front matter fields later:
|
|
38
|
+
author?: string;
|
|
39
|
+
author_title?: string;
|
|
40
|
+
author_url?: string;
|
|
41
|
+
author_image_url?: string;
|
|
42
|
+
|
|
43
|
+
/** @deprecated */
|
|
44
|
+
authorTitle?: string;
|
|
45
|
+
/** @deprecated */
|
|
46
|
+
authorURL?: string;
|
|
47
|
+
/** @deprecated */
|
|
48
|
+
authorImageURL?: string;
|
|
49
|
+
|
|
50
|
+
image?: string;
|
|
51
|
+
keywords?: string[];
|
|
52
|
+
hide_table_of_contents?: boolean;
|
|
53
|
+
toc_min_heading_level?: number;
|
|
54
|
+
toc_max_heading_level?: number;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type BlogPostFrontMatterAuthor = Record<string, unknown> & {
|
|
58
|
+
key?: string;
|
|
59
|
+
name?: string;
|
|
60
|
+
imageURL?: string;
|
|
61
|
+
url?: string;
|
|
62
|
+
title?: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// All the possible variants that the user can use for convenience
|
|
66
|
+
export type BlogPostFrontMatterAuthors =
|
|
67
|
+
| string
|
|
68
|
+
| BlogPostFrontMatterAuthor
|
|
69
|
+
| (string | BlogPostFrontMatterAuthor)[];
|
|
70
|
+
|
|
71
|
+
export type EditUrlFunction = (editUrlParams: {
|
|
72
|
+
blogDirPath: string;
|
|
73
|
+
blogPath: string;
|
|
74
|
+
permalink: string;
|
|
75
|
+
locale: string;
|
|
76
|
+
}) => string | undefined;
|
|
77
|
+
|
|
78
|
+
export type FeedType = 'rss' | 'atom' | 'json';
|
|
79
|
+
export type FeedOptions = {
|
|
80
|
+
type?: FeedType[] | null;
|
|
81
|
+
title?: string;
|
|
82
|
+
description?: string;
|
|
83
|
+
copyright: string;
|
|
84
|
+
language?: string;
|
|
85
|
+
};
|
|
86
|
+
// Feed options, as provided by user config
|
|
87
|
+
export type UserFeedOptions = Overwrite<
|
|
88
|
+
Partial<FeedOptions>,
|
|
89
|
+
{type?: FeedOptions['type'] | 'all'} // Handle the type: "all" shortcut
|
|
90
|
+
>;
|
|
91
|
+
|
|
92
|
+
// Duplicate from ngryman/reading-time to keep stability of API
|
|
93
|
+
type ReadingTimeOptions = {
|
|
94
|
+
wordsPerMinute?: number;
|
|
95
|
+
wordBound?: (char: string) => boolean;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export type ReadingTimeFunction = (params: {
|
|
99
|
+
content: string;
|
|
100
|
+
frontMatter?: BlogPostFrontMatter & Record<string, unknown>;
|
|
101
|
+
options?: ReadingTimeOptions;
|
|
102
|
+
}) => number;
|
|
103
|
+
|
|
104
|
+
export type ReadingTimeFunctionOption = (
|
|
105
|
+
params: Required<Omit<Parameters<ReadingTimeFunction>[0], 'options'>> & {
|
|
106
|
+
defaultReadingTime: ReadingTimeFunction;
|
|
107
|
+
},
|
|
108
|
+
) => number | undefined;
|
|
109
|
+
|
|
110
|
+
export type PluginOptions = RemarkAndRehypePluginOptions & {
|
|
111
|
+
id?: string;
|
|
112
|
+
path: string;
|
|
113
|
+
routeBasePath: string;
|
|
114
|
+
tagsBasePath: string;
|
|
115
|
+
archiveBasePath: string | null;
|
|
116
|
+
include: string[];
|
|
117
|
+
exclude: string[];
|
|
118
|
+
postsPerPage: number | 'ALL';
|
|
119
|
+
blogListComponent: string;
|
|
120
|
+
blogPostComponent: string;
|
|
121
|
+
blogTagsListComponent: string;
|
|
122
|
+
blogTagsPostsComponent: string;
|
|
123
|
+
blogTitle: string;
|
|
124
|
+
blogDescription: string;
|
|
125
|
+
blogSidebarCount: number | 'ALL';
|
|
126
|
+
blogSidebarTitle: string;
|
|
127
|
+
truncateMarker: RegExp;
|
|
128
|
+
showReadingTime: boolean;
|
|
129
|
+
feedOptions: {
|
|
130
|
+
type?: FeedType[] | null;
|
|
131
|
+
title?: string;
|
|
132
|
+
description?: string;
|
|
133
|
+
copyright: string;
|
|
134
|
+
language?: string;
|
|
135
|
+
};
|
|
136
|
+
editUrl?: string | EditUrlFunction;
|
|
137
|
+
editLocalizedFiles?: boolean;
|
|
138
|
+
admonitions: Record<string, unknown>;
|
|
139
|
+
authorsMapPath: string;
|
|
140
|
+
readingTime: ReadingTimeFunctionOption;
|
|
141
|
+
sortPosts: 'ascending' | 'descending';
|
|
142
|
+
};
|
|
143
|
+
// Options, as provided in the user config (before normalization)
|
|
144
|
+
export type Options = Overwrite<
|
|
145
|
+
Partial<PluginOptions>,
|
|
146
|
+
{feedOptions?: UserFeedOptions}
|
|
147
|
+
>;
|
|
10
148
|
}
|
|
11
149
|
|
|
12
150
|
declare module '@theme/BlogSidebar' {
|
|
@@ -27,9 +165,13 @@ declare module '@theme/BlogSidebar' {
|
|
|
27
165
|
declare module '@theme/BlogPostPage' {
|
|
28
166
|
import type {BlogSidebar} from '@theme/BlogSidebar';
|
|
29
167
|
import type {TOCItem} from '@docusaurus/types';
|
|
168
|
+
import type {
|
|
169
|
+
BlogPostFrontMatter,
|
|
170
|
+
Author,
|
|
171
|
+
Assets,
|
|
172
|
+
} from '@docusaurus/plugin-content-blog';
|
|
30
173
|
|
|
31
|
-
export type FrontMatter =
|
|
32
|
-
export type Assets = import('./types').Assets;
|
|
174
|
+
export type FrontMatter = BlogPostFrontMatter;
|
|
33
175
|
|
|
34
176
|
export type Metadata = {
|
|
35
177
|
readonly title: string;
|
|
@@ -42,7 +184,8 @@ declare module '@theme/BlogPostPage' {
|
|
|
42
184
|
readonly truncated?: string;
|
|
43
185
|
readonly nextItem?: {readonly title: string; readonly permalink: string};
|
|
44
186
|
readonly prevItem?: {readonly title: string; readonly permalink: string};
|
|
45
|
-
readonly authors:
|
|
187
|
+
readonly authors: Author[];
|
|
188
|
+
readonly frontMatter: FrontMatter & Record<string, unknown>;
|
|
46
189
|
readonly tags: readonly {
|
|
47
190
|
readonly label: string;
|
|
48
191
|
readonly permalink: string;
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
URISchema,
|
|
14
14
|
} from '@docusaurus/utils-validation';
|
|
15
15
|
import {GlobExcludeDefault} from '@docusaurus/utils';
|
|
16
|
-
import {PluginOptions} from '
|
|
16
|
+
import type {PluginOptions} from '@docusaurus/plugin-content-blog';
|
|
17
17
|
|
|
18
18
|
export const DEFAULT_OPTIONS: PluginOptions = {
|
|
19
19
|
feedOptions: {type: ['rss', 'atom'], copyright: ''},
|
|
@@ -47,7 +47,9 @@ export const DEFAULT_OPTIONS: PluginOptions = {
|
|
|
47
47
|
|
|
48
48
|
export const PluginOptionSchema = Joi.object<PluginOptions>({
|
|
49
49
|
path: Joi.string().default(DEFAULT_OPTIONS.path),
|
|
50
|
-
archiveBasePath: Joi.string()
|
|
50
|
+
archiveBasePath: Joi.string()
|
|
51
|
+
.default(DEFAULT_OPTIONS.archiveBasePath)
|
|
52
|
+
.allow(null),
|
|
51
53
|
routeBasePath: Joi.string()
|
|
52
54
|
// '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
|
|
53
55
|
// .allow('')
|
|
@@ -90,12 +92,12 @@ export const PluginOptionSchema = Joi.object<PluginOptions>({
|
|
|
90
92
|
feedOptions: Joi.object({
|
|
91
93
|
type: Joi.alternatives()
|
|
92
94
|
.try(
|
|
93
|
-
Joi.array().items(Joi.string()),
|
|
95
|
+
Joi.array().items(Joi.string().equal('rss', 'atom', 'json')),
|
|
94
96
|
Joi.alternatives().conditional(
|
|
95
|
-
Joi.string().equal('all', 'rss', 'atom'),
|
|
97
|
+
Joi.string().equal('all', 'rss', 'atom', 'json'),
|
|
96
98
|
{
|
|
97
99
|
then: Joi.custom((val) =>
|
|
98
|
-
val === 'all' ? ['rss', 'atom'] : [val],
|
|
100
|
+
val === 'all' ? ['rss', 'atom', 'json'] : [val],
|
|
99
101
|
),
|
|
100
102
|
},
|
|
101
103
|
),
|
package/src/translations.ts
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {BlogContent,
|
|
8
|
+
import type {BlogContent, BlogPaginated} from './types';
|
|
9
9
|
import type {TranslationFileContent, TranslationFiles} from '@docusaurus/types';
|
|
10
|
+
import type {PluginOptions} from '@docusaurus/plugin-content-blog';
|
|
10
11
|
|
|
11
12
|
function translateListPage(
|
|
12
13
|
blogListPaginated: BlogPaginated[],
|
|
@@ -51,13 +52,13 @@ export function translateContent(
|
|
|
51
52
|
content: BlogContent,
|
|
52
53
|
translationFiles: TranslationFiles,
|
|
53
54
|
): BlogContent {
|
|
54
|
-
const [{content:
|
|
55
|
+
const [{content: optionsTranslations}] = translationFiles;
|
|
55
56
|
return {
|
|
56
57
|
...content,
|
|
57
|
-
blogSidebarTitle:
|
|
58
|
+
blogSidebarTitle: optionsTranslations['sidebar.title'].message,
|
|
58
59
|
blogListPaginated: translateListPage(
|
|
59
60
|
content.blogListPaginated,
|
|
60
|
-
|
|
61
|
+
optionsTranslations,
|
|
61
62
|
),
|
|
62
63
|
};
|
|
63
64
|
}
|