@docusaurus/plugin-content-blog 2.0.0-beta.14 → 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/src/index.ts CHANGED
@@ -16,12 +16,13 @@ import {
16
16
  posixPath,
17
17
  addTrailingPathSeparator,
18
18
  createAbsoluteFilePathMatcher,
19
+ getContentPathList,
20
+ getDataFilePath,
19
21
  DEFAULT_PLUGIN_ID,
20
22
  } from '@docusaurus/utils';
21
23
  import {translateContent, getTranslationFiles} from './translations';
22
24
 
23
- import {
24
- PluginOptions,
25
+ import type {
25
26
  BlogTags,
26
27
  BlogContent,
27
28
  BlogItemsToMetadata,
@@ -30,32 +31,33 @@ import {
30
31
  BlogContentPaths,
31
32
  BlogMarkdownLoaderOptions,
32
33
  MetaData,
33
- Assets,
34
34
  } from './types';
35
35
  import {PluginOptionSchema} from './pluginOptionSchema';
36
- import {
36
+ import type {
37
37
  LoadContext,
38
38
  ConfigureWebpackUtils,
39
- Props,
40
39
  Plugin,
41
40
  HtmlTags,
42
41
  OptionValidationContext,
43
42
  ValidationResult,
44
43
  } from '@docusaurus/types';
45
- import {Configuration} from 'webpack';
44
+ import type {Configuration} from 'webpack';
46
45
  import {
47
46
  generateBlogPosts,
48
- getContentPathList,
49
47
  getSourceToPermalink,
50
48
  getBlogTags,
51
49
  } from './blogUtils';
52
- import {BlogPostFrontMatter} from './blogFrontMatter';
53
50
  import {createBlogFeedFiles} from './feed';
51
+ import type {
52
+ PluginOptions,
53
+ BlogPostFrontMatter,
54
+ Assets,
55
+ } from '@docusaurus/plugin-content-blog';
54
56
 
55
- export default function pluginContentBlog(
57
+ export default async function pluginContentBlog(
56
58
  context: LoadContext,
57
59
  options: PluginOptions,
58
- ): Plugin<BlogContent> {
60
+ ): Promise<Plugin<BlogContent>> {
59
61
  if (options.admonitions) {
60
62
  options.remarkPlugins = options.remarkPlugins.concat([
61
63
  [admonitions, options.admonitions],
@@ -89,24 +91,23 @@ export default function pluginContentBlog(
89
91
  const aliasedSource = (source: string) =>
90
92
  `~blog/${posixPath(path.relative(pluginDataDirRoot, source))}`;
91
93
 
94
+ const authorsMapFilePath = await getDataFilePath({
95
+ filePath: options.authorsMapPath,
96
+ contentPaths,
97
+ });
98
+
92
99
  return {
93
100
  name: 'docusaurus-plugin-content-blog',
94
101
 
95
102
  getPathsToWatch() {
96
- const {include, authorsMapPath} = options;
103
+ const {include} = options;
97
104
  const contentMarkdownGlobs = getContentPathList(contentPaths).flatMap(
98
105
  (contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
99
106
  );
100
107
 
101
- // TODO: we should read this path in plugin! but plugins do not support async init for now :'(
102
- // const authorsMapFilePath = await getAuthorsMapFilePath({authorsMapPath,contentPaths,});
103
- // simplified impl, better than nothing for now:
104
- const authorsMapFilePath = path.join(
105
- contentPaths.contentPath,
106
- authorsMapPath,
107
- );
108
-
109
- return [authorsMapFilePath, ...contentMarkdownGlobs];
108
+ return [authorsMapFilePath, ...contentMarkdownGlobs].filter(
109
+ Boolean,
110
+ ) as string[];
110
111
  },
111
112
 
112
113
  async getTranslationFiles() {
@@ -240,25 +241,26 @@ export default function pluginContentBlog(
240
241
  ? blogPosts
241
242
  : blogPosts.slice(0, options.blogSidebarCount);
242
243
 
243
- const archiveUrl = normalizeUrl([
244
- baseUrl,
245
- routeBasePath,
246
- archiveBasePath,
247
- ]);
248
-
249
- // creates a blog archive route
250
- const archiveProp = await createData(
251
- `${docuHash(archiveUrl)}.json`,
252
- JSON.stringify({blogPosts}, null, 2),
253
- );
254
- addRoute({
255
- path: archiveUrl,
256
- component: '@theme/BlogArchivePage',
257
- exact: true,
258
- modules: {
259
- archive: aliasedSource(archiveProp),
260
- },
261
- });
244
+ if (archiveBasePath) {
245
+ const archiveUrl = normalizeUrl([
246
+ baseUrl,
247
+ routeBasePath,
248
+ archiveBasePath,
249
+ ]);
250
+ // creates a blog archive route
251
+ const archiveProp = await createData(
252
+ `${docuHash(archiveUrl)}.json`,
253
+ JSON.stringify({blogPosts}, null, 2),
254
+ );
255
+ addRoute({
256
+ path: archiveUrl,
257
+ component: '@theme/BlogArchivePage',
258
+ exact: true,
259
+ modules: {
260
+ archive: aliasedSource(archiveProp),
261
+ },
262
+ });
263
+ }
262
264
 
263
265
  // This prop is useful to provide the blog list sidebar
264
266
  const sidebarProp = await createData(
@@ -509,14 +511,11 @@ export default function pluginContentBlog(
509
511
  };
510
512
  },
511
513
 
512
- async postBuild({outDir}: Props) {
514
+ async postBuild({outDir, content}) {
513
515
  if (!options.feedOptions.type) {
514
516
  return;
515
517
  }
516
-
517
- // TODO: we shouldn't need to re-read the posts here!
518
- // postBuild should receive loadedContent
519
- const blogPosts = await generateBlogPosts(contentPaths, context, options);
518
+ const {blogPosts} = content;
520
519
  if (!blogPosts.length) {
521
520
  return;
522
521
  }
@@ -6,8 +6,7 @@
6
6
  */
7
7
 
8
8
  import {truncate, linkify} from './blogUtils';
9
- import {parseQuery} from 'loader-utils';
10
- import {BlogMarkdownLoaderOptions} from './types';
9
+ import type {BlogMarkdownLoaderOptions} from './types';
11
10
  import type {LoaderContext} from 'webpack';
12
11
 
13
12
  export default function markdownLoader(
@@ -28,7 +27,7 @@ export default function markdownLoader(
28
27
 
29
28
  // Truncate content if requested (e.g: file.md?truncated=true).
30
29
  const truncated: boolean | undefined = this.resourceQuery
31
- ? !!parseQuery(this.resourceQuery).truncated
30
+ ? !!new URLSearchParams(this.resourceQuery.slice(1)).get('truncated')
32
31
  : undefined;
33
32
 
34
33
  if (truncated) {
@@ -6,7 +6,145 @@
6
6
  */
7
7
 
8
8
  declare module '@docusaurus/plugin-content-blog' {
9
- export type Options = Partial<import('./types').UserPluginOptions>;
9
+ import type {RemarkAndRehypePluginOptions} from '@docusaurus/mdx-loader';
10
+ import type {FrontMatterTag} from '@docusaurus/utils';
11
+ import type {Overwrite} from 'utility-types';
12
+
13
+ export interface Assets {
14
+ image?: string;
15
+ authorsImageUrls: (string | undefined)[]; // Array of same size as the original MetaData.authors array
16
+ }
17
+
18
+ // We allow passing custom fields to authors, e.g., twitter
19
+ export interface Author extends Record<string, unknown> {
20
+ name?: string;
21
+ imageURL?: string;
22
+ url?: string;
23
+ title?: string;
24
+ }
25
+
26
+ export type BlogPostFrontMatter = {
27
+ id?: string;
28
+ title?: string;
29
+ description?: string;
30
+ tags?: FrontMatterTag[];
31
+ slug?: string;
32
+ draft?: boolean;
33
+ date?: Date | string; // Yaml automatically convert some string patterns as Date, but not all
34
+
35
+ authors?: BlogPostFrontMatterAuthors;
36
+
37
+ // We may want to deprecate those older author front matter fields later:
38
+ author?: string;
39
+ author_title?: string;
40
+ author_url?: string;
41
+ author_image_url?: string;
42
+
43
+ /** @deprecated */
44
+ authorTitle?: string;
45
+ /** @deprecated */
46
+ authorURL?: string;
47
+ /** @deprecated */
48
+ authorImageURL?: string;
49
+
50
+ image?: string;
51
+ keywords?: string[];
52
+ hide_table_of_contents?: boolean;
53
+ toc_min_heading_level?: number;
54
+ toc_max_heading_level?: number;
55
+ };
56
+
57
+ export type BlogPostFrontMatterAuthor = Record<string, unknown> & {
58
+ key?: string;
59
+ name?: string;
60
+ imageURL?: string;
61
+ url?: string;
62
+ title?: string;
63
+ };
64
+
65
+ // All the possible variants that the user can use for convenience
66
+ export type BlogPostFrontMatterAuthors =
67
+ | string
68
+ | BlogPostFrontMatterAuthor
69
+ | (string | BlogPostFrontMatterAuthor)[];
70
+
71
+ export type EditUrlFunction = (editUrlParams: {
72
+ blogDirPath: string;
73
+ blogPath: string;
74
+ permalink: string;
75
+ locale: string;
76
+ }) => string | undefined;
77
+
78
+ export type FeedType = 'rss' | 'atom' | 'json';
79
+ export type FeedOptions = {
80
+ type?: FeedType[] | null;
81
+ title?: string;
82
+ description?: string;
83
+ copyright: string;
84
+ language?: string;
85
+ };
86
+ // Feed options, as provided by user config
87
+ export type UserFeedOptions = Overwrite<
88
+ Partial<FeedOptions>,
89
+ {type?: FeedOptions['type'] | 'all'} // Handle the type: "all" shortcut
90
+ >;
91
+
92
+ // Duplicate from ngryman/reading-time to keep stability of API
93
+ type ReadingTimeOptions = {
94
+ wordsPerMinute?: number;
95
+ wordBound?: (char: string) => boolean;
96
+ };
97
+
98
+ export type ReadingTimeFunction = (params: {
99
+ content: string;
100
+ frontMatter?: BlogPostFrontMatter & Record<string, unknown>;
101
+ options?: ReadingTimeOptions;
102
+ }) => number;
103
+
104
+ export type ReadingTimeFunctionOption = (
105
+ params: Required<Omit<Parameters<ReadingTimeFunction>[0], 'options'>> & {
106
+ defaultReadingTime: ReadingTimeFunction;
107
+ },
108
+ ) => number | undefined;
109
+
110
+ export type PluginOptions = RemarkAndRehypePluginOptions & {
111
+ id?: string;
112
+ path: string;
113
+ routeBasePath: string;
114
+ tagsBasePath: string;
115
+ archiveBasePath: string | null;
116
+ include: string[];
117
+ exclude: string[];
118
+ postsPerPage: number | 'ALL';
119
+ blogListComponent: string;
120
+ blogPostComponent: string;
121
+ blogTagsListComponent: string;
122
+ blogTagsPostsComponent: string;
123
+ blogTitle: string;
124
+ blogDescription: string;
125
+ blogSidebarCount: number | 'ALL';
126
+ blogSidebarTitle: string;
127
+ truncateMarker: RegExp;
128
+ showReadingTime: boolean;
129
+ feedOptions: {
130
+ type?: FeedType[] | null;
131
+ title?: string;
132
+ description?: string;
133
+ copyright: string;
134
+ language?: string;
135
+ };
136
+ editUrl?: string | EditUrlFunction;
137
+ editLocalizedFiles?: boolean;
138
+ admonitions: Record<string, unknown>;
139
+ authorsMapPath: string;
140
+ readingTime: ReadingTimeFunctionOption;
141
+ sortPosts: 'ascending' | 'descending';
142
+ };
143
+ // Options, as provided in the user config (before normalization)
144
+ export type Options = Overwrite<
145
+ Partial<PluginOptions>,
146
+ {feedOptions?: UserFeedOptions}
147
+ >;
10
148
  }
11
149
 
12
150
  declare module '@theme/BlogSidebar' {
@@ -27,9 +165,13 @@ declare module '@theme/BlogSidebar' {
27
165
  declare module '@theme/BlogPostPage' {
28
166
  import type {BlogSidebar} from '@theme/BlogSidebar';
29
167
  import type {TOCItem} from '@docusaurus/types';
168
+ import type {
169
+ BlogPostFrontMatter,
170
+ Author,
171
+ Assets,
172
+ } from '@docusaurus/plugin-content-blog';
30
173
 
31
- export type FrontMatter = import('./blogFrontMatter').BlogPostFrontMatter;
32
- export type Assets = import('./types').Assets;
174
+ export type FrontMatter = BlogPostFrontMatter;
33
175
 
34
176
  export type Metadata = {
35
177
  readonly title: string;
@@ -42,7 +184,8 @@ declare module '@theme/BlogPostPage' {
42
184
  readonly truncated?: string;
43
185
  readonly nextItem?: {readonly title: string; readonly permalink: string};
44
186
  readonly prevItem?: {readonly title: string; readonly permalink: string};
45
- readonly authors: import('./types').Author[];
187
+ readonly authors: Author[];
188
+ readonly frontMatter: FrontMatter & Record<string, unknown>;
46
189
  readonly tags: readonly {
47
190
  readonly label: string;
48
191
  readonly permalink: string;
@@ -13,7 +13,7 @@ import {
13
13
  URISchema,
14
14
  } from '@docusaurus/utils-validation';
15
15
  import {GlobExcludeDefault} from '@docusaurus/utils';
16
- import {PluginOptions} from './types';
16
+ import type {PluginOptions} from '@docusaurus/plugin-content-blog';
17
17
 
18
18
  export const DEFAULT_OPTIONS: PluginOptions = {
19
19
  feedOptions: {type: ['rss', 'atom'], copyright: ''},
@@ -47,7 +47,9 @@ export const DEFAULT_OPTIONS: PluginOptions = {
47
47
 
48
48
  export const PluginOptionSchema = Joi.object<PluginOptions>({
49
49
  path: Joi.string().default(DEFAULT_OPTIONS.path),
50
- archiveBasePath: Joi.string().default(DEFAULT_OPTIONS.archiveBasePath),
50
+ archiveBasePath: Joi.string()
51
+ .default(DEFAULT_OPTIONS.archiveBasePath)
52
+ .allow(null),
51
53
  routeBasePath: Joi.string()
52
54
  // '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
53
55
  // .allow('')
@@ -5,8 +5,9 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import type {BlogContent, PluginOptions, BlogPaginated} from './types';
8
+ import type {BlogContent, BlogPaginated} from './types';
9
9
  import type {TranslationFileContent, TranslationFiles} from '@docusaurus/types';
10
+ import type {PluginOptions} from '@docusaurus/plugin-content-blog';
10
11
 
11
12
  function translateListPage(
12
13
  blogListPaginated: BlogPaginated[],
@@ -51,13 +52,13 @@ export function translateContent(
51
52
  content: BlogContent,
52
53
  translationFiles: TranslationFiles,
53
54
  ): BlogContent {
54
- const [{content: optonsTranslations}] = translationFiles;
55
+ const [{content: optionsTranslations}] = translationFiles;
55
56
  return {
56
57
  ...content,
57
- blogSidebarTitle: optonsTranslations['sidebar.title'].message,
58
+ blogSidebarTitle: optionsTranslations['sidebar.title'].message,
58
59
  blogListPaginated: translateListPage(
59
60
  content.blogListPaginated,
60
- optonsTranslations,
61
+ optionsTranslations,
61
62
  ),
62
63
  };
63
64
  }
package/src/types.ts CHANGED
@@ -5,14 +5,15 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import type {RemarkAndRehypePluginOptions} from '@docusaurus/mdx-loader';
9
8
  import type {Tag} from '@docusaurus/utils';
10
9
  import type {
11
10
  BrokenMarkdownLink,
12
11
  ContentPaths,
13
12
  } from '@docusaurus/utils/lib/markdownLinks';
14
- import {Overwrite} from 'utility-types';
15
- import {BlogPostFrontMatter} from './blogFrontMatter';
13
+ import type {
14
+ BlogPostFrontMatter,
15
+ Author,
16
+ } from '@docusaurus/plugin-content-blog';
16
17
 
17
18
  export type BlogContentPaths = ContentPaths;
18
19
 
@@ -24,87 +25,6 @@ export interface BlogContent {
24
25
  blogTagsListPath: string | null;
25
26
  }
26
27
 
27
- export type FeedType = 'rss' | 'atom' | 'json';
28
-
29
- export type FeedOptions = {
30
- type?: FeedType[] | null;
31
- title?: string;
32
- description?: string;
33
- copyright: string;
34
- language?: string;
35
- };
36
-
37
- // Feed options, as provided by user config
38
- export type UserFeedOptions = Overwrite<
39
- Partial<FeedOptions>,
40
- {type?: FeedOptions['type'] | 'all'} // Handle the type: "all" shortcut
41
- >;
42
-
43
- export type EditUrlFunction = (editUrlParams: {
44
- blogDirPath: string;
45
- blogPath: string;
46
- permalink: string;
47
- locale: string;
48
- }) => string | undefined;
49
-
50
- // Duplicate from ngryman/reading-time to keep stability of API
51
- type ReadingTimeOptions = {
52
- wordsPerMinute?: number;
53
- wordBound?: (char: string) => boolean;
54
- };
55
-
56
- export type ReadingTimeFunction = (params: {
57
- content: string;
58
- frontMatter?: BlogPostFrontMatter & Record<string, unknown>;
59
- options?: ReadingTimeOptions;
60
- }) => number;
61
-
62
- export type ReadingTimeFunctionOption = (
63
- params: Required<Omit<Parameters<ReadingTimeFunction>[0], 'options'>> & {
64
- defaultReadingTime: ReadingTimeFunction;
65
- },
66
- ) => number | undefined;
67
-
68
- export type PluginOptions = RemarkAndRehypePluginOptions & {
69
- id?: string;
70
- path: string;
71
- routeBasePath: string;
72
- tagsBasePath: string;
73
- archiveBasePath: string;
74
- include: string[];
75
- exclude: string[];
76
- postsPerPage: number | 'ALL';
77
- blogListComponent: string;
78
- blogPostComponent: string;
79
- blogTagsListComponent: string;
80
- blogTagsPostsComponent: string;
81
- blogTitle: string;
82
- blogDescription: string;
83
- blogSidebarCount: number | 'ALL';
84
- blogSidebarTitle: string;
85
- truncateMarker: RegExp;
86
- showReadingTime: boolean;
87
- feedOptions: {
88
- type?: FeedType[] | null;
89
- title?: string;
90
- description?: string;
91
- copyright: string;
92
- language?: string;
93
- };
94
- editUrl?: string | EditUrlFunction;
95
- editLocalizedFiles?: boolean;
96
- admonitions: Record<string, unknown>;
97
- authorsMapPath: string;
98
- readingTime: ReadingTimeFunctionOption;
99
- sortPosts: 'ascending' | 'descending';
100
- };
101
-
102
- // Options, as provided in the user config (before normalization)
103
- export type UserPluginOptions = Overwrite<
104
- Partial<PluginOptions>,
105
- {feedOptions?: UserFeedOptions}
106
- >;
107
-
108
28
  export interface BlogTags {
109
29
  [key: string]: BlogTag;
110
30
  }
@@ -138,14 +58,6 @@ export interface BlogPaginated {
138
58
  items: string[];
139
59
  }
140
60
 
141
- // We allow passing custom fields to authors, e.g., twitter
142
- export interface Author extends Record<string, unknown> {
143
- name?: string;
144
- imageURL?: string;
145
- url?: string;
146
- title?: string;
147
- }
148
-
149
61
  export interface MetaData {
150
62
  permalink: string;
151
63
  source: string;
@@ -160,11 +72,7 @@ export interface MetaData {
160
72
  truncated: boolean;
161
73
  editUrl?: string;
162
74
  authors: Author[];
163
- }
164
-
165
- export interface Assets {
166
- image?: string;
167
- authorsImageUrls: (string | undefined)[]; // Array of same size as the original MetaData.authors array
75
+ frontMatter: BlogPostFrontMatter & Record<string, unknown>;
168
76
  }
169
77
 
170
78
  export interface Paginator {