@docusaurus/plugin-content-blog 2.0.0-beta.1decd6f80 → 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 (58) 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 +214 -141
  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/frontMatter.js +62 -0
  9. package/lib/index.d.ts +4 -4
  10. package/lib/index.js +182 -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 -17
  21. package/src/authors.ts +164 -0
  22. package/src/blogUtils.ts +316 -196
  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 +245 -249
  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 -4415
  35. package/lib/blogFrontMatter.d.ts +0 -28
  36. package/lib/blogFrontMatter.js +0 -50
  37. package/lib/pluginOptionSchema.d.ts +0 -33
  38. package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  39. package/src/__tests__/__fixtures__/website/blog/complex-slug.md +0 -7
  40. package/src/__tests__/__fixtures__/website/blog/date-matter.md +0 -5
  41. package/src/__tests__/__fixtures__/website/blog/draft.md +0 -6
  42. package/src/__tests__/__fixtures__/website/blog/heading-as-title.md +0 -5
  43. package/src/__tests__/__fixtures__/website/blog/simple-slug.md +0 -7
  44. package/src/__tests__/__fixtures__/website/blog-with-ref/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  45. package/src/__tests__/__fixtures__/website/blog-with-ref/post-with-broken-links.md +0 -11
  46. package/src/__tests__/__fixtures__/website/blog-with-ref/post.md +0 -5
  47. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
  48. package/src/__tests__/__fixtures__/website-blog-without-date/blog/no date.md +0 -1
  49. package/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap +0 -101
  50. package/src/__tests__/__snapshots__/linkify.test.ts.snap +0 -24
  51. package/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap +0 -5
  52. package/src/__tests__/blogFrontMatter.test.ts +0 -265
  53. package/src/__tests__/generateBlogFeed.test.ts +0 -100
  54. package/src/__tests__/index.test.ts +0 -336
  55. package/src/__tests__/linkify.test.ts +0 -93
  56. package/src/__tests__/pluginOptionSchema.test.ts +0 -150
  57. package/src/blogFrontMatter.ts +0 -87
  58. package/tsconfig.json +0 -9
