@docusaurus/plugin-content-blog 3.3.2 → 3.4.0

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.
package/src/blogUtils.ts CHANGED
@@ -17,9 +17,7 @@ import {
17
17
  getEditUrl,
18
18
  getFolderContainingFile,
19
19
  posixPath,
20
- replaceMarkdownLinks,
21
20
  Globby,
22
- normalizeFrontMatterTags,
23
21
  groupTaggedItems,
24
22
  getTagVisibility,
25
23
  getFileCommitDate,
@@ -27,9 +25,12 @@ import {
27
25
  isUnlisted,
28
26
  isDraft,
29
27
  readLastUpdateData,
28
+ normalizeTags,
30
29
  } from '@docusaurus/utils';
30
+ import {getTagsFile} from '@docusaurus/utils-validation';
31
31
  import {validateBlogPostFrontMatter} from './frontMatter';
32
32
  import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
33
+ import type {TagsFile} from '@docusaurus/utils';
33
34
  import type {LoadContext, ParseFrontMatter} from '@docusaurus/types';
34
35
  import type {
35
36
  PluginOptions,
@@ -38,20 +39,12 @@ import type {
38
39
  BlogTags,
39
40
  BlogPaginated,
40
41
  } from '@docusaurus/plugin-content-blog';
41
- import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types';
42
+ import type {BlogContentPaths} from './types';
42
43
 
43
44
  export function truncate(fileString: string, truncateMarker: RegExp): string {
44
45
  return fileString.split(truncateMarker, 1).shift()!;
45
46
  }
46
47
 
47
- export function getSourceToPermalink(blogPosts: BlogPost[]): {
48
- [aliasedPath: string]: string;
49
- } {
50
- return Object.fromEntries(
51
- blogPosts.map(({metadata: {source, permalink}}) => [source, permalink]),
52
- );
53
- }
54
-
55
48
  export function paginateBlogPosts({
56
49
  blogPosts,
57
50
  basePageUrl,
@@ -126,9 +119,11 @@ export function getBlogTags({
126
119
  isUnlisted: (item) => item.metadata.unlisted,
127
120
  });
128
121
  return {
122
+ inline: tag.inline,
129
123
  label: tag.label,
130
- items: tagVisibility.listedItems.map((item) => item.id),
131
124
  permalink: tag.permalink,
125
+ description: tag.description,
126
+ items: tagVisibility.listedItems.map((item) => item.id),
132
127
  pages: paginateBlogPosts({
133
128
  blogPosts: tagVisibility.listedItems,
134
129
  basePageUrl: tag.permalink,
@@ -198,6 +193,7 @@ async function processBlogSourceFile(
198
193
  contentPaths: BlogContentPaths,
199
194
  context: LoadContext,
200
195
  options: PluginOptions,
196
+ tagsFile: TagsFile | null,
201
197
  authorsMap?: AuthorsMap,
202
198
  ): Promise<BlogPost | undefined> {
203
199
  const {
@@ -316,13 +312,21 @@ async function processBlogSourceFile(
316
312
  return undefined;
317
313
  }
318
314
 
319
- const tagsBasePath = normalizeUrl([
315
+ const tagsBaseRoutePath = normalizeUrl([
320
316
  baseUrl,
321
317
  routeBasePath,
322
318
  tagsRouteBasePath,
323
319
  ]);
324
320
  const authors = getBlogPostAuthors({authorsMap, frontMatter, baseUrl});
325
321
 
322
+ const tags = normalizeTags({
323
+ options,
324
+ source: blogSourceRelative,
325
+ frontMatterTags: frontMatter.tags,
326
+ tagsBaseRoutePath,
327
+ tagsFile,
328
+ });
329
+
326
330
  return {
327
331
  id: slug,
328
332
  metadata: {
@@ -332,7 +336,7 @@ async function processBlogSourceFile(
332
336
  title,
333
337
  description,
334
338
  date,
335
- tags: normalizeFrontMatterTags(tagsBasePath, frontMatter.tags),
339
+ tags,
336
340
  readingTime: showReadingTime
337
341
  ? options.readingTime({
338
342
  content,
@@ -372,6 +376,8 @@ export async function generateBlogPosts(
372
376
  authorsMapPath: options.authorsMapPath,
373
377
  });
374
378
 
379
+ const tagsFile = await getTagsFile({contentPaths, tags: options.tags});
380
+
375
381
  async function doProcessBlogSourceFile(blogSourceFile: string) {
376
382
  try {
377
383
  return await processBlogSourceFile(
@@ -379,6 +385,7 @@ export async function generateBlogPosts(
379
385
  contentPaths,
380
386
  context,
381
387
  options,
388
+ tagsFile,
382
389
  authorsMap,
383
390
  );
384
391
  } catch (err) {
@@ -403,35 +410,6 @@ export async function generateBlogPosts(
403
410
  return blogPosts;
404
411
  }
405
412
 
406
- export type LinkifyParams = {
407
- filePath: string;
408
- fileString: string;
409
- } & Pick<
410
- BlogMarkdownLoaderOptions,
411
- 'sourceToPermalink' | 'siteDir' | 'contentPaths' | 'onBrokenMarkdownLink'
412
- >;
413
-
414
- export function linkify({
415
- filePath,
416
- contentPaths,
417
- fileString,
418
- siteDir,
419
- sourceToPermalink,
420
- onBrokenMarkdownLink,
421
- }: LinkifyParams): string {
422
- const {newContent, brokenMarkdownLinks} = replaceMarkdownLinks({
423
- siteDir,
424
- fileString,
425
- filePath,
426
- contentPaths,
427
- sourceToPermalink,
428
- });
429
-
430
- brokenMarkdownLinks.forEach((l) => onBrokenMarkdownLink(l));
431
-
432
- return newContent;
433
- }
434
-
435
413
  export async function applyProcessBlogPosts({
436
414
  blogPosts,
437
415
  processBlogPosts,
package/src/feed.ts CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  applyTrailingSlash,
17
17
  } from '@docusaurus/utils-common';
18
18
  import {load as cheerioLoad} from 'cheerio';
19
- import type {DocusaurusConfig} from '@docusaurus/types';
19
+ import type {DocusaurusConfig, HtmlTags, LoadContext} from '@docusaurus/types';
20
20
  import type {
21
21
  FeedType,
22
22
  PluginOptions,
@@ -254,3 +254,59 @@ export async function createBlogFeedFiles({
254
254
  ),
255
255
  );
256
256
  }
257
+
258
+ export function createFeedHtmlHeadTags({
259
+ context,
260
+ options,
261
+ }: {
262
+ context: LoadContext;
263
+ options: PluginOptions;
264
+ }): HtmlTags {
265
+ const feedTypes = options.feedOptions.type;
266
+ if (!feedTypes) {
267
+ return [];
268
+ }
269
+ const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
270
+ const feedsConfig = {
271
+ rss: {
272
+ type: 'application/rss+xml',
273
+ path: 'rss.xml',
274
+ title: `${feedTitle} RSS Feed`,
275
+ },
276
+ atom: {
277
+ type: 'application/atom+xml',
278
+ path: 'atom.xml',
279
+ title: `${feedTitle} Atom Feed`,
280
+ },
281
+ json: {
282
+ type: 'application/json',
283
+ path: 'feed.json',
284
+ title: `${feedTitle} JSON Feed`,
285
+ },
286
+ };
287
+ const headTags: HtmlTags = [];
288
+
289
+ feedTypes.forEach((feedType) => {
290
+ const {
291
+ type,
292
+ path: feedConfigPath,
293
+ title: feedConfigTitle,
294
+ } = feedsConfig[feedType];
295
+
296
+ headTags.push({
297
+ tagName: 'link',
298
+ attributes: {
299
+ rel: 'alternate',
300
+ type,
301
+ href: normalizeUrl([
302
+ context.siteConfig.baseUrl,
303
+ options.routeBasePath,
304
+ feedConfigPath,
305
+ ]),
306
+ title: feedConfigTitle,
307
+ },
308
+ });
309
+ });
310
+
311
+ return headTags;
312
+ }
package/src/index.ts CHANGED
@@ -18,9 +18,11 @@ import {
18
18
  getContentPathList,
19
19
  getDataFilePath,
20
20
  DEFAULT_PLUGIN_ID,
21
+ resolveMarkdownLinkPathname,
22
+ type SourceToPermalink,
21
23
  } from '@docusaurus/utils';
24
+ import {getTagsFilePathsToWatch} from '@docusaurus/utils-validation';
22
25
  import {
23
- getSourceToPermalink,
24
26
  getBlogTags,
25
27
  paginateBlogPosts,
26
28
  shouldBeListed,
@@ -29,11 +31,11 @@ import {
29
31
  } from './blogUtils';
30
32
  import footnoteIDFixer from './remark/footnoteIDFixer';
31
33
  import {translateContent, getTranslationFiles} from './translations';
32
- import {createBlogFeedFiles} from './feed';
34
+ import {createBlogFeedFiles, createFeedHtmlHeadTags} from './feed';
33
35
 
34
36
  import {createAllRoutes} from './routes';
35
37
  import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types';
36
- import type {LoadContext, Plugin, HtmlTags} from '@docusaurus/types';
38
+ import type {LoadContext, Plugin} from '@docusaurus/types';
37
39
  import type {
38
40
  PluginOptions,
39
41
  BlogPostFrontMatter,
@@ -43,6 +45,37 @@ import type {
43
45
  BlogContent,
44
46
  BlogPaginated,
45
47
  } from '@docusaurus/plugin-content-blog';
48
+ import type {Options as MDXLoaderOptions} from '@docusaurus/mdx-loader/lib/loader';
49
+ import type {RuleSetUseItem} from 'webpack';
50
+
51
+ const PluginName = 'docusaurus-plugin-content-blog';
52
+
53
+ // TODO this is bad, we should have a better way to do this (new lifecycle?)
54
+ // The source to permalink is currently a mutable map passed to the mdx loader
55
+ // for link resolution
56
+ // see https://github.com/facebook/docusaurus/pull/10185
57
+ function createSourceToPermalinkHelper() {
58
+ const sourceToPermalink: SourceToPermalink = new Map();
59
+
60
+ function computeSourceToPermalink(content: BlogContent): SourceToPermalink {
61
+ return new Map(
62
+ content.blogPosts.map(({metadata: {source, permalink}}) => [
63
+ source,
64
+ permalink,
65
+ ]),
66
+ );
67
+ }
68
+
69
+ // Mutable map update :/
70
+ function update(content: BlogContent): void {
71
+ sourceToPermalink.clear();
72
+ computeSourceToPermalink(content).forEach((value, key) => {
73
+ sourceToPermalink.set(key, value);
74
+ });
75
+ }
76
+
77
+ return {get: () => sourceToPermalink, update};
78
+ }
46
79
 
47
80
  export default async function pluginContentBlog(
48
81
  context: LoadContext,
@@ -55,22 +88,29 @@ export default async function pluginContentBlog(
55
88
  localizationDir,
56
89
  i18n: {currentLocale},
57
90
  } = context;
91
+
92
+ const router = siteConfig.future.experimental_router;
93
+ const isBlogFeedDisabledBecauseOfHashRouter =
94
+ router === 'hash' && !!options.feedOptions.type;
95
+ if (isBlogFeedDisabledBecauseOfHashRouter) {
96
+ logger.warn(
97
+ `${PluginName} feed feature does not support the Hash Router. Feeds won't be generated.`,
98
+ );
99
+ }
100
+
58
101
  const {onBrokenMarkdownLinks, baseUrl} = siteConfig;
59
102
 
60
103
  const contentPaths: BlogContentPaths = {
61
104
  contentPath: path.resolve(siteDir, options.path),
62
105
  contentPathLocalized: getPluginI18nPath({
63
106
  localizationDir,
64
- pluginName: 'docusaurus-plugin-content-blog',
107
+ pluginName: PluginName,
65
108
  pluginId: options.id,
66
109
  }),
67
110
  };
68
111
  const pluginId = options.id ?? DEFAULT_PLUGIN_ID;
69
112
 
70
- const pluginDataDirRoot = path.join(
71
- generatedFilesDir,
72
- 'docusaurus-plugin-content-blog',
73
- );
113
+ const pluginDataDirRoot = path.join(generatedFilesDir, PluginName);
74
114
  const dataDir = path.join(pluginDataDirRoot, pluginId);
75
115
  // TODO Docusaurus v4 breaking change
76
116
  // module aliasing should be automatic
@@ -83,8 +123,10 @@ export default async function pluginContentBlog(
83
123
  contentPaths,
84
124
  });
85
125
 
126
+ const sourceToPermalinkHelper = createSourceToPermalinkHelper();
127
+
86
128
  return {
87
- name: 'docusaurus-plugin-content-blog',
129
+ name: PluginName,
88
130
 
89
131
  getPathsToWatch() {
90
132
  const {include} = options;
@@ -92,9 +134,16 @@ export default async function pluginContentBlog(
92
134
  (contentPath) => include.map((pattern) => `${contentPath}/${pattern}`),
93
135
  );
94
136
 
95
- return [authorsMapFilePath, ...contentMarkdownGlobs].filter(
96
- Boolean,
97
- ) as string[];
137
+ const tagsFilePaths = getTagsFilePathsToWatch({
138
+ contentPaths,
139
+ tags: options.tags,
140
+ });
141
+
142
+ return [
143
+ authorsMapFilePath,
144
+ ...tagsFilePaths,
145
+ ...contentMarkdownGlobs,
146
+ ].filter(Boolean) as string[];
98
147
  },
99
148
 
100
149
  getTranslationFiles() {
@@ -181,6 +230,8 @@ export default async function pluginContentBlog(
181
230
  },
182
231
 
183
232
  async contentLoaded({content, actions}) {
233
+ sourceToPermalinkHelper.update(content);
234
+
184
235
  await createAllRoutes({
185
236
  baseUrl,
186
237
  content,
@@ -194,7 +245,7 @@ export default async function pluginContentBlog(
194
245
  return translateContent(content, translationFiles);
195
246
  },
196
247
 
197
- configureWebpack(_config, isServer, utils, content) {
248
+ configureWebpack() {
198
249
  const {
199
250
  admonitions,
200
251
  rehypePlugins,
@@ -204,22 +255,80 @@ export default async function pluginContentBlog(
204
255
  beforeDefaultRehypePlugins,
205
256
  } = options;
206
257
 
207
- const markdownLoaderOptions: BlogMarkdownLoaderOptions = {
208
- siteDir,
209
- contentPaths,
210
- truncateMarker,
211
- sourceToPermalink: getSourceToPermalink(content.blogPosts),
212
- onBrokenMarkdownLink: (brokenMarkdownLink) => {
213
- if (onBrokenMarkdownLinks === 'ignore') {
214
- return;
215
- }
216
- logger.report(
217
- onBrokenMarkdownLinks,
218
- )`Blog markdown link couldn't be resolved: (url=${brokenMarkdownLink.link}) in path=${brokenMarkdownLink.filePath}`;
219
- },
220
- };
221
-
222
258
  const contentDirs = getContentPathList(contentPaths);
259
+
260
+ function createMDXLoader(): RuleSetUseItem {
261
+ const loaderOptions: MDXLoaderOptions = {
262
+ admonitions,
263
+ remarkPlugins,
264
+ rehypePlugins,
265
+ beforeDefaultRemarkPlugins: [
266
+ footnoteIDFixer,
267
+ ...beforeDefaultRemarkPlugins,
268
+ ],
269
+ beforeDefaultRehypePlugins,
270
+ staticDirs: siteConfig.staticDirectories.map((dir) =>
271
+ path.resolve(siteDir, dir),
272
+ ),
273
+ siteDir,
274
+ isMDXPartial: createAbsoluteFilePathMatcher(
275
+ options.exclude,
276
+ contentDirs,
277
+ ),
278
+ metadataPath: (mdxPath: string) => {
279
+ // Note that metadataPath must be the same/in-sync as
280
+ // the path from createData for each MDX.
281
+ const aliasedPath = aliasedSitePath(mdxPath, siteDir);
282
+ return path.join(dataDir, `${docuHash(aliasedPath)}.json`);
283
+ },
284
+ // For blog posts a title in markdown is always removed
285
+ // Blog posts title are rendered separately
286
+ removeContentTitle: true,
287
+ // Assets allow to convert some relative images paths to
288
+ // require() calls
289
+ // @ts-expect-error: TODO fix typing issue
290
+ createAssets: ({
291
+ frontMatter,
292
+ metadata,
293
+ }: {
294
+ frontMatter: BlogPostFrontMatter;
295
+ metadata: BlogPostMetadata;
296
+ }): Assets => ({
297
+ image: frontMatter.image,
298
+ authorsImageUrls: metadata.authors.map((author) => author.imageURL),
299
+ }),
300
+ markdownConfig: siteConfig.markdown,
301
+ resolveMarkdownLink: ({linkPathname, sourceFilePath}) => {
302
+ const permalink = resolveMarkdownLinkPathname(linkPathname, {
303
+ sourceFilePath,
304
+ sourceToPermalink: sourceToPermalinkHelper.get(),
305
+ siteDir,
306
+ contentPaths,
307
+ });
308
+ if (permalink === null) {
309
+ logger.report(
310
+ onBrokenMarkdownLinks,
311
+ )`Blog markdown link couldn't be resolved: (url=${linkPathname}) in source file path=${sourceFilePath}`;
312
+ }
313
+ return permalink;
314
+ },
315
+ };
316
+ return {
317
+ loader: require.resolve('@docusaurus/mdx-loader'),
318
+ options: loaderOptions,
319
+ };
320
+ }
321
+
322
+ function createBlogMarkdownLoader(): RuleSetUseItem {
323
+ const loaderOptions: BlogMarkdownLoaderOptions = {
324
+ truncateMarker,
325
+ };
326
+ return {
327
+ loader: path.resolve(__dirname, './markdownLoader.js'),
328
+ options: loaderOptions,
329
+ };
330
+ }
331
+
223
332
  return {
224
333
  resolve: {
225
334
  alias: {
@@ -233,61 +342,7 @@ export default async function pluginContentBlog(
233
342
  include: contentDirs
234
343
  // Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
235
344
  .map(addTrailingPathSeparator),
236
- use: [
237
- {
238
- loader: require.resolve('@docusaurus/mdx-loader'),
239
- options: {
240
- admonitions,
241
- remarkPlugins,
242
- rehypePlugins,
243
- beforeDefaultRemarkPlugins: [
244
- footnoteIDFixer,
245
- ...beforeDefaultRemarkPlugins,
246
- ],
247
- beforeDefaultRehypePlugins,
248
- staticDirs: siteConfig.staticDirectories.map((dir) =>
249
- path.resolve(siteDir, dir),
250
- ),
251
- siteDir,
252
- isMDXPartial: createAbsoluteFilePathMatcher(
253
- options.exclude,
254
- contentDirs,
255
- ),
256
- metadataPath: (mdxPath: string) => {
257
- // Note that metadataPath must be the same/in-sync as
258
- // the path from createData for each MDX.
259
- const aliasedPath = aliasedSitePath(mdxPath, siteDir);
260
- return path.join(
261
- dataDir,
262
- `${docuHash(aliasedPath)}.json`,
263
- );
264
- },
265
- // For blog posts a title in markdown is always removed
266
- // Blog posts title are rendered separately
267
- removeContentTitle: true,
268
-
269
- // Assets allow to convert some relative images paths to
270
- // require() calls
271
- createAssets: ({
272
- frontMatter,
273
- metadata,
274
- }: {
275
- frontMatter: BlogPostFrontMatter;
276
- metadata: BlogPostMetadata;
277
- }): Assets => ({
278
- image: frontMatter.image,
279
- authorsImageUrls: metadata.authors.map(
280
- (author) => author.imageURL,
281
- ),
282
- }),
283
- markdownConfig: siteConfig.markdown,
284
- },
285
- },
286
- {
287
- loader: path.resolve(__dirname, './markdownLoader.js'),
288
- options: markdownLoaderOptions,
289
- },
290
- ].filter(Boolean),
345
+ use: [createMDXLoader(), createBlogMarkdownLoader()],
291
346
  },
292
347
  ],
293
348
  },
@@ -295,15 +350,16 @@ export default async function pluginContentBlog(
295
350
  },
296
351
 
297
352
  async postBuild({outDir, content}) {
298
- if (!options.feedOptions.type) {
299
- return;
300
- }
301
- const {blogPosts} = content;
302
- if (!blogPosts.length) {
353
+ if (
354
+ !content.blogPosts.length ||
355
+ !options.feedOptions.type ||
356
+ isBlogFeedDisabledBecauseOfHashRouter
357
+ ) {
303
358
  return;
304
359
  }
360
+
305
361
  await createBlogFeedFiles({
306
- blogPosts,
362
+ blogPosts: content.blogPosts,
307
363
  options,
308
364
  outDir,
309
365
  siteConfig,
@@ -312,56 +368,15 @@ export default async function pluginContentBlog(
312
368
  },
313
369
 
314
370
  injectHtmlTags({content}) {
315
- if (!content.blogPosts.length || !options.feedOptions.type) {
371
+ if (
372
+ !content.blogPosts.length ||
373
+ !options.feedOptions.type ||
374
+ isBlogFeedDisabledBecauseOfHashRouter
375
+ ) {
316
376
  return {};
317
377
  }
318
378
 
319
- const feedTypes = options.feedOptions.type;
320
- const feedTitle = options.feedOptions.title ?? context.siteConfig.title;
321
- const feedsConfig = {
322
- rss: {
323
- type: 'application/rss+xml',
324
- path: 'rss.xml',
325
- title: `${feedTitle} RSS Feed`,
326
- },
327
- atom: {
328
- type: 'application/atom+xml',
329
- path: 'atom.xml',
330
- title: `${feedTitle} Atom Feed`,
331
- },
332
- json: {
333
- type: 'application/json',
334
- path: 'feed.json',
335
- title: `${feedTitle} JSON Feed`,
336
- },
337
- };
338
- const headTags: HtmlTags = [];
339
-
340
- feedTypes.forEach((feedType) => {
341
- const {
342
- type,
343
- path: feedConfigPath,
344
- title: feedConfigTitle,
345
- } = feedsConfig[feedType];
346
-
347
- headTags.push({
348
- tagName: 'link',
349
- attributes: {
350
- rel: 'alternate',
351
- type,
352
- href: normalizeUrl([
353
- baseUrl,
354
- options.routeBasePath,
355
- feedConfigPath,
356
- ]),
357
- title: feedConfigTitle,
358
- },
359
- });
360
- });
361
-
362
- return {
363
- headTags,
364
- };
379
+ return {headTags: createFeedHtmlHeadTags({context, options})};
365
380
  },
366
381
  };
367
382
  }
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {truncate, linkify} from './blogUtils';
8
+ import {truncate} from './blogUtils';
9
9
  import type {BlogMarkdownLoaderOptions} from './types';
10
10
  import type {LoaderContext} from 'webpack';
11
11
 
@@ -13,23 +13,19 @@ export default function markdownLoader(
13
13
  this: LoaderContext<BlogMarkdownLoaderOptions>,
14
14
  source: string,
15
15
  ): void {
16
- const filePath = this.resourcePath;
17
16
  const fileString = source;
18
17
  const callback = this.async();
19
18
  const markdownLoaderOptions = this.getOptions();
20
19
 
21
20
  // Linkify blog posts
22
- let finalContent = linkify({
23
- fileString,
24
- filePath,
25
- ...markdownLoaderOptions,
26
- });
21
+ let finalContent = fileString;
27
22
 
28
23
  // Truncate content if requested (e.g: file.md?truncated=true).
29
24
  const truncated: boolean | undefined = this.resourceQuery
30
25
  ? !!new URLSearchParams(this.resourceQuery.slice(1)).get('truncated')
31
26
  : undefined;
32
27
 
28
+ // TODO truncate with the AST instead of the string ?
33
29
  if (truncated) {
34
30
  finalContent = truncate(finalContent, markdownLoaderOptions.truncateMarker);
35
31
  }
package/src/options.ts CHANGED
@@ -54,6 +54,8 @@ export const DEFAULT_OPTIONS: PluginOptions = {
54
54
  showLastUpdateTime: false,
55
55
  showLastUpdateAuthor: false,
56
56
  processBlogPosts: async () => undefined,
57
+ onInlineTags: 'warn',
58
+ tags: undefined,
57
59
  };
58
60
 
59
61
  const PluginOptionSchema = Joi.object<PluginOptions>({
@@ -144,6 +146,13 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
144
146
  processBlogPosts: Joi.function()
145
147
  .optional()
146
148
  .default(() => DEFAULT_OPTIONS.processBlogPosts),
149
+ onInlineTags: Joi.string()
150
+ .equal('ignore', 'log', 'warn', 'throw')
151
+ .default(DEFAULT_OPTIONS.onInlineTags),
152
+ tags: Joi.string()
153
+ .disallow('')
154
+ .allow(null, false)
155
+ .default(() => DEFAULT_OPTIONS.tags),
147
156
  }).default(DEFAULT_OPTIONS);
148
157
 
149
158
  export function validateOptions({