@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
|
@@ -40,7 +40,9 @@ exports.DEFAULT_OPTIONS = {
|
|
|
40
40
|
};
|
|
41
41
|
exports.PluginOptionSchema = utils_validation_1.Joi.object({
|
|
42
42
|
path: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.path),
|
|
43
|
-
archiveBasePath: utils_validation_1.Joi.string()
|
|
43
|
+
archiveBasePath: utils_validation_1.Joi.string()
|
|
44
|
+
.default(exports.DEFAULT_OPTIONS.archiveBasePath)
|
|
45
|
+
.allow(null),
|
|
44
46
|
routeBasePath: utils_validation_1.Joi.string()
|
|
45
47
|
// '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
|
|
46
48
|
// .allow('')
|
|
@@ -74,8 +76,8 @@ exports.PluginOptionSchema = utils_validation_1.Joi.object({
|
|
|
74
76
|
beforeDefaultRehypePlugins: utils_validation_1.RehypePluginsSchema.default(exports.DEFAULT_OPTIONS.beforeDefaultRehypePlugins),
|
|
75
77
|
feedOptions: utils_validation_1.Joi.object({
|
|
76
78
|
type: utils_validation_1.Joi.alternatives()
|
|
77
|
-
.try(utils_validation_1.Joi.array().items(utils_validation_1.Joi.string()), utils_validation_1.Joi.alternatives().conditional(utils_validation_1.Joi.string().equal('all', 'rss', 'atom'), {
|
|
78
|
-
then: utils_validation_1.Joi.custom((val) => val === 'all' ? ['rss', 'atom'] : [val]),
|
|
79
|
+
.try(utils_validation_1.Joi.array().items(utils_validation_1.Joi.string().equal('rss', 'atom', 'json')), utils_validation_1.Joi.alternatives().conditional(utils_validation_1.Joi.string().equal('all', 'rss', 'atom', 'json'), {
|
|
80
|
+
then: utils_validation_1.Joi.custom((val) => val === 'all' ? ['rss', 'atom', 'json'] : [val]),
|
|
79
81
|
}))
|
|
80
82
|
.allow(null)
|
|
81
83
|
.default(exports.DEFAULT_OPTIONS.feedOptions.type),
|
package/lib/translations.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 type { BlogContent
|
|
7
|
+
import type { BlogContent } from './types';
|
|
8
8
|
import type { TranslationFiles } from '@docusaurus/types';
|
|
9
|
+
import type { PluginOptions } from '@docusaurus/plugin-content-blog';
|
|
9
10
|
export declare function getTranslationFiles(options: PluginOptions): TranslationFiles;
|
|
10
11
|
export declare function translateContent(content: BlogContent, translationFiles: TranslationFiles): BlogContent;
|
package/lib/translations.js
CHANGED
|
@@ -43,11 +43,11 @@ function getTranslationFiles(options) {
|
|
|
43
43
|
}
|
|
44
44
|
exports.getTranslationFiles = getTranslationFiles;
|
|
45
45
|
function translateContent(content, translationFiles) {
|
|
46
|
-
const [{ content:
|
|
46
|
+
const [{ content: optionsTranslations }] = translationFiles;
|
|
47
47
|
return {
|
|
48
48
|
...content,
|
|
49
|
-
blogSidebarTitle:
|
|
50
|
-
blogListPaginated: translateListPage(content.blogListPaginated,
|
|
49
|
+
blogSidebarTitle: optionsTranslations['sidebar.title'].message,
|
|
50
|
+
blogListPaginated: translateListPage(content.blogListPaginated, optionsTranslations),
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
exports.translateContent = translateContent;
|
package/lib/types.d.ts
CHANGED
|
@@ -4,11 +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 { RemarkAndRehypePluginOptions } from '@docusaurus/mdx-loader';
|
|
8
7
|
import type { Tag } from '@docusaurus/utils';
|
|
9
8
|
import type { BrokenMarkdownLink, ContentPaths } from '@docusaurus/utils/lib/markdownLinks';
|
|
10
|
-
import {
|
|
11
|
-
import { BlogPostFrontMatter } from './blogFrontMatter';
|
|
9
|
+
import type { BlogPostFrontMatter, Author } from '@docusaurus/plugin-content-blog';
|
|
12
10
|
export declare type BlogContentPaths = ContentPaths;
|
|
13
11
|
export interface BlogContent {
|
|
14
12
|
blogSidebarTitle: string;
|
|
@@ -17,71 +15,6 @@ export interface BlogContent {
|
|
|
17
15
|
blogTags: BlogTags;
|
|
18
16
|
blogTagsListPath: string | null;
|
|
19
17
|
}
|
|
20
|
-
export declare type FeedType = 'rss' | 'atom';
|
|
21
|
-
export declare type FeedOptions = {
|
|
22
|
-
type?: FeedType[] | null;
|
|
23
|
-
title?: string;
|
|
24
|
-
description?: string;
|
|
25
|
-
copyright: string;
|
|
26
|
-
language?: string;
|
|
27
|
-
};
|
|
28
|
-
export declare type UserFeedOptions = Overwrite<Partial<FeedOptions>, {
|
|
29
|
-
type?: FeedOptions['type'] | 'all';
|
|
30
|
-
}>;
|
|
31
|
-
export declare type EditUrlFunction = (editUrlParams: {
|
|
32
|
-
blogDirPath: string;
|
|
33
|
-
blogPath: string;
|
|
34
|
-
permalink: string;
|
|
35
|
-
locale: string;
|
|
36
|
-
}) => string | undefined;
|
|
37
|
-
declare type ReadingTimeOptions = {
|
|
38
|
-
wordsPerMinute?: number;
|
|
39
|
-
wordBound?: (char: string) => boolean;
|
|
40
|
-
};
|
|
41
|
-
export declare type ReadingTimeFunction = (params: {
|
|
42
|
-
content: string;
|
|
43
|
-
frontMatter?: BlogPostFrontMatter & Record<string, unknown>;
|
|
44
|
-
options?: ReadingTimeOptions;
|
|
45
|
-
}) => number;
|
|
46
|
-
export declare type ReadingTimeFunctionOption = (params: Required<Omit<Parameters<ReadingTimeFunction>[0], 'options'>> & {
|
|
47
|
-
defaultReadingTime: ReadingTimeFunction;
|
|
48
|
-
}) => number | undefined;
|
|
49
|
-
export declare type PluginOptions = RemarkAndRehypePluginOptions & {
|
|
50
|
-
id?: string;
|
|
51
|
-
path: string;
|
|
52
|
-
routeBasePath: string;
|
|
53
|
-
tagsBasePath: string;
|
|
54
|
-
archiveBasePath: string;
|
|
55
|
-
include: string[];
|
|
56
|
-
exclude: string[];
|
|
57
|
-
postsPerPage: number | 'ALL';
|
|
58
|
-
blogListComponent: string;
|
|
59
|
-
blogPostComponent: string;
|
|
60
|
-
blogTagsListComponent: string;
|
|
61
|
-
blogTagsPostsComponent: string;
|
|
62
|
-
blogTitle: string;
|
|
63
|
-
blogDescription: string;
|
|
64
|
-
blogSidebarCount: number | 'ALL';
|
|
65
|
-
blogSidebarTitle: string;
|
|
66
|
-
truncateMarker: RegExp;
|
|
67
|
-
showReadingTime: boolean;
|
|
68
|
-
feedOptions: {
|
|
69
|
-
type?: FeedType[] | null;
|
|
70
|
-
title?: string;
|
|
71
|
-
description?: string;
|
|
72
|
-
copyright: string;
|
|
73
|
-
language?: string;
|
|
74
|
-
};
|
|
75
|
-
editUrl?: string | EditUrlFunction;
|
|
76
|
-
editLocalizedFiles?: boolean;
|
|
77
|
-
admonitions: Record<string, unknown>;
|
|
78
|
-
authorsMapPath: string;
|
|
79
|
-
readingTime: ReadingTimeFunctionOption;
|
|
80
|
-
sortPosts: 'ascending' | 'descending';
|
|
81
|
-
};
|
|
82
|
-
export declare type UserPluginOptions = Overwrite<Partial<PluginOptions>, {
|
|
83
|
-
feedOptions?: UserFeedOptions;
|
|
84
|
-
}>;
|
|
85
18
|
export interface BlogTags {
|
|
86
19
|
[key: string]: BlogTag;
|
|
87
20
|
}
|
|
@@ -110,12 +43,6 @@ export interface BlogPaginated {
|
|
|
110
43
|
metadata: BlogPaginatedMetadata;
|
|
111
44
|
items: string[];
|
|
112
45
|
}
|
|
113
|
-
export interface Author extends Record<string, unknown> {
|
|
114
|
-
name?: string;
|
|
115
|
-
imageURL?: string;
|
|
116
|
-
url?: string;
|
|
117
|
-
title?: string;
|
|
118
|
-
}
|
|
119
46
|
export interface MetaData {
|
|
120
47
|
permalink: string;
|
|
121
48
|
source: string;
|
|
@@ -130,10 +57,7 @@ export interface MetaData {
|
|
|
130
57
|
truncated: boolean;
|
|
131
58
|
editUrl?: string;
|
|
132
59
|
authors: Author[];
|
|
133
|
-
|
|
134
|
-
export interface Assets {
|
|
135
|
-
image?: string;
|
|
136
|
-
authorsImageUrls: (string | undefined)[];
|
|
60
|
+
frontMatter: BlogPostFrontMatter & Record<string, unknown>;
|
|
137
61
|
}
|
|
138
62
|
export interface Paginator {
|
|
139
63
|
title: string;
|
|
@@ -160,4 +84,3 @@ export declare type BlogMarkdownLoaderOptions = {
|
|
|
160
84
|
sourceToPermalink: Record<string, string>;
|
|
161
85
|
onBrokenMarkdownLink: (brokenMarkdownLink: BlogBrokenMarkdownLink) => void;
|
|
162
86
|
};
|
|
163
|
-
export {};
|
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.15",
|
|
4
4
|
"description": "Blog plugin for Docusaurus.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "src/plugin-content-blog.d.ts",
|
|
@@ -18,17 +18,15 @@
|
|
|
18
18
|
},
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@docusaurus/core": "2.0.0-beta.
|
|
22
|
-
"@docusaurus/
|
|
23
|
-
"@docusaurus/
|
|
24
|
-
"@docusaurus/utils
|
|
25
|
-
"
|
|
26
|
-
"
|
|
21
|
+
"@docusaurus/core": "2.0.0-beta.15",
|
|
22
|
+
"@docusaurus/logger": "2.0.0-beta.15",
|
|
23
|
+
"@docusaurus/mdx-loader": "2.0.0-beta.15",
|
|
24
|
+
"@docusaurus/utils": "2.0.0-beta.15",
|
|
25
|
+
"@docusaurus/utils-common": "2.0.0-beta.15",
|
|
26
|
+
"@docusaurus/utils-validation": "2.0.0-beta.15",
|
|
27
|
+
"cheerio": "^1.0.0-rc.10",
|
|
27
28
|
"feed": "^4.2.2",
|
|
28
29
|
"fs-extra": "^10.0.0",
|
|
29
|
-
"globby": "^11.0.2",
|
|
30
|
-
"js-yaml": "^4.0.0",
|
|
31
|
-
"loader-utils": "^2.0.0",
|
|
32
30
|
"lodash": "^4.17.20",
|
|
33
31
|
"reading-time": "^1.5.0",
|
|
34
32
|
"remark-admonitions": "^1.2.1",
|
|
@@ -37,7 +35,8 @@
|
|
|
37
35
|
"webpack": "^5.61.0"
|
|
38
36
|
},
|
|
39
37
|
"devDependencies": {
|
|
40
|
-
"@docusaurus/types": "2.0.0-beta.
|
|
38
|
+
"@docusaurus/types": "2.0.0-beta.15",
|
|
39
|
+
"escape-string-regexp": "^4.0.0"
|
|
41
40
|
},
|
|
42
41
|
"peerDependencies": {
|
|
43
42
|
"react": "^16.8.4 || ^17.0.0",
|
|
@@ -46,5 +45,5 @@
|
|
|
46
45
|
"engines": {
|
|
47
46
|
"node": ">=14"
|
|
48
47
|
},
|
|
49
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "6cfad16436c07d8d11e5c2e1486dc59afd483e33"
|
|
50
49
|
}
|
package/src/authors.ts
CHANGED
|
@@ -5,94 +5,48 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import path from 'path';
|
|
11
|
-
import {Author, BlogContentPaths} from './types';
|
|
12
|
-
import {findFolderContainingFile} from '@docusaurus/utils';
|
|
8
|
+
import type {BlogContentPaths} from './types';
|
|
9
|
+
import {getDataFileData} from '@docusaurus/utils';
|
|
13
10
|
import {Joi, URISchema} from '@docusaurus/utils-validation';
|
|
14
|
-
import {
|
|
11
|
+
import type {
|
|
12
|
+
Author,
|
|
15
13
|
BlogPostFrontMatter,
|
|
16
14
|
BlogPostFrontMatterAuthor,
|
|
17
15
|
BlogPostFrontMatterAuthors,
|
|
18
|
-
} from '
|
|
19
|
-
import {getContentPathList} from './blogUtils';
|
|
20
|
-
import Yaml from 'js-yaml';
|
|
16
|
+
} from '@docusaurus/plugin-content-blog';
|
|
21
17
|
|
|
22
18
|
export type AuthorsMap = Record<string, Author>;
|
|
23
19
|
|
|
24
20
|
const AuthorsMapSchema = Joi.object<AuthorsMap>().pattern(
|
|
25
21
|
Joi.string(),
|
|
26
22
|
Joi.object({
|
|
27
|
-
name: Joi.string()
|
|
23
|
+
name: Joi.string(),
|
|
28
24
|
url: URISchema,
|
|
29
25
|
imageURL: URISchema,
|
|
30
26
|
title: Joi.string(),
|
|
31
27
|
})
|
|
32
28
|
.rename('image_url', 'imageURL')
|
|
29
|
+
.or('name', 'imageURL')
|
|
33
30
|
.unknown()
|
|
34
31
|
.required(),
|
|
35
32
|
);
|
|
36
33
|
|
|
37
|
-
export function
|
|
34
|
+
export function validateAuthorsMap(content: unknown): AuthorsMap {
|
|
38
35
|
return Joi.attempt(content, AuthorsMapSchema);
|
|
39
36
|
}
|
|
40
37
|
|
|
41
|
-
export async function
|
|
42
|
-
filePath: string,
|
|
43
|
-
): Promise<AuthorsMap | undefined> {
|
|
44
|
-
if (await fs.pathExists(filePath)) {
|
|
45
|
-
const contentString = await fs.readFile(filePath, {encoding: 'utf8'});
|
|
46
|
-
try {
|
|
47
|
-
const unsafeContent = Yaml.load(contentString);
|
|
48
|
-
return validateAuthorsMapFile(unsafeContent);
|
|
49
|
-
} catch (e) {
|
|
50
|
-
// TODO replace later by error cause: see https://v8.dev/features/error-cause
|
|
51
|
-
console.error(chalk.red('The author list file looks invalid!'));
|
|
52
|
-
throw e;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return undefined;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
type AuthorsMapParams = {
|
|
38
|
+
export async function getAuthorsMap(params: {
|
|
59
39
|
authorsMapPath: string;
|
|
60
40
|
contentPaths: BlogContentPaths;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
getContentPathList(contentPaths),
|
|
70
|
-
authorsMapPath,
|
|
41
|
+
}): Promise<AuthorsMap | undefined> {
|
|
42
|
+
return getDataFileData(
|
|
43
|
+
{
|
|
44
|
+
filePath: params.authorsMapPath,
|
|
45
|
+
contentPaths: params.contentPaths,
|
|
46
|
+
fileType: 'authors map',
|
|
47
|
+
},
|
|
48
|
+
validateAuthorsMap,
|
|
71
49
|
);
|
|
72
|
-
|
|
73
|
-
if (contentPath) {
|
|
74
|
-
return path.join(contentPath, authorsMapPath);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export async function getAuthorsMap(
|
|
81
|
-
params: AuthorsMapParams,
|
|
82
|
-
): Promise<AuthorsMap | undefined> {
|
|
83
|
-
const filePath = await getAuthorsMapFilePath(params);
|
|
84
|
-
if (!filePath) {
|
|
85
|
-
return undefined;
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
return await readAuthorsMapFile(filePath);
|
|
89
|
-
} catch (e) {
|
|
90
|
-
// TODO replace later by error cause, see https://v8.dev/features/error-cause
|
|
91
|
-
console.error(
|
|
92
|
-
chalk.red(`Couldn't read blog authors map at path ${filePath}`),
|
|
93
|
-
);
|
|
94
|
-
throw e;
|
|
95
|
-
}
|
|
96
50
|
}
|
|
97
51
|
|
|
98
52
|
type AuthorsParam = {
|
|
@@ -100,7 +54,7 @@ type AuthorsParam = {
|
|
|
100
54
|
authorsMap: AuthorsMap | undefined;
|
|
101
55
|
};
|
|
102
56
|
|
|
103
|
-
// Legacy v1/early-v2
|
|
57
|
+
// Legacy v1/early-v2 front matter fields
|
|
104
58
|
// We may want to deprecate those in favor of using only frontMatter.authors
|
|
105
59
|
function getFrontMatterAuthorLegacy(
|
|
106
60
|
frontMatter: BlogPostFrontMatter,
|
|
@@ -110,7 +64,6 @@ function getFrontMatterAuthorLegacy(
|
|
|
110
64
|
const url = frontMatter.author_url ?? frontMatter.authorURL;
|
|
111
65
|
const imageURL = frontMatter.author_image_url ?? frontMatter.authorImageURL;
|
|
112
66
|
|
|
113
|
-
// Shouldn't we require at least an author name?
|
|
114
67
|
if (name || title || url || imageURL) {
|
|
115
68
|
return {
|
|
116
69
|
name,
|
|
@@ -170,7 +123,7 @@ ${Object.keys(authorsMap)
|
|
|
170
123
|
|
|
171
124
|
function toAuthor(frontMatterAuthor: BlogPostFrontMatterAuthor): Author {
|
|
172
125
|
return {
|
|
173
|
-
// Author def from authorsMap can be locally overridden by
|
|
126
|
+
// Author def from authorsMap can be locally overridden by front matter
|
|
174
127
|
...getAuthorsMapAuthor(frontMatterAuthor.key),
|
|
175
128
|
...frontMatterAuthor,
|
|
176
129
|
};
|
|
@@ -184,11 +137,11 @@ export function getBlogPostAuthors(params: AuthorsParam): Author[] {
|
|
|
184
137
|
const authors = getFrontMatterAuthors(params);
|
|
185
138
|
|
|
186
139
|
if (authorLegacy) {
|
|
187
|
-
// Technically, we could allow mixing legacy/authors
|
|
140
|
+
// Technically, we could allow mixing legacy/authors front matter, but do we really want to?
|
|
188
141
|
if (authors.length > 0) {
|
|
189
142
|
throw new Error(
|
|
190
|
-
`To declare blog post authors, use the 'authors'
|
|
191
|
-
Don't mix 'authors' with other existing 'author_*'
|
|
143
|
+
`To declare blog post authors, use the 'authors' front matter in priority.
|
|
144
|
+
Don't mix 'authors' with other existing 'author_*' front matter. Choose one or the other, not both at the same time.`,
|
|
192
145
|
);
|
|
193
146
|
}
|
|
194
147
|
return [authorLegacy];
|
package/src/blogFrontMatter.ts
CHANGED
|
@@ -6,27 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
|
-
JoiFrontMatter as Joi, // Custom instance for
|
|
9
|
+
JoiFrontMatter as Joi, // Custom instance for front matter
|
|
10
10
|
URISchema,
|
|
11
11
|
validateFrontMatter,
|
|
12
12
|
FrontMatterTagsSchema,
|
|
13
13
|
FrontMatterTOCHeadingLevels,
|
|
14
14
|
} from '@docusaurus/utils-validation';
|
|
15
|
-
import type {
|
|
16
|
-
|
|
17
|
-
export type BlogPostFrontMatterAuthor = Record<string, unknown> & {
|
|
18
|
-
key?: string;
|
|
19
|
-
name?: string;
|
|
20
|
-
imageURL?: string;
|
|
21
|
-
url?: string;
|
|
22
|
-
title?: string;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// All the possible variants that the user can use for convenience
|
|
26
|
-
export type BlogPostFrontMatterAuthors =
|
|
27
|
-
| string
|
|
28
|
-
| BlogPostFrontMatterAuthor
|
|
29
|
-
| (string | BlogPostFrontMatterAuthor)[];
|
|
15
|
+
import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
|
|
30
16
|
|
|
31
17
|
const BlogPostFrontMatterAuthorSchema = Joi.object({
|
|
32
18
|
key: Joi.string(),
|
|
@@ -35,40 +21,9 @@ const BlogPostFrontMatterAuthorSchema = Joi.object({
|
|
|
35
21
|
url: URISchema,
|
|
36
22
|
imageURL: Joi.string(),
|
|
37
23
|
})
|
|
38
|
-
.or('key', 'name')
|
|
24
|
+
.or('key', 'name', 'imageURL')
|
|
39
25
|
.rename('image_url', 'imageURL', {alias: true});
|
|
40
26
|
|
|
41
|
-
export type BlogPostFrontMatter = {
|
|
42
|
-
id?: string;
|
|
43
|
-
title?: string;
|
|
44
|
-
description?: string;
|
|
45
|
-
tags?: FrontMatterTag[];
|
|
46
|
-
slug?: string;
|
|
47
|
-
draft?: boolean;
|
|
48
|
-
date?: Date | string; // Yaml automagically convert some string patterns as Date, but not all
|
|
49
|
-
|
|
50
|
-
authors?: BlogPostFrontMatterAuthors;
|
|
51
|
-
|
|
52
|
-
// We may want to deprecate those older author frontmatter fields later:
|
|
53
|
-
author?: string;
|
|
54
|
-
author_title?: string;
|
|
55
|
-
author_url?: string;
|
|
56
|
-
author_image_url?: string;
|
|
57
|
-
|
|
58
|
-
/** @deprecated */
|
|
59
|
-
authorTitle?: string;
|
|
60
|
-
/** @deprecated */
|
|
61
|
-
authorURL?: string;
|
|
62
|
-
/** @deprecated */
|
|
63
|
-
authorImageURL?: string;
|
|
64
|
-
|
|
65
|
-
image?: string;
|
|
66
|
-
keywords?: string[];
|
|
67
|
-
hide_table_of_contents?: boolean;
|
|
68
|
-
toc_min_heading_level?: number;
|
|
69
|
-
toc_max_heading_level?: number;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
27
|
const FrontMatterAuthorErrorMessage =
|
|
73
28
|
'{{#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).';
|
|
74
29
|
|
|
@@ -80,7 +35,7 @@ const BlogFrontMatterSchema = Joi.object<BlogPostFrontMatter>({
|
|
|
80
35
|
draft: Joi.boolean(),
|
|
81
36
|
date: Joi.date().raw(),
|
|
82
37
|
|
|
83
|
-
// New multi-authors
|
|
38
|
+
// New multi-authors front matter:
|
|
84
39
|
authors: Joi.alternatives()
|
|
85
40
|
.try(
|
|
86
41
|
Joi.string(),
|
|
@@ -95,7 +50,7 @@ const BlogFrontMatterSchema = Joi.object<BlogPostFrontMatter>({
|
|
|
95
50
|
.messages({
|
|
96
51
|
'alternatives.match': FrontMatterAuthorErrorMessage,
|
|
97
52
|
}),
|
|
98
|
-
// Legacy author
|
|
53
|
+
// Legacy author front matter
|
|
99
54
|
author: Joi.string(),
|
|
100
55
|
author_title: Joi.string(),
|
|
101
56
|
author_url: URISchema,
|
package/src/blogUtils.ts
CHANGED
|
@@ -6,20 +6,17 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import fs from 'fs-extra';
|
|
9
|
-
import chalk from 'chalk';
|
|
10
9
|
import path from 'path';
|
|
11
10
|
import readingTime from 'reading-time';
|
|
12
11
|
import {keyBy, mapValues} from 'lodash';
|
|
13
|
-
import {
|
|
14
|
-
PluginOptions,
|
|
12
|
+
import type {
|
|
15
13
|
BlogPost,
|
|
16
14
|
BlogContentPaths,
|
|
17
15
|
BlogMarkdownLoaderOptions,
|
|
18
16
|
BlogTags,
|
|
19
|
-
ReadingTimeFunction,
|
|
20
17
|
} from './types';
|
|
21
18
|
import {
|
|
22
|
-
|
|
19
|
+
parseMarkdownString,
|
|
23
20
|
normalizeUrl,
|
|
24
21
|
aliasedSitePath,
|
|
25
22
|
getEditUrl,
|
|
@@ -29,10 +26,16 @@ import {
|
|
|
29
26
|
Globby,
|
|
30
27
|
normalizeFrontMatterTags,
|
|
31
28
|
groupTaggedItems,
|
|
29
|
+
getContentPathList,
|
|
32
30
|
} from '@docusaurus/utils';
|
|
33
|
-
import {LoadContext} from '@docusaurus/types';
|
|
31
|
+
import type {LoadContext} from '@docusaurus/types';
|
|
34
32
|
import {validateBlogPostFrontMatter} from './blogFrontMatter';
|
|
35
|
-
import {AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
|
|
33
|
+
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
|
|
34
|
+
import logger from '@docusaurus/logger';
|
|
35
|
+
import type {
|
|
36
|
+
PluginOptions,
|
|
37
|
+
ReadingTimeFunction,
|
|
38
|
+
} from '@docusaurus/plugin-content-blog';
|
|
36
39
|
|
|
37
40
|
export function truncate(fileString: string, truncateMarker: RegExp): string {
|
|
38
41
|
return fileString.split(truncateMarker, 1).shift()!;
|
|
@@ -60,7 +63,7 @@ export function getBlogTags(blogPosts: BlogPost[]): BlogTags {
|
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
const DATE_FILENAME_REGEX =
|
|
63
|
-
/^(?<date>\d{4}[-/]\d{1,2}[-/]\d{1,2})[-/]?(?<text>.*?)(\/index)?.mdx?$/;
|
|
66
|
+
/^(?<folder>.*)(?<date>\d{4}[-/]\d{1,2}[-/]\d{1,2})[-/]?(?<text>.*?)(\/index)?.mdx?$/;
|
|
64
67
|
|
|
65
68
|
type ParsedBlogFileName = {
|
|
66
69
|
date: Date | undefined;
|
|
@@ -73,12 +76,11 @@ export function parseBlogFileName(
|
|
|
73
76
|
): ParsedBlogFileName {
|
|
74
77
|
const dateFilenameMatch = blogSourceRelative.match(DATE_FILENAME_REGEX);
|
|
75
78
|
if (dateFilenameMatch) {
|
|
76
|
-
const dateString = dateFilenameMatch.groups
|
|
77
|
-
const text = dateFilenameMatch.groups!.text!;
|
|
79
|
+
const {folder, text, date: dateString} = dateFilenameMatch.groups!;
|
|
78
80
|
// Always treat dates as UTC by adding the `Z`
|
|
79
81
|
const date = new Date(`${dateString}Z`);
|
|
80
82
|
const slugDate = dateString.replace(/-/g, '/');
|
|
81
|
-
const slug = `/${slugDate}/${text}`;
|
|
83
|
+
const slug = `/${slugDate}/${folder}${text}`;
|
|
82
84
|
return {date, text, slug};
|
|
83
85
|
} else {
|
|
84
86
|
const text = blogSourceRelative.replace(/(\/index)?\.mdx?$/, '');
|
|
@@ -101,13 +103,22 @@ function formatBlogPostDate(locale: string, date: Date): string {
|
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
async function parseBlogPostMarkdownFile(blogSourceAbsolute: string) {
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
const markdownString = await fs.readFile(blogSourceAbsolute, 'utf-8');
|
|
107
|
+
try {
|
|
108
|
+
const result = parseMarkdownString(markdownString, {
|
|
109
|
+
removeContentTitle: true,
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
...result,
|
|
113
|
+
frontMatter: validateBlogPostFrontMatter(result.frontMatter),
|
|
114
|
+
};
|
|
115
|
+
} catch (e) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Error while parsing blog post file ${blogSourceAbsolute}: "${
|
|
118
|
+
(e as Error).message
|
|
119
|
+
}".`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
111
122
|
}
|
|
112
123
|
|
|
113
124
|
const defaultReadingTime: ReadingTimeFunction = ({content, options}) =>
|
|
@@ -151,11 +162,7 @@ async function processBlogSourceFile(
|
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
if (frontMatter.id) {
|
|
154
|
-
|
|
155
|
-
chalk.yellow(
|
|
156
|
-
`"id" header option is deprecated in ${blogSourceRelative} file. Please use "slug" option instead.`,
|
|
157
|
-
),
|
|
158
|
-
);
|
|
165
|
+
logger.warn`name=${'id'} header option is deprecated in path=${blogSourceRelative} file. Please use name=${'slug'} option instead.`;
|
|
159
166
|
}
|
|
160
167
|
|
|
161
168
|
const parsedBlogFileName = parseBlogFileName(blogSourceRelative);
|
|
@@ -163,7 +170,12 @@ async function processBlogSourceFile(
|
|
|
163
170
|
async function getDate(): Promise<Date> {
|
|
164
171
|
// Prefer user-defined date.
|
|
165
172
|
if (frontMatter.date) {
|
|
166
|
-
|
|
173
|
+
if (typeof frontMatter.date === 'string') {
|
|
174
|
+
// Always treat dates as UTC by adding the `Z`
|
|
175
|
+
return new Date(`${frontMatter.date}Z`);
|
|
176
|
+
}
|
|
177
|
+
// YAML only converts YYYY-MM-DD to dates and leaves others as strings.
|
|
178
|
+
return frontMatter.date;
|
|
167
179
|
} else if (parsedBlogFileName.date) {
|
|
168
180
|
return parsedBlogFileName.date;
|
|
169
181
|
}
|
|
@@ -238,6 +250,7 @@ async function processBlogSourceFile(
|
|
|
238
250
|
: undefined,
|
|
239
251
|
truncated: truncateMarker?.test(content) || false,
|
|
240
252
|
authors,
|
|
253
|
+
frontMatter,
|
|
241
254
|
},
|
|
242
255
|
content,
|
|
243
256
|
};
|
|
@@ -276,11 +289,7 @@ export async function generateBlogPosts(
|
|
|
276
289
|
authorsMap,
|
|
277
290
|
);
|
|
278
291
|
} catch (e) {
|
|
279
|
-
|
|
280
|
-
chalk.red(
|
|
281
|
-
`Processing of blog source file failed for path "${blogSourceFile}"`,
|
|
282
|
-
),
|
|
283
|
-
);
|
|
292
|
+
logger.error`Processing of blog source file failed for path path=${blogSourceFile}.`;
|
|
284
293
|
throw e;
|
|
285
294
|
}
|
|
286
295
|
}),
|
|
@@ -325,8 +334,3 @@ export function linkify({
|
|
|
325
334
|
|
|
326
335
|
return newContent;
|
|
327
336
|
}
|
|
328
|
-
|
|
329
|
-
// Order matters: we look in priority in localized folder
|
|
330
|
-
export function getContentPathList(contentPaths: BlogContentPaths): string[] {
|
|
331
|
-
return [contentPaths.contentPathLocalized, contentPaths.contentPath];
|
|
332
|
-
}
|