◆ 色々ありそうなのにあんまり良さそうなのなかった
  ◆ PHP のほうが興味持てるのあるくらい
  ◆ mustache というシンプルなのが人気上位にあるくらい
◆ handlebars は使い方わかりづらい
◆ jade (pug) 系は HTML タグ書きたくないときには良さそうだけど今回の目的に合わない
◆ ejs は昔からあって更新も続いてるし ドキュメント・使いやすさ・信頼性全部良さそう
  ◆ だけどタグが <% なのだけがだめ
◆ 追記: タグ変更できたので ejs で良さそう

複数のパーツに分けて HTML を作っていて include などの処理を行って最終的な HTML ファイルを作るためのツールを探しています

基本は簡単に include できることと あとは HTML 中の好きなところで式を評価した値を埋め込めれば十分というちょっとした機能だけあればいいです
テンプレートエンジンならだいたいのが対応してそうです
機能面はメジャーどころのテンプレートエンジンなら大丈夫そうですが 長く使う可能性があるので一時的に流行ってるだけですぐ消えそうなのは避けたいです
あとは書き方が気に入れば

PHP そのものや PHP の Blade などはそれなりに使いやすくてこれでもいいといえばいいのですが include するファイルに JavaScript の処理を通したりするので JavaScript だけで済むように node.js で使えるのを探してみました
ブラウザ上の動的に実行するものじゃなくて サーバに配置する時点でビルド済みのものを配置するのでローカルで実行できる node.js じゃないとだめです

あんまりなかった

node.js ももうバージョンは 8.x
0.x を抜けてけっこうたちますし node.js 製アプリも増えているのでテンプレートエンジンは色々あるかな と思ったのですが PHP に比べるとこれというのがありませんでした
github 探せばいつ終わるのかわからない というかもう終わってそうなものはいっぱいありますが ドキュメントもあって実用されてるレベルのは少ない感じです


ググって名前をあちこちで見るようなものは

  • Jade (Pug) の派生
  • mustache の拡張

のどちらかが多かったです

最近の人気はこの辺のようです

https://colorlib.com/wp/top-templating-engines-for-javascript/
https://www.slant.co/topics/51/~best-javascript-templating-engines


React などのフレームワークも含まれてますが そういうのを抜けば昔からあるテンプレって感じのものばかりです

聞いたことない新しい?のもありますが 人気はそこまで高くないようで 新しいのに移っていくというよりは長続きせずに昔からあるもののほうが長く残りそうに見えます
古くからあるものでも開発が継続されてるなら 新しいのがすごく魅力的でもないと移る必要もないですからね

jade 系

結構昔から名前を聞いていた jade は今では pug という名前に変わっていました
かっこいい系の名前だったのにいつのまにかかわいい系の名前になっていて脳内であんまり結びつかないです

これ系は HTML タグじゃなくてインデントベースで独特な書き方をします
HTML タグを書くのが面倒なときは便利だと思いますが HTML を書いて一部に何か埋め込んだりしたいときには使えません

今回は HTML ベースなのでパスします

mustache 系

mustache はいろんな言語で実装されてるシンプルなテンプレートエンジンです

{{ }}

形式で個人的には好きな書き方です

ただ シンプルさを重視してる感じで 将来的に複雑なことしたくなったら対応してなそう なのと イマイチわかりやすくまとまったドキュメントが見つからずもうちょっとわかりやすい今風なドキュメントなのがいいなー とか思うと 悪くはないけどこれにしよう とも言えない感じです


mustache 系には handlebars という人気が高いテンプレートエンジンもあります
最初はこれでいいかな と思ったのですがドキュメントが不足気味
API リファレンスに機能はあってもそれをどう使えばいいのか とか求めてる情報がないタイプのドキュメントで結局 stackoverflow とかに頼ることになって これもなんか違うかなーという感じです
mustache よりは機能多そうですが 使いこなすための詳しい情報は書いてなくて使ってみて動き確認してって印象です

あと include が partial という機能で {{> }} というなんかコレじゃない感じの書き方
パーツ部分は最初に登録しとかないといけないみたいですし dynamic 版もあるようですが () で関数名?を囲んだりやっぱり変な書き方です

ドキュメント全体を見なくても使いたい機能だけを検索してサッと使いたいのに できるのかを調べるのすら時間がかかったのでもうパスしました
暇な時にゆっくりいじってみる分にはいいのですが 需要にあったものをサッと使いたいときには向いてないです


handlebars に似たもので Template 7 というのもあります
こっちは人気はそこまで高くなくて いつまで続くのかも心配があるので長く使うにはちょっとためらいます