package/lib/index.js CHANGED
@@ -8,57 +8,63 @@
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))}`;
40
- let blogPosts = [];
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
+ });
41
43
  return {
42
44
  name: 'docusaurus-plugin-content-blog',
43
45
  getPathsToWatch() {
44
- const { include = [] } = options;
45
- return lodash_1.flatten(blogUtils_1.getContentPathList(contentPaths).map((contentPath) => {
46
- return include.map((pattern) => `${contentPath}/${pattern}`);
47
- }));
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);
48
49
  },
49
- getClientModules() {
50
- const modules = [];
51
- if (options.admonitions) {
52
- modules.push(require.resolve('remark-admonitions/styles/infima.css'));
53
- }
54
- return modules;
50
+ getTranslationFiles() {
51
+ return (0, translations_1.getTranslationFiles)(options);
55
52
  },
56
53
  // Fetches blog contents and returns metadata for the necessary routes.
57
54
  async loadContent() {
58
- const { postsPerPage, routeBasePath } = options;
59
- 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);
60
59
  if (!blogPosts.length) {
61
- return null;
60
+ return {
61
+ blogSidebarTitle,
62
+ blogPosts: [],
63
+ blogListPaginated: [],
64
+ blogTags: {},
65
+ blogTagsListPath,
66
+ blogTagsPaginated: [],
67
+ };
62
68
  }
63
69
  // Colocate next and prev metadata.
64
70
  blogPosts.forEach((blogPost, index) => {
@@ -77,72 +83,21 @@ function pluginContentBlog(context, options) {
77
83
  };
78
84
  }
79
85
  });
80
- // Blog pagination routes.
81
- // Example: `/blog`, `/blog/page/1`, `/blog/page/2`
82
- const totalCount = blogPosts.length;
83
- const numberOfPages = Math.ceil(totalCount / postsPerPage);
84
- const { siteConfig: { baseUrl = '' }, } = context;
85
- const basePageUrl = utils_1.normalizeUrl([baseUrl, routeBasePath]);
86
- const blogListPaginated = [];
87
- function blogPaginationPermalink(page) {
88
- return page > 0
89
- ? utils_1.normalizeUrl([basePageUrl, `page/${page + 1}`])
90
- : basePageUrl;
91
- }
92
- for (let page = 0; page < numberOfPages; page += 1) {
93
- blogListPaginated.push({
94
- metadata: {
95
- permalink: blogPaginationPermalink(page),
96
- page: page + 1,
97
- postsPerPage,
98
- totalPages: numberOfPages,
99
- totalCount,
100
- previousPage: page !== 0 ? blogPaginationPermalink(page - 1) : null,
101
- nextPage: page < numberOfPages - 1
102
- ? blogPaginationPermalink(page + 1)
103
- : null,
104
- blogDescription: options.blogDescription,
105
- blogTitle: options.blogTitle,
106
- },
107
- items: blogPosts
108
- .slice(page * postsPerPage, (page + 1) * postsPerPage)
109
- .map((item) => item.id),
110
- });
111
- }
112
- const blogTags = {};
113
- const tagsPath = utils_1.normalizeUrl([basePageUrl, 'tags']);
114
- blogPosts.forEach((blogPost) => {
115
- const { tags } = blogPost.metadata;
116
- if (!tags || tags.length === 0) {
117
- // TODO: Extract tags out into a separate plugin.
118
- // eslint-disable-next-line no-param-reassign
119
- blogPost.metadata.tags = [];
120
- return;
121
- }
122
- // eslint-disable-next-line no-param-reassign
123
- blogPost.metadata.tags = tags.map((tag) => {
124
- if (typeof tag === 'string') {
125
- const normalizedTag = lodash_1.kebabCase(tag);
126
- const permalink = utils_1.normalizeUrl([tagsPath, normalizedTag]);
127
- if (!blogTags[normalizedTag]) {
128
- blogTags[normalizedTag] = {
129
- // Will only use the name of the first occurrence of the tag.
130
- name: tag.toLowerCase(),
131
- items: [],
132
- permalink,
133
- };
134
- }
135
- blogTags[normalizedTag].items.push(blogPost.id);
136
- return {
137
- label: tag,
138
- permalink,
139
- };
140
- }
141
- return tag;
142
- });
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,
143
98
  });
144
- const blogTagsListPath = Object.keys(blogTags).length > 0 ? tagsPath : null;
145
99
  return {
100
+ blogSidebarTitle,
146
101
  blogPosts,
147
102
  blogListPaginated,
148
103
  blogTags,
@@ -153,37 +108,54 @@ function pluginContentBlog(context, options) {
153
108
  if (!blogContents) {
154
109
  return;
155
110
  }
156
- const { blogListComponent, blogPostComponent, blogTagsListComponent, blogTagsPostsComponent, } = options;
111
+ const { blogListComponent, blogPostComponent, blogTagsListComponent, blogTagsPostsComponent, blogArchiveComponent, routeBasePath, archiveBasePath, } = options;
157
112
  const { addRoute, createData } = actions;
158
- const { blogPosts: loadedBlogPosts, blogListPaginated, blogTags, blogTagsListPath, } = blogContents;
113
+ const { blogSidebarTitle, blogPosts, blogListPaginated, blogTags, blogTagsListPath, } = blogContents;
159
114
  const blogItemsToMetadata = {};
160
115
  const sidebarBlogPosts = options.blogSidebarCount === 'ALL'
161
116
  ? blogPosts
162
- : 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
+ }
163
135
  // This prop is useful to provide the blog list sidebar
164
136
  const sidebarProp = await createData(
165
137
  // Note that this created data path must be in sync with
166
138
  // metadataPath provided to mdx-loader.
167
139
  `blog-post-list-prop-${pluginId}.json`, JSON.stringify({
168
- title: options.blogSidebarTitle,
140
+ title: blogSidebarTitle,
169
141
  items: sidebarBlogPosts.map((blogPost) => ({
170
142
  title: blogPost.metadata.title,
171
143
  permalink: blogPost.metadata.permalink,
172
144
  })),
173
145
  }, null, 2));
174
146
  // Create routes for blog entries.
175
- await Promise.all(loadedBlogPosts.map(async (blogPost) => {
147
+ await Promise.all(blogPosts.map(async (blogPost) => {
176
148
  const { id, metadata } = blogPost;
177
149
  await createData(
178
150
  // Note that this created data path must be in sync with
179
151
  // metadataPath provided to mdx-loader.
180
- `${utils_1.docuHash(metadata.source)}.json`, JSON.stringify(metadata, null, 2));
152
+ `${(0, utils_1.docuHash)(metadata.source)}.json`, JSON.stringify(metadata, null, 2));
181
153
  addRoute({
182
154
  path: metadata.permalink,
183
155
  component: blogPostComponent,
184
156
  exact: true,
185
157
  modules: {
186
- sidebar: sidebarProp,
158
+ sidebar: aliasedSource(sidebarProp),
187
159
  content: metadata.source,
188
160
  },
189
161
  });
@@ -193,94 +165,103 @@ function pluginContentBlog(context, options) {
193
165
  await Promise.all(blogListPaginated.map(async (listPage) => {
194
166
  const { metadata, items } = listPage;
195
167
  const { permalink } = metadata;
196
- 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));
197
169
  addRoute({
198
170
  path: permalink,
199
171
  component: blogListComponent,
200
172
  exact: true,
201
173
  modules: {
202
- sidebar: sidebarProp,
203
- items: items.map((postID) => {
204
- // To tell routes.js this is an import and not a nested object to recurse.
205
- return {
206
- content: {
207
- __import: true,
208
- path: blogItemsToMetadata[postID].source,
209
- query: {
210
- truncated: true,
211
- },
174
+ sidebar: aliasedSource(sidebarProp),
175
+ items: items.map((postID) => ({
176
+ content: {
177
+ __import: true,
178
+ path: blogItemsToMetadata[postID].source,
179
+ query: {
180
+ truncated: true,
212
181
  },
213
- };
214
- }),
182
+ },
183
+ })),
215
184
  metadata: aliasedSource(pageMetadataPath),
216
185
  },
217
186
  });
218
187
  }));
219
- // Tags.
220
- 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) {
221
190
  return;
222
191
  }
223
- const tagsModule = {};
224
- await Promise.all(Object.keys(blogTags).map(async (tag) => {
225
- const { name, items, permalink } = blogTags[tag];
226
- tagsModule[tag] = {
227
- allTagsPath: blogTagsListPath,
228
- slug: tag,
229
- name,
230
- count: items.length,
231
- permalink,
232
- };
233
- const tagsMetadataPath = await createData(`${utils_1.docuHash(permalink)}.json`, JSON.stringify(tagsModule[tag], null, 2));
234
- addRoute({
235
- path: permalink,
236
- component: blogTagsPostsComponent,
237
- exact: true,
238
- modules: {
239
- sidebar: sidebarProp,
240
- items: items.map((postID) => {
241
- const metadata = blogItemsToMetadata[postID];
242
- return {
243
- content: {
244
- __import: true,
245
- path: metadata.source,
246
- query: {
247
- truncated: true,
248
- },
249
- },
250
- };
251
- }),
252
- metadata: aliasedSource(tagsMetadataPath),
253
- },
254
- });
255
- }));
256
- // Only create /tags page if there are tags.
257
- if (Object.keys(blogTags).length > 0) {
258
- 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));
259
199
  addRoute({
260
200
  path: blogTagsListPath,
261
201
  component: blogTagsListComponent,
262
202
  exact: true,
263
203
  modules: {
264
- sidebar: sidebarProp,
265
- tags: aliasedSource(tagsListPath),
204
+ sidebar: aliasedSource(sidebarProp),
205
+ tags: aliasedSource(tagsPropPath),
266
206
  },
267
207
  });
268
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);
269
249
  },
270
- configureWebpack(_config, isServer, { getJSLoader }) {
250
+ configureWebpack(_config, isServer, { getJSLoader }, content) {
271
251
  const { rehypePlugins, remarkPlugins, truncateMarker, beforeDefaultRemarkPlugins, beforeDefaultRehypePlugins, } = options;
272
252
  const markdownLoaderOptions = {
273
253
  siteDir,
274
254
  contentPaths,
275
255
  truncateMarker,
276
- sourceToPermalink: blogUtils_1.getSourceToPermalink(blogPosts),
256
+ sourceToPermalink: (0, blogUtils_1.getSourceToPermalink)(content.blogPosts),
277
257
  onBrokenMarkdownLink: (brokenMarkdownLink) => {
278
258
  if (onBrokenMarkdownLinks === 'ignore') {
279
259
  return;
280
260
  }
281
- 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);
282
262
  },
283
263
  };
264
+ const contentDirs = (0, utils_1.getContentPathList)(contentPaths);
284
265
  return {
285
266
  resolve: {
286
267
  alias: {
@@ -290,8 +271,8 @@ function pluginContentBlog(context, options) {
290
271
  module: {
291
272
  rules: [
292
273
  {
293
- test: /(\.mdx?)$/,
294
- include: blogUtils_1.getContentPathList(contentPaths)
274
+ test: /\.mdx?$/i,
275
+ include: contentDirs
295
276
  // Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
296
277
  .map(utils_1.addTrailingPathSeparator),
297
278
  use: [
@@ -301,15 +282,29 @@ function pluginContentBlog(context, options) {
301
282
  options: {
302
283
  remarkPlugins,
303
284
  rehypePlugins,
304
- beforeDefaultRemarkPlugins,
285
+ beforeDefaultRemarkPlugins: [
286
+ footnoteIDFixer_1.default,
287
+ ...beforeDefaultRemarkPlugins,
288
+ ],
305
289
  beforeDefaultRehypePlugins,
306
- staticDir: path_1.default.join(siteDir, constants_1.STATIC_DIR_NAME),
307
- // Note that metadataPath must be the same/in-sync as
308
- // 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),
309
293
  metadataPath: (mdxPath) => {
310
- const aliasedPath = utils_1.aliasedSitePath(mdxPath, siteDir);
311
- 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`);
312
298
  },
