@docusaurus/plugin-content-blog 2.0.0-beta.fbdeefcac → 2.0.0-rc.1

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 (57) hide show
  1. package/lib/authors.d.ts +22 -0
  2. package/lib/authors.js +122 -0
  3. package/lib/blogUtils.d.ts +27 -7
  4. package/lib/blogUtils.js +201 -145
  5. package/lib/feed.d.ts +15 -0
  6. package/lib/feed.js +102 -0
  7. package/lib/frontMatter.d.ts +10 -0
  8. package/lib/{blogFrontMatter.js → frontMatter.js} +31 -19
  9. package/lib/index.d.ts +4 -4
  10. package/lib/index.js +165 -201
  11. package/lib/markdownLoader.d.ts +3 -6
  12. package/lib/markdownLoader.js +6 -7
  13. package/lib/options.d.ts +10 -0
  14. package/lib/{pluginOptionSchema.js → options.js} +44 -14
  15. package/lib/remark/footnoteIDFixer.d.ts +14 -0
  16. package/lib/remark/footnoteIDFixer.js +29 -0
  17. package/lib/translations.d.ts +10 -0
  18. package/lib/translations.js +53 -0
  19. package/lib/types.d.ts +4 -109
  20. package/package.json +23 -19
  21. package/src/authors.ts +168 -0
  22. package/src/blogUtils.ts +306 -204
  23. package/src/feed.ts +171 -0
  24. package/src/frontMatter.ts +81 -0
  25. package/src/index.ts +230 -269
  26. package/src/markdownLoader.ts +11 -16
  27. package/src/{pluginOptionSchema.ts → options.ts} +57 -16
  28. package/src/plugin-content-blog.d.ts +587 -0
  29. package/src/remark/footnoteIDFixer.ts +29 -0
  30. package/src/translations.ts +69 -0
  31. package/src/types.ts +2 -128
  32. package/index.d.ts +0 -138
  33. package/lib/.tsbuildinfo +0 -1
  34. package/lib/blogFrontMatter.d.ts +0 -28
  35. package/lib/pluginOptionSchema.d.ts +0 -33
  36. package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  37. package/src/__tests__/__fixtures__/website/blog/complex-slug.md +0 -7
  38. package/src/__tests__/__fixtures__/website/blog/date-matter.md +0 -5
  39. package/src/__tests__/__fixtures__/website/blog/draft.md +0 -6
  40. package/src/__tests__/__fixtures__/website/blog/heading-as-title.md +0 -5
  41. package/src/__tests__/__fixtures__/website/blog/simple-slug.md +0 -7
  42. package/src/__tests__/__fixtures__/website/blog-with-ref/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  43. package/src/__tests__/__fixtures__/website/blog-with-ref/post-with-broken-links.md +0 -11
  44. package/src/__tests__/__fixtures__/website/blog-with-ref/post.md +0 -5
  45. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  46. package/src/__tests__/__fixtures__/website-blog-without-date/blog/no date.md +0 -1
  47. package/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap +0 -76
  48. package/src/__tests__/__snapshots__/linkify.test.ts.snap +0 -24
  49. package/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap +0 -5
  50. package/src/__tests__/blogFrontMatter.test.ts +0 -317
  51. package/src/__tests__/generateBlogFeed.test.ts +0 -100
  52. package/src/__tests__/index.test.ts +0 -336
  53. package/src/__tests__/linkify.test.ts +0 -93
  54. package/src/__tests__/pluginOptionSchema.test.ts +0 -150
  55. package/src/blogFrontMatter.ts +0 -88
  56. package/tsconfig.json +0 -9
  57. package/types.d.ts +0 -13
package/lib/index.js CHANGED
@@ -8,60 +8,56 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.validateOptions = void 0;
10
10
  const tslib_1 = require("tslib");
11
- const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
12
11
  const path_1 = tslib_1.__importDefault(require("path"));
