Nuxt Nationカンファレンス開催!11月12日〜13日ご参加ください。

mdc
@nuxtjs/mdc

MDCは通常のMarkdownを強化し、任意のVueコンポーネントと深く連携するドキュメント作成を可能にします。

Nuxt MDC

Nuxt MDC

npm versionnpm downloadsLicenseNuxt

MDCは通常のMarkdownを強化し、任意のVueコンポーネントと深く連携するドキュメント作成を可能にします。MDCはMarkDown Componentsの略です。

機能

  • Markdown構文とHTMLタグまたはVueコンポーネントを組み合わせる
  • 生成されたコンテンツ(例:各Markdown段落によって追加された<p>タグ)を削除する
  • 名前付きスロットを使用してVueコンポーネントを使用する
  • インラインコンポーネントをサポートする
  • インラインHTMLタグに属性とクラスを追加する

MDC構文の詳細については、https://content.nuxtjs.org/guide/writing/mdcをご覧ください。

!注記 このパッケージは、Nuxtプロジェクト(標準設定)内または任意のVueプロジェクト内で使用できます。

詳細については、下記のVueプロジェクトでのレンダリングを参照してください。

インストール

npx nuxi@latest module add mdc

次に、@nuxtjs/mdcnuxt.config.tsのmodulesセクションに追加します。

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/mdc']
})

これで完了です!NuxtプロジェクトでMarkdownファイルの記述とレンダリングを開始できます ✨

レンダリング

@nuxtjs/mdcは、Markdownファイルをレンダリングするための3つのコンポーネントを提供します。

<MDC>

<MDC>を使用すると、コンポーネント/ページ内でMarkdownコンテンツを直接解析してレンダリングできます。このコンポーネントは生のMarkdownを受け取り、parseMarkdown関数を使用して解析し、<MDCRenderer>を使用してレンダリングします。

<script setup lang="ts">
const md = `
::alert
Hello MDC
::
`
</script>

<template>
  <MDC :value="md" tag="article" />
</template>

::alertcomponents/global/Alert.vueコンポーネントを使用することに注意してください。

<MDCRenderer>

このコンポーネントは、parseMarkdown関数の結果を受け取り、コンテンツをレンダリングします。たとえば、これはブラウザセクションのサンプルコードの拡張版で、MDCRendererを使用して解析されたMarkdownをレンダリングします。

mdc-test.vue
<script setup lang="ts">
import { parseMarkdown } from '@nuxtjs/mdc/runtime'

const { data: ast } = await useAsyncData('markdown', () => parseMarkdown('::alert\nMissing markdown input\n::'))
</script>

<template>
  <MDCRenderer :body="ast.body" :data="ast.data" />
</template>

<MDCSlot>

このコンポーネントは、Vueの<slot/>コンポーネントの代替であり、特にMDC用に設計されています。このコンポーネントを使用すると、1つ以上のラッピング要素を削除しながら、コンポーネントの子をレンダリングできます。以下の例では、Alertコンポーネントはテキストとそのデフォルトスロット(子)を受け取ります。しかし、コンポーネントが通常の<slot/>を使用してこのスロットをレンダリングする場合、テキストの周りに<p>要素がレンダリングされます。

markdown.md
::alert
This is an Alert
::
Alert.vue
<template>
  <div class="alert">
    <!-- Slot will render <p> tag around the text -->
    <slot />
  </div>
</template>

すべてのテキストを段落内にラップすることは、Markdownのデフォルトの動作です。MDCはMarkdownの動作を壊すためのものではなく、Markdownをより強力にすることを目的としています。この例や同様の状況では、<MDCSlot />を使用して不要なラッパーを削除できます。

Alert.vue
<template>
  <div class="alert">
    <!-- MDCSlot will only render the actual text without the wrapping <p> -->
    <MDCSlot unwrap="p" />
  </div>
</template>

散文コンポーネント

散文コンポーネントは、通常のHTMLタグの代わりにレンダリングされるコンポーネントのリストです。たとえば、<p>タグをレンダリングする代わりに、@nuxtjs/mdc<ProseP>コンポーネントをレンダリングします。これは、Markdownファイルに追加機能を追加する場合に役立ちます。たとえば、コードブロックに「コピー」ボタンを追加できます。

nuxt.config.tsproseオプションをfalseに設定して、散文コンポーネントを無効にすることができます。または、散文コンポーネントのマップを拡張して独自のコンポーネントを追加することもできます。

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/mdc'],
  mdc: {
    components: {
      prose: false, // Disable predefined prose components
      map: {
        p: 'MyCustomPComponent'
      }
    }
  }
})

利用可能な散文コンポーネントのリストを以下に示します。

タグコンポーネントソース説明
p<ProseP>ProseP.vue段落
h1<ProseH1>ProseH1.vue見出し1
h2<ProseH2>ProseH2.vue見出し2
h3<ProseH3>ProseH3.vue見出し3
h4<ProseH4>ProseH4.vue見出し4
h5<ProseH5>ProseH5.vue見出し5
h6<ProseH6>ProseH6.vue見出し6
ul<ProseUl>ProseUl.vue箇条書きリスト
ol<ProseOl>ProseOl.vue番号付きリスト
li<ProseLi>ProseLi.vueリスト項目
blockquote<ProseBlockquote>ProseBlockquote.vue引用ブロック
hr<ProseHr>ProseHr.vue水平線
pre<ProsePre>ProsePre.vue整形済みテキスト
code<ProseCode>ProseCode.vueコードブロック
table<ProseTable>ProseTable.vueテーブル
thead<ProseThead>ProseThead.vueテーブルヘッダー
tbody<ProseTbody>ProseTbody.vueテーブルボディ
tr<ProseTr>ProseTr.vueテーブル行
th<ProseTh>ProseTh.vueテーブルヘッダーセル
td<ProseTd>ProseTd.vueテーブルデータセル
a<ProseA>ProseA.vueアンカーリンク
img<ProseImg>ProseImg.vue画像
em<ProseEm>ProseEm.vue強調
strong<ProseStrong>ProseStrong.vue強調(太字)

Markdownの解析

Nuxt MDCは、MDCファイルを解析するための便利なヘルパーを提供します。@nuxtjs/mdc/runtimeからparseMarkdown関数をインポートして、MDC構文で記述されたMarkdownファイルを解析するために使用できます。

Node.js

// server/api/parse-mdc.ts
import { parseMarkdown } from '@nuxtjs/mdc/runtime'

export default eventHandler(async () => {
  const mdc = [
    '# Hello MDC',
    '',
    '::alert',
    'This is an Alert',
    '::'
  ].join('\n')

  const ast = await parseMarkdown(mdc)

  return ast
})

ブラウザ

parseMarkdown関数は汎用的なヘルパーであり、ブラウザ内(たとえば、Vueコンポーネント内)でも使用できます。

<script setup lang="ts">
import { parseMarkdown } from '@nuxtjs/mdc/runtime'

const { data: ast } = await useAsyncData('markdown', () => parseMarkdown('::alert\nMissing markdown input\n::'))
</script>

<template>
  <MDCRenderer :body="ast.body" :data="ast.data" />
</template>

オプション

parseMarkdownヘルパーは、2番目の引数としてオプションを受け取り、パーサーの動作を制御します。(MDCParseOptionsインターフェース↗︎を確認してください)。

名前デフォルト説明
remark.plugins{}パーサーのremarkプラグインを登録/設定します。
rehype.options{}remark-rehypeオプションを設定します。
rehype.plugins{}パーサーのrehypeプラグインを登録/設定します。
highlightfalseコードブロックをハイライト表示するかどうかを制御します。カスタムハイライターを提供することもできます。
toc.depth2目次を含める見出しの最大深度。
toc.searchDepth2見出しを検索する入れ子になったタグの最大深度。

MDCParseOptionsタイプ↗︎を確認してください。

設定

nuxt.config.jsmdcプロパティを提供することで、モジュールを設定できます。デフォルトオプションを以下に示します。

import { defineNuxtConfig } from 'nuxt/config'

export default defineNuxtConfig({
  modules: ['@nuxtjs/mdc'],
  mdc: {
    remarkPlugins: {
      plugins: {
        // Register/Configure remark plugin to extend the parser
      }
    },
    rehypePlugins: {
      options: {
        // Configure rehype options to extend the parser
      },
      plugins: {
        // Register/Configure rehype plugin to extend the parser
      }
    },
    headings: {
      anchorLinks: {
        // Enable/Disable heading anchor links. { h1: true, h2: false }
      }
    },
    highlight: false, // Control syntax highlighting
    components: {
      prose: false, // Add predefined map to render Prose Components instead of HTML tags, like p, ul, code
      map: {
        // This map will be used in `<MDCRenderer>` to control rendered components
      }
    }
  }
})

ModuleOptionsタイプ↗︎を確認してください。

Vueプロジェクトでのレンダリング

いくつかのエクスポートされたパッケージユーティリティと組み合わせた<MDCRenderer>コンポーネントは、通常の(非Nuxt)Vueプロジェクトでも使用できます。

