【Server Side Swift】MarkdownProviderを使う【Vapor】

Server Side Swift のフレームワークVaporにおいて、Markdown文字列をHTMLに変換してくれるライブラリを使ってみます。

今回のサンプルコードは[こちらのリポジトリ](https://github.com/taji-taji/vaporSamples/tree/master/MarkdownSamples)に置いています。


# 各種バージョン

- Vapor Toolbox: 2.0.3
- Vapor: 2.0.5
- MarkdownProvider: 1.0.0

# Vapor で **`MarkdownProvider`** を使う

## ● MarkdownProviderとは？

リポジトリ：https://github.com/vapor-community/markdown-provider

- HTMLテンプレートエンジン`Leaf`にて、Markdown文字列をHTML文字列に展開してくれる`#markdown()`タグを使えるようになります
    - Leafについては[こちら](https://docs.vapor.codes/2.0/leaf/leaf/)
- [SwiftMarkdownパッケージ](https://github.com/vapor-community/markdown)のVaporProviderです
- MarkdownProviderは、SwiftMarkdownをVaporプロジェクトで簡単に使用できるようにしてくれるパッケージです



### → SwiftMarkdownパッケージ

- Markdown文字列をHTML文字列に変換してくれるライブラリです
- 実装は[cmark](https://github.com/github/cmark)のSwiftラッパーです

## ● 基本的な使い方

### Package.swiftにパッケージを追加

```swift
dependencies: [
  ...,
  .Package(url: "https://github.com/vapor-community/markdown-provider", majorVersion: 1)
]
```

上記で

- `MarkdownProvider`パッケージ
- `SwiftMarkdown`パッケージ

がプロジェクト内で使用可能になります。

### ▼ Leafに`#markdown()`タグを追加する

２通りの方法でLeafのカスタムタグとして`#markdown()`タグが使えるようになります。

#### - Providerとして`Config`に追加する場合

```swift:Sources/App/Config+Setup.swift
import LeafProvider
import MarkdownProvider

extension Config {
    public func setup() throws {
        // allow fuzzy conversions for these types
        // (add your own types here)
        Node.fuzzy = [JSON.self, Node.self]

        try setupProviders()
    }
    
    /// Configure providers
    private func setupProviders() throws {
        try addProvider(LeafProvider.Provider.self)
        try addProvider(MarkdownProvider.Provider.self) // <- MarkdownProviderを追加
    }
}
```

#### - LeafRendererに直接追加する場合

`main.swift`などで、LeafRendererに直接登録することでも`markdown`タグが使えるようになります。

```swift:main.swift
import App
import LeafProvider
import MarkdownProvider

// some code...

if let leaf = drop.view as? LeafRenderer {
    leaf.stem.register(Markdown())
}

// some code...
```

### ▼ Viewテンプレート(Leaf)にマークダウンを渡す

#### 1. ControllerからViewに渡すMarkdown文字列を設定する

```swift:Sources/App/Controllers/Sample1.swift
import Vapor
import HTTP

final class Sample1Controller: ResourceRepresentable {
    let view: ViewRenderer
    init(_ view: ViewRenderer) {
        self.view = view
    }
    
    /// GET /sample1
    func index(_ req: Request) throws -> ResponseRepresentable {
        let markdown: String = {
            return "# Hello world!\n"
                + "## This is sample1.\n"
                + "This sample is to use Leaf custom tag `#markdown()`"
                + " for Simple Markdown string."
        }()

        return try view.make("sample1", [
            "markdown": markdown
        ], for: req)
    }

    func makeResource() -> Resource<String> {
        return Resource(
            index: index
        )
    }
}
```

#### 2. Viewテンプレートで`#markdown()`タグを使用する

```leaf:Resources/Views/sample1.leaf
#extend("base")

#export("title") { Sample1 }

#export("content") {
    #markdown(markdown)
}
```
**`#markdown(markdown)`** のところでControllerから渡ってきたMarkdown文字列がHTMLにパースされる

ちなみに、`#extend("base")`によって次のようなbaseテンプレートが継承されています。
この辺りはLeafの使い方になりますので、docsを参照ください。
https://docs.vapor.codes/2.0/leaf/leaf/

```leaf:Resources/Views/base.leaf
<!DOCTYPE html>
<html>
<head>
	<title>#import("title")</title>
	<link rel="stylesheet" href="/styles/app.css">
</head>
<body>

#import("content")

</body>
</html>
```

![スクリーンショット 2017-06-18 12.57.59.png](https://qiita-image-store.s3.amazonaws.com/0/11914/0f65c3bf-a9c5-8ce7-49c9-c9e343a245f6.png)



## ● Advanced

### ▼ HTMLタグを含むMarkdown文字列を使用する場合

Markdown文字列にHTMLタグを記述している場合、前述のような使用方法ではHTMLタグ部分はエスケープされてしまいレンダリングされません。
なので、Controller側でHTML文字列にして、Leaf側で`#raw()`タグを使うことで表示させることができます。

```swift:Sources/App/Controllers/Sample2.swift
import Vapor
import HTTP
import SwiftMarkdown

final class Sample2Controller: ResourceRepresentable {
    let view: ViewRenderer
    init(_ view: ViewRenderer) {
        self.view = view
    }

    /// GET /sample2
    func index(_ req: Request) throws -> ResponseRepresentable {
        // HTMLタグが含まれるMarkdown文字列
        let markdownString: String = {
            return "# Hello world!\n"
                + "## This is sample2.\n"
                + "<p>This sample is in case of that the Markdown String contains raw HTML String.</p>\n"
                + "In this case, Leaf is ignore raw HTML String. So, you could use `markdownToHTML()` method "
                + "and put parsed HTML String to Leaf template by `#raw()`"
        }()
        // SwiftMarkdownパッケージの `markdownToHTML` を使ってHTMLに変換する
        let markdown = try markdownToHTML(markdownString, options: [])

        return try view.make("sample2", [
            "markdown": markdown
            ], for: req)
    }

    func makeResource() -> Resource<String> {
        return Resource(
            index: index
        )
    }
}
```

↑ SwiftMarkdownパッケージの **`markdownToHTML`** メソッドを使用することで、Markdown文字列をHTML文字列に変換することができます。


```leaf:Resources/Views/sample1.leaf
#extend("base")

#export("title") { Sample2 }

#export("content") {
    #raw(markdown)
}
```

↑ 渡ってきた`markdown`変数はHTML文字列になっているので、`#raw()`タグを使うことでエスケープされずに表示することができます。

![スクリーンショット 2017-06-18 12.58.11.png](https://qiita-image-store.s3.amazonaws.com/0/11914/3d55b88f-756f-6cec-f36d-70566ca7a7d1.png)


# 最後に

ちょっと雑な説明になりましたが、、こんな感じで使えます。

使う場面は限られるかもしれませんが、気軽にMarkdownをHTMLにパースできていいですね。
WebコンテンツをMarkdownで書いて、CSSに[github-markdown-css](https://github.com/sindresorhus/github-markdown-css)とかを用いれば、Markdownベースで見やすいWebコンテンツを作成できるかもしれません。
また、Vaporでの使用のみならず、SwiftMarkdownパッケージ単体で使用するだけでも十分便利なので、使い方は色々考えられるかなと思います。

あ、あと、サンプルのMarkdown文字列の英文が多少おかしいのは見逃してください:joy: