@docusaurus/plugin-content-blog 2.0.0-beta.0e652730d → 2.0.0-beta.10

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 (66) hide show
  1. package/lib/.tsbuildinfo +1 -1
  2. package/lib/authors.d.ts +23 -0
  3. package/lib/authors.js +147 -0
  4. package/lib/blogFrontMatter.d.ts +19 -6
  5. package/lib/blogFrontMatter.js +35 -23
  6. package/lib/blogUtils.d.ts +10 -4
  7. package/lib/blogUtils.js +141 -136
  8. package/lib/feed.d.ts +20 -0
  9. package/lib/feed.js +90 -0
  10. package/lib/index.d.ts +1 -1
  11. package/lib/index.js +109 -112
  12. package/lib/markdownLoader.d.ts +3 -6
  13. package/lib/markdownLoader.js +5 -5
  14. package/lib/pluginOptionSchema.d.ts +3 -26
  15. package/lib/pluginOptionSchema.js +28 -7
  16. package/lib/translations.d.ts +10 -0
  17. package/lib/translations.js +53 -0
  18. package/lib/types.d.ts +54 -14
  19. package/package.json +19 -13
  20. package/src/__tests__/__fixtures__/authorsMapFiles/authors.json +29 -0
  21. package/src/__tests__/__fixtures__/authorsMapFiles/authors.yml +27 -0
  22. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.json +5 -0
  23. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad1.yml +3 -0
  24. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.json +3 -0
  25. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad2.yml +2 -0
  26. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.json +8 -0
  27. package/src/__tests__/__fixtures__/authorsMapFiles/authorsBad3.yml +3 -0
  28. package/src/__tests__/__fixtures__/component/Typography.tsx +6 -0
  29. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathEmpty/empty +0 -0
  30. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson1/authors.json +0 -0
  31. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathJson2/authors.json +0 -0
  32. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathNestedYml/sub/folder/authors.yml +0 -0
  33. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml1/authors.yml +0 -0
  34. package/src/__tests__/__fixtures__/getAuthorsMapFilePath/contentPathYml2/authors.yml +0 -0
  35. package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +3 -0
  36. package/src/__tests__/__fixtures__/website/blog/_partials/somePartial.md +3 -0
  37. package/src/__tests__/__fixtures__/website/blog/_partials/subfolder/somePartial.md +3 -0
  38. package/src/__tests__/__fixtures__/website/blog/_somePartial.md +3 -0
  39. package/src/__tests__/__fixtures__/website/blog/authors.yml +4 -0
  40. package/src/__tests__/__fixtures__/website/blog/mdx-blog-post.mdx +36 -0
  41. package/src/__tests__/__fixtures__/website/blog/mdx-require-blog-post.mdx +14 -0
  42. package/src/__tests__/__fixtures__/website/blog/simple-slug.md +4 -0
  43. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md +3 -0
  44. package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/authors.yml +5 -0
  45. package/src/__tests__/__fixtures__/website/static/img/docusaurus.png +0 -0
  46. package/src/__tests__/__snapshots__/feed.test.ts.snap +164 -0
  47. package/src/__tests__/__snapshots__/translations.test.ts.snap +64 -0
  48. package/src/__tests__/authors.test.ts +608 -0
  49. package/src/__tests__/blogFrontMatter.test.ts +165 -36
  50. package/src/__tests__/blogUtils.test.ts +94 -0
  51. package/src/__tests__/{generateBlogFeed.test.ts → feed.test.ts} +35 -9
  52. package/src/__tests__/index.test.ts +84 -12
  53. package/src/__tests__/pluginOptionSchema.test.ts +3 -3
  54. package/src/__tests__/translations.test.ts +92 -0
  55. package/src/authors.ts +198 -0
  56. package/src/blogFrontMatter.ts +76 -37
  57. package/src/blogUtils.ts +202 -179
  58. package/{types.d.ts → src/deps.d.ts} +0 -0
  59. package/src/feed.ts +129 -0
  60. package/src/index.ts +131 -112
  61. package/src/markdownLoader.ts +8 -12
  62. package/{index.d.ts → src/plugin-content-blog.d.ts} +35 -31
  63. package/src/pluginOptionSchema.ts +31 -9
  64. package/src/translations.ts +63 -0
  65. package/src/types.ts +69 -16
  66. package/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap +0 -76
