diff --git a/src/module.ts b/src/module.ts index 72b45eafa..eaf8cc55b 100644 --- a/src/module.ts +++ b/src/module.ts @@ -65,7 +65,17 @@ export default defineNuxtModule({ }, }, build: { - markdown: {}, + markdown: { + remarkPlugins: { + 'remark-mdc': { + options: { + experimental: { + autoUnwrap: true, + }, + }, + }, + }, + }, yaml: {}, csv: { delimeter: ',', diff --git a/src/types/module.ts b/src/types/module.ts index 70a78bd70..7ae6c6487 100644 --- a/src/types/module.ts +++ b/src/types/module.ts @@ -85,14 +85,14 @@ export interface ModuleOptions { * * @default [] */ - remarkPlugins?: Array | Record + remarkPlugins?: Record /** * Register custom remark plugin to provide new feature into your markdown contents. * Checkout: https://github.com/rehypejs/rehype/blob/main/doc/plugins.md * * @default [] */ - rehypePlugins?: Array | Record + rehypePlugins?: Record /** * Content module uses `shiki` to highlight code blocks. diff --git a/src/utils/content/index.ts b/src/utils/content/index.ts index 1b1338c66..06a7d4fdb 100644 --- a/src/utils/content/index.ts +++ b/src/utils/content/index.ts @@ -22,8 +22,11 @@ type HighlightedNode = { type: 'element', properties?: Record - highlighter: Highlighter + options?: { + theme?: Record + highlighter: Highlighter + } + } let highlightPluginPromise: Promise async function getHighlightPluginInstance(options: HighlighterOptions) { @@ -61,32 +64,34 @@ async function _getHighlightPlugin(options: HighlighterOptions) { key, instance: rehypeHighlight, ...options, - highlighter: async (code, lang, theme, opts) => { - const result = await highlighter(code, lang, theme, opts) - const visitTree = { - type: 'element', - children: result.tree as Array, - } - if (options.compress) { - const stylesMap: Record = {} - visit( - visitTree, - node => !!(node as HighlightedNode).properties?.style, - (_node) => { - const node = _node as HighlightedNode - const style = node.properties!.style! - stylesMap[style] = stylesMap[style] || 's' + hash(style).substring(0, 4) - node.properties!.class = `${node.properties!.class || ''} ${stylesMap[style]}`.trim() - node.properties!.style = undefined - }, - ) + options: { + highlighter: async (code, lang, theme, opts) => { + const result = await highlighter(code, lang, theme, opts) + const visitTree = { + type: 'element', + children: result.tree as Array, + } + if (options.compress) { + const stylesMap: Record = {} + visit( + visitTree, + node => !!(node as HighlightedNode).properties?.style, + (_node) => { + const node = _node as HighlightedNode + const style = node.properties!.style! + stylesMap[style] = stylesMap[style] || 's' + hash(style).substring(0, 4) + node.properties!.class = `${node.properties!.class || ''} ${stylesMap[style]}`.trim() + node.properties!.style = undefined + }, + ) - result.style = Object.entries(stylesMap).map(([style, cls]) => `.${cls}{${style}}`).join('') + result.style - } + result.style = Object.entries(stylesMap).map(([style, cls]) => `.${cls}{${style}}`).join('') + result.style + } - return result + return result + }, + theme: Object.fromEntries(bundledThemes), }, - theme: Object.fromEntries(bundledThemes), } } return highlightPlugin diff --git a/src/utils/content/transformers/markdown.ts b/src/utils/content/transformers/markdown.ts index 262980273..432c4f312 100644 --- a/src/utils/content/transformers/markdown.ts +++ b/src/utils/content/transformers/markdown.ts @@ -68,7 +68,7 @@ async function importPlugins(plugins: Record = { if (plugin) { resolvedPlugins[name] = { instance: plugin.instance || await import(/* @vite-ignore */ name).then(m => m.default || m), - options: plugin, + options: plugin.options || {}, } } else { diff --git a/src/utils/mdc.ts b/src/utils/mdc.ts index 7ac913c8c..19a9b88dc 100644 --- a/src/utils/mdc.ts +++ b/src/utils/mdc.ts @@ -7,9 +7,9 @@ import type { ModuleOptions } from '../types' import { setParserOptions } from './content' export async function installMDCModule(contentOptions: ModuleOptions, nuxt: Nuxt) { - const options = nuxt.options as unknown as { mdc: MDCModuleOptions, content: ModuleOptions } + const options = nuxt.options as unknown as { mdc: MDCModuleOptions } // Install mdc module - const highlight = options.content?.build?.markdown?.highlight as unknown as MDCModuleOptions['highlight'] + const highlight = contentOptions.build?.markdown?.highlight as unknown as MDCModuleOptions['highlight'] options.mdc = defu({ highlight: highlight !== false @@ -22,6 +22,8 @@ export async function installMDCModule(contentOptions: ModuleOptions, nuxt: Nuxt headings: { anchorLinks: contentOptions.renderer.anchorLinks, }, + remarkPlugins: contentOptions.build?.markdown?.remarkPlugins, + rehypePlugins: contentOptions.build?.markdown?.rehypePlugins, }, options.mdc) as MDCModuleOptions // Hook into mdc configs and store them for parser diff --git a/test/unit/parseContent.md-highlighter.test.ts b/test/unit/parseContent.md-highlighter.test.ts index dc48c28a7..55d88cbf7 100644 --- a/test/unit/parseContent.md-highlighter.test.ts +++ b/test/unit/parseContent.md-highlighter.test.ts @@ -10,6 +10,21 @@ import type { MarkdownRoot } from '../../src/types/content' const nuxtMock = { options: { + content: { + build: { + markdown: { + remarkPlugins: { + 'remark-mdc': { + options: { + experimental: { + autoUnwrap: true, + }, + }, + }, + }, + }, + }, + }, mdc: { compress: false, highlight: { diff --git a/test/unit/parseContent.md.test.ts b/test/unit/parseContent.md.test.ts index fc28c78ba..d41f27805 100644 --- a/test/unit/parseContent.md.test.ts +++ b/test/unit/parseContent.md.test.ts @@ -8,6 +8,21 @@ import { resolveCollection } from '../../src/utils/collection' const nuxtMock = { options: { + content: { + build: { + markdown: { + remarkPlugins: { + 'remark-mdc': { + options: { + experimental: { + autoUnwrap: true, + }, + }, + }, + }, + }, + }, + }, mdc: { compress: false, markdown: { @@ -251,4 +266,19 @@ describe('Parser (.md)', () => { expect(parsed.body.children[0].props.id).toEqual('alert') } }) + + test('Unwrap component only child', async () => { + const tests = [ + { markdown: `::component\nHello\n::`, firstChild: { type: 'text', value: 'Hello' } }, + { markdown: `::component\nHello :world\n::`, firstChild: { type: 'text', value: 'Hello ' } }, + { markdown: `::component\n - item 1\n - item 2\n::`, firstChild: { type: 'element', tag: 'li' } }, + { markdown: `:::component\n::nested-component\nHello\n::\n:::`, firstChild: { type: 'element', tag: 'nested-component' } }, + + ] + for (const { markdown, firstChild } of tests) { + const parsed = await parseContent('content/index.md', markdown, collection, nuxtMock) + + expect(parsed.body.children[0].children[0]).toMatchObject(firstChild) + } + }) })