markdown-itの導入

記事本文をmarkdown-itで拡張したかったんですが@nuxtjs/markdownitがまだNuxt3に対応していなかったため、こちらを参考にプラグインを作ってみました。

インストール

まずはこちらをインストール。

yarn add markdown-it @types/markdown-it

プラグインの作成

作成したプラグインは以下のとおりです。

import MarkdownIt from 'markdown-it'

import attrs from 'markdown-it-attrs'
import fn from 'markdown-it-footnote'
import sub from 'markdown-it-sub'
import sup from 'markdown-it-sup'
import mark from 'markdown-it-mark'
import mila from 'markdown-it-link-attributes'

import hljs from '~/utils/highlightjs'


const mdit: MarkdownIt = new MarkdownIt({
  html:        true,
  breaks:      true,
  langPrefix:  'language-',
  typographer: false,
  quotes:      '“”‘’',
  highlight:   function (str, lang, _) {
    if (lang && hljs.getLanguage(lang)) {
      return hljs.highlight(str, { language: lang, ignoreIllegals: true }).value
    }
    return mdit.utils.escapeHtml(str)
  },
})
  .use(attrs, {
    leftDelimiter: '{',
    rightDelimiter: '}',
    allowedAttributes: [],
  })
  .use(fn)
  .use(sub)
  .use(sup)
  .use(mark)
  .use(mila, {
    matcher(href: string) {
      return href.match(/^https?:\/\//)
    },
    attrs: {
      target: '_blank',
      rel: 'noopener noreferrer',
    },
  })

mdit.renderer.rules.fence = function (tokens, idx, options, _, slf) {
  const token = tokens[idx]
  const info = token.info ? mdit.utils.unescapeAll(token.info).trim() : ''
  let langName = ''
  let langAttrs = ''
  let highlighted, arr

  if (info) {
    arr = info.split(/(\s+)/g)
    langName = arr[0]
    langAttrs = arr.slice(2).join('')
  }

  if (options.highlight) {
    highlighted = options.highlight(token.content, langName, langAttrs) || mdit.utils.escapeHtml(token.content)
  } else {
    highlighted = mdit.utils.escapeHtml(token.content)
  }

  if (highlighted.indexOf('<pre') === 0) {
    return highlighted + '\n'
  }

  return  '<pre' + slf.renderAttrs(token) + '><code class="hljs">' + highlighted + '</code></pre>\n'
}


export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.provide('md', mdit)
})

利用したいmarkdown-itのプラグインをimportし、設定が必要なものは適宜設定していきます。ちなみにhighlight.jsのプラグインもあるんですが hljs さえimportできればオッケーなんで、今回は以前作ったhighlight.jsプラグインを手直しして言語ファイルの登録はそちらで管理することにしました。

markdown-itのプラグインの型定義

プラグインの中には型定義がないものもあります。その際は、

declare module 'markdown-it-sub' {
  import MarkdownIt = require('markdown-it');
  declare function sub(md: MarkdownIt): void;
  export = sub;
}

みたいな型定義ファイルを作ってあげます。これで問題なく読み込めます。

コードブロックのレンダリングの修正

デフォルトではpreタグにclassがつくのをcodeタグにつくようにしたかったのと、あとはPrism.jsみたいにpreタグにdata-labelを持たせてファイル名表記を行いたかったんで renderer.rules.fence を上書き。コードは基本まるっとコピペしました。

Newtの仕様では、コード内の特殊文字については記事入力段階でエスケープさせる必要がありましたが、ハイライトを組み込むことでその必要もなくなり喜ばしい限りです。

まとめ

とまあ、サラッと書いてますが、実際はあちこちでハマりながらもどうにか導入完了した感じです。しかしながらこれで記事が非常に書きやすくなりました。