標準的なVueプロジェクトに実装するには、以下の手順に従ってください。

パッケージのインストール

nuxt.config.tsファイルにNuxtモジュールを追加する手順を無視して、上記のインストール手順に従ってください。

Nuxtモジュールのインポートのスタブ作成

Nuxtを使用していないため、VueプロジェクトのVite設定ファイルでモジュールのインポートをいくつかスタブ化する必要があります。これは、モジュールがNuxt固有のインポートにアクセスしようとしたときにエラーを回避するために必要です。

Vueプロジェクトのルートディレクトリに、stub-mdc-imports.jsなどの新しいファイルを作成し、次の内容を追加します。

// stub-mdc-imports.js
export default {}

次に、VueプロジェクトのVite設定ファイル(例:vite.config.ts)を更新して、モジュールのインポートをスタブファイルにエイリアスします。

import { defineConfig } from 'vite'
import path from 'path'

export default defineConfig({
  resolve: {
    alias: {
      '#mdc-imports': path.resolve(__dirname, './stub-mdc-imports.js'),
      '#mdc-configs': path.resolve(__dirname, './stub-mdc-imports.js'),
    }
  }
})

使用方法

次に、Markdownコンテンツの解析と、Shikiを使用してコードブロックへの構文ハイライトの追加を処理するための新しいVueコンポーザブルを作成しましょう。

// composables/useMarkdownParser.ts
// Import package exports
import {
  createMarkdownParser,
  rehypeHighlight,
  createShikiHighlighter,
} from '@nuxtjs/mdc/runtime'
// Import desired Shiki themes and languages
import MaterialThemePalenight from 'shiki/themes/material-theme-palenight.mjs'
import HtmlLang from 'shiki/langs/html.mjs'
import MdcLang from 'shiki/langs/mdc.mjs'
import TsLang from 'shiki/langs/typescript.mjs'
import VueLang from 'shiki/langs/vue.mjs'
import ScssLang from 'shiki/langs/scss.mjs'
import YamlLang from 'shiki/langs/yaml.mjs'

export default function useMarkdownParser() {
  let parser: Awaited<ReturnType<typeof createMarkdownParser>>

  const parse = async (markdown: string) => {
    if (!parser) {
      parser = await createMarkdownParser({
        rehype: {
          plugins: {
            highlight: {
              instance: rehypeHighlight,
              options: {
                // Pass in your desired theme(s)
                theme: 'material-theme-palenight',
                // Create the Shiki highlighter
                highlighter: createShikiHighlighter({
                  bundledThemes: {
                    'material-theme-palenight': MaterialThemePalenight,
                  },
                  // Configure the bundled languages
                  bundledLangs: {
                    html: HtmlLang,
                    mdc: MdcLang,
                    vue: VueLang,
                    yml: YamlLang,
                    scss: ScssLang,
                    ts: TsLang,
                    typescript: TsLang,
                  },
                }),
              },
            },
          },
        },
      })
    }
    return parser(markdown)
  }

  return parse
}

作成したuseMarkdownParserコンポーザブルとエクスポートされた型インターフェースをホストプロジェクトのVueコンポーネントにインポートし、それらを使用して生のMarkdownを処理し、<MDCRenderer>コンポーネントを初期化します。

<script setup lang="ts">
import { onBeforeMount, ref, watch } from 'vue'
// Import package exports
import MDCRenderer from '@nuxtjs/mdc/runtime/components/MDCRenderer.vue'
import type { MDCParserResult } from '@nuxtjs/mdc'
import useMarkdownParser from './composables/useMarkdownParser';

const md = ref(`
# Just a Vue app

This is markdown content rendered via the \`<MDCRenderer>\` component, including MDC below.

::alert
Hello MDC
::

\`\`\`ts
const a = 1;
\`\`\`
`);

const ast = ref<MDCParserResult | null>(null)
const parse = useMarkdownParser()

onBeforeMount(async () => {
  ast.value = await parse(md.value)
})
</script>

<template>
  <Suspense>
    <MDCRenderer v-if="ast?.body" :body="ast.body" :data="ast.data" />
  </Suspense>
</template>

貢献

StackBlitzを使用して、このモジュールをオンラインで確認できます。

Edit @nuxtjs/mdc

またはローカルで

  1. このリポジトリをクローンします。
  2. pnpm installを使用して依存関係をインストールします。
  3. pnpm devを使用して開発サーバーを起動します。

ライセンス

MITライセンス

Copyright (c) NuxtLabs