@docusaurus/plugin-content-blog 2.0.0-beta.2 → 2.0.0-beta.20

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