@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/lib/authors.d.ts
CHANGED
|
@@ -4,17 +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
|
-
import {
|
|
8
|
-
import { BlogPostFrontMatter } from '
|
|
7
|
+
import type { BlogContentPaths } from './types';
|
|
8
|
+
import type { Author, BlogPostFrontMatter } from '@docusaurus/plugin-content-blog';
|
|
9
9
|
export declare type AuthorsMap = Record<string, Author>;
|
|
10
|
-
export declare function
|
|
11
|
-
export declare function
|
|
12
|
-
declare type AuthorsMapParams = {
|
|
10
|
+
export declare function validateAuthorsMap(content: unknown): AuthorsMap;
|
|
11
|
+
export declare function getAuthorsMap(params: {
|
|
13
12
|
authorsMapPath: string;
|
|
14
13
|
contentPaths: BlogContentPaths;
|
|
15
|
-
}
|
|
16
|
-
export declare function getAuthorsMapFilePath({ authorsMapPath, contentPaths, }: AuthorsMapParams): Promise<string | undefined>;
|
|
17
|
-
export declare function getAuthorsMap(params: AuthorsMapParams): Promise<AuthorsMap | undefined>;
|
|
14
|
+
}): Promise<AuthorsMap | undefined>;
|
|
18
15
|
declare type AuthorsParam = {
|
|
19
16
|
frontMatter: BlogPostFrontMatter;
|
|
20
17
|
authorsMap: AuthorsMap | undefined;
|
package/lib/authors.js
CHANGED
|
@@ -6,69 +6,32 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.getBlogPostAuthors = exports.getAuthorsMap = exports.
|
|
10
|
-
const tslib_1 = require("tslib");
|
|
11
|
-
const fs_extra_1 = (0, tslib_1.__importDefault)(require("fs-extra"));
|
|
12
|
-
const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
|
|
13
|
-
const path_1 = (0, tslib_1.__importDefault)(require("path"));
|
|
9
|
+
exports.getBlogPostAuthors = exports.getAuthorsMap = exports.validateAuthorsMap = void 0;
|
|
14
10
|
const utils_1 = require("@docusaurus/utils");
|
|
15
11
|
const utils_validation_1 = require("@docusaurus/utils-validation");
|
|
16
|
-
const blogUtils_1 = require("./blogUtils");
|
|
17
|
-
const js_yaml_1 = (0, tslib_1.__importDefault)(require("js-yaml"));
|
|
18
12
|
const AuthorsMapSchema = utils_validation_1.Joi.object().pattern(utils_validation_1.Joi.string(), utils_validation_1.Joi.object({
|
|
19
|
-
name: utils_validation_1.Joi.string()
|
|
13
|
+
name: utils_validation_1.Joi.string(),
|
|
20
14
|
url: utils_validation_1.URISchema,
|
|
21
15
|
imageURL: utils_validation_1.URISchema,
|
|
22
16
|
title: utils_validation_1.Joi.string(),
|
|
23
17
|
})
|
|
24
18
|
.rename('image_url', 'imageURL')
|
|
19
|
+
.or('name', 'imageURL')
|
|
25
20
|
.unknown()
|
|
26
21
|
.required());
|
|
27
|
-
function
|
|
22
|
+
function validateAuthorsMap(content) {
|
|
28
23
|
return utils_validation_1.Joi.attempt(content, AuthorsMapSchema);
|
|
29
24
|
}
|
|
30
|
-
exports.
|
|
31
|
-
async function readAuthorsMapFile(filePath) {
|
|
32
|
-
if (await fs_extra_1.default.pathExists(filePath)) {
|
|
33
|
-
const contentString = await fs_extra_1.default.readFile(filePath, { encoding: 'utf8' });
|
|
34
|
-
try {
|
|
35
|
-
const unsafeContent = js_yaml_1.default.load(contentString);
|
|
36
|
-
return validateAuthorsMapFile(unsafeContent);
|
|
37
|
-
}
|
|
38
|
-
catch (e) {
|
|
39
|
-
// TODO replace later by error cause: see https://v8.dev/features/error-cause
|
|
40
|
-
console.error(chalk_1.default.red('The author list file looks invalid!'));
|
|
41
|
-
throw e;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
exports.readAuthorsMapFile = readAuthorsMapFile;
|
|
47
|
-
async function getAuthorsMapFilePath({ authorsMapPath, contentPaths, }) {
|
|
48
|
-
// Useful to load an eventually localize authors map
|
|
49
|
-
const contentPath = await (0, utils_1.findFolderContainingFile)((0, blogUtils_1.getContentPathList)(contentPaths), authorsMapPath);
|
|
50
|
-
if (contentPath) {
|
|
51
|
-
return path_1.default.join(contentPath, authorsMapPath);
|
|
52
|
-
}
|
|
53
|
-
return undefined;
|
|
54
|
-
}
|
|
55
|
-
exports.getAuthorsMapFilePath = getAuthorsMapFilePath;
|
|
25
|
+
exports.validateAuthorsMap = validateAuthorsMap;
|
|
56
26
|
async function getAuthorsMap(params) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return await readAuthorsMapFile(filePath);
|
|
63
|
-
}
|
|
64
|
-
catch (e) {
|
|
65
|
-
// TODO replace later by error cause, see https://v8.dev/features/error-cause
|
|
66
|
-
console.error(chalk_1.default.red(`Couldn't read blog authors map at path ${filePath}`));
|
|
67
|
-
throw e;
|
|
68
|
-
}
|
|
27
|
+
return (0, utils_1.getDataFileData)({
|
|
28
|
+
filePath: params.authorsMapPath,
|
|
29
|
+
contentPaths: params.contentPaths,
|
|
30
|
+
fileType: 'authors map',
|
|
31
|
+
}, validateAuthorsMap);
|
|
69
32
|
}
|
|
70
33
|
exports.getAuthorsMap = getAuthorsMap;
|
|
71
|
-
// Legacy v1/early-v2
|
|
34
|
+
// Legacy v1/early-v2 front matter fields
|
|
72
35
|
// We may want to deprecate those in favor of using only frontMatter.authors
|
|
73
36
|
function getFrontMatterAuthorLegacy(frontMatter) {
|
|
74
37
|
var _a, _b, _c;
|
|
@@ -76,7 +39,6 @@ function getFrontMatterAuthorLegacy(frontMatter) {
|
|
|
76
39
|
const title = (_a = frontMatter.author_title) !== null && _a !== void 0 ? _a : frontMatter.authorTitle;
|
|
77
40
|
const url = (_b = frontMatter.author_url) !== null && _b !== void 0 ? _b : frontMatter.authorURL;
|
|
78
41
|
const imageURL = (_c = frontMatter.author_image_url) !== null && _c !== void 0 ? _c : frontMatter.authorImageURL;
|
|
79
|
-
// Shouldn't we require at least an author name?
|
|
80
42
|
if (name || title || url || imageURL) {
|
|
81
43
|
return {
|
|
82
44
|
name,
|
|
@@ -124,7 +86,7 @@ ${Object.keys(authorsMap)
|
|
|
124
86
|
}
|
|
125
87
|
function toAuthor(frontMatterAuthor) {
|
|
126
88
|
return {
|
|
127
|
-
// Author def from authorsMap can be locally overridden by
|
|
89
|
+
// Author def from authorsMap can be locally overridden by front matter
|
|
128
90
|
...getAuthorsMapAuthor(frontMatterAuthor.key),
|
|
129
91
|
...frontMatterAuthor,
|
|
130
92
|
};
|
|
@@ -135,10 +97,10 @@ function getBlogPostAuthors(params) {
|
|
|
135
97
|
const authorLegacy = getFrontMatterAuthorLegacy(params.frontMatter);
|
|
136
98
|
const authors = getFrontMatterAuthors(params);
|
|
137
99
|
if (authorLegacy) {
|
|
138
|
-
// Technically, we could allow mixing legacy/authors
|
|
100
|
+
// Technically, we could allow mixing legacy/authors front matter, but do we really want to?
|
|
139
101
|
if (authors.length > 0) {
|
|
140
|
-
throw new Error(`To declare blog post authors, use the 'authors'
|
|
141
|
-
Don't mix 'authors' with other existing 'author_*'
|
|
102
|
+
throw new Error(`To declare blog post authors, use the 'authors' front matter in priority.
|
|
103
|
+
Don't mix 'authors' with other existing 'author_*' front matter. Choose one or the other, not both at the same time.`);
|
|
142
104
|
}
|
|
143
105
|
return [authorLegacy];
|
|
144
106
|
}
|
package/lib/blogFrontMatter.d.ts
CHANGED
|
@@ -4,38 +4,5 @@
|
|
|
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
|
-
export declare type BlogPostFrontMatterAuthor = Record<string, unknown> & {
|
|
9
|
-
key?: string;
|
|
10
|
-
name?: string;
|
|
11
|
-
imageURL?: string;
|
|
12
|
-
url?: string;
|
|
13
|
-
title?: string;
|
|
14
|
-
};
|
|
15
|
-
export declare type BlogPostFrontMatterAuthors = string | BlogPostFrontMatterAuthor | (string | BlogPostFrontMatterAuthor)[];
|
|
16
|
-
export declare type BlogPostFrontMatter = {
|
|
17
|
-
id?: string;
|
|
18
|
-
title?: string;
|
|
19
|
-
description?: string;
|
|
20
|
-
tags?: FrontMatterTag[];
|
|
21
|
-
slug?: string;
|
|
22
|
-
draft?: boolean;
|
|
23
|
-
date?: Date | string;
|
|
24
|
-
authors?: BlogPostFrontMatterAuthors;
|
|
25
|
-
author?: string;
|
|
26
|
-
author_title?: string;
|
|
27
|
-
author_url?: string;
|
|
28
|
-
author_image_url?: string;
|
|
29
|
-
/** @deprecated */
|
|
30
|
-
authorTitle?: string;
|
|
31
|
-
/** @deprecated */
|
|
32
|
-
authorURL?: string;
|
|
33
|
-
/** @deprecated */
|
|
34
|
-
authorImageURL?: string;
|
|
35
|
-
image?: string;
|
|
36
|
-
keywords?: string[];
|
|
37
|
-
hide_table_of_contents?: boolean;
|
|
38
|
-
toc_min_heading_level?: number;
|
|
39
|
-
toc_max_heading_level?: number;
|
|
40
|
-
};
|
|
7
|
+
import type { BlogPostFrontMatter } from '@docusaurus/plugin-content-blog';
|
|
41
8
|
export declare function validateBlogPostFrontMatter(frontMatter: Record<string, unknown>): BlogPostFrontMatter;
|
package/lib/blogFrontMatter.js
CHANGED
|
@@ -15,7 +15,7 @@ const BlogPostFrontMatterAuthorSchema = utils_validation_1.JoiFrontMatter.object
|
|
|
15
15
|
url: utils_validation_1.URISchema,
|
|
16
16
|
imageURL: utils_validation_1.JoiFrontMatter.string(),
|
|
17
17
|
})
|
|
18
|
-
.or('key', 'name')
|
|
18
|
+
.or('key', 'name', 'imageURL')
|
|
19
19
|
.rename('image_url', 'imageURL', { alias: true });
|
|
20
20
|
const FrontMatterAuthorErrorMessage = '{{#label}} does not look like a valid blog post author. Please use an author key or an author object (with a key and/or name).';
|
|
21
21
|
const BlogFrontMatterSchema = utils_validation_1.JoiFrontMatter.object({
|
|
@@ -25,7 +25,7 @@ const BlogFrontMatterSchema = utils_validation_1.JoiFrontMatter.object({
|
|
|
25
25
|
tags: utils_validation_1.FrontMatterTagsSchema,
|
|
26
26
|
draft: utils_validation_1.JoiFrontMatter.boolean(),
|
|
27
27
|
date: utils_validation_1.JoiFrontMatter.date().raw(),
|
|
28
|
-
// New multi-authors
|
|
28
|
+
// New multi-authors front matter:
|
|
29
29
|
authors: utils_validation_1.JoiFrontMatter.alternatives()
|
|
30
30
|
.try(utils_validation_1.JoiFrontMatter.string(), BlogPostFrontMatterAuthorSchema, utils_validation_1.JoiFrontMatter.array()
|
|
31
31
|
.items(utils_validation_1.JoiFrontMatter.string(), BlogPostFrontMatterAuthorSchema)
|
|
@@ -36,7 +36,7 @@ const BlogFrontMatterSchema = utils_validation_1.JoiFrontMatter.object({
|
|
|
36
36
|
.messages({
|
|
37
37
|
'alternatives.match': FrontMatterAuthorErrorMessage,
|
|
38
38
|
}),
|
|
39
|
-
// Legacy author
|
|
39
|
+
// Legacy author front matter
|
|
40
40
|
author: utils_validation_1.JoiFrontMatter.string(),
|
|
41
41
|
author_title: utils_validation_1.JoiFrontMatter.string(),
|
|
42
42
|
author_url: utils_validation_1.URISchema,
|
package/lib/blogUtils.d.ts
CHANGED
|
@@ -4,8 +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 {
|
|
8
|
-
import { LoadContext } from '@docusaurus/types';
|
|
7
|
+
import type { BlogPost, BlogContentPaths, BlogMarkdownLoaderOptions, BlogTags } from './types';
|
|
8
|
+
import type { LoadContext } from '@docusaurus/types';
|
|
9
|
+
import type { PluginOptions } from '@docusaurus/plugin-content-blog';
|
|
9
10
|
export declare function truncate(fileString: string, truncateMarker: RegExp): string;
|
|
10
11
|
export declare function getSourceToPermalink(blogPosts: BlogPost[]): Record<string, string>;
|
|
11
12
|
export declare function getBlogTags(blogPosts: BlogPost[]): BlogTags;
|
|
@@ -21,5 +22,4 @@ export declare type LinkifyParams = {
|
|
|
21
22
|
fileString: string;
|
|
22
23
|
} & Pick<BlogMarkdownLoaderOptions, 'sourceToPermalink' | 'siteDir' | 'contentPaths' | 'onBrokenMarkdownLink'>;
|
|
23
24
|
export declare function linkify({ filePath, contentPaths, fileString, siteDir, sourceToPermalink, onBrokenMarkdownLink, }: LinkifyParams): string;
|
|
24
|
-
export declare function getContentPathList(contentPaths: BlogContentPaths): string[];
|
|
25
25
|
export {};
|
package/lib/blogUtils.js
CHANGED
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.
|
|
9
|
+
exports.linkify = exports.generateBlogPosts = exports.parseBlogFileName = exports.getBlogTags = exports.getSourceToPermalink = exports.truncate = void 0;
|
|
10
10
|
const tslib_1 = require("tslib");
|
|
11
11
|
const fs_extra_1 = (0, tslib_1.__importDefault)(require("fs-extra"));
|
|
12
|
-
const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
|
|
13
12
|
const path_1 = (0, tslib_1.__importDefault)(require("path"));
|
|
14
13
|
const reading_time_1 = (0, tslib_1.__importDefault)(require("reading-time"));
|
|
15
14
|
const lodash_1 = require("lodash");
|
|
16
15
|
const utils_1 = require("@docusaurus/utils");
|
|
17
16
|
const blogFrontMatter_1 = require("./blogFrontMatter");
|
|
18
17
|
const authors_1 = require("./authors");
|
|
18
|
+
const logger_1 = (0, tslib_1.__importDefault)(require("@docusaurus/logger"));
|
|
19
19
|
function truncate(fileString, truncateMarker) {
|
|
20
20
|
return fileString.split(truncateMarker, 1).shift();
|
|
21
21
|
}
|
|
@@ -33,16 +33,15 @@ function getBlogTags(blogPosts) {
|
|
|
33
33
|
}));
|
|
34
34
|
}
|
|
35
35
|
exports.getBlogTags = getBlogTags;
|
|
36
|
-
const DATE_FILENAME_REGEX = /^(?<date>\d{4}[-/]\d{1,2}[-/]\d{1,2})[-/]?(?<text>.*?)(\/index)?.mdx?$/;
|
|
36
|
+
const DATE_FILENAME_REGEX = /^(?<folder>.*)(?<date>\d{4}[-/]\d{1,2}[-/]\d{1,2})[-/]?(?<text>.*?)(\/index)?.mdx?$/;
|
|
37
37
|
function parseBlogFileName(blogSourceRelative) {
|
|
38
38
|
const dateFilenameMatch = blogSourceRelative.match(DATE_FILENAME_REGEX);
|
|
39
39
|
if (dateFilenameMatch) {
|
|
40
|
-
const dateString = dateFilenameMatch.groups
|
|
41
|
-
const text = dateFilenameMatch.groups.text;
|
|
40
|
+
const { folder, text, date: dateString } = dateFilenameMatch.groups;
|
|
42
41
|
// Always treat dates as UTC by adding the `Z`
|
|
43
42
|
const date = new Date(`${dateString}Z`);
|
|
44
43
|
const slugDate = dateString.replace(/-/g, '/');
|
|
45
|
-
const slug = `/${slugDate}/${text}`;
|
|
44
|
+
const slug = `/${slugDate}/${folder}${text}`;
|
|
46
45
|
return { date, text, slug };
|
|
47
46
|
}
|
|
48
47
|
else {
|
|
@@ -66,13 +65,19 @@ function formatBlogPostDate(locale, date) {
|
|
|
66
65
|
}
|
|
67
66
|
}
|
|
68
67
|
async function parseBlogPostMarkdownFile(blogSourceAbsolute) {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
const markdownString = await fs_extra_1.default.readFile(blogSourceAbsolute, 'utf-8');
|
|
69
|
+
try {
|
|
70
|
+
const result = (0, utils_1.parseMarkdownString)(markdownString, {
|
|
71
|
+
removeContentTitle: true,
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
...result,
|
|
75
|
+
frontMatter: (0, blogFrontMatter_1.validateBlogPostFrontMatter)(result.frontMatter),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
throw new Error(`Error while parsing blog post file ${blogSourceAbsolute}: "${e.message}".`);
|
|
80
|
+
}
|
|
76
81
|
}
|
|
77
82
|
const defaultReadingTime = ({ content, options }) => (0, reading_time_1.default)(content, options).minutes;
|
|
78
83
|
async function processBlogSourceFile(blogSourceRelative, contentPaths, context, options, authorsMap) {
|
|
@@ -80,7 +85,7 @@ async function processBlogSourceFile(blogSourceRelative, contentPaths, context,
|
|
|
80
85
|
const { siteConfig: { baseUrl }, siteDir, i18n, } = context;
|
|
81
86
|
const { routeBasePath, tagsBasePath: tagsRouteBasePath, truncateMarker, showReadingTime, editUrl, } = options;
|
|
82
87
|
// Lookup in localized folder in priority
|
|
83
|
-
const blogDirPath = await (0, utils_1.getFolderContainingFile)(getContentPathList(contentPaths), blogSourceRelative);
|
|
88
|
+
const blogDirPath = await (0, utils_1.getFolderContainingFile)((0, utils_1.getContentPathList)(contentPaths), blogSourceRelative);
|
|
84
89
|
const blogSourceAbsolute = path_1.default.join(blogDirPath, blogSourceRelative);
|
|
85
90
|
const { frontMatter, content, contentTitle, excerpt } = await parseBlogPostMarkdownFile(blogSourceAbsolute);
|
|
86
91
|
const aliasedSource = (0, utils_1.aliasedSitePath)(blogSourceAbsolute, siteDir);
|
|
@@ -88,13 +93,18 @@ async function processBlogSourceFile(blogSourceRelative, contentPaths, context,
|
|
|
88
93
|
return undefined;
|
|
89
94
|
}
|
|
90
95
|
if (frontMatter.id) {
|
|
91
|
-
|
|
96
|
+
logger_1.default.warn `name=${'id'} header option is deprecated in path=${blogSourceRelative} file. Please use name=${'slug'} option instead.`;
|
|
92
97
|
}
|
|
93
98
|
const parsedBlogFileName = parseBlogFileName(blogSourceRelative);
|
|
94
99
|
async function getDate() {
|
|
95
100
|
// Prefer user-defined date.
|
|
96
101
|
if (frontMatter.date) {
|
|
97
|
-
|
|
102
|
+
if (typeof frontMatter.date === 'string') {
|
|
103
|
+
// Always treat dates as UTC by adding the `Z`
|
|
104
|
+
return new Date(`${frontMatter.date}Z`);
|
|
105
|
+
}
|
|
106
|
+
// YAML only converts YYYY-MM-DD to dates and leaves others as strings.
|
|
107
|
+
return frontMatter.date;
|
|
98
108
|
}
|
|
99
109
|
else if (parsedBlogFileName.date) {
|
|
100
110
|
return parsedBlogFileName.date;
|
|
@@ -157,6 +167,7 @@ async function processBlogSourceFile(blogSourceRelative, contentPaths, context,
|
|
|
157
167
|
: undefined,
|
|
158
168
|
truncated: (truncateMarker === null || truncateMarker === void 0 ? void 0 : truncateMarker.test(content)) || false,
|
|
159
169
|
authors,
|
|
170
|
+
frontMatter,
|
|
160
171
|
},
|
|
161
172
|
content,
|
|
162
173
|
};
|
|
@@ -179,7 +190,7 @@ async function generateBlogPosts(contentPaths, context, options) {
|
|
|
179
190
|
return await processBlogSourceFile(blogSourceFile, contentPaths, context, options, authorsMap);
|
|
180
191
|
}
|
|
181
192
|
catch (e) {
|
|
182
|
-
|
|
193
|
+
logger_1.default.error `Processing of blog source file failed for path path=${blogSourceFile}.`;
|
|
183
194
|
throw e;
|
|
184
195
|
}
|
|
185
196
|
}))).filter(Boolean);
|
|
@@ -202,8 +213,3 @@ function linkify({ filePath, contentPaths, fileString, siteDir, sourceToPermalin
|
|
|
202
213
|
return newContent;
|
|
203
214
|
}
|
|
204
215
|
exports.linkify = linkify;
|
|
205
|
-
// Order matters: we look in priority in localized folder
|
|
206
|
-
function getContentPathList(contentPaths) {
|
|
207
|
-
return [contentPaths.contentPathLocalized, contentPaths.contentPath];
|
|
208
|
-
}
|
|
209
|
-
exports.getContentPathList = getContentPathList;
|
package/lib/feed.d.ts
CHANGED
|
@@ -4,14 +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 {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
export declare function generateBlogFeed({ blogPosts, options, siteConfig, }: {
|
|
11
|
-
blogPosts: BlogPost[];
|
|
12
|
-
options: PluginOptions;
|
|
13
|
-
siteConfig: DocusaurusConfig;
|
|
14
|
-
}): Promise<Feed | null>;
|
|
7
|
+
import type { BlogPost } from './types';
|
|
8
|
+
import type { DocusaurusConfig } from '@docusaurus/types';
|
|
9
|
+
import type { PluginOptions } from '@docusaurus/plugin-content-blog';
|
|
15
10
|
export declare function createBlogFeedFiles({ blogPosts, options, siteConfig, outDir, }: {
|
|
16
11
|
blogPosts: BlogPost[];
|
|
17
12
|
options: PluginOptions;
|
package/lib/feed.js
CHANGED
|
@@ -6,34 +6,22 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.createBlogFeedFiles =
|
|
9
|
+
exports.createBlogFeedFiles = void 0;
|
|
10
10
|
const tslib_1 = require("tslib");
|
|
11
11
|
const feed_1 = require("feed");
|
|
12
12
|
const utils_1 = require("@docusaurus/utils");
|
|
13
|
+
const cheerio_1 = (0, tslib_1.__importDefault)(require("cheerio"));
|
|
13
14
|
const path_1 = (0, tslib_1.__importDefault)(require("path"));
|
|
14
15
|
const fs_extra_1 = (0, tslib_1.__importDefault)(require("fs-extra"));
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// See https://github.com/facebook/docusaurus/issues/5664
|
|
18
|
-
function mdxToFeedContent(mdxContent) {
|
|
19
|
-
try {
|
|
20
|
-
return (0, utils_1.mdxToHtml)(mdxContent);
|
|
21
|
-
}
|
|
22
|
-
catch (e) {
|
|
23
|
-
// TODO will we need a plugin option to configure how to handle such an error
|
|
24
|
-
// Swallow the error on purpose for now, until we understand better the problem space
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
async function generateBlogFeed({ blogPosts, options, siteConfig, }) {
|
|
16
|
+
const utils_common_1 = require("@docusaurus/utils-common");
|
|
17
|
+
async function generateBlogFeed({ blogPosts, options, siteConfig, outDir, }) {
|
|
29
18
|
if (!blogPosts.length) {
|
|
30
19
|
return null;
|
|
31
20
|
}
|
|
32
21
|
const { feedOptions, routeBasePath } = options;
|
|
33
22
|
const { url: siteUrl, baseUrl, title, favicon } = siteConfig;
|
|
34
23
|
const blogBaseUrl = (0, utils_1.normalizeUrl)([siteUrl, baseUrl, routeBasePath]);
|
|
35
|
-
const updated =
|
|
36
|
-
new Date('2015-10-25T16:29:00.000-07:00'); // weird legacy magic date
|
|
24
|
+
const updated = blogPosts[0] && blogPosts[0].metadata.date;
|
|
37
25
|
const feed = new feed_1.Feed({
|
|
38
26
|
id: blogBaseUrl,
|
|
39
27
|
title: feedOptions.title || `${title} Blog`,
|
|
@@ -49,42 +37,65 @@ async function generateBlogFeed({ blogPosts, options, siteConfig, }) {
|
|
|
49
37
|
// RSS feed requires email to render authors
|
|
50
38
|
return { name: author.name, link: author.url };
|
|
51
39
|
}
|
|
52
|
-
|
|
53
|
-
const { id, metadata: { title: metadataTitle, permalink, date, description, authors }, } = post;
|
|
54
|
-
|
|
40
|
+
await (0, utils_1.mapAsyncSequential)(blogPosts, async (post) => {
|
|
41
|
+
const { id, metadata: { title: metadataTitle, permalink, date, description, authors, tags, }, } = post;
|
|
42
|
+
const content = await (0, utils_1.readOutputHTMLFile)(permalink.replace(siteConfig.baseUrl, ''), outDir, siteConfig.trailingSlash);
|
|
43
|
+
const $ = cheerio_1.default.load(content);
|
|
44
|
+
const feedItem = {
|
|
55
45
|
title: metadataTitle,
|
|
56
46
|
id,
|
|
57
47
|
link: (0, utils_1.normalizeUrl)([siteUrl, permalink]),
|
|
58
48
|
date,
|
|
59
49
|
description,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
// Atom feed demands the "term", while other feeds use "name"
|
|
51
|
+
category: tags.map((tag) => ({ name: tag.label, term: tag.label })),
|
|
52
|
+
content: $(`#${utils_common_1.blogPostContainerID}`).html(),
|
|
53
|
+
};
|
|
54
|
+
// json1() method takes the first item of authors array
|
|
55
|
+
// it causes an error when authors array is empty
|
|
56
|
+
const feedItemAuthors = authors.map(toFeedAuthor);
|
|
57
|
+
if (feedItemAuthors.length > 0) {
|
|
58
|
+
feedItem.author = feedItemAuthors;
|
|
59
|
+
}
|
|
60
|
+
feed.addItem(feedItem);
|
|
63
61
|
});
|
|
64
62
|
return feed;
|
|
65
63
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
async function createBlogFeedFile({ feed, feedType, generatePath, }) {
|
|
65
|
+
const [feedContent, feedPath] = (() => {
|
|
66
|
+
switch (feedType) {
|
|
67
|
+
case 'rss':
|
|
68
|
+
return [feed.rss2(), 'rss.xml'];
|
|
69
|
+
case 'json':
|
|
70
|
+
return [feed.json1(), 'feed.json'];
|
|
71
|
+
case 'atom':
|
|
72
|
+
return [feed.atom1(), 'atom.xml'];
|
|
73
|
+
default:
|
|
74
|
+
throw new Error(`Feed type ${feedType} not supported.`);
|
|
75
|
+
}
|
|
76
|
+
})();
|
|
69
77
|
try {
|
|
70
|
-
await fs_extra_1.default.outputFile(
|
|
78
|
+
await fs_extra_1.default.outputFile((0, utils_1.posixPath)(path_1.default.join(generatePath, feedPath)), feedContent);
|
|
71
79
|
}
|
|
72
80
|
catch (err) {
|
|
73
81
|
throw new Error(`Generating ${feedType} feed failed: ${err}.`);
|
|
74
82
|
}
|
|
75
83
|
}
|
|
76
84
|
async function createBlogFeedFiles({ blogPosts, options, siteConfig, outDir, }) {
|
|
77
|
-
const feed = await generateBlogFeed({
|
|
85
|
+
const feed = await generateBlogFeed({
|
|
86
|
+
blogPosts,
|
|
87
|
+
options,
|
|
88
|
+
siteConfig,
|
|
89
|
+
outDir,
|
|
90
|
+
});
|
|
78
91
|
const feedTypes = options.feedOptions.type;
|
|
79
92
|
if (!feed || !feedTypes) {
|
|
80
93
|
return;
|
|
81
94
|
}
|
|
82
|
-
await Promise.all(feedTypes.map(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
88
|
-
}));
|
|
95
|
+
await Promise.all(feedTypes.map((feedType) => createBlogFeedFile({
|
|
96
|
+
feed,
|
|
97
|
+
feedType,
|
|
98
|
+
generatePath: path_1.default.join(outDir, options.routeBasePath),
|
|
99
|
+
})));
|
|
89
100
|
}
|
|
90
101
|
exports.createBlogFeedFiles = createBlogFeedFiles;
|
package/lib/index.d.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import { LoadContext, Plugin, OptionValidationContext, ValidationResult } from '@docusaurus/types';
|
|
9
|
-
|
|
7
|
+
import type { BlogContent } from './types';
|
|
8
|
+
import type { LoadContext, Plugin, OptionValidationContext, ValidationResult } from '@docusaurus/types';
|
|
9
|
+
import type { PluginOptions } from '@docusaurus/plugin-content-blog';
|
|
10
|
+
export default function pluginContentBlog(context: LoadContext, options: PluginOptions): Promise<Plugin<BlogContent>>;
|
|
10
11
|
export declare function validateOptions({ validate, options, }: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions>;
|
package/lib/index.js
CHANGED
|
@@ -15,7 +15,7 @@ const translations_1 = require("./translations");
|
|
|
15
15
|
const pluginOptionSchema_1 = require("./pluginOptionSchema");
|
|
16
16
|
const blogUtils_1 = require("./blogUtils");
|
|
17
17
|
const feed_1 = require("./feed");
|
|
18
|
-
function pluginContentBlog(context, options) {
|
|
18
|
+
async function pluginContentBlog(context, options) {
|
|
19
19
|
var _a;
|
|
20
20
|
if (options.admonitions) {
|
|
21
21
|
options.remarkPlugins = options.remarkPlugins.concat([
|
|
@@ -37,16 +37,16 @@ function pluginContentBlog(context, options) {
|
|
|
37
37
|
const pluginDataDirRoot = path_1.default.join(generatedFilesDir, 'docusaurus-plugin-content-blog');
|
|
38
38
|
const dataDir = path_1.default.join(pluginDataDirRoot, pluginId);
|
|
39
39
|
const aliasedSource = (source) => `~blog/${(0, utils_1.posixPath)(path_1.default.relative(pluginDataDirRoot, source))}`;
|
|
40
|
+
const authorsMapFilePath = await (0, utils_1.getDataFilePath)({
|
|
41
|
+
filePath: options.authorsMapPath,
|
|
42
|
+
contentPaths,
|
|
43
|
+
});
|
|
40
44
|
return {
|
|
41
45
|
name: 'docusaurus-plugin-content-blog',
|
|
42
46
|
getPathsToWatch() {
|
|
43
|
-
const { include
|
|
44
|
-
const contentMarkdownGlobs = (0,
|
|
45
|
-
|
|
46
|
-
// const authorsMapFilePath = await getAuthorsMapFilePath({authorsMapPath,contentPaths,});
|
|
47
|
-
// simplified impl, better than nothing for now:
|
|
48
|
-
const authorsMapFilePath = path_1.default.join(contentPaths.contentPath, authorsMapPath);
|
|
49
|
-
return [authorsMapFilePath, ...contentMarkdownGlobs];
|
|
47
|
+
const { include } = options;
|
|
48
|
+
const contentMarkdownGlobs = (0, utils_1.getContentPathList)(contentPaths).flatMap((contentPath) => include.map((pattern) => `${contentPath}/${pattern}`));
|
|
49
|
+
return [authorsMapFilePath, ...contentMarkdownGlobs].filter(Boolean);
|
|
50
50
|
},
|
|
51
51
|
async getTranslationFiles() {
|
|
52
52
|
return (0, translations_1.getTranslationFiles)(options);
|
|
@@ -135,21 +135,23 @@ function pluginContentBlog(context, options) {
|
|
|
135
135
|
const sidebarBlogPosts = options.blogSidebarCount === 'ALL'
|
|
136
136
|
? blogPosts
|
|
137
137
|
: blogPosts.slice(0, options.blogSidebarCount);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
138
|
+
if (archiveBasePath) {
|
|
139
|
+
const archiveUrl = (0, utils_1.normalizeUrl)([
|
|
140
|
+
baseUrl,
|
|
141
|
+
routeBasePath,
|
|
142
|
+
archiveBasePath,
|
|
143
|
+
]);
|
|
144
|
+
// creates a blog archive route
|
|
145
|
+
const archiveProp = await createData(`${(0, utils_1.docuHash)(archiveUrl)}.json`, JSON.stringify({ blogPosts }, null, 2));
|
|
146
|
+
addRoute({
|
|
147
|
+
path: archiveUrl,
|
|
148
|
+
component: '@theme/BlogArchivePage',
|
|
149
|
+
exact: true,
|
|
150
|
+
modules: {
|
|
151
|
+
archive: aliasedSource(archiveProp),
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
153
155
|
// This prop is useful to provide the blog list sidebar
|
|
154
156
|
const sidebarProp = await createData(
|
|
155
157
|
// Note that this created data path must be in sync with
|
|
@@ -274,7 +276,7 @@ function pluginContentBlog(context, options) {
|
|
|
274
276
|
(0, utils_1.reportMessage)(`Blog markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath}`, onBrokenMarkdownLinks);
|
|
275
277
|
},
|
|
276
278
|
};
|
|
277
|
-
const contentDirs = (0,
|
|
279
|
+
const contentDirs = (0, utils_1.getContentPathList)(contentPaths);
|
|
278
280
|
return {
|
|
279
281
|
resolve: {
|
|
280
282
|
alias: {
|
|
@@ -326,13 +328,11 @@ function pluginContentBlog(context, options) {
|
|
|
326
328
|
},
|
|
327
329
|
};
|
|
328
330
|
},
|
|
329
|
-
async postBuild({ outDir }) {
|
|
331
|
+
async postBuild({ outDir, content }) {
|
|
330
332
|
if (!options.feedOptions.type) {
|
|
331
333
|
return;
|
|
332
334
|
}
|
|
333
|
-
|
|
334
|
-
// postBuild should receive loadedContent
|
|
335
|
-
const blogPosts = await (0, blogUtils_1.generateBlogPosts)(contentPaths, context, options);
|
|
335
|
+
const { blogPosts } = content;
|
|
336
336
|
if (!blogPosts.length) {
|
|
337
337
|
return;
|
|
338
338
|
}
|
|
@@ -364,6 +364,11 @@ function pluginContentBlog(context, options) {
|
|
|
364
364
|
path: 'atom.xml',
|
|
365
365
|
title: `${feedTitle} Atom Feed`,
|
|
366
366
|
},
|
|
367
|
+
json: {
|
|
368
|
+
type: 'application/json',
|
|
369
|
+
path: 'feed.json',
|
|
370
|
+
title: `${feedTitle} JSON Feed`,
|
|
371
|
+
},
|
|
367
372
|
};
|
|
368
373
|
const headTags = [];
|
|
369
374
|
feedTypes.forEach((feedType) => {
|
package/lib/markdownLoader.d.ts
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
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 { BlogMarkdownLoaderOptions } from './types';
|
|
7
|
+
import type { BlogMarkdownLoaderOptions } from './types';
|
|
8
8
|
import type { LoaderContext } from 'webpack';
|
|
9
9
|
export default function markdownLoader(this: LoaderContext<BlogMarkdownLoaderOptions>, source: string): void;
|
package/lib/markdownLoader.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
const blogUtils_1 = require("./blogUtils");
|
|
10
|
-
const loader_utils_1 = require("loader-utils");
|
|
11
10
|
function markdownLoader(source) {
|
|
12
11
|
const filePath = this.resourcePath;
|
|
13
12
|
const fileString = source;
|
|
@@ -21,7 +20,7 @@ function markdownLoader(source) {
|
|
|
21
20
|
});
|
|
22
21
|
// Truncate content if requested (e.g: file.md?truncated=true).
|
|
23
22
|
const truncated = this.resourceQuery
|
|
24
|
-
? !!
|
|
23
|
+
? !!new URLSearchParams(this.resourceQuery.slice(1)).get('truncated')
|
|
25
24
|
: undefined;
|
|
26
25
|
if (truncated) {
|
|
27
26
|
finalContent = (0, blogUtils_1.truncate)(finalContent, markdownLoaderOptions.truncateMarker);
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
import { Joi } from '@docusaurus/utils-validation';
|
|
8
|
-
import { PluginOptions } from '
|
|
8
|
+
import type { PluginOptions } from '@docusaurus/plugin-content-blog';
|
|
9
9
|
export declare const DEFAULT_OPTIONS: PluginOptions;
|
|
10
10
|
export declare const PluginOptionSchema: Joi.ObjectSchema<PluginOptions>;
|