マルチパートなMarkdown文書を作る

# マルチパートなMarkdown文書を作る

## ぼやっとおもっていたこと

* Excelのシートのような概念で１ファイルにまとめつつドキュメントを分けられたらいいな
* 画像ファイルも１ファイルにまとめられたらいいな
* VCSとの相性が良くなるようにすべてプレーンテキストで
* 既存のMarkdownエディタやMarkdown->HTML変換ツールが使えるようになるべくMarkdownの仕様範囲内で

そんなことを考えつつMarkdownでドキュメントを作る手法について情報を集めているとこんな記述に目がとまった。

「Markdownのマルチパート化」
※どこで見たかはわすれてしまいました

[MarkdownにBase64で画像を埋め込む](https://qiita.com/kedama17/items/5fc285200ae5d9d0023a)試みは以前やりました。これとMarkdownのマルチパート化という考え方を組み合わせれば、Office文書のように全部入りでかつプレーンテキストでバージョン管理可能なMarkdown文書がつくれるのでは？という思いでつくってみました。

## やってみよう

Markdownは文書中にHTMLタグやコメントを書くことができます。
コメントに各パートのヘッダとフッタを書くことで複数のMarkdown文書を一つにまとめ、かつ画像も埋め込み可能になるような仕様を考えてみます。さらにそのコンバータを実装します。便宜上定義する仕様をMultipartMarkdown（MultiMarkdownではない）と呼ぶことにします。

## 目的

例えば以下のようなMarkdown文書があるとしましょう。

```md:sample-index.md
# sample-index

This is sample-index.

[sample1](./sample1.html)

![sample-img](./images/sample-img.png)
```

```md:sample1.md
# sample1

This is sample1.
```

sample-indexはドキュメントのルートとなるMarkdown文書です。そこから別ファイルで作られるMarkdown文書にリンクを張っています。最終的にはHTMLにして出力する予定なのでリンクのアドレスは相対パスで拡張子がhtmlになっています。さらに画像がひとつあります。

```
sample
├── sample-index.md
├── sample1.md
└── images
    └── sample-img.png
```

こんな感じのディレクトリでリソースが管理されているとしましょう。
目的はこれらのファイルを一つのプレーンテキストでMarkdownとして解釈可能なテキストファイルにすることです。

## MultipartMarkdownの仕様

問題はどこからどこまでが一つのファイル、すなわちパートであるかを表現するかです。当初の目論見通りコメントに特定の形式のヘッダとフッタを書くことでそれを表現するようにしてみます。目指す最終形は以下のような形です。

```md:sample.md
<!-- begin-part markdown ./sample-index.md -->
# sample-index

This is sample-index.

[sample1](./sample1.html)

![sample-img](./images/sample-img.png)
<!-- end-part -->

<!-- begin-part markdown ./sample1.md -->
# sample1

This is sample1.
<!-- end-part -->

<!-- begin-part image ./images/sample-img.png -->
... BASE64化された画像 ....
<!-- end-part -->
```

コンバータは複数のMarkdown文書＋画像ファイルからMutlipartMarkdown文書への変換とその逆変換（MultipartMarkdown文書から複数のMarkdown文書＋画像ファイルへの展開）ができるようにすることを考えます。

逆変換をする際にはMarkdown文書中でリンクされている文書を含めるようにするのですが、ここで問題がひとつ。リンクしている文書がMarkdownからHTMLに変換される予定の文書なのか、それともローカルに置かれたHTMLファイルなのか判断がつかないのです。根本的な解決方法は思いつかなかったのでとりあえず相対パスのhtmlへのリンクを見つけたら、そのファイル名と同名のMarkdown文書（拡張子.mdのファイル）があった場合はMarkdown文書へのリンクをしようとしているとみなす、とすることで対応することにしました。

## 実装

仕様が決まったら実装です。変換時はリンクを正規表現で検出し、再帰的に辿りつつMarkdownへのリンクをみつけたら連結し、画像へのリンクを見つけたらBase64にエンコードしてから連結することにします。
逆変換時はこれまた正規表現でヘッダとフッタを検出し、Markdownの場合はテキストファイルに書き出し、画像の場合はBase64をデコードしてバイナリファイルに書き出します。
なんだかとても無駄なことをしている気がします。

ちなみにCommon Lispで作っています。

## 成果物

できたものはこちら。

[GitHub - multipart-markdown](https://github.com/singy15/multipart-markdown)

こんな感じのファイル群を

```
sample
├── sample-index.md
├── sample1.md
├── images
│   └── sample-img.png
└── sub
    └── sample2.md
```
```md:sample-index.md

# sample

This is a sample document.

![sample-img](./images/sample-img.png)

[multipart-markdown](https://github.com/singy15/multipart-markdown)

## sample1

Link sample1.
[sample1](./sample1.html)

## sample2

Link sample2.

[sample2](./sub/sample2.html)
```

```md:sample1.md

# sample1

This is sample1.
```

```md:sub/sample2.md
# sample2

This is sample2.
```

sample-img.png
![sample-img.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74246/0af75cdb-1fe1-361b-90b5-0b43f6e01438.png)

こんな感じの１ファイルにできます。

```md:sample.md
<!-- begin-part markdown ./sample-index.md -->
# sample

This is a sample document.

![sample-img](./images/sample-img.png)

[multipart-markdown](https://github.com/singy15/multipart-markdown)

## sample1

Link sample1.
[sample1](./sample1.html)

## sample2

Link sample2.

[sample2](./sub/sample2.html)
<!-- end-part -->

<!-- begin-part image ./images/sample-img.png -->
iVBORw0KGgoAAAANSUhEUgAAAGQAAABBCAIAAACo4ZaGAAAACXBIWXMAADXUAAA11AFeZeUIAAAH
... 長いので省略 ...
9v8uOgdb4qjMuPQa/P8J1+9vvyvEhqwS2JBVAhuySmBDVglsyCqBDVklsCGrBDZklcB/JDGKQx2j
0HsAAAAASUVORK5CYII=
<!-- end-part -->

<!-- begin-part markdown ./sample1.md -->
# sample1

This is sample1.
<!-- end-part -->

<!-- begin-part markdown ./sub/sample2.md -->
# sample2

This is sample2.
<!-- end-part -->
```

もちろん作成されたMultipartMarkdown文書から複数のMarkdown文書＋画像ファイルに逆変換することもできます。

## どんなことができそうか

ぼやっとおもっていたとおり以下のようなことができそうです。

* 編集時やHTMLへの変換時には逆変換してやることで既存のエディタや変換ツール（TyporaやPandoc）を利用しつつ１ファイルなMarkdownを実現
* ディレクトリごとzip圧縮する場合と異なりバイナリファイルではないのでGitなどのVCSで管理でき、差分も簡単に取れる

うーん、作ってみたはいいのですが、結局のところMarkdownとしても解釈できる独自形式のファイルフォーマットとそのコンバータを作っただけなので正直微妙ですね。