feat(markdown): accept booleans for plugin options

Plugin options that could be disabled with `false` (`attrs`, `emoji`,
`tasklist`, `anchor`, `toc`, `image`, `component`) now also accept
`true`, which - like leaving them unset - enables the plugin with its
default options. Only object values are forwarded to the plugins.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull/5335/head
Divyansh Singh 2 months ago
parent 97f87817ea
commit c2be0bf936

@ -121,6 +121,26 @@ describe('node/markdown/markdown', () => {
'<strong>' '<strong>'
) )
}) })
test('`true` enables a plugin with its default options', async () => {
const html = await render(
'## Title {#custom-id}\n\n[[toc]]\n\n:tada:\n\n- [ ] todo',
{
anchor: true,
attrs: true,
emoji: true,
tasklist: true,
toc: true,
image: true,
component: true
}
)
expect(html).toContain('id="custom-id"')
expect(html).toContain('header-anchor')
expect(html).toContain('table-of-contents')
expect(html).toContain('🎉')
expect(html).toContain('<input type="checkbox"')
})
}) })
describe('attrs', () => { describe('attrs', () => {

@ -52,6 +52,14 @@ export type { Header } from '../shared'
// not exported from @mdit/plugin-emoji, so derive it from the plugin signature // not exported from @mdit/plugin-emoji, so derive it from the plugin signature
type EmojiPluginOptions = NonNullable<Parameters<typeof emojiPlugin>[1]> type EmojiPluginOptions = NonNullable<Parameters<typeof emojiPlugin>[1]>
// `true` and `undefined` enable a plugin with its default options - only an
// object carries user-provided plugin options
function normalizePluginOptions<T>(
value: T | boolean | undefined
): T | undefined {
return typeof value === 'boolean' ? undefined : value
}
export type ThemeOptions = export type ThemeOptions =
| ThemeRegistrationAny | ThemeRegistrationAny
| BuiltinTheme | BuiltinTheme
@ -191,18 +199,18 @@ export interface MarkdownOptions extends MarkdownItAsyncOptions {
* transformers instead. * transformers instead.
* @see https://mdit-plugins.github.io/attrs.html * @see https://mdit-plugins.github.io/attrs.html
*/ */
attrs?: MarkdownItAttrsOptions | false attrs?: MarkdownItAttrsOptions | boolean
/** /**
* Options for `@mdit/plugin-emoji`. Set to `false` to disable. * Options for `@mdit/plugin-emoji`. Set to `false` to disable.
* @see https://mdit-plugins.github.io/emoji.html * @see https://mdit-plugins.github.io/emoji.html
*/ */
emoji?: EmojiPluginOptions | false emoji?: EmojiPluginOptions | boolean
/** /**
* Options for `@mdit/plugin-tasklist` (GitHub-style task lists, * Options for `@mdit/plugin-tasklist` (GitHub-style task lists,
* `- [ ] task`). Set to `false` to disable. * `- [ ] task`). Set to `false` to disable.
* @see https://mdit-plugins.github.io/tasklist.html * @see https://mdit-plugins.github.io/tasklist.html
*/ */
tasklist?: MarkdownItTaskListOptions | false tasklist?: MarkdownItTaskListOptions | boolean
/** /**
* Improves emphasis (`**bold**`) handling in Japanese, Chinese, and * Improves emphasis (`**bold**`) handling in Japanese, Chinese, and
* Korean text. * Korean text.
@ -216,7 +224,7 @@ export interface MarkdownOptions extends MarkdownItAsyncOptions {
* heading hash links rely on these ids. * heading hash links rely on these ids.
* @see https://mdit-plugins.github.io/anchor.html * @see https://mdit-plugins.github.io/anchor.html
*/ */
anchor?: AnchorOptions | false anchor?: AnchorOptions | boolean
/** /**
* Options for `@mdit-vue/plugin-headers`. Set to `true` or pass options * Options for `@mdit-vue/plugin-headers`. Set to `true` or pass options
* to collect page headers into page data. * to collect page headers into page data.
@ -229,7 +237,7 @@ export interface MarkdownOptions extends MarkdownItAsyncOptions {
* `[[toc]]` syntax. * `[[toc]]` syntax.
* @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-toc * @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-toc
*/ */
toc?: TocPluginOptions | false toc?: TocPluginOptions | boolean
/** /**
* Math support. * Math support.
* *
@ -263,7 +271,7 @@ export interface MarkdownOptions extends MarkdownItAsyncOptions {
* to disable. * to disable.
* @see https://vitepress.dev/guide/markdown#image-lazy-loading * @see https://vitepress.dev/guide/markdown#image-lazy-loading
*/ */
image?: ImageOptions | false image?: ImageOptions | boolean
/* ==================== Vue Integration ==================== */ /* ==================== Vue Integration ==================== */
@ -271,7 +279,7 @@ export interface MarkdownOptions extends MarkdownItAsyncOptions {
* Options for `@mdit-vue/plugin-component`. Set to `false` to disable. * Options for `@mdit-vue/plugin-component`. Set to `false` to disable.
* @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-component * @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-component
*/ */
component?: ComponentPluginOptions | false component?: ComponentPluginOptions | boolean
/** /**
* Options for `@mdit-vue/plugin-frontmatter`. * Options for `@mdit-vue/plugin-frontmatter`.
* @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-frontmatter * @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-frontmatter
@ -332,7 +340,7 @@ export async function createMarkdownRenderer(
} }
const slugify = const slugify =
(options.anchor ? options.anchor.slugify : undefined) ?? defaultSlugify normalizePluginOptions(options.anchor)?.slugify ?? defaultSlugify
// VitePress customizations // VitePress customizations
if (options.preWrapper !== false) { if (options.preWrapper !== false) {
@ -351,7 +359,7 @@ export async function createMarkdownRenderer(
gitHubAlertsPlugin(md, options.container) gitHubAlertsPlugin(md, options.container)
} }
if (options.image !== false) { if (options.image !== false) {
imagePlugin(md, publicDir, options.image) imagePlugin(md, publicDir, normalizePluginOptions(options.image))
} }
linkPlugin( linkPlugin(
md, md,
@ -369,14 +377,14 @@ export async function createMarkdownRenderer(
// no `fence` - code block meta (e.g. line highlighting) must reach // no `fence` - code block meta (e.g. line highlighting) must reach
// the highlighter intact // the highlighter intact
rule: ['inline', 'table', 'list', 'heading', 'hr', 'softbreak', 'block'], rule: ['inline', 'table', 'list', 'heading', 'hr', 'softbreak', 'block'],
...options.attrs ...normalizePluginOptions(options.attrs)
}) })
} }
if (options.emoji !== false) { if (options.emoji !== false) {
emojiPlugin(md, options.emoji) emojiPlugin(md, normalizePluginOptions(options.emoji))
} }
if (options.tasklist !== false) { if (options.tasklist !== false) {
tasklistPlugin(md, options.tasklist) tasklistPlugin(md, normalizePluginOptions(options.tasklist))
} }
if (options.cjkFriendlyEmphasis !== false) { if (options.cjkFriendlyEmphasis !== false) {
mditCjkFriendly(md) mditCjkFriendly(md)
@ -415,14 +423,14 @@ export async function createMarkdownRenderer(
state.tokens[idx + 1].children?.push(...linkTokens) state.tokens[idx + 1].children?.push(...linkTokens)
}, },
...options.anchor ...normalizePluginOptions(options.anchor)
}) })
} }
if (options.math) { if (options.math) {
try { try {
const mathPlugin = await import('markdown-it-mathjax3') const mathPlugin = await import('markdown-it-mathjax3')
;(mathPlugin.default ?? mathPlugin)(md, { ;(mathPlugin.default ?? mathPlugin)(md, {
...(typeof options.math === 'boolean' ? {} : options.math) ...normalizePluginOptions(options.math)
}) })
const origMathInline = md.renderer.rules.math_inline! const origMathInline = md.renderer.rules.math_inline!
md.renderer.rules.math_inline = function (...args) { md.renderer.rules.math_inline = function (...args) {
@ -445,20 +453,20 @@ export async function createMarkdownRenderer(
// mdit-vue plugins // mdit-vue plugins
if (options.component !== false) { if (options.component !== false) {
componentPlugin(md, options.component) componentPlugin(md, normalizePluginOptions(options.component))
} }
frontmatterPlugin(md, options.frontmatter) frontmatterPlugin(md, options.frontmatter)
if (options.headers) { if (options.headers) {
headersPlugin(md, { headersPlugin(md, {
level: [2, 3, 4, 5, 6], level: [2, 3, 4, 5, 6],
slugify, slugify,
...(typeof options.headers === 'boolean' ? undefined : options.headers) ...normalizePluginOptions(options.headers)
}) })
} }
sfcPlugin(md, options.sfc) sfcPlugin(md, options.sfc)
titlePlugin(md) titlePlugin(md)
const tocOptions = options.toc const tocOptions = normalizePluginOptions(options.toc)
if (tocOptions !== false) { if (options.toc !== false) {
tocPlugin(md, { tocPlugin(md, {
slugify, slugify,
...tocOptions, ...tocOptions,

Loading…
Cancel
Save