◆ Python の jinja2 に近いもの
◆ ググってよく出てきたりする人気どころ比較では目にしないけどけっこうよさそう

以前にも書いたのですが Node.js はコレっていうテンプレートエンジンがありません

ejs

ひとまず ejs でいいかとも思ったのですが 初期の頃からあるすごくシンプルなもので web サーバの express みたいな立ち位置だと思います
どうせならもう少し新しめでもう少し機能のあるいい感じなものが使いたいです

ejs の機能で十分なものなら ejs でいいと思いますが ちょっと複雑なものになると辛いですし
拡張できるのかもよくわからないです
ejs 自体はそんな大きなライブラリでもないので そういうことするならいっそ自分で作ってしまったほうがカスタムしやすいと思えるくらいです

自作

前の記事でも自分でちょっとしたものを作ってましたが 単純に埋め込むだけで制御文に当たるものはありませんでした
関数で出し分けたり繰り返し処理したものを返せば分岐や繰り返しはできますが テンプレートじゃなく関数側に表示内容や条件を書くことになります
埋め込む値として分岐などの処理済みの値を渡すのと同じようなものです

また テンプレート用のファイルということで {{ }} を使っていましたが テンプレートストリングのほうがパース早いし JavaScript 標準のものなので扱いやすいかと思ってこんなものも作りました

これがテンプレートファイルです
テンプレートストリングを使うので JavaScript ファイルです

const h = require("./template-helper.js")
module.exports = require("./create-template.js")`
<h1>${data => ({text: data.title})}</h1>
<a href="${"url"}">${"text"}</a>
<a href="${{ name: "url2", default: "" }}">${{ name: "text2", escape: false }}</a>
${
data => data.edit
? { html: `<input value="${h.esc(data.value)}">` }
: { html: `<span>${h.esc(data.value)}</span>` }
}
`

使うときはこういう感じです

const t = require("./example-template.js")
t.render({
title: "TITLE",
url: "/a/b.html",
text: "xxxxx",
text2: "<b>yyy</b>zz",
})

create-template のところでいろいろ処理して render メソッドを使うと埋め込んで文字列を返すようにしてます
あくまでテンプレートという形で保持して後から値を埋め込むので ${} の中には基本は文字列で名前を指定します
オプションを指定するときにはオブジェクト形式で name に文字列で名前 その他のプロパティでデフォルト値やエスケープのするしないなどを設定します
その場で処理したいものは関数を渡します
JavaScript ファイルなので関数も普通に使えます
最後の data.edit のように分岐させればテンプレート中で条件指定して分岐ということができます

いったん作っておいたテンプレートデータから文字列化するときに値を埋め込むのではなく テンプレートストリングを含む関数を実行してしまう方が楽な気もするのでこういうバージョンも作りました
エスケープの手間以外はこっちのほうがスッキリしてます

const h = require("./template-helper.js")
module.exports = data => `
<h1>${h.esc(data.title)}</h1>
<a href="${h.esc(data.url)}">${h.esc(data.text)}</a>
<a href="${h.esc(data.urls2) || ""}">${data.text2)}</a>
${
data => data.edit
? `<input value="${h.esc(data.value)}">`
: `<span>${h.esc(data.value)}</span>`
}
`

テンプレートストリングのタグでデフォルトをエスケープにするなどいろいろ考えられます

問題点

割といい感じではあったのですが JavaScript ファイルなので HTML 部分がハイライトされないとかテンプレートが大きくなるとけっこう不便にもなりました
hyperHTML 使ってたときに HTML ファイルをテンプレートにするとかもやってたくらいです
HTML ファイルをテンプレートにして hyperHTML でレンダリングする

一周回ってやっぱりテンプレートファイルはテキストファイルのほうがいいのかもと またテンプレートエンジン探しをしていました

pug

既存 HTML じゃなく新しく書くならコレでいいかと思ったのですが 使ってみると思ったより大変でした
独特な書き方なので 〇〇 するにはどうすればいいのかを毎回探すことになります
慣れるまでが大変です

さらに インストールサイズがけっこう大きめでした
もうちょっとシンプルなものでいいかな と早いうちに諦めました

昔 自分で似たようなインデントベースの HTML 出力ツールを作ったこともあって 自作なわけですから そっちのほうが書きやすくて pug って書きづらいなと感じてしまい相性も悪かった感があります
それを覆すくらいの便利っていう機能があればよかったのですが特に見つかりませんでした

handlebars

けっこう人気で比較的新しい方だと思うのでとりあえず使ってみました
簡単なものを書くうちはこれで十分で ejs よりデリミタが好きなのでこっちでいいかもと思っていました

あるとき if 文を書こうとしたら if 文で式を書けないという衝撃的なことに気づきました
値を書いて true/false の判断をする if 文はありますが === や > など演算子は書けません
mustache というロジックレステンプレートエンジンがベースなのでロジックは標準機能ではサポートしないようです

書くためには自分でヘルパを作る必要があります
https://stackoverflow.com/questions/8853396/logical-operator-in-a-handlebars-js-if-conditional

大きめなものを作るときに 自分で色々ヘルパを作るのならついでに書いておけばいいのですが ライブラリだけ入れて自分でカスタムせずサッと使いたいって時には向かなそうです

handlebars で大丈夫そうと思ったときにこれで有名どころはどれもコレって言うのがありませんでした

Nunjucks

諦めて自分でそこそこちゃんとしたものを作るのがいいのかなと考え始めたときに偶然見つけたのがこれです

https://mozilla.github.io/nunjucks/

Nunjucks という聞いたことないような名前です
マイナーなものも含めて 10 個以上のツールを比較してるサイトだとあったかもしれませんが特に目に止まらずマイナーなものだろうとスルーしていたものだと思います

探していたときに Python の jinja2 を使っていたのでそれに近いものを探して出てきたものです
jinja2 inspired らしくほぼ同じようにかけるそうです
(まだ使ってみてはいないです)

デフォルトは ejs と同じく % を使っていますが 自由に変更できるようです

var env = nunjucks.configure('/path/to/templates', {
tags: {
blockStart: '<%',
blockEnd: '%>',
variableStart: '<$',
variableEnd: '$>',
commentStart: '<#',
commentEnd: '#>'
}
});

ドキュメントの情報も多くて見やすいほうだと思います
https://mozilla.github.io/nunjucks/templating.html

私も驚いたのですが mozilla が作ってるライブラリでした
上の公式サイトの URL にもありますが github のリポジトリのグループが mozilla になってます
Firefox Marketplace などでこのライブラリが使われてるようです……が私は見たことないです
見た目けっこう新しそうなのに そこまで新しいものでもなく 2013 年に 1.0 がリリースされたものでした
ejs は 2010 年からありますが 1.0 になったのは 2014 年です
https://github.com/tj/ejs/blob/master/History.md

けっこう古めですね
それでいて今でもアップデートは続いてます
とりあえず mozilla なら安心できますね

twig

残りの候補が twig という PHP の twig を JavaScript で実装したものらしいライブラリだったのですが これも Python の jinja に近いものでこれなら Nunjucks でいいかなというところでした