Nimで静的ファイルの配信

[htmlgen](https://nim-lang.org/docs/htmlgen.html)の公式しか頼れなかったので、ここにメモします。

htmlgenをimportすると、HTMLタグに対応したmacroが使えます。

# 例

## h1
例えばh1タグ

```nim:nim
h1("hello world!")
```

↓のように解釈されます

```html:html
<h1>hello world!</h1>
```

サーバーとして実行して試すなら下記のように記述します。
フレームワークに[ Jester ](https://github.com/dom96/jester)を使っています。
`$ nimble install jester`としてjesterを使えるようにしてください。

```nim:nim
import std/htmlgen, jester

router route:
  get "/":
    let html = h1("hello world!")
    resp(Http200, html)

proc main() =
  let settings = newSettings(port = Port(3333), staticDir = "static/")
  var jes = initJester(route, settings = settings)
  jes.serve()

when isMainModule:
  main()
```

これでブラウザから`localhost:3333`へアクセスすると、"hello world!"と大きな文字で表示されます。


## a
例えばaタグ

```nim:nim
a(href="https://qiita.com/", "Qiitaのトップページ")
```

↓のように解釈されます

```html:html
<a href="https://qiita.com/">Qiitaのトップページ</a>
```


[htmlgen](https://github.com/nim-lang/Nim/blob/version-1-6/lib/pure/htmlgen.nim#L118)のソースを見てみるとどんなタグが指定可能かわかります。
たいていのHTMLタグが列挙されていることがわかると思います。
aタグだったら、"href target download rel hreflang type " とあるので、これらのスペース区切りの文字列を指定可能なタグの属性として指定できます。

```nim
macro a*(e: varargs[untyped]): untyped =
  ## generates the HTML ``a`` element.
  result = xmlCheckedTag(e, "a", "href target download rel hreflang type " &
    commonAttr)
```

### commonAttr
[commonAttr](https://nim-lang.org/docs/htmlgen.html#commonAttr)は文字通り共通の属性で、すべてのマクロに使える属性です。
classやid, styleなどが含まれます。

```nim
commonAttr = " accesskey class contenteditable dir hidden id lang spellcheck style tabindex title translate onabort onblur oncancel oncanplay oncanplaythrough onchange onclick oncuechange ondblclick ondurationchange onemptied onended onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart onmousedown onmouseenter onmouseleave onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange onreset onresize onscroll onseeked onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate ontoggle onvolumechange onwaiting  role "
```



実際にブラウザーへアクセスして確かめるなら、先ほどのサンプルの`router`ブロックを下記のように書き換えます。

```nim:nim
router route:
  get "/":
    let
      h1 = h1("hello world!")
      a = a(href = "https://qiita.com/", "Qiitaのトップページ")
      html = h1 & a
    resp(Http200, html)
```


## input
例えばinputタグ

```nim:nim
input(type = "text",
      name = "search-word",
      id = "search-word",
      placeholder = "検索キーワードを入力",
      size = "20")
```

↓のように解釈されます

```html:html
<input type="text" name="search-word" id="search-word" placeholder="検索キーワードを入力" size="20">
```

複数の属性を指定してみます。

```nim:nim
router route:
  get "/":
    let
      h1 = h1("hello world!")
      a = a(href = "https://qiita.com/", "Qiitaのトップページ")
      input = input(type = "text",
                    name = "search-word",
                    id = "search-word",
                    placeholder = "検索キーワードを入力",
                    size = "20")
      html = h1 & a & input
    resp(Http200, html)
```


## table
例えばtableタグ

tableを書くときはtrタグとtdタグを組み合わせます。

```nim:nim
table(
  tr(td("要素"), td(input(id = "短い", value = "短い文書"))),
  tr(td("文書"), td(
    textarea(
      id = "長い",
      readonly = "",
      style = "width:242px; height:100px;",
      "長い文書",
      )
    )
  )
)
```

↓のように解釈されます。
適当にインデントしています。

```html:html
<table>
  <tbody>
    <tr>
      <td>要素</td>
      <td><input id="短い" value="短い文書"></td>
    </tr>
    <tr>
      <td>文書</td>
      <td><textarea id="長い" readonly="" style="width:242px; height:100px;">長い文書</textarea></td>
    </tr>
  </tbody>
</table>
```

例ではテーブルの要素の中にinputタグやtextareaタグを入れて複雑なテーブルになっています。

textareaタグではreadonly属性を使っていますが、
readonlyのような値を持たない属性をNimのマクロで指定するときは空の文字列を指定するようです。

```nim:nim
textarea(readonly = "", "長い文書")
```

↓のように解釈されます

```html:html
<textarea readonly> 長い文書 </textarea>
```

## head, link
CSSやfaviconのパスを記述するために使うheadタグとlinkタグ

```nim:nim
favicon = link(type = "image/png",
            rel = "icon",
            href = "/favicon.png")
bootstrap = link(href = "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css",
              rel = "stylesheet",
              crossorigin = "anonymous")
head = head(favicon & bootstrap)
```

↓のように解釈されます。
適当にインデントしています。

```html:html
<head>
  <link type="image/png" rel="icon" href="/favicon.png">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
</head>
```

headタグにlinkなんかを書く例。
これでfaviconを表示し、Bootstrap5が使えます。

## script
例えばscriptタグ

```nim:nim
js = script(type = "module", src = "/dist/main.js")
```

↓のように解釈されます。

```html:html
<script type="module" src="/dist/main.js"></script>
```


# さいごに

全部結合すればHTMLファイルの完成です。

```nim:nim
router route:
  get "/":
    let
      favicon = link(type = "image/png",
                  rel = "icon",
                  href = "/favicon.png")
      bootstrap = link(href = "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css",
                    rel = "stylesheet",
                    crossorigin = "anonymous")
      head = head(favicon & bootstrap)

      h1 = h1("hello world!")
      a = a(href = "https://qiita.com/", "Qiitaのトップページ")
      input = input(type = "text",
                    name = "search-word",
                    id = "search-word",
                    placeholder = "検索キーワードを入力",
                    size = "20")
      table = table(
        tr(td("要素"), td(input(id = "短い", value = "短い文書"))),
        tr(td("文書"), td(
          textarea(
            id = "長い",
            readonly = "",
            style = "width:242px; height:100px;",
            "長い文書",
            )
          )
        )
      )
      js = script(type = "module", src = "/dist/main.js")
      body = h1 & a & input & table & js

      html = head & body
    resp(Http200, html)
```

index.html的なものを最初に書いておいて変数を埋め込む、という形ではなく、
マクロによってNimの関数を書くようにHTMLを記述していくスタイルでした。

[マクロのソース](https://github.com/nim-lang/Nim/blob/version-1-6/lib/pure/htmlgen.nim)を眺めているだけでも、HTMLタグやタグに指定できる属性を知ることができるので勉強になりますね。