@docusaurus/plugin-content-blog 0.0.0-6012 → 0.0.0-6016

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/blogUtils.js CHANGED
@@ -30,7 +30,7 @@ function truncate(fileString, truncateMarker) {
30
30
  function paginateBlogPosts({ blogPosts, basePageUrl, blogTitle, blogDescription, postsPerPageOption, pageBasePath, }) {
31
31
  const totalCount = blogPosts.length;
32
32
  const postsPerPage = postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption;
33
- const numberOfPages = Math.ceil(totalCount / postsPerPage);
33
+ const numberOfPages = Math.max(1, Math.ceil(totalCount / postsPerPage));
34
34
  const pages = [];
35
35
  function permalink(page) {
36
36
  return page > 0
@@ -236,7 +236,7 @@ async function processBlogSourceFile(blogSourceRelative, contentPaths, context,
236
236
  content,
237
237
  };
238
238
  }
239
- async function generateBlogPosts(contentPaths, context, options) {
239
+ async function generateBlogPosts(contentPaths, context, options, authorsMap) {
240
240
  const { include, exclude } = options;
241
241
  if (!(await fs_extra_1.default.pathExists(contentPaths.contentPath))) {
242
242
  return [];
@@ -245,10 +245,6 @@ async function generateBlogPosts(contentPaths, context, options) {
245
245
  cwd: contentPaths.contentPath,
246
246
  ignore: exclude,
247
247
  });
248
- const authorsMap = await (0, authors_1.getAuthorsMap)({
249
- contentPaths,
250
- authorsMapPath: options.authorsMapPath,
251
- });
252
248
  const tagsFile = await (0, utils_validation_1.getTagsFile)({ contentPaths, tags: options.tags });
253
249
  async function doProcessBlogSourceFile(blogSourceFile) {
254
250
  try {
package/lib/feed.d.ts CHANGED
@@ -4,14 +4,16 @@
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 { BlogContentPaths } from './types';
7
8
  import type { DocusaurusConfig, HtmlTags, LoadContext } from '@docusaurus/types';
8
9
  import type { PluginOptions, BlogPost } from '@docusaurus/plugin-content-blog';
9
- export declare function createBlogFeedFiles({ blogPosts: allBlogPosts, options, siteConfig, outDir, locale, }: {
10
+ export declare function createBlogFeedFiles({ blogPosts: allBlogPosts, options, siteConfig, outDir, locale, contentPaths, }: {
10
11
  blogPosts: BlogPost[];
11
12
  options: PluginOptions;
12
13
  siteConfig: DocusaurusConfig;
13
14
  outDir: string;
14
15
  locale: string;
16
+ contentPaths: BlogContentPaths;
15
17
  }): Promise<void>;
16
18
  export declare function createFeedHtmlHeadTags({ context, options, }: {
17
19
  context: LoadContext;
package/lib/feed.js CHANGED
@@ -11,12 +11,12 @@ exports.createFeedHtmlHeadTags = createFeedHtmlHeadTags;
11
11
  const tslib_1 = require("tslib");
12
12
  const path_1 = tslib_1.__importDefault(require("path"));
13
13
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
14
- const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
15
14
  const feed_1 = require("feed");
16
15
  const srcset = tslib_1.__importStar(require("srcset"));
17
16
  const utils_1 = require("@docusaurus/utils");
18
17
  const utils_common_1 = require("@docusaurus/utils-common");
19
18
  const cheerio_1 = require("cheerio");
19
+ const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
20
20
  async function generateBlogFeed({ blogPosts, options, siteConfig, outDir, locale, }) {
21
21
  if (!blogPosts.length) {
22
22
  return null;
@@ -106,25 +106,72 @@ async function defaultCreateFeedItems({ blogPosts, siteConfig, outDir, }) {
106
106
  return feedItem;
107
107
  }));
108
108
  }
109
- async function createBlogFeedFile({ feed, feedType, generatePath, }) {
110
- const [feedContent, feedPath] = (() => {
111
- switch (feedType) {
112
- case 'rss':
113
- return [feed.rss2(), 'rss.xml'];
114
- case 'json':
115
- return [feed.json1(), 'feed.json'];
116
- case 'atom':
117
- return [feed.atom1(), 'atom.xml'];
118
- default:
119
- throw new Error(`Feed type ${feedType} not supported.`);
120
- }
121
- })();
109
+ async function resolveXsltFilePaths({ xsltFilePath, contentPaths, }) {
110
+ const xsltAbsolutePath = path_1.default.isAbsolute(xsltFilePath)
111
+ ? xsltFilePath
112
+ : (await (0, utils_1.getDataFilePath)({ filePath: xsltFilePath, contentPaths })) ??
113
+ path_1.default.resolve(contentPaths.contentPath, xsltFilePath);
114
+ if (!(await fs_extra_1.default.pathExists(xsltAbsolutePath))) {
115
+ throw new Error(logger_1.default.interpolate `Blog feed XSLT file not found at path=${path_1.default.relative(process.cwd(), xsltAbsolutePath)}`);
116
+ }
117
+ const parsedPath = path_1.default.parse(xsltAbsolutePath);
118
+ const cssAbsolutePath = path_1.default.resolve(parsedPath.dir, `${parsedPath.name}.css`);
119
+ if (!(await fs_extra_1.default.pathExists(xsltAbsolutePath))) {
120
+ throw new Error(logger_1.default.interpolate `Blog feed XSLT file was found at path=${path_1.default.relative(process.cwd(), xsltAbsolutePath)}
121
+ But its expected co-located CSS file could not be found at path=${path_1.default.relative(process.cwd(), cssAbsolutePath)}
122
+ If you want to provide a custom XSLT file, you must provide a CSS file with the exact same name.`);
123
+ }
124
+ return { xsltAbsolutePath, cssAbsolutePath };
125
+ }
126
+ async function generateXsltFiles({ xsltFilePath, generatePath, contentPaths, }) {
127
+ const { xsltAbsolutePath, cssAbsolutePath } = await resolveXsltFilePaths({
128
+ xsltFilePath,
129
+ contentPaths,
130
+ });
131
+ const xsltOutputPath = path_1.default.join(generatePath, path_1.default.basename(xsltAbsolutePath));
132
+ const cssOutputPath = path_1.default.join(generatePath, path_1.default.basename(cssAbsolutePath));
133
+ await fs_extra_1.default.copy(xsltAbsolutePath, xsltOutputPath);
134
+ await fs_extra_1.default.copy(cssAbsolutePath, cssOutputPath);
135
+ }
136
+ // This modifies the XML feed content to add a relative href to the XSLT file
137
+ // Good enough for now: we probably don't need a full XML parser just for that
138
+ // See also https://darekkay.com/blog/rss-styling/
139
+ function injectXslt({ feedContent, xsltFilePath, }) {
140
+ return feedContent.replace('<?xml version="1.0" encoding="utf-8"?>', `<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="${path_1.default.basename(xsltFilePath)}"?>`);
141
+ }
142
+ const FeedConfigs = {
143
+ rss: {
144
+ outputFileName: 'rss.xml',
145
+ getContent: (feed) => feed.rss2(),
146
+ getXsltFilePath: (xslt) => xslt.rss,
147
+ },
148
+ atom: {
149
+ outputFileName: 'atom.xml',
150
+ getContent: (feed) => feed.atom1(),
151
+ getXsltFilePath: (xslt) => xslt.atom,
152
+ },
153
+ json: {
154
+ outputFileName: 'feed.json',
155
+ getContent: (feed) => feed.json1(),
156
+ getXsltFilePath: () => null,
157
+ },
158
+ };
159
+ async function createBlogFeedFile({ feed, feedType, generatePath, feedOptions, contentPaths, }) {
122
160
  try {
123
- await fs_extra_1.default.outputFile(path_1.default.join(generatePath, feedPath), feedContent);
161
+ const feedConfig = FeedConfigs[feedType];
162
+ let feedContent = feedConfig.getContent(feed);
163
+ const xsltFilePath = feedConfig.getXsltFilePath(feedOptions.xslt);
164
+ if (xsltFilePath) {
165
+ await generateXsltFiles({ xsltFilePath, contentPaths, generatePath });
166
+ feedContent = injectXslt({ feedContent, xsltFilePath });
167
+ }
168
+ const outputPath = path_1.default.join(generatePath, feedConfig.outputFileName);
169
+ await fs_extra_1.default.outputFile(outputPath, feedContent);
124
170
  }
125
171
  catch (err) {
126
- logger_1.default.error(`Generating ${feedType} feed failed.`);
127
- throw err;
172
+ throw new Error(`Generating ${feedType} feed failed.`, {
173
+ cause: err,
174
+ });
128
175
  }
129
176
  }
130
177
  function shouldBeInFeed(blogPost) {
@@ -132,7 +179,7 @@ function shouldBeInFeed(blogPost) {
132
179
  blogPost.metadata.frontMatter.unlisted;
133
180
  return !excluded;
134
181
  }
135
- async function createBlogFeedFiles({ blogPosts: allBlogPosts, options, siteConfig, outDir, locale, }) {
182
+ async function createBlogFeedFiles({ blogPosts: allBlogPosts, options, siteConfig, outDir, locale, contentPaths, }) {
136
183
  const blogPosts = allBlogPosts.filter(shouldBeInFeed);
137
184
  const feed = await generateBlogFeed({
138
185
  blogPosts,
@@ -149,6 +196,8 @@ async function createBlogFeedFiles({ blogPosts: allBlogPosts, options, siteConfi
149
196
  feed,
150
197
  feedType,
151
198
  generatePath: path_1.default.join(outDir, options.routeBasePath),
199
+ feedOptions: options.feedOptions,
200
+ contentPaths,
152
201
  })));
153
202
  }
154
203
  function createFeedHtmlHeadTags({ context, options, }) {
package/lib/index.js CHANGED
@@ -18,6 +18,7 @@ const footnoteIDFixer_1 = tslib_1.__importDefault(require("./remark/footnoteIDFi
18
18
  const translations_1 = require("./translations");
19
19
  const feed_1 = require("./feed");
20
20
  const routes_1 = require("./routes");
21
+ const authorsMap_1 = require("./authorsMap");
21
22
  const PluginName = 'docusaurus-plugin-content-blog';
22
23
  // TODO this is bad, we should have a better way to do this (new lifecycle?)
23
24
  // The source to permalink is currently a mutable map passed to the mdx loader
@@ -88,10 +89,20 @@ async function pluginContentBlog(context, options) {
88
89
  },
89
90
  // Fetches blog contents and returns metadata for the necessary routes.
90
91
  async loadContent() {
91
- const { postsPerPage: postsPerPageOption, routeBasePath, tagsBasePath, blogDescription, blogTitle, blogSidebarTitle, pageBasePath, } = options;
92
+ const { postsPerPage: postsPerPageOption, routeBasePath, tagsBasePath, blogDescription, blogTitle, blogSidebarTitle, pageBasePath, authorsBasePath, authorsMapPath, } = options;
92
93
  const baseBlogUrl = (0, utils_1.normalizeUrl)([baseUrl, routeBasePath]);
93
94
  const blogTagsListPath = (0, utils_1.normalizeUrl)([baseBlogUrl, tagsBasePath]);
94
- let blogPosts = await (0, blogUtils_1.generateBlogPosts)(contentPaths, context, options);
95
+ const authorsMap = await (0, authorsMap_1.getAuthorsMap)({
96
+ contentPaths,
97
+ authorsMapPath,
98
+ authorsBaseRoutePath: (0, utils_1.normalizeUrl)([
99
+ baseUrl,
100
+ routeBasePath,
101
+ authorsBasePath,
102
+ ]),
103
+ });
104
+ (0, authorsMap_1.checkAuthorsMapPermalinkCollisions)(authorsMap);
105
+ let blogPosts = await (0, blogUtils_1.generateBlogPosts)(contentPaths, context, options, authorsMap);
95
106
  blogPosts = await (0, blogUtils_1.applyProcessBlogPosts)({
96
107
  blogPosts,
97
108
  processBlogPosts: options.processBlogPosts,
@@ -104,6 +115,7 @@ async function pluginContentBlog(context, options) {
104
115
  blogListPaginated: [],
105
116
  blogTags: {},
106
117
  blogTagsListPath,
118
+ authorsMap,
107
119
  };
108
120
  }
109
121
  // Collocate next and prev metadata.
@@ -146,6 +158,7 @@ async function pluginContentBlog(context, options) {
146
158
  blogListPaginated,
147
159
  blogTags,
148
160
  blogTagsListPath,
161
+ authorsMap,
149
162
  };
150
163
  },
151
164
  async contentLoaded({ content, actions }) {
@@ -253,6 +266,7 @@ async function pluginContentBlog(context, options) {
253
266
  outDir,
254
267
  siteConfig,
255
268
  locale: currentLocale,
269
+ contentPaths,
256
270
  });
257
271
  },
258
272
  injectHtmlTags({ content }) {
package/lib/options.d.ts CHANGED
@@ -7,4 +7,8 @@
7
7
  import type { PluginOptions, Options } from '@docusaurus/plugin-content-blog';
8
8
  import type { OptionValidationContext } from '@docusaurus/types';
9
9
  export declare const DEFAULT_OPTIONS: PluginOptions;
10
+ export declare const XSLTBuiltInPaths: {
11
+ rss: string;
12
+ atom: string;
13
+ };
10
14
  export declare function validateOptions({ validate, options, }: OptionValidationContext<Options | undefined, PluginOptions>): PluginOptions;
package/lib/options.js CHANGED
@@ -6,12 +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.DEFAULT_OPTIONS = void 0;
9
+ exports.XSLTBuiltInPaths = exports.DEFAULT_OPTIONS = void 0;
10
10
  exports.validateOptions = validateOptions;
11
+ const tslib_1 = require("tslib");
12
+ const path_1 = tslib_1.__importDefault(require("path"));
11
13
  const utils_validation_1 = require("@docusaurus/utils-validation");
12
14
  const utils_1 = require("@docusaurus/utils");
13
15
  exports.DEFAULT_OPTIONS = {
14
- feedOptions: { type: ['rss', 'atom'], copyright: '', limit: 20 },
16
+ feedOptions: {
17
+ type: ['rss', 'atom'],
18
+ copyright: '',
19
+ limit: 20,
20
+ xslt: {
21
+ rss: null,
22
+ atom: null,
23
+ },
24
+ },
15
25
  beforeDefaultRehypePlugins: [],
16
26
  beforeDefaultRemarkPlugins: [],
17
27
  admonitions: true,
@@ -22,6 +32,8 @@ exports.DEFAULT_OPTIONS = {
22
32
  showReadingTime: true,
23
33
  blogTagsPostsComponent: '@theme/BlogTagsPostsPage',
24
34
  blogTagsListComponent: '@theme/BlogTagsListPage',
35
+ blogAuthorsPostsComponent: '@theme/Blog/Pages/BlogAuthorsPostsPage',
36
+ blogAuthorsListComponent: '@theme/Blog/Pages/BlogAuthorsListPage',
25
37
  blogPostComponent: '@theme/BlogPostPage',
26
38
  blogListComponent: '@theme/BlogListPage',
27
39
  blogArchiveComponent: '@theme/BlogArchivePage',
@@ -46,8 +58,75 @@ exports.DEFAULT_OPTIONS = {
46
58
  processBlogPosts: async () => undefined,
47
59
  onInlineTags: 'warn',
48
60
  tags: undefined,
61
+ authorsBasePath: 'authors',
49
62
  onInlineAuthors: 'warn',
50
63
  };
64
+ exports.XSLTBuiltInPaths = {
65
+ rss: path_1.default.resolve(__dirname, '..', 'assets', 'rss.xsl'),
66
+ atom: path_1.default.resolve(__dirname, '..', 'assets', 'atom.xsl'),
67
+ };
68
+ function normalizeXsltOption(option, type) {
69
+ if (typeof option === 'string') {
70
+ return option;
71
+ }
72
+ if (option === true) {
73
+ return exports.XSLTBuiltInPaths[type];
74
+ }
75
+ return null;
76
+ }
77
+ function createXSLTFilePathSchema(type) {
78
+ return utils_validation_1.Joi.alternatives()
79
+ .try(utils_validation_1.Joi.string().required(), utils_validation_1.Joi.boolean()
80
+ .allow(null, () => undefined)
81
+ .custom((val) => normalizeXsltOption(val, type)))
82
+ .optional()
83
+ .default(null);
84
+ }
85
+ const FeedXSLTOptionsSchema = utils_validation_1.Joi.alternatives()
86
+ .try(utils_validation_1.Joi.object({
87
+ rss: createXSLTFilePathSchema('rss'),
88
+ atom: createXSLTFilePathSchema('atom'),
89
+ }).required(), utils_validation_1.Joi.boolean()
90
+ .allow(null, () => undefined)
91
+ .custom((val) => ({
92
+ rss: normalizeXsltOption(val, 'rss'),
93
+ atom: normalizeXsltOption(val, 'atom'),
94
+ })))
95
+ .optional()
96
+ .custom((val) => {
97
+ if (val === null) {
98
+ return {
99
+ rss: null,
100
+ atom: null,
101
+ };
102
+ }
103
+ return val;
104
+ })
105
+ .default(exports.DEFAULT_OPTIONS.feedOptions.xslt);
106
+ const FeedOptionsSchema = utils_validation_1.Joi.object({
107
+ type: utils_validation_1.Joi.alternatives()
108
+ .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'), {
109
+ then: utils_validation_1.Joi.custom((val) => val === 'all' ? ['rss', 'atom', 'json'] : [val]),
110
+ }))
111
+ .allow(null)
112
+ .default(exports.DEFAULT_OPTIONS.feedOptions.type),
113
+ xslt: FeedXSLTOptionsSchema,
114
+ title: utils_validation_1.Joi.string().allow(''),
115
+ description: utils_validation_1.Joi.string().allow(''),
116
+ // Only add default value when user actually wants a feed (type is not null)
117
+ copyright: utils_validation_1.Joi.when('type', {
118
+ is: utils_validation_1.Joi.any().valid(null),
119
+ then: utils_validation_1.Joi.string().optional(),
120
+ otherwise: utils_validation_1.Joi.string()
121
+ .allow('')
122
+ .default(exports.DEFAULT_OPTIONS.feedOptions.copyright),
123
+ }),
124
+ language: utils_validation_1.Joi.string(),
125
+ createFeedItems: utils_validation_1.Joi.function(),
126
+ limit: utils_validation_1.Joi.alternatives()
127
+ .try(utils_validation_1.Joi.number(), utils_validation_1.Joi.valid(null), utils_validation_1.Joi.valid(false))
128
+ .default(exports.DEFAULT_OPTIONS.feedOptions.limit),
129
+ }).default(exports.DEFAULT_OPTIONS.feedOptions);
51
130
  const PluginOptionSchema = utils_validation_1.Joi.object({
52
131
  path: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.path),
53
132
  archiveBasePath: utils_validation_1.Joi.string()
@@ -65,6 +144,8 @@ const PluginOptionSchema = utils_validation_1.Joi.object({
65
144
  blogPostComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.blogPostComponent),
66
145
  blogTagsListComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.blogTagsListComponent),
67
146
  blogTagsPostsComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.blogTagsPostsComponent),
147
+ blogAuthorsPostsComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.blogAuthorsPostsComponent),
148
+ blogAuthorsListComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.blogAuthorsListComponent),
68
149
  blogArchiveComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.blogArchiveComponent),
69
150
  blogTitle: utils_validation_1.Joi.string().allow('').default(exports.DEFAULT_OPTIONS.blogTitle),
70
151
  blogDescription: utils_validation_1.Joi.string()
@@ -84,29 +165,7 @@ const PluginOptionSchema = utils_validation_1.Joi.object({
84
165
  truncateMarker: utils_validation_1.Joi.object().default(exports.DEFAULT_OPTIONS.truncateMarker),
85
166
  beforeDefaultRemarkPlugins: utils_validation_1.RemarkPluginsSchema.default(exports.DEFAULT_OPTIONS.beforeDefaultRemarkPlugins),
86
167
  beforeDefaultRehypePlugins: utils_validation_1.RehypePluginsSchema.default(exports.DEFAULT_OPTIONS.beforeDefaultRehypePlugins),
87
- feedOptions: utils_validation_1.Joi.object({
88
- type: utils_validation_1.Joi.alternatives()
89
- .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'), {
90
- then: utils_validation_1.Joi.custom((val) => val === 'all' ? ['rss', 'atom', 'json'] : [val]),
91
- }))
92
- .allow(null)
93
- .default(exports.DEFAULT_OPTIONS.feedOptions.type),
94
- title: utils_validation_1.Joi.string().allow(''),
95
- description: utils_validation_1.Joi.string().allow(''),
96
- // Only add default value when user actually wants a feed (type is not null)
97
- copyright: utils_validation_1.Joi.when('type', {
98
- is: utils_validation_1.Joi.any().valid(null),
99
- then: utils_validation_1.Joi.string().optional(),
100
- otherwise: utils_validation_1.Joi.string()
101
- .allow('')
102
- .default(exports.DEFAULT_OPTIONS.feedOptions.copyright),
103
- }),
104
- language: utils_validation_1.Joi.string(),
105
- createFeedItems: utils_validation_1.Joi.function(),
106
- limit: utils_validation_1.Joi.alternatives()
107
- .try(utils_validation_1.Joi.number(), utils_validation_1.Joi.valid(null), utils_validation_1.Joi.valid(false))
108
- .default(exports.DEFAULT_OPTIONS.feedOptions.limit),
109
- }).default(exports.DEFAULT_OPTIONS.feedOptions),
168
+ feedOptions: FeedOptionsSchema,
110
169
  authorsMapPath: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.authorsMapPath),
111
170
  readingTime: utils_validation_1.Joi.function().default(() => exports.DEFAULT_OPTIONS.readingTime),
112
171
  sortPosts: utils_validation_1.Joi.string()
@@ -124,6 +183,9 @@ const PluginOptionSchema = utils_validation_1.Joi.object({
124
183
  .disallow('')
125
184
  .allow(null, false)
126
185
  .default(() => exports.DEFAULT_OPTIONS.tags),
186
+ authorsBasePath: utils_validation_1.Joi.string()
187
+ .default(exports.DEFAULT_OPTIONS.authorsBasePath)
188
+ .disallow(''),
127
189
  onInlineAuthors: utils_validation_1.Joi.string()
128
190
  .equal('ignore', 'log', 'warn', 'throw')
129
191
  .default(exports.DEFAULT_OPTIONS.onInlineAuthors),
package/lib/props.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import type { TagsListItem, TagModule } from '@docusaurus/utils';
8
- import type { BlogPost, BlogSidebar, BlogTag, BlogTags } from '@docusaurus/plugin-content-blog';
8
+ import type { AuthorItemProp, AuthorWithKey, BlogPost, BlogSidebar, BlogTag, BlogTags } from '@docusaurus/plugin-content-blog';
9
9
  export declare function toTagsProp({ blogTags }: {
10
10
  blogTags: BlogTags;
11
11
  }): TagsListItem[];
@@ -13,6 +13,10 @@ export declare function toTagProp({ blogTagsListPath, tag, }: {
13
13
  blogTagsListPath: string;
14
14
  tag: BlogTag;
15
15
  }): TagModule;
16
+ export declare function toAuthorItemProp({ author, count, }: {
17
+ author: AuthorWithKey;
18
+ count: number;
19
+ }): AuthorItemProp;
16
20
  export declare function toBlogSidebarProp({ blogSidebarTitle, blogPosts, }: {
17
21
  blogSidebarTitle: string;
18
22
  blogPosts: BlogPost[];
package/lib/props.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toTagsProp = toTagsProp;
4
4
  exports.toTagProp = toTagProp;
5
+ exports.toAuthorItemProp = toAuthorItemProp;
5
6
  exports.toBlogSidebarProp = toBlogSidebarProp;
6
7
  function toTagsProp({ blogTags }) {
7
8
  return Object.values(blogTags)
@@ -23,6 +24,12 @@ function toTagProp({ blogTagsListPath, tag, }) {
23
24
  unlisted: tag.unlisted,
24
25
  };
25
26
  }
27
+ function toAuthorItemProp({ author, count, }) {
28
+ return {
29
+ ...author,
30
+ count,
31
+ };
32
+ }
26
33
  function toBlogSidebarProp({ blogSidebarTitle, blogPosts, }) {
27
34
  return {
28
35
  title: blogSidebarTitle,
package/lib/routes.js CHANGED
@@ -13,15 +13,21 @@ const lodash_1 = tslib_1.__importDefault(require("lodash"));
13
13
  const utils_1 = require("@docusaurus/utils");
14
14
  const blogUtils_1 = require("./blogUtils");
15
15
  const props_1 = require("./props");
16
+ const authors_1 = require("./authors");
16
17
  async function createAllRoutes(param) {
17
18
  const routes = await buildAllRoutes(param);
18
19
  routes.forEach(param.actions.addRoute);
19
20
  }
20
21
  async function buildAllRoutes({ baseUrl, content, actions, options, aliasedSource, }) {
21
- const { blogListComponent, blogPostComponent, blogTagsListComponent, blogTagsPostsComponent, blogArchiveComponent, routeBasePath, archiveBasePath, blogTitle, } = options;
22
+ const { blogListComponent, blogPostComponent, blogTagsListComponent, blogAuthorsListComponent, blogAuthorsPostsComponent, blogTagsPostsComponent, blogArchiveComponent, routeBasePath, archiveBasePath, blogTitle, authorsBasePath, postsPerPage, blogDescription, } = options;
22
23
  const pluginId = options.id;
23
24
  const { createData } = actions;
24
- const { blogSidebarTitle, blogPosts, blogListPaginated, blogTags, blogTagsListPath, } = content;
25
+ const { blogSidebarTitle, blogPosts, blogListPaginated, blogTags, blogTagsListPath, authorsMap, } = content;
26
+ const authorsListPath = (0, utils_1.normalizeUrl)([
27
+ baseUrl,
28
+ routeBasePath,
29
+ authorsBasePath,
30
+ ]);
25
31
  const listedBlogPosts = blogPosts.filter(blogUtils_1.shouldBeListed);
26
32
  const blogPostsById = lodash_1.default.keyBy(blogPosts, (post) => post.id);
27
33
  function getBlogPostById(id) {
@@ -46,6 +52,7 @@ async function buildAllRoutes({ baseUrl, content, actions, options, aliasedSourc
46
52
  const blogMetadata = {
47
53
  blogBasePath: (0, utils_1.normalizeUrl)([baseUrl, routeBasePath]),
48
54
  blogTitle,
55
+ authorsListPath,
49
56
  };
50
57
  const modulePath = await createData(`blogMetadata-${pluginId}.json`, blogMetadata);
51
58
  return aliasedSource(modulePath);
@@ -168,10 +175,76 @@ async function buildAllRoutes({ baseUrl, content, actions, options, aliasedSourc
168
175
  const tagsPaginatedRoutes = Object.values(blogTags).flatMap(createTagPaginatedRoutes);
169
176
  return [tagsListRoute, ...tagsPaginatedRoutes];
170
177
  }
178
+ function createAuthorsRoutes() {
179
+ if (authorsMap === undefined || Object.keys(authorsMap).length === 0) {
180
+ return [];
181
+ }
182
+ const blogPostsByAuthorKey = (0, authors_1.groupBlogPostsByAuthorKey)({
183
+ authorsMap,
184
+ blogPosts,
185
+ });
186
+ const authors = Object.values(authorsMap);
187
+ return [
188
+ createAuthorListRoute(),
189
+ ...authors.flatMap(createAuthorPaginatedRoute),
190
+ ];
191
+ function createAuthorListRoute() {
192
+ return {
193
+ path: authorsListPath,
194
+ component: blogAuthorsListComponent,
195
+ exact: true,
196
+ modules: {
197
+ sidebar: sidebarModulePath,
198
+ },
199
+ props: {
200
+ authors: authors.map((author) => (0, props_1.toAuthorItemProp)({
201
+ author,
202
+ count: blogPostsByAuthorKey[author.key]?.length ?? 0,
203
+ })),
204
+ },
205
+ context: {
206
+ blogMetadata: blogMetadataModulePath,
207
+ },
208
+ };
209
+ }
210
+ function createAuthorPaginatedRoute(author) {
211
+ const authorBlogPosts = blogPostsByAuthorKey[author.key] ?? [];
212
+ if (!author.page) {
213
+ return [];
214
+ }
215
+ const pages = (0, blogUtils_1.paginateBlogPosts)({
216
+ blogPosts: authorBlogPosts,
217
+ basePageUrl: author.page.permalink,
218
+ blogDescription,
219
+ blogTitle,
220
+ pageBasePath: authorsBasePath,
221
+ postsPerPageOption: postsPerPage,
222
+ });
223
+ return pages.map(({ metadata, items }) => {
224
+ return {
225
+ path: metadata.permalink,
226
+ component: blogAuthorsPostsComponent,
227
+ exact: true,
228
+ modules: {
229
+ items: blogPostItemsModule(items),
230
+ sidebar: sidebarModulePath,
231
+ },
232
+ props: {
233
+ author: (0, props_1.toAuthorItemProp)({ author, count: authorBlogPosts.length }),
234
+ listMetadata: metadata,
235
+ },
236
+ context: {
237
+ blogMetadata: blogMetadataModulePath,
238
+ },
239
+ };
240
+ });
241
+ }
242
+ }
171
243
  return [
172
244
  ...createBlogPostRoutes(),
173
245
  ...createBlogPostsPaginatedRoutes(),
174
246
  ...createTagsRoutes(),
175
247
  ...createArchiveRoute(),
248
+ ...createAuthorsRoutes(),
176
249
  ];
177
250
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/plugin-content-blog",
3
- "version": "0.0.0-6012",
3
+ "version": "0.0.0-6016",
4
4
  "description": "Blog plugin for Docusaurus.",
5
5
  "main": "lib/index.js",
6
6
  "types": "src/plugin-content-blog.d.ts",
@@ -31,14 +31,14 @@
31
31
  },
32
32
  "license": "MIT",
33
33
  "dependencies": {
34
- "@docusaurus/core": "0.0.0-6012",
35
- "@docusaurus/logger": "0.0.0-6012",
36
- "@docusaurus/mdx-loader": "0.0.0-6012",
37
- "@docusaurus/theme-common": "0.0.0-6012",
38
- "@docusaurus/types": "0.0.0-6012",
39
- "@docusaurus/utils": "0.0.0-6012",
40
- "@docusaurus/utils-common": "0.0.0-6012",
41
- "@docusaurus/utils-validation": "0.0.0-6012",
34
+ "@docusaurus/core": "0.0.0-6016",
35
+ "@docusaurus/logger": "0.0.0-6016",
36
+ "@docusaurus/mdx-loader": "0.0.0-6016",
37
+ "@docusaurus/theme-common": "0.0.0-6016",
38
+ "@docusaurus/types": "0.0.0-6016",
39
+ "@docusaurus/utils": "0.0.0-6016",
40
+ "@docusaurus/utils-common": "0.0.0-6016",
41
+ "@docusaurus/utils-validation": "0.0.0-6016",
42
42
  "cheerio": "^1.0.0-rc.12",
43
43
  "feed": "^4.2.2",
44
44
  "fs-extra": "^11.1.1",
@@ -59,7 +59,8 @@
59
59
  "node": ">=18.0"
60
60
  },
61
61
  "devDependencies": {
62
- "@total-typescript/shoehorn": "^0.1.2"
62
+ "@total-typescript/shoehorn": "^0.1.2",
63
+ "tree-node-cli": "^1.6.0"
63
64
  },
64
- "gitHead": "052fdffd244b09f6cdfba0e03511170037da5681"
65
+ "gitHead": "c51435db0c58b94667da1c745f1635de65240c63"
65
66
  }