package/lib/index.js CHANGED
@@ -8,14 +8,13 @@
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
- const path_1 = tslib_1.__importDefault(require("path"));
13
- const remark_admonitions_1 = tslib_1.__importDefault(require("remark-admonitions"));
11
+ const path_1 = (0, tslib_1.__importDefault)(require("path"));
12
+ const remark_admonitions_1 = (0, tslib_1.__importDefault)(require("remark-admonitions"));
14
13
  const utils_1 = require("@docusaurus/utils");
15
- const constants_1 = require("@docusaurus/core/lib/constants");
16
- const lodash_1 = require("lodash");
14
+ const translations_1 = require("./translations");
17
15
  const pluginOptionSchema_1 = require("./pluginOptionSchema");
18
16
  const blogUtils_1 = require("./blogUtils");
17
+ const feed_1 = require("./feed");
19
18
  function pluginContentBlog(context, options) {
20
19
  var _a;
21
20
  if (options.admonitions) {
@@ -23,42 +22,47 @@ function pluginContentBlog(context, options) {
23
22
  [remark_admonitions_1.default, options.admonitions],
24
23
  ]);
25
24
  }
26
- const { siteDir, siteConfig: { onBrokenMarkdownLinks }, generatedFilesDir, i18n: { currentLocale }, } = context;
25
+ const { siteDir, siteConfig, generatedFilesDir, i18n: { currentLocale }, } = context;
26
+ const { onBrokenMarkdownLinks, baseUrl } = siteConfig;
27
27
  const contentPaths = {
28
28
  contentPath: path_1.default.resolve(siteDir, options.path),
29
- contentPathLocalized: utils_1.getPluginI18nPath({
29
+ contentPathLocalized: (0, utils_1.getPluginI18nPath)({
30
30
  siteDir,
31
31
  locale: currentLocale,
32
32
  pluginName: 'docusaurus-plugin-content-blog',
33
33
  pluginId: options.id,
34
34
  }),
35
35
  };
36
- const pluginId = (_a = options.id) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_PLUGIN_ID;
36
+ const pluginId = (_a = options.id) !== null && _a !== void 0 ? _a : utils_1.DEFAULT_PLUGIN_ID;
37
37
  const pluginDataDirRoot = path_1.default.join(generatedFilesDir, 'docusaurus-plugin-content-blog');
38
38
  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 = [];
39
+ const aliasedSource = (source) => `~blog/${(0, utils_1.posixPath)(path_1.default.relative(pluginDataDirRoot, source))}`;
41
40
  return {
42
41
  name: 'docusaurus-plugin-content-blog',
43
42
  getPathsToWatch() {
44
- const { include = [] } = options;
45
- return lodash_1.flatten(blogUtils_1.getContentPathList(contentPaths).map((contentPath) => {
46
- return include.map((pattern) => `${contentPath}/${pattern}`);
47
- }));
43
+ const { include, authorsMapPath } = options;
44
+ const contentMarkdownGlobs = (0, blogUtils_1.getContentPathList)(contentPaths).flatMap((contentPath) => include.map((pattern) => `${contentPath}/${pattern}`));
45
+ // TODO: we should read this path in plugin! but plugins do not support async init for now :'(
46
+ // const authorsMapFilePath = await getAuthorsMapFilePath({authorsMapPath,contentPaths,});
47
+ // simplified impl, better than nothing for now:
48
+ const authorsMapFilePath = path_1.default.join(contentPaths.contentPath, authorsMapPath);
49
+ return [authorsMapFilePath, ...contentMarkdownGlobs];
48
50
  },
49
- getClientModules() {
50
- const modules = [];
51
- if (options.admonitions) {
52
- modules.push(require.resolve('remark-admonitions/styles/infima.css'));
53
- }
54
- return modules;
51
+ async getTranslationFiles() {
52
+ return (0, translations_1.getTranslationFiles)(options);
55
53
  },
56
54
  // Fetches blog contents and returns metadata for the necessary routes.
57
55
  async loadContent() {
58
- const { postsPerPage, routeBasePath } = options;
59
- blogPosts = await blogUtils_1.generateBlogPosts(contentPaths, context, options);
56
+ const { postsPerPage: postsPerPageOption, routeBasePath, tagsBasePath, blogDescription, blogTitle, blogSidebarTitle, } = options;
57
+ const blogPosts = await (0, blogUtils_1.generateBlogPosts)(contentPaths, context, options);
60
58
  if (!blogPosts.length) {
61
- return null;
59
+ return {
60
+ blogSidebarTitle,
61
+ blogPosts: [],
62
+ blogListPaginated: [],
63
+ blogTags: {},
64
+ blogTagsListPath: null,
65
+ };
62
66
  }
63
67
  // Colocate next and prev metadata.
64
68
  blogPosts.forEach((blogPost, index) => {
@@ -80,14 +84,14 @@ function pluginContentBlog(context, options) {
80
84
  // Blog pagination routes.
81
85
  // Example: `/blog`, `/blog/page/1`, `/blog/page/2`
82
86
  const totalCount = blogPosts.length;
87
+ const postsPerPage = postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption;
83
88
  const numberOfPages = Math.ceil(totalCount / postsPerPage);
84
- const { siteConfig: { baseUrl = '' }, } = context;
85
- const basePageUrl = utils_1.normalizeUrl([baseUrl, routeBasePath]);
89
+ const baseBlogUrl = (0, utils_1.normalizeUrl)([baseUrl, routeBasePath]);
86
90
  const blogListPaginated = [];
87
91
  function blogPaginationPermalink(page) {
88
92
  return page > 0
89
- ? utils_1.normalizeUrl([basePageUrl, `page/${page + 1}`])
90
- : basePageUrl;
93
+ ? (0, utils_1.normalizeUrl)([baseBlogUrl, `page/${page + 1}`])
94
+ : baseBlogUrl;
91
95
  }
92
96
  for (let page = 0; page < numberOfPages; page += 1) {
93
97
  blogListPaginated.push({
@@ -101,48 +105,19 @@ function pluginContentBlog(context, options) {
101
105
  nextPage: page < numberOfPages - 1
102
106
  ? blogPaginationPermalink(page + 1)
103
107
  : null,
104
- blogDescription: options.blogDescription,
105
- blogTitle: options.blogTitle,
108
+ blogDescription,
109
+ blogTitle,
106
110
  },
107
111
  items: blogPosts
108
112
  .slice(page * postsPerPage, (page + 1) * postsPerPage)
109
113
  .map((item) => item.id),
110
114
  });
111
115
  }
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
- });
143
- });
116
+ const blogTags = (0, blogUtils_1.getBlogTags)(blogPosts);
117
+ const tagsPath = (0, utils_1.normalizeUrl)([baseBlogUrl, tagsBasePath]);
144
118
  const blogTagsListPath = Object.keys(blogTags).length > 0 ? tagsPath : null;
145
119
  return {
120
+ blogSidebarTitle,
146
121
  blogPosts,
147
122
  blogListPaginated,
148
123
  blogTags,
@@ -153,31 +128,46 @@ function pluginContentBlog(context, options) {
153
128
  if (!blogContents) {
154
129
  return;
155
130
  }
156
- const { blogListComponent, blogPostComponent, blogTagsListComponent, blogTagsPostsComponent, } = options;
131
+ const { blogListComponent, blogPostComponent, blogTagsListComponent, blogTagsPostsComponent, routeBasePath, archiveBasePath, } = options;
157
132
  const { addRoute, createData } = actions;
158
- const { blogPosts: loadedBlogPosts, blogListPaginated, blogTags, blogTagsListPath, } = blogContents;
133
+ const { blogSidebarTitle, blogPosts, blogListPaginated, blogTags, blogTagsListPath, } = blogContents;
159
134
  const blogItemsToMetadata = {};
160
135
  const sidebarBlogPosts = options.blogSidebarCount === 'ALL'
161
136
  ? blogPosts
162
- : lodash_1.take(blogPosts, options.blogSidebarCount);
137
+ : blogPosts.slice(0, options.blogSidebarCount);
138
+ const archiveUrl = (0, utils_1.normalizeUrl)([
139
+ baseUrl,
140
+ routeBasePath,
141
+ archiveBasePath,
142
+ ]);
143
+ // creates a blog archive route
144
+ const archiveProp = await createData(`${(0, utils_1.docuHash)(archiveUrl)}.json`, JSON.stringify({ blogPosts }, null, 2));
145
+ addRoute({
146
+ path: archiveUrl,
147
+ component: '@theme/BlogArchivePage',
148
+ exact: true,
149
+ modules: {
150
+ archive: aliasedSource(archiveProp),
151
+ },
152
+ });
163
153
  // This prop is useful to provide the blog list sidebar
164
154
  const sidebarProp = await createData(
165
155
  // Note that this created data path must be in sync with
166
156
  // metadataPath provided to mdx-loader.
167
157
  `blog-post-list-prop-${pluginId}.json`, JSON.stringify({
168
- title: options.blogSidebarTitle,
158
+ title: blogSidebarTitle,
169
159
  items: sidebarBlogPosts.map((blogPost) => ({
170
160
  title: blogPost.metadata.title,
171
161
  permalink: blogPost.metadata.permalink,
172
162
  })),
173
163
  }, null, 2));
174
164
  // Create routes for blog entries.
175
- await Promise.all(loadedBlogPosts.map(async (blogPost) => {
165
+ await Promise.all(blogPosts.map(async (blogPost) => {
176
166
  const { id, metadata } = blogPost;
177
167
  await createData(
178
168
  // Note that this created data path must be in sync with
179
169
  // metadataPath provided to mdx-loader.
180
- `${utils_1.docuHash(metadata.source)}.json`, JSON.stringify(metadata, null, 2));
170
+ `${(0, utils_1.docuHash)(metadata.source)}.json`, JSON.stringify(metadata, null, 2));
181
171
  addRoute({
182
172
  path: metadata.permalink,
183
173
  component: blogPostComponent,
@@ -193,25 +183,24 @@ function pluginContentBlog(context, options) {
193
183
  await Promise.all(blogListPaginated.map(async (listPage) => {
194
184
  const { metadata, items } = listPage;
195
185
  const { permalink } = metadata;
196
- const pageMetadataPath = await createData(`${utils_1.docuHash(permalink)}.json`, JSON.stringify(metadata, null, 2));
186
+ const pageMetadataPath = await createData(`${(0, utils_1.docuHash)(permalink)}.json`, JSON.stringify(metadata, null, 2));
197
187
  addRoute({
198
188
  path: permalink,
199
189
  component: blogListComponent,
200
190
  exact: true,
201
191
  modules: {
202
192
  sidebar: aliasedSource(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
- },
193
+ items: items.map((postID) =>
194
+ // To tell routes.js this is an import and not a nested object to recurse.
195
+ ({
196
+ content: {
197
+ __import: true,
198
+ path: blogItemsToMetadata[postID].source,
199
+ query: {
200
+ truncated: true,
212
201
  },
213
- };
214
- }),
202
+ },
203
+ })),
215
204
  metadata: aliasedSource(pageMetadataPath),
216
205
  },
217
206
  });
@@ -223,6 +212,7 @@ function pluginContentBlog(context, options) {
223
212
  const tagsModule = {};
224
213
  await Promise.all(Object.keys(blogTags).map(async (tag) => {
225
214
  const { name, items, permalink } = blogTags[tag];
215
+ // Refactor all this, see docs implementation
226
216
  tagsModule[tag] = {
227
217
  allTagsPath: blogTagsListPath,
228
218
  slug: tag,
@@ -230,7 +220,7 @@ function pluginContentBlog(context, options) {
230
220
  count: items.length,
231
221
  permalink,
232
222
  };
233
- const tagsMetadataPath = await createData(`${utils_1.docuHash(permalink)}.json`, JSON.stringify(tagsModule[tag], null, 2));
223
+ const tagsMetadataPath = await createData(`${(0, utils_1.docuHash)(permalink)}.json`, JSON.stringify(tagsModule[tag], null, 2));
234
224
  addRoute({
235
225
  path: permalink,
236
226
  component: blogTagsPostsComponent,
@@ -255,7 +245,7 @@ function pluginContentBlog(context, options) {
255
245
  }));
256
246
  // Only create /tags page if there are tags.
257
247
  if (Object.keys(blogTags).length > 0) {
258
- const tagsListPath = await createData(`${utils_1.docuHash(`${blogTagsListPath}-tags`)}.json`, JSON.stringify(tagsModule, null, 2));
248
+ const tagsListPath = await createData(`${(0, utils_1.docuHash)(`${blogTagsListPath}-tags`)}.json`, JSON.stringify(tagsModule, null, 2));
259
249
  addRoute({
260
250
  path: blogTagsListPath,
261
251
  component: blogTagsListComponent,
@@ -267,20 +257,24 @@ function pluginContentBlog(context, options) {
267
257
  });
268
258
  }
269
259
  },
270
- configureWebpack(_config, isServer, { getJSLoader }) {
260
+ translateContent({ content, translationFiles }) {
261
+ return (0, translations_1.translateContent)(content, translationFiles);
262
+ },
263
+ configureWebpack(_config, isServer, { getJSLoader }, content) {
271
264
  const { rehypePlugins, remarkPlugins, truncateMarker, beforeDefaultRemarkPlugins, beforeDefaultRehypePlugins, } = options;
272
265
  const markdownLoaderOptions = {
273
266
  siteDir,
274
267
  contentPaths,
275
268
  truncateMarker,
276
- sourceToPermalink: blogUtils_1.getSourceToPermalink(blogPosts),
269
+ sourceToPermalink: (0, blogUtils_1.getSourceToPermalink)(content.blogPosts),
277
270
  onBrokenMarkdownLink: (brokenMarkdownLink) => {
278
271
  if (onBrokenMarkdownLinks === 'ignore') {
279
272
  return;
280
273
  }
281
- utils_1.reportMessage(`Blog markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath}`, onBrokenMarkdownLinks);
274
+ (0, utils_1.reportMessage)(`Blog markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath}`, onBrokenMarkdownLinks);
282
275
  },
283
276
  };
277
+ const contentDirs = (0, blogUtils_1.getContentPathList)(contentPaths);
284
278
  return {
285
279
  resolve: {
286
280
  alias: {
@@ -291,7 +285,7 @@ function pluginContentBlog(context, options) {
291
285
  rules: [
292
286
  {
293
287
  test: /(\.mdx?)$/,
294
- include: blogUtils_1.getContentPathList(contentPaths)
288
+ include: contentDirs
295
289
  // Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
296
290
  .map(utils_1.addTrailingPathSeparator),
297
291
  use: [
@@ -303,16 +297,23 @@ function pluginContentBlog(context, options) {
303
297
  rehypePlugins,
304
298
  beforeDefaultRemarkPlugins,
305
299
  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.
300
+ staticDirs: siteConfig.staticDirectories.map((dir) => path_1.default.resolve(siteDir, dir)),
301
+ siteDir,
302
+ isMDXPartial: (0, utils_1.createAbsoluteFilePathMatcher)(options.exclude, contentDirs),
309
303
  metadataPath: (mdxPath) => {
310
- const aliasedPath = utils_1.aliasedSitePath(mdxPath, siteDir);
311
- return path_1.default.join(dataDir, `${utils_1.docuHash(aliasedPath)}.json`);
304
+ // Note that metadataPath must be the same/in-sync as
305
+ // the path from createData for each MDX.
306
+ const aliasedPath = (0, utils_1.aliasedSitePath)(mdxPath, siteDir);
307
+ return path_1.default.join(dataDir, `${(0, utils_1.docuHash)(aliasedPath)}.json`);
312
308
  },
313
309
  // For blog posts a title in markdown is always removed
314
310
  // Blog posts title are rendered separately
315
311
  removeContentTitle: true,
312
+ // Assets allow to convert some relative images paths to require() calls
313
+ createAssets: ({ frontMatter, metadata, }) => ({
314
+ image: frontMatter.image,
315
+ authorsImageUrls: metadata.authors.map((author) => author.imageURL),
316
+ }),
316
317
  },
317
318
  },
318
319
  {
@@ -326,46 +327,42 @@ function pluginContentBlog(context, options) {
326
327
  };
327
328
  },
328
329
  async postBuild({ outDir }) {
329
- var _a;
330
- if (!((_a = options.feedOptions) === null || _a === void 0 ? void 0 : _a.type)) {
330
+ if (!options.feedOptions.type) {
331
331
  return;
332
332
  }
333
- const feed = await blogUtils_1.generateBlogFeed(contentPaths, context, options);
334
- if (!feed) {
333
+ // TODO: we shouldn't need to re-read the posts here!
334
+ // postBuild should receive loadedContent
335
+ const blogPosts = await (0, blogUtils_1.generateBlogPosts)(contentPaths, context, options);
336
+ if (!blogPosts.length) {
335
337
  return;
336
338
  }
337
- const feedTypes = options.feedOptions.type;
338
- await Promise.all(feedTypes.map(async (feedType) => {
339
- const feedPath = path_1.default.join(outDir, options.routeBasePath, `${feedType}.xml`);
340
- const feedContent = feedType === 'rss' ? feed.rss2() : feed.atom1();
341
- try {
342
- await fs_extra_1.default.outputFile(feedPath, feedContent);
343
- }
344
- catch (err) {
345
- throw new Error(`Generating ${feedType} feed failed: ${err}.`);
346
- }
347
- }));
339
+ await (0, feed_1.createBlogFeedFiles)({
340
+ blogPosts,
341
+ options,
342
+ outDir,
343
+ siteConfig,
344
+ });
348
345
  },
349
- injectHtmlTags() {
350
- var _a;
351
- if (!blogPosts.length) {
346
+ injectHtmlTags({ content }) {
347
+ var _a, _b;
348
+ if (!content.blogPosts.length) {
352
349
  return {};
353
350
  }
354
351
  if (!((_a = options.feedOptions) === null || _a === void 0 ? void 0 : _a.type)) {
355
352
  return {};
356
353
  }
357
354
  const feedTypes = options.feedOptions.type;
358
- const { siteConfig: { title }, baseUrl, } = context;
355
+ const feedTitle = (_b = options.feedOptions.title) !== null && _b !== void 0 ? _b : context.siteConfig.title;
359
356
  const feedsConfig = {
360
357
  rss: {
361
358
  type: 'application/rss+xml',
362
359
  path: 'rss.xml',
363
- title: `${title} Blog RSS Feed`,
360
+ title: `${feedTitle} RSS Feed`,
364
361
  },
365
362
  atom: {
366
363
  type: 'application/atom+xml',
367
364
  path: 'atom.xml',
368
- title: `${title} Blog Atom Feed`,
365
+ title: `${feedTitle} Atom Feed`,
369
366
  },
370
367
  };
371
368
  const headTags = [];
@@ -380,7 +377,7 @@ function pluginContentBlog(context, options) {
380
377
  attributes: {
381
378
  rel: 'alternate',
382
379
  type,
383
- href: utils_1.normalizeUrl([
380
+ href: (0, utils_1.normalizeUrl)([
384
381
  baseUrl,
385
382
  options.routeBasePath,
386
383
  feedConfigPath,
@@ -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 { BlogMarkdownLoaderOptions } from './types';
8
+ import type { LoaderContext } from 'webpack';
9
+ export default function markdownLoader(this: LoaderContext<BlogMarkdownLoaderOptions>, source: string): void;
@@ -8,24 +8,24 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  const blogUtils_1 = require("./blogUtils");
10
10
  const loader_utils_1 = require("loader-utils");
11
- const markdownLoader = function (source) {
11
+ function markdownLoader(source) {
12
12
  const filePath = this.resourcePath;
13
13
  const fileString = source;
14
14
  const callback = this.async();
15
15
  const markdownLoaderOptions = this.getOptions();
16
16
  // Linkify blog posts
17
- let finalContent = blogUtils_1.linkify({
17
+ let finalContent = (0, blogUtils_1.linkify)({
18
18
  fileString,
19
19
  filePath,
20
20
  ...markdownLoaderOptions,
21
21
  });
22
22
  // Truncate content if requested (e.g: file.md?truncated=true).
23
23
  const truncated = this.resourceQuery
24
- ? !!loader_utils_1.parseQuery(this.resourceQuery).truncated
24
+ ? !!(0, loader_utils_1.parseQuery)(this.resourceQuery).truncated
25
25
  : undefined;
26
26
  if (truncated) {
27
- finalContent = blogUtils_1.truncate(finalContent, markdownLoaderOptions.truncateMarker);
27
+ finalContent = (0, blogUtils_1.truncate)(finalContent, markdownLoaderOptions.truncateMarker);
28
28
  }
29
29
  return callback && callback(null, finalContent);
30
- };
30
+ }
31
31
  exports.default = markdownLoader;
@@ -5,29 +5,6 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import { Joi } from '@docusaurus/utils-validation';
8
- export declare const DEFAULT_OPTIONS: {
9
- feedOptions: {
10
- type: string[];
11
- };
12
- beforeDefaultRehypePlugins: never[];
13
- beforeDefaultRemarkPlugins: never[];
14
- admonitions: {};
15
- truncateMarker: RegExp;
16
- rehypePlugins: never[];
17
- remarkPlugins: never[];
18
- showReadingTime: boolean;
19
- blogTagsPostsComponent: string;
20
- blogTagsListComponent: string;
21
- blogPostComponent: string;
22
- blogListComponent: string;
23
- blogDescription: string;
24
- blogTitle: string;
25
- blogSidebarCount: number;
26
- blogSidebarTitle: string;
27
- postsPerPage: number;
28
- include: string[];
29
- routeBasePath: string;
30
- path: string;
31
- editLocalizedFiles: boolean;
32
- };
33
- export declare const PluginOptionSchema: Joi.ObjectSchema<any>;
8
+ import { PluginOptions } from './types';
9
+ export declare const DEFAULT_OPTIONS: PluginOptions;
10
+ export declare const PluginOptionSchema: Joi.ObjectSchema<PluginOptions>;
@@ -8,8 +8,9 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.PluginOptionSchema = exports.DEFAULT_OPTIONS = void 0;
10
10
  const utils_validation_1 = require("@docusaurus/utils-validation");
11
+ const utils_1 = require("@docusaurus/utils");
11
12
  exports.DEFAULT_OPTIONS = {
12
- feedOptions: { type: ['rss', 'atom'] },
13
+ feedOptions: { type: ['rss', 'atom'], copyright: '' },
13
14
  beforeDefaultRehypePlugins: [],
14
15
  beforeDefaultRemarkPlugins: [],
15
16
  admonitions: {},
@@ -26,21 +27,29 @@ exports.DEFAULT_OPTIONS = {
26
27
  blogSidebarCount: 5,
27
28
  blogSidebarTitle: 'Recent posts',
28
29
  postsPerPage: 10,
29
- include: ['*.md', '*.mdx'],
30
+ include: ['**/*.{md,mdx}'],
31
+ exclude: utils_1.GlobExcludeDefault,
30
32
  routeBasePath: 'blog',
33
+ tagsBasePath: 'tags',
34
+ archiveBasePath: 'archive',
31
35
  path: 'blog',
32
36
  editLocalizedFiles: false,
37
+ authorsMapPath: 'authors.yml',
38
+ readingTime: ({ content, defaultReadingTime }) => defaultReadingTime({ content }),
39
+ sortPosts: 'descending',
33
40
  };
34
41
  exports.PluginOptionSchema = utils_validation_1.Joi.object({
35
42
  path: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.path),
43
+ archiveBasePath: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.archiveBasePath),
36
44
  routeBasePath: utils_validation_1.Joi.string()
37
45
  // '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
38
46
  // .allow('')
39
47
  .default(exports.DEFAULT_OPTIONS.routeBasePath),
48
+ tagsBasePath: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.tagsBasePath),
40
49
  include: utils_validation_1.Joi.array().items(utils_validation_1.Joi.string()).default(exports.DEFAULT_OPTIONS.include),
41
- postsPerPage: utils_validation_1.Joi.number()
42
- .integer()
43
- .min(1)
50
+ exclude: utils_validation_1.Joi.array().items(utils_validation_1.Joi.string()).default(exports.DEFAULT_OPTIONS.exclude),
51
+ postsPerPage: utils_validation_1.Joi.alternatives()
52
+ .try(utils_validation_1.Joi.equal('ALL').required(), utils_validation_1.Joi.number().integer().min(1).required())
44
53
  .default(exports.DEFAULT_OPTIONS.postsPerPage),
45
54
  blogListComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.blogListComponent),
46
55
  blogPostComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.blogPostComponent),
@@ -51,7 +60,7 @@ exports.PluginOptionSchema = utils_validation_1.Joi.object({
51
60
  .allow('')
52
61
  .default(exports.DEFAULT_OPTIONS.blogDescription),
53
62
  blogSidebarCount: utils_validation_1.Joi.alternatives()
54
- .try(utils_validation_1.Joi.equal('ALL').required(), utils_validation_1.Joi.number().required())
63
+ .try(utils_validation_1.Joi.equal('ALL').required(), utils_validation_1.Joi.number().integer().min(0).required())
55
64
  .default(exports.DEFAULT_OPTIONS.blogSidebarCount),
56
65
  blogSidebarTitle: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.blogSidebarTitle),
57
66
  showReadingTime: utils_validation_1.Joi.bool().default(exports.DEFAULT_OPTIONS.showReadingTime),
@@ -72,7 +81,19 @@ exports.PluginOptionSchema = utils_validation_1.Joi.object({
72
81
  .default(exports.DEFAULT_OPTIONS.feedOptions.type),
73
82
  title: utils_validation_1.Joi.string().allow(''),
74
83
  description: utils_validation_1.Joi.string().allow(''),
75
- copyright: utils_validation_1.Joi.string(),
84
+ // only add default value when user actually wants a feed (type is not null)
85
+ copyright: utils_validation_1.Joi.when('type', {
86
+ is: utils_validation_1.Joi.any().valid(null),
87
+ then: utils_validation_1.Joi.string().optional(),
88
+ otherwise: utils_validation_1.Joi.string()
89
+ .allow('')
90
+ .default(exports.DEFAULT_OPTIONS.feedOptions.copyright),
91
+ }),
76
92
  language: utils_validation_1.Joi.string(),
77
93
  }).default(exports.DEFAULT_OPTIONS.feedOptions),
94
+ authorsMapPath: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.authorsMapPath),
95
+ readingTime: utils_validation_1.Joi.function().default(() => exports.DEFAULT_OPTIONS.readingTime),
96
+ sortPosts: utils_validation_1.Joi.string()
97
+ .valid('descending', 'ascending')
98
+ .default(exports.DEFAULT_OPTIONS.sortPosts),
78
99
  });
@@ -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 { BlogContent, PluginOptions } from './types';
8
+ import type { TranslationFiles } from '@docusaurus/types';
9
+ export declare function getTranslationFiles(options: PluginOptions): TranslationFiles;
10
+ export declare function translateContent(content: BlogContent, translationFiles: TranslationFiles): BlogContent;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.translateContent = exports.getTranslationFiles = void 0;
10
+ function translateListPage(blogListPaginated, translations) {
11
+ return blogListPaginated.map((page) => {
12
+ const { items, metadata } = page;
13
+ return {
14
+ items,
15
+ metadata: {
16
+ ...metadata,
17
+ blogTitle: translations.title.message,
18
+ blogDescription: translations.description.message,
19
+ },
20
+ };
21
+ });
22
+ }
23
+ function getTranslationFiles(options) {
24
+ return [
25
+ {
26
+ path: 'options',
27
+ content: {
28
+ title: {
29
+ message: options.blogTitle,
30
+ description: 'The title for the blog used in SEO',
31
+ },
32
+ description: {
33
+ message: options.blogDescription,
34
+ description: 'The description for the blog used in SEO',
35
+ },
36
+ 'sidebar.title': {
37
+ message: options.blogSidebarTitle,
38
+ description: 'The label for the left sidebar',
39
+ },
40
+ },
41
+ },
42
+ ];
43
+ }
44
+ exports.getTranslationFiles = getTranslationFiles;
45
+ function translateContent(content, translationFiles) {
46
+ const [{ content: optonsTranslations }] = translationFiles;
47
+ return {
48
+ ...content,
49
+ blogSidebarTitle: optonsTranslations['sidebar.title'].message,
50
+ blogListPaginated: translateListPage(content.blogListPaginated, optonsTranslations),
51
+ };
52
+ }
53
+ exports.translateContent = translateContent;