node.js で使えるテンプレートエンジン


node.js で使用できる HTML ベースのテンプレートエンジンの調査結果を共有します。

# 何故 HTML ベースか

[Jade](http://jade-lang.com/)、[Slim](http://slim-lang.com/)、[Haml](http://haml.info/) などの、HTML を Python のようにインデントでブロック表現し、閉じタグを省略することでシンプルに書き下すテンプレートエンジンが世の中にはありますが、今回はデザイナさんが制作した HTML をそのままテンプレートとして使用したいため、HTML ベースの（あるいは汎用）テンプレートエンジンを調査することにします。

なお、Jade は node.js 用、Slim、Haml は Ruby 用です。

----

# ThymeleafJS

https://github.com/ultraq/thymeleafjs

このテンプレートエンジンは Java の Spring Boot と組み合わせて使用されることがある有名な [Thymeleaf](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf_ja.html) の Node.js 版です。

今回挙げたテンプレートエンジンの中でデザイナの作成した HTML にエンジニアが手を加えた後、デザイナが更に手を加えた際に最もデザイナ、エンジニアの負担が軽いと思います。

## ライセンス

Apache License, Version 2.0

## 特徴

* もとの HTML の見た目にほぼ影響を与えずにコードを埋め込める（Natural templates）
* Thymeleaf サブセット

## サンプル

公式サイトのサンプルです。

```html
<p>Hello <span thjs:text="${username}">(username)</span>
You're using Thymeleaf for JavaScript!  Wanna see some random fruit?</p>
<ul>
  <li thjs:each="product: ${allProducts}">
    <div thjs:text="${product.name}">Oranges</div>
    <div thjs:text="${product.price}">0.99</div>
  </li>
</ul>
```

`thjs:` で始まるブラウザが無視する属性に構文を施す形になるため、他のテンプレートエンジンのように `{xxx}` などがプレビューで散見されたり、それによってデザインが崩れたりしないのが特徴です。拡張子も .html なのがありがたいです。

----

# EJS

https://ejs.co/

今回取り上げた中では多分一番有名で一番実績があると思われます。

実は JavaScript にはもうひとつ EJS というテンプレートエンジンがあります。

こちらの EJS は "Effective JavaScript templating" の略語ですが、もうひとつの [EJS](http://www.embeddedjs.com/) は "Embedded JavaScript" の略語です。こちらは node.js では利用できません。

## ライセンス

Apache License, Version 2.0

## 特徴

* コンパイルとレンダリングが速い
* インクルード機能
* サーバとブラウザの両方で動作する
* 中間 JavaScript を静的にキャッシュ
* テンプレートを静的にキャッシュ
* Express ビューシステムでコンパイル（Express と相性が良い？）

## サンプル

公式サイトのサンプルを見てみます。

### サンプル 1

```html+erb
<% if (user) { %>
  <h2><%= user.name %></h2>
<% } %>
```

第一印象で JSP に似ていると思いました。Scriptlet タグは幾つか種類があるのですが、すべて `<%` で始まり `%>` で終わります。タグのデリミタは、例えば `<?` `?>` などに変更可能です。

### サンプル 2

```html+erb
<ul>
  <% users.forEach(function(user){ %>
    <%- include('user/show', {user: user}); %>
  <% }); %>
</ul>
```

ロジックを Scriptlet タグ内に JavaScript で書き下します（それが良いかどうかは別として）。

# Hogan.js

https://twitter.github.io/hogan.js/

Twitter が開発しているテンプレートエンジンです。Hulk というコマンドラインユーティリティもあり、ロゴなどからもわかる通りどうやら有名なプロレスラーの名前を借りているようです。

## ライセンス

Apache License, Version 2.0

## 特徴

* 3.4k と小さい
* パフォーマンスが良い
* スタンドアロンで動作する
* パーサー API を備える

## サンプル

公式サイトのサンプルです。

### サンプル 1

```html+django
<div class="timeline">

  <!-- load more button -->
  <button>{{message}}</button>

  <!-- tweet object -->
  {{#tweets}}
    {{> tweet}}
  {{/tweets}}

</div>
```

Scriptlet タグは `{{` `}}` を使います。タグのデリミタは `<%` `%>` などに置き換え可能です。

----

# Mustache

https://mustache.github.io/

ロジックレステンプレートです。以下の言語に対応しているのが特徴です。

Ruby, JavaScript, Python, Erlang, node.js, PHP, Perl, Perl6, Objective-C, Java, C#/.NET, Android, C++, Go, Lua, ooc, ActionScript, ColdFusion, Scala, Clojure, Fantom, CoffeeScript, D, Haskell, XQuery, ASP, Io, Dart, Haxe, Delphi, Racket, Rust, OCaml, Swift, Bash, Julia, R, Crystal, Common Lisp

## ライセンス

MIT License

## サンプル

以下のようなテンプレートに：

```html+django
<h1>{{header}}</h1>
{{#bug}}
{{/bug}}

{{#items}}
  {{#first}}
    <li><strong>{{name}}</strong></li>
  {{/first}}
  {{#link}}
    <li><a href="{{url}}">{{name}}</a></li>
  {{/link}}
{{/items}}

{{#empty}}
```

以下の様な JSON を適用すると：

```json
{
  "header": "Colors",
  "items": [
      {"name": "red", "first": true, "url": "#Red"},
      {"name": "green", "link": true, "url": "#Green"},
      {"name": "blue", "link": true, "url": "#Blue"}
  ],
  "empty": true
}
```

以下の様な HTML が生成されます。

```html
<h1>Colors</h1>
<li><strong>red</strong></li>
<li><a href="#Green">green</a></li>
<li><a href="#Blue">blue</a></li>
<p>The list is empty.</p>
```

----

# Handlebars

https://handlebarsjs.com/

Mustache 互換のテンプレートで、以下の機能が強化されています。

* パスの中に Scriptlet でパスをネスト可能
* 変数定義ヘルパーによる、よりシンプルなテンプレート記述が可能
* '#' と '/' によるブロック表現
* リテラル値
* デリミタを用いたコメント

## ライセンス

MIT License

## サンプル

以下のテンプレートに：

```html+django
<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{body}}
  </div>
</div>
```

以下の JSON を適用すると：

```json
{ 
  title: "My New Post", 
  body: "This is my first post!"
}
```

以下の様な HTML が生成されます。

```html
<div class="entry">
  <h1>My New Post</h1>
  <div class="body">
    This is my first post!
  </div>
</div>
```

----

# Swig

https://node-swig.github.io/swig-templates/

シンプルでパワフルで拡張性のある JavaScript テンプレートエンジンです。

## 特徴

* node.js とメジャーなブラウザで動作する
* Django、Jinja2、Twig のテンプレートエンジンと同じ方法論を用いたテンプレートエンジン
* Express 互換
* オブジェクト指向のテンプレート継承
* テンプレート出力時のフィルタ適用と変形
* 安全な HTML レンダリングのための自動エスケープ
* たくさんの繰り返しと条件判定構文をサポート
* 肥大化せず堅牢
* 拡張性とカスタマイズ性
* テストコードカバレッジの凄さ

## ライセンス

MIT っぽい独自ライセンス

## サンプル

以下の様なテンプレートに：

```html+django
<h1>{{ pagename|title }}</h1>
<ul>
{% for author in authors %}
  <li{% if loop.first %} class="first"{% endif %}>
    {{ author }}
  </li>
{% endfor %}
</ul>
```

以下のようにデータを渡すと：

```js
var swig  = require('swig');
swig.renderFile('/path/to/template.html', {
    pagename: 'awesome people',
    authors: ['Paul', 'Jim', 'Jane']
});
```
以下の様な HTML が生成されます。

```html
<h1>Awesome People</h1>
<ul>
  <li class="first">Paul</li>
  <li>Jim</li>
  <li>Jane</li>
</ul>
```

----

# まとめ

ThymeleafJs を除き、Scriptlet タグのデリミタは大きく分けて `<%` `%>` と `{{` `}}` の流派があることがわかりました。テンプレートをブラウザや HTML エディタで開いた際に Scriptlet タグが消えてしまうか見えたままか、という違いがあり、デザイナーさん的には消えてもらった方が良いのかもしれません。

Mustache の対応言語の多さには惹かれています。今回は node.js で動作するテンプレートエンジンで選定評価を行っていますが、node.js 専用ではないというのはアドバンテージです。

EJS、Handlebars、Hogan.js は WebStorm で node.js アプリ開発を行う際に選択可能なテンプレートエンジンである、という点で WebStorem の強力なサポートが得られる可能性があるため、ポイントが高いです。

EJS は普及しているだけあり、ネット上に情報が多いです。Handlebars は公式サイトのドキュメントがしっかりしていて機能を調べやすかったです。

Handlebars と Swig は、Web 上にデモページがあり、特に習得初期にちょっと試してみたいという時には便利です。

などと色々書きましたが、Thymeleaf の Natural templates を継承している ThymeleafJS が今回取り上げた中で一番デザイナフレンドリーだと感じました。2022 年 6 月現在、まだバージョン 1 になっていないため仕様変更が起こりうる、というのが懸念といえば懸念です。