👨‍💻

#01 marked.js + highlight.js でMarkdownをきれいにHTMLに変換

2024/07/25に公開

概要

Markdownで書いたページをHTMLに変換する際に、
marked.js と highlight.js のライブラリの組み合わせが使いやすかったのでご紹介です。

  • marked.js (Markdown→HTML変換用)
  • highlight.js (コードを書いてる部分をきれいにする用)

準備

marked.js

Documentation: https://marked.js.org/
Github: https://github.com/markedjs/marked

NPMで適用することもできますが、今回はgithubから直接ダウンロードしました。

  1. marked.min.jsをダウンロード
  2. /assets/js ディレクトリに配置
  3. HTMLファイルに以下を追記
<script src="/assets/js/marked.min.js"></script>

highlight.js

配布ページ: https://highlightjs.org/
GitHub: https://github.com/highlightjs/highlight.js

こちらは、NPMで適用するか、ファイルをダウンロードするか、CDNで適用するかですが、
今回は表示したい言語が決まっていなかったのでCDNで適用しました。

どの言語のコードを表示したいかあらかじめ決まっていた場合は、
その言語用のファイルだけダウンロードしたほうが軽量化になると思います。

1.HTMLファイルに以下を追記

<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>

2.デモページで、表示テーマを選択
3.HTMLファイルに以下を追記

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/{テーマ名}.min.css">

今回は、Monokai Sublime を選択したのでテーマ名部分はmonokai-sublimeとなります。

実装

ライブラリを適用したので、コードに組み込んでいきます。

var markdown_text = '~~~';
// Markdown→HTMLに変換
var html = marked.parse(markdown);
// HTML要素にセット
document.getElementById('markdown').innerHTML = html;
// コードハイライト
hljs.highlightAll();

あとは、出力されたHTMLを見て、好みに合わせてCSSスタイルを調整すれば完成です。

カスタマイズ

ここから先は必須ではありません。

コード部分にファイル名を表示する

コードを記載しているところに、ファイル名も一緒に表示したかったのですが、
marked.jsのデフォルトの設定だと表示してくれないようです。

↓これが問題のMarkdownです。

1. 言語名だけ
```javascript
console.log('Hello World');
```

2. 言語名+ファイル名
```javascript:hogehoge.js
console.log('Hello World');
```

3. ファイル名だけ
```hogehoge.js
console.log('Hello World');
```

↓上のMarkdownをHTMLに変換するとこうなります。
image.png

1はファイル名を指定していないので期待通りの動作です。
2はファイル名が表示されない。。。
3はそもそもハイライトされない。。。

そこで、以下のように修正します。

<script>
var highlight = function(code, lang, callback){
    if (lang) {
        return hljs.highlight(code, {language: lang, ignoreIllegals: true}).value;
    } else {
        return hljs.highlightAuto(code).value;
    }
}

var renderer = new marked.Renderer();
renderer.code = function(code, fileInfo, escaped) {
  if (!fileInfo) {
    fileInfo = '';
  }
  var info = fileInfo.split(':');
  langs = hljs.listLanguages();
  // hljsの対応言語に含まれていなければファイル名とする
  if (!langs.includes(info[0])) {
    var fileName = info[0];
  } else {
    var lang = info[0];
    var fileName = info[1];
  }
  var fileTag = '';
  if (fileName) {
    fileTag = '<span class="filename">'+fileName+'</span>'
  }

  if (this.options.highlight) {
    var out = this.options.highlight(code, lang);
    if (out != null && out !== code) {
      escaped = true;
      code = out;
    }
  }

  if (!lang) {
    return '<pre>'+fileTag+'<code>'
      + (escaped === false ? escape(code, true) : code)
      + '\n</code></pre>';
  }

  return '<pre>'+fileTag+'<code class="'
    + this.options.langPrefix
    + escape(lang, true)
    + '">'
    + (escaped === false ? escape(code, true) : code)
    + '\n</code></pre>\n';
};

marked.setOptions({
    renderer: renderer,
    highlight: highlight
});
</script>

コード部分のHTML→Markdown処理を上書きし、
ファイル名が<span class="filename">ファイル名</span>で表示されるようにしました。

↓改めて、HTMLに変換してみます。
image.png

無事にファイル名が表示されるようになりました。
あとは、表示が不格好なので、CSSで整えれば完了です。

<style>
pre span.filename {
    color: #000;
    background-color: #eeede7;
    padding: 2px 8px;
    display: block;
    overflow: hidden;
}
</style>

image.png

いい感じの表示になりました。


コード以外も、rendererに設定すればHTML変換処理をカスタマイズ可能です。
詳細はmarked.jsのDocumentationを参照してください。


以上です。ご清覧ありがとうございます。

参考

Discussion