@docusaurus/plugin-content-blog 2.0.0-beta.1ec2c95e3 → 2.0.0-beta.21

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 +166 -192
  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 +168 -0
  22. package/src/blogUtils.ts +306 -204
  23. package/{types.d.ts → src/deps.d.ts} +1 -1
  24. package/src/feed.ts +171 -0
  25. package/src/frontMatter.ts +81 -0
  26. package/src/index.ts +227 -256
  27. package/src/markdownLoader.ts +11 -16
  28. package/src/{pluginOptionSchema.ts → options.ts} +56 -15
  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
- const remark_admonitions_1 = tslib_1.__importDefault(require("remark-admonitions"));
14
12
  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");
13
+ const remark_admonitions_1 = tslib_1.__importDefault(require("remark-admonitions"));
18
14
  const blogUtils_1 = require("./blogUtils");
19
- function pluginContentBlog(context, options) {
20
- var _a;
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) {
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,
@@ -154,22 +105,36 @@ function pluginContentBlog(context, options) {
154
105
  };
155
106
  },
156
107
  async contentLoaded({ content: blogContents, actions }) {
157
- if (!blogContents) {
158
- return;
159
- }
160
- const { blogListComponent, blogPostComponent, blogTagsListComponent, blogTagsPostsComponent, } = options;
108
+ const { blogListComponent, blogPostComponent, blogTagsListComponent, blogTagsPostsComponent, blogArchiveComponent, routeBasePath, archiveBasePath, } = options;
161
109
  const { addRoute, createData } = actions;
162
- const { blogPosts, blogListPaginated, blogTags, blogTagsListPath, } = blogContents;
110
+ const { blogSidebarTitle, blogPosts, blogListPaginated, blogTags, blogTagsListPath, } = blogContents;
163
111
  const blogItemsToMetadata = {};
164
112
  const sidebarBlogPosts = options.blogSidebarCount === 'ALL'
165
113
  ? blogPosts
166
- : lodash_1.take(blogPosts, options.blogSidebarCount);
114
+ : blogPosts.slice(0, options.blogSidebarCount);
115
+ if (archiveBasePath && blogPosts.length) {
116
+ const archiveUrl = (0, utils_1.normalizeUrl)([
117
+ baseUrl,
118
+ routeBasePath,
119
+ archiveBasePath,
120
+ ]);
121
+ // Create a blog archive route
122
+ const archiveProp = await createData(`${(0, utils_1.docuHash)(archiveUrl)}.json`, JSON.stringify({ blogPosts }, null, 2));
123
+ addRoute({
124
+ path: archiveUrl,
125
+ component: blogArchiveComponent,
126
+ exact: true,
127
+ modules: {
128
+ archive: aliasedSource(archiveProp),
129
+ },
130
+ });
131
+ }
167
132
  // This prop is useful to provide the blog list sidebar
168
133
  const sidebarProp = await createData(
169
134
  // Note that this created data path must be in sync with
170
135
  // metadataPath provided to mdx-loader.
171
136
  `blog-post-list-prop-${pluginId}.json`, JSON.stringify({
172
- title: options.blogSidebarTitle,
137
+ title: blogSidebarTitle,
173
138
  items: sidebarBlogPosts.map((blogPost) => ({
174
139
  title: blogPost.metadata.title,
175
140
  permalink: blogPost.metadata.permalink,
@@ -181,7 +146,7 @@ function pluginContentBlog(context, options) {
181
146
  await createData(
182
147
  // Note that this created data path must be in sync with
183
148
  // metadataPath provided to mdx-loader.
184
- `${utils_1.docuHash(metadata.source)}.json`, JSON.stringify(metadata, null, 2));
149
+ `${(0, utils_1.docuHash)(metadata.source)}.json`, JSON.stringify(metadata, null, 2));
185
150
  addRoute({
186
151
  path: metadata.permalink,
187
152
  component: blogPostComponent,
@@ -197,79 +162,87 @@ function pluginContentBlog(context, options) {
197
162
  await Promise.all(blogListPaginated.map(async (listPage) => {
198
163
  const { metadata, items } = listPage;
199
164
  const { permalink } = metadata;
200
- const pageMetadataPath = await createData(`${utils_1.docuHash(permalink)}.json`, JSON.stringify(metadata, null, 2));
165
+ const pageMetadataPath = await createData(`${(0, utils_1.docuHash)(permalink)}.json`, JSON.stringify(metadata, null, 2));
201
166
  addRoute({
202
167
  path: permalink,
203
168
  component: blogListComponent,
204
169
  exact: true,
205
170
  modules: {
206
171
  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
- },
172
+ items: items.map((postID) => ({
173
+ content: {
174
+ __import: true,
175
+ path: blogItemsToMetadata[postID].source,
176
+ query: {
177
+ truncated: true,
216
178
  },
217
- };
218
- }),
179
+ },
180
+ })),
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: items.map((postID) => {
224
+ const blogPostMetadata = blogItemsToMetadata[postID];
225
+ return {
226
+ content: {
227
+ __import: true,
228
+ path: blogPostMetadata.source,
229
+ query: {
230
+ truncated: true,
231
+ },
232
+ },
233
+ };
234
+ }),
235
+ tag: aliasedSource(tagPropPath),
236
+ listMetadata: aliasedSource(listMetadataPath),
237
+ },
238
+ });
239
+ }));
240
+ }
241
+ await createTagsListPage();
242
+ await Promise.all(Object.values(blogTags).map(createTagPostsListPage));
243
+ },
244
+ translateContent({ content, translationFiles }) {
245
+ return (0, translations_1.translateContent)(content, translationFiles);
273
246
  },
274
247
  configureWebpack(_config, isServer, { getJSLoader }, content) {
275
248
  const { rehypePlugins, remarkPlugins, truncateMarker, beforeDefaultRemarkPlugins, beforeDefaultRehypePlugins, } = options;
@@ -277,14 +250,15 @@ function pluginContentBlog(context, options) {
277
250
  siteDir,
278
251
  contentPaths,
279
252
  truncateMarker,
280
- sourceToPermalink: blogUtils_1.getSourceToPermalink(content.blogPosts),
253
+ sourceToPermalink: (0, blogUtils_1.getSourceToPermalink)(content.blogPosts),
281
254
  onBrokenMarkdownLink: (brokenMarkdownLink) => {
282
255
  if (onBrokenMarkdownLinks === 'ignore') {
283
256
  return;
284
257
  }
285
- utils_1.reportMessage(`Blog markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath}`, onBrokenMarkdownLinks);
258
+ (0, utils_1.reportMessage)(`Blog markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath}`, onBrokenMarkdownLinks);
286
259
  },
287
260
  };
261
+ const contentDirs = (0, utils_1.getContentPathList)(contentPaths);
288
262
  return {
289
263
  resolve: {
290
264
  alias: {
@@ -294,8 +268,8 @@ function pluginContentBlog(context, options) {
294
268
  module: {
295
269
  rules: [
296
270
  {
297
- test: /(\.mdx?)$/,
298
- include: blogUtils_1.getContentPathList(contentPaths)
271
+ test: /\.mdx?$/i,
272
+ include: contentDirs
299
273
  // Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
300
274
  .map(utils_1.addTrailingPathSeparator),
301
275
  use: [
@@ -305,18 +279,29 @@ function pluginContentBlog(context, options) {
305
279
  options: {
306
280
  remarkPlugins,
307
281
  rehypePlugins,
308
- beforeDefaultRemarkPlugins,
282
+ beforeDefaultRemarkPlugins: [
283
+ footnoteIDFixer_1.default,
284
+ ...beforeDefaultRemarkPlugins,
285
+ ],
309
286
  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.
287
+ staticDirs: siteConfig.staticDirectories.map((dir) => path_1.default.resolve(siteDir, dir)),
288
+ siteDir,
289
+ isMDXPartial: (0, utils_1.createAbsoluteFilePathMatcher)(options.exclude, contentDirs),
313
290
  metadataPath: (mdxPath) => {
314
- const aliasedPath = utils_1.aliasedSitePath(mdxPath, siteDir);
315
- return path_1.default.join(dataDir, `${utils_1.docuHash(aliasedPath)}.json`);
291
+ // Note that metadataPath must be the same/in-sync as
292
+ // the path from createData for each MDX.
293
+ const aliasedPath = (0, utils_1.aliasedSitePath)(mdxPath, siteDir);
294
+ return path_1.default.join(dataDir, `${(0, utils_1.docuHash)(aliasedPath)}.json`);
316
295
  },
317
296
  // For blog posts a title in markdown is always removed
318
297
  // Blog posts title are rendered separately
319
298
  removeContentTitle: true,
299
+ // Assets allow to convert some relative images paths to
300
+ // require() calls
301
+ createAssets: ({ frontMatter, metadata, }) => ({
302
+ image: frontMatter.image,
303
+ authorsImageUrls: metadata.authors.map((author) => author.imageURL),
304
+ }),
320
305
  },
321
306
  },
322
307
  {
@@ -329,62 +314,54 @@ function pluginContentBlog(context, options) {
329
314
  },
330
315
  };
331
316
  },
332
- async postBuild({ outDir }) {
333
- var _a;
334
- if (!((_a = options.feedOptions) === null || _a === void 0 ? void 0 : _a.type)) {
317
+ async postBuild({ outDir, content }) {
318
+ if (!options.feedOptions.type) {
335
319
  return;
336
320
  }
337
- const feed = await blogUtils_1.generateBlogFeed(contentPaths, context, options);
338
- if (!feed) {
321
+ const { blogPosts } = content;
322
+ if (!blogPosts.length) {
339
323
  return;
340
324
  }
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
- }));
325
+ await (0, feed_1.createBlogFeedFiles)({
326
+ blogPosts,
327
+ options,
328
+ outDir,
329
+ siteConfig,
330
+ locale: currentLocale,
331
+ });
352
332
  },
353
333
  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)) {
334
+ if (!content.blogPosts.length || !options.feedOptions.type) {
359
335
  return {};
360
336
  }
361
337
  const feedTypes = options.feedOptions.type;
362
- const { siteConfig: { title }, baseUrl, } = context;
338
+ const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
363
339
  const feedsConfig = {
364
340
  rss: {
365
341
  type: 'application/rss+xml',
366
342
  path: 'rss.xml',
367
- title: `${title} Blog RSS Feed`,
343
+ title: `${feedTitle} RSS Feed`,
368
344
  },
369
345
  atom: {
370
346
  type: 'application/atom+xml',
371
347
  path: 'atom.xml',
372
- title: `${title} Blog Atom Feed`,
348
+ title: `${feedTitle} Atom Feed`,
349
+ },
350
+ json: {
351
+ type: 'application/json',
352
+ path: 'feed.json',
353
+ title: `${feedTitle} JSON Feed`,
373
354
  },
374
355
  };
375
356
  const headTags = [];
376
357
  feedTypes.forEach((feedType) => {
377
- const feedConfig = feedsConfig[feedType] || {};
378
- if (!feedsConfig) {
379
- return;
380
- }
381
- const { type, path: feedConfigPath, title: feedConfigTitle } = feedConfig;
358
+ const { type, path: feedConfigPath, title: feedConfigTitle, } = feedsConfig[feedType];
382
359
  headTags.push({
383
360
  tagName: 'link',
384
361
  attributes: {
385
362
  rel: 'alternate',
386
363
  type,
387
- href: utils_1.normalizeUrl([
364
+ href: (0, utils_1.normalizeUrl)([
388
365
  baseUrl,
389
366
  options.routeBasePath,
390
367
  feedConfigPath,
@@ -400,8 +377,5 @@ function pluginContentBlog(context, options) {
400
377
  };
401
378
  }
402
379
  exports.default = pluginContentBlog;
403
- function validateOptions({ validate, options, }) {
404
- const validatedOptions = validate(pluginOptionSchema_1.PluginOptionSchema, options);
405
- return validatedOptions;
406
- }
407
- exports.validateOptions = validateOptions;
380
+ var options_1 = require("./options");
381
+ 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;