@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.
Files changed (76) hide show
  1. package/lib/authors.d.ts +5 -8
  2. package/lib/authors.js +15 -53
  3. package/lib/blogFrontMatter.d.ts +1 -34
  4. package/lib/blogFrontMatter.js +3 -3
  5. package/lib/blogUtils.d.ts +3 -3
  6. package/lib/blogUtils.js +28 -22
  7. package/lib/feed.d.ts +3 -8
  8. package/lib/feed.js +46 -35
  9. package/lib/index.d.ts +4 -3
  10. package/lib/index.js +33 -28
  11. package/lib/markdownLoader.d.ts +1 -1
  12. package/lib/markdownLoader.js +1 -2
  13. package/lib/pluginOptionSchema.d.ts +1 -1
  14. package/lib/pluginOptionSchema.js +5 -3
  15. package/lib/translations.d.ts +2 -1
  16. package/lib/translations.js +3 -3
  17. package/lib/types.d.ts +2 -79
  18. package/package.json +11 -12
  19. package/src/authors.ts +22 -69
  20. package/src/blogFrontMatter.ts +5 -50
  21. package/src/blogUtils.ts +38 -34
  22. package/src/feed.ts +80 -37
  23. package/src/index.ts +48 -44
  24. package/src/markdownLoader.ts +2 -3
  25. package/src/plugin-content-blog.d.ts +147 -4
  26. package/src/pluginOptionSchema.ts +7 -5
  27. package/src/translations.ts +5 -4
  28. package/src/types.ts +5 -97
  29. package/lib/.tsbuildinfo +0 -1
  30. package/src/__tests__/__fixtures__/authorsMapFiles/authors.json +0 -29
  31. package/src/__tests__/__fixtures__/authorsMapFiles/authors.yml +0 -27
  32. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.json +0 -5
  33. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.yml +0 -3
  34. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.json +0 -3
  35. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.yml +0 -2
  36. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.json +0 -8
  37. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.yml +0 -3
  38. package/src/__tests__/__fixtures__/component/Typography.tsx +0 -6
  39. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathEmpty/empty +0 -0
  40. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson1/authors.json +0 -0
  41. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson2/authors.json +0 -0
  42. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathNestedYml/sub/folder/authors.yml +0 -0
  43. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml1/authors.yml +0 -0
  44. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml2/authors.yml +0 -0
  45. package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -8
  46. package/src/__tests__/__fixtures__/website/blog/_partials/somePartial.md +0 -3
  47. package/src/__tests__/__fixtures__/website/blog/_partials/subfolder/somePartial.md +0 -3
  48. package/src/__tests__/__fixtures__/website/blog/_somePartial.md +0 -3
  49. package/src/__tests__/__fixtures__/website/blog/authors.yml +0 -4
  50. package/src/__tests__/__fixtures__/website/blog/complex-slug.md +0 -7
  51. package/src/__tests__/__fixtures__/website/blog/date-matter.md +0 -5
  52. package/src/__tests__/__fixtures__/website/blog/draft.md +0 -6
  53. package/src/__tests__/__fixtures__/website/blog/heading-as-title.md +0 -5
  54. package/src/__tests__/__fixtures__/website/blog/mdx-blog-post.mdx +0 -36
  55. package/src/__tests__/__fixtures__/website/blog/mdx-require-blog-post.mdx +0 -14
  56. package/src/__tests__/__fixtures__/website/blog/simple-slug.md +0 -11
  57. package/src/__tests__/__fixtures__/website/blog-with-ref/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  58. package/src/__tests__/__fixtures__/website/blog-with-ref/post-with-broken-links.md +0 -11
  59. package/src/__tests__/__fixtures__/website/blog-with-ref/post.md +0 -5
  60. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -8
  61. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/authors.yml +0 -5
  62. package/src/__tests__/__fixtures__/website/static/img/docusaurus.png +0 -0
  63. package/src/__tests__/__fixtures__/website-blog-without-date/blog/no date.md +0 -1
  64. package/src/__tests__/__snapshots__/feed.test.ts.snap +0 -164
  65. package/src/__tests__/__snapshots__/linkify.test.ts.snap +0 -24
  66. package/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap +0 -5
  67. package/src/__tests__/__snapshots__/translations.test.ts.snap +0 -64
  68. package/src/__tests__/authors.test.ts +0 -608
  69. package/src/__tests__/blogFrontMatter.test.ts +0 -394
  70. package/src/__tests__/blogUtils.test.ts +0 -94
  71. package/src/__tests__/feed.test.ts +0 -126
  72. package/src/__tests__/index.test.ts +0 -408
  73. package/src/__tests__/linkify.test.ts +0 -93
  74. package/src/__tests__/pluginOptionSchema.test.ts +0 -150
  75. package/src/__tests__/translations.test.ts +0 -92
  76. 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().default(exports.DEFAULT_OPTIONS.archiveBasePath),
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),
@@ -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, PluginOptions } from './types';
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;
@@ -43,11 +43,11 @@ function getTranslationFiles(options) {
43
43
  }
44
44
  exports.getTranslationFiles = getTranslationFiles;
45
45
  function translateContent(content, translationFiles) {
46
- const [{ content: optonsTranslations }] = translationFiles;
46
+ const [{ content: optionsTranslations }] = translationFiles;
47
47
  return {
48
48
  ...content,
49
- blogSidebarTitle: optonsTranslations['sidebar.title'].message,
50
- blogListPaginated: translateListPage(content.blogListPaginated, optonsTranslations),
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 { Overwrite } from 'utility-types';
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.13",
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.13",
22
- "@docusaurus/mdx-loader": "2.0.0-beta.13",
23
- "@docusaurus/utils": "2.0.0-beta.13",
24
- "@docusaurus/utils-validation": "2.0.0-beta.13",
25
- "chalk": "^4.1.2",
26
- "escape-string-regexp": "^4.0.0",
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.13"
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": "b6ca12aedf10a10c2375f4f8b7e7380cfd7c7202"
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 fs from 'fs-extra';
9
- import chalk from 'chalk';
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 './blogFrontMatter';
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().required(),
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 validateAuthorsMapFile(content: unknown): AuthorsMap {
34
+ export function validateAuthorsMap(content: unknown): AuthorsMap {
38
35
  return Joi.attempt(content, AuthorsMapSchema);
39
36
  }
40
37
 
41
- export async function readAuthorsMapFile(
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
- export async function getAuthorsMapFilePath({
64
- authorsMapPath,
65
- contentPaths,
66
- }: AuthorsMapParams): Promise<string | undefined> {
67
- // Useful to load an eventually localize authors map
68
- const contentPath = await findFolderContainingFile(
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 frontmatter fields
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 frontmatter
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 frontmatter, but do we really want to?
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' FrontMatter in priority.
191
- Don't mix 'authors' with other existing 'author_*' FrontMatter. Choose one or the other, not both at the same time.`,
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];
@@ -6,27 +6,13 @@
6
6
  */
7
7
 
8
8
  import {
9
- JoiFrontMatter as Joi, // Custom instance for frontmatter
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 {FrontMatterTag} from '@docusaurus/utils';
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 frontmatter:
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 frontmatter
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
- parseMarkdownFile,
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!.date!;
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 result = await parseMarkdownFile(blogSourceAbsolute, {
105
- removeContentTitle: true,
106
- });
107
- return {
108
- ...result,
109
- frontMatter: validateBlogPostFrontMatter(result.frontMatter),
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
- console.warn(
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
- return new Date(frontMatter.date);
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
- console.error(
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
- }