299
+ // For blog posts a title in markdown is always removed
300
+ // Blog posts title are rendered separately
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
+ }),
313
308
  },
314
309
  },
315
310
  {
@@ -322,59 +317,57 @@ function pluginContentBlog(context, options) {
322
317
  },
323
318
  };
324
319
  },
325
- async postBuild({ outDir }) {
326
- var _a;
327
- if (!((_a = options.feedOptions) === null || _a === void 0 ? void 0 : _a.type)) {
320
+ async postBuild({ outDir, content }) {
321
+ if (!options.feedOptions.type) {
328
322
  return;
329
323
  }
330
- const feed = await blogUtils_1.generateBlogFeed(contentPaths, context, options);
331
- if (!feed) {
324
+ const { blogPosts } = content;
325
+ if (!blogPosts.length) {
332
326
  return;
333
327
  }
334
- const feedTypes = options.feedOptions.type;
335
- await Promise.all(feedTypes.map(async (feedType) => {
336
- const feedPath = path_1.default.join(outDir, options.routeBasePath, `${feedType}.xml`);
337
- const feedContent = feedType === 'rss' ? feed.rss2() : feed.atom1();
338
- try {
339
- await fs_extra_1.default.outputFile(feedPath, feedContent);
340
- }
341
- catch (err) {
342
- throw new Error(`Generating ${feedType} feed failed: ${err}`);
343
- }
344
- }));
328
+ await (0, feed_1.createBlogFeedFiles)({
329
+ blogPosts,
330
+ options,
331
+ outDir,
332
+ siteConfig,
333
+ locale: currentLocale,
334
+ });
345
335
  },
346
- injectHtmlTags() {
347
- var _a;
348
- if (!((_a = options.feedOptions) === null || _a === void 0 ? void 0 : _a.type)) {
336
+ injectHtmlTags({ content }) {
337
+ if (!content.blogPosts.length) {
338
+ return {};
339
+ }
340
+ if (!options.feedOptions?.type) {
349
341
  return {};
350
342
  }
351
343
  const feedTypes = options.feedOptions.type;
352
- const { siteConfig: { title }, baseUrl, } = context;
344
+ const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
353
345
  const feedsConfig = {
354
346
  rss: {
355
347
  type: 'application/rss+xml',
356
348
  path: 'rss.xml',
357
- title: `${title} Blog RSS Feed`,
349
+ title: `${feedTitle} RSS Feed`,
358
350
  },
359
351
  atom: {
360
352
  type: 'application/atom+xml',
361
353
  path: 'atom.xml',
362
- 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`,
363
360
  },
364
361
  };
365
362
  const headTags = [];
366
363
  feedTypes.forEach((feedType) => {
367
- const feedConfig = feedsConfig[feedType] || {};
368
- if (!feedsConfig) {
369
- return;
370
- }
371
- const { type, path: feedConfigPath, title: feedConfigTitle } = feedConfig;
364
+ const { type, path: feedConfigPath, title: feedConfigTitle, } = feedsConfig[feedType];
372
365
  headTags.push({
373
366
  tagName: 'link',
374
367
  attributes: {
375
368
  rel: 'alternate',
376
369
  type,
377
- href: utils_1.normalizeUrl([
370
+ href: (0, utils_1.normalizeUrl)([
378
371
  baseUrl,
379
372
  options.routeBasePath,
380
373
  feedConfigPath,
@@ -390,8 +383,5 @@ function pluginContentBlog(context, options) {
390
383
  };
391
384
  }
392
385
  exports.default = pluginContentBlog;
393
- function validateOptions({ validate, options, }) {
394
- const validatedOptions = validate(pluginOptionSchema_1.PluginOptionSchema, options);
395
- return validatedOptions;
396
- }
397
- 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;