ただ 公式ページの見やすさは handlebars 以上で わかりやすくまとまってます
ほとんど同じような書き方なので 先にこっちを見て handlebars や mustache 系の基本的な構文や機能を把握してからそれぞれのドキュメント見たほうがわかりやすいと思います

jade などの HTML タグ書かない系のや mutache などの {{}} 構文が多いですが他にもあります

有名なのだと ejs
最初はこれでいいかな と思いました

ただ唯一の気に入らないところが <% %> 形式
キーボードの 「%&'」 の 3 つはホームポジションから指が届かなくて腕を動かさないといけないので個人的にあんまり打ちたくない文字です
あと <% があんまりキレイな見た目じゃないです
<$ $> とか <@ @> なら特に気にしなかったのですが……

ちょっとした部分ではあるもののテンプレートエンジンの機能を使う部分全てに関わってるところでもあって ejs はやめました


ほかには doT や Dust というのがあるようです
ejs, jade, mustache は調べる前から知っていたくらいに有名なものですが この辺は聞いたことなかったです

はっきりとした理由があるわけではないですが 公式ページ見てなんとなく長期的には生き残れず消えそうな予感がしたので 詳しく調べてないですがパスです
mustache 系みたいな他に互換があれば開発終わっても 移りやすいですが 独自のフォーマットだと書き直しが大変です

選ばれたのは・・・?

ejs と handlebars が最後まで候補に残ったのですが ejs は% がイヤ handlebars は書き方調べるのが面倒そうで苦労多そうというところです
ejs が {{ }} 形式ならよかったのに

両方試して簡単なもの作ってみるのが一番ですが できればそんな時間も取りたくないです

考え直してみれば 今欲しい機能自体は 最初に上げた 2 つだけです
if や foreach とかはなくてもいいです

……これくらいだと自分でつくれるんじゃない?

と思いついたのでサッと作ってみました
自作なら簡単に拡張できるので 後からあの機能がないから別のに移行したいとか困りません
途中で開発終わってバグが放置されるとか 使い方調べるのが面倒という心配もないです

!function simpleTemplateBuilder(){
const AsyncFunction = (async () => {}).constructor

async function build(str, vars = {}){
const {texts, expressions} = parse(str)

const resolved = await Promise.all(expressions.map(e => {
return AsyncFunction(...Object.keys(vars), `return ${e}`)(...Object.values(vars))
}))

return texts.reduce((a, e, i) => a + resolved[i - 1] + e)
}

function parse(str){
const m = str.match(/\{\{([^\r\n]*?)\}\}/)
if(m === null){
return {
texts: [str],
expressions: [],
}
}else{
const pre = str.substr(0, m.index)
const exp = m[1].trim()

const {texts: post_texts, expressions: post_expressions} = parse(str.substr(m.index + m[0].length))

return {
texts: [pre, ...post_texts],
expressions: [exp, ...post_expressions],
}
}
}

this.stb = build
}()

使うときはこんな感じ

await stb("***{{fn(location.href)}}***", {fn: url => fetch(url).then(e => e.text()).then(e => e.substr(0, 40))})
// "***<!DOCTYPE html>
// <html lang="ja" itemscop***"

await stb("{{0}}{{v}}", {v: 1})
// 01

{{ }} 形式で内側の式を実行するだけの単純なものです
非同期関数を実行して Promise 型を返す式でも大丈夫です

include する場合は include 関数を作っておいて 第二引数いれます
今回だと include するファイルの拡張子に応じてそれぞれの処理を通した結果を include したいので関数にしたほうが扱いやすいので include 用の構文とかはつけてないです



今回は自作ので十分だったのですが 自分で作ってられないレベルの複雑なことしたいときとか出てくる可能性はありますし node.js でも普段使いにするテンプレートエンジンあったほうがいいな と思ったのですがやっぱり上記の問題で決められないんですよねー

何かピンとくるやつでてこないかなー


追記: ejs に Custom delimiters があった!!!

書き終わってすぐですが ないだろうなーと思いつつ 「ejs change tag symbol」 みたいなワードでぐぐってみると

ありました!
カスタムデリミタ

好きな記号に変えれるようです!!

mustache や handlebars は特にですが ejs も公式ページのロゴっぽい部分が <%= EJS %> になっていて タグの文字がそのテンプレートエンジンのアイデンティティみたいな印象でそこを変えれるなんて思ってませんでした

せっかく自作の作っては見たけど テンプレートはまだ作り始めてないし ejs でいいかなー
include する時のプリプロセス用の Custom FileLoader って機能まであるし handlebars と違って必要機能は簡単に把握できてすぐに書けそうですし