Home About Contact
Markdown , Go

goldmark を使って markdown を HTMLに変換する mymark コマンドをつくった

hugo という静的サイトジェネレータを使いはじめて、 急にこのブログサイトのオレオレ静的サイトジェネレータがみすぼらしく感じはじめた。 ならば、このブログサイトも hugo で構築すればいいじゃないか、と思ったのだが、 hugo 用の気に入ったテーマが見つからない。

hugo で使うコンテンツは markdown で記述するのだが、それをHTMLに変換するエンジンには goldmark という golang 製の markdown パーサーが使われているという。 ならば、変換部分だけは goldmark を使うことにして、残りのサイト構築部分は既存のものを流用しつつ 良い感じにすればいいんじゃないの?ということで、オレオレサイトジェネレータの刷新がはじまった。

その顛末は長いので割愛するが、肝心の markdown を HTML に変換する goldmark がすばらしく便利なので、 そこだけ紹介します。

mymark コマンドをつくる

mymark ディレクトリを作成して初期化。

mkdir mymark
cd mymark
go mod init mymark

main.go に以下を記述。

package main

import (
	"bytes"
	"fmt"
	"github.com/yuin/goldmark"
	"io/ioutil"
	"os"
)

func main() {
	source, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		panic(err)
	}

	var buf bytes.Buffer
	if err := goldmark.Convert(source, &buf); err != nil {
		panic(err)
	}

	fmt.Println(buf.String())
}

ビルド

go build

goldmark モジュールがない!的なエラーが出るので、メッセージに従い取得して再度ビルド:

go get github.com/yuin/goldmark
go build

mymark コマンドが生成されます。 標準入力から markdown テキストを与えると結果がHTMLになって 標準出力されます。

echo '# hello world!' | ./mymark

あとは、この mymark をパスの通った場所に配置するだけです。

コード部分のシンタックスハイライト

このブログにはコード記載が割と多いので、その部分を読みやすく出力したい。 mymark ( goldmark ) で変換した結果の HTML のコード部分にシンタックスハイライトをつけるにはどうしたらいいか?
結論としては highlightjs.org を使いました。

たとえば、次の入力に対して:

class Main {
    public static void main(String[] args){
        System.out.println(args);
    }
}

goldmark は次の HTML を出力します。

<pre><code class="language-java">class Main {
    public static void main(String[] args){
        System.out.println(args);
    }
}
</code></pre>

code 要素に language-java とここでは出力されていますが、 つまり、hoge 言語に対して language-hoge というクラス名をつけてくれるわけです。

これがちょうど highlightjs.org が期待する記述と一致しているので、それがそのまま使えます。 おそらく、goldmark が highlightjs を想定しているのか、それとも、最近ではこの手の記述方法がデファクトスタンダードなのでしょうか。
詳しくは https://highlightjs.org/usage/ をご覧ください。

追伸: コード部分のシンタックスハイライトを直接に

先ほどのシンタックスハイライトはCSSとの併用で実現するものですが、 CSSなしに直接コード部分に色付けした状態のHTMLを出力する方法がありました。

詳細はこちら goldmark-highlightingをご覧ください。

とりあえず一番単純なものの場合、コードにするとこれだけのことです。

package main

import (
    "bytes"
    "fmt"
    "github.com/yuin/goldmark"
    "github.com/yuin/goldmark-highlighting"
    "io/ioutil"
    "os"
)

func main() {
    source, err := ioutil.ReadAll(os.Stdin)
    if err != nil {
        panic(err)
    }

    markdown := goldmark.New(
        goldmark.WithExtensions(
            highlighting.Highlighting,
        ),
    )

    var buf bytes.Buffer
    if err := markdown.Convert(source, &buf); err == nil {
        fmt.Println(buf.String())
    }
}