13
- const remark_admonitions_1 = tslib_1.__importDefault(require("remark-admonitions"));
12
+ const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
14
13
  const utils_1 = require("@docusaurus/utils");
15
- const constants_1 = require("@docusaurus/core/lib/constants");
16
- const lodash_1 = require("lodash");
17
- const pluginOptionSchema_1 = require("./pluginOptionSchema");
18
14
  const blogUtils_1 = require("./blogUtils");
19
- function pluginContentBlog(context, options) {
20
- var _a;
21
- if (options.admonitions) {
22
- options.remarkPlugins = options.remarkPlugins.concat([
23
- [remark_admonitions_1.default, options.admonitions],
24
- ]);
25
- }
26
- const { siteDir, siteConfig: { onBrokenMarkdownLinks }, generatedFilesDir, i18n: { currentLocale }, } = context;
15
+ const footnoteIDFixer_1 = tslib_1.__importDefault(require("./remark/footnoteIDFixer"));
16
+ const translations_1 = require("./translations");
17
+ const feed_1 = require("./feed");
18
+ async function pluginContentBlog(context, options) {
19
+ const { siteDir, siteConfig, generatedFilesDir, localizationDir, i18n: { currentLocale }, } = context;
20
+ const { onBrokenMarkdownLinks, baseUrl } = siteConfig;
27
21
  const contentPaths = {
28
22
  contentPath: path_1.default.resolve(siteDir, options.path),
29
- contentPathLocalized: utils_1.getPluginI18nPath({
30
- siteDir,
31
- locale: currentLocale,
23
+ contentPathLocalized: (0, utils_1.getPluginI18nPath)({
24
+ localizationDir,
32
25
  pluginName: 'docusaurus-plugin-content-blog',
33
26
  pluginId: options.id,
34
27
  }),
35
28
  };
36
- const pluginId = (_a = options.id) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_PLUGIN_ID;
29
+ const pluginId = options.id ?? utils_1.DEFAULT_PLUGIN_ID;
37
30
  const pluginDataDirRoot = path_1.default.join(generatedFilesDir, 'docusaurus-plugin-content-blog');
38
31
  const dataDir = path_1.default.join(pluginDataDirRoot, pluginId);
39
- const aliasedSource = (source) => `~blog/${utils_1.posixPath(path_1.default.relative(pluginDataDirRoot, source))}`;
32
+ const aliasedSource = (source) => `~blog/${(0, utils_1.posixPath)(path_1.default.relative(pluginDataDirRoot, source))}`;
33
+ const authorsMapFilePath = await (0, utils_1.getDataFilePath)({
34
+ filePath: options.authorsMapPath,
35
+ contentPaths,
36
+ });
40
37
  return {
41
38
  name: 'docusaurus-plugin-content-blog',
42
39
  getPathsToWatch() {
43
- const { include = [] } = options;
44
- return lodash_1.flatten(blogUtils_1.getContentPathList(contentPaths).map((contentPath) => {
45
- return include.map((pattern) => `${contentPath}/${pattern}`);
46
- }));
40
+ const { include } = options;
41
+ const contentMarkdownGlobs = (0, utils_1.getContentPathList)(contentPaths).flatMap((contentPath) => include.map((pattern) => `${contentPath}/${pattern}`));
42
+ return [authorsMapFilePath, ...contentMarkdownGlobs].filter(Boolean);
47
43
  },
48
- getClientModules() {
49
- const modules = [];
50
- if (options.admonitions) {
51
- modules.push(require.resolve('remark-admonitions/styles/infima.css'));
52
- }
53
- return modules;
44
+ getTranslationFiles() {
45
+ return (0, translations_1.getTranslationFiles)(options);
54
46
  },
55
47
  // Fetches blog contents and returns metadata for the necessary routes.
56
48
  async loadContent() {
57
- const { postsPerPage, routeBasePath } = options;
58
- const blogPosts = await blogUtils_1.generateBlogPosts(contentPaths, context, options);
49
+ const { postsPerPage: postsPerPageOption, routeBasePath, tagsBasePath, blogDescription, blogTitle, blogSidebarTitle, } = options;
50
+ const baseBlogUrl = (0, utils_1.normalizeUrl)([baseUrl, routeBasePath]);
51
+ const blogTagsListPath = (0, utils_1.normalizeUrl)([baseBlogUrl, tagsBasePath]);
52
+ const blogPosts = await (0, blogUtils_1.generateBlogPosts)(contentPaths, context, options);
59
53
  if (!blogPosts.length) {
60
54
  return {
55
+ blogSidebarTitle,
61
56
  blogPosts: [],
62
57
  blogListPaginated: [],
63
58
  blogTags: {},
64
- blogTagsListPath: null,
59
+ blogTagsListPath,
60
+ blogTagsPaginated: [],
65
61
  };
66
62
  }
67
63
  // Colocate next and prev metadata.
@@ -81,72 +77,21 @@ function pluginContentBlog(context, options) {
81
77
  };
82
78
  }
83
79
  });
84
- // Blog pagination routes.
85
- // Example: `/blog`, `/blog/page/1`, `/blog/page/2`
86
- const totalCount = blogPosts.length;
87
- const numberOfPages = Math.ceil(totalCount / postsPerPage);
88
- const { siteConfig: { baseUrl = '' }, } = context;
89
- const basePageUrl = utils_1.normalizeUrl([baseUrl, routeBasePath]);
90
- const blogListPaginated = [];
91
- function blogPaginationPermalink(page) {
92
- return page > 0
93
- ? utils_1.normalizeUrl([basePageUrl, `page/${page + 1}`])
94
- : basePageUrl;
95
- }
96
- for (let page = 0; page < numberOfPages; page += 1) {
97
- blogListPaginated.push({
98
- metadata: {
99
- permalink: blogPaginationPermalink(page),
100
- page: page + 1,
101
- postsPerPage,
102
- totalPages: numberOfPages,
103
- totalCount,
104
- previousPage: page !== 0 ? blogPaginationPermalink(page - 1) : null,
105
- nextPage: page < numberOfPages - 1
106
- ? blogPaginationPermalink(page + 1)
107
- : null,
108
- blogDescription: options.blogDescription,
109
- blogTitle: options.blogTitle,
110
- },
111
- items: blogPosts
112
- .slice(page * postsPerPage, (page + 1) * postsPerPage)
113
- .map((item) => item.id),
114
- });
115
- }
116
- const blogTags = {};
117
- const tagsPath = utils_1.normalizeUrl([basePageUrl, 'tags']);
118
- blogPosts.forEach((blogPost) => {
119
- const { tags } = blogPost.metadata;
120
- if (!tags || tags.length === 0) {
121
- // TODO: Extract tags out into a separate plugin.
122
- // eslint-disable-next-line no-param-reassign
123
- blogPost.metadata.tags = [];
124
- return;
125
- }
126
- // eslint-disable-next-line no-param-reassign
127
- blogPost.metadata.tags = tags.map((tag) => {
128
- if (typeof tag === 'string') {
129
- const normalizedTag = lodash_1.kebabCase(tag);
130
- const permalink = utils_1.normalizeUrl([tagsPath, normalizedTag]);
131
- if (!blogTags[normalizedTag]) {
132
- blogTags[normalizedTag] = {
133
- // Will only use the name of the first occurrence of the tag.
134
- name: tag.toLowerCase(),
135
- items: [],
136
- permalink,
137
- };
138
- }
139
- blogTags[normalizedTag].items.push(blogPost.id);
140
- return {
141
- label: tag,
142
- permalink,
143
- };
144
- }
145
- return tag;
146
- });
80
+ const blogListPaginated = (0, blogUtils_1.paginateBlogPosts)({
81
+ blogPosts,
82
+ blogTitle,
83
+ blogDescription,
84
+ postsPerPageOption,
85
+ basePageUrl: baseBlogUrl,
86
+ });
87
+ const blogTags = (0, blogUtils_1.getBlogTags)({
88
+ blogPosts,
89
+ postsPerPageOption,
90
+ blogDescription,
91
+ blogTitle,
147
92
  });
148
- const blogTagsListPath = Object.keys(blogTags).length > 0 ? tagsPath : null;
149
93
  return {
94
+ blogSidebarTitle,
150
95
  blogPosts,
151
96
  blogListPaginated,
152
97
  blogTags,
@@ -154,22 +99,50 @@ function pluginContentBlog(context, options) {
154
99
  };
155
100
  },
156
101
  async contentLoaded({ content: blogContents, actions }) {
157
- if (!blogContents) {
158
- return;
159
- }
160
- const { blogListComponent, blogPostComponent, blogTagsListComponent, blogTagsPostsComponent, } = options;
102
+ const { blogListComponent, blogPostComponent, blogTagsListComponent, blogTagsPostsComponent, blogArchiveComponent, routeBasePath, archiveBasePath, } = options;
161
103
  const { addRoute, createData } = actions;
162
- const { blogPosts, blogListPaginated, blogTags, blogTagsListPath, } = blogContents;
104
+ const { blogSidebarTitle, blogPosts, blogListPaginated, blogTags, blogTagsListPath, } = blogContents;
163
105
  const blogItemsToMetadata = {};
164
106
  const sidebarBlogPosts = options.blogSidebarCount === 'ALL'
165
107
  ? blogPosts
166
- : lodash_1.take(blogPosts, options.blogSidebarCount);
108
+ : blogPosts.slice(0, options.blogSidebarCount);
109
+ function blogPostItemsModule(items) {
110
+ return items.map((postId) => {
111
+ const blogPostMetadata = blogItemsToMetadata[postId];
112
+ return {
113
+ content: {
114
+ __import: true,
115
+ path: blogPostMetadata.source,
116
+ query: {
117
+ truncated: true,
118
+ },
119
+ },
120
+ };
121
+ });
122
+ }
123
+ if (archiveBasePath && blogPosts.length) {
124
+ const archiveUrl = (0, utils_1.normalizeUrl)([
125
+ baseUrl,
126
+ routeBasePath,
127
+ archiveBasePath,
128
+ ]);
129
+ // Create a blog archive route
130
+ const archiveProp = await createData(`${(0, utils_1.docuHash)(archiveUrl)}.json`, JSON.stringify({ blogPosts }, null, 2));
131
+ addRoute({
132
+ path: archiveUrl,
133
+ component: blogArchiveComponent,
134
+ exact: true,
135
+ modules: {
136
+ archive: aliasedSource(archiveProp),
137
+ },
138
+ });
139
+ }
167
140
  // This prop is useful to provide the blog list sidebar
168
141
  const sidebarProp = await createData(
169
142
  // Note that this created data path must be in sync with
170
143
  // metadataPath provided to mdx-loader.
171
144
  `blog-post-list-prop-${pluginId}.json`, JSON.stringify({
172
- title: options.blogSidebarTitle,
145
+ title: blogSidebarTitle,
173
146
  items: sidebarBlogPosts.map((blogPost) => ({
174
147
  title: blogPost.metadata.title,
175
148
  permalink: blogPost.metadata.permalink,
@@ -181,7 +154,7 @@ function pluginContentBlog(context, options) {
181
154
  await createData(
182
155
  // Note that this created data path must be in sync with
183
156
  // metadataPath provided to mdx-loader.
184
- `${utils_1.docuHash(metadata.source)}.json`, JSON.stringify(metadata, null, 2));
157
+ `${(0, utils_1.docuHash)(metadata.source)}.json`, JSON.stringify(metadata, null, 2));
185
158
  addRoute({
186
159
  path: metadata.permalink,
187
160
  component: blogPostComponent,
@@ -197,94 +170,84 @@ function pluginContentBlog(context, options) {
197
170
  await Promise.all(blogListPaginated.map(async (listPage) => {
198
171
  const { metadata, items } = listPage;
199
172
  const { permalink } = metadata;
200
- const pageMetadataPath = await createData(`${utils_1.docuHash(permalink)}.json`, JSON.stringify(metadata, null, 2));
173
+ const pageMetadataPath = await createData(`${(0, utils_1.docuHash)(permalink)}.json`, JSON.stringify(metadata, null, 2));
201
174
  addRoute({
202
175
  path: permalink,
203
176
  component: blogListComponent,
204
177
  exact: true,
205
178
  modules: {
206
179
  sidebar: aliasedSource(sidebarProp),
207
- items: items.map((postID) => {
208
- // To tell routes.js this is an import and not a nested object to recurse.
209
- return {
210
- content: {
211
- __import: true,
212
- path: blogItemsToMetadata[postID].source,
213
- query: {
214
- truncated: true,
215
- },
216
- },
217
- };
218
- }),
180
+ items: blogPostItemsModule(items),
219
181
  metadata: aliasedSource(pageMetadataPath),
220
182
  },
221
183
  });
222
184
  }));
223
- // Tags.
224
- if (blogTagsListPath === null) {
185
+ // Tags. This is the last part so we early-return if there are no tags.
186
+ if (Object.keys(blogTags).length === 0) {
225
187
  return;
226
188
  }
227
- const tagsModule = {};
228
- await Promise.all(Object.keys(blogTags).map(async (tag) => {
229
- const { name, items, permalink } = blogTags[tag];
230
- tagsModule[tag] = {
231
- allTagsPath: blogTagsListPath,
232
- slug: tag,
233
- name,
234
- count: items.length,
235
- permalink,
236
- };
237
- const tagsMetadataPath = await createData(`${utils_1.docuHash(permalink)}.json`, JSON.stringify(tagsModule[tag], null, 2));
238
- addRoute({
239
- path: permalink,
240
- component: blogTagsPostsComponent,
241
- exact: true,
242
- modules: {
243
- sidebar: aliasedSource(sidebarProp),
244
- items: items.map((postID) => {
245
- const metadata = blogItemsToMetadata[postID];
246
- return {
247
- content: {
248
- __import: true,
249
- path: metadata.source,
250
- query: {
251
- truncated: true,
252
- },
253
- },
254
- };
255
- }),
256
- metadata: aliasedSource(tagsMetadataPath),
257
- },
258
- });
259
- }));
260
- // Only create /tags page if there are tags.
261
- if (Object.keys(blogTags).length > 0) {
262
- const tagsListPath = await createData(`${utils_1.docuHash(`${blogTagsListPath}-tags`)}.json`, JSON.stringify(tagsModule, null, 2));
189
+ async function createTagsListPage() {
190
+ const tagsProp = Object.values(blogTags).map((tag) => ({
191
+ label: tag.label,
192
+ permalink: tag.permalink,
193
+ count: tag.items.length,
194
+ }));
195
+ const tagsPropPath = await createData(`${(0, utils_1.docuHash)(`${blogTagsListPath}-tags`)}.json`, JSON.stringify(tagsProp, null, 2));
263
196
  addRoute({
264
197
  path: blogTagsListPath,
265
198
  component: blogTagsListComponent,
266
199
  exact: true,
267
200
  modules: {
268
201
  sidebar: aliasedSource(sidebarProp),
269
- tags: aliasedSource(tagsListPath),
202
+ tags: aliasedSource(tagsPropPath),
270
203
  },
271
204
  });
272
205
  }
206
+ async function createTagPostsListPage(tag) {
207
+ await Promise.all(tag.pages.map(async (blogPaginated) => {
208
+ const { metadata, items } = blogPaginated;
209
+ const tagProp = {
210
+ label: tag.label,
211
+ permalink: tag.permalink,
212
+ allTagsPath: blogTagsListPath,
213
+ count: tag.items.length,
214
+ };
215
+ const tagPropPath = await createData(`${(0, utils_1.docuHash)(metadata.permalink)}.json`, JSON.stringify(tagProp, null, 2));
216
+ const listMetadataPath = await createData(`${(0, utils_1.docuHash)(metadata.permalink)}-list.json`, JSON.stringify(metadata, null, 2));
217
+ addRoute({
218
+ path: metadata.permalink,
219
+ component: blogTagsPostsComponent,
220
+ exact: true,
221
+ modules: {
222
+ sidebar: aliasedSource(sidebarProp),
223
+ items: blogPostItemsModule(items),
224
+ tag: aliasedSource(tagPropPath),
225
+ listMetadata: aliasedSource(listMetadataPath),
226
+ },
227
+ });
228
+ }));
229
+ }
230
+ await createTagsListPage();
231
+ await Promise.all(Object.values(blogTags).map(createTagPostsListPage));
232
+ },
233
+ translateContent({ content, translationFiles }) {
234
+ return (0, translations_1.translateContent)(content, translationFiles);
273
235
  },
274
236
  configureWebpack(_config, isServer, { getJSLoader }, content) {
275
- const { rehypePlugins, remarkPlugins, truncateMarker, beforeDefaultRemarkPlugins, beforeDefaultRehypePlugins, } = options;
237
+ const { admonitions, rehypePlugins, remarkPlugins, truncateMarker, beforeDefaultRemarkPlugins, beforeDefaultRehypePlugins, } = options;
276
238
  const markdownLoaderOptions = {
277
239
  siteDir,
278
240
  contentPaths,
279
241
  truncateMarker,
280
- sourceToPermalink: blogUtils_1.getSourceToPermalink(content.blogPosts),
242
+ sourceToPermalink: (0, blogUtils_1.getSourceToPermalink)(content.blogPosts),
281
243
  onBrokenMarkdownLink: (brokenMarkdownLink) => {
282
244
  if (onBrokenMarkdownLinks === 'ignore') {
283
245
  return;
284
246
  }
285
- utils_1.reportMessage(`Blog markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath}`, onBrokenMarkdownLinks);
247
+ logger_1.default.report(onBrokenMarkdownLinks) `Blog markdown link couldn't be resolved: (url=${brokenMarkdownLink.link}) in path=${brokenMarkdownLink.filePath}`;
286
248
  },
287
249
  };
250
+ const contentDirs = (0, utils_1.getContentPathList)(contentPaths);
288
251
  return {
289
252
  resolve: {
290
253
  alias: {
@@ -294,8 +257,8 @@ function pluginContentBlog(context, options) {
294
257
  module: {
295
258
  rules: [
296
259
  {
297
- test: /(\.mdx?)$/,
298
- include: blogUtils_1.getContentPathList(contentPaths)
260
+ test: /\.mdx?$/i,
261
+ include: contentDirs
299
262
  // Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
300
263
  .map(utils_1.addTrailingPathSeparator),
301
264
  use: [
@@ -303,20 +266,32 @@ function pluginContentBlog(context, options) {
303
266
  {
304
267
  loader: require.resolve('@docusaurus/mdx-loader'),
305
268
  options: {
269
+ admonitions,
306
270
  remarkPlugins,
307
271
  rehypePlugins,
308
- beforeDefaultRemarkPlugins,
272
+ beforeDefaultRemarkPlugins: [
273
+ footnoteIDFixer_1.default,
274
+ ...beforeDefaultRemarkPlugins,
275
+ ],
309
276
  beforeDefaultRehypePlugins,
310
- staticDir: path_1.default.join(siteDir, constants_1.STATIC_DIR_NAME),
311
- // Note that metadataPath must be the same/in-sync as
312
- // the path from createData for each MDX.
277
+ staticDirs: siteConfig.staticDirectories.map((dir) => path_1.default.resolve(siteDir, dir)),
278
+ siteDir,
279
+ isMDXPartial: (0, utils_1.createAbsoluteFilePathMatcher)(options.exclude, contentDirs),
313
280
  metadataPath: (mdxPath) => {
314
- const aliasedPath = utils_1.aliasedSitePath(mdxPath, siteDir);
315
- return path_1.default.join(dataDir, `${utils_1.docuHash(aliasedPath)}.json`);
281
+ // Note that metadataPath must be the same/in-sync as
282
+ // the path from createData for each MDX.
283
+ const aliasedPath = (0, utils_1.aliasedSitePath)(mdxPath, siteDir);
284
+ return path_1.default.join(dataDir, `${(0, utils_1.docuHash)(aliasedPath)}.json`);
316
285
  },
317
286
  // For blog posts a title in markdown is always removed
318
287
  // Blog posts title are rendered separately
319
288
  removeContentTitle: true,
289
+ // Assets allow to convert some relative images paths to
290
+ // require() calls
291
+ createAssets: ({ frontMatter, metadata, }) => ({
292
+ image: frontMatter.image,
293
+ authorsImageUrls: metadata.authors.map((author) => author.imageURL),
294
+ }),
320
295
  },
321
296
  },
322
297
  {
@@ -329,62 +304,54 @@ function pluginContentBlog(context, options) {
329
304
  },
330
305
  };
331
306
  },
332
- async postBuild({ outDir }) {
333
- var _a;
334
- if (!((_a = options.feedOptions) === null || _a === void 0 ? void 0 : _a.type)) {
307
+ async postBuild({ outDir, content }) {
308
+ if (!options.feedOptions.type) {
335
309
  return;
336
310
  }
337
- const feed = await blogUtils_1.generateBlogFeed(contentPaths, context, options);
338
- if (!feed) {
311
+ const { blogPosts } = content;
312
+ if (!blogPosts.length) {
339
313
  return;
340
314
  }
341
- const feedTypes = options.feedOptions.type;
342
- await Promise.all(feedTypes.map(async (feedType) => {
343
- const feedPath = path_1.default.join(outDir, options.routeBasePath, `${feedType}.xml`);
344
- const feedContent = feedType === 'rss' ? feed.rss2() : feed.atom1();
345
- try {
346
- await fs_extra_1.default.outputFile(feedPath, feedContent);
347
- }
348
- catch (err) {
349
- throw new Error(`Generating ${feedType} feed failed: ${err}.`);
350
- }
351
- }));
315
+ await (0, feed_1.createBlogFeedFiles)({
316
+ blogPosts,
317
+ options,
318
+ outDir,
319
+ siteConfig,
320
+ locale: currentLocale,
321
+ });
352
322
  },
353
323
  injectHtmlTags({ content }) {
354
- var _a;
355
- if (!content.blogPosts.length) {
356
- return {};
357
- }
358
- if (!((_a = options.feedOptions) === null || _a === void 0 ? void 0 : _a.type)) {
324
+ if (!content.blogPosts.length || !options.feedOptions.type) {
359
325
  return {};
360
326
  }
361
327
  const feedTypes = options.feedOptions.type;
362
- const { siteConfig: { title }, baseUrl, } = context;
328
+ const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
363
329
  const feedsConfig = {
364
330
  rss: {
365
331
  type: 'application/rss+xml',
366
332
  path: 'rss.xml',
367
- title: `${title} Blog RSS Feed`,
333
+ title: `${feedTitle} RSS Feed`,
368
334
  },
369
335
  atom: {
370
336
  type: 'application/atom+xml',
371
337
  path: 'atom.xml',
372
- title: `${title} Blog Atom Feed`,
338
+ title: `${feedTitle} Atom Feed`,
339
+ },
340
+ json: {
341
+ type: 'application/json',
342
+ path: 'feed.json',
343
+ title: `${feedTitle} JSON Feed`,
373
344
  },
374
345
  };
375
346
  const headTags = [];
376
347
  feedTypes.forEach((feedType) => {
377
- const feedConfig = feedsConfig[feedType] || {};
378
- if (!feedsConfig) {
379
- return;
380
- }
381
- const { type, path: feedConfigPath, title: feedConfigTitle } = feedConfig;
348
+ const { type, path: feedConfigPath, title: feedConfigTitle, } = feedsConfig[feedType];
382
349
  headTags.push({
383
350
  tagName: 'link',
384
351
  attributes: {
385
352
  rel: 'alternate',
386
353
  type,
387
- href: utils_1.normalizeUrl([
354
+ href: (0, utils_1.normalizeUrl)([
388
355
  baseUrl,
389
356
  options.routeBasePath,
390
357
  feedConfigPath,
@@ -400,8 +367,5 @@ function pluginContentBlog(context, options) {
400
367
  };
401
368
  }
402
369
  exports.default = pluginContentBlog;
403
- function validateOptions({ validate, options, }) {
404
- const validatedOptions = validate(pluginOptionSchema_1.PluginOptionSchema, options);
405
- return validatedOptions;
406
- }
407
- exports.validateOptions = validateOptions;
370
+ var options_1 = require("./options");
371
+ Object.defineProperty(exports, "validateOptions", { enumerable: true, get: function () { return options_1.validateOptions; } });
@@ -4,9 +4,6 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- /// <reference types="node" />
8
- interface Loader extends Function {
9
- (this: any, source: string): string | Buffer | void | undefined;
10
- }
11
- declare const markdownLoader: Loader;
12
- export default markdownLoader;
7
+ import type { BlogMarkdownLoaderOptions } from './types';
8
+ import type { LoaderContext } from 'webpack';
9
+ export default function markdownLoader(this: LoaderContext<BlogMarkdownLoaderOptions>, source: string): void;
@@ -7,25 +7,24 @@
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  const blogUtils_1 = require("./blogUtils");
10
- const loader_utils_1 = require("loader-utils");
11
- const markdownLoader = function (source) {
10
+ function markdownLoader(source) {
12
11
  const filePath = this.resourcePath;
13
12
  const fileString = source;
14
13
  const callback = this.async();
15
14
  const markdownLoaderOptions = this.getOptions();
16
15
  // Linkify blog posts
17
- let finalContent = blogUtils_1.linkify({
16
+ let finalContent = (0, blogUtils_1.linkify)({
18
17
  fileString,
19
18
  filePath,
20
19
  ...markdownLoaderOptions,
21
20
  });
22
21
  // Truncate content if requested (e.g: file.md?truncated=true).
23
22
  const truncated = this.resourceQuery
24
- ? !!loader_utils_1.parseQuery(this.resourceQuery).truncated
23
+ ? !!new URLSearchParams(this.resourceQuery.slice(1)).get('truncated')
25
24
  : undefined;
26
25
  if (truncated) {
27
- finalContent = blogUtils_1.truncate(finalContent, markdownLoaderOptions.truncateMarker);
26
+ finalContent = (0, blogUtils_1.truncate)(finalContent, markdownLoaderOptions.truncateMarker);
28
27
  }
29
- return callback && callback(null, finalContent);
30
- };
28
+ return callback(null, finalContent);
29
+ }
31
30
  exports.default = markdownLoader;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import type { PluginOptions, Options } from '@docusaurus/plugin-content-blog';
8
+ import type { OptionValidationContext } from '@docusaurus/types';
9
+ export declare const DEFAULT_OPTIONS: PluginOptions;
10
+ export declare function validateOptions({ validate, options, }: OptionValidationContext<Options | undefined, PluginOptions>): PluginOptions;